From a1b07ba665e6c7f0929df1ccdee58f064ab7de54 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 3 May 2002 03:28:07 +0000 Subject: [PATCH] Initial revision --- Makefile | 255 + doc/guided_tour.txt | 170 + doc/interesting_stats.txt | 52 + doc/release_log.txt | 17 + include/2d.h | 713 + include/3d.h | 333 + include/3dfx.h | 112 + include/3dinternal.h | 67 + include/ac.h | 31 + include/acm.h | 58 + include/addvariabledlg.h | 59 + include/adjustgriddlg.h | 73 + include/afterburner.h | 60 + include/ai.h | 715 + include/aibig.h | 78 + include/aigoals.h | 226 + include/ailocal.h | 8 + include/alphacolors.h | 50 + include/animplay.h | 142 + include/asteroid.h | 253 + include/asteroideditordlg.h | 77 + include/audiostr.h | 140 + include/awacs.h | 65 + include/barracks.h | 33 + include/beam.h | 231 + include/bgbitmapdlg.h | 185 + include/bitblt.h | 37 + include/bmpman.h | 348 + include/briefingeditordlg.h | 226 + include/campaigneditordlg.h | 110 + include/campaignfilelistbox.h | 39 + include/campaigntreeview.h | 107 + include/campaigntreewnd.h | 125 + include/cfile.h | 595 + include/cfilearchive.h | 70 + include/cfilesystem.h | 67 + include/cftp.h | 97 + include/channel.h | 70 + include/chatbox.h | 142 + include/childfrm.h | 42 + include/chttpget.h | 109 + include/circle.h | 39 + include/cmdbrief.h | 92 + include/cmdline.h | 180 + include/cmeasure.h | 162 + include/codec1.h | 132 + include/colors.h | 53 + include/contexthelp.h | 55 + include/controlsconfig.h | 481 + include/convert.h | 94 + include/corkscrew.h | 35 + include/createwingdlg.h | 37 + include/credits.h | 36 + include/crypt.h | 42 + include/cutscenes.h | 76 + include/debriefingeditordlg.h | 114 + include/debris.h | 195 + include/demo.h | 104 + include/dialog1.h | 62 + include/ds.h | 295 + include/ds3d.h | 56 + include/dscap.h | 49 + include/dumpstats.h | 56 + include/editor.h | 40 + include/emp.h | 142 + include/encrypt.h | 53 + include/eventeditor.h | 245 + include/eventmusic.h | 212 + include/exceptionhandler.h | 82 + include/fhash.h | 54 + include/fireballs.h | 198 + include/fishtank.h | 29 + include/fix.h | 42 + include/flak.h | 71 + include/floating.h | 137 + include/font.h | 149 + include/fonttool.h | 44 + include/fred.h | 212 + include/freddoc.h | 169 + include/fredrender.h | 146 + include/fredview.h | 519 + include/freespace.h | 346 + include/freespaceresource.h | 94 + include/fvi.h | 188 + include/fxdll.h | 125 + include/fxglob.h | 31 + include/fxos.h | 51 + include/gameplayhelp.h | 35 + include/gamesequence.h | 483 + include/gamesnd.h | 566 + include/glide.h | 1377 ++ include/glidesys.h | 128 + include/glideutl.h | 133 + include/gradient.h | 53 + include/grd3d.h | 60 + include/grd3dinternal.h | 214 + include/grdirectdraw.h | 33 + include/grglide.h | 37 + include/grglideinternal.h | 58 + include/grid.h | 78 + include/grinternal.h | 210 + include/gropengl.h | 37 + include/grsoft.h | 56 + include/grzbuffer.h | 48 + include/helped.h | 50 + include/helpeddoc.h | 63 + include/helpedline.h | 26 + include/helpedview.h | 66 + include/hud.h | 407 + include/hudartillery.h | 45 + include/hudbrackets.h | 84 + include/hudconfig.h | 210 + include/hudescort.h | 81 + include/hudets.h | 70 + include/hudgauges.h | 120 + include/hudlock.h | 77 + include/hudmessage.h | 196 + include/hudobserver.h | 32 + include/hudresource.h | 2 + include/hudreticle.h | 61 + include/hudshield.h | 84 + include/hudsquadmsg.h | 213 + include/hudtarget.h | 409 + include/hudtargetbox.h | 165 + include/hudwingmanstatus.h | 51 + include/ia3d.h | 250 + include/ignoreordersdlg.h | 78 + include/inetgetfile.h | 117 + include/initialships.h | 45 + include/initialstatus.h | 70 + include/joy.h | 113 + include/joy_ff.h | 56 + include/jumpnode.h | 48 + include/key.h | 244 + include/keycontrol.h | 69 + include/levelpaging.h | 31 + include/lighting.h | 126 + include/line.h | 211 + include/linklist.h | 96 + include/localize.h | 147 + include/mainfrm.h | 46 + include/mainhallmenu.h | 87 + include/mainhalltemp.h | 43 + include/management.h | 343 + include/managepilot.h | 170 + include/medals.h | 108 + include/messageeditordlg.h | 124 + include/midifile.h | 31 + include/midiseq.h | 150 + include/missionbrief.h | 172 + include/missionbriefcommon.h | 411 + include/missioncampaign.h | 475 + include/missioncmdbrief.h | 72 + include/missiondebrief.h | 82 + include/missiongoals.h | 244 + include/missiongoalsdlg.h | 166 + include/missiongrid.h | 82 + include/missionhotkey.h | 68 + include/missionload.h | 58 + include/missionlog.h | 176 + include/missionloopbrief.h | 43 + include/missionmessage.h | 376 + include/missionnotesdlg.h | 155 + include/missionparse.h | 549 + include/missionpause.h | 56 + include/missionrecommend.h | 31 + include/missionsave.h | 139 + include/missionscreencommon.h | 201 + include/missionshipchoice.h | 290 + include/missionstats.h | 28 + include/missiontraining.h | 59 + include/missionweaponchoice.h | 171 + include/model.h | 1105 ++ include/modelsinc.h | 74 + include/modifyvariabledlg.h | 73 + include/monopub.h | 59 + include/mouse.h | 121 + include/multi.h | 1102 ++ include/multi_campaign.h | 109 + include/multi_data.h | 76 + include/multi_dogfight.h | 49 + include/multi_endgame.h | 120 + include/multi_ingame.h | 232 + include/multi_kick.h | 74 + include/multi_log.h | 57 + include/multi_obj.h | 93 + include/multi_observer.h | 65 + include/multi_oo.h | 130 + include/multi_options.h | 233 + include/multi_pause.h | 88 + include/multi_pinfo.h | 59 + include/multi_ping.h | 85 + include/multi_pmsg.h | 122 + include/multi_rate.h | 48 + include/multi_respawn.h | 81 + include/multi_team.h | 137 + include/multi_update.h | 50 + include/multi_voice.h | 161 + include/multi_xfer.h | 166 + include/multilag.h | 100 + include/multimsgs.h | 699 + include/multiteamselect.h | 208 + include/multiui.h | 452 + include/multiutil.h | 357 + include/muzzleflash.h | 69 + include/neb.h | 153 + include/neblightning.h | 107 + include/nebula.h | 48 + include/objcollide.h | 225 + include/object.h | 593 + include/objectsnd.h | 89 + include/objecttree.h | 66 + include/observer.h | 43 + include/operatorargtypeselect.h | 67 + include/optionsmenu.h | 87 + include/optionsmenumulti.h | 70 + include/orienteditor.h | 100 + include/osapi.h | 67 + include/osregistry.h | 53 + include/outwnd.h | 17 + include/packunpack.h | 242 + include/palman.h | 108 + include/parselo.h | 352 + include/particle.h | 144 + include/pcxutils.h | 89 + include/physics.h | 268 + include/pixel.h | 60 + include/player.h | 388 + include/playermenu.h | 68 + include/playerstarteditor.h | 141 + include/pofview.h | 51 + include/pofviewdoc.h | 46 + include/pofviewview.h | 140 + include/popup.h | 191 + include/popupdead.h | 46 + include/prefsdlg.h | 73 + include/psnet.h | 324 + include/psnet2.h | 224 + include/pstypes.h | 693 + include/radar.h | 59 + include/rbaudio.h | 74 + include/readyroom.h | 46 + include/rect.h | 38 + include/redalert.h | 66 + include/reinforcementeditordlg.h | 147 + include/resource.h | 61 + include/rsx.h | 612 + include/rsx_lib.h | 47 + include/rtvoice.h | 89 + include/scaler.h | 58 + include/scoring.h | 307 + include/scramble.h | 42 + include/sexp.h | 824 + include/sexp_tree.h | 250 + include/shade.h | 53 + include/shieldsysdlg.h | 70 + include/ship.h | 1246 ++ include/ship_select.h | 114 + include/shipchecklistbox.h | 48 + include/shipclasseditordlg.h | 92 + include/shipcontrails.h | 55 + include/shipeditordlg.h | 258 + include/shipflagsdlg.h | 76 + include/shipfx.h | 238 + include/shipgoalsdlg.h | 137 + include/shiphit.h | 141 + include/shipspecialdamage.h | 59 + include/shockwave.h | 138 + include/snazzyui.h | 100 + include/sound.h | 360 + include/spline.h | 96 + include/sst1vid.h | 120 + include/stand_gui.h | 322 + include/starfield.h | 180 + include/starfieldeditor.h | 80 + include/staticrand.h | 54 + include/stats.h | 82 + include/stdafx.h | 13 + include/subsysdamage.h | 76 + include/supernova.h | 91 + include/sw_force.h | 477 + include/swarm.h | 60 + include/systemvars.h | 339 + include/techmenu.h | 89 + include/textviewdlg.h | 37 + include/tga.h | 21 + include/tgautils.h | 52 + include/timer.h | 179 + include/tmapper.h | 122 + include/tmapscanline.h | 190 + include/trails.h | 76 + include/trainingmenu.h | 57 + include/ui.h | 1041 ++ include/uidefs.h | 112 + include/unix.h | 39 + include/vasync.h | 132 + include/vd3d.h | 897 ++ include/vd3dcaps.h | 314 + include/vd3di.h | 794 + include/vd3drm.h | 229 + include/vd3drmdef.h | 473 + include/vd3drmobj.h | 1101 ++ include/vd3drmwin.h | 49 + include/vd3dtypes.h | 1202 ++ include/vddraw.h | 3792 +++++ include/vdinput.h | 1850 +++ include/vdplay.h | 1711 +++ include/vdplobby.h | 628 + include/vdsetup.h | 268 + include/vdsound.h | 864 ++ include/vdvp.h | 832 + include/vecmat.h | 640 + include/verifya3d.h | 30 + include/version.h | 152 + include/waypointpathdlg.h | 83 + include/weapon.h | 606 + include/weaponeditordlg.h | 124 + include/wing.h | 56 + include/wing_editor.h | 201 + include/winmidi.h | 30 + src/ac/ac.cpp | 218 + src/ac/ac_stubs.cpp | 135 + src/ac/convert.cpp | 782 + src/anim/animplay.cpp | 1306 ++ src/anim/packunpack.cpp | 1270 ++ src/asteroid/asteroid.cpp | 2269 +++ src/bmpman/bmpman.cpp | 2556 ++++ src/cfile/cfile.cpp | 1686 ++ src/cfile/cfilearchive.cpp | 274 + src/cfile/cfilelist.cpp | 480 + src/cfile/cfilesystem.cpp | 1141 ++ src/cfilearchiver/cfilearchiver.cpp | 273 + src/cmdline/cmdline.cpp | 586 + src/cmeasure/cmeasure.cpp | 480 + src/code.dsp | 2503 +++ src/controlconfig/controlsconfig.cpp | 2529 +++ src/controlconfig/controlsconfigcommon.cpp | 858 ++ src/cryptstring/cryptstring.cpp | 72 + src/cutscene/cutscenes.cpp | 741 + src/debris/debris.cpp | 1262 ++ src/debugconsole/console.cpp | 790 + src/demo/demo.cpp | 996 ++ src/directx/vd3dvec.inl | 240 + src/directx/vddraw.lib | Bin 0 -> 14628 bytes src/directx/vdinput.lib | Bin 0 -> 15336 bytes src/directx/vdsound.lib | Bin 0 -> 5646 bytes src/directx/vdxguid.lib | Bin 0 -> 56332 bytes src/exceptionhandler/exceptionhandler.cpp | 502 + src/fireball/fireballs.cpp | 1162 ++ src/fireball/warpineffect.cpp | 258 + src/fonttool/fontcreate.cpp | 592 + src/fonttool/fontkern.cpp | 596 + src/fonttool/fontkerncopy.cpp | 57 + src/fonttool/fontstubs.cpp | 99 + src/fonttool/fonttool.cpp | 123 + src/fonttool/fonttool.pcx | Bin 0 -> 94669 bytes src/fred2/addvariabledlg.cpp | 219 + src/fred2/adjustgriddlg.cpp | 161 + src/fred2/asteroideditordlg.cpp | 670 + src/fred2/bgbitmapdlg.cpp | 880 ++ src/fred2/briefingeditordlg.cpp | 1432 ++ src/fred2/campaigneditordlg.cpp | 876 ++ src/fred2/campaignfilelistbox.cpp | 78 + src/fred2/campaigntreeview.cpp | 1329 ++ src/fred2/campaigntreewnd.cpp | 564 + src/fred2/cmdbrief.cpp | 340 + src/fred2/createwingdlg.cpp | 108 + src/fred2/debriefingeditordlg.cpp | 501 + src/fred2/dialog1.cpp | 66 + src/fred2/dumpstats.cpp | 908 ++ src/fred2/eventeditor.cpp | 1591 ++ src/fred2/fred.aps | Bin 0 -> 143578 bytes src/fred2/fred.cpp | 822 + src/fred2/fred.rc | 2934 ++++ src/fred2/freddoc.cpp | 1045 ++ src/fred2/fredrender.cpp | 1841 +++ src/fred2/fredstubs.cpp | 633 + src/fred2/fredview.cpp | 4819 ++++++ src/fred2/grid.cpp | 224 + src/fred2/hlp/afxcore.rtf | 23 + src/fred2/hlp/afxhelp.hm | 272 + src/fred2/hlp/afxprint.rtf | 98 + src/fred2/hlp/appexit.bmp | Bin 0 -> 2262 bytes src/fred2/hlp/bullet.bmp | Bin 0 -> 142 bytes src/fred2/hlp/curarw2.bmp | Bin 0 -> 310 bytes src/fred2/hlp/curarw4.bmp | Bin 0 -> 566 bytes src/fred2/hlp/curhelp.bmp | Bin 0 -> 502 bytes src/fred2/hlp/editcopy.bmp | Bin 0 -> 502 bytes src/fred2/hlp/editcut.bmp | Bin 0 -> 502 bytes src/fred2/hlp/editpast.bmp | Bin 0 -> 502 bytes src/fred2/hlp/editundo.bmp | Bin 0 -> 502 bytes src/fred2/hlp/filenew.bmp | Bin 0 -> 566 bytes src/fred2/hlp/fileopen.bmp | Bin 0 -> 566 bytes src/fred2/hlp/fileprnt.bmp | Bin 0 -> 502 bytes src/fred2/hlp/filesave.bmp | Bin 0 -> 502 bytes src/fred2/hlp/fred.hm | 351 + src/fred2/hlp/fred.hpj | 65 + src/fred2/hlp/help.log | 0 src/fred2/hlp/hlpsbar.bmp | Bin 0 -> 7158 bytes src/fred2/hlp/hlptbar.bmp | Bin 0 -> 2354 bytes src/fred2/hlp/recfirst.bmp | Bin 0 -> 502 bytes src/fred2/hlp/reclast.bmp | Bin 0 -> 502 bytes src/fred2/hlp/recnext.bmp | Bin 0 -> 502 bytes src/fred2/hlp/recprev.bmp | Bin 0 -> 502 bytes src/fred2/hlp/scmax.bmp | Bin 0 -> 502 bytes src/fred2/hlp/scmenu.bmp | Bin 0 -> 2134 bytes src/fred2/hlp/scmin.bmp | Bin 0 -> 502 bytes src/fred2/ignoreordersdlg.cpp | 358 + src/fred2/initialships.cpp | 159 + src/fred2/initialstatus.cpp | 599 + src/fred2/mainfrm.cpp | 710 + src/fred2/makehelp.bat | 31 + src/fred2/management.cpp | 2655 ++++ src/fred2/messageeditordlg.cpp | 687 + src/fred2/missiongoalsdlg.cpp | 790 + src/fred2/missionnotesdlg.cpp | 486 + src/fred2/missionsave.cpp | 2742 ++++ src/fred2/modifyvariabledlg.cpp | 485 + src/fred2/operatorargtypeselect.cpp | 94 + src/fred2/orienteditor.cpp | 338 + src/fred2/playerstarteditor.cpp | 381 + src/fred2/prefsdlg.cpp | 114 + src/fred2/reinforcementeditordlg.cpp | 462 + src/fred2/res/bitmap1.bmp | Bin 0 -> 246 bytes src/fred2/res/black_do.bmp | Bin 0 -> 246 bytes src/fred2/res/bmp00001.bmp | Bin 0 -> 162 bytes src/fred2/res/chained.bmp | Bin 0 -> 246 bytes src/fred2/res/chained_directive.bmp | Bin 0 -> 246 bytes src/fred2/res/cursor1.cur | Bin 0 -> 326 bytes src/fred2/res/cursor2.cur | Bin 0 -> 326 bytes src/fred2/res/data.bmp | Bin 0 -> 246 bytes src/fred2/res/fred.ico | Bin 0 -> 3310 bytes src/fred2/res/fred.rc2 | 13 + src/fred2/res/freddoc.ico | Bin 0 -> 3310 bytes src/fred2/res/green_do.bmp | Bin 0 -> 246 bytes src/fred2/res/root.bmp | Bin 0 -> 246 bytes src/fred2/res/root_directive.bmp | Bin 0 -> 246 bytes src/fred2/res/toolbar.bmp | Bin 0 -> 5178 bytes src/fred2/res/toolbar1.bmp | Bin 0 -> 2234 bytes src/fred2/res/variable.bmp | Bin 0 -> 246 bytes src/fred2/resource.hm | 5 + src/fred2/sexp_tree.cpp | 5531 +++++++ src/fred2/shieldsysdlg.cpp | 193 + src/fred2/ship_select.cpp | 592 + src/fred2/shipchecklistbox.cpp | 67 + src/fred2/shipclasseditordlg.cpp | 123 + src/fred2/shipeditordlg.cpp | 2164 +++ src/fred2/shipflagsdlg.cpp | 691 + src/fred2/shipgoalsdlg.cpp | 1378 ++ src/fred2/shipspecialdamage.cpp | 204 + src/fred2/starfieldeditor.cpp | 131 + src/fred2/stdafx.cpp | 36 + src/fred2/textviewdlg.cpp | 102 + src/fred2/waypointpathdlg.cpp | 447 + src/fred2/weaponeditordlg.cpp | 667 + src/fred2/wing.cpp | 758 + src/fred2/wing_editor.cpp | 1374 ++ src/freespace2/app_icon.ico | Bin 0 -> 3310 bytes src/freespace2/freespace.cpp | 8560 +++++++++++ src/freespace2/freespace.rc | 376 + src/freespace2/goal_com.bmp | Bin 0 -> 246 bytes src/freespace2/goal_fail.bmp | Bin 0 -> 246 bytes src/freespace2/goal_inc.bmp | Bin 0 -> 246 bytes src/freespace2/goal_none.bmp | Bin 0 -> 246 bytes src/freespace2/goal_ord.bmp | Bin 0 -> 246 bytes src/freespace2/levelpaging.cpp | 104 + src/gamehelp/contexthelp.cpp | 901 ++ src/gamehelp/gameplayhelp.cpp | 942 ++ src/gamesequence/gamesequence.cpp | 614 + src/gamesnd/eventmusic.cpp | 1480 ++ src/gamesnd/gamesnd.cpp | 414 + src/glide/glide.cpp | 455 + src/globalincs/alphacolors.cpp | 65 + src/globalincs/crypt.cpp | 49 + src/globalincs/systemvars.cpp | 700 + src/globalincs/version.cpp | 141 + src/globalincs/windebug.cpp | 1201 ++ src/graphics/2d.cpp | 1363 ++ src/graphics/aaline.cpp | 677 + src/graphics/bitblt.cpp | 490 + src/graphics/circle.cpp | 96 + src/graphics/colors.cpp | 606 + src/graphics/font.cpp | 896 ++ src/graphics/gradient.cpp | 196 + src/graphics/grd3d.cpp | 3045 ++++ src/graphics/grd3drender.cpp | 2258 +++ src/graphics/grd3dtexture.cpp | 1403 ++ src/graphics/grdirectdraw.cpp | 1286 ++ src/graphics/grglide.cpp | 3758 +++++ src/graphics/grglidetexture.cpp | 1052 ++ src/graphics/gropengl.cpp | 726 + src/graphics/grsoft.cpp | 1843 +++ src/graphics/grzbuffer.cpp | 110 + src/graphics/line.cpp | 188 + src/graphics/pixel.cpp | 99 + src/graphics/rect.cpp | 126 + src/graphics/scaler.cpp | 1153 ++ src/graphics/shade.cpp | 254 + src/graphics/tmapgenericscans.cpp | 6944 +++++++++ src/graphics/tmapper.cpp | 881 ++ src/graphics/tmapscanline.cpp | 4574 ++++++ src/graphics/tmapscantiled128x128.cpp | 1246 ++ src/graphics/tmapscantiled16x16.cpp | 1287 ++ src/graphics/tmapscantiled256x256.cpp | 2256 +++ src/graphics/tmapscantiled32x32.cpp | 1286 ++ src/graphics/tmapscantiled64x64.cpp | 1292 ++ src/helped/helped.cpp | 157 + src/helped/helped.rc | 357 + src/helped/helpeddoc.cpp | 115 + src/helped/helpedline.cpp | 41 + src/helped/helpedview.cpp | 100 + src/helped/mainfrm.cpp | 105 + src/helped/res/helped.ico | Bin 0 -> 1078 bytes src/helped/res/helped.rc2 | 13 + src/helped/res/helpeddoc.ico | Bin 0 -> 1078 bytes src/helped/res/toolbar.bmp | Bin 0 -> 1078 bytes src/helped/stdafx.cpp | 8 + src/helped/tga.cpp | 27 + src/hud/hud.cpp | 3156 ++++ src/hud/hudartillery.cpp | 355 + src/hud/hudbrackets.cpp | 767 + src/hud/hudconfig.cpp | 2065 +++ src/hud/hudescort.cpp | 1008 ++ src/hud/hudets.cpp | 897 ++ src/hud/hudlock.cpp | 1238 ++ src/hud/hudmessage.cpp | 1563 ++ src/hud/hudobserver.cpp | 73 + src/hud/hudresource.cpp | 0 src/hud/hudreticle.cpp | 834 + src/hud/hudshield.cpp | 845 + src/hud/hudsquadmsg.cpp | 2737 ++++ src/hud/hudtarget.cpp | 5538 +++++++ src/hud/hudtargetbox.cpp | 1919 +++ src/hud/hudwingmanstatus.cpp | 862 ++ src/inetfile/cftp.cpp | 559 + src/inetfile/chttpget.cpp | 774 + src/inetfile/inetgetfile.cpp | 281 + src/io/joy.cpp | 1455 ++ src/io/joy_ff.cpp | 809 + src/io/key.cpp | 1190 ++ src/io/keycontrol.cpp | 2856 ++++ src/io/mouse.cpp | 767 + src/io/sw_error.hpp | 144 + src/io/sw_guid.hpp | 296 + src/io/swff_lib.cpp | 2485 +++ src/io/timer.cpp | 583 + src/jumpnode/jumpnode.cpp | 132 + src/lighting/lighting.cpp | 1006 ++ src/localization/fhash.cpp | 227 + src/localization/localize.cpp | 1614 ++ src/math/fix.cpp | 49 + src/math/floating.cpp | 193 + src/math/fvi.cpp | 1549 ++ src/math/spline.cpp | 310 + src/math/staticrand.cpp | 126 + src/math/vecmat.cpp | 2827 ++++ src/menuui/barracks.cpp | 1663 ++ src/menuui/credits.cpp | 753 + src/menuui/fishtank.cpp | 265 + src/menuui/mainhallmenu.cpp | 2151 +++ src/menuui/mainhalltemp.cpp | 272 + src/menuui/optionsmenu.cpp | 1727 +++ src/menuui/optionsmenumulti.cpp | 2538 +++ src/menuui/playermenu.cpp | 1517 ++ src/menuui/readyroom.cpp | 1900 +++ src/menuui/snazzyui.cpp | 471 + src/menuui/techmenu.cpp | 1664 ++ src/menuui/trainingmenu.cpp | 221 + src/mission/missionbriefcommon.cpp | 2584 ++++ src/mission/missioncampaign.cpp | 1888 +++ src/mission/missiongoals.cpp | 1680 ++ src/mission/missiongrid.cpp | 331 + src/mission/missionhotkey.cpp | 1434 ++ src/mission/missionload.cpp | 557 + src/mission/missionlog.cpp | 1010 ++ src/mission/missionmessage.cpp | 2091 +++ src/mission/missionparse.cpp | 5116 +++++++ src/mission/missiontraining.cpp | 1180 ++ src/missionui/chatbox.cpp | 1388 ++ src/missionui/missionbrief.cpp | 2258 +++ src/missionui/missioncmdbrief.cpp | 811 + src/missionui/missiondebrief.cpp | 2841 ++++ src/missionui/missionloopbrief.cpp | 287 + src/missionui/missionpause.cpp | 313 + src/missionui/missionrecommend.cpp | 22 + src/missionui/missionscreencommon.cpp | 1566 ++ src/missionui/missionshipchoice.cpp | 3384 ++++ src/missionui/missionstats.cpp | 22 + src/missionui/missionweaponchoice.cpp | 3826 +++++ src/missionui/redalert.cpp | 931 ++ src/model/modelcollide.cpp | 1044 ++ src/model/modelinterp.cpp | 3312 ++++ src/model/modeloctant.cpp | 555 + src/model/modelread.cpp | 3432 +++++ src/nebedit/nebedit.cpp | 967 ++ src/nebedit/nebstubs.cpp | 85 + src/nebula/neb.cpp | 1845 +++ src/nebula/neblightning.cpp | 1322 ++ src/network/multi.cpp | 1966 +++ src/network/multi_campaign.cpp | 922 ++ src/network/multi_data.cpp | 526 + src/network/multi_dogfight.cpp | 534 + src/network/multi_endgame.cpp | 739 + src/network/multi_ingame.cpp | 2120 +++ src/network/multi_kick.cpp | 361 + src/network/multi_log.cpp | 231 + src/network/multi_obj.cpp | 1987 +++ src/network/multi_observer.cpp | 286 + src/network/multi_oo.cpp | 2084 +++ src/network/multi_options.cpp | 785 + src/network/multi_pause.cpp | 541 + src/network/multi_pinfo.cpp | 933 ++ src/network/multi_ping.cpp | 197 + src/network/multi_pmsg.cpp | 859 ++ src/network/multi_rate.cpp | 253 + src/network/multi_respawn.cpp | 1016 ++ src/network/multi_team.cpp | 984 ++ src/network/multi_update.cpp | 307 + src/network/multi_voice.cpp | 2331 +++ src/network/multi_xfer.cpp | 1334 ++ src/network/multilag.cpp | 741 + src/network/multimsgs.cpp | 8059 ++++++++++ src/network/multiteamselect.cpp | 3155 ++++ src/network/multiui.cpp | 9435 ++++++++++++ src/network/multiutil.cpp | 4040 +++++ src/network/psnet.cpp | 3089 ++++ src/network/psnet2.cpp | 2589 ++++ src/network/stand_gui.cpp | 2377 +++ src/object/collidedebrisship.cpp | 443 + src/object/collidedebrisweapon.cpp | 135 + src/object/collideshipship.cpp | 1895 +++ src/object/collideshipweapon.cpp | 510 + src/object/collideweaponweapon.cpp | 160 + src/object/objcollide.cpp | 1138 ++ src/object/object.cpp | 2148 +++ src/object/objectsnd.cpp | 1063 ++ src/object/objectsort.cpp | 336 + src/observer/observer.cpp | 109 + src/osapi/os_unix.cpp | 217 + src/osapi/os_win.cpp | 693 + src/osapi/osregistry.cpp | 488 + src/osapi/outwnd.cpp | 1354 ++ src/palman/palman.cpp | 854 ++ src/parse/encrypt.cpp | 453 + src/parse/parselo.cpp | 1876 +++ src/parse/sexp.cpp | 9661 ++++++++++++ src/particle/particle.cpp | 771 + src/pcxutils/pcxutils.cpp | 718 + src/physics/physics.cpp | 1381 ++ src/platform/unix.cpp | 16 + src/playerman/managepilot.cpp | 1388 ++ src/playerman/playercontrol.cpp | 2284 +++ src/pofview/childfrm.cpp | 68 + src/pofview/mainfrm.cpp | 107 + src/pofview/objecttree.cpp | 285 + src/pofview/pofview.cpp | 406 + src/pofview/pofview.rc | 520 + src/pofview/pofviewdoc.cpp | 87 + src/pofview/pofviewstubs.cpp | 106 + src/pofview/pofviewview.cpp | 854 ++ src/pofview/res/pofview.ico | Bin 0 -> 766 bytes src/pofview/res/pofview.rc2 | 13 + src/pofview/res/pofviewdoc.ico | Bin 0 -> 766 bytes src/pofview/res/toolbar.bmp | Bin 0 -> 2278 bytes src/pofview/stdafx.cpp | 6 + src/popup/popup.cpp | 1304 ++ src/popup/popupdead.cpp | 673 + src/radar/radar.cpp | 858 ++ src/render/3dclipper.cpp | 317 + src/render/3ddraw.cpp | 1837 +++ src/render/3dlaser.cpp | 461 + src/render/3dmath.cpp | 411 + src/render/3dsetup.cpp | 432 + src/scramble/scramble.cpp | 314 + src/ship/afterburner.cpp | 447 + src/ship/ai.cpp | 93 + src/ship/aibig.cpp | 1890 +++ src/ship/aicode.cpp | 15284 +++++++++++++++++++ src/ship/aigoals.cpp | 2314 +++ src/ship/awacs.cpp | 545 + src/ship/shield.cpp | 1242 ++ src/ship/ship.cpp | 9456 ++++++++++++ src/ship/shipcontrails.cpp | 266 + src/ship/shipfx.cpp | 2968 ++++ src/ship/shiphit.cpp | 2481 +++ src/sound/acm.cpp | 363 + src/sound/audiostr.cpp | 1922 +++ src/sound/ds.cpp | 2798 ++++ src/sound/ds3d.cpp | 339 + src/sound/dscap.cpp | 364 + src/sound/midifile.cpp | 25 + src/sound/rbaudio.cpp | 713 + src/sound/rsx_lib.cpp | 319 + src/sound/rtvoice.cpp | 698 + src/sound/sound.cpp | 1525 ++ src/sound/verifya3d.lib | Bin 0 -> 21652 bytes src/sound/winmidi.cpp | 24 + src/sound/winmidi_base.cpp | 24 + src/starfield/nebula.cpp | 344 + src/starfield/starfield.cpp | 1401 ++ src/starfield/supernova.cpp | 402 + src/stats/medals.cpp | 734 + src/stats/scoring.cpp | 1188 ++ src/stats/stats.cpp | 507 + src/tgautils/tgautils.cpp | 763 + src/ui/button.cpp | 621 + src/ui/checkbox.cpp | 244 + src/ui/gadget.cpp | 741 + src/ui/icon.cpp | 145 + src/ui/inputbox.cpp | 710 + src/ui/keytrap.cpp | 69 + src/ui/listbox.cpp | 603 + src/ui/radio.cpp | 267 + src/ui/scroll.cpp | 385 + src/ui/slider.cpp | 599 + src/ui/slider2.cpp | 339 + src/ui/uidraw.cpp | 148 + src/ui/uimouse.cpp | 133 + src/ui/window.cpp | 933 ++ src/vcodec/codec1.cpp | 1964 +++ src/weapon/beam.cpp | 3255 ++++ src/weapon/corkscrew.cpp | 363 + src/weapon/emp.cpp | 676 + src/weapon/flak.cpp | 304 + src/weapon/muzzleflash.cpp | 296 + src/weapon/shockwave.cpp | 644 + src/weapon/swarm.cpp | 695 + src/weapon/trails.cpp | 463 + src/weapon/weapons.cpp | 3722 +++++ winfiles/Ac.dsp | 106 + winfiles/Ac.plg | 40 + winfiles/Cfilearchiver.dsp | 90 + winfiles/Cfilearchiver.plg | 32 + winfiles/Cryptstring.dsp | 89 + winfiles/Cryptstring.plg | 32 + winfiles/FS2AutoRun.ico | Bin 0 -> 3310 bytes winfiles/Fonttool.dsp | 110 + winfiles/Fonttool.plg | 46 + winfiles/Fred2.dsp | 593 + winfiles/Fred2.plg | 77 + winfiles/Freespace2.dsp | 140 + winfiles/Freespace2.plg | 310 + winfiles/HelpEd.dsp | 167 + winfiles/HelpEd.plg | 52 + winfiles/Nebedit.dsp | 99 + winfiles/Nebedit.plg | 37 + winfiles/Pofview.dsp | 179 + winfiles/Pofview.plg | 58 + winfiles/Scramble.dsp | 94 + winfiles/Scramble.plg | 33 + winfiles/freespace2.dsw | 173 + winfiles/freespace2.opt | Bin 0 -> 115200 bytes 752 files changed, 471419 insertions(+) create mode 100644 Makefile create mode 100644 doc/guided_tour.txt create mode 100644 doc/interesting_stats.txt create mode 100644 doc/release_log.txt create mode 100644 include/2d.h create mode 100644 include/3d.h create mode 100644 include/3dfx.h create mode 100644 include/3dinternal.h create mode 100644 include/ac.h create mode 100644 include/acm.h create mode 100644 include/addvariabledlg.h create mode 100644 include/adjustgriddlg.h create mode 100644 include/afterburner.h create mode 100644 include/ai.h create mode 100644 include/aibig.h create mode 100644 include/aigoals.h create mode 100644 include/ailocal.h create mode 100644 include/alphacolors.h create mode 100644 include/animplay.h create mode 100644 include/asteroid.h create mode 100644 include/asteroideditordlg.h create mode 100644 include/audiostr.h create mode 100644 include/awacs.h create mode 100644 include/barracks.h create mode 100644 include/beam.h create mode 100644 include/bgbitmapdlg.h create mode 100644 include/bitblt.h create mode 100644 include/bmpman.h create mode 100644 include/briefingeditordlg.h create mode 100644 include/campaigneditordlg.h create mode 100644 include/campaignfilelistbox.h create mode 100644 include/campaigntreeview.h create mode 100644 include/campaigntreewnd.h create mode 100644 include/cfile.h create mode 100644 include/cfilearchive.h create mode 100644 include/cfilesystem.h create mode 100644 include/cftp.h create mode 100644 include/channel.h create mode 100644 include/chatbox.h create mode 100644 include/childfrm.h create mode 100644 include/chttpget.h create mode 100644 include/circle.h create mode 100644 include/cmdbrief.h create mode 100644 include/cmdline.h create mode 100644 include/cmeasure.h create mode 100644 include/codec1.h create mode 100644 include/colors.h create mode 100644 include/contexthelp.h create mode 100644 include/controlsconfig.h create mode 100644 include/convert.h create mode 100644 include/corkscrew.h create mode 100644 include/createwingdlg.h create mode 100644 include/credits.h create mode 100644 include/crypt.h create mode 100644 include/cutscenes.h create mode 100644 include/debriefingeditordlg.h create mode 100644 include/debris.h create mode 100644 include/demo.h create mode 100644 include/dialog1.h create mode 100644 include/ds.h create mode 100644 include/ds3d.h create mode 100644 include/dscap.h create mode 100644 include/dumpstats.h create mode 100644 include/editor.h create mode 100644 include/emp.h create mode 100644 include/encrypt.h create mode 100644 include/eventeditor.h create mode 100644 include/eventmusic.h create mode 100644 include/exceptionhandler.h create mode 100644 include/fhash.h create mode 100644 include/fireballs.h create mode 100644 include/fishtank.h create mode 100644 include/fix.h create mode 100644 include/flak.h create mode 100644 include/floating.h create mode 100644 include/font.h create mode 100644 include/fonttool.h create mode 100644 include/fred.h create mode 100644 include/freddoc.h create mode 100644 include/fredrender.h create mode 100644 include/fredview.h create mode 100644 include/freespace.h create mode 100644 include/freespaceresource.h create mode 100644 include/fvi.h create mode 100644 include/fxdll.h create mode 100644 include/fxglob.h create mode 100644 include/fxos.h create mode 100644 include/gameplayhelp.h create mode 100644 include/gamesequence.h create mode 100644 include/gamesnd.h create mode 100644 include/glide.h create mode 100644 include/glidesys.h create mode 100644 include/glideutl.h create mode 100644 include/gradient.h create mode 100644 include/grd3d.h create mode 100644 include/grd3dinternal.h create mode 100644 include/grdirectdraw.h create mode 100644 include/grglide.h create mode 100644 include/grglideinternal.h create mode 100644 include/grid.h create mode 100644 include/grinternal.h create mode 100644 include/gropengl.h create mode 100644 include/grsoft.h create mode 100644 include/grzbuffer.h create mode 100644 include/helped.h create mode 100644 include/helpeddoc.h create mode 100644 include/helpedline.h create mode 100644 include/helpedview.h create mode 100644 include/hud.h create mode 100644 include/hudartillery.h create mode 100644 include/hudbrackets.h create mode 100644 include/hudconfig.h create mode 100644 include/hudescort.h create mode 100644 include/hudets.h create mode 100644 include/hudgauges.h create mode 100644 include/hudlock.h create mode 100644 include/hudmessage.h create mode 100644 include/hudobserver.h create mode 100644 include/hudresource.h create mode 100644 include/hudreticle.h create mode 100644 include/hudshield.h create mode 100644 include/hudsquadmsg.h create mode 100644 include/hudtarget.h create mode 100644 include/hudtargetbox.h create mode 100644 include/hudwingmanstatus.h create mode 100644 include/ia3d.h create mode 100644 include/ignoreordersdlg.h create mode 100644 include/inetgetfile.h create mode 100644 include/initialships.h create mode 100644 include/initialstatus.h create mode 100644 include/joy.h create mode 100644 include/joy_ff.h create mode 100644 include/jumpnode.h create mode 100644 include/key.h create mode 100644 include/keycontrol.h create mode 100644 include/levelpaging.h create mode 100644 include/lighting.h create mode 100644 include/line.h create mode 100644 include/linklist.h create mode 100644 include/localize.h create mode 100644 include/mainfrm.h create mode 100644 include/mainhallmenu.h create mode 100644 include/mainhalltemp.h create mode 100644 include/management.h create mode 100644 include/managepilot.h create mode 100644 include/medals.h create mode 100644 include/messageeditordlg.h create mode 100644 include/midifile.h create mode 100644 include/midiseq.h create mode 100644 include/missionbrief.h create mode 100644 include/missionbriefcommon.h create mode 100644 include/missioncampaign.h create mode 100644 include/missioncmdbrief.h create mode 100644 include/missiondebrief.h create mode 100644 include/missiongoals.h create mode 100644 include/missiongoalsdlg.h create mode 100644 include/missiongrid.h create mode 100644 include/missionhotkey.h create mode 100644 include/missionload.h create mode 100644 include/missionlog.h create mode 100644 include/missionloopbrief.h create mode 100644 include/missionmessage.h create mode 100644 include/missionnotesdlg.h create mode 100644 include/missionparse.h create mode 100644 include/missionpause.h create mode 100644 include/missionrecommend.h create mode 100644 include/missionsave.h create mode 100644 include/missionscreencommon.h create mode 100644 include/missionshipchoice.h create mode 100644 include/missionstats.h create mode 100644 include/missiontraining.h create mode 100644 include/missionweaponchoice.h create mode 100644 include/model.h create mode 100644 include/modelsinc.h create mode 100644 include/modifyvariabledlg.h create mode 100644 include/monopub.h create mode 100644 include/mouse.h create mode 100644 include/multi.h create mode 100644 include/multi_campaign.h create mode 100644 include/multi_data.h create mode 100644 include/multi_dogfight.h create mode 100644 include/multi_endgame.h create mode 100644 include/multi_ingame.h create mode 100644 include/multi_kick.h create mode 100644 include/multi_log.h create mode 100644 include/multi_obj.h create mode 100644 include/multi_observer.h create mode 100644 include/multi_oo.h create mode 100644 include/multi_options.h create mode 100644 include/multi_pause.h create mode 100644 include/multi_pinfo.h create mode 100644 include/multi_ping.h create mode 100644 include/multi_pmsg.h create mode 100644 include/multi_rate.h create mode 100644 include/multi_respawn.h create mode 100644 include/multi_team.h create mode 100644 include/multi_update.h create mode 100644 include/multi_voice.h create mode 100644 include/multi_xfer.h create mode 100644 include/multilag.h create mode 100644 include/multimsgs.h create mode 100644 include/multiteamselect.h create mode 100644 include/multiui.h create mode 100644 include/multiutil.h create mode 100644 include/muzzleflash.h create mode 100644 include/neb.h create mode 100644 include/neblightning.h create mode 100644 include/nebula.h create mode 100644 include/objcollide.h create mode 100644 include/object.h create mode 100644 include/objectsnd.h create mode 100644 include/objecttree.h create mode 100644 include/observer.h create mode 100644 include/operatorargtypeselect.h create mode 100644 include/optionsmenu.h create mode 100644 include/optionsmenumulti.h create mode 100644 include/orienteditor.h create mode 100644 include/osapi.h create mode 100644 include/osregistry.h create mode 100644 include/outwnd.h create mode 100644 include/packunpack.h create mode 100644 include/palman.h create mode 100644 include/parselo.h create mode 100644 include/particle.h create mode 100644 include/pcxutils.h create mode 100644 include/physics.h create mode 100644 include/pixel.h create mode 100644 include/player.h create mode 100644 include/playermenu.h create mode 100644 include/playerstarteditor.h create mode 100644 include/pofview.h create mode 100644 include/pofviewdoc.h create mode 100644 include/pofviewview.h create mode 100644 include/popup.h create mode 100644 include/popupdead.h create mode 100644 include/prefsdlg.h create mode 100644 include/psnet.h create mode 100644 include/psnet2.h create mode 100644 include/pstypes.h create mode 100644 include/radar.h create mode 100644 include/rbaudio.h create mode 100644 include/readyroom.h create mode 100644 include/rect.h create mode 100644 include/redalert.h create mode 100644 include/reinforcementeditordlg.h create mode 100644 include/resource.h create mode 100644 include/rsx.h create mode 100644 include/rsx_lib.h create mode 100644 include/rtvoice.h create mode 100644 include/scaler.h create mode 100644 include/scoring.h create mode 100644 include/scramble.h create mode 100644 include/sexp.h create mode 100644 include/sexp_tree.h create mode 100644 include/shade.h create mode 100644 include/shieldsysdlg.h create mode 100644 include/ship.h create mode 100644 include/ship_select.h create mode 100644 include/shipchecklistbox.h create mode 100644 include/shipclasseditordlg.h create mode 100644 include/shipcontrails.h create mode 100644 include/shipeditordlg.h create mode 100644 include/shipflagsdlg.h create mode 100644 include/shipfx.h create mode 100644 include/shipgoalsdlg.h create mode 100644 include/shiphit.h create mode 100644 include/shipspecialdamage.h create mode 100644 include/shockwave.h create mode 100644 include/snazzyui.h create mode 100644 include/sound.h create mode 100644 include/spline.h create mode 100644 include/sst1vid.h create mode 100644 include/stand_gui.h create mode 100644 include/starfield.h create mode 100644 include/starfieldeditor.h create mode 100644 include/staticrand.h create mode 100644 include/stats.h create mode 100644 include/stdafx.h create mode 100644 include/subsysdamage.h create mode 100644 include/supernova.h create mode 100644 include/sw_force.h create mode 100644 include/swarm.h create mode 100644 include/systemvars.h create mode 100644 include/techmenu.h create mode 100644 include/textviewdlg.h create mode 100644 include/tga.h create mode 100644 include/tgautils.h create mode 100644 include/timer.h create mode 100644 include/tmapper.h create mode 100644 include/tmapscanline.h create mode 100644 include/trails.h create mode 100644 include/trainingmenu.h create mode 100644 include/ui.h create mode 100644 include/uidefs.h create mode 100644 include/unix.h create mode 100644 include/vasync.h create mode 100644 include/vd3d.h create mode 100644 include/vd3dcaps.h create mode 100644 include/vd3di.h create mode 100644 include/vd3drm.h create mode 100644 include/vd3drmdef.h create mode 100644 include/vd3drmobj.h create mode 100644 include/vd3drmwin.h create mode 100644 include/vd3dtypes.h create mode 100644 include/vddraw.h create mode 100644 include/vdinput.h create mode 100644 include/vdplay.h create mode 100644 include/vdplobby.h create mode 100644 include/vdsetup.h create mode 100644 include/vdsound.h create mode 100644 include/vdvp.h create mode 100644 include/vecmat.h create mode 100644 include/verifya3d.h create mode 100644 include/version.h create mode 100644 include/waypointpathdlg.h create mode 100644 include/weapon.h create mode 100644 include/weaponeditordlg.h create mode 100644 include/wing.h create mode 100644 include/wing_editor.h create mode 100644 include/winmidi.h create mode 100644 src/ac/ac.cpp create mode 100644 src/ac/ac_stubs.cpp create mode 100644 src/ac/convert.cpp create mode 100644 src/anim/animplay.cpp create mode 100644 src/anim/packunpack.cpp create mode 100644 src/asteroid/asteroid.cpp create mode 100644 src/bmpman/bmpman.cpp create mode 100644 src/cfile/cfile.cpp create mode 100644 src/cfile/cfilearchive.cpp create mode 100644 src/cfile/cfilelist.cpp create mode 100644 src/cfile/cfilesystem.cpp create mode 100644 src/cfilearchiver/cfilearchiver.cpp create mode 100644 src/cmdline/cmdline.cpp create mode 100644 src/cmeasure/cmeasure.cpp create mode 100644 src/code.dsp create mode 100644 src/controlconfig/controlsconfig.cpp create mode 100644 src/controlconfig/controlsconfigcommon.cpp create mode 100644 src/cryptstring/cryptstring.cpp create mode 100644 src/cutscene/cutscenes.cpp create mode 100644 src/debris/debris.cpp create mode 100644 src/debugconsole/console.cpp create mode 100644 src/demo/demo.cpp create mode 100644 src/directx/vd3dvec.inl create mode 100644 src/directx/vddraw.lib create mode 100644 src/directx/vdinput.lib create mode 100644 src/directx/vdsound.lib create mode 100644 src/directx/vdxguid.lib create mode 100644 src/exceptionhandler/exceptionhandler.cpp create mode 100644 src/fireball/fireballs.cpp create mode 100644 src/fireball/warpineffect.cpp create mode 100644 src/fonttool/fontcreate.cpp create mode 100644 src/fonttool/fontkern.cpp create mode 100644 src/fonttool/fontkerncopy.cpp create mode 100644 src/fonttool/fontstubs.cpp create mode 100644 src/fonttool/fonttool.cpp create mode 100644 src/fonttool/fonttool.pcx create mode 100644 src/fred2/addvariabledlg.cpp create mode 100644 src/fred2/adjustgriddlg.cpp create mode 100644 src/fred2/asteroideditordlg.cpp create mode 100644 src/fred2/bgbitmapdlg.cpp create mode 100644 src/fred2/briefingeditordlg.cpp create mode 100644 src/fred2/campaigneditordlg.cpp create mode 100644 src/fred2/campaignfilelistbox.cpp create mode 100644 src/fred2/campaigntreeview.cpp create mode 100644 src/fred2/campaigntreewnd.cpp create mode 100644 src/fred2/cmdbrief.cpp create mode 100644 src/fred2/createwingdlg.cpp create mode 100644 src/fred2/debriefingeditordlg.cpp create mode 100644 src/fred2/dialog1.cpp create mode 100644 src/fred2/dumpstats.cpp create mode 100644 src/fred2/eventeditor.cpp create mode 100644 src/fred2/fred.aps create mode 100644 src/fred2/fred.cpp create mode 100644 src/fred2/fred.rc create mode 100644 src/fred2/freddoc.cpp create mode 100644 src/fred2/fredrender.cpp create mode 100644 src/fred2/fredstubs.cpp create mode 100644 src/fred2/fredview.cpp create mode 100644 src/fred2/grid.cpp create mode 100644 src/fred2/hlp/afxcore.rtf create mode 100644 src/fred2/hlp/afxhelp.hm create mode 100644 src/fred2/hlp/afxprint.rtf create mode 100644 src/fred2/hlp/appexit.bmp create mode 100644 src/fred2/hlp/bullet.bmp create mode 100644 src/fred2/hlp/curarw2.bmp create mode 100644 src/fred2/hlp/curarw4.bmp create mode 100644 src/fred2/hlp/curhelp.bmp create mode 100644 src/fred2/hlp/editcopy.bmp create mode 100644 src/fred2/hlp/editcut.bmp create mode 100644 src/fred2/hlp/editpast.bmp create mode 100644 src/fred2/hlp/editundo.bmp create mode 100644 src/fred2/hlp/filenew.bmp create mode 100644 src/fred2/hlp/fileopen.bmp create mode 100644 src/fred2/hlp/fileprnt.bmp create mode 100644 src/fred2/hlp/filesave.bmp create mode 100644 src/fred2/hlp/fred.hm create mode 100644 src/fred2/hlp/fred.hpj create mode 100644 src/fred2/hlp/help.log create mode 100644 src/fred2/hlp/hlpsbar.bmp create mode 100644 src/fred2/hlp/hlptbar.bmp create mode 100644 src/fred2/hlp/recfirst.bmp create mode 100644 src/fred2/hlp/reclast.bmp create mode 100644 src/fred2/hlp/recnext.bmp create mode 100644 src/fred2/hlp/recprev.bmp create mode 100644 src/fred2/hlp/scmax.bmp create mode 100644 src/fred2/hlp/scmenu.bmp create mode 100644 src/fred2/hlp/scmin.bmp create mode 100644 src/fred2/ignoreordersdlg.cpp create mode 100644 src/fred2/initialships.cpp create mode 100644 src/fred2/initialstatus.cpp create mode 100644 src/fred2/mainfrm.cpp create mode 100644 src/fred2/makehelp.bat create mode 100644 src/fred2/management.cpp create mode 100644 src/fred2/messageeditordlg.cpp create mode 100644 src/fred2/missiongoalsdlg.cpp create mode 100644 src/fred2/missionnotesdlg.cpp create mode 100644 src/fred2/missionsave.cpp create mode 100644 src/fred2/modifyvariabledlg.cpp create mode 100644 src/fred2/operatorargtypeselect.cpp create mode 100644 src/fred2/orienteditor.cpp create mode 100644 src/fred2/playerstarteditor.cpp create mode 100644 src/fred2/prefsdlg.cpp create mode 100644 src/fred2/reinforcementeditordlg.cpp create mode 100644 src/fred2/res/bitmap1.bmp create mode 100644 src/fred2/res/black_do.bmp create mode 100644 src/fred2/res/bmp00001.bmp create mode 100644 src/fred2/res/chained.bmp create mode 100644 src/fred2/res/chained_directive.bmp create mode 100644 src/fred2/res/cursor1.cur create mode 100644 src/fred2/res/cursor2.cur create mode 100644 src/fred2/res/data.bmp create mode 100644 src/fred2/res/fred.ico create mode 100644 src/fred2/res/fred.rc2 create mode 100644 src/fred2/res/freddoc.ico create mode 100644 src/fred2/res/green_do.bmp create mode 100644 src/fred2/res/root.bmp create mode 100644 src/fred2/res/root_directive.bmp create mode 100644 src/fred2/res/toolbar.bmp create mode 100644 src/fred2/res/toolbar1.bmp create mode 100644 src/fred2/res/variable.bmp create mode 100644 src/fred2/resource.hm create mode 100644 src/fred2/sexp_tree.cpp create mode 100644 src/fred2/shieldsysdlg.cpp create mode 100644 src/fred2/ship_select.cpp create mode 100644 src/fred2/shipchecklistbox.cpp create mode 100644 src/fred2/shipclasseditordlg.cpp create mode 100644 src/fred2/shipeditordlg.cpp create mode 100644 src/fred2/shipflagsdlg.cpp create mode 100644 src/fred2/shipgoalsdlg.cpp create mode 100644 src/fred2/shipspecialdamage.cpp create mode 100644 src/fred2/starfieldeditor.cpp create mode 100644 src/fred2/stdafx.cpp create mode 100644 src/fred2/textviewdlg.cpp create mode 100644 src/fred2/waypointpathdlg.cpp create mode 100644 src/fred2/weaponeditordlg.cpp create mode 100644 src/fred2/wing.cpp create mode 100644 src/fred2/wing_editor.cpp create mode 100644 src/freespace2/app_icon.ico create mode 100644 src/freespace2/freespace.cpp create mode 100644 src/freespace2/freespace.rc create mode 100644 src/freespace2/goal_com.bmp create mode 100644 src/freespace2/goal_fail.bmp create mode 100644 src/freespace2/goal_inc.bmp create mode 100644 src/freespace2/goal_none.bmp create mode 100644 src/freespace2/goal_ord.bmp create mode 100644 src/freespace2/levelpaging.cpp create mode 100644 src/gamehelp/contexthelp.cpp create mode 100644 src/gamehelp/gameplayhelp.cpp create mode 100644 src/gamesequence/gamesequence.cpp create mode 100644 src/gamesnd/eventmusic.cpp create mode 100644 src/gamesnd/gamesnd.cpp create mode 100644 src/glide/glide.cpp create mode 100644 src/globalincs/alphacolors.cpp create mode 100644 src/globalincs/crypt.cpp create mode 100644 src/globalincs/systemvars.cpp create mode 100644 src/globalincs/version.cpp create mode 100644 src/globalincs/windebug.cpp create mode 100644 src/graphics/2d.cpp create mode 100644 src/graphics/aaline.cpp create mode 100644 src/graphics/bitblt.cpp create mode 100644 src/graphics/circle.cpp create mode 100644 src/graphics/colors.cpp create mode 100644 src/graphics/font.cpp create mode 100644 src/graphics/gradient.cpp create mode 100644 src/graphics/grd3d.cpp create mode 100644 src/graphics/grd3drender.cpp create mode 100644 src/graphics/grd3dtexture.cpp create mode 100644 src/graphics/grdirectdraw.cpp create mode 100644 src/graphics/grglide.cpp create mode 100644 src/graphics/grglidetexture.cpp create mode 100644 src/graphics/gropengl.cpp create mode 100644 src/graphics/grsoft.cpp create mode 100644 src/graphics/grzbuffer.cpp create mode 100644 src/graphics/line.cpp create mode 100644 src/graphics/pixel.cpp create mode 100644 src/graphics/rect.cpp create mode 100644 src/graphics/scaler.cpp create mode 100644 src/graphics/shade.cpp create mode 100644 src/graphics/tmapgenericscans.cpp create mode 100644 src/graphics/tmapper.cpp create mode 100644 src/graphics/tmapscanline.cpp create mode 100644 src/graphics/tmapscantiled128x128.cpp create mode 100644 src/graphics/tmapscantiled16x16.cpp create mode 100644 src/graphics/tmapscantiled256x256.cpp create mode 100644 src/graphics/tmapscantiled32x32.cpp create mode 100644 src/graphics/tmapscantiled64x64.cpp create mode 100644 src/helped/helped.cpp create mode 100644 src/helped/helped.rc create mode 100644 src/helped/helpeddoc.cpp create mode 100644 src/helped/helpedline.cpp create mode 100644 src/helped/helpedview.cpp create mode 100644 src/helped/mainfrm.cpp create mode 100644 src/helped/res/helped.ico create mode 100644 src/helped/res/helped.rc2 create mode 100644 src/helped/res/helpeddoc.ico create mode 100644 src/helped/res/toolbar.bmp create mode 100644 src/helped/stdafx.cpp create mode 100644 src/helped/tga.cpp create mode 100644 src/hud/hud.cpp create mode 100644 src/hud/hudartillery.cpp create mode 100644 src/hud/hudbrackets.cpp create mode 100644 src/hud/hudconfig.cpp create mode 100644 src/hud/hudescort.cpp create mode 100644 src/hud/hudets.cpp create mode 100644 src/hud/hudlock.cpp create mode 100644 src/hud/hudmessage.cpp create mode 100644 src/hud/hudobserver.cpp create mode 100644 src/hud/hudresource.cpp create mode 100644 src/hud/hudreticle.cpp create mode 100644 src/hud/hudshield.cpp create mode 100644 src/hud/hudsquadmsg.cpp create mode 100644 src/hud/hudtarget.cpp create mode 100644 src/hud/hudtargetbox.cpp create mode 100644 src/hud/hudwingmanstatus.cpp create mode 100644 src/inetfile/cftp.cpp create mode 100644 src/inetfile/chttpget.cpp create mode 100644 src/inetfile/inetgetfile.cpp create mode 100644 src/io/joy.cpp create mode 100644 src/io/joy_ff.cpp create mode 100644 src/io/key.cpp create mode 100644 src/io/keycontrol.cpp create mode 100644 src/io/mouse.cpp create mode 100644 src/io/sw_error.hpp create mode 100644 src/io/sw_guid.hpp create mode 100644 src/io/swff_lib.cpp create mode 100644 src/io/timer.cpp create mode 100644 src/jumpnode/jumpnode.cpp create mode 100644 src/lighting/lighting.cpp create mode 100644 src/localization/fhash.cpp create mode 100644 src/localization/localize.cpp create mode 100644 src/math/fix.cpp create mode 100644 src/math/floating.cpp create mode 100644 src/math/fvi.cpp create mode 100644 src/math/spline.cpp create mode 100644 src/math/staticrand.cpp create mode 100644 src/math/vecmat.cpp create mode 100644 src/menuui/barracks.cpp create mode 100644 src/menuui/credits.cpp create mode 100644 src/menuui/fishtank.cpp create mode 100644 src/menuui/mainhallmenu.cpp create mode 100644 src/menuui/mainhalltemp.cpp create mode 100644 src/menuui/optionsmenu.cpp create mode 100644 src/menuui/optionsmenumulti.cpp create mode 100644 src/menuui/playermenu.cpp create mode 100644 src/menuui/readyroom.cpp create mode 100644 src/menuui/snazzyui.cpp create mode 100644 src/menuui/techmenu.cpp create mode 100644 src/menuui/trainingmenu.cpp create mode 100644 src/mission/missionbriefcommon.cpp create mode 100644 src/mission/missioncampaign.cpp create mode 100644 src/mission/missiongoals.cpp create mode 100644 src/mission/missiongrid.cpp create mode 100644 src/mission/missionhotkey.cpp create mode 100644 src/mission/missionload.cpp create mode 100644 src/mission/missionlog.cpp create mode 100644 src/mission/missionmessage.cpp create mode 100644 src/mission/missionparse.cpp create mode 100644 src/mission/missiontraining.cpp create mode 100644 src/missionui/chatbox.cpp create mode 100644 src/missionui/missionbrief.cpp create mode 100644 src/missionui/missioncmdbrief.cpp create mode 100644 src/missionui/missiondebrief.cpp create mode 100644 src/missionui/missionloopbrief.cpp create mode 100644 src/missionui/missionpause.cpp create mode 100644 src/missionui/missionrecommend.cpp create mode 100644 src/missionui/missionscreencommon.cpp create mode 100644 src/missionui/missionshipchoice.cpp create mode 100644 src/missionui/missionstats.cpp create mode 100644 src/missionui/missionweaponchoice.cpp create mode 100644 src/missionui/redalert.cpp create mode 100644 src/model/modelcollide.cpp create mode 100644 src/model/modelinterp.cpp create mode 100644 src/model/modeloctant.cpp create mode 100644 src/model/modelread.cpp create mode 100644 src/nebedit/nebedit.cpp create mode 100644 src/nebedit/nebstubs.cpp create mode 100644 src/nebula/neb.cpp create mode 100644 src/nebula/neblightning.cpp create mode 100644 src/network/multi.cpp create mode 100644 src/network/multi_campaign.cpp create mode 100644 src/network/multi_data.cpp create mode 100644 src/network/multi_dogfight.cpp create mode 100644 src/network/multi_endgame.cpp create mode 100644 src/network/multi_ingame.cpp create mode 100644 src/network/multi_kick.cpp create mode 100644 src/network/multi_log.cpp create mode 100644 src/network/multi_obj.cpp create mode 100644 src/network/multi_observer.cpp create mode 100644 src/network/multi_oo.cpp create mode 100644 src/network/multi_options.cpp create mode 100644 src/network/multi_pause.cpp create mode 100644 src/network/multi_pinfo.cpp create mode 100644 src/network/multi_ping.cpp create mode 100644 src/network/multi_pmsg.cpp create mode 100644 src/network/multi_rate.cpp create mode 100644 src/network/multi_respawn.cpp create mode 100644 src/network/multi_team.cpp create mode 100644 src/network/multi_update.cpp create mode 100644 src/network/multi_voice.cpp create mode 100644 src/network/multi_xfer.cpp create mode 100644 src/network/multilag.cpp create mode 100644 src/network/multimsgs.cpp create mode 100644 src/network/multiteamselect.cpp create mode 100644 src/network/multiui.cpp create mode 100644 src/network/multiutil.cpp create mode 100644 src/network/psnet.cpp create mode 100644 src/network/psnet2.cpp create mode 100644 src/network/stand_gui.cpp create mode 100644 src/object/collidedebrisship.cpp create mode 100644 src/object/collidedebrisweapon.cpp create mode 100644 src/object/collideshipship.cpp create mode 100644 src/object/collideshipweapon.cpp create mode 100644 src/object/collideweaponweapon.cpp create mode 100644 src/object/objcollide.cpp create mode 100644 src/object/object.cpp create mode 100644 src/object/objectsnd.cpp create mode 100644 src/object/objectsort.cpp create mode 100644 src/observer/observer.cpp create mode 100644 src/osapi/os_unix.cpp create mode 100644 src/osapi/os_win.cpp create mode 100644 src/osapi/osregistry.cpp create mode 100644 src/osapi/outwnd.cpp create mode 100644 src/palman/palman.cpp create mode 100644 src/parse/encrypt.cpp create mode 100644 src/parse/parselo.cpp create mode 100644 src/parse/sexp.cpp create mode 100644 src/particle/particle.cpp create mode 100644 src/pcxutils/pcxutils.cpp create mode 100644 src/physics/physics.cpp create mode 100644 src/platform/unix.cpp create mode 100644 src/playerman/managepilot.cpp create mode 100644 src/playerman/playercontrol.cpp create mode 100644 src/pofview/childfrm.cpp create mode 100644 src/pofview/mainfrm.cpp create mode 100644 src/pofview/objecttree.cpp create mode 100644 src/pofview/pofview.cpp create mode 100644 src/pofview/pofview.rc create mode 100644 src/pofview/pofviewdoc.cpp create mode 100644 src/pofview/pofviewstubs.cpp create mode 100644 src/pofview/pofviewview.cpp create mode 100644 src/pofview/res/pofview.ico create mode 100644 src/pofview/res/pofview.rc2 create mode 100644 src/pofview/res/pofviewdoc.ico create mode 100644 src/pofview/res/toolbar.bmp create mode 100644 src/pofview/stdafx.cpp create mode 100644 src/popup/popup.cpp create mode 100644 src/popup/popupdead.cpp create mode 100644 src/radar/radar.cpp create mode 100644 src/render/3dclipper.cpp create mode 100644 src/render/3ddraw.cpp create mode 100644 src/render/3dlaser.cpp create mode 100644 src/render/3dmath.cpp create mode 100644 src/render/3dsetup.cpp create mode 100644 src/scramble/scramble.cpp create mode 100644 src/ship/afterburner.cpp create mode 100644 src/ship/ai.cpp create mode 100644 src/ship/aibig.cpp create mode 100644 src/ship/aicode.cpp create mode 100644 src/ship/aigoals.cpp create mode 100644 src/ship/awacs.cpp create mode 100644 src/ship/shield.cpp create mode 100644 src/ship/ship.cpp create mode 100644 src/ship/shipcontrails.cpp create mode 100644 src/ship/shipfx.cpp create mode 100644 src/ship/shiphit.cpp create mode 100644 src/sound/acm.cpp create mode 100644 src/sound/audiostr.cpp create mode 100644 src/sound/ds.cpp create mode 100644 src/sound/ds3d.cpp create mode 100644 src/sound/dscap.cpp create mode 100644 src/sound/midifile.cpp create mode 100644 src/sound/rbaudio.cpp create mode 100644 src/sound/rsx_lib.cpp create mode 100644 src/sound/rtvoice.cpp create mode 100644 src/sound/sound.cpp create mode 100644 src/sound/verifya3d.lib create mode 100644 src/sound/winmidi.cpp create mode 100644 src/sound/winmidi_base.cpp create mode 100644 src/starfield/nebula.cpp create mode 100644 src/starfield/starfield.cpp create mode 100644 src/starfield/supernova.cpp create mode 100644 src/stats/medals.cpp create mode 100644 src/stats/scoring.cpp create mode 100644 src/stats/stats.cpp create mode 100644 src/tgautils/tgautils.cpp create mode 100644 src/ui/button.cpp create mode 100644 src/ui/checkbox.cpp create mode 100644 src/ui/gadget.cpp create mode 100644 src/ui/icon.cpp create mode 100644 src/ui/inputbox.cpp create mode 100644 src/ui/keytrap.cpp create mode 100644 src/ui/listbox.cpp create mode 100644 src/ui/radio.cpp create mode 100644 src/ui/scroll.cpp create mode 100644 src/ui/slider.cpp create mode 100644 src/ui/slider2.cpp create mode 100644 src/ui/uidraw.cpp create mode 100644 src/ui/uimouse.cpp create mode 100644 src/ui/window.cpp create mode 100644 src/vcodec/codec1.cpp create mode 100644 src/weapon/beam.cpp create mode 100644 src/weapon/corkscrew.cpp create mode 100644 src/weapon/emp.cpp create mode 100644 src/weapon/flak.cpp create mode 100644 src/weapon/muzzleflash.cpp create mode 100644 src/weapon/shockwave.cpp create mode 100644 src/weapon/swarm.cpp create mode 100644 src/weapon/trails.cpp create mode 100644 src/weapon/weapons.cpp create mode 100644 winfiles/Ac.dsp create mode 100644 winfiles/Ac.plg create mode 100644 winfiles/Cfilearchiver.dsp create mode 100644 winfiles/Cfilearchiver.plg create mode 100644 winfiles/Cryptstring.dsp create mode 100644 winfiles/Cryptstring.plg create mode 100644 winfiles/FS2AutoRun.ico create mode 100644 winfiles/Fonttool.dsp create mode 100644 winfiles/Fonttool.plg create mode 100644 winfiles/Fred2.dsp create mode 100644 winfiles/Fred2.plg create mode 100644 winfiles/Freespace2.dsp create mode 100644 winfiles/Freespace2.plg create mode 100644 winfiles/HelpEd.dsp create mode 100644 winfiles/HelpEd.plg create mode 100644 winfiles/Nebedit.dsp create mode 100644 winfiles/Nebedit.plg create mode 100644 winfiles/Pofview.dsp create mode 100644 winfiles/Pofview.plg create mode 100644 winfiles/Scramble.dsp create mode 100644 winfiles/Scramble.plg create mode 100644 winfiles/freespace2.dsw create mode 100644 winfiles/freespace2.opt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c6dfe25 --- /dev/null +++ b/Makefile @@ -0,0 +1,255 @@ +# Makefile for code module +# for that freespace 2 thing + +CC=g++-3.0 +BINARY=code.so +LDFLAGS=$(shell sdl-config --libs) +CFLAGS=-Wall -g -DPLAT_UNIX -O2 $(shell sdl-config --cflags) -Iinclude/ + + +%.o: %.cpp + $(CC) -c -o $@ $< $(CFLAGS) + + +SOURCES =./src/anim/animplay.cpp \ + ./src/anim/packunpack.cpp \ + ./src/asteroid/asteroid.cpp \ + ./src/bmpman/bmpman.cpp \ + ./src/cfile/cfile.cpp \ + ./src/cfile/cfilearchive.cpp \ + ./src/cfile/cfilelist.cpp \ + ./src/cfile/cfilesystem.cpp \ + ./src/cmdline/cmdline.cpp \ + ./src/cmeasure/cmeasure.cpp \ + ./src/controlconfig/controlsconfig.cpp \ + ./src/controlconfig/controlsconfigcommon.cpp \ + ./src/cutscene/cutscenes.cpp \ + ./src/debris/debris.cpp \ + ./src/debugconsole/console.cpp \ + ./src/fireball/fireballs.cpp \ + ./src/fireball/warpineffect.cpp \ + ./src/gamehelp/contexthelp.cpp \ + ./src/gamehelp/gameplayhelp.cpp \ + ./src/gamesequence/gamesequence.cpp \ + ./src/gamesnd/eventmusic.cpp \ + ./src/gamesnd/gamesnd.cpp \ + ./src/globalincs/alphacolors.cpp \ + ./src/globalincs/crypt.cpp \ + ./src/globalincs/systemvars.cpp \ + ./src/globalincs/version.cpp \ + ./src/graphics/2d.cpp \ + ./src/graphics/aaline.cpp \ + ./src/graphics/bitblt.cpp \ + ./src/graphics/circle.cpp \ + ./src/graphics/colors.cpp \ + ./src/graphics/font.cpp \ + ./src/graphics/gradient.cpp \ + ./src/graphics/gropengl.cpp \ + ./src/graphics/grzbuffer.cpp \ + ./src/graphics/line.cpp \ + ./src/graphics/pixel.cpp \ + ./src/graphics/rect.cpp \ + ./src/graphics/scaler.cpp \ + ./src/graphics/shade.cpp \ + ./src/graphics/tmapper.cpp \ + ./src/graphics/tmapscanline.cpp \ + ./src/graphics/tmapscantiled128x128.cpp \ + ./src/graphics/tmapscantiled16x16.cpp \ + ./src/graphics/tmapscantiled256x256.cpp \ + ./src/graphics/tmapscantiled32x32.cpp \ + ./src/graphics/tmapscantiled64x64.cpp \ + ./src/hud/hud.cpp \ + ./src/hud/hudartillery.cpp \ + ./src/hud/hudbrackets.cpp \ + ./src/hud/hudconfig.cpp \ + ./src/hud/hudescort.cpp \ + ./src/hud/hudets.cpp \ + ./src/hud/hudlock.cpp \ + ./src/hud/hudmessage.cpp \ + ./src/hud/hudobserver.cpp \ + ./src/hud/hudreticle.cpp \ + ./src/hud/hudshield.cpp \ + ./src/hud/hudsquadmsg.cpp \ + ./src/hud/hudtarget.cpp \ + ./src/hud/hudtargetbox.cpp \ + ./src/hud/hudwingmanstatus.cpp \ + ./src/io/key.cpp \ + ./src/io/keycontrol.cpp \ + ./src/io/mouse.cpp \ + ./src/io/timer.cpp \ + ./src/jumpnode/jumpnode.cpp \ + ./src/lighting/lighting.cpp \ + ./src/math/fix.cpp \ + ./src/math/floating.cpp \ + ./src/math/fvi.cpp \ + ./src/math/spline.cpp \ + ./src/math/staticrand.cpp \ + ./src/math/vecmat.cpp \ + ./src/menuui/barracks.cpp \ + ./src/menuui/credits.cpp \ + ./src/menuui/fishtank.cpp \ + ./src/menuui/mainhallmenu.cpp \ + ./src/menuui/mainhalltemp.cpp \ + ./src/menuui/optionsmenu.cpp \ + ./src/menuui/optionsmenumulti.cpp \ + ./src/menuui/playermenu.cpp \ + ./src/menuui/readyroom.cpp \ + ./src/menuui/snazzyui.cpp \ + ./src/menuui/techmenu.cpp \ + ./src/menuui/trainingmenu.cpp \ + ./src/mission/missionbriefcommon.cpp \ + ./src/mission/missioncampaign.cpp \ + ./src/mission/missiongoals.cpp \ + ./src/mission/missiongrid.cpp \ + ./src/mission/missionhotkey.cpp \ + ./src/mission/missionload.cpp \ + ./src/mission/missionlog.cpp \ + ./src/mission/missionmessage.cpp \ + ./src/mission/missionparse.cpp \ + ./src/mission/missiontraining.cpp \ + ./src/missionui/chatbox.cpp \ + ./src/missionui/missionbrief.cpp \ + ./src/missionui/missioncmdbrief.cpp \ + ./src/missionui/missiondebrief.cpp \ + ./src/missionui/missionloopbrief.cpp \ + ./src/missionui/missionpause.cpp \ + ./src/missionui/missionrecommend.cpp \ + ./src/missionui/missionscreencommon.cpp \ + ./src/missionui/missionshipchoice.cpp \ + ./src/missionui/missionstats.cpp \ + ./src/missionui/missionweaponchoice.cpp \ + ./src/missionui/redalert.cpp \ + ./src/model/modelcollide.cpp \ + ./src/model/modelinterp.cpp \ + ./src/model/modeloctant.cpp \ + ./src/model/modelread.cpp \ + ./src/object/collidedebrisship.cpp \ + ./src/object/collidedebrisweapon.cpp \ + ./src/object/collideshipship.cpp \ + ./src/object/collideshipweapon.cpp \ + ./src/object/collideweaponweapon.cpp \ + ./src/object/objcollide.cpp \ + ./src/object/object.cpp \ + ./src/object/objectsnd.cpp \ + ./src/object/objectsort.cpp \ + ./src/observer/observer.cpp \ + ./src/osapi/os_unix.cpp \ + ./src/palman/palman.cpp \ + ./src/parse/encrypt.cpp \ + ./src/particle/particle.cpp \ + ./src/pcxutils/pcxutils.cpp \ + ./src/physics/physics.cpp \ + ./src/playerman/managepilot.cpp \ + ./src/playerman/playercontrol.cpp \ + ./src/popup/popup.cpp \ + ./src/popup/popupdead.cpp \ + ./src/radar/radar.cpp \ + ./src/render/3dclipper.cpp \ + ./src/render/3ddraw.cpp \ + ./src/render/3dlaser.cpp \ + ./src/render/3dmath.cpp \ + ./src/render/3dsetup.cpp \ + ./src/ship/afterburner.cpp \ + ./src/ship/ai.cpp \ + ./src/ship/aibig.cpp \ + ./src/ship/aicode.cpp \ + ./src/ship/aigoals.cpp \ + ./src/ship/awacs.cpp \ + ./src/ship/shield.cpp \ + ./src/ship/ship.cpp \ + ./src/ship/shipcontrails.cpp \ + ./src/ship/shipfx.cpp \ + ./src/ship/shiphit.cpp \ + ./src/sound/acm.cpp \ + ./src/sound/audiostr.cpp \ + ./src/sound/ds.cpp \ + ./src/sound/ds3d.cpp \ + ./src/sound/dscap.cpp \ + ./src/sound/midifile.cpp \ + ./src/sound/rbaudio.cpp \ + ./src/sound/rsx_lib.cpp \ + ./src/sound/rtvoice.cpp \ + ./src/sound/sound.cpp \ + ./src/sound/winmidi.cpp \ + ./src/sound/winmidi_base.cpp \ + ./src/starfield/nebula.cpp \ + ./src/starfield/starfield.cpp \ + ./src/starfield/supernova.cpp \ + ./src/stats/medals.cpp \ + ./src/stats/scoring.cpp \ + ./src/stats/stats.cpp \ + ./src/ui/button.cpp \ + ./src/ui/checkbox.cpp \ + ./src/ui/gadget.cpp \ + ./src/ui/icon.cpp \ + ./src/ui/inputbox.cpp \ + ./src/ui/keytrap.cpp \ + ./src/ui/listbox.cpp \ + ./src/ui/radio.cpp \ + ./src/ui/scroll.cpp \ + ./src/ui/slider.cpp \ + ./src/ui/slider2.cpp \ + ./src/ui/uidraw.cpp \ + ./src/ui/uimouse.cpp \ + ./src/ui/window.cpp \ + ./src/weapon/beam.cpp \ + ./src/weapon/corkscrew.cpp \ + ./src/weapon/emp.cpp \ + ./src/weapon/flak.cpp \ + ./src/weapon/muzzleflash.cpp \ + ./src/weapon/shockwave.cpp \ + ./src/weapon/swarm.cpp \ + ./src/weapon/trails.cpp \ + ./src/weapon/weapons.cpp \ + ./src/nebula/neb.cpp \ + ./src/nebula/neblightning.cpp \ + ./src/localization/fhash.cpp \ + ./src/localization/localize.cpp \ + ./src/tgautils/tgautils.cpp \ + ./src/demo/demo.cpp \ + ./src/inetfile/cftp.cpp \ + ./src/inetfile/chttpget.cpp \ + ./src/inetfile/inetgetfile.cpp \ + ./src/exceptionhandler/exceptionhandler.cpp \ + ./src/network/multi.cpp \ + ./src/network/multi_campaign.cpp \ + ./src/network/multi_data.cpp \ + ./src/network/multi_dogfight.cpp \ + ./src/network/multi_endgame.cpp \ + ./src/network/multi_ingame.cpp \ + ./src/network/multi_kick.cpp \ + ./src/network/multi_log.cpp \ + ./src/network/multi_obj.cpp \ + ./src/network/multi_observer.cpp \ + ./src/network/multi_oo.cpp \ + ./src/network/multi_options.cpp \ + ./src/network/multi_pause.cpp \ + ./src/network/multi_pinfo.cpp \ + ./src/network/multi_ping.cpp \ + ./src/network/multi_pmsg.cpp \ + ./src/network/multi_rate.cpp \ + ./src/network/multi_respawn.cpp \ + ./src/network/multi_team.cpp \ + ./src/network/multi_update.cpp \ + ./src/network/multi_voice.cpp \ + ./src/network/multi_xfer.cpp \ + ./src/network/multilag.cpp \ + ./src/network/multimsgs.cpp \ + ./src/network/multiteamselect.cpp \ + ./src/network/multiui.cpp \ + ./src/network/multiutil.cpp \ + ./src/network/psnet.cpp \ + ./src/network/psnet2.cpp \ + ./src/network/stand_gui.cpp \ + ./src/platform/unix.cpp + +OBJECTS=$(SOURCES:.cpp=.o) + + +all: code + +code: $(OBJECTS) + $(CC) -shared -o $(BINARY) $(LDFLAGS) $(OBJECTS) + +clean: + rm -rf $(BINARY) $(OBJECTS) diff --git a/doc/guided_tour.txt b/doc/guided_tour.txt new file mode 100644 index 0000000..0ac0479 --- /dev/null +++ b/doc/guided_tour.txt @@ -0,0 +1,170 @@ +A guided tour of the FS2 source code, by DaveB +----------------------------------------------- + +Within this code you will find both razor sharp coolness and horrifying +atrocities. I'm hoping it'll serve not just as tool for learning how to +do things, but also for learning how _not_ to do things. Have fun with it. + + +Overview +-------- +FS2 is an MsDev 6.0 project. Within the workspace are several subprojects. + +- Ac. This is the animation conversion utility for generating FS2's + custom .ani files. + +- CFileArchiver. A very simple command line utility for quickly packing + files into .vp files. + +- code. 99% of all FS2 code exists in this module. Many of the utility + projects have this library as a dependancy and it is organized as a + simple set of code folders. + +- Cryptstring. A command-line tool for generating super-secret codes from + input text. More of a curiosity than anything else ;) + +- Fonttool. A simple utility for generating and kerning the font files + FS2 uses. + +- Fred2. The FS2 mission editor. A giant mass of MFC code - it references + plenty of stuff from the code library. + +- Freespace2. This generates the FS2 executable. There are only a + handful of files in this subproject - most of the game comes from the + code library. + +- Nebedit. An old utility used for generating FS1 style background nebulae + patterns. Not used directly in FS2, but the code to display the old + nebulae still exists in the FS2 codebase. + +- Pofview. A simple MFC app for viewing POF files as they would appear + ingame. + +- Scramble. Top-secret Canadian encryption program. Guaranteed to be + unbreakable for at least 20 to 25 minutes. + +The critical subprojects most people will be concerned with are code, +Freespace2, and Fred2. All of these subprojects have Debug and Release +(optimized) builds. pstypes.h also contains quite a few conditional +build switches. + +All source safe checkin headers have been left in for maximum comedy. + +This is code-only. You cannot run without having a full copy of the game +to supply the data. + + +Stuff that doesn't exist +------------------------ + +Several bits of the game have been removed from this release. Nothing +of major importance. They include the launcher, the movie player, the +auto-updater, and all code related to PXO and squadwar (for obvious +reasons). + + +Project settings (Msdev 6.0) +---------------------------- + +All of the subprojects that rely on the FS2 data tree to run (code, fred2, +freespace2, nebedit, pofview) are all currently targeted at + +e:\games\freespace2\ + +which is the directory I had on my local hard drive for putting this together. +Before you'll really be able to do anything, you'll need to point the targets +at wherever your local FS2 install is (make sure to make a copy of your +installed executables so you can backtrack!). + +To change the destination in MsDev, you'll need to change +settings->debug->executable for debug session +settings->debug->working directory +settings->link->output file name + +All projects have been verified as compiling and running properly. So, unless +I screwed something up between the time of checking everything and building +the zip file (entirely possible) you should just be able to load the workspace +and hit F7. If not, hey, just one more thing to play with ;) + + +Post-release addition +--------------------- + +I hacked in very quick-and-dirty windowed support for Direct3d mode. +The way FS2 was developed, we always could rely on running Glide +on a second monitor. Trying to do any kind of serious debugging in full +screen Direct3D is just a recipe for agonizing pain. So windowed D3D +should make debugging a whole lot easier for the average guy. I make no +guarantees about completeness or stability since it was hacked together +in 3 hours - however it does appear to function properly in both resolutions, +in both 16 and 32 bit desktops. Depending on your 3d card and your driver +settings, you may get some texture coordinate wackiness (particularly +visible on interface screens) but for the purpose of debugging/development +it should be sufficient. Fiddling with your driver settings in Control Panel +can make this go away. + +You can run the game in windowed mode by adding -window on the command-line. + + + +Interesting projects +-------------------- +There's a bunch of cool things that could be done with the code. + +- It uses directX 5. It might be fun to bring that whole thing up to + speed with DX8. + +- The lighting and model rendering code works, but is much more expensive + than it needs to be on average. With even a little bit of work, it + wouldn't be too hard to probably triple the throughput of that part of + the pipeline (taking advantage of the guard band present in almost all + modern 3d cards, maybe even a dash of T&L). + +- The multiplayer code is huge and scary. Its nor particularly "modern" in + terms of motion prediction and lag compensation. It could use some taming. + +- With the higher fillrates available on video cards today, I'm sure the + full nebula effect could be improved/changed quite a bit. + +- The FS community has been clamoring for higher ship counts and object limits. + It would be a pretty trivial matter to have a commandline option that lets + you specify MAX_SHIPS and similar constants. The internal stuff could be + changed from a static array to a dynamically allocated array at startup. A + few #defines would be need to changed to ints, but beyond that it should be + pretty trivial. + +- The Ai code is another huge, intimidating system. There's plenty of room for + cool AI functionality. + +- There's a mostly empty opengl implementation of the low level graphics code. + Might be fun to complete. Although I doubt it would be nearly as fast as D3D + or Glide, you never know. + +- Fred2 is the omega of all giant unwieldy pieces of code. Its big, its horrifying, + and it just doesn't care. View it at your own risk. + +- The physics library is pretty well partitioned and lives in its own little + world. If you want to add newtonian motion, that's where you'd start. + +- The particle system code, while also functional is a bit old fashioned. A + little elbow grease could really bring it up to speed and make it able to + handle much higher particle counts. + +- Mods. Have at it. + +- Multi-res interface. For all the masochists out there. + +- There was a demo recording/playback system I half-heartedly was playing with at + one point. Its still in there, in very incomplete form. + +- The math library is very powerful. Not necessarily a "project" on its own, but + its very sophisticated. + +- Bmpman is rather brutish. It could be made far more elegant. + +- The sexpression code is some of the more elegant and powerful code in the game. + Its very extensible and fairly easy to work with. Much functionality can be + added with these. + +- For every bug you can find, I'll give you $5. No, I'm kidding. I don't have + that much money. \ No newline at end of file diff --git a/doc/interesting_stats.txt b/doc/interesting_stats.txt new file mode 100644 index 0000000..9090868 --- /dev/null +++ b/doc/interesting_stats.txt @@ -0,0 +1,52 @@ +Counts for various words in the code +------------------------------------ + +fixed : 2169 + +bogus : 350 +hack : 157 +whee : 145 +cool : 76 +stupid : 57 +tweak : 43 +broken : 41 +dumb : 28 +fish : 25 +broke : 22 +nasty : 21 +yay : 21 +efficient : 20 +complex : 20 +ugly : 13 +nicely : 13 +great : 11 +silly : 9 +fixme : 9 +smart : 8 +ugh : 7 +confused : 6 +insane : 5 +whoops : 4 +awesome : 3 +fancy : 2 +horrible : 2 +brute force : 2 +sucks : 2 +hateful : 1 +speedy : 1 +badly : 1 +painful : 1 +intense : 1 + +Total lines of source : + +.cpp files : 394,813 +.h files : 64,187 +total : 459,000 + + +Total lines of source, not counting comments : + +.cpp files : 287,553 +.h files : 36,163 +total : 323,716 \ No newline at end of file diff --git a/doc/release_log.txt b/doc/release_log.txt new file mode 100644 index 0000000..a4773a5 --- /dev/null +++ b/doc/release_log.txt @@ -0,0 +1,17 @@ +4/20/02 : Added very basic windowed D3D mode to help those who don't have access to 2 monitors + for Glide debugging. Hooked it in as the -window commandline parameter. + +4/21/02 : Removed updatelauncher, stamper, movieplayer, autorun, fs2launch projects. +4/21/02 : Added some stubs to Pofview to get it compiling. +4/21/02 : Added stubs to Nebedit to get it compiling. Added Nebedit_running to ensure software mode startup. +4/21/02 : Added stubs to fonttool to get it compiling. +4/21/02 : Added stubs to AC to get it compiling. +4/21/02 : Hacked out movieplayer and all references to it. +4/21/02 : Hacked out amd 3d-now specific stuff. +4/21/02 : Hacked out PXO code. + +4/22/02 : Removed stamper stuff. Cleaned out remaining unused files. +4/22/02 : Final pass checking all builds. + +4/24/02 : Added word statistics for "hack" and "fixme" at the request of jefff. +4/24/02 : Added more notes to guided_tour.txt \ No newline at end of file diff --git a/include/2d.h b/include/2d.h new file mode 100644 index 0000000..6b0741b --- /dev/null +++ b/include/2d.h @@ -0,0 +1,713 @@ +/* + * $Logfile: /Freespace2/code/Graphics/2d.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for 2d primitives. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 16 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 15 8/16/99 9:45a Jefff + * changes to cursor management to allow a 2nd temporary cursor + * + * 14 7/15/99 3:07p Dave + * 32 bit detection support. Mouse coord commandline. + * + * 13 7/14/99 9:42a Dave + * Put in clear_color debug function. Put in base for 3dnow stuff / P3 + * stuff + * + * 12 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 11 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 10 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 9 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 8 1/24/99 11:36p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 7 12/21/98 5:02p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 6 12/18/98 1:49a Dave + * Fixed Fred initialization problem resulting from hi-res mode changes. + * + * 5 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 4 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 3 11/11/98 5:37p Dave + * Checkin for multiplayer testing. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 75 5/20/98 9:45p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 74 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 73 4/14/98 12:15p John + * Made 16-bpp movies work. + * + * 72 4/10/98 5:20p John + * Changed RGB in lighting structure to be ubytes. Removed old + * not-necessary 24 bpp software stuff. + * + * 71 3/25/98 8:07p John + * Restructured software rendering into two modules; One for windowed + * debug mode and one for DirectX fullscreen. + * + * 70 3/24/98 8:31a John + * Added function to set gamma + * + * 69 3/17/98 5:55p John + * Added code to dump Glide frames. Moved Allender's "hack" code out of + * Freespace.cpp into the proper place, graphics lib. + * + * 68 3/12/98 5:36p John + * Took out any unused shaders. Made shader code take rgbc instead of + * matrix and vector since noone used it like a matrix and it would have + * been impossible to do in hardware. Made Glide implement a basic + * shader for online help. + * + * 67 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 66 2/07/98 7:50p John + * Added code so that we can use the old blending type of alphacolors if + * we want to. Made the stars use them. + * + * 65 1/08/98 1:54p John + * Added code to fix palette problems when Alt+Tabbing + * + * 64 12/30/97 6:46p John + * Added first rev of palette fade in out functions + * + * 63 12/03/97 10:47a John + * added functions to save/restore entire screens. + * + * 62 12/02/97 3:59p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 61 11/20/97 9:51a John + * added code to force screen to 16-bit even if rendering 8. + * + * 60 11/03/97 10:08p Hoffoss + * Changed gr_get_string_size to utilize an optional length specifier, if + * you want to use non-null terminated strings. + * + * 59 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 58 10/03/97 10:02a John + * added better comments for lines. + * + * 57 10/03/97 9:10a John + * added better antialiased line drawer + * + * 56 9/23/97 10:45a John + * made so you can tell bitblt code to rle a bitmap by passing flag to + * gr_set_bitmap + * + * 55 9/07/97 10:01p Lawrance + * add in support for animating mouse pointer + * + * 54 8/04/97 4:47p John + * added gr_aascaler. + * + * 53 7/16/97 3:07p John + * + * 52 7/10/97 2:06p John + * added code to specify alphablending type for bitmaps. + * + * 51 6/25/97 2:35p John + * added some functions to use the windows font for Fred. + * + * 50 6/17/97 7:04p John + * added d3d support for gradients. + * fixed some color bugs by adding screen signatures instead of watching + * flags and palette changes. + * + * 49 6/17/97 12:03p John + * Moved color/alphacolor functions into their own module. Made all color + * functions be part of the low-level graphics drivers, not just the + * grsoft. + * + * 48 6/13/97 5:35p John + * added some antialiased bitmaps and lines + * + * 47 6/11/97 5:49p John + * Changed palette code to only recalculate alphacolors when needed, not + * when palette changes. + * + * 46 6/11/97 4:11p John + * addec function to get font height + * + * 45 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 44 6/09/97 9:24a John + * Changed the way fonts are set. + * + * 43 6/06/97 4:41p John + * Fixed alpha colors to be smoothly integrated into gr_set_color_fast + * code. + * + * 42 6/05/97 4:53p John + * First rev of new antialiased font stuff. + * + * 41 5/29/97 3:09p John + * Took out debug menu. + * Made software scaler draw larger bitmaps. + * Optimized Direct3D some. + * + * 40 5/14/97 10:53a John + * fixed some discrepencies between d3d and software palette setting. + * + * 39 5/12/97 3:09p John + * fixed a stupid macro bug. + * + * 38 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 37 4/28/97 4:46p John + * + * 36 4/23/97 5:26p John + * First rev of new debug console stuff. + * + * 35 3/12/97 2:51p John + * Added some test code for tmapper. + * + * 34 3/12/97 9:25a John + * fixed a bug with zbuffering. Reenabled it by default. + * + * 33 3/04/97 3:36p John + * took out old debug "h' key. Made zbuffer flag bit bit field so you + * can turn on/off each value. Fixed a bug with turret rotation where + * different handedness turrets wouldn't work. Fixed a bug with two + * large ships within each other's radius not rendering correctly. + * + * 32 1/27/97 9:08a John + * Added code to turn zbuffering on/off in call to g3_start_frame + * + * 31 1/09/97 11:35a John + * Added some 2d functions to get/put screen images. + * + * 30 1/07/97 2:01p John + * Fairly fast zbuffering for object sorting. + * + * 29 1/06/97 2:44p John + * Added in slow (but correct) zbuffering + * + * 28 12/11/96 12:41p John + * Added new code to draw 3d laser using 2d ellipses. + * + * 27 12/10/96 10:37a John + * Restructured texture mapper to remove some overhead from each scanline + * setup. This gave about a 30% improvement drawing trans01.pof, which is + * a really complex model. In the process, I cleaned up the scanline + * functions and separated them into different modules for each pixel + * depth. + * + * 26 11/21/96 11:21a John + * Made gr_get_string_size handle multi line text. + * Took out gr_get_multiline_string_size + * + * 25 11/20/96 10:01a Hoffoss + * A few minor improvements. + * + * 24 11/18/96 4:35p Allender + * new 16bpp gradient functions + * + * 23 11/18/96 12:36p John + * Added code to dump screen to a PCX file. + * + * 22 11/18/96 11:40a John + * Added faster gr_set_color method. + * + * 21 11/15/96 3:34p Allender + * added bpp variable to the shader structure + * + * 20 11/13/96 6:47p John + * Added gr_flip function. + * + * 19 11/13/96 10:10a John + * Increases MAX_WIDTH & HEIGHT for Jasen's massive 1600x1200 display. + * + * 18 10/30/96 10:36a Lawrance + * added gr_diamond function + * + * 17 10/26/96 2:56p John + * Got gradient code working. + * + * 16 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#ifndef _GRAPHICS_H +#define _GRAPHICS_H + +/* ========================= pixel plotters ========================= +In the 2d/texture mapper, bitmaps to be drawn will be passed by number. +The 2d function will call a bmpman function to get the bitmap into whatever +format it needs. Then it will render. The only pixels that will ever +get drawn go thru the 2d/texture mapper libraries only. This will make +supporting accelerators and psx easier. Colors will always be set with +the color set functions. + +gr_surface_flip() switch onscreen, offscreen + +gr_set_clip(x,y,w,h) // sets the clipping region +gr_reset_clip(x,y,w,h) // sets the clipping region +gr_set_color --? 8bpp, 15bpp? +gr_set_font(int fontnum) +// see GR_ALPHABLEND defines for values for alphablend_mode +// see GR_BITBLT_MODE defines for bitblt_mode. +// Alpha = scaler for intensity +gr_set_bitmap( int bitmap_num, int alphblend_mode, int bitblt_mode, float alpha ) +gr_set_shader( int value ) 0=normal -256=darken, 256=brighten +gr_set_palette( ubyte * palette ) + +gr_clear() // clears entire clipping region +gr_bitmap(x,y) +gr_bitmap_ex(x,y,w,h,sx,sy) +gr_rect(x,y,w,h) +gr_shade(x,y,w,h) +gr_string(x,y,char * text) +gr_line(x1,y1,x2,y2) + + +*/ + +#include "pstypes.h" +#include "tmapper.h" + +// This is a structure used by the shader to keep track +// of the values you want to use in the shade primitive. +typedef struct shader { + uint screen_sig; // current mode this is in + float r,g,b,c; // factors and constant + ubyte lookup[256]; +} shader; + +#define AC_TYPE_NONE 0 // Not an alphacolor +#define AC_TYPE_HUD 1 // Doesn't change hue depending on background. Used for HUD stuff. +#define AC_TYPE_BLEND 2 // Changes hue depending on background. Used for stars, etc. + +// NEVER REFERENCE THESE VALUES OUTSIDE OF THE GRAPHICS LIBRARY!!! +// If you need to get the rgb values of a "color" struct call +// gr_get_colors after calling gr_set_colors_fast. +typedef struct color { + uint screen_sig; + ubyte red; + ubyte green; + ubyte blue; + ubyte alpha; + ubyte ac_type; // The type of alphacolor. See AC_TYPE_??? defines + int is_alphacolor; + ubyte raw8; + int alphacolor; + int magic; +} color; + +#define GR_ALPHABLEND_NONE 0 // no blending +#define GR_ALPHABLEND_FILTER 1 // 50/50 mix of foreground, background, using intensity as alpha + +#define GR_BITBLT_MODE_NORMAL 0 // Normal bitblting +#define GR_BITBLT_MODE_RLE 1 // RLE would be faster + +// fog modes +#define GR_FOGMODE_NONE 0 // set this to turn off fog +#define GR_FOGMODE_FOG 1 // linear fog + +typedef struct screen { + uint signature; // changes when mode or palette or width or height changes + int max_w, max_h; // Width and height + int res; // GR_640 or GR_1024 + int mode; // What mode gr_init was called with. + float aspect; // Aspect ratio + int rowsize; // What you need to add to go to next row (includes bytes_per_pixel) + int bits_per_pixel; // How many bits per pixel it is. (7,8,15,16,24,32) + int bytes_per_pixel; // How many bytes per pixel (1,2,3,4) + int offset_x, offset_y; // The offsets into the screen + int clip_width, clip_height; + + float fog_near, fog_far; + + // the clip_l,r,t,b are used internally. left and top are + // actually always 0, but it's nice to have the code work with + // arbitrary clipping regions. + int clip_left, clip_right, clip_top, clip_bottom; + + int current_alphablend_mode; // See GR_ALPHABLEND defines above + int current_bitblt_mode; // See GR_BITBLT_MODE defines above + int current_fog_mode; // See GR_FOGMODE_* defines above + int current_bitmap; + int current_bitmap_sx; // bitmap x section + int current_bitmap_sy; // bitmap y section + color current_color; + color current_fog_color; // current fog color + color current_clear_color; // current clear color + shader current_shader; + float current_alpha; + void *offscreen_buffer; // NEVER ACCESS! This+rowsize*y = screen offset + void *offscreen_buffer_base; // Pointer to lowest address of offscreen buffer + + //switch onscreen, offscreen + void (*gf_flip)(); + void (*gf_flip_window)(uint _hdc, int x, int y, int w, int h ); + + // Sets the current palette + void (*gf_set_palette)(ubyte * new_pal, int restrict_alphacolor = 0); + + // Fade the screen in/out + void (*gf_fade_in)(int instantaneous); + void (*gf_fade_out)(int instantaneous); + + // Flash the screen + void (*gf_flash)( int r, int g, int b ); + + // sets the clipping region + void (*gf_set_clip)(int x, int y, int w, int h); + + // resets the clipping region to entire screen + void (*gf_reset_clip)(); + + void (*gf_set_color)( int r, int g, int b ); + void (*gf_get_color)( int * r, int * g, int * b ); + void (*gf_init_color)( color * dst, int r, int g, int b ); + + void (*gf_init_alphacolor)( color * dst, int r, int g, int b, int alpha, int type=AC_TYPE_HUD ); + void (*gf_set_color_fast)( color * dst ); + + void (*gf_set_font)(int fontnum); + + // Sets the current bitmap + void (*gf_set_bitmap)( int bitmap_num, int alphablend=GR_ALPHABLEND_NONE, int bitbltmode=GR_BITBLT_MODE_NORMAL, float alpha=1.0f, int sx = -1, int sy = -1 ); + + // Call this to create a shader. + // This function takes a while, so don't call it once a frame! + // r,g,b, and c should be between -1.0 and 1.0f + + // The matrix is used as follows: + // Dest(r) = Src(r)*r + Src(g)*r + Src(b)*r + c; + // Dest(g) = Src(r)*g + Src(g)*g + Src(b)*g + c; + // Dest(b) = Src(r)*b + Src(g)*b + Src(b)*b + c; + // For instance, to convert to greyscale, use + // .3 .3 .3 0 + // To turn everything green, use: + // 0 .3 0 0 + void (*gf_create_shader)(shader * shade, float r, float g, float b, float c ); + + // Initialize the "shader" by calling gr_create_shader() + // Passing a NULL makes a shader that turns everything black. + void (*gf_set_shader)( shader * shade ); + + // clears entire clipping region to current color + void (*gf_clear)(); + + // void (*gf_bitmap)(int x,int y); + // void (*gf_bitmap_ex)(int x,int y,int w,int h,int sx,int sy); + + void (*gf_aabitmap)(int x, int y); + void (*gf_aabitmap_ex)(int x, int y, int w, int h, int sx, int sy); + + void (*gf_rect)(int x, int y, int w, int h); + void (*gf_shade)(int x, int y, int w, int h); + void (*gf_string)(int x, int y, char * text); + + // Draw a gradient line... x1,y1 is bright, x2,y2 is transparent. + void (*gf_gradient)(int x1, int y1, int x2, int y2); + + void (*gf_circle)(int x, int y, int r); + + // Integer line. Used to draw a fast but pixely line. + void (*gf_line)(int x1, int y1, int x2, int y2); + + // Draws an antialiased line is the current color is an + // alphacolor, otherwise just draws a fast line. This + // gets called internally by g3_draw_line. This assumes + // the vertex's are already clipped, so call g3_draw_line + // not this if you have two 3d points. + void (*gf_aaline)(vertex *v1, vertex *v2); + + void (*gf_pixel)( int x, int y ); + + // Scales current bitmap between va and vb with clipping + void (*gf_scaler)(vertex *va, vertex *vb ); + + // Scales current bitmap between va and vb with clipping, draws an aabitmap + void (*gf_aascaler)(vertex *va, vertex *vb ); + + // Texture maps the current bitmap. See TMAP_FLAG_?? defines for flag values + void (*gf_tmapper)(int nv, vertex *verts[], uint flags ); + + // dumps the current screen to a file + void (*gf_print_screen)(char * filename); + + // Call once before rendering anything. + void (*gf_start_frame)(); + + // Call after rendering is over. + void (*gf_stop_frame)(); + + // Retrieves the zbuffer mode. + int (*gf_zbuffer_get)(); + + // Sets mode. Returns previous mode. + int (*gf_zbuffer_set)(int mode); + + // Clears the zbuffer. If use_zbuffer is FALSE, then zbuffering mode is ignored and zbuffer is always off. + void (*gf_zbuffer_clear)(int use_zbuffer); + + // Saves screen. Returns an id you pass to restore and free. + int (*gf_save_screen)(); + + // Resets clip region and copies entire saved screen to the screen. + void (*gf_restore_screen)(int id); + + // Frees up a saved screen. + void (*gf_free_screen)(int id); + + // CODE FOR DUMPING FRAMES TO A FILE + // Begin frame dumping + void (*gf_dump_frame_start)( int first_frame_number, int nframes_between_dumps ); + + // Dump the current frame to file + void (*gf_dump_frame)(); + + // Dump the current frame to file + void (*gf_dump_frame_stop)(); + + // Sets the gamma + void (*gf_set_gamma)(float gamma); + + // Lock/unlock the screen + // Returns non-zero if sucessful (memory pointer) + uint (*gf_lock)(); + void (*gf_unlock)(); + + // grab a region of the screen. assumes data is large enough + void (*gf_get_region)(int front, int w, int h, ubyte *data); + + // set fog attributes + void (*gf_fog_set)(int fog_mode, int r, int g, int b, float fog_near = -1.0f, float fog_far = -1.0f); + + // get the current pixel color in the framebuffer + void (*gf_get_pixel)(int x, int y, int *r, int *g, int *b); + + // poly culling + void (*gf_set_cull)(int cull); + + // cross fade + void (*gf_cross_fade)(int bmap1, int bmap2, int x1, int y1, int x2, int y2, float pct); + + // filtering + void (*gf_filter_set)(int filter); + + // set a texture into cache. for sectioned bitmaps, pass in sx and sy to set that particular section of the bitmap + int (*gf_tcache_set)(int bitmap_id, int bitmap_type, float *u_scale, float *v_scale, int fail_on_full = 0, int sx = -1, int sy = -1, int force = 0); + + // set the color to be used when clearing the background + void (*gf_set_clear_color)(int r, int g, int b); +} screen; + +// cpu types +extern int Gr_amd3d; +extern int Gr_katmai; +extern int Gr_cpu; +extern int Gr_mmx; + +// handy macro +#define GR_MAYBE_CLEAR_RES(bmap) do { int bmw = -1; int bmh = -1; if(bmap != -1){ bm_get_info( bmap, &bmw, &bmh); if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){gr_clear();} } else {gr_clear();} } while(0); + +//Window's interface to set up graphics: +//-------------------------------------- +// Call this at application startup + +#define GR_SOFTWARE (100) // Software renderer using standard Win32 functions in a window. +#define GR_DIRECTDRAW (101) // Software renderer using DirectDraw fullscreen. +#define GR_DIRECT3D (102) // Use Direct3d hardware renderer +#define GR_GLIDE (103) // Use Glide hardware renderer +#define GR_OPENGL (104) // Use OpenGl hardware renderer + +// resolution constants - always keep resolutions in ascending order and starting from 0 +#define GR_NUM_RESOLUTIONS 2 +#define GR_640 0 // 640 x 480 +#define GR_1024 1 // 1024 x 768 + +extern int gr_init(int res, int mode, int depth = 16, int fred_x = -1, int fred_y = -1 ); + +// Call this when your app ends. +extern void gr_close(); + +extern screen gr_screen; + +#define GR_ZBUFF_NONE 0 +#define GR_ZBUFF_WRITE (1<<0) +#define GR_ZBUFF_READ (1<<1) +#define GR_ZBUFF_FULL (GR_ZBUFF_WRITE|GR_ZBUFF_READ) + +// Returns -1 if couldn't init font, otherwise returns the +// font id number. If you call this twice with the same typeface, +// it will return the same font number both times. This font is +// then set to be the current font, and default font if none is +// yet specified. +int gr_init_font( char * typeface ); + +// Does formatted printing. This calls gr_string after formatting, +// so if you don't need to format the string, then call gr_string +// directly. +extern void _cdecl gr_printf( int x, int y, char * format, ... ); + +// Returns the size of the string in pixels in w and h +extern void gr_get_string_size( int *w, int *h, char * text, int len = 9999 ); + +// Returns the height of the current font +extern int gr_get_font_height(); + +extern void gr_set_palette(char *name, ubyte *palette, int restrict_to_128 = 0); + +// These two functions use a Windows mono font. Only for use +// in the editor, please. +void gr_get_string_size_win(int *w, int *h, char *text); +void gr_string_win(int x, int y, char *s ); + +// set the mouse pointer to a specific bitmap, used for animating cursors +#define GR_CURSOR_LOCK 1 +#define GR_CURSOR_UNLOCK 2 +void gr_set_cursor_bitmap(int n, int lock = 0); +int gr_get_cursor_bitmap(); +extern int Web_cursor_bitmap; + +// Called by OS when application gets/looses focus +extern void gr_activate(int active); + +#define GR_CALL(x) (*x) + +// These macros make the function indirection look like the +// old Descent-style gr_xxx calls. + +#define gr_print_screen GR_CALL(gr_screen.gf_print_screen) + +#define gr_flip GR_CALL(gr_screen.gf_flip) +#define gr_flip_window GR_CALL(gr_screen.gf_flip_window) + +#define gr_set_clip GR_CALL(gr_screen.gf_set_clip) +#define gr_reset_clip GR_CALL(gr_screen.gf_reset_clip) +#define gr_set_font GR_CALL(gr_screen.gf_set_font) + +#define gr_init_color GR_CALL(gr_screen.gf_init_color) +#define gr_init_alphacolor GR_CALL(gr_screen.gf_init_alphacolor) +#define gr_set_color GR_CALL(gr_screen.gf_set_color) +#define gr_get_color GR_CALL(gr_screen.gf_get_color) +#define gr_set_color_fast GR_CALL(gr_screen.gf_set_color_fast) + +#define gr_set_bitmap GR_CALL(gr_screen.gf_set_bitmap) + +#define gr_create_shader GR_CALL(gr_screen.gf_create_shader) +#define gr_set_shader GR_CALL(gr_screen.gf_set_shader) +#define gr_clear GR_CALL(gr_screen.gf_clear) +// #define gr_bitmap GR_CALL(gr_screen.gf_bitmap) +// #define gr_bitmap_ex GR_CALL(gr_screen.gf_bitmap_ex) +#define gr_aabitmap GR_CALL(gr_screen.gf_aabitmap) +#define gr_aabitmap_ex GR_CALL(gr_screen.gf_aabitmap_ex) +#define gr_rect GR_CALL(gr_screen.gf_rect) +#define gr_shade GR_CALL(gr_screen.gf_shade) +#define gr_string GR_CALL(gr_screen.gf_string) + +#define gr_circle GR_CALL(gr_screen.gf_circle) + +#define gr_line GR_CALL(gr_screen.gf_line) +#define gr_aaline GR_CALL(gr_screen.gf_aaline) +#define gr_pixel GR_CALL(gr_screen.gf_pixel) +#define gr_scaler GR_CALL(gr_screen.gf_scaler) +#define gr_aascaler GR_CALL(gr_screen.gf_aascaler) +#define gr_tmapper GR_CALL(gr_screen.gf_tmapper) + +#define gr_gradient GR_CALL(gr_screen.gf_gradient) + + +#define gr_fade_in GR_CALL(gr_screen.gf_fade_in) +#define gr_fade_out GR_CALL(gr_screen.gf_fade_out) +#define gr_flash GR_CALL(gr_screen.gf_flash) + +#define gr_zbuffer_get GR_CALL(gr_screen.gf_zbuffer_get) +#define gr_zbuffer_set GR_CALL(gr_screen.gf_zbuffer_set) +#define gr_zbuffer_clear GR_CALL(gr_screen.gf_zbuffer_clear) + +#define gr_save_screen GR_CALL(gr_screen.gf_save_screen) +#define gr_restore_screen GR_CALL(gr_screen.gf_restore_screen) +#define gr_free_screen GR_CALL(gr_screen.gf_free_screen) + +#define gr_dump_frame_start GR_CALL(gr_screen.gf_dump_frame_start) +#define gr_dump_frame_stop GR_CALL(gr_screen.gf_dump_frame_stop) +#define gr_dump_frame GR_CALL(gr_screen.gf_dump_frame) + +#define gr_set_gamma GR_CALL(gr_screen.gf_set_gamma) + +#define gr_lock GR_CALL(gr_screen.gf_lock) +#define gr_unlock GR_CALL(gr_screen.gf_unlock) + +#define gr_get_region GR_CALL(gr_screen.gf_get_region) + +#define gr_fog_set GR_CALL(gr_screen.gf_fog_set) + +#define gr_get_pixel GR_CALL(gr_screen.gf_get_pixel) + +#define gr_set_cull GR_CALL(gr_screen.gf_set_cull) + +#define gr_cross_fade GR_CALL(gr_screen.gf_cross_fade) + +#define gr_filter_set GR_CALL(gr_screen.gf_filter_set) + +#define gr_tcache_set GR_CALL(gr_screen.gf_tcache_set) + +#define gr_set_clear_color GR_CALL(gr_screen.gf_set_clear_color) + +// new bitmap functions +extern int Gr_bitmap_poly; +void gr_bitmap(int x, int y); +void gr_bitmap_ex(int x, int y, int w, int h, int sx, int sy); + +// special function for drawing polylines. this function is specifically intended for +// polylines where each section is no more than 90 degrees away from a previous section. +// Moreover, it is _really_ intended for use with 45 degree angles. +void gr_pline_special(vector **pts, int num_pts, int thickness); + +#endif + diff --git a/include/3d.h b/include/3d.h new file mode 100644 index 0000000..5b1fa2e --- /dev/null +++ b/include/3d.h @@ -0,0 +1,333 @@ +/* + * $Logfile: /Freespace2/code/Render/3D.H $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for 3d rendering functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 10 9/06/99 3:23p Andsager + * Make fireball and weapon expl ani LOD choice look at resolution of the + * bitmap + * + * 9 8/27/99 9:07p Dave + * LOD explosions. Improved beam weapon accuracy. + * + * 8 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 7 7/02/99 3:05p Anoop + * Oops. Fixed g3_draw_2d_poly() so that it properly handles poly bitmap + * and LFB bitmap calls. + * + * 6 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 5 6/03/99 6:37p Dave + * More TNT fun. Made perspective bitmaps more flexible. + * + * 4 5/27/99 6:17p Dave + * Added in laser glows. + * + * 3 4/07/99 6:22p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 28 5/05/98 11:15p Lawrance + * Optimize weapon flyby sound determination + * + * 27 3/16/98 5:02p John + * Better comments + * + * 26 3/16/98 4:51p John + * Added low-level code to clip all polygons against an arbritary plane. + * Took out all old model_interp_zclip and used this new method instead. + * + * 25 1/29/98 9:46a John + * Capped debris length + * + * 24 1/06/98 6:18p John + * Made debris render as a blur. Had to make g3_draw_laser take tmap + * flags. + * + * 23 12/30/97 6:44p John + * Made g3_Draw_bitmap functions account for aspect of bitmap. + * + * 22 12/15/97 11:32a John + * New Laser Code + * + * 21 10/20/97 4:49p John + * added weapon trails. + * added facing bitmap code to 3d lib. + * + * 20 7/11/97 11:54a John + * added rotated 3d bitmaps. + * + * 19 4/29/97 12:24p Adam + * JAS: Added code for delayed point to vec. Fixed some FRED + * sequencing problems with g3_start_frame / g3_end_frame. + * + * 18 4/29/97 9:55a John + * + * 17 4/08/97 5:18p John + * First rev of decent (dynamic, correct) lighting in FreeSpace. + * + * 16 3/05/97 12:49p John + * added Viewer_obj. Took out the interp_??? variables for turning + * outline,etc on and put them in flags you pass to model_render. + * Cleaned up model_interp code to fit new coding styles. + * + * 15 2/26/97 5:24p John + * Added g3_draw_sphere_ez + * + * 14 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _3D_H +#define _3D_H + +#include "vecmat.h" +#include "tmapper.h" + +//flags for point structure +#define PF_PROJECTED 1 //has been projected, so sx,sy valid +#define PF_OVERFLOW 2 //can't project +#define PF_TEMP_POINT 4 //created during clip + +//clipping codes flags +#define CC_OFF_LEFT 1 +#define CC_OFF_RIGHT 2 +#define CC_OFF_BOT 4 +#define CC_OFF_TOP 8 +#define CC_OFF_USER 16 +#define CC_BEHIND 0x80 + +// Combo! +#define CC_OFF (CC_OFF_LEFT | CC_OFF_RIGHT | CC_OFF_BOT | CC_OFF_TOP | CC_OFF_USER | CC_BEHIND) + + +//Functions in library + +//Frame setup functions: +//start the frame. Pass non-zero to enable zbuffering +#define g3_start_frame(zbuffer_flag) g3_start_frame_func(zbuffer_flag, __FILE__, __LINE__ ) + +// use the g3_start_frame macro instead of calling this directly. +extern void g3_start_frame_func(int zbuffer_flag, char * filename, int lineno); + + +//set view from x,y,z & p,b,h, zoom. Must call one of g3_set_view_*() +void g3_set_view_angles(vector *view_pos,angles *view_orient,float zoom); + +//set view from x,y,z, viewer matrix, and zoom. Must call one of g3_set_view_*() +void g3_set_view_matrix(vector *view_pos,matrix *view_matrix,float zoom); + +// Never set these! +extern matrix View_matrix; // The matrix to convert local coordinates to screen +extern vector View_position; // The offset to convert local coordinates to screen + +extern matrix Light_matrix; // Used to rotate world points into current local coordinates +extern vector Light_base; // Used to rotate world points into current local coordinates + +extern matrix Eye_matrix; // Where the viewer's eye is pointing in World coordinates +extern vector Eye_position; // Where the viewer's eye is at in World coordinates + + +//end the frame +void g3_end_frame(void); + +//draw a horizon +void g3_draw_horizon(int sky_color,int ground_color); + +//draws a line representing the horizon +void g3_draw_horizon_line(); + +//get vectors that are edge of horizon +int g3_compute_sky_polygon(float *points_2d,vector *vecs); + +//Instancing + +//instance at specified point with specified orientation +void g3_start_instance_matrix(vector *pos,matrix *orient); + +//instance at specified point with specified orientation +void g3_start_instance_angles(vector *pos,angles *orient); + +//pops the old context +void g3_done_instance(); + +//Misc utility functions: + +//get current field of view. Fills in angle for x & y +void g3_get_FOV(float *fov_x,float *fov_y); + +//get zoom. For a given window size, return the zoom which will achieve +//the given FOV along the given axis. +float g3_get_zoom(char axis,float fov,int window_width,int window_height); + +//returns the normalized, unscaled view vectors +void g3_get_view_vectors(vector *forward,vector *up,vector *right); + +//returns true if a plane is facing the viewer. takes the unrotated surface +//normal of the plane, and a point on it. The normal need not be normalized +int g3_check_normal_facing(vector *v,vector *norm); + +//Point definition and rotation functions: + +//specify the arrays refered to by the 'pointlist' parms in the following +//functions. I'm not sure if we will keep this function, but I need +//it now. +//void g3_set_points(g3s_point *points,vms_vector *vecs); + +//returns codes_and & codes_or of a list of points numbers +ccodes g3_check_codes(int nv,vertex **pointlist); + +//rotates a point. returns codes. does not check if already rotated +ubyte g3_rotate_vertex(vertex *dest,vector *src); + +// same as above, only ignores the current instancing +ubyte g3_rotate_vertex_popped(vertex *dest,vector *src); + +//use this for stars, etc +ubyte g3_rotate_faraway_vertex(vertex *dest,vector *src); + + +//projects a point +int g3_project_vertex(vertex *point); + +//projects a vector +ubyte g3_project_vector(vector *p, float *sx, float *sy ); + +//rotates a point. returns codes. +ubyte g3_rotate_vector(vector *dest,vector *src); + +//Codes a vector. Returns the codes of a point. +ubyte g3_code_vector(vector * p); + +//calculate the depth of a point - returns the z coord of the rotated point +float g3_calc_point_depth(vector *pnt); + +//From a 2d point, compute the vector through that point +void g3_point_to_vec(vector *v,int sx,int sy); + +//From a 2d point, compute the vector through that point. +// This can be called outside of a g3_start_frame/g3_end_frame +// pair as long g3_start_frame was previously called. +void g3_point_to_vec_delayed(vector *v,int sx,int sy); + +//code a point. fills in the p3_codes field of the point, and returns the codes +ubyte g3_code_vertex(vertex *point); + +//delta rotation functions +vector *g3_rotate_delta_x(vector *dest,float dx); +vector *g3_rotate_delta_y(vector *dest,float dy); +vector *g3_rotate_delta_z(vector *dest,float dz); +vector *g3_rotate_delta_vec(vector *dest,vector *src); +ubyte g3_add_delta_vec(vertex *dest,vertex *src,vector *deltav); + +//Drawing functions: + +//draw a polygon. +//Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture. +//returns 1 if off screen, 0 if drew +int g3_draw_poly(int nv,vertex **pointlist,uint tmap_flags); + +// Draw a polygon. Same as g3_draw_poly, but it bashes sw to a constant value +// for all vertexes. Needs to be done after clipping to get them all. +//Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture. +//returns 1 if off screen, 0 if drew +int g3_draw_poly_constant_sw(int nv,vertex **pointlist,uint tmap_flags, float constant_sw); + +//like g3_draw_poly(), but checks to see if facing. If surface normal is +//NULL, this routine must compute it, which will be slow. It is better to +//pre-compute the normal, and pass it to this function. When the normal +//is passed, this function works like g3_check_normal_facing() plus +//g3_draw_poly(). +//Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture. +//returns -1 if not facing, 1 if off screen, 0 if drew +int g3_draw_poly_if_facing(int nv,vertex **pointlist,uint tmap_flags,vector *norm,vector *pnt); + +//draws a line. takes two points. +int g3_draw_line(vertex *p0,vertex *p1); + +// Draws a polygon always facing the viewer. +// compute the corners of a rod. fills in vertbuf. +// Verts has any needs uv's or l's or can be NULL if none needed. +int g3_draw_rod(vector *p0,float width1,vector *p1,float width2, vertex * verts, uint tmap_flags); + +//draws a bitmap with the specified 3d width & height +//Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture. +//returns 1 if off screen, 0 if drew +// If bitmap is not square, rad will be the 3d size of the smallest dimension. +// orient flips the bitmap in some way. Pass 0 for normal or else pass a +// random nuber between 0 and 7, inclusive. +int g3_draw_bitmap(vertex *pos,int orient, float radius, uint tmap_flags); + +// get bitmap dims onscreen as if g3_draw_bitmap() had been called +int g3_get_bitmap_dims(int bitmap, vertex *pos, float radius, int *x, int *y, int *w, int *h, int *size); + +//draw a sortof sphere - i.e., the 2d radius is proportional to the 3d +//radius, but not to the distance from the eye. Uses the current 2d color. +int g3_draw_sphere(vertex *pnt,float rad); + +// Same as g3_draw_sphere, but you pass a vector and this rotates +// and projects it and then call g3_draw_sphere. +int g3_draw_sphere_ez(vector *pnt,float rad); + +//Draw a laser shaped 3d looking thing. +// If max_len is > 1.0, then this caps the length to be no longer than max_len pixels +float g3_draw_laser(vector *headp, float head_width, vector *tailp, float tail_width, uint tmap_flags = TMAP_FLAG_TEXTURED, float max_len = 0.0f ); + +// Draw a laser shaped 3d looking thing using vertex coloring (useful for things like colored laser glows) +// If max_len is > 1.0, then this caps the length to be no longer than max_len pixels +float g3_draw_laser_rgb(vector *headp, float head_width, vector *tailp, float tail_width, int r, int g, int b, uint tmap_flags = TMAP_FLAG_TEXTURED | TMAP_FLAG_RGB, float max_len = 0.0f ); + +// Draw a bitmap that is always facing, but rotates. +// If bitmap is not square, rad will be the 3d size of the smallest dimension. +int g3_draw_rotated_bitmap(vertex *pnt,float angle, float radius, uint tmap_flags); + +// draw a perspective bitmap based on angles and radius +int g3_draw_perspective_bitmap(angles *a, float scale_x, float scale_y, int div_x, int div_y, uint tmap_flags); + +// draw a 2d bitmap on a poly +int g3_draw_2d_poly_bitmap(int x, int y, int w, int h, uint additional_tmap_flags = 0); + +// Enables clipping with an arbritary plane. This will be on +// until g3_stop_clip_plane is called or until next frame. +// The points passed should be relative to the instance. Probably +// that means world coordinates. +/* + This works like any other clip plane... if this is enabled and you +rotate a point, the CC_OFF_USER bit will be set in the clipping codes. +It is completely handled by most g3_draw primitives, except maybe lines. + + As far as performance, when enabled, it will slow down each point +rotation (or g3_code_vertex call) by a vector subtraction and dot +product. It won't slow anything down for polys that are completely +clipped on or off by the plane, and will slow each clipped polygon by +not much more than any other clipping we do. +*/ +void g3_start_user_clip_plane( vector *plane_point, vector *plane_normal ); + +// Stops arbritary plane clipping +void g3_stop_user_clip_plane(); + + + +#endif + diff --git a/include/3dfx.h b/include/3dfx.h new file mode 100644 index 0000000..c95db20 --- /dev/null +++ b/include/3dfx.h @@ -0,0 +1,112 @@ +/* +** Copyright (c) 1995, 3Dfx Interactive, Inc. +** All Rights Reserved. +** +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; +** the contents of this file may not be disclosed to third parties, copied or +** duplicated in any form, in whole or in part, without the prior written +** permission of 3Dfx Interactive, Inc. +** +** RESTRICTED RIGHTS LEGEND: +** Use, duplication or disclosure by the Government is subject to restrictions +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - +** rights reserved under the Copyright Laws of the United States. +** +** $Revision$ +** $Date$ +*/ +#ifndef __3DFX_H__ +#define __3DFX_H__ + +/* +** basic data types +*/ +typedef unsigned char FxU8; +typedef signed char FxI8; +typedef unsigned short FxU16; +typedef signed short FxI16; +typedef signed long FxI32; +typedef unsigned long FxU32; +typedef int FxBool; +typedef float FxFloat; +typedef double FxDouble; + +/* +** color types +*/ +typedef unsigned long FxColor_t; +typedef struct { float r, g, b, a; } FxColor4; + +/* +** fundamental types +*/ +#define FXTRUE 1 +#define FXFALSE 0 + +/* +** helper macros +*/ +#define FXUNUSED( a ) ( (a) = (a) ) +#define FXBIT( i ) ( 1L << (i) ) + +/* +** export macros +*/ + +#if defined(__MSC__) + #if defined (MSVC16) + #define FX_ENTRY + #define FX_CALL + #else + #define FX_ENTRY extern + #define FX_CALL __stdcall + #endif +#elif defined(__WATCOMC__) + #define FX_ENTRY extern + #define FX_CALL __stdcall +#elif defined (__IBMC__) || defined (__IBMCPP__) + /* IBM Visual Age C/C++: */ + #define FX_ENTRY extern + #define FX_CALL __stdcall +#elif defined(__DJGPP__) + #define FX_ENTRY extern + #define FX_CALL +#elif defined(__unix__) + #define FX_ENTRY extern + #define FX_CALL +#else + #warning define FX_ENTRY & FX_CALL for your compiler + #define FX_ENTRY extern + #define FX_CALL +#endif + +/* +** x86 compiler specific stuff +*/ +#if defined(__BORLANDC_) + # define REALMODE + + # define REGW( a, b ) ((a).x.b) + # define REGB( a, b ) ((a).h.b) + # define INT86( a, b, c ) int86(a,b,c) + # define INT86X( a, b, c, d ) int86x(a,b,c,d) + + # define RM_SEG( a ) FP_SEG( a ) + # define RM_OFF( a ) FP_OFF( a ) +#elif defined(__WATCOMC__) + # undef FP_SEG + # undef FP_OFF + + # define REGW( a, b ) ((a).w.b) + # define REGB( a, b ) ((a).h.b) + # define INT86( a, b, c ) int386(a,b,c) + # define INT86X( a, b, c, d ) int386x(a,b,c,d) + + # define RM_SEG( a ) ( ( ( ( FxU32 ) (a) ) & 0x000F0000 ) >> 4 ) + # define RM_OFF( a ) ( ( FxU16 ) (a) ) +#endif + +#endif /* !__3DFX_H__ */ + diff --git a/include/3dinternal.h b/include/3dinternal.h new file mode 100644 index 0000000..0a4280c --- /dev/null +++ b/include/3dinternal.h @@ -0,0 +1,67 @@ +/* + * $Logfile: /Freespace2/code/Render/3dInternal.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Used internally by the 3d renderer lib + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 5 3/16/98 4:51p John + * Added low-level code to clip all polygons against an arbritary plane. + * Took out all old model_interp_zclip and used this new method instead. + * + * 4 4/29/97 9:55a John + * + * 3 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _3DINTERNAL_H +#define _3DINTERNAL_H + +#include "3d.h" + +extern int Canvas_width,Canvas_height; //the actual width & height +extern float Canv_w2,Canv_h2; //fixed-point width,height/2 + +extern vector Window_scale; +extern int free_point_num; + +extern float View_zoom; +extern vector View_position,Matrix_scale; +extern matrix View_matrix,Unscaled_matrix; + + +//vertex buffers for polygon drawing and clipping +extern vertex *Vbuf0[]; +extern vertex *Vbuf1[]; + +extern void free_temp_point(vertex *p); +extern vertex **clip_polygon(vertex **src,vertex **dest,int *nv,ccodes *cc,uint flags); +extern void init_free_points(void); +extern void clip_line(vertex **p0,vertex **p1,ubyte codes_or, uint flags); + +extern int G3_count; + +extern int G3_user_clip; +extern vector G3_user_clip_normal; +extern vector G3_user_clip_point; + +// Returns TRUE if point is behind user plane +extern int g3_point_behind_user_plane( vector *pnt ); + +#endif + diff --git a/include/ac.h b/include/ac.h new file mode 100644 index 0000000..f59afef --- /dev/null +++ b/include/ac.h @@ -0,0 +1,31 @@ +/* + * $Logfile: /Freespace2/code/AC/ac.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for console version of anim converter + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/23/98 6:03p Dave + * + * 1 10/23/98 5:04p Dave + * + * 1 10/23/98 4:32p Dave + * + * 1 2/19/97 7:26p Lawrance + * + * $NoKeywords: $ + */ + + +#ifndef __AC_H__ +#define __AC_H__ + + +#endif /* __AC_H__ */ + diff --git a/include/acm.h b/include/acm.h new file mode 100644 index 0000000..ec24c8e --- /dev/null +++ b/include/acm.h @@ -0,0 +1,58 @@ +/* + * $Logfile: /Freespace2/code/Sound/acm.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for interface to Audio Compression Manager functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 5 2/18/98 5:49p Lawrance + * Even if the ADPCM codec is unavailable, allow game to continue. + * + * 4 11/28/97 2:09p Lawrance + * Overhaul how ADPCM conversion works... use much less memory... safer + * too. + * + * 3 11/22/97 11:32p Lawrance + * decompress ADPCM data into 8 bit (not 16bit) for regular sounds (ie not + * music) + * + * 2 5/29/97 12:03p Lawrance + * creation of file to hold AudioCompressionManager specific code + * + * $NoKeywords: $ + */ + +#ifndef __FREESPACE_ACM_H__ +#define __FREESPACE_ACM_H__ + +#include +#include +#include "pstypes.h" + +int ACM_convert_ADPCM_to_PCM(WAVEFORMATEX *pwfxSrc, ubyte *src, int src_len, ubyte **dest, int max_dest_bytes, int *dest_len, unsigned int *src_bytes_used, unsigned short dest_bps=16); +int ACM_init(); +void ACM_close(); +int ACM_is_inited(); + + +int ACM_stream_open(WAVEFORMATEX *pwfxSrc, WAVEFORMATEX *pwfxDest, void **stream, int dest_bps=16); +int ACM_stream_close(void *stream); +int ACM_query_source_size(void *stream, int dest_len); +int ACM_query_dest_size(void *stream, int src_len); + +int ACM_convert(void *stream, ubyte *src, int src_len, ubyte *dest, int max_dest_bytes, unsigned int *dest_len, unsigned int *src_bytes_used); + + +#endif /* __ACM_H__ */ + diff --git a/include/addvariabledlg.h b/include/addvariabledlg.h new file mode 100644 index 0000000..18c2238 --- /dev/null +++ b/include/addvariabledlg.h @@ -0,0 +1,59 @@ +#if !defined(AFX_ADDVARIABLEDLG_H__0F668CB5_AAEE_11D2_A899_0060088FAE88__INCLUDED_) +#define AFX_ADDVARIABLEDLG_H__0F668CB5_AAEE_11D2_A899_0060088FAE88__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// AddVariableDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CAddVariableDlg dialog + +class CAddVariableDlg : public CDialog +{ +// Construction +public: + CAddVariableDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CAddVariableDlg) + enum { IDD = IDD_ADD_VARIABLE }; + CString m_default_value; + CString m_variable_name; + bool m_name_validated; + bool m_data_validated; + bool m_type_number; + bool m_create; + int m_sexp_var_index; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAddVariableDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CAddVariableDlg) + virtual void OnOK(); + virtual BOOL OnInitDialog(); + afx_msg void validate_variable_name(int set_focus); + afx_msg void validate_data(int set_focus); + afx_msg void OnTypeNumber(); + afx_msg void OnTypeString(); + afx_msg void set_variable_type(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_ADDVARIABLEDLG_H__0F668CB5_AAEE_11D2_A899_0060088FAE88__INCLUDED_) + diff --git a/include/adjustgriddlg.h b/include/adjustgriddlg.h new file mode 100644 index 0000000..9081602 --- /dev/null +++ b/include/adjustgriddlg.h @@ -0,0 +1,73 @@ +/* + * $Logfile: /Freespace2/code/FRED2/AdjustGridDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Editor to allow one to change Fred's grid orientation and position. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 2:59p Dave + * + * 3 8/18/97 10:01p Hoffoss + * Improved dialog by graying out fields that don't have any effect on + * current plane setting. + * + * 2 8/18/97 9:31p Hoffoss + * Added grid adjustment dialog and shield system editor dialog. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// adjust_grid_dlg dialog + +class adjust_grid_dlg : public CDialog +{ +// Construction +public: + void OnOK(); + adjust_grid_dlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(adjust_grid_dlg) + enum { IDD = IDD_ADJUST_GRID }; + CSpinButtonCtrl m_spinz; + CSpinButtonCtrl m_spiny; + CSpinButtonCtrl m_spinx; + int m_x; + int m_y; + int m_z; + //}}AFX_DATA + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(adjust_grid_dlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(adjust_grid_dlg) + virtual BOOL OnInitDialog(); + afx_msg void OnXyPlane(); + afx_msg void OnXzPlane(); + afx_msg void OnYzPlane(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/afterburner.h b/include/afterburner.h new file mode 100644 index 0000000..b734e3e --- /dev/null +++ b/include/afterburner.h @@ -0,0 +1,60 @@ +/* + * $Logfile: /Freespace2/code/Ship/Afterburner.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for managing the afterburners + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 6 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 5 8/05/97 10:48a Lawrance + * save afterburner data for the player + * + * 4 7/27/97 5:14p Lawrance + * add afterburners to the player control info + * + * 3 7/16/97 4:42p Mike + * Make afterburner shake viewer, not ship. + * Shake for limited time. + * Add timestamp_until() function to timer library. + * + * 2 7/11/97 8:57a Lawrance + * make afterburner work same for player and AI ships + * + * 1 7/10/97 2:24p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __AFTERBURNER_H__ +#define __AFTERBURNER_H__ + +#define AFTERBURNER_MIN_DELAY 1000 +#define ABURN_DECAY_TIME 1500 // time in ms ship shakes after afterburner pressed + +#include "cfile.h" +#include "object.h" + +void afterburners_start(object *objp); +void afterburners_stop(object *objp, int key_released = 0); +void afterburner_stop_sounds(); +void afterburners_update(object *objp, float fl_frametime); +void afterburner_level_init(); + +#endif /* __AFTERBURNER_H__ */ + diff --git a/include/ai.h b/include/ai.h new file mode 100644 index 0000000..725f2ea --- /dev/null +++ b/include/ai.h @@ -0,0 +1,715 @@ +/* + * $Logfile: /Freespace2/code/Ship/ai.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 12 8/29/99 4:18p Andsager + * New "burst" limit for friendly damage. Also credit more damage done + * against large friendly ships. + * + * 11 8/26/99 5:14p Andsager + * + * 10 7/26/99 12:14p Andsager + * Apply cap to how much slower a transport flies with cargo. Remove + * limit on waypoint speed for training. Enemy ai get stealth exact pos + * when stealth fires + * + * 9 7/02/99 10:56a Andsager + * Put in big ship - big ship attack mode. Modify stealth sweep ai. + * + * 8 6/30/99 5:53p Dave + * Put in new anti-camper code. + * + * 7 6/23/99 5:51p Andsager + * Add waypoint-cap-speed. Checkin stealth ai - inactive. + * + * 6 6/14/99 10:45a Dave + * Made beam weapons specify accuracy by skill level in the weapons.tbl + * + * 5 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 4 4/20/99 3:40p Andsager + * Changes to big ship ai. Uses bounding box as limit where to fly to + * when flying away. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 186 6/09/98 5:15p Lawrance + * French/German localization + * + * 185 5/23/98 2:41p Mike + * Make Easy the default skill level and prevent old pilot's skill level + * from carrying into new pilot. + * + * 184 5/20/98 12:45a Mike + * Fix bug in support ships aborting rearm. Also, remove test code that + * made any ship whose name began, "Kami" a kamikaze. + * + * 183 5/18/98 1:58a Mike + * Make Phoenix not be fired at fighters (but yes bombers). + * Improve positioning of ships in guard mode. + * Make turrets on player ship not fire near end of support ship docking. + * + * 182 5/13/98 4:41p Mike + * Make big ships try a tiny bit to avoid collision with each other when + * attacking another big ship. Make ships a little less likely to collide + * into player when in formation, drop off if player flying wacky. + * + * 181 5/12/98 4:53p Mike + * Make ships recover from collisions with big ships, eventually flying + * around. + * + * 180 5/10/98 11:30p Mike + * Better firing of bombs, less likely to go into strafe mode. + * + * 179 4/12/98 2:02p Mike + * Make small ships avoid big ships. + * Turn on Collide_friendly flag. + * + * 178 4/10/98 11:02p Mike + * Make countermeasures less effective against aspect seekers than against + * heat seekers. + * Make AI ships match bank with each other when attacking a faraway + * target. + * Make ships not do silly loop-de-loop sometimes when attacking a faraway + * target. + * + * 177 4/08/98 8:38a Mike + * Make ships avoid player when attacking or guarding a faraway object. + * + * 176 4/06/98 12:46a Allender + * bump max ai goals down to 5 + * + * 175 4/01/98 9:20a Mike + * Reduce MAX_SHIPS, MAX_OBJECTS and make MAX_AI_INFO same as MAX_SHIPS + * + * 174 3/21/98 3:36p Mike + * Fix/optimize attacking of big ships. + * + * 173 3/17/98 12:50a Mike + * Improved kamikaze behavior. + * + * 172 3/16/98 8:27p Allender + * Fred support for two new AI flags -- kamikaze and no dynamic goals. + * + * 171 3/16/98 12:03a Mike + * Add support for kamikaze mode. Add AIF_NO_DYNAMIC which means + * relentlessly pursue current goal. + * + * 170 3/15/98 9:44p Lawrance + * Get secondary weapons for turrets working + * + * 169 3/13/98 8:34a Mike + * Intermediate checkin to resolve link errors. Working on detecting + * whether dock with player ship is obstructed by another ship + * + * 168 3/09/98 5:12p Mike + * Make sure Pl_objp uses are valid. + * Throw asteroids at ships in asteroid field, not "Gal" + * Support ships warp out if no goals after 30 seconds. + * + * 167 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 166 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 165 2/18/98 10:34p Allender + * repair/rearm system (for single and multi) about finished. + * dock/undock and ai goals packets implemented for multiplayer + * + * 164 2/17/98 5:03p Allender + * major cdhanges to rearm repair code. All flag and variable setting + * done in one function. A little more work to do. Fix bug in squad + * messaging when hotkey was used on invalid target + * + * 163 2/16/98 10:13p Allender + * initial work on being able to target weapons (bombs specifically). + * Work on getting rearm/repair working under multiplayer + * + * $NoKeywords: $ + */ + +#ifndef _AI_H +#define _AI_H + +#include "pstypes.h" +#include "object.h" +#include "parselo.h" +#include "cfile.h" +#include "systemvars.h" + +#define AI_DEFAULT_CLASS 3 // default AI class for new ships (Fred) + +#define AIF_FORMATION_WING (1 << 0) // Fly in formation as part of wing. +#define AIF_AWAITING_REPAIR (1 << 1) // Awaiting a repair ship. +#define AIF_BEING_REPAIRED (1 << 2) // Currently docked with repair ship. +#define AIF_REPAIRING (1 << 3) // Repairing a ship (or going to repair a ship) +#define AIF_DOCKED (1 << 4) // this object docked with something else +#define AIF_SEEK_LOCK (1 << 5) // set if should focus on gaining aspect lock, not hitting with lasers +#define AIF_FORMATION_OBJECT (1 << 6) // Fly in formation off a specific object. +#define AIF_TEMPORARY_IGNORE (1 << 7) // Means current ignore_objnum is only temporary, not an order from the player. +#define AIF_USE_EXIT_PATH (1 << 8) // Used by path code, to flag path as an exit path +#define AIF_USE_STATIC_PATH (1 << 9) // Used by path code, use fixed path, don't try to recreate +#define AIF_TARGET_COLLISION (1 << 10) // Collided with aip->target_objnum last frame. Avoid that ship for half a second or so. +#define AIF_UNLOAD_SECONDARIES (1 << 11) // Fire secondaries as fast as possible! +#define AIF_ON_SUBSYS_PATH (1 << 12) // Current path leads to a subsystem +#define AIF_AVOID_SHOCKWAVE_SHIP (1 << 13) // Avoid an existing shockwave from a ship. +#define AIF_AVOID_SHOCKWAVE_WEAPON (1 << 14) // Avoid an expected shockwave from a weapon. shockwave_object field contains object index. +#define AIF_AVOID_SHOCKWAVE_STARTED (1 << 15) // Already started avoiding shockwave, don't keep deciding whether to avoid. +#define AIF_ATTACK_SLOWLY (1 << 16) // Move slowly while attacking. +#define AIF_REPAIR_OBSTRUCTED (1 << 17) // Ship wants to be repaired, but path is obstructed. +#define AIF_KAMIKAZE (1 << 18) // Crash into target +#define AIF_NO_DYNAMIC (1 << 19) // Not allowed to get dynamic goals +#define AIF_AVOIDING_SMALL_SHIP (1 << 20) // Avoiding a player ship. +#define AIF_AVOIDING_BIG_SHIP (1 << 21) // Avoiding a large ship. +#define AIF_BIG_SHIP_COLLIDE_RECOVER_1 (1 << 22) // Collided into a big ship. Recovering by flying away. +#define AIF_BIG_SHIP_COLLIDE_RECOVER_2 (1 << 23) // Collided into a big ship. Fly towards big ship sphere perimeter. +#define AIF_STEALTH_PURSIUT (1 << 24) // Ai is trying to fight stealth ship + +#define AIF_AVOID_SHOCKWAVE (AIF_AVOID_SHOCKWAVE_SHIP | AIF_AVOID_SHOCKWAVE_WEAPON) +#define AIF_FORMATION (AIF_FORMATION_WING | AIF_FORMATION_OBJECT) + +// dock_orient_and_approach() modes. +#define DOA_APPROACH 1 // Approach the current point on the path (aip->path_cur) +#define DOA_DOCK 2 // Dock with goal object. +#define DOA_UNDOCK_1 3 // Begin undocking with goal object. Just move away. +#define DOA_UNDOCK_2 4 // Secondary undocking. Move away. +#define DOA_UNDOCK_3 5 // Tertiary undocking. Move away and orient away. +#define DOA_DOCK_STAY 6 // Rigidly maintain position in dock bay. + +// Type values for ai_dock_with_object() dock_type parameter. +#define AIDO_DOCK 1 // Set goal of docking with object. +#define AIDO_DOCK_NOW 2 // Immediately move into dock position. For ships that start mission docked. +#define AIDO_UNDOCK 3 // Set goal of undocking with object. + +#define MAX_AI_GOALS 5 + +// Submodes for seeking safety. +#define AISS_1 41 // Pick a spot to fly to. +#define AISS_2 42 // Flying to spot. +#define AISS_3 43 // Gotten near spot, fly about there. +#define AISS_1a 44 // Pick a new nearby spot because we are endangered, then go to AISS_2 + +// types of ai goals -- tyese types will help us to determination on which goals should +// have priority over others (i.e. when a player issues a goal to a wing, then a seperate +// goal to a ship in that wing). We would probably use this type in conjunction with +// goal priority to establish which goal to follow +#define AIG_TYPE_EVENT_SHIP 1 // from mission event direct to ship +#define AIG_TYPE_EVENT_WING 2 // from mission event direct to wing +#define AIG_TYPE_PLAYER_SHIP 3 // from player direct to ship +#define AIG_TYPE_PLAYER_WING 4 // from player direct to wing +#define AIG_TYPE_DYNAMIC 5 // created on the fly + +// flags for AI_GOALS +#define AIGF_DOCKER_NAME_VALID (1<<0) // when set, name field for docker is valid +#define AIGF_DOCKEE_NAME_VALID (1<<1) // when set, name field for dockee is valid +#define AIGF_GOAL_ON_HOLD (1<<2) // when set, this goal cannot currently be satisfied, although it could be in the future +#define AIGF_SUBSYS_NAME_VALID (1<<3) // when set, the subsystem name (for a destroy subsystem goal) is valid, and stored in docker.name field!! +#define AIGF_GOAL_OVERRIDE (1<<4) // paired with AIG_TYPE_DYNAMIC to mean this goal overrides any other goal +#define AIGF_PURGE (1<<5) // purge this goal next time we process +#define AIGF_GOALS_PURGED (1<<6) // this goal has already caused other goals to get purged + +// Flags to ai_turn_towards_vector(). +#define AITTV_FAST (1<<0) // Turn fast, not slowed down based on skill level. + +#define KAMIKAZE_HULL_ON_DEATH -1000.0f // Hull strength ship gets set to if it crash-dies. + +// structure for AI goals +typedef struct ai_goals { + int signature; // Unique identifier. All goals ever created (per mission) have a unique signature. + int ai_mode; // one of the AIM_* modes for this goal + int ai_submode; // maybe need a submode + int type; // one of the AIG_TYPE_* values above + int flags; // one of the AIGF_* values above + fix time; // time at which this goal was issued. + int priority; // how important is this goal -- number 0 - 100 + char *ship_name; // name of the ship that this goal acts upon + int ship_name_index; // index of ship_name in Goal_ship_names[][] + int wp_index; // index into waypoints list of waypoints that this ship might fly. + int weapon_signature; // signature of weapon this ship might be chasing. Paired with above value to get target. + + // unions for docking stuff. + union { + char *name; + int index; + } docker; + + union { + char *name; + int index; + } dockee; + +} ai_goal; + +#include "ship.h" // ai_goal must be declared before including this. + +#define MAX_AI_CLASSES 10 +#define MAX_GOAL_SHIP_NAMES 100 + +#define AIM_CHASE 0 +#define AIM_EVADE 1 +#define AIM_GET_BEHIND 2 +#define AIM_STAY_NEAR 3 // Stay near another ship. +#define AIM_STILL 4 // Sit still. Don't move. Hold your breath. Don't blink. +#define AIM_GUARD 5 // Guard an object +#define AIM_AVOID 6 // Avoid an object +#define AIM_WAYPOINTS 7 // Fly waypoints +#define AIM_DOCK 8 // Dock with ship. +#define AIM_NONE 9 // Uh, do nothing. +#define AIM_BIGSHIP 10 // Like a capital ship, doesn't focus on one ship. +#define AIM_PATH 11 // Follow path on ship +#define AIM_BE_REARMED 12 // Allow self to be rearmed +#define AIM_SAFETY 13 // Seek safety at periphery of battle +#define AIM_EVADE_WEAPON 14 // Evade a weapon. +#define AIM_STRAFE 15 // attack a big ship by strafing it +#define AIM_PLAY_DEAD 16 // Play dead. Get it? Don't move, fire, etc. +#define AIM_BAY_EMERGE 17 // Emerging from a fighter bay, following path to do so +#define AIM_BAY_DEPART 18 // Departing to a fighter bay, following path to do so +#define AIM_SENTRYGUN 19 // AI mode for sentry guns only (floating turrets) +#define AIM_WARP_OUT 20 // Commence warp out sequence. Point in legal direction. Then call John's code. + +#define MAX_AI_BEHAVIORS 21 // Number of AIM_xxxx types + +#define MAX_WAYPOINTS_PER_LIST 20 +#define MAX_WAYPOINT_LISTS 32 +#define MAX_ENEMY_DISTANCE 2500.0f // maximum distance from which a ship will pursue an enemy. + +// waypoint list flags bitmasks. +#define WL_MARKED 0x01 + +typedef struct waypoint_list { + char name[NAME_LENGTH]; + int count; + char flags[MAX_WAYPOINTS_PER_LIST]; + vector waypoints[MAX_WAYPOINTS_PER_LIST]; +} waypoint_list; + +#define AI_GOAL_NONE -1 + +#define AI_ACTIVE_GOAL_DYNAMIC 999 + +typedef struct ai_class { + char name[NAME_LENGTH]; + float ai_accuracy[NUM_SKILL_LEVELS]; + float ai_evasion[NUM_SKILL_LEVELS]; + float ai_courage[NUM_SKILL_LEVELS]; + float ai_patience[NUM_SKILL_LEVELS]; +} ai_class; + +// Submode definitions. +// Note: These need to be renamed to be of the form: AIS_mode_xxxx +#define SM_CONTINUOUS_TURN 1 // takes parm: vector_id {0..3 = right, -right, up, -up} +#define SM_ATTACK 2 +#define SM_EVADE_SQUIGGLE 3 +#define SM_EVADE_BRAKE 4 +#define SM_EVADE 5 +#define SM_SUPER_ATTACK 6 +#define SM_AVOID 7 +#define SM_GET_BEHIND 8 +#define SM_GET_AWAY 9 +#define SM_EVADE_WEAPON 10 // Evade incoming weapon +#define SM_FLY_AWAY 11 // Fly away from target_objnum +#define SM_ATTACK_FOREVER 12 // Engine subsystem destroyed, so attack, never evading, avoiding, etc. +#define SM_STEALTH_FIND 13 // Stealth ship is "targeted", but not visible, so try to find based on predicted pos +#define SM_STEALTH_SWEEP 14 // General sweep, looking for stealth after not visible for some time. +#define SM_BIG_APPROACH 15 // Big ship approaches another +#define SM_BIG_CIRCLE 16 // Big ship flies circle around other big ship to get good angle to go parallel +#define SM_BIG_PARALLEL 17 // Big ship flies parallel to another + +// Submodes for docking behavior +#define AIS_DOCK_0 21 +#define AIS_DOCK_1 22 +#define AIS_DOCK_2 23 +#define AIS_DOCK_3 24 +#define AIS_DOCK_3A 25 +#define AIS_DOCK_4 26 // Only for rearm/repair. +#define AIS_DOCK_4A 27 // Only for not rearm/repair. MK, 7/15/97 +#define AIS_UNDOCK_0 30 +#define AIS_UNDOCK_1 31 +#define AIS_UNDOCK_2 32 +#define AIS_UNDOCK_3 33 +#define AIS_UNDOCK_4 34 + +// Submodes for Guard behavior +#define AIS_GUARD_PATROL 101 +#define AIS_GUARD_ATTACK 102 +#define AIS_GUARD_2 103 +#define AIS_GUARD_STATIC 104 // maintain current relative position to guard object, if possible + +// Submodes for strafing big ships behavior (AIM_STRAFE) +#define AIS_STRAFE_ATTACK 201 // fly towards target and attack +#define AIS_STRAFE_AVOID 202 // fly evasive vector to avoid incoming fire +#define AIS_STRAFE_RETREAT1 203 // fly away from attack point +#define AIS_STRAFE_RETREAT2 204 +#define AIS_STRAFE_POSITION 205 // re-position to resume strafing attack + +#define WPF_REPEAT (1 << 0) +#define WPF_BACKTRACK (1 << 1) + +#define PD_FORWARD 1 +#define PD_BACKWARD -1 + +#define MIN_TRACKABLE_ASPECT_DOT 0.992f // dot of fvec and vec_to_enemy to progress towards aspect lock + +// Submodes for warping out. +#define AIS_WARP_1 300 // Make sure there is no obstruction to warping out. +#define AIS_WARP_2 301 +#define AIS_WARP_3 302 +#define AIS_WARP_4 303 +#define AIS_WARP_5 304 + +// A node on a path. +// Contains global location of point. +// Contains hooks back to original path information. +// This hook is used to extract information on the point such as whether it is +// protected by turrets. +typedef struct pnode { + vector pos; + int path_num; // path number from polymodel, ie in polymodel, paths[path_num] + int path_index; // index in original model path of point, ie in model_path, use verts[path_index] +} pnode; + +#define MAX_PATH_POINTS 1000 +extern pnode Path_points[MAX_PATH_POINTS]; +extern pnode *Ppfp; // Free pointer in path points. + +typedef struct ai_info { + int ai_flags; // Special flags for AI behavior. + int shipnum; // Ship using this slot, -1 means none. + int type; // + int wing; // Member of what wing? -1 means none. + + int behavior; // AI Class. Doesn't change after initial setting. + int mode; + int previous_mode; + int mode_time; // timestamp at which current mode elapses. + int target_objnum; // object index of current target. + int target_signature; // Signature of current target. + int previous_target_objnum; // On 5/19/97, only used for player. + + int stealth_last_cheat_visible_stamp; // when within 100m, always update pos and velocity, with error increasing for increasing time from last legal visible + int stealth_last_visible_stamp; + float stealth_sweep_box_size; + vector stealth_last_pos; + vector stealth_velocity; + + float previous_dot_to_enemy; // dot(fvec, vec_to_enemy) last frame + float target_time; // Amount of time continuously targeting this ship. + + int enemy_wing; // When picking an enemy wing, only allow to be in enemy_wing, unless == -1, in which case don't care. + int attacker_objnum; + int goal_objnum; // mode specific goal. In DOCK, ship to dock with. + int goal_signature; + + int guard_objnum; // Ship to guard. + int guard_signature; // Signature of ship to guard. + int guard_wingnum; // Wing to guard. guard_objnum set to leader. + + int ignore_objnum; // ship to be ignored, based on player order. UNUSED_OBJNUM if none. -(wing_num+1) if ignoring wing. + int ignore_signature; // signature of ship to be ignored + + int ai_class; // Class. Might be override of default. + + // Probably become obsolete, to be replaced by path_start, path_cur, etc. + int wp_list; // waypoint list index + int wp_index; // waypoint index in list + int wp_flags; // waypoint flags, see WPF_xxxx + int wp_dir; // 1 or -1, amount to add to get to next waypoint index. + char waypoint_speed_cap; // -1 no cap, otherwise cap + + // Path following information + int path_start; // Index into global array, start of path. + int path_cur; // Index into global array, current location in path. + int path_length; // Number of links in this path. + int path_dir; // PD_FORWARD, PD_BACKWARD + int path_flags; // loop, backtrack, whatever else. + int path_objnum; // Object of interest. It's model contains the path. + int path_goal_obj_hash; // Hash value of goal object when global path created. + fix path_next_create_time; // Next time at which we'll create a global path. + vector path_create_pos; // Object's position at time of global path creation. + matrix path_create_orient; // Object's orientation at time of global path creation. + int mp_index; // Model path index. Index in polymodel:model_paths + fix path_next_check_time; // Last time checked to see if would collide with model. + int path_goal_dist; // minimum distance to first path point to consider path reached + int path_subsystem_next_check; // timestamp to next check if subsystem is still visible + + int submode; + int previous_submode; // previous submode, get it? + float best_dot_to_enemy; // best dot product to enemy in last BEST_DOT_TIME seconds + float best_dot_from_enemy; // best dot product for enemy to player in last BEST_DOT_TIME seconds + fix best_dot_to_time; // time at which best dot occurred + fix best_dot_from_time; // time at which best dot occurred + fix submode_start_time; // time at which we entered the current submode + int submode_parm0; // parameter specific to current submode + fix next_predict_pos_time; // Next time to predict position. + + ai_goal goals[MAX_AI_GOALS]; + int active_goal; // index of active goal, -1 if none, AI_ACTIVE_GOAL_DYNAMIC if dynamic (runtime-created) goal + int goal_check_time; // timer used for processing goals for this ai object + + vector last_predicted_enemy_pos; // Where he thought enemy was last time. + float time_enemy_in_range; // Amount of time enemy continuously in "sight", near crosshair. + fix last_attack_time; // Missiontime of last time this ship attacked its enemy. + fix last_hit_time; // Missiontime of last time this ship was hit by anyone. + int last_hit_quadrant; // Shield section of last hit. + fix last_hit_target_time; // Missiontime of last time this ship successfully hit target. + int hitter_objnum; // Object index of ship that hit this ship last time. + int hitter_signature; // Signature of hitter. Prevents stupidity if hitter gets killed. + fix resume_goal_time; // Time at which to resume interrupted goal, if nothing else intervenes. + float prev_accel; // Acceleration last frame. + float prev_dot_to_goal; // dot of fvec to goal last frame, used to see if making progress towards goal. + vector goal_point; // Used in AIM_SAFETY, AIM_STILL and in circling. + vector prev_goal_point; // Previous location of goal point, used at least for evading. + float ai_accuracy, ai_evasion, ai_courage, ai_patience; + union { + float lead_scale; // Amount to lead current opponent by. + float stay_near_distance; // Distance to stay within for AIM_STAY_NEAR mode. + }; + + ship_subsys* targeted_subsys; // Targeted subobject on current target. NULL if none; + ship_subsys* last_subsys_target; // last known subsystem target + int targeted_subsys_parent; // Parent objnum of subobject, not necessarily targeted + + float aspect_locked_time; // Time towards acquiring lock for current_target + +// ship_subsys *targeted_subobject; // subsystem to attack +// int attack_subsystem_parent; // objnum of the object containing the attack_subsystem + int dock_index; // index of docking point to use when docking. + int dockee_index; // index of dock point on other ship. + int dock_path_index; // index of docking path to use when docking. + int dock_objnum; // objnum of ship we are docked with. + int dock_signature; // Signature of repair object. + int danger_weapon_objnum; // Closest objnum of weapon fired at this ship. + int danger_weapon_signature; // Signature of object danger_weapon_objnum. + + vector guard_vec; // vector to object being guarded, only used in AIS_GUARD_STATIC submode + int nearest_locked_object; // Nearest locked object. + float nearest_locked_distance; // Distance to nearest locked object. + + float current_target_distance; // Distance of current target from player + int current_target_is_locked; // Flag to indicate whether the current target is locked for missile fire + int current_target_dist_trend; // Tracks whether distance to target is increasing or decreasing + int current_target_speed_trend; // Tracks whether speed of target is increasing or decreasing + + float last_dist; // last frame's distance between player and target + float last_speed; // last frame's target speed + int last_secondary_index; // needed for secondary weapon change check + int last_target; + + int rearm_first_missile; // flag to show that reloading of missilies hasn't begun yet + int rearm_release_delay; // timestamp used to delay separation of ships after rearm complete + + fix afterburner_stop_time; // Missiontime to turn off afterburner + int last_objsig_hit; // The object number signature of the ship last hit by this ship + int ignore_expire_timestamp; // Timestamp at which temporary ignore (AIF_TEMPORARY_IGNORE) expires. + int warp_out_timestamp; // Timestamp at which this ship is to warp out. + int next_rearm_request_timestamp; // Timestamp at which ship might next request rearm. + int primary_select_timestamp; // When to next select a primary weapon. + int secondary_select_timestamp; // When to next select a secondary weapon. + + int scan_for_enemy_timestamp; // When to next look for enemy fighters if sitting still while pounding + // on a bigship. SCAN_FIGHTERS_INTERVAL is defined in AiBig.h + int choose_enemy_timestamp; // Time at which it is next legal to choose a new enemy (does not apply + // to special situations, like getting hit by a weapon) + int force_warp_time; // time at which to give up avoiding a ship and just warp out + + int shockwave_object; // Object index of missile that will generate a shockwave. We will try to avoid. + + int shield_manage_timestamp; // Time at which to next manage shield. + int self_destruct_timestamp; // Time at which to self-destruct, probably due to being disabled. + int ok_to_target_timestamp; // Time at which this ship can dynamically target. + + float kamikaze_damage; // some damage value used to produce a shockwave from a kamikaze ship + vector big_attack_point; // Global point this ship is attacking on a big ship. + vector big_attack_surface_normal; // Surface normal at ship at big_attack_point; + int pick_big_attack_point_timestamp; // timestamp at which to pick a new point to attack on a big ship. + + // Note: These three avoid_XX terms are shared between the code that avoids small (only player now) and large ships + // The bits in ai_flags determine which is occurring. AIF_AVOID_SMALL_SHIP, AIF_AVOID_BIG_SHIP + int avoid_ship_num; // object index of small ship to avoid + vector avoid_goal_point; // point to aim at when avoiding a ship + fix avoid_check_timestamp; // timestamp at which to next check for having to avoid ship + + vector big_collision_normal; // Global normal of collision with big ship. Helps find direction to fly away from big ship. Set for each collision. + vector big_recover_pos_1; // Global point to fly towards when recovering from collision with a big ship, stage 1. + vector big_recover_pos_2; // Global point to fly towards when recovering from collision with a big ship, stage 2. + int big_recover_timestamp; // timestamp at which it's OK to re-enter stage 1. + + int abort_rearm_timestamp; // time at which this rearm should be aborted in a multiplayer game. + + // artillery targeting info + int artillery_objnum; // object currently being targeted for artillery lock/attack + int artillery_sig; // artillery object signature + float artillery_lock_time; // how long we've been locked onto this guy + vector artillery_lock_pos; // base position of the lock point on (in model's frame of reference) + float lethality; // measure of how dangerous ship is to enemy BIG|HUGE ships (likelyhood of targeting) +} ai_info; + +#define MAX_AI_INFO MAX_SHIPS + +// SUBSYS_PATH_DIST is used as the distance that a subsystem path should terminate from the actual +// subsystem. We don't want to rely on the model path points for this, since we may need to be +// changed. +#define SUBSYS_PATH_DIST 500.0f + +// Friendly damage defines +#define MAX_BURST_DAMAGE 20 // max damage that can be done in BURST_DURATION +#define BURST_DURATION 500 // decay time over which Player->damage_this_burst falls from MAX_BURST_DAMAGE to 0 + +extern int Mission_all_attack; // !0 means all teams attack all teams. +extern int Total_goal_ship_names; +extern char Goal_ship_names[MAX_GOAL_SHIP_NAMES][NAME_LENGTH]; + +extern void update_ai_info_for_hit(int hitter_obj, int hit_obj); +extern void ai_frame_all(void); + +extern int find_guard_obj(void); + +extern ai_info Ai_info[]; +extern ai_info *Player_ai; + +extern int Waypoints_created; // externed since needed for save/restore + +extern ai_class Ai_classes[]; +extern char *Ai_class_names[]; + +extern int Num_ai_classes; +extern int Ai_firing_enabled; + +extern char *Skill_level_names(int skill_level, int translate = 1); +extern int Skill_level_max_attackers[NUM_SKILL_LEVELS]; +extern int Ai_goal_signature; + +// need access to following data in AiBig.cpp +extern object *Pl_objp; +extern object *En_objp; +extern float AI_frametime; + + +// Return index of free AI slot. +// Return 0 if no free slot. +int ai_get_slot(int shipnum); + +// Releases an AI slot to be used by someone else. +void ai_free_slot(int ai_index); + +// call to init one ai object.. you can pass all sorts of +// stuff by adding new paramters. +void ai_object_init(object * obj, int ai_index); + +// Called once a frame +void ai_process( object * obj, int ai_index, float frametime ); + +int get_wingnum(int objnum); + +void set_wingnum(int objnum, int wingnum); +char *ai_get_goal_ship_name(char *name, int *index); + +extern waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS]; +extern int Num_waypoint_lists; + +extern void init_ai_system(void); +extern void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp); +extern void ai_evade_object(object *evader, object *evaded, int priority); +extern void ai_ignore_object(object *ignorer, object *ignored, int priority); +extern void ai_ignore_wing(object *ignorer, int wingnum, int priority); +extern void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index); +extern void ai_stay_still(object *still_objp, vector *view_pos); +extern void ai_set_default_behavior(object *obj, int classnum); +extern void ai_do_default_behavior(object *obj); +extern void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags); +extern void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal); +extern void ai_ship_destroy(int shipnum, int method); +extern void ai_turn_towards_vector(vector *dest, object *objp, float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec = NULL); +extern void init_ai_object(int objnum); +extern void ai_init(void); // Call this one to parse ai.tbl. +extern void ai_level_init(void); // Call before each level to reset AI + +extern int ai_set_attack_subsystem(object *objp, int subnum); +extern int ai_issue_rearm_request(object *requester_objp); // Object requests rearm/repair. +extern int ai_abort_rearm_request(object *requester_objp); // Object aborts rearm/repair. +extern void ai_do_repair_frame(object *objp, ai_info *aip, float frametime); // Repair a ship object, player or AI. +extern float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode); // Move to a position relative to a dock bay using thrusters. +extern void ai_update_danger_weapon(int objnum, int weapon_objnum); + +// called externally from MissionParse.cpp to position ships in wings upon arrival into the +// mission. +extern void get_absolute_wing_pos( vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag); + + +// Interface from goals code to AI. Set ship to guard. *objp guards *other_objp +extern void ai_set_guard_object(object *objp, object *other_objp); +extern void ai_set_evade_object(object *objp, object *other_objp); +extern void ai_set_guard_wing(object *objp, int wingnum); +extern void ai_warp_out(object *objp, vector *vp); +extern void ai_attack_wing(object *attacker, int wingnum, int priority); +extern void ai_deathroll_start(object *ship_obj); +extern void ai_fly_in_formation(int wing_num); // Force wing to fly in formation. +extern void ai_disband_formation(int wing_num); // Force wing to disband formation flying. +extern object *ai_find_docked_object( object *objp ); // returns object that objp is docked to +extern int set_target_objnum(ai_info *aip, int objnum); +extern void ai_form_on_wing(object *objp, object *goal_objp); +extern void ai_do_stay_near(object *objp, object *other_obj, float dist); +extern ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum); +extern void ai_rearm_repair( object *objp, object *goal_objp, int priority, int docker_index, int dockee_index ); +extern void ai_add_rearm_goal( object *requester_objp, object *support_objp ); +extern void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path=0); + +// used to get path info for fighter bay emerging and departing +int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec); +int ai_acquire_depart_path(object *pl_objp, int parent_objnum); + +// used by AiBig.cpp +extern void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos); +extern void accelerate_ship(ai_info *aip, float accel); +extern void turn_away_from_point(object *objp, vector *point, float bank_override); +extern float ai_endangered_by_weapon(ai_info *aip); +extern void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius); +extern void ai_chase_ct(); +extern void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path=0); +extern float ai_path(); +extern void evade_weapon(); +extern int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration); +extern void ai_fire_primary_weapon(object *objp); +extern int ai_fire_secondary_weapon(object *objp, int priority1 = -1, int priority2 = -1); +extern float ai_get_weapon_dist(ship_weapon *swp); +extern void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override); +extern int ai_maybe_fire_afterburner(object *objp, ai_info *aip); +extern void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip); + +extern int is_instructor(object *objp); +extern int find_enemy(int objnum, float range, int max_attackers); + +float ai_get_weapon_speed(ship_weapon *swp); +void set_predicted_enemy_pos_turret(vector *predicted_enemy_pos, vector *gun_pos, object *pobjp, vector *enemy_pos, vector *enemy_vel, float weapon_speed, float time_enemy_in_range); +void ai_turret_select_default_weapon(ship_subsys *turret); + +// function to change rearm status for ai ships (called from sexpression code) +extern void ai_set_rearm_status( int team, int new_status ); +extern void ai_good_secondary_time( int team, int weapon_index, int num_weapons, char *shipname ); + +extern void ai_do_objects_docked_stuff( object *docker, object *dockee ); +extern void ai_do_objects_undocked_stuff( object *docker, object *dockee ); +extern void ai_do_objects_repairing_stuff( object *repaired_obj, object *repair_obj, int how ); + +extern int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold); + +void ai_set_mode_warp_out(object *objp, ai_info *aip); + +#endif + diff --git a/include/aibig.h b/include/aibig.h new file mode 100644 index 0000000..6f42bcb --- /dev/null +++ b/include/aibig.h @@ -0,0 +1,78 @@ +/* + * $Logfile: /Freespace2/code/Ship/AiBig.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for AI code related to large ships + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 13 4/29/98 5:01p Mike + * Large overhaul in how turrets fire. + * + * 12 4/27/98 11:59p Mike + * Intermediate checkin. Getting big ship turrets firing at big ships to + * use pick_big_attack_point. + * + * 11 3/21/98 3:36p Mike + * Fix/optimize attacking of big ships. + * + * 10 1/29/98 1:39p Mike + * Better heat seeking homing on big ships. + * + * 9 1/22/98 5:14p Lawrance + * clean up ai_big code, clear path info when stop attacking a subsystem + * + * 8 1/06/98 6:58p Lawrance + * Attack turrets (sometimes) when fired upon while attacking a ship. + * + * 7 12/15/97 7:16p Lawrance + * improving subsystem attacking + * + * 6 12/01/97 5:11p Lawrance + * make strafe mode more effective... slow down when approaching and use + * afterburner in avoids + * + * 5 10/30/97 9:17p Lawrance + * work on getting AIM_STRAFE working well with disable/disarm, try to + * balance + * + * 4 10/30/97 12:32a Lawrance + * further work on AIM_STRAFE + * + * 3 10/29/97 6:24p Lawrance + * extern ai_strafe() + * + * 2 10/26/97 3:24p Lawrance + * split off large ship ai code into AiBig.cpp + * + * $NoKeywords: $ + */ + + +#ifndef __AIBIG_H__ +#define __AIBIG_H__ + +void ai_big_ship(object *objp); +void ai_big_chase(); +void ai_big_subsys_path_cleanup(ai_info *aip); + +// strafe functions +void ai_big_strafe(); +int ai_big_maybe_enter_strafe_mode(object *objp, int weapon_objnum, int consider_target_only=0); +void ai_big_strafe_maybe_attack_turret(object *ship_objp, object *weapon_objp); +void ai_big_pick_attack_point(object *objp, object *attacker_objp, vector *attack_point, float fov=1.0f); +void ai_big_pick_attack_point_turret(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, vector *attack_point, float weapon_travel_dist, float fov=1.0f); + + +#endif + diff --git a/include/aigoals.h b/include/aigoals.h new file mode 100644 index 0000000..cedf27b --- /dev/null +++ b/include/aigoals.h @@ -0,0 +1,226 @@ +/* + * $Logfile: /Freespace2/code/Ship/AiGoals.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 4 3/26/99 4:49p Dave + * Made cruisers able to dock with stuff. Made docking points and paths + * visible in fred. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 46 2/26/98 10:08p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 45 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 44 2/16/98 10:13p Allender + * initial work on being able to target weapons (bombs specifically). + * Work on getting rearm/repair working under multiplayer + * + * 43 2/13/98 3:21p Lawrance + * Set up allowed goals for escape pods. + * + * 42 1/30/98 10:01a Allender + * made large ships able to attack other ships. Made goal code recognize + * when ships removed from wings during ship select + * + * 41 12/15/97 5:25p Allender + * fix problem with docked ships receiving another dock goal. They now + * properly undock + * + * 40 11/17/97 6:39p Lawrance + * add AI_goal_text[] array, used by HUD code to show text description of + * order + * + * 39 10/29/97 10:02p Allender + * be sure that player starting wings that arrive late also form on the + * players wing if there are no goals + * + * 38 10/23/97 4:41p Allender + * lots of new rearm/repair code. Rearm requests now queue as goals for + * support ship. Warp in of new support ships functional. Support for + * stay-still and play-dead. + * + * 37 10/22/97 1:44p Allender + * more work on stay-still and play-dead + * + * 36 10/22/97 11:07a Allender + * Hooked in form on my wing into Mikes AI code + * + * 35 10/22/97 10:33a Hoffoss + * Added AI_GOAL_PLAY_DEAD as a valid goal for all ships. + * + * 34 10/22/97 9:54a Allender + * added ai_goal_play_dead + * + * 33 10/10/97 5:03p Allender + * started work on ai-stay-still + * + * 32 9/23/97 4:35p Allender + * added two function to add "internal" goals to ships and wings. Used by + * AI when it needs to do something special + * + * 31 8/15/97 11:07a Hoffoss + * Added warning to help eliminate problems later on down the road. + * + * 30 8/15/97 9:59a Hoffoss + * Changed ai_query_goal_valid() again. I can't think of any time you + * wouldn't find it easier just to pass in a ship instead of a ship_info + * flag structure filtered of all flags that aren't ship type bits. Since + * I'll call this quite a few times, it will make my life easier. + * + * 29 8/14/97 10:00p Allender + * made ai goals bitfields so that we can have set of orders that certain + * ship types are allowed to receive. Added function for Hoffoss to check + * goal vs. ship type validity + * + * 28 8/12/97 1:55a Hoffoss + * Made extensive changes to object reference checking and handling for + * object deletion call. + * + * 27 8/08/97 1:30p Allender + * added commands (and ai goals) for support ships. stay near ship (which + * could be player or current target), and keep safe distance + * + * 26 8/06/97 9:41a Allender + * renamed from ai_goal function. made a function to remove a goal from a + * wing (used strictly for waypoints now). ai-chase-wing and + * ai-guard-wing now appear to user as ai-chase and ai-guard. Tidied up + * function which deals with sexpression goals + * + * 25 8/06/97 8:06a Lawrance + * add more stuff to save/restore + * + * 24 7/24/97 4:55p Allender + * added ai-evade-ship to fred and to Freespace + * + * 23 7/17/97 4:23p Allender + * ai-guard-wing now in both Fred and Freespace + * + * 22 7/15/97 11:57a Hoffoss + * Added a general ai goal reference update mode. + * + * 21 6/18/97 11:46a Hoffoss + * Fixed initial order object reference updating and added briefing dialog + * window tracking data. + * + * $NoKeywords: $ + */ + +#ifndef _AIGOALS_H +#define _AIGOALS_H + +#include "ai.h" +#include "cfile.h" + +// macros for goals which get set via sexpressions in the mission code + +// IMPORTANT! If you add a new AI_GOAL_x define, be sure to update the functions +// ai_update_goal_references() and query_referenced_in_ai_goals() or else risk breaking +// Fred. If the goal you add doesn't have a target (such as chase_any), then you don't have +// to worry about doing this. Also add it to list in Fred\Management.cpp, and let Hoffoss know! +#define AI_GOAL_CHASE (1<<1) +#define AI_GOAL_DOCK (1<<2) // used for undocking as well +#define AI_GOAL_WAYPOINTS (1<<3) +#define AI_GOAL_WAYPOINTS_ONCE (1<<4) +#define AI_GOAL_WARP (1<<5) +#define AI_GOAL_DESTROY_SUBSYSTEM (1<<6) +#define AI_GOAL_FORM_ON_WING (1<<7) +#define AI_GOAL_UNDOCK (1<<8) +#define AI_GOAL_CHASE_WING (1<<9) +#define AI_GOAL_GUARD (1<<10) +#define AI_GOAL_DISABLE_SHIP (1<<11) +#define AI_GOAL_DISARM_SHIP (1<<12) +#define AI_GOAL_CHASE_ANY (1<<13) +#define AI_GOAL_IGNORE (1<<14) +#define AI_GOAL_GUARD_WING (1<<15) +#define AI_GOAL_EVADE_SHIP (1<<16) + +// the next goals are for support ships only +#define AI_GOAL_STAY_NEAR_SHIP (1<<17) +#define AI_GOAL_KEEP_SAFE_DISTANCE (1<<18) +#define AI_GOAL_REARM_REPAIR (1<<19) + +#define AI_GOAL_STAY_STILL (1<<20) +#define AI_GOAL_PLAY_DEAD (1<<21) +#define AI_GOAL_CHASE_WEAPON (1<<22) + +// now the masks for ship types + +#define AI_GOAL_ACCEPT_FIGHTER ( AI_GOAL_CHASE | AI_GOAL_WAYPOINTS | AI_GOAL_WAYPOINTS_ONCE | AI_GOAL_WARP | AI_GOAL_DESTROY_SUBSYSTEM | AI_GOAL_CHASE_WING | AI_GOAL_GUARD | AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP | AI_GOAL_CHASE_ANY | AI_GOAL_IGNORE | AI_GOAL_GUARD_WING | AI_GOAL_EVADE_SHIP | AI_GOAL_STAY_STILL | AI_GOAL_PLAY_DEAD) +#define AI_GOAL_ACCEPT_BOMBER ( AI_GOAL_ACCEPT_FIGHTER ) +#define AI_GOAL_ACCEPT_STEALTH ( AI_GOAL_ACCEPT_FIGHTER ) +#define AI_GOAL_ACCEPT_TRANSPORT ( AI_GOAL_CHASE | AI_GOAL_CHASE_WING | AI_GOAL_DOCK | AI_GOAL_WAYPOINTS | AI_GOAL_WAYPOINTS_ONCE | AI_GOAL_WARP | AI_GOAL_UNDOCK | AI_GOAL_STAY_STILL | AI_GOAL_PLAY_DEAD) +#define AI_GOAL_ACCEPT_FREIGHTER ( AI_GOAL_ACCEPT_TRANSPORT ) +#define AI_GOAL_ACCEPT_CRUISER ( AI_GOAL_ACCEPT_FREIGHTER ) +#define AI_GOAL_ACCEPT_CORVETTE ( AI_GOAL_ACCEPT_CRUISER ) +#define AI_GOAL_ACCEPT_GAS_MINER ( AI_GOAL_ACCEPT_CRUISER ) +#define AI_GOAL_ACCEPT_AWACS ( AI_GOAL_ACCEPT_CRUISER ) +#define AI_GOAL_ACCEPT_CAPITAL ( AI_GOAL_ACCEPT_CRUISER & ~(AI_GOAL_DOCK | AI_GOAL_UNDOCK) ) +#define AI_GOAL_ACCEPT_SUPERCAP ( AI_GOAL_ACCEPT_CAPITAL ) +#define AI_GOAL_ACCEPT_SUPPORT ( AI_GOAL_DOCK | AI_GOAL_UNDOCK | AI_GOAL_WAYPOINTS | AI_GOAL_WAYPOINTS_ONCE | AI_GOAL_STAY_NEAR_SHIP | AI_GOAL_KEEP_SAFE_DISTANCE | AI_GOAL_STAY_STILL | AI_GOAL_PLAY_DEAD) +#define AI_GOAL_ACCEPT_ESCAPEPOD ( AI_GOAL_ACCEPT_TRANSPORT ) + +#define MAX_AI_DOCK_NAMES 25 + +extern int Num_ai_dock_names; +extern char Ai_dock_names[MAX_AI_DOCK_NAMES][NAME_LENGTH]; + +extern char *Ai_goal_text(int goal); + +// extern function definitions +extern void ai_post_process_mission(); +extern void ai_maybe_add_form_goal( wing *wingp ); +extern void ai_process_mission_orders( int objnum, ai_info *aip ); + +// adds goals to ships/wing through sexpressions +extern void ai_add_ship_goal_sexp( int sexp, int type, ai_info *aip ); +extern void ai_add_wing_goal_sexp( int sexp, int type, int wingnum ); +extern void ai_add_goal_sub_sexp( int sexp, int type, ai_goal *aigp ); + +// adds goals to ships/sings through player orders +extern void ai_add_ship_goal_player( int type, int mode, int submode, char *shipname, ai_info *aip ); +extern void ai_add_wing_goal_player( int type, int mode, int submode, char *shipname, int wingnum ); + +extern void ai_remove_ship_goal( ai_info *aip, int index ); +extern void ai_clear_ship_goals( ai_info *aip ); +extern void ai_clear_wing_goals( int wingnum ); + +extern void ai_copy_mission_wing_goal( ai_goal *aigp, ai_info *aip ); + +extern void ai_mission_goal_complete( ai_info *aip ); +extern void ai_mission_wing_goal_complete( int wingnum, ai_goal *remove_goalp ); + +extern int ai_get_subsystem_type( char *subsystem ); +extern char *ai_get_subsystem_type_name(int type); +extern void ai_update_goal_references(ai_goal *goals, int type, char *old_name, char *new_name); +extern int query_referenced_in_ai_goals(ai_goal *goals, int type, char *name); +extern char *ai_add_dock_name(char *str); + +extern int ai_query_goal_valid( int ship, int ai_goal ); + +extern void ai_add_goal_ship_internal( ai_info *aip, int goal_type, char *name, int docker_point, int dockee_point, int immediate = 1 ); +extern void ai_add_goal_wing_internal( wing *wingp, int goal_type, char *name, int immediate = 1 ); + +#endif + diff --git a/include/ailocal.h b/include/ailocal.h new file mode 100644 index 0000000..89e3882 --- /dev/null +++ b/include/ailocal.h @@ -0,0 +1,8 @@ +#ifndef _AILOCAL_H +#define _AILOCAL_H + +// -- Don't put anything in here -- +// Put it in ai.h! + +#endif + diff --git a/include/alphacolors.h b/include/alphacolors.h new file mode 100644 index 0000000..10e83ba --- /dev/null +++ b/include/alphacolors.h @@ -0,0 +1,50 @@ +#ifndef _GLOBAL_ALPHACOLORS_HEADER_FILE +#define _GLOBAL_ALPHACOLORS_HEADER_FILE + +// ----------------------------------------------------------------------------------- +// ALPHA DEFINES/VARS +// + +// Colors for UI +// See Freespace.cpp for usage + +// The following colors are for text drawing: +// normal text +#define Color_text_normal Color_white +// text highlighted while down still down on a line +#define Color_text_subselected Color_blue +// text highlighted as the selected line +#define Color_text_selected Color_bright_blue +// text that indicates an error +#define Color_text_error Color_red +// text that indicates an error, and line is selected or subselected +#define Color_text_error_hi Color_bright_red +// text that indicates line is active item +#define Color_text_active Color_bright_white +// text that indicates line is active item, and line is selected or subselected +#define Color_text_active_hi Color_bright_white +// text drawn as a heading for a section or title, etc +#define Color_text_heading Color_violet_gray + +#define Color_bright Color_bright_blue +#define Color_normal Color_white +extern color Color_black, Color_grey, Color_blue, Color_bright_blue, Color_violet_gray; +extern color Color_green, Color_bright_green, Color_bright_white, Color_white; +extern color Color_red, Color_bright_red, Color_yellow, Color_bright_yellow, Color_dim_red; + +extern color Color_ui_light_green, Color_ui_green; +extern color Color_ui_light_pink, Color_ui_pink; + +// netplayer colors +extern color *Color_netplayer[12]; + +// ----------------------------------------------------------------------------------- +// ALPHA FUNCTIONS +// + +// initialize all alpha colors (call at startup) +void alpha_colors_init(); + + +#endif + diff --git a/include/animplay.h b/include/animplay.h new file mode 100644 index 0000000..8a0b2d2 --- /dev/null +++ b/include/animplay.h @@ -0,0 +1,142 @@ +/* + * $Logfile: /Freespace2/code/Anim/AnimPlay.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for playing back anim files + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 3 10/22/98 6:14p Dave + * Optimized some #includes in Anim folder. Put in the beginnings of + * parse/localization support for externalized strings and tstrings.tbl + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 11 5/07/98 3:11a Lawrance + * Implement custom streaming code + * + * 10 4/27/98 3:36p Dave + * + * 9 3/25/98 8:43p Hoffoss + * Changed anim_play() to not be so damn complex when you try and call it. + * + * 8 12/24/97 8:57p Lawrance + * Added anim_ignore_next_frametime() + * + * 7 11/19/97 8:28p Dave + * Hooked in Main Hall screen. Put in Anim support for ping ponging + * animations as well as general reversal of anim direction. + * + * 6 8/30/97 2:11p Lawrance + * allow animations to loop + * + * 5 8/25/97 11:13p Lawrance + * support framerate independent playback with the option of now advancing + * more than one frame at a time + * + * 4 7/21/97 11:41a Lawrance + * make playback time of .ani files keyed of frametime + * + * 3 7/20/97 6:57p Lawrance + * supporting new RLE format + * + * 2 6/26/97 12:12a Lawrance + * supporting anti-aliased bitmap animations + * + * 1 6/23/97 5:09p Lawrance + * + * 10 5/19/97 2:28p Lawrance + * changes some variables to flags + * + * 9 5/15/97 5:58p Lawrance + * fix some bugs that were present with animations when playing multiple + * missions + * + * 8 5/15/97 4:42p Lawrance + * suporting anims in-game + * + * 7 5/15/97 11:46a Lawrance + * add function to check if an anim is playing + * + * 6 2/28/97 12:17p Lawrance + * supporting mapping file to memory + * + * 5 2/25/97 11:06a Lawrance + * moved some higher level functions to from PackUnpack to AnimPlay + * + * 4 2/19/97 9:27p Lawrance + * added pause capability to anim playback + * + * 3 2/17/97 4:19p Lawrance + * using frame numbers instead of percentages for accessing keyframes + * + * 2 2/17/97 3:01p Lawrance + * code for playing back an anim + * + * $NoKeywords: $ + */ + +#ifndef __ANIMPLAY_H__ +#define __ANIMPLAY_H__ + +#include "pstypes.h" + +struct anim; +struct anim_info; +struct anim_instance; +struct CFILE; + +// structure passed in when playing an anim. Talk about overkill.. +typedef struct { + anim *anim_info; + int x; + int y; + int start_at; + int stop_at; + int screen_id; + vector *world_pos; + float radius; + int framerate_independent; + void *color; + int skip_frames; + int looped; + int ping_pong; +} anim_play_struct; + +extern int Anim_paused; + +void anim_init(); +void anim_level_init(); +void anim_level_close(); +void anim_render_all(int screen_id, float frametime); +void anim_render_one(int screen_id, anim_instance *ani, float frametime); +void anim_play_init(anim_play_struct *aps, anim *a_info, int x, int y); +anim_instance *anim_play(anim_play_struct *aps); +void anim_ignore_next_frametime(); +int anim_stop_playing(anim_instance* anim_instance); +int anim_show_next_frame(anim_instance *instance, float frametime); +void anim_release_all_instances(int screen_id = 0); +void anim_release_render_instance(anim_instance* instance); +anim *anim_load(char *name, int file_mapped = 0); +int anim_free(anim *ptr); +int anim_playing(anim_instance *ai); +int anim_write_frames_out(char *filename); +void anim_display_info(char *filename); +void anim_read_header(anim *ptr, CFILE *fp); +void anim_reverse_direction(anim_instance *ai); // called automatically for ping-ponging, and can also be called externally +void anim_pause(anim_instance *ai); +void anim_unpause(anim_instance *ai); + +int anim_instance_is_streamed(anim_instance *ai); +unsigned char anim_instance_get_byte(anim_instance *ai, int offset); + +#endif /* __ANIMPLAY_H__ */ + diff --git a/include/asteroid.h b/include/asteroid.h new file mode 100644 index 0000000..1bddcb2 --- /dev/null +++ b/include/asteroid.h @@ -0,0 +1,253 @@ +/* + * $Logfile: /Freespace2/code/Asteroid/Asteroid.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for asteroids + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 12 6/09/99 2:55p Andsager + * Allow multiple asteroid subtypes (of large, medium, small) and follow + * family. + * + * 11 6/07/99 1:18p Andsager + * Make asteroids choose consistent texture from large to small. Modify + * fireball radius of dying asteroids. + * + * 10 5/03/99 10:50p Andsager + * Make Asteroid_obj_list. Change get_nearest_turret_objnum() to use + * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs. + * obj_used_list. + * + * 9 4/16/99 2:34p Andsager + * Second pass on debris fields + * + * 8 4/15/99 5:00p Andsager + * Frist pass on Debris field + * + * 7 3/31/99 9:51a Andsager + * Add for generalization to debris field + * + * 6 2/07/99 8:51p Andsager + * Add inner bound to asteroid field. Inner bound tries to stay astroid + * free. Wrap when within and don't throw at ships inside. + * + * 5 1/20/99 6:04p Dave + * Another bit of stuff for beam weapons. Ships will properly use them + * now, although they're really deadly. + * + * 4 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 3 10/16/98 1:22p Andsager + * clean up header files + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 17 3/26/98 9:19a Lawrance + * Support multiple asteroid pofs + * + * 16 3/17/98 12:16a Allender + * asteroids in multiplayer -- minor problems with position being correct + * + * 15 3/14/98 1:44p Mike + * Todolist items 3365..3368. Make child asteroids collide properly. + * Make asteroid throwing less bunchy, toss asteroids earlier, make + * facing-ness not break mission balance. + * + * 14 3/11/98 12:13a Lawrance + * Auto-target asteroids if no hostile ships present + * + * 13 3/08/98 4:16p Hoffoss + * + * 12 3/07/98 3:48p Lawrance + * Show offscreen indicators for asteroids + * + * 11 3/04/98 11:59p Lawrance + * create an asteroid.tbl, read all asteroid data from there + * + * 10 3/04/98 4:11p Lawrance + * Have area effects affect asteroids, have asteroids cast an area effect, + * fix ship shockwaves + * + * 9 3/03/98 12:48a Lawrance + * Ensure collide_objnum is still valid each frame. + * + * 8 3/02/98 11:35p Lawrance + * Keep track of asteroids that will impact ships on the escort view. + * + * 7 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 6 2/26/98 4:22p Lawrance + * Change wrapping behavior, make vel random + * + * 5 2/20/98 8:31p Lawrance + * delay creation of sub-asteroids + * + * 4 2/19/98 4:33p Lawrance + * hook in new sounds and custom explosion animation + * + * 3 2/19/98 12:46a Lawrance + * Further work on asteroids. + * + * 2 2/10/98 6:43p Lawrance + * Moved asteroid code to a separate lib. + * + * 1 2/10/98 6:05p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __ASTEROID_H__ +#define __ASTEROID_H__ + +#include "ship.h" +#include "parselo.h" // for NAME_LENGTH + +struct object; +struct polymodel; +struct collision_info_struct; + +#define MAX_ASTEROIDS 256 + +// DEBRIS TYPES +#define MAX_DEBRIS_TYPES 12 +#define ASTEROID_TYPE_SMALL 0 +#define ASTEROID_TYPE_MEDIUM 1 +#define ASTEROID_TYPE_BIG 2 +// +#define DEBRIS_TERRAN_SMALL 3 +#define DEBRIS_TERRAN_MEDIUM 4 +#define DEBRIS_TERRAN_LARGE 5 +// +#define DEBRIS_VASUDAN_SMALL 6 +#define DEBRIS_VASUDAN_MEDIUM 7 +#define DEBRIS_VASUDAN_LARGE 8 +// +#define DEBRIS_SHIVAN_SMALL 9 +#define DEBRIS_SHIVAN_MEDIUM 10 +#define DEBRIS_SHIVAN_LARGE 11 +// END DEBRIS TYPES + +typedef struct debris_struct { + int index; + char *name; +} debris_struct; + +// Data structure to track the active asteroids +typedef struct asteroid_obj { + asteroid_obj *next, *prev; + int flags, objnum; +} asteroid_obj; +extern asteroid_obj Asteroid_obj_list; + + +extern debris_struct Field_debris_info[]; + +#define MAX_ASTEROID_POFS 3 // Max number of POFs per asteroid type + +#define AF_USED (1<<0) // Set means used. + +typedef struct asteroid_info { + char name[NAME_LENGTH]; // name for the asteroid + char pof_files[MAX_ASTEROID_POFS][NAME_LENGTH]; // POF files to load/associate with ship + int num_detail_levels; // number of detail levels for this ship + int detail_distance[MAX_SHIP_DETAIL_LEVELS]; // distance to change detail levels at + float max_speed; // cap on speed for asteroid + float inner_rad; // radius within which maximum area effect damage is applied + float outer_rad; // radius at which no area effect damage is applied + float damage; // maximum damage applied from area effect explosion + float blast; // maximum blast impulse from area effect explosion + float initial_hull_strength; // starting strength of asteroid + polymodel *modelp[MAX_ASTEROID_POFS]; + int model_num[MAX_ASTEROID_POFS]; +} asteroid_info; + +typedef struct asteroid { + int flags; + int objnum; + int type; // In 0..Num_asteroid_types + int asteroid_subtype; // Which index into asteroid_info for modelnum and modelp + int check_for_wrap; // timestamp to check for asteroid wrapping around field + int check_for_collide; // timestamp to check for asteroid colliding with escort ships + int final_death_time; // timestamp to swap in new models after explosion starts + int collide_objnum; // set to objnum that asteroid will be impacting soon + int collide_objsig; // object signature corresponding to collide_objnum + vector death_hit_pos; // hit pos that caused death + int target_objnum; // Yes, hah! Asteroids can have targets. See asteroid_aim_at_target(). +} asteroid; + +// TYPEDEF FOR SPECIES OF DEBRIS - BITFIELD +#define DS_TERRAN 0x01 +#define DS_VASUDAN 0x02 +#define DS_SHIVAN 0x04 + +// TYPEDEF FOR DEBRIS TYPE +typedef enum { + DG_ASTEROID, + DG_SHIP +} debris_genre_t; + +// TYPEDEF FOR FIELD TYPE +typedef enum { + FT_ACTIVE, + FT_PASSIVE +} field_type_t; + +#define MAX_ACTIVE_DEBRIS_TYPES 3 + +typedef struct asteroid_field { + vector min_bound; // Minimum range of field. + vector max_bound; // Maximum range of field. + int has_inner_bound; + vector inner_min_bound; + vector inner_max_bound; + vector vel; // Average asteroid moves at this velocity. + float speed; // Average speed of field + int num_initial_asteroids; // Number of asteroids at creation. + field_type_t field_type; // active throws and wraps, passive does not + debris_genre_t debris_genre; // type of debris (ship or asteroid) [generic type] + int field_debris_type[MAX_ACTIVE_DEBRIS_TYPES]; // one of the debris type defines above +} asteroid_field; + +extern asteroid_info Asteroid_info[MAX_DEBRIS_TYPES]; +extern asteroid Asteroids[MAX_ASTEROIDS]; +extern asteroid_field Asteroid_field; + +extern int Num_asteroid_types; +extern int Num_asteroids; +extern int Asteroids_enabled; + +void asteroid_init(); +void asteroid_level_init(); +void asteroid_level_close(); +void asteroid_create_all(); +void asteroid_render( object *asteroid_objp ); +void asteroid_delete( object *asteroid_objp ); +void asteroid_process_pre( object *asteroid_objp, float frame_time); +void asteroid_process_post( object *asteroid_objp, float frame_time); +int asteroid_check_collision( object *asteroid_objp, object * other_obj, vector * hitpos, collision_info_struct *asteroid_hit_info=NULL ); +void asteroid_hit( object *asteroid_objp, object *other_objp, vector *hitpos, float damage ); +int asteroid_count(); +int asteroid_collide_objnum(object *asteroid_objp); +float asteroid_time_to_impact(object *asteroid_objp); +void asteroid_show_brackets(); +void asteroid_target_closest_danger(); +int asteroid_get_random_in_cone(vector *pos, vector *dir, float ang, int danger = 0); + +// need to extern for multiplayer +void asteroid_sub_create(object *parent_objp, int asteroid_type, vector *relvec); + +void asteroid_frame(); + +#endif // __ASTEROID_H__ + diff --git a/include/asteroideditordlg.h b/include/asteroideditordlg.h new file mode 100644 index 0000000..6437096 --- /dev/null +++ b/include/asteroideditordlg.h @@ -0,0 +1,77 @@ +// AsteroidEditorDlg.h : header file +// + +#include "starfield.h" +#include "asteroid.h" + + +///////////////////////////////////////////////////////////////////////////// +// asteroid_editor dialog + +class asteroid_editor : public CDialog +{ +// Construction +public: + void update_init(); + int query_modified(); + void OnCancel(); + void OnOK(); + void OnEnableField(); + int validate_data(); + + asteroid_editor(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(asteroid_editor) + enum { IDD = IDD_ASTEROID_EDITOR }; + CSpinButtonCtrl m_density_spin; + int m_avg_speed; + int m_density; + BOOL m_enable_asteroids; + CString m_max_x; + CString m_max_y; + CString m_max_z; + CString m_min_x; + CString m_min_y; + CString m_min_z; + BOOL m_enable_inner_bounds; + field_type m_field_type; // active or passive + debris_genre m_debris_genre; // ship or asteroid + int m_field_debris_type[3]; // species and size of ship debris + CString m_box_max_x; + CString m_box_max_y; + CString m_box_max_z; + CString m_box_min_x; + CString m_box_min_y; + CString m_box_min_z; + //}}AFX_DATA + + int cur_field, last_field; + asteroid_field a_field[1 /*MAX_ASTEROID_FIELDS*/]; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(asteroid_editor) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(asteroid_editor) + virtual BOOL OnInitDialog(); + afx_msg void OnInitMenu(CMenu* pMenu); + afx_msg void OnEnableAsteroids(); + afx_msg void OnClose(); + afx_msg void OnEnableInnerBox(); + afx_msg void OnPassiveField(); + afx_msg void OnFieldShip(); + afx_msg void OnActiveField(); + afx_msg void OnFieldAsteroid(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/audiostr.h b/include/audiostr.h new file mode 100644 index 0000000..16df4c5 --- /dev/null +++ b/include/audiostr.h @@ -0,0 +1,140 @@ +/* + * $Logfile: /Freespace2/code/Sound/AudioStr.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to stream large WAV files from disk + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 9 5/24/98 4:42p Dan + * AL: Fix several bugs related to pausing and enabling/disabling event + * music + * + * 8 3/31/98 4:50p Dan + * AL: Clean up all audio streams if necessary in + * event_music_level_close() + * + * 7 12/10/97 10:04p Lawrance + * modify what happens in Audio_stream constructor + * + * 6 12/09/97 6:14p Lawrance + * add -nomusic flag + * + * 5 11/20/97 1:06a Lawrance + * Add Master_voice_volume, make voices play back at correctly scaled + * volumes + * + * 4 10/03/97 8:24a Lawrance + * When unpausing, be sure to retain looping status + * + * 3 9/18/97 10:31p Lawrance + * add functions to pause and unpause all audio streams + * + * 2 6/04/97 1:19p Lawrance + * added function to check if system is initialized + * + * 1 4/28/97 4:45p John + * Initial version of ripping sound & movie out of OsAPI. + * + * 8 4/14/97 1:52p Lawrance + * making transitions happen on measure boundries + * + * 7 4/09/97 11:14a Lawrance + * working on event music transitions + * + * 6 4/07/97 3:15p Lawrance + * allowing event music to pause + * + * 5 4/03/97 4:27p Lawrance + * expanding functionality to support event driven music + * + * 4 4/01/97 1:31p Lawrance + * make music fade quickly out when stopping. Delay onset of new music to + * allow old music to fade out. + * + * 3 3/31/97 5:45p Lawrance + * supporting changes to allow multiple streamed audio files + * + * 2 3/31/97 3:56p Lawrance + * decompress ADPCM->PCM for streaming sounds working + * + * 1 1/22/97 10:43a John + * + * $NoKeywords: $ + */ + +#ifndef _AUDIOSTR_H +#define _AUDIOSTR_H + +// type of audio stream +#define ASF_SOUNDFX 0 +#define ASF_EVENTMUSIC 1 +#define ASF_VOICE 2 +#define ASF_NONE 3 // used to catch errors + + +// Initializes the audio streaming library. Called +// automatically when the sound stuff is inited. +void audiostream_init(); + +// Closes down the audio streaming library +void audiostream_close(); + +// Opens a wave file but doesn't play it. +int audiostream_open( char * filename, int type ); + +// Closes the opened wave file. This doesn't have to be +// called between songs, because when you open the next +// song, it will call this internally. +void audiostream_close_file(int i, int fade = 1); + +void audiostream_close_all(int fade); + +// Plays the currently opened wave file +void audiostream_play(int i, float volume = -1.0f, int looping = 1); + +// See if a particular stream is playing +int audiostream_is_playing(int i); + +// Stops the currently opened wave file +void audiostream_stop(int i, int rewind = 1, int paused = 0); + +// set the volume for every audio stream of a particular type +void audiostream_set_volume_all(float volume, int type); + +// set the volume for a particular audio stream +void audiostream_set_volume(int i, float volume); + +// see if a particular stream is paused +int audiostream_is_paused(int i); + +// set the number of bytes that the sound should cutoff after +void audiostream_set_byte_cutoff(int i, unsigned int cutoff); + +// return the number of bytes streamed to the Direct Sound buffer so far +unsigned int audiostream_get_bytes_committed(int i); + +// check if the streaming has read all the bytes from disk yet +int audiostream_done_reading(int i); + +// return if audiostream has initialized ok +int audiostream_is_inited(); + +void audiostream_pause(int i); // pause a particular stream +void audiostream_pause_all(); // pause all audio streams + +void audiostream_unpause(int i); // unpause a particular stream +void audiostream_unpause_all(); // unpause all audio streams + +#endif // _AUDIOSTR_H + diff --git a/include/awacs.h b/include/awacs.h new file mode 100644 index 0000000..1cebf6e --- /dev/null +++ b/include/awacs.h @@ -0,0 +1,65 @@ +/* + * $Logfile: /Freespace2/code/Ship/AWACS.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * all sorts of cool stuff about ships + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 6/02/99 12:52p Andsager + * Added team-wide ship visibility. Implemented for player. + * + * 3 1/25/99 5:03a Dave + * First run of stealth, AWACS and TAG missile support. New mission type + * :) + * + * 2 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * + * $NoKeywords: $ + */ + +#ifndef __FS2_AWACS_HEADER_FILE +#define __FS2_AWACS_HEADER_FILE + +// ---------------------------------------------------------------------------------------------------- +// AWACS DEFINES/VARS +// + +// DAVE'S OFFICIAL DEFINITION OF AWACS + +// total awacs levels for all teams +extern float Awacs_team[MAX_TEAMS]; // total AWACS capabilities for each team +extern float Awacs_level; // Awacs_friendly - Awacs_hostile + +// ---------------------------------------------------------------------------------------------------- +// AWACS FUNCTIONS +// + +// call when initializing level, before parsing mission +void awacs_level_init(); + +// call every frame to process AWACS details +void awacs_process(); + +// get the total AWACS level for target to viewer +// < 0.0f : untargetable +// 0.0 - 1.0f : marginally targetable +// 1.0f : fully targetable as normal +float awacs_get_level(object *target, ship *viewer, int use_awacs=1); + +// Determine if ship is visible by team +// return 1 if ship is fully visible +// return 0 if ship is only partly visible +int ship_is_visible_by_team(int ship_num, int team); + + +#endif + diff --git a/include/barracks.h b/include/barracks.h new file mode 100644 index 0000000..23b2e59 --- /dev/null +++ b/include/barracks.h @@ -0,0 +1,33 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/Barracks.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * C source file for implementing barracks + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 2/02/99 11:58a Neilk + * added vss revision/log comments + * + * $NoKeywords: $ + */ + +#ifndef _BARRACKS_H +#define _BARRACKS_H + +// initialize the barracks +void barracks_init(); + +// do a frame for the barrracks +void barracks_do_frame(float frametime); + +// close the barracks +void barracks_close(); + +#endif // _BARRACKS_H + diff --git a/include/beam.h b/include/beam.h new file mode 100644 index 0000000..bc1a4bc --- /dev/null +++ b/include/beam.h @@ -0,0 +1,231 @@ +/* + * $Logfile: /Freespace2/code/Weapon/Beam.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * all sorts of cool stuff about ships + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 21 9/08/99 10:29p Dave + * Make beam sound pausing and unpausing much safer. + * + * 20 6/29/99 7:39p Dave + * Lots of small bug fixes. + * + * 19 6/21/99 7:25p Dave + * netplayer pain packet. Added type E unmoving beams. + * + * 18 6/18/99 5:16p Dave + * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD + * dialog to PXO screen. + * + * 17 6/04/99 2:16p Dave + * Put in shrink effect for beam weapons. + * + * 16 5/14/99 11:47a Andsager + * Added beam_get_weapon_info_index(object *bm) + * + * 15 5/08/99 8:25p Dave + * Upped object pairs. First run of nebula lightning. + * + * 14 4/22/99 11:06p Dave + * Final pass at beam weapons. Solidified a lot of stuff. All that remains + * now is to tweak and fix bugs as they come up. No new beam weapon + * features. + * + * 13 4/21/99 6:15p Dave + * Did some serious housecleaning in the beam code. Made it ready to go + * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added + * a handy macro for recalculating collision pairs for a given object. + * + * 12 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 11 4/19/99 11:01p Dave + * More sophisticated targeting laser support. Temporary checkin. + * + * 10 4/16/99 5:54p Dave + * Support for on/off style "stream" weapons. Real early support for + * target-painting lasers. + * + * 9 3/08/99 7:03p Dave + * First run of new object update system. Looks very promising. + * + * 8 3/04/99 6:09p Dave + * Added in sexpressions for firing beams and checking for if a ship is + * tagged. + * + * 7 1/30/99 1:29a Dave + * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot + * screen. Fixed beam weapon death messages. + * + * 6 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 5 1/21/99 10:45a Dave + * More beam weapon stuff. Put in warmdown time. + * + * 4 1/14/99 12:48a Dave + * Todo list bug fixes. Made a pass at putting briefing icons back into + * FRED. Sort of works :( + * + * 3 1/12/99 12:53a Dave + * More work on beam weapons - made collision detection very efficient - + * collide against all object types properly - made 3 movement types + * smooth. Put in test code to check for possible non-darkening pixels on + * object textures. + * + * 2 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * + * $NoKeywords: $ + */ + +#ifndef __FS2_BEAM_WEAPON_HEADER_FILE +#define __FS2_BEAM_WEAPON_HEADER_FILE + +#include "model.h" + +// ------------------------------------------------------------------------------------------------ +// BEAM WEAPON DEFINES/VARS +// + +// prototypes +struct object; +struct ship_subsys; +struct obj_pair; +struct beam_weapon_info; +struct vector; + +// beam types +// REMINDER : if you change the behavior of any of these beam types, make sure to update their "cones" of possible +// movement inside of the function beam_get_cone_dot(...) in beam.cpp Otherwise it could cause collisions to not +// function properly!!!!!! +#define BEAM_TYPE_A 0 // unidirectional beam +#define BEAM_TYPE_B 1 // "slash" in one direction +#define BEAM_TYPE_C 2 // targeting lasers (only lasts one frame) +#define BEAM_TYPE_D 3 // similar to the type A beams, but takes multiple shots and "chases" fighters around +#define BEAM_TYPE_E 4 // stupid beam. like type A, only it doesn't aim. it just shoots directly out of the turret + +// max # of "shots" an individual beam will take +#define MAX_BEAM_SHOTS 5 + +// uses to define beam behavior ahead of time - needed for multiplayer +typedef struct beam_info { + vector dir_a, dir_b; // direction vectors for beams + float delta_ang; // angle between dir_a and dir_b + ubyte shot_count; // # of shots + float shot_aim[MAX_BEAM_SHOTS]; // accuracy. this is a constant multiple of radius. anything < 1.0 will guarantee a hit +} beam_info; + +// pass to beam fire +typedef struct beam_fire_info { + int beam_info_index; // weapon info index + object *shooter; // whos shooting + vector targeting_laser_offset; // offset from the center of the object (for targeting lasers only) + ship_subsys *turret; // where he's shooting from + float accuracy; // 0.0 to 1.0 (only really effects targeting on small ships) + object *target; // whos getting shot + ship_subsys *target_subsys; // (optional), specific subsystem to be targeted on the target + beam_info *beam_info_override; // (optional), pass this in to override all beam movement info (for multiplayer) + int num_shots; // (optional), only used for type D weapons +} beam_fire_info; + +// collision info +typedef struct beam_collision { + mc_info cinfo; // collision info + int c_objnum; // objnum of the guy we recently collided with + int c_sig; // object sig + int c_stamp; // when we should next apply damage +} beam_collision; + +// beam lighting effects +extern int Beam_lighting; + +// ------------------------------------------------------------------------------------------------ +// BEAM WEAPON FUNCTIONS +// + +// --------------- +// the next functions are probably the only ones anyone should care about calling. the rest require somewhat detailed knowledge of beam weapons + +// fire a beam, returns objnum on success. the innards of the code handle all the rest, foo +int beam_fire(beam_fire_info *fire_info); + +// fire a targeting beam, returns objnum on success. a much much simplified version of a beam weapon +// targeting lasers last _one_ frame. For a continuous stream - they must be created every frame. +// this allows it to work smoothly in multiplayer (detect "trigger down". every frame just create a targeting laser firing straight out of the +// object. this way you get all the advantages of nice rendering and collisions). +// NOTE : only references beam_info_index and shooter +int beam_fire_targeting(beam_fire_info *fire_info); + +// return an object index of the guy who's firing this beam +int beam_get_parent(object *bm); + +// return weapon_info_index of beam +int beam_get_weapon_info_index(object *bm); + +// render the beam itself +void beam_render(beam_weapon_info *bwi, vector *start, vector *shot, float shrink = 1.0f); + +// given a beam object, get the # of collisions which happened during the last collision check (typically, last frame) +int beam_get_num_collisions(int objnum); + +// stuff collision info, returns 1 on success +int beam_get_collision(int objnum, int num, int *collision_objnum, mc_info **cinfo); +// --------------- + +// init at game startup +void beam_init(); + +// initialize beam weapons for this level +void beam_level_init(); + +// shutdown beam weapons for this level +void beam_level_close(); + +// collide a beam with a ship, returns 1 if we can ignore all future collisions between the 2 objects +int beam_collide_ship(obj_pair *pair); + +// collide a beam with an asteroid, returns 1 if we can ignore all future collisions between the 2 objects +int beam_collide_asteroid(obj_pair *pair); + +// collide a beam with a missile, returns 1 if we can ignore all future collisions between the 2 objects +int beam_collide_missile(obj_pair *pair); + +// collide a beam with debris, returns 1 if we can ignore all future collisions between the 2 objects +int beam_collide_debris(obj_pair *pair); + +// pre-move (before collision checking - but AFTER ALL OTHER OBJECTS HAVE BEEN MOVED) +void beam_move_all_pre(); + +// post-collision time processing for beams +void beam_move_all_post(); + +// render all beam weapons +void beam_render_all(); + +// early-out function for when adding object collision pairs, return 1 if the pair should be ignored +int beam_collide_early_out(object *a, object *b); + +// pause all looping beam sounds +void beam_pause_sounds(); + +// unpause looping beam sounds +void beam_unpause_sounds(); + +// debug code +void beam_test(int whee); +void beam_test_new(int whee); + +#endif + diff --git a/include/bgbitmapdlg.h b/include/bgbitmapdlg.h new file mode 100644 index 0000000..69bf269 --- /dev/null +++ b/include/bgbitmapdlg.h @@ -0,0 +1,185 @@ +/* + * $Logfile: /Freespace2/code/Fred2/BgBitmapDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Background space images manager dialog + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 8 7/02/99 4:30p Dave + * Much more sophisticated lightning support. + * + * 7 6/03/99 6:37p Dave + * More TNT fun. Made perspective bitmaps more flexible. + * + * 6 4/26/99 8:47p Dave + * Made all pof related nebula stuff customizable through Fred. + * + * 5 4/07/99 6:21p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 4 1/25/99 5:03a Dave + * First run of stealth, AWACS and TAG missile support. New mission type + * :) + * + * 3 11/14/98 5:37p Dave + * Put in support for full nebulas. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 2:59p Dave + * + * 12 4/13/98 10:25p Hoffoss + * Added a flag for subspace missions, and for aboard the Galatea or + * Bastion. + * + * 11 12/08/97 4:48p Hoffoss + * Moved starfield editor controls to background editor. + * + * 10 11/25/97 11:40a Hoffoss + * Added support for nebula placement editing. + * + * 9 11/21/97 2:55p Hoffoss + * Added Nebula support to Fred. Implemented loading and saving nebula + * info to/from mission files. + * + * 8 3/31/97 6:07p Hoffoss + * Fixed several errors, including BG editor not graying fields, BG editor + * not updating image when changed, Removed obsolete data from Weapon + * editor, priority not being saved when missions saved, priority not + * editable in initial orders editor. + * + * 7 3/27/97 2:24p Hoffoss + * Fixed bug in image not updating when new image selected from listbox of + * combo box. + * + * 6 3/21/97 4:24p Hoffoss + * Fixed bug in changing image to an external image file. + * + * 5 3/17/97 1:54p Hoffoss + * fixed bugs in BG editor, and added delete button functionality. + * + * 4 3/12/97 4:33p Hoffoss + * added spin controls to orient editor, light intensity level can be + * specified in BG editor. + * + * 3 2/04/97 3:09p Hoffoss + * Background bitmap editor implemented fully. + * + * 2 1/30/97 2:24p Hoffoss + * Added remaining mission file structures and implemented load/save of + * them. + * + * $NoKeywords: $ + */ + +#ifndef _BG_BITMAP_DLG_H +#define _BG_BITMAP_DLG_H + +///////////////////////////////////////////////////////////////////////////// +// bg_bitmap_dlg dialog + +class bg_bitmap_dlg : public CDialog +{ +// Construction +public: + void update_data(int update = 1); + void create(); + + // sun data functions + void sun_data_init(); + void sun_data_close(); + void sun_data_save_current(); + + // bitmap data functions + void bitmap_data_init(); + void bitmap_data_close(); + void bitmap_data_save_current(); + + bg_bitmap_dlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(bg_bitmap_dlg) + enum { IDD = IDD_BG_BITMAP }; + CString m_neb_intensity; + int m_nebula_color; + int m_nebula_index; + int m_bank; + int m_heading; + int m_pitch; + CSliderCtrl m_slider; + int m_neb2_texture; + BOOL m_subspace; + BOOL m_fullneb; + int m_poof[6]; + + // lightning storm data + CString m_storm_name; + + // sun data + CString s_name; + int s_pitch, s_bank, s_heading; + float s_scale; + int s_index; + + // bitmap data + CString b_name; + int b_pitch, b_bank, b_heading; + float b_scale_x, b_scale_y; + int b_div_x, b_div_y; + int b_index; + + //}}AFX_DATA + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(bg_bitmap_dlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // clear and build the nebula filename list appropriately + void build_nebfile_list(); + + // Generated message map functions + //{{AFX_MSG(bg_bitmap_dlg) + afx_msg void OnClose(); + afx_msg void OnSelchangeNebcolor(); + afx_msg void OnSelchangeNebpattern(); + afx_msg void OnFullNeb(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + + // handlers for sun stuff + afx_msg void OnSunChange(); + afx_msg void OnAddSun(); + afx_msg void OnDelSun(); + afx_msg void OnSunDropdownChange(); + + // handlers for bitmap stuff + afx_msg void OnBitmapChange(); + afx_msg void OnAddBitmap(); + afx_msg void OnDelBitmap(); + afx_msg void OnBitmapDropdownChange(); + + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: +}; + +#endif + diff --git a/include/bitblt.h b/include/bitblt.h new file mode 100644 index 0000000..03e1973 --- /dev/null +++ b/include/bitblt.h @@ -0,0 +1,37 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Bitblt.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for software bitblt type stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 2 4/14/98 12:15p John + * Made 16-bpp movies work. + * + * 1 3/25/98 8:07p John + * Split software renderer into Win32 and DirectX + * + * $NoKeywords: $ + */ + +#ifndef _BITBLT_H +#define _BITBLT_H + +void grx_bitmap(int x,int y); +void grx_bitmap_ex(int x,int y,int w,int h,int sx,int sy); +void grx_aabitmap(int x,int y); +void grx_aabitmap_ex(int x,int y,int w,int h,int sx,int sy); + +#endif //_BITBLT_H + diff --git a/include/bmpman.h b/include/bmpman.h new file mode 100644 index 0000000..ef50c05 --- /dev/null +++ b/include/bmpman.h @@ -0,0 +1,348 @@ +/* + * $Logfile: /Freespace2/code/Bmpman/BmpMan.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Prototypes for Bitmap Manager functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 16 8/10/99 6:54p Dave + * Mad optimizations. Added paging to the nebula effect. + * + * 15 8/06/99 1:52p Dave + * Bumped up MAX_BITMAPS for the demo. + * + * 14 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 13 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 12 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 11 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 10 5/05/99 9:02p Dave + * Fixed D3D aabitmap rendering. Spiffed up nebula effect a bit (added + * rotations, tweaked values, made bitmap selection more random). Fixed + * D3D beam weapon clipping problem. Added D3d frame dumping. + * + * 9 2/08/99 5:07p Dave + * FS2 chat server support. FS2 specific validated missions. + * + * 8 2/05/99 12:52p Dave + * Fixed Glide nondarkening textures. + * + * 7 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 6 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 5 12/01/98 4:46p Dave + * Put in targa bitmap support (16 bit). + * + * 4 12/01/98 8:06a Dave + * Temporary checkin to fix some texture transparency problems in d3d. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 32 4/16/98 6:31p Hoffoss + * Added function to get filename of a bitmap handle, which we don't have + * yet and I need. + * + * 31 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 30 3/30/98 4:02p John + * Made machines with < 32 MB of RAM use every other frame of certain + * bitmaps. Put in code to ke7ep track of how much RAM we've malloc'd. + * + * 29 3/29/98 4:05p John + * New paging code that loads everything necessary at level startup. + * + * 28 3/26/98 5:21p John + * Added new code to preload all bitmaps at the start of a level. + * Commented it out, though. + * + * 27 3/24/98 6:18p John + * Hacked MAX_BITMAPS up to 3500 + * + * 26 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 25 3/02/98 6:46p John + * Upped MAX_BITMAPS to 2000 + * + * 24 3/02/98 6:00p John + * Moved MAX_BITMAPS into BmpMan.h so the stuff in the graphics code that + * is dependent on it won't break if it changes. Made ModelCache slots + * be equal to MAX_OBJECTS which is what it is. + * + * 23 2/06/98 8:25p John + * Added code for new bitmaps since last frame + * + * 22 2/06/98 8:10p John + * Added code to show amout of texture usage each frame. + * + * 21 1/29/98 11:48a John + * Added new counter measure rendering as model code. Made weapons be + * able to have impact explosion. + * + * 20 1/11/98 2:14p John + * Changed a lot of stuff that had to do with bitmap loading. Made cfile + * not do callbacks, I put that in global code. Made only bitmaps that + * need to load for a level load. + * + * 19 9/03/97 4:19p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 18 8/25/97 11:14p Lawrance + * added support for .ani files in bm_load_animation() + * + * 17 7/16/97 3:07p John + * + * 16 6/17/97 8:58p Lawrance + * fixed bug with not nulling bm.data with USER bitmaps + * + * 15 6/12/97 2:44a Lawrance + * changed bm_unlock() to take an index into bm_bitmaps(). Added + * ref_count to bitmap_entry struct + * + * 14 5/20/97 10:36a John + * Fixed problem with user bitmaps and direct3d caching. + * + * 13 3/24/97 3:25p John + * Cleaned up and restructured model_collide code and fvi code. In fvi + * made code that finds uvs work.. Added bm_get_pixel to BmpMan. + * + * 12 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _BMPMAN_H +#define _BMPMAN_H + +#include "pstypes.h" + +#ifdef FS2_DEMO + #define MAX_BITMAPS 3500 +#else + #define MAX_BITMAPS 3500 // How many bitmaps the game can handle +#endif + +// 16 bit pixel formats +#define BM_PIXEL_FORMAT_ARGB 0 // for glide - can assume certain things, like 1555 LFB writes, whee! +#define BM_PIXEL_FORMAT_D3D 1 // d3d - card dependant. booo! +#define BM_PIXEL_FORMAT_ARGB_D3D 2 // this card has nice 1555 textures like Glide - ahhhhh! + +// 16 bit pixel formats +extern int Bm_pixel_format; + +#define BYTES_PER_PIXEL(x) ((x+7)/8) + +// how many bytes of textures are used. +extern int bm_texture_ram; + +// This loads a bitmap so we can draw with it later. +// It returns a negative number if it couldn't load +// the bitmap. On success, it returns the bitmap +// number. +int bm_load(char * filename); + +// special load function. basically allows you to load a bitmap which already exists (by filename). +// this is useful because in some cases we need to have a bitmap which is locked in screen format +// _and_ texture format, such as pilot pics and squad logos +int bm_load_duplicate(char *filename); + +// Creates a bitmap that exists in RAM somewhere, instead +// of coming from a disk file. You pass in a pointer to a +// block of data. The data can be in the following formats: +// 8 bpp (mapped into game palette) +// 32 bpp +// On success, it returns the bitmap number. You cannot +// free that RAM until bm_release is called on that bitmap. +// See example at bottom of this file +int bm_create( int bpp, int w, int h, void * data, int flags = 0); + +// Frees up a bitmap's data, but bitmap number 'n' can +// still be used, it will just have to be paged in next +// time it is locked. +int bm_unload( int n ); + +// Frees up a bitmap's data, and it's slot, so bitmap +// number 'n' cannot be used anymore, and bm_load or +// bm_create might reuse the slot. +void bm_release(int n); + +// This loads a bitmap sequence so we can draw with it later. +// It returns a negative number if it couldn't load +// the bitmap. On success, it returns the bitmap +// number of the first frame and nframes is set. +extern int bm_load_animation( char * filename, int * nframes, int *fps = NULL, int can_drop_frames = 0 ); + +// This locks down a bitmap and returns a pointer to a bitmap +// that can be accessed until you call bm_unlock. Only lock +// a bitmap when you need it! This will convert it into the +// appropriate format also. +extern bitmap * bm_lock( int bitmapnum, ubyte bpp, ubyte flags ); + +// The signature is a field that gets filled in with +// a unique signature for each bitmap. The signature for each bitmap +// will also change when the bitmap's data changes. +extern uint bm_get_signature( int bitmapnum); + +// Unlocks a bitmap +extern void bm_unlock( int bitmapnum ); + +// Gets info. w,h,or flags,nframes or fps can be NULL if you don't care. +extern void bm_get_info( int bitmapnum, int *w=NULL, int * h=NULL, ubyte * flags=NULL, int *nframes=NULL, int *fps=NULL, bitmap_section_info **sections = NULL ); + +// get filename +extern void bm_get_filename(int bitmapnum, char *filename); + +// resyncs all the bitmap palette +extern void bm_update(); + +// call to load all data for all bitmaps that have been requested to be loaded +extern void bm_load_all(); +extern void bm_unload_all(); + +// call to get the palette for a bitmap +extern void bm_get_palette(int n, ubyte *pal, char *name); + +// Hacked function to get a pixel from a bitmap. +// Only works good in 8bpp mode. +void bm_get_pixel( int bitmap, float u, float v, ubyte *r, ubyte *g, ubyte *b ); + +// Returns number of bytes of bitmaps locked this frame +// ntotal = number of bytes of bitmaps locked this frame +// nnew = number of bytes of bitmaps locked this frame that weren't locked last frame +void bm_get_frame_usage(int *ntotal, int *nnew); + +/* + * Example on using bm_create + * + { + static int test_inited = 0; + static int test_bmp; + static uint test_bmp_data[128*64]; + + if ( !test_inited ) { + test_inited = 1; + // Create the new bitmap and fill in its data. + // When you're done with it completely, call + // bm_release to free up the bitmap handle + test_bmp = bm_create( 32, 128, 64, test_bmp_data ); + int i,j; + for (i=0; i<64; i++ ) { + for (j=0; j<64; j++ ) { + uint r=i*4; + test_bmp_data[j+i*128] = r; + } + } + } + + bm_unload(test_bmp); // this pages out the data, so that the + // next bm_lock will convert the new data to the + // correct bpp + + // put in new data + int x,y; + gr_reset_clip(); + for (y=0; y<64; y++) + for (x=0; x<128; x++ ) + test_bmp_data[y*128+x] = 15; + + // Draw the bitmap to upper left corner + gr_set_bitmap(test_bmp); + gr_bitmap( 0,0 ); + } +*/ + + +//============================================================================ +// Paging stuff +//============================================================================ + +void bm_page_in_start(); +void bm_page_in_stop(); + +// Paging code in a library should call these functions +// in its page in function. + +// Marks a texture as being used for this level +// If num_frames is passed, assume this is an animation +void bm_page_in_texture( int bitmapnum, int num_frames=1 ); + +// Marks a texture as being used for this level +// If num_frames is passed, assume this is an animation +void bm_page_in_nondarkening_texture( int bitmap, int num_frames=1 ); + +// marks a texture as being a transparent textyre used for this level +// Marks a texture as being used for this level +// If num_frames is passed, assume this is an animation +void bm_page_in_xparent_texture( int bitmapnum, int num_frames=1 ); + +// Marks an aabitmap as being used for this level +// If num_frames is passed, assume this is an animation +void bm_page_in_aabitmap( int bitmapnum, int num_frames=1 ); + +// +// Mode: 0 = High memory +// 1 = Low memory ( every other frame of ani's) +// 2 = Debug low memory ( only use first frame of each ani ) +void bm_set_low_mem( int mode ); + +char *bm_get_filename(int handle); + +void BM_SELECT_SCREEN_FORMAT(); +void BM_SELECT_TEX_FORMAT(); +void BM_SELECT_ALPHA_TEX_FORMAT(); + +// convert a 24 bit value to a 16 bit value +void bm_24_to_16(int bit_24, ushort *bit_16); + +// set the rgba components of a pixel, any of the parameters can be NULL +extern void (*bm_set_components)(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a); +void bm_set_components_argb(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a); +void bm_set_components_d3d(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a); +void bm_set_components_argb_d3d_16_screen(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a); +void bm_set_components_argb_d3d_32_screen(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a); +void bm_set_components_argb_d3d_16_tex(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a); +void bm_set_components_argb_d3d_32_tex(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a); + +// get the rgba components of a pixel, any of the parameters can be NULL +void bm_get_components(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a); + +//============================================================================ +// section info stuff +//============================================================================ + +// given a bitmap and a section, return the size (w, h) +void bm_get_section_size(int bitmapnum, int sx, int sy, int *w, int *h); + +#endif + diff --git a/include/briefingeditordlg.h b/include/briefingeditordlg.h new file mode 100644 index 0000000..35456a2 --- /dev/null +++ b/include/briefingeditordlg.h @@ -0,0 +1,226 @@ +/* + * $Logfile: /Freespace2/code/FRED2/BriefingEditorDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Briefing editor dialog box class. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 3 5/20/99 1:46p Andsager + * Add briefing view copy and paste between stages + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 24 4/20/98 4:40p Hoffoss + * Added a button to 4 editors to play the chosen wave file. + * + * 23 4/07/98 4:51p Dave + * (Hoffoss) Fixed a boat load of bugs caused by the new change to + * multiple briefings. Allender's code changed to support this in the + * briefing editor wasn't quite correct. + * + * 22 4/06/98 5:37p Hoffoss + * Added sexp tree support to briefings in Fred. + * + * 21 2/18/98 6:45p Hoffoss + * Added support for lines between icons in briefings for Fred. + * + * 20 2/09/98 9:25p Allender + * team v team support. multiple pools and breifings + * + * 19 2/04/98 4:32p Allender + * support for multiple briefings and debriefings. Changes to mission + * type (now a bitfield). Bitfield defs for multiplayer modes + * + * 18 11/04/97 4:33p Hoffoss + * Made saving keep the current briefing state intact. + * + * 17 10/19/97 11:32p Hoffoss + * Added support for briefing cuts in Fred. + * + * 16 9/30/97 5:56p Hoffoss + * Added music selection combo boxes to Fred. + * + * 15 8/19/97 10:15a Hoffoss + * Made escape close briefing window through normal channels. + * + * 14 8/14/97 6:37p Hoffoss + * Added briefing icon id support to Fred. + * + * 13 8/07/97 3:45p Hoffoss + * Added in several requested features to the briefing editor. + * + * 12 8/06/97 12:25p Hoffoss + * Fixed bugs: Briefing editor dialog wasn't getting reset when new + * mission created, and hitting MFC assert when quitting while briefing + * editor open. + * + * 11 7/03/97 9:42a Hoffoss + * Added a ship type field in briefing editor. + * + * 10 6/26/97 6:04p Hoffoss + * Briefing icons now are selected before normal objects. + * + * 9 6/26/97 5:18p Hoffoss + * Major rework of briefing editor functionality. + * + * 8 6/25/97 4:13p Hoffoss + * Added functionality to the make icon button. + * + * 7 6/25/97 10:37a Hoffoss + * Added icon text field support to briefing editor. + * + * 6 6/24/97 3:03p Hoffoss + * New stages now copy another stage if possible. + * + * 5 6/24/97 11:31a Hoffoss + * Added code for handling icon editing. + * + * 4 6/24/97 10:16a Hoffoss + * Changes to briefing editor code. + * + * 3 6/23/97 2:58p Hoffoss + * Added more functionality to briefing editor. + * + * 2 6/18/97 2:39p Hoffoss + * Improved on briefing editor. Still far from done, though. + * + * $NoKeywords: $ + */ + +#ifndef __BRIEFINGEDITORDLG_H__ +#define __BRIEFINGEDITORDLG_H__ + +#include "sexp.h" +#include "missionbriefcommon.h" + +///////////////////////////////////////////////////////////////////////////// +// briefing_editor_dlg dialog + +class briefing_editor_dlg : public CDialog +{ +// Construction +public: + void focus_sexp(int select_sexp_node); + int calc_num_lines_for_icons(int num); + void batch_render(); + void save_editor_state(); + void restore_editor_state(); + void reset_icon_loop(int stage); + int get_next_icon(int id); + void OnOK(); + void OnCancel(); + int find_icon(int id, int stage); + void propagate_icon(int num); + void reset_editor(); + int check_mouse_hit(int x, int y); + void delete_icon(int num); + void update_positions(); + void icon_select(int num); + void draw_icon(object *objp); + void create(); + void update_data(int update = 1); + briefing_editor_dlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(briefing_editor_dlg) + enum { IDD = IDD_BRIEFING_EDITOR }; + sexp_tree m_tree; + CButton m_lines; + BOOL m_hilight; + int m_icon_image; + CString m_icon_label; + CString m_stage_title; + CString m_text; + CString m_time; + CString m_voice; + CString m_icon_text; + int m_icon_team; + int m_ship_type; + BOOL m_change_local; + int m_id; + int m_briefing_music; + BOOL m_cut_next; + BOOL m_cut_prev; + int m_current_briefing; + //}}AFX_DATA + + CBitmap m_play_bm; + + // copy view variables + int m_copy_view_set; + vector m_copy_view_pos; + matrix m_copy_view_orient; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(briefing_editor_dlg) + public: + virtual BOOL DestroyWindow(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + //}}AFX_VIRTUAL + +// Implementation +protected: + brief_icon *iconp; + briefing *save_briefing; + int icon_loop, stage_loop; + int m_cur_stage; + int m_last_stage; + int m_cur_icon; + int m_last_icon; + int stage_saved; + int icon_saved; + int modified; +// int point_obj; + int icon_obj[MAX_STAGE_ICONS]; + int icon_marked[MAX_STAGE_ICONS]; + int line_marked[MAX_BRIEF_STAGE_LINES]; + + void copy_stage(int from, int to); + + // Generated message map functions + //{{AFX_MSG(briefing_editor_dlg) + afx_msg void OnClose(); + afx_msg void OnNext(); + afx_msg void OnPrev(); + afx_msg void OnBrowse(); + afx_msg void OnAddStage(); + afx_msg void OnDeleteStage(); + afx_msg void OnInsertStage(); + afx_msg void OnMakeIcon(); + afx_msg void OnDeleteIcon(); + afx_msg void OnGotoView(); + afx_msg void OnSaveView(); + afx_msg void OnSelchangeIconImage(); + afx_msg void OnSelchangeTeam(); + afx_msg void OnPropagateIcons(); + afx_msg void OnInitMenu(CMenu* pMenu); + afx_msg void OnLines(); + afx_msg void OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnPlay(); + afx_msg void OnCopyView(); + afx_msg void OnPasteView(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +private: +}; + +#endif + diff --git a/include/campaigneditordlg.h b/include/campaigneditordlg.h new file mode 100644 index 0000000..c0ea684 --- /dev/null +++ b/include/campaigneditordlg.h @@ -0,0 +1,110 @@ +// CampaignEditorDlg.h : header file +// + +#include "resource.h" +#include "sexp_tree.h" +#include "campaignfilelistbox.h" + +///////////////////////////////////////////////////////////////////////////// +// campaign_editor form view + +#ifndef __AFXEXT_H__ +#include +#endif + +class campaign_sexp_tree : public sexp_tree +{ +public: + int load_sub_tree(int index); + int get_new_node_position(); +}; + +class campaign_editor : public CFormView +{ +private: + int m_num_links; + int m_last_mission; + +protected: + campaign_editor(); // protected constructor used by dynamic creation + DECLARE_DYNCREATE(campaign_editor) + +// Form Data +public: + void mission_selected(int num); + void insert_handler(int old, int node); + void swap_handler(int node1, int node2); + void update(); + void load_tree(int save = 1); + void save_tree(int clear = 1); + int handler(int code, int node, char *str = NULL); + void initialize( int init_files = 1 ); + void load_campaign(); + void update_loop_desc_window(); + void campaign_editor::save_loop_desc_window(); + //{{AFX_DATA(campaign_editor) + enum { IDD = IDD_CAMPAIGN }; + campaign_sexp_tree m_tree; + campaign_filelist_box m_filelist; + CString m_name; + int m_type; + CString m_num_players; + CString m_desc; + CString m_loop_desc; + CString m_loop_brief_anim; + CString m_loop_brief_sound; + //}}AFX_DATA + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(campaign_editor) + public: + virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint); + //}}AFX_VIRTUAL + +// Implementation +protected: + virtual ~campaign_editor(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + // Generated message map functions + //{{AFX_MSG(campaign_editor) + afx_msg void OnLoad(); + afx_msg void OnAlign(); + afx_msg void OnCpgnClose(); + afx_msg void OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBeginlabeleditSexpTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnEndlabeleditSexpTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnSelchangedSexpTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnMoveUp(); + afx_msg void OnMoveDown(); + afx_msg void OnEndEdit(); + afx_msg void OnChangeBriefingCutscene(); + afx_msg void OnSelchangeType(); + afx_msg void OnGalatea(); + afx_msg void OnBastion(); + afx_msg void OnToggleLoop(); + afx_msg void OnBrowseLoopAni(); + afx_msg void OnBrowseLoopSound(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +extern campaign_editor *Campaign_tree_formp; +extern int Cur_campaign_mission; +extern int Cur_campaign_link; + diff --git a/include/campaignfilelistbox.h b/include/campaignfilelistbox.h new file mode 100644 index 0000000..ba013b1 --- /dev/null +++ b/include/campaignfilelistbox.h @@ -0,0 +1,39 @@ +// CampaignFilelistBox.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// campaign_filelist_box window + +class campaign_filelist_box : public CListBox +{ +// Construction +public: + void initialize(); + campaign_filelist_box(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(campaign_filelist_box) + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~campaign_filelist_box(); + + // Generated message map functions +protected: + //{{AFX_MSG(campaign_filelist_box) + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + diff --git a/include/campaigntreeview.h b/include/campaigntreeview.h new file mode 100644 index 0000000..2e6b962 --- /dev/null +++ b/include/campaigntreeview.h @@ -0,0 +1,107 @@ +// CampaignTreeView.h : header file +// + +#include "missioncampaign.h" + +#define MAX_LEVELS 100 +#define MAX_CAMPAIGN_TREE_LINKS 300 + +typedef struct campaign_tree_element { + int from_links; // total branches from this mission + int to_links; // total branches that lead to this mission + CRect box; // coordinates of drawn box +} campaign_tree_element; + +typedef struct campaign_tree_link { + int from; // index of source mission + int to; // index of mission link leads to + int sexp; // sexp index of condition that allows this branch + int node; // node tracker when link is in sexp tree window + int from_pos; // from link drawing offset + int to_pos; // to link drawing offset + bool mission_loop; // whether link leads to mission loop + char *mission_loop_txt; // text describing mission loop + char *mission_loop_brief_anim; // filename of anim to play in the brief + char *mission_loop_brief_sound; // filename of anim to play in the brief + CPoint p1; // coordinates of line last draw for link, from p1 to p2 + CPoint p2; +} campaign_tree_link; + +extern int Total_links; +extern int Level_counts[MAX_LEVELS]; +extern int Sorted[MAX_CAMPAIGN_MISSIONS]; +extern campaign_tree_element Elements[MAX_CAMPAIGN_MISSIONS]; +extern campaign_tree_link Links[MAX_CAMPAIGN_TREE_LINKS]; + +///////////////////////////////////////////////////////////////////////////// +// campaign_tree_view view + +class campaign_tree_view : public CScrollView +{ +protected: + campaign_tree_view(); // protected constructor used by dynamic creation + DECLARE_DYNCREATE(campaign_tree_view) + +// Attributes +public: + void drop_mission(int m, CPoint point); + int add_link(int from, int to); + void remove_mission(int m); + void delete_link(int num); + int get_root_mission(); + void horizontally_align_mission(int num, int dir); + void correct_position(int num); + void free_links(); + void sort_elements(); + int query_alternate_pos(const CPoint& p); + int query_pos(const CPoint& p); + int query_level(const CPoint& p); + void sort_links(); + void realign_tree(); + int total_levels; + int total_width; + campaign_tree_link *first_link; + +// Operations +public: + void construct_tree(); + void initialize(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(campaign_tree_view) + public: + virtual void OnInitialUpdate(); + protected: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + //}}AFX_VIRTUAL + +// Implementation +protected: + virtual ~campaign_tree_view(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + + // Generated message map functions +protected: + //{{AFX_MSG(campaign_tree_view) + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); + afx_msg void OnRemoveMission(); + afx_msg void OnDeleteRow(); + afx_msg void OnInsertRow(); + afx_msg void OnAddRepeat(); + afx_msg void OnEndOfCampaign(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +extern campaign_tree_view *Campaign_tree_viewp; + diff --git a/include/campaigntreewnd.h b/include/campaigntreewnd.h new file mode 100644 index 0000000..a61180e --- /dev/null +++ b/include/campaigntreewnd.h @@ -0,0 +1,125 @@ +/* + * $Logfile: /Freespace2/code/FRED2/CampaignTreeWnd.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Campaign display tree window code. Works very closely with the Campaign editor dialog box. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 12 12/18/97 5:11p Allender + * initial support for ship/weapon persistence + * + * 11 8/14/97 11:54p Hoffoss + * Added more error checking to Campaign editor, and made exit from + * Campaign editor reload last mission in Fred (unless specifically + * loading another mission). + * + * 10 8/13/97 5:49p Hoffoss + * Fixed bugs, made additions. + * + * 9 8/13/97 12:46p Hoffoss + * Added campaign error checker, accelerator table, and mission goal data + * listings to sexp tree right click menu. + * + * 8 5/15/97 12:45p Hoffoss + * Extensive changes to fix many little bugs. + * + * 7 5/14/97 12:54p Hoffoss + * Added sexp tree for campaign branches, branch hilighting, and branch + * reordering. + * + * 6 5/13/97 12:46p Hoffoss + * Added close campaign editor functions, changed global pointer to have + * capped first letter. + * + * 5 5/13/97 11:13a Hoffoss + * Added remaining file menu options to campaign editor. + * + * 4 5/13/97 10:52a Hoffoss + * Added campaign saving code. + * + * 3 5/09/97 9:50a Hoffoss + * Routine code check in. + * + * 2 5/01/97 4:11p Hoffoss + * Started on Campaign editor stuff, being sidetracked with fixing bugs + * now, though, so checking it for now. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// campaign_tree_wnd window + +class campaign_tree_wnd : public CFrameWnd +{ + DECLARE_DYNCREATE(campaign_tree_wnd) + +// Construction +public: + campaign_tree_wnd(); + +// Attributes +public: + +// Operations +public: + int error_checker(); + int fred_check_sexp(int sexp, int type, char *msg, ...); + int error(char *msg, ...); + int internal_error(char *msg, ...); + int save_modified(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(campaign_tree_wnd) + //}}AFX_VIRTUAL + +private: + int g_err; + +// Implementation +public: + virtual ~campaign_tree_wnd(); + virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext); + + // Generated message map functions + CSplitterWnd m_splitter; + + //{{AFX_MSG(campaign_tree_wnd) + afx_msg void OnUpdateCpgnFileOpen(CCmdUI* pCmdUI); + afx_msg void OnCpgnFileOpen(); + afx_msg void OnDestroy(); + afx_msg void OnCpgnFileSave(); + afx_msg void OnCpgnFileSaveAs(); + afx_msg void OnCpgnFileNew(); + afx_msg void OnClose2(); + afx_msg void OnErrorChecker(); + afx_msg void OnClose(); + afx_msg void OnInitialShips(); + afx_msg void OnInitialWeapons(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +extern int Mission_filename_cb_format; +extern int Campaign_modified; +extern int Bypass_clear_mission; +extern campaign_tree_wnd *Campaign_wnd; + diff --git a/include/cfile.h b/include/cfile.h new file mode 100644 index 0000000..df5580c --- /dev/null +++ b/include/cfile.h @@ -0,0 +1,595 @@ +/* + * $Logfile: /Freespace2/code/CFile/cfile.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 11 9/08/99 12:03a Dave + * Make squad logos render properly in D3D all the time. Added intel anim + * directory. + * + * 10 8/31/99 9:46a Dave + * Support for new cfile cbanims directory. + * + * 9 5/19/99 4:07p Dave + * Moved versioning code into a nice isolated common place. Fixed up + * updating code on the pxo screen. Fixed several stub problems. + * + * 8 3/28/99 5:58p Dave + * Added early demo code. Make objects move. Nice and framerate + * independant, but not much else. Don't use yet unless you're me :) + * + * 7 3/24/99 4:05p Dave + * Put in support for assigning the player to a specific squadron with a + * specific logo. Preliminary work for doing pos/orient checksumming in + * multiplayer to reduce bandwidth. + * + * 6 1/12/99 3:15a Dave + * Barracks screen support for selecting squad logos. We need real artwork + * :) + * + * 5 10/29/98 10:41a Dave + * Change the way cfile initializes exe directory. + * + * 4 10/13/98 9:19a Andsager + * Add localization support to cfile. Optional parameter with cfopen that + * looks for localized files. + * + * 3 10/12/98 9:54a Dave + * Fixed a few file organization things. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 84 9/09/98 5:53p Dave + * Put in new tracker packets in API. Change cfile to be able to checksum + * portions of a file. + * + * 83 8/12/98 4:53p Dave + * Put in 32 bit checksumming for PXO missions. No validation on the + * actual tracker yet, though. + * + * 82 5/19/98 1:19p Allender + * new low level reliable socket reading code. Make all missions/campaign + * load/save to data missions folder (i.e. we are rid of the player + * missions folder) + * + * 81 5/13/98 10:22p John + * Added cfile functions to read/write rle compressed blocks of data. + * Made palman use it for .clr files. Made alphacolors calculate on the + * fly rather than caching to/from disk. + * + * 80 5/01/98 10:21a John + * Added code to find all pack files in all trees. Added code to create + * any directories that we write to. + * + * 79 4/30/98 10:29p John + * Added code to refresh filelist if cd-rom changed or packfiles created + * or deleted. + * + * 78 4/30/98 10:06p John + * Started adding code for splitting the maps data tree for hardware + * textures. + * + * 77 4/30/98 9:43p John + * Restructured some stuff. + * + * 76 4/30/98 8:23p John + * Fixed some bugs with Fred caused by my new cfile code. + * + * 75 4/30/98 4:53p John + * Restructured and cleaned up cfile code. Added capability to read off + * of CD-ROM drive and out of multiple pack files. + * + * 74 4/20/98 6:04p Dave + * Implement multidata cache flushing and xferring mission files to + * multidata. Make sure observers can't change hud config. Fix pilot image + * viewing in popup. Put in game status field. Tweaked multi options. + * + * 73 4/01/98 6:06p Hoffoss + * Added a command briefing directory for those voice files. + * + * 72 3/31/98 4:51p Dave + * Removed medals screen and multiplayer buttons from demo version. Put in + * new pilot popup screen. Make ships in mp team vs. team have proper team + * ids. Make mp respawns a permanent option saved in the player file. + * + * 71 3/26/98 6:01p Dave + * Put in file checksumming routine in cfile. Made pilot pic xferring more + * robust. Cut header size of voice data packets in half. Put in + * restricted game host query system. + * + * 70 3/10/98 2:27p Hoffoss + * Added change directory with history stack functions, so you can switch + * to a new directory, do something, and restore to what it was. useful + * for Fred. + * + * 69 3/07/98 3:48p Lawrance + * get save game working, allow restore from main menu + * + * 68 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 67 2/20/98 4:43p Dave + * Finished support for multiplayer player data files. Split off + * multiplayer campaign functionality. + * + * 66 2/19/98 6:26p Dave + * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in + * initial support for player data uploading. + * + * 65 2/06/98 3:47p Allender + * subtitling for movies + * + * 64 2/05/98 10:15p Lawrance + * Add support for .svg filenames. + * + * 63 1/12/98 10:07p Hoffoss + * Made tab cycle properly in debriefing screen and put in aux. pilot + * image directory. + * + * 62 1/11/98 2:45p John + * Changed .lst to .clt + * + * 61 1/11/98 2:14p John + * Changed a lot of stuff that had to do with bitmap loading. Made cfile + * not do callbacks, I put that in global code. Made only bitmaps that + * need to load for a level load. + * + * 60 1/02/98 4:41p Allender + * added new mission folder for "player" missions + * + * 59 12/28/97 12:42p John + * Put in support for reading archive files; Made missionload use the + * cf_get_file_list function. Moved demos directory out of data tree. + * + * 58 12/17/97 10:16p Allender + * implemented a "no callback" flag to tell the cfile code not to use the + * cf_callback on files which specify this flag + * + * 57 12/08/97 6:23p Lawrance + * add cflush() + * + * 56 12/07/97 4:30p John + * Fixed bug with cfile versioning if two files use it at once. Added + * code to cfile so I could display a loading box while loading. + * + * 55 11/24/97 9:28a Hoffoss + * Moved define of CF_TYPE_MULTI_PLAYERS so it's in order. I started to + * add it because I couldn't find it, so that's indication enough it + * needed it. + * + * 54 11/20/97 1:07a Lawrance + * add support for voice/debriefings directory + * + * 53 11/19/97 7:27p Hoffoss + * Added version checking read functions. + * + * 52 11/18/97 10:53a Hoffoss + * + * 51 11/17/97 6:07p Hoffoss + * Make get_file_list() allow filtering via a callback function. + * + * 50 11/16/97 2:29p John + * added versioning to nebulas; put nebula code into freespace. + * + * 49 11/15/97 6:10p Lawrance + * add in support for new voice directories + * + * 48 11/11/97 4:54p Dave + * Put in support for single vs. multiplayer pilots. Put in initial player + * selection screen (no command line option yet). Started work on + * multiplayer campaign file save gaming. + * + * 47 11/07/97 4:00p Hoffoss + * Capitalized directory names, changed the player/image directory to work + * like the rest, and forced the player/images directory to be created. + * + * 46 11/06/97 5:38p Hoffoss + * Added a new player image directory and added support for it. + * + * 45 11/04/97 7:46p Lawrance + * Add support for data\interface\HUD directory + * + * 44 10/29/97 6:22p Hoffoss + * Added some new file listing functions. + * + * 43 10/28/97 10:54a Lawrance + * support for 8b22k and 16b11k directories under the sound directory + * + * 42 10/14/97 11:34p Lawrance + * add function to get full path for a given filename + * + * 41 9/24/97 5:30p Lawrance + * add directory for voices + * + * 40 9/20/97 8:16a John + * Made .clr files go into the Cache directory. Replaced cfopen(name,NULL) + * to delete a file with cf_delete. + * + * 39 9/09/97 6:50p Hoffoss + * Fixed bug with mission saving. + * + * 38 9/05/97 4:52p Lawrance + * fix prototype for cfread_angles() and cfwrite_angles() + * + * 37 8/29/97 4:47p Dave + * Added an extension for state transfer status files. + * + * 36 8/21/97 12:14p Dave + * Changed demo file extension from .keg to .fsd + * + * 35 8/19/97 5:51p Hoffoss + * Fixes to cfopen to not check the default directory first, but check it + * last. Also tracks info as to where a file is located when opened. + * + * 34 8/17/97 10:22p Hoffoss + * Fixed several bugs in Fred with Undo feature. In the process, recoded + * a lot of CFile.cpp. + * + * 33 8/17/97 12:47p Hoffoss + * Changed code so I can force missions to load from the missions + * directory regardless of it's extension. + * + * 32 8/13/97 1:39p Adam + * make .ani files available in the data/effects directory + * + * 31 8/13/97 12:24p Lawrance + * Add support for effects directory + * + * 30 7/30/97 5:23p Dave + * Added file extensions for demo stuff + * + * 29 7/28/97 10:42p Lawrance + * added ctmpfile(), analog to tmpfile() + * + * 28 7/16/97 5:29p John + * added palette table caching and made scaler and liner no light tmapper + * do alpha blending in 8 bpp mode. + * + * 27 6/05/97 4:53p John + * First rev of new antialiased font stuff. + * + * 26 6/05/97 1:22p Allender + * added .ntl as an extension for nettest program + * + * 25 4/25/97 11:31a Allender + * Campaign state now saved in campaign save file in player directory. + * Made some global variables follow naming convention. Solidified + * continuing campaigns based on new structure + * + * 24 4/17/97 9:01p Allender + * start of campaign stuff. Campaigns now stored in external file (no + * filenames in code). Continuing campaign won't work at this point + * + * 23 4/03/97 4:26p Lawrance + * adding .wav search to music directory + * + * 22 4/01/97 9:26a Allender + * added support for descent style fonts although they are not used in the + * game yet + * + * 21 3/04/97 8:17a John + * Fixed movie code to not require a file handle. Used CFILE instead. + * Took cfile_get_handle or whatever out. + * + * 20 3/03/97 8:57a Lawrance + * took out cf_returnfp() + * + * 19 3/01/97 2:09p Lawrance + * supporting memory mapped files, moved cfile implementation details into + * .cpp file + * + * 18 2/17/97 3:00p Lawrance + * added .ani type to MAPS_EXT and INTERFACE_EXT + * + * 17 2/07/97 9:00a Lawrance + * added cfread_uint() and cwrite_uint() + * + * 16 2/04/97 9:29a Allender + * added cfwrite* functions + * + * 15 1/22/97 10:48a Lawrance + * supporting AVI playback + * + * 14 12/23/96 10:56a John + * Totally restructured the POF stuff to support multiple + * detail levels in one POF file. + * + * + * 13 11/20/96 10:00a Hoffoss + * Added cfile_chdir() function. + * + * 12 11/13/96 10:14a Allender + * added small routines to read basic data types. Also changed code to + * try to find extensions in more than 1 directory. + * + * 11 11/11/96 3:21p Allender + * added extension for movies + * + * $NoKeywords: $ + */ + +#ifndef __CFILE_H__ +#define __CFILE_H__ + +#include +#include "pstypes.h" + +#define CF_EOF (-1) + +#define CF_SEEK_SET (0) +#define CF_SEEK_CUR (1) +#define CF_SEEK_END (2) + +typedef struct CFILE { + int id; // Index into cfile.cpp specific structure + int version; // version of this file +} CFILE; + +// extra info that can be returned when getting a file listing +typedef struct { + time_t write_time; +} file_list_info; + + +#define CF_MAX_FILENAME_LENGTH 32 // Includes null terminater, so real length is 31 +#define CF_MAX_PATHNAME_LENGTH 256 // Includes null terminater, so real length is 255 + +#define CF_TYPE_ANY -1 // Used to check in any directory + +#define CF_TYPE_INVALID 0 +#define CF_TYPE_ROOT 1 // Root must be 1!! +#define CF_TYPE_DATA 2 +#define CF_TYPE_MAPS 3 +#define CF_TYPE_TEXT 4 +#define CF_TYPE_MISSIONS 5 +#define CF_TYPE_MODELS 6 +#define CF_TYPE_TABLES 7 +#define CF_TYPE_SOUNDS 8 +#define CF_TYPE_SOUNDS_8B22K 9 +#define CF_TYPE_SOUNDS_16B11K 10 +#define CF_TYPE_VOICE 11 +#define CF_TYPE_VOICE_BRIEFINGS 12 +#define CF_TYPE_VOICE_CMD_BRIEF 13 +#define CF_TYPE_VOICE_DEBRIEFINGS 14 +#define CF_TYPE_VOICE_PERSONAS 15 +#define CF_TYPE_VOICE_SPECIAL 16 +#define CF_TYPE_VOICE_TRAINING 17 +#define CF_TYPE_MUSIC 18 +#define CF_TYPE_MOVIES 19 +#define CF_TYPE_INTERFACE 20 +#define CF_TYPE_FONT 21 +#define CF_TYPE_EFFECTS 22 +#define CF_TYPE_HUD 23 +#define CF_TYPE_PLAYER_MAIN 24 +#define CF_TYPE_PLAYER_IMAGES_MAIN 25 +#define CF_TYPE_CACHE 26 +#define CF_TYPE_PLAYERS 27 +#define CF_TYPE_SINGLE_PLAYERS 28 +#define CF_TYPE_MULTI_PLAYERS 29 +#define CF_TYPE_MULTI_CACHE 30 +#define CF_TYPE_CONFIG 31 +#define CF_TYPE_SQUAD_IMAGES_MAIN 32 +#define CF_TYPE_DEMOS 33 +#define CF_TYPE_CBANIMS 34 +#define CF_TYPE_INTEL_ANIMS 35 + +#define CF_MAX_PATH_TYPES 36 // Can be as high as you'd like + +// TRUE if type is specified and valid +#define CF_TYPE_SPECIFIED(path_type) (((path_type)>CF_TYPE_INVALID) && ((path_type) +#include "vdsound.h" + +#ifndef __CHANNEL_H__ +#define __CHANNEL_H__ + +typedef struct channel +{ + int sig; // uniquely identifies the sound playing on the channel + int snd_id; // identifies which kind of sound is playing + LPDIRECTSOUNDBUFFER pdsb; // pointer to the secondary buffer that was duplicated + LPDIRECTSOUND3DBUFFER pds3db; // 3D interface, only used if sound buffer created with CTRL3D flag + int looping; // flag to indicate that the sound is looping + int vol; // in DirectSound units + int priority; // implementation dependant priority + bool is_voice_msg; + DWORD last_position; +} channel; + + +// #define MAX_CHANNELS 16 +extern channel* Channels; //[MAX_CHANNELS]; + +#endif /* __CHANNEL_H__ */ + diff --git a/include/chatbox.h b/include/chatbox.h new file mode 100644 index 0000000..8097d88 --- /dev/null +++ b/include/chatbox.h @@ -0,0 +1,142 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/Chatbox.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for chat box code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 5/22/99 5:35p Dave + * Debrief and chatbox screens. Fixed small hi-res HUD bug. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 14 9/11/98 5:08p Dave + * More tweaks to kick notification system. + * + * 13 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 12 5/15/98 5:15p Dave + * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy + * status for team vs. team. Put in asserts to check for invalid team vs. + * team situations. + * + * 11 4/14/98 5:06p Dave + * Don't load or send invalid pilot pics. Fixed chatbox graphic errors. + * Made chatbox display team icons in a team vs. team game. Fixed up pause + * and endgame sequencing issues. + * + * 10 4/12/98 2:09p Dave + * Make main hall door text less stupid. Make sure inputbox focus in the + * multi host options screen is managed more intelligently. + * + * 9 4/01/98 11:19p Dave + * Put in auto-loading of xferred pilot pic files. Grey out background + * behind pinfo popup. Put a chatbox message in when players are kicked. + * Moved mission title down in briefing. Other ui fixes. + * + * 8 3/29/98 1:24p Dave + * Make chatbox not clear between multiplayer screens. Select player ship + * as default in mp team select and weapons select screens. Made create + * game mission list use 2 fixed size columns. + * + * 7 2/13/98 3:46p Dave + * Put in dynamic chatbox sizing. Made multiplayer file lookups use cfile + * functions. + * + * 6 1/16/98 2:34p Dave + * Made pause screen work properly (multiplayer). Changed how chat packets + * work. + * + * 5 1/07/98 5:20p Dave + * Put in support for multiplayer campaigns with the new interface + * screens. + * + * 4 12/18/97 8:59p Dave + * Finished putting in basic support for weapon select and ship select in + * multiplayer. + * + * 3 10/01/97 4:47p Lawrance + * move some #defines out of header file into .cpp file + * + * 2 10/01/97 4:39p Lawrance + * move chat code into Chatbox.cpp, simplify interface + * + * 1 10/01/97 10:54a Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __FREESPACE_CHATBOX_H__ +#define __FREESPACE_CHATBOX_H__ + +// prototype +struct net_player; + +#define CHATBOX_MAX_LEN 125 // max length of the actual text string + +// chatbox flags for creation/switching between modes +#define CHATBOX_FLAG_SMALL (1<<0) // small chatbox +#define CHATBOX_FLAG_BIG (1<<1) // big chatbox +#define CHATBOX_FLAG_MULTI_PAUSED (1<<2) // chatbox in the multiplayer paused screen +#define CHATBOX_FLAG_DRAW_BOX (1<<3) // should be drawn by the chatbox code +#define CHATBOX_FLAG_BUTTONS (1<<4) // the chatbox should be drawing/checking its own buttons +// NOTE : CHATBOX_FLAG_BUTTONS requires that CHATBOX_FLAG_DRAW_BOX is also set! + +// initialize all chatbox details with the given mode flags +int chatbox_create(int mode_flags = (CHATBOX_FLAG_SMALL | CHATBOX_FLAG_DRAW_BOX | CHATBOX_FLAG_BUTTONS)); + +// process this frame for the chatbox +int chatbox_process(int key_in=-1); + +// shutdown all chatbox functionality +void chatbox_close(); + +// render the chatbox for this frame +void chatbox_render(); + +// try and scroll the chatbox up. return 0 or 1 on fail or success +int chatbox_scroll_up(); + +// try and scroll the chatbox down, return 0 or 1 on fail or success +int chatbox_scroll_down(); + +// clear the contents of the chatbox +void chatbox_clear(); + +// add a line of text (from the player identified by pid) to the chatbox +void chatbox_add_line(char *msg,int pid,int add_id = 1); + +// force the chatbox to go into small mode (if its in large mode) - will not wotk if in multi paused chatbox mode +void chatbox_force_small(); + +// force the chatbox to go into big mode (if its in small mode) - will not work if in multi paused chatbox mode +void chatbox_force_big(); + +// "lose" the focus on the chatbox inputbox +void chatbox_lose_focus(); + +// return if the inputbox for the chatbox currently has focus +int chatbox_has_focus(); + +// grab the focus for the chatbox inputbox +void chatbox_set_focus(); + +// return if the inputbox was pressed - "clicked on" +int chatbox_pressed(); + +// reset all timestamps associated with the chatbox +void chatbox_reset_timestamps(); + +#endif + diff --git a/include/childfrm.h b/include/childfrm.h new file mode 100644 index 0000000..9adff61 --- /dev/null +++ b/include/childfrm.h @@ -0,0 +1,42 @@ +// ChildFrm.h : interface of the CChildFrame class +// +///////////////////////////////////////////////////////////////////////////// + +class CChildFrame : public CMDIChildWnd +{ + DECLARE_DYNCREATE(CChildFrame) +public: + CChildFrame(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CChildFrame) + protected: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CChildFrame(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +// Generated message map functions +protected: + //{{AFX_MSG(CChildFrame) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + diff --git a/include/chttpget.h b/include/chttpget.h new file mode 100644 index 0000000..279eed8 --- /dev/null +++ b/include/chttpget.h @@ -0,0 +1,109 @@ +/* +* $Logfile: /Freespace2/code/Inetfile/Chttpget.h $ +* $Revision$ +* $Date$ +* $Author$ +* +* HTTP Client class (get only) +* +* $Log$ +* Revision 1.1 2002/05/03 03:28:12 root +* Initial revision +* + * + * 3 8/22/99 1:19p Dave + * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in + * which d3d cards are detected. + * + * 5 8/20/99 3:01p Kevin + * Added support for Proxies (I hope!) + * + * 4 7/31/98 12:19p Nate + * Fixed http abort problem. + * + * 3 7/31/98 11:57a Kevin + * Added new functions for getting state + * + * 2 6/01/98 10:10a Kevin + * Added DLL connection interface and auto update DLL + * + * 1 5/27/98 9:54a Kevin + * + * 1 5/25/98 5:31p Kevin + * Initial version +* +* $NoKeywords: $ +*/ +#ifndef _CHTTPGET_HEADER_ +#define _CHTTPGET_HEADER_ + +#define HTTP_STATE_INTERNAL_ERROR 0 +#define HTTP_STATE_SOCKET_ERROR 1 +#define HTTP_STATE_URL_PARSING_ERROR 2 +#define HTTP_STATE_CONNECTING 3 +#define HTTP_STATE_HOST_NOT_FOUND 4 +#define HTTP_STATE_CANT_CONNECT 5 +#define HTTP_STATE_CONNECTED 6 +#define HTTP_STATE_FILE_NOT_FOUND 10 +#define HTTP_STATE_RECEIVING 11 +#define HTTP_STATE_FILE_RECEIVED 12 +#define HTTP_STATE_UNKNOWN_ERROR 13 +#define HTTP_STATE_RECV_FAILED 14 +#define HTTP_STATE_CANT_WRITE_FILE 15 +#define HTTP_STATE_STARTUP 16 + +#define MAX_URL_LEN 300 + +class ChttpGet +{ +public: + ChttpGet(char *URL,char *localfile); + ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport); + ~ChttpGet(); + void GetFile(char *URL,char *localfile); + int GetStatus(); + unsigned int GetBytesIn(); + unsigned int GetTotalBytes(); + void AbortGet(); + void WorkerThread(); + bool m_Aborted; + +protected: + int ConnectSocket(); + char *GetHTTPLine(); + unsigned int ReadDataChannel(); + unsigned int m_iBytesIn; + unsigned int m_iBytesTotal; + unsigned int m_State; + bool m_ProxyEnabled; + char *m_ProxyIP; + char m_URL[MAX_URL_LEN]; + unsigned short m_ProxyPort; + + char m_szUserName[100]; + char m_szPassword[100]; + char m_szHost[200]; + char m_szDir[200]; + char m_szFilename[100]; + + bool m_Aborting; + + + SOCKET m_DataSock; + + FILE *LOCALFILE; + char recv_buffer[1000]; + +}; + + + + + + + + + + +#endif + diff --git a/include/circle.h b/include/circle.h new file mode 100644 index 0000000..b9d4e19 --- /dev/null +++ b/include/circle.h @@ -0,0 +1,39 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Circle.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for circle.cpp + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 5 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 4 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#ifndef _CIRCLE_H +#define _CIRCLE_H + +extern void gr8_circle( int xc, int yc, int d ); + +#endif + diff --git a/include/cmdbrief.h b/include/cmdbrief.h new file mode 100644 index 0000000..0b40121 --- /dev/null +++ b/include/cmdbrief.h @@ -0,0 +1,92 @@ +/* + * $Logfile: /Freespace2/code/FRED2/CmdBrief.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Command Briefing Editor + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 4 4/20/98 4:40p Hoffoss + * Added a button to 4 editors to play the chosen wave file. + * + * 3 3/19/98 4:24p Hoffoss + * Added remaining support for command brief screen (ANI and WAVE file + * playing). + * + * 2 3/05/98 3:59p Hoffoss + * Added a bunch of new command brief stuff, and asteroid initialization + * to Fred. + * + * $NoKeywords: $ + */ + +#include "missioncmdbrief.h" + +///////////////////////////////////////////////////////////////////////////// +// cmd_brief_dlg dialog + +class cmd_brief_dlg : public CDialog +{ +// Construction +public: + cmd_brief_dlg(CWnd* pParent = NULL); // standard constructor + void update_data(int update = 1); + void OnOK(); + +// Dialog Data + //{{AFX_DATA(cmd_brief_dlg) + enum { IDD = IDD_CMD_BRIEF }; + CString m_ani_filename; + CString m_text; + CString m_stage_title; + CString m_wave_filename; + //}}AFX_DATA + + CBitmap m_play_bm; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(cmd_brief_dlg) + public: + virtual BOOL DestroyWindow(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + int m_cur_stage; + int m_last_stage; + cmd_brief *last_cmd_brief; + + void copy_stage(int from, int to); + + // Generated message map functions + //{{AFX_MSG(cmd_brief_dlg) + virtual BOOL OnInitDialog(); + afx_msg void OnNext(); + afx_msg void OnPrev(); + afx_msg void OnAddStage(); + afx_msg void OnInsertStage(); + afx_msg void OnDeleteStage(); + afx_msg void OnBrowseAni(); + afx_msg void OnBrowseWave(); + afx_msg void OnPlay(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/cmdline.h b/include/cmdline.h new file mode 100644 index 0000000..c6740ff --- /dev/null +++ b/include/cmdline.h @@ -0,0 +1,180 @@ +/* + * $Logfile: /Freespace2/code/Cmdline/cmdline.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 8 8/26/99 8:51p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 7 7/15/99 3:07p Dave + * 32 bit detection support. Mouse coord commandline. + * + * 6 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 5 6/22/99 9:37p Dave + * Put in pof spewing. + * + * 4 1/12/99 5:45p Dave + * Moved weapon pipeline in multiplayer to almost exclusively client side. + * Very good results. Bandwidth goes down, playability goes up for crappy + * connections. Fixed object update problem for ship subsystems. + * + * 3 11/17/98 11:12a Dave + * Removed player identification by address. Now assign explicit id #'s. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 27 9/15/98 4:04p Allender + * added back in the -ip_addr command line switch because it needs to be + * in the standalone server only executable + * + * 26 9/14/98 11:28a Allender + * support for server bashing of address when received from client. Added + * a cmdline.cfg file to process command line arguments from a file + * + * 25 9/08/98 2:20p Allender + * temporary code to force IP address to a specific value. + * + * 24 8/20/98 5:30p Dave + * Put in handy multiplayer logfile system. Now need to put in useful + * applications of it all over the code. + * + * 23 8/07/98 10:40a Allender + * new command line flags for starting netgames. Only starting currently + * works, and PXO isn't implemented yet + * + * 22 7/24/98 11:14a Allender + * start of new command line options for version 1.04 + * + * 21 5/21/98 1:50a Dave + * Remove obsolete command line functions. Reduce shield explosion packets + * drastically. Tweak PXO screen even more. Fix file xfer system so that + * we can guarantee file uniqueness. + * + * 20 5/18/98 9:10p Dave + * Put in many new PXO features. Fixed skill level bashing in multiplayer. + * Removed several old command line options. Put in network config files. + * + * 19 5/09/98 7:16p Dave + * Put in CD checking. Put in standalone host password. Made pilot into + * popup scrollable. + * + * 18 4/23/98 8:27p Allender + * basic support for cutscene playback. Into movie code in place. Tech + * room can view cutscenes stored in CDROM_dir variable + * + * 17 3/14/98 2:48p Dave + * Cleaned up observer joining code. Put in support for file xfers to + * ingame joiners (observers or not). Revamped and reinstalled pseudo + * lag/loss system. + * + * 16 1/31/98 4:32p Dave + * Put in new support for VMT player validation, game logging in and game + * logging out. + * + * 15 12/10/97 4:45p Dave + * Added in more detailed support for multiplayer packet lag/loss. Fixed + * some multiplayer stuff. Added some controls to the standalone. + * + * 14 12/09/97 6:14p Lawrance + * add -nomusic flag + * + * 13 11/28/97 7:04p Dave + * Emergency checkin due to big system crash. + * + * 12 11/28/97 5:06p Dave + * Put in facilities for simulating multiplayer lag. + * + * 11 11/24/97 5:42p Dave + * Fixed a file xfer buffer free/malloc problem. Lengthened command line + * switch string parse length. + * + * 10 11/12/97 4:39p Dave + * Put in multiplayer campaign support parsing, loading and saving. Made + * command-line variables better named. Changed some things on the initial + * pilot select screen. + * + * 9 11/11/97 4:54p Dave + * Put in support for single vs. multiplayer pilots. Put in initial player + * selection screen (no command line option yet). Started work on + * multiplayer campaign file save gaming. + * + * 8 9/18/97 10:13p Dave + * Added -gimmemedals, which gives the current pilot all the medals in the + * game (debug) + * + * 7 9/18/97 9:20a Dave + * Minor modifications + * + * 6 9/15/97 11:40p Lawrance + * remove demo granularity switch + * + * 5 9/03/97 5:03p Lawrance + * add support for -nosound command line parm + * + * 4 8/21/97 4:55p Dave + * Added a switch for multiplayer chat streaming. Added a section for + * global command line vars. + * + * 3 8/06/97 2:26p Dave + * Made the command line parse more robust. Made it easier to add and + * process new command-line switches. + * + * 2 8/04/97 3:13p Dave + * Added command line functions. See cmdline.cpp for directions on adding + * new switches + * + * 1 8/04/97 9:58a Dave + * + * $NoKeywords: $ + */ + +#ifndef FS_CMDLINE_HEADER_FILE +#define FS_CMDLINE_HEADER_FILE + +int parse_cmdline(char *cmdline); + +// COMMAND LINE SETTINGS +// This section is for reference by all the *_init() functions. For example, the multiplayer init function +// could check to see if (int Cmdline_multi_stream_chat_to_file) has been set by the command line parser. +// +// Add any extern definitions here and put the actual variables inside of cmdline.cpp for ease of use +// Also, check to make sure anything you add doesn't break Fred or TestCode + +extern int Cmdline_multi_stream_chat_to_file; +extern int Cmdline_freespace_no_sound; +extern int Cmdline_freespace_no_music; +extern int Cmdline_gimme_all_medals; +extern int Cmdline_use_last_pilot; +extern int Cmdline_cd_check; +extern int Cmdline_start_netgame; +extern int Cmdline_closed_game; +extern int Cmdline_restricted_game; +extern int Cmdline_network_port; +extern char *Cmdline_game_name; +extern char *Cmdline_game_password; +extern char *Cmdline_rank_above; +extern char *Cmdline_rank_below; +extern char *Cmdline_connect_addr; +extern int Cmdline_multi_log; +extern int Cmdline_server_firing; +extern int Cmdline_client_dodamage; +extern int Cmdline_spew_pof_info; +extern int Cmdline_force_32bit; +extern int Cmdline_mouse_coords; +extern int Cmdline_timeout; + +extern int Cmdline_window; + +#endif + diff --git a/include/cmeasure.h b/include/cmeasure.h new file mode 100644 index 0000000..bb47b45 --- /dev/null +++ b/include/cmeasure.h @@ -0,0 +1,162 @@ +/* + * $Logfile: /Freespace2/code/CMeasure/CMeasure.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Counter measures. Created by Mike Kulas, May 12, 1997. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 20 4/11/98 5:17p Jim + * Reduced countermeasure firing rate from 2 times per second to 3 times + * per second. + * + * 19 4/10/98 11:02p Mike + * Make countermeasures less effective against aspect seekers than against + * heat seekers. + * Make AI ships match bank with each other when attacking a faraway + * target. + * Make ships not do silly loop-de-loop sometimes when attacking a faraway + * target. + * + * 18 4/01/98 5:34p John + * Made only the used POFs page in for a level. Reduced some interp + * arrays. Made custom detail level work differently. + * + * 17 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 16 2/23/98 4:30p Mike + * Make homing missiles detonate after they pass up their target. Make + * countermeasures less effective. + * + * 15 2/09/98 8:04p Lawrance + * Add objnum to cmeasure struct, correctly set source_objnum + * + * 14 2/05/98 11:20p Lawrance + * save/restore countermeasure data + * + * 13 1/29/98 11:48a John + * Added new counter measure rendering as model code. Made weapons be + * able to have impact explosion. + * + * 12 1/16/98 11:43a Mike + * Fix countermeasures. + * + * 11 8/13/97 9:50p Allender + * split *_move into *_process_pre and *_process_post functions. + * process_pre functions called before object is moved. _process_post + * functions called after object is moved. Reordered code in ship_post + * and weapon_post for multiplayer + * + * 10 8/13/97 4:45p Allender + * fixed ship_fire_primary and fire_secondary to not take parameter for + * determining whether to count ammo or not. Fixed countermeasure firing + * for multiplayer + * + * 9 8/08/97 4:29p Allender + * countermeasure stuff for multiplayer + * + * 8 7/09/97 12:04a Mike + * Error prevention in matrix_interpolate(). + * + * 7 6/24/97 10:04a Allender + * major multiplayer improvements. Better sequencing before game. + * Dealing with weapon/fireball/counter measure objects between + * client/host. + * + * 6 5/22/97 5:45p Mike + * Better countermeasure firing, key off availability, specify in + * weapons.tbl + * + * 5 5/15/97 5:05p Mike + * In the midst of changing subsystem targetnig from type-based to + * pointer-based. + * Also allowed you to view a target while dead. + * + * 4 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 3 5/14/97 10:50a Mike + * More countermeasure stuff. + * + * 2 5/12/97 5:58p Mike + * Add countermeasures. + * + * 1 5/12/97 2:23p Mike + * + * $NoKeywords: $ + */ + +#ifndef _CMEASURE_H +#define _CMEASURE_H + +#include "parselo.h" +#include "object.h" + +#define MAX_CMEASURES 64 + +// Goes in cmeasure.subtype +#define CMEASURE_UNUSED -1 + +#define MAX_CMEASURE_TYPES 3 + +#define CMEASURE_WAIT 333 // delay in milliseconds between countermeasure firing. + +#define CMF_DUD_HEAT 0x01 // If set, this cmeasure is a dud to heat seekers. Set at create time. +#define CMF_DUD_ASPECT 0x02 // If set, this cmeasure is a dud to aspect seekers. Set at create time. + +#define CMF_DUD (CMF_DUD_HEAT | CMF_DUD_ASPECT) + +// Maximum distance at which a countermeasure can be tracked +// If this value is too large, missiles will always be tracking countermeasures. +#define MAX_CMEASURE_TRACK_DIST 300.0f + +typedef struct cmeasure_info { + char cmeasure_name[NAME_LENGTH]; + float max_speed; // launch speed, relative to ship + float fire_wait; // time between launches + float life_min, life_max; // lifetime will be in range min..max. + int launch_sound; // Sound played when launched. + char pof_name[NAME_LENGTH]; + int model_num; // What this renders as +} cmeasure_info; + +typedef struct cmeasure { + int flags; // You know, flag type things. + int subtype; // See CMEASURE_??? defines + int objnum; + int source_objnum; // What object this came from + int source_sig; // Signature of the source object + int team; // Team of the ship where the cmeasure came from + float lifeleft; // When 0 or less object dies +} cmeasure; + +extern cmeasure_info Cmeasure_info[MAX_CMEASURE_TYPES]; +extern cmeasure Cmeasures[MAX_CMEASURES]; +extern int Num_cmeasure_types; +extern int Num_cmeasures; +extern int Cmeasures_homing_check; + +extern void cmeasure_init(); +extern void cmeasure_render( object * obj ); +extern void cmeasure_delete( object * obj ); +extern void cmeasure_process_pre( object * obj, float frame_time); +extern void cmeasure_process_post( object * obj, float frame_time); +extern int cmeasure_create( object * source_obj, vector *pos, int cm_type, int rand_val = -1 ); +extern void cmeasure_select_next(object *objp); + +#endif // _CMEASURE_H + diff --git a/include/codec1.h b/include/codec1.h new file mode 100644 index 0000000..91bbf85 --- /dev/null +++ b/include/codec1.h @@ -0,0 +1,132 @@ +// Codec1.h +// +// Contains interface definition of VoiCTech (Voice Communications Technology) +// voice encoder/decoder. +// +// Written by Matthew F. Storch, Ph.D., copyright (c) 1998 Volition Inc. + + +////////////////////////////////////////////////////////////////////////////// +// +// Introduction to the VoiCTech encoder +// ------------------------------------ +// The VoiCTech (short for Voice Communication Technology, pronounced +// "voice-tech") audio codec uses two separate algorithm suites: Codec1 +// and LPC-10. Codec1 does compression without using traditional signal +// processing algorithms (expcept for a simple FIR low-pass filter). As +// a result, it is extremely fast, and yields moderate-quality voice at a +// compression ratio of between 3-to-1 and 10-to-1, assuming 11KHz sampled +// data. LPC-10 does significantly more analysis and is therefore slower +// (approximately 3 times slower than Codec1), but achieves substantially +// better compression (25-1) at a comparable quality. (However, the +// *character* of the loss in quality is quite different.) +// +// The externally-callable interface for both algorithm suites is through +// a simple, generic front-end that is prototyped in this file. +// +// codec1.cpp contains the implementions of both the generic interface and +// Codec1. LPC-10 is implemented in a set of files in the LPC10 subdirectory. +// +////////////////////////////////////////////////////////////////////////////// + + +// VoiCTech is only intended to work with 8-bit samples recorded at 11KHz. +// 8KHz should also work. +typedef unsigned char t_Sample; + +// Algorithm suites. +enum t_Code { e_cCodec1, e_cLPC10 }; + +// One of these is passed with every buffer that is transmitted; it contains +// data on how to decode the buffer. +struct t_CodeInfo { t_Code Code; double Gain; }; + + +// WARNING ***** DANGER ***** WARNING ***** DANGER ***** WARNING ***** DANGER +// If LPC-10 is used, all buffers must be a multiple of LFRAME (Frame length +// for LPC-10 codec) in length +#define LFRAME 180 +// WARNING ***** DANGER ***** WARNING ***** DANGER ***** WARNING ***** DANGER + + +////////////////////////////////////////////////////////////////////////////// +// InitDecoder must be called once to initialize the decoder. +// Input: +// QoS - Quality of Service: 1..10, 1 = highest compression/lowest +// quality, 10 = worst compression/best quality (currently +// *not* used by decoder) +// tempBuf - Temporary buffer that must be available until Encode() will +// no longer be called. Must be at least as large as the +// largest sizeIn that will be passed to Encode(). +// Output: +// initializes global variables +// +void InitDecoder(int QoS, t_Sample* tempBuf); +// +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// Decode is the main decoder entry point. +// Input: +// codeInfo - Information about how the buffer was encoded +// bufIn - Encoded data to decode +// bufOut - Empty buffer in which to place decoded data (should be +// at least decodeSize in length) +// encodeSize - Size in bytes of encoded data in bufIn +// decodeSize - Size data should be after decoding +// Output: +// bufOut - Decoded data written here +// returns - Nothing +// +void Decode(t_CodeInfo* codeInfo, t_Sample* bufIn, t_Sample* bufOut, + int encodeSize, int decodeSize); +// +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// InitEncoder must be called once to initialize the encoder. May safely be +// called again to change tuning parameters. +// Input: +// code - The algorithm that will be used to encode the data in +// subsequent calls to Encode() +// QoS - Quality of Service: 1..10, 1 = highest compression/lowest +// quality, 10 = worst compression/best quality +// tempBuf1 - Temporary buffer that must be available until Encode() will +// no longer be called. Must be at least as large as the +// largest sizeIn that will be passed to Encode(). +// tempBuf2 - Another temporary buffer (cannot be the same buffer as +// tempBuf1) with same lifetime and size as tempBuf1. +// Output: +// initializes global variables +// +void InitEncoder(t_Code code, int QoS, t_Sample* tempBuf1, t_Sample* tempBuf2); +// +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// Encode is the main Encoder entry point. +// Input: +// bufIn - encoded data to decode +// bufOut - empty buffer in which to place decoded data +// sizeIn - size of bufIn in bytes +// sizeOut - size of bufOut in bytes +// Output: +// bufOut - encoded data written here +// returns - number of bytes written to bufOut +// +#if defined(CODEC_DEMO) +// Test program version +int Encode(t_Sample* bufIn, t_Sample* bufOut, int sizeIn, int sizeOut, + t_CodeInfo* codeInfo, + t_Sample* levels, int* modes, int samples[9], int storage[9]); +#else +// Release version +int Encode(t_Sample* bufIn, t_Sample* bufOut, int sizeIn, int sizeOut, + t_CodeInfo* codeInfo); +#endif +// +////////////////////////////////////////////////////////////////////////////// + diff --git a/include/colors.h b/include/colors.h new file mode 100644 index 0000000..f47ad50 --- /dev/null +++ b/include/colors.h @@ -0,0 +1,53 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Colors.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Functions to deal with colors & alphacolors + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 1/14/99 12:48a Dave + * Todo list bug fixes. Made a pass at putting briefing icons back into + * FRED. Sort of works :( + * + * 4 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 2 2/07/98 7:50p John + * Added code so that we can use the old blending type of alphacolors if + * we want to. Made the stars use them. + * + * 1 6/17/97 12:01p John + * + * $NoKeywords: $ + */ + +#ifndef _COLORS_H +#define _COLORS_H + +struct alphacolor_old; + +void grx_init_alphacolors(); +void grx_init_color( color *clr, int r, int g, int b ); +void grx_init_alphacolor( color *clr, int r, int g, int b, int alpha, int type ); +void grx_set_color( int r, int g, int b ); +void grx_set_color_fast( color *clr ); +void grx_get_color( int *r, int *g, int *b ); + +void calc_alphacolor_old(alphacolor_old *ac); + +#endif + diff --git a/include/contexthelp.h b/include/contexthelp.h new file mode 100644 index 0000000..2e16df0 --- /dev/null +++ b/include/contexthelp.h @@ -0,0 +1,55 @@ +// ContextHelp.h +// +// + +#ifndef __CONTEXTHELP_H__ +#define __CONTEXTHELP_H__ + +// Help overlays +// +// Adding a help overlay: 1) Add a #define that uniquely identifies your help overlay +// 2) Increment MAX_HELP_OVERLAYS +// 3) Add the filename for the help overlay to Help_overlays[] array in ContextHelp.cpp +// +#define MAX_HELP_OVERLAYS 16 // Must be kept current + +#define SS_OVERLAY 0 // ship selection help +#define WL_OVERLAY 1 // weapons loadout help +#define BR_OVERLAY 2 // briefing help +#define MH_OVERLAY 3 // main hall help +#define BARRACKS_OVERLAY 4 // barracks help +#define CONTROL_CONFIG_OVERLAY 5 // control config help +#define DEBRIEFING_OVERLAY 6 // debriefing help +#define MULTI_CREATE_OVERLAY 7 // multi create game help +#define MULTI_START_OVERLAY 8 // multi start game help overlay +#define MULTI_JOIN_OVERLAY 9 // join game help overlay +#define MH2_OVERLAY 10 // main hall 2 help overlay +#define HOTKEY_OVERLAY 11 // hotkey assignment help overlay +#define CAMPAIGN_ROOM_OVERLAY 12 // campaign room help overlay +#define SIM_ROOM_OVERLAY 13 // sim room help overlay +#define TECH_ROOM_OVERLAY 14 // tech room (general) help overlay +#define CMD_BRIEF_OVERLAY 15 // command briefing help overlay + +// other help overlay constants +#define HELP_MAX_ITEM 50 // max number of screen elements per element type per overlay +#define HELP_PADDING 1 // +#define HELP_MAX_STRING_LENGTH 128 // max string length for text overlay element +#define HELP_MAX_PLINE_VERTICES 21 // good for 20 segments, can prolly reduce this (FIXME) +#define HELP_PLINE_THICKNESS 2 +#define HELP_OVERLAY_FILENAME "help.tbl" + +// help overlay calls +int help_overlay_active(int overlay_id); +void help_overlay_set_state(int overlay_id, int state); +void help_overlay_load(int overlay_id); +void help_overlay_unload(int overlay_id); +void help_overlay_maybe_blit(int overlay_id); + +void context_help_init(); // called once at game startup +void context_help_grey_screen(); // call to grey out a screen (normally when applying a help overlay) + +void launch_context_help(); + + +#endif /* __CONTEXTHELP_H__ */ + diff --git a/include/controlsconfig.h b/include/controlsconfig.h new file mode 100644 index 0000000..35fcf8f --- /dev/null +++ b/include/controlsconfig.h @@ -0,0 +1,481 @@ +/* + * $Logfile: /Freespace2/code/ControlConfig/ControlsConfig.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for keyboard, joystick and mouse configuration + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 6 8/22/99 5:53p Dave + * Scoring fixes. Added self destruct key. Put callsigns in the logfile + * instead of ship designations for multiplayer players. + * + * 5 8/19/99 10:59a Dave + * Packet loss detection. + * + * 4 8/01/99 12:39p Dave + * Added HUD contrast control key (for nebula). + * + * 3 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 26 6/13/98 5:19p Hoffoss + * externalized control config texts. + * + * 25 5/15/98 8:37p Lawrance + * Add 'equalize recharge rates' and 'target ship that sent last + * transmission' key bindings + * + * 24 5/14/98 5:32p Hoffoss + * Improved axis binding code some more. + * + * 23 5/13/98 7:15p Hoffoss + * Fixed remaining bugs with axis binding. + * + * 22 5/13/98 1:17a Hoffoss + * Added joystick axes configurability. + * + * 21 4/30/98 9:43p Hoffoss + * Added new actions for free look movement which are default bounded to + * the hat. + * + * 20 4/25/98 1:59p Lawrance + * fix merge typo + * + * 19 4/25/98 1:24p Lawrance + * Add time compression keys to key config + * + * 18 4/25/98 12:43p Allender + * added new shortcut key for attack my subsystem + * + * 17 4/18/98 5:00p Dave + * Put in observer zoom key. Made mission sync screen more informative. + * + * 16 4/10/98 12:47p Allender + * changed working on replay popup. Don't reference repair in comm menu. + * Added Shift-R for repair me + * + * 15 4/08/98 11:11a Hoffoss + * Fixed some bugs that showed up due to fixing other bugs the other day + * with controls. + * + * 14 3/31/98 4:15p Hoffoss + * Disabled the show objectives action. + * + * 13 3/23/98 12:27p Hoffoss + * Whoops, changed order in list but forget to actually change the number. + * + * 12 3/23/98 11:35a Hoffoss + * Fixed #defines that were out of sync. + * + * 11 3/19/98 5:04p Dave + * Put in support for targeted multiplayer text and voice messaging (all, + * friendly, hostile, individual). + * + * 10 3/11/98 5:28p Hoffoss + * Added control config debug display info to possibly aid in tracking + * down a bug. + * + * 9 2/28/98 7:02p Lawrance + * overhaul on-line help + * + * 8 2/26/98 12:33a Lawrance + * Added back slew mode, lots of changes to external and chase views. + * + * 7 2/02/98 6:59p Lawrance + * Adding new targeting keys (bomb, uninspected cargo, new ship, live + * turrets). + * + * 6 1/30/98 10:40a Lawrance + * remove any binding references to hotkey screen + * + * 5 1/28/98 6:19p Dave + * Reduced standalone memory usage ~8 megs. Put in support for handling + * multiplayer submenu handling for endgame, etc. + * + * 4 1/22/98 4:53p Hoffoss + * Made training messages/directives display a joystick button in place of + * a keypress if there is no keypress bound to the action. + * + * 3 1/02/98 4:42p Allender + * removed unused key bindings from control config list + * + * 2 12/30/97 4:47p Allender + * work with ignore my target command. Added new keyboard hotkey. Made + * it work globally + * + * 1 12/24/97 11:59a Hoffoss + * + * 41 11/24/97 10:20p Lawrance + * Add key 'KEY_N' to target next ship on monitoring view + * + * 40 11/21/97 5:57p Hoffoss + * Fixed bug where timef type controls weren't checking joystick buttons. + * + * 39 11/20/97 1:08a Lawrance + * add support for 'R' key - target closest friendly repair ship + * + * 38 11/17/97 6:40p Lawrance + * Changed 'I' key to toggle of extended target info, moved target closest + * locked missile to 'L' + * + * 37 11/02/97 1:15p Hoffoss + * Externed an array I need elsewhere. + * + * 36 10/28/97 12:12a Lawrance + * remove unused keys (Alt-H and Alt-F) + * + * 35 10/24/97 11:00p Hoffoss + * Controls config screen much improved. + * + * 34 10/22/97 4:50p Hoffoss + * Disabled throttle by default. + * + * 33 10/21/97 7:05p Hoffoss + * Overhauled the key/joystick control structure and usage throughout the + * entire FreeSpace code. The whole system is very different now. + * + * 32 10/18/97 7:19p Hoffoss + * Added timestamp recording when a key is pressed. + * + * 31 9/14/97 10:24p Lawrance + * add damage screen popup window + * + * 30 9/10/97 6:02p Hoffoss + * Added code to check for key-pressed sexp operator in FreeSpace as part + * of training mission stuff. + * + * 29 9/09/97 9:18a Hoffoss + * Added message token translation (phase 1) to FreeSpace for training + * missions support. + * + * 28 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 27 8/29/97 5:02p Sandeep + * fixed joystick support for shield config + * + * 26 8/25/97 12:24a Lawrance + * added keys for shield management + * + * 25 8/22/97 10:44p Sandeep + * Added Configurable support for energy management keys, keyboard and + * joystick + * + * 24 8/21/97 5:03p Sandeep + * Fixed joystick functions past "Target Closest Hostile Targeting Self" + * bug. + * + * 23 8/20/97 4:22p Sandeep + * Add support for joystick mapping to all keyboard configurable commands. + * Changed pilot file to allow support for new joystick, incompatible + * version 33 + * + * 22 8/07/97 11:27p Lawrance + * add support for 4th joystick axis (rudder), add new option to roll ship + * + * 21 7/08/97 11:37a Lawrance + * add joystick support for afterburners + * + * 20 7/01/97 11:52a Lawrance + * add targeting key, allow player to clear all bindings + * + * 19 6/24/97 12:38a Lawrance + * added in some new joystick bindings + * + * 18 6/17/97 8:57p Lawrance + * added target previous hostile/friendly + * + * 17 6/03/97 1:39a Allender + * made macros for joystick button id's + * + * 16 5/28/97 9:53a Lawrance + * move goals keys and squadmate message key from function keys to + * assignable keys + * + * 15 5/26/97 5:49p Lawrance + * supporting max range on radar + * + * 14 5/26/97 3:31p Lawrance + * assign key bindings for view modes and view controls + * + * 13 5/14/97 10:50a Mike + * More countermeasure stuff. + * + * 12 4/24/97 2:40p Lawrance + * add key for targeting closest ship attacking self + * + * 11 4/05/97 3:46p Allender + * lots 'o messaging stuff. Make shortcut keys for squadmate messaging + * work. Make menus a little more dynamic + * + * 10 4/01/97 7:50p Lawrance + * added many new keys, don't allow Shft or Alt as action keys, only allow + * Ctrl as an action key + * + * 9 3/10/97 8:30a Lawrance + * changed -/+ 5% throttle keys to 1/3 2/3 throttle keys + * + * 8 12/18/96 6:09p Lawrance + * allowing shift+alt+ctrl modifiers on keyboard bindings + * + * 7 12/18/96 10:18a Lawrance + * integrating joystick axis configuration + * + * 6 12/17/96 11:10a Lawrance + * added targeting of subsystem in reticle + * + * 5 12/06/96 12:27a Lawrance + * adding support for joystick configuration + * + * 4 12/05/96 2:47p Lawrance + * changed how keyboard configuration was done to make code more easily + * changeable + * + * 3 12/03/96 10:11p Lawrance + * Integrating keyboard configuration + * + * 2 12/03/96 3:45p Lawrance + * + * $NoKeywords: $ + * +*/ + +#ifndef CONTROLS_CONFIG_H +#define CONTROLS_CONFIG_H + +#define CONTROL_CONFIG_XSTR 507 + +#define JOY_X_AXIS 0 +#define JOY_Y_AXIS 1 +#define JOY_Z_AXIS 2 +#define JOY_RX_AXIS 3 +#define JOY_RY_AXIS 4 +#define JOY_RZ_AXIS 5 + +#define NUM_JOY_AXIS_ACTIONS 5 + +#define JOY_HEADING_AXIS 0 +#define JOY_PITCH_AXIS 1 +#define JOY_BANK_AXIS 2 +#define JOY_ABS_THROTTLE_AXIS 3 +#define JOY_REL_THROTTLE_AXIS 4 + +// -------------------------------------------------- +// different types of controls that can be assigned +// -------------------------------------------------- + +#define CC_TYPE_TRIGGER 0 +#define CC_TYPE_CONTINUOUS 1 + +typedef struct config_item { + short key_default; // default key bound to action + short joy_default; // default joystick button bound to action + char tab; // what tab (catagory) it belongs in + char *text; // describes the action in the config screen + char type; // manner control should be checked in + short key_id; // actual key bound to action + short joy_id; // joystick button bound to action + int used; // has control been used yet in mission? If so, this is the timestamp +} config_item; + +// -------------------------------------------------- +// Keyboard #defines for the actions +// This is the value of the id field in config_item +// -------------------------------------------------- + +// targeting a ship + +#define TARGET_NEXT 0 +#define TARGET_PREV 1 +#define TARGET_NEXT_CLOSEST_HOSTILE 2 +#define TARGET_PREV_CLOSEST_HOSTILE 3 +#define TOGGLE_AUTO_TARGETING 4 +#define TARGET_NEXT_CLOSEST_FRIENDLY 5 +#define TARGET_PREV_CLOSEST_FRIENDLY 6 +#define TARGET_SHIP_IN_RETICLE 7 +#define TARGET_CLOSEST_SHIP_ATTACKING_TARGET 8 +#define TARGET_LAST_TRANMISSION_SENDER 9 +#define STOP_TARGETING_SHIP 10 + +// targeting a ship's subsystem +#define TARGET_SUBOBJECT_IN_RETICLE 11 +#define TARGET_NEXT_SUBOBJECT 12 +#define TARGET_PREV_SUBOBJECT 13 +#define STOP_TARGETING_SUBSYSTEM 14 + +// speed matching +#define MATCH_TARGET_SPEED 15 +#define TOGGLE_AUTO_MATCH_TARGET_SPEED 16 + +// weapons +#define FIRE_PRIMARY 17 +#define FIRE_SECONDARY 18 +#define CYCLE_NEXT_PRIMARY 19 +#define CYCLE_PREV_PRIMARY 20 +#define CYCLE_SECONDARY 21 +#define CYCLE_NUM_MISSLES 22 +#define LAUNCH_COUNTERMEASURE 23 + +// controls +#define FORWARD_THRUST 24 +#define REVERSE_THRUST 25 +#define BANK_LEFT 26 +#define BANK_RIGHT 27 +#define PITCH_FORWARD 28 +#define PITCH_BACK 29 +#define YAW_LEFT 30 +#define YAW_RIGHT 31 + +// throttle control +#define ZERO_THROTTLE 32 +#define MAX_THROTTLE 33 +#define ONE_THIRD_THROTTLE 34 +#define TWO_THIRDS_THROTTLE 35 +#define PLUS_5_PERCENT_THROTTLE 36 +#define MINUS_5_PERCENT_THROTTLE 37 + +// squadmate messaging keys +#define ATTACK_MESSAGE 38 +#define DISARM_MESSAGE 39 +#define DISABLE_MESSAGE 40 +#define ATTACK_SUBSYSTEM_MESSAGE 41 +#define CAPTURE_MESSAGE 42 +#define ENGAGE_MESSAGE 43 +#define FORM_MESSAGE 44 +#define IGNORE_MESSAGE 45 +#define PROTECT_MESSAGE 46 +#define COVER_MESSAGE 47 +#define WARP_MESSAGE 48 +#define REARM_MESSAGE 49 + +#define TARGET_CLOSEST_SHIP_ATTACKING_SELF 50 + +// Views +#define VIEW_CHASE 51 +#define VIEW_EXTERNAL 52 +#define VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK 53 +#define VIEW_SLEW 54 +#define VIEW_OTHER_SHIP 55 +#define VIEW_DIST_INCREASE 56 +#define VIEW_DIST_DECREASE 57 +#define VIEW_CENTER 58 +#define PADLOCK_UP 59 +#define PADLOCK_DOWN 60 +#define PADLOCK_LEFT 61 +#define PADLOCK_RIGHT 62 + + +#define RADAR_RANGE_CYCLE 63 +#define SQUADMSG_MENU 64 +#define SHOW_GOALS 65 +#define END_MISSION 66 +#define TARGET_TARGETS_TARGET 67 +#define AFTERBURNER 68 + +#define INCREASE_WEAPON 69 +#define DECREASE_WEAPON 70 +#define INCREASE_SHIELD 71 +#define DECREASE_SHIELD 72 +#define INCREASE_ENGINE 73 +#define DECREASE_ENGINE 74 +#define ETS_EQUALIZE 75 +#define SHIELD_EQUALIZE 76 +#define SHIELD_XFER_TOP 77 +#define SHIELD_XFER_BOTTOM 78 +#define SHIELD_XFER_LEFT 79 +#define SHIELD_XFER_RIGHT 80 + +#define XFER_SHIELD 81 +#define XFER_LASER 82 +#define SHOW_DAMAGE_POPUP 83 // AL: this binding should be removing next time the controls are reorganized + +#define BANK_WHEN_PRESSED 84 +#define SHOW_NAVMAP 85 +#define ADD_REMOVE_ESCORT 86 +#define ESCORT_CLEAR 87 +#define TARGET_NEXT_ESCORT_SHIP 88 + +#define TARGET_CLOSEST_REPAIR_SHIP 89 +#define TARGET_NEXT_UNINSPECTED_CARGO 90 +#define TARGET_PREV_UNINSPECTED_CARGO 91 +#define TARGET_NEWEST_SHIP 92 +#define TARGET_NEXT_LIVE_TURRET 93 +#define TARGET_PREV_LIVE_TURRET 94 + +#define TARGET_NEXT_BOMB 95 +#define TARGET_PREV_BOMB 96 + +// multiplayer messaging keys +#define MULTI_MESSAGE_ALL 97 +#define MULTI_MESSAGE_FRIENDLY 98 +#define MULTI_MESSAGE_HOSTILE 99 +#define MULTI_MESSAGE_TARGET 100 + +// multiplayer misc keys +#define MULTI_OBSERVER_ZOOM_TO 101 + +#define TIME_SPEED_UP 102 +#define TIME_SLOW_DOWN 103 + +#define TOGGLE_HUD_CONTRAST 104 + +#define MULTI_TOGGLE_NETINFO 105 + +#define MULTI_SELF_DESTRUCT 106 + +// this should be the total number of control action defines above (or last define + 1) +#define CCFG_MAX 107 + +extern int Failed_key_index; +extern int Invert_heading; +extern int Invert_pitch; +extern int Invert_roll; +extern int Invert_thrust; +extern int Disable_axis2; +extern int Disable_axis3; + +extern int Axis_map_to[]; +extern int Invert_axis[]; +extern int Invert_axis_defaults[]; + +extern config_item Control_config[]; // stores the keyboard configuration +extern char **Scan_code_text; +extern char **Joy_button_text; + +// initialize common control config stuff - call at game startup after localization has been initialized +void control_config_common_init(); + +void control_config_init(); +void control_config_do_frame(float frametime); +void control_config_close(); + +void control_config_cancel_exit(); + +void control_config_reset_defaults(); +int translate_key_to_index(char *key); +char *translate_key(char *key); +char *textify_scancode(int code); +float check_control_timef(int id); +int check_control(int id, int key = -1); +void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr); +void control_used(int id); +void control_config_clear(); +void clear_key_binding(short key); +void control_check_indicate(); +void control_config_clear_used_status(); + +#endif + diff --git a/include/convert.h b/include/convert.h new file mode 100644 index 0000000..36a3ad3 --- /dev/null +++ b/include/convert.h @@ -0,0 +1,94 @@ +/* + * $Logfile: /Freespace2/code/AC/convert.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the conversion of standard animation files to our own ANIM format + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/23/98 6:03p Dave + * + * 1 10/23/98 5:34p Dave + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 7 6/24/98 10:43a Hoffoss + * Changed default to 15 FPS instead of 30 FPS. + * + * 6 4/10/98 5:20p John + * Changed RGB in lighting structure to be ubytes. Removed old + * not-necessary 24 bpp software stuff. + * + * 5 7/20/97 6:58p Lawrance + * supporting new RLE format + * + * 4 5/21/97 11:06a Lawrance + * enabling a user-defined transparent value + * + * 3 5/19/97 3:21p Lawrance + * add fps parm, version num to anim header + * + * 2 2/19/97 9:26p Lawrance + * added force_key_frame global + * + * 1 2/19/97 7:14p Lawrance + * + * 5 2/17/97 3:02p Hoffoss + * Added headers to files, and implemented key frame dialog stuff. + * + * 4 2/14/97 7:33p Lawrance + * added convert_avi_to_anim() function + * + * 3 2/14/97 3:28p Hoffoss + * Wrote functions to save an anim file. + * + * 2 2/13/97 5:55p Lawrance + * reading AVI / decompressing AVI functions in + * + * $NoKeywords: $ + */ + +#include "pstypes.h" + +#ifndef __CONVERT_H__ +#define __CONVERT_H__ + +// !!IMPORTANT!! +// ANIM_HEADER_SIZE is the size (in bytes) of the header. If the header format changes, you +// must update this number. +#define ANIM_HEADER_SIZE 790 + +#define ANIM_BUFFER_MAX 400000 +#define ANIM_VERSION 2 // update this when save format changes + +#define ANIM_DEFAULT_FPS 15 + +// specify the different types of compression that can be used (Compression_type is assigned one of these values) +#define CUSTOM_DELTA_RLE 0 +#define STD_DELTA_RLE 1 + +int convert_avi_to_anim(char* filename); +int convert_frames_to_anim(char *filename); + +typedef struct rgb_triple { + ubyte r, g, b; +} rgb_triple; + +extern int key_frame_rate; +extern int force_key_frame; +extern int Default_fps; +extern rgb_triple Xparent_color; +extern int Use_custom_xparent_color; +extern int Compression_type; + + +#endif /* __CONVERT_H__ */ + diff --git a/include/corkscrew.h b/include/corkscrew.h new file mode 100644 index 0000000..9788bbd --- /dev/null +++ b/include/corkscrew.h @@ -0,0 +1,35 @@ +/* + * $Logfile: /Freespace2/code/Weapon/Corkscrew.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for managing corkscrew missiles + * + * $NoKeywords: $ + */ + + +#ifndef __FREESPACE_CORKSCREW_H__ +#define __FREESPACE_CORKSCREW_H__ + +#include "object.h" +#include "weapon.h" + +extern int Corkscrew_num_missiles_fired; + +void cscrew_level_init(); +void cscrew_delete(int index); +int cscrew_create(object *obj); + +// pre process the corkscrew weapon by putting him in the "center" of his corkscrew +void cscrew_process_pre(object *objp); + +// post process the corkscrew weapon by putting him back to the right spot on his corkscrew +void cscrew_process_post(object *objp); + +// maybe fire another corkscrew-style missile +void cscrew_maybe_fire_missile(int shipnum); + +#endif /* __FREESPACE_CORKSCREW_H__ */ + diff --git a/include/createwingdlg.h b/include/createwingdlg.h new file mode 100644 index 0000000..fa1ee8d --- /dev/null +++ b/include/createwingdlg.h @@ -0,0 +1,37 @@ +// CreateWingDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// create_wing_dlg dialog + +class create_wing_dlg : public CDialog +{ +// Construction +public: + void OnOK(); + create_wing_dlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(create_wing_dlg) + enum { IDD = IDD_WING_CREATE }; + CString m_name; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(create_wing_dlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(create_wing_dlg) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/credits.h b/include/credits.h new file mode 100644 index 0000000..02764c7 --- /dev/null +++ b/include/credits.h @@ -0,0 +1,36 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/Credits.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * C header file for displaying game credits + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 3 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 2 4/22/97 11:06a Lawrance + * credits music playing, credits screen is a separate state + * + * $NoKeywords: $ + */ + +#ifndef __CREDITS_H__ +#define __CREDITS_H__ + +void credits_init(); +void credits_do_frame(float frametime); +void credits_close(); + +#endif /* __CREDITS_H__ */ + diff --git a/include/crypt.h b/include/crypt.h new file mode 100644 index 0000000..118aa01 --- /dev/null +++ b/include/crypt.h @@ -0,0 +1,42 @@ +/* + * $Logfile: /Freespace2/code/GlobalIncs/crypt.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header for crypt stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 8/05/99 2:05a Dave + * Fixes. Optimized detail level stuff. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 3 5/19/98 12:19p Mike + * Cheat codes! + * + * 2 3/30/98 9:33p Allender + * string encryption stuff for cheat codes + * + * 1 3/30/98 3:20p Allender + * + * $NoKeywords: $ + */ + +#ifndef _CRYPT_H +#define _CRYPT_H + +// define for the length of a crypted string +#define CRYPT_STRING_LENGTH 17 + +char *jcrypt (char *plainstring); + +#endif + diff --git a/include/cutscenes.h b/include/cutscenes.h new file mode 100644 index 0000000..7bfb0fa --- /dev/null +++ b/include/cutscenes.h @@ -0,0 +1,76 @@ +/* + * $Logfile: /Freespace2/code/Cutscene/Cutscenes.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for the cutscenes viewer screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 6 5/21/98 12:35a Lawrance + * Tweak how CD is checked for + * + * 5 5/10/98 10:05p Allender + * only show cutscenes which have been seen before. Made Fred able to + * write missions anywhere, defaulting to player misison folder, not data + * mission folder. Fix FreeSpace code to properly read missions from + * correct locations + * + * 4 5/08/98 5:30p Lawrance + * add cutscenes_validate_cd() + * + * 3 5/08/98 4:07p Allender + * more cutscene stuff + * + * 2 4/17/98 6:33p Hoffoss + * Made changes to the tech room group of screens. Cutscenes screen is + * now in a new file. + * + * $NoKeywords: $ + */ + +#ifndef _FREESPACE_CUTSCENES_SCREEN_HEADER_FILE +#define _FREESPACE_CUTSCENES_SCREEN_HEADER_FILE + +#include "parselo.h" + +#define MAX_CUTSCENES 10 + +// this cutscene is always available. +#define INTRO_CUTSCENE_FLAG (1<<0) + +typedef struct cutscene_info +{ + char filename[MAX_FILENAME_LEN]; + char name[NAME_LENGTH]; + char *description; + int cd; +} cutscene_info; + +extern cutscene_info Cutscenes[MAX_CUTSCENES]; +extern int Num_cutscenes; +extern int Cutscenes_viewable; + +// initializa table data +void cutscene_init(); +int cutscene_get_cd_num(char *filename); + + +void cutscenes_screen_init(); +void cutscenes_screen_close(); +void cutscenes_screen_do_frame(); + +int cutscenes_validate_cd(char *mve_name, int prompt_for_cd = 1); +void cutscene_mark_viewable(char *filename); + +#endif + diff --git a/include/debriefingeditordlg.h b/include/debriefingeditordlg.h new file mode 100644 index 0000000..230d158 --- /dev/null +++ b/include/debriefingeditordlg.h @@ -0,0 +1,114 @@ +/* + * $Logfile: /Freespace2/code/FRED2/DebriefingEditorDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Debriefing editor dialog. Used to edit mission debriefings of course. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 9 7/07/98 2:11p Hoffoss + * + * 8 4/20/98 4:40p Hoffoss + * Added a button to 4 editors to play the chosen wave file. + * + * 7 3/17/98 2:06p Hoffoss + * Made enter key not close the dialog box (default windows behavior, even + * when no ok button. Talk about stupid. :) + * + * 6 2/09/98 9:25p Allender + * team v team support. multiple pools and breifings + * + * 5 2/04/98 4:32p Allender + * support for multiple briefings and debriefings. Changes to mission + * type (now a bitfield). Bitfield defs for multiplayer modes + * + * 4 11/10/97 11:58a Johnson + * Added support to debriefing editor for "press cancel to go to reference + * of sexp". + * + * 3 10/14/97 12:06p Hoffoss + * Recoded debriefing editor to utilize new format. + * + * 2 7/08/97 2:03p Hoffoss + * Debriefing editor coded and implemented. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// debriefing_editor_dlg dialog + +class debriefing_editor_dlg : public CDialog +{ +// Construction +public: + void OnOK(); + void update_data(int update = 1); + debriefing_editor_dlg(CWnd* pParent = NULL); // standard constructor + int select_sexp_node; + +// Dialog Data + //{{AFX_DATA(debriefing_editor_dlg) + enum { IDD = IDD_DEBRIEFING_EDITOR }; + sexp_tree m_tree; + CString m_text; + CString m_voice; + CString m_stage_title; + CString m_rec_text; + int m_current_debriefing; + //}}AFX_DATA + + CBitmap m_play_bm; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(debriefing_editor_dlg) + public: + virtual BOOL DestroyWindow(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + //}}AFX_VIRTUAL + +// Implementation +protected: + int m_cur_stage; + int m_last_stage; + int modified; + + void copy_stage(int from, int to, int clear_formula = 0); + + // Generated message map functions + //{{AFX_MSG(debriefing_editor_dlg) + afx_msg void OnNext(); + afx_msg void OnPrev(); + afx_msg void OnBrowse(); + afx_msg void OnAddStage(); + afx_msg void OnDeleteStage(); + afx_msg void OnInsertStage(); + virtual BOOL OnInitDialog(); + afx_msg void OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnClose(); + afx_msg void OnInitMenu(CMenu* pMenu); + afx_msg void OnPlay(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +private: +}; + diff --git a/include/debris.h b/include/debris.h new file mode 100644 index 0000000..0cf8b5b --- /dev/null +++ b/include/debris.h @@ -0,0 +1,195 @@ +/* + * $Logfile: /Freespace2/code/Debris/Debris.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for the pieces of exploding object debris. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 6 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 5 7/01/99 11:44a Dave + * Updated object sound system to allow multiple obj sounds per ship. + * Added hit-by-beam sound. Added killed by beam sound. + * + * 4 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 3 10/16/98 1:22p Andsager + * clean up header files + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 29 4/02/98 6:30p Lawrance + * reduce max debris piece if DEMO defined + * + * 28 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 27 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 26 2/19/98 12:46a Lawrance + * Further work on asteroids. + * + * 25 2/10/98 6:43p Lawrance + * Moved asteroid code to a separate lib. + * + * 24 2/06/98 3:08p Mike + * More asteroid stuff, including resolving conflicts between the two + * asteroid_field structs! + * + * 23 2/05/98 12:51a Mike + * Early asteroid stuff. + * + * 22 1/29/98 5:50p John + * Made electrical arcing on debris pieces be persistent from frame to + * frame + * + * 21 1/21/98 7:18p Lawrance + * Fix bug with creating debris before the mission starts. + * + * 20 12/22/97 9:56p Andsager + * Implement ship:debris collisions. Generalize and move + * ship_ship_or_debris_hit struct from CollideShipShip to ObjCollide.h + * + * 19 11/19/97 5:57p Mike + * Make debris go away if more than N units away from the nearest player. + * Make speed of a docked object not read zero if it's moving. + * + * 18 10/01/97 10:52a Mike + * Change debris prototype to take an additional parameter to give debris + * more thrust when it is created. + * + * 17 9/20/97 9:25a John + * fixed bug with species specific debris shards. + * + * 16 8/13/97 9:50p Allender + * split *_move into *_process_pre and *_process_post functions. + * process_pre functions called before object is moved. _process_post + * functions called after object is moved. Reordered code in ship_post + * and weapon_post for multiplayer + * + * 15 7/24/97 8:35a Allender + * allow ships to blow up before missions starts. Made some debris system + * items available in other modules + * + * 14 7/13/97 5:53p Lawrance + * save model name in debris + * + * 13 7/02/97 11:48a Lawrance + * update debris to delay persistant sound + * + * 12 6/06/97 4:13p Lawrance + * leave debris chunks from very large ships persistant, scale fireball + * lifetime by ship radius + * + * 11 6/06/97 12:12p Lawrance + * limit hull debris pieces, destroying oldest debris to make way for new + * debris. + * + * 10 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 9 4/03/97 12:23a Mike + * Make debris collidable-with. Can hit it, too. + * Make AI ships stop firing when target enters death roll. + * + * 8 3/31/97 11:11a John + * added ship species specific debris. + * + * 7 3/25/97 3:55p Lawrance + * allowing debris to be targeted and shown on radar + * + * 6 2/27/97 12:07p John + * Added code to suppord debris chunks after a ship is exploded.. + * + * 5 2/26/97 2:18p Lawrance + * adding Debris[] to save/restore + * + * 4 2/10/97 12:38p John + * made all ships blow up into debris pieces when exploded. + * + * 3 2/07/97 10:45a John + * Initial bare bones implementation of blowing off turrets. + * + * 2 2/07/97 9:30a John + * + * 1 2/06/97 4:13p John + * + * $NoKeywords: $ + */ + +#ifndef _DEBRIS_H +#define _DEBRIS_H + +struct object; +struct CFILE; + +#define MAX_DEBRIS_ARCS 8 // Must be less than MAX_ARC_EFFECTS in model.h + +typedef struct debris { + debris *next, *prev; // used for a linked list of the hull debris chunks + int flags; // See DEBRIS_??? defines + int source_objnum; // What object this came from + int source_sig; // Signature of the source object + int ship_info_index; // Ship info index of the ship type debris came from + int team; // Team of the ship where the debris came from + int objnum; // What object this is linked to + float lifeleft; // When 0 or less object dies + int model_num; // What model this uses + int submodel_num; // What submodel this uses + int next_fireball; // When to start a fireball + int is_hull; // indicates a large hull chunk of debris + int species; // What species this is from. -1 if don't care. + int fire_timeout; // timestamp that holds time for fireballs to stop appearing + int sound_delay; // timestamp to signal when sound should start + fix time_started; // time when debris was created + int next_distance_check; // timestamp to determine whether to delete this piece of debris. + + vector arc_pts[MAX_DEBRIS_ARCS][2]; // The endpoints of each arc + int arc_timestamp[MAX_DEBRIS_ARCS]; // When this times out, the spark goes away. -1 is not used + int arc_frequency; // Starts at 0, gets bigger + +} debris; + + +// flags for debris pieces +#define DEBRIS_USED (1<<0) +#define DEBRIS_EXPIRE (1<<1) // debris can expire (ie hull chunks from small ships) + + +#ifdef FS2_DEMO + #define MAX_DEBRIS_PIECES 48 +#else + #define MAX_DEBRIS_PIECES 64 +#endif + +extern debris Debris[MAX_DEBRIS_PIECES]; + +extern int Num_debris_pieces; + +struct collision_info_struct; + +void debris_init(); +void debris_render( object * obj ); +void debris_delete( object * obj ); +void debris_process_pre( object * obj, float frame_time); +void debris_process_post( object * obj, float frame_time); +object *debris_create( object * source_obj, int model_num, int submodel_num, vector *pos, vector *exp_center, int hull_flag, float exp_force ); +int debris_check_collision( object * obj, object * other_obj, vector * hitpos, collision_info_struct *debris_hit_info=NULL ); +void debris_hit( object * debris_obj, object * other_obj, vector * hitpos, float damage ); +int debris_get_team(object *objp); +void debris_clear_expired_flag(debris *db); + +#endif // _DEBRIS_H + diff --git a/include/demo.h b/include/demo.h new file mode 100644 index 0000000..7ff402e --- /dev/null +++ b/include/demo.h @@ -0,0 +1,104 @@ +/* + * $Logfile: /Freespace2/code/Demo/Demo.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 3 3/29/99 6:17p Dave + * More work on demo system. Got just about everything in except for + * blowing ships up, secondary weapons and player death/warpout. + * + * 2 3/28/99 5:58p Dave + * Added early demo code. Make objects move. Nice and framerate + * independant, but not much else. Don't use yet unless you're me :) + * + * + * $NoKeywords: $ + */ + +#ifndef _FS2_DEMO_SYSTEM_HEADER_FILE +#define _FS2_DEMO_SYSTEM_HEADER_FILE + +// ----------------------------------------------------------------------------------------------------------------------------- +// DEMO DEFINES/VARS +// + +// compile in the demo system +#define DEMO_SYSTEM + +struct object; +struct ship; + +// an error reading or writing the demo file +#define DEMO_ERROR_NONE 0 // no error +#define DEMO_ERROR_GENERAL 1 // general problem +#define DEMO_ERROR_FRAMESIZE 2 // frame size was too big (> 32k) +#define DEMO_ERROR_DISK_SPACE 3 // out of disk space +#define DEMO_ERROR_DISK_ACCESS 4 // problem accessing disk +#define DEMO_ERROR_VERSION 5 // bad version # +#define DEMO_ERROR_MISSION 6 // different mission file checksums +extern int Demo_error; + +extern int Demo_make; + +// ----------------------------------------------------------------------------------------------------------------------------- +// DEMO FUNCTIONS +// + +// do frame for the demo - start for the frame +int demo_do_frame_start(); + +// do frame for the demo - end for the frame +int demo_do_frame_end(); + +// initialize a demo for recording +// NOTE : call this after loading the mission and going through the briefing, but _before_ physically moving into the mission +int demo_start_record(char *file); + +// initialize a demo for playback - calling this will load up the demo file and move the player into the playback state +int demo_start_playback(char *file); + +// finish the demo, whether recording or playing back +void demo_close(); + +// if we should run the simulation for this object, or let the demo system handle it +int demo_should_sim(object *objp); + + +// functions for posting critical events to the demo system ------------------------------------------------------------ + +// post an object dump event (only used internally) +void demo_POST_object_dump(); + +// post a primary fired event +void demo_POST_primary_fired(object *objp, int banks, int linked); + +// post a unique message +void demo_POST_unique_message(char *id, char *who_from, int m_source, int priority); + +// post a builtin message +void demo_POST_builtin_message(int type, ship *shipp, int priority, int timing); + +// post an object create message +void demo_POST_obj_create(char *pobj_name, int signature); + +// post a warpin event +void demo_POST_warpin(int signature, int ship_flags); + +// post a warpout event +void demo_POST_warpout(int signature, int ship_flags); + +// post a departed event +void demo_POST_departed(int signature, int ship_flags); + +// post a ship kill event +void demo_POST_ship_kill(object *objp); + +#endif + diff --git a/include/dialog1.h b/include/dialog1.h new file mode 100644 index 0000000..8cbc447 --- /dev/null +++ b/include/dialog1.h @@ -0,0 +1,62 @@ +/* + * $Logfile: /Freespace2/code/FRED2/dialog1.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * key usage summary screen (actually a dialog box, but it's not very interactive) + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// dialog1 dialog + +class dialog1 : public CDialog +{ +// Construction +public: + dialog1(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(dialog1) + enum { IDD = IDD_HELP_INPUT_INTERFACE }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(dialog1) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(dialog1) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/ds.h b/include/ds.h new file mode 100644 index 0000000..729db44 --- /dev/null +++ b/include/ds.h @@ -0,0 +1,295 @@ +/* + * $Logfile: /Freespace2/code/Sound/ds.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for interface to DirectSound + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 8/27/99 6:38p Alanl + * crush the blasted repeating messages bug + * + * 4 8/01/99 2:06p Alanl + * increase the rolloff for A3D + * + * 3 5/23/99 8:11p Alanl + * Added support for EAX + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 21 5/05/98 4:49p Lawrance + * Put in code to authenticate A3D, improve A3D support + * + * 20 4/20/98 12:03a Lawrance + * Allow prioritizing of CTRL3D buffers + * + * 19 4/18/98 9:12p Lawrance + * Added Aureal support. + * + * 18 4/13/98 5:04p Lawrance + * Write functions to determine how many milliseconds are left in a sound + * + * 17 3/23/98 10:32a Lawrance + * Add functions for extracting raw sound data + * + * 16 2/15/98 11:10p Lawrance + * more work on real-time voice system + * + * 15 2/15/98 4:43p Lawrance + * work on real-time voice + * + * 14 2/06/98 7:30p John + * Added code to monitor the number of channels of sound actually playing. + * + * 13 2/06/98 8:56a Allender + * fixed calling convention problem with DLL handles + * + * 12 2/04/98 6:08p Lawrance + * Read function pointers from dsound.dll, further work on + * DirectSoundCapture. + * + * 11 1/31/98 5:48p Lawrance + * Start on real-time voice recording + * + * 10 12/05/97 5:19p Lawrance + * re-do sound priorities to make more general and extensible + * + * 9 11/20/97 5:36p Dave + * Hooked in a bunch of main hall changes (including sound). Made it + * possible to reposition (rewind/ffwd) + * sound buffer pointers. Fixed animation direction change framerate + * problem. + * + * 8 10/13/97 7:41p Lawrance + * store duration of sound + * + * 7 7/28/97 11:39a Lawrance + * allow individual volume scaling on 3D buffers + * + * 6 7/17/97 9:32a John + * made all directX header files name start with a v + * + * 5 7/15/97 11:15a Lawrance + * limit the max instances of simultaneous sound effects, implement + * priorities to force critical sounds + * + * 4 6/09/97 11:50p Lawrance + * integrating DirectSound3D + * + * 3 6/02/97 1:45p Lawrance + * implementing hardware mixing + * + * 2 5/29/97 12:04p Lawrance + * creation of file to hold DirectSound specific portions + * + * $NoKeywords: $ + */ + +#ifndef __DS_H__ +#define __DS_H__ + +#include +#include +#include "vdsound.h" +#include "pstypes.h" + +#define DS_HARDWARE (1<<0) +#define DS_3D (1<<1) +#define DS_USE_DS3D (1<<2) + +// Constants that DirectSound should assign, but doesn't +#define MAX_PAN 1500.0f +#define MIN_PITCH 100 +#define MAX_PITCH 100000 + + +// limits placed on how many concurrent sounds of the same type can play simultaneously +#define DS_MUST_PLAY 0 +#define DS_LIMIT_ONE 1 +#define DS_LIMIT_TWO 2 +#define DS_LIMIT_THREE 3 + +#define DS_DUP_FAILED -99 + +typedef struct sound_info { + int format; // WAVE_FORMAT_* defines from mmreg.h + uint size; + int sample_rate; + int avg_bytes_per_sec; + int n_block_align; + int bits; + int n_channels; + int duration; // time in ms for duration of sound + ubyte *data; +} sound_info; + +extern int ds_initialized; +extern LPDIRECTSOUNDBUFFER pPrimaryBuffer; +extern LPDIRECTSOUND pDirectSound; + +extern HRESULT (__stdcall *pfn_DirectSoundCaptureCreate)(LPGUID lpGUID, LPDIRECTSOUNDCAPTURE *lplpDSC, LPUNKNOWN pUnkOuter); + +int ds_init(int use_a3d, int use_eax); +void ds_close(); +void ds_get_primary_format(WAVEFORMATEX *wfx); +int ds_parse_wave(char *filename, ubyte **dest, uint *dest_size, WAVEFORMATEX **header); +int ds_load_buffer(int *sid, int *hid, int *final_size, void *header, sound_info *si, int flags); +void ds_unload_buffer(int sid, int hid); +int ds_play(int sid, int hid, int snd_id, int priority, int volume, int pan, int looping, bool is_voice_msg = false); +int ds_convert_volume(float volume); // Convert a volume from 0.0f->1.0f to -10000 -> 0 +float ds_get_percentage_vol(int ds_vol); // Convert a volume from -10000 -> 0 to 0.0f->1.0f +int ds_get_channel(int sig); +int ds_is_channel_playing(int channel); +void ds_stop_channel(int channel); +void ds_stop_channel_all(); +void ds_set_volume( int channel, int vol ); +void ds_set_pan( int channel, int pan ); +int ds_get_pitch(int channel); +void ds_set_pitch(int channel, int pitch); +void ds_chg_loop_status(int channel, int loop); +void ds_set_position(int channel, DWORD offset); +DWORD ds_get_play_position(int channel); +DWORD ds_get_write_position(int channel); +int ds_get_data(int sid, char *data); +int ds_get_size(int sid, int *size); + +int ds_create_buffer(int frequency, int bits_per_sample, int nchannels, int nseconds); +int ds_lock_data(int sid, unsigned char *data, int size); +int ds_play_easy(int sid, int volume); +void ds_stop_easy(int sid); +int ds_get_channel_size(int channel); +int ds_is_3d_buffer(int sid); +int ds_using_ds3d(); +bool ds_using_a3d(); + +unsigned int ds_get_primary_buffer_interface(); +unsigned int ds_get_dsound_interface(); +unsigned int ds_get_property_set_interface(); + +// Returns the number of channels that are actually playing +int ds_get_number_channels(); + +int ds3d_play( int sid, int hid, int snd_id, vector *pos, vector *vel, int min, int max, int looping, int max_volume, int estimated_vol, int priority=DS_MUST_PLAY ); + +// Get a character string for the error code +char *get_DSERR_text(int DSResult); + +void ds_do_frame(); + +// -------------------- +// +// Creative eax.h +// +// -------------------- + +// EAX (listener) reverb property set {4a4e6fc1-c341-11d1-b73a-444553540000} +DEFINE_GUID(DSPROPSETID_EAX_ReverbProperties, + 0x4a4e6fc1, + 0xc341, + 0x11d1, + 0xb7, 0x3a, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00); + +typedef enum +{ + DSPROPERTY_EAX_ALL, // all reverb properties + DSPROPERTY_EAX_ENVIRONMENT, // standard environment no. + DSPROPERTY_EAX_VOLUME, // loudness of the reverb + DSPROPERTY_EAX_DECAYTIME, // how long the reverb lasts + DSPROPERTY_EAX_DAMPING // the high frequencies decay faster +} DSPROPERTY_EAX_REVERBPROPERTY; + +#define EAX_NUM_STANDARD_PROPERTIES (DSPROPERTY_EAX_DAMPING + 1) + +// use this structure for get/set all properties... +typedef struct +{ + unsigned long environment; // 0 to EAX_ENVIRONMENT_COUNT-1 + float fVolume; // 0 to 1 + float fDecayTime_sec; // seconds, 0.1 to 100 + float fDamping; // 0 to 1 +} EAX_REVERBPROPERTIES; + + +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_COUNT // total number of environments +}; + +#define EAX_MAX_ENVIRONMENT (EAX_ENVIRONMENT_COUNT - 1) + +// EAX buffer reverb property set {4a4e6fc0-c341-11d1-b73a-444553540000} +DEFINE_GUID(DSPROPSETID_EAXBUFFER_ReverbProperties, + 0x4a4e6fc0, + 0xc341, + 0x11d1, + 0xb7, 0x3a, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00); + +typedef enum +{ + DSPROPERTY_EAXBUFFER_ALL, // all reverb buffer properties + DSPROPERTY_EAXBUFFER_REVERBMIX // the wet source amount +} DSPROPERTY_EAXBUFFER_REVERBPROPERTY; + +// use this structure for get/set all properties... +typedef struct +{ + float fMix; // linear factor, 0.0F to 1.0F +} EAXBUFFER_REVERBPROPERTIES; + +#define EAX_REVERBMIX_USEDISTANCE -1.0F // out of normal range + // signifies the reverb engine should + // calculate it's own reverb mix value + // based on distance + +// prototypes + +int ds_eax_init(); +void ds_eax_close(); + +int ds_eax_set_preset(unsigned long envid); + +int ds_eax_set_volume(float volume); +int ds_eax_set_decay_time(float seconds); +int ds_eax_set_damping(float damp); +int ds_eax_set_environment(unsigned long envid); +int ds_eax_set_all(unsigned long id, float volume, float damping, float decay); +int ds_eax_get_all(EAX_REVERBPROPERTIES *er); +int ds_eax_is_inited(); + +#endif /* __DS_H__ */ + diff --git a/include/ds3d.h b/include/ds3d.h new file mode 100644 index 0000000..865eb18 --- /dev/null +++ b/include/ds3d.h @@ -0,0 +1,56 @@ +/* + * $Logfile: /Freespace2/code/Sound/ds3d.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for interface to DirectSound3D + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 5/23/99 8:11p Alanl + * Added support for EAX + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 8 4/19/98 9:31p Lawrance + * Use Aureal_enabled flag + * + * 7 7/28/97 11:39a Lawrance + * allow individual volume scaling on 3D buffers + * + * 6 6/09/97 11:50p Lawrance + * integrating DirectSound3D + * + * 5 6/08/97 5:59p Lawrance + * integrate DirectSound3D into sound system + * + * 4 6/02/97 1:45p Lawrance + * implementing hardware mixing + * + * 3 5/29/97 4:02p Lawrance + * listener interface in place + * + * 2 5/29/97 12:03p Lawrance + * creation of file to hold DirectSound3D specific code + * + * $NoKeywords: $ + */ + +#ifndef __DS3D_H__ +#define __DS3D_H__ + +int ds3d_init(int voice_manager_required); +void ds3d_close(); +int ds3d_update_listener(vector *pos, vector *vel, matrix *orient); +int ds3d_update_buffer(int channel, float min, float max, vector *pos, vector *vel); +int ds3d_set_sound_cone(int channel, int inner_angle, int outer_angle, int vol); + +#endif /* __DS3D_H__ */ + diff --git a/include/dscap.h b/include/dscap.h new file mode 100644 index 0000000..f10f922 --- /dev/null +++ b/include/dscap.h @@ -0,0 +1,49 @@ +/* + * $Logfile: /Freespace2/code/Sound/dscap.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for DirectSoundCapture code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 4 2/15/98 11:10p Lawrance + * more work on real-time voice system + * + * 3 2/15/98 4:43p Lawrance + * work on real-time voice + * + * 2 2/03/98 11:53p Lawrance + * Adding support for DirectSoundCapture + * + * 1 2/03/98 4:48p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __DSCAP_H__ +#define __DSCAP_H__ + +int dscap_init(); +void dscap_close(); +int dscap_supported(); +int dscap_create_buffer(int freq, int bits_per_sample, int nchannels, int nseconds); +void dscap_release_buffer(); + +int dscap_start_record(); +int dscap_stop_record(); +int dscap_max_buffersize(); +int dscap_get_raw_data(unsigned char *outbuf, unsigned int max_size); + + +#endif // __DSCAP_H__ + diff --git a/include/dumpstats.h b/include/dumpstats.h new file mode 100644 index 0000000..90139f7 --- /dev/null +++ b/include/dumpstats.h @@ -0,0 +1,56 @@ +#if !defined(AFX_DUMPSTATS_H__04996431_1A80_11D3_A923_0060088FAE88__INCLUDED_) +#define AFX_DUMPSTATS_H__04996431_1A80_11D3_A923_0060088FAE88__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// DumpStats.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// DumpStats dialog + +class DumpStats : public CDialog +{ +// Construction +public: + DumpStats(CWnd* pParent = NULL); // standard constructor + void get_mission_stats(CString &buffer); + void get_background_stats(CString &buffer); + void get_object_stats(CString &buffer); + void get_objectives_and_goals(CString &buffer); + void get_ship_weapon_selection(CString &buffer); + void get_messaging_info(CString &buffer); + void get_species_ship_breakdown(CString &buffer); + void get_default_ship_loadouts(CString &buffer); + +// Dialog Data + //{{AFX_DATA(DumpStats) + enum { IDD = IDD_DUMP_STATS }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(DumpStats) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(DumpStats) + virtual BOOL OnInitDialog(); + afx_msg void OnDumpToFile(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_DUMPSTATS_H__04996431_1A80_11D3_A923_0060088FAE88__INCLUDED_) + diff --git a/include/editor.h b/include/editor.h new file mode 100644 index 0000000..bbfa169 --- /dev/null +++ b/include/editor.h @@ -0,0 +1,40 @@ +#ifndef _EDITOR_H +#define _EDITOR_H + +/* +#ifdef __cplusplus +extern "C" { +#endif +*/ + +#include "physics.h" +#include "3d.h" +#include "missiongrid.h" + +#define MAX_GRID_POINTS 1000 + +void create_object(int objnum, vector *pos); + +// This stuff properly belongs in editor.h, but I gave up trying to figure out how +// to add an include file to the project. + +extern void test_form_wing(int x); + +extern int select_object(int cx, int cy); +extern void game_init(); + +extern matrix Grid_gmatrix; +extern vector Grid_center; + +extern int Show_stars; +extern void rpd_line(vector *v0, vector *v1); + + +/* +#ifdef __cplusplus +} +#endif +*/ + +#endif + diff --git a/include/emp.h b/include/emp.h new file mode 100644 index 0000000..0218aa3 --- /dev/null +++ b/include/emp.h @@ -0,0 +1,142 @@ +/* + * $Logfile: /Freespace2/code/Weapon/Emp.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for managing corkscrew missiles + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 7/02/99 4:31p Dave + * Much more sophisticated lightning support. + * + * 3 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 3 8/28/98 3:29p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 2 8/25/98 1:49p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 1 8/24/98 9:29a Dave + * + * $NoKeywords: $ + */ + +#ifndef __FREESPACE_EMP_MISSILE_HEADER_FILE_ +#define __FREESPACE_EMP_MISSILE_HEADER_FILE_ + +// ---------------------------------------------------------------------------------------------------- +// EMP EFFECT DEFINES/VARS +// + +// default EMP effect values for weapons which do not specify them +// NOTE : anything aboce intensity max or time max will be capped +#define EMP_INTENSITY_MAX (500.0f) +#define EMP_TIME_MAX (30.0f) +#define EMP_DEFAULT_INTENSITY (300.0f) +#define EMP_DEFAULT_TIME (10.0f) + +// for identifying specific text gauges when messing text up +#define NUM_TEXT_STAMPS 36 +#define EG_NULL -1 // meaning, always make the string empty +#define EG_WEAPON_TITLE 0 // title bar of the weapon gauge +#define EG_WEAPON_P1 1 // first primary weapon slot +#define EG_WEAPON_P2 2 // second primary weapon slot +#define EG_WEAPON_P3 3 // third primary weapon slot +#define EG_WEAPON_S1 4 // first secondary weapon slot +#define EG_WEAPON_S2 5 // second secondary weapon slot +#define EG_ESCORT1 6 // first item on escort list +#define EG_ESCORT2 7 // second item on escort list +#define EG_ESCORT3 8 // third item on escort list +#define EG_OBJ_TITLE 9 // titlebar of the directives gauge +#define EG_OBJ1 10 // line 1 of the directives display +#define EG_OBJ2 11 // line 2 +#define EG_OBJ3 12 // line 3 +#define EG_OBJ4 13 // line 4 +#define EG_OBJ5 14 // line 5 +#define EG_TBOX_EXTRA1 15 // extra target info line 1 +#define EG_TBOX_EXTRA2 16 // extra target info line 2 +#define EG_TBOX_EXTRA3 17 // extra target info line 3 +#define EG_TBOX_CLASS 18 // target class +#define EG_TBOX_DIST 20 // target dist +#define EG_TBOX_SPEED 21 // target speed +#define EG_TBOX_CARGO 22 // target cargo +#define EG_TBOX_HULL 23 // target hull +#define EG_TBOX_NAME 24 // target name +#define EG_TBOX_INTEG 25 // target integrity +#define EG_SQ1 26 // squadmsg 1 +#define EG_SQ2 27 // squadmsg 2 +#define EG_SQ3 28 // squadmsg 3 +#define EG_SQ4 29 // squadmsg 4 +#define EG_SQ5 30 // squadmsg 5 +#define EG_SQ6 31 // squadmsg 6 +#define EG_SQ7 32 // squadmsg 7 +#define EG_SQ8 33 // squadmsg 8 +#define EG_SQ9 34 // squadmsg 9 +#define EG_SQ10 35 // squadmsg 10 + +struct object; +struct ship; +struct weapon_info; + + +// ---------------------------------------------------------------------------------------------------- +// EMP EFFECT FUNCTIONS +// + +// initialize the EMP effect for the mission +void emp_level_init(); + +// apply the EMP effect to all relevant ships +void emp_apply(vector *pos, float inner_radius, float outer_radius, float emp_intensity, float emp_time); + +// start the emp effect for the passed ship (setup lightning arcs, timestamp, etc) +// NOTE : if this ship is also me, I should call emp_start_local() as well +void emp_start_ship(object *ship_obj, float intensity, float time); + +// process a ship for this frame +void emp_process_ship(ship *shipp); + +// start the emp effect for MYSELF (intensity == arbitrary intensity variable, time == time the effect will last) +// NOTE : time should be in seconds +void emp_start_local(float intensity, float time); + +// stop the emp effect cold +void emp_stop_local(); + +// if the EMP effect is active +int emp_active_local(); + +// process some stuff every frame (before frame is rendered) +void emp_process_local(); + +// randomly say yes or no to a gauge, if emp is not active, always say yes +int emp_should_blit_gauge(); + +// emp hud string +void emp_hud_string(int x, int y, int gauge_id, char *str); + +// emp hud printf +void emp_hud_printf(int x, int y, int gauge_id, char *format, ...); + +// throw some jitter into HUD x and y coords +void emp_hud_jitter(int *x, int *y); + +// current intensity of the EMP effect (0.0 - 1.0) +float emp_current_intensity(); + +#endif + diff --git a/include/encrypt.h b/include/encrypt.h new file mode 100644 index 0000000..eb360b3 --- /dev/null +++ b/include/encrypt.h @@ -0,0 +1,53 @@ +/* + * $Logfile: /Freespace2/code/parse/Encrypt.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header for encryption code common to FreeSpace and related tools + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 3/25/99 11:26a Dave + * Beefed up encryption scheme so that even someone viewing the + * disassembly would have a hard time cracking it. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 4 8/09/98 4:44p Lawrance + * support alternate encryption scheme (doesn't pack chars into 7 bits) + * + * 3 3/31/98 4:57p Lawrance + * Add signature at the beginning of encrypted files + * + * 2 3/31/98 1:14a Lawrance + * Get .tbl and mission file encryption working. + * + * 1 3/30/98 11:02p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __ENCRYPT_H__ +#define __ENCRYPT_H__ + +// initialize encryption +void encrypt_init(); + +// Return 1 if the file is encrypted, otherwise return 0 +int is_encrpyted(char *scrambled_text); + +// Encrpyt text data +void encrypt(char *text, int text_len, char *scrambled_text, int *scrambled_len, int use_8bit); + +// Decrypt scrambled_text +void unencrypt(char *scrambled_text, int scrambled_len, char *text, int *text_len); + +#endif + diff --git a/include/eventeditor.h b/include/eventeditor.h new file mode 100644 index 0000000..35d21af --- /dev/null +++ b/include/eventeditor.h @@ -0,0 +1,245 @@ +/* + * $Logfile: /Freespace2/code/Fred2/EventEditor.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Event editor dialog box class and event tree class (used for dialog) + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 8/26/99 8:52p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 4 5/04/99 5:21p Andsager + * + * 3 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 27 5/15/98 5:51p Hoffoss + * Fixed escape key and cancel button bugs. + * + * 26 4/22/98 9:56a Sandeep + * + * 25 4/20/98 4:40p Hoffoss + * Added a button to 4 editors to play the chosen wave file. + * + * 24 4/03/98 5:20p Hoffoss + * Changed code so that changing a message's wave file will update the + * persona as well, if the wave file has the proper prefix. + * + * 23 2/16/98 6:25p Hoffoss + * Did major rework of the whole right_clicked() handler to simplify it + * all, break it down and make it more flexible. Should be a lot easier + * to work with from now on. + * + * 22 2/16/98 2:42p Hoffoss + * Added new code in preparation to simplify the sexp_tree monster. + * Checking in code now as a good foundation point that I can revert back + * to if needed. + * + * 21 1/08/98 11:18a Hoffoss + * Fixed several bugs in new Event Editor. + * + * 20 1/07/98 5:58p Hoffoss + * Combined message editor into event editor. + * + * 19 1/06/98 8:25p Hoffoss + * Added insert event functionality to event editor. + * + * 18 10/10/97 6:21p Hoffoss + * Put in Fred support for training object list editing. + * + * 17 10/09/97 1:03p Hoffoss + * Renaming events or goals now updates sexp references as well. + * + * 16 9/30/97 10:01a Hoffoss + * Added event chaining support to Fred and FreeSpace. + * + * 15 8/12/97 3:33p Hoffoss + * Fixed the "press cancel to go to reference" code to work properly. + * + * 14 7/30/97 5:23p Hoffoss + * Removed Sexp tree verification code, since it duplicates normal sexp + * verification, and is just another set of code to keep maintained. + * + * 13 7/25/97 3:05p Allender + * added score field to goals and events editor + * + * 12 7/24/97 12:45p Hoffoss + * Added sexp help system to sexp trees and some dialog boxes. + * + * 11 7/17/97 4:10p Hoffoss + * Added drag and drop to sexp trees for reordering root items. + * + * 10 7/15/97 10:30a Allender + * added repeat count and interval time to event editor. For use in + * repeating events at regular intervals + * + * 9 6/02/97 8:47p Hoffoss + * Fixed bug with inserting an operator at root position, but under a + * label. + * + * 8 5/20/97 2:28p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 7 5/01/97 4:12p Hoffoss + * Added return handling to dialogs. + * + * 6 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 5 4/11/97 10:10a Hoffoss + * Name fields supported by Fred for Events and Mission Goals. + * + * 4 4/07/97 3:48p Hoffoss + * Event editor now supports a sporty new delete button! + * + * 3 1/22/97 11:01a Hoffoss + * Many bug fixes (those pointed out by Mark during Fred testing trying to + * make mission 5). + * + * 2 1/13/97 4:54p Hoffoss + * Added event editor. + * + * $NoKeywords: $ + */ + +#ifndef _EVENTEDITOR_H +#define _EVENTEDITOR_H + +#include "sexp_tree.h" +#include "missiongoals.h" +#include "missionmessage.h" + +#define MAX_SEARCH_MESSAGE_DEPTH 5 // maximum search number of event nodes with message text + +class sexp_event_tree : public sexp_tree +{ +public: + int load_sub_tree(int index); + int get_new_node_position(); +}; + +///////////////////////////////////////////////////////////////////////////// +// event_editor dialog + +class event_editor : public CDialog +{ +// Construction +public: + void update_persona(); + void save(); + char *current_message_name(int index); + char *get_message_list_item(int i); + int save_message(int num); + void update_cur_message(); + HTREEITEM get_event_handle(int num); + void reset_event(int num, HTREEITEM after); + void save_event(int e); + void swap_handler(int node1, int node2); + void insert_handler(int old, int node); + int query_modified(); + void OnOK(); + void OnCancel(); + int handler(int code, int node, char *str = NULL); + void create_tree(); + void load_tree(); + int modified; + int select_sexp_node; + event_editor(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(event_editor) + enum { IDD = IDD_EVENT_EDITOR }; + sexp_event_tree m_event_tree; + UINT m_repeat_count; + UINT m_interval; + int m_event_score; + int m_chain_delay; + BOOL m_chained; + CString m_obj_text; + CString m_obj_key_text; + CString m_avi_filename; + CString m_message_name; + CString m_message_text; + int m_persona; + CString m_wave_filename; + int m_cur_msg; + int m_team; + int m_message_team; + int m_last_message_node; + //}}AFX_DATA + + CBitmap m_play_bm; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(event_editor) + public: + virtual BOOL DestroyWindow(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(event_editor) + virtual BOOL OnInitDialog(); + afx_msg void OnRclickEventTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBeginlabeleditEventTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnEndlabeleditEventTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnButtonNewEvent(); + afx_msg void OnDelete(); + afx_msg void OnOk(); + afx_msg void OnClose(); + afx_msg void OnSelchangedEventTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnUpdateRepeatCount(); + afx_msg void OnChained(); + afx_msg void OnInsert(); + afx_msg void OnSelchangeMessageList(); + afx_msg void OnNewMsg(); + afx_msg void OnDeleteMsg(); + afx_msg void OnBrowseAvi(); + afx_msg void OnBrowseWave(); + afx_msg void OnSelchangeWaveFilename(); + afx_msg void OnPlay(); + afx_msg void OnUpdate(); + afx_msg void On_Cancel(); + afx_msg void OnSelchangeTeam(); + afx_msg void OnSelchangeMessageTeam(); + afx_msg void OnDblclkMessageList(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: + int cur_event; + void update_cur_event(); + int m_num_events; + int m_sig[MAX_MISSION_EVENTS]; + mission_event m_events[MAX_MISSION_EVENTS]; + int m_num_messages; + MMessage m_messages[MAX_MISSION_MESSAGES]; + int m_msg_sig[MAX_MISSION_MESSAGES]; +}; + +extern event_editor *Event_editor_dlg; // global reference needed by event tree class + +#endif + diff --git a/include/eventmusic.h b/include/eventmusic.h new file mode 100644 index 0000000..f0fc141 --- /dev/null +++ b/include/eventmusic.h @@ -0,0 +1,212 @@ +/* + * $Logfile: /Freespace2/code/Gamesnd/EventMusic.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for high-level control of event driven music + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 8/11/99 5:33p Jefff + * added 3rd debrief music track + * + * 4 6/20/99 12:06a Alanl + * new event music changes + * + * 3 11/20/98 4:08p Dave + * Fixed flak effect in multiplayer. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 27 9/18/98 1:22p Dave + * Added new event music stuff defines. + * + * 26 5/18/98 5:22p Lawrance + * Support new briefing music + * + * 25 5/03/98 1:54a Lawrance + * Fix event music problems related to respawning + * + * 24 12/30/97 11:46a Lawrance + * Support a success debriefing music. + * + * 23 12/28/97 5:52p Lawrance + * Add support for debriefing success/fail music. + * + * 22 12/26/97 10:01p Lawrance + * Add goal failure music... remove unused arrival music patterns + * + * 21 9/19/97 5:13p Lawrance + * add support for specifying music in the mission file + * + * 20 9/09/97 5:24p Lawrance + * added ability to switch soundtracks, gave soundtracks real names + * + * 19 9/06/97 2:13p Mike + * Replace support for TEAM_NEUTRAL + * + * 18 5/14/97 9:54a Lawrance + * supporting mission-specific briefing music + * + * 17 4/21/97 5:36p Lawrance + * add in hooks to play death music when pilot dies + * + * 16 4/17/97 3:28p Lawrance + * documented functions + * + * 15 4/14/97 1:52p Lawrance + * making transitions happen on measure boundries + * + * 14 4/07/97 1:39p Lawrance + * added event_music_first_pattern(); + * + * 13 4/03/97 4:26p Lawrance + * getting digital event music working + * + * 12 3/07/97 8:53a Lawrance + * Read event at time 0 when music first starts + * + * 11 2/25/97 11:10a Lawrance + * using text of the mission name to match up which midi file gets played + * for which mission + * + * 10 2/18/97 9:43a Lawrance + * make BTTL_2 play after 2 enemy arrivals in battle mode. Then switch + * back to default BTTL_1 until 2 more. + * + * 9 2/14/97 11:50a Lawrance + * added BTTL_2 track + * + * 8 2/11/97 4:22p Lawrance + * adding song switching request to the event music level + * + * 7 2/11/97 9:15a Lawrance + * taking out BTTL_2 and BTTL_3 + * + * 6 2/10/97 9:26a Lawrance + * + * 5 2/05/97 3:12p Lawrance + * supporting changes in MIDI system that remove any high-level + * dependencies + * + * 4 2/04/97 11:58p Lawrance + * volume change code, and fixing some bugs + * + * 3 2/04/97 12:02p Lawrance + * fixed temp bug, integrating music.tbl + * + * 2 2/03/97 6:49p Lawrance + * Event Music interface working + * + * $NoKeywords: $ + */ + + +#ifndef __EVENT_MUSIC_H__ +#define __EVENT_MUSIC_H__ + +#include "parselo.h" + +// Identifies songs in the Soundtrack_filenames[] structure. The order matches up with +// what is in music.tbl. Do not modify without synching music.tbl. +#define SONG_NRML_1 0 // Normal Song 1 +#define SONG_AARV_1 1 // Allied Arrival 1 +#define SONG_EARV_1 2 // Enemy Arrival 1 +#define SONG_BTTL_1 3 // Battle Song 1 +#define SONG_BTTL_2 4 // Battle Song 2 +#define SONG_BTTL_3 5 // Battle Song 3 +#define SONG_AARV_2 6 // Allied Arrival 2 +#define SONG_EARV_2 7 // Enemy Arrival 2 +#define SONG_VICT_1 8 // Victory Song 1 +#define SONG_VICT_2 9 // Victory Song 2 +#define SONG_FAIL_1 10 // Goal Failed +#define SONG_DEAD_1 11 // Death Song 1 + +#define MAX_PATTERNS 12 + +// if player targets a hostile ship at less than this range, switch to battle track +#define BATTLE_START_MIN_TARGET_DIST 500 + +extern int Event_Music_battle_started; // flag that will tell us if we've started a battle in the current mission +extern int Event_music_enabled; +extern float Master_event_music_volume; // range is 0->1 + + +///////////////////////////////////////////////////////////////////////////// +// Used to track what briefing and debriefing music is played for the mission +///////////////////////////////////////////////////////////////////////////// +#define NUM_SCORES 4 +#define SCORE_BRIEFING 0 +#define SCORE_DEBRIEF_SUCCESS 1 +#define SCORE_DEBRIEF_AVERAGE 2 +#define SCORE_DEBRIEF_FAIL 3 +extern int Mission_music[NUM_SCORES]; // indicies into Spooled_music[] +///////////////////////////////////////////////////////////////////////////// + +extern int Current_soundtrack_num; // index into Soundtracks[] + +// menu music storage +typedef struct menu_music { + char name[NAME_LENGTH]; // name music is known by + char filename[MAX_FILENAME_LEN]; // name music is stored on disk as +} menu_music; + +#define MAX_SPOOLED_MUSIC 20 + +extern menu_music Spooled_music[MAX_SPOOLED_MUSIC]; +extern int Num_music_files; + +// event music soundtrack storage +typedef struct tagSOUNDTRACK_INFO { + int num_patterns; + char name[NAME_LENGTH]; + char pattern_fnames[MAX_PATTERNS][MAX_FILENAME_LEN]; +} SOUNDTRACK_INFO; + +#define MAX_SOUNDTRACKS 10 + +extern SOUNDTRACK_INFO Soundtracks[MAX_SOUNDTRACKS]; +extern int Num_soundtracks; + +void event_music_init(); +void event_music_close(); +void event_music_level_init(int force_soundtrack = -1); +void event_music_level_close(); +void event_music_do_frame(); +void event_music_disable(); +void event_music_enable(); +void event_music_pause(); +void event_music_unpause(); +void event_music_set_volume_all(float volume); +void event_music_parse_musictbl(); +void event_music_change_pattern(int new_pattern); +int event_music_return_current_pattern(); +void event_music_first_pattern(); +int event_music_battle_start(); +int event_music_enemy_arrival(); +int event_music_friendly_arrival(); +void event_music_arrival(int team); +int event_music_primary_goals_met(); +int event_music_primary_goal_failed(); +int event_music_player_death(); +void event_music_start_default(); +void event_music_get_info(char *outbuf); +void event_music_get_soundtrack_name(char *outbuf); +int event_music_next_soundtrack(int delta); +void event_music_set_soundtrack(char *name); +void event_music_set_score(int score_index, char *name); +int event_music_get_spooled_music_index(char *name); +void event_music_reset_choices(); +int event_music_player_respawn(); +int event_music_player_respawn_as_observer(); +void event_music_hostile_ship_destroyed(); + +#endif /* __EVENT_MUSIC_H__ */ + diff --git a/include/exceptionhandler.h b/include/exceptionhandler.h new file mode 100644 index 0000000..055ef74 --- /dev/null +++ b/include/exceptionhandler.h @@ -0,0 +1,82 @@ +/* + * $Logfile: /Freespace2/code/ExceptionHandler/ExceptionHandler.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for program exception handling + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 1 6/29/99 7:42p Dave + * + * 2 1/18/99 4:34p Allender + * added the exception handler routines from Game Developer for structured + * exception handling in vsdk code + * + * $NoKeywords: $ + */ + +#pragma once // Include this file only once + +#ifndef __EXCEPTION_HANDLER_H +#define __EXCEPTION_HANDLER_H + +// -------------------- +// +// Defines +// +// -------------------- + + +// -------------------- +// +// Enumerated types +// +// -------------------- + + +// -------------------- +// +// Structures +// +// -------------------- + +// this is a forward declaration so we don't need to include windows.h + +typedef struct _EXCEPTION_POINTERS EXCEPTION_POINTERS, *PEXCEPTION_POINTERS; + +// -------------------- +// +// Classes +// +// -------------------- + + +// -------------------- +// +// Variables +// +// -------------------- + + +// -------------------- +// +// Prototypes +// +// -------------------- + +int __cdecl RecordExceptionInfo(PEXCEPTION_POINTERS data, const char *Message); + +// -------------------- +// +// Helper Macros +// +// -------------------- + + +#endif // __EXCEPTION_HANDLER_H + diff --git a/include/fhash.h b/include/fhash.h new file mode 100644 index 0000000..339e53a --- /dev/null +++ b/include/fhash.h @@ -0,0 +1,54 @@ +/* + * $Logfile: /Freespace2/code/Localization/fhash.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 12/01/98 4:46p Dave + * Put in targa bitmap support (16 bit). + * + * $NoKeywords: $ + */ + +#ifndef _FRED_XSTR_HASH_TABLE_HEADER_FILE +#define _FRED_XSTR_HASH_TABLE_HEADER_FILE + +// ----------------------------------------------------------------------------------------------- +// HASH DEFINES/VARS +// + + +// ----------------------------------------------------------------------------------------------- +// HASH FUNCTIONS +// + +// initialize the hash table +void fhash_init(); + +// set the hash table to be active for parsing +void fhash_activate(); + +// set the hash table to be inactive for parsing +void fhash_deactivate(); + +// if the hash table is active +int fhash_active(); + +// flush out the hash table, freeing up everything +void fhash_flush(); + +// add a string with the given id# to the has table +void fhash_add_str(char *str, int id); + +// determine if the passed string exists in the table +// returns : -2 if the string doesn't exit, or >= -1 as the string id # otherwise +int fhash_string_exists(char *str); + +#endif + diff --git a/include/fireballs.h b/include/fireballs.h new file mode 100644 index 0000000..73e6da1 --- /dev/null +++ b/include/fireballs.h @@ -0,0 +1,198 @@ +/* + * $Logfile: /Freespace2/code/Fireball/FireBalls.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Prototypes for fireball functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 8 9/13/99 10:09a Andsager + * Add debug console commands to lower model render detail and fireball + * LOD for big ship explosiosns. + * + * 7 9/06/99 6:57p Jamesa + * Cranked down num large fireball explosions. + * + * 6 9/06/99 6:14p Jamesa + * Reduced max fireball types. + * + * 5 8/31/99 10:13p Andsager + * Add Knossos warp effect fireball + * + * 4 4/28/99 11:13p Dave + * Temporary checkin of artillery code. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 34 5/15/98 3:54p John + * Added code so that only "perishable" fireballs get removed. + * + * 33 4/15/98 9:42a Adam + * added 2 more explosion types (1, actually, but placeholder for 2) + * + * 32 4/12/98 9:56a John + * Made lighting detail flags work. Made explosions cast light on + * highest. + * + * 31 3/30/98 4:02p John + * Made machines with < 32 MB of RAM use every other frame of certain + * bitmaps. Put in code to keep track of how much RAM we've malloc'd. + * + * 30 3/18/98 12:36p John + * Made hardware have nicer looking warp effect + * + * 29 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 28 2/19/98 4:32p Lawrance + * Add asteroid explosion + * + * 27 1/23/98 5:06p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 26 1/15/98 4:58p John + * Made warp effect use a looping ani. Made the scaling up & down be in + * software. + * + * 25 1/15/98 2:32p John + * Added code to optionally take a velocity for a fireball. + * + * 24 1/02/98 5:04p John + * Several explosion related changes. Made fireballs not be used as + * ani's. Made ship spark system expell particles. Took away impact + * explosion for weapon hitting ship... this needs to get added to weapon + * info and makes shield hit more obvious. Only make sparks when hit + * hull, not shields. + * + * 23 12/08/97 11:15a John + * added parameter to warpout for life. + * + * 22 11/01/97 1:49p John + * added code to page fireballs in during level load. Made player warpout + * use Adam's new camera movement pattern. Make ships docked to warping + * out ships render correctly. + * + * 21 10/24/97 6:24p John + * added code to return the life left of a fireball + * + * 20 9/14/97 4:49p Lawrance + * extern Num_fireballs + * + * 19 9/12/97 4:02p John + * put in ship warp out effect. + * put in dynamic lighting for warp in/out + * + * 18 9/09/97 4:49p John + * Almost done ship warp in code + * + * 17 9/03/97 4:32p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 16 8/29/97 2:26p John + * first rev of ship warp in effect. Nothing more than a fireball inside + * of freespace, but textest.cpp contains the correct effect code that + * needs to be transferred into the game next. + * + * 15 8/13/97 9:50p Allender + * split *_move into *_process_pre and *_process_post functions. + * process_pre functions called before object is moved. _process_post + * functions called after object is moved. Reordered code in ship_post + * and weapon_post for multiplayer + * + * 14 8/13/97 12:06p Lawrance + * supporting multiple types of fireball explosions + * + * 13 7/21/97 11:41a Lawrance + * clean up fireballs at the end of each level + * + * 12 7/11/97 11:19a Lawrance + * fix mem leaks, move save code from State.cpp here + * + * 11 7/10/97 1:51p Lawrance + * sorting anim fireballs + * + * 10 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 9 3/11/97 10:47p Mike + * Add a slew of secondary weapons. + * Support exhaust blobs. + * Add weapons that spawn weapons. + * Add remotely detonatable weapons. + * Add heat-seeking missiles. + * + * 8 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _FIREBALLS_H +#define _FIREBALLS_H + +#include "object.h" +#include "cfile.h" + +// these values correspond to the fireball.tbl entries +#define FIREBALL_EXPLOSION_MEDIUM 0 // Used for the 4 little explosions before a ship explodes +#define FIREBALL_WARP_EFFECT 1 // Used for the warp in / warp out effect +#define FIREBALL_KNOSSOS_EFFECT 2 // Used for the KNOSSOS warp in / warp out effect +#define FIREBALL_ASTEROID 3 +#define FIREBALL_EXPLOSION_LARGE1 4 // Used for the big explosion when a ship breaks into pieces +#define FIREBALL_EXPLOSION_LARGE2 5 // Used for the big explosion when a ship breaks into pieces +// #define FIREBALL_EXPLOSION_LARGE3 6 // Used for the big explosion when a ship breaks into pieces +#define MAX_FIREBALL_TYPES 6 // How many types there are + +#define FIREBALL_NUM_LARGE_EXPLOSIONS 2 + +void fireball_init(); +void fireball_render(object * obj); +void fireball_delete( object * obj ); +void fireball_process_pre(object * obj, float frame_time); +void fireball_process_post(object * obj, float frame_time); + +// reversed is for warp_in/out effects +// Velocity: If not NULL, the fireball will move at a constant velocity. +// warp_lifetime: If warp_lifetime > 0.0f then makes the explosion loop so it lasts this long. Only works for warp effect +int fireball_create(vector *pos, int fireball_type, int parent_obj, float size, int reversed=0, vector *velocity=NULL, float warp_lifetime=0.0f, int ship_class=-1, matrix *orient=NULL, int low_res=0); +void fireball_render_plane(int plane); +void fireball_close(); +void fireball_level_close(); +void fireball_preload(); // page in warpout effect data + +// Returns 1 if you can remove this fireball +int fireball_is_perishable(object * obj); + +// Returns 1 if this fireball is a warp +int fireball_is_warp(object * obj); + +// Returns life left of a fireball in seconds +float fireball_lifeleft( object *obj ); + +// Returns life left of a fireball in percent +float fireball_lifeleft_percent( object *obj ); + +// internal function to draw warp grid. +extern void warpin_render(matrix *orient, vector *pos, int texture_bitmap_num, float radius, float life_percent, float max_radius ); +extern int Warp_glow_bitmap; // Internal + +#endif /* _FIREBALLS_H */ + diff --git a/include/fishtank.h b/include/fishtank.h new file mode 100644 index 0000000..4db32ed --- /dev/null +++ b/include/fishtank.h @@ -0,0 +1,29 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/fishtank.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * bloop + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 8/26/99 9:45a Dave + * First pass at easter eggs and cheats. + * + * + * $NoKeywords: $ + */ + +#ifndef __FISHTANK_H__ +#define __FISHTANK_H__ + +void fishtank_start(); +void fishtank_stop(); +void fishtank_process(); + +#endif /* __CREDITS_H__ */ + diff --git a/include/fix.h b/include/fix.h new file mode 100644 index 0000000..c3a167b --- /dev/null +++ b/include/fix.h @@ -0,0 +1,42 @@ +/* + * $Logfile: /Freespace2/code/Math/fix.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for fixed point math + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 4 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _FIX_H +#define _FIX_H + +//#include "pstypes.h" + +#define F1_0 65536 +#define f1_0 65536 + +fix fixmul(fix a, fix b); +fix fixdiv(fix a, fix b); +fix fixmuldiv(fix a, fix b, fix c); + +#define f2i(a) ((int)((a)>>16)) +#define i2f(a) ((fix)((a)<<16)) + +#endif + diff --git a/include/flak.h b/include/flak.h new file mode 100644 index 0000000..5aed6a6 --- /dev/null +++ b/include/flak.h @@ -0,0 +1,71 @@ +/* + * $Logfile: /Freespace2/code/Weapon/Flak.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * flak functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 7/31/99 2:57p Dave + * Scaled flak aim and jitter by weapon subsystem strength. + * + * 5 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * $NoKeywords: $ + */ + +#ifndef _FLAK_WEAPONS_HEADER_FILE +#define _FLAK_WEAPONS_HEADER_FILE + +// -------------------------------------------------------------------------------------------------------------------------------------- +// FLAK DEFINES/VARS +// + +struct weapon; +struct object; + + +// -------------------------------------------------------------------------------------------------------------------------------------- +// FLAK FUNCTIONS +// + +// initialize flak stuff for the level +void flak_level_init(); + +// close down flak stuff +void flak_level_close(); + +// given a newly created weapon, turn it into a flak weapon +void flak_create(weapon *wp); + +// free up a flak object +void flak_delete(int flak_index); + +// given a just fired flak shell, pick a detonating distance for it +void flak_pick_range(object *objp, vector *predicted_target_pos, float weapon_subsys_strength); + +// add some jitter to a flak gun's aiming direction, take into account range to target so that we're never _too_ far off +// assumes dir is normalized +void flak_jitter_aim(vector *dir, float dist_to_target, float weapon_subsys_strength); + +// create a muzzle flash from a flak gun based upon firing position and weapon type +void flak_muzzle_flash(vector *pos, vector *dir, int turret_weapon_class); + +// maybe detonate a flak shell early/late (call from weapon_process_pre(...)) +void flak_maybe_detonate(object *obj); + +// set the range on a flak object +void flak_set_range(object *objp, vector *start_pos, float range); + +// get the current range for the flak object +float flak_get_range(object *objp); + +#endif + diff --git a/include/floating.h b/include/floating.h new file mode 100644 index 0000000..acdb421 --- /dev/null +++ b/include/floating.h @@ -0,0 +1,137 @@ +/* + * $Logfile: /Freespace2/code/Math/Floating.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Low-level floating point math macros and routines + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 4/07/99 6:22p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 3 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 13 2/26/98 3:28p John + * Changed all sqrt's to use fl_sqrt. Took out isqrt function + * + * 12 1/26/98 10:43p Mike + * Make ships not all zoom away from an impending shockwave at the same + * time. Based on ai class and randomness + * + * 11 1/17/98 3:32p Mike + * Add rand_range(), returns random float in min..max. + * + * 10 7/29/97 2:36p Hoffoss + * Added header file required by _isnan(). + * + * 9 7/29/97 2:35p Hoffoss + * Added a NaN check macro. + * + * 8 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _FLOATING_H +#define _FLOATING_H + +#include +#include + +extern float fl_isqrt( float a ); +extern float frand(); +extern int rand_chance(float frametime, float chance = 1.0f); +float frand_range(float min, float max); + +// determine if a floating point number is NaN (Not a Number) +#define fl_is_nan(fl) _isnan(fl) + +// Handy macros to prevent type casting all over the place + +#define fl_sqrt(fl) (float)sqrt((float)(fl)) +#define fl_isqrt(fl) (1.0f/(float)sqrt((float)(fl))) +#define fl_abs(fl) (float)fabs((double)(fl)) +#define i2fl(i) ((float)(i)) +#define fl2i(fl) ((int)(fl)) +#define flceil(fl) (int)ceil(fl) +#define flfloor(fl) (int)floor(fl) +#define f2fl(fx) ((float)(fx)/65536.0f) +#define fl2f(fl) (int)((fl)*65536.0f) + +// convert a measurement in degrees to radians +#define fl_radian(fl) ((float)((fl * 3.14159f)/180.0f)) + +// convert a measurement in radians to degrees +#define fl_degrees(fl) ((float)((fl * 180.0f)/3.14159)) + +// use this instead of: +// for: (int)floor(x+0.5f) use fl_round_2048(x) +// (int)ceil(x-0.5f) use fl_round_2048(x) +// (int)floor(x-0.5f) use fl_round_2048(x-1.0f) +// (int)floor(x) use fl_round_2048(x-0.5f) +// for values in the range -2048 to 2048 +// use this instead of: +// for: (int)floor(x+0.5f) use fl_round_2048(x) +// (int)ceil(x-0.5f) use fl_round_2048(x) +// (int)floor(x-0.5f) use fl_round_2048(x-1.0f) +// (int)floor(x) use fl_round_2048(x-0.5f) +// for values in the range -2048 to 2048 + +extern const float *p_fl_magic; + +inline int fl_round_2048( float x ) +{ + double tmp_quad; + tmp_quad = x + *p_fl_magic; + return *((int *)&tmp_quad); +} + +/* +inline float fl_sqrt( float x) +{ + float retval; + + _asm fld x + _asm fsqrt + _asm fstp retval + + return retval; +} + +float fl_isqrt( float x ) +{ + float retval; + + _asm fld x + _asm fsqrt + _asm fstp retval + + return 1.0f / retval; +} +*/ + + + +// rounds off a floating point number to a multiple of some number +extern float fl_roundoff(float x, int multiple); + + +#endif + diff --git a/include/font.h b/include/font.h new file mode 100644 index 0000000..cec40f1 --- /dev/null +++ b/include/font.h @@ -0,0 +1,149 @@ +/*7 + * $Logfile: /Freespace2/code/Graphics/Font.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * header file for font stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 7/09/99 10:32p Dave + * Command brief and red alert screens. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 14 5/25/98 10:32a John + * Took out redundant code for font bitmap offsets that converted to a + * float, then later on converted back to an integer. Quite unnecessary. + * + * 13 3/25/98 8:07p John + * Restructured software rendering into two modules; One for windowed + * debug mode and one for DirectX fullscreen. + * + * 12 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 11 3/09/98 6:06p John + * Restructured font stuff to avoid duplicate code in Direct3D and Glide. + * Restructured Glide to avoid redundent state setting. + * + * 10 2/19/98 9:04a John + * Fixed fonts with glide + * + * 9 2/17/98 7:27p John + * Got fonts and texturing working in Direct3D + * + * 8 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 7 11/06/97 5:42p Hoffoss + * Added support for fixed size timstamp rendering. + * + * 6 11/03/97 10:59a John + * added support for more than one font. + * + * 5 10/24/97 12:13p Hoffoss + * Added gr_force_fit_string(). + * + * 4 10/09/97 5:23p John + * Added support for more 16-bpp functions + * + * 3 6/05/97 4:53p John + * First rev of new antialiased font stuff. + * + * 2 4/22/97 10:33a John + * fixed the 2d resource leaks that Alan found. + * + * 1 3/31/97 9:42a Allender + * + * $NoKeywords: $ + */ + +#ifndef _FONT_H +#define _FONT_H + +#include "pstypes.h" + +#define MAX_FONTS 3 + +#define FONT_VERSION 0 +#define WIDEST_DIGIT "4" // the widest number character +#define WIDEST_CHAR "W" // the widest character + +typedef struct font_char { + int spacing; + int byte_width; + int offset; + short kerning_entry; + short user_data; +} font_char; + +typedef struct font_kernpair { + char c1,c2; + signed char offset; +} font_kernpair; + + +typedef struct font { + char filename[MAX_FILENAME_LEN]; + int id; // Should be 'VFNT' + int version; // font version + int num_chars; + int first_ascii; + int w; + int h; + int num_kern_pairs; + int kern_data_size; + int char_data_size; + int pixel_data_size; + font_kernpair *kern_data; + font_char *char_data; + ubyte *pixel_data; + + // Data for 3d cards + int bitmap_id; // A bitmap representing the font data + int bm_w, bm_h; // Bitmap width and height + ubyte *bm_data; // The actual font data + int *bm_u; // U offset of each character + int *bm_v; // V offset of each character + +} font; + +extern int Num_fonts; +extern font Fonts[MAX_FONTS]; +extern font *Current_font; + +#define FONT1 0 // font01.vf +#define FONT2 1 // font02.vf +#define FONT3 2 // font03.vf + +// extern definitions for basic font functions +extern void grx_set_font(int fontnum); +extern void gr8_string(int x,int y,char * text); + +void gr_print_timestamp(int x, int y, int timestamp); +char *gr_force_fit_string(char *str, int max_str, int max_width); +void gr_font_init(); +void gr_font_close(); + +extern font * Current_font; +extern int get_char_width(ubyte c1,ubyte c2,int *width,int *spacing); +extern int get_centered_x(char *s); + +#endif + diff --git a/include/fonttool.h b/include/fonttool.h new file mode 100644 index 0000000..53c1365 --- /dev/null +++ b/include/fonttool.h @@ -0,0 +1,44 @@ +/* + * $Logfile: /Freespace2/code/Fonttool/FontTool.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for font creating/kerning tools + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 2 10/24/98 5:15p Dave + * + * 1 10/24/98 4:58p Dave + * + * 2 6/05/97 4:53p John + * First rev of new antialiased font stuff. + * + * 1 6/02/97 4:04p John + * + * $NoKeywords: $ + */ + +#ifndef _FONTTOOL_H +#define _FONTTOOL_H + +#include "2d.h" +#include "font.h" + +void fonttool_edit_kerning(char *fname1); +void fonttool_kerning_copy( char *fname1, char *fname2 ); +void fonttool_create_font(char *pcx_filename, char *font_filename); + +void fonttool_read( char *fname2, font *fnt2 ); +void fonttool_copy_kern( font *fnt1, font *fnt2 ); +void fonttool_dump( char *fname1, font *fnt1 ); +void fonttool_remove_kerning( font *fnt ); +void fonttool_resync_kerning(font *fnt); + + +#endif + diff --git a/include/fred.h b/include/fred.h new file mode 100644 index 0000000..f6470fb --- /dev/null +++ b/include/fred.h @@ -0,0 +1,212 @@ +/* + * $Logfile: /Freespace2/code/FRED2/FRED.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * FRED.h : main header file for the FRED application + * Global editor dialog box classes are instantiated here, initializes the + * application (MFC level at least), processes the INI file. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 32 3/23/98 4:04p Hoffoss + * Fixed dialog window initialization so it looks better at startup (they + * don't flash on for a second). + * + * 31 9/09/97 10:25a Hoffoss + * Fixed a potential problem. + * + * 30 8/01/97 3:10p Hoffoss + * Made Sexp help hidable. + * + * 29 6/18/97 11:46a Hoffoss + * Fixed initial order object reference updating and added briefing dialog + * window tracking data. + * + * 28 6/05/97 6:10p Hoffoss + * Added features: Autosaving, object hiding. Also fixed some minor bugs. + * + * 27 6/02/97 11:52a Hoffoss + * Custom cursors displayed when over objects in different modes. + * + * 26 5/29/97 5:15p Allender + * fixed macro for MODIFY so that it will work properly within 'if' + * statements + * + * 25 4/29/97 12:24p Adam + * JAS: Added code for delayed point to vec. Fixed some FRED + * sequencing problems with g3_start_frame / g3_end_frame. + * + * 24 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 23 4/01/97 11:07p Mike + * Clean up game sequencing functions. Get rid of Multiplayer and add + * Game_mode. Add SystemVars.cpp + * + * 22 3/26/97 11:45a Hoffoss + * Fred uses fvi_ray_* functions now (as much as possible) for checking + * mouse selection stuff. + * + * 21 2/28/97 11:31a Hoffoss + * Implemented modeless dialog saving and restoring, and changed some + * variables names. + * + * 20 2/27/97 5:54p Hoffoss + * Implemented support for saving and restoring window positions. + * + * 19 2/24/97 12:50p Hoffoss + * First attempt at non-continuous redrawing. + * + * 18 2/20/97 5:42p Hoffoss + * Fixed bug in modification checking and updating macro. + * + * 17 2/20/97 4:28p Hoffoss + * Added modification tracking to ship editor dialog box, and support + * functions. + * + * 16 2/20/97 4:03p Hoffoss + * Several ToDo items: new reinforcement clears arrival cue, reinforcement + * control from ship and wing dialogs, show grid toggle. + * + * 15 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 14 2/12/97 12:25p Hoffoss + * Expanded on global error checker, added initial orders conflict + * checking and warning, added waypoint editor dialog and code. + * + * 13 1/30/97 2:24p Hoffoss + * Added remaining mission file structures and implemented load/save of + * them. + * + * $NoKeywords: $ + */ + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "systemvars.h" // Low level variables, common to FreeSpace and Fred +#include "resource.h" // main symbols +#include "missionparse.h" +#include "shipeditordlg.h" +#include "wing_editor.h" +#include "waypointpathdlg.h" +#include "bgbitmapdlg.h" +#include "briefingeditordlg.h" +#include "systemvars.h" + +#define MODIFY(a, b) do { \ + if (a != (b)) { \ + a = (b); \ + set_modified(); \ + } \ +} while(0) + +#define F_RENDER_SHIP_MODELS 0x01 +#define F_RENDER_SHIP_ICONS 0x02 + +// user interface types +#define HOFFOSS_INTERFACE 1 +#define ALLENDER_INTERFACE 2 + +typedef struct window_data { + WINDOWPLACEMENT p; + int visible; + int valid; + int processed; +} window_data; + +///////////////////////////////////////////////////////////////////////////// +// CFREDApp: +// See FRED.cpp for the implementation of this class +// + +class CFREDApp : public CWinApp +{ + int app_init; + +public: + void record_window_data(window_data *wndd, CWnd *wnd); + int init_window(window_data *wndd, CWnd *wnd, int adjust = 0, int pre = 0); + void read_window(char *name, window_data *wndd); + void write_window(char *name, window_data *wndd); + void write_ini_file(int degree = 0); + CFREDApp(); + + // Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CFREDApp) + public: + virtual BOOL InitInstance(); + virtual BOOL OnIdle(LONG lCount); + //}}AFX_VIRTUAL + +// Implementation + + //{{AFX_MSG(CFREDApp) + afx_msg void OnAppAbout(); + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +// Add a message to be processed at the end of this frame. +// This is useful if you need the display to update before it's useful +// to process the message. For example, right click brings up a popup menu. +// But the menu it brings up depends on where you right clicked. If you +// right click on a ship, you get a message that pertains to the chosen +// ship. It is useful to have a visual indication that you have changed the +// current ship. +void add_pending_message(HWND hwnd, int id, int wparam, int lparam, int skip_count); +void init_pending_messages(void); +void update_map_window(); + +extern int User_interface; +extern int Fred_active; +extern int Update_window; +extern HCURSOR h_cursor_move, h_cursor_rotate; + +extern CWnd *Prev_window; +extern CShipEditorDlg Ship_editor_dialog; +extern wing_editor Wing_editor_dialog; +extern waypoint_path_dlg Waypoint_editor_dialog; +extern bg_bitmap_dlg *Bg_bitmap_dialog; +extern briefing_editor_dlg *Briefing_dialog; + +extern CFREDApp theApp; + +extern window_data Main_wnd_data; +extern window_data Ship_wnd_data; +extern window_data Wing_wnd_data; +extern window_data Object_wnd_data; +extern window_data Mission_goals_wnd_data; +extern window_data Messages_wnd_data; +extern window_data Player_wnd_data; +extern window_data Events_wnd_data; +extern window_data Bg_wnd_data; +extern window_data Briefing_wnd_data; +extern window_data Reinforcement_wnd_data; +extern window_data Waypoint_wnd_data; +extern window_data Starfield_wnd_data; +extern window_data Asteroid_wnd_data; +extern window_data Mission_notes_wnd_data; + diff --git a/include/freddoc.h b/include/freddoc.h new file mode 100644 index 0000000..da602c8 --- /dev/null +++ b/include/freddoc.h @@ -0,0 +1,169 @@ +/* + * $Logfile: /Freespace2/code/FRED2/FREDDoc.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * FREDDoc.h : interface of the CFREDDoc class + * Document class for document/view architechure, which we don't really use in + * Fred, but MFC forces you do use like it or not. Handles loading/saving + * mainly. Most of the MFC related stuff is handled in FredView. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 15 8/17/97 10:22p Hoffoss + * Fixed several bugs in Fred with Undo feature. In the process, recoded + * a lot of CFile.cpp. + * + * 14 8/13/97 5:49p Hoffoss + * Fixed bugs, made additions. + * + * 13 8/13/97 12:46p Hoffoss + * Added campaign error checker, accelerator table, and mission goal data + * listings to sexp tree right click menu. + * + * 12 6/09/97 4:57p Hoffoss + * Added autosave and undo to Fred. + * + * 11 6/05/97 6:10p Hoffoss + * Added features: Autosaving, object hiding. Also fixed some minor bugs. + * + * 10 5/21/97 5:42p Hoffoss + * Added features requested on Todo list. + * + * 9 2/20/97 4:03p Hoffoss + * Several ToDo items: new reinforcement clears arrival cue, reinforcement + * control from ship and wing dialogs, show grid toggle. + * + * 8 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 14 2/12/97 12:25p Hoffoss + * Expanded on global error checker, added initial orders conflict + * checking and warning, added waypoint editor dialog and code. + * + * 13 1/30/97 2:24p Hoffoss + * Added remaining mission file structures and implemented load/save of + * them. + * + * $NoKeywords: $ + */ + +#ifndef _FREDDOC_H +#define _FREDDOC_H + +#include "ailocal.h" +#include "missionsave.h" + +#define MISSION_BACKUP_NAME "Backup" + +#define US_WORLD_CHANGED 0x01 +#define US_VIEW_CHANGED 0x02 + +class CFREDDoc : public CDocument +{ +protected: // create from serialization only + CFREDDoc(); + DECLARE_DYNCREATE(CFREDDoc) + +// Attributes +public: + int check_undo(); + int autoload(); + int load_mission(char *pathname); + int autosave(char *desc); + int save_matrix(matrix &m, FILE *fp); + int save_vector(vector &v, FILE *fp); + BOOL confirm_deleting; + BOOL show_capital_ships; + BOOL show_elevations; + BOOL show_fighters; + BOOL show_grid; + BOOL show_misc_objects; + BOOL show_planets; + BOOL show_waypoints; + BOOL show_starfield; + char mission_pathname[256]; + +// Operations +public: + CString undo_desc[BACKUP_DEPTH + 1]; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CFREDDoc) + public: + virtual BOOL OnNewDocument(); + virtual void Serialize(CArchive& ar); + virtual void OnEditClearAll(); + virtual void DeleteContents(); + virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); + virtual BOOL OnSaveDocument(LPCTSTR lpszPathName); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CFREDDoc(); + static void UpdateStatus(int flags = US_WORLD_CHANGED); + +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext &dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CFREDDoc) + afx_msg void OnEditDelete(); + afx_msg void OnDuplicate(); + afx_msg void OnEditCopy(); + afx_msg void OnEditCut(); + afx_msg void OnEditHold(); + afx_msg void OnEditFetch(); + afx_msg void OnEditPaste(); + afx_msg void OnEditUndo(); + afx_msg void OnFilePreferences(); + afx_msg void OnFileSave(); + afx_msg void OnFileNew(); + afx_msg void editor_init_mission(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: + int save_waypoint_list(waypoint_list &w, FILE *fp); + int save_waypoints(FILE *fp); + int save_goals(FILE *fp); + int save_wings(FILE *fp); + int save_objects(FILE *fp); + int save_players(FILE *fp); + int save_briefing_info(FILE *fp); + int save_plot_info(FILE *fp); + int save_mission_info(FILE *FP); +}; + +extern int Local_modified; +extern int Undo_available; +extern int Undo_count; +extern CFREDDoc *FREDDoc_ptr; + +void set_modified(BOOL arg = TRUE); + +///////////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/include/fredrender.h b/include/fredrender.h new file mode 100644 index 0000000..d35a540 --- /dev/null +++ b/include/fredrender.h @@ -0,0 +1,146 @@ +/* + * $Logfile: /Freespace2/code/FRED2/FredRender.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Handles all view rendering in FRED. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 26 3/19/98 11:41a Hoffoss + * Fixed problems with rendering and reading of flying controls in Fred. + * + * 25 10/30/97 3:30p Hoffoss + * Made anti-aliased gridlines an option in Fred. + * + * 24 9/09/97 2:12p Hoffoss + * Added code to allow briefing editor view to be a 1:1 pixel size fixed + * as the FreeSpace view will have. + * + * 23 8/25/97 5:58p Hoffoss + * Created menu items for keypress functions in Fred, and fixed bug this + * uncovered with wing_delete function. + * + * 22 8/07/97 6:01p Hoffoss + * Added a rotate about selected object button to toolbar and + * functionality, as requested by Comet. + * + * 21 8/06/97 6:10p Hoffoss + * Changed Fred to display a forced aspect ratio while briefing editor is + * open. This aspect ratio is the same as the briefing view in FreeSpace, + * so icons appear on and off screen the same as would be in FreeSpace. + * + * 20 6/26/97 5:18p Hoffoss + * Major rework of briefing editor functionality. + * + * 19 6/23/97 3:00p Hoffoss + * Added a define. + * + * 18 6/23/97 2:58p Hoffoss + * Added briefing lookat point variables. + * + * 17 6/18/97 11:36p Lawrance + * move grid rendering code to MissionGrid.cpp + * + * 16 6/12/97 11:27a Lawrance + * separating FRED dependant briefing code + * + * 15 3/06/97 3:35p Hoffoss + * Added Show_outline stuff, moved show options to the view menu, fixed a + * bug in message dialog editor. + * + * 14 2/20/97 4:03p Hoffoss + * Several ToDo items: new reinforcement clears arrival cue, reinforcement + * control from ship and wing dialogs, show grid toggle. + * + * 13 12/02/96 3:36p Hoffoss + * Show horizon now implemented. + * + * 12 11/22/96 12:24p Hoffoss + * Editor functionality added. + * + * 11 11/21/96 12:52p Hoffoss + * Changes to flying controls, etc. + * + * 10 11/21/96 9:20a Hoffoss + * New show distances feature. + * + * 9 11/20/96 5:16p Hoffoss + * New grid system working as suggested. + * + * 8 11/20/96 10:01a Hoffoss + * A few minor improvements. + * + * 7 11/19/96 9:50a Hoffoss + * New interface working, but not finished yet. + * + * 6 11/15/96 1:43p Hoffoss + * Improvements to the Ship Dialog editor window. It is now an + * independant window that updates data correctly. + * + * 5 11/14/96 10:43a Hoffoss + * Made changes to grid display and how it works, etc. + * + * 4 11/13/96 10:15a Hoffoss + * Waypoint editing added, but not quite finished yet. + * + * 3 11/12/96 11:14a Hoffoss + * Everything check in because I don't know what's changed and what's not + * to prevent compiling. + * + * 2 11/11/96 3:47p Hoffoss + * Milestone Checkin. + * + * 1 10/29/96 12:17p Hoffoss + * + * $NoKeywords: $ + */ + +#include "missiongrid.h" + +#define BRIEFING_LOOKAT_POINT_ID 99999 + +extern int Aa_gridlines; +extern int player_start1; +extern int Editing_mode; +extern int Control_mode; +extern int Show_grid; +extern int Show_grid_positions; +extern int Show_coordinates; +extern int Show_outlines; +extern int Single_axis_constraint; +extern int Show_distances; +extern int Universal_heading; +extern int Flying_controls_mode; +extern int Group_rotate; +extern int Show_horizon; +extern int Lookat_mode; +extern int True_rw, True_rh; +extern int Fixed_briefing_size; +extern vector Constraint, Anticonstraint; +extern vector Tp1, Tp2; // test points +extern physics_info view_physics; +extern vector view_pos, eye_pos; +extern matrix view_orient, eye_orient; + +void fred_render_init(); +void generate_starfield(); +void move_mouse(int btn, int mdx, int mdy); +void game_do_frame(); +void render_frame(); +void level_controlled(); +void verticalize_controlled(); + diff --git a/include/fredview.h b/include/fredview.h new file mode 100644 index 0000000..5148649 --- /dev/null +++ b/include/fredview.h @@ -0,0 +1,519 @@ +/* + * $Logfile: /Freespace2/code/fred2/FREDView.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * View class for a document/view architechure design program, which we don't + * want or need, but MFC forces us to use. This is the main place we handle + * MFC messages, events, etc. Sort of the interface between our code and MFC. + * There is also a lot of our code in here related to these things. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 6/04/99 2:20p Andsager + * Add dump stats basic functionality + * + * 5 3/26/99 4:49p Dave + * Made cruisers able to dock with stuff. Made docking points and paths + * visible in fred. + * + * 4 1/27/99 4:09p Andsager + * Added highlight to ship subsystems + * + * 3 10/13/98 9:27a Dave + * Started neatening up freespace.h + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 84 5/21/98 11:48a Hoffoss + * Removed check in and check out options. + * + * 83 5/14/98 5:32p Hoffoss + * Added some more error checking. + * + * 82 4/01/98 10:48a Hoffoss + * Changed Fred to not allow command briefings in multiplayer missions. + * + * 81 3/25/98 4:14p Hoffoss + * Split ship editor up into ship editor and a misc dialog, which tracks + * flags and such. + * + * 80 3/09/98 10:56a Hoffoss + * Added jump node objects to Fred. + * + * 79 3/05/98 3:59p Hoffoss + * Added a bunch of new command brief stuff, and asteroid initialization + * to Fred. + * + * 78 1/29/98 5:14p Hoffoss + * Added error checking for more than 4 ships in a player starting wing. + * + * 77 10/30/97 3:30p Hoffoss + * Made anti-aliased gridlines an option in Fred. + * + * 76 10/14/97 10:59a Allender + * more persona work. Made global error checker call funciton to assign + * and check personas + * + * 75 9/06/97 2:13p Mike + * Replace support for TEAM_NEUTRAL + * + * 74 9/01/97 6:59p Hoffoss + * Added source safe checkin and checkout capabilities to Fred. + * + * 73 8/26/97 4:18p Hoffoss + * Added error checking to initial orders dialog when ok is clicked. + * + * 72 8/25/97 5:58p Hoffoss + * Created menu items for keypress functions in Fred, and fixed bug this + * uncovered with wing_delete function. + * + * 71 8/18/97 9:31p Hoffoss + * Added grid adjustment dialog and shield system editor dialog. + * + * 70 8/14/97 2:32p Hoffoss + * fixed bug where controlling an object doesn't cause screen updates, and + * added a number of cool features to viewpoint/control object code. + * + * 69 8/12/97 6:32p Hoffoss + * Added code to allow hiding of arrival and departure cues in editors. + * + * 68 8/11/97 6:54p Hoffoss + * Groups now supported in Fred. + * + * 67 8/11/97 11:51a Allender + * added stamp stuff to Fred + * + * 66 8/07/97 6:01p Hoffoss + * Added a rotate about selected object button to toolbar and + * functionality, as requested by Comet. + * + * 65 8/01/97 3:10p Hoffoss + * Made Sexp help hidable. + * + * 64 8/01/97 12:52p Hoffoss + * Added variable, fixed bug with global error check. + * + * 63 7/28/97 2:51p Hoffoss + * Re-evaluated and improved global error checker in Fred. + * + * 62 7/24/97 12:45p Hoffoss + * Added camera position save and restore. + * + * 61 7/08/97 2:03p Hoffoss + * Debriefing editor coded and implemented. + * + * 60 6/18/97 3:07p Hoffoss + * Wing ship names are 1 indexes instead of 0 indexed now. + * + * 59 6/09/97 4:57p Hoffoss + * Added autosave and undo to Fred. + * + * 58 6/05/97 6:10p Hoffoss + * Added features: Autosaving, object hiding. Also fixed some minor bugs. + * + * 57 6/02/97 11:52a Hoffoss + * Custom cursors displayed when over objects in different modes. + * + * 56 5/21/97 5:43p Hoffoss + * Added features requested on Todo list. + * + * 55 5/06/97 3:01p Hoffoss + * Added some accelerator keys. + * + * 54 5/05/97 5:44p Hoffoss + * Added specialized popup menu choices, save before running FreeSpace, + * and display filters. + * + * 53 5/05/97 9:42a Hoffoss + * Single axis contraint code changed to operate better. + * + * 52 4/24/97 5:15p Hoffoss + * fixes to Fred. + * + * 51 4/17/97 5:23p Hoffoss + * Implemented ability to run FreeSpace from Fred. + * + * 50 4/16/97 5:18p Hoffoss + * Moved Asteroid field editor stuff to a seperate dialog box. + * + * 49 4/11/97 4:22p Hoffoss + * Fixed bug in Sexp trees, moved Show starfield option to view menu and + * removed preferences dialog box. + * + * 48 3/12/97 12:40p Hoffoss + * Fixed bugs in wing object management functions, several small additions + * and rearrangements. + * + * 47 3/10/97 4:58p Hoffoss + * Added waypoint and start types to drop down toolbar combo box and fixed + * context menu new ship type selection to also work. + * + * 46 3/10/97 12:54p Hoffoss + * Added drop down combo box to toolbar and fixed compiling errors Mark + * (maybe Mike?) introduced to code. + * + * 45 3/06/97 3:35p Hoffoss + * Added Show_outline stuff, moved show options to the view menu, fixed a + * bug in message dialog editor. + * + * 44 3/04/97 6:27p Hoffoss + * Changes to Fred to handle new wing structure. + * + * 43 2/28/97 11:31a Hoffoss + * Implemented modeless dialog saving and restoring, and changed some + * variables names. + * + * 42 2/27/97 5:54p Hoffoss + * Implemented support for saving and restoring window positions. + * + * 41 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 72 2/12/97 5:50p Hoffoss + * Expanded on error checking. + * + * 71 2/12/97 12:25p Hoffoss + * Expanded on global error checker, added initial orders conflict + * checking and warning, added waypoint editor dialog and code. + * + * 70 2/10/97 5:18p Hoffoss + * Global error checker work. Added checks for many things, but still + * more to add tomorrow! + * + * 69 2/04/97 3:09p Hoffoss + * Background bitmap editor implemented fully. + * + * 68 1/30/97 2:24p Hoffoss + * Added remaining mission file structures and implemented load/save of + * them. + * + * $NoKeywords: $ + */ + +#ifndef STAMPER_PROGRAM + +#include "freddoc.h" + +#define WM_MENU_POPUP_SHIPS (WM_USER+6) +#define WM_MENU_POPUP_EDIT (WM_USER+7) +#define SEXP_HELP_BOX_SIZE 170 + +typedef struct Marking_box { + int x1, y1, x2, y2; +} Marking_box; + +typedef struct subsys_to_render +{ + bool do_render; + object *ship_obj; + ship_subsys *cur_subsys; +} subsys_to_render; + +class CShipEditorDlg; +class CGrid; + +class CFREDView : public CView +{ +private: + CGrid* m_pGDlg; + int global_error_check_player_wings(int multi); + +protected: // create from serialization only + CFREDView(); + DECLARE_DYNCREATE(CFREDView) + +// Attributes +public: + int global_error_check_mixed_player_wing(int w); + int fred_check_sexp(int sexp, int type, char *msg, ...); + int internal_error(char *msg, ...); + int error(char *msg, ...); + int global_error_check(); + void place_background_bitmap(vector v); + void cycle_constraint(); + CFREDDoc *GetDocument(); + + static CFREDView *GetView(); + +LONG OnGoodbye(UINT wParam, LONG lParam); +// LONG OnMenuPopupShips(CWnd *pWnd, CPoint point); +// LONG OnMenuPopupShips(UINT wParam, LONG lParam); + + //BOOL m_ConfirmDeleting; + //BOOL m_ShowCapitalShips; + //BOOL m_ShowElevations; + //BOOL m_ShowFighters; + //BOOL m_ShowGrid; + //BOOL m_ShowMiscObjects; + //BOOL m_ShowPlanets; + //BOOL m_ShowWaypoints; + + // Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CFREDView) + public: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo); + virtual void OnInitialUpdate(); + protected: + virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); + virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); + virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CFREDView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +// virtual void SEDlg_destroy(); + +// Generated message map functions +protected: + //{{AFX_MSG(CFREDView) + afx_msg void OnViewGrid(); + afx_msg void OnUpdateViewGrid(CCmdUI* pCmdUI); + afx_msg void OnViewWaypoints(); + afx_msg void OnUpdateViewWaypoints(CCmdUI* pCmdUI); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnEditorsShips(); + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnMiscstuffShowshipsasicons(); + afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); + afx_msg void OnEditPopupShowShipIcons(); + afx_msg void OnUpdateEditPopupShowShipIcons(CCmdUI* pCmdUI); + afx_msg void OnEditPopupShowShipModels(); + afx_msg void OnUpdateEditPopupShowShipModels(CCmdUI* pCmdUI); + afx_msg void OnMiscStatistics(); + afx_msg void OnEditPopupShowCompass(); + afx_msg void OnUpdateEditPopupShowCompass(CCmdUI* pCmdUI); + afx_msg void OnUpdateChangeViewpointExternal(CCmdUI* pCmdUI); + afx_msg void OnChangeViewpointExternal(); + afx_msg void OnUpdateChangeViewpointFollow(CCmdUI* pCmdUI); + afx_msg void OnChangeViewpointFollow(); + afx_msg void OnEditorsGoals(); + afx_msg void OnSpeed1(); + afx_msg void OnSpeed2(); + afx_msg void OnSpeed5(); + afx_msg void OnSpeed10(); + afx_msg void OnUpdateSpeed1(CCmdUI* pCmdUI); + afx_msg void OnSpeed3(); + afx_msg void OnSpeed8(); + afx_msg void OnRot1(); + afx_msg void OnRot2(); + afx_msg void OnRot3(); + afx_msg void OnRot4(); + afx_msg void OnRot5(); + afx_msg void OnUpdateSpeed2(CCmdUI* pCmdUI); + afx_msg void OnUpdateSpeed3(CCmdUI* pCmdUI); + afx_msg void OnUpdateSpeed5(CCmdUI* pCmdUI); + afx_msg void OnUpdateSpeed8(CCmdUI* pCmdUI); + afx_msg void OnUpdateSpeed10(CCmdUI* pCmdUI); + afx_msg void OnUpdateRot1(CCmdUI* pCmdUI); + afx_msg void OnUpdateRot2(CCmdUI* pCmdUI); + afx_msg void OnUpdateRot3(CCmdUI* pCmdUI); + afx_msg void OnUpdateRot4(CCmdUI* pCmdUI); + afx_msg void OnUpdateRot5(CCmdUI* pCmdUI); + afx_msg void OnControlModeCamera(); + afx_msg void OnUpdateControlModeCamera(CCmdUI* pCmdUI); + afx_msg void OnControlModeShip(); + afx_msg void OnUpdateControlModeShip(CCmdUI* pCmdUI); + afx_msg void OnShowGridPositions(); + afx_msg void OnUpdateShowGridPositions(CCmdUI* pCmdUI); + afx_msg void OnShowCoordinates(); + afx_msg void OnUpdateShowCoordinates(CCmdUI* pCmdUI); + afx_msg void OnSpeed50(); + afx_msg void OnUpdateSpeed50(CCmdUI* pCmdUI); + afx_msg void OnSpeed100(); + afx_msg void OnUpdateSpeed100(CCmdUI* pCmdUI); + afx_msg void OnSelect(); + afx_msg void OnUpdateSelect(CCmdUI* pCmdUI); + afx_msg void OnSelectAndMove(); + afx_msg void OnUpdateSelectAndMove(CCmdUI* pCmdUI); + afx_msg void OnSelectAndRotate(); + afx_msg void OnUpdateSelectAndRotate(CCmdUI* pCmdUI); + afx_msg void OnConstrainX(); + afx_msg void OnUpdateConstrainX(CCmdUI* pCmdUI); + afx_msg void OnConstrainY(); + afx_msg void OnUpdateConstrainY(CCmdUI* pCmdUI); + afx_msg void OnConstrainZ(); + afx_msg void OnUpdateConstrainZ(CCmdUI* pCmdUI); + afx_msg void OnConstrainXz(); + afx_msg void OnUpdateConstrainXz(CCmdUI* pCmdUI); + afx_msg void OnSelectionLock(); + afx_msg void OnUpdateSelectionLock(CCmdUI* pCmdUI); + afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); + afx_msg void OnDoubleFineGridlines(); + afx_msg void OnUpdateDoubleFineGridlines(CCmdUI* pCmdUI); + afx_msg void OnShowDistances(); + afx_msg void OnUpdateShowDistances(CCmdUI* pCmdUI); + afx_msg void OnUniversalHeading(); + afx_msg void OnUpdateUniversalHeading(CCmdUI* pCmdUI); + afx_msg void OnFlyingControls(); + afx_msg void OnUpdateFlyingControls(CCmdUI* pCmdUI); + afx_msg void OnRotateLocally(); + afx_msg void OnUpdateRotateLocally(CCmdUI* pCmdUI); + afx_msg void OnConstrainXy(); + afx_msg void OnUpdateConstrainXy(CCmdUI* pCmdUI); + afx_msg void OnUpdateConstrainYz(CCmdUI* pCmdUI); + afx_msg void OnConstrainYz(); + afx_msg void OnSelectList(); + afx_msg void OnZoomExtents(); + afx_msg void OnZoomSelected(); + afx_msg void OnUpdateZoomSelected(CCmdUI* pCmdUI); + afx_msg void OnFormWing(); + afx_msg void OnUpdateFormWing(CCmdUI* pCmdUI); + afx_msg void OnDisbandWing(); + afx_msg void OnUpdateDisbandWing(CCmdUI* pCmdUI); + afx_msg void OnShowHorizon(); + afx_msg void OnUpdateShowHorizon(CCmdUI* pCmdUI); + afx_msg void OnEditorsWing(); + afx_msg void OnEditorsPlayer(); + afx_msg void OnEditorsOrient(); + afx_msg void OnEditorsEvents(); + afx_msg void OnUpdateEditorsOrient(CCmdUI* pCmdUI); + afx_msg void OnEditorsMessage(); + afx_msg void OnEditorsStarfield(); + afx_msg void OnEditorsBgBitmaps(); + afx_msg void OnEditorsReinforcement(); + afx_msg void OnErrorChecker(); + afx_msg void OnEditorsWaypoint(); + afx_msg void OnViewOutlines(); + afx_msg void OnUpdateViewOutlines(CCmdUI* pCmdUI); + afx_msg void OnUpdateNewShipType(CCmdUI* pCmdUI); + afx_msg void OnShowStarfield(); + afx_msg void OnUpdateShowStarfield(CCmdUI* pCmdUI); + afx_msg void OnAsteroidEditor(); + afx_msg void OnRunFreespace(); + afx_msg void OnEditorCampaign(); + afx_msg void OnShowShips(); + afx_msg void OnUpdateShowShips(CCmdUI* pCmdUI); + afx_msg void OnShowStarts(); + afx_msg void OnUpdateShowStarts(CCmdUI* pCmdUI); + afx_msg void OnShowFriendly(); + afx_msg void OnUpdateShowFriendly(CCmdUI* pCmdUI); + afx_msg void OnShowHostile(); + afx_msg void OnUpdateShowHostile(CCmdUI* pCmdUI); + afx_msg void OnToggleViewpoint(); + afx_msg void OnRevert(); + afx_msg void OnUpdateRevert(CCmdUI* pCmdUI); + afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message); + afx_msg void OnHideObjects(); + afx_msg void OnShowHiddenObjects(); + afx_msg void OnEditUndo(); + afx_msg void OnUpdateEditUndo(CCmdUI* pCmdUI); + afx_msg void OnEditorsBriefing(); + afx_msg void OnEditorsDebriefing(); + afx_msg void OnSaveCamera(); + afx_msg void OnRestoreCamera(); + afx_msg void OnUpdateRestoreCamera(CCmdUI* pCmdUI); + afx_msg void OnShowSexpHelp(); + afx_msg void OnUpdateShowSexpHelp(CCmdUI* pCmdUI); + afx_msg void OnLookatObj(); + afx_msg void OnUpdateLookatObj(CCmdUI* pCmdUI); + afx_msg void OnEditorsAdjustGrid(); + afx_msg void OnEditorsShieldSys(); + afx_msg void OnLevelObj(); + afx_msg void OnAlignObj(); + afx_msg void OnControlObj(); + afx_msg void OnNextObj(); + afx_msg void OnPrevObj(); + afx_msg void OnEditDeleteWing(); + afx_msg void OnMarkWing(); + afx_msg void OnUpdateControlObj(CCmdUI* pCmdUI); + afx_msg void OnEditDelete(); + afx_msg void OnAaGridlines(); + afx_msg void OnUpdateAaGridlines(CCmdUI* pCmdUI); + afx_msg void OnCmdBrief(); + afx_msg void OnDisableUndo(); + afx_msg void OnUpdateDisableUndo(CCmdUI* pCmdUI); + afx_msg void OnUpdateCmdBrief(CCmdUI* pCmdUI); + afx_msg void OnNextSubsys(); + afx_msg void OnPrevSubsys(); + afx_msg void OnCancelSubsys(); + afx_msg void OnDumpStats(); + afx_msg void OnShowPaths(); + afx_msg void OnUpdateShowPaths(CCmdUI* pCmdUI); + afx_msg void OnShowDockPoints(); + afx_msg void OnUpdateShowDockPoints(CCmdUI* pCmdUI); + //}}AFX_MSG + afx_msg void OnGroup(UINT nID); + afx_msg void OnSetGroup(UINT nID); + +// LONG OnMenuPopupShips(CWnd *pWnd, CPoint point); +LONG OnMenuPopupShips(UINT wParam, LONG lParam); +LONG OnMenuPopupEdit(UINT wParam, LONG lParam); + + DECLARE_MESSAGE_MAP() +}; + +#ifndef _DEBUG // debug version in FREDView.cpp +inline CFREDDoc* CFREDView::GetDocument() + { return (CFREDDoc*)m_pDocument; } +#endif + +///////////////////////////////////////////////////////////////////////////// + +void cancel_drag(); +char *error_check_initial_orders(ai_goal *goals, int ship, int wing); +extern void fred_check_message_personas(); + +extern int Autosave_disabled; +extern int Show_sexp_help; +extern int Show_ships; +extern int Show_starts; +extern int Show_friendly; +extern int Show_hostile; +extern int Show_neutral; +extern int physics_speed; +extern int physics_rot; +extern int viewpoint; +extern int view_obj; +extern int box_marking; // Are we currently box marking? (i.e. draging out a box to mark) +extern int button_down; // Is the left mouse button down and we are handling it? +extern int Marked; // number of marked objects +extern int Show_compass; +extern int Show_ship_models; +extern int Show_ship_info; +extern int Show_dock_points; +extern int Show_paths_fred; +extern int Selection_lock; +extern int Cursor_over; +extern int Cur_bitmap; +extern int Id_select_type_jump_node; +extern int Id_select_type_start; +extern int Id_select_type_waypoint; +extern int Hide_ship_cues, Hide_wing_cues; +extern Marking_box marking_box; +extern object_orient_pos rotation_backup[MAX_OBJECTS]; + +extern CFREDView *Fred_view_wnd; + +#endif // if #ifndef STAMPER_PROGRAM + diff --git a/include/freespace.h b/include/freespace.h new file mode 100644 index 0000000..32af186 --- /dev/null +++ b/include/freespace.h @@ -0,0 +1,346 @@ +/* + * $Logfile: /Freespace2/code/FREESPACE2/FreeSpace.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * FreeSpace, the game, not the project, header information. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 24 10/06/99 10:32a Jefff + * oem updates + * + * 23 9/30/99 6:04p Jefff + * OEM changes + * + * 22 9/08/99 3:22p Dave + * Updated builtin mission list. + * + * 21 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 20 9/06/99 1:30a Dave + * Intermediate checkin. Started on enforcing CD-in-drive to play the + * game. + * + * 19 9/06/99 1:16a Dave + * Make sure the user sees the intro movie. + * + * 18 9/05/99 11:19p Dave + * Made d3d texture cache much more safe. Fixed training scoring bug where + * it would backout scores without ever having applied them in the first + * place. + * + * 17 9/03/99 1:32a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 16 8/19/99 10:12a Alanl + * preload mission-specific messages on machines greater than 48MB + * + * 15 5/19/99 4:07p Dave + * Moved versioning code into a nice isolated common place. Fixed up + * updating code on the pxo screen. Fixed several stub problems. + * + * 14 5/09/99 8:57p Dave + * Final E3 build preparations. + * + * 13 5/05/99 10:06a Dave + * Upped beta version to 0.04 + * + * 12 4/29/99 3:02p Dave + * New beta version. + * + * 11 4/25/99 3:02p Dave + * Build defines for the E3 build. + * + * 10 4/09/99 2:27p Dave + * Upped version # + * + * 9 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 8 4/08/99 2:10a Dave + * Numerous bug fixes for the beta. Added builtin mission info for the + * beta. + * + * 7 3/19/99 9:52a Dave + * Checkin to repair massive source safe crash. Also added support for + * pof-style nebulae, and some new weapons code. + * + * 6 12/03/98 5:22p Dave + * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl + * checksumming. + * + * 5 10/13/98 9:26a Dave + * Began neatening up freespace.h. + * + * 4 10/09/98 1:35p Dave + * Split off registry stuff into seperate file. + * + * 3 10/07/98 6:27p Dave + * Globalized mission and campaign file extensions. Removed Silent Threat + * special code. Moved \cache \players and \multidata into the \data + * directory. + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 79 9/13/98 10:51p Dave + * Put in newfangled icons for mission simulator room. New mdisk.vp + * checksum and file length. + * + * 78 9/11/98 6:59p Dave + * First beta/rev thingie to interplay. + * + * 77 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 76 9/04/98 3:51p Dave + * Put in validated mission updating and application during stats + * updating. + * + * 75 5/23/98 2:41p Mike + * Make Easy the default skill level and prevent old pilot's skill level + * from carrying into new pilot. + * + * 74 5/19/98 1:19p Allender + * new low level reliable socket reading code. Make all missions/campaign + * load/save to data missions folder (i.e. we are rid of the player + * missions folder) + * + * 73 5/13/98 11:34p Mike + * Model caching system. + * + * 72 5/10/98 10:05p Allender + * only show cutscenes which have been seen before. Made Fred able to + * write missions anywhere, defaulting to player misison folder, not data + * mission folder. Fix FreeSpace code to properly read missions from + * correct locations + * + * 71 5/08/98 7:08p Dave + * Lots of UI tweaking. + * + * 70 5/08/98 5:31p Lawrance + * extern cd checking routines + * + * $NoKeywords: $ + */ + +#ifndef _FREESPACE_H +#define _FREESPACE_H +#ifndef STAMPER_PROGRAM // because of all the dependancies, I have to do this...yuck!!! MWA 7/21/97 + +#include "pstypes.h" +#include "systemvars.h" +#include "2d.h" + +// -------------------------------------------------------------------------------------------------------- +// FREESPACE DEFINES/VARS +// + +// filename extensions +#define FS_MISSION_FILE_EXT NOX(".fs2") +#define FS_CAMPAIGN_FILE_EXT NOX(".fc2") + +// CDROM volume names +#ifdef MULTIPLAYER_BETA_BUILD + #define FS_CDROM_VOLUME_1 NOX("FS2_BETA") + #define FS_CDROM_VOLUME_2 NOX("FS2_BETA") +#elif defined(E3_BUILD) + #define FS_CDROM_VOLUME_1 NOX("FS2_E3DEMO") + #define FS_CDROM_VOLUME_2 NOX("FS2_E3DEMO") +#elif defined(OEM_BUILD) + #define FS_CDROM_VOLUME_1 NOX("FS2_OEM") + #define FS_CDROM_VOLUME_2 NOX("FS2_OEM") + #define FS_CDROM_VOLUME_3 NOX("FS2_OEM") +#else + #define FS_CDROM_VOLUME_1 NOX("FREESPACE2_1") + #define FS_CDROM_VOLUME_2 NOX("FREESPACE2_2") + #define FS_CDROM_VOLUME_3 NOX("FREESPACE2_3") + + // old volume names + // #define FS_CDROM_VOLUME_1 NOX("FREESPACE_1") + // #define FS_CDROM_VOLUME_2 NOX("FREESPACE_2") + // #define FS_CDROM_VOLUME_3 NOX("FREESPACE_3") +#endif + +// frametime/missiontime variables +extern fix Frametime; +extern float flFrametime; +extern fix Missiontime; + +// 0 - 4 +extern int Game_skill_level; + +// see GM_* defines in systemvars.h +extern int Game_mode; + +// if this value is set anywhere within the game_do_state_common() function, the normal do_state() will not be called +// for this frame. Useful for getting out of sticky sequencing situations. +extern int Game_do_state_should_skip; + +// time compression +extern fix Game_time_compression; + +// Set if subspace is active this level +extern int Game_subspace_effect; + +// The current mission being played. +extern char Game_current_mission_filename[MAX_FILENAME_LEN]; + +// game's CDROM directory +extern char Game_CDROM_dir[MAX_PATH_LEN]; + +// if the ships.tbl the player has is valid +extern int Game_ships_tbl_valid; + +// if the weapons.tbl the player has is valid +extern int Game_weapons_tbl_valid; + + +// this is a mission actually designed at Volition +#define MAX_BUILTIN_MISSIONS 100 +#define FSB_FROM_VOLITION (1<<0) // we made it in-house +#define FSB_MULTI (1<<1) // is a multiplayer mission +#define FSB_TRAINING (1<<2) // is a training mission +#define FSB_CAMPAIGN (1<<3) // is a campaign mission +#define FSB_CAMPAIGN_FILE (1<<4) // is actually a campaign file + +typedef struct fs_builtin_mission { + char filename[MAX_FILENAME_LEN]; + int flags; // see FSB_* defines above + char cd_volume[MAX_FILENAME_LEN]; // cd volume which this needs +} fs_builtin_mission; + + +// -------------------------------------------------------------------------------------------------------- +// FREESPACE FUNCTIONS +// + +// mission management ------------------------------------------------- + +// loads in the currently selected mission +int game_start_mission(); + +// shutdown a mission +void game_level_close(); + + +// gameplay stuff ----------------------------------------------------- + +// stop the game (mission) timer +void game_stop_time(); + +// start the game (mission) timer +void game_start_time(); + +// call whenever in a loop or if you need to get a keypress +int game_check_key(); + +// poll for keypresses +int game_poll(); + +// function to read keyboard stuff +void game_process_keys(); + +// call this to set frametime properly (once per frame) +void game_set_frametime(int state); + +// Used to halt all looping game sounds +void game_stop_looped_sounds(); + +// do stuff that may need to be done regardless of state +void game_do_state_common(int state,int no_networking = 0); + + +// skill level -------------------------------------------------------- + +// increase the skill level (will wrap around to min skill level) +void game_increase_skill_level(); + +// get the default game skill level +int game_get_default_skill_level(); + +// a keypress. See CPP file for more info. +void game_flush(); + +// running with low-memory (less than 48MB) +bool game_using_low_mem(); + +// misc --------------------------------------------------------------- + +// lookup the specified filename. return an fs_builtin_mission* if found, NULL otherwise +fs_builtin_mission *game_find_builtin_mission(char *filename); + + + +//================================================================ +// GAME FLASH STUFF - code in FreeSpace.cpp + +// Resets the flash +void game_flash_reset(); + +// Adds a flash effect. These can be positive or negative. +// The range will get capped at around -1 to 1, so stick +// with a range like that. +void game_flash( float r, float g, float b ); + +// Adds a flash for Big Ship explosions +// cap range from 0 to 1 +void big_explosion_flash(float flash); + +// Call once a frame to diminish the +// flash effect to 0. +void game_flash_diminish(); + +// Loads the best palette for this level, based +// on nebula color and hud color. You could just call palette_load_table with +// the appropriate filename, but who wants to do that. +void game_load_palette(); + +//================================================================ + +// Call at the beginning of each frame +void game_whack_reset(); + +// Call to apply a whack to a the ship. Used for force feedback +void game_whack_apply( float x, float y ); + +// call to apply a "shudder" +void game_shudder_apply(int time, float intensity); + +//=================================================================== + +// make sure a CD is in the drive before continuing (returns 1 to continue, otherwise 0). +int game_do_cd_check(char *volume_name=NULL); +int game_do_cd_check_specific(char *volume_name, int cdnum); +int find_freespace_cd(char *volume_name=NULL); +int set_cdrom_path(int drive_num); +int game_do_cd_mission_check(char *filename); + +// Used to tell the player that a feature isn't available in the demo version of FreeSpace +void game_feature_not_in_demo_popup(); + +// Return version string for demo or full version, depending on build. +void get_version_string(char *str); + +// format the specified time (fixed point) into a nice string +void game_format_time(fix m_time,char *time_str); + +// if the game is running using hacked data +int game_hacked_data(); + +// show the oem upsell screens (end of campaign, or close of game +void oem_upsell_show_screens(); + +#endif // endif of #ifndef STAMPER_PROGRAM +#endif + diff --git a/include/freespaceresource.h b/include/freespaceresource.h new file mode 100644 index 0000000..432909d --- /dev/null +++ b/include/freespaceresource.h @@ -0,0 +1,94 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by FreeSpace.rc +// +#define IDD_DEBUG_MENU 101 +#define IDD_CONNECT 108 +#define IDD_MULTI 109 +#define IDC_CURSOR1 111 +#define IDC_CURSOR2 112 +#define IDD_PLAYER_DIALOG 114 +#define IDD_GODSTUFF 115 +#define IDD_DEBUG_DIALOG 116 +#define IDB_GOAL_INC 118 +#define IDB_GOAL_COMP 119 +#define IDB_GOAL_ORD 120 +#define IDB_GOAL_NONE 121 +#define IDB_GOAL_FAIL 122 +#define IDI_APP_ICON 124 +#define IDD_GEN 127 +#define IDC_LIST_DEBUG 1000 +#define IDC_DEBUG_TEXT1 1001 +#define IDC_DEBUG_TEXT2 1002 +#define IDC_TEXT 1003 +#define IDC_HOST_IS 1005 +#define IDC_CONPING 1007 +#define IDC_RESET_MULTI 1011 +#define IDC_STANDALONE_STATE 1012 +#define IDC_STATIC_A 1013 +#define IDC_STANDALONE_FPS 1014 +#define IDC_STATIC_A2 1015 +#define IDC_STANDALONE_MTIME 1016 +#define IDC_PLAYER_LIST 1016 +#define IDC_PSHIP_TYPE 1017 +#define IDC_NETPLAYER_NUM 1018 +#define IDC_PHITS 1019 +#define IDC_PBHHITS 1020 +#define IDC_PPCT 1021 +#define IDC_PBHPCT 1022 +#define IDC_SSHOTS 1023 +#define IDC_MPSHOTS 1024 +#define IDC_PLAYER_GOD_LIST 1026 +#define IDC_SECHITS 1028 +#define IDC_SBHHITS 1029 +#define IDC_SPCT 1030 +#define IDC_SBHPCT 1031 +#define IDC_MPHITS 1032 +#define IDC_MPBHHITS 1033 +#define IDC_MPPCT 1034 +#define IDC_MPBHPCT 1035 +#define IDC_MSSHOTS 1036 +#define IDC_MSECHITS 1037 +#define IDC_MSBHHITS 1038 +#define IDC_MSPCT 1039 +#define IDC_MSBHPCT 1040 +#define IDC_PSHOTS 1041 +#define IDC_PING_TIME 1042 +#define IDC_MISSION_NAME 1043 +#define IDC_GODSTUFF_BROADCAST 1044 +#define IDC_GODSTUFF_SENDMESS 1045 +#define IDC_STD_GOALS 1049 +#define IDC_PLAYER_MESSAGES 1051 +#define IDC_ASSISTS 1052 +#define IDC_MASSISTS 1053 +#define IDC_KICK_BUTTON 1054 +#define IDC_STD_NAME 1055 +#define IDC_NG_MAXPLAYERS 1057 +#define IDC_NG_MAXOBSERVERS 1058 +#define IDC_NG_SECURITY 1059 +#define IDC_NG_RESPAWNS 1060 +#define IDC_GOD_CHAT 1061 +#define IDC_STD_HOST_PASSWD 1062 +#define IDC_FIELD1 1064 +#define IDC_FIELD2 1065 +#define IDC_FIELD3 1066 +#define IDC_PXO_REFRESH 1067 +#define IDC_CON_COUNT 2000 +#define IDC_FRAMECAP_STATIC 2001 +#define IDC_GODSTUFF_FPS 2002 +#define IDC_PINFO_PING 2003 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 128 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1068 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif + diff --git a/include/fvi.h b/include/fvi.h new file mode 100644 index 0000000..78adc91 --- /dev/null +++ b/include/fvi.h @@ -0,0 +1,188 @@ +/* + * $Logfile: /Freespace2/code/Math/Fvi.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Prototypes for fvi stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 8/16/99 8:19a Andsager + * Add project_point_onto_bbox() to fvi and include in aicode + * + * 4 3/08/99 7:03p Dave + * First run of new object update system. Looks very promising. + * + * 3 11/13/98 11:10a Andsager + * Add fvi_two_lines_in_3space() - returns closest point of intersection + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 16 1/12/98 9:20p Andsager + * Modify calling procedure to fvi_sphere_plane. + * + * 15 10/19/97 9:42p Andsager + * add fvi_sphere_plane to header + * + * 14 10/19/97 9:28p Andsager + * removed local function from header + * + * 13 10/17/97 1:21a Andsager + * add new function to check sphere-polgon edge collision + * + * 12 10/03/97 5:06p Andsager + * added sphere polygon intersection code + * + * 11 9/28/97 2:16p Andsager + * added fvi_moving_sphere_intersect_plane + * + * 10 4/01/97 1:03p John + * Changed fvi_ray_plane to take a dir, not two points. + * + * 9 3/26/97 10:48a Hoffoss + * JAS: Added fvi_ray_sphere + * + * 8 3/24/97 3:55p John + * renamed fvi functions so rays and segments aren't confusing. + * + * 7 3/24/97 3:26p John + * Cleaned up and restructured model_collide code and fvi code. In fvi + * made code that finds uvs work.. Added bm_get_pixel to BmpMan. + * + * 6 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + + +#ifndef _FVI_H +#define _FVI_H + +#include "pstypes.h" + +// fvi functions - fvi stands for Find Vector Intersection +// fvi_a_b - means find the intersection of something of type a with something of type b +// type can be: +// ray: a line from p0 through p1 extending to inifinity +// segment: a line from p0 stopping at p1 +// sphere, point, face + +//-------------------------------------------------------------------- +// fvi_ray_plane - Finds the point on the specified plane where the +// infinite ray intersects. +// +// Returns scaled-distance plane is from the ray_origin (t), so +// P = O + t*D, where P is the point of intersection, O is the ray origin, +// and D is the ray's direction. So 0.0 would mean the intersection is +// exactly on the ray origin, 1.0 would be on the ray origin plus the ray +// direction vector, anything negative would be behind the ray's origin. +// If you pass a pointer to the new_pnt, this routine will perform the P= +// calculation to calculate the point of intersection and put the result +// in *new_pnt. +// +// If radius is anything other than 0.0, it assumes you want the intersection +// point to be that radius from the plane. +// +// Note that ray_direction doesn't have to be normalized unless you want +// the return value to be in units from the ray origin. +// +// Also note that new_pnt will always be filled in to some valid value, +// even if it is a point at infinity. +// +// If the plane and line are parallel, this will return the largest +// negative float number possible. +// +// So if you want to check a line segment from p0 to p1, you would pass +// p0 as ray_origin, p1-p0 as the ray_direction, and there would be an +// intersection if the return value is between 0 and 1. + +float fvi_ray_plane(vector *new_pnt, + vector *plane_pnt,vector *plane_norm, // Plane description, a point and a normal + vector *ray_origin,vector *ray_direction, // Ray description, a point and a direction + float rad); + + +//find the point on the specified plane where the line segment intersects +//returns true if point found, false if line parallel to plane +//new_pnt is the found point on the plane +//plane_pnt & plane_norm describe the plane +//p0 & p1 are the ends of the line +int fvi_segment_plane(vector *new_pnt, vector *plane_pnt, vector *plane_norm, vector *p0, vector *p1, float rad); + + +// fvi_point_face +// see if a point in inside a face by projecting into 2d. Also +// Finds uv's if uvls is not NULL. Returns 0 if point isn't on +// face, non-zero otherwise. +// From Graphics Gems I, "An efficient Ray-Polygon intersection", p390 +// checkp - The point to check +// nv - how many verts in the poly +// verts - the vertives for the polygon +// norm1 - the polygon's normal +// u_out,vout - if not null and v_out not null and uvls not_null and point is on face, the uv's of where it hit +// uvls - a list of uv pairs for each vertex +// This replaces the old check_point_to_face & find_hitpoint_uv +int fvi_point_face(vector *checkp, int nv, vector **verts, vector * norm1, float *u_out, float *v_out, uv_pair * uvls ); + + +//maybe this routine should just return the distance and let the caller +//decide it it's close enough to hit +//determine if and where a vector intersects with a sphere +//vector defined by p0,p1 +//returns 1 if intersects, and fills in intp +//else returns 0 +int fvi_segment_sphere(vector *intp, vector *p0, vector *p1, vector *sphere_pos, float sphere_rad); + +//determine if and where a ray intersects with a sphere +//vector defined by p0,p1 +//returns 1 if intersects, and fills in intp +//else returns 0 +int fvi_ray_sphere(vector *intp, vector *p0, vector *p1, vector *sphere_pos, float sphere_rad); + + +//============================================================== +// Finds intersection of a ray and an axis-aligned bounding box +// Given a ray with origin at p0, and direction pdir, this function +// returns non-zero if that ray intersects an axis-aligned bounding box +// from min to max. If there was an intersection, then hitpt will contain +// the point where the ray begins inside the box. +// Fast ray-box intersection taken from Graphics Gems I, pages 395,736. +int fvi_ray_boundingbox( vector *min, vector *max, vector * p0, vector *pdir, vector *hitpt ); + +// sphere polygon collision prototypes + +// Given a polygon vertex list and a moving sphere, find the first contact the sphere makes with the edge, if any +int fvi_polyedge_sphereline(vector *hit_point, vector *xs0, vector *vs, float Rs, int nv, vector **verts, float *hit_time); + +int fvi_sphere_plane(vector *intersect_point, vector *sphere_center_start, vector *sphere_velocity, float sphere_radius, + vector *plane_normal, vector *plane_point, float *hit_time, float *delta_time); + +// finds the point of intersection between two lines or the closest points if lines do not intersect +// closest points - line 1: p1 + v1 * s, line 2: p2 + v2 * t +// p1 - point on line 1 +// v1 - vector direction of line 1 +// p2 - point on line 2 +// v2 - vector direction of line 2 +// s - parameter of intersection of line 1 +// t - parameter of intersection of line 2 +void fvi_two_lines_in_3space(vector *p1, vector *v1, vector *p2, vector *v2, float *s, float *t); + +// vector mins - minimum extents of bbox +// vector maxs - maximum extents of bbox +// vector start - point in bbox reference frame +// vector box_pt - point in bbox reference frame. +// NOTE: if a coordinate of start is *inside* the bbox, it is *not* moved to surface of bbox +// return: 1 if inside, 0 otherwise. +int project_point_onto_bbox(vector *mins, vector *maxs, vector *start, vector *box_pt); + +#endif + diff --git a/include/fxdll.h b/include/fxdll.h new file mode 100644 index 0000000..ab218ac --- /dev/null +++ b/include/fxdll.h @@ -0,0 +1,125 @@ +/* +** Copyright (c) 1995, 1996, 3Dfx Interactive, Inc. +** All Rights Reserved. +** +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; +** the contents of this file may not be disclosed to third parties, copied or +** duplicated in any form, in whole or in part, without the prior written +** permission of 3Dfx Interactive, Inc. +** +** RESTRICTED RIGHTS LEGEND: +** Use, duplication or disclosure by the Government is subject to restrictions +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - +** rights reserved under the Copyright Laws of the United States. +** +** $Revision$ +** $Date$ +*/ +/* preprocessor defines for libraries to support DLL creation */ + +/* in header file, use FX_ENTRY return-type FX_CALL function-name ( ... ) */ +/* in source file, use FX_EXPORT return-type FX_CSTYLE function-name (... ) */ + +/* in source file, set FX_DLL_DEFINITION, include this file, then include + header file for library. */ + +/* we need to use two macros per declaration/definition because MSVC + requires __stdcall and __declspec( dllexport ) be in different parts + of the prototype! */ + +/* I use two sets in case we need to control declarations and definitions + differently. If it turns out we don't, it should be easy to do a search + and replace to eliminate one set */ + +/* NOTE: this header file may be included more than once, and FX_DLL_DEFINITION + may have changed, so we do not protect this with an #fndef __FXDLL_H__ + statement like we normally would. */ + + +#ifdef FX_ENTRY +#undef FX_ENTRY +#endif + +#ifdef FX_CALL +#undef FX_CALL +#endif + +#ifdef FX_EXPORT +#undef FX_EXPORT +#endif + +#ifdef FX_CSTYLE +#undef FX_CSTYLE +#endif + +#if defined(FX_DLL_DEFINITION) + #if defined(FX_DLL_ENABLE) + #if defined(__MSC__) + #ifndef KERNEL + #define FX_ENTRY __declspec( dllexport ) + #define FX_EXPORT __declspec( dllexport ) + #else + #define FX_ENTRY + #define FX_EXPORT + #endif /* #ifndef KERNEL */ + #define FX_CALL __stdcall + #define FX_CSTYLE __stdcall + + #elif defined(__WATCOMC__) + #define FX_ENTRY + #define FX_CALL __stdcall __export + + #define FX_EXPORT + #define FX_CSTYLE __stdcall __export + + #else /* compiler */ + #error define FX_ENTRY,FX_CALL & FX_EXPORT,FX_CSTYLE for your compiler + #endif /* compiler */ + + #else /* FX_DLL_ENABLE */ + #define FX_ENTRY + #define FX_CALL __stdcall + + #define FX_EXPORT + #define FX_CSTYLE __stdcall + #endif /* FX_DLL_ENABLE */ + +#else /* FX_DLL_DEFINITION */ + #define FX_ENTRY extern + #define FX_CALL __stdcall +#endif /* FX_DLL_DEFINITION */ + +/* + * We don't want any of this DLL junk for DJGPP or UNIX + * so undo what is done above. + */ +#if defined(__DJGPP__) || defined(__unix__) + #ifdef FX_CALL + #undef FX_CALL + #endif + + #ifdef FX_CSTYLE + #undef FX_CSTYLE + #endif + + #ifdef FX_EXPORT + #undef FX_EXPORT + #endif + + #ifdef FX_ENTRY + #undef FX_ENTRY + #endif + + #define FX_CALL + #define FX_CSTYLE + #define FX_EXPORT + #define FX_ENTRY +#endif + +#if defined (MSVC16) + #undef FX_CALL + #define FX_CALL +#endif + diff --git a/include/fxglob.h b/include/fxglob.h new file mode 100644 index 0000000..3cd4ff6 --- /dev/null +++ b/include/fxglob.h @@ -0,0 +1,31 @@ +/* +** Copyright (c) 1995, 3Dfx Interactive, Inc. +** All Rights Reserved. +** +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; +** the contents of this file may not be disclosed to third parties, copied or +** duplicated in any form, in whole or in part, without the prior written +** permission of 3Dfx Interactive, Inc. +** +** RESTRICTED RIGHTS LEGEND: +** Use, duplication or disclosure by the Government is subject to restrictions +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - +** rights reserved under the Copyright Laws of the United States. +** +** $ Revision: $ +** $ Date: $ +** +*/ + + +#ifndef _FXGLOB_H_ +#define _FXGLOB_H_ + + +void fxGlobify( int *argc, char ***argv ); + + +#endif + diff --git a/include/fxos.h b/include/fxos.h new file mode 100644 index 0000000..727aa64 --- /dev/null +++ b/include/fxos.h @@ -0,0 +1,51 @@ +/* +** Copyright (c) 1995, 3Dfx Interactive, Inc. +** All Rights Reserved. +** +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; +** the contents of this file may not be disclosed to third parties, copied or +** duplicated in any form, in whole or in part, without the prior written +** permission of 3Dfx Interactive, Inc. +** +** RESTRICTED RIGHTS LEGEND: +** Use, duplication or disclosure by the Government is subject to restrictions +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - +** rights reserved under the Copyright Laws of the United States. +** +** $ Revision: $ +** $ Date: $ +** +*/ + + +#ifndef _FXOS_H_ +#define _FXOS_H_ + +#include + +# ifdef __cplusplus +extern "C" { +# endif + +# ifdef WIN32 +void sleep(int secs); +#define gethostname fxGethostname + +int gethostname(char *name, int namelen); + +# endif + +float fxTime(void); +float timer(int flag); + +FILE *fxFopenPath(const char *filename, const char *mode, + const char *path, const char **pprefix); + +# ifdef __cplusplus +} +# endif + +#endif + diff --git a/include/gameplayhelp.h b/include/gameplayhelp.h new file mode 100644 index 0000000..42560f8 --- /dev/null +++ b/include/gameplayhelp.h @@ -0,0 +1,35 @@ +/* + * $Logfile: /Freespace2/code/GameHelp/GameplayHelp.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header for displaying in-game help + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 2 3/09/98 9:54p Lawrance + * integrate new art for gameplay help + * + * 1 3/09/98 5:05p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __GAMEPLAY_HELP_H__ +#define __GAMEPLAY_HELP_H__ + +void gameplay_help_init(); +void gameplay_help_close(); +void gameplay_help_do_frame(float frametime); + +#endif + diff --git a/include/gamesequence.h b/include/gamesequence.h new file mode 100644 index 0000000..ec69558 --- /dev/null +++ b/include/gamesequence.h @@ -0,0 +1,483 @@ +/* + * $Logfile: /Freespace2/code/GameSequence/GameSequence.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for Game Sequencing items + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 8 9/03/99 1:31a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 7 8/27/99 12:04a Dave + * Campaign loop screen. + * + * 6 8/04/99 5:36p Andsager + * Show upsell screens at end of demo campaign before returning to main + * hall. + * + * 5 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * 4 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 3 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 83 5/15/98 12:09a Dave + * New tracker api code. New game tracker code. Finished up first run of + * the PXO screen. Fixed a few game server list exceptions. + * + * 82 5/12/98 2:46a Dave + * Rudimentary communication between Parallax Online and freespace. Can + * get and store channel lists. + * + * 81 4/25/98 7:39p Allender + * fixd some small hotkey stuff. Worked on turret orientation being + * correct for multiplayer. new sexpression called end-campaign will will + * end the main campaign + * + * 80 4/23/98 7:08p John + * Removed some obsoleted states. + * + * 79 4/16/98 4:31p Hoffoss + * Changed demo screen referenced to view cutscenes screen, which is now + * what it's called. + * + * 78 4/02/98 5:40p Hoffoss + * Added the Load Mission screen to FreeSpace. + * + * 77 3/11/98 5:32p Lawrance + * Fix up text arrays for events/states + * + * 76 3/09/98 12:13a Lawrance + * Add support for Red Alert missions + * + * 75 3/07/98 5:44p Dave + * Finished player info popup. Ironed out a few todo bugs. + * + * 74 3/05/98 4:12p John + * Made Debug+F4 switch Glide and windowed. + * + * 73 3/03/98 1:00p Hoffoss + * Added new command briefing event and state. + * + * 72 3/02/98 3:44p Hoffoss + * Added new Campaign Room state and event. + * + * 71 2/19/98 6:26p Dave + * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in + * initial support for player data uploading. + * + * 70 2/18/98 10:21p Dave + * Ripped out old file xfer system. Put in brand new xfer system. + * + * 69 2/08/98 5:07p Dave + * Put in support for multiplayer furball mode. + * + * 68 1/28/98 6:22p Dave + * Made standalone use ~8 megs less memory. Fixed multiplayer submenu + * sequencing bug. + * + * 67 1/23/98 5:43p Dave + * Finished bringing standalone up to speed. Coded in new host options + * screen. + * + * 66 1/22/98 5:25p Dave + * Modified some pregame sequencing packets. Starting to repair broken + * standalone stuff. + * + * 65 1/20/98 5:42p Dave + * Moved ingame join to its own module. Improved it a bit. + * + * 64 1/15/98 6:12p Dave + * Fixed weapons loadout bugs with multiplayer respawning. Added + * multiplayer start screen. Fixed a few chatbox bugs. + * + * 63 1/15/98 6:00p Hoffoss + * Added option to quit menu (in game) to restart the mission. Doesn't + * seem to quite work, though. Checking code in so someone else can look + * into it. + * + * 62 1/05/98 10:05a Dave + * Big re-sequencing of server transfer. Centralized _all_ server transfer + * code to one module. + * + * 61 12/30/97 4:28p Lawrance + * Give text descriptions for events, change debug output to give text + * desciption of event/state + * + * 60 12/24/97 8:56p Lawrance + * took out obsolete state used for non-existant sound config screen + * + * 59 12/17/97 8:44p John + * added code to warp the player out of mission no matter what; no + * cancelling, no max speed check. + * + * 58 11/15/97 2:37p Dave + * More multiplayer campaign support. + * + * 57 11/13/97 7:01p Hoffoss + * Fixed GS_state_text[], which didn't match the current states we have + * available. + * + * 56 11/11/97 4:57p Dave + * Put in support for single vs. multiplayer pilots. Began work on + * multiplayer campaign saving. Put in initial player select screen + * + * 55 11/10/97 6:02p Hoffoss + * Added new debug paused state. + * + * 54 11/03/97 10:12p Hoffoss + * Finished up work on the hud message/mission log scrollback screen. + * + * 53 10/27/97 6:11p Dave + * Changed host/server transfer around. Added some multiplayer data to + * state save/restore. Made multiplayer quitting more intelligent. + * + * 52 10/27/97 8:33a John + * added code for new player warpout sequence + * + * 51 10/24/97 6:19p Dave + * More standalone testing/fixing. Added reliable endgame sequencing. + * Added reliable ingame joining. Added reliable stats transfer (endgame). + * Added support for dropping players in debriefing. Removed a lot of old + * unused code. + * + * 50 10/18/97 7:47p Hoffoss + * Changed state workings for controls config screen. + * + * 49 10/02/97 9:53p Hoffoss + * Added event evaluation analysis debug screen so we can determine the + * state of events and their sexp trees to track down logic problems and + * such. + * + * 48 10/02/97 4:52p Dave + * Added event to move to the multi wait state with a post_event + * + * 47 10/01/97 4:52p Dave + * Got ingame join and observer mode to correctly work under the new + * player_obj system. + * + * 46 9/30/97 5:08p Dave + * Began work on ingame join ship selection screen/state. + * + * 45 9/23/97 11:53p Lawrance + * add state do perform multiplayer on-line help + * + * 44 9/22/97 4:55p Hoffoss + * Added a training message window display thingy. + * + * 43 9/19/97 4:24p Allender + * added team selection state -- initialze player* variable in + * player_level_init + * + * 42 9/18/97 10:17p Lawrance + * add help state for briefing + * + * 41 9/18/97 9:21a Dave + * Added view medals state. Changed pilot scoring struct to reflect. + * + * 40 8/29/97 4:51p Dave + * Added a state and even for multiplayer pausing. + * + * 39 8/15/97 5:15p Dave + * Added a file xfer state. + * + * 38 8/15/97 9:29a Dave + * Removed standalone server briefing wait state. + * + * 37 8/04/97 4:39p Dave + * Added first 3 standalone state handlers + * + * 36 7/30/97 5:24p Dave + * Added in demo system state. + * + * 35 7/24/97 2:17p Dave + * Added show statistics state. + * + * 34 7/23/97 4:51p Dave + * Added join tracker state + * + * 33 7/14/97 12:03a Lawrance + * added navmap state + * + * 32 6/13/97 2:30p Lawrance + * Added debriefings + * + * 31 6/12/97 9:13a Allender + * added sequencing state to the end of ship selection. Changed some + * packet names and host sequencing + * + * 30 6/10/97 9:56p Allender + * get multiplayer mission selection working. Host can select mission and + * have himself and clients load the mission -- no sequencing past this + * point however + * + * 29 6/06/97 10:40a Allender + * added 'type' to mission (single/multi/etc). Added a couple of new game + * states for allowing to choose mission for multiplayer game + * + * 28 5/20/97 10:02a Lawrance + * added view medals screen + * + * 27 4/28/97 2:17p Lawrance + * added help state for hotkey assignment screen + * + * 26 4/25/97 3:41p Lawrance + * added support for hotkey assignment screen + * + * 25 4/23/97 9:54a Lawrance + * made show goals screen a separate state + * + * 24 4/22/97 11:06a Lawrance + * added credits state + * + * 23 4/17/97 9:01p Allender + * start of campaign stuff. Campaigns now stored in external file (no + * filenames in code). Continuing campaign won't work at this point + * + * 22 4/03/97 8:40p Lawrance + * give GS_STATE's for player death correct numbering + * + * 21 4/02/97 6:03p Mike + * Make dying work through event driven code. + * + * 20 3/05/97 5:04p Lawrance + * added new states for different context help + * + * 19 3/03/97 1:21p Allender + * mission log stuff -- display the log during/after game. Enhanced + * structure + * + * 18 3/02/97 4:43p Lawrance + * Added in state for sound/music sound config + * + * 17 2/25/97 11:07a Lawrance + * Added support for weapon loadout state + * + * 16 1/09/97 12:57p Lawrance + * supporting a new state where the player picks to either save or restore + * + * 15 1/09/97 12:41a Lawrance + * added function to pop a state without restoring that state + * + * 14 1/06/97 10:44p Lawrance + * Changes to make save/restore functional + * + * 13 1/01/97 6:44p Lawrance + * added new state for protocol choice in a net game + * + * 12 12/03/96 3:45p Lawrance + * supporting control configuration + * + * 11 12/01/96 3:49a Lawrance + * adding support for various multiplayer states + * + * 10 11/29/96 6:09p Lawrance + * added a state for HUD configuration + * + * 9 11/27/96 3:21p Lawrance + * added state for when player is examining message scroll-back + * + * 8 11/19/96 1:22p Lawrance + * added event to start a briefing + * + * 7 11/18/96 8:12p John + * Changed some briefing and sequencing stuff around. + * + * 6 11/18/96 5:07p John + * Changed sequencing code to call entry,leave functions for each state + * change. Added Shift+Pause debug pause thing. + * + * 5 11/13/96 4:02p Lawrance + * complete over-haul of the menu system and the states associated with + * them + * + * 4 10/23/96 9:08a Allender + * Removed primary and secondary goal complete states -- to be implemented + * later. + * +*/ + + +// defines for game sequencing + +#ifndef __GAMESEQUENCE_H__ +#define __GAMESEQUENCE_H__ + +#include "pstypes.h" + +// defines for game sequencing events +// + +#define GS_EVENT_MAIN_MENU 0 // first event to move to first state +#define GS_EVENT_START_GAME 1 // start a new game (Loads a mission then goes to briefing state) +#define GS_EVENT_ENTER_GAME 2 // switches into game state, probably after mission briefing or ship selection. +#define GS_EVENT_START_GAME_QUICK 3 // start a new game (Loads a mission then goes to directly to game state) +#define GS_EVENT_END_GAME 4 // end the current game (i.e. back to main menu) +#define GS_EVENT_QUIT_GAME 5 // quit the entire game +#define GS_EVENT_PAUSE_GAME 6 // pause the current game +#define GS_EVENT_PREVIOUS_STATE 7 // return to the previous state +#define GS_EVENT_OPTIONS_MENU 8 // go to the options menu +#define GS_EVENT_BARRACKS_MENU 9 // go to the barracks menu +#define GS_EVENT_TRAINING_MENU 10 // go to the training menu +#define GS_EVENT_TECH_MENU 11 // go to the tech room menu +#define GS_EVENT_LOAD_MISSION_MENU 12 // go to the load mission menu +#define GS_EVENT_SHIP_SELECTION 13 // Show ship selection menu +#define GS_EVENT_TOGGLE_FULLSCREEN 14 // toggle fullscreen mode +#define GS_EVENT_START_BRIEFING 15 // go to the briefing for the current mission +#define GS_EVENT_DEBUG_PAUSE_GAME 16 +#define GS_EVENT_HUD_CONFIG 17 // start the HUD configuration screen +#define GS_EVENT_MULTI_JOIN_GAME 18 // start multiplayer join game screen +#define GS_EVENT_CONTROL_CONFIG 19 // get user to choose what type of controller to config +#define GS_EVENT_EVENT_DEBUG 20 // an event debug trace scroll list display screen +#define GS_EVENT_WEAPON_SELECTION 21 // Do weapon loadout +#define GS_EVENT_MISSION_LOG_SCROLLBACK 22 // scrollback screen for message log entries +#define GS_EVENT_GAMEPLAY_HELP 23 // show help for the gameplay +#define GS_EVENT_DEATH_DIED 24 // Player just died +#define GS_EVENT_DEATH_BLEW_UP 25 // Saw ship explode. +#define GS_EVENT_NEW_CAMPAIGN 26 +#define GS_EVENT_CREDITS 27 // Got to the credits +#define GS_EVENT_SHOW_GOALS 28 // Show the goal status screen +#define GS_EVENT_HOTKEY_SCREEN 29 // Show the hotkey assignment screen +#define GS_EVENT_VIEW_MEDALS 30 // Go to the View Medals screen +#define GS_EVENT_MULTI_HOST_SETUP 31 // host setup for multiplayer +#define GS_EVENT_MULTI_CLIENT_SETUP 32 // client setup for multiplayer +#define GS_EVENT_DEBRIEF 33 // go to debriefing +#define GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN 34 // go to the demo management screen +#define GS_EVENT_MULTI_STD_WAIT 35 // standalone wait state +#define GS_EVENT_STANDALONE_MAIN 36 // the main do-nothing state of the standalone +#define GS_EVENT_MULTI_PAUSE 37 // pause your multiplayer game +#define GS_EVENT_TEAM_SELECT 38 // team selection for multiplayer +#define GS_EVENT_TRAINING_PAUSE 39 // pause game while training message is displayed +#define GS_EVENT_INGAME_PRE_JOIN 40 // go to ship selection screen for ingame join +#define GS_EVENT_PLAYER_WARPOUT_START 41 // player hit 'j' to warp out +#define GS_EVENT_PLAYER_WARPOUT_START_FORCED 42 // player is being forced out of mission no matter what +#define GS_EVENT_PLAYER_WARPOUT_STOP 43 // player hit 'esc' or something to cancel warp out +#define GS_EVENT_PLAYER_WARPOUT_DONE_STAGE1 44 // player ship got up to speed +#define GS_EVENT_PLAYER_WARPOUT_DONE_STAGE2 45 // player ship got through the warp effect +#define GS_EVENT_PLAYER_WARPOUT_DONE 46 // warp effect went away +#define GS_EVENT_STANDALONE_POSTGAME 47 // debriefing, etc +#define GS_EVENT_INITIAL_PLAYER_SELECT 48 // initial screen where player selects from multi/single player pilots +#define GS_EVENT_GAME_INIT 49 +#define GS_EVENT_MULTI_MISSION_SYNC 50 // sychronize/transfer/load any mission specific data in multiplayer +#define GS_EVENT_MULTI_START_GAME 51 // immediately before the create game screen for the host to set the game variables +#define GS_EVENT_MULTI_HOST_OPTIONS 52 // options the host can set while in the create game scree +#define GS_EVENT_MULTI_DOGFIGHT_DEBRIEF 53 // multiplayer furball debriefing screen (replaces normal debriefing) +#define GS_EVENT_CAMPAIGN_ROOM 54 +#define GS_EVENT_CMD_BRIEF 55 // switch to command briefing screen +#define GS_EVENT_TOGGLE_GLIDE 56 // GS_EVENT_TOGGLE_GLIDE +#define GS_EVENT_RED_ALERT 57 // go to red alert screen +#define GS_EVENT_SIMULATOR_ROOM 58 +#define GS_EVENT_END_CAMPAIGN 59 // end of the whole thang. +#define GS_EVENT_END_DEMO 60 // end of demo campaign +#define GS_EVENT_LOOP_BRIEF 61 // campaign loop brief +#define GS_EVENT_CAMPAIGN_CHEAT 62 // skip to a mission in a campaign + +// IMPORTANT: When you add a new event, update the initialization for GS_event_text[] +// which is done in GameSequence.cpp +// +extern char *GS_event_text[]; // text description for the GS_EVENT_* #defines above + + +// defines for game sequencing states +// +// IMPORTANT: When you add a new state, update the initialization for GS_state_text[] +// which is done in GameSequence.cpp +#define GS_STATE_MAIN_MENU 1 +#define GS_STATE_GAME_PLAY 2 +#define GS_STATE_GAME_PAUSED 3 +#define GS_STATE_QUIT_GAME 4 +#define GS_STATE_OPTIONS_MENU 5 +#define GS_STATE_BARRACKS_MENU 7 +#define GS_STATE_TECH_MENU 8 +#define GS_STATE_TRAINING_MENU 9 +#define GS_STATE_LOAD_MISSION_MENU 10 +#define GS_STATE_BRIEFING 11 +#define GS_STATE_SHIP_SELECT 12 +#define GS_STATE_DEBUG_PAUSED 13 +#define GS_STATE_HUD_CONFIG 14 +#define GS_STATE_MULTI_JOIN_GAME 15 +#define GS_STATE_CONTROL_CONFIG 16 +#define GS_STATE_WEAPON_SELECT 17 +#define GS_STATE_MISSION_LOG_SCROLLBACK 18 +#define GS_STATE_DEATH_DIED 19 // Player just died +#define GS_STATE_DEATH_BLEW_UP 20 // Saw ship explode. +#define GS_STATE_SIMULATOR_ROOM 21 +#define GS_STATE_CREDITS 22 +#define GS_STATE_SHOW_GOALS 23 +#define GS_STATE_HOTKEY_SCREEN 24 +#define GS_STATE_VIEW_MEDALS 25 // Go to the View Medals screen +#define GS_STATE_MULTI_HOST_SETUP 26 // state where host sets up multiplayer game +#define GS_STATE_MULTI_CLIENT_SETUP 27 // client setup for multiplayer game +#define GS_STATE_DEBRIEF 28 +#define GS_STATE_VIEW_CUTSCENES 29 +#define GS_STATE_MULTI_STD_WAIT 30 +#define GS_STATE_STANDALONE_MAIN 31 +#define GS_STATE_MULTI_PAUSED 32 +#define GS_STATE_TEAM_SELECT 33 +#define GS_STATE_TRAINING_PAUSED 34 // game is paused while training msg is being read. +#define GS_STATE_INGAME_PRE_JOIN 35 // go to ship selection screen for ingame join +#define GS_STATE_EVENT_DEBUG 36 // an event debug trace scroll list display screen +#define GS_STATE_STANDALONE_POSTGAME 37 // debriefing, etc. +#define GS_STATE_INITIAL_PLAYER_SELECT 38 +#define GS_STATE_MULTI_MISSION_SYNC 39 +#define GS_STATE_MULTI_START_GAME 40 +#define GS_STATE_MULTI_HOST_OPTIONS 41 +#define GS_STATE_MULTI_DOGFIGHT_DEBRIEF 42 +#define GS_STATE_CAMPAIGN_ROOM 43 +#define GS_STATE_CMD_BRIEF 44 // command briefing screen +#define GS_STATE_RED_ALERT 45 // red alert screen +#define GS_STATE_END_OF_CAMPAIGN 46 // end of main campaign -- only applicable in single player +#define GS_STATE_GAMEPLAY_HELP 47 +#define GS_STATE_END_DEMO 48 // end of demo campaign (upsell then main menu) +#define GS_STATE_LOOP_BRIEF 49 + + +// IMPORTANT: When you add a new state, update the initialization for GS_state_text[] +// which is done in GameSequence.cpp +// +extern char *GS_state_text[]; // text description for the GS_STATE_* #defines above + + +// function prototypes +// +void gameseq_init(); +int gameseq_process_events( void ); // returns current game state +int gameseq_get_state( int depth = 0 ); +void gameseq_post_event( int event ); +int gameseq_get_event( void ); + +void gameseq_set_state(int new_state, int override = 0); +void gameseq_push_state( int new_state ); +void gameseq_pop_state( void ); +int gameseq_get_pushed_state(); +int gameseq_get_depth(); +void gameseq_pop_and_discard_state(void); + + +// Called by the sequencing code when things happen. +void game_process_event(int current_state, int event); +void game_leave_state(int old_state,int new_state); +void game_enter_state(int old_state,int new_state); +void game_do_state(int current_state); + +#endif /* __GAMESEQUENCE_H__ */ + diff --git a/include/gamesnd.h b/include/gamesnd.h new file mode 100644 index 0000000..56cbb5f --- /dev/null +++ b/include/gamesnd.h @@ -0,0 +1,566 @@ +/* + * $Logfile: /Freespace2/code/Gamesnd/GameSnd.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to keep track of which sound files go where + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 15 9/12/99 8:09p Dave + * Fixed problem where skip-training button would cause mission messages + * not to get paged out for the current mission. + * + * 14 9/09/99 11:40p Dave + * Handle an Assert() in beam code. Added supernova sounds. Play the right + * 2 end movies properly, based upon what the player did in the mission. + * + * 13 8/27/99 11:59a Jefff + * changed some sound names to better reflect new uses + * + * 12 8/26/99 9:45a Dave + * First pass at easter eggs and cheats. + * + * 11 7/19/99 11:47a Jefff + * Added sound hook for countermeasure success + * + * 10 7/02/99 4:31p Dave + * Much more sophisticated lightning support. + * + * 9 7/01/99 11:44a Dave + * Updated object sound system to allow multiple obj sounds per ship. + * Added hit-by-beam sound. Added killed by beam sound. + * + * 8 6/25/99 3:08p Dave + * Multiple flyby sounds. + * + * 7 6/18/99 5:16p Dave + * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD + * dialog to PXO screen. + * + * 6 4/19/99 11:01p Dave + * More sophisticated targeting laser support. Temporary checkin. + * + * 5 2/04/99 6:29p Dave + * First full working rev of FS2 PXO support. Fixed Glide lighting + * problems. + * + * 4 1/29/99 12:47a Dave + * Put in sounds for beam weapon. A bunch of interface screens (tech + * database stuff). + * + * 3 10/26/98 9:42a Dave + * Early flak gun support. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 76 5/18/98 12:59a Lawrance + * Replace shockwave impact sound with a new "whoosh" sound that + * originates from the shockwave center + * + * 75 5/06/98 10:29a Dave + * Put in support for panning sounds. Put in new sound hooks for main hall + * 2 intercom sounds. + * + * 74 4/27/98 3:36p Dave + * + * 73 4/25/98 1:25p Lawrance + * Make function for playing generic error beep + * + * 72 4/19/98 9:33p Lawrance + * Added sound hooks for Shivan flyby sound, subspace ambient effect + * + * 71 3/23/98 4:13p Lawrance + * Add hooks for captial ship specific warp sounds + * + * 70 3/21/98 3:34p Lawrance + * Added highlight icon sound, added static sound for briefing cut + * + * 69 3/17/98 5:55p Lawrance + * Support object-linked sounds for asteroids. + * + * 68 3/17/98 3:53p Lawrance + * First pass at weapon flyby sounds + * + * 67 3/16/98 5:54p Lawrance + * Play cargo scanning sound + * + * 66 3/05/98 10:18p Lawrance + * Play voice cue sound when there is no voice file present + * + * 65 2/19/98 4:33p Lawrance + * add asteroid sound hooks + * + * 64 2/12/98 4:59p Lawrance + * Add new sound hooks for subsystem explosions, and subsystems getting + * destroyed + * + * 63 2/11/98 5:32p Dave + * Put in hooks for 3 random intercom sounds in the main hall. + * + * 62 2/06/98 11:33a Dave + * Made sounds customizable through sounds.tbl + * + * 61 2/04/98 6:08p Lawrance + * Add a light collision sound, overlay a shield collide sound if + * applicable. + * + * 60 1/30/98 11:48a John + * Made debris arcs cast light. Added sound effects for them. + * + * 59 1/11/98 11:14p Lawrance + * Preload sounds that we expect will get played. + * + * 58 1/07/98 11:10a Lawrance + * Add new several new sound hooks. + * + * 57 12/24/97 8:54p Lawrance + * Integrating new popup code + * + * 56 12/09/97 11:31a Lawrance + * add missile launch warnings, re-work proximity beep + * + * 55 12/05/97 2:39p Lawrance + * added some different sounds to main hall, add support for looping + * ambient sounds + * + * 54 12/03/97 4:58p Hoffoss + * + * 53 12/03/97 4:16p Hoffoss + * Changed sound stuff used in interface screens for interface purposes. + * + * 52 12/01/97 5:25p Hoffoss + * Routed interface sound playing through a special function that will + * only allow one instance of the sound to play at a time, avoiding + * over-mixing problems. + * + * 51 11/20/97 5:36p Dave + * Hooked in a bunch of main hall changes (including sound). Made it + * possible to reposition (rewind/ffwd) + * sound buffer pointers. Fixed animation direction change framerate + * problem. + * + * 50 11/11/97 10:25p Lawrance + * add sound hook for when missile threat flashes + * + * 49 11/03/97 11:08p Lawrance + * Add sound for collisions with shields. + * + * 48 11/03/97 2:07p Lawrance + * add ship-to-ship collision sound + * + * 47 10/28/97 4:49p Lawrance + * add sound hook that gets played when player warps out + * + * 46 10/27/97 10:48p Lawrance + * add second explosion sound + * + * 45 10/11/97 6:39p Lawrance + * added sound hooks for static sound + * + * 44 10/10/97 7:45p Lawrance + * add warp fail sound + * + * 43 9/19/97 2:45p Lawrance + * add hook for weapon animation sound + * + * 42 9/12/97 4:02p John + * put in ship warp out effect. + * put in dynamic lighting for warp in/out + * + * 41 9/07/97 10:01p Lawrance + * add some interface hooks + * + * 40 9/05/97 4:59p Lawrance + * added warp sound hook + * + * 39 9/03/97 5:05p Lawrance + * add support for ship flyby sound + * + * 38 8/25/97 12:25a Lawrance + * added sound for when shield energy is transferred between quadrants + * + * 37 8/18/97 5:28p Lawrance + * added some new interface sounds + * + * 36 8/12/97 5:52p Lawrance + * added hook for lock warning from aspect missile + * + * 35 8/11/97 9:48p Lawrance + * add sound hook for swarm missile + * + * 34 8/06/97 10:27a Lawrance + * added hook for shockwave create sound + * + * 33 7/17/97 10:50a Lawrance + * create a Shivan light laser so sound can be assigned + * + * 32 6/24/97 11:47p Lawrance + * add briefing sound fx + * + * 31 6/24/97 3:14p Lawrance + * add briefing sounds + * + * 30 6/13/97 4:44p Lawrance + * added another sound hook in ship selection + * + * 29 6/12/97 5:15p Lawrance + * added hook for ambient sound in briefing/ship select + * + * 28 6/05/97 11:25a Lawrance + * use sound signatures to ensure correct sound is loaded + * + * 27 6/05/97 1:07a Lawrance + * changes to support sound interface + * + * 26 6/04/97 1:18p Lawrance + * added hooks for shield impacts + * + * 25 5/23/97 11:20a Lawrance + * counter measure sounds specified in weapons.tbl + * + * 24 5/22/97 12:04p Lawrance + * added soundhook for cmeasure cycle + * + * 23 5/16/97 11:33a Lawrance + * add countermeasures and rearm sounds + * + * 22 5/14/97 11:24a Lawrance + * break up docking sound effect into an approach/depart and attach/detach + * + * 21 5/14/97 11:09a Lawrance + * add hooks for sounds played when player is hit by lasers or missiles + * + * 20 5/14/97 9:54a Lawrance + * supporting mission-specific briefing music + * + * 19 5/13/97 3:09p Lawrance + * added repair/rearm sound + * + * 18 5/13/97 10:44a Lawrance + * add several new sound effects + * + * 17 5/12/97 11:58a Lawrance + * removed obsolete sound + * + * 16 5/06/97 2:12p Lawrance + * rearrange order of sounds + * + * 15 5/06/97 9:36a Lawrance + * added support for min and max distances for 3d sounds + * + * 14 4/23/97 5:19p Lawrance + * split up misc sounds into: gamewide, ingame, and interface + * + * 13 4/20/97 11:48a Lawrance + * added array of filenames for misc sounds. Will be useful if we want to + * unload then re-load sounds + * + * 12 4/18/97 2:54p Lawrance + * sounds now have a default volume, when playing, pass a scaling factor + * not the actual volume + * + * 11 4/15/97 2:49p Lawrance + * add new sounds + * + * 10 3/26/97 11:27a Lawrance + * added hooks for sounds to be played when trying to fire missles when + * none left / trying to fire lasers with no energy + * + * 9 3/19/97 5:53p Lawrance + * integrating new Misc_sounds[] array (replaces old Game_sounds + * structure) + * + * 8 3/17/97 3:47p Mike + * Homing missile lock sound. + * More on AI ships firing missiles. + * + * 7 3/10/97 8:54a Lawrance + * added gamesnd_init_looping_sounds() + * + * 6 2/28/97 8:41a Lawrance + * added afterburner engage and burn sounds + * + * 5 2/14/97 12:37a Lawrance + * added hooks to play docking/undocking sounds + * + * 4 2/13/97 12:03p Lawrance + * hooked in throttle sounds + * + * 3 2/05/97 10:35a Lawrance + * supporting spooled music at menus, briefings, credits etc. + * + * 2 1/20/97 7:58p John + * Fixed some link errors with testcode. + * + * 1 1/20/97 7:08p John + * + * $NoKeywords: $ + */ + +#ifndef __GAMESND_H__ +#define __GAMESND_H__ + +#include "sound.h" +#include "missionparse.h" + +void gamesnd_parse_soundstbl(); // Loads in general game sounds from sounds.tbl +void gamesnd_init_sounds(); // initializes the Snds[] and Snds_iface[] array +void gamesnd_load_gameplay_sounds(); +void gamesnd_unload_gameplay_sounds(); +void gamesnd_load_interface_sounds(); +void gamesnd_unload_interface_sounds(); +void gamesnd_preload_common_sounds(); +void gamesnd_play_iface(int n); + +void gamesnd_play_error_beep(); + +void common_play_highlight_sound(); // called from interface code + +// Misc_sounds[] holds handles for misc sounds in the game (list appears in sounds.tbl) +#define MAX_GAME_SOUNDS 200 +#define MAX_INTERFACE_SOUNDS 70 +#define MAX_SPECIES_FLYBY_SOUNDS 4 // 4 different possible flybys for species + +extern game_snd Snds[MAX_GAME_SOUNDS]; +extern game_snd Snds_iface[MAX_INTERFACE_SOUNDS]; + +// flyby sounds - 2 for each species (fighter and bomber flybys) +extern game_snd Snds_flyby[MAX_SPECIES_NAMES][2]; + + +// symbolic names for misc. game sounds. The order here must match the order in +// sounds.tbl +// +// INSTRUCTIONS FOR ADDING A NEW SOUND: +// +// Add interface (ie non-gameplay) sounds to the end of the interface list of sounds. +// Add gameplay sounds to the correct portion of the gameplay sounds section (ie Misc, +// Weapons, or Ship). +// +// Then add a symbolic name to the appropriate position in the #define list below +// and add an entry to sounds.tbl. If there is no .wav file for the sound yet, +// specify sound_hook.wav in sounds.tbl. +// +// + +//--------------------------------------------------- +// Misc Sounds +//--------------------------------------------------- +#define SND_MISSILE_TRACKING 0 +#define SND_MISSILE_LOCK 1 +#define SND_PRIMARY_CYCLE 2 +#define SND_SECONDARY_CYCLE 3 +#define SND_ENGINE 4 +#define SND_CARGO_REVEAL 5 +#define SND_DEATH_ROLL 6 +#define SND_SHIP_EXPLODE_1 7 +#define SND_TARGET_ACQUIRE 8 +#define SND_ENERGY_ADJUST 9 +#define SND_ENERGY_ADJUST_FAIL 10 +#define SND_ENERGY_TRANS 11 +#define SND_ENERGY_TRANS_FAIL 12 +#define SND_FULL_THROTTLE 13 +#define SND_ZERO_THROTTLE 14 +#define SND_THROTTLE_UP 15 +#define SND_THROTTLE_DOWN 16 +#define SND_DOCK_APPROACH 17 +#define SND_DOCK_ATTACH 18 +#define SND_DOCK_DETACH 19 +#define SND_DOCK_DEPART 20 +#define SND_ABURN_ENGAGE 21 +#define SND_ABURN_LOOP 22 +#define SND_VAPORIZED 23 +#define SND_ABURN_FAIL 24 +#define SND_HEATLOCK_WARN 25 +#define SND_OUT_OF_MISSLES 26 +#define SND_OUT_OF_WEAPON_ENERGY 27 +#define SND_TARGET_FAIL 28 +#define SND_SQUADMSGING_ON 29 +#define SND_SQUADMSGING_OFF 30 +#define SND_DEBRIS 31 +#define SND_SUBSYS_DIE_1 32 +#define SND_MISSILE_START_LOAD 33 +#define SND_MISSILE_LOAD 34 +#define SND_SHIP_REPAIR 35 +#define SND_PLAYER_HIT_LASER 36 +#define SND_PLAYER_HIT_MISSILE 37 +#define SND_CMEASURE_CYCLE 38 +#define SND_SHIELD_HIT 39 +#define SND_SHIELD_HIT_YOU 40 +#define SND_GAME_MOUSE_CLICK 41 +#define SND_ASPECTLOCK_WARN 42 +#define SND_SHIELD_XFER_OK 43 +#define SND_ENGINE_WASH 44 +#define SND_WARP_IN 45 +#define SND_WARP_OUT 46 // Same as warp in for now +#define SND_PLAYER_WARP_FAIL 47 +#define SND_STATIC 48 +#define SND_SHIP_EXPLODE_2 49 +#define SND_PLAYER_WARP_OUT 50 +#define SND_SHIP_SHIP_HEAVY 51 +#define SND_SHIP_SHIP_LIGHT 52 +#define SND_SHIP_SHIP_SHIELD 53 +#define SND_THREAT_FLASH 54 +#define SND_PROXIMITY_WARNING 55 +#define SND_PROXIMITY_ASPECT_WARNING 56 +#define SND_DIRECTIVE_COMPLETE 57 +#define SND_SUBSYS_EXPLODE 58 +#define SND_CAPSHIP_EXPLODE 59 +#define SND_CAPSHIP_SUBSYS_EXPLODE 60 +#define SND_LARGESHIP_WARPOUT 61 +#define SND_ASTEROID_EXPLODE_BIG 62 +#define SND_ASTEROID_EXPLODE_SMALL 63 +#define SND_CUE_VOICE 64 +#define SND_END_VOICE 65 +#define SND_CARGO_SCAN 66 +#define SND_WEAPON_FLYBY 67 +#define SND_ASTEROID 68 +#define SND_CAPITAL_WARP_IN 69 +#define SND_CAPITAL_WARP_OUT 70 +#define SND_ENGINE_LOOP_LARGE 71 +#define SND_SUBSPACE_LEFT_CHANNEL 72 +#define SND_SUBSPACE_RIGHT_CHANNEL 73 +#define SND_MISSILE_EVADED_POPUP 74 +#define SND_ENGINE_LOOP_HUGE 75 + +// Weapon sounds +#define SND_LIGHT_LASER_FIRE 76 +#define SND_LIGHT_LASER_IMPACT 77 +#define SND_HVY_LASER_FIRE 78 +#define SND_HVY_LASER_IMPACT 79 +#define SND_MASSDRV_FIRED 80 +#define SND_MASSDRV_IMPACT 81 +#define SND_FLAIL_FIRED 82 +#define SND_FLAIL_IMPACT 83 +#define SND_NEUTRON_FLUX_FIRED 84 +#define SND_NEUTRON_FLUX_IMPACT 85 +#define SND_DEBUG_LASER_FIRED 86 +#define SND_ROCKEYE_FIRED 87 +#define SND_MISSILE_IMPACT1 88 +#define SND_MAG_MISSILE_LAUNCH 89 +#define SND_FURY_MISSILE_LAUNCH 90 +#define SND_SHRIKE_MISSILE_LAUNCH 91 +#define SND_ANGEL_MISSILE_LAUNCH 92 +#define SND_CLUSTER_MISSILE_LAUNCH 93 +#define SND_CLUSTERB_MISSILE_LAUNCH 94 +#define SND_STILETTO_MISSILE_LAUNCH 95 +#define SND_TSUNAMI_MISSILE_LAUNCH 96 +#define SND_HARBINGER_MISSILE_LAUNCH 97 +#define SND_MEGAWOKKA_MISSILE_LAUNCH 98 +#define SND_CMEASURE1_LAUNCH 99 +#define SND_SHIVAN_LIGHT_LASER_FIRE 100 +#define SND_SHOCKWAVE_EXPLODE 101 +#define SND_SWARM_MISSILE_LAUNCH 102 +#define SND_SHOCKWAVE_IMPACT 109 + +#define SND_TARG_LASER_LOOP 115 +#define SND_FLAK_FIRE 116 +#define SND_SHIELD_BREAKER 117 +#define SND_EMP_MISSILE 118 +#define SND_AUTOCANNON_LOOP 119 +#define SND_AUTOCANNON_SHOT 120 +#define SND_BEAM_LOOP 121 +#define SND_BEAM_UP 122 +#define SND_BEAM_DOWN 123 +#define SND_BEAM_SHOT 124 +#define SND_BEAM_VAPORIZE 125 + +// Ship engine sounds +#define SND_TERRAN_FIGHTER_ENG 126 +#define SND_TERRAN_BOMBER_ENG 127 +#define SND_TERRAN_CAPITAL_ENG 128 +#define SND_SPECIESB_FIGHTER_ENG 129 +#define SND_SPECIESB_BOMBER_ENG 130 +#define SND_SPECIESB_CAPITAL_ENG 131 +#define SND_SHIVAN_FIGHTER_ENG 132 +#define SND_SHIVAN_BOMBER_ENG 133 +#define SND_SHIVAN_CAPITAL_ENG 134 +#define SND_REPAIR_SHIP_ENG 135 + +// Debris electric arcing sounds +#define SND_DEBRIS_ARC_01 139 // 0.10 second spark sound effect (3d sound) +#define SND_DEBRIS_ARC_02 140 // 0.25 second spark sound effect (3d sound) +#define SND_DEBRIS_ARC_03 141 // 0.50 second spark sound effect (3d sound) +#define SND_DEBRIS_ARC_04 142 // 0.75 second spark sound effect (3d sound) +#define SND_DEBRIS_ARC_05 143 // 1.00 second spark sound effect (3d sound) + +// copilot +#define SND_COPILOT 162 + +// supernova 1 and supernova 2 +#define SND_SUPERNOVA_1 173 +#define SND_SUPERNOVA_2 174 + +// lightning sounds +#define SND_LIGHTNING_1 180 +#define SND_LIGHTNING_2 181 + + +//--------------------------------------------------- +// Interface sounds +//--------------------------------------------------- +#define SND_IFACE_MOUSE_CLICK 0 +#define SND_ICON_PICKUP 1 +#define SND_ICON_DROP_ON_WING 2 +#define SND_ICON_DROP 3 +#define SND_SCREEN_MODE_PRESSED 4 +#define SND_SWITCH_SCREENS 5 +#define SND_HELP_PRESSED 6 +#define SND_COMMIT_PRESSED 7 +#define SND_PREV_NEXT_PRESSED 8 +#define SND_SCROLL 9 +#define SND_GENERAL_FAIL 10 +#define SND_SHIP_ICON_CHANGE 11 +#define SND_MAIN_HALL_AMBIENT 12 +#define SND_BTN_SLIDE 13 +#define SND_BRIEF_STAGE_CHG 14 +#define SND_BRIEF_STAGE_CHG_FAIL 15 +#define SND_BRIEF_ICON_SELECT 16 +#define SND_USER_OVER 17 +#define SND_USER_SELECT 18 +#define SND_RESET_PRESSED 19 +#define SND_BRIEF_TEXT_WIPE 20 +#define SND_VASUDAN_PA_1 21 // vasudan pa 1 +#define SND_WEAPON_ANIM_START 22 +#define SND_MAIN_HALL_DOOR_OPEN 23 +#define SND_MAIN_HALL_DOOR_CLOSE 24 +#define SND_GLOW_OPEN 25 +#define SND_VASUDAN_PA_2 26 // vasudan pa 2 +#define SND_AMBIENT_MENU 27 +#define SND_POPUP_APPEAR 28 +#define SND_POPUP_DISAPPEAR 29 +#define SND_VOICE_SLIDER_CLIP 30 +#define SND_VASUDAN_PA_3 31 // vasudan pa 3 +#define SND_MAIN_HALL_GET_PEPSI 32 +#define SND_MAIN_HALL_LIFT_UP 33 +#define SND_MAIN_HALL_WELD1 34 +#define SND_MAIN_HALL_WELD2 35 +#define SND_MAIN_HALL_WELD3 36 +#define SND_MAIN_HALL_WELD4 37 +#define SND_MAIN_HALL_INT1 38 // random intercom message 1 +#define SND_MAIN_HALL_INT2 39 // random intercom message 2 +#define SND_MAIN_HALL_INT3 40 // random intercom message 3 +#define SND_ICON_HIGHLIGHT 41 +#define SND_BRIEFING_STATIC 42 +#define SND_MAIN_HALL2_CRANE1_1 43 +#define SND_MAIN_HALL2_CRANE1_2 44 +#define SND_MAIN_HALL2_CRANE2_1 45 +#define SND_MAIN_HALL2_CRANE2_2 46 +#define SND_MAIN_HALL2_CAR1 47 +#define SND_MAIN_HALL2_CAR2 48 +#define SND_MAIN_HALL2_INT1 49 +#define SND_MAIN_HALL2_INT2 50 +#define SND_MAIN_HALL2_INT3 51 + +#define SND_VASUDAN_BUP 61 + +#endif /* __GAMESND_H__ */ + diff --git a/include/glide.h b/include/glide.h new file mode 100644 index 0000000..70a3b05 --- /dev/null +++ b/include/glide.h @@ -0,0 +1,1377 @@ +/* +** Copyright (c) 1995, 3Dfx Interactive, Inc. +** All Rights Reserved. +** +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; +** the contents of this file may not be disclosed to third parties, copied or +** duplicated in any form, in whole or in part, without the prior written +** permission of 3Dfx Interactive, Inc. +** +** RESTRICTED RIGHTS LEGEND: +** Use, duplication or disclosure by the Government is subject to restrictions +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - +** rights reserved under the Copyright Laws of the United States. +*/ + +/* +** GLIDE.H +** +** The following #defines are relevant when using Glide: +** +** One of the following "platform constants" must be defined during +** compilation: +** +** __DOS__ Defined for 32-bit DOS applications +** __WIN32__ Defined for 32-bit Windows applications +** __sparc__ Defined for Sun Solaris/SunOS +** __linux__ Defined for Linux applications +** __IRIX__ Defined for SGI Irix applications +** +*/ +#ifndef __GLIDE_H__ +#define __GLIDE_H__ + +#define __MSC__ +#define FX_GLIDE_NO_FUNC_PROTO + +#include "3dfx.h" +#include "sst1vid.h" +#include "glidesys.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** ----------------------------------------------------------------------- +** TYPE DEFINITIONS +** ----------------------------------------------------------------------- +*/ +typedef FxU32 GrColor_t; +typedef FxU8 GrAlpha_t; +typedef FxU32 GrMipMapId_t; +typedef FxU8 GrFog_t; + +/* +** ----------------------------------------------------------------------- +** CONSTANTS AND TYPES +** ----------------------------------------------------------------------- +*/ +#define MAX_NUM_SST 4 +#define MAX_MIPMAPS_PER_SST 1024 +#define GR_FOG_TABLE_SIZE 64 +#define GR_NULL_MIPMAP_HANDLE ((GrMipMapId_t) -1) +#define GR_ZDEPTHVALUE_NEAREST 0xFFFF +#define GR_ZDEPTHVALUE_FARTHEST 0x0000 +#define GR_WDEPTHVALUE_NEAREST 0x0000 +#define GR_WDEPTHVALUE_FARTHEST 0xFFFF + +#define GR_MIPMAPLEVELMASK_EVEN FXBIT(0) +#define GR_MIPMAPLEVELMASK_ODD FXBIT(1) +#define GR_MIPMAPLEVELMASK_BOTH (GR_MIPMAPLEVELMASK_EVEN | GR_MIPMAPLEVELMASK_ODD ) + +#define GR_LODBIAS_BILINEAR 0.5 +#define GR_LODBIAS_TRILINEAR 0.0 + +typedef FxI32 GrChipID_t; +#define GR_TMU0 0x0 +#define GR_TMU1 0x1 +#define GR_TMU2 0x2 +#define GR_FBI 0x3 + +typedef FxI32 GrCombineFunction_t; +#define GR_COMBINE_FUNCTION_ZERO 0x0 +#define GR_COMBINE_FUNCTION_NONE GR_COMBINE_FUNCTION_ZERO +#define GR_COMBINE_FUNCTION_LOCAL 0x1 +#define GR_COMBINE_FUNCTION_LOCAL_ALPHA 0x2 +#define GR_COMBINE_FUNCTION_SCALE_OTHER 0x3 +#define GR_COMBINE_FUNCTION_BLEND_OTHER GR_COMBINE_FUNCTION_SCALE_OTHER +#define GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL 0x4 +#define GR_COMBINE_FUNCTION_SCALE_OTHER_ADD_LOCAL_ALPHA 0x5 +#define GR_COMBINE_FUNCTION_SCALE_OTHER_MINUS_LOCAL 0x6 +#define GR_COMBINE_FUNCTION_SCALE_OTHER_MINUS_LOCAL_ADD_LOCAL 0x7 +#define GR_COMBINE_FUNCTION_BLEND GR_COMBINE_FUNCTION_SCALE_OTHER_MINUS_LOCAL_ADD_LOCAL +#define GR_COMBINE_FUNCTION_SCALE_OTHER_MINUS_LOCAL_ADD_LOCAL_ALPHA 0x8 +#define GR_COMBINE_FUNCTION_SCALE_MINUS_LOCAL_ADD_LOCAL 0x9 +#define GR_COMBINE_FUNCTION_BLEND_LOCAL GR_COMBINE_FUNCTION_SCALE_MINUS_LOCAL_ADD_LOCAL +#define GR_COMBINE_FUNCTION_SCALE_MINUS_LOCAL_ADD_LOCAL_ALPHA 0x10 + +typedef FxI32 GrCombineFactor_t; +#define GR_COMBINE_FACTOR_ZERO 0x0 +#define GR_COMBINE_FACTOR_NONE GR_COMBINE_FACTOR_ZERO +#define GR_COMBINE_FACTOR_LOCAL 0x1 +#define GR_COMBINE_FACTOR_OTHER_ALPHA 0x2 +#define GR_COMBINE_FACTOR_LOCAL_ALPHA 0x3 +#define GR_COMBINE_FACTOR_TEXTURE_ALPHA 0x4 +#define GR_COMBINE_FACTOR_DETAIL_FACTOR GR_COMBINE_FACTOR_TEXTURE_ALPHA +#define GR_COMBINE_FACTOR_LOD_FRACTION 0x5 +#define GR_COMBINE_FACTOR_ONE 0x8 +#define GR_COMBINE_FACTOR_ONE_MINUS_LOCAL 0x9 +#define GR_COMBINE_FACTOR_ONE_MINUS_OTHER_ALPHA 0xa +#define GR_COMBINE_FACTOR_ONE_MINUS_LOCAL_ALPHA 0xb +#define GR_COMBINE_FACTOR_ONE_MINUS_TEXTURE_ALPHA 0xc +#define GR_COMBINE_FACTOR_ONE_MINUS_DETAIL_FACTOR GR_COMBINE_FACTOR_ONE_MINUS_TEXTURE_ALPHA +#define GR_COMBINE_FACTOR_ONE_MINUS_LOD_FRACTION 0xd + + +typedef FxI32 GrCombineLocal_t; +#define GR_COMBINE_LOCAL_ITERATED 0x0 +#define GR_COMBINE_LOCAL_CONSTANT 0x1 +#define GR_COMBINE_LOCAL_NONE GR_COMBINE_LOCAL_CONSTANT +#define GR_COMBINE_LOCAL_DEPTH 0x2 + +typedef FxI32 GrCombineOther_t; +#define GR_COMBINE_OTHER_ITERATED 0x0 +#define GR_COMBINE_OTHER_TEXTURE 0x1 +#define GR_COMBINE_OTHER_CONSTANT 0x2 +#define GR_COMBINE_OTHER_NONE GR_COMBINE_OTHER_CONSTANT + + +typedef FxI32 GrAlphaSource_t; +#define GR_ALPHASOURCE_CC_ALPHA 0x0 +#define GR_ALPHASOURCE_ITERATED_ALPHA 0x1 +#define GR_ALPHASOURCE_TEXTURE_ALPHA 0x2 +#define GR_ALPHASOURCE_TEXTURE_ALPHA_TIMES_ITERATED_ALPHA 0x3 + + +typedef FxI32 GrColorCombineFnc_t; +#define GR_COLORCOMBINE_ZERO 0x0 +#define GR_COLORCOMBINE_CCRGB 0x1 +#define GR_COLORCOMBINE_ITRGB 0x2 +#define GR_COLORCOMBINE_ITRGB_DELTA0 0x3 +#define GR_COLORCOMBINE_DECAL_TEXTURE 0x4 +#define GR_COLORCOMBINE_TEXTURE_TIMES_CCRGB 0x5 +#define GR_COLORCOMBINE_TEXTURE_TIMES_ITRGB 0x6 +#define GR_COLORCOMBINE_TEXTURE_TIMES_ITRGB_DELTA0 0x7 +#define GR_COLORCOMBINE_TEXTURE_TIMES_ITRGB_ADD_ALPHA 0x8 +#define GR_COLORCOMBINE_TEXTURE_TIMES_ALPHA 0x9 +#define GR_COLORCOMBINE_TEXTURE_TIMES_ALPHA_ADD_ITRGB 0xa +#define GR_COLORCOMBINE_TEXTURE_ADD_ITRGB 0xb +#define GR_COLORCOMBINE_TEXTURE_SUB_ITRGB 0xc +#define GR_COLORCOMBINE_CCRGB_BLEND_ITRGB_ON_TEXALPHA 0xd +#define GR_COLORCOMBINE_DIFF_SPEC_A 0xe +#define GR_COLORCOMBINE_DIFF_SPEC_B 0xf +#define GR_COLORCOMBINE_ONE 0x10 + +typedef FxI32 GrAlphaBlendFnc_t; +#define GR_BLEND_ZERO 0x0 +#define GR_BLEND_SRC_ALPHA 0x1 +#define GR_BLEND_SRC_COLOR 0x2 +#define GR_BLEND_DST_COLOR GR_BLEND_SRC_COLOR +#define GR_BLEND_DST_ALPHA 0x3 +#define GR_BLEND_ONE 0x4 +#define GR_BLEND_ONE_MINUS_SRC_ALPHA 0x5 +#define GR_BLEND_ONE_MINUS_SRC_COLOR 0x6 +#define GR_BLEND_ONE_MINUS_DST_COLOR GR_BLEND_ONE_MINUS_SRC_COLOR +#define GR_BLEND_ONE_MINUS_DST_ALPHA 0x7 +#define GR_BLEND_RESERVED_8 0x8 +#define GR_BLEND_RESERVED_9 0x9 +#define GR_BLEND_RESERVED_A 0xa +#define GR_BLEND_RESERVED_B 0xb +#define GR_BLEND_RESERVED_C 0xc +#define GR_BLEND_RESERVED_D 0xd +#define GR_BLEND_RESERVED_E 0xe +#define GR_BLEND_ALPHA_SATURATE 0xf +#define GR_BLEND_PREFOG_COLOR GR_BLEND_ALPHA_SATURATE + +typedef FxI32 GrAspectRatio_t; +#define GR_ASPECT_8x1 0x0 /* 8W x 1H */ +#define GR_ASPECT_4x1 0x1 /* 4W x 1H */ +#define GR_ASPECT_2x1 0x2 /* 2W x 1H */ +#define GR_ASPECT_1x1 0x3 /* 1W x 1H */ +#define GR_ASPECT_1x2 0x4 /* 1W x 2H */ +#define GR_ASPECT_1x4 0x5 /* 1W x 4H */ +#define GR_ASPECT_1x8 0x6 /* 1W x 8H */ + +typedef FxI32 GrBuffer_t; +#define GR_BUFFER_FRONTBUFFER 0x0 +#define GR_BUFFER_BACKBUFFER 0x1 +#define GR_BUFFER_AUXBUFFER 0x2 +#define GR_BUFFER_DEPTHBUFFER 0x3 +#define GR_BUFFER_ALPHABUFFER 0x4 +#define GR_BUFFER_TRIPLEBUFFER 0x5 + +typedef FxI32 GrChromakeyMode_t; +#define GR_CHROMAKEY_DISABLE 0x0 +#define GR_CHROMAKEY_ENABLE 0x1 + +typedef FxI32 GrCmpFnc_t; +#define GR_CMP_NEVER 0x0 +#define GR_CMP_LESS 0x1 +#define GR_CMP_EQUAL 0x2 +#define GR_CMP_LEQUAL 0x3 +#define GR_CMP_GREATER 0x4 +#define GR_CMP_NOTEQUAL 0x5 +#define GR_CMP_GEQUAL 0x6 +#define GR_CMP_ALWAYS 0x7 + +typedef FxI32 GrColorFormat_t; +#define GR_COLORFORMAT_ARGB 0x0 +#define GR_COLORFORMAT_ABGR 0x1 + +#define GR_COLORFORMAT_RGBA 0x2 +#define GR_COLORFORMAT_BGRA 0x3 + +typedef FxI32 GrCullMode_t; +#define GR_CULL_DISABLE 0x0 +#define GR_CULL_NEGATIVE 0x1 +#define GR_CULL_POSITIVE 0x2 + +typedef FxI32 GrDepthBufferMode_t; +#define GR_DEPTHBUFFER_DISABLE 0x0 +#define GR_DEPTHBUFFER_ZBUFFER 0x1 +#define GR_DEPTHBUFFER_WBUFFER 0x2 +#define GR_DEPTHBUFFER_ZBUFFER_COMPARE_TO_BIAS 0x3 +#define GR_DEPTHBUFFER_WBUFFER_COMPARE_TO_BIAS 0x4 + +typedef FxI32 GrDitherMode_t; +#define GR_DITHER_DISABLE 0x0 +#define GR_DITHER_2x2 0x1 +#define GR_DITHER_4x4 0x2 + +typedef FxI32 GrFogMode_t; +#define GR_FOG_DISABLE 0x0 +#define GR_FOG_WITH_ITERATED_ALPHA 0x1 +#define GR_FOG_WITH_TABLE 0x2 +#define GR_FOG_WITH_ITERATED_Z 0x3 /* Bug 735 */ +#define GR_FOG_MULT2 0x100 +#define GR_FOG_ADD2 0x200 + +typedef FxU32 GrLock_t; +#define GR_LFB_READ_ONLY 0x00 +#define GR_LFB_WRITE_ONLY 0x01 +#define GR_LFB_IDLE 0x00 +#define GR_LFB_NOIDLE 0x10 + +typedef FxI32 GrLfbBypassMode_t; +#define GR_LFBBYPASS_DISABLE 0x0 +#define GR_LFBBYPASS_ENABLE 0x1 + +typedef FxI32 GrLfbWriteMode_t; +#define GR_LFBWRITEMODE_565 0x0 /* RGB:RGB */ +#define GR_LFBWRITEMODE_555 0x1 /* RGB:RGB */ +#define GR_LFBWRITEMODE_1555 0x2 /* ARGB:ARGB */ +#define GR_LFBWRITEMODE_RESERVED1 0x3 +#define GR_LFBWRITEMODE_888 0x4 /* RGB */ +#define GR_LFBWRITEMODE_8888 0x5 /* ARGB */ +#define GR_LFBWRITEMODE_RESERVED2 0x6 +#define GR_LFBWRITEMODE_RESERVED3 0x7 +#define GR_LFBWRITEMODE_RESERVED4 0x8 +#define GR_LFBWRITEMODE_RESERVED5 0x9 +#define GR_LFBWRITEMODE_RESERVED6 0xa +#define GR_LFBWRITEMODE_RESERVED7 0xb +#define GR_LFBWRITEMODE_565_DEPTH 0xc /* RGB:DEPTH */ +#define GR_LFBWRITEMODE_555_DEPTH 0xd /* RGB:DEPTH */ +#define GR_LFBWRITEMODE_1555_DEPTH 0xe /* ARGB:DEPTH */ +#define GR_LFBWRITEMODE_ZA16 0xf /* DEPTH:DEPTH */ +#define GR_LFBWRITEMODE_ANY 0xFF + + +typedef FxI32 GrOriginLocation_t; +#define GR_ORIGIN_UPPER_LEFT 0x0 +#define GR_ORIGIN_LOWER_LEFT 0x1 +#define GR_ORIGIN_ANY 0xFF + +typedef struct { + int size; + void *lfbPtr; + FxU32 strideInBytes; + GrLfbWriteMode_t writeMode; + GrOriginLocation_t origin; +} GrLfbInfo_t; + +typedef FxI32 GrLOD_t; +#define GR_LOD_256 0x0 +#define GR_LOD_128 0x1 +#define GR_LOD_64 0x2 +#define GR_LOD_32 0x3 +#define GR_LOD_16 0x4 +#define GR_LOD_8 0x5 +#define GR_LOD_4 0x6 +#define GR_LOD_2 0x7 +#define GR_LOD_1 0x8 + +typedef FxI32 GrMipMapMode_t; +#define GR_MIPMAP_DISABLE 0x0 /* no mip mapping */ +#define GR_MIPMAP_NEAREST 0x1 /* use nearest mipmap */ +#define GR_MIPMAP_NEAREST_DITHER 0x2 /* GR_MIPMAP_NEAREST + LOD dith */ + + +typedef FxI32 GrSmoothingMode_t; +#define GR_SMOOTHING_DISABLE 0x0 +#define GR_SMOOTHING_ENABLE 0x1 + +typedef FxI32 GrTextureClampMode_t; +#define GR_TEXTURECLAMP_WRAP 0x0 +#define GR_TEXTURECLAMP_CLAMP 0x1 + +typedef FxI32 GrTextureCombineFnc_t; +#define GR_TEXTURECOMBINE_ZERO 0x0 /* texout = 0 */ +#define GR_TEXTURECOMBINE_DECAL 0x1 /* texout = texthis */ +#define GR_TEXTURECOMBINE_OTHER 0x2 /* this TMU in passthru mode */ +#define GR_TEXTURECOMBINE_ADD 0x3 /* tout = tthis + t(this+1) */ +#define GR_TEXTURECOMBINE_MULTIPLY 0x4 /* texout = tthis * t(this+1) */ +#define GR_TEXTURECOMBINE_SUBTRACT 0x5 /* Sutract from upstream TMU */ +#define GR_TEXTURECOMBINE_DETAIL 0x6 /* detail--detail on tthis */ +#define GR_TEXTURECOMBINE_DETAIL_OTHER 0x7 /* detail--detail on tthis+1 */ +#define GR_TEXTURECOMBINE_TRILINEAR_ODD 0x8 /* trilinear--odd levels tthis*/ +#define GR_TEXTURECOMBINE_TRILINEAR_EVEN 0x9 /*trilinear--even levels tthis*/ +#define GR_TEXTURECOMBINE_ONE 0xa /* texout = 0xFFFFFFFF */ + +typedef FxI32 GrTextureFilterMode_t; +#define GR_TEXTUREFILTER_POINT_SAMPLED 0x0 +#define GR_TEXTUREFILTER_BILINEAR 0x1 + +typedef FxI32 GrTextureFormat_t; +#define GR_TEXFMT_8BIT 0x0 +#define GR_TEXFMT_RGB_332 GR_TEXFMT_8BIT +#define GR_TEXFMT_YIQ_422 0x1 +#define GR_TEXFMT_ALPHA_8 0x2 /* (0..0xFF) alpha */ +#define GR_TEXFMT_INTENSITY_8 0x3 /* (0..0xFF) intensity */ +#define GR_TEXFMT_ALPHA_INTENSITY_44 0x4 +#define GR_TEXFMT_P_8 0x5 /* 8-bit palette */ +#define GR_TEXFMT_RSVD0 0x6 +#define GR_TEXFMT_RSVD1 0x7 +#define GR_TEXFMT_16BIT 0x8 +#define GR_TEXFMT_ARGB_8332 GR_TEXFMT_16BIT +#define GR_TEXFMT_AYIQ_8422 0x9 +#define GR_TEXFMT_RGB_565 0xa +#define GR_TEXFMT_ARGB_1555 0xb +#define GR_TEXFMT_ARGB_4444 0xc +#define GR_TEXFMT_ALPHA_INTENSITY_88 0xd +#define GR_TEXFMT_AP_88 0xe /* 8-bit alpha 8-bit palette */ +#define GR_TEXFMT_RSVD2 0xf + +typedef FxU32 GrTexTable_t; +#define GR_TEXTABLE_NCC0 0x0 +#define GR_TEXTABLE_NCC1 0x1 +#define GR_TEXTABLE_PALETTE 0x2 + +typedef FxU32 GrNCCTable_t; +#define GR_NCCTABLE_NCC0 0x0 +#define GR_NCCTABLE_NCC1 0x1 + +typedef FxU32 GrTexBaseRange_t; +#define GR_TEXBASE_256 0x0 +#define GR_TEXBASE_128 0x1 +#define GR_TEXBASE_64 0x2 +#define GR_TEXBASE_32_TO_1 0x3 + +#define GLIDE_STATE_PAD_SIZE 312 +#ifdef GLIDE_LIB +typedef struct _GrState_s GrState; +#else +typedef struct _GrState_s { + char pad[GLIDE_STATE_PAD_SIZE]; +} GrState; +#endif + +/* +** ----------------------------------------------------------------------- +** STRUCTURES +** ----------------------------------------------------------------------- +*/ +/* +** 3DF texture file structs +*/ +typedef struct +{ + FxU32 width, height; + int small_lod, large_lod; + GrAspectRatio_t aspect_ratio; + GrTextureFormat_t format; +} Gu3dfHeader; + +typedef struct +{ + FxU8 yRGB[16]; + FxI16 iRGB[4][3]; + FxI16 qRGB[4][3]; + FxU32 packed_data[12]; +} GuNccTable; + +typedef struct { + FxU32 data[256]; +} GuTexPalette; + +typedef union { + GuNccTable nccTable; + GuTexPalette palette; +} GuTexTable; + +typedef struct +{ + Gu3dfHeader header; + GuTexTable table; + void *data; + FxU32 mem_required; /* memory required for mip map in bytes. */ +} Gu3dfInfo; + +typedef struct { + GrLOD_t smallLod; + GrLOD_t largeLod; + GrAspectRatio_t aspectRatio; + GrTextureFormat_t format; + void *data; +} GrTexInfo; + +typedef struct +{ + int sst; /* SST where this texture map was stored */ + FxBool valid; /* set when this table entry is allocated*/ + int width, height; + GrAspectRatio_t aspect_ratio; /* aspect ratio of the mip map. */ + void *data; /* actual texture data */ + + GrTextureFormat_t format; /* format of the texture table */ + GrMipMapMode_t mipmap_mode; /* mip map mode for this texture */ + GrTextureFilterMode_t magfilter_mode; /* filtering to be used when magnified */ + GrTextureFilterMode_t minfilter_mode; /* filtering to be used with minified */ + GrTextureClampMode_t s_clamp_mode; /* how this texture should be clamped in s */ + GrTextureClampMode_t t_clamp_mode; /* how this texture should be clamped in t */ + FxU32 tLOD; /* Register value for tLOD register */ + FxU32 tTextureMode; /* Register value for tTextureMode register + not including non-texture specific bits */ + FxU32 lod_bias; /* LOD bias of the mip map in preshifted 4.2*/ + GrLOD_t lod_min, lod_max; /* largest and smallest levels of detail */ + int tmu; /* tmu on which this texture resides */ + FxU32 odd_even_mask; /* mask specifying levels on this tmu */ + FxU32 tmu_base_address; /* base addr (in TMU mem) of this texture */ + FxBool trilinear; /* should we blend by lod? */ + + GuNccTable ncc_table; /* NCC compression table (optional) */ +} GrMipMapInfo; + +typedef int GrSstType; +#define GR_SSTTYPE_VOODOO 0 +#define GR_SSTTYPE_SST96 1 +#define GR_SSTTYPE_AT3D 2 +#define GR_SSTTYPE_Voodoo2 3 + +typedef struct GrTMUConfig_St { + int tmuRev; /* Rev of Texelfx chip */ + int tmuRam; /* 1, 2, or 4 MB */ +} GrTMUConfig_t; + +typedef struct GrVoodooConfig_St { + int fbRam; /* 1, 2, or 4 MB */ + int fbiRev; /* Rev of Pixelfx chip */ + int nTexelfx; /* How many texelFX chips are there? */ + FxBool sliDetect; /* Is it a scan-line interleaved board? */ + GrTMUConfig_t tmuConfig[GLIDE_NUM_TMU]; /* Configuration of the Texelfx chips */ +} GrVoodooConfig_t; + +typedef struct GrSst96Config_St { + int fbRam; /* How much? */ + int nTexelfx; + GrTMUConfig_t tmuConfig; +} GrSst96Config_t; + +typedef GrVoodooConfig_t GrVoodoo2Config_t; + +typedef struct GrAT3DConfig_St { + int rev; +} GrAT3DConfig_t; + +typedef struct { + int num_sst; /* # of HW units in the system */ + struct { + GrSstType type; /* Which hardware is it? */ + union SstBoard_u { + GrVoodooConfig_t VoodooConfig; + GrSst96Config_t SST96Config; + GrAT3DConfig_t AT3DConfig; + GrVoodoo2Config_t Voodoo2Config; + } sstBoard; + } SSTs[MAX_NUM_SST]; /* configuration for each board */ +} GrHwConfiguration; + +typedef struct GrSstPerfStats_s { + FxU32 pixelsIn; /* # pixels processed (minus buffer clears) */ + FxU32 chromaFail; /* # pixels not drawn due to chroma key */ + FxU32 zFuncFail; /* # pixels not drawn due to Z comparison */ + FxU32 aFuncFail; /* # pixels not drawn due to alpha comparison */ + FxU32 pixelsOut; /* # pixels drawn (including buffer clears) */ +} GrSstPerfStats_t; + + +typedef struct { + float sow; /* s texture ordinate (s over w) */ + float tow; /* t texture ordinate (t over w) */ + float oow; /* 1/w (used mipmapping - really 0xfff/w) */ +} GrTmuVertex; + +/* +** GrVertex +** If these are changed the C & assembly language trisetup routines MUST +** be changed, for they will no longer work. +*/ +typedef struct +{ + float x, y, z; /* X, Y, and Z of scrn space -- Z is ignored */ + float r, g, b; /* R, G, B, ([0..255.0]) */ + float ooz; /* 65535/Z (used for Z-buffering) */ + float a; /* Alpha [0..255.0] */ + float oow; /* 1/W (used for W-buffering, texturing) */ + GrTmuVertex tmuvtx[GLIDE_NUM_TMU]; +} GrVertex; + +/* For indexing GrVertex as a float *. + CHANGE THESE IF THE VERTEX STRUCTURE CHANGES! + */ +#define GR_VERTEX_X_OFFSET 0 +#define GR_VERTEX_Y_OFFSET 1 +#define GR_VERTEX_Z_OFFSET 2 +#define GR_VERTEX_R_OFFSET 3 +#define GR_VERTEX_G_OFFSET 4 +#define GR_VERTEX_B_OFFSET 5 +#define GR_VERTEX_OOZ_OFFSET 6 +#define GR_VERTEX_A_OFFSET 7 +#define GR_VERTEX_OOW_OFFSET 8 +#define GR_VERTEX_SOW_TMU0_OFFSET 9 +#define GR_VERTEX_TOW_TMU0_OFFSET 10 +#define GR_VERTEX_OOW_TMU0_OFFSET 11 +#define GR_VERTEX_SOW_TMU1_OFFSET 12 +#define GR_VERTEX_TOW_TMU1_OFFSET 13 +#define GR_VERTEX_OOW_TMU1_OFFSET 14 +#if (GLIDE_NUM_TMU > 2) +#define GR_VERTEX_SOW_TMU2_OFFSET 15 +#define GR_VERTEX_TOW_TMU2_OFFSET 16 +#define GR_VERTEX_OOW_TMU2_OFFSET 17 +#endif + +typedef FxU32 GrLfbSrcFmt_t; +#define GR_LFB_SRC_FMT_565 0x00 +#define GR_LFB_SRC_FMT_555 0x01 +#define GR_LFB_SRC_FMT_1555 0x02 +#define GR_LFB_SRC_FMT_888 0x04 +#define GR_LFB_SRC_FMT_8888 0x05 +#define GR_LFB_SRC_FMT_565_DEPTH 0x0c +#define GR_LFB_SRC_FMT_555_DEPTH 0x0d +#define GR_LFB_SRC_FMT_1555_DEPTH 0x0e +#define GR_LFB_SRC_FMT_ZA16 0x0f +#define GR_LFB_SRC_FMT_RLE16 0x80 + +typedef FxI32 GrPassthruMode_t; +#define GR_PASSTHRU_SHOW_VGA 0x0 +#define GR_PASSTHRU_SHOW_SST1 0x1 + +typedef FxU32 GrHint_t; +#define GR_HINTTYPE_MIN 0 +#define GR_HINT_STWHINT 0 +#define GR_HINT_FIFOCHECKHINT 1 +#define GR_HINT_FPUPRECISION 2 +#define GR_HINT_ALLOW_MIPMAP_DITHER 3 +#define GR_HINTTYPE_MAX 3 + +typedef FxU32 GrSTWHint_t; +#define GR_STWHINT_W_DIFF_FBI FXBIT(0) +#define GR_STWHINT_W_DIFF_TMU0 FXBIT(1) +#define GR_STWHINT_ST_DIFF_TMU0 FXBIT(2) +#define GR_STWHINT_W_DIFF_TMU1 FXBIT(3) +#define GR_STWHINT_ST_DIFF_TMU1 FXBIT(4) +#define GR_STWHINT_W_DIFF_TMU2 FXBIT(5) +#define GR_STWHINT_ST_DIFF_TMU2 FXBIT(6) + +typedef FxU32 GrControl_t; +#define GR_CONTROL_ACTIVATE 0x1 +#define GR_CONTROL_DEACTIVATE 0x2 +#define GR_CONTROL_RESIZE 0x3 +#define GR_CONTROL_MOVE 0x4 + +#define GR_GENERATE_FIFOCHECK_HINT_MASK(swHWM, swLWM) \ + (((swHWM & 0xffff) << 16) | (swLWM & 0xffff)) + +/* +** ----------------------------------------------------------------------- +** FUNCTION PROTOTYPES +** ----------------------------------------------------------------------- +*/ +#ifndef FX_GLIDE_NO_FUNC_PROTO +/* +** rendering functions +*/ +FX_ENTRY void FX_CALL +grDrawLine( const GrVertex *v1, const GrVertex *v2 ); + +FX_ENTRY void FX_CALL +grDrawPlanarPolygon( int nverts, const int ilist[], const GrVertex vlist[] ); + +FX_ENTRY void FX_CALL +grDrawPlanarPolygonVertexList( int nverts, const GrVertex vlist[] ); + +FX_ENTRY void FX_CALL +grDrawPoint( const GrVertex *pt ); + +FX_ENTRY void FX_CALL +grDrawPolygon( int nverts, const int ilist[], const GrVertex vlist[] ); + +FX_ENTRY void FX_CALL +grDrawPolygonVertexList( int nverts, const GrVertex vlist[] ); + +FX_ENTRY void FX_CALL +grDrawTriangle( const GrVertex *a, const GrVertex *b, const GrVertex *c ); + +FX_ENTRY void FX_CALL +guDrawTriangleWithClip( const GrVertex *a, const GrVertex *b, const GrVertex *c ); + +/* +** buffer management +*/ +FX_ENTRY void FX_CALL +grBufferClear( GrColor_t color, GrAlpha_t alpha, FxU16 depth ); + +FX_ENTRY int FX_CALL +grBufferNumPending( void ); + +FX_ENTRY void FX_CALL +grBufferSwap( int swap_interval ); + +FX_ENTRY void FX_CALL +grRenderBuffer( GrBuffer_t buffer ); + +/* +** error management +*/ +typedef void (*GrErrorCallbackFnc_t)( const char *string, FxBool fatal ); + +FX_ENTRY void FX_CALL +grErrorSetCallback( GrErrorCallbackFnc_t fnc ); + +/* +** SST routines +*/ +FX_ENTRY void FX_CALL +grSstIdle(void); + +FX_ENTRY FxU32 FX_CALL +grSstVideoLine( void ); + +FX_ENTRY FxBool FX_CALL +grSstVRetraceOn( void ); + +FX_ENTRY FxBool FX_CALL +grSstIsBusy( void ); + +FX_ENTRY FxBool FX_CALL +grSstWinOpen( + FxU32 hWnd, + GrScreenResolution_t screen_resolution, + GrScreenRefresh_t refresh_rate, + GrColorFormat_t color_format, + GrOriginLocation_t origin_location, + int nColBuffers, + int nAuxBuffers); + +FX_ENTRY void FX_CALL +grSstWinClose( void ); + +FX_ENTRY FxBool FX_CALL +grSstControl( FxU32 code ); + +FX_ENTRY FxBool FX_CALL +grSstQueryHardware( GrHwConfiguration *hwconfig ); + +FX_ENTRY FxBool FX_CALL +grSstQueryBoards( GrHwConfiguration *hwconfig ); + +FX_ENTRY void FX_CALL +grSstOrigin(GrOriginLocation_t origin); + +FX_ENTRY void FX_CALL +grSstSelect( int which_sst ); + +FX_ENTRY FxU32 FX_CALL +grSstScreenHeight( void ); + +FX_ENTRY FxU32 FX_CALL +grSstScreenWidth( void ); + +FX_ENTRY FxU32 FX_CALL +grSstStatus( void ); + +/* +** Drawing Statistics +*/ +FX_ENTRY void FX_CALL +grSstPerfStats(GrSstPerfStats_t *pStats); + +FX_ENTRY void FX_CALL +grSstResetPerfStats(void); + +FX_ENTRY void FX_CALL +grResetTriStats(); + +FX_ENTRY void FX_CALL +grTriStats(FxU32 *trisProcessed, FxU32 *trisDrawn); + +/* +** Glide configuration and special effect maintenance functions +*/ +FX_ENTRY void FX_CALL +grAlphaBlendFunction( + GrAlphaBlendFnc_t rgb_sf, GrAlphaBlendFnc_t rgb_df, + GrAlphaBlendFnc_t alpha_sf, GrAlphaBlendFnc_t alpha_df + ); + +FX_ENTRY void FX_CALL +grAlphaCombine( + GrCombineFunction_t function, GrCombineFactor_t factor, + GrCombineLocal_t local, GrCombineOther_t other, + FxBool invert + ); + +FX_ENTRY void FX_CALL +grAlphaControlsITRGBLighting( FxBool enable ); + +FX_ENTRY void FX_CALL +grAlphaTestFunction( GrCmpFnc_t function ); + +FX_ENTRY void FX_CALL +grAlphaTestReferenceValue( GrAlpha_t value ); + +FX_ENTRY void FX_CALL +grChromakeyMode( GrChromakeyMode_t mode ); + +FX_ENTRY void FX_CALL +grChromakeyValue( GrColor_t value ); + +FX_ENTRY void FX_CALL +grClipWindow( FxU32 minx, FxU32 miny, FxU32 maxx, FxU32 maxy ); + +FX_ENTRY void FX_CALL +grColorCombine( + GrCombineFunction_t function, GrCombineFactor_t factor, + GrCombineLocal_t local, GrCombineOther_t other, + FxBool invert ); + +FX_ENTRY void FX_CALL +grColorMask( FxBool rgb, FxBool a ); + +FX_ENTRY void FX_CALL +grCullMode( GrCullMode_t mode ); + +FX_ENTRY void FX_CALL +grConstantColorValue( GrColor_t value ); + +FX_ENTRY void FX_CALL +grConstantColorValue4( float a, float r, float g, float b ); + +FX_ENTRY void FX_CALL +grDepthBiasLevel( FxI16 level ); + +FX_ENTRY void FX_CALL +grDepthBufferFunction( GrCmpFnc_t function ); + +FX_ENTRY void FX_CALL +grDepthBufferMode( GrDepthBufferMode_t mode ); + +FX_ENTRY void FX_CALL +grDepthMask( FxBool mask ); + +FX_ENTRY void FX_CALL +grDisableAllEffects( void ); + +FX_ENTRY void FX_CALL +grDitherMode( GrDitherMode_t mode ); + +FX_ENTRY void FX_CALL +grFogColorValue( GrColor_t fogcolor ); + +FX_ENTRY void FX_CALL +grFogMode( GrFogMode_t mode ); + +FX_ENTRY void FX_CALL +grFogTable( const GrFog_t ft[GR_FOG_TABLE_SIZE] ); + +FX_ENTRY void FX_CALL +grGammaCorrectionValue( float value ); + +FX_ENTRY void FX_CALL +grSplash(float x, float y, float width, float height, FxU32 frame); + +/* +** texture mapping control functions +*/ +FX_ENTRY FxU32 FX_CALL +grTexCalcMemRequired( + GrLOD_t lodmin, GrLOD_t lodmax, + GrAspectRatio_t aspect, GrTextureFormat_t fmt); + +FX_ENTRY FxU32 FX_CALL +grTexTextureMemRequired( FxU32 evenOdd, + GrTexInfo *info ); + +FX_ENTRY FxU32 FX_CALL +grTexMinAddress( GrChipID_t tmu ); + + +FX_ENTRY FxU32 FX_CALL +grTexMaxAddress( GrChipID_t tmu ); + + +FX_ENTRY void FX_CALL +grTexNCCTable( GrChipID_t tmu, GrNCCTable_t table ); + +FX_ENTRY void FX_CALL +grTexSource( GrChipID_t tmu, + FxU32 startAddress, + FxU32 evenOdd, + GrTexInfo *info ); + +FX_ENTRY void FX_CALL +grTexClampMode( + GrChipID_t tmu, + GrTextureClampMode_t s_clampmode, + GrTextureClampMode_t t_clampmode + ); + +FX_ENTRY void FX_CALL +grTexCombine( + GrChipID_t tmu, + GrCombineFunction_t rgb_function, + GrCombineFactor_t rgb_factor, + GrCombineFunction_t alpha_function, + GrCombineFactor_t alpha_factor, + FxBool rgb_invert, + FxBool alpha_invert + ); + +FX_ENTRY void FX_CALL +grTexCombineFunction( + GrChipID_t tmu, + GrTextureCombineFnc_t fnc + ); + +FX_ENTRY void FX_CALL +grTexDetailControl( + GrChipID_t tmu, + int lod_bias, + FxU8 detail_scale, + float detail_max + ); + +FX_ENTRY void FX_CALL +grTexFilterMode( + GrChipID_t tmu, + GrTextureFilterMode_t minfilter_mode, + GrTextureFilterMode_t magfilter_mode + ); + + +FX_ENTRY void FX_CALL +grTexLodBiasValue(GrChipID_t tmu, float bias ); + +FX_ENTRY void FX_CALL +grTexDownloadMipMap( GrChipID_t tmu, + FxU32 startAddress, + FxU32 evenOdd, + GrTexInfo *info ); + +FX_ENTRY void FX_CALL +grTexDownloadMipMapLevel( GrChipID_t tmu, + FxU32 startAddress, + GrLOD_t thisLod, + GrLOD_t largeLod, + GrAspectRatio_t aspectRatio, + GrTextureFormat_t format, + FxU32 evenOdd, + void *data ); + +FX_ENTRY void FX_CALL +grTexDownloadMipMapLevelPartial( GrChipID_t tmu, + FxU32 startAddress, + GrLOD_t thisLod, + GrLOD_t largeLod, + GrAspectRatio_t aspectRatio, + GrTextureFormat_t format, + FxU32 evenOdd, + void *data, + int start, + int end ); + + +FX_ENTRY void FX_CALL +ConvertAndDownloadRle( GrChipID_t tmu, + FxU32 startAddress, + GrLOD_t thisLod, + GrLOD_t largeLod, + GrAspectRatio_t aspectRatio, + GrTextureFormat_t format, + FxU32 evenOdd, + FxU8 *bm_data, + long bm_h, + FxU32 u0, + FxU32 v0, + FxU32 width, + FxU32 height, + FxU32 dest_width, + FxU32 dest_height, + FxU16 *tlut); + +FX_ENTRY void FX_CALL +grCheckForRoom(FxI32 n); + +FX_ENTRY void FX_CALL +grTexDownloadTable( GrChipID_t tmu, + GrTexTable_t type, + void *data ); + +FX_ENTRY void FX_CALL +grTexDownloadTablePartial( GrChipID_t tmu, + GrTexTable_t type, + void *data, + int start, + int end ); + +FX_ENTRY void FX_CALL +grTexMipMapMode( GrChipID_t tmu, + GrMipMapMode_t mode, + FxBool lodBlend ); + +FX_ENTRY void FX_CALL +grTexMultibase( GrChipID_t tmu, + FxBool enable ); + +FX_ENTRY void FX_CALL +grTexMultibaseAddress( GrChipID_t tmu, + GrTexBaseRange_t range, + FxU32 startAddress, + FxU32 evenOdd, + GrTexInfo *info ); + +/* +** utility texture functions +*/ +FX_ENTRY GrMipMapId_t FX_CALL +guTexAllocateMemory( + GrChipID_t tmu, + FxU8 odd_even_mask, + int width, int height, + GrTextureFormat_t fmt, + GrMipMapMode_t mm_mode, + GrLOD_t smallest_lod, GrLOD_t largest_lod, + GrAspectRatio_t aspect, + GrTextureClampMode_t s_clamp_mode, + GrTextureClampMode_t t_clamp_mode, + GrTextureFilterMode_t minfilter_mode, + GrTextureFilterMode_t magfilter_mode, + float lod_bias, + FxBool trilinear + ); + +FX_ENTRY FxBool FX_CALL +guTexChangeAttributes( + GrMipMapId_t mmid, + int width, int height, + GrTextureFormat_t fmt, + GrMipMapMode_t mm_mode, + GrLOD_t smallest_lod, GrLOD_t largest_lod, + GrAspectRatio_t aspect, + GrTextureClampMode_t s_clamp_mode, + GrTextureClampMode_t t_clamp_mode, + GrTextureFilterMode_t minFilterMode, + GrTextureFilterMode_t magFilterMode + ); + +FX_ENTRY void FX_CALL +guTexCombineFunction( + GrChipID_t tmu, + GrTextureCombineFnc_t fnc + ); + +FX_ENTRY GrMipMapId_t FX_CALL +guTexGetCurrentMipMap( GrChipID_t tmu ); + +FX_ENTRY GrMipMapInfo * FX_CALL +guTexGetMipMapInfo( GrMipMapId_t mmid ); + +FX_ENTRY FxU32 FX_CALL +guTexMemQueryAvail( GrChipID_t tmu ); + +FX_ENTRY void FX_CALL +guTexMemReset( void ); + +FX_ENTRY void FX_CALL +guTexDownloadMipMap( + GrMipMapId_t mmid, + const void *src, + const GuNccTable *table + ); + +FX_ENTRY void FX_CALL +guTexDownloadMipMapLevel( + GrMipMapId_t mmid, + GrLOD_t lod, + const void **src + ); +FX_ENTRY void FX_CALL +guTexSource( GrMipMapId_t id ); + +/* +** linear frame buffer functions +*/ + +FX_ENTRY FxBool FX_CALL +grLfbLock( GrLock_t type, GrBuffer_t buffer, GrLfbWriteMode_t writeMode, + GrOriginLocation_t origin, FxBool pixelPipeline, + GrLfbInfo_t *info ); + +FX_ENTRY FxBool FX_CALL +grLfbUnlock( GrLock_t type, GrBuffer_t buffer ); + +FX_ENTRY void FX_CALL +grLfbConstantAlpha( GrAlpha_t alpha ); + +FX_ENTRY void FX_CALL +grLfbConstantDepth( FxU16 depth ); + +FX_ENTRY void FX_CALL +grLfbWriteColorSwizzle(FxBool swizzleBytes, FxBool swapWords); + +FX_ENTRY void FX_CALL +grLfbWriteColorFormat(GrColorFormat_t colorFormat); + + +FX_ENTRY FxBool FX_CALL +grLfbWriteRegion( GrBuffer_t dst_buffer, + FxU32 dst_x, FxU32 dst_y, + GrLfbSrcFmt_t src_format, + FxU32 src_width, FxU32 src_height, + FxI32 src_stride, void *src_data ); + +FX_ENTRY FxBool FX_CALL +grLfbReadRegion( GrBuffer_t src_buffer, + FxU32 src_x, FxU32 src_y, + FxU32 src_width, FxU32 src_height, + FxU32 dst_stride, void *dst_data ); + + +/* +** Antialiasing Functions +*/ +FX_ENTRY void FX_CALL +grAADrawLine(const GrVertex *v1, const GrVertex *v2); + +FX_ENTRY void FX_CALL +grAADrawPoint(const GrVertex *pt ); + +FX_ENTRY void FX_CALL +grAADrawPolygon(const int nverts, const int ilist[], const GrVertex vlist[]); + +FX_ENTRY void FX_CALL +grAADrawPolygonVertexList(const int nverts, const GrVertex vlist[]); + +FX_ENTRY void FX_CALL +grAADrawTriangle( + const GrVertex *a, const GrVertex *b, const GrVertex *c, + FxBool ab_antialias, FxBool bc_antialias, FxBool ca_antialias + ); + +/* +** glide management functions +*/ +FX_ENTRY void FX_CALL +grGlideInit( void ); + +FX_ENTRY void FX_CALL +grGlideShutdown( void ); + +FX_ENTRY void FX_CALL +grGlideGetVersion( char version[80] ); + +FX_ENTRY void FX_CALL +grGlideGetState( GrState *state ); + +FX_ENTRY void FX_CALL +grGlideSetState( const GrState *state ); + +FX_ENTRY void FX_CALL +grGlideShamelessPlug(const FxBool on); + +FX_ENTRY void FX_CALL +grHints(GrHint_t hintType, FxU32 hintMask); + +#endif /* FX_GLIDE_NO_FUNC_PROTO */ + +#ifdef __cplusplus +} +#endif + +#include "glideutl.h" + +#endif /* __GLIDE_H__ */ + + +#ifndef _VOLITION_GLIDE_EXT +#define _VOLITION_GLIDE_EXT + +// Returns 1 if Glide found, else 0 if not +int vglide_init(); + +// shuts glide lib +void vglide_close(); + +extern void (__stdcall *grDrawLine)(const GrVertex *v1, const GrVertex *v2); +extern void (__stdcall *grDrawPlanarPolygon)(int nverts, const int ilist[], const GrVertex vlist[]); +extern void (__stdcall *grDrawPlanarPolygonVertexList)(int nverts, const GrVertex vlist[]); +extern void (__stdcall *grDrawPoint)(const GrVertex *pt); +extern void (__stdcall *grDrawPolygon)(int nverts, const int ilist[], const GrVertex vlist[]); +extern void (__stdcall *grDrawPolygonVertexList)(int nverts, const GrVertex vlist[]); +extern void (__stdcall *grDrawTriangle)(const GrVertex *a, const GrVertex *b, const GrVertex *c); +extern void (__stdcall *guDrawTriangleWithClip)(const GrVertex *a, const GrVertex *b, const GrVertex *c); +extern void (__stdcall *grBufferClear)(GrColor_t color, GrAlpha_t alpha, FxU16 depth); +extern int (__stdcall *grBufferNumPending)(void); +extern void (__stdcall *grBufferSwap)(int swap_interval); +extern void (__stdcall *grRenderBuffer)(GrBuffer_t buffer); + +typedef void (*GrErrorCallbackFnc_t)(const char *string, FxBool fatal); + +extern void (__stdcall *grErrorSetCallback)(GrErrorCallbackFnc_t fnc); +extern void (__stdcall *grSstIdle)(void); +extern FxU32 (__stdcall *grSstVideoLine)(void); +extern FxBool (__stdcall *grSstVRetraceOn)(void); +extern FxBool (__stdcall *grSstIsBusy)(void); +extern FxBool (__stdcall *grSstWinOpen)( + FxU32 hWnd, + GrScreenResolution_t screen_resolution, + GrScreenRefresh_t refresh_rate, + GrColorFormat_t color_format, + GrOriginLocation_t origin_location, + int nColBuffers, + int nAuxBuffers); +extern void (__stdcall *grSstWinClose)(void); +extern FxBool (__stdcall *grSstControl)(FxU32 code); +extern FxBool (__stdcall *grSstQueryHardware)(GrHwConfiguration *hwconfig); +extern FxBool (__stdcall *grSstQueryBoards)(GrHwConfiguration *hwconfig); +extern void (__stdcall *grSstOrigin)(GrOriginLocation_t origin); +extern void (__stdcall *grSstSelect)(int which_sst); +extern FxU32 (__stdcall *grSstScreenHeight)(void); +extern FxU32 (__stdcall *grSstScreenWidth)(void); +extern FxU32 (__stdcall *grSstStatus)(void); +extern void (__stdcall *grSstPerfStats)(GrSstPerfStats_t *pStats); +extern void (__stdcall *grSstResetPerfStats)(void); +extern void (__stdcall *grResetTriStats)(); +extern void (__stdcall *grTriStats)(FxU32 *trisProcessed, FxU32 *trisDrawn); +extern void (__stdcall *grAlphaBlendFunction)(GrAlphaBlendFnc_t rgb_sf, GrAlphaBlendFnc_t rgb_df, GrAlphaBlendFnc_t alpha_sf, GrAlphaBlendFnc_t alpha_df); +extern void (__stdcall *grAlphaCombine)(GrCombineFunction_t function, GrCombineFactor_t factor, GrCombineLocal_t local, GrCombineOther_t other, FxBool invert); +extern void (__stdcall *grAlphaControlsITRGBLighting)(FxBool enable); +extern void (__stdcall *grAlphaTestFunction)(GrCmpFnc_t function); +extern void (__stdcall *grAlphaTestReferenceValue)(GrAlpha_t value); +extern void (__stdcall *grChromakeyMode)(GrChromakeyMode_t mode); +extern void (__stdcall *grChromakeyValue)(GrColor_t value); +extern void (__stdcall *grClipWindow)(FxU32 minx, FxU32 miny, FxU32 maxx, FxU32 maxy); +extern void (__stdcall *grColorCombine)( + GrCombineFunction_t function, GrCombineFactor_t factor, + GrCombineLocal_t local, GrCombineOther_t other, + FxBool invert); +extern void (__stdcall *grColorMask)(FxBool rgb, FxBool a); +extern void (__stdcall *grCullMode)(GrCullMode_t mode); +extern void (__stdcall *grConstantColorValue)(GrColor_t value); +extern void (__stdcall *grConstantColorValue4)(float a, float r, float g, float b); +extern void (__stdcall *grDepthBiasLevel)(FxI16 level); +extern void (__stdcall *grDepthBufferFunction)(GrCmpFnc_t function); +extern void (__stdcall *grDepthBufferMode)(GrDepthBufferMode_t mode); +extern void (__stdcall *grDepthMask)(FxBool mask); +extern void (__stdcall *grDisableAllEffects)(void); +extern void (__stdcall *grDitherMode)( GrDitherMode_t mode); +extern void (__stdcall *grFogColorValue)( GrColor_t fogcolor); +extern void (__stdcall *grFogMode)(GrFogMode_t mode); +extern void (__stdcall *grFogTable)(const GrFog_t ft[GR_FOG_TABLE_SIZE]); +extern void (__stdcall *grGammaCorrectionValue)(float value); +extern void (__stdcall *grSplash)(float x, float y, float width, float height, FxU32 frame); +extern FxU32 (__stdcall *grTexCalcMemRequired)( + GrLOD_t lodmin, GrLOD_t lodmax, + GrAspectRatio_t aspect, GrTextureFormat_t fmt); +extern FxU32 (__stdcall *grTexTextureMemRequired)(FxU32 evenOdd, GrTexInfo *info); +extern FxU32 (__stdcall *grTexMinAddress)(GrChipID_t tmu); +extern FxU32 (__stdcall *grTexMaxAddress)(GrChipID_t tmu); +extern void (__stdcall *grTexNCCTable)(GrChipID_t tmu, GrNCCTable_t table); +extern void (__stdcall *grTexSource)(GrChipID_t tmu, FxU32 startAddress, FxU32 evenOdd, GrTexInfo *info); +extern void (__stdcall *grTexClampMode)(GrChipID_t tmu, GrTextureClampMode_t s_clampmode, GrTextureClampMode_t t_clampmode); +extern void (__stdcall *grTexCombine)( + GrChipID_t tmu, + GrCombineFunction_t rgb_function, + GrCombineFactor_t rgb_factor, + GrCombineFunction_t alpha_function, + GrCombineFactor_t alpha_factor, + FxBool rgb_invert, + FxBool alpha_invert + ); +extern void (__stdcall *grTexCombineFunction)( + GrChipID_t tmu, + GrTextureCombineFnc_t fnc + ); +extern void (__stdcall *grTexDetailControl)( + GrChipID_t tmu, + int lod_bias, + FxU8 detail_scale, + float detail_max + ); +extern void (__stdcall *grTexFilterMode)( + GrChipID_t tmu, + GrTextureFilterMode_t minfilter_mode, + GrTextureFilterMode_t magfilter_mode + ); +extern void (__stdcall *grTexLodBiasValue)(GrChipID_t tmu, float bias); +extern void (__stdcall *grTexDownloadMipMap)(GrChipID_t tmu, + FxU32 startAddress, + FxU32 evenOdd, + GrTexInfo *info); + +extern void (__stdcall *grTexDownloadMipMapLevel)(GrChipID_t tmu, + FxU32 startAddress, + GrLOD_t thisLod, + GrLOD_t largeLod, + GrAspectRatio_t aspectRatio, + GrTextureFormat_t format, + FxU32 evenOdd, + void *data); + +extern void (__stdcall *grTexDownloadMipMapLevelPartial)(GrChipID_t tmu, + FxU32 startAddress, + GrLOD_t thisLod, + GrLOD_t largeLod, + GrAspectRatio_t aspectRatio, + GrTextureFormat_t format, + FxU32 evenOdd, + void *data, + int start, + int end); + + +extern void (__stdcall *ConvertAndDownloadRle)(GrChipID_t tmu, + FxU32 startAddress, + GrLOD_t thisLod, + GrLOD_t largeLod, + GrAspectRatio_t aspectRatio, + GrTextureFormat_t format, + FxU32 evenOdd, + FxU8 *bm_data, + long bm_h, + FxU32 u0, + FxU32 v0, + FxU32 width, + FxU32 height, + FxU32 dest_width, + FxU32 dest_height, + FxU16 *tlut); + +extern void (__stdcall *grCheckForRoom)(FxI32 n); + +extern void (__stdcall *grTexDownloadTable)(GrChipID_t tmu, + GrTexTable_t type, + void *data); + +extern void (__stdcall *grTexDownloadTablePartial)(GrChipID_t tmu, + GrTexTable_t type, + void *data, + int start, + int end); + +extern void (__stdcall *grTexMipMapMode)(GrChipID_t tmu, + GrMipMapMode_t mode, + FxBool lodBlend); + +extern void (__stdcall *grTexMultibase)(GrChipID_t tmu, + FxBool enable); + +extern void (__stdcall *grTexMultibaseAddress)(GrChipID_t tmu, + GrTexBaseRange_t range, + FxU32 startAddress, + FxU32 evenOdd, + GrTexInfo *info); + +extern GrMipMapId_t (__stdcall *guTexAllocateMemory)( + GrChipID_t tmu, + FxU8 odd_even_mask, + int width, int height, + GrTextureFormat_t fmt, + GrMipMapMode_t mm_mode, + GrLOD_t smallest_lod, GrLOD_t largest_lod, + GrAspectRatio_t aspect, + GrTextureClampMode_t s_clamp_mode, + GrTextureClampMode_t t_clamp_mode, + GrTextureFilterMode_t minfilter_mode, + GrTextureFilterMode_t magfilter_mode, + float lod_bias, + FxBool trilinear + ); + +extern FxBool (__stdcall *guTexChangeAttributes)( + GrMipMapId_t mmid, + int width, int height, + GrTextureFormat_t fmt, + GrMipMapMode_t mm_mode, + GrLOD_t smallest_lod, GrLOD_t largest_lod, + GrAspectRatio_t aspect, + GrTextureClampMode_t s_clamp_mode, + GrTextureClampMode_t t_clamp_mode, + GrTextureFilterMode_t minFilterMode, + GrTextureFilterMode_t magFilterMode + ); +extern void (__stdcall *guTexCombineFunction)( + GrChipID_t tmu, + GrTextureCombineFnc_t fnc + ); +extern GrMipMapId_t (__stdcall *guTexGetCurrentMipMap)(GrChipID_t tmu); +extern GrMipMapInfo* (__stdcall *guTexGetMipMapInfo)(GrMipMapId_t mmid); +extern FxU32 (__stdcall *guTexMemQueryAvail)(GrChipID_t tmu); +extern void (__stdcall *guTexMemReset)(void); +extern void (__stdcall *guTexDownloadMipMap)( + GrMipMapId_t mmid, + const void *src, + const GuNccTable *table + ); +extern void (__stdcall *guTexDownloadMipMapLevel)( + GrMipMapId_t mmid, + GrLOD_t lod, + const void **src + ); +extern void (__stdcall *guTexSource)(GrMipMapId_t id); +extern void (__stdcall *guColorCombineFunction)(GrColorCombineFnc_t fnc); + +extern FxBool (__stdcall *grLfbLock)( GrLock_t type, GrBuffer_t buffer, GrLfbWriteMode_t writeMode, + GrOriginLocation_t origin, FxBool pixelPipeline, + GrLfbInfo_t *info); +extern FxBool (__stdcall *grLfbUnlock)(GrLock_t type, GrBuffer_t buffer); +extern void (__stdcall *grLfbConstantAlpha)(GrAlpha_t alpha); +extern void (__stdcall *grLfbConstantDepth)(FxU16 depth); +extern void (__stdcall *grLfbWriteColorSwizzle)(FxBool swizzleBytes, FxBool swapWords); +extern void (__stdcall *grLfbWriteColorFormat)(GrColorFormat_t colorFormat); +extern FxBool (__stdcall *grLfbWriteRegion)(GrBuffer_t dst_buffer, + FxU32 dst_x, FxU32 dst_y, + GrLfbSrcFmt_t src_format, + FxU32 src_width, FxU32 src_height, + FxI32 src_stride, void *src_data); + +extern FxBool (__stdcall *grLfbReadRegion)(GrBuffer_t src_buffer, + FxU32 src_x, FxU32 src_y, + FxU32 src_width, FxU32 src_height, + FxU32 dst_stride, void *dst_data); + +extern void (__stdcall *grAADrawLine)(const GrVertex *v1, const GrVertex *v2); +extern void (__stdcall *grAADrawPoint)(const GrVertex *pt); +extern void (__stdcall *grAADrawPolygon)(const int nverts, const int ilist[], const GrVertex vlist[]); +extern void (__stdcall *grAADrawPolygonVertexList)(const int nverts, const GrVertex vlist[]); +extern void (__stdcall *grAADrawTriangle)( + const GrVertex *a, const GrVertex *b, const GrVertex *c, + FxBool ab_antialias, FxBool bc_antialias, FxBool ca_antialias + ); +extern void (__stdcall *grGlideInit)(void); +extern void (__stdcall *grGlideShutdown)(void); +extern void (__stdcall *grGlideGetVersion)(char version[80]); +extern void (__stdcall *grGlideGetState)(GrState *state); +extern void (__stdcall *grGlideSetState)( const GrState *state); +extern void (__stdcall *grGlideShamelessPlug)(const FxBool on); +extern void (__stdcall *grHints)(GrHint_t hintType, FxU32 hintMask); + +extern float (__stdcall *guFogTableIndexToW)(int i); +extern void (__stdcall *guFogGenerateExp)( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float density ); +extern void (__stdcall *guFogGenerateExp2)( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float density ); +extern void (__stdcall *guFogGenerateLinear)( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float nearZ, float farZ ); + +#endif //_VOLITION_GLIDE_EXT + diff --git a/include/glidesys.h b/include/glidesys.h new file mode 100644 index 0000000..fc8430a --- /dev/null +++ b/include/glidesys.h @@ -0,0 +1,128 @@ +/* +** Copyright (c) 1995, 3Dfx Interactive, Inc. +** All Rights Reserved. +** +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; +** the contents of this file may not be disclosed to third parties, copied or +** duplicated in any form, in whole or in part, without the prior written +** permission of 3Dfx Interactive, Inc. +** +** RESTRICTED RIGHTS LEGEND: +** Use, duplication or disclosure by the Government is subject to restrictions +n** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - +** rights reserved under the Copyright Laws of the United States. +** +** $Header$ +** $Log$ +** Revision 1.1 2002/05/03 03:28:12 root +** Initial revision +** + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 2 2/27/98 3:25p Frank + * (John/Frank K.) updated to 2.43 headers + * + * 1 10/16/97 11:51a Klier +*/ +#ifndef __GLIDESYS_H__ +#define __GLIDESYS_H__ + +/* +n** ----------------------------------------------------------------------- +** COMPILER/ENVIRONMENT CONFIGURATION +** ----------------------------------------------------------------------- +*/ + +/* Endianness is stored in bits [30:31] */ +#define GLIDE_ENDIAN_SHIFT 30 +#define GLIDE_ENDIAN_LITTLE (0x1 << GLIDE_ENDIAN_SHIFT) +#define GLIDE_ENDIAN_BIG (0x2 << GLIDE_ENDIAN_SHIFT) + +/* OS is stored in bits [0:6] */ +#define GLIDE_OS_SHIFT 0 +#define GLIDE_OS_UNIX 0x1 +#define GLIDE_OS_DOS32 0x2 +#define GLIDE_OS_WIN32 0x4 +#define GLIDE_OS_SYSTEM7 0x8 +#define GLIDE_OS_OS2 0x10 +#define GLIDE_OS_OTHER 0x20 /* For Proprietary Arcade HW */ + +/* Sim vs. Hardware is stored in bits [7:8] */ +#define GLIDE_SST_SHIFT 7 +#define GLIDE_SST_SIM (0x1 << GLIDE_SST_SHIFT) +#define GLIDE_SST_HW (0x2 << GLIDE_SST_SHIFT ) + +/* Hardware Type is stored in bits [9:12] */ +#define GLIDE_HW_SHIFT 9 +#define GLIDE_HW_SST1 (0x1 << GLIDE_HW_SHIFT) +#define GLIDE_HW_SST96 (0x2 << GLIDE_HW_SHIFT) +#define GLIDE_HW_SSTH3 (0x4 << GLIDE_HW_SHIFT) +#define GLIDE_HW_SST2 (0x8 << GLIDE_HW_SHIFT) + +/* +** Make sure we handle all instances of WIN32 +*/ +#ifndef __WIN32__ +# if defined ( _WIN32 ) || defined (WIN32) || defined(__NT__) +# define __WIN32__ +# endif +#endif + +/* We need two checks on the OS: one for endian, the other for OS */ +/* Check for endianness */ +#if defined(__IRIX__) || defined(__sparc__) || defined(MACOS) +# define GLIDE_ENDIAN GLIDE_ENDIAN_BIG +#else +# define GLIDE_ENDIAN GLIDE_ENDIAN_LITTLE +#endif + +/* Check for OS */ +#if defined(__IRIX__) || defined(__sparc__) || defined(__linux__) +# define GLIDE_OS GLIDE_OS_UNIX +#elif defined(__DOS__) +# define GLIDE_OS GLIDE_OS_DOS32 +#elif defined(__WIN32__) +# define GLIDE_OS GLIDE_OS_WIN32 +#endif + +/* Check for Simulator vs. Hardware */ +#ifdef GLIDE_SIMULATOR +# define GLIDE_SST GLIDE_SST_SIM +#else +# define GLIDE_SST GLIDE_SST_HW +#endif + +/* Check for type of hardware */ +#ifdef SST96 +# define GLIDE_HW GLIDE_HW_SST96 +#elif defined(SSTH3) +# define GLIDE_HW GLIDE_HW_SSTH3 +#elif defined(SST2) +# define GLIDE_HW GLIDE_HW_SST2 +#else /* Default to SST1 */ +# define GLIDE_HW GLIDE_HW_SST1 +#endif + + +#define GLIDE_PLATFORM (GLIDE_ENDIAN | GLIDE_OS | GLIDE_SST | GLIDE_HW) + +/* +** Control the number of TMUs +*/ +#ifndef GLIDE_NUM_TMU +# define GLIDE_NUM_TMU 2 +#endif + + +#if ( ( GLIDE_NUM_TMU < 0 ) && ( GLIDE_NUM_TMU > 3 ) ) +# error "GLIDE_NUM_TMU set to an invalid value" +#endif + +#endif /* __GLIDESYS_H__ */ + diff --git a/include/glideutl.h b/include/glideutl.h new file mode 100644 index 0000000..c047f23 --- /dev/null +++ b/include/glideutl.h @@ -0,0 +1,133 @@ +/* +** Copyright (c) 1995, 3Dfx Interactive, Inc. +** All Rights Reserved. +** +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; +** the contents of this file may not be disclosed to third parties, copied or +** duplicated in any form, in whole or in part, without the prior written +** permission of 3Dfx Interactive, Inc. +** +** RESTRICTED RIGHTS LEGEND: +** Use, duplication or disclosure by the Government is subject to restrictions +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - +** rights reserved under the Copyright Laws of the United States. +** +** $Header$ +** $Log$ +** Revision 1.1 2002/05/03 03:28:12 root +** Initial revision +** + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 2 2/27/98 3:25p Frank + * (John/Frank K.) updated to 2.43 headers + * + * 1 10/16/97 11:51a Klier + * + * 6 8/14/97 5:32p Pgj + * remove dead code per GMT + * + * 5 6/12/97 5:19p Pgj + * Fix bug 578 + * + * 4 3/05/97 9:36p Jdt + * Removed guFbWriteRegion added guEncodeRLE16 + * + * 3 1/16/97 3:45p Dow + * Embedded fn protos in ifndef FX_GLIDE_NO_FUNC_PROTO +*/ + +/* Glide Utility routines */ + +#ifndef __GLIDEUTL_H__ +#define __GLIDEUTL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef FX_GLIDE_NO_FUNC_PROTO +/* +** rendering functions +*/ +FX_ENTRY void FX_CALL +guAADrawTriangleWithClip( const GrVertex *a, const GrVertex + *b, const GrVertex *c); + +FX_ENTRY void FX_CALL +guDrawTriangleWithClip( + const GrVertex *a, + const GrVertex *b, + const GrVertex *c + ); + +FX_ENTRY void FX_CALL +guDrawPolygonVertexListWithClip( int nverts, const GrVertex vlist[] ); + +/* +** hi-level rendering utility functions +*/ +FX_ENTRY void FX_CALL +guAlphaSource( GrAlphaSource_t mode ); + +FX_ENTRY void FX_CALL +guColorCombineFunction( GrColorCombineFnc_t fnc ); + +FX_ENTRY int FX_CALL +guEncodeRLE16( void *dst, + void *src, + FxU32 width, + FxU32 height ); + +FX_ENTRY FxU16 * FX_CALL +guTexCreateColorMipMap( void ); + +/* +** fog stuff +*/ +FX_ENTRY float FX_CALL +guFogTableIndexToW( int i ); + +FX_ENTRY void FX_CALL +guFogGenerateExp( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float density ); + +FX_ENTRY void FX_CALL +guFogGenerateExp2( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float density ); + +FX_ENTRY void FX_CALL +guFogGenerateLinear( + GrFog_t fogtable[GR_FOG_TABLE_SIZE], + float nearZ, float farZ ); + +/* +** endian stuff +*/ +FX_ENTRY FxU32 FX_CALL +guEndianSwapWords( FxU32 value ); + +FX_ENTRY FxU16 FX_CALL +guEndianSwapBytes( FxU16 value ); + +/* +** hi-level texture manipulation tools. +*/ +FX_ENTRY FxBool FX_CALL +gu3dfGetInfo( const char *filename, Gu3dfInfo *info ); + +FX_ENTRY FxBool FX_CALL +gu3dfLoad( const char *filename, Gu3dfInfo *data ); + +#endif /* FX_GLIDE_NO_FUNC_PROTO */ + +#ifdef __cplusplus +} +#endif + +#endif /* __GLIDEUTL_H__ */ + diff --git a/include/gradient.h b/include/gradient.h new file mode 100644 index 0000000..ee153da --- /dev/null +++ b/include/gradient.h @@ -0,0 +1,53 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Gradient.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Prototype for Gradient.cpp + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 6 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 5 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 4 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 3 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 2 11/18/96 4:34p Allender + * new 16 bit gradient functions + * + * 1 10/26/96 1:45p John + * Initial skeletion code. + * + * $NoKeywords: $ + */ + +#ifndef _GRADIENT_H +#define _GRADIENT_H + +extern void gr8_gradient(int x1,int y1,int x2,int y2); + +#endif + diff --git a/include/grd3d.h b/include/grd3d.h new file mode 100644 index 0000000..4c2f94c --- /dev/null +++ b/include/grd3d.h @@ -0,0 +1,60 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrD3D.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for our Direct3D renderer + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 9/13/99 11:25p Dave + * Fixed problem with mode-switching and D3D movies. + * + * 5 9/04/99 8:00p Dave + * Fixed up 1024 and 32 bit movie support. + * + * 4 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 3 1/15/99 11:29a Neilk + * Fixed D3D screen/texture pixel formatting problem. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 2 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 1 5/01/97 2:17p John + * + * $NoKeywords: $ + */ + +#ifndef _GRD3D_H +#define _GRD3D_H + +void gr_d3d_init(); +void gr_d3d_cleanup(); + +// call this to safely fill in the texture shift and scale values for the specified texture type (Gr_t_*) +void gr_d3d_get_tex_format(int alpha); + +// bitmap functions +void gr_d3d_bitmap(int x, int y); +void gr_d3d_bitmap_ex(int x, int y, int w, int h, int sx, int sy); + +// create all rendering objects (surfaces, d3d device, viewport, etc) +int gr_d3d_create_rendering_objects(int clear); +void gr_d3d_release_rendering_objects(); + + +void gr_d3d_set_initial_render_state(); + +#endif + diff --git a/include/grd3dinternal.h b/include/grd3dinternal.h new file mode 100644 index 0000000..964a648 --- /dev/null +++ b/include/grd3dinternal.h @@ -0,0 +1,214 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrD3DInternal.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Prototypes for the variables used internally by the Direct3D renderer + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 4 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 3 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 21 5/23/98 4:14p John + * Added code to preload textures to video card for AGP. Added in code + * to page in some bitmaps that weren't getting paged in at level start. + * + * 20 5/20/98 9:45p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 19 5/12/98 10:34a John + * Added d3d_shade functionality. Added d3d_flush function, since the + * shader seems to get reorganzed behind the overlay text stuff! + * + * 18 5/12/98 8:18a John + * Put in code to use a different texture format for alpha textures and + * normal textures. Turned off filtering for aabitmaps. Took out + * destblend=invsrccolor alpha mode that doesn't work on riva128. + * + * 17 5/11/98 10:19a John + * Added caps checking + * + * 16 5/07/98 3:02p John + * Mpre texture cleanup. You can now reinit d3d without a crash. + * + * 15 5/07/98 9:54a John + * Added in palette flash functionallity. + * + * 14 5/06/98 11:21p John + * Fixed a bitmap bug with Direct3D. Started adding new caching code into + * D3D. + * + * 13 5/06/98 8:41p John + * Fixed some font clipping bugs. Moved texture handle set code for d3d + * into the texture module. + * + * 12 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 11 5/05/98 10:37p John + * Added code to optionally use execute buffers. + * + * 10 5/04/98 3:36p John + * Got zbuffering working with Direct3D. + * + * 9 4/14/98 12:15p John + * Made 16-bpp movies work. + * + * 8 3/12/98 5:36p John + * Took out any unused shaders. Made shader code take rgbc instead of + * matrix and vector since noone used it like a matrix and it would have + * been impossible to do in hardware. Made Glide implement a basic + * shader for online help. + * + * 7 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 6 3/09/98 6:06p John + * Restructured font stuff to avoid duplicate code in Direct3D and Glide. + * Restructured Glide to avoid redundent state setting. + * + * 5 3/08/98 12:33p John + * Made d3d cleanup free textures. Made d3d always divide texture size by + * 2 for now. + * + * 4 3/07/98 8:29p John + * Put in some Direct3D features. Transparency on bitmaps. Made fonts & + * aabitmaps render nice. + * + * 3 2/17/98 7:28p John + * Got fonts and texturing working in Direct3D + * + * 2 2/07/98 7:50p John + * Added code so that we can use the old blending type of alphacolors if + * we want to. Made the stars use them. + * + * 1 2/03/98 9:24p John + * + * $NoKeywords: $ + */ + + +#ifndef _GRD3DINTERNAL_H +#define _GRD3DINTERNAL_H + +#include +#include + +#define D3D_OVERLOADS +#include "vddraw.h" + +// To remove an otherwise well-lodged compiler error +// 4201 nonstandard extension used : nameless struct/union (happens a lot in Windows include headers) +#pragma warning(disable: 4201) + +#include "vd3d.h" +#include "2d.h" +#include "grinternal.h" + + +extern LPDIRECTDRAW lpDD1; +extern LPDIRECTDRAW2 lpDD; +extern LPDIRECT3D2 lpD3D; +extern LPDIRECT3DDEVICE lpD3DDeviceEB; +extern LPDIRECT3DDEVICE2 lpD3DDevice; +extern LPDIRECTDRAWSURFACE lpBackBuffer; +extern LPDIRECTDRAWSURFACE lpFrontBuffer; +extern LPDIRECTDRAWSURFACE lpZBuffer; + +extern LPDIRECT3DVIEWPORT2 lpViewport; +extern LPDIRECTDRAWPALETTE lpPalette; + +extern DDPIXELFORMAT AlphaTextureFormat; +extern DDPIXELFORMAT NonAlphaTextureFormat; +extern DDPIXELFORMAT ScreenFormat; + +extern D3DDEVICEDESC D3DHWDevDesc, D3DHELDevDesc; +extern LPD3DDEVICEDESC lpDevDesc; +extern DDCAPS DD_driver_caps; +extern DDCAPS DD_hel_caps; + +extern int D3D_texture_divider; + +extern int D3D_32bit; + +extern char* d3d_error_string(HRESULT error); + +void d3d_tcache_init(int use_sections); +void d3d_tcache_cleanup(); +void d3d_tcache_flush(); +void d3d_tcache_frame(); + +// Flushes any pending operations +void d3d_flush(); + +int d3d_tcache_set(int bitmap_id, int bitmap_type, float *u_ratio, float *v_ratio, int fail_on_full=0, int sx = -1, int sy = -1, int force = 0); + +// Functions in GrD3DRender.cpp stuffed into gr_screen structure + +void gr_d3d_flash(int r, int g, int b); +void gr_d3d_zbuffer_clear(int mode); +int gr_d3d_zbuffer_get(); +int gr_d3d_zbuffer_set(int mode); +void gr_d3d_tmapper( int nverts, vertex **verts, uint flags ); +void gr_d3d_scaler(vertex *va, vertex *vb ); +void gr_d3d_aascaler(vertex *va, vertex *vb ); +void gr_d3d_pixel(int x, int y); +void gr_d3d_clear(); +void gr_d3d_set_clip(int x,int y,int w,int h); +void gr_d3d_reset_clip(); +void gr_d3d_init_color(color *c, int r, int g, int b); +void gr_d3d_init_alphacolor( color *clr, int r, int g, int b, int alpha, int type ); +void gr_d3d_set_color( int r, int g, int b ); +void gr_d3d_get_color( int * r, int * g, int * b ); +void gr_d3d_set_color_fast(color *dst); +void gr_d3d_set_bitmap( int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha, int sx=-1, int sy=-1 ); +void gr_d3d_bitmap_ex(int x,int y,int w,int h,int sx,int sy); +void gr_d3d_bitmap(int x, int y); +void gr_d3d_aabitmap_ex(int x,int y,int w,int h,int sx,int sy); +void gr_d3d_aabitmap(int x, int y); +void gr_d3d_rect(int x,int y,int w,int h); +void gr_d3d_create_shader(shader * shade, float r, float g, float b, float c ); +void gr_d3d_set_shader( shader * shade ); +void gr_d3d_shade(int x,int y,int w,int h); +void gr_d3d_create_font_bitmap(); +void gr_d3d_char(int x,int y,int letter); +void gr_d3d_string( int sx, int sy, char *s ); +void gr_d3d_circle( int xc, int yc, int d ); +void gr_d3d_line(int x1,int y1,int x2,int y2); +void gr_d3d_aaline(vertex *v1, vertex *v2); +void gr_d3d_gradient(int x1,int y1,int x2,int y2); +void gr_d3d_set_palette(ubyte *new_palette, int restrict_alphacolor); +void gr_d3d_diamond(int x, int y, int width, int height); +void gr_d3d_print_screen(char *filename); + + +// Functions used to render. Calls either DrawPrim or Execute buffer code +HRESULT d3d_SetRenderState( D3DRENDERSTATETYPE dwRenderStateType, DWORD dwRenderState ); +HRESULT d3d_DrawPrimitive( D3DPRIMITIVETYPE dptPrimitiveType, D3DVERTEXTYPE dvtVertexType, LPVOID lpvVertices, DWORD dwVertexCount, DWORD dwFlags ); + +#endif //_GRD3DINTERNAL_H + diff --git a/include/grdirectdraw.h b/include/grdirectdraw.h new file mode 100644 index 0000000..08c031f --- /dev/null +++ b/include/grdirectdraw.h @@ -0,0 +1,33 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrDirectDraw.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for software 8-bpp rendering using DirectDraw + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 1 3/25/98 8:07p John + * Split software renderer into Win32 and DirectX + * + * $NoKeywords: $ + */ + + +#ifndef _GRDIRECTDRAW_H +#define _GRDIRECTDRAW_H + +void gr_directdraw_init(); +void gr_directdraw_cleanup(); + +#endif //_GRDIRECTDRAW_H + diff --git a/include/grglide.h b/include/grglide.h new file mode 100644 index 0000000..4de6dae --- /dev/null +++ b/include/grglide.h @@ -0,0 +1,37 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrGlide.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for a 3DFX's Glide graphics lib + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 1 5/12/97 12:13p John + * + * $NoKeywords: $ + */ + +#ifndef _GRGLIDE_H +#define _GRGLIDE_H + +void gr_glide_init(); +void gr_glide_cleanup(); + +void gr_glide_bitmap(int x, int y); +void gr_glide_bitmap_ex(int x, int y, int w, int h, int sx, int sy); + +#endif + diff --git a/include/grglideinternal.h b/include/grglideinternal.h new file mode 100644 index 0000000..ab9af10 --- /dev/null +++ b/include/grglideinternal.h @@ -0,0 +1,58 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrGlideInternal.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Common include file for Glide modules + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 3 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 4 5/06/98 11:21p John + * Fixed a bitmap bug with Direct3D. Started adding new caching code into + * D3D. + * + * 3 4/09/98 4:38p John + * Made non-darkening and transparent textures work under Glide. Fixed + * bug with Jim's computer not drawing any bitmaps. + * + * 2 4/08/98 8:47a John + * Moved all texture caching into a new module + * + * 1 3/03/98 4:42p John + * Added in Leighton's code to do texture caching on Glide. + * + * $NoKeywords: $ + */ + +#ifndef _GRGLIDEINTERNAL_H +#define _GRGLIDEINTERNAL_H + +#include "grinternal.h" + +void glide_tcache_init(); +void glide_tcache_cleanup(); +void glide_tcache_flush(); +void glide_tcache_frame(); + +// Bitmap_type see TCACHE_ defines in GrInternal.h +int glide_tcache_set(int bitmap_id, int bitmap_type, float *u_ratio, float *v_ratio, int fail_on_full = 0, int sx = -1, int sy = -1, int force = 0); + +extern int Glide_textures_in; + +#endif //_GRGLIDEINTERNAL_H + diff --git a/include/grid.h b/include/grid.h new file mode 100644 index 0000000..447a9a0 --- /dev/null +++ b/include/grid.h @@ -0,0 +1,78 @@ +/* + * $Logfile: /Freespace2/code/FRED2/Grid.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Grid dialog box created by Mike. Probably will never be used again. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// CGrid dialog + +class CGrid : public CDialog +{ +private: + CView* m_pGView; + +// Construction +public: + CGrid(CWnd* pParent = NULL); // standard constructor + CGrid(CView* pView); + + BOOL Create(); + +// Dialog Data + //{{AFX_DATA(CGrid) + enum { IDD = IDD_GRID }; + UINT m_GridSize; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CGrid) + public: + virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL); + virtual BOOL DestroyWindow(); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CGrid) + afx_msg void OnGridXyPlane(); + afx_msg void OnGridXzPlane(); + afx_msg void OnGridYzPlane(); + afx_msg void OnClose(); + afx_msg void OnDestroy(); + afx_msg void OnKillFocus(CWnd* pNewWnd); + virtual BOOL OnInitDialog(); + afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/grinternal.h b/include/grinternal.h new file mode 100644 index 0000000..3a5db04 --- /dev/null +++ b/include/grinternal.h @@ -0,0 +1,210 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrInternal.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for our Graphics directory + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 7 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 6 1/15/99 11:29a Neilk + * Fixed D3D screen/texture pixel formatting problem. + * + * 5 1/14/99 12:48a Dave + * Todo list bug fixes. Made a pass at putting briefing icons back into + * FRED. Sort of works :( + * + * 4 12/02/98 5:47p Dave + * Put in interface xstr code. Converted barracks screen to new format. + * + * 3 12/01/98 10:32a Johnson + * Fixed direct3d font problems. Fixed sun bitmap problem. Fixed direct3d + * starfield problem. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 23 5/06/98 11:21p John + * Fixed a bitmap bug with Direct3D. Started adding new caching code into + * D3D. + * + * 22 4/08/98 4:09p John + * Fixed some potential bugs with high bit set in screen piointer. + * + * 21 3/25/98 8:07p John + * Restructured software rendering into two modules; One for windowed + * debug mode and one for DirectX fullscreen. + * + * 20 3/24/98 3:58p John + * Put in (hopefully) final gamma setting code. + * + * 19 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 18 2/07/98 7:50p John + * Added code so that we can use the old blending type of alphacolors if + * we want to. Made the stars use them. + * + * 17 1/26/98 5:12p John + * Added in code for Pentium Pro specific optimizations. Speed up + * zbuffered correct tmapper about 35%. Speed up non-zbuffered scalers + * by about 25%. + * + * 16 11/30/97 3:57p John + * Made fixed 32-bpp translucency. Made BmpMan always map translucent + * color into 255 even if you aren't supposed to remap and make it's + * palette black. + * + * 15 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 14 11/29/97 2:06p John + * added mode 16-bpp support + * + * 13 11/14/97 12:30p John + * Fixed some DirectX bugs. Moved the 8-16 xlat tables into Graphics + * libs. Made 16-bpp DirectX modes know what bitmap format they're in. + * + * 12 11/03/97 10:08p Hoffoss + * Changed gr_get_string_size to utilize an optional length specifier, if + * you want to use non-null terminated strings. + * + * 11 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 10 10/14/97 4:50p John + * more 16 bpp stuff. + * + * 9 10/14/97 8:08a John + * added a bunch more 16 bit support + * + * 8 7/18/97 12:40p John + * cached alphacolors to disk. Also made cfopen be able to delete a file + * by passing NULL for mode. + * + * 7 6/17/97 12:03p John + * Moved color/alphacolor functions into their own module. Made all color + * functions be part of the low-level graphics drivers, not just the + * grsoft. + * + * 6 6/12/97 5:04p John + * Initial rev of Glide support + * + * 5 6/11/97 5:49p John + * Changed palette code to only recalculate alphacolors when needed, not + * when palette changes. + * + * 4 6/06/97 4:41p John + * Fixed alpha colors to be smoothly integrated into gr_set_color_fast + * code. + * + * 3 6/06/97 2:40p John + * Made all the radar dim in/out + * + * 2 5/14/97 4:38p John + * Fixed print_screen bug. + * + * 1 5/12/97 12:13p John + * + * $NoKeywords: $ + */ + +#ifndef _GRINTERNAL_H +#define _GRINTERNAL_H + +#include "font.h" +#include "2d.h" +#include "grzbuffer.h" + +extern int Gr_cursor; + +#define GR_SCREEN_PTR(type,x,y) ((type *)(uint(gr_screen.offscreen_buffer) + uint(((x)+gr_screen.offset_x)*sizeof(type)) + uint(((y)+gr_screen.offset_y)*gr_screen.rowsize))) +#define GR_SCREEN_PTR_SIZE(bpp,x,y) ((uint)(uint(gr_screen.offscreen_buffer) + uint(((x)+gr_screen.offset_x)*(bpp)) + uint(((y)+gr_screen.offset_y)*gr_screen.rowsize))) + +extern ubyte Gr_original_palette[768]; // The palette +extern ubyte Gr_current_palette[768]; + +typedef struct alphacolor { + int used; + int r,g,b,alpha; + int type; // See AC_TYPE_??? define + color *clr; + /* + union { + ubyte lookup[16][256]; // For 8-bpp rendering modes + } table; + */ +} alphacolor; + +// for backwards fred aabitmap compatibility +typedef struct alphacolor_old { + int used; + int r,g,b,alpha; + int type; // See AC_TYPE_??? define + color *clr; + union { + ubyte lookup[16][256]; // For 8-bpp rendering modes + } table; +} alphacolor_old; + +extern alphacolor * Current_alphacolor; +void gr_init_alphacolors(); + +extern char Gr_current_palette_name[128]; + +typedef struct color_gun { + int bits; + int shift; + int scale; + int mask; +} color_gun; + +// screen format +extern color_gun Gr_red, Gr_green, Gr_blue, Gr_alpha; + +// texture format +extern color_gun Gr_t_red, Gr_t_green, Gr_t_blue, Gr_t_alpha; + +// alpha texture format +extern color_gun Gr_ta_red, Gr_ta_green, Gr_ta_blue, Gr_ta_alpha; + +// CURRENT FORMAT - note - this is what bmpman uses when fiddling with pixels/colors. so be sure its properly set to one +// of the above values +extern color_gun *Gr_current_red, *Gr_current_green, *Gr_current_blue, *Gr_current_alpha; + + +// Translate the 768 byte 'src' palette into +// the current screen format's palette. +// The size of the dst array is assumed to be gr_screen.bpp +// bytes per element. +void gr_xlat_palette( void *dst, bitmap *bmp ); + +// CPU identification variables +extern int Gr_cpu; // What type of CPU. 5=Pentium, 6=Ppro/PII +extern int Gr_mmx; // MMX capabilities? 0=No, 1=Yes + +extern float Gr_gamma; +extern int Gr_gamma_int; // int(Gr_gamma*100) +extern int Gr_gamma_lookup[256]; + +#define TCACHE_TYPE_AABITMAP 0 // HUD bitmap. All Alpha. +#define TCACHE_TYPE_NORMAL 1 // Normal bitmap. Alpha = 0. +#define TCACHE_TYPE_XPARENT 2 // Bitmap with 0,255,0 = transparent. Alpha=0 if transparent, 1 if not. +#define TCACHE_TYPE_NONDARKENING 3 // Bitmap with 255,255,255 = non-darkening. Alpha=1 if non-darkening, 0 if not. +#define TCACHE_TYPE_BITMAP_SECTION 4 // section of a bitmap + +#endif + diff --git a/include/gropengl.h b/include/gropengl.h new file mode 100644 index 0000000..b2666e6 --- /dev/null +++ b/include/gropengl.h @@ -0,0 +1,37 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrOpenGL.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for OpenGL renderer + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 1 5/12/97 12:14p John + * + * $NoKeywords: $ + */ + +#ifndef _GROPENGL_H +#define _GROPENGL_H + +void gr_opengl_init(); +void gr_opengl_cleanup(); + +void gr_opengl_bitmap(int x, int y); +void gr_opengl_bitmap_ex(int x, int y, int w, int h, int sx, int sy); + +#endif + diff --git a/include/grsoft.h b/include/grsoft.h new file mode 100644 index 0000000..5588625 --- /dev/null +++ b/include/grsoft.h @@ -0,0 +1,56 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrSoft.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for our software renderer + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 2 3/25/98 8:07p John + * Restructured software rendering into two modules; One for windowed + * debug mode and one for DirectX fullscreen. + * + * 1 5/12/97 12:14p John + * + * $NoKeywords: $ + */ + +#ifndef _GRSOFT_H +#define _GRSOFT_H + +void gr_soft_init(); +void gr_soft_cleanup(); + + +// Functions/variables common between grsoft and grdirectdraw +extern int Grx_mouse_saved; +void grx_save_mouse_area(int x, int y, int w, int h ); +void grx_restore_mouse_area(); +void grx_print_screen(char * filename); +int gr8_save_screen(); +void gr8_restore_screen(int id); +void gr8_free_screen(int id); +void gr8_dump_frame_start(int first_frame, int frames_between_dumps); +void gr8_dump_frame(); +void gr8_dump_frame_stop(); +void gr8_set_gamma(float gamma); + +// bitmap functions +void grx_bitmap(int x, int y); +void grx_bitmap_ex(int x, int y, int w, int h, int sx, int sy); + +#endif + diff --git a/include/grzbuffer.h b/include/grzbuffer.h new file mode 100644 index 0000000..a7e7f95 --- /dev/null +++ b/include/grzbuffer.h @@ -0,0 +1,48 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrZbuffer.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include for software render zbuffering + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 1 3/25/98 8:07p John + * Split software renderer into Win32 and DirectX + * + * $NoKeywords: $ + */ + +#ifndef _GRZBUFFER_H +#define _GRZBUFFER_H + +// Z-buffer stuff +extern uint *gr_zbuffer; +extern uint gr_zbuffer_offset; // Add this to pixel location to get zbuffer location +extern int gr_zoffset; // add this to w before interpolation + +extern int gr_zbuffering, gr_zbuffering_mode; +extern int gr_global_zbuffering; + +#define GR_Z_RANGE 0x400000 //(2^31)/GR_Z_COUNT +#define GR_Z_COUNT 500 // How many frames between zbuffer clear. + // The bigger, the less precise. + +// If mode is FALSE, turn zbuffer off the entire frame, +// no matter what people pass to gr_zbuffer_set. +void gr8_zbuffer_clear(int mode); +int gr8_zbuffer_get(); +int gr8_zbuffer_set(int mode); + + +#endif //_GRZBUFFER_H + diff --git a/include/helped.h b/include/helped.h new file mode 100644 index 0000000..0e7e6e7 --- /dev/null +++ b/include/helped.h @@ -0,0 +1,50 @@ +// HelpEd.h : main header file for the HELPED application +// + +#if !defined(AFX_HELPED_H__159085C4_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_) +#define AFX_HELPED_H__159085C4_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdApp: +// See HelpEd.cpp for the implementation of this class +// + +class CHelpEdApp : public CWinApp +{ +public: + CHelpEdApp(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CHelpEdApp) + public: + virtual BOOL InitInstance(); + //}}AFX_VIRTUAL + +// Implementation + //{{AFX_MSG(CHelpEdApp) + afx_msg void OnAppAbout(); + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_HELPED_H__159085C4_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_) + diff --git a/include/helpeddoc.h b/include/helpeddoc.h new file mode 100644 index 0000000..3c2ed74 --- /dev/null +++ b/include/helpeddoc.h @@ -0,0 +1,63 @@ +// HelpEdDoc.h : interface of the CHelpEdDoc class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_HELPEDDOC_H__159085CA_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_) +#define AFX_HELPEDDOC_H__159085CA_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "helpedline.h" + +class CHelpEdDoc : public CDocument +{ +protected: // create from serialization only + CHelpEdDoc(); + DECLARE_DYNCREATE(CHelpEdDoc) + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CHelpEdDoc) + public: + virtual BOOL OnNewDocument(); + virtual void Serialize(CArchive& ar); + //}}AFX_VIRTUAL + +// Implementation +public: + int get_line_count(); + HelpEdLine * AddLine(CPoint pointFrom, CPoint pointTo); + virtual ~CHelpEdDoc(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CHelpEdDoc) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +private: + CObArray line_array; +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_HELPEDDOC_H__159085CA_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_) + diff --git a/include/helpedline.h b/include/helpedline.h new file mode 100644 index 0000000..cca8862 --- /dev/null +++ b/include/helpedline.h @@ -0,0 +1,26 @@ +// HelpEdLine.h: interface for the HelpEdLine class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_HELPEDLINE_H__159085D7_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_) +#define AFX_HELPEDLINE_H__159085D7_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +class HelpEdLine : public CObject +{ +public: + void Draw(CDC *pDC); + HelpEdLine(CPoint point_from, CPoint point_to); + HelpEdLine(); + virtual ~HelpEdLine(); + +private: + CPoint line_end; + CPoint line_start; +}; + +#endif // !defined(AFX_HELPEDLINE_H__159085D7_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_) + diff --git a/include/helpedview.h b/include/helpedview.h new file mode 100644 index 0000000..ae0b732 --- /dev/null +++ b/include/helpedview.h @@ -0,0 +1,66 @@ +// HelpEdView.h : interface of the CHelpEdView class +// +///////////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_HELPEDVIEW_H__159085CC_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_) +#define AFX_HELPEDVIEW_H__159085CC_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + + +class CHelpEdView : public CView +{ +protected: // create from serialization only + CHelpEdView(); + DECLARE_DYNCREATE(CHelpEdView) + CPoint m_ptPrevPos; +// Attributes +public: + CHelpEdDoc* GetDocument(); + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CHelpEdView) + public: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + protected: + //}}AFX_VIRTUAL + +// Implementation +public: + void OnLButtonDown(UINT nFlags, CPoint point); + virtual ~CHelpEdView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CHelpEdView) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#ifndef _DEBUG // debug version in HelpEdView.cpp +inline CHelpEdDoc* CHelpEdView::GetDocument() + { return (CHelpEdDoc*)m_pDocument; } +#endif + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_HELPEDVIEW_H__159085CC_C1B5_11D2_9904_00A0CC39C0BE__INCLUDED_) + diff --git a/include/hud.h b/include/hud.h new file mode 100644 index 0000000..8e54781 --- /dev/null +++ b/include/hud.h @@ -0,0 +1,407 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUD.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for functions that contain HUD functions at a high level + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 9 8/09/99 3:14p Dave + * Make "launch" warning gauge draw in code. + * + * 8 8/01/99 12:39p Dave + * Added HUD contrast control key (for nebula). + * + * 7 7/24/99 1:54p Dave + * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert + * missions. + * + * 6 6/10/99 3:43p Dave + * Do a better job of syncing text colors to HUD gauges. + * + * 5 6/07/99 4:20p Andsager + * Add HUD color for tagged object. Apply to target and radar. + * + * 4 5/21/99 1:44p Andsager + * Add engine wash gauge + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 64 8/28/98 3:28p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 63 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 62 5/04/98 6:12p Lawrance + * Write generic function hud_end_string_at_first_hash_symbol(), to use in + * various spots on the HUD + * + * 61 4/30/98 6:04p Lawrance + * Make subspace gauge report "aborted" when ESC pressed while starting + * warp out. + * + * 60 4/23/98 1:49a Allender + * major rearm/repair fixes for multiplayer. Fixed respawning of AI ships + * to not respawn until 5 seconds after they die. Send escort information + * to ingame joiners + * + * 59 4/20/98 12:36a Mike + * Make team vs. team work when player is hostile. Several targeting + * problems. + * + * 58 4/13/98 12:50p Allender + * made rearm shortcut work more appropriately. Make countermeasure + * succeed work on clients in multiplayer + * + * 57 3/30/98 1:08a Lawrance + * Implement "blast" icon. Blink HUD icon when player ship is hit by a + * blast. + * + * 56 3/19/98 5:05p Dave + * Put in support for targeted multiplayer text and voice messaging (all, + * friendly, hostile, individual). + * + * 55 3/17/98 12:29a Dave + * Put in hud support for rtvoice. Several ui interface changes. + * + * 54 3/14/98 4:59p Lawrance + * Totally rework HUD wingman status gauge to work with 5 arbitrary wings + * + * 53 3/11/98 12:13a Lawrance + * Pop up weapon gauge when rearm time is showing + * + * 52 3/09/98 4:22p Lawrance + * Don't do certain HUD functions when the hud is disabled + * + * 51 3/07/98 6:27p Lawrance + * Add support for disabled hud. + * + * 50 3/06/98 5:10p Allender + * made time to: field in extended targetbox use support time to dock code + * for all docking shpis. Only display for waypoints and docking (not + * undocking). Small fixups to message menu -- not allowing depart when + * disabled. Depart is now by default ignored for all non-small ships + * + * 49 2/23/98 6:49p Lawrance + * Use gr_aabitmap_ex() instead of clipping regions + * + * 48 2/12/98 4:58p Lawrance + * Change to new flashing method. + * + * 47 2/11/98 9:44p Allender + * rearm repair code fixes. hud support view shows abort status. New + * support ship killed message. More network stats + * + * 46 2/09/98 8:05p Lawrance + * Add new gauges: cmeasure success, warp-out, and missiontime + * + * 45 1/28/98 7:19p Lawrance + * Get fading/highlighting animations working + * + * 44 1/24/98 3:21p Lawrance + * Add flashing when hit, and correct association with the wingman status + * gauge. + * + * 43 1/21/98 7:20p Lawrance + * Make subsystem locking only work with line-of-sight, cleaned up locking + * code, moved globals to player struct. + * + * 42 1/20/98 12:52p Lawrance + * Draw talking head as alpha-color bitmap, black out region behind + * animation. + * + * 41 1/19/98 10:01p Lawrance + * Implement "Electronics" missiles + * + * 40 1/15/98 5:23p Lawrance + * Add HUD gauge to indicate completed objectives. + * + * 39 1/14/98 11:07p Lawrance + * Hook in brightness slider to HUD config. + * + * 38 1/12/98 11:16p Lawrance + * Wonderful HUD config. + * + * 37 1/10/98 12:41a Lawrance + * start work on new HUD config + * + * 36 1/05/98 9:38p Lawrance + * Implement flashing HUD gauges. + * + * 35 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 34 12/18/97 8:46p Lawrance + * Move IFF_color definitions from HUD->ship, so FRED can use them. + * + * 33 12/01/97 12:27a Lawrance + * redo default alpha color for HUD, make it easy to modify in the future + * + * 32 11/17/97 6:37p Lawrance + * new gauges: extended target view, new lock triangles, support ship view + * + * 31 11/13/97 10:46p Lawrance + * implemented new escort view, damage view and weapons + * + * 30 11/13/97 6:15p Lawrance + * Add new weapons gauge + * + * 29 11/11/97 5:05p Lawrance + * use global value for target integrity, to avoid recalculation + * + * 28 11/11/97 11:06a Lawrance + * add function to convert a numbered string to use mono-spaced 1's + * + * 27 11/09/97 3:25p Lawrance + * increase default alpha color + * + * 26 11/05/97 11:19p Lawrance + * create an array of 16 HUD colors, that span the different alpha values + * + * 25 11/04/97 7:50p Lawrance + * supporting new HUD reticle and shield icons + * + * 24 10/11/97 6:38p Lawrance + * added functions to manage HUD animations + * + * 23 9/14/97 10:24p Lawrance + * add damage screen popup window + * + * 22 7/14/97 11:47a Lawrance + * add function to display hud messages (so navmap can call it) + * + * 21 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 20 6/06/97 4:41p John + * Fixed alpha colors to be smoothly integrated into gr_set_color_fast + * code. + * + * 19 6/05/97 6:47p John + * First pass at changing HUD translucency. + * + * 18 4/13/97 3:53p Lawrance + * separate out the non-rendering dependant portions of the HUD ( sounds, + * updating lock position, changing targets, etc) and put into + * hud_update_frame() + * + * 17 4/10/97 5:29p Lawrance + * hud rendering split up into hud_render_3d(), hud_render_2d() and + * hud_render_target_model() + * + * 16 4/07/97 3:50p Allender + * ability to assign > 1 ship to a hotkey. Enabled use of hotkeys in + * squadmate messaging + * + * 15 3/19/97 5:53p Lawrance + * integrating new Misc_sounds[] array (replaces old Game_sounds + * structure) + * + * 14 1/13/97 5:36p Lawrance + * integrating new Game_sounds structure for general game sounds + * + * 13 1/07/97 6:56p Lawrance + * adding sound hooks + * + * 12 1/02/97 7:12p Lawrance + * adding hooks for more sounds + * + * 11 11/26/96 2:35p John + * Made so you can change HUD colors. + * + * 10 11/19/96 10:16a Lawrance + * adding colors to header file + * + * 9 11/17/96 5:27p Lawrance + * added externs for globals that specify the HUD gauge colors + * + * 8 11/15/96 12:11a Lawrance + * took out old message scrolling and moved to HUDmessage + * + * $NoKeywords: $ + * +*/ + + +#ifndef __HUD_H__ +#define __HUD_H__ + +#include "pstypes.h" +#include "hudmessage.h" +#include "hudgauges.h" +#include "2d.h" + +#define SCREEN_CENTER_X ((gr_screen.clip_left + gr_screen.clip_right) / 2.0f) +#define SCREEN_CENTER_Y ((gr_screen.clip_top + gr_screen.clip_bottom) / 2.0f) + +struct object; + +typedef struct hud_anim +{ + char name[MAX_FILENAME_LEN]; + int first_frame; // the bitmap id for the first frame in the animation... note that + // all bitmap id's following this frame are numbered sequentially + int num_frames; // number of frames in the animation + int sx, sy; // screen (x,y) of top-left corner of animation + float total_time; // total time in seconds for the animation (depends on animation fps) + float time_elapsed; // time that has elapsed (in seconds) since animation started playing +} hud_anim; + +typedef struct hud_frames +{ + int first_frame; + int num_frames; +} hud_frames; + +typedef struct hud_frames_info +{ + char *name; + int x, y; +} hud_frames_info; + +#define HUD_NUM_COLOR_LEVELS 16 +extern color HUD_color_defaults[HUD_NUM_COLOR_LEVELS]; + +// extern globals that will control the color of the HUD gauges +#define HUD_COLOR_ALPHA_USER_MAX 13 // max user-settable alpha, absolute max is 15 +#define HUD_COLOR_ALPHA_USER_MIN 3 // min user-settable alpha, absolute min is 0 + +#define HUD_COLOR_ALPHA_MAX 15 +#define HUD_COLOR_ALPHA_DEFAULT 8 + +#define HUD_BRIGHT_DELTA 7 // Level added to HUD_color_alpha to make brightness used for flashing + +// hud macro for maybe flickering all gauges +#define GR_AABITMAP(a, b, c) { int jx, jy; if(emp_should_blit_gauge()) { gr_set_bitmap(a); jx = b; jy = c; emp_hud_jitter(&jx, &jy); gr_aabitmap(jx, jy); } } +#define GR_AABITMAP_EX(a, b, c, d, e, f, g) { int jx, jy; if(emp_should_blit_gauge()) { gr_set_bitmap(a); jx = b; jy = c; emp_hud_jitter(&jx, &jy); gr_aabitmap_ex(jx, jy, d, e, f, g); } } + +extern int HUD_color_red; +extern int HUD_color_green; +extern int HUD_color_blue; +extern int HUD_color_alpha; + +extern color HUD_color_debug; + +// animations for damages gauges +extern hud_anim Target_static; +extern hud_anim Radar_static; + +// Values used "wiggle" the HUD. In the 2D HUD case, the clip region accounts +// for these, but for the 3d-type hud stuff, you need to add these in manually. +extern float HUD_offset_x; +extern float HUD_offset_y; + +// Global: integrity of player's target +extern float Pl_target_integrity; + +void HUD_init_colors(); +void HUD_init(); +void hud_update_frame(); // updates hud systems not dependant on rendering +void HUD_render_3d(float frametime); // renders 3d dependant gauges +void HUD_render_2d(float frametime); // renders the 2d gauges +void hud_stop_looped_engine_sounds(); +void hud_show_messages(); +void hud_damage_popup_toggle(); + +// set the offset values for this render frame +void HUD_set_offsets(object *viewer_obj, int wiggedy_wack); + +// Basically like gr_reset_clip only it accounts for hud jittering +void HUD_reset_clip(); + +// Basically like gr_set_clip only it accounts for hud jittering +void HUD_set_clip(int x, int y, int w, int h); + +// do flashing text gauge +void hud_start_text_flash(char *txt, int t); + +// convert a string to use mono spaced numbers +void hud_num_make_mono(char *num_str); + +// cut any text off after (and including) '#' char +void hud_end_string_at_first_hash_symbol(char *src); + +// functions for handling hud animations +void hud_anim_init(hud_anim *ha, int sx, int sy, char *filename); +int hud_anim_render(hud_anim *ha, float frametime, int draw_alpha=0, int loop=1, int hold_last=0, int reverse=0); +int hud_anim_load(hud_anim *ha); +void hud_anim_release(hud_anim *ha); + +// flash text at the given y +void hud_show_text_flash_icon(char *txt, int y, int bright); + +// functions for displaying the support view popup +void hud_support_view_start(); +void hud_support_view_stop(int stop_now=0); +void hud_support_view_abort(); +void hud_support_view_blit(); + +void HUD_init_hud_color_array(); + +// setting HUD colors +void hud_set_default_color(); +void hud_set_iff_color(object *objp, int is_bright=0); +void hud_set_bright_color(); +void hud_set_dim_color(); + +// HUD gauge functions +#define HUD_C_NONE -4 +#define HUD_C_BRIGHT -3 +#define HUD_C_DIM -2 +#define HUD_C_NORMAL -1 +void hud_set_gauge_color(int gauge_index, int bright_index = HUD_C_NONE); +int hud_gauge_active(int gauge_index); +void hud_gauge_start_flash(int gauge_index); +int hud_gauge_maybe_flash(int gauge_index); + +// popup gauges +void hud_init_popup_timers(); +void hud_gauge_popup_start(int gauge_index, int time=4000); +int hud_gauge_is_popup(int gauge_index); +int hud_gauge_popup_active(int gauge_index); + +// objective status gauge +void hud_add_objective_messsage(int type, int status); + +int hud_team_matches_filter(int team_filter, int ship_team); +void hud_maybe_clear_head_area(); + +int hud_wing_index_from_ship(int shipnum); +int hud_wing_slot_from_name(char *name); +int hud_support_get_dock_time( int objnum ); +void hud_show_radar(); +void hud_show_target_model(); +void hud_show_voice_status(); + +void hud_subspace_notify_abort(); + +// render multiplayer text message currently being entered if any +void hud_maybe_render_multi_text(); + +int hud_disabled(); +int hud_support_find_closest( int objnum ); + +// contrast stuff +void hud_toggle_contrast(); +void hud_set_contrast(int high); + +// Return mask of enemies. +// Works in team vs. team multiplayer. +int opposing_team_mask(int team_mask); + +#endif /* __HUD_H__ */ + diff --git a/include/hudartillery.h b/include/hudartillery.h new file mode 100644 index 0000000..d01d10d --- /dev/null +++ b/include/hudartillery.h @@ -0,0 +1,45 @@ +/* + * $Logfile: /Freespace2/code/Hud/HudArtillery.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 1 4/20/99 12:00a Dave + * + * + * $NoKeywords: $ + */ + +#ifndef _FS2_HUD_ARTILLERY_HEADER_FILE +#define _FS2_HUD_ARTILLERY_HEADER_FILE + +// ----------------------------------------------------------------------------------------------------------------------- +// ARTILLERY DEFINES/VARS +// + + +// ----------------------------------------------------------------------------------------------------------------------- +// ARTILLERY FUNCTIONS +// + +// level init +void hud_init_artillery(); + +// update all hud artillery related stuff +void hud_artillery_update(); + +// render all hud artillery related stuff +void hud_artillery_render(); + +#endif + diff --git a/include/hudbrackets.h b/include/hudbrackets.h new file mode 100644 index 0000000..d43f52c --- /dev/null +++ b/include/hudbrackets.h @@ -0,0 +1,84 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDbrackets.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for drawing target brackets on the HUD + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 12/21/98 5:02p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 14 5/03/98 1:07a Mike + * Show + for ships attacking your target, whether hostile or friendly. + * + * 13 3/19/98 5:36p Lawrance + * Let subsystem brackets grow to screen size + * + * 12 3/02/98 11:32p Lawrance + * Allow asteroids about to impact ships to be bracketed + * + * 11 11/27/97 4:24p Lawrance + * change appearance of subsystem targeting brackets + * + * 10 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 9 4/09/97 3:30p Lawrance + * let target brackets grow to bracket ship entirely + * + * 8 4/08/97 1:28p Lawrance + * get brackets for targeting and messaging drawing right + * + * 7 4/08/97 9:58a Lawrance + * center bracket on target center. Give min and max dimensions to + * subsystem target brackets. + * + * 6 4/07/97 3:50p Allender + * ability to assign > 1 ship to a hotkey. Enabled use of hotkeys in + * squadmate messaging + * + * 5 3/27/97 5:44p Lawrance + * drawing dashed lines for sub-object targeting box that is not in line + * of sight + * + * 4 3/27/97 3:59p Lawrance + * made brackets draw even if center of target is offscreen + * + * 3 3/27/97 9:29a Lawrance + * If reach maximum bounding box size, use radius targeting box method + * + * 2 12/24/96 4:30p Lawrance + * Target bracket drawing code moved to separate files + * + * $NoKeywords: $ + */ + + +#ifndef HUD_BRACKETS +#define HUD_BRACKETS + +#include "2d.h" + +void hud_init_brackets(); +void draw_bounding_brackets(int x1, int y1, int x2, int y2, int w_correction, int h_correction, float distance=0.0f, int target_objnum=-1); +void draw_bounding_brackets_subobject(); +void draw_brackets_square(int x1, int y1, int x2, int y2); +void draw_brackets_diamond(int x1, int y1, int x2, int y2); +void draw_brackets_square_quick(int x1, int y1, int x2, int y2, int thick=0); +void draw_brackets_diamond_quick(int x1, int y1, int x2, int y2, int thick=0); +void draw_brackets_dashed_square_quick(int x1, int y1, int x2, int y2); +int hud_brackets_get_iff_color(int team); + +#endif + diff --git a/include/hudconfig.h b/include/hudconfig.h new file mode 100644 index 0000000..b293424 --- /dev/null +++ b/include/hudconfig.h @@ -0,0 +1,210 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDconfig.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for HUD configuration + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 8/02/99 9:55p Dave + * Hardcode a nice hud color config for the demo. + * + * 3 6/08/99 1:14a Dave + * Multi colored hud test. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 30 4/29/98 6:00p Dave + * Fixed chatbox font colors. Made observer offscreen indicators work. + * Numerous small UI fixes. Fix rank limitations for mp games. + * + * 29 4/16/98 4:07p Hoffoss + * Fixed bug with palette reseting when loading a new pilot. Also made + * barracks default to palette merging mode. + * + * 28 4/14/98 6:16p Lawrance + * Fix bug that was preventing non-green HUD color from getting set. + * + * 27 3/26/98 5:45p Lawrance + * Added new gauges to HUD config + * + * 26 3/12/98 5:45p Dave + * Put in new observer HUD. Made it possible for observers to join at the + * beginning of a game and follow it around as an observer full-time. + * + * 25 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 24 1/28/98 6:19p Dave + * Reduced standalone memory usage ~8 megs. Put in support for handling + * multiplayer submenu handling for endgame, etc. + * + * 23 1/18/98 5:09p Lawrance + * Added support for TEAM_TRAITOR + * + * 22 1/17/98 10:02p Lawrance + * Fix bug with damage popup that would sometimes display with . + * + * 21 1/14/98 11:07p Lawrance + * Hook in brightness slider to HUD config. + * + * 20 1/13/98 5:33p Lawrance + * Tweaking HUD config. + * + * 19 1/12/98 11:16p Lawrance + * Wonderful HUD config. + * + * 18 1/10/98 12:41a Lawrance + * start work on new HUD config + * + * 17 1/05/98 9:38p Lawrance + * Implement flashing HUD gauges. + * + * 16 12/16/97 9:13p Lawrance + * Integrate new gauges into HUD config. + * + * 15 11/06/97 5:01p Dave + * Finished reworking standalone multiplayer sequencing. Put in + * configurable observer-mode HUD. + * + * 14 11/06/97 9:53a Dave + * + * 13 11/03/97 5:38p Dave + * Cleaned up more multiplayer sequencing. Added OBJ_OBSERVER module/type. + * Restructured HUD_config structs/flags. + * + * 12 10/28/97 12:43a Lawrance + * fix bug that was not setting HUD config correctly when a new pilot was + * made + * + * 11 9/22/97 11:46p Lawrance + * make default radar range infinity + * + * 10 9/06/97 2:13p Mike + * Replace support for TEAM_NEUTRAL + * + * 9 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 8 5/26/97 5:49p Lawrance + * supporting max range on radar + * + * 7 3/26/97 8:56a Lawrance + * removing target and game debug flags from HUD config + * + * 6 3/19/97 5:53p Lawrance + * integrating new Misc_sounds[] array (replaces old Game_sounds + * structure) + * + * 5 1/28/97 5:33p Lawrance + * saving number of msg window lines in save game and player file + * + * 4 12/10/96 4:18p Lawrance + * took out In_hud_config and use game state instead + * + * 3 12/08/96 1:54a Lawrance + * integrating hud configuration + * + * 2 11/29/96 6:10p Lawrance + * HUD configuration working + * + * $NoKeywords: $ + * +*/ + +#ifndef _HUDCONFIG_H +#define _HUDCONFIG_H + +#include "hud.h" +#include "player.h" + +#define HUD_COLOR_GREEN 0 +#define HUD_COLOR_BLUE 1 +#define HUD_COLOR_AMBER 2 + +// specify the max distance that the radar should detect objects +// Index in Radar_ranges[] array to get values + +#define RR_MAX_RANGES 3 // keep up to date +#define RR_SHORT 0 +#define RR_LONG 1 +#define RR_INFINITY 2 +extern float Radar_ranges[RR_MAX_RANGES]; +extern char *Radar_range_text(int range_num); + +#define RP_SHOW_DEBRIS (1<<0) +#define RP_SHOW_FRIENDLY_MISSILES (1<<1) +#define RP_SHOW_HOSTILE_MISSILES (1<<2) + +#define RP_DEFAULT ( RP_SHOW_DEBRIS | RP_SHOW_FRIENDLY_MISSILES | RP_SHOW_HOSTILE_MISSILES ) + +extern int HUD_observer_default_flags; +extern int HUD_observer_default_flags2; +extern int HUD_default_popup_mask; +extern int HUD_default_popup_mask2; +extern int HUD_config_default_flags; +extern int HUD_config_default_flags2; + +typedef struct HUD_CONFIG_TYPE { + int show_flags; // whether to show gauge + int show_flags2; // whether to show gauge + int popup_flags; // whether gauge is popup + int popup_flags2; // whether gauge is popup + int num_msg_window_lines; + int rp_flags; // see RP_ flags above + int rp_dist; // one of RR_ #defines above + int is_observer; // 1 or 0, observer mode or not, respectively + int main_color; // the main color + + // colors for all the gauges + color clr[NUM_HUD_GAUGES]; +} HUD_CONFIG_TYPE; + +extern HUD_CONFIG_TYPE HUD_config; + +void hud_config_init(); +void hud_config_do_frame(float frametime); +void hud_config_close(); + +void hud_set_default_hud_config(player *p); +void hud_config_set_gauge_flags(int gauge_index, int on_flag, int popup_flag); + +void hud_config_restore(); +void hud_config_backup(); +void hud_config_as_observer(ship *shipp,ai_info *aif); + + +void hud_config_as_observer(); +void hud_config_as_player(); +void hud_config_display_text(char* gauge_text, int x, int y); +void hud_set_display_gauge_cbox(); + +// leave hud config without accepting changes +void hud_config_cancel(); + +// leave hud config with accepting changes +void hud_config_commit(); + +// flag access/manipulation routines +int hud_config_show_flag_is_set(int i);; +void hud_config_show_flag_set(int i); +void hud_config_show_flag_clear(int i); +int hud_config_popup_flag_is_set(int i); +void hud_config_popup_flag_set(int i); +void hud_config_popup_flag_clear(int i); + +void hud_config_record_color(int color); + +// load up the given hcf file +void hud_config_color_load(char *name); + +#endif + diff --git a/include/hudescort.h b/include/hudescort.h new file mode 100644 index 0000000..93491d5 --- /dev/null +++ b/include/hudescort.h @@ -0,0 +1,81 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDescort.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for managing and displaying ships that are in an escort + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 7/30/99 7:01p Dave + * Dogfight escort gauge. Fixed up laser rendering in Glide. + * + * 5 5/24/99 11:28a Dave + * Sexpression for adding/removing ships from the hud escort list. + * + * 4 3/04/99 9:22a Andsager + * Make escort list work with ship-is-visible. When not visible, dump, + * when becoming visible, maybe add. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 9 4/30/98 3:32p Lawrance + * Cull dead/departed ships from escort ship in hud_update_frame() + * + * 8 3/02/98 11:31p Lawrance + * create functions to access ships on escort list + * + * 7 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 6 1/20/98 4:45p Allender + * made HUD escorts which arrive late show up on list + * + * 5 11/24/97 10:20p Lawrance + * Add key 'KEY_N' to target next ship on monitoring view + * + * 4 11/18/97 5:58p Lawrance + * flash escort view info when that ship is taking hits + * + * 3 11/13/97 10:46p Lawrance + * implemented new escort view, damage view and weapons + * + * 2 8/19/97 11:46p Lawrance + * adding new hud gauges for shileds, escort view, and weapons + * + * 1 8/19/97 3:13p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __FREESPACE_HUDESCORT_VIEW_H__ +#define __FREESPACE_HUDESCORT_VIEW_H__ + +void hud_escort_init(); +void hud_setup_escort_list(int level = 1); +void hud_display_escort(); +void hud_escort_view_toggle(); +void hud_add_remove_ship_escort(int objnum, int supress_feedback = 0); +void hud_escort_clear_all(); +void hud_escort_ship_hit(object *objp, int quadrant); +void hud_escort_target_next(); +void hud_escort_cull_list(); +void hud_add_ship_to_escort(int objnum, int supress_feedback); +void hud_remove_ship_from_escort(int objnum); +int hud_escort_num_ships_on_list(); +int hud_escort_return_objnum(int index); +void hud_escort_add_player(short id); +void hud_escort_remove_player(short id); + +#endif /* __FREESPACE_HUDESCORT_VIEW_H__ */ + diff --git a/include/hudets.h b/include/hudets.h new file mode 100644 index 0000000..69b8f58 --- /dev/null +++ b/include/hudets.h @@ -0,0 +1,70 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDets.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file that supports code to manage and display the Energy Transfer System (ETS) + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 7 8/11/97 10:51a Lawrance + * fix problem that was not setting correct weapon energy when + * change_ship_type() was called + * + * 6 4/03/97 5:29p Mike + * + * 5 2/25/97 4:12p Lawrance + * using frametime to calculate energy recharge + * + * 4 1/01/97 7:34p Lawrance + * added cheat (Del+W) which keeps weapon energy at max levels. + * + * 3 12/24/96 4:31p Lawrance + * refining energy transfer system + * + * 2 12/22/96 3:41p Lawrance + * ETS system working + * + * $NoKeywords: $ + */ + + +#ifndef _HUD_ETS_H +#define _HUD_ETS_H + +#include "object.h" + +#define ETS_RECHARGE_RATE 4.0f // Recharge this percent of total shields/second + +extern float Energy_levels[]; +extern int Weapon_energy_cheat; + + +enum SYSTEM_TYPE {WEAPONS, SHIELDS, ENGINES}; + +void update_ets(object* obj, float fl_frametime); +void ets_init_ship(object* obj); +void ai_manage_ets(object* obj); + +void hud_init_ets(); +void hud_show_ets(); + +void increase_recharge_rate(object* obj, SYSTEM_TYPE enum_value); +void decrease_recharge_rate(object* obj, SYSTEM_TYPE enum_value); +void set_default_recharge_rates(object* obj); + +void transfer_energy_to_shields(object* obj); +void transfer_energy_to_weapons(object* obj); + + +#endif + diff --git a/include/hudgauges.h b/include/hudgauges.h new file mode 100644 index 0000000..b1ed6dd --- /dev/null +++ b/include/hudgauges.h @@ -0,0 +1,120 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDgauges.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * HUD data common to FRED and FreeSpace + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 7 8/19/99 9:20a Andsager + * Enable flashing for all guages + * + * 5 8/16/99 4:04p Dave + * Big honking checkin. + * + * 4 7/30/99 10:31p Dave + * Added comm menu to the configurable hud files. + * + * 3 7/24/99 1:54p Dave + * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert + * missions. + * + * 2 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 1 10/12/98 1:53p Dave + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 10 5/08/98 10:16a Lawrance + * Add new "ship attacking count" gauge + * + * 9 4/05/98 7:42p Lawrance + * Add kills gauge + * + * 8 3/26/98 5:45p Lawrance + * Added new gauges to HUD config + * + * 7 1/23/98 6:26p Lawrance + * Added wingman status gauge + * + * 6 1/17/98 1:30a Lawrance + * Add countermeasure gauge + * + * 5 1/12/98 11:16p Lawrance + * Wonderful HUD config. + * + * 4 1/05/98 9:38p Lawrance + * Implement flashing HUD gauges. + * + * 3 1/05/98 4:24p Allender + * added sexpression to flash a hud gauge -- a training only operator + * + * 2 1/05/98 3:18p Lawrance + * Common HUD data for FRED and FreeSpace + * + * 1 1/05/98 3:07p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __HUD_COMMON_H__ +#define __HUD_COMMON_H__ + +// HUD gauge types +#define NUM_HUD_GAUGES 39 + +#define HUD_LEAD_INDICATOR 0 +#define HUD_ORIENTATION_TEE 1 +#define HUD_HOSTILE_TRIANGLE 2 +#define HUD_TARGET_TRIANGLE 3 +#define HUD_MISSION_TIME 4 +#define HUD_RETICLE_CIRCLE 5 +#define HUD_THROTTLE_GAUGE 6 +#define HUD_RADAR 7 +#define HUD_TARGET_MONITOR 8 +#define HUD_CENTER_RETICLE 9 +#define HUD_TARGET_MONITOR_EXTRA_DATA 10 +#define HUD_TARGET_SHIELD_ICON 11 +#define HUD_PLAYER_SHIELD_ICON 12 +#define HUD_ETS_GAUGE 13 +#define HUD_AUTO_TARGET 14 +#define HUD_AUTO_SPEED 15 +#define HUD_WEAPONS_GAUGE 16 +#define HUD_ESCORT_VIEW 17 +#define HUD_DIRECTIVES_VIEW 18 +#define HUD_THREAT_GAUGE 19 +#define HUD_AFTERBURNER_ENERGY 20 +#define HUD_WEAPONS_ENERGY 21 +#define HUD_WEAPON_LINKING_GAUGE 22 +#define HUD_TARGET_MINI_ICON 23 +#define HUD_OFFSCREEN_INDICATOR 24 +#define HUD_TALKING_HEAD 25 +#define HUD_DAMAGE_GAUGE 26 +#define HUD_MESSAGE_LINES 27 +#define HUD_MISSILE_WARNING_ARROW 28 +#define HUD_CMEASURE_GAUGE 29 +#define HUD_OBJECTIVES_NOTIFY_GAUGE 30 +#define HUD_WINGMEN_STATUS 31 +#define HUD_OFFSCREEN_RANGE 32 +#define HUD_KILLS_GAUGE 33 +#define HUD_ATTACKING_TARGET_COUNT 34 +#define HUD_TEXT_FLASH 35 // (formerly split up among emp, collision, etc) +#define HUD_MESSAGE_BOX 36 +#define HUD_SUPPORT_GAUGE 37 +#define HUD_LAG_GAUGE 38 + +extern char *HUD_gauge_text[NUM_HUD_GAUGES]; // defined in sexp.cpp!!!! + + +#endif /* __HUD_COMMON_H__ */ + diff --git a/include/hudlock.h b/include/hudlock.h new file mode 100644 index 0000000..2b5dee3 --- /dev/null +++ b/include/hudlock.h @@ -0,0 +1,77 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDlock.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for missile locking code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 12 4/08/98 8:33p Lawrance + * Make player re-acquire lock when targeting subsystems on a locked ship. + * + * 11 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 10 2/28/98 7:03p Lawrance + * Change player missile locking to use dot product, so we can use it in + * the external views + * + * 9 1/23/98 6:25p Lawrance + * Change player missile locking to lock on subsystem points automatically + * + * 8 1/21/98 7:20p Lawrance + * Make subsystem locking only work with line-of-sight, cleaned up locking + * code, moved globals to player struct. + * + * 7 1/19/98 10:02p Lawrance + * Fix bug with locking on friendlies + * + * 6 11/17/97 6:37p Lawrance + * new gauges: extended target view, new lock triangles, support ship view + * + * 5 4/13/97 3:53p Lawrance + * separate out the non-rendering dependant portions of the HUD ( sounds, + * updating lock position, changing targets, etc) and put into + * hud_update_frame() + * + * 4 3/19/97 5:53p Lawrance + * integrating new Misc_sounds[] array (replaces old Game_sounds + * structure) + * + * 3 1/02/97 7:12p Lawrance + * adding hooks for more sounds + * + * 2 12/23/96 7:53p Lawrance + * missile locking working in new source files + * + * $NoKeywords: $ + */ + +#ifndef _HUDLOCK_H +#define _HUDLOCK_H + +void hud_init_missile_lock(); +void hud_draw_lock_triangles(int center_x, int center_y, float frametime); +void hud_calculate_lock_position(float frametime); +void hud_calculate_lock_start_pos(); +void hud_show_lock_indicator(float frametime); +void hud_update_lock_indicator(float frametime); +void hud_stop_looped_locking_sounds(); +void hud_lock_reset(float lock_time_scale=1.0f); + +#endif + diff --git a/include/hudmessage.h b/include/hudmessage.h new file mode 100644 index 0000000..f5021a8 --- /dev/null +++ b/include/hudmessage.h @@ -0,0 +1,196 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDmessage.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for functions that control and manage the message window on the HUD + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 8/23/99 11:11a Jefff + * increased MAX_HUD_LINE_LENGTH + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 25 4/14/98 5:06p Dave + * Don't load or send invalid pilot pics. Fixed chatbox graphic errors. + * Made chatbox display team icons in a team vs. team game. Fixed up pause + * and endgame sequencing issues. + * + * 24 4/05/98 3:30p Dave + * Print netplayer messages in brighter green on the hud, with + * accompanying sound. Echo netplayer messages on sending machine. Fixed + * standalone sequencing bug where host never get the "enter mission" + * button. + * + * 23 3/17/98 4:01p Hoffoss + * Added HUD_SOURCE_TERRAN_CMD and changed code to utilize it when a + * message is being sent from Terran Command. + * + * 22 3/12/98 4:03p Hoffoss + * Changed formatting used in hug scrollbacl log. + * + * 21 1/18/98 5:09p Lawrance + * Added support for TEAM_TRAITOR + * + * 20 12/03/97 11:35a Hoffoss + * Made changes to HUD messages send throughout the game. + * + * 19 12/02/97 5:57p Hoffoss + * Changed Hud messaging code to align text to right after sending ship's + * name. + * + * 18 11/13/97 10:16p Hoffoss + * Added icons to mission log scrollback. + * + * 17 11/13/97 4:05p Hoffoss + * Added hiding code for mission log entries. + * + * 16 11/13/97 1:13p Hoffoss + * Added HUD_SOURCE_HIDDEN to be used to hide items from the message + * scrollback log. + * + * 15 11/12/97 6:00p Hoffoss + * Added training messages to hud scrollback log. + * + * 14 11/05/97 7:11p Hoffoss + * Made changed to the hud message system. Hud messages can now have + * sources so they can be color coded. + * + * 13 11/03/97 10:12p Hoffoss + * Finished up work on the hud message/mission log scrollback screen. + * + * 12 10/25/97 4:02p Lawrance + * took out unused hud_message struct members + * + * 11 9/05/97 4:59p Lawrance + * changed MAX_HUD_LINE_LEN + * + * 10 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 9 6/23/97 12:03p Lawrance + * move split_str() to Parselo + * + * 8 6/17/97 12:25p Lawrance + * HUD message lines are split into multiple lines when they exceed + * display width + * + * 7 4/15/97 1:26p Lawrance + * using a static array of nodes to store hud scrollback messages, storage + * for text is dynamic + * + * 6 4/14/97 9:55a Mike + * Fixed HUD message system. + * Better game sequencing. + * + * 5 1/24/97 9:47a Lawrance + * made number of message lines in HUD message area confiurable + * + * 4 1/07/97 5:36p Lawrance + * Enabled save/restore for old/present/pending hud messages + * + * 3 11/27/96 3:20p Lawrance + * added scroll-back message code + * + * 2 11/15/96 12:11a Lawrance + * HUD message bar working + * + * $NoKeywords: $ + * +*/ + +#ifndef _HUDMESSAGE_H +#define _HUDMESSAGE_H + +#define SCROLL_BUFFER_LINES 128 // maximum number of HUD messages that can be stored +#define SCROLL_TIME 30 // time in milliseconds between scrolling a message +#define SCROLL_STEP_SIZE 3 +#define MAX_HUD_LINE_LEN 256 // maximum number of characters for a HUD message +#define MAX_ACTIVE_BUFFER_LINES 10 + +#define HUD_SOURCE_COMPUTER 0 +#define HUD_SOURCE_FRIENDLY 1 +#define HUD_SOURCE_HOSTILE 2 +#define HUD_SOURCE_NEUTRAL 3 +#define HUD_SOURCE_UNKNOWN 4 +#define HUD_SOURCE_TRAITOR 5 +#define HUD_SOURCE_TRAINING 6 +#define HUD_SOURCE_HIDDEN 7 +#define HUD_SOURCE_IMPORTANT 8 +#define HUD_SOURCE_FAILED 9 +#define HUD_SOURCE_SATISFIED 10 +#define HUD_SOURCE_TERRAN_CMD 11 +#define HUD_SOURCE_NETPLAYER 12 + +extern int ACTIVE_BUFFER_LINES; // user-preferred number of message buffer lines + +typedef struct { + char text[MAX_HUD_LINE_LEN]; + int source; // where this message came from so we can color code it + int time; // timestamp message was originally sent + int x; +} HUD_message_data; + +typedef struct line_node { + line_node* next; + line_node* prev; + int time; // timestamp when message was added + int source; // who/what the source of the message was (for color coding) + int x; + int y; + int underline_width; + char *text; +} line_node; + +extern line_node Msg_scrollback_used_list; + +typedef struct Hud_display_info { + HUD_message_data msg; + int y; // y Coordinate to draw message at + int target_y; + int total_life; // timestamp id to control how long a HUD message stays alive +} Hud_display_info; + +extern HUD_message_data HUD_pending[SCROLL_BUFFER_LINES]; +extern Hud_display_info HUD_active_msgs_list[MAX_ACTIVE_BUFFER_LINES]; +extern int Hud_list_start; // points to the next msg to be printed in the queue +extern int Hud_list_end; // points to the last msg in the queue +extern int Scroll_time_id; +extern int Active_index; +extern int Scroll_needed; +extern int Scroll_in_progress; + +extern int MSG_WINDOW_HEIGHT; // extern'ed since needed in save/restore code +extern int MSG_WINDOW_FONT_HEIGHT; // extern'ed since needed in save/restore code + +void hud_scrollback_init(); +void hud_scrollback_close(); +void hud_scrollback_do_frame(float frametime); +void hud_scrollback_exit(); + +void hud_init_msg_window(); +void hud_show_msg_window(); +void hud_show_fixed_text(); +int HUD_get_team_source(int team); +void HUD_printf(char *format, ...); +void hud_sourced_print(int source, char *msg); +void HUD_sourced_printf(int source, char *format, ...); // send hud message from specified source +void HUD_ship_sent_printf(int sh, char *format, ...); // send hud message from a specific ship +void HUD_fixed_printf(float duration, char *format, ...); // Display a single message for duration seconds. +void HUD_init_fixed_text(); // Clear all pending fixed text. + +void HUD_add_to_scrollback(char *text, int source); +void hud_add_line_to_scrollback(char *text, int source, int t, int x, int y, int w); +void hud_add_msg_to_scrollback(char *text, int source, int t); +void hud_free_scrollback_list(); + +#endif + diff --git a/include/hudobserver.h b/include/hudobserver.h new file mode 100644 index 0000000..80f4d0d --- /dev/null +++ b/include/hudobserver.h @@ -0,0 +1,32 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDObserver.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $NoKeywords: $ + * + */ + +#ifndef _HUD_OBSERVER_FILE +#define _HUD_OBSERVER_FILE + +#include "hud.h" + +// prototypes +struct ship; +struct ai_info; + +// use these to redirect Player_ship and Player_ai when switching into ai mode +extern ship Hud_obs_ship; +extern ai_info Hud_obs_ai; + +// initialize observer hud stuff +void hud_observer_init(ship *shipp,ai_info *aip); + +// render any specific observer stuff +void hud_render_observer(); + + +#endif + diff --git a/include/hudresource.h b/include/hudresource.h new file mode 100644 index 0000000..139597f --- /dev/null +++ b/include/hudresource.h @@ -0,0 +1,2 @@ + + diff --git a/include/hudreticle.h b/include/hudreticle.h new file mode 100644 index 0000000..1f8dd89 --- /dev/null +++ b/include/hudreticle.h @@ -0,0 +1,61 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDreticle.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for functions to draw and manage the reticle + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 12/21/98 5:02p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 5 4/08/98 10:34p Allender + * make threat indicators work in multiplayer. Fix socket problem (once + * and for all???) + * + * 4 11/04/97 7:49p Lawrance + * integrating new HUD reticle and shield icons + * + * 3 12/08/96 1:54a Lawrance + * integrating hud configuration + * + * 2 10/28/96 4:54p Lawrance + * moving #defines to header file since other files need to reference them + * + * 1 10/24/96 11:50a Lawrance + * + * $NoKeywords: $ + * +*/ + +#ifndef _HUDRETICLE_H +#define _HUDRETICLE_H + +#include "player.h" + +extern int Outer_circle_radius[GR_NUM_RESOLUTIONS]; +extern int Hud_reticle_center[GR_NUM_RESOLUTIONS][2]; + +void hud_init_reticle(); +void hud_update_reticle( player *pp ); +void hud_show_reticle(); + +void hud_draw_outer_reticle(); +void hud_draw_center_reticle(); +void hud_draw_throttle_gauge(); +void hud_draw_target_throttle_gauge(); + + + +#endif + diff --git a/include/hudshield.h b/include/hudshield.h new file mode 100644 index 0000000..2d47102 --- /dev/null +++ b/include/hudshield.h @@ -0,0 +1,84 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDshield.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the display and management of the HUD shield + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 8/27/99 10:36a Dave + * Impose a 2% penalty for hitting the shield balance key. + * + * 3 7/22/99 4:00p Dave + * Fixed beam weapon muzzle glow rendering. Externalized hud shield info. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 7 4/25/98 2:00p Dave + * Installed a bunch of multiplayer context help screens. Reworked ingame + * join ship select screen. Fix places where network timestamps get hosed. + * + * 6 2/12/98 4:58p Lawrance + * Change to new flashing method. + * + * 5 11/18/97 5:58p Lawrance + * flash escort view info when that ship is taking hits + * + * 4 11/08/97 11:08p Lawrance + * implement new "mini-shield" view that sits near bottom of reticle + * + * 3 11/04/97 7:49p Lawrance + * integrating new HUD reticle and shield icons + * + * 2 8/25/97 12:24a Lawrance + * implemented HUD shield management + * + * 1 8/24/97 10:31p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __FREESPACE_HUDSHIELD_H__ +#define __FREESPACE_HUDSHIELD_H__ + +#define SHIELD_HIT_DURATION 1400 // time a shield quadrant flashes after being hit +#define SHIELD_FLASH_INTERVAL 200 // time between shield quadrant flashes + +#define NUM_SHIELD_HIT_MEMBERS 5 +#define HULL_HIT_OFFSET 4 // used to access the members in shield_hit_info that pertain to the hull +typedef struct shield_hit_info +{ + int shield_hit_status; // bitfield, if offset for shield quadrant is set, that means shield is being hit + int shield_show_bright; // bitfield, if offset for shield quadrant is set, that means play bright frame + int shield_hit_timers[NUM_SHIELD_HIT_MEMBERS]; // timestamps that get set for SHIELD_FLASH_TIME when a quadrant is hit + int shield_hit_next_flash[NUM_SHIELD_HIT_MEMBERS]; +} shield_hit_info; + +extern ubyte Quadrant_xlate[4]; + +struct player; + +void hud_shield_game_init(); +void hud_shield_level_init(); +void hud_shield_show(object *objp); +void hud_shield_equalize(object *objp, player *pl); +void hud_augment_shield_quadrant(object *objp, int direction); +void hud_shield_assign_info(ship_info *sip, char *filename); +void hud_show_mini_ship_integrity(object *objp, int force_x = -1, int force_y = -1); +void hud_shield_show_mini(object *objp, int x_force = -1, int y_force = -1, int x_hull_offset = 0, int y_hull_offset = 0); +void hud_shield_hit_update(); +void hud_shield_quadrant_hit(object *objp, int quadrant); +void hud_shield_hit_reset(int player=0); + +void shield_info_reset(shield_hit_info *shi); + +#endif /* __FREESPACE_HUDSHIELDBOX_H__ */ + diff --git a/include/hudsquadmsg.h b/include/hudsquadmsg.h new file mode 100644 index 0000000..a9cf390 --- /dev/null +++ b/include/hudsquadmsg.h @@ -0,0 +1,213 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDsquadmsg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * header file for squadmate messaging + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 3/30/99 5:40p Dave + * Fixed reinforcements for TvT in multiplayer. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 26 9/11/98 2:05p Allender + * make reinforcements work correctly in multiplayer games. There still + * may be a team vs team issue that I haven't thought of yet :-( + * + * 25 5/19/98 12:19p Mike + * Cheat codes! + * + * 24 5/13/98 5:09p Allender + * Make send_command functions return whether message was sent or not + * + * 23 5/04/98 8:58a Allender + * new key saving routine + * + * 22 4/22/98 4:59p Allender + * new multiplayer dead popup. big changes to the comm menu system for + * team vs. team. Start of debriefing stuff for team vs. team Make form + * on my wing work with individual ships who have high priority orders + * + * 21 4/13/98 12:51p Allender + * made countermeasure succeed indicator work in multiplayer. Make rearm + * shortcut work more appropriately. + * + * 20 2/26/98 4:21p John + * Changed comm_orders around so I could externalize the strings + * + * 19 2/20/98 8:34p Lawrance + * added hud_squadmsg_reinforcements_available(), since this information + * is required outside of hudsquadmsg + * + * 18 2/11/98 9:44p Allender + * rearm repair code fixes. hud support view shows abort status. New + * support ship killed message. More network stats + * + * 17 11/20/97 12:08a Allender + * added 'all fighters' option at top level menu. cleaned up messaging to + * player a little bit + * + * 16 11/07/97 4:35p Dave + * Fixed multiplayer support ship bug. Put in player kill/assists client + * side stats update. + * + * 15 11/03/97 10:09a Allender + * fixed up graying out of comm menu options + * + * 14 10/31/97 4:59p Allender + * remove protect_ship from list of filtered orders + * + * 13 10/31/97 4:33p Allender + * fix bogus repair message. gray out messages based on players target + * + * 12 9/30/97 5:07p Dave + * Adapted messaging to correctly work with client-server situations in + * multiplayer. + * + * 11 9/23/97 4:34p Allender + * made squadmessaging use main keyboard reading loop instead of separate + * key_inkey() + * + * 10 9/18/97 5:21p Hoffoss + * Added column breaks for message list in sexp trees, and added code for + * orders sexp. + * + * 9 7/25/97 9:52a Allender + * added order dialog to Fred for designer to choose which orders for ship + * to accept/ignore. Code in ship_create to set up variables for Fred use + * instead of Freespace use (for destroying ships before mission starts) + * + * 8 6/16/97 2:58p Allender + * added ignore my target order to player's comm menu. Fixed a couple of + * problem when trying to message wings + * + * 7 4/30/97 2:32p Allender + * add ability to message enemies as debug tool + * + * 6 4/07/97 3:50p Allender + * ability to assign > 1 ship to a hotkey. Enabled use of hotkeys in + * squadmate messaging + * + * 5 4/05/97 3:46p Allender + * lots 'o messaging stuff. Make shortcut keys for squadmate messaging + * work. Make menus a little more dynamic + * + * 4 2/06/97 1:49p Allender + * more messaging stuff -- most menus printout out stuff -- nothing + * functional in terms of action + * + * 3 2/03/97 4:50p Allender + * saving/restoring of keys used for messaging mode + * + * 2 1/31/97 9:38a Allender + * checkpoint for sqaudmate messaging + * + * 1 1/24/97 2:18p Allender + * + * $NoKeywords: $ + */ + +#ifndef _HUD_SQUADMSG +#define _HUD_SQUADMSG + +#include "multi.h" + +// defines for messages that can be sent from the player. Defined at bitfields so that we can enable +// and disable messages on a message by message basis +#define ATTACK_TARGET_ITEM (1<<0) +#define DISABLE_TARGET_ITEM (1<<1) +#define DISARM_TARGET_ITEM (1<<2) +#define PROTECT_TARGET_ITEM (1<<3) +#define IGNORE_TARGET_ITEM (1<<4) +#define FORMATION_ITEM (1<<5) +#define COVER_ME_ITEM (1<<6) +#define ENGAGE_ENEMY_ITEM (1<<7) +#define CAPTURE_TARGET_ITEM (1<<8) + +// the next are for the support ship only +#define REARM_REPAIR_ME_ITEM (1<<9) +#define ABORT_REARM_REPAIR_ITEM (1<<10) +#define STAY_NEAR_ME_ITEM (1<<11) +#define STAY_NEAR_TARGET_ITEM (1<<12) +#define KEEP_SAFE_DIST_ITEM (1<<13) + +// next item for all ships again -- to try to preserve relative order within the message menu +#define DEPART_ITEM (1<<14) +#define DISABLE_SUBSYSTEM_ITEM (1<<15) + +#define MAX_SHIP_ORDERS 13 // Must sync correctly with Comm_orders array in HUDsquadmsg.cpp + +// following defines are the set of possible commands that can be given to a ship. A mission designer +// might not allow some messages + +#define FIGHTER_MESSAGES (ATTACK_TARGET_ITEM | DISABLE_TARGET_ITEM | DISARM_TARGET_ITEM | PROTECT_TARGET_ITEM | IGNORE_TARGET_ITEM | FORMATION_ITEM | COVER_ME_ITEM | ENGAGE_ENEMY_ITEM | DEPART_ITEM | DISABLE_SUBSYSTEM_ITEM) + +#define BOMBER_MESSAGES FIGHTER_MESSAGES // bombers can do the same things as fighters + +#define TRANSPORT_MESSAGES (ATTACK_TARGET_ITEM | CAPTURE_TARGET_ITEM | DEPART_ITEM ) +#define FREIGHTER_MESSAGES TRANSPORT_MESSAGES // freighters can do the same things as transports + +#define CRUISER_MESSAGES (ATTACK_TARGET_ITEM | DEPART_ITEM) + +#define CAPITAL_MESSAGES (DEPART_ITEM) // can't order capitals to do much!!!! + +#define SUPPORT_MESSAGES (REARM_REPAIR_ME_ITEM | ABORT_REARM_REPAIR_ITEM | STAY_NEAR_ME_ITEM | STAY_NEAR_TARGET_ITEM | KEEP_SAFE_DIST_ITEM | DEPART_ITEM ) + +// these messages require an active target. They are also the set of messages +// which cannot be given to a ship when the target is on the same team, or the target +// is not a ship. +#define ENEMY_TARGET_MESSAGES (ATTACK_TARGET_ITEM | DISABLE_TARGET_ITEM | DISARM_TARGET_ITEM | IGNORE_TARGET_ITEM | STAY_NEAR_TARGET_ITEM | CAPTURE_TARGET_ITEM | DISABLE_SUBSYSTEM_ITEM ) +#define FRIENDLY_TARGET_MESSAGES (PROTECT_TARGET_ITEM) + +#define TARGET_MESSAGES (ENEMY_TARGET_MESSAGES | FRIENDLY_TARGET_MESSAGES) + + +#define SQUADMSG_HISTORY_MAX 160 + +typedef struct { + int ship; // ship that received the order + int order; // order that the ship received (see defines above) + int target; // ship that is the target of the order +} squadmsg_history; + +extern int squadmsg_history_index; +extern squadmsg_history Squadmsg_history[SQUADMSG_HISTORY_MAX]; + +extern int Multi_squad_msg_local; +extern int Multi_squad_msg_targ; + +extern void hud_init_squadmsg(); +extern void hud_squadmsg_toggle(); // toggles the state of messaging mode +extern void hud_squadmsg_shortcut( int command ); // use of a shortcut key +extern int hud_squadmsg_hotkey_select( int k ); // a hotkey was hit -- maybe send a message to those ship(s) +extern void hud_squadmsg_save_keys( int do_scroll = 0 ); // saves into local area keys which need to be saved/restored when in messaging mode +extern int hud_squadmsg_do_frame(); +extern int hud_query_order_issued(char *name, char *order, char *target); +extern int hud_squadmsg_read_key( int k ); // called from high level keyboard code + +extern void hud_squadmsg_repair_rearm( int toggle_state, object *obj = NULL ); +extern void hud_squadmsg_repair_rearm_abort( int toggle_state, object *obj = NULL ); +extern void hud_squadmsg_rearm_shortcut(); + +extern int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message, int player_num = -1 ); +extern int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message, int player_num = -1 ); +extern void hud_squadmsg_send_to_all_fighters( int command, int player_num = -1 ); +extern void hud_squadmsg_call_reinforcement(int reinforcement_num, int player_num = -1); + +extern int hud_squadmsg_reinforcements_available(int team); + +//#ifndef NDEBUG +void hud_enemymsg_toggle(); // debug function to allow messaging of enemies +//#endif + +#endif + diff --git a/include/hudtarget.h b/include/hudtarget.h new file mode 100644 index 0000000..a2f7b6e --- /dev/null +++ b/include/hudtarget.h @@ -0,0 +1,409 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDtarget.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for HUD targeting functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 8 9/07/99 11:26p Andsager + * Fix "r" targeting key, making evaluate_ship_as_closest_target() and + * hud_target_live_turret() consider if turret is targeting player + * + * 7 8/24/99 2:55p Andsager + * Add new prioritized turret selection code. + * + * 6 7/09/99 12:00a Andsager + * Added target box with distance for remote detonate weapons + * + * 5 5/28/99 10:00a Andsager + * Make player hud target affected by Nebula range + * + * 4 2/26/99 6:01p Andsager + * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay + * + * 3 12/21/98 5:03p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 101 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 100 5/15/98 8:36p Lawrance + * Add 'target ship that last sent transmission' target key + * + * 99 5/04/98 6:12p Lawrance + * Write generic function hud_end_string_at_first_hash_symbol(), to use in + * various spots on the HUD + * + * 98 4/13/98 5:06p Lawrance + * truncate secondary weapons names at the '#' char + * + * 97 4/08/98 1:18a Lawrance + * Externalize function to find distance between two objects. + * + * 96 4/07/98 5:30p Lawrance + * Player can't send/receive messages when comm is destroyed. Garble + * messages when comm is damaged. + * + * 95 3/30/98 12:20a Lawrance + * remove CARGO_REVEAL time, it is now in ship_info + * + * 94 3/20/98 5:40p Lawrance + * Change targeting of uninspected cargo to select the next closest. + * + * 93 3/12/98 5:36p John + * Took out any unused shaders. Made shader code take rgbc instead of + * matrix and vector since noone used it like a matrix and it would have + * been impossible to do in hardware. Made Glide implement a basic + * shader for online help. + * + * 92 3/11/98 12:14a Lawrance + * Added hud_target_auto_target_next() + * + * 91 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 90 3/07/98 3:50p Lawrance + * Add lead indicator for asteroids + * + * 89 3/04/98 3:44p Lawrance + * Only flash docking text for a couple of seconds + * + * 88 3/02/98 11:32p Lawrance + * Allow asteroids about to impact ships to be bracketed + * + * 87 2/26/98 12:33a Lawrance + * Added back slew mode, lots of changes to external and chase views. + * + * 86 2/24/98 2:49p Lawrance + * Enable cheat key to disable 'hidden from sensors' targeting + * + * 85 2/06/98 4:33p Lawrance + * extern hud_get_best_primary_bank() + * + * 84 2/02/98 7:00p Lawrance + * Adding new targeting keys (bomb, uninspected cargo, new ship, live + * turrets). + * + * 83 1/29/98 11:53p Lawrance + * Change repair ship targeting to favor support ships that are headed for + * the player. + * + * 82 1/20/98 9:30p Lawrance + * Have lead indicator, offscreen indicator and distance be relative to + * targeted subsystem when possible. + * + * 81 1/17/98 1:30a Lawrance + * Add countermeasure gauge + * + * 80 1/10/98 12:42a Lawrance + * make cargo inspection more realistic + * + * 79 1/08/98 11:34p Lawrance + * change cargo reveal formula to max(150,1.5*radius) + * + * 78 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 77 12/16/97 9:13p Lawrance + * Integrate new gauges into HUD config. + * + * 76 12/09/97 8:12a Allender + * changes to hotkey stuff. Don't allow mission defined hotkeys to + * override user defined ones once the mission starts + * + * 75 12/04/97 10:23a Lawrance + * don't allow matching speed unless target speed is above + * MATCH_SPEED_THRESHOLD + * + * 74 12/01/97 5:12p Lawrance + * have minimial targeting gauges shown in chase and external view + * + * 73 12/01/97 12:27a Lawrance + * redo default alpha color for HUD, make it easy to modify in the future + * + * 72 11/27/97 4:23p Lawrance + * add subsys_in_view to the player struct, which indicates that the + * player has a line of sight to his targeted subsystem + * + * 71 11/20/97 5:40p Lawrance + * Make cargo revealing work so that anyone (including AI) on your team + * can reveal cargo for everyone else on the team. + * + * 70 11/20/97 1:09a Lawrance + * add support for 'target closest friendly repair ship' + * + * 69 11/11/97 12:58a Lawrance + * implement new target monitor view + * + * 68 10/30/97 12:33a Lawrance + * update to show AIM_STRAFE debug output + * + * 67 10/27/97 10:48p Lawrance + * get previous hostile/friendly targeting working.. simplify code a lot + * + * 66 10/22/97 5:53p Lawrance + * move out subsystem_in_sight function + * + * 65 10/12/97 3:44p Lawrance + * use target lists to cycle through closest hostile and friendly ships + * + * 64 10/11/97 6:38p Lawrance + * having damage affect targeting + * + * 63 10/10/97 6:15p Hoffoss + * Implemented a training objective list display. + * + * 62 10/08/97 5:07p Lawrance + * make sensors and communication subsystem damage affect player + * + * 61 9/22/97 4:55p Hoffoss + * Added a training message window display thingy. + * + * 60 8/31/97 6:40p Lawrance + * make auto-target always target fighters/bombers first + * + * 59 8/25/97 12:24a Lawrance + * implemented HUD shield management + * + * 58 8/19/97 11:46p Lawrance + * adding new hud gauges for shileds, escort view, and weapons + * + * 57 8/15/97 9:26a Lawrance + * split off target box code into HUDtargetbox.cpp + * + * 56 8/14/97 5:29p Lawrance + * restructure target monitor code... support missile view and red shading + * + * 55 8/12/97 5:51p Lawrance + * allow targeting of missiles + * + * 54 7/13/97 5:54p Lawrance + * fix bug with restore game and the keyed targets + * + * 53 7/01/97 11:53a Lawrance + * allow cycling through targets in the reticle + * + * 52 5/14/97 8:55a Lawrance + * fix autotargeting so it switches to a new target as soon as current + * target is dying + * + * 51 5/02/97 2:11p Lawrance + * added hud_prune_hotkeys() + * + * 50 4/28/97 2:18p Lawrance + * made hotkey_add_remove more generic + * + * 49 4/22/97 4:53p Lawrance + * allow auto-targeting to not play target fail sound when searching for + * the closest target + * + * 48 4/15/97 4:00p Mike + * Intermediate checkin caused by getting other files. Working on camera + * slewing system. + * + * 47 4/13/97 3:53p Lawrance + * separate out the non-rendering dependant portions of the HUD ( sounds, + * updating lock position, changing targets, etc) and put into + * hud_update_frame() + * + * 46 4/12/97 4:29p Lawrance + * get missle locking and offscreen indicator working properly with + * different sized screens + * + * 45 4/10/97 5:29p Lawrance + * hud rendering split up into hud_render_3d(), hud_render_2d() and + * hud_render_target_model() + * + * 44 4/08/97 10:55a Allender + * draw purple brackets on ship sending a message + * + * 43 4/07/97 3:50p Allender + * ability to assign > 1 ship to a hotkey. Enabled use of hotkeys in + * squadmate messaging + * + * 42 3/27/97 3:58p Lawrance + * modified target_closest() to allow targeting closest ship that is + * attacking a given objnum + * + * 41 3/27/97 9:29a Lawrance + * If reach maximum bounding box size, use radius targeting box method + * + * 40 3/26/97 6:43p Lawrance + * implemented new method for detecting target in reticle, that doesn't + * require center point of target to be in reticle + * + * 39 3/26/97 12:44p Lawrance + * modified targeting functions to take team into accout for Target + * Next/Prev/Closest + * + * 38 3/19/97 5:53p Lawrance + * integrating new Misc_sounds[] array (replaces old Game_sounds + * structure) + * + * 37 3/17/97 3:47p Mike + * Homing missile lock sound. + * More on AI ships firing missiles. + * + * 36 3/10/97 8:53a Lawrance + * using hud_stop_looped_locking_sounds() in place of + * hud_stop_looped_sounds() + * + * 35 1/02/97 10:32a Lawrance + * fixed some bugs related to stopping looped sounds when targets die and + * going to menus + * + * 34 12/24/96 4:31p Lawrance + * Target bracket drawing code moved to separate files + * + * 33 12/23/96 7:53p Lawrance + * took out missile lock code and moved to HUDlock.cpp and HUDlock.h + * + * 32 12/17/96 11:10a Lawrance + * added targeting of subsystem in reticle + * +*/ + +#ifndef _HUDTARGET_H +#define _HUDTARGET_H + +#include "object.h" +#include "ailocal.h" +#include "ship.h" +#include "2d.h" +#include "weapon.h" + +#define INCREASING 0 +#define DECREASING 1 +#define NO_CHANGE 2 + +#define MATCH_SPEED_THRESHOLD 0.1f // minimum speed target must be moving for match speed to apply +#define CARGO_RADIUS_DELTA 100 // distance added to radius required for cargo scanning +#define CAPITAL_CARGO_RADIUS_DELTA 250 // distance added to radius required for cargo scanning +#define CARGO_REVEAL_MIN_DIST 150 // minimum distance for reveal cargo (used if radius+CARGO_RADIUS_DELTA < CARGO_REVEAL_MIN_DIST) +#define CAP_CARGO_REVEAL_MIN_DIST 300 // minimum distance for reveal cargo (used if radius+CARGO_RADIUS_DELTA < CARGO_REVEAL_MIN_DIST) +#define CARGO_MIN_DOT_TO_REVEAL 0.95 // min dot to proceed to have cargo scanning take place + +// structure and defines used for hotkey targeting +#define MAX_HOTKEY_TARGET_ITEMS 50 // maximum number of ships that can be targeted on *all* keys +#define SELECTION_SET 0x5000 // variable used for drawing brackets. The bracketinng code uses + // TEAM_* values. I picked this value to be totally out of that + // range. Only used for drawing selection sets +#define MESSAGE_SENDER 0x5001 // variable used for drawing brackets around a message sender. + // See above comments for SELECTION_SET + +// defines used to tell how a particular hotkey was added +#define HOTKEY_USER_ADDED 1 +#define HOTKEY_MISSION_FILE_ADDED 2 + +typedef struct htarget_list { + struct htarget_list *next, *prev; // for linked lists + int how_added; // determines how this hotkey was added (mission default or player) + object *objp; // the actual object +} htarget_list; + +extern htarget_list htarget_free_list; +extern int Hud_target_w, Hud_target_h; + +extern shader Training_msg_glass; + +extern char *Ai_class_names[]; +extern char *Submode_text[]; +extern char *Strafe_submode_text[]; + +extern void hud_init_targeting_colors(); + +void hud_init_targeting(); +void hud_target_next(int team = -1); +void hud_target_prev(int team = -1); +int hud_target_closest(int team = -1, int attacked_objnum = -1, int play_fail_sound = TRUE, int filter = 0, int turret_attacking_target = 0); +void hud_target_in_reticle_old(); +void hud_target_in_reticle_new(); +void hud_target_subsystem_in_reticle(); +void hud_show_targeting_gauges(float frametime, int in_cockpit=1); +void hud_target_targets_target(); +void hud_check_reticle_list(); +void hud_target_closest_locked_missile(object *A); +void hud_target_missile(object *source_obj, int next_flag); +void hud_target_next_list(int hostile=1, int next_flag=1); +int hud_target_closest_repair_ship(int goal_objnum=-1); +void hud_target_auto_target_next(); +void hud_show_remote_detonate_missile(); + +void hud_target_uninspected_object(int next_flag); +void hud_target_newest_ship(); +void hud_target_live_turret(int next_flag, int auto_advance=0, int turret_attacking_target=0); + +void hud_target_last_transmit_level_init(); +void hud_target_last_transmit(); +void hud_target_last_transmit_add(int ship_num); + +void hud_target_random_ship(); + +void hud_target_next_subobject(); +void hud_target_prev_subobject(); +void hud_cease_subsystem_targeting(int print_message=1); +void hud_cease_targeting(); +void hud_restore_subsystem_target(ship* shipp); +int subsystem_in_sight(object* objp, ship_subsys* subsys, vector *eye, vector* subsystem); +vector* get_subsystem_world_pos(object* parent_obj, ship_subsys* subsys, vector* world_pos); +void hud_target_change_check(); + +void hud_show_target_triangle_indicator(vertex *projected_v); +void hud_show_lead_indicator(vector *target_world_pos); +void hud_show_orientation_tee(); +void hud_show_hostile_triangle(); +void hud_show_target_data(); +void hud_show_afterburner_gauge(); +void hud_show_weapons(); +void hud_start_flash_weapon(int index); +void hud_show_auto_icons(); +void hud_show_weapon_energy_gauge(); +void hud_show_cmeasure_gague(); +void hud_show_brackets(object *targetp, vertex *projected_v); +void hud_draw_offscreen_indicator(vertex* target_point, vector *tpos, float distance=0.0f); +void hud_show_homing_missiles(void); + +int hud_sensors_ok(ship *sp, int show_msg = 1); +int hud_communications_state(ship *sp, int show_msg = 0); + +int hud_get_best_primary_bank(float *range); +void hud_target_toggle_hidden_from_sensors(); +void hud_maybe_flash_docking_text(object *objp); +int hud_target_invalid_awacs(object *objp); + +// functions for hotkey selection sets + +extern void hud_target_hotkey_select( int k ); +extern void hud_target_hotkey_clear( int k ); + +extern void hud_target_hotkey_add_remove( int k, object *objp, int how_to_add); +extern void hud_show_selection_set(); +extern void hud_show_message_sender(); +void hud_prune_hotkeys(); +void hud_keyed_targets_clear(); + +// Code to draw filled triangles +void hud_tri(float x1,float y1,float x2,float y2,float x3,float y3); +// Code to draw empty triangles. +void hud_tri_empty(float x1,float y1,float x2,float y2,float x3,float y3); + +float hud_find_target_distance( object *targetee, object *targeter ); + +#endif + diff --git a/include/hudtargetbox.h b/include/hudtargetbox.h new file mode 100644 index 0000000..3bba5ea --- /dev/null +++ b/include/hudtargetbox.h @@ -0,0 +1,165 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDtargetbox.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for drawing the target monitor box on the HUD + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 5/21/99 1:44p Andsager + * Add engine wash gauge + * + * 3 12/21/98 5:03p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 26 8/28/98 3:28p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 25 5/08/98 5:32p Lawrance + * Allow cargo scanning even if target gauge is disabled + * + * 24 3/30/98 1:08a Lawrance + * Implement "blast" icon. Blink HUD icon when player ship is hit by a + * blast. + * + * 23 2/16/98 8:47p Lawrance + * Allow flashing to occur at twice normal rate + * + * 22 2/12/98 11:54p Lawrance + * abbreviate communication to comm + * + * 21 2/12/98 4:58p Lawrance + * Add support for 'All Clear' radio message + * + * 20 2/09/98 8:05p Lawrance + * Add new gauges: cmeasure success, warp-out, and missiontime + * + * 19 1/15/98 5:23p Lawrance + * Add HUD gauge to indicate completed objectives. + * + * 18 1/12/98 10:57p Allender + * minor changes to the message box. made some indicators flash to bring + * attention to them. + * + * 17 1/12/98 9:44p Lawrance + * ug, fix return type error + * + * 16 1/12/98 9:40p Lawrance + * extern hud_targetbox_maybe_flash() + * + * 15 1/12/98 9:39p Lawrance + * flash DOCKED_WITH text + * + * 14 1/10/98 12:42a Lawrance + * make cargo inspection more realistic + * + * 13 12/16/97 9:13p Lawrance + * Integrate new gauges into HUD config. + * + * 12 12/09/97 6:15p Lawrance + * add flashing of destroyed subsystems on target box + * + * 11 12/01/97 12:27a Lawrance + * redo default alpha color for HUD, make it easy to modify in the future + * + * 10 11/19/97 10:19p Lawrance + * add target status to targetbox + * + * 9 11/17/97 6:37p Lawrance + * new gauges: extended target view, new lock triangles, support ship view + * + * 8 11/11/97 10:27p Lawrance + * show docking information on the target monitor + * + * 7 11/11/97 5:06p Lawrance + * flash different areas of the target box + * + * 6 11/11/97 12:58a Lawrance + * implement new target monitor view + * + * 5 11/06/97 5:01p Dave + * Finished reworking standalone multiplayer sequencing. Put in + * configurable observer-mode HUD. + * + * 4 10/11/97 6:38p Lawrance + * having damage affect targeting + * + * 3 8/19/97 11:46p Lawrance + * adding new hud gauges for shileds, escort view, and weapons + * + * 2 8/15/97 9:26a Lawrance + * split off target box code into HUDtargetbox.cpp + * + * 1 8/15/97 8:54a Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __FREESPACE_HUDTARGETBOX_H__ +#define __FREESPACE_HUDTARGETBOX_H__ + +#define TBOX_FLASH_DURATION 1400 +#define TBOX_FLASH_INTERVAL 200 + +#define NUM_TBOX_FLASH_TIMERS 14 +#define TBOX_FLASH_NAME 0 +#define TBOX_FLASH_CARGO 1 +#define TBOX_FLASH_HULL 2 +#define TBOX_FLASH_STATUS 3 +#define TBOX_FLASH_SUBSYS 4 +#define TBOX_FLASH_DOCKED 5 +#define TBOX_FLASH_SQUADMSG 6 +#define TBOX_FLASH_OBJECTIVE 7 +#define TBOX_FLASH_COLLISION 8 +#define TBOX_FLASH_CMEASURE 9 +#define TBOX_FLASH_NETLAG 10 +#define TBOX_FLASH_BLAST 11 +#define TBOX_FLASH_EMP 12 +#define TBOX_FLASH_ENGINE_WASH 13 + +extern int Target_static_looping; + +extern int Target_window_coords[GR_NUM_RESOLUTIONS][4]; + +// flag to indicate whether to show the extra information about a target +// The HUD_config controls whether this can be shown... but the player can still toggle it on/off +// during the game. +extern int Targetbox_show_extra_info; + +void hud_targetbox_init(); +void hud_targetbox_init_flash(); +void hud_render_target_model(); +void hud_show_target_data(float frametime); +void hud_get_target_strength(object *objp, float *shields, float *integrity); + +// used to flash text, uses the TBOX_FLASH_ #defines above +void hud_targetbox_start_flash(int index, int duration=TBOX_FLASH_DURATION); +int hud_targetbox_maybe_flash(int index, int flash_fast=0); +void hud_targetbox_end_flash(int index); +int hud_targetbox_is_bright(int index); +int hud_targetbox_flash_expired(int index); + +// functions to manage the targetbox static that appears when sensors are severely damaged +void hud_targetbox_static_init(); +int hud_targetbox_static_maybe_blit(float frametime); + +void hud_render_target_ship(object *target_objp); +void hud_render_target_debris(object *target_objp); +void hud_render_target_weapon(object *target_objp); + +void hud_update_cargo_scan_sound(); +void hud_cargo_scan_update(object *targetp, float frametime); + +char *hud_targetbox_truncate_subsys_name(char *outstr); + +#endif /* __FREESPACE_HUDTARGETBOX_H__ */ diff --git a/include/hudwingmanstatus.h b/include/hudwingmanstatus.h new file mode 100644 index 0000000..e0b40f2 --- /dev/null +++ b/include/hudwingmanstatus.h @@ -0,0 +1,51 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDWingmanStatus.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header for the wingman status gauge + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 4 3/31/98 11:46p Lawrance + * Fix several bugs related to departing ships. + * + * 3 3/22/98 11:13p Allender + * work with respawning -- save parse object so ship can be correctly + * restored. Restore wingman status information so gauge updates + * correctly + * + * 2 3/14/98 4:59p Lawrance + * Totally rework HUD wingman status gauge to work with 5 arbitrary wings + * + * 1 3/14/98 8:23a Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __HUDWINGMAN_STATUS_H__ +#define __HUDWINGMAN_STATUS_H__ + +void hud_init_wingman_status_gauge(); +void hud_wingman_status_update(); +void hud_wingman_status_render(); +void hud_wingman_status_init_flash(); +int hud_wingman_status_maybe_flash(int wing_index, int wing_pos); +void hud_set_wingman_status_dead(int wing_index, int wing_pos); +void hud_set_wingman_status_departed(int wing_index, int wing_pos); +void hud_set_wingman_status_alive( int wing_index, int wing_pos); +void hud_set_wingman_status_none( int wing_index, int wing_pos); +void hud_wingman_status_start_flash(int wing_index, int wing_pos); +void hud_wingman_status_set_index(int shipnum); + +#endif + diff --git a/include/ia3d.h b/include/ia3d.h new file mode 100644 index 0000000..e40633a --- /dev/null +++ b/include/ia3d.h @@ -0,0 +1,250 @@ +/*--------------------------------------------------------------------- + * + * ia3d.h + * + *--------------------------------------------------------------------- + * + * $Id$ + * + *--------------------------------------------------------------------- + * + * ia3d header file. It's the part the outside world needs to see. + * + *--------------------------------------------------------------------- + * + * AUREAL SEMICONDUCTOR, INC. PROPRIETARY AND CONFIDENTIAL + * Copyright (c) 1996 Aureal Semiconductor, Inc. - All rights + * reserved. + * + *--------------------------------------------------------------------- + */ + + +#ifndef _IA3D_H_ +#define _IA3D_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +// A3d Class ID! {D8F1EEE0-F634-11cf-8700-00A0245D918B} +DEFINE_GUID(CLSID_A3d, +0xd8f1eee0, 0xf634, 0x11cf, 0x87, 0x0, 0x0, 0xa0, 0x24, 0x5d, 0x91, 0x8b); + +// A3d Interface ID! {D8F1EEE1-F634-11cf-8700-00A0245D918B} +DEFINE_GUID(IID_IA3d, +0xd8f1eee1, 0xf634, 0x11cf, 0x87, 0x0, 0x0, 0xa0, 0x24, 0x5d, 0x91, 0x8b); + +// GUID generated from UUGENID.EXE, Good until the year 3400 AD +// A3d Interface ID! {fb80d1e0-98d3-11d1-90fb-006008a1f441} +DEFINE_GUID(IID_IA3d2, +0xfb80d1e0, 0x98d3, 0x11d1, 0x90, 0xfb, 0x00, 0x60, 0x08, 0xa1, 0xf4, 0x41); + +// Bits for manipulating output modes + +// Values for bOutputMode +#define OUTPUT_MODE_STEREO 0x00000001 +#define OUTPUT_MODE_QUAD 0x00000002 + +// Values for FrontXtalkMode and bRearXtalkMode +#define OUTPUT_HEADPHONES 0x00000001 // headphones +#define OUTPUT_SPEAKERS_WIDE 0x00000002 +#define OUTPUT_SPEAKERS_NARROW 0x00000003 + +// Values for Resource Management Mode +#define A3D_RESOURCE_MODE_OFF 0x00000000 +#define A3D_RESOURCE_MODE_NOTIFY 0x00000001 +#define A3D_RESOURCE_MODE_DYNAMIC 0x00000002 +#define A3D_RESOURCE_MODE_DYNAMIC_LOOPERS 0x00000003 +#define A3D_RESOURCE_MODE_LAST 0x00000003 + + +// Version Definitions for A3DCAPS + +#define A3D_CURRENT_VERSION IA3DVERSION_RELEASE12 + +#define IA3DVERSION_RELEASE10 10 +#define IA3DVERSION_RELEASE12 12 + +// A3d Caps structure for A3d2 interface +// If Fail to get IA3d2 interface, version of DLL is IA3DVERSION_PRE12 + +typedef struct __A3DCAPS_SOFTWARE +{ + DWORD dwSize; // Use for internal version control + DWORD dwVersion; // For Backwards capablities purposes + DWORD dwFlags; + DWORD dwReserved; + DWORD dwReserved2; +} A3DCAPS_SOFTWARE, *LPA3DCAPS_SOFTWARE; + +typedef struct __A3DCAPS_HARDWARE +{ + DWORD dwSize; // Use for internal version control + DWORD dwFlags; + DWORD dwReserved; + DWORD dwReserved2; +} A3DCAPS_HARDWARE, *LPA3DCAPS_HARDWARE; + + +// Declare the IA3d Interface. It's not very complex at all. + +#undef INTERFACE +#define INTERFACE IA3d + +typedef struct IA3d *LPIA3D; + +DECLARE_INTERFACE_(IA3d, IUnknown) +{ + // IUnknown + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3d + STDMETHOD(SetOutputMode)(THIS_ DWORD dwFrontXtalkMode, DWORD dwBackXtalkMode, DWORD dwQuadMode) PURE; + STDMETHOD(GetOutputMode)(THIS_ DWORD *lpdwFrontXtalkMode, DWORD *lpdwBackXtalkMode, DWORD *lpdwQuadMode) PURE; + + STDMETHOD(SetResourceManagerMode) (THIS_ DWORD ) PURE; + STDMETHOD(GetResourceManagerMode) (THIS_ DWORD *) PURE; + + STDMETHOD(SetHFAbsorbFactor)(THIS_ FLOAT ) PURE; + STDMETHOD(GetHFAbsorbFactor)(THIS_ FLOAT *) PURE; + +}; + + +// IA3d2 interface derived from IA3d + +#undef INTERFACE +#define INTERFACE IA3d2 + +typedef struct IA3d2 *LPIA3D2; + +DECLARE_INTERFACE_(IA3d2, IUnknown) +{ + // IUnknown + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IA3d + STDMETHOD(SetOutputMode)(THIS_ DWORD dwFrontXtalkMode, DWORD dwBackXtalkMode, DWORD dwQuadMode) PURE; + STDMETHOD(GetOutputMode)(THIS_ DWORD *lpdwFrontXtalkMode, DWORD *lpdwBackXtalkMode, DWORD *lpdwQuadMode) PURE; + + STDMETHOD(SetResourceManagerMode) (THIS_ DWORD ) PURE; + STDMETHOD(GetResourceManagerMode) (THIS_ DWORD *) PURE; + + STDMETHOD(SetHFAbsorbFactor)(THIS_ FLOAT ) PURE; + STDMETHOD(GetHFAbsorbFactor)(THIS_ FLOAT *) PURE; + + // New Methods inaddition to old ones + STDMETHOD(RegisterVersion) (THIS_ DWORD dwVersionNr) PURE; + STDMETHOD(GetSoftwareCaps) (THIS_ LPA3DCAPS_SOFTWARE lpCaps) PURE; + STDMETHOD(GetHardwareCaps) (THIS_ LPA3DCAPS_HARDWARE lpCaps) PURE; +}; + + +// The library function that gets things going. It returns an interface +// pointer to DirectSound. + +#define A3D_OK 1 // A3dCreate returns this upon detection of A3D Dll. +#define A3D_OK_OLD_DLL 2 // A3dCreate returns this upon detection of A3D Dll but user's version is older than expected. + +// Usefull Macros for C folks. + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IA3d_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3d_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3d_Release(p) (p)->lpVtbl->Release(p) +#define IA3d_SetOutputMode(p,a,b,c) (p)->lpVtbl->SetOutputMode(p,a,b,c) +#define IA3d_GetOutputMode(p,a,b,c) (p)->lpVtbl->GetOutputMode(p,a,b,c) +#define IA3d_SetResourceManagerMode(p,a) (p)->lpVtbl->SetResourceManagerMode(p,a) +#define IA3d_GetResourceManagerMode(p,a) (p)->lpVtbl->GetResourceManagerMode(p,a) +#define IA3d_SetHFAbsorbFactor(p,a) (p)->lpVtbl->SetHFAbsorbFactor(p,a) +#define IA3d_GetHFAbsorbFactor(p,a) (p)->lpVtbl->GetHFAbsorbFactor(p,a) + +// C function def +#define IA3d2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IA3d2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IA3d2_Release(p) (p)->lpVtbl->Release(p) +#define IA3d2_SetOutputMode(p,a,b,c) (p)->lpVtbl->SetOutputMode(p,a,b,c) +#define IA3d2_GetOutputMode(p,a,b,c) (p)->lpVtbl->GetOutputMode(p,a,b,c) +#define IA3d2_SetResourceManagerMode(p,a) (p)->lpVtbl->SetResourceManagerMode(p,a) +#define IA3d2_GetResourceManagerMode(p,a) (p)->lpVtbl->GetResourceManagerMode(p,a) +#define IA3d2_SetHFAbsorbFactor(p,a) (p)->lpVtbl->SetHFAbsorbFactor(p,a) +#define IA3d2_GetHFAbsorbFactor(p,a) (p)->lpVtbl->GetHFAbsorbFactor(p,a) +#define IA3d2_RegisterVersion(p,a) (p)->lpVtbl->RegisterVersion(p,a) +#define IA3d2_GetSoftwareCaps(p,a) (p)->lpVtbl->GetSoftwareCaps(p,a) +#define IA3d2_GetHardwareCaps(p,a) (p)->lpVtbl->GetHardwareCaps(p,a) + +#else +#define IA3d_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3d_AddRef(p) (p)->AddRef() +#define IA3d_Release(p) (p)->Release() +#define IA3d_SetOutputMode(p,a,b,c) (p)->SetOutputMode(a,b,c) +#define IA3d_GetOutputMode(p,a,b,c) (p)->GetOutputMode(a,b,c) +#define IA3d_SetResourceManagerMode(p,a) (p)->SetResourceManagerMode(a) +#define IA3d_GetResourceManagerMode(p,a) (p)->GetResourceManagerMode(a) +#define IA3d_SetHFAbsorbFactor(p,a) (p)->SetHFAbsorbFactor(a) +#define IA3d_GetHFAbsorbFactor(p,a) (p)->GetHFAbsorbFactor(a) + +// C++ function def +#define IA3d2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IA3d2_AddRef(p) (p)->AddRef() +#define IA3d2_Release(p) (p)->Release() +#define IA3d2_SetOutputMode(p,a,b,c) (p)->SetOutputMode(a,b,c) +#define IA3d2_GetOutputMode(p,a,b,c) (p)->GetOutputMode(a,b,c) +#define IA3d2_SetResourceManagerMode(p,a) (p)->SetResourceManagerMode(a) +#define IA3d2_GetResourceManagerMode(p,a) (p)->GetResourceManagerMode(a) +#define IA3d2_SetHFAbsorbFactor(p,a) (p)->SetHFAbsorbFactor(a) +#define IA3d2_GetHFAbsorbFactor(p,a) (p)->GetHFAbsorbFactor(a) +#define IA3d2_RegisterVersion(p,a) (p)->RegisterVersion(a) +#define IA3d2_GetSoftwareCaps(p,a) (p)->GetSoftwareCaps(a) +#define IA3d2_GetHardwareCaps(p,a) (p)->GetHardwareCaps(a) + +#endif + +// Convenience Macro A3D_REGISTER_VERSION() +// Register version for backwards compatibility +// Pass in any COM object from CLSID_A3d + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define A3D_UNKNOWN_QI(pU,I,ppI) (SUCCEEDED((pU)->lpVtbl->QueryInterface(pU,&I,ppI))) +#else +#define A3D_UNKNOWN_QI(pU,I,ppI) (SUCCEEDED((pU)->QueryInterface(I,ppI))) +#endif + +#define A3D_REGISTER_VERSION(p) \ +{ \ + LPIA3D2 __pIA3d2__=NULL; \ + IUnknown *__pU__ = (IUnknown *)p; \ + \ + if (p) \ + { \ + if (A3D_UNKNOWN_QI(__pU__,IID_IA3d2,(void **)&__pIA3d2__))\ + { \ + IA3d2_RegisterVersion(__pIA3d2__,A3D_CURRENT_VERSION); \ + IA3d2_Release(__pIA3d2__); \ + } \ + } \ + \ +} + +// Helper functions in ia3d.lib + +extern HRESULT WINAPI A3dInitialize(void); + +extern void WINAPI A3dUninitialize(void); + +extern HRESULT WINAPI A3dCreate(GUID * lpGUID, /* in, Prefered Driver Guid, NULL ok */ + void **ppDS, /* out, Direct Sound pointer */ + IUnknown FAR *pUnkOuter); /* in, Outer COM object, for Aggregate only NULL oK */ + +#ifdef __cplusplus +}; +#endif + +#endif // _IA3D_H_ + diff --git a/include/ignoreordersdlg.h b/include/ignoreordersdlg.h new file mode 100644 index 0000000..c3f2e29 --- /dev/null +++ b/include/ignoreordersdlg.h @@ -0,0 +1,78 @@ +/* + * $Logfile: /Freespace2/code/FRED2/IgnoreOrdersDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * header for dialog to set which orders from the player that a particular ship should ignore + * + */ + +#include "ship.h" + +// we won't have more than 9 checkboxes per dialog +#define MAX_CHECKBOXES 10 + +///////////////////////////////////////////////////////////////////////////// +// ignore_orders_dlg dialog + +typedef struct check_box_info { + CButton *button; + int id; +} check_box_info; + +class ignore_orders_dlg : public CDialog +{ +// Construction +public: + int m_ship; + ignore_orders_dlg(CWnd* pParent = NULL); // standard constructor + + ship *m_shipp; + check_box_info check_boxes[MAX_CHECKBOXES]; + int m_num_checks_active; + +// Dialog Data + //{{AFX_DATA(ignore_orders_dlg) + enum { IDD = IDD_IGNORE_ORDERS }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ignore_orders_dlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(ignore_orders_dlg) + virtual BOOL OnInitDialog(); + virtual void OnOK(); + afx_msg void OnCheck1(); + afx_msg void OnCheck2(); + afx_msg void OnCheck3(); + afx_msg void OnCheck4(); + afx_msg void OnCheck5(); + afx_msg void OnCheck6(); + afx_msg void OnCheck7(); + afx_msg void OnCheck8(); + afx_msg void OnCheck9(); + afx_msg void OnCheck10(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +typedef struct fred_comm_order { + int value; + char *menu_text; +} fred_comm_order; + +fred_comm_order Fred_comm_orders[]; + +extern int Fred_comm_orders_max; + diff --git a/include/inetgetfile.h b/include/inetgetfile.h new file mode 100644 index 0000000..7e81b2f --- /dev/null +++ b/include/inetgetfile.h @@ -0,0 +1,117 @@ +/* +* $Logfile: /Freespace2/code/Inetfile/inetgetfile.h $ +* $Revision$ +* $Date$ +* $Author$ +* +* InternetGetFile Class header +* +* $Log$ +* Revision 1.1 2002/05/03 03:28:12 root +* Initial revision +* + * + * 2 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 1 4/20/99 4:37p Dave + * + * Initial version +* +* $NoKeywords: $ +*/ +#ifndef _INET_GETFILE_HEADER_ +#define _INET_GETFILE_HEADER_ + +//At the end of this file is an example of usage + +#include "cftp.h" +#include "chttpget.h" + +#define INET_ERROR_NO_ERROR 0 +#define INET_ERROR_BADPARMS 1 +#define INET_ERROR_CANT_WRITE_FILE 2 +#define INET_ERROR_CANT_PARSE_URL 3 +#define INET_ERROR_BAD_FILE_OR_DIR 4 +#define INET_ERROR_HOST_NOT_FOUND 5 +#define INET_ERROR_UNKNOWN_ERROR 6 +#define INET_ERROR_NO_MEMORY 7 + +class InetGetFile +{ +public: + InetGetFile(char *URL,char *localfile); + ~InetGetFile(); + BOOL IsFileReceived(); + BOOL IsFileError(); + BOOL IsConnecting(); + BOOL IsReceiving(); + int GetErrorCode(); + int GetBytesIn(); + int GetTotalBytes(); + void AbortGet(); + +protected: + CFtpGet *ftp; + ChttpGet *http; + BOOL m_bUseHTTP; + int m_ErrorCode; + int m_State; + int m_HardError; + +}; + +#endif + +/* + +#include +#include +#include +#include + +#include "inetgetfile.h" + +int main(int argc,char **argv) +{ + unsigned int LastPrintbytes = time(NULL); + InetGetFile *inetfile; + WSADATA ws_data; + WORD ver=MAKEWORD(1,1); + + int error=WSAStartup(ver,&ws_data); + inetfile = new InetGetFile("http://www.volition-inc.com/images/download/freespace/fsdemo1x-12u.exe","e:\\fsdemo1x-12u.exe"); + do + { + if(inetfile->IsFileReceived()) + { + printf("File received\n"); + break; + } + if(inetfile->IsFileError()) + { + printf("File not received. Error code: %d\n",inetfile->GetErrorCode()); + break; + } + if(time(NULL)-LastPrintbytes>=1) + { + int ipct = 0; + if(inetfile->GetTotalBytes()) + { + ipct = 100*(float)((float)inetfile->GetBytesIn()/(float)inetfile->GetTotalBytes()); + } + printf("Received %d Bytes out of %d (%d%%).\n",inetfile->GetBytesIn(),inetfile->GetTotalBytes(),ipct); + LastPrintbytes = time(NULL); + } + + + }while(!kbhit()); + return 0; + +} + + + + */ + diff --git a/include/initialships.h b/include/initialships.h new file mode 100644 index 0000000..09e0ad4 --- /dev/null +++ b/include/initialships.h @@ -0,0 +1,45 @@ +// InitialShips.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// InitialShips dialog + +#define INITIAL_SHIPS 1 +#define INITIAL_WEAPONS 2 + +#define MAX_INITIAL_CHECKBOXES 30 + +class InitialShips : public CDialog +{ +// Construction +public: + int m_initial_items; + InitialShips(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(InitialShips) + enum { IDD = IDD_INITIAL_SHIPS }; + CCheckListBox m_initial_list; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(InitialShips) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(InitialShips) + virtual BOOL OnInitDialog(); + virtual void OnOK(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +private: + int m_list_count; +}; + diff --git a/include/initialstatus.h b/include/initialstatus.h new file mode 100644 index 0000000..4bad530 --- /dev/null +++ b/include/initialstatus.h @@ -0,0 +1,70 @@ +// InitialStatus.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// initial_status dialog + +class initial_status : public CDialog +{ +// Construction +public: + void undock(int obj); + void OnOK(); + int inited; + void change_subsys(); + initial_status(CWnd* pParent = NULL); // standard constructor + void initialize_docker_points(); + void initialize_dockee_points(); + + int m_ship; + int m_docked_with; + int m_multi_edit; + +// Dialog Data + //{{AFX_DATA(initial_status) + enum { IDD = IDD_INITIAL_STATUS }; + CSpinButtonCtrl m_hull_spin; + CSpinButtonCtrl m_velocity_spin; + CSpinButtonCtrl m_shields_spin; + CSpinButtonCtrl m_damage_spin; + int m_damage; + int m_docked; + int m_shields; + int m_velocity; + int m_hull; + int m_dockee_point; + int m_docker_point; + BOOL m_has_shields; + int m_locked; + CString m_cargo_name; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(initial_status) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(initial_status) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangeSubsys(); + afx_msg void OnSelchangeDocked(); + afx_msg void OnSelchangeDockerPoint(); + afx_msg void OnHasShields(); + afx_msg void OnLocked(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: + void reset_arrival_to_false( int shipnum ); + int cur_subsys; + int m_docker_index; + int m_dockee_index; +}; + diff --git a/include/joy.h b/include/joy.h new file mode 100644 index 0000000..f27229c --- /dev/null +++ b/include/joy.h @@ -0,0 +1,113 @@ +/* + * $Logfile: /Freespace2/code/Io/Joy.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for joystick stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 20 5/13/98 1:17a Hoffoss + * Added joystick axes configurability. + * + * 19 5/05/98 8:38p Hoffoss + * Added sensitivity adjustment to options menu and made it save to pilot + * file. + * + * 18 5/04/98 11:08p Hoffoss + * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp. + * Updated references everywhere to it. + * + * 17 4/29/98 12:13a Lawrance + * Add function for reading down count without resetting internal count. + * Add hook to reset demo trailer timer. + * + * 16 3/21/98 11:29a John + * Made joy_flush work when a button is held down + * + * 15 3/06/98 10:02a Hoffoss + * Made dead zone adjustable, and defaulted it to 10% instead of 5%. + * + * 14 1/29/98 11:04a Sandeep + * + * 13 1/27/98 8:40p Sandeep + * + * 12 1/08/98 6:35p Hoffoss + * Fixed joystick undefined detection. + * + * 11 9/15/97 11:42p Lawrance + * change button_info to joy_button_info to avoid name conflict + * + * 10 8/07/97 11:26p Lawrance + * add support for 4th axis (rudder) + * + * 9 7/29/97 5:30p Lawrance + * move gettime() from keyboard module to timer module + * + * 8 7/10/97 12:29a Lawrance + * fix problem with NT not recognizing an axis that was set under 95 + * + * 7 7/09/97 11:41p Lawrance + * added throttle and hat support + * + * 6 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef __JOY_H__ +#define __JOY_H__ + +#define JOY_NUM_BUTTONS 32 +#define JOY_NUM_HAT_POS 4 +#define JOY_TOTAL_BUTTONS (JOY_NUM_BUTTONS + JOY_NUM_HAT_POS) +#define JOY_NUM_AXES 6 + +#define JOY_HATBACK (JOY_NUM_BUTTONS) +#define JOY_HATFORWARD (JOY_NUM_BUTTONS+1) +#define JOY_HATLEFT (JOY_NUM_BUTTONS+2) +#define JOY_HATRIGHT (JOY_NUM_BUTTONS+3) + +#define JOY_AXIS_UNDEFINED -10000 + +typedef struct Joy_info { + int axis_valid[JOY_NUM_AXES]; + int axis_min[JOY_NUM_AXES]; + int axis_center[JOY_NUM_AXES]; + int axis_max[JOY_NUM_AXES]; +} Joy_info; + +extern int Joy_sensitivity; +extern int Dead_zone_size; // percentage of range that is dead zone + +int joy_init(); +void joy_flush(); +int joy_get_pos(int * x, int * y, int *z, int *r); +int joy_down_count(int btn, int reset_count = 1); +int joy_down(int btn); +int joy_up_count(int btn); +float joy_down_time(int btn); +void joy_get_cal_vals(int *axis_min, int *axis_center, int *axis_max); +void joy_set_cal_vals(int *axis_min, int *axis_center, int *axis_max); +void joy_set_ul(); +void joy_set_lr(); +void joy_set_cen(); +void joy_cheap_cal(); +int joystick_read_raw_axis( int num_axes, int * axis ); +void joy_get_delta(int *dx, int *dy); +int joy_get_scaled_reading(int raw, int axn); +int joy_get_unscaled_reading(int raw, int axn); + +#endif /* __JOY_H__ */ + diff --git a/include/joy_ff.h b/include/joy_ff.h new file mode 100644 index 0000000..dab7f9b --- /dev/null +++ b/include/joy_ff.h @@ -0,0 +1,56 @@ +/* + * $Logfile: /Freespace2/code/Io/Joy_ff.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for joystick Force Feedback. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 4 5/08/98 5:31p Hoffoss + * Isolated the joystick force feedback code more from dependence on other + * libraries. + * + * 3 5/07/98 12:24a Hoffoss + * Finished up sidewinder force feedback support. + * + * 2 5/04/98 11:08p Hoffoss + * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp. + * Updated references everywhere to it. + * + * $NoKeywords: $ + */ + +#ifndef __JOY_FF_H__ +#define __JOY_FF_H__ + +int joy_ff_init(); +void joy_ff_shutdown(); +void joy_ff_stop_effects(); +void joy_ff_mission_init(vector v); +void joy_reacquire_ff(); +void joy_unacquire_ff(); +void joy_ff_play_vector_effect(vector *v, float scaler); +void joy_ff_play_dir_effect(float x, float y); +void joy_ff_play_primary_shoot(int gain); +void joy_ff_play_secondary_shoot(int gain); +void joy_ff_adjust_handling(int speed); +void joy_ff_docked(); +void joy_ff_play_reload_effect(); +void joy_ff_afterburn_on(); +void joy_ff_afterburn_off(); +void joy_ff_explode(); +void joy_ff_fly_by(int mag); +void joy_ff_deathroll(); + +#endif + diff --git a/include/jumpnode.h b/include/jumpnode.h new file mode 100644 index 0000000..3632ba0 --- /dev/null +++ b/include/jumpnode.h @@ -0,0 +1,48 @@ +/* + * $Logfile: /Freespace2/code/JumpNode/JumpNode.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header for everything to do with jump nodes + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 2 3/21/98 7:36p Lawrance + * Move jump nodes to own lib. + * + * 1 3/21/98 3:53p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __JUMPNODE_H__ +#define __JUMPNODE_H__ + +#include "parselo.h" + +#define MAX_JUMP_NODES 3 + +typedef struct { + int modelnum; + int objnum; // objnum of this jump node + char name[NAME_LENGTH]; +} jump_node_struct; + +extern int Num_jump_nodes; +extern jump_node_struct Jump_nodes[MAX_JUMP_NODES]; + +int jumpnode_create(vector *pos); +void jumpnode_render(object *jumpnode_objp, vector *pos, vector *view_pos = NULL); +void jumpnode_render_all(); // called by FRED + +#endif + diff --git a/include/key.h b/include/key.h new file mode 100644 index 0000000..172abea --- /dev/null +++ b/include/key.h @@ -0,0 +1,244 @@ +/* + * $Logfile: /Freespace2/code/Io/Key.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for keyboard reading routines + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 26 5/19/98 12:28a Mike + * Cheat stuff. + * + * 25 5/18/98 11:01p Mike + * Adding support for cheat system. + * + * 24 5/01/98 4:23p Lawrance + * Remap the scancode for the UK "\" key + * + * 23 1/07/98 6:41p Lawrance + * Pass message latency to the keyboard lib. + * + * 22 11/14/97 4:33p Mike + * Change Debug key to backquote (from F11). + * Balance a ton of subsystems in ships.tbl. + * Change "Heavy Laser" to "Disruptor". + * + * 21 10/21/97 7:18p Hoffoss + * Overhauled the key/joystick control structure and usage throughout the + * entire FreeSpace code. The whole system is very different now. + * + * 20 9/13/97 9:30a Lawrance + * added ability to block certain keys from the keyboard + * + * 19 9/10/97 6:02p Hoffoss + * Added code to check for key-pressed sexp operator in FreeSpace as part + * of training mission stuff. + * + * 18 4/15/97 3:47p Allender + * moved type selection of list box items into actual UI code. Made it + * behave more like windows listboxes do + * + * 17 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _KEY_H +#define _KEY_H + +/* +#ifdef __cplusplus +extern "C" { +#endif +*/ + +#include "pstypes.h" + +#define NUM_KEYS 256 + +extern int shifted_ascii_table[]; +extern int ascii_table[]; + +extern ubyte keyd_pressed[NUM_KEYS]; + +// O/S level hooks... +void key_init(); +void key_level_init(); +void key_lost_focus(); +void key_got_focus(); +void key_mark( uint code, int state, uint latency ); +int key_getch(); +void key_flush(); + +// Routines/data you can access: +//NOT USED! extern fix key_down_time( uint code ); +float key_down_timef( uint code ); + +int key_to_ascii(int keycode ); +int key_inkey(); + +// global flag that will enable/disable the backspace key from stopping execution +//extern int Backspace_debug; + +uint key_get_shift_status(); +int key_down_count(int scancode); +int key_up_count(int scancode); +int key_checkch(); +int key_check(int key); + +// Put "key" back in the input buffer. +void key_outkey(int key); + +// used to restrict keys that are read into keyboard buffer +void key_set_filter(int *filter_array, int num); +void key_clear_filter(); + +extern int Cheats_enabled; +extern int Key_normal_game; + +#define KEY_SHIFTED 0x1000 +#define KEY_ALTED 0x2000 +#define KEY_CTRLED 0x4000 +#define KEY_DEBUGGED 0x8000 +#define KEY_DEBUGGED1 0x0800 // Cheat bit in release version of game. +#define KEY_MASK 0x00FF + +#define KEY_DEBUG_KEY 0x29 // KEY_LAPOSTRO (shifted = tilde, near upper-left of keyboard) + +#define KEY_0 0x0B +#define KEY_1 0x02 +#define KEY_2 0x03 +#define KEY_3 0x04 +#define KEY_4 0x05 +#define KEY_5 0x06 +#define KEY_6 0x07 +#define KEY_7 0x08 +#define KEY_8 0x09 +#define KEY_9 0x0A + +#define KEY_A 0x1E +#define KEY_B 0x30 +#define KEY_C 0x2E +#define KEY_D 0x20 +#define KEY_E 0x12 +#define KEY_F 0x21 +#define KEY_G 0x22 +#define KEY_H 0x23 +#define KEY_I 0x17 +#define KEY_J 0x24 +#define KEY_K 0x25 +#define KEY_L 0x26 +#define KEY_M 0x32 +#define KEY_N 0x31 +#define KEY_O 0x18 +#define KEY_P 0x19 +#define KEY_Q 0x10 +#define KEY_R 0x13 +#define KEY_S 0x1F +#define KEY_T 0x14 +#define KEY_U 0x16 +#define KEY_V 0x2F +#define KEY_W 0x11 +#define KEY_X 0x2D +#define KEY_Y 0x15 +#define KEY_Z 0x2C + +#define KEY_MINUS 0x0C +#define KEY_EQUAL 0x0D +#define KEY_DIVIDE 0x35 +#define KEY_SLASH 0x2B +#define KEY_SLASH_UK 0x56 +#define KEY_COMMA 0x33 +#define KEY_PERIOD 0x34 +#define KEY_SEMICOL 0x27 + +#define KEY_LBRACKET 0x1A +#define KEY_RBRACKET 0x1B + +#define KEY_RAPOSTRO 0x28 +#define KEY_LAPOSTRO 0x29 + +#define KEY_ESC 0x01 +#define KEY_ENTER 0x1C +#define KEY_BACKSP 0x0E +#define KEY_TAB 0x0F +#define KEY_SPACEBAR 0x39 + +#define KEY_NUMLOCK 0x45 +#define KEY_SCROLLOCK 0x46 +#define KEY_CAPSLOCK 0x3A + +#define KEY_LSHIFT 0x2A +#define KEY_RSHIFT 0x36 + +#define KEY_LALT 0x38 +#define KEY_RALT 0xB8 + +#define KEY_LCTRL 0x1D +#define KEY_RCTRL 0x9D + +#define KEY_F1 0x3B +#define KEY_F2 0x3C +#define KEY_F3 0x3D +#define KEY_F4 0x3E +#define KEY_F5 0x3F +#define KEY_F6 0x40 +#define KEY_F7 0x41 +#define KEY_F8 0x42 +#define KEY_F9 0x43 +#define KEY_F10 0x44 +#define KEY_F11 0x57 +#define KEY_F12 0x58 + +#define KEY_PAD0 0x52 +#define KEY_PAD1 0x4F +#define KEY_PAD2 0x50 +#define KEY_PAD3 0x51 +#define KEY_PAD4 0x4B +#define KEY_PAD5 0x4C +#define KEY_PAD6 0x4D +#define KEY_PAD7 0x47 +#define KEY_PAD8 0x48 +#define KEY_PAD9 0x49 +#define KEY_PADMINUS 0x4A +#define KEY_PADPLUS 0x4E +#define KEY_PADPERIOD 0x53 +#define KEY_PADDIVIDE 0xB5 +#define KEY_PADMULTIPLY 0x37 +#define KEY_PADENTER 0x9C + +#define KEY_INSERT 0xD2 +#define KEY_HOME 0xC7 +#define KEY_PAGEUP 0xC9 +#define KEY_DELETE 0xd3 +#define KEY_END 0xCF +#define KEY_PAGEDOWN 0xD1 +#define KEY_UP 0xC8 +#define KEY_DOWN 0xD0 +#define KEY_LEFT 0xCB +#define KEY_RIGHT 0xCD + +#define KEY_PRINT_SCRN 0xB7 +#define KEY_PAUSE 0x45 //DOS: 0x61 +#define KEY_BREAK 0xc6 + +/* +#ifdef __cplusplus +} +#endif +*/ + +#endif + diff --git a/include/keycontrol.h b/include/keycontrol.h new file mode 100644 index 0000000..0b38d7f --- /dev/null +++ b/include/keycontrol.h @@ -0,0 +1,69 @@ +/* + * $Logfile: /Freespace2/code/Io/KeyControl.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for managing keyboard/joystick/mouse button presses + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 6 4/23/98 1:28a Dave + * Seemingly nailed the current_primary_bank and current_secondary_bank -1 + * problem. Made sure non-critical button presses are _never_ sent to the + * server. + * + * 5 3/12/98 4:02p Lawrance + * Cleanup how pause works, fix couple pause-related bugs. + * + * 4 1/27/98 5:54p Lawrance + * extern Dead key set, so popups can use it. + * + * 3 10/21/97 7:06p Hoffoss + * Overhauled the key/joystick control structure and usage throughout the + * entire FreeSpace code. The whole system is very different now. + * + * 2 9/15/97 11:38p Lawrance + * redo how game actions are recorded from player input and executed + * + * 1 9/15/97 1:23p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __FREESPACE_KEYCONTROL_H__ +#define __FREESPACE_KEYCONTROL_H__ + +#include "controlsconfig.h" + +// Holds the bit arrays that indicate which action is to be executed. +#define NUM_BUTTON_FIELDS ((CCFG_MAX + 31) / 32) + +extern int Dead_key_set[]; +extern int Dead_key_set_size; + +typedef struct button_info +{ + int status[NUM_BUTTON_FIELDS]; +} button_info; + +void button_info_set(button_info *bi, int n); +void button_info_unset(button_info *bi, int n); +int button_info_query(button_info *bi, int n); +void button_info_do(button_info *bi); +void button_info_clear(button_info *bi); +void process_set_of_keys(int key, int count, int *list); +void game_process_pause_key(); +void button_strip_noncritical_keys(button_info *bi); + + +#endif + diff --git a/include/levelpaging.h b/include/levelpaging.h new file mode 100644 index 0000000..f6a3201 --- /dev/null +++ b/include/levelpaging.h @@ -0,0 +1,31 @@ +/* + * $Logfile: /Freespace2/code/FREESPACE2/LevelPaging.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to page in all the bitmaps at the beginning of a level. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 1 3/26/98 5:14p John + * + * $NoKeywords: $ + */ + +#ifndef _LEVELPAGING_H +#define _LEVELPAGING_H + +// Call this and it calls the page in code for all the subsystems +void level_page_in(); + +#endif //_LEVELPAGING_H + diff --git a/include/lighting.h b/include/lighting.h new file mode 100644 index 0000000..87b1c8b --- /dev/null +++ b/include/lighting.h @@ -0,0 +1,126 @@ +/* + * $Logfile: /Freespace2/code/Lighting/Lighting.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for lighting functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 6/18/99 5:16p Dave + * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD + * dialog to PXO screen. + * + * 3 5/09/99 6:00p Dave + * Lots of cool new effects. E3 build tweaks. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 13 4/10/98 5:20p John + * Changed RGB in lighting structure to be ubytes. Removed old + * not-necessary 24 bpp software stuff. + * + * 12 2/13/98 5:00p John + * Made lighting push functions return number of releveent lights. + * + * 11 1/29/98 8:14a John + * Added support for RGB lighting + * + * 10 1/23/98 5:08p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 9 12/12/97 3:02p John + * First Rev of Ship Shadows + * + * 8 11/07/97 7:24p John + * changed lighting to take two ranges. + * In textest, added test code to draw nebulas + * + * 7 11/04/97 9:19p John + * Optimized dynamic lighting more. + * + * 6 10/29/97 5:05p John + * Changed dynamic lighting to only rotate and calculate lighting for + * point lights that are close to an object. Changed lower framerate cap + * from 4 to .5. + * + * 5 4/08/97 5:18p John + * First rev of decent (dynamic, correct) lighting in FreeSpace. + * + * 4 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * 3 1/30/97 9:35a Hoffoss + * Added header for files. + * + * $NoKeywords: $ + */ + +#ifndef _LIGHTING_H +#define _LIGHTING_H + +// Light stuff works like this: +// At the start of the frame, call light_reset. +// For each light source, call light_add_??? functions. +// To calculate lighting, do: +// call light_filter_reset or light_filter. +// set up matrices with g3 functions +// call light_rotatate_all to rotate all valid +// lights into current coordinates. +// call light_apply to fill in lighting for a point. + +void light_reset(); +void light_set_ambient(float ambient_light); + +// Intensity - how strong the light is. 1.0 will cast light around 5meters or so. +// r,g,b - only used for colored lighting. Ignored currently. +void light_add_directional( vector *dir, float intensity, float r, float g, float b ); +void light_add_point( vector * pos, float r1, float r2, float intensity, float r, float g, float b, int ignore_objnum ); +void light_add_point_unique( vector * pos, float r1, float r2, float intensity, float r, float g, float b, int affected_objnum); +void light_add_tube(vector *p0, vector *p1, float r1, float r2, float intensity, float r, float g, float b, int affected_objnum); +void light_rotate_all(); + +// Reset the list of lights to point to all lights. +void light_filter_reset(); + +// Makes a list of only the lights that will affect +// the sphere specified by 'pos' and 'rad' and 'objnum'. +// Returns number of lights active. +int light_filter_push( int objnum, vector *pos, float rad ); +int light_filter_push_box( vector *min, vector *max ); +void light_filter_pop(); + +// Applies light to a vertex. In order for this to work, +// it assumes that one of light_filter or light_filter_reset +// have been called. It only uses 'vert' to fill in it's light +// fields. 'pos' is position of point, 'norm' is the norm. +ubyte light_apply( vector *pos, vector * norm, float static_light_val ); + +// Same as above only does RGB. +void light_apply_rgb( ubyte *param_r, ubyte *param_g, ubyte *param_b, vector *pos, vector * norm, float static_light_val ); + +// return the # of global light sources +int light_get_global_count(); + +// Fills direction of global light source N in pos. +// Returns 0 if there is no global light. +int light_get_global_dir(vector *pos, int n); + +// Set to non-zero if we're in a shadow. +void light_set_shadow( int state ); + + +#endif + diff --git a/include/line.h b/include/line.h new file mode 100644 index 0000000..5a30e94 --- /dev/null +++ b/include/line.h @@ -0,0 +1,211 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Line.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for line.cpp + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 10 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 9 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 8 10/03/97 9:10a John + * added better antialiased line drawer + * + * 7 6/13/97 5:35p John + * added some antialiased bitmaps and lines + * + * 6 11/26/96 6:50p John + * Added some more hicolor primitives. Made windowed mode run as current + * bpp, if bpp is 8,16,or 32. + * + * 5 10/26/96 2:56p John + * Got gradient code working. + * + * 4 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#ifndef _LINE_H +#define _LINE_H + +void gr8_line(int x1,int y1,int x2,int y2); +void gr8_aaline(vertex *v1, vertex *v2); + + +#define INT_EXCHG(a,b) do { \ + int __temp__ = (a); \ + (a) = (b); \ + (b) = __temp__; \ +} while(0) + +//#define INT_SCALE(var,arg,num,den) ((var) = ((arg) * (num)) / (den)) +#define INT_SCALE(var,arg,num,den) ((var) = MulDiv(arg, num, den)) + +#define INT_CLIPLINE(x1,y1,x2,y2,XMIN,YMIN,XMAX,YMAX,WHEN_OUTSIDE,WHEN_CLIPPED,WHEN_SWAPPED) do { \ + int temp; \ + \ + if(y1 > y2) \ + { INT_EXCHG(y1,y2); INT_EXCHG(x1,x2); WHEN_SWAPPED; } \ + if((y2 < YMIN) || (y1 > YMAX)) \ + { WHEN_OUTSIDE; } \ + if(x1 < x2) { \ + if((x2 < XMIN) || (x1 > XMAX)) { \ + WHEN_OUTSIDE; \ + } \ + if(x1 < XMIN) { \ + INT_SCALE(temp,(y2 - y1),(XMIN - x1),(x2 - x1)); \ + if((y1 += temp) > YMAX) { WHEN_OUTSIDE; } \ + x1 = XMIN; \ + WHEN_CLIPPED; \ + } \ + if(x2 > XMAX) { \ + INT_SCALE(temp,(y2 - y1),(x2 - XMAX),(x2 - x1)); \ + if((y2 -= temp) < YMIN) { WHEN_OUTSIDE; } \ + x2 = XMAX; \ + WHEN_CLIPPED; \ + } \ + if(y1 < YMIN) { \ + INT_SCALE(temp,(x2 - x1),(YMIN - y1),(y2 - y1)); \ + x1 += temp; \ + y1 = YMIN; \ + WHEN_CLIPPED; \ + } \ + if(y2 > YMAX) { \ + INT_SCALE(temp,(x2 - x1),(y2 - YMAX),(y2 - y1)); \ + x2 -= temp; \ + y2 = YMAX; \ + WHEN_CLIPPED; \ + } \ + } \ + else { \ + if((x1 < XMIN) || (x2 > XMAX)) { \ + WHEN_OUTSIDE; \ + } \ + if(x1 > XMAX) { \ + INT_SCALE(temp,(y2 - y1),(x1 - XMAX),(x1 - x2)); \ + if((y1 += temp) > YMAX) { WHEN_OUTSIDE; } \ + x1 = XMAX; \ + WHEN_CLIPPED; \ + } \ + if(x2 < XMIN) { \ + INT_SCALE(temp,(y2 - y1),(XMIN - x2),(x1 - x2)); \ + if((y2 -= temp) < YMIN) { WHEN_OUTSIDE; } \ + x2 = XMIN; \ + WHEN_CLIPPED; \ + } \ + if(y1 < YMIN) { \ + INT_SCALE(temp,(x1 - x2),(YMIN - y1),(y2 - y1)); \ + x1 -= temp; \ + y1 = YMIN; \ + WHEN_CLIPPED; \ + } \ + if(y2 > YMAX) { \ + INT_SCALE(temp,(x1 - x2),(y2 - YMAX),(y2 - y1)); \ + x2 += temp; \ + y2 = YMAX; \ + WHEN_CLIPPED; \ + } \ + } \ +} while(0) + +#define FL_EXCHG(a,b) do { \ + float __temp__ = (a); \ + (a) = (b); \ + (b) = __temp__; \ +} while(0) + +#define FL_SCALE(var,arg,num,den) ((var) = ((arg) * (num)) / (den)) + +#define FL_CLIPLINE(x1,y1,x2,y2,XMIN,YMIN,XMAX,YMAX,WHEN_OUTSIDE,WHEN_CLIPPED,WHEN_SWAPPED) do { \ + float temp; \ + \ + if(y1 > y2) \ + { FL_EXCHG(y1,y2); FL_EXCHG(x1,x2); WHEN_SWAPPED; } \ + if((y2 < YMIN) || (y1 > YMAX)) \ + { WHEN_OUTSIDE; } \ + if(x1 < x2) { \ + if((x2 < XMIN) || (x1 > XMAX)) { \ + WHEN_OUTSIDE; \ + } \ + if(x1 < XMIN) { \ + FL_SCALE(temp,(y2 - y1),(XMIN - x1),(x2 - x1)); \ + if((y1 += temp) > YMAX) { WHEN_OUTSIDE; } \ + x1 = XMIN; \ + WHEN_CLIPPED; \ + } \ + if(x2 > XMAX) { \ + FL_SCALE(temp,(y2 - y1),(x2 - XMAX),(x2 - x1)); \ + if((y2 -= temp) < YMIN) { WHEN_OUTSIDE; } \ + x2 = XMAX; \ + WHEN_CLIPPED; \ + } \ + if(y1 < YMIN) { \ + FL_SCALE(temp,(x2 - x1),(YMIN - y1),(y2 - y1)); \ + x1 += temp; \ + y1 = YMIN; \ + WHEN_CLIPPED; \ + } \ + if(y2 > YMAX) { \ + FL_SCALE(temp,(x2 - x1),(y2 - YMAX),(y2 - y1)); \ + x2 -= temp; \ + y2 = YMAX; \ + WHEN_CLIPPED; \ + } \ + } \ + else { \ + if((x1 < XMIN) || (x2 > XMAX)) { \ + WHEN_OUTSIDE; \ + } \ + if(x1 > XMAX) { \ + FL_SCALE(temp,(y2 - y1),(x1 - XMAX),(x1 - x2)); \ + if((y1 += temp) > YMAX) { WHEN_OUTSIDE; } \ + x1 = XMAX; \ + WHEN_CLIPPED; \ + } \ + if(x2 < XMIN) { \ + FL_SCALE(temp,(y2 - y1),(XMIN - x2),(x1 - x2)); \ + if((y2 -= temp) < YMIN) { WHEN_OUTSIDE; } \ + x2 = XMIN; \ + WHEN_CLIPPED; \ + } \ + if(y1 < YMIN) { \ + FL_SCALE(temp,(x1 - x2),(YMIN - y1),(y2 - y1)); \ + x1 -= temp; \ + y1 = YMIN; \ + WHEN_CLIPPED; \ + } \ + if(y2 > YMAX) { \ + FL_SCALE(temp,(x1 - x2),(y2 - YMAX),(y2 - y1)); \ + x2 += temp; \ + y2 = YMAX; \ + WHEN_CLIPPED; \ + } \ + } \ +} while(0) + +#endif + diff --git a/include/linklist.h b/include/linklist.h new file mode 100644 index 0000000..ff26019 --- /dev/null +++ b/include/linklist.h @@ -0,0 +1,96 @@ +/* + * $Logfile: /Freespace2/code/GlobalIncs/LinkList.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Macros to handle doubly linked lists + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 5 7/01/97 11:53a Lawrance + * add list_insert_before() + * + * 4 4/15/97 1:27p Lawrance + * added a GET_PREV() macro + * + * 3 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _LINKLIST_H +#define _LINKLIST_H + +// Initializes a list of zero elements +#define list_init( head ) \ +do { \ + (head)->next = (head); \ + (head)->prev = (head); \ +} while (0) + +// Inserts element onto the front of the list +#define list_insert( head, elem ) \ +do { \ + (elem)->next = (head)->next; \ + (head)->next->prev = (elem); \ + (head)->next = (elem); \ + (elem)->prev = (head); \ +} while (0) + +// Inserts new_elem before elem +#define list_insert_before(elem, new_elem) \ +do { \ + (elem)->prev->next = (new_elem); \ + (new_elem)->prev = (elem)->prev; \ + (elem)->prev = (new_elem); \ + (new_elem)->next = (elem); \ +} while (0) + +// Appends an element on to the tail of the list +#define list_append( head, elem ) \ +do { \ + (elem)->prev = (head)->prev; \ + (elem)->next = (head); \ + (head)->prev->next = (elem); \ + (head)->prev = (elem); \ +} while (0) + +// Adds list b onto the end of list a +#define list_merge( a, b ) \ +do { \ + (a)->prev->next = (b)->next; \ + (b)->next->prev = (a)->prev; \ + (a)->prev = (b)->prev; \ + (b)->prev->next = (a); \ +} while (0) + +// Removes an element from listit's in +#define list_remove( head, elem ) \ +do { \ + (elem)->prev->next = (elem)->next; \ + (elem)->next->prev = (elem)->prev; \ + (elem)->next = NULL; \ + (elem)->prev = NULL; \ +} while(0) + +#define GET_FIRST(head) ((head)->next) +#define GET_LAST(head) ((head)->prev) +#define GET_NEXT(elem) ((elem)->next) +#define GET_PREV(elem) ((elem)->prev) +#define END_OF_LIST(head) (head) +#define NOT_EMPTY(head) ((head)->next != (head)) +#define EMPTY(head) ((head)->next == (head)) + +#endif + diff --git a/include/localize.h b/include/localize.h new file mode 100644 index 0000000..d68b16f --- /dev/null +++ b/include/localize.h @@ -0,0 +1,147 @@ +/* + * $Logfile: /Freespace2/code/Localization/localize.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 18 11/02/99 3:24p Jefff + * added translation functions for a few key instances where english was + * showing up + * + * 17 10/25/99 5:46p Jefff + * Many localization fixes/changes for German builds + * + * 16 10/14/99 2:52p Jefff + * localization fixes. added support for hi-res specific xstr offsets + * + * 15 7/13/99 6:07p Jefff + * Added support for localization string offsets. + * + * 14 5/26/99 11:46a Dave + * Added ship-blasting lighting and made the randomization of lighting + * much more customizable. + * + * 13 2/23/99 11:18a Andsager + * Localize launcher using strings.tbl + * + * 12 2/22/99 9:35p Andsager + * Add lcl_get_language_name() returns string with current lang. Added + * localization for launcher. + * + * 11 12/01/98 4:46p Dave + * Put in targa bitmap support (16 bit). + * + * $NoKeywords: $ + */ + + +#ifndef __FREESPACE2_LOCALIZATION_UTILITIES_HEADER_FILE +#define __FREESPACE2_LOCALIZATION_UTILITIES_HEADER_FILE + +// ------------------------------------------------------------------------------------------------------------ +// LOCALIZE DEFINES/VARS +// + +// language defines +#define LCL_NUM_LANGUAGES 3 // keep this up to date +#define LCL_ENGLISH 0 +#define LCL_GERMAN 1 +#define LCL_FRENCH 2 + +#define LCL_DEFAULT_LANGUAGE LCL_ENGLISH + +// following is the offset where special characters start in our font +extern int Lcl_special_chars; + +// for language name strings +#define LCL_LANG_NAME_LEN 32 + +// use these to replace *_BUILD values +// only 1 will be active at a time +extern int Lcl_fr; +extern int Lcl_gr; +extern int Lcl_english; + + +// ------------------------------------------------------------------------------------------------------------ +// LOCALIZE FUNCTIONS +// + +// initialize localization, if no language is passed - use the language specified in the registry +void lcl_init(int lang = -1); + +// shutdown localization +void lcl_close(); + +// initialize the xstr table +void lcl_xstr_init(); + +// free the xstr table +void lcl_xstr_close(); + +// determine what language we're running in, see LCL_* defines above +int lcl_get_language(); + +// returns the current language character string +void lcl_get_language_name(char *lang_name); + +// set our current language +void lcl_set_language(int lang); + + +// NOTE : generally you should only care about the above functions. Below are very low level functions +// which should already be well entrenched in Freespace. If you think you need to use one of the below +// functions - ask first :) +// externalization of table/mission files (only parse routines ever need to deal with these functions) ----------------------- + +// maybe add on an appropriate subdirectory when opening a localized file +void lcl_add_dir(char *current_path); + +// maybe add localized directory to full path with file name when opening a localized file +void lcl_add_dir_to_path_with_filename(char *current_path); + +// open the externalization file for use during parsing (call before parsing a given file) +void lcl_ext_open(); + +// close the externalization file (call after parsing a given file) +void lcl_ext_close(); + +// get the localized version of the string. if none exists, return the original string +// valid input to this function includes : +// "this is some text" +// XSTR("wheeee", -1) +// XSTR("whee", 20) +// and these should cover all the externalized string cases +// fills in id if non-NULL. a value of -2 indicates it is not an external string +void lcl_ext_localize(char *in, char *out, int max_len, int *id = NULL); + +// translate the specified string based upon the current language +char *XSTR(char *str, int index); +int lcl_get_xstr_offset(int index, int res); + +// translate umlauted chars from ascii to ansi codes +// used in launcher +#define LCL_TO_ANSI 0 +#define LCL_TO_ASCII 1 +char* lcl_fix_umlauts(char *str, int which_way); + +// macro for launcher xstrs +#if defined(GERMAN_BUILD) +#define LXSTR(str, i) (lcl_fix_umlauts(XSTR(str, i), LCL_TO_ANSI)) +#else +#define LXSTR(str, i) (XSTR(str, i)) +#endif // defined(GERMAN_BUILD) + +void lcl_translate_wep_name(char *name); +void lcl_translate_ship_name(char *name); +void lcl_translate_brief_icon_name(char *name); +void lcl_translate_targetbox_name(char *name); + +#endif // defined __FREESPACE2_LOCALIZATION_UTILITIES_HEADER_FILE + diff --git a/include/mainfrm.h b/include/mainfrm.h new file mode 100644 index 0000000..c814e02 --- /dev/null +++ b/include/mainfrm.h @@ -0,0 +1,46 @@ +// MainFrm.h : interface of the CMainFrame class +// +///////////////////////////////////////////////////////////////////////////// + +class CMainFrame : public CMDIFrameWnd +{ + DECLARE_DYNAMIC(CMainFrame) +public: + CMainFrame(); + +// Attributes +public: + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMainFrame) + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CMainFrame(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: // control bar embedded members + CStatusBar m_wndStatusBar; + CToolBar m_wndToolBar; + +// Generated message map functions +protected: + //{{AFX_MSG(CMainFrame) + afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + diff --git a/include/mainhallmenu.h b/include/mainhallmenu.h new file mode 100644 index 0000000..7866508 --- /dev/null +++ b/include/mainhallmenu.h @@ -0,0 +1,87 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/MainHallMenu.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for main-hall menu code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 8/26/99 9:45a Dave + * First pass at easter eggs and cheats. + * + * 3 6/03/99 10:15p Dave + * Put in temporary main hall screen. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 7 4/25/98 2:00p Dave + * Installed a bunch of multiplayer context help screens. Reworked ingame + * join ship select screen. Fix places where network timestamps get hosed. + * + * 6 3/31/98 7:09p Dave + * Changed around main hall code to make way for multiple different halls. + * + * 5 2/05/98 7:13p Dave + * Put in new misc animation sound triggers. + * + * 4 12/06/97 1:11a Lawrance + * make a general interface for help overlays + * + * 3 12/05/97 2:39p Lawrance + * added some different sounds to main hall, add support for looping + * ambient sounds + * + * 2 12/01/97 4:59p Dave + * Synchronized multiplayer debris objects. Put in pilot popup in main + * hall. Optimized simulated multiplayer lag module. Fixed a potential + * file_xfer bug. + * + * 1 11/19/97 8:30p Dave + * + * $NoKeywords: $ + * + */ + +#ifndef _MAIN_HALL_MENU_HEADER_FILE +#define _MAIN_HALL_MENU_HEADER_FILE + +// the # of main halls we're supporting +#define NUM_MAIN_HALLS 2 + +// initialize the main hall proper +void main_hall_init(int main_hall_num); + +// do a frame for the main hall +void main_hall_do(float frametime); + +// close the main hall proper +void main_hall_close(); + +// start the main hall music playing +void main_hall_start_music(); + +// stop the main hall music +void main_hall_stop_music(); + +// what main hall we're on (should be 0 or 1) +int main_hall_id(); + +// start the ambient sounds playing in the main hall +void main_hall_start_ambient(); +void main_hall_stop_ambient(); +void main_hall_reset_ambient_vol(); + +void main_hall_do_multi_ready(); + +// make the vasudan main hall funny +void main_hall_vasudan_funny(); + +#endif diff --git a/include/mainhalltemp.h b/include/mainhalltemp.h new file mode 100644 index 0000000..6d06b7a --- /dev/null +++ b/include/mainhalltemp.h @@ -0,0 +1,43 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/MainHallTemp.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for main-hall menu code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 6/03/99 10:15p Dave + * Put in temporary main hall screen. + * + * $NoKeywords: $ + * + */ + +#ifndef __TEMP_MAIN_HALL_MENU_HEADER_FILE +#define __TEMP_MAIN_HALL_MENU_HEADER_FILE + +// ------------------------------------------------------------------------------------------------------------------------ +// TEMP MAIN HALL DEFINES/VARS +// + + +// ------------------------------------------------------------------------------------------------------------------------ +// TEMP MAIN HALL FUNCTIONS +// + +// initialize the temporary main hall +void mht_init(); + +// do a frame for the main hall +void mht_do(); + +// close the temporary main hall +void mht_close(); + +#endif + diff --git a/include/management.h b/include/management.h new file mode 100644 index 0000000..f1dd656 --- /dev/null +++ b/include/management.h @@ -0,0 +1,343 @@ +/* + * $Logfile: /Freespace2/code/Fred2/Management.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * This file handles the management of Objects, Ships, Wings, etc. Basically + * all the little structures we have that usually inter-relate that need to + * be handled in a standard way, and thus should be handled by a single + * function. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 5/20/99 6:59p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 5 4/07/99 6:21p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 4 3/20/99 5:09p Dave + * Fixed release build fred warnings and unhandled exception. + * + * 3 10/29/98 10:41a Dave + * Change the way cfile initializes exe directory. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 56 12/05/97 4:07p Hoffoss + * Changed code to allow WHO_FROM type ship sources to only show flyable + * ships in list. + * + * 55 11/21/97 2:55p Hoffoss + * Added Nebula support to Fred. Implemented loading and saving nebula + * info to/from mission files. + * + * 54 11/17/97 6:41p Lawrance + * moved bitmask_2_bitnum to ship lib + * + * 53 11/11/97 2:13p Allender + * docking bay support for Fred and Freespace. Added hook to ai code for + * arrival/departure from dock bays. Fred support now sufficient. + * + * 52 10/08/97 11:47a Hoffoss + * Added better fred handling of Weaponry_pool. + * + * 51 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 50 9/09/97 10:29a Hoffoss + * Added support for neutral team, and fixed changes made to how team is + * used in ship structure. + * + * 49 8/22/97 4:16p Hoffoss + * added support for arrival and departure info in ship editor using + * wing's info if editing marked ships in a wing instead of using ship's. + * + * 48 8/17/97 10:22p Hoffoss + * Fixed several bugs in Fred with Undo feature. In the process, recoded + * a lot of CFile.cpp. + * + * 47 8/16/97 9:24p Hoffoss + * Added support for team of players in multiplayer. + * + * 46 8/16/97 4:51p Hoffoss + * Fixed bugs with wing deletion and removing ships from a wing. + * + * 45 8/16/97 2:02a Hoffoss + * Made docked objects move together in Fred. + * + * 44 8/15/97 5:14p Hoffoss + * Completely changed around how initial orders dialog worked. It's + * pretty awesome now. + * + * 43 8/15/97 11:09a Hoffoss + * Created a list of order types that can be used for several things, and + * yet easily changable. Added order error checking against ship types. + * + * 42 8/14/97 2:32p Hoffoss + * fixed bug where controlling an object doesn't cause screen updates, and + * added a number of cool features to viewpoint/control object code. + * + * 41 8/12/97 3:33p Hoffoss + * Fixed the "press cancel to go to reference" code to work properly. + * + * 40 8/12/97 1:55a Hoffoss + * Made extensive changes to object reference checking and handling for + * object deletion call. + * + * 39 8/08/97 1:31p Hoffoss + * Added syncronization protection to cur_object_index changes. + * + * 38 8/05/97 5:12p Jasen + * Added advanced_stricmp() function to handle NULL pointers gracefully, + * and utilized it in message editor query update function. + * + * 37 8/05/97 1:28p Hoffoss + * Fixed bug: if dockee ship deleted, docker becomes undocked. Other + * little changes. + * + * 36 7/09/97 2:38p Allender + * organized ship/wing editor dialogs. Added protect ship and ignore + * count checkboxes to those dialogs. Changed flag code for + * parse_objects. Added unprotect sexpressions + * + * 35 6/18/97 11:46a Hoffoss + * Fixed initial order object reference updating and added briefing dialog + * window tracking data. + * + * 34 5/30/97 4:43p Hoffoss + * Added code for allowing ships to be initially docked at mission start. + * + * 33 5/05/97 5:44p Hoffoss + * Added specialized popup menu choices, save before running FreeSpace, + * and display filters. + * + * 32 4/29/97 1:58p Hoffoss + * Added some debugging to Fred to try and track down sexp corruption + * causes. + * + * 31 4/24/97 5:15p Hoffoss + * fixes to Fred. + * + * 30 4/23/97 11:55a Hoffoss + * Fixed many bugs uncovered while trying to create Mission 6. + * + * 29 3/28/97 5:39p Hoffoss + * Player ships treated like other ships in sexp tree editor now. + * + * 28 3/27/97 1:43p Hoffoss + * Ship duplication (cloning) supported now. + * + * 27 3/20/97 3:55p Hoffoss + * Major changes to how dialog boxes initialize (load) and update (save) + * their internal data. This should simplify things and create less + * problems. + * + * 26 3/12/97 12:40p Hoffoss + * Fixed bugs in wing object management functions, several small additions + * and rearrangements. + * + * 25 3/04/97 6:27p Hoffoss + * Changes to Fred to handle new wing structure. + * + * 24 3/03/97 4:32p Hoffoss + * Initial orders supports new docking stuff Allender added. + * + * 23 2/25/97 6:10p Hoffoss + * Fixed bug with modeless dialog box errors on update. + * + * 22 2/20/97 4:28p Hoffoss + * Added modification tracking to ship editor dialog box, and support + * functions. + * + * 21 2/20/97 4:03p Hoffoss + * Several ToDo items: new reinforcement clears arrival cue, reinforcement + * control from ship and wing dialogs, show grid toggle. + * + * 20 2/12/97 5:50p Hoffoss + * Expanded on error checking. + * + * 19 2/12/97 12:26p Hoffoss + * Expanded on global error checker, added initial orders conflict + * checking and warning, added waypoint editor dialog and code. + * + * 18 2/06/97 3:42p Hoffoss + * Added mission critical checking in deletion handler. + * + * 17 2/04/97 3:09p Hoffoss + * Background bitmap editor implemented fully. + * + * 16 1/27/97 10:03a Hoffoss + * + * 15 1/10/97 3:56p Hoffoss + * Added a recursive menu graying function. + * + * 14 1/02/97 3:50p Hoffoss + * More fixes to player start support in Fred. + * + * 13 12/11/96 3:29p Hoffoss + * Worked on getting the Wing dialog changes updated when focus moves. + * Works now. + * + * 12 12/03/96 3:58p Hoffoss + * Added Wing editor to Fred. + * + * 11 11/20/96 10:01a Hoffoss + * A few minor improvements. + * + * 10 11/19/96 9:50a Hoffoss + * New interface working, but not finished yet. + * + * 9 11/15/96 1:43p Hoffoss + * Improvements to the Ship Dialog editor window. It is now an + * independant window that updates data correctly. + * + * 8 11/14/96 10:43a Hoffoss + * Made changes to grid display and how it works, etc. + * + * 7 11/13/96 10:15a Hoffoss + * Waypoint editing added, but not quite finished yet. + * + * 6 11/11/96 9:59a Hoffoss + * Many great improvements. + * + * 5 11/05/96 2:46p Hoffoss + * Lots of Fred changes (getting ready for milestone) + * + * 4 10/29/96 6:20p Hoffoss + * Added ability to delete selected or marked ships. + * + * 3 10/28/96 5:28p Hoffoss + * Extensive rearrangement, modifications and fixes of the Editor system. + * + * 2 10/25/96 12:05p Hoffoss + * This new files added to makefile. + * + * 1 10/25/96 12:02p Hoffoss + * + * $NoKeywords: $ + */ + +#ifndef __MANAGEMENT_H__ +#define __MANAGEMENT_H__ + +#include +#include "pstypes.h" + +#define SHIP_FILTER_PLAYERS (1<<0) // set: add players to list as well +#define SHIP_FILTER_FLYABLE (1<<1) // set: filter out non-flyable ships + +typedef struct { + char *name; + int def; +} ai_goal_list; + +extern int cur_object_index; +extern int cur_ship; +extern int cur_wing; +extern int cur_wing_index; +extern int cur_model_index; +extern int cur_waypoint; +extern int cur_waypoint_list; +extern int Update_ship; +extern int Update_wing; +extern int Fred_font; + +// alternate ship name stuff +extern char Fred_alt_names[MAX_SHIPS][NAME_LENGTH+1]; + +extern int wing_objects[MAX_WINGS][MAX_SHIPS_PER_WING]; + +extern char *Docking_bay_list[]; + +extern char Fred_exe_dir[512]; + +extern CCriticalSection CS_cur_object_index; + +extern ai_goal_list Ai_goal_list[]; +extern int Ai_goal_list_size; + +void string_copy(char *dest, CString &src, int max_len, int modify = 0); +CString convert_multiline_string(char *src); +void deconvert_multiline_string(char *buf, CString &str, int max_len); +void fred_init(); +void set_physics_controls(); +int dup_object(object *objp); +int create_object_on_grid(int list); +int create_object(vector *pos, int list = cur_waypoint_list); +int create_player(int num, vector *pos, matrix *orient, int type = -1, int init = 1); +void create_new_mission(); +void reset_mission(); +void clear_mission(); +int query_valid_object(int index = cur_object_index); +int query_valid_ship(int index = cur_object_index); +int query_valid_waypoint(int index = cur_object_index); +void set_cur_indices(int obj = -1); +void set_cur_object_index(int obj = -1); +int delete_object(int obj); +int delete_object(object *ptr); +int delete_ship(int ship); +void delete_marked(); +void delete_reinforcement(int num); +int delete_ship_from_wing(int ship = cur_ship); +int find_free_wing(); +void add_ship_to_wing(); +int query_object_in_wing(int obj = cur_object_index); +void mark_object(int obj); +void unmark_object(int obj); +void unmark_all(); +void clear_menu(CMenu *ptr); +void generate_wing_popup_menu(CMenu *mptr, int first_id, int state); +void generate_ship_popup_menu(CMenu *mptr, int first_id, int state, int filter = 0); +int string_lookup(CString str1, char *strlist[], int max); +int update_dialog_boxes(); +void set_cur_wing(int wing); +int gray_menu_tree(CMenu *base); +int query_initial_orders_conflict(int wing); +int query_initial_orders_empty(ai_goal *ai_goals); +int set_reinforcement(char *name, int state); +int get_docking_list(int model_index); +int rename_ship(int ship, char *name); +void fix_ship_name(int ship); +int internal_integrity_check(); +void correct_marking(); +int get_ship_from_obj(int obj); +int get_ship_from_obj(object *objp); +void set_valid_dock_points(int ship, int type, CComboBox *box); +void ai_update_goal_references(int type, char *old_name, char *new_name); +int query_referenced_in_ai_goals(int type, char *name); +int advanced_stricmp(char *one, char *two); +int reference_handler(char *name, int type, int obj); +int orders_reference_handler(int code, char *msg); +int sexp_reference_handler(int node, int code, char *msg); +char *object_name(int obj); +char *get_order_name(int order); +void object_moved(object *ptr, int mark = 0); +int invalidate_references(char *name, int type); +int query_whole_wing_marked(int wing); +void generate_weaponry_usage_list(int *arr); + +// function and defines to use when adding ships to combo boxes +#define SHIPS_2_COMBO_SPECIAL (1<<0) +#define SHIPS_2_COMBO_ALL_SHIPS (1<<1) +#define SHIPS_2_COMBO_DOCKING_BAY_ONLY (1<<2) + +extern void management_add_ships_to_combo( CComboBox *box, int flags ); + +#endif + diff --git a/include/managepilot.h b/include/managepilot.h new file mode 100644 index 0000000..d3a6ae5 --- /dev/null +++ b/include/managepilot.h @@ -0,0 +1,170 @@ +/* + * $Logfile: /Freespace2/code/Playerman/ManagePilot.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * ManagePilot.h is a header file for code to load and save pilot files, and + * to select and manage the pilot + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 1/12/99 3:15a Dave + * Barracks screen support for selecting squad logos. We need real artwork + * :) + * + * 4 12/14/98 12:13p Dave + * Spiffed up xfer system a bit. Put in support for squad logo file xfer. + * Need to test now. + * + * 3 10/09/98 5:17p Andsager + * move barracks screen into barracks.cpp + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 19 4/27/98 4:56p Hoffoss + * Added 'rank pips' to pilot names in barracks screen. + * + * 18 4/19/98 4:05p Dave + * Changed main hall region selection. Put an overwrite request in for new + * pilots with same callsign as existing pilots. Highlight local player by + * default in multi debriefing. + * + * 17 4/09/98 5:43p Dave + * Remove all command line processing from the demo. Began work fixing up + * the new multi host options screen. + * + * 16 3/25/98 2:16p Dave + * Select random default image for newly created pilots. Fixed several + * multi-pause messaging bugs. Begin work on online help for multiplayer + * keys. + * + * 15 12/23/97 12:00p Allender + * change write_pilot_file to *not* take is_single as a default parameter. + * causing multiplayer pilots to get written to the single player folder + * + * 14 12/22/97 5:08p Hoffoss + * Changed inputbox class to be able to accept only certain keys, changed + * pilot screens to utilize this feature. Added to assert with pilot file + * saving. + * + * 13 11/18/97 10:53a Hoffoss + * + * 12 11/12/97 4:40p Dave + * Put in multiplayer campaign support parsing, loading and saving. Made + * command-line variables better named. Changed some things on the initial + * pilot select screen. + * + * 11 11/11/97 4:57p Dave + * Put in support for single vs. multiplayer pilots. Began work on + * multiplayer campaign saving. Put in initial player select screen + * + * 10 10/24/97 10:59p Hoffoss + * Added in create pilot popup window and barracks screen. + * + * 9 10/21/97 7:18p Hoffoss + * Overhauled the key/joystick control structure and usage throughout the + * entire FreeSpace code. The whole system is very different now. + * + * 8 10/09/97 4:57p Lawrance + * store short_callsign in the player struct + * + * 7 12/18/96 10:18a Lawrance + * integrating joystick axis configuration + * + * 6 11/13/96 9:03a Lawrance + * Capitalized various global varibles + * + * 5 11/13/96 8:40a Lawrance + * fixed bug when a new campaign would not clear out the missions played + * from the previous campaign + * + * 4 11/05/96 8:30a Lawrance + * made backspace work while entering pilots names + * + * 3 11/04/96 2:56p Lawrance + * changed the way missions completed are read and written from .PLR file. + * + * 2 11/01/96 3:22p Lawrance + * implemented pilot selection, and pilot file save and restore + * + * $NoKeywords: $ + * + */ + +#include "cfile.h" +#include "controlsconfig.h" +#include "player.h" + +#define VALID_PILOT_CHARS " _-" + +#define MAX_PILOTS 20 +#define MAX_PILOT_IMAGES 64 + +// pilot pic image list stuff ( call pilot_load_pic_list() to make these valid ) +extern char Pilot_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN]; +extern char *Pilot_image_names[MAX_PILOT_IMAGES]; +extern int Num_pilot_images; + +// squad logo list stuff (call pilot_load_squad_pic_list() to make these valid ) +extern char Pilot_squad_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN]; +extern char *Pilot_squad_image_names[MAX_PILOT_IMAGES]; +extern int Num_pilot_squad_images; + +// low-level read/writes to files +int read_int(CFILE *file); +short read_short(CFILE *file); +ubyte read_byte(CFILE *file); +void write_int(int i, CFILE *file); +void write_short(short s, CFILE *file); +void write_byte(ubyte i, CFILE *file); + +void read_string(char *s, CFILE *f); +void write_string(char *s, CFILE *f); + +// two ways of determining if a given pilot is multiplayer +// note, that the first version of this function can possibly return -1 if the file is invalid, etc. +int is_pilot_multi(CFILE *fp); // pass a newly opened (at the beginning) file pointer to the pilot file itself +int is_pilot_multi(player *p); // pass a pointer to a player struct + +int verify_pilot_file(char *filename, int single = 1, int *rank = NULL); +int read_pilot_file(char* callsign, int single = 1, player *p = NULL); +int write_pilot_file(player *p = NULL); + +// function to get default pilot callsign for game +void choose_pilot(); + +void init_new_pilot(player *p, int reset = 1); + +// load up the list of pilot image filenames (do this at game startup as well as barracks startup) +void pilot_load_pic_list(); + +// load up the list of pilot squad filenames +void pilot_load_squad_pic_list(); + +// set the truncated version of the callsign in the player struct +void pilot_set_short_callsign(player *p, int max_width); + +// pick a random image for the passed player +void pilot_set_random_pic(player *p); + +// pick a random squad logo for the passed player +void pilot_set_random_squad_pic(player *p); + +// format a pilot's callsign into a "personal" form - ie, adding a 's or just an ' as appropriate +void pilot_format_callsign_personal(char *in_callsign,char *out_callsign); + +// throw up a popup asking the user to verify the overwrite of an existing pilot name +// 1 == ok to overwrite, 0 == not ok +int pilot_verify_overwrite(); + +// functions that update player information that is stored in PLR file +void update_missions_played(int mission_number); +void clear_missions_played(); + diff --git a/include/medals.h b/include/medals.h new file mode 100644 index 0000000..365a596 --- /dev/null +++ b/include/medals.h @@ -0,0 +1,108 @@ +/* + * $Logfile: /Freespace2/code/Stats/Medals.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 10/29/99 10:40p Jefff + * hack to make german medal names display without actually changing them + * + * 4 9/02/99 3:41p Jefff + * changed badge voice handling to be similar to promotion voice handling + * + * 3 8/26/99 8:49p Jefff + * Updated medals screen and about everything that ever touches medals in + * one way or another. Sheesh. + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 8 4/10/98 4:51p Hoffoss + * Made several changes related to tooltips. + * + * 7 3/07/98 5:44p Dave + * Finished player info popup. Ironed out a few todo bugs. + * + * 6 1/27/98 4:23p Allender + * enhanced internal scoring mechanisms. + * + * 5 11/06/97 4:39p Allender + * a ton of medal work. Removed an uneeded elemen in the scoring + * structure. Fix up medals screen to apprioriate display medals (after + * mask was changed). Fix Fred to only display medals which may actually + * be granted. Added image_filename to player struct for Jason Hoffoss + * + * 4 11/05/97 4:43p Allender + * reworked medal/rank system to read all data from tables. Made Fred + * read medals.tbl. Changed ai-warp to ai-warp-out which doesn't require + * waypoint for activation + * + * $NoKeywords: $ + */ + +#ifndef FREESPACE_MEDAL_HEADER_FILE +#define FREESPACE_MEDAL_HEADER_FILE + +#include "player.h" +#include "scoring.h" + +#define MAX_BADGES 3 +#define MAX_ASSIGNABLE_MEDALS 12 // index into Medals array of the first medal which cannot be assigned + +extern scoring_struct *Player_score; + +// NUM_MEDALS stored in scoring.h since needed for player scoring structure + +typedef struct medal_stuff { + char name[NAME_LENGTH+1]; + char bitmap[NAME_LENGTH]; + int num_versions; + int kills_needed; +} medal_stuff; + +typedef struct badge_stuff { + char voice_base[MAX_FILENAME_LEN + 1]; + char *promotion_text; +} badge_stuff; + +extern medal_stuff Medals[NUM_MEDALS]; +extern badge_stuff Badge_info[MAX_BADGES]; +extern int Badge_index[MAX_BADGES]; // array which contains indices into Medals to indicate which medals are badges + +extern void parse_medal_tbl(); + +// modes for this screen +#define MM_NORMAL 0 // normal - run through the state code +#define MM_POPUP 1 // called from within some other tight loop (don't use gameseq_ functions) + +// main medals screen +void medal_main_init(player *pl,int mode = MM_NORMAL); + +// return 0 if the screen should close (used for MM_POPUP mode) +int medal_main_do(); +void medal_main_close(); + +//void init_medal_palette(); +void init_medal_bitmaps(); +void init_snazzy_regions(); +void blit_medals(); +void blit_label(char *label,int *coords); +void blit_callsign(); + +// individual medals + +extern int Medal_ID; // ID of the medal to display in this screen. Should be set by the caller + +void blit_text(); + +void medals_translate_name(char *name, int max_len); + +#endif + diff --git a/include/messageeditordlg.h b/include/messageeditordlg.h new file mode 100644 index 0000000..091b597 --- /dev/null +++ b/include/messageeditordlg.h @@ -0,0 +1,124 @@ +/* + * $Logfile: /Freespace2/code/FRED2/MessageEditorDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Old message editor dialog box handling code. This was designed a LONG time ago + * and because so much changed, I created a new one from scratch instead. This is + * only around just in case it might be useful. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 11 1/07/98 5:58p Hoffoss + * Combined message editor into event editor. + * + * 10 1/06/98 4:19p Hoffoss + * Made message editor accept returns instead of closing dialog. + * + * 9 10/13/97 11:37a Allender + * added personas to message editor in Fred + * + * 8 10/08/97 4:41p Hoffoss + * Changed the way message editor works. Each message is updated + * perminently when you switch messages (as if ok button was pressed). + * + * 7 7/14/97 9:55p Hoffoss + * Making changes to message editor system. + * + * 6 7/10/97 2:32p Hoffoss + * Made message editor dialog box modeless. + * + * 5 7/02/97 5:09p Hoffoss + * Added browse buttons to message editor. + * + * 4 5/20/97 2:28p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 3 3/11/97 2:19p Hoffoss + * New message structure support for Fred. + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "missionmessage.h" + +///////////////////////////////////////////////////////////////////////////// +// CMessageEditorDlg dialog + +class CMessageEditorDlg : public CDialog +{ +// Construction +public: + int find_event(); + int query_modified(); + void OnCancel(); + int update(int num); + void update_cur_message(); + void OnOK(); + CMessageEditorDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CMessageEditorDlg) + enum { IDD = IDD_MESSAGE_EDITOR }; + sexp_tree m_tree; + CString m_avi_filename; + CString m_wave_filename; + CString m_message_text; + CString m_message_name; + int m_cur_msg; + int m_priority; + int m_sender; + int m_persona; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMessageEditorDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + int m_event_num; // event index if existing event is being used for formula + int modified; + + // Generated message map functions + //{{AFX_MSG(CMessageEditorDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangeMessageList(); + afx_msg void OnUpdateName(); + afx_msg void OnDelete(); + afx_msg void OnNew(); + afx_msg void OnClose(); + afx_msg void OnBrowseAvi(); + afx_msg void OnBrowseWave(); + afx_msg void OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnOk(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +extern CMessageEditorDlg *Message_editor_dlg; + diff --git a/include/midifile.h b/include/midifile.h new file mode 100644 index 0000000..8fa69cc --- /dev/null +++ b/include/midifile.h @@ -0,0 +1,31 @@ +/* + * $Logfile: /Freespace2/code/Sound/midifile.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file to support MIDI file reading and manipulation + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 2 1/19/98 11:37p Lawrance + * Fixing Optimization build warnings + * + + * + * $NoKeywords: $ + */ + +#ifndef __MSTREAM_H__ +#define __MSTREAM_H__ + +#endif + diff --git a/include/midiseq.h b/include/midiseq.h new file mode 100644 index 0000000..7bc5607 --- /dev/null +++ b/include/midiseq.h @@ -0,0 +1,150 @@ +/* + * $Logfile: /Freespace2/code/Sound/midiseq.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file with MIDI parsing constants and data structures + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 1 4/28/97 4:45p John + * Initial version of ripping sound & movie out of OsAPI. + * + * 4 2/10/97 9:26a Lawrance + * + * 3 1/31/97 2:40p Lawrance + * MIDI playback working + * + * $NoKeywords: $ + */ + +#ifndef __MIDIBASE_H__ +#define __MIDIBASE_H__ + + +// MIDI file constants +// + +#define MThd 0x6468544D // Start of file +#define MTrk 0x6B72544D // Start of track + +#define MIDI_SYSEX ((BYTE)0xF0) // SysEx begin +#define MIDI_SYSEXEND ((BYTE)0xF7) // SysEx begin +#define MIDI_META ((BYTE)0xFF) // Meta event begin +#define MIDI_META_TEMPO ((BYTE)0x51) // Tempo change +#define MIDI_META_EOT ((BYTE)0x2F) // End-of-track + +#define MIDI_NOTEOFF ((BYTE)0x80) // + note + velocity +#define MIDI_NOTEON ((BYTE)0x90) // + note + velocity +#define MIDI_POLYPRESS ((BYTE)0xA0) // + pressure (2 bytes) +#define MIDI_CTRLCHANGE ((BYTE)0xB0) // + ctrlr + value +#define MIDI_PRGMCHANGE ((BYTE)0xC0) // + new patch +#define MIDI_CHANPRESS ((BYTE)0xD0) // + pressure (1 byte) +#define MIDI_PITCHBEND ((BYTE)0xE0) // + pitch bend (2 bytes) + +#define MIDI_META_MARKER ((BYTE)0x06) // Marker META event +#define NUM_CHANNELS 16 + +#define MIDICTRL_VOLUME ((BYTE)0x07) +#define MIDICTRL_VOLUME_LSB ((BYTE)0x27) +#define MIDICTRL_PAN ((BYTE)0x0A) + +#define MIDIEVENT_CHANNEL(dw) (dw & 0x0000000F) +#define MIDIEVENT_TYPE(dw) (dw & 0x000000F0) +#define MIDIEVENT_DATA1(dw) ((dw & 0x0000FF00) >> 8) +#define MIDIEVENT_VOLUME(dw) ((dw & 0x007F0000) >> 16) + +// Macros for swapping hi/lo-endian data +// +#define WORDSWAP(w) (((w) >> 8) | \ + (((w) << 8) & 0xFF00)) + +#define DWORDSWAP(dw) (((dw) >> 24) | \ + (((dw) >> 8) & 0x0000FF00) | \ + (((dw) << 8) & 0x00FF0000) | \ + (((dw) << 24) & 0xFF000000)) + +// Make a little distinction here so the various structure members are a bit +// more clearly labelled -- we have offsets and byte counts to keep track of +// that deal with both in-memory buffers and the file on disk + +#define FILEOFF DWORD + +// These structures are stored in MIDI files; they need to be byte aligned. +// +#pragma pack(1) + +// Chunk header. dwTag is either MTrk or MThd. +// +typedef struct +{ + DWORD dwTag; // Type + DWORD dwChunkLength; // Length (hi-lo) +} MIDICHUNK; + +// Contents of MThd chunk. +typedef struct +{ + WORD wFormat; // Format (hi-lo) + WORD wTrackCount; // # tracks (hi-lo) + WORD wTimeDivision; // Time division (hi-lo) +} MIDIFILEHDR; + +#pragma pack() // End of need for byte-aligned structures + + +// Temporary event structure which stores event data until we're ready to +// dump it into a stream buffer +// +typedef struct +{ + DWORD tkEvent; // Absolute time of event + BYTE byShortData[4]; // Event type and parameters if channel msg + DWORD dwEventLength; // Length of data which follows if meta or sysex + LPBYTE pLongData; // -> Event data if applicable +} TEMPEVENT, *PTEMPEVENT; + +#define ITS_F_ENDOFTRK 0x00000001 + +// Description of a track open for read +// +typedef struct +{ + DWORD fdwTrack; // Track status + DWORD dwTrackLength; // Total bytes in track + DWORD dwLeftInBuffer; // Bytes left unread in track buffer + LPBYTE pTrackStart; // -> start of track data buffer + LPBYTE pTrackCurrent; // -> next byte to read in buffer + DWORD tkNextEventDue; // Absolute time of next event in track + BYTE byRunningStatus;// Running status from last channel msg + + FILEOFF foTrackStart; // Start of track -- used for walking the file + FILEOFF foNextReadStart;// File offset of next read from disk + DWORD dwLeftOnDisk; // Bytes left unread on disk + +// int in_song[MAX_SONGS]; + +} INTRACKSTATE, *PINTRACKSTATE; + +// Description of the input MIDI file +// +typedef struct +{ + DWORD cbFileLength; // Total bytes in file + DWORD dwTimeDivision; // Original time division + DWORD dwFormat; // Original format + DWORD dwTrackCount; // Track count (specifies pitsTracks size) + INTRACKSTATE *pitsTracks; // -> array of tracks in this file +} INFILESTATE, *PINFILESTATE; + +#endif /* __MIDIBASE_H__ */ + diff --git a/include/missionbrief.h b/include/missionbrief.h new file mode 100644 index 0000000..9b9d072 --- /dev/null +++ b/include/missionbrief.h @@ -0,0 +1,172 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionBrief.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for code to display the mission briefing to the player + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 9/07/99 6:53p Jefff + * functionality to break out of a loop + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 7 5/19/98 8:35p Dave + * Revamp PXO channel listing system. Send campaign goals/events to + * clients for evaluation. Made lock button pressable on all screens. + * + * 6 4/20/98 3:53p Lawrance + * Fix various bugs with auto-advancing through briefings. + * + * 5 3/30/98 5:16p Lawrance + * centralize a check for disabled loadout screens + * + * 4 3/11/98 10:28a Lawrance + * Add 'skip training' button + * + * 3 1/13/98 5:35p Lawrance + * Added brief_turn_off_closeup_icon() + * + * 2 12/05/97 2:39p Lawrance + * added some different sounds to main hall, add support for looping + * ambient sounds + * + * 1 9/30/97 10:16a Lawrance + * move files from Mission lib to MissionUI lib + * + * 39 9/24/97 5:29p Lawrance + * add voice playback of briefing text + * + * 38 9/24/97 5:03p Dave + * Spliced a bunch of stuff into MissionScreenCommon.[h,cpp] + * + * 37 9/22/97 5:12p Dave + * Added stats transfer game _mode_. Started work on multiplayer chat + * screens for weapon and ship select + * + * 36 9/18/97 11:11p Lawrance + * extern Brief_background_bitmap + * + * 35 8/19/97 1:33p Dave + * Enhancements to multi brief chat screen (sounds, scrolling, etc.) + * + * 34 8/17/97 2:41p Lawrance + * improving interface + * + * 33 8/15/97 8:25p Lawrance + * fix bug with freeing input box on briefing screens + * + * 32 8/15/97 7:58p Lawrance + * integrate new art for the briefing screens + * + * 31 8/14/97 5:21p Dave + * Added multiplayer briefing chat system. + * + * 30 7/31/97 1:38p Lawrance + * show multiplayer chat window in all screens (blited from common_render) + * + * 29 7/20/97 6:59p Lawrance + * changed name of some anim functions to be more consistent + * + * 28 7/14/97 3:58p Lawrance + * limit frametime to 33 ms for animation timing + * + * 27 6/26/97 12:12a Lawrance + * supporting anti-aliased bitmap animations + * + * 26 6/24/97 11:46p Lawrance + * supporting icon text and rotating models + * + * 25 6/18/97 11:00a Lawrance + * add in ship icons, move briefing render code into MissionBriefCommon + * + * 24 6/12/97 11:09p Lawrance + * getting map and text integrated into briefing + * + * 23 6/12/97 5:15p Lawrance + * added hook for ambient sound in briefing/ship select + * + * 22 6/12/97 11:27a Lawrance + * separating FRED dependant briefing code + * + * 21 6/12/97 9:58a Lawrance + * Move grid header stuff to separate file + * + * 20 6/12/97 2:48a Lawrance + * integrating briefing into ship select / weapon loadout screen + * + * 19 6/11/97 11:55a Lawrance + * added new data structures to hold briefing/debriefing info + * + * 18 4/02/97 11:57a Lawrance + * pre-load buffer for briefing music so no delay when briefing starts + * + * 17 3/31/97 5:45p Lawrance + * supporting changes to allow multiple streamed audio files + * + * 16 2/05/97 10:35a Lawrance + * supporting spooled music at menus, briefings, credits etc. + * + * + * $NoKeywords: $ + * + */ + +#ifndef _MISSIONBRIEF_H +#define _MISSIONBRIEF_H + +#include "ui.h" + +// #defines to identify which screen we are on +#define ON_BRIEFING_SELECT 1 +#define ON_SHIP_SELECT 2 +#define ON_WEAPON_SELECT 3 + +// briefing buttons +#define BRIEF_BUTTON_LAST_STAGE 0 +#define BRIEF_BUTTON_NEXT_STAGE 1 +#define BRIEF_BUTTON_PREV_STAGE 2 +#define BRIEF_BUTTON_FIRST_STAGE 3 +#define BRIEF_BUTTON_SCROLL_UP 4 +#define BRIEF_BUTTON_SCROLL_DOWN 5 +#define BRIEF_BUTTON_SKIP_TRAINING 6 +#define BRIEF_BUTTON_PAUSE 7 +#define BRIEF_BUTTON_MULTI_LOCK 8 +#define BRIEF_BUTTON_EXIT_LOOP 9 + + +#define NUM_BREIFING_REGIONS (NUM_COMMON_REGIONS + 8) + +extern int Brief_multitext_bitmap; // bitmap for multiplayer chat window +extern int Brief_background_bitmap; +extern UI_INPUTBOX Common_multi_text_inputbox[3]; + +// Sounds +#define BRIEFING_MUSIC_DELAY 2500 // 650 ms delay before breifing music starts +extern int Briefing_music_handle; +extern int Briefing_music_begin_timestamp; + +void brief_init(); +void brief_close(); +void brief_do_frame(float frametime); +void brief_unhide_buttons(); +uint brief_get_closeup_icon(); +void brief_turn_off_closeup_icon(); + +void briefing_stop_music(); +void briefing_start_music(); +void briefing_load_music(char* fname); +void brief_stop_voices(); + +int brief_only_allow_briefing(); + +#endif // don't add anything past this line + diff --git a/include/missionbriefcommon.h b/include/missionbriefcommon.h new file mode 100644 index 0000000..2dfe21a --- /dev/null +++ b/include/missionbriefcommon.h @@ -0,0 +1,411 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionBriefCommon.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for briefing stuff common to FreeSpace and FRED + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 10 8/24/99 11:30a Jefff + * Increased number of debriefing lines + * + * 9 7/20/99 7:09p Jefff + * briefing text occupies full window in 1024x768 + * + * 8 7/19/99 3:01p Dave + * Fixed icons. Added single transport icon. + * + * 7 7/18/99 5:20p Dave + * Jump node icon. Fixed debris fogging. Framerate warning stuff. + * + * 6 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 5 6/29/99 7:39p Dave + * Lots of small bug fixes. + * + * 4 1/30/99 4:03p Johnson + * Include #defines for Fred + * + * 3 1/29/99 4:17p Dave + * New interface screens. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 71 6/10/98 6:47p Lawrance + * increase MAX_RECOMMENDATION_LEN to 1024, to accommodate other languages + * + * 70 4/25/98 3:49p Lawrance + * Save briefing auto-advance pref + * + * 69 4/20/98 3:53p Lawrance + * Fix various bugs with auto-advancing through briefings. + * + * 68 4/15/98 10:27a Sandeep + * + * 67 4/13/98 7:06p Lawrance + * implement auto advance of briefing stages + * + * 66 4/06/98 2:51p Hoffoss + * Added sexp for briefing to FreeSpace. + * + * 65 4/03/98 5:22p Hoffoss + * Changes: training missions don't show objectives anymore, text for + * objective stage is different than last briefing stage now, and + * objectives have a black background now. + * + * 64 4/03/98 10:31a John + * Made briefing and debriefing arrays be malloc'd + * + * 63 4/01/98 8:38p Lawrance + * Add support for jump node icons in the briefings. + * + * 62 3/26/98 5:47p Lawrance + * remove icon text functionality + * + * 61 3/17/98 4:13p Hoffoss + * finessed the coordinates of the text window in the briefing screen a + * little to improve visual quality. + * + * 60 3/12/98 4:04p Hoffoss + * Changed text window size and spaced text out an extra pixel. + * + * 59 3/09/98 12:13a Lawrance + * Add support for Red Alert missions + * + * 58 3/07/98 11:50a Lawrance + * Fix bug with displaying the closeup title + * + * 57 3/05/98 9:38p Hoffoss + * Finished up command brief screen. + * + * 56 3/05/98 10:39a Hoffoss + * Added command brief stuff. + * + * 55 2/26/98 4:59p Allender + * groundwork for team vs team briefings. Moved weaponry pool into the + * Team_data structure. Added team field into the p_info structure. + * Allow for mutliple structures in the briefing code. + * + * 54 2/24/98 6:21p Lawrance + * Fix up map grid region + * + * 53 2/24/98 12:22a Lawrance + * New coords for revamped briefing graphics + * + * 52 2/18/98 6:45p Hoffoss + * Added support for lines between icons in briefings for Fred. + * + * 51 2/13/98 5:16p Lawrance + * Add support for icon lines in the briefing. + * + * 50 2/09/98 9:25p Allender + * team v team support. multiple pools and breifings + * + * 49 2/06/98 4:32p Lawrance + * Allow briefing voices to be toggled on/off + * + * 48 2/04/98 4:32p Allender + * support for multiple briefings and debriefings. Changes to mission + * type (now a bitfield). Bitfield defs for multiplayer modes + * + * 47 1/28/98 7:22p Lawrance + * Put back in highlight member in brief_icon for FRED compatability. + * + * 46 1/28/98 7:19p Lawrance + * Get fading/highlighting animations working + * + * 45 10/19/97 5:14p Lawrance + * add flags member to brief_stage + * + * 44 10/15/97 4:45p Lawrance + * implement scene cut in debriefing + * + * 43 10/13/97 7:40p Lawrance + * implement new debriefing/recommendation format + * + * 42 10/04/97 12:00a Lawrance + * grey out background when help overlay is activated + * + * 41 10/03/97 8:25a Lawrance + * be able to pause brief narration + * + * 40 10/01/97 4:39p Lawrance + * move chat code into Chatbox.cpp, simplify interface + * + * $NoKeywords: $ + */ + +#ifndef __MISSIONBRIEFCOMMON_H__ +#define __MISSIONBRIEFCOMMON_H__ + +#include "packunpack.h" +#include "hud.h" + +#define MAX_TEXT_STREAMS 2 // how many concurrent streams of text can be displayed + +// ------------------------------------------------------------------------ +// names for the icons that can appear in the briefing. If you modify this list, +// update the Icons_names[] string array located in MissionParse.cpp +// ------------------------------------------------------------------------ +#define MAX_BRIEF_ICONS 35 // keep up to date + +#define ICON_FIGHTER 0 +#define ICON_FIGHTER_WING 1 +#define ICON_CARGO 2 +#define ICON_CARGO_WING 3 +#define ICON_LARGESHIP 4 +#define ICON_LARGESHIP_WING 5 +#define ICON_CAPITAL 6 +#define ICON_PLANET 7 +#define ICON_ASTEROID_FIELD 8 +#define ICON_WAYPOINT 9 +#define ICON_SUPPORT_SHIP 10 +#define ICON_FREIGHTER_NO_CARGO 11 +#define ICON_FREIGHTER_WITH_CARGO 12 +#define ICON_FREIGHTER_WING_NO_CARGO 13 +#define ICON_FREIGHTER_WING_WITH_CARGO 14 +#define ICON_INSTALLATION 15 +#define ICON_BOMBER 16 +#define ICON_BOMBER_WING 17 +#define ICON_CRUISER 18 +#define ICON_CRUISER_WING 19 +#define ICON_UNKNOWN 20 +#define ICON_UNKNOWN_WING 21 +#define ICON_FIGHTER_PLAYER 22 +#define ICON_FIGHTERW_PLAYER 23 +#define ICON_BOMBER_PLAYER 24 +#define ICON_BOMBERW_PLAYER 25 +#define ICON_KNOSSOS_DEVICE 26 +#define ICON_TRANSPORT_WING 27 +#define ICON_CORVETTE 28 +#define ICON_GAS_MINER 29 +#define ICON_AWACS 30 +#define ICON_SUPERCAP 31 +#define ICON_SENTRYGUN 32 +#define ICON_JUMP_NODE 33 +#define ICON_TRANSPORT 34 + +// ------------------------------------------------------------------------ +// Structures to hold briefing data +// ------------------------------------------------------------------------ + +#define MAX_BRIEF_LEN 4096 // size of char array which holds briefing text +#define MAX_BRIEF_LINES 50 +#define MAX_BRIEF_LINE_LEN 256 // max number of chars in a briefing line +#define MAX_BRIEF_LINE_W_640 375 // max width of line in pixels in 640x480 mode +#define MAX_BRIEF_LINE_W_1024 600 // max width of line in pixels in 1024x768 mode + +#define MAX_DEBRIEF_LEN 2048 // size of char array which holds debriefing text +#define MAX_DEBRIEF_LINES 60 +#define MAX_DEBRIEF_LINE_LEN 256 // max number of chars in a debriefing line +#define MAX_DEBRIEF_LINE_W 500 // max width of line in pixels + +#define MAX_ICON_TEXT_LEN 1024 // max number of chars for icon info +#define MAX_ICON_TEXT_LINES 30 +#define MAX_ICON_TEXT_LINE_LEN 256 // max number of chars in icon info line +#define MAX_ICON_TEXT_LINE_W 170 // max width of line in pixels + +#define MAX_STAGE_ICONS 20 +#define MAX_BRIEF_STAGES 15 +#define MAX_DEBRIEF_STAGES 20 +#define MAX_LABEL_LEN 64 + +#define MAX_RECOMMENDATION_LEN 1024 + +#define BI_HIGHLIGHT (1<<0) +#define BI_SHOWHIGHLIGHT (1<<1) +#define BI_FADEIN (1<<2) + +typedef struct brief_icon +{ + int x,y,w,h; + int hold_x, hold_y; // 2D screen position of icon, used to place animations + int ship_class; + int modelnum; + float radius; + int type; // ICON_* defines from MissionBriefCommon.h + int bitmap_id; + int id; + int team; + vector pos; + char label[MAX_LABEL_LEN]; + char closeup_label[MAX_LABEL_LEN]; +// char text[MAX_ICON_TEXT_LEN]; + hud_anim fadein_anim; + hud_anim fadeout_anim; + hud_anim highlight_anim; + int flags; // BI_* flags defined above +} brief_icon; + +#define MAX_BRIEF_STAGE_LINES 20 + +typedef struct brief_line +{ + int start_icon; // index into icons[], where line starts + int end_icon; // index into icons[], where line ends +} brief_line; + +#define BS_FORWARD_CUT (1<<0) +#define BS_BACKWARD_CUT (1<<1) + +typedef struct brief_stage +{ + char *new_text; + char voice[MAX_FILENAME_LEN]; + vector camera_pos; + matrix camera_orient; + int camera_time; // ms + int flags; // see BS_ flags above + int formula; + int num_icons; + brief_icon *icons; + int num_lines; + brief_line *lines; +} brief_stage; + +typedef struct debrief_stage +{ + int formula; + char *new_text; + char voice[MAX_FILENAME_LEN]; + char *new_recommendation_text; +} debrief_stage; + +typedef struct briefing { + int num_stages; + brief_stage stages[MAX_BRIEF_STAGES]; +} briefing; + +typedef struct debriefing { + int num_stages; + debrief_stage stages[MAX_DEBRIEF_STAGES]; +} debriefing; + + +// Code to free/init the above structures between levels: + +// -------------------------------------------------------------------------------------- +// Does one time initialization of the briefing and debriefing structures. +// Namely setting all malloc'ble pointers to NULL. Called once at game startup. +void mission_brief_common_init(); + +//-------------------------------------------------------------------------------------- +// Frees all the memory allocated in the briefing and debriefing structures +// and sets all pointers to NULL. +void mission_brief_common_reset(); + + +// -------------------------------------------------------------------------------------- +// briefing screen +// -------------------------------------------------------------------------------------- +extern int Brief_bmap_coords[GR_NUM_RESOLUTIONS][2]; +extern int Brief_grid_coords[GR_NUM_RESOLUTIONS][4]; +extern int Brief_text_coords[GR_NUM_RESOLUTIONS][4]; +extern int Brief_text_max_lines[GR_NUM_RESOLUTIONS]; +extern char *Brief_static_name[GR_NUM_RESOLUTIONS]; +extern int Brief_static_coords[GR_NUM_RESOLUTIONS][2]; + +// Needed for Fred +#define BRIEF_GRID3_X1 42 +#define BRIEF_GRID3_Y1 122 +#define BRIEF_GRID0_X2 585 +#define BRIEF_GRID0_Y2 371 +#define BRIEF_GRID_W (BRIEF_GRID0_X2-BRIEF_GRID3_X1+1) +#define BRIEF_GRID_H (BRIEF_GRID0_Y2-BRIEF_GRID3_Y1+1) +/* +#define BRIEF_GRID0_X1 63 +#define BRIEF_GRID0_Y1 122 +#define BRIEF_GRID1_X1 575 +#define BRIEF_GRID1_Y1 122 +#define BRIEF_GRID2_X1 63 +#define BRIEF_GRID2_Y1 350 + +#define BRIEF_TEXT_X1 0 +#define BRIEF_TEXT_Y1 397 +#define BRIEF_TEXT_X2 441 +#define BRIEF_TEXT_Y2 477 +#define BRIEF_TEXT_BEGIN_X 50 +#define BRIEF_TEXT_BEGIN_Y 414 +#define BRIEF_TEXT_H 54 + +*/ + +typedef struct brief_screen +{ + int map_x1, map_x2, map_y1, map_y2; + int btext_x1, btext_x2, btext_y1, btext_y2; + int cup_x1, cup_x2, cup_y1, cup_y2; + int cupinfo_x1, cupinfo_x2, cupinfo_y1, cupinfo_y2; +} brief_sceen; + +extern brief_screen bscreen; + +// ------------------------------------------------------------------------ +// Global briefing/debriefing data +// ------------------------------------------------------------------------ +extern briefing Briefings[MAX_TEAMS]; +extern debriefing Debriefings[MAX_TEAMS]; +extern briefing *Briefing; +extern debriefing *Debriefing; +extern float Brief_text_wipe_time_elapsed; + +extern int Cur_brief_id; +extern int Briefing_voice_enabled; + +extern int Num_brief_text_lines[MAX_TEXT_STREAMS]; +extern int Top_brief_text_line; +extern int Current_screen; + +// ------------------------------------------------------------------------ +// External interface +// ------------------------------------------------------------------------ +void brief_reset(); +void debrief_reset(); +void brief_close_map(); +void brief_init_map(); +void brief_init_screen(int multiplayer_flag); +void brief_render_map(int stage_num, float frametime); +void brief_set_new_stage(vector *pos, matrix *orient, int time, int stage_num); +void brief_camera_move(float frametime, int stage_num); +void brief_render_icon(int stage_num, int icon_num, float frametime, int selected = 0, float w_scale_factor = 1.0f, float h_scale_factor = 1.0f); +void brief_render_icon_line(int stage_num, int line_num); +void brief_init_icons(); +void brief_load_icons(); +void brief_unload_icons(); +void brief_common_close(); +void brief_reset_icons(int stage_num); +void brief_restart_text_wipe(); +void brief_reset_last_new_stage(); +void brief_blit_stage_num(int stage_num, int stage_max); + +void brief_common_get_icon_dimensions(int *w, int *h, int type, int ship_class); + +// voice streaming interface +void brief_voice_init(); +void brief_voice_load_all(); +void brief_voice_unload_all(); +void brief_voice_play(int stage_num); +void brief_voice_stop(int stage_num); +void brief_voice_pause(int stage_num); +void brief_voice_unpause(int stage_num); + +// fancy briefing style text functions for use in other modules. +int brief_color_text_init(char *src, int w, int instance = 0); +int brief_render_text(int line_offset, int x, int y, int h, float frametime, int instance = 0, int line_spacing = 0); + +void cmd_brief_reset(); + +int brief_time_to_advance(int stage_num, float frametime); + +#endif + diff --git a/include/missioncampaign.h b/include/missioncampaign.h new file mode 100644 index 0000000..39c7851 --- /dev/null +++ b/include/missioncampaign.h @@ -0,0 +1,475 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionCampaign.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * header file for dealing with campaigns + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 16 9/30/99 6:02p Jefff + * Changes for OEM + * + * 15 9/09/99 11:40p Dave + * Handle an Assert() in beam code. Added supernova sounds. Play the right + * 2 end movies properly, based upon what the player did in the mission. + * + * 14 9/07/99 6:55p Jefff + * functionality to break out of a loop. hacked functionality to jump to + * a specific mission in a campaign -- doesnt grant ships/weapons from + * skipped missions tho. + * + * 13 9/06/99 9:45p Jefff + * break out of loop and skip mission support + * + * 12 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 11 8/27/99 12:04a Dave + * Campaign loop screen. + * + * 10 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 9 4/25/99 3:02p Dave + * Build defines for the E3 build. + * + * 8 2/25/99 4:19p Dave + * Added multiplayer_beta defines. Added cd_check define. Fixed a few + * release build warnings. Added more data to the squad war request and + * response packets. + * + * 7 12/12/98 3:17p Andsager + * Clean up mission eval, goal, event and mission scoring. + * + * 6 12/10/98 9:59a Andsager + * Fix some bugs with mission loops + * + * 5 12/09/98 1:56p Andsager + * Initial checkin of mission loop + * + * 4 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 3 10/07/98 6:27p Dave + * Globalized mission and campaign file extensions. Removed Silent Threat + * special code. Moved \cache \players and \multidata into the \data + * directory. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 47 9/10/98 1:17p Dave + * Put in code to flag missions and campaigns as being MD or not in Fred + * and Freespace. Put in multiplayer support for filtering out MD + * missions. Put in multiplayer popups for warning of non-valid missions. + * + * 46 6/05/98 9:54a Lawrance + * OEM changes + * + * 45 5/21/98 9:25p Allender + * endgame movie always viewable at end of campaign + * + * 44 5/13/98 5:14p Allender + * red alert support to go back to previous mission + * + * 43 5/12/98 4:16p Hoffoss + * Fixed bug where not all missions in all campaigns were being filtered + * out of stand alone mission listing in simulator room. + * + * 42 4/28/98 5:25p Sandeep + * + * 41 4/27/98 7:30p Hoffoss + * Added campaign descriptions to campaign files and changed campaign room + * to display them. + * + * 40 4/25/98 7:40p Allender + * fixd some small hotkey stuff. Worked on turret orientation being + * correct for multiplayer. new sexpression called end-campaign will will + * end the main campaign + * + * 39 4/13/98 10:25p Hoffoss + * Added a flag for subspace missions, and for aboard the Galatea or + * Bastion. + * + * 38 4/08/98 6:16p Hoffoss + * Added a description field to campaign structure, since the campaign + * room has a window for this. We'll need to add this in sometime. + * + * 37 4/06/98 6:37p Dave + * Put in max_observers netgame server option. Make sure host is always + * defaulted to alpha 1 or zeta 1. Changed create game so that MAX_PLAYERS + * can always join but need to be kicked before commit can happen. Put in + * support for server ending a game and notifying clients of a special + * condition. + * + * 36 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 35 3/31/98 12:23a Allender + * changed macro names of campaign types to be more descriptive. Added + * "team" to objectives dialog for team v. team missions. Added two + * distinct multiplayer campaign types + * + * 34 3/30/98 10:37p Allender + * added demo.fsc as default campaign for demo. Delete obsolete pilots + * (including their campaign save games) + * + * 33 3/26/98 5:24p Allender + * put in respawn edit box into mission notes dialog. Made loading of + * missions/campaign happen when first entering the game setup screen. + * + * 32 3/17/98 4:17p Allender + * made Fred/FreeSpace use the same campaign loading code + * + * 31 3/02/98 5:22p Hoffoss + * Removed ready room and added campaign room. + * + * 30 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 29 2/23/98 11:08p Dave + * Finished up multiplayer campaign support. Seems bug-free. + * + * 28 2/10/98 5:51p Hoffoss + * Changed campaign stuff so new pilots start out with no active campaign + * (will get set in readyroom before user can move beyond the readyroom). + * + * 27 2/05/98 10:14p Lawrance + * Implement Save and Quit + * + * 26 12/19/97 2:59p Allender + * more stuff to get persistent ships/weapons across campaign missions + * + * 25 12/19/97 12:03p Allender + * Added GM_CAMPAIGN_MODE to indicate when player is in a campaign or not. + * Started adding FreeSpace support for carrying of ship/weapon types + * across missions in a campaign. + * + * 24 12/18/97 5:12p Allender + * initial support for ship/weapon persistence + * + * 23 12/05/97 1:55p Allender + * delete campaign save files when pilot is deleted + * + * 22 11/20/97 7:19p Hoffoss + * Renamed mission_campaign_do_new() to mission_campaign_load(). Changed + * readyroom to not lose campaign savefile until commiting to a different + * comapaign. + * + * 21 11/20/97 5:36p Dave + * Hooked in a bunch of main hall changes (including sound). Made it + * possible to reposition (rewind/ffwd) + * sound buffer pointers. Fixed animation direction change framerate + * problem. + * + * 20 11/16/97 1:11p Hoffoss + * Coded up readyroom screen, first pass. + * + * 19 11/15/97 2:37p Dave + * More multiplayer campaign support. + * + * 18 11/13/97 5:03p Sandeep + * added CSFE support + * + * 17 11/12/97 4:40p Dave + * Put in multiplayer campaign support parsing, loading and saving. Made + * command-line variables better named. Changed some things on the initial + * pilot select screen. + * + * 16 11/11/97 4:57p Dave + * Put in support for single vs. multiplayer pilots. Began work on + * multiplayer campaign saving. Put in initial player select screen + * + * 15 11/06/97 4:36p Allender + * campaign work to add a briefing cutscene to play before the briefing. + * + * 14 10/21/97 4:50p Allender + * goal/event sexpression support. Fixed campaign save file, sexpressions + * handling, sexpression editing, etc + * + * 13 10/20/97 5:13p Allender + * new subsystem sabotage/repair/set sexpressions. Added new event/goal + * status checking sexpressions (not fully implemented yet). Change + * campaign save files to save all events as well as goals + * + * 12 9/18/97 10:18p Lawrance + * add function to extract the mission names from a campaign file + * + * 11 8/21/97 10:58p Hoffoss + * Fixed bug in mission scanning for campaign editor, and added mission + * notes to sexp help window in campaign editor when mission is selected. + * + * 10 8/13/97 5:49p Hoffoss + * Fixed bugs, made additions. + * + * 9 8/08/97 5:18p Jasen + * Fixed bug with current campaign in pilot file has been deleted. Now it + * defaults back to default if current doesn't exist. + * + * 8 5/09/97 9:49a Hoffoss + * Added campaign loading for Fred and 2 new tokens to campaign file + * format. + * + * 7 4/25/97 1:55p Allender + * added close() routine to free memory at game shutdown + * + * 6 4/25/97 11:31a Allender + * Campaign state now saved in campaign save file in player directory. + * Made some global variables follow naming convention. Solidified + * continuing campaigns based on new structure + * + * 5 4/23/97 3:21p Allender + * more campaign stuff -- mission branching through campaign file now + * works!!!! + * + * 4 4/22/97 10:44a Allender + * more campaign stuff. Info about multiple campaigns now stored in + * player file -- not saving some player information in save games. + * + * 3 4/18/97 9:59a Allender + * more campaign stuff. All campaign related varaibles now stored in + * campaign structure + * + * 2 4/17/97 9:02p Allender + * campaign stuff. All campaign material stored in external file. + * Continuing campaign won't work at this point + * + * 1 4/15/97 2:11p Allender + * + * $NoKeywords: $ + */ + +#ifndef _MISSION_CAMPAIGN_H +#define _MISSION_CAMPAIGN_H + +#include "parselo.h" +#include "cfile.h" +#include "ship.h" +#include "weapon.h" +#include "scoring.h" + +// name of the builtin campaign. +#if defined(FS2_DEMO) + #define BUILTIN_CAMPAIGN "demo" + #define BUILTIN_CAMPAIGN_NAME "DEMO Campaign" +#elif defined(OEM_BUILD) + #define BUILTIN_CAMPAIGN "FreeSpace2OEM" + #define BUILTIN_CAMPAIGN_NAME "OEM Campaign" +#elif defined(E3_BUILD) + #define BUILTIN_CAMPAIGN "E3" + #define BUILTIN_CAMPAIGN_NAME "E3 Campaign" +#else + #define BUILTIN_CAMPAIGN "FreeSpace2" + #define BUILTIN_CAMPAIGN_NAME "The Main Freespace2 Campaign" +#endif + +#define MAX_CAMPAIGN_MISSIONS 100 // maximum number of missions in a campaign + +#define CAMPAIGN_ERROR_CORRUPT -1 +#define CAMPAIGN_ERROR_SEXP_EXHAUSTED -2 + +// types of campaigns -- these defines match the string literals listed below which +// are found in the campaign files. I don't think that we need campaigns for furball +// missions. +#define CAMPAIGN_TYPE_SINGLE 0 +#define CAMPAIGN_TYPE_MULTI_COOP 1 +#define CAMPAIGN_TYPE_MULTI_TEAMS 2 + +#define MAX_CAMPAIGN_TYPES 3 + +// type of movies we may be able to play +#define CAMPAIGN_MOVIE_PRE_MISSION 1 +#define CMAPAIGN_MOVIE_POST_MISSION 2 + +#define CAMPAIGN_SINGLE_PLAYER_SIG 0xddddeeee +#define CAMPAIGN_MULTI_PLAYER_SIG 0xeeeeffff + +// defines for possibly persistent information +#define CAMPAIGN_PERSISTENT_SHIP 1 +#define CAMPAIGN_PERSISTENT_WEAPON 2 + +#define CMISSION_FLAG_BASTION (1<<0) // set if stationed on Bastion, else Galatea +#define CMISSION_FLAG_SKIPPED (1<<1) // set if skipped, else not + +#define CAMPAIGN_LOOP_MISSION_UNINITIALIZED -2 + +extern char *campaign_types[MAX_CAMPAIGN_TYPES]; + +// structure for a campaign definition. It contains the mission names and other interesting +// information about a campaign and the mission strucuture within. + +typedef struct mgoal { + char name[NAME_LENGTH]; // name of the goal (same as name in the mission_goal structure + char status; // failed, satisfied, or incomplete (same as goal completion); +} mgoal; + +typedef struct mevent { + char name[NAME_LENGTH]; + char status; +} mevent; + +typedef struct cmission { + char *name; // name of the mission + char *notes; // mission notes for mission (used by Fred) + char briefing_cutscene[NAME_LENGTH]; // name of the cutscene to be played before this mission + int formula; // sexpression used to determine mission branching. + int completed; // has the player completed this mission + int num_goals; // number of goals this mission had + mgoal *goals; // malloced array of mgoals (of num_goals size) which has the goal completion status + int num_events; // number of events this mission had + mevent *events; // malloced array of mevents (of num_events) size) which has event completion status + int has_mission_loop; // whether current mission has side loop + int mission_loop_formula;// formula to determine whether to allow a side loop + char *mission_loop_desc; // message in popup + char *mission_loop_brief_anim; + char *mission_loop_brief_sound; + int level; // what level of the tree it's on (Fred) + int pos; // what x position on level it's on (Fred) + int flags; + scoring_struct stats; +} cmission; + +typedef struct campaign { + char name[NAME_LENGTH]; // name of the campaign + char filename[MAX_FILENAME_LEN]; // filename the campaign info is in + char *desc; // description of campaign + int type; // type of campaign + int num_missions; // number of missions in the campaign + int num_missions_completed; // number of missions in the campaign that have been flown + int current_mission; // the current mission that the player is playing. Only valid during the mission + int next_mission; // number of the next mission to fly when comtinuing the campaign. Always valid + int prev_mission; // mission that we just came from. Always valid + int loop_enabled; // whether mission loop is chosen - true during a loop, false otherwise + int loop_mission; // mission number of misssion loop (if any) + int loop_reentry; // mission number to return to after loop is finished + int realign_required; // are any missions missing alignment info? (Fred) + int num_players; // valid in multiplayer campaigns -- number of players campaign supports. + ubyte ships_allowed[MAX_SHIP_TYPES]; // which ships the player can use + ubyte weapons_allowed[MAX_WEAPON_TYPES]; // which weapons the player can use + cmission missions[MAX_CAMPAIGN_MISSIONS]; // decription of the missions +} campaign; + +extern campaign Campaign; + +// campaign wasn't ended +extern int Campaign_ended_in_mission; + +// structure for players. Holds the campaign name, number of missions flown in the campaign, and result +// of the missions flown. This structure is stored in the player file and thus is persistent across +// games +typedef struct campaign_info +{ + char filename[NAME_LENGTH]; + int num_missions_completed; + ubyte missions_completed[MAX_CAMPAIGN_MISSIONS]; +} campaign_info; + +// extern'ed so the mission loading can get a list of campains. Only use this +// data after mission_campaign_build_list() is called +#define MAX_CAMPAIGNS 128 +extern char *Campaign_names[MAX_CAMPAIGNS]; +extern char *Campaign_file_names[MAX_CAMPAIGNS]; +extern int Num_campaigns; + +// called at game startup time to load the default single player campaign +void mission_campaign_init( void ); + +// called to reload the default campaign +int mission_campaign_load_by_name( char *filename ); +int mission_campaign_load_by_name_csfe( char *filename, char *callsign ); + + +// load up and initialize a new campaign +int mission_campaign_load( char *filename, int load_savefile = 1 ); + +// function to save the state of the campaign between missions or to load a campaign save file +extern int mission_campaign_save( void ); + +// declaration for local campaign save game load function +extern void mission_campaign_savefile_load( char *cfilename ); +extern void mission_campaign_savefile_delete( char *cfilename, int is_multi = -1 ); +extern void mission_campaign_delete_all_savefiles( char *pilot_name, int is_multi ); + +// if a given campaign is a multiplayer campaign, we can load and save the multiplayer info portion with these functions +extern int mission_campaign_parse_is_multi(char *filename, char *name); + +// function which sets up internal variable for player to play next mission in the campaign +extern int mission_campaign_next_mission( void ); + +// function which is called with the current mission in this campaign is over +extern void mission_campaign_mission_over( void ); + +// frees all memory at game close time +extern void mission_campaign_close( void ); + +// read in a campaign file. Used by Fred. +int mission_campaign_load_fred(char *filename, char *name_verify = NULL); + +// used by Fred to get a mission's list of goals. +void read_mission_goal_list(int num); + +void mission_campaign_build_list( int multiplayer ); + +// returns index of mission with passed name +extern int mission_campaign_find_mission( char *name ); + +// maybe play a movie. type indicates before or after mission +extern void mission_campaign_maybe_play_movie(int type); + +// save persistent information +extern void mission_campaign_save_persistent( int type, int index ); + +void mission_campaign_savefile_generate_root(char *filename); + +// The following are functions I added to set up the globals and then +// execute the corresponding mission_campaign_savefile functions. + +// Saves the campaign camp under the player name pname +int campaign_savefile_save(char *pname); +// Deletes the campaign save camp under the player name pname +void campaign_delete_save( char *cfn, char *pname); +// Loads campaign camp from fname under player name pname +void campaign_savefile_load(char *fname, char *pname); + +// get name and type of specified campaign file +int mission_campaign_get_info(char *filename, char *name, int *type, int *max_players, char **desc = NULL); + +// get a listing of missions in a campaign +int mission_campaign_get_mission_list(char *filename, char **list, int max); + +// load up a campaign for the current player. +int mission_load_up_campaign(); + +// stores mission goals and events in Campaign struct +void mission_campaign_store_goals_and_events(); + +// evaluates next mission and possible loop mission +void mission_campaign_eval_next_mission(); + +// returns to the beginning of the previous mission +int mission_campaign_previous_mission(); + +// proceeds to next mission in campaign +void mission_campaign_skip_to_next(int start_game = 1); + +// break out of loop +void mission_campaign_exit_loop(); + +// jump to specified mission +void mission_campaign_jump_to_mission(char *name); + +// stuff for the end of the campaign of the single player game +void mission_campaign_end_init(); +void mission_campaign_end_close(); +void mission_campaign_end_do(); + +// End CSFE stuff +#endif + diff --git a/include/missioncmdbrief.h b/include/missioncmdbrief.h new file mode 100644 index 0000000..598127f --- /dev/null +++ b/include/missioncmdbrief.h @@ -0,0 +1,72 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionCmdBrief.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Mission Command Briefing Screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 7 4/06/98 8:37p Hoffoss + * Fixed a few bugs with command brief screen. Now the voice starts after + * the text has printed, and options screen doesn't reset cmd brief. + * + * 6 3/26/98 5:24p Hoffoss + * Changed Command Brief to use memory mapped ani files instead, so we + * avoid the huge pauses for huge anis that play! + * + * 5 3/19/98 4:25p Hoffoss + * Added remaining support for command brief screen (ANI and WAVE file + * playing). + * + * 4 3/17/98 6:26p Hoffoss + * Added wave filename to command brief structure. + * + * 3 3/05/98 9:38p Hoffoss + * Finished up command brief screen. + * + * 2 3/05/98 3:59p Hoffoss + * Added a bunch of new command brief stuff, and asteroid initialization + * to Fred. + * + * 1 3/02/98 6:14p Hoffoss + * + * $NoKeywords: $ + */ + +#define CMD_BRIEF_TEXT_MAX 16384 +#define CMD_BRIEF_STAGES_MAX 10 + +typedef struct { + char *text; // text to display + char ani_filename[MAX_FILENAME_LEN]; // associated ani file to play + anim_t *anim; + anim_instance_t *anim_instance; + int anim_ref; // potential reference to another index (use it's anim instead of this's) + char wave_filename[MAX_FILENAME_LEN]; + int wave; // instance number of above +} cmd_brief_stage; + +typedef struct { + int num_stages; + cmd_brief_stage stage[CMD_BRIEF_STAGES_MAX]; +} cmd_brief; + +extern cmd_brief Cmd_briefs[MAX_TEAMS]; +extern cmd_brief *Cur_cmd_brief; // pointer to one of the Cmd_briefs elements (the active one) + +void cmd_brief_init(int stages); +void cmd_brief_close(); +void cmd_brief_do_frame(float frametime); +void cmd_brief_hold(); +void cmd_brief_unhold(); + diff --git a/include/missiondebrief.h b/include/missiondebrief.h new file mode 100644 index 0000000..5163c81 --- /dev/null +++ b/include/missiondebrief.h @@ -0,0 +1,82 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionDebrief.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for running the debriefing + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 12/17/98 4:50p Andsager + * Added debrief_assemble_optional_mission_popup_text() for single and + * multiplayer + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 7 5/13/98 5:14p Allender + * red alert support to go back to previous mission + * + * 6 4/27/98 9:08p Allender + * fix the debriefing stage problems when clients get to screen long after + * server + * + * 5 4/25/98 11:24a Allender + * finsihed multiplayer debriefing stuff. Work on object updates. + * External view shoudl work in multiplayer correctly + * + * 4 4/09/98 4:32p Hoffoss + * Fixed several bugs in debriefing. + * + * 3 12/30/97 6:42p Hoffoss + * New debriefing screen implemented. + * + * 2 10/24/97 6:19p Dave + * More standalone testing/fixing. Added reliable endgame sequencing. + * Added reliable ingame joining. Added reliable stats transfer (endgame). + * Added support for dropping players in debriefing. Removed a lot of old + * unused code. + * + * 1 9/30/97 10:16a Lawrance + * move files from Mission lib to MissionUI lib + * + * 3 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 2 6/13/97 2:30p Lawrance + * Added debriefings + * + * 1 6/13/97 10:42a Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __MISSIONDEBRIEF_H__ +#define __MISSIONDEBRIEF_H__ + +extern int Debrief_multi_stages_loaded; + +void debrief_init(); +void debrief_do_frame(float frametime); +void debrief_close(); + +// useful so that the server can reset the list and ship slots if a player drops +void debrief_rebuild_player_list(); +void debrief_handle_player_drop(); + +void debrief_disable_accept(); +void debrief_assemble_optional_mission_popup_text(char *buffer, char *mission_loop_desc); + + +// multiplayer call to set up the client side debriefings +void debrief_multi_server_stuff(); +void debrief_set_multi_clients( int stage_count, int active_stages[] ); + +#endif /* __MISSIONDEBRIEF_H__ */ + diff --git a/include/missiongoals.h b/include/missiongoals.h new file mode 100644 index 0000000..e943a45 --- /dev/null +++ b/include/missiongoals.h @@ -0,0 +1,244 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionGoals.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for Mission support. Included detection of primary + * and secondary goals. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 8 9/06/99 9:46p Jefff + * skip mission support + * + * 7 8/28/99 4:54p Dave + * Fixed directives display for multiplayer clients for wings with + * multiple waves. Fixed hud threat indicator rendering color. + * + * 6 7/29/99 3:06p Andsager + * Increase max number of events to 150 + * + * 5 7/29/99 2:58p Jefff + * Ingame objective screen icon key now uses normal objective icons and + * text is drawn in code. + * + * 4 2/17/99 2:10p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 51 5/21/98 2:47a Lawrance + * Fix some problems with event music + * + * 50 4/15/98 9:05a Allender + * fix skpping of training mission with branchs + * + * 49 4/03/98 2:47p Allender + * made directives act different when multiple waves of a wing take a long + * time to reappear + * + * 48 3/31/98 12:23a Allender + * changed macro names of campaign types to be more descriptive. Added + * "team" to objectives dialog for team v. team missions. Added two + * distinct multiplayer campaign types + * + * 47 2/27/98 4:37p Hoffoss + * Combined Objectives screen into Mission Log screen. + * + * 46 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 45 2/22/98 4:30p John + * More string externalization classification + * + * 44 2/20/98 8:33p Lawrance + * Added mission_goals_incomplete() + * + * 43 1/30/98 4:24p Hoffoss + * Added a 3 second delay for directives before they get displayed. + * + * 42 1/28/98 6:21p Dave + * Made the standalone use ~8 megs less memory. Fixed multiplayer submenu + * endgame problem. + * + * 41 1/27/98 11:00a Lawrance + * Fix bug with showing number of resolved goals in the objective status + * popup. + * + * 40 1/15/98 5:23p Lawrance + * Add HUD gauge to indicate completed objectives. + * + * 39 1/12/98 5:17p Allender + * fixed primary fired problem and ship warp out problem. Made new + * mission goal info packet to deal with goals more accurately. + * + * 38 12/29/97 12:16p Johnson + * Upped event max. + * + * 37 12/22/97 6:07p Hoffoss + * Made directives flash when completed, fixed but with is-destroyed + * operator. + * + * 36 12/19/97 12:43p Hoffoss + * Changed code to allow counts in directives. + * + * 35 12/01/97 12:26a Lawrance + * Add flag MGF_NO_MUSIC to mission_goal struct, to avoid playing music + * for certain goals + * + * 34 11/02/97 10:09p Lawrance + * add source control header + * + * $NoKeywords: $ + */ + +#ifndef _MISSIONGOAL_H +#define _MISSIONGOAL_H + +#include "object.h" +#include "ai.h" +#include "cfile.h" + +// defines for types of primary and secondary missions + +#define MAX_GOALS 30 // maximum number of goals for any given mission + +// defines for types of goals. We will use part of the int field of the mission_goal struct +// as a bit field for goal flags + +#define PRIMARY_GOAL 0 +#define SECONDARY_GOAL 1 +#define BONUS_GOAL 2 + +// defines for bitfields of type, (and mask to get the type field quickly) +#define INVALID_GOAL (1 << 16) // is this goal valid or not? +#define GOAL_TYPE_MASK (0xffff) // mask to get us the type + +// defines for goal status. These status are also used in campaign file for marking goal status +// in campaign save file +#define GOAL_FAILED 0 // status of goal +#define GOAL_COMPLETE 1 +#define GOAL_INCOMPLETE 2 + +#define PRIMARY_GOALS_COMPLETE 1 +#define PRIMARY_GOALS_INCOMPLETE 0 +#define PRIMARY_GOALS_FAILED -1 + +extern char *Goal_type_text(int n); + +// structures for primary and secondary goals + +#define MAX_GOAL_TEXT 128 + +#define MGF_NO_MUSIC (1<<0) // don't play any event music when goal is achieved + +typedef struct mission_goal { + char name[NAME_LENGTH]; // used for storing status of goals in player file + int type; // primary/secondary/bonus + int satisfied; // has this goal been satisfied + char message[MAX_GOAL_TEXT]; // Brief description, such as "Destroy all vile aliens!" + int rating; // Some importance figure or something. + int formula; // Index in Sexp_nodes of this Sexp. + int score; // score for this goal + int flags; // MGF_ + int team; // which team is this objective for. +} mission_goal; + +extern mission_goal Mission_goals[MAX_GOALS]; // structure for the goals of this mission +extern int Num_goals; // number of goals for this mission + +// structures and defines for mission events + +#define MAX_MISSION_EVENTS 150 +#define MISSION_EVENTS_WARN 100 + +// defined for event states. We will also use the satisfied/failed for saving event information +// in campaign save file +#define EVENT_UNBORN 0 // event can't be evaluated yet +#define EVENT_CURRENT 1 // event can currently be evaluated, but not satisfied or failed yet +#define EVENT_SATISFIED 2 +#define EVENT_FAILED 3 +#define EVENT_INCOMPLETE 4 // used in campaign save file. used when event isn't satisfied yet + +#define MEF_CURRENT (1 << 0) // is event current or past current yet? +#define MEF_DIRECTIVE_SPECIAL (1 << 1) // used to mark a directive as true even though not fully satisfied +#define MEF_DIRECTIVE_TEMP_TRUE (1 << 2) // this directive is temporarily true. + +typedef struct mission_event { + char name[NAME_LENGTH]; // used for storing status of events in player file + int formula; // index into sexpression array for this formula + int result; // result of most recent evaluation of event + int repeat_count; // number of times to repeat this goal + int interval; // interval (in seconds) at which an evaulation is repeated once true. + int timestamp; // set at 'interval' seconds when we start to eval. + int score; // score for this event + int chain_delay; + int flags; + char *objective_text; + char *objective_key_text; + int count; // object count for directive display + int satisfied_time; + int born_on_date; // timestamp at which event was born + int team; // for multiplayer games +} mission_event; + +extern int Num_mission_events; +extern mission_event Mission_events[MAX_MISSION_EVENTS]; +extern int Mission_goal_timestamp; +extern int Event_index; // used by sexp code to tell what event it came from + +// prototypes +void mission_init_goals( void ); +void mission_show_goals_init(); +void mission_show_goals_close(); +void mission_show_goals_do_frame(float frametime); // displays goals on screen +void mission_eval_goals(); // evaluate player goals +int mission_ai_goal_achievable( ai_goals *aigp ); // determines if an AI goal is achievable +void mission_add_ai_goal( int sexp, ai_info *aip ); // adds a goal onto the given ai_info structure +int mission_evaluate_primary_goals(void); // determine if the primary goals for the mission are complete -- returns one of the above defines +int mission_goals_met(); + +// function used by single and multiplayer code to change the status on goals +void mission_goal_status_change( int goal_num, int new_status); + +// functions used to change goal validity status +void mission_goal_mark_invalid( char *name ); +void mission_goal_mark_valid( char *name ); + +// function used to mark all goals as invalid, and incomplete goals as invalid. +extern void mission_goal_fail_all(); +extern void mission_goal_fail_incomplete(); + +void mission_goal_fetch_num_resolved(int desired_type, int *num_resolved, int *total, int team = -1); +int mission_goals_incomplete(int desired_type, int team = -1); +void mission_goal_mark_objectives_complete(); +void mission_goal_mark_events_complete(); + +int mission_get_event_status(int event); +void mission_event_shutdown(); +void mission_goal_validation_change( int goal_num, int valid ); + +// mark an event as directive special +void mission_event_set_directive_special(int event); +void mission_event_unset_directive_special(int event); + +void mission_goal_exit(); + +int ML_objectives_init(int x, int y, int w, int h); +void ML_objectives_close(); +void ML_objectives_do_frame(int scroll_offset); +void ML_render_objectives_key(); // renders objectives key on ingame objectives screen + +#endif + diff --git a/include/missiongoalsdlg.h b/include/missiongoalsdlg.h new file mode 100644 index 0000000..5a4d252 --- /dev/null +++ b/include/missiongoalsdlg.h @@ -0,0 +1,166 @@ +/* + * $Logfile: /Freespace2/code/FRED2/MissionGoalsDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Mission goals editor dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 26 5/20/98 1:04p Hoffoss + * Made credits screen use new artwork and removed rating field usage from + * Fred (a goal struct member). + * + * 25 3/31/98 12:23a Allender + * changed macro names of campaign types to be more descriptive. Added + * "team" to objectives dialog for team v. team missions. Added two + * distinct multiplayer campaign types + * + * 24 12/08/97 2:03p Hoffoss + * Added Fred support for MGF_NO_MUSIC flag in objectives. + * + * 23 10/09/97 1:03p Hoffoss + * Renaming events or goals now updates sexp references as well. + * + * 22 8/12/97 3:33p Hoffoss + * Fixed the "press cancel to go to reference" code to work properly. + * + * 21 7/30/97 5:23p Hoffoss + * Removed Sexp tree verification code, since it duplicates normal sexp + * verification, and is just another set of code to keep maintained. + * + * 20 7/25/97 3:05p Allender + * added score field to goals and events editor + * + * 19 7/17/97 4:10p Hoffoss + * Added drag and drop to sexp trees for reordering root items. + * + * 18 7/07/97 12:04p Allender + * mission goal validation. + * + * 17 6/02/97 8:47p Hoffoss + * Fixed bug with inserting an operator at root position, but under a + * label. + * + * 16 5/20/97 2:28p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 15 5/01/97 4:12p Hoffoss + * Added return handling to dialogs. + * + * 14 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 13 4/11/97 10:11a Hoffoss + * Name fields supported by Fred for Events and Mission Goals. + * + * 12 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "sexp_tree.h" +#include "missiongoals.h" + +///////////////////////////////////////////////////////////////////////////// +// CMissionGoalsDlg dialog + +#define MAX_GOAL_ELEMENTS 300 +#define OPERAND 0x01 +#define EDITABLE 0x02 + +class sexp_goal_tree : public sexp_tree +{ +public: + int load_sub_tree(int index); + int get_new_node_position(); +}; + +class CMissionGoalsDlg : public CDialog +{ +// Construction +public: + void swap_handler(int node1, int node2); + int query_modified(); + void OnCancel(); + void OnOK(); + void load_tree(); + void update_cur_goal(); + void add_sub_tree(int node, HTREEITEM root); + void create_tree(); + CMissionGoalsDlg(CWnd* pParent = NULL); // standard constructor + BOOL OnInitDialog(); + int handler(int code, int goal); + void insert_handler(int old, int node); + int select_sexp_node; + +// Dialog Data + //{{AFX_DATA(CMissionGoalsDlg) + enum { IDD = IDD_MISSION_GOALS }; + sexp_goal_tree m_goals_tree; + CString m_goal_desc; + int m_goal_type; + int m_display_goal_types; + CString m_name; + BOOL m_goal_invalid; + int m_goal_score; + BOOL m_no_music; + int m_team; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMissionGoalsDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + // Generated message map functions + //{{AFX_MSG(CMissionGoalsDlg) + afx_msg void OnSelchangeDisplayGoalTypesDrop(); + afx_msg void OnSelchangedGoalsTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnRclickGoalsTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnEndlabeleditGoalsTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBeginlabeleditGoalsTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnButtonNewGoal(); + afx_msg void OnChangeGoalDesc(); + afx_msg void OnChangeGoalRating(); + afx_msg void OnSelchangeGoalTypeDrop(); + afx_msg void OnChangeGoalName(); + afx_msg void OnOk(); + afx_msg void OnClose(); + afx_msg void OnGoalInvalid(); + afx_msg void OnChangeGoalScore(); + afx_msg void OnNoMusic(); + afx_msg void OnSelchangeTeam(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + + int cur_goal; + int m_num_goals; + int m_sig[MAX_GOALS]; + mission_goal m_goals[MAX_GOALS]; + int modified; +}; + +extern CMissionGoalsDlg *Goal_editor_dlg; // global reference needed by sexp_tree class + diff --git a/include/missiongrid.h b/include/missiongrid.h new file mode 100644 index 0000000..562b85d --- /dev/null +++ b/include/missiongrid.h @@ -0,0 +1,82 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionGrid.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Type and defines for grids + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 6 10/03/97 8:55a John + * moved Fred's grid_render code out of MissionGrid and into Fred. Added + * code to turn background under overlays grey. + * + * 5 7/14/97 12:04a Lawrance + * added function that navmap calls to draw elevation lines + * + * 4 6/18/97 11:36p Lawrance + * move grid rendering code to MissionGrid.cpp + * + * 3 6/12/97 11:25a Lawrance + * added grid_read_camera_controls() + * + * 2 6/12/97 9:58a Lawrance + * holds grid specific types and #defines + * + * 1 6/12/97 9:51a Lawrance + * + * $NoKeywords: $ + */ + + +#ifndef __MISSIONGRID_H__ +#define __MISSIONGRID_H__ + +#define MAX_GRIDLINE_POINTS 201 +#define L_MAX_LINES 128 + +typedef struct grid { + int nrows, ncols; + vector center; + matrix gmatrix; + physics_info physics; + float square_size; + float planeD; // D component of plane equation (A, B, C are uvec in gmatrix) + vector gpoints1[MAX_GRIDLINE_POINTS]; // 1 -4 are edge gridpoints for small grid. + vector gpoints2[MAX_GRIDLINE_POINTS]; + vector gpoints3[MAX_GRIDLINE_POINTS]; + vector gpoints4[MAX_GRIDLINE_POINTS]; + vector gpoints5[MAX_GRIDLINE_POINTS]; // 5-8 are edge gridpoints for large grid. + vector gpoints6[MAX_GRIDLINE_POINTS]; + vector gpoints7[MAX_GRIDLINE_POINTS]; + vector gpoints8[MAX_GRIDLINE_POINTS]; +} grid; + +typedef struct tline { + int istart, iend, color; +} tline; + +extern grid Global_grid; +extern grid *The_grid; +extern int double_fine_gridlines; + +void grid_read_camera_controls( control_info * ci, float frametime ); +void maybe_create_new_grid(grid *gridp, vector *pos, matrix *orient, int force = 0); +grid *create_grid(grid *gridp, vector *forward, vector *right, vector *center, int nrows, int ncols, float square_size); +grid *create_default_grid(void); +void render_grid(grid *gridp); +void modify_grid(grid *gridp); +void rpd_line(vector *v0, vector *v1); +void grid_render_elevation_line(vector *pos, grid* gridp); + +#endif + diff --git a/include/missionhotkey.h b/include/missionhotkey.h new file mode 100644 index 0000000..7220aab --- /dev/null +++ b/include/missionhotkey.h @@ -0,0 +1,68 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionHotKey.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the Hotkey selection screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 9 1/28/98 6:23p Dave + * Made standalone use ~8 megs less memory. Fixed multiplayer submenu + * sequencing problem. + * + * 8 1/26/98 4:42p Allender + * fixed restoration of hotkeys when replaying mission. Change the + * meaning of "departed wing" to mean anytime a wing "departs" (with any + * number of remaining wingmen). + * + * 7 1/14/98 5:22p Allender + * save/restore hotkey selections when replaying the same mission + * + * 6 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 5 5/30/97 1:00p Allender + * aded F11 and F12 to hotkey selection -- F12 requies that + * UserDebuggerKey in registry be changed!!! + * + * 4 4/30/97 11:34a Lawrance + * making ship selection and hotkey assignment work in all cases + * + * 3 4/28/97 5:43p Lawrance + * allow hotkey assignment screen to work from ship selection + * + * 2 4/25/97 3:41p Lawrance + * added support for hotkey assignment screen + * + * $NoKeywords: $ + */ + +#ifndef __MISSIONHOTKEY_H__ +#define __MISSIONHOTKEY_H__ + +void mission_hotkey_init(); +void mission_hotkey_close(); +void mission_hotkey_do_frame(float frametime); +void mission_hotkey_set_defaults(); +void mission_hotkey_validate(); +void mission_hotkey_maybe_save_sets(); +void mission_hotkey_reset_saved(); +void mission_hotkey_mf_add( int set, int objnum, int how_to_add ); + +void mission_hotkey_exit(); + +// function to return the hotkey set number of the given key +extern int mission_hotkey_get_set_num( int k ); + +#endif + diff --git a/include/missionload.h b/include/missionload.h new file mode 100644 index 0000000..546fb2e --- /dev/null +++ b/include/missionload.h @@ -0,0 +1,58 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionLoad.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Mission load header file + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 20 2/23/98 6:55p Lawrance + * Rip out obsolete code. + * + * 19 6/26/97 5:53p Lawrance + * save recently played missions, allow player to choose from list + * + * 18 4/25/97 11:31a Allender + * Campaign state now saved in campaign save file in player directory. + * Made some global variables follow naming convention. Solidified + * continuing campaigns based on new structure + * + * $NoKeywords: $ + */ + +#ifndef _MISSIONLOAD_H +#define _MISSIONLOAD_H + +#include "pstypes.h" + +// ----------------------------------------------- +// For recording most recent missions played +// ----------------------------------------------- +#define MAX_RECENT_MISSIONS 10 +extern char Recent_missions[MAX_RECENT_MISSIONS][MAX_FILENAME_LEN]; +extern int Num_recent_missions; + +// Mission_load takes no parameters. +// It expects the following variables to be set correctly: +// Game_current_mission_filename + +int mission_load(); +void mission_init(); + +// Functions for mission load menu +void mission_load_menu_init(); +void mission_load_menu_close(); +void mission_load_menu_do(); + +#endif + diff --git a/include/missionlog.h b/include/missionlog.h new file mode 100644 index 0000000..162e49a --- /dev/null +++ b/include/missionlog.h @@ -0,0 +1,176 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionLog.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file to deal with Mission logs + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 8/22/99 5:53p Dave + * Scoring fixes. Added self destruct key. Put callsigns in the logfile + * instead of ship designations for multiplayer players. + * + * 3 2/26/99 6:01p Andsager + * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 21 4/28/98 4:45p Allender + * fix mission log problems on clients + * + * 20 1/07/98 4:41p Allender + * minor modification to special messages. Fixed cargo_revealed problem + * for multiplayer and problem with is-cargo-known sexpression + * + * 19 11/13/97 1:21p Hoffoss + * Added a hidden flag to entries can be hidden from being displayed to + * the user (but kept in log for checking things). + * + * 18 11/03/97 10:12p Hoffoss + * Finished up work on the hud message/mission log scrollback screen. + * + * 17 9/10/97 8:53a Allender + * made the mission log prettier. Added team info to log entries -- meant + * reordering some code + * + * 16 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 15 8/07/97 11:37a Allender + * made mission log cull non-essential entries when log starts getting + * full. More drastic action is taken as the log gets more full. + * + * 14 7/02/97 10:49p Allender + * added waypoints-done sexpressions + * + * 13 7/01/97 2:52p Allender + * added packets for mission log stuff and for pregame chat stuff + * + * 12 3/04/97 1:21p Mike + * Repair/Rearm aborting, code cleanup. + * + * 11 3/03/97 1:21p Allender + * mission log stuff -- display the log during/after game. Enhanced + * structure + * + * 10 2/20/97 5:05p Allender + * support for docking and undocking multiple times -- also able to + * specify dock points through sexpression + * + * 9 2/17/97 3:50p Allender + * change to allow ability to destroy single subsystem (through orders) or + * all engines (disable) or all turrets (disarm) + * + * 8 1/06/97 10:44p Lawrance + * Changes to make save/restore functional + * + * 7 1/06/97 11:29a Allender + * added define for logging a ship that undocks + * + * 6 1/01/97 4:18p Allender + * added new field in mission log entry -- secondary name. Used when + * there are two objects for a log entry (i.e. docking) + * + * 5 12/17/96 1:04p Allender + * added subtype field for mission messages -- not used yet and may be + * removed + * + * 4 11/08/96 9:06a Allender + * added LOG_SHIP_DOCK + * + * 3 10/24/96 8:36a Allender + * added a couple of new event types + * + * 2 10/23/96 12:48p Allender + * increased mission log functionality -- function to get the timestamp + * that a recorded event happened at + * + * 1 10/22/96 4:49p Allender + * Mission log files + * +*/ + +#ifndef _MISSIONLOG_H +#define _MISSIONLOG_H + +// defined for different mission log entries + +#define LOG_SHIP_DESTROYED 1 +#define LOG_WING_DESTROYED 2 +#define LOG_SHIP_ARRIVE 3 +#define LOG_WING_ARRIVE 4 +#define LOG_SHIP_DEPART 5 +#define LOG_WING_DEPART 6 +#define LOG_SHIP_DOCK 7 +#define LOG_SHIP_SUBSYS_DESTROYED 8 +#define LOG_SHIP_UNDOCK 9 +#define LOG_SHIP_DISABLED 10 +#define LOG_SHIP_DISARMED 11 +#define LOG_PLAYER_REARM 12 +#define LOG_PLAYER_REINFORCEMENT 13 +#define LOG_GOAL_SATISFIED 14 +#define LOG_GOAL_FAILED 15 +#define LOG_PLAYER_REARM_ABORT 16 +#define LOG_WAYPOINTS_DONE 17 +#define LOG_CARGO_REVEALED 18 +#define LOG_CAP_SUBSYS_CARGO_REVEALED 19 +#define LOG_SELF_DESTRUCT 20 + +// structure definition for log entries + +#define MLF_ESSENTIAL (1<<0) // this entry is essential for goal checking code +#define MLF_OBSOLETE (1<<1) // this entry is obsolete and will be removed +#define MLF_PRIMARY_FRIENDLY (1<<2) // primary object in this entry is friendly +#define MLF_PRIMARY_HOSTILE (1<<3) // primary object in this entry is hostile +#define MLF_SECONDARY_FRIENDLY (1<<4) // secondary object is friendly +#define MLF_SECONDARY_HOSTILE (1<<5) // secondary object is hostile +#define MLF_HIDDEN (1<<6) // entry doesn't show up in displayed log. + +typedef struct { + int type; // one of the log #defines in MissionLog.h + int flags; // flags used for status of this log entry + fix timestamp; // time in fixed seconds when entry was made from beginning of mission + char pname[NAME_LENGTH]; // name of primary object of this action + char sname[NAME_LENGTH]; // name of secondary object of this action + int index; // a generic entry which can contain things like wave # (for wing arrivals), goal #, etc +} log_entry; + +extern log_entry log_entries[]; +extern int last_entry; +extern int Num_log_lines; + +// function prototypes + +// to be called before each mission starts +extern void mission_log_init(); + +// adds an entry to the mission log. The name is a string identifier that is the object +// of the event. The multiplayer version of this takes the actual entry number to modify. +extern void mission_log_add_entry(int type, char *pname, char *sname, int index = -1 ); +extern void mission_log_add_entry_multi( int type, char *pname, char *sname, int index, fix timestamp, int flags ); + +// function to determine if event happened and what time it happened +extern int mission_log_get_time( int type, char *name, char *sname, fix *time); + +// function to determine if event happend count times and return time that the count event +// happened +extern int mission_log_get_time_indexed( int type, char *name, char *sname, int count, fix *time); + +// function to show all message log entries during or after mission +// (code stolen liberally from Alan!) +extern void mission_log_scrollback(float frametime); + +void message_log_init_scrollback(int pw); +void message_log_shutdown_scrollback(); +void mission_log_scrollback(int scroll_offset, int list_x, int list_y, int list_w, int list_h); + +#endif + diff --git a/include/missionloopbrief.h b/include/missionloopbrief.h new file mode 100644 index 0000000..b614f2a --- /dev/null +++ b/include/missionloopbrief.h @@ -0,0 +1,43 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionLoopBrief.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Campaign Loop briefing screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 8/27/99 12:04a Dave + * Campaign loop screen. + * + * + * $NoKeywords: $ + */ + +#ifndef __FS2_CAMPAIGN_LOOP_BRIEF_HEADER_FILE +#define __FS2_CAMPAIGN_LOOP_BRIEF_HEADER_FILE + +// --------------------------------------------------------------------------------------------------------------------------------------- +// MISSION LOOP BRIEF DEFINES/VARS +// + + +// --------------------------------------------------------------------------------------------------------------------------------------- +// MISSION LOOP BRIEF FUNCTIONS +// + +// init +void loop_brief_init(); + +// do +void loop_brief_do(); + +// close +void loop_brief_close(); + +#endif + diff --git a/include/missionmessage.h b/include/missionmessage.h new file mode 100644 index 0000000..34aa558 --- /dev/null +++ b/include/missionmessage.h @@ -0,0 +1,376 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionMessage.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for mission messaging + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 8 9/06/99 10:13a Jasons + * Bump up MAX_PERSONAS from 10 to 13 + * + * 7 8/26/99 8:51p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 6 8/19/99 10:12a Alanl + * preload mission-specific messages on machines greater than 48MB + * + * 5 7/31/99 2:30p Dave + * Added nifty mission message debug viewing keys. + * + * 4 7/06/99 10:41a Andsager + * Add AWACS need help messages + * + * 3 6/16/99 10:20a Dave + * Added send-message-list sexpression. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 46 4/25/98 11:49p Lawrance + * Add Terran Command stray messages + * + * 45 4/15/98 4:18p Allender + * Make Terran Command who_from be just Command + * + * 44 4/08/98 3:45p Allender + * mission message overhaul. Make message from any wingman mean any + * wingman with that persona. Terran command wave and ani's for dead + * ships now play correctly. + * + * 43 4/07/98 12:04a Mike + * New system for instructor chastising player if he fires at instructor. + * + * 42 4/02/98 10:07a Allender + * wing arrival message for delta and epsilon wings + * + * 41 4/01/98 10:47p Lawrance + * Supporting builtin messages for rearm and repair requests + * + * 40 2/20/98 8:33p Lawrance + * Add the 'All Alone' message + * + * 39 2/12/98 4:58p Lawrance + * Add support for 'All Clear' radio message + * + * 38 2/11/98 9:44p Allender + * rearm repair code fixes. hud support view shows abort status. New + * support ship killed message. More network stats + * + * 37 1/29/98 11:38a Allender + * support for Vasudan personas + * + * 36 1/28/98 11:38a Dan + * AL: bump up MAX_MISSION_MESSAGES to 300 from 200. + * + * 35 1/21/98 7:20p Lawrance + * Make subsystem locking only work with line-of-sight, cleaned up locking + * code, moved globals to player struct. + * + * 34 1/18/98 9:51p Lawrance + * Add support for 'Player Died' messages. + * + * 33 1/14/98 9:49p Allender + * removed 3'oclock and 9'oclock messages + * + * 32 1/13/98 3:11p Allender + * new messages for disable/disarm + * + * 31 1/07/98 4:41p Allender + * minor modification to special messages. Fixed cargo_revealed problem + * for multiplayer and problem with is-cargo-known sexpression + * + * 30 12/01/97 5:10p Lawrance + * + * 29 11/25/97 5:00p Allender + * big changed in the messaging code -- implemented "timouts" -- windows + * in which messages need to be played, or they are pitched + * + * 28 11/21/97 4:09p Allender + * added "Command" persona + * + * 27 11/20/97 5:06p Allender + * personas have types. personas should now correctly get assigned to the + * appropriate ship type. + * + * 26 11/17/97 6:38p Lawrance + * added message_anim_is_playing() + * + * 25 11/17/97 4:57p Allender + * persona support in FreeSpace + * + * 24 11/14/97 3:52p Allender + * removed '#' from TERRAN_COMMAND define + * + * 23 11/11/97 4:57p Dave + * Put in support for single vs. multiplayer pilots. Began work on + * multiplayer campaign saving. Put in initial player select screen + * + * 22 11/07/97 11:50a Allender + * fixed ai_destroy_ship to know whether ship was actually destroyed or + * just departed. Fixed messages for making player hammer of light + * + * 21 11/05/97 7:11p Hoffoss + * Made changed to the hud message system. Hud messages can now have + * sources so they can be color coded. + * + * 20 10/29/97 9:31p Allender + * more rearm stuff. Give player more apprioriate message when ship + * already on way. Don't count arriving ship as in the mission yet (for + * find_support_ship() ). + * + * 19 10/23/97 9:40p Allender + * more repari/rearm stuff. Warped in support ships now go directly to + * ship who they warped in for. Player gets (hud only) message when + * support ship warping in + * + * 18 10/16/97 8:52p Duncan + * Raised limit on messages to 100. + * + * 17 10/13/97 7:40p Lawrance + * have received messages get distorted if comm subsystem is severely + * damaged + * + * 16 10/10/97 5:02p Allender + * started rudimentary work on personas + * + * 15 10/09/97 4:44p Hoffoss + * Dimmed training window glass and made it less transparent, added flags + * to events, set he stage for detecting current events. + * + * 14 10/02/97 9:53p Hoffoss + * Added event evaluation analysis debug screen so we can determine the + * state of events and their sexp trees to track down logic problems and + * such. + * + * 13 9/29/97 4:17p Duncan + * Raised limit on number of waves and avis a single mission can utilize. + * + * 12 9/22/97 4:55p Hoffoss + * Added a training message window display thingy. + * + * 11 8/04/97 11:17a Mike + * Make rearm process abort if speed gets too high. + * + * 10 5/15/97 11:46a Lawrance + * play wingmen heads as anim's + * + * 9 4/23/97 2:37p Lawrance + * added message_is_playing() function + * + * 8 4/08/97 10:55a Allender + * draw purple brackets on ship sending a message + * + * 7 3/13/97 10:57a Allender + * new mission messages + * + * 6 3/11/97 10:14a Allender + * added unions for wave/avi files in messages to make things easier for + * Fred. Freespace will use the avi/wave indexing system. Fred will use + * only names + * + * 5 3/10/97 4:16p Allender + * new messaging system. builtin5 fixed to support it. made new sound + * function to determine if sound is still playing. + * + * 4 3/09/97 2:23p Allender + * Major changes to player messaging system. Added messages.tbl. Made + * all currently player messages go through new system. Not done yet. + * + * 3 1/08/97 11:45p Lawrance + * moved some typedefs and defines to header file so visible for + * save/restore + * + * 2 12/17/96 1:02p Allender + * rudimentary messaging working + * + * 1 12/12/96 4:37p Allender + * + * $NoKeywords: $ + */ + +#ifndef _MISSIONMESSAGE_H +#define _MISSIONMESSAGE_H + +#include "ship.h" +#include "parselo.h" // include so that we can gets defs for lengths of tokens +#include "animplay.h" + + +#define MAX_MISSION_MESSAGES 300 + +// keep seperate lists of AVI's and wav files. I suspect that many messages will have +// duplicate avi's for different messages. Seperate list for wave files since some messages +// might not have wave file information. + +typedef struct message_avi { + char name[MAX_FILENAME_LEN]; + int num; + anim *anim_data; +} message_extra; + +#define MAX_MESSAGE_AVIS MAX_MISSION_MESSAGES +extern int Num_message_avis; +extern message_extra Message_avis[MAX_MESSAGE_AVIS]; + +#define MAX_MESSAGE_WAVES MAX_MISSION_MESSAGES +extern int Num_message_waves; +extern message_extra Message_waves[MAX_MESSAGE_WAVES]; + +// defines for message priorities +#define MESSAGE_PRIORITY_LOW 1 +#define MESSAGE_PRIORITY_NORMAL 2 +#define MESSAGE_PRIORITY_HIGH 3 + +// defines for how quickly we should send a message +#define MESSAGE_TIME_IMMEDIATE 1 +#define MESSAGE_TIME_SOON 2 +#define MESSAGE_TIME_ANYTIME 3 + +// sources for messages +#define MESSAGE_SOURCE_SHIP 1 +#define MESSAGE_SOURCE_WINGMAN 2 +#define MESSAGE_SOURCE_COMMAND 3 +#define MESSAGE_SOURCE_SPECIAL 4 + +// define used for sender of a message when you want it to be Terran Command +#define TERRAN_COMMAND "Command" + +// defines for message id's used in Freespace code. Callers to message_send_to_player() should +// probably use these defines. + +// this number in this define should match the number of elements in the next array +#define MAX_BUILTIN_MESSAGE_TYPES 41 + +extern char *Builtin_message_types[MAX_BUILTIN_MESSAGE_TYPES]; + +#define MESSAGE_ARRIVE_ENEMY 0 +#define MESSAGE_ATTACK_TARGET 1 +#define MESSAGE_BETA_ARRIVED 2 +#define MESSAGE_CHECK_6 3 +#define MESSAGE_ENGAGE 4 +#define MESSAGE_GAMMA_ARRIVED 5 +#define MESSAGE_HELP 6 +#define MESSAGE_PRAISE 7 +#define MESSAGE_REINFORCEMENTS 8 +#define MESSAGE_IGNORE 9 +#define MESSAGE_NOSIR 10 +#define MESSAGE_OOPS 11 +#define MESSAGE_PERMISSION 12 +#define MESSAGE_STRAY 13 +#define MESSAGE_WARP_OUT 14 +#define MESSAGE_YESSIR 15 +#define MESSAGE_REARM_ON_WAY 16 +#define MESSAGE_ON_WAY 17 +#define MESSAGE_REARM_WARP 18 +#define MESSAGE_NO_TARGET 19 +#define MESSAGE_DOCK_YES 20 +#define MESSAGE_REPAIR_DONE 21 +#define MESSAGE_REPAIR_ABORTED 22 +#define MESSAGE_HAMMER_SWINE 23 +#define MESSAGE_REARM_REQUEST 24 // wingman messages player when he calls a support ship +#define MESSAGE_DISABLE_TARGET 25 +#define MESSAGE_DISARM_TARGET 26 +#define MESSAGE_PLAYED_DIED 27 // message sent when player starts death roll +#define MESSAGE_WINGMAN_SCREAM 28 +#define MESSAGE_SUPPORT_KILLED 29 +#define MESSAGE_ALL_CLEAR 30 +#define MESSAGE_ALL_ALONE 31 // message sent when player is last ship left and primary objectives still exist +#define MESSAGE_REPAIR_REQUEST 32 +#define MESSAGE_DELTA_ARRIVED 33 +#define MESSAGE_EPSILON_ARRIVED 34 +#define MESSAGE_INSTRUCTOR_HIT 35 +#define MESSAGE_INSTRUCTOR_ATTACK 36 +#define MESSAGE_STRAY_WARNING 37 +#define MESSAGE_STRAY_WARNING_FINAL 38 +#define MESSAGE_AWACS_75 39 +#define MESSAGE_AWACS_25 40 + +typedef struct MissionMessage { + char name[NAME_LENGTH]; // used to identify this message + char message[MESSAGE_LENGTH]; // actual message + int persona_index; // which persona says this message + int multi_team; // multiplayer team filter (important for TvT only) + + // unions for avi/wave information. Because of issues with Fred, we are using + // the union to specify either the index into the avi or wave arrays above, + // or refernce the name directly. The currently plan is to only have Fred reference + // the name field!!! + union { + int index; // index of avi file to play + char *name; + } avi_info; + + union { + int index; + char *name; + } wave_info; + +} MMessage; + +extern MMessage Messages[MAX_MISSION_MESSAGES]; + +extern int Num_messages; +extern int Num_builtin_messages; // from messages.tbl -- index of message location to load mission specific messages into +extern int Message_shipnum; // used to display info on hud when message is sent + +// variable, etc for persona information +#define MAX_PERSONAS 13 +#define MAX_PERSONA_TYPES 4 + +// flags for personas. the type flags must be sequential starting from 0, and must match +// the persona_type_names defined in missionmessage.cpp +#define PERSONA_FLAG_WINGMAN (1<<0) +#define PERSONA_FLAG_SUPPORT (1<<1) +#define PERSONA_FLAG_LARGE (1<<2) // for large ships +#define PERSONA_FLAG_COMMAND (1<<3) // for terran command +// be sure that MAX_PERSONA_TYPES is always 1 greater than the last type bitfield above!!! + +#define PERSONA_FLAG_VASUDAN (1<<30) +#define PERSONA_FLAG_USED (1<<31) + +typedef struct persona_s { + char name[NAME_LENGTH]; + int flags; +} Persona; + +extern Persona Personas[MAX_PERSONAS]; +extern int Num_personas; + +// function to parse a message from either messages.tbl or the mission file. Both files have the +// exact same format, so this function just gets reused in both instances. +void message_parse(); +void persona_parse(); + +void messages_init(); +void message_mission_shutdown(); +void message_queue_process(); +int message_is_playing(); +void message_maybe_distort(); +int message_anim_is_playing(); +void message_kill_all( int kill_all ); + +void message_queue_message( int message_num, int priority, int timing, char *who_from, int source, int group, int delay, int builtin_type=-1 ); + +// functions which send messages to player -- called externally +void message_send_unique_to_player( char *id, void *data, int source, int priority, int group, int delay); +void message_send_builtin_to_player( int type, ship *shipp, int priority, int timing, int group, int delay, int multi_target, int multi_team_filter ); + +// functions to deal with personas +int message_persona_name_lookup( char *name ); + +// preload mission messages (this is called by the level paging code when running with low memory) +void message_pagein_mission_messages(); + +// given a message id#, should it be filtered for me? +int message_filter_multi(int id); + +#endif + diff --git a/include/missionnotesdlg.h b/include/missionnotesdlg.h new file mode 100644 index 0000000..8828787 --- /dev/null +++ b/include/missionnotesdlg.h @@ -0,0 +1,155 @@ +/* + * $Logfile: /Freespace2/code/Fred2/MissionNotesDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Mission notes editor dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 8/23/99 6:21p Jefff + * added "no traitor" option to missions (and fred) + * + * 4 8/23/99 5:04p Jefff + * Added new mission flag to disable built-in messages from playing. + * Added fred support as well. + * + * 3 3/24/99 4:05p Dave + * Put in support for assigning the player to a specific squadron with a + * specific logo. Preliminary work for doing pos/orient checksumming in + * multiplayer to reduce bandwidth. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 16 6/17/98 2:47p Hoffoss + * Changed so missions are single, multi or training. Not supporting + * combos of the above anymore (in Fred). + * + * 15 5/05/98 11:05p Allender + * ability to flag mission as "no promotion" where promotions and badges + * are *not* granted even if they should be. Slight fix to multiplayer + * problem where locking_subsys is wrong for players current target + * + * 14 4/03/98 12:17a Allender + * new sexpression to detect departed or destroyed. optionally disallow + * support ships. Allow docking with escape pods + * + * 13 3/26/98 5:24p Allender + * put in respawn edit box into mission notes dialog. Made loading of + * missions/campaign happen when first entering the game setup screen. + * + * 12 3/16/98 8:27p Allender + * Fred support for two new AI flags -- kamikaze and no dynamic goals. + * + * 11 3/09/98 4:30p Allender + * multiplayer secondary weapon changes. red-alert and cargo-known-delay + * sexpressions. Add time cargo revealed to ship structure + * + * 10 2/04/98 4:32p Allender + * support for multiple briefings and debriefings. Changes to mission + * type (now a bitfield). Bitfield defs for multiplayer modes + * + * 9 1/02/98 4:55p Hoffoss + * Added support for Mission_all_attack flag to Fred and loading/saving + * code. + * + * 8 9/30/97 5:56p Hoffoss + * Added music selection combo boxes to Fred. + * + * 7 8/11/97 3:19p Hoffoss + * Implemented mission description. + * + * 6 6/11/97 2:14p Hoffoss + * Added game type (mission type) selection to Fred. + * + * 5 5/20/97 2:28p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 4 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 3 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// CMissionNotesDlg dialog + +class CMissionNotesDlg : public CDialog +{ +// Construction +public: + int query_modified(); + void OnCancel(); + void OnOK(); + int update_data(); + int initialize_data(); + CMissionNotesDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CMissionNotesDlg) + enum { IDD = IDD_MISSION_NOTES }; + CSpinButtonCtrl m_respawn_spin; + CString m_created; + CString m_modified; + CString m_mission_notes; + CString m_designer_name; + CString m_mission_title; + CString m_mission_desc; + CString m_squad_filename; + CString m_squad_name; + int m_music; + BOOL m_full_war; + BOOL m_red_alert; + BOOL m_scramble; + UINT m_num_respawns; + BOOL m_disallow_support; + BOOL m_no_promotion; + BOOL m_no_builtin_msgs; + BOOL m_no_traitor; + //}}AFX_DATA + + CString m_mission_notes_orig; + CString m_designer_name_orig; + CString m_mission_title_orig; + CString m_mission_desc_orig; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CMissionNotesDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + int m_type; + void set_types(); + + // Generated message map functions + //{{AFX_MSG(CMissionNotesDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnClose(); + afx_msg void OnTraining(); + afx_msg void OnMulti(); + afx_msg void OnSingle(); + afx_msg void OnSquadLogo(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/missionparse.h b/include/missionparse.h new file mode 100644 index 0000000..89a2167 --- /dev/null +++ b/include/missionparse.h @@ -0,0 +1,549 @@ +/* + * $Source$ + * $Revision$ + * $Author$ + * $Date$ + * + * main header file for parsing code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 26 8/23/99 6:21p Jefff + * added "no traitor" option to missions (and fred) + * + * 25 8/23/99 5:04p Jefff + * Added new mission flag to disable built-in messages from playing. + * Added fred support as well. + * + * 24 8/16/99 3:53p Andsager + * Add special warp in interface in Fred and saving / reading. + * + * 23 8/16/99 2:01p Andsager + * Knossos warp-in warp-out. + * + * 22 7/28/99 1:36p Andsager + * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp + * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack + * better. + * + * 21 7/26/99 5:50p Dave + * Revised ingame join. Better? We'll see.... + * + * 20 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 19 7/02/99 4:31p Dave + * Much more sophisticated lightning support. + * + * 18 7/01/99 11:44a Dave + * Updated object sound system to allow multiple obj sounds per ship. + * Added hit-by-beam sound. Added killed by beam sound. + * + * 17 6/28/99 4:51p Andsager + * Add ship-guardian sexp (does not allow ship to be killed) + * + * 16 5/20/99 7:00p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 15 4/26/99 8:49p Dave + * Made all pof based nebula stuff full customizable through fred. + * + * 14 4/26/99 12:49p Andsager + * Add protect object from beam support to Fred + * + * 13 3/24/99 4:05p Dave + * Put in support for assigning the player to a specific squadron with a + * specific logo. Preliminary work for doing pos/orient checksumming in + * multiplayer to reduce bandwidth. + * + * 12 3/01/99 7:39p Dave + * Added prioritizing ship respawns. Also fixed respawns in TvT so teams + * don't mix respawn points. + * + * 11 2/26/99 6:01p Andsager + * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay + * + * 10 2/23/99 8:11p Dave + * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing. + * Small pass over todolist items. + * + * 9 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * 8 2/11/99 2:15p Andsager + * Add ship explosion modification to FRED + * + * 7 2/03/99 12:42p Andsager + * Add escort priority. Modify ship_flags_dlg to include field. Save and + * Load. Add escort priority field to ship. + * + * 6 11/14/98 5:32p Dave + * Lots of nebula work. Put in ship contrails. + * + * 5 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 4 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 3 10/07/98 6:27p Dave + * Globalized mission and campaign file extensions. Removed Silent Threat + * special code. Moved \cache \players and \multidata into the \data + * directory. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 147 8/31/98 2:06p Dave + * Make cfile sort the ordering or vp files. Added support/checks for + * recognizing "mission disk" players. + * + * 146 5/11/98 4:33p Allender + * fixed ingame join problems -- started to work on new object updating + * code (currently ifdef'ed out) + * + * 145 5/05/98 11:05p Allender + * ability to flag mission as "no promotion" where promotions and badges + * are *not* granted even if they should be. Slight fix to multiplayer + * problem where locking_subsys is wrong for players current target + * + * 144 5/04/98 6:06p Lawrance + * Make red alert mode work! + * + * 143 4/20/98 4:56p Allender + * allow AI ships to respawn as many times as there are respawns in the + * mission. + * + * 142 4/14/98 12:08a Allender + * save wingman status information in parse object and restore from parse + * object when respawned + * + * 141 4/13/98 10:25p Hoffoss + * Added a flag for subspace missions, and for aboard the Galatea or + * Bastion. + * + * 140 4/06/98 10:24p Dave + * Fixed up Netgame.respawn for the standalone case. + * + * 139 4/03/98 12:17a Allender + * new sexpression to detect departed or destroyed. optionally disallow + * support ships. Allow docking with escape pods + * + * 138 4/02/98 6:31p Lawrance + * reduce MAX_SUBSYS_STATUS to 125 if DEMO defined + * + * 137 3/26/98 5:24p Allender + * put in respawn edit box into mission notes dialog. Made loading of + * missions/campaign happen when first entering the game setup screen. + * + * 136 3/18/98 10:38p Allender + * added required "num players" for multiplayer missions. Put in required + * "num players" for multiplayer campaigns. Added campaign editor support + * to determine "num players" + * + * 135 3/16/98 8:27p Allender + * Fred support for two new AI flags -- kamikaze and no dynamic goals. + * + * + */ + +#ifndef _PARSE_H +#define _PARSE_H + +#include +#include "parselo.h" +#include "ship.h" +#include "ai.h" +#include "missionbriefcommon.h" +#include "weapon.h" + +#define NUM_NEBULAS 3 // how many background nebulas we have altogether +#define NUM_NEBULA_COLORS 9 + +// arrival anchor types +#define SPECIAL_ARRIVAL_ANCHORS_OFFSET 90000 // offset used to avoid conflicting with ship anchors +#define ANY_FRIENDLY (SPECIAL_ARRIVAL_ANCHORS_OFFSET + 0) +#define ANY_HOSTILE (SPECIAL_ARRIVAL_ANCHORS_OFFSET + 1) +#define ANY_NEUTRAL (SPECIAL_ARRIVAL_ANCHORS_OFFSET + 2) +#define ANY_FRIENDLY_PLAYER (SPECIAL_ARRIVAL_ANCHORS_OFFSET + 3) +#define ANY_HOSTILE_PLAYER (SPECIAL_ARRIVAL_ANCHORS_OFFSET + 4) +#define ANY_NEUTRAL_PLAYER (SPECIAL_ARRIVAL_ANCHORS_OFFSET + 5) + +// update version when mission file format changes, and add approprate code +// to check loaded mission version numbers in the parse code. Also, be sure +// to update both MissionParse and MissionSave (FRED) when changing the +// mission file format! +#define MISSION_VERSION 0.10f +#define FRED_MISSION_VERSION 0.10f + +// maximum number of starting wings for the player +#define MAX_STARTING_WINGS 3 +extern char *Starting_wing_names[MAX_STARTING_WINGS+1]; + +#define WING_PLAYER_BASE 0x80000 // used by Fred to tell ship_index in a wing points to a player + +// defines used for parse_mission() to tell what kind of information to get from the mission file +#define MISSION_PARSE_MISSION_INFO 1 + +// bitfield definitions for missions game types +#define OLD_MAX_GAME_TYPES 4 // needed for compatibility +#define OLD_GAME_TYPE_SINGLE_ONLY 0 +#define OLD_GAME_TYPE_MULTI_ONLY 1 +#define OLD_GAME_TYPE_SINGLE_MULTI 2 +#define OLD_GAME_TYPE_TRAINING 3 + +#define MAX_MISSION_TYPES 5 +#define MISSION_TYPE_SINGLE (1<<0) +#define MISSION_TYPE_MULTI (1<<1) +#define MISSION_TYPE_TRAINING (1<<2) +#define MISSION_TYPE_MULTI_COOP (1<<3) +#define MISSION_TYPE_MULTI_TEAMS (1<<4) +#define MISSION_TYPE_MULTI_DOGFIGHT (1<<5) + +#define MISSION_FLAG_SUBSPACE (1<<0) // mission takes place in subspace +#define MISSION_FLAG_NO_PROMOTION (1<<1) // cannot get promoted or badges in this mission +#define MISSION_FLAG_FULLNEB (1<<2) // mission is a full nebula mission +#define MISSION_FLAG_NO_BUILTIN_MSGS (1<<3) // disables builtin msgs +#define MISSION_FLAG_NO_TRAITOR (1<<4) // player cannot become a traitor + +// some mice macros for mission type +#define IS_MISSION_MULTI_COOP (The_mission.game_type & MISSION_TYPE_MULTI_COOP) +#define IS_MISSION_MULTI_TEAMS (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) +#define IS_MISSION_MULTI_DOGFIGHT (The_mission.game_type & MISSION_TYPE_MULTI_DOGFIGHT) + +#define MISSION_DESC_LENGTH 512 + +typedef struct mission { + char name[NAME_LENGTH]; + char author[NAME_LENGTH]; + float version; + char created[DATE_TIME_LENGTH]; + char modified[DATE_TIME_LENGTH]; + char notes[NOTES_LENGTH]; + char mission_desc[MISSION_DESC_LENGTH]; + int game_type; + int flags; + int num_players; // valid in multiplayer missions -- number of players supported + uint num_respawns; // valid in multiplayer missions -- number of respawns allowed + int red_alert; + int scramble; + int disallow_support; // should support ships be disallowed. + char tour_name[NAME_LENGTH]; + char pre_briefing_cutscene[FILESPEC_LENGTH]; + char pre_mission_cutscene[FILESPEC_LENGTH]; + char next_mission_success[NAME_LENGTH]; + char next_mission_partial[NAME_LENGTH]; + char next_mission_failure[NAME_LENGTH]; + char squad_filename[MAX_FILENAME_LEN+1]; // if the player has been reassigned to a squadron, this is the filename of the logo, otherwise empty string + char squad_name[NAME_LENGTH+1]; // if the player has been reassigned to a squadron, this is the name of the squadron, otherwise empty string +} mission; + +// cargo defines +// NOTE: MAX_CARGO MUST REMAIN <= 64 (CARGO_NO_DEPLETE) for NO_DEPLETE to work. +#define CARGO_INDEX_MASK 0xBF +#define CARGO_NO_DEPLETE 0x40 +#define MAX_CARGO 30 + +extern mission The_mission; +extern char Mission_filename[80]; // filename of mission in The_mission (Fred only) + +#define MAX_IFF 3 +#define MAX_FORMATION_NAMES 3 +#define MAX_STATUS_NAMES 3 +#define MAX_TEAM_NAMES 4 + +// defines for arrival locations. These defines should match their counterparts in the arrival location +// array +#define MAX_ARRIVAL_NAMES 4 +#define ARRIVE_AT_LOCATION 0 +#define ARRIVE_NEAR_SHIP 1 +#define ARRIVE_IN_FRONT_OF_SHIP 2 +#define ARRIVE_FROM_DOCK_BAY 3 + +// defines for departure locations. These defines should match their counterparts in the departure location +// array +#define MAX_DEPARTURE_NAMES 2 +#define DEPART_AT_LOCATION 0 +#define DEPART_AT_DOCK_BAY 1 + +#define MAX_GOAL_TYPE_NAMES 3 + +// alternate ship type names +#define MAX_ALT_TYPE_NAMES 10 +extern char Mission_alt_types[MAX_ALT_TYPE_NAMES][NAME_LENGTH]; +extern int Mission_alt_type_count; + +// These species defines must match the contents of the Species_names[MAX_SPECIES_NAMES] array. +#define MAX_SPECIES_NAMES 3 +#define SPECIES_TERRAN 0 +#define SPECIES_VASUDAN 1 +#define SPECIES_SHIVAN 2 +#define SPECIES_NONE 3 + +#ifdef FS2_DEMO + #define MAX_SUBSYS_STATUS 125 +#else + #define MAX_SUBSYS_STATUS 250 +#endif + +#define MAX_SPECIAL_ARRIVAL_ANCHORS 6 +extern char *Special_arrival_anchor_names[MAX_SPECIAL_ARRIVAL_ANCHORS]; + +extern char *Ship_class_names[MAX_SHIP_TYPES]; +extern char *Iff_names[MAX_IFF]; +extern char *Ai_behavior_names[MAX_AI_BEHAVIORS]; +extern char *Formation_names[MAX_FORMATION_NAMES]; +extern char *Team_names[MAX_TEAM_NAMES]; +extern int Team_names_index_xlate[MAX_TEAM_NAMES_INDEX+1]; +extern char *Status_desc_names[MAX_STATUS_NAMES]; +extern char *Status_type_names[MAX_STATUS_NAMES]; +extern char *Status_target_names[MAX_STATUS_NAMES]; +extern char *Arrival_location_names[MAX_ARRIVAL_NAMES]; +extern char *Departure_location_names[MAX_ARRIVAL_NAMES]; +extern char *Goal_type_names[MAX_GOAL_TYPE_NAMES]; +extern char *Species_names[MAX_SPECIES_NAMES]; +extern char *Reinforcement_type_names[]; +extern char *Object_flags[]; +extern char *Parse_object_flags[]; +extern char *Icon_names[]; + +extern char *Cargo_names[MAX_CARGO]; +extern char Cargo_names_buf[MAX_CARGO][NAME_LENGTH]; + +extern char Mission_parse_storm_name[NAME_LENGTH]; + +extern int Num_iff; +extern int Num_ai_behaviors; +extern int Num_ai_classes; +extern int Num_cargo; +extern int Num_status_names; +extern int Num_arrival_names; +extern int Num_formation_names; +extern int Num_goal_type_names; +extern int Num_team_names; +extern int Num_reinforcement_type_names; +extern int Player_starts; +extern fix Entry_delay_time; + +extern ushort Current_file_checksum; +extern int Current_file_length; + +#define SUBSYS_STATUS_NO_CHANGE -999 + +typedef struct subsys_status { + char name[NAME_LENGTH]; + float percent; // percent damaged + int primary_banks[MAX_PRIMARY_BANKS]; + int secondary_banks[MAX_SECONDARY_BANKS]; + int secondary_ammo[MAX_SECONDARY_BANKS]; + int ai_class; + int subsys_cargo_name; +} subsys_status; + +// a parse object +// information from a $OBJECT: definition is read into this struct to +// be copied into the real object, ship, etc. structs +typedef struct p_object { + struct p_object *next, *prev; + char name[NAME_LENGTH]; + vector pos; + matrix orient; + int ship_class; + int iff; + int team; + int behavior; // ai_class; + int ai_goals; // sexp of lists of goals that this ship should try and do + char cargo1; + int status_count; + int status_type[MAX_OBJECT_STATUS]; + int status[MAX_OBJECT_STATUS]; + int target[MAX_OBJECT_STATUS]; + int arrival_location; + int arrival_distance; // used when arrival location is near or in front of some ship + int arrival_anchor; // ship used for anchoring an arrival point + int arrival_cue; // Index in Sexp_nodes of this sexp. + int arrival_delay; + int subsys_index; // index into subsys_status array + int subsys_count; // number of elements used in subsys_status array + int initial_velocity; + int initial_hull; + int initial_shields; + + int departure_location; + int departure_anchor; + int departure_cue; // Index in Sexp_nodes of this sexp. + int departure_delay; + + char misc[NAME_LENGTH]; + int determination; + int wingnum; // set to -1 if not in a wing -- Wing array index otherwise + int flags; // mission savable flags + int escort_priority; // priority in escort list + int ai_class; + int hotkey; // hotkey number (between 0 and 9) -1 means no hotkey + int score; + int orders_accepted; // which orders this ship will accept from the player + char docked_with[NAME_LENGTH]; + char docker_point[NAME_LENGTH]; + char dockee_point[NAME_LENGTH]; + int group; // group object is within or -1 if none. + int persona_index; + float kamikaze_damage; // base damage for a kamikaze attack + int special_exp_index; + ushort net_signature; // network signature this object can have + + char wing_status_wing_index; // wing index (0-4) in wingman status gauge + char wing_status_wing_pos; // wing position (0-5) in wingman status gauge + + uint respawn_count; // number of respawns for this object. Applies only to player wing ships in multiplayer + int respawn_priority; // priority this ship has for controlling respawn points + + char alt_type_index; // optional alt type index +} p_object; + +// defines for flags used for p_objects when they are created. Used to help create +// special circumstances for those ships. The order that these bitfields appear +// in this list MUST match the order that they appear in the flags array in MissionParse.cpp!!!! + +#define MAX_PARSE_OBJECT_FLAGS 20 + +#define P_OF_CARGO_KNOWN (1<<0) +#define P_SF_IGNORE_COUNT (1<<1) +#define P_OF_PROTECTED (1<<2) +#define P_SF_REINFORCEMENT (1<<3) +#define P_OF_NO_SHIELDS (1<<4) +#define P_SF_ESCORT (1<<5) +#define P_OF_PLAYER_START (1<<6) +#define P_SF_NO_ARRIVAL_MUSIC (1<<7) +#define P_SF_NO_ARRIVAL_WARP (1<<8) +#define P_SF_NO_DEPARTURE_WARP (1<<9) +#define P_SF_LOCKED (1<<10) +#define P_SF_INVULNERABLE (1<<11) +#define P_SF_HIDDEN_FROM_SENSORS (1<<12) +#define P_SF_SCANNABLE (1<<13) // ship is a "scannable" ship +#define P_AIF_KAMIKAZE (1<<14) +#define P_AIF_NO_DYNAMIC (1<<15) +#define P_SF_RED_ALERT_STORE_STATUS (1<<16) +#define P_OF_BEAM_PROTECTED (1<<17) +#define P_SF_GUARDIAN (1<<18) +#define P_KNOSSOS_WARP_IN (1<<19) + + +// the following parse object flags are used internally by Freespace +#define P_SF_USE_UNIQUE_ORDERS (1<<26) // tells a newly created ship to use the default orders for that ship +#define P_SF_INITIALLY_DOCKED (1<<27) // is this parse object initially docked with something else +#define P_SF_CANNOT_ARRIVE (1<<28) // used to indicate that this ship's arrival cue will never be true +#define P_SF_WARP_BROKEN (1<<29) // warp engine should be broken for this ship +#define P_SF_WARP_NEVER (1<<30) // warp drive is destroyed +#define P_SF_PLAYER_START_VALID (1<<31) // this is a valid player start object + +extern p_object ship_arrival_list; // used by sexpression parser + +#ifdef FS2_DEMO + #define MAX_SHIP_ARRIVALS 45 + #define MAX_WING_ARRIVALS 10 +#else + #define MAX_SHIP_ARRIVALS 90 // maximum of 90 objects can arrive later + #define MAX_WING_ARRIVALS 20 // maximum of 20 wings can arrive later +#endif + +extern p_object ship_arrivals[MAX_SHIP_ARRIVALS]; +extern int num_ship_arrivals; + +extern p_object Support_ship_pobj, *Arriving_support_ship; + +typedef struct { + int default_ship; // default ship type for player start point (recommended choice) + int number_choices; // number of ship choices inside ship_list + int ship_list[MAX_SHIP_TYPES]; + int ship_count[MAX_SHIP_TYPES]; + int weaponry_pool[MAX_WEAPON_TYPES]; +} team_data; + +#define MAX_P_WINGS 16 +#define MAX_SHIP_LIST 16 + +#define TOKEN_LENGTH 32 + +extern team_data Team_data[MAX_TEAMS]; +extern subsys_status Subsys_status[MAX_SUBSYS_STATUS]; +extern int Subsys_index; + +extern vector Parse_viewer_pos; +extern matrix Parse_viewer_orient; + +extern int Mission_arrival_timestamp; +extern int Mission_departure_timestamp; +extern fix Mission_end_time; + +extern char Parse_names[MAX_SHIPS + MAX_WINGS][NAME_LENGTH]; +extern int Num_parse_names; +extern int Num_teams; + +extern char Player_start_shipname[NAME_LENGTH]; +extern int Player_start_shipnum; +extern p_object Player_start_pobject; + +extern int Mission_palette; // index of palette file to use for mission +extern int Nebula_index; // index into Nebula_filenames[] of nebula to use in mission. +extern char *Nebula_filenames[NUM_NEBULAS]; +extern char *Nebula_colors[NUM_NEBULA_COLORS]; +extern p_object *Arriving_support_ship; + +extern char Neb2_texture_name[MAX_FILENAME_LEN]; + +int parse_main(char *mission_name, int flags = 0); +int mission_parse_ship_arrived(char *shipname); +p_object *mission_parse_get_arrival_ship( char *name ); +p_object *mission_parse_get_arrival_ship( ushort net_signature ); +p_object *mission_parse_get_original_ship( ushort net_signature ); +int parse_create_object(p_object *objp); + +// used in squadmate messaging stuff to create wings from reinforcements. +int parse_wing_create_ships(wing *wingp, int num_to_create, int force = 0, int specific_instance = -1 ); + +// function for getting basic mission data without loading whole mission +int mission_parse_is_multi(char *filename, char *mission_name ); +int mission_parse_get_multi_mission_info(char *filename); + +// called externally from multiplayer code +void mission_do_departure(object *objp); + +// called externally from Freespace.cpp +void mission_parse_fixup_players(void); + +// get a index to a perminently kept around name of a ship or wing +int get_parse_name_index(char *name); + +// called from freespace game level loop +void mission_parse_eval_stuff(); + +// function to set the ramaing time left in the mission +void mission_parse_set_end_time( int seconds ); + +// code to warp in a repair ship. +void mission_warp_in_support_ship( object *requester_objp ); +int mission_is_support_ship_arriving( void ); +void mission_add_to_arriving_support( object *requester_objp ); +int mission_is_repair_scheduled( object *objp ); +int mission_remove_scheduled_repair( object *objp ); +void mission_parse_support_arrived( int objnum ); + +// alternate name stuff +int mission_parse_lookup_alt(char *name); +void mission_parse_lookup_alt_index(int index, char *out); +int mission_parse_add_alt(char *name); +void mission_parse_reset_alt(); + +// code to save/restore mission parse stuff +int get_mission_info(char *filename, mission *missionp = NULL); + +#endif + diff --git a/include/missionpause.h b/include/missionpause.h new file mode 100644 index 0000000..efcbc33 --- /dev/null +++ b/include/missionpause.h @@ -0,0 +1,56 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionPause.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 6/09/99 2:17p Dave + * Fixed up pleasewait bitmap rendering. + * + * + * $NoKeywords: $ + */ + +#ifndef _MISSION_PAUSE_HEADER_FILE +#define _MISSION_PAUSE_HEADER_FILE + +#include "2d.h" + +// ---------------------------------------------------------------------------------------------------------------- +// PAUSE DEFINES/VARS +// + +// pause bitmap display stuff +extern int Please_wait_coords[GR_NUM_RESOLUTIONS][4]; + + +// ---------------------------------------------------------------------------------------------------------------- +// PAUSE FUNCTIONS +// + +// initialize the pause screen +void pause_init(int multi); + +// pause do frame - will handle running multiplayer operations if necessary +void pause_do(int multi); + +// close the pause screen +void pause_close(int multi); + +// debug pause init +void pause_debug_init(); + +// debug pause do frame +void pause_debug_do(); + +// debug pause close +void pause_debug_close(); + +#endif + diff --git a/include/missionrecommend.h b/include/missionrecommend.h new file mode 100644 index 0000000..a0fa0dc --- /dev/null +++ b/include/missionrecommend.h @@ -0,0 +1,31 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionRecommend.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the post-mission recommendation interface + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 2 9/30/97 8:49p Lawrance + * implementing an integrated debriefing screen + * + * 1 9/30/97 9:26a Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __FREESPACE_MISSIONRECOMMEND_H__ +#define __FREESPACE_MISSIONRECOMMEND_H__ + +#endif + diff --git a/include/missionsave.h b/include/missionsave.h new file mode 100644 index 0000000..e66d4bf --- /dev/null +++ b/include/missionsave.h @@ -0,0 +1,139 @@ +/* + * $Logfile: /Freespace2/code/FRED2/MissionSave.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Mission saving in Fred. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 1/19/99 3:57p Andsager + * Round 2 of variables + * + * 3 10/29/98 6:49p Dave + * Finished up Fred support for externalizing mission and campaign files. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:00p Dave + * + * 21 9/10/98 1:17p Dave + * Put in code to flag missions and campaigns as being MD or not in Fred + * and Freespace. Put in multiplayer support for filtering out MD + * missions. Put in multiplayer popups for warning of non-valid missions. + * + * 20 4/14/98 11:55a Allender + * add end-of-campaign sexpression to allow for mission replay at the end + * of campaigns + * + * 19 3/05/98 3:59p Hoffoss + * Added a bunch of new command brief stuff, and asteroid initialization + * to Fred. + * + * 18 9/30/97 5:56p Hoffoss + * Added music selection combo boxes to Fred. + * + * 17 8/25/97 5:56p Hoffoss + * Added multiple asteroid field support, loading and saving of asteroid + * fields, and ship score field to Fred. + * + * 16 8/17/97 10:22p Hoffoss + * Fixed several bugs in Fred with Undo feature. In the process, recoded + * a lot of CFile.cpp. + * + * 15 6/17/97 3:01p Lawrance + * allow FRED to save new briefing format + * + * 14 6/09/97 4:57p Hoffoss + * Added autosave and undo to Fred. + * + * 13 6/05/97 6:10p Hoffoss + * Added features: Autosaving, object hiding. Also fixed some minor bugs. + * + * 12 5/13/97 10:52a Hoffoss + * Added campaign saving code. + * + * 11 4/21/97 5:02p Hoffoss + * Player/player status editing supported, and both saved and loaded from + * Mission files. + * + * 10 4/16/97 2:05p Hoffoss + * Mission saving and loading of turret info now implemented. + * + * 9 3/10/97 6:43p Hoffoss + * Standardized docking goal usage by fred to use names instead of + * indexes. + * + * 8 2/05/97 2:57p Hoffoss + * Added support for wing goals (initial orders) in Fred. + * + * 7 1/30/97 2:24p Hoffoss + * Added remaining mission file structures and implemented load/save of + * them. + * + * $NoKeywords: $ + */ + +#ifndef __MISSION_SAVE_CPP__ +#define __MISSION_SAVE_CPP__ + +#include +#include "missionparse.h" +#include "parselo.h" +#include "ailocal.h" +#include "ai.h" + +#define BACKUP_DEPTH 9 + +class CFred_mission_save { +private: + char *raw_ptr; + int err; + CFILE *fp; + + int save_mission_info(); + int save_plot_info(); + int save_variables(); +// int save_briefing_info(); + int save_cmd_brief(); + int save_cmd_briefs(); + int save_briefing(); + int save_debriefing(); + int save_players(); + int save_objects(); + int save_common_object_data(object *objp, ship *shipp); + int save_wings(); + int save_goals(); + int save_waypoints(); + int save_waypoint_list(waypoint_list &w); + int save_vector(vector &v); + int save_matrix(matrix &m); + int save_messages(); + int save_events(); + int save_asteroid_fields(); + int save_music(); + void save_campaign_sexp(int node, int link); + +public: + void save_turret_info(ship_subsys *ptr, int ship); + int save_bitmaps(); + int save_reinforcements(); + void save_ai_goals(ai_goals *goalp, int ship); + int fout(char *format, ...); + int fout_ext(char *format, ...); + void parse_comments(int = 1); + CFred_mission_save() : err(0), raw_ptr(Mission_text_raw) { } + int save_mission_file(char *pathname); + int autosave_mission_file(char *pathname); + int save_campaign_file(char *pathname); +}; + +#endif + diff --git a/include/missionscreencommon.h b/include/missionscreencommon.h new file mode 100644 index 0000000..b0008ab --- /dev/null +++ b/include/missionscreencommon.h @@ -0,0 +1,201 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionScreenCommon.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $NoKeywords: $ + * + */ + +#ifndef _MISSION_SCREEN_COMMON_HEADER_FILE +#define _MISSION_SCREEN_COMMON_HEADER_FILE + +#include "ui.h" +#include "weapon.h" + +#define BACKGROUND_FRAME_TO_START_SHIP_ANIM 87 +#define BUTTON_SLIDE_IN_FRAME 1 + +/////////////////////////////////////////////////////// +// Common to briefing/ship selection/weapons loadout +/////////////////////////////////////////////////////// + +#define COMMON_BRIEFING_REGION 0 +#define COMMON_SS_REGION 1 +#define COMMON_WEAPON_REGION 2 +#define COMMON_COMMIT_REGION 5 +#define COMMON_HELP_REGION 6 +#define COMMON_OPTIONS_REGION 7 +#define NUM_COMMON_REGIONS 6 + +#define NUM_COMMON_BUTTONS 6 + +struct brief_common_buttons { + char *filename; + int x, y; + int xt, yt; + int hotspot; + int repeat; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + brief_common_buttons(char *name, int x1, int y1, int xt1, int yt1, int h, int r = 0) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h), repeat(r) {} +}; + +extern brief_common_buttons Common_buttons[3][GR_NUM_RESOLUTIONS][NUM_COMMON_BUTTONS]; + +extern int Background_playing; + +extern int Common_select_inited; +extern int Current_screen; + +extern int Common_team; + +extern int Drop_icon_mflag; +extern int Drop_on_wing_mflag; +extern int Brief_mouse_up_flag; +extern int Mouse_down_last_frame; + + +extern int Wing_slot_empty_bitmap; +extern int Wing_slot_disabled_bitmap; + +extern int Flash_timer; // timestamp used to start flashing +extern int Flash_toggle; // timestamp used to toggle flashing +extern int Flash_bright; // state of button to flash + +void common_button_do(int i); + +// common_select_init() performs initialization common to the briefing/ship select/weapon select +// screens. This includes loading/setting the palette, loading the background animation, loading +// the screen switching animations, loading the button animation frames +void common_select_init(); +int common_select_do(float frametime); +void common_select_close(); +void common_draw_buttons(); +void common_check_buttons(); +void common_check_keys(int k); +void commit_pressed(); +void common_render(float frametime); +void common_buttons_init(UI_WINDOW *ui_window); +void common_buttons_maybe_reload(UI_WINDOW *ui_window); +void common_render_selected_screen_button(); +void common_reset_buttons(); +void common_redraw_pressed_buttons(); +void common_maybe_clear_focus(); +void ship_select_common_init(); + +void common_set_interface_palette(char *filename = NULL); // set the interface palette +void common_free_interface_palette(); // restore game palette + +void load_wing_icons(char *filename); +void unload_wing_icons(); + +void common_flash_button_init(); +int common_flash_bright(); + +// functions for the multiplayer chat window +void common_render_chat_window(); +void multi_chat_scroll_up(); +void multi_chat_scroll_down(); + +void set_active_ui(UI_WINDOW *ui_window); + +// music functions exported for multiplayer team selection screen to start briefing music +void common_music_init( int score_index ); +void common_music_do(); +void common_music_close(); + +int common_scroll_down_pressed(int *start, int size, int max_show); +int common_scroll_up_pressed(int *start, int size, int max_show); + +////////////////////////////////////////////////////////////////////////////////////// +// NEWSTUFF BEGIN +////////////////////////////////////////////////////////////////////////////////////// + +#define MAX_WL_PRIMARY 3 +#define MAX_WL_SECONDARY 4 +#define MAX_WL_WEAPONS (MAX_WL_PRIMARY+MAX_WL_SECONDARY) + +#define MAX_WING_SLOTS 4 +#define MAX_WING_BLOCKS 3 +#define MAX_WSS_SLOTS (MAX_WING_BLOCKS*MAX_WING_SLOTS) + +#define WING_SLOT_FILLED (1<<0) +#define WING_SLOT_EMPTY (1<<1) +#define WING_SLOT_DISABLED (1<<2) +#define WING_SLOT_IS_PLAYER (1<<3) +#define WING_SLOT_LOCKED (1<<4) + +#define WING_SLOT_IGNORE (WING_SLOT_DISABLED|WING_SLOT_LOCKED) + +// different operations used in xx_apply() +#define WSS_DUMP_TO_LIST 0 +#define WSS_GRAB_FROM_LIST 1 +#define WSS_SWAP_SLOT_SLOT 2 +#define WSS_SWAP_LIST_SLOT 3 + +// icons +#define NUM_ICON_FRAMES 6 +#define ICON_FRAME_NORMAL 0 +#define ICON_FRAME_HOT 1 +#define ICON_FRAME_SELECTED 2 +#define ICON_FRAME_PLAYER 3 +#define ICON_FRAME_DISABLED 4 +#define ICON_FRAME_DISABLED_HIGH 5 + +////////////////////////////////////////////// +// Slots +////////////////////////////////////////////// +typedef struct wss_unit { + int ship_class; + int wep[MAX_WL_WEAPONS]; + int wep_count[MAX_WL_WEAPONS]; +} wss_unit; + +extern wss_unit Wss_slots_teams[MAX_TEAMS][MAX_WSS_SLOTS]; +extern wss_unit *Wss_slots; + +extern int Wss_num_wings; // number of player wings +extern int Wss_num_wings_teams[MAX_TEAMS]; + +////////////////////////////////////////////// +// Weapon pool +////////////////////////////////////////////// +extern int Wl_pool_teams[MAX_TEAMS][MAX_WEAPON_TYPES]; +extern int *Wl_pool; + +////////////////////////////////////////////// +// Ship pool +////////////////////////////////////////////// +extern int Ss_pool_teams[MAX_TEAMS][MAX_SHIP_TYPES]; +extern int *Ss_pool; + +////////////////////////////////////////////// +// Saving loadout +////////////////////////////////////////////// +typedef struct loadout_data +{ + char filename[MAX_FILENAME_LEN]; // mission filename + char last_modified[DATE_TIME_LENGTH]; // when mission was last modified + wss_unit unit_data[MAX_WSS_SLOTS]; // ship and weapon data + int weapon_pool[MAX_WEAPON_TYPES]; // available weapons + int ship_pool[MAX_SHIP_TYPES]; // available ships +} loadout_data; + +extern loadout_data Player_loadout; + +void wss_save_loadout(); +void wss_restore_loadout(); +void wss_direct_restore_loadout(); + +int wss_get_mode(int from_slot, int from_list, int to_slot, int to_list, int wl_ship_slot); +int store_wss_data(ubyte *block, int max_size, int sound,int player_index); +int restore_wss_data(ubyte *block); + +/////////////////////////////////////////////////////////// +// NEWSTUFF END +/////////////////////////////////////////////////////////// + +#endif + diff --git a/include/missionshipchoice.h b/include/missionshipchoice.h new file mode 100644 index 0000000..7473a91 --- /dev/null +++ b/include/missionshipchoice.h @@ -0,0 +1,290 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionShipChoice.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file to support functions that allow player ship selection for the mission + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 7/15/99 6:36p Jamesa + * Moved default ship name into the ships.tbl + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 21 3/05/98 5:03p Dave + * More work on team vs. team support for multiplayer. Need to fix bugs in + * weapon select. + * + * 20 2/24/98 6:21p Lawrance + * Integrate new reset button into loadout screens + * + * 19 2/18/98 3:56p Dave + * Several bugs fixed for mp team select screen. Put in standalone packet + * routing for team select. + * + * 18 2/17/98 6:07p Dave + * Tore out old multiplayer team select screen, installed new one. + * + * 17 1/15/98 4:11p Lawrance + * Add call to check if slot is player occupied. + * + * 16 1/09/98 6:06p Dave + * Put in network sound support for multiplayer ship/weapon select + * screens. Made clients exit game correctly through warp effect. Fixed + * main hall menu help overlay bug. + * + * 15 12/29/97 4:21p Lawrance + * Flash buttons on briefing/ship select/weapons loadout when enough time + * has elapsed without activity. + * + * 14 12/24/97 1:19p Lawrance + * fix some bugs with the multiplayer ship/weapons loadout + * + * 13 12/23/97 5:25p Allender + * more fixes to multiplayer ship selection. Fixed strange reentrant + * problem with cf_callback when loading freespace data + * + * 12 12/22/97 6:18p Lawrance + * Get save/restore of player loadout working with new code + * + * 11 12/22/97 1:40a Lawrance + * Re-write ship select/weapons loadout to be multiplayer friendly + * + * 10 12/19/97 1:23p Dave + * Put in multiplayer groundwork for new weapon/ship select screens. + * + * 9 12/18/97 8:59p Dave + * Finished putting in basic support for weapon select and ship select in + * multiplayer. + * + * 8 12/18/97 10:24a Lawrance + * Fix problem with ships having no weapons due to bogus weapons pools. + * + * 7 12/08/97 9:37p Lawrance + * fix up auto-restore of player ship selection and weapons + * + * 6 12/03/97 1:22p Lawrance + * implement saving/restoring of ship selection and weapons loadout + * + * 5 12/02/97 10:51p Lawrance + * implement save/restore of ship selection and weapon loadouts + * + * 4 11/27/97 10:03a Lawrance + * supporting SF_LOCKED flag + * + * 3 10/04/97 5:56p Lawrance + * keep track of original ship class in ship selection + * + * 2 10/04/97 12:00a Lawrance + * grey out background when help overlay is activated + * + * 1 9/30/97 10:16a Lawrance + * move files from Mission lib to MissionUI lib + * + * 34 9/25/97 3:49p Lawrance + * add a disabled icon to ship icon frame list + * + * 33 9/24/97 11:43p Hoffoss + * Changed Parse_player to Team_data, removed the fields we don't want in + * it anymore, and fixed some code that had problems with that. + * + * 32 9/24/97 5:03p Dave + * Spliced a bunch of stuff into MissionScreenCommon.[h,cpp] + * + * 31 9/23/97 11:55p Lawrance + * add ss_return_name() function + * + * 30 9/19/97 4:22p Allender + * externed some functions that multi team selection screen will use + * + * 29 9/18/97 7:58a Lawrance + * fix some bugs associated with the player ship being created early on + * + * 28 9/16/97 5:15p Allender + * fixed link problem with Fred + * + * 27 9/07/97 10:04p Lawrance + * make drag/drop and selection work as Windows does with mouse-button up + * for selection + * + * 26 8/30/97 12:24p Lawrance + * supporting animations in the weapons loadout screen, fixed some bugs + * + * 25 8/29/97 7:33p Lawrance + * further work on weapons loadout + * + * 24 8/24/97 5:24p Lawrance + * improve drawing of buttons + * + * 23 8/18/97 5:28p Lawrance + * integrating sounds for when mouse goes over an active control + * + * 22 8/17/97 2:41p Lawrance + * improving interface + * + * 21 8/15/97 8:00p Lawrance + * integrating new art for the briefing screens + * + * 20 8/11/97 9:47p Lawrance + * get multiplayer chat window hooks working + * + * 19 7/23/97 11:36a Lawrance + * support common buttons through the briefing/ship select/weapons + * loadout, be able to hide buttons when necessary + * + * 18 7/21/97 11:41a Lawrance + * make playback time of .ani files keyed of frametime + * + * 17 7/20/97 6:59p Lawrance + * changed name of some anim functions to be more consistent + * + * 16 7/14/97 3:58p Lawrance + * limit frametime to 33 ms for animation timing + * + * 15 6/26/97 12:12a Lawrance + * supporting anti-aliased bitmap animations + * + * 14 6/24/97 11:46p Lawrance + * supporting icon text and rotating models + * + * 13 6/12/97 11:09p Lawrance + * getting map and text integrated into briefing + * + * 12 6/12/97 11:28a Lawrance + * separating FRED dependant code + * + * 11 6/12/97 2:48a Lawrance + * integrating briefing into ship select / weapon loadout screen + * + * 10 5/23/97 2:01p Lawrance + * added update_player_weapons() + * + * 9 5/20/97 2:44p Allender + * move player ship creation into player_level_init which is done before + * most other things when a level loads + * + * 8 3/28/97 12:09p Lawrance + * change create_default_player_ship() to allow the default to be the last + * ship the player has flown + * + * 7 3/20/97 3:01p Lawrance + * made scroll buttons repeat action when held down, don't do button + * actions until down and release + * + * 6 3/07/97 8:14p Lawrance + * added code to drag and drop to wing formations + * + * 5 2/26/97 10:05a Lawrance + * move ship_create() to end of loop when commit pressed, so don't skip on + * animation + * + * 4 2/25/97 11:11a Lawrance + * ship selection and weapon loadout interfaces working at basic level + * + * 3 12/30/96 1:58a Lawrance + * supporting ship choice in multiplayer + * + * 2 11/19/96 1:21p Lawrance + * got ship selection working inside out + * + * $NoKeywords: $ + * +*/ + +#ifndef __MISSIONSHIPCHOICE_H__ +#define __MISSIONSHIPCHOICE_H__ + +#include "missionscreencommon.h" +#include "missionparse.h" + +/////////////////////////////////////////////////////// +// Ships selection hot spots +/////////////////////////////////////////////////////// +#define SHIP_SELECT_SHIP_SCROLL_UP 8 +#define SHIP_SELECT_SHIP_SCROLL_DOWN 9 + +#define SHIP_SELECT_ICON_0 10 +#define SHIP_SELECT_ICON_1 11 +#define SHIP_SELECT_ICON_2 12 +#define SHIP_SELECT_ICON_3 13 + +#define WING_0_SHIP_0 14 +#define WING_0_SHIP_1 15 +#define WING_0_SHIP_2 16 +#define WING_0_SHIP_3 17 +#define WING_1_SHIP_0 18 +#define WING_1_SHIP_1 19 +#define WING_1_SHIP_2 20 +#define WING_1_SHIP_3 21 +#define WING_2_SHIP_0 22 +#define WING_2_SHIP_1 23 +#define WING_2_SHIP_2 24 +#define WING_2_SHIP_3 25 + +#define SHIP_SELECT_RESET 39 + +#define NUM_SHIP_SELECT_REGIONS (NUM_COMMON_REGIONS + 19) + +extern int Ship_select_open; // This game-wide global flag is set to 1 to indicate that the ship + // select screen has been opened and memory allocated. This flag + // is needed so we can know if ship_select_close() needs to called if + // restoring a game from the Options screen invoked from ship select + +extern int Commit_pressed; // flag to indicate that the commit button was pressed + // use a flag, so the ship_create() can be done at the end of the loop + +extern char default_player_ship[255]; +extern int Select_default_ship; + +void draw_wing_block(int wb_num, int hot_slot, int selected_slot, int class_select); +void ship_select_init(); +void ship_select_do(float frametime); +void ship_select_close(); +void ship_select_common_init(); +void ship_stop_animation(); +int ss_get_ship_class(int ship_entry_index); +int ss_get_selected_ship(); + +void ss_blit_ship_icon(int x,int y,int ship_class,int bmap_num); + +// called from weapon select +int ss_return_ship(int wing_block, int wing_slot, int *ship_index, p_object **ppobjp); +void ss_return_name(int wing_block, int wing_slot, char *name); +int ss_return_original_ship_class(int slot_num); +int ss_return_saindex(int slot_num); +int ss_disabled_slot(int slot_num); +int ss_wing_slot_is_console_player(int index); + +// lock/unlock any necessary slots for multiplayer +void ss_recalc_multiplayer_slots(); + +int create_default_player_ship( int use_last_flown = 1 ); +void update_player_ship(int si_index); + +void ss_synch_interface(); + +// set the necessary pointers +void ss_set_team_pointers(int team); + +// called by multiplayer team select to set the slot based flags +void ss_make_slot_empty(int slot_index); +void ss_make_slot_full(int slot_index); + +int ss_dump_to_list(int from_slot, int to_list, int *sound); +int ss_swap_slot_slot(int from_slot, int to_slot, int *sound); +int ss_grab_from_list(int from_list, int to_slot, int *sound); +int ss_swap_list_slot(int from_list, int to_slot, int *sound); + +void ss_apply(int mode, int from_slot,int from_index,int to_slot,int to_index,int player_index = -1); +void ss_drop(int from_slot,int from_index,int to_slot,int to_index,int player_index = -1); + +#endif /* __MISSIONSHIPCHOICE_H__ */ + diff --git a/include/missionstats.h b/include/missionstats.h new file mode 100644 index 0000000..77fd730 --- /dev/null +++ b/include/missionstats.h @@ -0,0 +1,28 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionStats.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the post-mission stats interface + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 1 9/30/97 9:25a Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __FREESPACE_MISSIONSTATS_H__ +#define __FREESPACE_MISSIONSTATS_H__ + +#endif + diff --git a/include/missiontraining.h b/include/missiontraining.h new file mode 100644 index 0000000..e9d945f --- /dev/null +++ b/include/missiontraining.h @@ -0,0 +1,59 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionTraining.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Special code for training missions. Stuff like displaying training messages in + * the special training window, listing the training objectives, etc. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 7 4/16/98 4:33p Hoffoss + * Added support for detecting instructor terminating training due to + * player shooting at him. + * + * 6 4/15/98 5:25p Lawrance + * extern Training_msg_visible + * + * 5 1/05/98 4:04p Hoffoss + * Changed training-msg sexp operator to allow it to control the length of + * time a message is displayed for. + * + * 4 10/17/97 6:39p Hoffoss + * Added delayability to key-pressed operator and training-msg operator. + * + * 3 10/10/97 6:15p Hoffoss + * Implemented a training objective list display. + * + * 2 10/09/97 4:44p Hoffoss + * Dimmed training window glass and made it less transparent, added flags + * to events, set he stage for detecting current events. + * + * 1 10/09/97 2:41p Hoffoss + * + * $NoKeywords: $ + */ + +extern int Training_msg_method; +extern int Training_num_lines; +extern int Training_msg_visible; +extern int Training_failure; + +void training_mission_init(); +void training_mission_shutdown(); +void training_check_objectives(); +void message_training_que(char *text, int timestamp, int length = -1); +void message_training_setup(int num, int length = -1); +void message_training_display(); +void message_translate_tokens(char *buf, char *text); +void training_fail(); + diff --git a/include/missionweaponchoice.h b/include/missionweaponchoice.h new file mode 100644 index 0000000..2cde5d5 --- /dev/null +++ b/include/missionweaponchoice.h @@ -0,0 +1,171 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionWeaponChoice.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the weapon loadout screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 21 4/17/98 5:27p Dave + * More work on the multi options screen. Fixed many minor ui todo bugs. + * + * 20 3/31/98 1:50p Duncan + * ALAN: fix bugs with selecting alternate weapons + * + * 19 2/28/98 7:04p Lawrance + * Don't show reset button in multiplayer + * + * 18 2/24/98 6:21p Lawrance + * Integrate new reset button into loadout screens + * + * 17 1/17/98 4:14p Lawrance + * fix mask problem with primary weapon scrolling + * + * 16 1/17/98 1:32a Lawrance + * fix mask problem that was mixing up scroll up and scroll down on + * weapons loadout + * + * 15 1/09/98 6:06p Dave + * Put in network sound support for multiplayer ship/weapon select + * screens. Made clients exit game correctly through warp effect. Fixed + * main hall menu help overlay bug. + * + * 14 12/24/97 1:19p Lawrance + * fix some bugs with the multiplayer ship/weapons loadout + * + * 13 12/23/97 5:25p Allender + * more fixes to multiplayer ship selection. Fixed strange reentrant + * problem with cf_callback when loading freespace data + * + * 12 12/22/97 6:18p Lawrance + * Get save/restore of player loadout working with new code + * + * 11 12/22/97 1:40a Lawrance + * Re-write ship select/weapons loadout to be multiplayer friendly + * + * 10 12/19/97 1:23p Dave + * Put in multiplayer groundwork for new weapon/ship select screens. + * + * 9 12/18/97 8:59p Dave + * Finished putting in basic support for weapon select and ship select in + * multiplayer. + * + * 8 12/17/97 7:42p Lawrance + * re-work how weapons are re-set when ships change in ship select + * + * 7 12/17/97 4:53p Lawrance + * changes to support multiplayer + * + * 6 12/17/97 2:33p Dave + * Finished up basic weapon select support for multiplayer. + * + * 5 12/16/97 6:17p Dave + * Put in primary weapon support for multiplayer weapon select screen. + * + * 4 12/03/97 1:22p Lawrance + * implement saving/restoring of ship selection and weapons loadout + * + * 3 12/02/97 10:51p Lawrance + * implement save/restore of ship selection and weapon loadouts + * + * 2 11/15/97 6:12p Lawrance + * don't allow Player ship to have all weapons removed + * + * 1 9/30/97 10:16a Lawrance + * move files from Mission lib to MissionUI lib + * + * 8 9/18/97 7:58a Lawrance + * fix some bugs associated with the player ship being created early on + * + * 7 8/30/97 12:24p Lawrance + * supporting animations in the weapons loadout screen, fixed some bugs + * + * 6 8/29/97 7:33p Lawrance + * further work on weapons loadout + * + * 5 8/15/97 8:00p Lawrance + * integrating new art for the briefing screens + * + * 4 7/23/97 11:36a Lawrance + * support common buttons through the briefing/ship select/weapons + * loadout, be able to hide buttons when necessary + * + * 3 7/14/97 3:58p Lawrance + * limit frametime to 33 ms for animation timing + * + * 2 2/25/97 11:11a Lawrance + * ship selection and weapon loadout interfaces working at basic level + * + * $NoKeywords: $ + */ + + +#ifndef __MISSION_WEAPON_CHOICE_H__ +#define __MISSION_WEAPON_CHOICE_H__ + +#include "missionparse.h" +#include "missionscreencommon.h" + +// mask regions for icons in the scrollable lists +#define ICON_PRIMARY_0 28 +#define ICON_PRIMARY_1 29 +#define ICON_PRIMARY_2 30 +#define ICON_PRIMARY_3 31 +#define ICON_SECONDARY_0 10 +#define ICON_SECONDARY_1 11 +#define ICON_SECONDARY_2 12 +#define ICON_SECONDARY_3 13 + +// mask regions for icons that sit above the ship +#define ICON_SHIP_PRIMARY_0 32 +#define ICON_SHIP_PRIMARY_1 33 +#define ICON_SHIP_PRIMARY_2 34 +#define ICON_SHIP_SECONDARY_0 35 +#define ICON_SHIP_SECONDARY_1 36 +#define ICON_SHIP_SECONDARY_2 37 +#define ICON_SHIP_SECONDARY_3 38 + +// mask region for weapon loadout specific buttons +#define PRIMARY_SCROLL_UP 27 +#define PRIMARY_SCROLL_DOWN 26 +#define SECONDARY_SCROLL_UP 9 +#define SECONDARY_SCROLL_DOWN 8 +#define WL_RESET_BUTTON_MASK 39 + +#define NUM_WEAPON_REGIONS (NUM_COMMON_REGIONS + 32) + +void weapon_select_init(); +void weapon_select_common_init(); +void weapon_select_do(float frametime); +void weapon_select_close(); + +void wl_update_parse_object_weapons(p_object *pobjp, wss_unit *slot); +int wl_update_ship_weapons(int objnum, wss_unit *slot); +void wl_bash_ship_weapons(ship_weapon *swp, wss_unit *slot); + +void wl_set_default_weapons(int index, int ship_class); +void wl_reset_to_defaults(); + +// Set selected slot to first placed ship +void wl_reset_selected_slot(); + +void wl_remove_weps_from_pool(int *wep, int *wep_count, int ship_class); +void wl_get_ship_class_weapons(int ship_class, int *wep, int *wep_count); +void wl_get_default_weapons(int ship_class, int slot_num, int *wep, int *wep_count); + +void wl_synch_interface(); +void wl_apply(int mode,int from_bank,int from_list,int to_bank,int to_list,int ship_slot,int player_index = -1); +void wl_drop(int from_bank,int from_list,int to_bank,int to_list, int ship_slot,int player_index = -1); + +#endif /* __MISSION_WEAPON_CHOICE_H__ */ + diff --git a/include/model.h b/include/model.h new file mode 100644 index 0000000..cd8af00 --- /dev/null +++ b/include/model.h @@ -0,0 +1,1105 @@ +/* + * $Logfile: /Freespace2/code/Model/MODEL.H $ + * $Revision$ + * $Date$ + * $Author$ + * + * header file for information about polygon models + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 38 9/13/99 10:09a Andsager + * Add debug console commands to lower model render detail and fireball + * LOD for big ship explosiosns. + * + * 37 9/01/99 10:09a Dave + * Pirate bob. + * + * 36 8/24/99 8:55p Dave + * Make sure nondimming pixels work properly in tech menu. + * + * 35 7/19/99 12:02p Andsager + * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to + * only blow up subsystem if its strength is > 0 + * + * 34 7/15/99 2:13p Dave + * Added 32 bit detection. + * + * 33 7/06/99 10:45a Andsager + * Modify engine wash to work on any ship that is not small. Add AWACS + * ask for help. + * + * 32 6/18/99 5:16p Dave + * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD + * dialog to PXO screen. + * + * 31 5/26/99 11:46a Dave + * Added ship-blasting lighting and made the randomization of lighting + * much more customizable. + * + * 30 5/26/99 9:09a Andsager + * Increase number of live debris and MAX_MODEL_TEXTURES (for rebel base) + * + * 29 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * 28 5/11/99 10:16p Andsager + * First pass on engine wash effect. Rotation (control input), damage, + * shake. + * + * 27 4/26/99 8:49p Dave + * Made all pof based nebula stuff full customizable through fred. + * + * 26 4/23/99 5:53p Dave + * Started putting in new pof nebula support into Fred. + * + * 25 4/19/99 12:51p Andsager + * Add function to find the nearest point on extneded bounding box and + * check if inside bounding box. + * + * 24 4/06/99 9:50a Enricco + * Bump up max live_debris from 4 to 5 + * + * 23 3/31/99 8:24p Dave + * Beefed up all kinds of stuff, incluging beam weapons, nebula effects + * and background nebulae. Added per-ship non-dimming pixel colors. + * + * 22 3/23/99 5:17p Dave + * Changed model file format somewhat to account for LOD's on insignias + * + * 21 3/19/99 9:51a Dave + * Checkin to repair massive source safe crash. Also added support for + * pof-style nebulae, and some new weapons code. + * + * 20 3/08/99 7:03p Dave + * First run of new object update system. Looks very promising. + * + * 19 3/02/99 9:25p Dave + * Added a bunch of model rendering debug code. Started work on fixing + * beam weapon wacky firing. + * + * 18 2/19/99 11:42a Dave + * Put in model rendering autocentering. + * + * 17 2/19/99 11:09a Jasen + * Upped MAX_MODEL_SUBSYSTEMS to 128 (for the souper cap + * + * 16 1/25/99 5:03a Dave + * First run of stealth, AWACS and TAG missile support. New mission type + * :) + * + * 15 1/14/99 6:06p Dave + * 100% full squad logo support for single player and multiplayer. + * + * 14 1/12/99 12:53a Dave + * More work on beam weapons - made collision detection very efficient - + * collide against all object types properly - made 3 movement types + * smooth. Put in test code to check for possible non-darkening pixels on + * object textures. + * + * 13 1/11/99 12:56p Andsager + * stupid merge error + * + * 12 1/11/99 12:42p Andsager + * Add live debris - debris which is created from a destroyed subsystem, + * when the ship is still alive + * + * 11 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * 10 12/23/98 2:53p Andsager + * Added stepped rotation support + * + * 9 12/09/98 7:34p Dave + * Cleanup up nebula effect. Tweaked many values. + * + * 8 12/04/98 3:34p Andsager + * Handle norotating submodels + * + * 7 12/03/98 3:14p Andsager + * Check in code that checks rotating submodel actually has ship subsystem + * + * 6 11/19/98 11:07p Andsager + * Check in of physics and collision detection of rotating submodels + * + * 5 11/11/98 5:37p Dave + * Checkin for multiplayer testing. + * + * 4 10/23/98 3:03p Andsager + * Initial support for changing rotation rate. + * + * 3 10/16/98 9:40a Andsager + * Remove ".h" files from model.h + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 159 8/28/98 3:29p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 158 5/19/98 8:31p Andsager + * Added split planes (for big ship explosions) + * + * 157 5/07/98 5:39p Andsager + * Changes to model to hold cross section info + * + * 156 4/22/98 9:58p John + * Added code to view invisible faces. + * + * 155 4/22/98 9:43p John + * Added code to allow checking of invisible faces, flagged by any texture + * name with invisible in it. + * + * 154 4/01/98 5:34p John + * Made only the used POFs page in for a level. Reduced some interp + * arrays. Made custom detail level work differently. + * + * 153 3/31/98 5:18p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * + * 152 3/31/98 11:19a Allender + * upped MAX_MODEL_SUBSYSTEMS to a resonable value + * + * 151 3/31/98 11:02a Adam + * upped MAX_MODEL_SUBSYSTEMS to 33 + * + * 150 3/25/98 11:23a Mike + * Fix stack overwrite due to async between MAX_SECONDARY_WEAPONS and + * MAX_WL_SECONDARY. + * + * 149 3/24/98 10:11p Sandeep + * + * 148 3/24/98 4:03p Lawrance + * JOHN: Fix up outline drawing code to support different colors + * + * 147 3/22/98 10:19a Adam + * upped subsystem & secondary weapon constants for Frank's new capital + * ship. + * + * 146 3/21/98 3:33p Lawrance + * Allow model outline color to be set directly + * + * 145 3/19/98 5:24p John + * Added code to find the closest point on a model to another point. Used + * this for detail levels and for targetting info. + * + * 144 3/16/98 4:51p John + * Added low-level code to clip all polygons against an arbritary plane. + * Took out all old model_interp_zclip and used this new method instead. + * + * 143 3/13/98 9:06a John + * Made missile thrusters use a different min dist for scaling. Made + * missilies not use lighting. + * + * 142 3/02/98 5:42p John + * Removed WinAVI stuff from Freespace. Made all HUD gauges wriggle from + * afterburner. Made gr_set_clip work good with negative x &y. Made + * model_caching be on by default. Made each cached model have it's own + * bitmap id. Made asteroids not rotate when model_caching is on. + * + * 141 2/24/98 5:04p Allender + * allow different ship classes to use the same model. Lot's of subsystem + * stuff to deal with + * + * 140 2/24/98 1:58p John + * Made asteroids use model_caching. Made asteroids darken with distance. + * + * 139 1/29/98 5:50p John + * Made electrical arcing on debris pieces be persistent from frame to + * frame + * + * 138 1/27/98 11:02a John + * Added first rev of sparks. Made all code that calls model_render call + * clear_instance first. Made debris pieces not render by default when + * clear_instance is called. + * + * 137 1/23/98 5:08p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 136 1/15/98 3:28p John + * Took out the unused ptnorms array. Saved a measly 8MB of RAM, so it + * might not have been worth ripping out. :-) + * + * 135 1/09/98 11:25a John + * Took the thruster animation stuff out of the model. + * + * 134 12/31/97 2:35p John + * Added code for core_radius + * + * 133 12/22/97 8:55a John + * added parameters for checking models. + * + * 132 12/17/97 5:11p John + * Added brightening back into fade table. Added code for doing the fast + * dynamic gun flashes and thruster flashes. + * + * 131 12/17/97 9:54a John + * added code for precalculated weapon flash lighting. + * + * 130 12/05/97 3:46p John + * made ship thruster glow scale instead of being an animation. + * + * + * $NoKeywords: $ + */ + +#ifndef _MODEL_H +#define _MODEL_H + +#include "pstypes.h" + +struct object; + +#define MAX_PRIMARY_BANKS 3 +#define MAX_SECONDARY_BANKS 4 // Lowered from 5 to 4 by MK on 3/25/98. This needs to be <= MAX_WL_SECONDARY or you'll get stack overwrites. + +#define MAX_POLYGON_MODELS 128 +#define MAX_DEBRIS_OBJECTS 32 +#define MAX_MODEL_TEXTURES 64 +#define FILENAME_LEN 64 +#define MAX_MODEL_DETAIL_LEVELS 8 +#define MAX_PROP_LEN 256 +#define MAX_NAME_LEN 32 +#define MAX_ARC_EFFECTS 8 + +#define MOVEMENT_TYPE_NONE -1 +#define MOVEMENT_TYPE_POS 0 +#define MOVEMENT_TYPE_ROT 1 +#define MOVEMENT_TYPE_ROT_SPECIAL 2 // for turrets only + +// DA 11/13/98 Reordered to account for difference between max and game +#define MOVEMENT_AXIS_NONE -1 +#define MOVEMENT_AXIS_X 0 +#define MOVEMENT_AXIS_Y 2 +#define MOVEMENT_AXIS_Z 1 + +#define MAX_ROTATING_SUBMODELS 10 + +// defines for special objects like gun and missile points, docking point, etc +// Hoffoss: Please make sure that subsystem NONE is always index 0, and UNKNOWN is +// always the last subsystem in the list. Also, make sure that MAX is correct. +// Otherwise, problems will arise in Fred. + +#define SUBSYSTEM_NONE 0 +#define SUBSYSTEM_ENGINE 1 +#define SUBSYSTEM_TURRET 2 +#define SUBSYSTEM_RADAR 3 +#define SUBSYSTEM_NAVIGATION 4 +#define SUBSYSTEM_COMMUNICATION 5 +#define SUBSYSTEM_WEAPONS 6 +#define SUBSYSTEM_SENSORS 7 +#define SUBSYSTEM_SOLAR 8 +#define SUBSYSTEM_GAS_COLLECT 9 +#define SUBSYSTEM_ACTIVATION 10 +#define SUBSYSTEM_UNKNOWN 11 + +#define SUBSYSTEM_MAX 12 // maximum value for subsystem_xxx, for error checking + +#define MAX_TFP 4 // maximum number of turret firing points + +#define MAX_SPLIT_PLANE 3 // number of artist specified split planes (used in big ship explosions) + +// Data specific to a particular instance of a submodel. This gets stuffed/unstuffed using +// the model_clear_instance, model_set_instance, model_get_instance functions. +typedef struct submodel_instance_info { + int blown_off; // If set, this subobject is blown off + angles angs; // The current angle this thing is turned to. + angles prev_angs; + vector pt_on_axis; // in ship RF + float cur_turn_rate; + float desired_turn_rate; + float turn_accel; + int axis_set; + int step_zero_timestamp; // timestamp determines when next step is to begin (for stepped rotation) + +} submodel_instance_info; + +#define MAX_MODEL_SUBSYSTEMS 128 // used in ships.cpp (only place?) for local stack variable + // when reading in ships.tbl + +#define MSS_FLAG_ROTATES (1<<0) // This means the object rotates automatically with "turn_rate" +#define MSS_FLAG_STEPPED_ROTATE (1<<1) // This means that the rotation occurs in steps +#define MSS_FLAG_AI_ROTATE (1<<2) // This means that the rotation is controlled by ai +#define MSS_FLAG_CREWPOINT (1<<3) // If set, this is a crew point. +#define MSS_FLAG_TURRET_MATRIX (1<<4) // If set, this has it's turret matrix created correctly. +#define MSS_FLAG_AWACS (1<<5) // If set, this subsystem has AWACS capability + +// definition of stepped rotation struct +typedef struct stepped_rotation { + int num_steps; // number of steps in complete revolution + float fraction; // fraction of time in step spent in accel + float t_transit; // time spent moving from one step to next + float t_pause; // time at rest between steps + float max_turn_rate; // max turn rate going betweens steps + float max_turn_accel; // max accel going between steps +} stepped_rotation_t; + +typedef struct ai_rotation { + void *p_rotation; + int type; + float dist; + float speed; +} ai_rotation_t; + +// definition for model subsystems. +typedef struct model_subsystem { /* contains rotation rate info */ + uint flags; // See MSS_FLAG_* defines above + char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD + char subobj_name[MAX_NAME_LEN]; // Temporary (hopefully) parameter used to match stuff in ships.tbl + int subobj_num; // subobject number (from bspgen) -- used to match subobjects of subsystems to these entries + int model_num; // Which model this is attached to (i.e. the polymodel[] index) + int type; // type. see SUBSYSTEM_* types above. A generic type thing + vector pnt; // center point of this subsystem + float radius; // the extent of the subsystem + float max_hits; // maximum hits of this subsystem + + // The following items are specific to turrets and will probably be moved to + // a separate struct so they don't take up space for all subsystem types. + char crewspot[MAX_NAME_LEN]; // unique identifying name for this turret -- used to assign AI class and multiplayer people + int turret_weapon_type; // index in Weapon_info of weapon this fires + vector turret_norm; // direction this turret faces + matrix turret_matrix; // turret_norm converted to a matrix. + float turret_fov; // dot of turret_norm:vec_to_enemy > this means can see + int turret_num_firing_points; // number of firing points on this turret + vector turret_firing_point[MAX_TFP]; // in parent object's reference frame, point from which to fire. + int turret_gun_sobj; // Which subobject in this model the firing points are linked to. + float turret_turning_rate; // How fast the turret turns. Read from ships.tbl + + // engine wash info + char engine_wash_index; // index into Engine_wash_info + + // Rotation specific info + float turn_rate; // The turning rate of this subobject, if MSS_FLAG_ROTATES is set. + stepped_rotation_t *stepped_rotation; // turn rotation struct + ai_rotation_t *ai_rotation; // ai controlled rotation struct + + // AWACS specific information + float awacs_intensity; // awacs intensity of this subsystem + float awacs_radius; // radius of effect of the AWACS + + int primary_banks[MAX_PRIMARY_BANKS]; // default primary weapons -hoffoss + int secondary_banks[MAX_SECONDARY_BANKS]; // default secondary weapons -hoffoss + int secondary_bank_capacity[MAX_SECONDARY_BANKS]; // capacity of a bank -hoffoss + int path_num; // path index into polymodel .paths array. -2 if none exists, -1 if not defined +} model_subsystem; + +typedef struct model_special { + struct model_special *next, *prev; // for using as a linked list + int bank; // used for sequencing gun/missile backs. approach/docking points + int slot; // location for gun or missile in this bank + vector pnt; // point where this special submodel thingy is at + vector norm; // normal for the special submodel thingy +} model_special; + +// model arc types +#define MARC_TYPE_NORMAL 0 // standard freespace 1 blue lightning arcs +#define MARC_TYPE_EMP 1 // EMP blast type arcs + +#define MAX_LIVE_DEBRIS 7 + +typedef struct bsp_info { + char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD + int movement_type; // -1 if no movement, otherwise rotational or positional movement -- subobjects only + int movement_axis; // which axis this subobject moves or rotates on. + + vector offset; // 3d offset from parent object + + int bsp_data_size; + ubyte *bsp_data; + + vector geometric_center; // geometric center of this subobject. In the same Frame Of + // Reference as all other vertices in this submodel. (Relative to pivot point) + float rad; // radius for each submodel + + vector min; // The min point of this object's geometry + vector max; // The max point of this object's geometry + vector bounding_box[8]; // caclulated fron min/max + + int blown_off; // If set, this subobject is blown off. Stuffed by model_set_instance + int my_replacement; // If not -1 this subobject is what should get rendered instead of this one + int i_replace; // If this is not -1, then this subobject will replace i_replace when it is damaged + angles angs; // The angles from parent. Stuffed by model_set_instance + + int is_live_debris; // whether current submodel is a live debris model + int num_live_debris; // num live debris models assocaiated with a submodel + int live_debris[MAX_LIVE_DEBRIS]; // array of live debris submodels for a submodel + + submodel_instance_info *sii; // stuff needed for collision from rotations + + int is_thruster; + int is_damaged; + + // Tree info + int parent; // what is parent for each submodel, -1 if none + int num_children; // How many children this model has + int first_child; // The first_child of this model, -1 if none + int next_sibling; // This submodel's next sibling, -1 if none + + int num_details; // How many submodels are lower detail "mirrors" of this submodel + int details[MAX_MODEL_DETAIL_LEVELS]; // A list of all the lower detail "mirrors" of this submodel + + // Electrical Arc Effect Info + // Sets a spark for this submodel between vertex v1 and v2 + int num_arcs; // See model_add_arc for more info + vector arc_pts[MAX_ARC_EFFECTS][2]; + ubyte arc_type[MAX_ARC_EFFECTS]; // see MARC_TYPE_* defines +} bsp_info; + + + +#define MP_TYPE_UNUSED 0 +#define MP_TYPE_SUBSYS 1 + +typedef struct mp_vert { + vector pos; // xyz coordinates of vertex in object's frame of reference + int nturrets; // number of turrets guarding this vertex + int *turret_ids; // array of indices into ship_subsys linked list (can't index using [] though) + float radius; // How far the closest obstruction is from this vertex +} mp_vert; + +typedef struct model_path { + char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD + char parent_name[MAX_NAME_LEN]; // parent name of submodel that path is linked to in POF + int parent_submodel; + int nverts; + mp_vert *verts; + int goal; // Which of the verts is the one closest to the goal of this path + int type; // What this path takes you to... See MP_TYPE_??? defines above for details + int value; // This depends on the type. + // For MP_TYPE_UNUSED, this means nothing. + // For MP_TYPE_SUBSYS, this is the subsystem number this path takes you to. +} model_path; + +typedef struct model_tmap_vert { + short vertnum; + short normnum; + float u,v; +} model_tmap_vert; + +// info for gun and missile banks. Also used for docking points. There should always +// only be two slots for each docking bay + +#define MAX_SLOTS 25 +#define MAX_THRUSTER_SLOTS 10 + +typedef struct w_bank { + int num_slots; + vector pnt[MAX_SLOTS]; + vector norm[MAX_SLOTS]; + float radius[MAX_SLOTS]; +} w_bank; + +typedef struct thruster_bank { + int num_slots; + vector pnt[MAX_THRUSTER_SLOTS]; + vector norm[MAX_THRUSTER_SLOTS]; + float radius[MAX_THRUSTER_SLOTS]; + + // Engine wash info + char wash_info_index; // index into Engine_wash_info +} thruster_bank; + + +// defines for docking bay things. The types are essentially flags since docking bays can probably +// be used for multiple things in some cases (i.e. rearming and general docking) + +#define DOCK_TYPE_CARGO (1<<0) +#define DOCK_TYPE_REARM (1<<1) +#define DOCK_TYPE_GENERIC (1<<2) + +#define MAX_DOCK_SLOTS 2 + +typedef struct dock_bay { + int num_slots; + int type_flags; // indicates what this docking bay can be used for (i.e. cargo/rearm, etc) + int num_spline_paths; // number of spline paths which lead to this docking bay + int *splines; // array of indices into the Spline_path array + char name[MAX_NAME_LEN]; // name of this docking location + vector pnt[MAX_DOCK_SLOTS]; + vector norm[MAX_DOCK_SLOTS]; +} dock_bay; + +// struct that holds the indicies into path information associated with a fighter bay on a capital ship +// NOTE: Fighter bay paths are identified by the path_name $bayN (where N is numbered from 1). +// Capital ships only have ONE fighter bay on the entire ship +#define MAX_SHIP_BAY_PATHS 10 +typedef struct ship_bay { + int num_paths; // how many paths are associated with the model's fighter bay + int paths[MAX_SHIP_BAY_PATHS]; // index into polymodel->paths[] array + int arrive_flags; // bitfield, set to 1 when that path number is reserved for an arrival + int depart_flags; // bitfield, set to 1 when that path number is reserved for a departure +} ship_bay; + +// three structures now used for representing shields. +// shield_tri structure stores information concerning each face of the shield. +// verts indexes into the verts array in the higher level structure +// neighbors indexes into the tris array in the higher level structure +typedef struct shield_tri { + int used; + int verts[3]; // 3 indices into vertex list of the shield. list found in shield_info struct + int neighbors[3]; // indices into array of triangles. neighbor = shares edge. list found in shield_info struct + vector norm; // norm of this triangle +} shield_tri; + +// a list of these shield_vertex structures comprimises the vertex list of the shield. +// The verts array in the shield_tri structure points to one of these members +typedef struct shield_vertex { + vector pos; + float u,v; +} shield_vertex; + +// the high level shield structure. A ship without any shield has nverts and ntris set to 0. +// The vertex list and the tris list are used by the shield_tri structure +typedef struct shield_info { + int nverts; + int ntris; + shield_vertex *verts; + shield_tri *tris; +} shield_info; + +#define BSP_LIGHT_TYPE_WEAPON 1 +#define BSP_LIGHT_TYPE_THRUSTER 2 + +typedef struct bsp_light { + vector pos; + int type; // See BSP_LIGHT_TYPE_?? for values + float value; // How much to light up this light. 0-1. +} bsp_light; + +// model_octant - There are 8 of these per model. They are a handy way to catagorize +// a lot of model properties to get some easy 8x optimizations for model stuff. +typedef struct model_octant { + vector min, max; // The bounding box that makes up this octant defined as 2 points. + int nverts; // how many vertices are in this octant + vector **verts; // The vertices in this octant in the high-res hull. A vertex can only be in one octant. + int nshield_tris; // how many shield triangles are in the octant + shield_tri **shield_tris; // the shield triangles that make up this octant. A tri could be in multiple octants. +} model_octant; + +#define MAX_EYES 10 + +typedef struct eye { + int parent; // parent's subobject number + vector pnt; // the point for the eye + vector norm; // direction the eye faces. Not used with first eye since player orient is used +} eye; + +typedef struct cross_section { + float z; + float radius; +} cross_section; + +#define MAX_MODEL_INSIGNIAS 6 +#define MAX_INS_FACE_VECS 3 +#define MAX_INS_VECS 20 +#define MAX_INS_FACES 10 +typedef struct insignia { + int detail_level; + int num_faces; + int faces[MAX_INS_FACES][MAX_INS_FACE_VECS]; // indices into the vecs array + float u[MAX_INS_FACES][MAX_INS_FACE_VECS]; // u tex coords on a per-face-per-vertex basis + float v[MAX_INS_FACES][MAX_INS_FACE_VECS]; // v tex coords on a per-face-per-vertex bases + vector vecs[MAX_INS_VECS]; // vertex list + vector offset; // global position offset for this insignia +} insignia; + +#define PM_FLAG_ALLOW_TILING (1<<0) // Allow texture tiling +#define PM_FLAG_AUTOCEN (1<<1) // contains autocentering info + +//used to describe a polygon model +typedef struct polymodel { + int id; // what the polygon model number is. (Index in Polygon_models) + int version; + char filename[FILENAME_LEN]; + + uint flags; // 1=allow tiling + int n_detail_levels; + int detail[MAX_MODEL_DETAIL_LEVELS]; + float detail_depth[MAX_MODEL_DETAIL_LEVELS]; + + int num_debris_objects; + int debris_objects[MAX_DEBRIS_OBJECTS]; + + int n_models; + + vector mins,maxs; //min,max for whole model + vector bounding_box[8]; + + int num_lights; // how many lights there are + bsp_light * lights; // array of light info + + int n_view_positions; // number of viewing positions available on this ship + eye view_positions[MAX_EYES]; //viewing positions. Default to {0,0,0}. in location 0 + + float rad; // The radius of everything in the model; shields, thrusters. + float core_radius; // The radius to be used for collision detection in small ship vs big ships. + // This is equal to 1/2 of the smallest dimension of the hull's bounding box. + int n_textures; + int original_textures[MAX_MODEL_TEXTURES]; // what gets read in from file + int textures[MAX_MODEL_TEXTURES]; // what textures you draw with. reset to original_textures by model_set_instance + + vector autocenter; // valid only if PM_FLAG_AUTOCEN is set + + bsp_info *submodel; // an array of size n_models of submodel info. + + // linked lists for special polygon types on this model. Most ships I think will have most + // of these. (most ships however, probably won't have approach points). + int n_guns; // number of primary gun points (not counting turrets) + int n_missiles; // number of secondary missile points (not counting turrets) + int n_docks; // number of docking points + int n_thrusters; // number of thrusters on this ship. + w_bank *gun_banks; // array of gun banks + w_bank *missile_banks; // array of missile banks + dock_bay *docking_bays; // array of docking point pairs + thruster_bank *thrusters; // array of thruster objects -- likely to change in the future + ship_bay *ship_bay_v; // contains path indexes for ship bay approach/depart paths + + shield_info shield; // new shield information + + int n_paths; + model_path *paths; + + // physics info + float mass; + vector center_of_mass; + matrix moment_of_inertia; + + model_octant octants[8]; + + int num_xc; // number of cross sections + cross_section* xc; // pointer to array of cross sections (used in big ship explosions) + + int num_split_plane; // number of split planes + float split_plane[MAX_SPLIT_PLANE]; // actual split plane z coords (for big ship explosions) + + insignia ins[MAX_MODEL_INSIGNIAS]; + int num_ins; + +#ifndef NDEBUG + int ram_used; // How much RAM this model uses + int debug_info_size; + char *debug_info; +#endif +} polymodel; + + +// Call once to initialize the model system +void model_init(); + +// call at the beginning of a level. after the level has been loaded +void model_level_post_init(); + +// Call to free all existing models +void model_free_all(); + +// Loads a model from disk and returns the model number it loaded into. +int model_load(char *filename, int n_subsystems, model_subsystem *subsystems); + +// notify the model system that a ship has died +void model_notify_dead_ship(int objnum); + +// Returns a pointer to the polymodel structure for model 'n' +polymodel * model_get(int model_num); + +// routine to copy susbsystems. Must be called when subsystems sets are the same -- see ship.cpp +void model_copy_subsystems( int n_subsystems, model_subsystem *d_sp, model_subsystem *s_sp ); + +// If MR_FLAG_OUTLINE bit set this color will be used for outlines. +// This defaults to black. +void model_set_outline_color(int r, int g, int b ); + +void model_set_outline_color_fast(void *outline_color); + +// IF MR_LOCK_DETAIL is set, then it will always draw detail level 'n' +// This defaults to 0. (0=highest, larger=lower) +void model_set_detail_level(int n); + +// Flags you can pass to model_render +#define MR_NORMAL (0) // Draw a normal object +#define MR_SHOW_OUTLINE (1<<0) // Draw the object in outline mode. Color specified by model_set_outline_color +#define MR_SHOW_PIVOTS (1<<1) // Show the pivot points +#define MR_SHOW_PATHS (1<<2) // Show the paths associated with a model +#define MR_SHOW_RADIUS (1<<3) // Show the radius around the object +#define MR_SHOW_DAMAGE (1<<4) // Show the "destroyed" subobjects +#define MR_SHOW_SHIELDS (1<<5) // Show the sheild mesh +#define MR_SHOW_THRUSTERS (1<<6) // Show the engine thrusters. See model_set_thrust for how long it draws. +#define MR_LOCK_DETAIL (1<<7) // Only draw the detail level defined in model_set_detail_level +#define MR_NO_POLYS (1<<8) // Don't draw the polygons. +#define MR_NO_LIGHTING (1<<9) // Don't perform any lighting on the model. +#define MR_NO_TEXTURING (1<<10) // Draw textures as flat-shaded polygons. +#define MR_NO_CORRECT (1<<11) // Don't to correct texture mapping +#define MR_NO_SMOOTHING (1<<12) // Don't perform smoothing on vertices. +#define MR_ALWAYS_REDRAW (1<<13) // Don't do any model caching; redraw this model each frame! +#define MR_IS_ASTEROID (1<<14) // When set, treat this as an asteroid. +#define MR_IS_MISSILE (1<<15) // When set, treat this as a missilie. No lighting, small thrusters. +#define MR_SHOW_OUTLINE_PRESET (1<<16) // Draw the object in outline mode. Color assumed to be set already. +#define MR_SHOW_INVISIBLE_FACES (1<<17) // Show invisible faces as green... +#define MR_AUTOCENTER (1<<18) // Always use the center of the hull bounding box as the center, instead of the pivot point +#define MR_BAY_PATHS (1<<19) // draw bay paths +#define MR_ALL_XPARENT (1<<20) // render it fully transparent +#define MR_NO_ZBUFFER (1<<21) // switch z-buffering off completely +#define MR_NO_CULL (1<<22) // don't cull backfacing poly's +#define MR_FORCE_TEXTURE (1<<23) // force a given texture to always be used +#define MR_FORCE_LOWER_DETAIL (1<<24) // force the model to draw 1 LOD lower, if possible + +// Renders a model and all it's submodels. +// See MR_? defines for values for flags +void model_render(int model_num, matrix *orient, vector * pos, uint flags = MR_NORMAL, int objnum = -1, int lighting_skip = -1 ); + +// Renders just one particular submodel on a model. +// See MR_? defines for values for flags +void submodel_render(int model_num,int submodel_num, matrix *orient, vector * pos, uint flags=MR_NORMAL, int light_ignore_id=-1 ); + + +// Returns the radius of a model +float model_get_radius(int modelnum); +float submodel_get_radius( int modelnum, int submodelnum ); + +// Returns the core radius (smallest dimension of hull's bounding box, used for collision detection with big ships only) +float model_get_core_radius( int modelnum ); + +// Returns zero is x1,y1,x2,y2 are valid +// returns 1 for invalid model, 2 for point offscreen. +// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates! +// This function just looks at the radius, and not the orientation, so the +// bounding box won't change depending on the obj's orient. +extern int model_find_2d_bound(int model_num,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 ); + +// Returns zero is x1,y1,x2,y2 are valid +// returns 1 for invalid model, 2 for point offscreen. +// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates! +// This function looks at the object's bounding box and it's orientation, +// so the bounds will change as the object rotates, to give the minimum bouding +// rect. +extern int model_find_2d_bound_min(int model_num,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 ); + +// Returns zero is x1,y1,x2,y2 are valid +// returns 1 for invalid model, 2 for point offscreen. +// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates! +// This function looks at the object's bounding box and it's orientation, +// so the bounds will change as the object rotates, to give the minimum bouding +// rect. +int submodel_find_2d_bound_min(int model_num,int submodel, matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 ); + + +// Returns zero is x1,y1,x2,y2 are valid +// Returns 2 for point offscreen. +// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates! +// This function just looks at the radius, and not the orientation, so the +// bounding box won't change depending on the obj's orient. +int subobj_find_2d_bound(float radius, matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 ); + +// stats variables +#ifndef NDEBUG +extern int modelstats_num_polys; +extern int modelstats_num_polys_drawn; +extern int modelstats_num_verts; +extern int modelstats_num_sortnorms; +#endif + +// Tries to move joints so that the turrent points to the point dst. +// turret1 is the angles of the turret, turret2 is the angles of the gun from turret +extern int model_rotate_gun(int model_num, model_subsystem * turret, matrix *orient, angles * turret1, angles *turret2, vector * pos, vector * dst); + +// Rotates the angle of a submodel. Use this so the right unlocked axis +// gets stuffed. +extern void submodel_rotate(model_subsystem *psub, submodel_instance_info * sii ); + +// Rotates the angle of a submodel. Use this so the right unlocked axis +// gets stuffed. Does this for stepped rotations +void submodel_stepped_rotate(model_subsystem *psub, submodel_instance_info *sii); + +// Given a point (pnt) that is in sub_model_num's frame of +// reference, and given the object's orient and position, +// return the point in 3-space in outpnt. +extern void model_find_world_point(vector * outpnt, vector *mpnt,int model_num, int sub_model_num, matrix * objorient, vector * objpos ); + +// Given a point in the world RF, find the corresponding point in the model RF. +// This is special purpose code, specific for model collision. +// NOTE - this code ASSUMES submodel is 1 level down from hull (detail[0]) +void world_find_model_point(vector *out, vector *world_pt, polymodel *pm, int submodel_num, matrix *orient, vector *pos); + +// Given a polygon model index, find a list of rotating submodels to be used for collision +void model_get_rotating_submodel_list(int *submodel_list, int *num_rotating_submodesl, object *objp); + +// For a rotating submodel, find a point on the axis +void model_init_submodel_axis_pt(submodel_instance_info *sii, int model_num, int submodel_num); + +// Given a direction (pnt) that is in sub_model_num's frame of +// reference, and given the object's orient and position, +// return the point in 3-space in outpnt. +extern void model_find_world_dir(vector * out_dir, vector *in_dir,int model_num, int sub_model_num, matrix * objorient, vector * objpos ); + +// Clears all the submodel instances stored in a model to their defaults. +extern void model_clear_instance(int model_num); + +// Sets rotating submodel turn info to that stored in model +void model_set_instance_info(submodel_instance_info *sii, float turn_rate, float turn_accel); + +// Clears all the values in a particular instance to their defaults. +extern void model_clear_instance_info(submodel_instance_info * sii); + +// Sets the submodel instance data in a submodel +extern void model_set_instance(int model_num, int sub_model_num, submodel_instance_info * sii ); + +// Adds an electrical arcing effect to a submodel +void model_add_arc(int model_num, int sub_model_num, vector *v1, vector *v2, int arc_type ); + +// Fills in an array with points from a model. Only gets up to max_num verts. +// Returns number of verts found +extern int submodel_get_points(int model_num, int submodel_num, int max_num, vector **nts ); + +// Gets two random points on the surface of a submodel +extern void submodel_get_two_random_points(int model_num, int submodel_num, vector *v1, vector *v2, vector *n1 = NULL, vector *n2 = NULL); + +// gets the index into the docking_bays array of the specified type of docking point +// Returns the index. second functions returns the index of the docking bay with +// the specified name +extern int model_find_dock_index(int modelnum, int dock_type); +extern int model_find_dock_name_index( int modelnum, char *name ); + +// returns the actual name of a docking point on a model, needed by Fred. +char *model_get_dock_name(int modelnum, int index); + +// Returns number of verts in a submodel; +int submodel_get_num_verts(int model_num, int submodel_num ); + +// Returns number of polygons in a submodel; +int submodel_get_num_polys(int model_num, int submodel_num ); + +// returns number of docking points for a model +int model_get_num_dock_points(int modelnum); +int model_get_dock_index_type(int modelnum, int index); + +// get all the different docking point types on a model +int model_get_dock_types(int modelnum); + +// Given a vector that is in sub_model_num's frame of +// reference, and given the object's orient and position, +// return the vector in the model's frame of reference. +void model_find_obj_dir(vector *w_vec, vector *m_vec, object *ship_obj, int sub_model_num); + + +// This is the interface to model_check_collision. Rather than passing all these +// values and returning values in globals, just fill in a temporary variable with +// the input values and call model_check_collision +typedef struct mc_info { + // Input values + int model_num; // What model to check + int submodel_num; // What submodel to check if MC_SUBMODEL is set + matrix *orient; // The orient of the model + vector *pos; // The pos of the model in world coordinates + vector *p0; // The starting point of the ray (sphere) to check + vector *p1; // The ending point of the ray (sphere) to check + int flags; // Flags that the model_collide code looks at. See MC_??? defines + float radius; // If MC_CHECK_THICK is set, checks a sphere moving with the radius. + + // Return values + int num_hits; // How many collisions were found + float hit_dist; // The distance from p0 to hitpoint + vector hit_point; // Where the collision occurred at in hit_submodel's coordinate system + vector hit_point_world; // Where the collision occurred at in world coordinates + int hit_submodel; // Which submodel got hit. + int hit_bitmap; // Which texture got hit. -1 if not a textured poly + float hit_u, hit_v; // Where on hit_bitmap the ray hit. Invalid if hit_bitmap < 0 + int shield_hit_tri; // Which triangle on the shield got hit or -1 if none + vector hit_normal; // Vector normal of polygon of collision. (This is in submodel RF) + int edge_hit; // Set if an edge got hit. Only valid if MC_CHECK_THICK is set. + ubyte *f_poly; // pointer to flat poly where we intersected + ubyte *t_poly; // pointer to tmap poly where we intersected + + // flags can be changed for the case of sphere check finds an edge hit +} mc_info; + + + +//======== MODEL_COLLIDE ============ + +// Model Collision flags, used in model_collide() +#define MC_CHECK_MODEL (1<<0) // Check the polygons in the model. +#define MC_CHECK_SHIELD (1<<1) // check for collision against shield, if it exists. +#define MC_ONLY_SPHERE (1<<2) // Only check bounding sphere. Not accurate, but fast. + // NOTE! This doesn't set hit_point correctly with MC_CHECK_SPHERELINE +#define MC_ONLY_BOUND_BOX (1<<3) // Only check bounding boxes. Pretty accurate and slower than MC_ONLY_SPHERE. + // Checks the rotatated bounding box of each submodel. + // NOTE! This doesn't set hit_point correctly with MC_CHECK_SPHERELINE +#define MC_CHECK_RAY (1<<4) // Checks a ray from p0 *through* p1 on to infinity +#define MC_CHECK_SPHERELINE (1<<5) // Checks a moving sphere rather than just a ray. Radius +#define MC_SUBMODEL (1<<6) // If this is set, only check the submodel specified in mc->submodel_num. Use with MC_CHECK_MODEL +#define MC_SUBMODEL_INSTANCE (1<<7) // Check submodel and its children (of a rotating submodel) +#define MC_CHECK_INVISIBLE_FACES (1<<8) // Check the invisible faces. + + +/* + Checks to see if a vector from p0 to p0 collides with a model of + type 'model_num' at 'orient' 'pos'. + + Returns the number of polys that were hit. Zero is none, obviously. + Return true if a collision with hull (or shield, if MC_CHECK_SHIELD set), + else return false. + + If it did it one or more, then hitpt is the closest 3d point that the + vector hit. See the MC_? defines for flag values. + + Model_collide can test a sphere against either (1) shield or (2) model. + + To check a sphere, set the radius of sphere in mc_info structure and + set the flag MC_CHECK_SPHERE. + + Here is a sample for how to use: + + mc_info mc; + + mc.model_num = ???; // Fill in the model to check + mc.orient = &obj->orient; // The object's orient + mc.pos = &obj->pos; // The object's position + mc.p0 = &p0; // Point 1 of ray to check + mc.p1 = &p1; // Point 2 of ray to check + mc.flags = MC_CHECK_MODEL; // flags + +** TO COLLIDE AGAINST A LINE SEGMENT + + model_collide(&mc); + if (mc.num_hits) { + // We hit submodel mc.hit_submodel on texture mc.hitbitmap, + // at point mc.hit_point_world, with uv's of mc.hit_u, mc.hit_v. + } + +** TO COLLIDE AGAINST A SPHERE + mc.flags |= MC_CHECK_SPHERELINE; + mc.radius = radius; + + model_collide(&mc, radius); + if (mc.num_hits) { + // We hit submodel mc.hit_submodel on texture mc.hitbitmap, + // at point mc.hit_point_world, with uv's of mc.hit_u, mc.hit_v. + // Check (mc.edge_hit) to see if we hit an edge + } +*/ + +int model_collide(mc_info * mc_info); + +// Sets the submodel instance data in a submodel +// If show_damaged is true it shows only damaged submodels. +// If it is false it shows only undamaged submodels. +void model_show_damaged(int model_num, int show_damaged ); + + +//=========================== MODEL OCTANT STUFF ================================ + +// Models are now divided into 8 octants. Sheilds too. +// This made the collision code faster. Shield is 4x and ship faces +// are about 2x faster. + +// Before, calling model_collide with flags=0 didn't check the shield +// but did check the model itself. Setting the sheild flags caused +// the shield to get check along with the ship. +// Now, you need to explicitly tell the model_collide code to check +// the model, so you can check the model or shield or both. + +// If you need to check them both, do it in one call; this saves some +// time. If checking the shield is sufficient for determining +// something (like if it is under the hud) then use just sheild +// check, it is at least 5x faster than checking the model itself. + + +// Model octant ordering - this is a made up ordering, but it makes sense. +// X Y Z index description +// - - - 0 left bottom rear +// - - + 1 left bottom front +// - + - 2 left top rear +// - + + 3 left top front +// + - - 4 right bottom rear +// + - + 5 right bottom front +// + + - 6 right top rear +// + + + 7 right top front + +// Returns which octant point 'pnt' is closet to. This will always return +// a valid octant (0-7) since the point doesn't have to be in an octant. +// If model_orient and/or model_pos are NULL, pnt is assumed to already +// be rotated into the model's local coordinates. +// If oct is not null, it will be filled in with a pointer to the octant +// data. +int model_which_octant_distant( vector *pnt, int model_num,matrix *model_orient, vector * model_pos, model_octant **oct ); + +// Returns which octant point 'pnt' is in. This might return +// -1 if the point isn't in any octant. +// If model_orient and/or model_pos are NULL, pnt is assumed to already +// be rotated into the model's local coordinates. +// If oct is not null, it will be filled in with a pointer to the octant +// data. Or NULL if the pnt isn't in the octant. +int model_which_octant( vector *pnt, int model_num,matrix *model_orient, vector * model_pos, model_octant **oct ); + +// scale the engines thrusters by this much +// Only enabled if MR_SHOW_THRUSTERS is on +void model_set_thrust(int model_num, float length, int bitmapnum, int glow_bitmapnum=-1, float glow_noise=1.0f); + +//========================================================= +// model caching + +// Call once to init the model caching stuff +void model_cache_init(); + +// Call before every level to clean up the model caching stuff +void model_cache_reset(); + +// If TRUE, then model caching is enabled +extern int Model_caching; + + +//======================================================================================= +// Finds the closest point on a model to a point in space. Actually only finds a point +// on the bounding box of the model. +// Given: +// model_num Which model +// submodel_num Which submodel, -1 for hull +// orient Orientation of the model +// pos Position of the model +// eye_pos Point that you want to find the closest point to +// Returns: +// distance from eye_pos to closest_point. 0 means eye_pos is +// on or inside the bounding box. +// Also fills in outpnt with the actual closest point. +float model_find_closest_point( vector *outpnt, int model_num, int submodel_num, matrix *orient, vector * pos, vector *eye_pos ); + +// set the insignia bitmap to be used when rendering a ship with an insignia (-1 switches it off altogether) +void model_set_insignia_bitmap(int bmap); + +// set model transparency for use with MR_ALL_XPARENT +void model_set_alpha(float alpha); + +// set the forces bitmap +void model_set_forced_texture(int bmap); + +// see if the given texture is used by the passed model. 0 if not used, 1 if used, -1 on error +int model_find_texture(int model_num, int bitmap); + +// find closest point on extended bounding box (the bounding box plus all the planes that make it up) +// returns closest distance to extended box +// positive return value means start_point is outside extended box +// displaces closest point an optional amount delta to the outside of the box +// closest_box_point can be NULL. +float get_world_closest_box_point_with_delta(vector *closest_box_point, object *box_obj, vector *start_point, int *is_inside, float delta); + +// given a newly loaded model, page in all textures +void model_page_in_textures(int modelnum, int ship_info_index); + +// is the given model a pirate ship? +int model_is_pirate_ship(int modelnum); + +#endif + diff --git a/include/modelsinc.h b/include/modelsinc.h new file mode 100644 index 0000000..cb0af5f --- /dev/null +++ b/include/modelsinc.h @@ -0,0 +1,74 @@ +#ifndef _MODELSINC_H +#define _MODELSINC_H + +#include "model.h" + +#ifndef MODEL_LIB +#error This should only be used internally by the model library. See John if you think you need to include this elsewhere. +#endif + +#define OP_EOF 0 +#define OP_DEFPOINTS 1 +#define OP_FLATPOLY 2 +#define OP_TMAPPOLY 3 +#define OP_SORTNORM 4 +#define OP_BOUNDBOX 5 + +// change header for freespace2 +//#define FREESPACE1_FORMAT +#define FREESPACE2_FORMAT +#if defined( FREESPACE1_FORMAT ) +#elif defined ( FREESPACE2_FORMAT ) +#else + #error Neither FREESPACE1_FORMAT or FREESPACE2_FORMAT defined +#endif + +// FREESPACE1 FORMAT +#if defined( FREESPACE1_FORMAT ) + #define ID_OHDR 'RDHO' // POF file header + #define ID_SOBJ 'JBOS' // Subobject header +#else + #define ID_OHDR '2RDH' // POF file header + #define ID_SOBJ '2JBO' // Subobject header +#endif +#define ID_TXTR 'RTXT' // Texture filename list +#define ID_INFO 'FNIP' // POF file information, like command line, etc +#define ID_GRID 'DIRG' // Grid information +#define ID_SPCL 'LCPS' // Special object -- like a gun, missile, docking point, etc. +#define ID_PATH 'HTAP' // A spline based path +#define ID_GPNT 'TNPG' // gun points +#define ID_MPNT 'TNPM' // missile points +#define ID_DOCK 'KCOD' // docking points +#define ID_TGUN 'NUGT' // turret gun points +#define ID_TMIS 'SIMT' // turret missile points +#define ID_FUEL 'LEUF' // thruster points +#define ID_SHLD 'DLHS' // shield definition +#define ID_EYE ' EYE' // eye information +#define ID_INSG 'GSNI' // insignia information +#define ID_ACEN 'NECA' // autocentering information + +#define uw(p) (*((uint *) (p))) +#define w(p) (*((int *) (p))) +#define wp(p) ((int *) (p)) +#define vp(p) ((vector *) (p)) +#define fl(p) (*((float *) (p))) + +extern int model_interp(matrix * orient, ubyte * data, polymodel * pm ); + +// Creates the octants for a given polygon model +void model_octant_create( polymodel * pm ); + +// frees the memory the octants use for a given polygon model +void model_octant_free( polymodel * pm ); + +void model_calc_bound_box( vector *box, vector *big_mn, vector *big_mx); + +void interp_clear_instance(); + +#define MAX_POLYGON_VECS 1100 //6500 (7x) +#define MAX_POLYGON_NORMS 2800 //6500 (3x) + +extern vector *Interp_verts[MAX_POLYGON_VECS]; + +#endif + diff --git a/include/modifyvariabledlg.h b/include/modifyvariabledlg.h new file mode 100644 index 0000000..f786416 --- /dev/null +++ b/include/modifyvariabledlg.h @@ -0,0 +1,73 @@ +#if !defined(AFX_MODIFYVARIABLEDLG_H__710D45F1_ABBF_11D2_A89A_0060088FAE88__INCLUDED_) +#define AFX_MODIFYVARIABLEDLG_H__710D45F1_ABBF_11D2_A89A_0060088FAE88__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// ModifyVariableDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// CModifyVariableDlg dialog + +class CModifyVariableDlg : public CDialog +{ +// Construction +public: + CModifyVariableDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CModifyVariableDlg) + enum { IDD = IDD_MODIFY_VARIABLE }; + CString m_cur_variable_name; + CString m_default_value; + CString m_old_var_name; + bool m_type_number; + bool m_modified_name; + bool m_modified_value; + bool m_modified_type; + bool m_deleted; + bool m_data_validated; + bool m_var_name_validated; + bool m_do_modify; + int m_combo_last_modified_index; + int m_traslate_combo_to_sexp[MAX_SEXP_VARIABLES]; + int m_start_index; // index of sexp_variables which is right clicked to get this menu + sexp_tree *m_p_sexp_tree; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CModifyVariableDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CModifyVariableDlg) + afx_msg void OnDeleteVariable(); + afx_msg void OnTypeString(); + afx_msg void OnTypeNumber(); + afx_msg void OnSelchangeModifyVariableName(); + afx_msg void OnEditchangeModifyVariableName(); + virtual BOOL OnInitDialog(); + virtual void OnOK(); + afx_msg void OnKillfocusModifyDefaultValue(); + afx_msg void set_variable_type(); + afx_msg void validate_data(CString &temp_data, int set_focus); + afx_msg void validate_var_name(int set_focus); + afx_msg int get_sexp_var_index(); + afx_msg void OnDropdownModifyVariableName(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_MODIFYVARIABLEDLG_H__710D45F1_ABBF_11D2_A89A_0060088FAE88__INCLUDED_) + diff --git a/include/monopub.h b/include/monopub.h new file mode 100644 index 0000000..6ebe9ba --- /dev/null +++ b/include/monopub.h @@ -0,0 +1,59 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + monopub.h + +Abstract: + + This module contains the PUBLIC (viewable by driver & Win32 apps) + definitions for the IOCTLs supported by the MONO device driver. + +Environment: + + Kernel & User mode + +Revision History: + + 03-22-93 : created + +--*/ + + + +// +// Define the various device type values. Note that values used by Microsoft +// Corporation are in the range 0-32767, and 32768-65535 are reserved for use +// by customers. +// + +#define FILE_DEVICE_MONO 0x00008100 + + + +// +// Macro definition for defining IOCTL and FSCTL function control codes. Note +// that function codes 0-2047 are reserved for Microsoft Corporation, and +// 2048-4095 are reserved for customers. +// + +#define MONO_IOCTL_INDEX 0x810 + + + +// +// The MONO device driver IOCTLs +// + +#define IOCTL_MONO_PRINT CTL_CODE(FILE_DEVICE_MONO, \ + MONO_IOCTL_INDEX, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + +#define IOCTL_MONO_CLEAR_SCREEN CTL_CODE(FILE_DEVICE_MONO, \ + MONO_IOCTL_INDEX + 1, \ + METHOD_BUFFERED, \ + FILE_ANY_ACCESS) + diff --git a/include/mouse.h b/include/mouse.h new file mode 100644 index 0000000..389396f --- /dev/null +++ b/include/mouse.h @@ -0,0 +1,121 @@ +/* + * $Logfile: /Freespace2/code/Io/Mouse.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for mouse reading routines + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 17 5/11/98 5:29p Hoffoss + * Added mouse button mapped to joystick button support. + * + * 16 5/07/98 6:58p Hoffoss + * Made changes to mouse code to fix a number of problems. + * + * 15 5/05/98 8:38p Hoffoss + * Added sensitivity adjustment to options menu and made it save to pilot + * file. + * + * 14 5/01/98 5:45p Hoffoss + * Made further improvements to the mouse code. + * + * 13 5/01/98 1:14p Hoffoss + * Changed mouse usage so directInput is only used for release version. + * + * 12 4/30/98 5:40p Hoffoss + * Added mouse as a supported control to fly the ship. + * + * 11 4/29/98 12:13a Lawrance + * Add function to check down count of mouse button without reseting the + * internal count. Added hook to reset demo trailer timer when a button + * is pressed. + * + * 10 4/02/98 5:26p John + * + * 9 12/04/97 3:47p John + * Made joystick move mouse cursor + * + * 8 11/20/97 5:36p Dave + * Hooked in a bunch of main hall changes (including sound). Made it + * possible to reposition (rewind/ffwd) + * sound buffer pointers. Fixed animation direction change framerate + * problem. + * + * 7 4/22/97 12:32p John + * added mouse_init function + * + * 6 3/26/97 10:52a Lawrance + * mouse always on in menus, disappears in gameplay after 1 second + * + * 5 3/11/97 1:37p Lawrance + * added mouse_up_count(), changed mouse_mark() to mouse_mark_button() & + * mouse_mark_move() + * + * 4 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _MOUSE_H +#define _MOUSE_H + +#include "pstypes.h" + +extern int Mouse_sensitivity; +extern int Use_mouse_to_fly; +extern int Mouse_hidden; +extern int Keep_mouse_centered; + +// call once to init the mouse +void mouse_init(); + +extern void mouse_mark_button( uint flags, int set ); + +// Fills in xpos & ypos if not NULL. +// Returns Button states +// Always returns coordinates clipped to screen coordinates. +extern int mouse_get_pos( int *xpos, int *ypos ); + +// get_real_pos could be negative. +extern void mouse_get_real_pos(int *mx, int *my); + +extern void mouse_set_pos(int xpos,int ypos); + +#define MOUSE_LEFT_BUTTON (1<<0) +#define MOUSE_RIGHT_BUTTON (1<<1) +#define MOUSE_MIDDLE_BUTTON (1<<2) + +#define MOUSE_NUM_BUTTONS 3 + +// keep the following two #defines up to date with the #defines above +#define LOWEST_MOUSE_BUTTON (1<<0) +#define HIGHEST_MOUSE_BUTTON (1<<2) + +// Returns the number of times button n went from up to down since last call +int mouse_down_count(int n, int reset_count = 1); +// Returns the number of times button n went from down to up since last call +int mouse_up_count(int n); + +extern void mouse_flush(); + +int mouse_down(int btn); // returns 1 if mouse button btn is down, 0 otherwise +float mouse_down_time(int btn); // returns the fraction of time btn has been down since last call +int mouse_is_visible(); // returns 1 if mouse is visible, 0 otherwise + +void mouse_eval_deltas(); +void mouse_get_delta(int *dx = NULL, int *dy = NULL, int *dz = NULL); + +#endif + diff --git a/include/multi.h b/include/multi.h new file mode 100644 index 0000000..385791b --- /dev/null +++ b/include/multi.h @@ -0,0 +1,1102 @@ +/* + * $Logfile: /Freespace2/code/Network/Multi.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file which contains type definitions for multiplayer, and support for high-level + * multiplayer functions. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 47 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 46 8/26/99 8:51p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 45 8/24/99 10:11a Dave + * New server version. + * + * 44 8/24/99 1:49a Dave + * Fixed client-side afterburner stuttering. Added checkbox for no version + * checking on PXO join. Made button info passing more friendly between + * client and server. + * + * 43 8/22/99 5:53p Dave + * Scoring fixes. Added self destruct key. Put callsigns in the logfile + * instead of ship designations for multiplayer players. + * + * 42 8/22/99 1:19p Dave + * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in + * which d3d cards are detected. + * + * 41 8/19/99 10:59a Dave + * Packet loss detection. + * + * 40 8/04/99 2:24a Dave + * Fixed escort gauge ordering for dogfight. + * + * 39 7/30/99 7:01p Dave + * Dogfight escort gauge. Fixed up laser rendering in Glide. + * + * 38 7/26/99 5:50p Dave + * Revised ingame join. Better? We'll see.... + * + * 37 7/22/99 7:17p Dave + * Fixed excessive whacks in multiplayer. + * + * 36 7/08/99 10:53a Dave + * New multiplayer interpolation scheme. Not 100% done yet, but still + * better than the old way. + * + * 35 7/03/99 5:50p Dave + * Make rotated bitmaps draw properly in padlock views. + * + * 34 6/07/99 9:51p Dave + * Consolidated all multiplayer ports into one. + * + * 33 5/17/99 9:32a Dave + * Bumped up server version. + * + * 32 5/14/99 1:59p Andsager + * Multiplayer message for subsystem cargo revealed. + * + * 31 4/29/99 3:02p Dave + * + * 30 4/29/99 2:29p Dave + * Made flak work much better in multiplayer. + * + * 29 4/28/99 11:13p Dave + * Temporary checkin of artillery code. + * + * 28 4/25/99 7:43p Dave + * Misc small bug fixes. Made sun draw properly. + * + * 27 4/21/99 6:15p Dave + * Did some serious housecleaning in the beam code. Made it ready to go + * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added + * a handy macro for recalculating collision pairs for a given object. + * + * 26 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 25 3/10/99 6:50p Dave + * Changed the way we buffer packets for all clients. Optimized turret + * fired packets. Did some weapon firing optimizations. + * + * 24 3/08/99 7:03p Dave + * First run of new object update system. Looks very promising. + * + * 23 3/01/99 7:39p Dave + * Added prioritizing ship respawns. Also fixed respawns in TvT so teams + * don't mix respawn points. + * + * 22 2/25/99 4:19p Dave + * Added multiplayer_beta defines. Added cd_check define. Fixed a few + * release build warnings. Added more data to the squad war request and + * response packets. + * + * 21 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * 20 2/19/99 11:42a Dave + * Put in model rendering autocentering. + * + * 19 2/17/99 2:10p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 18 2/12/99 6:16p Dave + * Pre-mission Squad War code is 95% done. + * + * 17 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 16 2/08/99 5:07p Dave + * FS2 chat server support. FS2 specific validated missions. + * + * 15 1/29/99 2:08a Dave + * Fixed beam weapon collisions with players. Reduced size of scoring + * struct for multiplayer. Disabled PXO. + * + * 14 1/14/99 12:48a Dave + * Todo list bug fixes. Made a pass at putting briefing icons back into + * FRED. Sort of works :( + * + * 13 1/12/99 5:45p Dave + * Moved weapon pipeline in multiplayer to almost exclusively client side. + * Very good results. Bandwidth goes down, playability goes up for crappy + * connections. Fixed object update problem for ship subsystems. + * + * 12 1/12/99 4:07a Dave + * Put in barracks code support for selecting squad logos. Properly + * distribute squad logos in a multiplayer game. + * + * 11 12/14/98 12:13p Dave + * Spiffed up xfer system a bit. Put in support for squad logo file xfer. + * Need to test now. + * + * 10 12/03/98 5:22p Dave + * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl + * checksumming. + * + * 9 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 8 11/19/98 8:03a Dave + * Full support for D3-style reliable sockets. Revamped packet lag/loss + * system, made it receiver side and at the lowest possible level. + * + * 7 11/17/98 11:12a Dave + * Removed player identification by address. Now assign explicit id #'s. + * + * 6 11/12/98 12:13a Dave + * Tidied code up for multiplayer test. Put in network support for flak + * guns. + * + * 5 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 4 10/19/98 11:15a Dave + * Changed requirements for stats storing in PXO mode. + * + * 3 10/07/98 6:27p Dave + * Globalized mission and campaign file extensions. Removed Silent Threat + * special code. Moved \cache \players and \multidata into the \data + * directory. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 257 9/20/98 7:19p Dave + * Added CHANGE_IFF packet. + * + * 256 9/16/98 6:54p Dave + * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort + * the ship list box. Added code so that tracker stats are not stored with + * only 1 player. + * + * 255 9/15/98 7:24p Dave + * Minor UI changes. Localized bunch of new text. + * + * 254 9/15/98 4:03p Dave + * Changed readyroom and multi screens to display "st" icon for all + * missions with mission disk content (not necessarily just those that + * come with Silent Threat). + * + * 253 9/11/98 5:08p Dave + * More tweaks to kick notification system. + * + * 252 9/11/98 2:05p Allender + * make reinforcements work correctly in multiplayer games. There still + * may be a team vs team issue that I haven't thought of yet :-( + * + * 251 9/04/98 3:51p Dave + * Put in validated mission updating and application during stats + * updating. + * + * 250 8/31/98 2:06p Dave + * Make cfile sort the ordering or vp files. Added support/checks for + * recognizing "mission disk" players. + * + * 249 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 248 8/12/98 4:53p Dave + * Put in 32 bit checksumming for PXO missions. No validation on the + * actual tracker yet, though. + * + * 247 7/24/98 9:27a Dave + * Tidied up endgame sequencing by removing several old flags and + * standardizing _all_ endgame stuff with a single function call. + * + * 246 7/10/98 5:04p Dave + * Fix connection speed bug on standalone server. + * + * 245 7/10/98 11:52a Allender + * changes to the connection type + * + * 244 7/10/98 1:13a Allender + * lots of small multiplayer update changes. Code in launcher to specify + * connection speed. A couple of small fixes regarding empty mission + * files. Time out players after 10 second when they don't connect on + * their reliable socket. + * + * 243 7/07/98 2:56p Dave + * + * 242 7/02/98 6:16p Dave + * Make rear facing prediction much better. Tweak update levels and + * viewcone values. Make sure observers send targeting info correctly. + * + * 241 6/30/98 2:17p Dave + * Revised object update system. Removed updates for all weapons. Put + * button info back into control info packet. + * + * 240 6/22/98 8:36a Allender + * revamping of homing weapon system. don't send as object updates + * anymore + * + * 239 6/17/98 10:56a Dave + * Put in debug code for detecting potential tracker stats update + * problems. + * + * 238 6/12/98 2:49p Dave + * Patch 1.02 changes. + * + * 237 6/10/98 2:56p Dave + * Substantial changes to reduce bandwidth and latency problems. + * + * 236 6/04/98 10:06p Allender + * upped packet version + * + * 235 6/04/98 11:46a Dave + * Drastically reduce size/rate of client control info update packets. Put + * in rate limiting for object updating from server. + * + * + * $NoKeywords: $ + */ + +#ifndef _MULTI_H +#define _MULTI_H + +#include "psnet.h" // for PSNET_SOCKET +#include "player.h" +#include "multi_ping.h" +#include "missionparse.h" +#include "multi_options.h" + +// ---------------------------------------------------------------------------------------- +// Basic defines +// +// + +struct CFILE; + +// defines for checking PXO valid missions +#ifdef NDEBUG + // NEVER COMMENT OUT THIS LINE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + #define PXO_CHECK_VALID_MISSIONS // always check for valid missions in a debug build +#else + // #define PXO_CHECK_VALID_MISSIONS // comment this in and out as necessary (for testing or not) +#endif + +// name of the validated mission file for PXO missions +#define MULTI_VALID_MISSION_FILE "mvalid.cfg" + +// server version and compatible version +// to join a game - your LOCAL.MULTI_FS_SERVER_COMPATIBLE_VERSION must be >= GAME_SERVER.MULTI_FS_SERVER_VERSION +// Version #. Please put the date down when you up these values +// NOTE: always keep SERVER_VERSION and SERVER_COMPATIBLE_VERSION the same +// +// version 32 - 1/29/99 +// version 33 - 2/22/99 +// version 34 - 4/8/99 +// version 35 - 4/21/99 +// version 36 - 4/28/99 +// version 37 - 4/29/99 +// version 38 - 5/17/77 +// version 39 - 7/3/99 +// version 40 - 7/7/99 +// version 41 - 7/22/99 +// version 42 - 7/26/99 (ingame join stuff) +// version 43 - 7/30/99 +// version 44 - 8/24/99 +// version 46 - 8/30/99 +// STANDALONE_ONLY +#define MULTI_FS_SERVER_VERSION 46 +#define MULTI_FS_SERVER_COMPATIBLE_VERSION MULTI_FS_SERVER_VERSION + +// version defines (i.e. demo, full version, special OEM version +// id's should not be > 255!!!! +#define NG_VERSION_ID_FULL 1 + +// set the next define to be what version you want to build for. +#define NG_VERSION_ID NG_VERSION_ID_FULL + +// the max # of active players (flying ships) +#define MULTI_MAX_PLAYERS 12 + +// the total max # of connections (players + observers + (possibly)standalone server) +#define MULTI_MAX_CONNECTIONS 16 + +// the max # of observers ever allowed +#define MAX_OBSERVERS 4 + +#define LOGIN_LEN 33 + +// string length defines +#define MAX_GAMENAME_LEN 32 // maximum length in characters of a game name +#define DESCRIPT_LENGTH 512 // maximum length of a mission description (as specified by Fred) +#define MAX_PASSWD_LEN 16 // maximum length of the password for a netgame + +// low level networking defines +#define IP_ADDRESS_LENGTH 4 // length of the address field for an IP address +#define IP_PORT_LENGTH 2 // length of the port field for an IP address +#define IPX_ADDRESS_LENGTH 6 // length of the address field for an IPX address +#define IPX_PORT_LENGTH 2 // length of the port field for an IPX address + +// netgame defines +#define RESPAWN_ANARCHY (0xffffffff)// respawn setting for an "anarchy" style game +#define MP_SINGLE 0 // not playing a campaign - single mission +#define MP_CAMPAIGN 1 // playing a campaign + +// respawn defines +#define RESPAWN_INVUL_TIMESTAMP 5000 // how long a player is invulnerable after he respawns +#define MAX_RESPAWN_POINTS 25 // the max # of respawn points we'll keep track of for any mission + +// player information defines +#define BUTTON_INFO_SAVE_COUNT 30 // how many buttons infos we keep track of for sending critical keypresses to the server +#define MAX_PINGS 10 // how many pings we keep track of for averaging player pings +#define OBJECT_UDPATE_DIFF_TOLERANCE 40000 // how big of a difference in sequence numbers (control_info and object_updates) before we reset + +// reliable connect wait +#define MULTI_RELIABLE_CONNECT_WAIT 15 + +// tracker mission validation status +#define MVALID_STATUS_UNKNOWN -1 +#define MVALID_STATUS_VALID 0 +#define MVALID_STATUS_INVALID 1 + +// ---------------------------------------------------------------------------------------- + + +// ---------------------------------------------------------------------------------------- +// Network macros +// +// + +// netplayer management +#define NET_PLAYER_INDEX(np) (np-Net_players) +#define NET_PLAYER_NUM(np) (NET_PLAYER_INDEX(np)) +#define MY_NET_PLAYER_NUM (NET_PLAYER_INDEX(Net_player)) + +// determine what the status of this machine is +#define MULTIPLAYER_MASTER ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) ) +#define MULTIPLAYER_HOST ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_GAME_HOST) ) +#define MULTIPLAYER_CLIENT ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ) +#define MULTIPLAYER_STANDALONE ( (Game_mode & GM_MULTIPLAYER) && (Net_players[0].flags & NETINFO_FLAG_AM_MASTER) && !(Net_players[0].flags & NETINFO_FLAG_GAME_HOST) ) + +// determine the status of the passed player +#define MULTI_CONNECTED(np) (np.flags & NETINFO_FLAG_CONNECTED) +#define MULTI_HOST(np) (np.flags & NETINFO_FLAG_GAME_HOST) +#define MULTI_SERVER(np) (np.flags & NETINFO_FLAG_AM_MASTER) +#define MULTI_STANDALONE(np) ((np.flags & NETINFO_FLAG_AM_MASTER) && !(np.flags & NETINFO_FLAG_GAME_HOST)) +#define MULTI_OBSERVER(np) (np.flags & NETINFO_FLAG_OBSERVER) +#define MULTI_TEMP_OBSERVER(np) ((np.flags & NETINFO_FLAG_OBSERVER) && (np.flags & NETINFO_FLAG_OBS_PLAYER)) +#define MULTI_PERM_OBSERVER(np) ((np.flags & NETINFO_FLAG_OBSERVER) && !(np.flags & NETINFO_FLAG_OBS_PLAYER)) + +// are we playing on a master tracker registered server +#define MULTI_IS_TRACKER_GAME (0) +// ---------------------------------------------------------------------------------------- + + +// ---------------------------------------------------------------------------------------- +// Packet definitions and additional packet data types +// +// + +#define NETPLAYER_SLOTS_P 0x01 // data telling clients what player has what object number ... +#define FIRING_INFO 0x02 // firing info packet +#define INGAME_SHIP_UPDATE 0x03 // ship status-like update for ingame joiners choosing ships. +#define INGAME_SHIP_REQUEST 0x04 // used for requesting a replying (confirm or no) ships for an ingame joiner +#define TEAM_SELECT_UPDATE 0x05 // update from host to players in team select regarding who has what ship +#define FILE_SIG_INFO 0x06 // file signature info ( as calculated by get_bigass_file_signature() ) +#define RESPAWN_NOTICE 0x07 // from client to server, or server to client +#define LOAD_MISSION_NOW 0x08 // clients should load the mission and return an ack +#define FILE_SIG_REQUEST 0x09 // request from server to client for filesig info +#define JUMP_INTO_GAME 0x0A // from server to client telling him to jump into the mission +#define RESPAWN_POINTS 0x0B // from server to ingame joiners, giving respawn data +#define CLIENT_REPAIR_INFO 0x0C // passing various data to clients indicating their repair status +#define CARGO_REVEALED 0x0E // cargo is known +#define SHIELD_EXPLOSION 0x0F // shield explosion + +#define SUBSYSTEM_DESTROYED 0x10 // update about a subsystem update +#define MISSION_SYNC_DATA 0x11 // this is a unique packet from the host to the server in a standalone game +#define STORE_MISSION_STATS 0x12 // sent to client indicating the host has hit "accept" and they should update their alltime stats +#define DEBRIS_UPDATE 0x13 // debris position, velocity, orientation, etc update +#define SHIP_WSTATE_CHANGE 0x14 // used to tell clients that a ship's primary/secondary state changed +#define WSS_REQUEST_PACKET 0x15 // from client to server, requesting to do an operation +#define WSS_UPDATE_PACKET 0x16 // from server to client, given any update +#define KICK_PLAYER 0x17 // kick a specific player (sent to server by those who are allowed to do this) +#define MISSION_GOAL_INFO 0x18 // update of mission goal info +#define ASTEROID_INFO 0x19 // update of asteroid stuff +#define NETPLAYER_PAIN 0x1A // to notify the player of hits which he may not otherwise see +#define OBJECT_UPDATE_NEW 0x1B +#define SUBSYS_CARGO_REVEALED 0X1C // Capital ship cargo subsystem is known + +#define POST_SYNC_DATA 0x20 // a very large packet containing all the data players will need before going into the mission itself +#define PLAYER_SETTINGS 0x21 // player settings for each individual net player +#define WSS_SLOTS_DATA 0x22 // all weapon slots information for the starting wings (needs to be synched) +#define PLAYER_STATS 0x23 // stats for a given player +#define SLOT_UPDATE 0x24 // an player slot position update in multiplayer ship/team select +#define TEAM_UPDATE 0x25 // used for performing misc operations on pregame interface screens for team vs. team games +#define INGAME_EXTRA 0x26 // extra data for ingame joiners +#define HOST_RESTR_QUERY 0x27 // query the host when a player joins a restricted game +#define OPTIONS_UPDATE 0x28 // options (netgame or local) update packet +#define CLIENT_UPDATE 0x29 // sent from server to client periodically to update important info (pause status, etc) +#define CD_VERIFY 0x2A // cd verification update +#define PRIMARY_FIRED_NEW 0x2B // for client-side firing - highly streamlined +#define COUNTERMEASURE_NEW 0x2C // for client-side firing +#define EVENT_UPDATE 0x2D // event change + +#define SECONDARY_FIRED_AI 0xA0 // fired a secondary weapon (ai ship) +#define SECONDARY_FIRED_PLR 0xA1 // fired a secondary weapon (player ship) +#define COUNTERMEASURE_FIRED 0xA2 // countermeasure was fired +#define FIRE_TURRET_WEAPON 0xA3 // a turret weapon was fired +#define SHIP_STATUS_CHANGE 0xA4 // any of the relevant ship status buttons have been hit (need ack from server) +#define PLAYER_ORDER_PACKET 0xA5 // ship and wing commands sent from client to server +#define AI_INFO_UPDATE 0xA6 // update ai information for the given ship +#define CAMPAIGN_UPDATE 0xA7 // one of several campaign informational packets +#define CAMPAIGN_UPDATE_INGAME 0xA8 // campaign info for ingame joiner +#define HOMING_WEAPON_UPDATE 0xA9 // update homing object and subsystem for homing missile +#define FLAK_FIRED 0xAA // flak gun fired +#define SELF_DESTRUCT 0xAB // self destruct + +#define JOIN 0xB1 // a join request to a server +#define ACCEPT 0xB2 // acceptance of a join packet +#define DENY 0xB3 // a join request is denied +#define NOTIFY_NEW_PLAYER 0xB4 // notify players of a new player +#define MISSION_REQUEST 0xB5 // request a list of missions on the master. +#define MISSION_ITEM 0xB6 // a bundle of mission filenames +#define GAME_INFO 0xB7 // game information packet for an active server +#define MULTI_PAUSE_REQUEST 0xB8 // send a request to the server to pause or unpause the game +#define TRANSFER_HOST 0xB9 // transfer host status to the receiver +#define CHANGE_SERVER_ADDR 0xBA // change your host_addr to this value +#define ACCEPT_PLAYER_DATA 0xBB // player data -- sent after guy is accepted +#define BEAM_FIRED 0xBC // a beam weapon was fired +#define SW_STD_QUERY 0xBD // query from host to standalone server to query PXO about a squad war match + +#define HUD_MSG 0xC1 // a hud message +#define LEAVE_GAME 0xC2 // indication that a player is leaving the game +#define GAME_CHAT 0xC3 // this chat packet used for Freespace +#define MISSION_MESSAGE 0xC4 // a message (squadmate, etc) +#define SHIP_DEPART 0xC5 // a ship has left the building +#define SHIPS_INGAME_PACKET 0xC6 // ingame join ship data packet +#define WINGS_INGAME_PACKET 0xC7 // ingame join wing data packet +#define MISSION_END 0xC8 // sent from host(master) to clients indicating they should go to the debriefing +#define INGAME_NAK 0xC9 // opposite of INGAME_ACK. Uses the ACK_* defines as well. +#define OBSERVER_UPDATE 0xCA // sent from observers to server to give position and orientation +#define SQUADMSG_PLAYER 0xCB // a squadmate message command has been sent to a netplayer +#define OBJ_UPDATE_SYNC 0xCC // object update timebase syncing code + +#define WEAPON_DET 0xD1 // a weapon has detonated, possibly with child weapons +#define SHIP_KILL 0xD2 // a ship was killed +#define WING_CREATE 0xD3 // create and warp in a wing of ships +#define SHIP_CREATE 0xD4 // create and wrap in a ship +#define PING 0xD5 // ping +#define PONG 0xD6 // pong +#define XFER_PACKET 0xD7 // file xfer data of one kind or another +#define VOICE_PACKET 0xD8 // a voice streaming packet of one kind or another +#define NETGAME_END_ERROR 0xD9 // the netgame has been ended by the server because of an error (passed error code) +#define COUNTERMEASURE_SUCCESS 0xDA // countermeasure was successful for this player. +#define REINFORCEMENT_AVAIL 0xDB // a reinforcement is available +#define LIGHTNING_PACKET 0xDC // lightning bolt packet for multiplayer nebula +#define BYTES_SENT 0xDD // how much data we've sent/received + +#define GAME_ACTIVE 0xE1 // info on an active game server +#define GAME_QUERY 0xE2 // request for a list of active game servers +#define GAME_UPDATE 0xE3 // update info on an active game server +#define NETPLAYER_UPDATE 0xE4 // a player update packet +#define OBJECT_UPDATE 0xE6 // an object update packet from server to all clients +#define MISSION_LOG_ENTRY 0xE7 // ad an item into the mission log +#define UPDATE_DESCRIPT 0xE8 // update the netgame description +#define COUNTDOWN 0xE9 // countdown timer for starting a game (pretty benign) +#define DEBRIEF_INFO 0xEA // end of mission debriefing information +#define EMP_EFFECT 0xEB // EMP effect (mission disk only) +#define CHANGE_IFF 0xEC // change iff (1.04+ only) + +#define MAX_TYPE_ID 0xFF // better not try to send > 255 in a single byte buddy + +// ingame ack data codes +#define ACK_RESPAWN_POINTS 0x1 // from ingame joiner to server, indicating he got the respawn points packet +#define ACK_FILE_ACCEPTED 0x2 // server to client saying their file is valid +#define ACK_FILE_REJECTED 0x3 // server to client saying their file is not valid + +// join request denial codes +#define JOIN_DENY_JR_STATE 0 // join request is rejected because the game is not in the proper state +#define JOIN_DENY_JR_TRACKER_INVAL 1 // join request is rejected because the game is an MT game and the passed player info is invalid +#define JOIN_DENY_JR_PASSWD 2 // join request is rejected because the game is password protected and the password sent is incorrect +#define JOIN_DENY_JR_CLOSED 3 // join request is rejected because the game is closed and is currently ingame +#define JOIN_DENY_JR_RANK_HIGH 4 // join request is rejected because the game is rank based and the passed rank is too high +#define JOIN_DENY_JR_RANK_LOW 5 // join request is rejected because the game is rank based and the passed rank is too low +#define JOIN_DENY_JR_DUP 6 // join request is denied because there is an exiting netplayer with matching characteristics +#define JOIN_DENY_JR_FULL 7 // join request is denied because the game is full +#define JOIN_DENY_JR_TEMP_CLOSED 8 // join request is denied because the _forming_ netgame has been toggled closed +#define JOIN_DENY_JR_BANNED 9 // join request is denied because the player has been banned for the duration of the game +#define JOIN_DENY_JR_NOOBS 10 // join request is denied because observers are not allowed +#define JOIN_DENY_JR_INGAME_JOIN 11 // join request is denied because someone else is already ingame joining +#define JOIN_DENY_JR_BAD_VERSION 12 // incompatible version types +#define JOIN_QUERY_RESTRICTED 13 // poll the host of the game to see if he accepts this player +#define JOIN_DENY_JR_TYPE 14 // cannot ingame join anything but dogfight games + +// repair info codes +#define REPAIR_INFO_BEGIN 0x1 // server to client - set your REPAIRING flags +#define REPAIR_INFO_END 0x2 // server to client - unset your REPAIRING flags +#define REPAIR_INFO_UPDATE 0x3 // server to client - here's some repair update info (not currently used) +#define REPAIR_INFO_QUEUE 0x4 // server to client - client is queued for rearming. +#define REPAIR_INFO_ABORT 0x5 // server to client - client has aborted a rearm/repair +#define REPAIR_INFO_BROKEN 0x6 // server to client - client is breaking repair -- might be reestablished +#define REPAIR_INFO_WARP_ADD 0x7 // server to client - add client onto list of people for arriving support ship +#define REPAIR_INFO_WARP_REMOVE 0x8 // server to client - remove client from list of people for arriving support ship +#define REPAIR_INFO_ONWAY 0x9 // server to client - repair ship on way +#define REPAIR_INFO_KILLED 0xa // server to client - repair ship was killed on way to rearm player +#define REPAIR_INFO_COMPLETE 0xb // server to client - repair of your ship is complete + +// debris update codes +#define DEBRIS_UPDATE_UPDATE 0x1 // update a piece +#define DEBRIS_UPDATE_REMOVE 0x2 // remove a piece of debris +#define DEBRIS_UPDATE_NUKE 0x3 // blow up a piece of debris +#define DEBRIS_UPDATE_CREATE_HULL 0x4 // create a piece of debris + +// weapon select/ship select update packets +#define WSS_WEAPON_SELECT 0x1 // ship select stuff +#define WSS_SHIP_SELECT 0x2 // weapon select stuff + +// accept packet codes +#define ACCEPT_INGAME (1<<0) // accept the player as an ingame joiner +#define ACCEPT_HOST (1<<1) // accept the player as the host of the game +#define ACCEPT_OBSERVER (1<<2) // accept the player as an observer +#define ACCEPT_CLIENT (1<<3) // accept the player as a normal, non ingame join, non host player + +// accept player data codes +#define APD_NEXT 0 // there is still more player data +#define APD_END_PACKET 1 // end of this packet +#define APD_END_DATA 2 // end of the data + +// ingame ship request codes +#define INGAME_SR_REQUEST 0x1 // request for the ship with the given net signature +#define INGAME_SR_CONFIRM 0x2 // confirmation to the client that he can use the requested ship +#define INGAME_SR_DENY 0x3 // deny the request the ingame joiner made for the ship +#define INGAME_PLAYER_CHOICE 0x4 // sent to other players informing them of ingame joiners choice + +// ai info update codes +#define AI_UPDATE_DOCK 0x1 // server tells clients which ships are now docked +#define AI_UPDATE_UNDOCK 0x2 // server tells clients which ships have undocked +#define AI_UPDATE_ORDERS 0x3 // server tells clients about new AI order for the ship + +// requests to the standalong +#define MISSION_LIST_REQUEST 0x1 // ask for list of missions +#define CAMPAIGN_LIST_REQUEST 0x2 // ask for list of campaigns + +// asteroid stuff +#define ASTEROID_CREATE 0x1 // create an asteroid +#define ASTEROID_THROW 0x2 // throw an asteroid +#define ASTEROID_HIT 0x3 // asteroid hit occured + +// commands for squadmate messages +#define SQUAD_MSG_SHIP 0x1 +#define SQUAD_MSG_WING 0x2 +#define SQUAD_MSG_ALL 0x3 +#define SQUAD_MSG_REINFORCEMENT 0x4 + +// SW_STD_QUERY codes +#define SW_STD_START 0x1 // from host to standalone saying "query the tracker" +#define SW_STD_OK 0x2 // from standalone to host - "everything is cool" +#define SW_STD_BAD 0x3 // from standalone to host - "everything is bad" + +// stats block packet +#define STATS_MISSION 0 // all stats for the mission, for one player +#define STATS_ALLTIME 1 // alltime stats, for one player +#define STATS_MISSION_KILLS 2 // mission kills and assists +#define STATS_DOGFIGHT_KILLS 3 // same as mission kills, but also sends per-player kills + + +// ---------------------------------------------------------------------------------------- + + +// ---------------------------------------------------------------------------------------- +// Multiplayer structure definitions +// +// + +// definition of header packet used in any protocol +typedef struct header { + int bytes_processed; // used to determine how many bytes this packet was + ubyte net_id[4]; // obtained from network layer header + ubyte addr[6]; // obtained from network-layer header + short port; // obtained from network-layer header + short id; // will be stuffed with player_id (short) +} header; + +// NETPLAYER INFORMATION THE SERVER AND THE INDIVIDUAL CLIENT MUST HAVE +typedef struct net_player_server_info { + ping_struct ping; // evaluated by the ping module + int wing_index_backup; // in case of fail on the last packet + int wing_index; // index of the next wing data item to be sent + int ingame_join_flags; // status flags for an ingame joiner + int invul_timestamp; // invulnerability flag timestamp (for respawning after dying) + button_info last_buttons[BUTTON_INFO_SAVE_COUNT]; // button info for sending critical control packets to the server + int last_buttons_id[BUTTON_INFO_SAVE_COUNT]; // + fix last_buttons_time[BUTTON_INFO_SAVE_COUNT];// + int num_last_buttons; // + fix last_full_update_time; // time when server last updated this player position/orientation + int xfer_handle; // handle to the file xfer handle (-1 if no file xfer is taking place) + int kick_timestamp; // timestamp with which we'll disconnect a player if he hasn't reponded to a kick packet + int kick_reason; // reason he was kicked + int voice_token_timestamp; // timestamp set when a player loses a token (so we can prevent him from getting it again too quickly) + int reliable_connect_time; // after sending an accept packet, wait for this long for the guy to connect on the reliable socket + + // weapon select/linking information (maintained on the server and passed on respawn to all clients) + char cur_primary_bank; // currently selected primary bank + char cur_secondary_bank; // currently selected secondary bank + ubyte cur_link_status; // if (1<<0) - primaries linked. if (1<<1) - secondaries are linked + + // information regarding the current view position of this player. + vector eye_pos; // eye position and orientation + matrix eye_orient; + + // ets information + ushort ship_ets; // ets settings (sigh......) + + // tracker information + int tracker_security_last; // this is the value returned when getting tracker data. it must be used when "sending" tracker data + unsigned int tracker_checksum; // tracker checksum + + // common targeting information + int target_objnum; + + // rate limiting information + int rate_stamp; // rate limiting timestamp + int rate_bytes; // bytes sent this "second" + + // firing info (1<<0) for primary fire, (1<<1) for secondary fired, (1<<2) for countermeasure fired, (1<<3) for afterburner on + // basically, we set these bits if necessary between control info sends from the client. once sent, these values are + // cleared until the next send time + ubyte accum_buttons; + + // buffered packet info + ubyte unreliable_buffer[MAX_PACKET_SIZE]; // buffer used to buffer unreliable packets before sending as a single UDP packet + int unreliable_buffer_size; // length (in bytes) of data in unreliable send_buffer + ubyte reliable_buffer[MAX_PACKET_SIZE]; // buffer used to buffer reliable packets before sending as a single UDP packet + int reliable_buffer_size; // length (in bytes) of data in reliable send_buffer +} net_player_server_info; + +// NETPLAYER INFORMATION ALL COMPUTERS IN THE GAME MUST HAVE +typedef struct net_player_info { + p_object *p_objp; // pointer to parse object for my ship -- used with respawns + int team; // valid for team v. team games -- which team is this guy on + int ship_index; // index into the ship choices in team select/ship select (out of 12 choices) + int ship_class; // the ship class of the players ship + multi_local_options options; // players options settings + net_addr addr; + char pxo_squad_name[LOGIN_LEN]; // PXO squadron name +} net_player_info; + +// NETPLAYER COMMON INFORMATION +typedef struct net_player { + player_t *player; // stuff pertaining directly to the player (callsign, etc). + short player_id; // player id (always use this instead of ip address for identification purposes) + int tracker_player_id; // the tracker id for this player, only matters in + // tracker games. + int flags; // tells us interesting information about this player + int state; // one of the NETGAME_STATE_* flags below -- used for sequencing + PSNET_SOCKET_RELIABLE reliable_socket; // reliable socket to server + + ushort client_cinfo_seq; // sequence # for client control info packets + ushort client_server_seq; // sequence # for incoming object update packets + + fix last_heard_time; // time when last heard from this player + + net_player_server_info s_info; // server critical info + net_player_info p_info; // player critical info + + // bytes sent and received + // SERVER-side + int sv_bytes_sent; // bytes we've sent to this guy (on the server) + int sv_last_pl; // packet loss + + // CLIENT-side + int cl_bytes_recvd; // bytes we've received (as a client) + int cl_last_pl; // packet loss +} net_player; + +// structure which describes the state of the multiplayer game currently being played +typedef struct netgame_info { + char name[MAX_GAMENAME_LEN+1]; // name of the netgame (host can set this!) + char mission_name[NAME_LENGTH+1]; // current mission name (filename) + char title[NAME_LENGTH+1]; // title of the mission (as appears in the mission file) + char campaign_name[NAME_LENGTH+1]; // current campaign name + char passwd[MAX_PASSWD_LEN+1]; // password for the game + int version_info; // version info for this game. + int type_flags; // see NG_TYPE_* defines + int mode; // see NG_MODE_* defines + int flags; // see NG_FLAG_* defines + int rank_base; // used to compare against connecting players (rank above/rank below) + int max_players; + int game_state; // state (briefing, in mission, etc) this game is in + int security; // some random number that should hopefully be unique for each game started + // I'm also using this value to use as a starting base for the net_signature + // for object synchronization. + float ping_time; // ping time to this server + net_addr server_addr; // address of the server + net_player *host; + net_player *server; // pointer to the server + + uint respawn; + + int campaign_mode; // 0 == single mission mode, 1 == starting campaign + + ushort server_update_seq; // the current object update reference count (server-side only) + int server_update_frame_ref; // used to determine when we should increment the server_update_seq value + + multi_server_options options; // server options settings + + ubyte debug_flags; // special debug flags (see NETD_FLAG_* defines) +} netgame_info; + +// netgame debug flags +// #define NETD_FLAG_CLIENT_FIRING (1<<0) // client side firing of primaries and countermeasures +// #define NETD_FLAG_CLIENT_NODAMAGE (1<<1) // client never applies damage himself. he simply waits for blanket updates +#define NETD_FLAG_OBJ_NEW (1<<2) // new style of object updating + +// structure for active games -- kind of like Descent, but using the linked list thing, we will +// be able to support many more games in the list. +#define AG_FLAG_COOP (1<<0) // is a coop game +#define AG_FLAG_TEAMS (1<<1) // is a team vs. team game +#define AG_FLAG_DOGFIGHT (1<<2) // is a dogfight game +#define AG_FLAG_FORMING (1<<3) // game is currently forming +#define AG_FLAG_BRIEFING (1<<4) // game is in the briefing state +#define AG_FLAG_DEBRIEF (1<<5) // game is in the debriefing state +#define AG_FLAG_PAUSE (1<<6) // game is paused +#define AG_FLAG_IN_MISSION (1<<7) // game is in mission +#define AG_FLAG_PASSWD (1<<8) // is a password protected game +#define AG_FLAG_STANDALONE (1<<9) // this is a standalone server +#define AG_FLAG_CAMPAIGN (1<<10) // the server is playing in campaign mode + +// flags for defining the connection speed +#define AG_FLAG_CONNECTION_SPEED_MASK ((1<<12)|(1<<13)|(1<<14)) // mask for the connection speed + +#define AG_FLAG_VALID_MISSION (1<<15) // the mission is a "valid" tracker mission + +#define AG_FLAG_CONNECTION_BIT 12 // number of bits to shift right or left to get speed + +#define AG_FLAG_TYPE_MASK (AG_FLAG_COOP|AG_FLAG_TEAMS|AG_FLAG_DOGFIGHT) +#define AG_FLAG_STATE_MASK (AG_FLAG_FORMING|AG_FLAG_BRIEFING|AG_FLAG_DEBRIEF|AG_FLAG_PAUSE|AG_FLAG_IN_MISSION) + +typedef struct active_game { + active_game *next, *prev; // next and previous elements in the list + int heard_from_timer; // when we last heard from the game + + char name[MAX_GAMENAME_LEN+1]; + char mission_name[NAME_LENGTH+1]; + char title[NAME_LENGTH+1]; + ubyte num_players; + net_addr server_addr; + ushort flags; // see above AG_FLAG_* defines + ubyte version,comp_version; // version and compatible version + ping_struct ping; // ping time to the server +} active_game; + +// permanent server list (read from tcp.cfg) +typedef struct server_item { + server_item *next, *prev; + + net_addr server_addr; +} server_item; + +// sent to the server on a join request with various data +#define JOIN_FLAG_AS_OBSERVER (1<<0) // wants to join as an aboserver +#define JOIN_FLAG_HAS_CD (1<<1) // currently has a CD in the drive +#define JOIN_FLAG_HAXOR (1<<2) // if the player has hacked data +typedef struct join_request { + char passwd[MAX_PASSWD_LEN+1]; // password for a password protected game + char callsign[CALLSIGN_LEN+1]; // player's callsign + char image_filename[MAX_FILENAME_LEN+1]; // player's image filename + char squad_filename[MAX_FILENAME_LEN+1]; // player's squad filename + ubyte player_rank; // the rank of the requesting player + ubyte flags; // misc flags associated with this guy + int tracker_id; // player's tracker ID # + multi_local_options player_options; // player's options + ubyte version, comp_version; // local version and comp_version + + // multiplayer squad war info + char pxo_squad_name[LOGIN_LEN]; // squad name +} join_request; + +// network buffer for sending and receiving packets +typedef struct network_buffer { + int size; // size of the buffer + ubyte data[MAX_PACKET_SIZE]; // MAX_PACKET_SIZE from psnet.h +} network_buffer; +// ------------------------------------------------------------------------------------- + + +// ---------------------------------------------------------------------------------------- +// Multiplayer structure flags/settings +// +// + +// flags used for the net_player structure +#define NETINFO_FLAG_CONNECTED (1<<0) // if this player connected +#define NETINFO_FLAG_AM_MASTER (1<<1) // is this player the master +#define NETINFO_FLAG_MT_CONNECTED (1<<2) // if everything is hunky dory with the tracker connection +#define NETINFO_FLAG_MT_FAILED (1<<3) // all attempts to connect have failed +#define NETINFO_FLAG_MT_STARTUP (1<<4) // the initial state (ie, we haven't tried anything yet) +#define NETINFO_FLAG_GAME_HOST (1<<5) // I'm the host +#define NETINFO_FLAG_INGAME_JOIN (1<<6) // means he is still in the process of joining ingame +#define NETINFO_FLAG_OBSERVER (1<<7) // means he's an observer +#define NETINFO_FLAG_OBS_PLAYER (1<<8) // means he's an observer, but he was formerly a player (should show his stats) +#define NETINFO_FLAG_LIMBO (1<<9) // (client side) means he has to choose whether to be an observer or quit (no more respawns) +#define NETINFO_FLAG_MISSION_OK (1<<10) // this client's mission has been verified +#define NETINFO_FLAG_RESPAWNING (1<<11) // so that we wait on a keypress, or other user input before respawning +#define NETINFO_FLAG_DO_NETWORKING (1<<12) // set when we can send/receive data +#define NETINFO_FLAG_TEAM_LOCKED (1<<13) // if this is set, only the host can modify the team settings for this player +#define NETINFO_FLAG_TEAM_CAPTAIN (1<<14) // this player is the captain of his team +#define NETINFO_FLAG_KICKED (1<<15) // this player was kicked +#define NETINFO_FLAG_ACCEPT_INGAME (1<<16) // accepted ingame +#define NETINFO_FLAG_ACCEPT_HOST (1<<17) // accetped as host +#define NETINFO_FLAG_ACCEPT_OBSERVER (1<<18) // accepted as observer +#define NETINFO_FLAG_ACCEPT_CLIENT (1<<19) // accepted as client +#define NETINFO_FLAG_WARPING_OUT (1<<20) // clients keep track of this for themselves to know if they should be leaving +#define NETINFO_FLAG_HAS_CD (1<<21) // the player has a CD in the drive +#define NETINFO_FLAG_RELIABLE_CONNECTED (1<<22) // reliable socket is now active +#define NETINFO_FLAG_MT_GET_FAILED (1<<23) // set during MT stats update process indicating we didn't properly get his stats +#define NETINFO_FLAG_MT_SEND_FAILED (1<<24) // set during MT stats update process indicating we didn't properly send his stats +#define NETINFO_FLAG_MT_DONE (1<<25) // set when a player has been processed for stats (fail, succeed, or otherwise) +#define NETINFO_FLAG_HAXOR (1<<26) // the player has some form of hacked client data + +#define NETPLAYER_IS_OBSERVER(player) (player->flags & (NETINFO_FLAG_OBSERVER|NETINFO_FLAG_OBS_PLAYER)) +#define NETPLAYER_IS_DEAD(player) (player->flags & (NETINFO_FLAG_LIMBO|NETINFO_FLAG_RESPAWNING)) + +// netgame modes +#define NG_MODE_OPEN 1 // an open game +#define NG_MODE_CLOSED 2 // a closed game +#define NG_MODE_PASSWORD 3 // a password protected game +#define NG_MODE_RESTRICTED 4 // a restricted game +#define NG_MODE_RANK_ABOVE 5 // ranks above a certain rank are allowed +#define NG_MODE_RANK_BELOW 6 // ranks below a certain rank are allowed + +// netgame option flags +#define NG_FLAG_TEMP_CLOSED (1<<0) // a forming netgame is temporarily closed (should not be checked otherwise) +#define NG_FLAG_SERVER_LOST (1<<1) // client has temporarily lost contact with the server +#define NG_FLAG_INGAME_JOINING (1<<2) // someone is ingame joining. +#define NG_FLAG_INGAME_JOINING_CRITICAL (1<<3) // someone is ingame joining and at the critical point where we cannot do certain things. +#define NG_FLAG_STORED_MT_STATS (1<<4) // stored tracker stats in the debriefing already +#define NG_FLAG_HACKED_SHIPS_TBL (1<<5) // set when the server is playing with a hacked ships.tbl (only needed to notify hosts playing on a standalone) +#define NG_FLAG_HACKED_WEAPONS_TBL (1<<6) // set when the server is playing with a hacked weapons.tbl (only needed to notify hosts playing on a standalone) + +// netgame type flags +#define NG_TYPE_COOP (1<<0) // cooperative mode +#define NG_TYPE_TVT (1<<1) // team vs. team mode +#define NG_TYPE_SW (1<<2) // squad war +#define NG_TYPE_TEAM ( NG_TYPE_TVT | NG_TYPE_SW ) +#define NG_TYPE_DOGFIGHT (1<<3) // plain old dogfight mode + +// state defines for netgame states +#define NETGAME_STATE_FORMING 1 // players are joining, host is selecting missions, etc +#define NETGAME_STATE_BRIEFING 2 // players are reading the mission briefing +#define NETGAME_STATE_IN_MISSION 3 // the mission itself is being played +#define NETGAME_STATE_SERVER_TRANSFER 4 // server status is being transferred from one computer to another +#define NETGAME_STATE_PAUSED 5 // the netgame is paused +#define NETGAME_STATE_DEBRIEF 6 // the debriefing screen +#define NETGAME_STATE_MISSION_SYNC 7 // client/server data sync screens before and after the briefing stage +#define NETGAME_STATE_ENDGAME 8 // game is moving from gameplay to the debriefing state +#define NETGAME_STATE_STD_HOST_SETUP 9 // the host is on the setup netgame screen for the standalone server +#define NETGAME_STATE_HOST_SETUP 10 // the host is on the setup netgame screen _non_ standalone + +// state defines for netplayer states +#define NETPLAYER_STATE_JOINING 0 // joining the netgame +#define NETPLAYER_STATE_JOINED 1 // has joined and opened a reliable socket connection +#define NETPLAYER_STATE_MISSION_LOADING 2 // in the process of loading the mission +#define NETPLAYER_STATE_MISSION_LOADED 3 // mission loaded +#define NETPLAYER_STATE_BRIEFING 4 // in the briefing +#define NETPLAYER_STATE_SHIP_SELECT 5 // in the ship selection screen +#define NETPLAYER_STATE_WEAPON_SELECT 6 // in the weapon selection screen +#define NETPLAYER_STATE_DATA_LOAD 7 // loading specific data (textures, etc) +#define NETPLAYER_STATE_WAITING 8 // waiting to do something (like enter the mission) +#define NETPLAYER_STATE_SLOT_ACK 9 // got player ship slot packets +#define NETPLAYER_STATE_IN_MISSION 10 // in the mission itself +#define NETPLAYER_STATE_INGAME_SHIPS 11 // player is receiving ingame join ship data +#define NETPLAYER_STATE_INGAME_WINGS 12 // player is receiving ingame join wing data +#define NETPLAYER_STATE_INGAME_RPTS 13 // player is receiving ingame join respawn data +#define NETPLAYER_STATE_INGAME_SHIP_SELECT 14 // player is in the ship select screen for ingame join +#define NETPLAYER_STATE_DEBRIEF 15 // player is in the debrief state (screen) +#define NETPLAYER_STATE_MISSION_SYNC 16 // player is in the mission sync screen +#define NETPLAYER_STATE_STD_HOST_SETUP 17 // the host is on the setup netgame screen for the standalone server +#define NETPLAYER_STATE_HOST_SETUP 18 // the host is on the setup netgame screen _non_ standalone +#define NETPLAYER_STATE_SETTINGS_ACK 19 // the player has received the player settings data for all players +#define NETPLAYER_STATE_SLOTS_ACK 20 // the player has received wss slots data (ingame join only) +#define NETPLAYER_STATE_POST_DATA_ACK 21 // the player has received the post briefing data block +#define NETPLAYER_STATE_WSS_ACK 22 // have received weapon slot information +#define NETPLAYER_STATE_FLAG_ACK 23 // the player has received his flag change information +#define NETPLAYER_STATE_MT_STATS 24 // the server is in the process of requesting or updating stats from the MT +#define NETPLAYER_STATE_MISSION_XFER 25 // the player is in the process of receiving a mission file +#define NETPLAYER_STATE_INGAME_STUFF 26 // received ingame "stuff" happens just before ship selection +#define NETPLAYER_STATE_DEBRIEF_ACCEPT 27 // the player has hit the accept button in the debrief and is good to go +#define NETPLAYER_STATE_DEBRIEF_REPLAY 28 // set on the host instead of NETPLAYER_STATE_DEBRIEF_ACCEPT to indicate he wants to replay the mission +#define NETPLAYER_STATE_CPOOL_ACK 29 // player has acked all campaign pool status data +#define NETPLAYER_STATE_INGAME_CINFO 30 // player has received campaign information (ingame join only) + +// defines for connection speed +#define CONNECTION_SPEED_NONE -1 // not really used except for error checking + +#define CONNECTION_SPEED_288 0 +#define CONNECTION_SPEED_56K 1 +#define CONNECTION_SPEED_SISDN 2 +#define CONNECTION_SPEED_CABLE 3 +#define CONNECTION_SPEED_T1 4 + +// use this to check and see whether a netgame is anywhere in mission (paused, etc, etc) +#define MULTI_IN_MISSION ( (Netgame.game_state == NETGAME_STATE_IN_MISSION) || (Netgame.game_state == NETGAME_STATE_PAUSED) ) + +extern int Multi_connection_speed; + +// ------------------------------------------------------------------------------------- + + +// ---------------------------------------------------------------------------------------- +// Multiplayer global vars +// +// + +// netplayer vars +extern net_player Net_players[MAX_PLAYERS]; // array of all netplayers in the game +extern net_player *Net_player; // pointer to console's net_player entry + +// network object management +#define SHIP_SIG_MIN 1 +#define SHIP_SIG_MAX (0x2fff) + +#define STANDALONE_SHIP_SIG (SHIP_SIG_MAX+1) +#define REAL_SHIP_SIG_MAX (0x3fff) + +#define DEBRIS_SIG_MIN (REAL_SHIP_SIG_MAX+1) +#define DEBRIS_SIG_MAX (0x5fff) + +#define ASTEROID_SIG_MIN (DEBRIS_SIG_MAX+1) +#define ASTEROID_SIG_MAX (0x7fff) + +#define NPERM_SIG_MIN (ASTEROID_SIG_MAX+1) +#define NPERM_SIG_MAX (0xffff) + +extern ushort Next_ship_signature; // next network signature to assign to an object +extern ushort Next_asteroid_signature; // next asteroid signature +extern ushort Next_non_perm_signature; // next non-permanent signature +extern ushort Next_debris_signature; // next debris signature + +// netgame vars +extern netgame_info Netgame; // netgame information +extern int Multi_mission_loaded; // flag, so that we dont' load the mission more than once client side +extern ushort Multi_ingame_join_sig; // signature for the player obj for use when joining ingame +extern int Multi_button_info_ok; // flag saying it is ok to apply critical button info on a client machine +extern int Multi_button_info_id; // identifier of the stored button info to be applying + +// low level networking vars +extern int ADDRESS_LENGTH; // will be 6 for IPX, 4 for IP +extern int PORT_LENGTH; // will be 2 for IPX, 2 for IP +extern int HEADER_LENGTH; // 1 byte (packet type) + +// misc data +extern active_game* Active_game_head; // linked list of active games displayed on Join screen +extern int Active_game_count; // for interface screens as well +extern CFILE* Multi_chat_stream; // for streaming multiplayer chat strings to a file +extern int Multi_has_cd; // if this machine has a cd or not (call multi_common_verify_cd() to set this) +extern int Multi_num_players_at_start; // the # of players present (kept track of only on the server) at the very start of the mission +extern short Multi_id_num; // for assigning player id #'s + +// permanent server list +extern server_item* Game_server_head; // list of permanent game servers to be querying + +// restricted game vars +#define MULTI_QUERY_RESTR_STAMP 5000 // 5 seconds to reply +#define MULTI_JOIN_RESTR_MODE_1 0 // mode 1 - normal restricted join +#define MULTI_JOIN_RESTR_MODE_2 1 // mode 2 - team vs. team, only team 0 has ships +#define MULTI_JOIN_RESTR_MODE_3 2 // mode 3 - team vs. team, only team 1 has ships +#define MULTI_JOIN_RESTR_MODE_4 3 // mode 4 - team vs. team, both teams have ships + +extern int Multi_restr_query_timestamp; // timestamp for querying the host to see if he will allow a new player to join ingame +extern join_request Multi_restr_join_request; // join request for the query +extern net_addr Multi_restr_addr; // net address of the join request +extern int Multi_join_restr_mode; // what mode we're in + +// non API master tracker vars +extern char Multi_tracker_login[100]; +extern char Multi_tracker_passwd[100]; +extern char Multi_tracker_squad_name[100]; +extern int Multi_tracker_id; +extern char Multi_tracker_id_string[255]; + +// current file checksum +extern ushort Multi_current_file_checksum; +extern int Multi_current_file_length; + + +// ---------------------------------------------------------------------------------------- +// Multiplayer main functions +// +// + +// module handling functions ------------------- + +// called at game startup +void multi_init(); + +// called whenever a netgame is started +void multi_level_init(); + +// reset all relevant main networking loop timestamps +void multi_reset_timestamps(); + +// returns true is server hasn't been heard from in N seconds. false otherwise +int multi_client_server_dead(); + + +// netgame data processing functions ----------- + +// do all network processing for this game frame +void multi_do_frame(); + +// analog of multi_do_frame() called when netgame is in the pause state +void multi_pause_do_frame(); + +// process all incoming packets +// void multi_process_bigdata(ubyte* data, int size, net_addr* from_addr); + +// process all reliable socket details +void multi_process_reliable_details(); + + +// standalone handling functions --------------- + +// initialize the standalone +void standalone_main_init(); + +// do frame for the main standalone state +void standalone_main_do(); + +// close for the main standalone state +void standalone_main_close(); + +// reset all standalone stuff, including gui, networking, netgame, etc, and go into the main state +void multi_standalone_reset_all(); + +// init for the wait mode of the standalone (when waiting for players to finish the briefing) +void multi_standalone_wait_init(); + +// do frame for the standalone wait state (when waiting for players to finish the briefing) +void multi_standalone_wait_do(); + +// close for the standalone wait state (when waiting for players to finish the briefing) +void multi_standalone_wait_close(); + +// init for the standalone postgame state (when players are in the debriefing) +void multi_standalone_postgame_init(); + +// do frame for the standalone postgame state (when players are in the debriefing) +void multi_standalone_postgame_do(); + +// close for the standalone postgame state (when players are in the debriefing_ +void multi_standalone_postgame_close(); + +#endif + diff --git a/include/multi_campaign.h b/include/multi_campaign.h new file mode 100644 index 0000000..cc65762 --- /dev/null +++ b/include/multi_campaign.h @@ -0,0 +1,109 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_campaign.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 7 5/21/98 12:14a Allender + * fix ingame join problems + * + * 6 5/19/98 8:35p Dave + * Revamp PXO channel listing system. Send campaign goals/events to + * clients for evaluation. Made lock button pressable on all screens. + * + * 5 5/05/98 5:02p Dave + * Fix end-of-campaign sequencing to work right. Make all individual + * missions of a campaign replayable. + * + * 4 5/05/98 2:10p Dave + * Verify campaign support for testing. More new tracker code. + * + * 3 2/23/98 11:09p Dave + * Finished up multiplayer campaign support. Seems bug-free. + * + * 2 2/22/98 2:53p Dave + * Put in groundwork for advanced multiplayer campaign options. + * + * 1 2/20/98 4:39p Dave + * Split up mp campaign functionality into its own module. + * + * $NoKeywords: $ + */ + +#ifndef _MULTIPLAYER_CAMPAIGN_HEADER_FILE +#define _MULTIPLAYER_CAMPAIGN_HEADER_FILE + + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER CAMPAIGN DEFINES/VARS +// + +struct net_player; + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER CAMPAIGN FUNCTIONS +// + +// load a new campaign file or notify the standalone if we're not the server +void multi_campaign_start(char *filename); + +// client-side start of a campaign +void multi_campaign_client_start(); + +// move everything and eveyrone into the next mission state +void multi_campaign_next_mission(); + +// flush all important data between missions +void multi_campaign_flush_data(); + +// call in the debriefing stage to evaluate what we should be doing in regards to the campaign +// if player_status == 0, nothing should be done +// == 1, players want to continue to the next mission +// == 2, players want to repeat the previous mission +void multi_campaign_do_debrief(int player_status); + +// display the done popup +void multi_campaign_done_popup(); + +// evaluate post mission goal stuff for the campaign and send all relevant stuff to clients +void multi_campaign_eval_debrief(); + + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER CAMPAIGN PACKET HANDLERS +// + +// process a campaign update packet +void multi_campaign_process_update(ubyte *data, header *hinfo); + +// send a "campaign finished" packet +void multi_campaign_send_done(); + +// send a campaign pool status packet +void multi_campaign_send_pool_status(); + +// send a campaign debrief update packet +void multi_campaign_send_debrief_info(); + +// send a "start campaign" packet +void multi_campaign_send_start(net_player *pl = NULL); + +// campaign information for ingame joiners +void multi_campaign_send_ingame_start(net_player *pl); +void multi_campaign_process_ingame_start( ubyte *data, header *hinfo ); + +#endif + diff --git a/include/multi_data.h b/include/multi_data.h new file mode 100644 index 0000000..c329446 --- /dev/null +++ b/include/multi_data.h @@ -0,0 +1,76 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_data.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 12/14/98 12:13p Dave + * Spiffed up xfer system a bit. Put in support for squad logo file xfer. + * Need to test now. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 4 3/26/98 6:01p Dave + * Put in file checksumming routine in cfile. Made pilot pic xferring more + * robust. Cut header size of voice data packets in half. Put in + * restricted game host query system. + * + * 3 3/23/98 5:42p Dave + * Put in automatic xfer of pilot pic files. Changed multi_xfer system so + * that it can support multiplayer sends/received between client and + * server simultaneously. + * + * 2 3/21/98 7:14p Dave + * Fixed up standalone player slot switching. Made training missions not + * count towards player stats. + * + * 1 2/19/98 6:21p Dave + * Player data file xfer module. + * + * $NoKeywords: $ + */ + +#ifndef _MULTI_PLAYER_DATA_HEADER_FILE +#define _MULTI_PLAYER_DATA_HEADER_FILE + +// ------------------------------------------------------------------------- +// MULTI DATA DEFINES/VARS +// + + +// ------------------------------------------------------------------------- +// MULTI DATA FUNCTIONS +// + +// reset the data xfer system +void multi_data_reset(); + +// handle a player join (clear out lists of files, etc) +void multi_data_handle_join(int player_index); + +// handle a player drop (essentially the same as multi_data_handle_join) +void multi_data_handle_drop(int player_index); + +// do all sync related data stuff (server-side only) +void multi_data_do(); + +// handle an incoming xfer request from the xfer system +void multi_data_handle_incoming(int handle); + +// send all my files as necessary +void multi_data_send_my_junk(); + + +#endif + diff --git a/include/multi_dogfight.h b/include/multi_dogfight.h new file mode 100644 index 0000000..5e63a7d --- /dev/null +++ b/include/multi_dogfight.h @@ -0,0 +1,49 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_dogfight.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * $NoKeywords: $ + */ + +#ifndef __FS2_MULTIPLAYER_DOGFIGHT_HEADER_FILE +#define __FS2_MULTIPLAYER_DOGFIGHT_HEADER_FILE + +// ---------------------------------------------------------------------------------------------------- +// MULTI DOGFIGHT DEFINES/VARS +// + +struct net_player; +struct object; + + +// ---------------------------------------------------------------------------------------------------- +// MULTI DOGFIGHT FUNCTIONS +// + +// call once per level just before entering the mission +void multi_df_level_pre_enter(); + +// evaluate a kill in dogfight by a netplayer +void multi_df_eval_kill(net_player *killer, object *dead_obj); + +// debrief +void multi_df_debrief_init(); + +// do frame +void multi_df_debrief_do(); + +// close +void multi_df_debrief_close(); + +#endif + diff --git a/include/multi_endgame.h b/include/multi_endgame.h new file mode 100644 index 0000000..d706581 --- /dev/null +++ b/include/multi_endgame.h @@ -0,0 +1,120 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_endgame.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 8/22/99 1:55p Dave + * Cleaned up host/team-captain leaving code. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 8 9/14/98 3:40p Allender + * better error checking for invalid number of waves for player wings in a + * multiplayer game. Better popup message in FreeSpace side. + * + * 7 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 6 7/24/98 9:27a Dave + * Tidied up endgame sequencing by removing several old flags and + * standardizing _all_ endgame stuff with a single function call. + * + * 5 7/13/98 5:19p Dave + * + * 4 5/17/98 6:32p Dave + * Make sure clients/servers aren't kicked out of the debriefing when team + * captains leave a game. Fixed chatbox off-by-one error. Fixed image + * xfer/pilot info popup stuff. + * + * 3 5/03/98 7:04p Dave + * Make team vs. team work mores smoothly with standalone. Change how host + * interacts with standalone for picking missions. Put in a time limit for + * ingame join ship select. Fix ingame join ship select screen for Vasudan + * ship icons. + * + * 2 4/30/98 5:12p Dave + * Fixed game polling code for joining clients. Reworked some file xfer + * stuff. + * + * 1 4/22/98 5:50p Dave + * + * + * $NoKeywords: $ + */ + +#ifndef _MULTI_ENDGAME_HEADER_FILE +#define _MULTI_ENDGAME_HEADER_FILE + +// ---------------------------------------------------------------------------------------------------------- +// Put all functions/data related to leaving a netgame, handling players leaving, handling the server leaving, +// and notifying the user of all of these actions, here. +// + + +// ---------------------------------------------------------------------------------------------------------- +// MULTI ENDGAME DEFINES/VARS +// + +// defines for calling multi_quit_game(...) +#define PROMPT_NONE 0 // don't prompt anyone when quitting (multi_quit_game) +#define PROMPT_HOST 1 // prompt the host when quitting (multi_quit_game) +#define PROMPT_CLIENT 2 // prompt the client when quitting (multi_quit_game) +#define PROMPT_ALL 3 // prompt any players when quitting (multi_quit_game) + +// notification defines for calling multi_quit_game(...) +#define MULTI_END_NOTIFY_NONE (-1) // no notification code +#define MULTI_END_NOTIFY_KICKED 1 // player was kicked +#define MULTI_END_NOTIFY_SERVER_LEFT 2 // server has left the game +#define MULTI_END_NOTIFY_FILE_REJECTED 3 // mission file was rejected by the server +#define MULTI_END_NOTIFY_EARLY_END 4 // game ended while the ingame joiner was joining +#define MULTI_END_NOTIFY_INGAME_TIMEOUT 5 // waited too long in the ship select screen +#define MULTI_END_NOTIFY_KICKED_BAD_XFER 6 // kicked because file xfer failed +#define MULTI_END_NOTIFY_KICKED_CANT_XFER 7 // kicked because can't xfer a builtin mission +#define MULTI_END_NOTIFY_KICKED_INGAME_ENDED 8 // kicked because was ingame joining in an ending game + +// error defines for calling multi_quit_game(...) +#define MULTI_END_ERROR_NONE (-1) // no error code +#define MULTI_END_ERROR_CONTACT_LOST 1 // contact with the server has been lost +#define MULTI_END_ERROR_CONNECT_FAIL 2 // failed to connect to the server +#define MULTI_END_ERROR_LOAD_FAIL 3 // failed to load the mission properly +#define MULTI_END_ERROR_INGAME_SHIP 4 // unable to create ingame join player ship +#define MULTI_END_ERROR_INGAME_BOGUS 5 // received bogus data on ingame join +#define MULTI_END_ERROR_STRANS_FAIL 6 // server transfer failed (obsolete) +#define MULTI_END_ERROR_SHIP_ASSIGN 7 // server had problems assigning players to ships +#define MULTI_END_ERROR_HOST_LEFT 8 // host has left a standalone game +#define MULTI_END_ERROR_XFER_FAIL 9 // mission file xfer failed on the client +#define MULTI_END_ERROR_WAVE_COUNT 10 // illegal data found in mission when parsing +#define MULTI_END_ERROR_TEAM0_EMPTY 11 // all of team 0 has left +#define MULTI_END_ERROR_TEAM1_EMPTY 12 // all of team 1 has left +#define MULTI_END_ERROR_CAPTAIN_LEFT 13 // captain of a team has left while not ingame + +// ---------------------------------------------------------------------------------------------------------- +// MULTI ENDGAME FUNCTIONS +// + +// initialize the endgame processor (call when joining/starting a new netgame) +void multi_endgame_init(); + +// process all endgame related events +void multi_endgame_process(); + +// if the game has been flagged as ended (ie, its going to be reset) +int multi_endgame_ending(); + +// general quit function, with optional notification, error, and winsock error codes +// return 0 if the act was cancelled, 1 if it was accepted +int multi_quit_game(int prompt,int notify_code = MULTI_END_NOTIFY_NONE,int err_code = MULTI_END_ERROR_NONE,int wsa_error = -1); + + +#endif + diff --git a/include/multi_ingame.h b/include/multi_ingame.h new file mode 100644 index 0000000..ebfaaa6 --- /dev/null +++ b/include/multi_ingame.h @@ -0,0 +1,232 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_ingame.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 12 5/20/98 3:25p Allender + * ingame join changes (which probably won't make the final version). + * Added RAS code into psnet + * + * 11 3/24/98 5:12p Allender + * ingame join packet sequencing + * + * 10 3/14/98 2:48p Dave + * Cleaned up observer joining code. Put in support for file xfers to + * ingame joiners (observers or not). Revamped and reinstalled pseudo + * lag/loss system. + * + * 9 3/13/98 2:51p Dave + * Put in support for observers to join ingame. + * + * 8 3/11/98 11:42p Allender + * more ingame join stuff. Fix to networking code to possibly + * reinitialize reliable socket when entering join screen + * + * 7 3/06/98 9:33a Allender + * more ingame join stuff. Wing packets are done. + * + * 6 3/03/98 8:23p Allender + * first pass of getting ingame join to a better state. Started rewriting + * the ship/wing list code + * + * 5 2/05/98 11:10a Dave + * Fixed an ingame join bug. Fixed a read-only file problem with + * multiplayer file xfer. + * + * 4 1/29/98 5:24p Dave + * Made ingame join handle bad packets gracefully + * + * 3 1/22/98 5:26p Dave + * Modified some pregame sequencing packets. Starting to repair broken + * standalone stuff. + * + * 2 1/21/98 5:58p Dave + * Finished ingame join. Coded in multiplayer interface artwork changes. + * + * 1 1/20/98 5:41p Dave + * Seperated ingame join functionality into its own module. + * + * $NoKeywords: $ + */ + +#ifndef _MULTI_INGAME_JOIN_HEADER_FILE +#define _MULTI_INGAME_JOIN_HEADER_FILE + +// -------------------------------------------------------------------------------------------------- +// DAVE's BIGASS INGAME JOIN WARNING/DISCLAIMER +// +// Ingame joining is another delicate system. Although not as delicate as server transfer, it will +// help to take as many precautions as possible when handling ingame joins. Please be sure to follow +// all the same rules as explained in multi_strans.h +// +// -------------------------------------------------------------------------------------------------- + +// -------------------------------------------------------------------------------------------------- +// INGAME JOIN DESCRIPTION +// +// 1.) Joiner sends a JOIN packet to the server +// 2.) If the server accepts him, he receives an ACCEPT packet in return +// 3.) The client then moves into the INGAME_SYNC state to begin receiving data from the server +// 4.) The first thing he does on this screen is send his filesig packet to the server. At which +// point the server will either let him in or deny him. There are no file transfers ingame. +// 5.) The server calls multi_handle_ingame_joiners() once per frame, through multi_do_frame() +// 6.) After verifiying or kicking the player because of his file signature, the server tells the +// player to load the mission +// 7.) When the mission is loaded, the server, sends a netgame update to the client +// 8.) Without waiting, the server then begins sending data ship packets to the player +// 9.) Upon confirmation of receiving these packets, the server sends wing data packets +// 10.) Upon completion of this, the server sends respawn point packets +// 11.) Upon completion of this, the server sends a post briefing data block packet containing ship class and +// weapon information +// 12.) After this, the server sends a player settings packet (to all players for good measure) +// 13.) At this point, the server sends a jump into mission packet +// 14.) Upon receipt of this packet, the client moves into the ingame ship select state +// 15.) The first thing the client does in this state is load the mission data (textures, etc) +// 16.) The player is presented with a list of ships he can choose from. He selects one and sends +// an INGAME_SHIP_REQUEST to the server. +// 17.) The server checks to see if this request is acceptable and sends an INGAME_SHIP_REQUEST back +// with the appropriate data. +// 18.) If the client received an affirmative, he selects the ship and jumps into the mission, otherwise +// he removes it from the list and tries for another ship +// -------------------------------------------------------------------------------------------------- + +// -------------------------------------------------------------------------------------------------- +// INGAME JOIN DEFINES +// + +// ingame join defines - NOTE : it is important to keep these flags so that they appear +// numerically in the order in which the events they represent are done +#define INGAME_JOIN_FLAG_SENDING_SETS (1<<0) // sending player settings to him - it is important that this is done _first_ +#define INGAME_JOIN_FLAG_CAMPAIGN_INFO (1<<1) // sending player settings to him - it is important that this is done _first_ +#define INGAME_JOIN_FLAG_LOADING_MISSION (1<<2) // player has finished loading the mission +#define INGAME_JOIN_FLAG_SENDING_SHIPS (1<<3) // sending ships to an ingame joiner +#define INGAME_JOIN_FLAG_SENDING_WINGS (1<<4) // sending wings to an ingame joiner +#define INGAME_JOIN_FLAG_SENDING_RPTS (1<<5) // sending respawn points to a player +#define INGAME_JOIN_FLAG_SENDING_POST (1<<6) // sending standard post briefing data block +#define INGAME_JOIN_FLAG_SENDING_WSS (1<<7) // sending wss slots info +#define INGAME_JOIN_FLAG_PICK_SHIP (1<<8) // player is in the "pick" ship screen +#define INGAME_JOIN_FLAG_FILE_XFER (1<<9) // player is in the process of downloading the mission file + +#define INGAME_SHIP_UPDATE_TIME 1500 // update time information for all ships the ingame joiner can see + +#define INGAME_SHIP_NEXT 0 // another ship to follow +#define INGAME_SHIP_WARP_SUPPORT 1 // support ship warping in +#define INGAME_SHIP_LIST_EOP 2 // end of packet +#define INGAME_SHIP_LIST_EOL 3 // end of list + +#define INGAME_WING_NEXT 0 // another wing to follow +#define INGAME_WING_LIST_EOP 1 // end of packet +#define INGAME_WING_LIST_EOL 2 // end of list + +// defines used for the ingame wings packet +#define INGAME_WING_NOT_ARRIVED 1 // wing not yet present +#define INGAME_WING_DEPARTED 2 // wing is gone -- never to be seen again +#define INGAME_WING_PRESENT 3 // wing is in mission + + +// -------------------------------------------------------------------------------------------------- +// INGAME JOIN SERVER FUNCTIONS +// + +// called on the server to process ingame joiners and move them through the motions of ingame joining +void multi_handle_ingame_joiners(); + +// pack the ship into the data string and return bytes processed +int multi_deconstruct_ship(ubyte *data, ship *s); + +// pack the wing into the data string and return bytes processed +int multi_deconstruct_wing(ubyte *data, wing *w); + +// -------------------------------------------------------------------------------------------------- +// INGAME JOIN CLIENT FUNCTIONS +// + +// unpack a ship from the data string and return bytes processed +int multi_reconstruct_ship(ubyte *data); + +// unpack a wing from the data string and return bytes processed +int multi_reconstruct_wing(ubyte *data); + +// the final step for an ingame joining observer - create my observer object, unflag myself as joining and jump into mission +void multi_ingame_observer_finish(); + + +// -------------------------------------------------------------------------------------------------- +// INGAME DATA SYNC SCREEN +// + +// mission sync screen init function for ingame joining +void multi_ingame_sync_init(); + +// mission sync screen do function for ingame joining +void multi_ingame_sync_do(); + +// mission sync screen do function for ingame joining +void multi_ingame_sync_close(); + + +// -------------------------------------------------------------------------------------------------- +// INGAME SHIP SELECT SCREEN +// + +// ingame join ship selection screen init +void multi_ingame_select_init(); + +// ingame join ship selection screen do +void multi_ingame_select_do(); + +// ingame join ship selection screen close +void multi_ingame_select_close(); + + +// -------------------------------------------------------------------------------------------------- +// PACKET HANDLER functions +// these are also defined in multimsgs.h, but the implementations are in the module for the sake of convenience +// + +// send ship information for the mission to the ingame joiner +void send_ingame_ships_packet(net_player *pl); + +// process ship information for the mission +void process_ingame_ships_packet(ubyte *data, header *hinfo); + +// send wing information for the mission to the ingame joiner +void send_ingame_wings_packet(net_player *pl); + +// process wing information for the mission +void process_ingame_wings_packet(ubyte *data, header *hinfo); + +// send respawn points information to the ingame joiner +void send_ingame_respawn_points_packet(net_player *pl = NULL); + +// process respawn points information for the mission +void process_ingame_respawn_points_packet(ubyte *data, header *hinfo); + +// send Wss_slots data to an ingame joiner +void send_ingame_slots_packet(net_player *p); + +// process Wss_slots data for the mission +void process_ingame_slots_packet(ubyte *data, header *hinfo); + +// send a request or a reply regarding ingame join ship choice +void send_ingame_ship_request_packet(int code,int rdata,net_player *pl = NULL); + +// process an ingame ship request packet +void process_ingame_ship_request_packet(ubyte *data, header *hinfo); + +// for extra mission information +void multi_ingame_process_mission_stuff( ubyte *data, header *hinfo ); + +#endif + diff --git a/include/multi_kick.h b/include/multi_kick.h new file mode 100644 index 0000000..97ebdbe --- /dev/null +++ b/include/multi_kick.h @@ -0,0 +1,74 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_kick.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 3 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 2 3/24/98 5:00p Dave + * Fixed several ui bugs. Put in pre and post voice stream playback sound + * fx. Put in error specific popups for clients getting dropped from games + * through actions other than their own. + * + * 1 2/12/98 4:38p Dave + * Multiplayer kick functionality + * + * $NoKeywords: $ + */ +#ifndef _MULTIPLAYER_KICK_HEADER_FILE +#define _MULTIPLAYER_KICK_HEADER_FILE + +// ---------------------------------------------------------------------------------- +// KICK DEFINES/VARS +// + +struct net_addr; +struct net_player; + +// special reasons for kicking players +#define KICK_REASON_NORM 0 // plain old kick +#define KICK_REASON_BAD_XFER 1 // error xferring mission +#define KICK_REASON_CANT_XFER 2 // can't xfer a builtin mission +#define KICK_REASON_INGAME_ENDED 3 // kicked while ingame joining a mission about to end + + +// ---------------------------------------------------------------------------------- +// KICK FUNCTIONS +// + +// initialize all kicking details (ban lists, etc). it is safe to call this function at any time +void multi_kick_init(); + +// process all kick details (disconnecting players who have been kicked but haven't closed their socket) +void multi_kick_process(); + +// attempt to kick a player. return success or fail +void multi_kick_player(int player_index, int ban = 1, int reason = KICK_REASON_NORM); + +// is this net address currently kicked and banded +int multi_kick_is_banned(net_addr *addr); + +// debug console function called to determine which player to kick +void multi_dcf_kick(); + +// fill in the passed string with the appropriate "kicked" string +void multi_kick_get_text(net_player *pl, int reason, char *str); + +#endif + diff --git a/include/multi_log.h b/include/multi_log.h new file mode 100644 index 0000000..ffc6e6c --- /dev/null +++ b/include/multi_log.h @@ -0,0 +1,57 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_log.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file to support multiplayer logging functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 2 8/20/98 5:31p Dave + * Put in handy multiplayer logfile system. Now need to put in useful + * applications of it all over the code. + * + * 1 8/20/98 2:00p Dave + * + * + * $NoKeywords: $ + */ + +#ifndef _FREESPACE_MULTIPLAYER_LOGFILE_HEADER_FILE +#define _FREESPACE_MULTIPLAYER_LOGFILE_HEADER_FILE + +// ---------------------------------------------------------------------------------------------------- +// MULTI LOGFILE DEFINES/VARS +// + + +// ---------------------------------------------------------------------------------------------------- +// MULTI LOGFILE FUNCTIONS +// + +// initialize the multi logfile +void multi_log_init(); + +// close down the multi logfile +void multi_log_close(); + +// give some processing time to the logfile system so it can check up on stuff +void multi_log_process(); + +// printf function itself called by the ml_printf macro +void ml_printf(char *format, ...); + +// string print function +void ml_string(char *string, int add_time = 1); + +#endif + diff --git a/include/multi_obj.h b/include/multi_obj.h new file mode 100644 index 0000000..562f383 --- /dev/null +++ b/include/multi_obj.h @@ -0,0 +1,93 @@ +#ifndef _MULTI_NEW_OBJECT_UPDATE_HEADER_FILE +#define _MULTI_NEW_OBJECT_UPDATE_HEADER_FILE + +// --------------------------------------------------------------------------------------------------- +// OBJECT UPDATE DEFINES/VARS +// +struct interp_info; +struct object; +struct header; +struct net_player; + +// client button info flags +#define OOC_FIRE_SECONDARY (1<<0) +#define OOC_TARGET_LOCKED (1<<1) +#define OOC_TARGET_SEEK_LOCK (1<<2) +#define OOC_LOCKING_ON_CENTER (1<<3) +#define OOC_TRIGGER_DOWN (1<<4) +#define OOC_PRIMARY_BANK (1<<5) +#define OOC_PRIMARY_LINKED (1<<6) + +// update info +typedef struct np_update { + ubyte seq; // sequence # + int update_stamp; // global update stamp + int status_update_stamp; + int subsys_update_stamp; + ushort pos_chksum; // positional checksum + ushort orient_chksum; // orient checksum +} np_update; + +// safer version of timestamp +#define timestamp_elapsed_safe(_a, _b) ( (_a != 0) ? (((timestamp_ticker >= (_a)) || (timestamp_ticker < (_a - (_b + 100)))) ? 1 : 0) : 1 ) + +// --------------------------------------------------------------------------------------------------- +// OBJECT UPDATE FUNCTIONS +// + +// process all object update details for this frame +void multi_oo_process(); + +// process incoming object update data +void multi_oo_process_update(ubyte *data, header *hinfo); + +// initialize all object update timestamps (call whenever entering gameplay state) +void multi_oo_gameplay_init(); + +// send control info for a client (which is basically a "reverse" object update) +void multi_oo_send_control_info(); + +// reset all sequencing info +void multi_oo_reset_sequencing(); + +// is this object one which needs to go through the interpolation +int multi_oo_is_interp_object(object *objp); + +// interp +void multi_oo_interp(object *objp); + + +// --------------------------------------------------------------------------------------------------- +// DATARATE DEFINES/VARS +// + +#define OO_HIGH_RATE_DEFAULT 11000 + + +// --------------------------------------------------------------------------------------------------- +// DATARATE FUNCTIONS +// + +// process all object update datarate details +void multi_oo_rate_process(); + +// initialize all datarate checking stuff +void multi_oo_rate_init_all(); + +// initialize the rate limiting for the passed in player +void multi_oo_rate_init(net_player *pl); + +// if the given net-player has exceeded his datarate limit, or if the overall datarate limit has been reached +int multi_oo_rate_exceeded(net_player *pl); + +// if it is ok for me to send a control info (will be ~N times a second) +int multi_oo_cirate_can_send(); + +// display any oo info on the hud +void multi_oo_display(); + +// notify of a player join +void multi_oo_player_reset_all(net_player *pl = NULL); + +#endif + diff --git a/include/multi_observer.h b/include/multi_observer.h new file mode 100644 index 0000000..ded73a4 --- /dev/null +++ b/include/multi_observer.h @@ -0,0 +1,65 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_observer.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 3 4/18/98 5:00p Dave + * Put in observer zoom key. Made mission sync screen more informative. + * + * 2 3/13/98 2:51p Dave + * Put in support for observers to join ingame. + * + * 1 3/12/98 5:44p Dave + * + * $NoKeywords: $ + */ + +#ifndef _MULTI_OBSERVER_HEADER_FILE +#define _MULTI_OBSERVER_HEADER_FILE + +// --------------------------------------------------------------------------------------- +// MULTI OBSERVER DEFINES/VARS +// + +struct net_addr; +struct player; +struct net_player; + +// --------------------------------------------------------------------------------------- +// MULTI OBSERVER FUNCTIONS +// + +// create a _permanent_ observer player +int multi_obs_create_player(int player_num,char *name,net_addr *addr,player *pl); + +// create an explicit observer object and assign it to the passed player +void multi_obs_create_observer(net_player *pl); + +// create observer object locally, and additionally, setup some other information +// ( client-side equivalent of multi_obs_create_observer() ) +void multi_obs_create_observer_client(); + +// create objects for all known observers in the game at level start +// call this before entering a mission +// this implies for the local player in the case of a client or for _all_ players in the case of a server +void multi_obs_level_init(); + +// if i'm an observer, zoom to near my targted object (if any) +void multi_obs_zoom_to_target(); + +#endif + diff --git a/include/multi_oo.h b/include/multi_oo.h new file mode 100644 index 0000000..37af9d9 --- /dev/null +++ b/include/multi_oo.h @@ -0,0 +1,130 @@ +#ifndef _MULTIPLAYER_OBJECT_UPDATE_HEADER_FILE +#define _MULTIPLAYER_OBJECT_UPDATE_HEADER_FILE + +#include "vecmat.h" + +#define OO_NEW + +#ifdef OO_NEW + #include "multi_obj.h" +#else + +// --------------------------------------------------------------------------------------------------- +// OBJECT UPDATE DEFINES/VARS +// +struct interp_info; +struct object; +struct header; +struct net_player; + +// client button info flags +#define OOC_FIRE_PRIMARY (1<<0) +#define OOC_FIRE_SECONDARY (1<<1) +#define OOC_FIRE_COUNTERMEASURE (1<<2) +#define OOC_TARGET_LOCKED (1<<3) +#define OOC_TARGET_SEEK_LOCK (1<<4) +#define OOC_LOCKING_ON_CENTER (1<<5) + +// interpolation info struct +typedef struct interp_info { + // position and timestamp + vector pos; + int pos_time; + + // velocity and timestamp + vector vel; + int vel_time; + + // desired velocity and timestamp + vector desired_vel; + int desired_vel_time; + + // orientation and timestamp + matrix orient; + int orient_time; + + // rotvel and timestamp + vector rotvel; + int rotvel_time; + + // desired rotvel and timestamp + vector desired_rotvel; + int desired_rotvel_time; + + // ping info (in ms) + int lowest_ping; // lowest ping (or -1, if not known) + int lowest_ping_avg; // (lowest ping + average ping)/2 or -1 if not known +} interp_info; + + +// --------------------------------------------------------------------------------------------------- +// OBJECT UPDATE FUNCTIONS +// + +// process all object update details for this frame +void multi_oo_process(); + +// process incoming object update data +void multi_oo_process_update(ubyte *data, header *hinfo); + +// initialize all object update timestamps (call whenever entering gameplay state) +void multi_oo_gameplay_init(); + +// process an object update sync packet +void multi_oo_process_update_sync(ubyte *data, header *hinfo); + +// send an update sync packet +void multi_oo_send_update_sync(net_player *pl = NULL); + +// initialize the server's time sync stuff +void multi_oo_sync_init(); + +// send control info for a client (which is basically a "reverse" object update) +void multi_oo_send_control_info(); + +// reset all sequencing info +void multi_oo_reset_sequencing(); + +// interpolate for this object +void multi_oo_interpolate(object *objp, interp_info *current, interp_info *last); + +// do all interpolation for this frame - client side and server side +void multi_oo_interpolate_all(); + +// set global object update timestamp for this frame +void multi_oo_set_global_timestamp(); + + +// --------------------------------------------------------------------------------------------------- +// DATARATE DEFINES/VARS +// + +#define OO_HIGH_RATE_DEFAULT 11000 + + +// --------------------------------------------------------------------------------------------------- +// DATARATE FUNCTIONS +// + +// process all object update datarate details +void multi_oo_rate_process(); + +// initialize all datarate checking stuff +void multi_oo_rate_init_all(); + +// initialize the rate limiting for the passed in player +void multi_oo_rate_init(net_player *pl); + +// if the given net-player has exceeded his datarate limit, or if the overall datarate limit has been reached +int multi_oo_rate_exceeded(net_player *pl); + +// if it is ok for me to send a control info (will be ~N times a second) +int multi_oo_cirate_can_send(); + +// display any oo info on the hud +void multi_oo_display(); + +#endif // #ifdef OO_NEW + +#endif + diff --git a/include/multi_options.h b/include/multi_options.h new file mode 100644 index 0000000..65a9287 --- /dev/null +++ b/include/multi_options.h @@ -0,0 +1,233 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_options.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 5 2/19/99 2:55p Dave + * Temporary checking to report the winner of a squad war match. + * + * 4 2/12/99 6:16p Dave + * Pre-mission Squad War code is 95% done. + * + * 3 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 12 7/07/98 2:49p Dave + * UI bug fixes. + * + * 11 5/03/98 7:04p Dave + * Make team vs. team work mores smoothly with standalone. Change how host + * interacts with standalone for picking missions. Put in a time limit for + * ingame join ship select. Fix ingame join ship select screen for Vasudan + * ship icons. + * + * 10 5/03/98 2:52p Dave + * Removed multiplayer furball mode. + * + * 9 4/30/98 12:57a Dave + * Put in new mode for ship/weapon selection. Rearranged how game querying + * is done a bit. + * + * 8 4/22/98 5:53p Dave + * Large reworking of endgame sequencing. Updated multi host options + * screen for new artwork. Put in checks for host or team captains leaving + * midgame. + * + * 7 4/16/98 11:39p Dave + * Put in first run of new multiplayer options screen. Still need to + * complete final tab. + * + * 6 4/09/98 11:01p Dave + * Put in new multi host options screen. Tweaked multiplayer options a + * bit. + * + * 5 4/09/98 5:43p Dave + * Remove all command line processing from the demo. Began work fixing up + * the new multi host options screen. + * + * 4 4/06/98 10:24p Dave + * Fixed up Netgame.respawn for the standalone case. + * + * 3 4/06/98 6:37p Dave + * Put in max_observers netgame server option. Make sure host is always + * defaulted to alpha 1 or zeta 1. Changed create game so that MAX_PLAYERS + * can always join but need to be kicked before commit can happen. Put in + * support for server ending a game and notifying clients of a special + * condition. + * + * 2 3/31/98 4:51p Dave + * Removed medals screen and multiplayer buttons from demo version. Put in + * new pilot popup screen. Make ships in mp team vs. team have proper team + * ids. Make mp respawns a permanent option saved in the player file. + * + * 1 3/30/98 6:24p Dave + * + * + * $NoKeywords: $ + */ + +#ifndef _MULTI_NETGAME_OPTIONS_HEADER_FILE +#define _MULTI_NETGAME_OPTIONS_HEADER_FILE + +// ---------------------------------------------------------------------------------- +// MULTI OPTIONS DEFINES/VARS +// + +struct header; +struct netgame_info; +struct net_player; + +// global options +#define STD_PASSWD_LEN 16 +#define STD_NAME_LEN 32 +#define MULTI_OPTIONS_STRING_LEN 256 +typedef struct multi_global_options { + // common options + int protocol; // selected network protocol + ushort port; // port we're running on - for allowing multiple servers on one machine + int log; // use a logfile + int datarate_cap; // datarate cap for OBJ_UPDATE_HIGH + char user_tracker_ip[MULTI_OPTIONS_STRING_LEN]; // ip address of user tracker + char game_tracker_ip[MULTI_OPTIONS_STRING_LEN]; // ip address of game tracker + char pxo_ip[MULTI_OPTIONS_STRING_LEN]; // ip address of pxo chat server + char pxo_rank_url[MULTI_OPTIONS_STRING_LEN]; // URL of pxo rankings page + char pxo_create_url[MULTI_OPTIONS_STRING_LEN]; // URL of pxo create account page + char pxo_verify_url[MULTI_OPTIONS_STRING_LEN]; // URL of pxo account validation page + char pxo_banner_url[MULTI_OPTIONS_STRING_LEN]; // URL of pxo banner files + + // standalone only options + int std_max_players; // max players allowed on the standalone + int std_datarate; // some OBJ_UPDATE_* value + int std_voice; // should standalone allow voice + char std_passwd[STD_PASSWD_LEN]; // standalone host password + char std_pname[STD_NAME_LEN]; // permanent name for the standalone - if any + int std_framecap; // standalone frame cap +} multi_global_options; + +extern multi_global_options Multi_options_g; + +// local (netplayer - nonserver) options - maintained on individual clients and on the server (no need for other clients to know this guy's settings) +#define MAX_OBJ_UPDATE_LEVELS 4 // the # of object update levels there are +#define OBJ_UPDATE_LOW 0 // low object updates +#define OBJ_UPDATE_MEDIUM 1 // medium object updates +#define OBJ_UPDATE_HIGH 2 // high object updates +#define OBJ_UPDATE_LAN 3 // ultra-high updates - no capping at all + +#define MLO_FLAG_ACCEPT_PIX (1<<0) // accept pix from server (pilot pics, squadron logos, etc) +#define MLO_FLAG_NO_VOICE (1<<1) // turn off voice altogether +#define MLO_FLAG_LOCAL_BROADCAST (1<<2) // broadcast on the local subnet when looking for games +#define MLO_FLAG_FLUSH_CACHE (1<<3) // flush the multidata cache before every game +#define MLO_FLAG_XFER_MULTIDATA (1<<4) // xfer mission files to the multidata cache directory +#define MLO_FLAG_TEMP_CLOSED (1<<5) // send to standalone to tell him to toggle the temp closed status + +// BE AWARE : any changes made to this structure will mess with the player file. it will have to be upped!!!! +typedef struct multi_local_options { + int flags; // misc player options + int obj_update_level; // one off the flags above indicating how often to refresh objects +} multi_local_options; + +// server options - maintained on the server _and_ clients +#define MSO_SQUAD_RANK 0 // only highest ranking players can message +#define MSO_SQUAD_LEADER 1 // only wingleaders can message +#define MSO_SQUAD_ANY 2 // anyone can message +#define MSO_SQUAD_HOST 3 // only the host can message + +#define MSO_END_RANK 0 // only the highest ranking players and the host can end the mission +#define MSO_END_LEADER 1 // only team/wing leaders and the host can end the mission +#define MSO_END_ANY 2 // any player can end the mission +#define MSO_END_HOST 3 // only the host can end the mission + +#define MSO_FLAG_INGAME_XFER (1<<0) // netgame allows file xfers to ingame joiners +#define MSO_FLAG_ACCEPT_PIX (1<<1) // netgame allows pilot pix, squad logos +#define MSO_FLAG_NO_VOICE (1<<2) // netgame is disallowing voice altogether +#define MSO_FLAG_SS_LEADERS (1<<3) // in ship/weapon select, only host or team captains can modify ships + +// BE AWARE : any changes made to this structure will mess with the player file. it will have to be upped!!!! +typedef struct multi_server_options { + // misc settings and flags + ubyte squad_set; // see MSO_SQUAD_* + ubyte endgame_set; // see MSO_END_* + int flags; // see MSO_FLAG_* + + // default respawn count + uint respawn; + + // default max # of observers + ubyte max_observers; + + // default skill level + ubyte skill_level; + + // voice settings + ubyte voice_qos; // voice quality of sound + int voice_token_wait; // min time between token gets for a given player + int voice_record_time; // max duration for voice recording (in ms) + + // time limit + fix mission_time_limit; // mission time limit (set to -1 for no limit) + + // kill limit + int kill_limit; // kill limit for a furball mission +} multi_server_options; + + +// ---------------------------------------------------------------------------------- +// MULTI OPTIONS FUNCTIONS +// + +// load in the config file +void multi_options_read_config(); + +// set netgame defaults +// NOTE : should be used when creating a newpilot +void multi_options_set_netgame_defaults(multi_server_options *options); + +// set local netplayer defaults +// NOTE : should be used when creating a newpilot +void multi_options_set_local_defaults(multi_local_options *options); + +// fill in the passed netgame options struct with the data from my player file data (only host/server should do this) +void multi_options_netgame_load(multi_server_options *options); + +// fill in the passed local options struct with the data from my player file data (all machines except standalone should do this) +void multi_options_local_load(multi_local_options *options, net_player *pxo_pl); + +// update everyone on the current netgame options +void multi_options_update_netgame(); + +// update everyone with my local settings +void multi_options_update_local(); + +// update the standalone with the settings I have picked at the "start game" screen +void multi_options_update_start_game(netgame_info *ng); + +// update the standalone with the mission settings I have picked (mission filename, etc) +void multi_options_update_mission(netgame_info *ng, int campaign_mode); + + +// ---------------------------------------------------------------------------------- +// MULTI OPTIONS FUNCTIONS +// + +// process an incoming multi options packet +void multi_options_process_packet(unsigned char *data, header *hinfo); + + +#endif + diff --git a/include/multi_pause.h b/include/multi_pause.h new file mode 100644 index 0000000..5923b9a --- /dev/null +++ b/include/multi_pause.h @@ -0,0 +1,88 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_pause.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 10/13/98 9:29a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 2 5/07/98 6:26p Dave + * Fix strange boundary conditions which arise when players die/respawn + * while the game is being ended. Spiff up the chatbox doskey thing a bit. + * + * 1 4/14/98 12:18p Dave + * + * + * $NoKeywords: $ + */ + + +#ifndef _MULTI_PAUSE_HEADER_FILE +#define _MULTI_PAUSE_HEADER_FILE + +// ---------------------------------------------------------------------------------- +// PAUSE DEFINES/VARS +// + +class UI_WINDOW; +struct net_player; + +// state of the game (paused or not) on _my_ machine. Obviously this is important for the server +// call multi_pause_reset() to reinitialize +extern int Multi_pause_status; + +// who paused the game +extern net_player *Multi_pause_pauser; + + +// ---------------------------------------------------------------------------------- +// PAUSE FUNCTIONS +// + +// re-initializes the pause system. call before entering the mission to reset +void multi_pause_reset(); + +// send a request to pause or unpause a game (all players should use this function) +void multi_pause_request(int pause); + +// (client) call when receiving a packet indicating we should pause +void multi_pause_pause(); + +// (client) call when receiving a packet indicating we should unpause +void multi_pause_unpause(); + +// (server) evaluate a pause request from the given player (should call for himself as well) +void multi_pause_server_eval_request(net_player *pl, int pause); + +// if we still want to eat keys +int multi_pause_eat_keys(); + + +// ---------------------------------------------------------------------------------- +// PAUSE UI FUNCTIONS +// + +// initialize multi pause screen +void multi_pause_init(UI_WINDOW *Ui_window); + +// do frame for the multi pause screen +void multi_pause_do(); + +// close the multi pause screen +void multi_pause_close(); + + +#endif + diff --git a/include/multi_pinfo.h b/include/multi_pinfo.h new file mode 100644 index 0000000..2d1ec3b --- /dev/null +++ b/include/multi_pinfo.h @@ -0,0 +1,59 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_pinfo.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 1/30/99 1:29a Dave + * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot + * screen. Fixed beam weapon death messages. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 2 4/20/98 6:04p Dave + * Implement multidata cache flushing and xferring mission files to + * multidata. Make sure observers can't change hud config. Fix pilot image + * viewing in popup. Put in game status field. Tweaked multi options. + * + * 1 3/05/98 8:20p Dave + * + * $NoKeywords: $ + */ + +#ifndef _MULTI_PLAYER_INFO_HEADER_FILE +#define _MULTI_PLAYER_INFO_HEADER_FILE + +// --------------------------------------------------------------------------------------- +// MULTI PLAYER INFO DEFINES/VARS +// + +// prototypes +struct net_player; + + +// --------------------------------------------------------------------------------------- +// MULTI PLAYER INFO FUNCTIONS +// + +// fire up the player info popup +void multi_pinfo_popup(net_player *np); + +// is the pilot info popup currently active? +int multi_pinfo_popup_active(); + +// kill the currently active popup (if any) +void multi_pinfo_popup_kill(); + +// notify the popup that a player has left +void multi_pinfo_notify_drop(net_player *np); + +#endif + diff --git a/include/multi_ping.h b/include/multi_ping.h new file mode 100644 index 0000000..64c1ad9 --- /dev/null +++ b/include/multi_ping.h @@ -0,0 +1,85 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_ping.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 3 6/30/98 2:17p Dave + * Revised object update system. Removed updates for all weapons. Put + * button info back into control info packet. + * + * 2 6/12/98 2:49p Dave + * Patch 1.02 changes. + * + * 1 3/03/98 5:09p Dave + * + * $NoKeywords: $ + */ + +#ifndef _MULTIPLAYER_PING_HEADER_FILE +#define _MULTIPLAYER_PING_HEADER_FILE + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER PING DEFINES/VARS +// + +struct header; +struct net_addr; +struct net_player; + +// the max ping we'll store to calculate the average +#define MAX_PINGS 10 + +typedef struct ping_struct { + float ping_start; // time the current ping was sent out, or -1 if none + float ping_times[MAX_PINGS]; // ping times for calculating the average + int num_pings; // # of pings in the ping_times array + int ping_add; // where to add the next ping + + int ping_avg; // in ms, this is the only thing we should be concerned with +} ping_struct; + + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER PING FUNCTIONS +// + +// initialize all player ping times +void multi_ping_reset_players(); + +// initialize the given ping struct +void multi_ping_reset(ping_struct *ps); + +// start a ping - call this when sending a ping packet +void multi_ping_start(ping_struct *ps); + +// evaluate a pong return on the given struct +void multi_ping_eval_pong(ping_struct *ps); + +// send a ping to a specific player +void multi_ping_send(net_player *p); + +// send a ping to the specified address +void multi_ping_send(net_addr *addr,ping_struct *ps); + +// send a ping to all players +void multi_ping_send_all(); + +// get the lowest existing ping in the ping struct, returning -1 if no pings +int multi_ping_get_lowest(ping_struct *ps); + +// (average ping + lowest ping)/2 +int multi_ping_lowest_avg(ping_struct *ps); + +#endif + diff --git a/include/multi_pmsg.h b/include/multi_pmsg.h new file mode 100644 index 0000000..ecb2618 --- /dev/null +++ b/include/multi_pmsg.h @@ -0,0 +1,122 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_pmsg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 6 5/08/98 5:05p Dave + * Go to the join game screen when quitting multiplayer. Fixed mission + * text chat bugs. Put mission type symbols on the create game list. + * Started updating standalone gui controls. + * + * 5 4/22/98 4:59p Allender + * new multiplayer dead popup. big changes to the comm menu system for + * team vs. team. Start of debriefing stuff for team vs. team Make form + * on my wing work with individual ships who have high priority orders + * + * 4 4/02/98 5:50p Dave + * Put in support for standard comm messages to get sent to netplayers as + * well as ai ships. Make critical button presses not get evaluated on the + * observer. + * + * 3 3/27/98 11:57a Dave + * Put in expression checking for text messages. + * + * 2 3/25/98 2:16p Dave + * Select random default image for newly created pilots. Fixed several + * multi-pause messaging bugs. Begin work on online help for multiplayer + * keys. + * + * 1 3/19/98 5:04p Dave + * + * + * $NoKeywords: $ + */ + +#ifndef _MULTIPLAYER_MESSAGING_HEADER_FILE +#define _MULTIPLAYER_MESSAGING_HEADER_FILE + + +// ---------------------------------------------------------------------------------- +// MULTI MESSAGING DEFINES/VARS +// + +struct net_player; +struct ai_info; +struct header; + +// messaging modes +// netgame messaging settings +#define MULTI_MSG_NONE -1 // not in messaging mode (send to no one) +#define MULTI_MSG_ALL 0 // all players in the game +#define MULTI_MSG_FRIENDLY 1 // friendly ships +#define MULTI_MSG_HOSTILE 2 // hostile ships +#define MULTI_MSG_TARGET 3 // to whatever is my targeted ship (if a player) +#define MULTI_MSG_EXPR 4 // send to all players whose callsigns match the expr + +// max length for an entered text message +#define MULTI_MSG_MAX_TEXT_LEN 255 + + +// ---------------------------------------------------------------------------------- +// MULTI MESSAGING FUNCTIONS +// + +// called when a messaging key has been detected as being pressed +void multi_msg_key_down(int mode); + +// returns true when messaging system has determined that we should be messaging with voice +int multi_msg_voice_record(); + +// general processing function to do things like timing keydown, etc. call from multi_do_frame() +void multi_msg_process(); + +// get the current messaging mode +int multi_msg_mode(); + +// maybe process a keypress in text messaging mode, return true if the key was processed +int multi_msg_text_process(int k); + +// return 0 or 1 if in text chat mode or not +int multi_msg_text_mode(); + +// return 0 or 1 if there is multi text to be rendered (filling in txt if necessary) +int multi_msg_message_text(char *txt); + +// display ingame,inmission message text +void multi_msg_display_mission_text(char *msg,int player_index); + +// if the passed net_player's callsign matches the reg expression of the passed expr +int multi_msg_matches_expr(net_player *player,char *expr); + +// if text input mode is active, clear it +void multi_msg_text_flush(); + + +// ----------------------------------------------------------------------------------- +// MULTI SQUADMATE MESSAGING FUNCTIONS +// + +// evaluate if a wing SQUADMATE MESSAGE command should be sent to a player +// return 0 if at least one ai ship got the order, 1 if only players +int multi_msg_eval_wing_squadmsg(int wingnum,int command,ai_info *aif,int player_num); + +// evaluate if a ship SQUADMATE MESSAGE command should be sent to a player +// return 0 if not sent to a netplayer, 1 if it was +int multi_msg_eval_ship_squadmsg(int shipnum,int command,ai_info *aif, int player_num); + +// process incoming squadmate messaging info +void multi_msg_process_squadmsg_packet(unsigned char *data, header *hinfo); + +#endif + diff --git a/include/multi_rate.h b/include/multi_rate.h new file mode 100644 index 0000000..6dcf4eb --- /dev/null +++ b/include/multi_rate.h @@ -0,0 +1,48 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_rate.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 3/09/99 6:24p Dave + * More work on object update revamping. Identified several sources of + * unnecessary bandwidth. + * + * + * $NoKeywords: $ + */ + +#ifndef _FS2_MULTI_DATA_RATE_HEADER_FILE +#define _FS2_MULTI_DATA_RATE_HEADER_FILE + +// ----------------------------------------------------------------------------------------------------------------------- +// MULTI RATE DEFINES/VARS +// + +#define MAX_RATE_TYPE_LEN 50 // max length of a type string +#define MAX_RATE_PLAYERS 12 // how many player we'll keep track of +#define MAX_RATE_TYPES 32 // how many types we'll keep track of per player + +// ----------------------------------------------------------------------------------------------------------------------- +// MULTI RATE FUNCTIONS +// + +// notify of a player join +void multi_rate_reset(int np_index); + +// add data of the specified type to datarate processing, returns 0 on fail (if we ran out of types, etc, etc) +int multi_rate_add(int np_index, char *type, int size); + +// process. call _before_ doing network operations each frame +void multi_rate_process(); + +// display +void multi_rate_display(int np_index, int x, int y); + +#endif + diff --git a/include/multi_respawn.h b/include/multi_respawn.h new file mode 100644 index 0000000..d7745b6 --- /dev/null +++ b/include/multi_respawn.h @@ -0,0 +1,81 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_respawn.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 8/06/99 2:44a Dave + * Make sure dead players who leave respawn AI. + * + * 3 3/01/99 7:39p Dave + * Added prioritizing ship respawns. Also fixed respawns in TvT so teams + * don't mix respawn points. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 3 4/23/98 1:49a Allender + * major rearm/repair fixes for multiplayer. Fixed respawning of AI ships + * to not respawn until 5 seconds after they die. Send escort information + * to ingame joiners + * + * 2 3/11/98 10:22p Dave + * Laid groundwork for new observer HUD. Split up multi respawning into + * its own module. + * + * 1 3/11/98 5:07p Dave + * + * $NoKeywords: $ + */ + +#ifndef _MULTIPLAYER_RESPAWN_HEADER_FILE +#define _MULTIPLAYER_RESPAWN_HEADER_FILE + +// --------------------------------------------------------------------------------------- +// MULTI RESPAWN DEFINES/VARS +// + +struct object; +struct header; + + +// --------------------------------------------------------------------------------------- +// MULTI RESPAWN FUNCTIONS +// + +// check to see if a net player needs to be respawned +void multi_respawn_check(object *objp); + +// respawn normally +void multi_respawn_normal(); + +// respawn as an observer +void multi_respawn_observer(); + +// server should check to see if any respawned players have run out of their invulnerability +void multi_respawn_handle_invul_players(); + +// build a list of base respawn points on the server, for this level +void multi_respawn_build_points(); + +void multi_respawn_init(); +void multi_respawn_check_ai(); + +// notify of a player leaving +void multi_respawn_player_leave(net_player *pl); + +// --------------------------------------------------------------------------------------- +// MULTI RESPAWN PACKET HANDLERS +// + +void multi_respawn_process_packet(ubyte *data, header *hinfo); + +#endif + diff --git a/include/multi_team.h b/include/multi_team.h new file mode 100644 index 0000000..b6f2e69 --- /dev/null +++ b/include/multi_team.h @@ -0,0 +1,137 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_team.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 9/04/99 1:54p Dave + * externed team scores. + * + * 4 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 3 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 8 5/22/98 9:35p Dave + * Put in channel based support for PXO. Put in "shutdown" button for + * standalone. UI tweaks for TvT + * + * 7 5/15/98 5:16p Dave + * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy + * status for team vs. team. Put in asserts to check for invalid team vs. + * team situations. + * + * 6 4/08/98 2:51p Dave + * Fixed pilot image xfer once again. Solidify team selection process in + * pre-briefing multiplayer. + * + * 5 3/31/98 4:51p Dave + * Removed medals screen and multiplayer buttons from demo version. Put in + * new pilot popup screen. Make ships in mp team vs. team have proper team + * ids. Make mp respawns a permanent option saved in the player file. + * + * 4 3/10/98 4:26p Dave + * Second pass at furball mode. Fixed several team vs. team bugs. + * + * 3 3/09/98 5:54p Dave + * Fixed stats to take asteroid hits into account. Polished up UI stuff in + * team select. Finished up pilot info popup. Tracked down and fixed + * double click bug. + * + * 2 3/03/98 8:55p Dave + * Finished pre-briefing team vs. team support. + * + * 1 3/03/98 5:09p Dave + * + * $NoKeywords: $ + */ + +#ifndef _MULTI_TEAMPLAY_HEADER_FILE +#define _MULTI_TEAMPLAY_HEADER_FILE + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER TEAMPLAY DEFINES/VARS +// + +// prototypes +struct header; +struct net_player; +struct ship; + +// score for teams 0 and 1 for this mission +extern int Multi_team0_score; +extern int Multi_team1_score; + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER TEAMPLAY FUNCTIONS +// + +// call before level load (pre-sync) +void multi_team_level_init(); + +// call to determine who won the sw match, -1 == tie, 0 == team 0, 1 == team 1 +int multi_team_winner(); + +// call to add score to a team +void multi_team_maybe_add_score(int points, int team); + +// reset all players and assign them to default teams +void multi_team_reset(); + +// set the captaincy status of this player +void multi_team_set_captain(net_player *pl,int set); + +// set the team of this given player (if called by the host, the player becomes locked, cnad only the host can modify him from thereon) +void multi_team_set_team(net_player *pl,int team); + +// is it ok for the host to hit commit +int multi_team_ok_to_commit(); + +// handle a player drop +void multi_team_handle_drop(); + +// handle a player join +void multi_team_handle_join(net_player *pl); + +// send a full update on a player-per-player basis (should call this to update all players after other relevant function calls) +void multi_team_send_update(); + +// set all ships in the mission to be marked as the proper team (TEAM_HOSTILE, TEAM_FRIENLY) +void multi_team_mark_all_ships(); + +// set the proper team for the passed in ship +void multi_team_mark_ship(ship *sp); + +// host locks all players into their teams +void multi_team_host_lock_all(); + +// verify that we have valid team stuff +void multi_team_verify(); + +// get the player counts for team 0 and team 1 (NULL values are valid) +void multi_team_get_player_counts(int *team0,int *team1); + +// report on the winner/loser of the game via chat text +void multi_team_report(); + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER TEAMPLAY PACKET HANDLERS +// + +// process an incoming team update packet +void multi_team_process_packet(unsigned char *data,header *hinfo); + +#endif + diff --git a/include/multi_update.h b/include/multi_update.h new file mode 100644 index 0000000..5784ea0 --- /dev/null +++ b/include/multi_update.h @@ -0,0 +1,50 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_update.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 2 7/09/98 4:51p Dave + * First revision of PXO autoupdate check system. + * + * 1 7/09/98 2:09p Dave + * + * + * $NoKeywords: $ + */ + +#ifndef _FREESPACE_AUTOUPDATE_THINGIE_HEADER_FILE +#define _FREESPACE_AUTOUPDATE_THINGIE_HEADER_FILE + +// ------------------------------------------------------------------------------------------------------------------- +// MULTI UPDATE DEFINES/VARS +// + +// operation return codes +#define MULTI_UPDATE_CONTINUE 0 // continue to next screen +#define MULTI_UPDATE_SHUTTING_DOWN 1 // freespace is exiting to the launcher +#define MULTI_UPDATE_MAIN_MENU 2 // caller should move back to the main menu + + +// ------------------------------------------------------------------------------------------------------------------- +// MULTI UPDATE FUNCTIONS +// + +// check to see if the version of FS on this machine is not recent. run in a popup +// if the versions don't check out, bail to the launcher +// see MULTI_UPDATE_* return codes, above +int multi_update_gobaby(); + + +#endif + diff --git a/include/multi_voice.h b/include/multi_voice.h new file mode 100644 index 0000000..57a5e1e --- /dev/null +++ b/include/multi_voice.h @@ -0,0 +1,161 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_voice.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 12 4/25/98 2:02p Dave + * Put in multiplayer context help screens. Reworked ingame join ship + * select screen. Fixed places where network timestamps get hosed. + * + * 11 4/21/98 4:44p Dave + * Implement Vasudan ships in multiplayer. Added a debug function to bash + * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui + * problem in options screen. + * + * 10 4/17/98 5:27p Dave + * More work on the multi options screen. Fixed many minor ui todo bugs. + * + * 9 4/09/98 11:01p Dave + * Put in new multi host options screen. Tweaked multiplayer options a + * bit. + * + * 8 4/07/98 5:42p Dave + * Put in support for ui display of voice system status (recording, + * playing back, etc). Make sure main hall music is stopped before + * entering a multiplayer game via ingame join. + * + * 7 3/30/98 6:27p Dave + * Put in a more official set of multiplayer options, including a system + * for distributing netplayer and netgame settings. + * + * 6 3/23/98 12:59a Lawrance + * remove obsolete parameters from multi_voice_process_next_chunk() + * + * 5 3/18/98 3:32p Dave + * Put in a hook for streamed rtvoice data from the rtvoice system. + * + * 4 3/17/98 12:30a Dave + * Put in hud support for rtvoice. Several ui interface changes. + * + * 3 3/16/98 2:35p Dave + * Numerous bug fixes. Made the "cue sound" sound play before incoming + * voice. + * + * 2 2/26/98 4:21p Dave + * More robust multiplayer voice. + * + * 1 2/24/98 10:12p Dave + * Initial pass at multiplayer voice streaming. + * + * $NoKeywords: $ + */ + +#ifndef _MULTIPLAYER_VOICE_STREAMING_HEADER_FILE +#define _MULTIPLAYER_VOICE_STREAMING_HEADER_FILE + +// -------------------------------------------------------------------------------------------------- +// MULTI VOICE DEFINES/VARS +// + +struct header; + +// voice system status defines +#define MULTI_VOICE_STATUS_IDLE 0 // nothing's happening, do nothing +#define MULTI_VOICE_STATUS_DENIED 1 // have been denied the token (show a red icon or something) +#define MULTI_VOICE_STATUS_RECORDING 2 // am currently recording (show a green icon or something) +#define MULTI_VOICE_STATUS_PLAYING 3 // playing back a stream (show another icon) + +// max recording time for one stream +#define MULTI_VOICE_MAX_TIME 5000 + +// capabilities of this machine (make sure multi_voice_init() is called before referencing these) +extern int Multi_voice_can_record; +extern int Multi_voice_can_play; + +// local muting preferences +extern int Multi_voice_local_prefs; + + +// -------------------------------------------------------------------------------------------------- +// MULTI VOICE FUNCTIONS +// + +// initialize the multiplayer voice system +void multi_voice_init(); + +// shutdown the multiplayer voice system +void multi_voice_close(); + +// reset between levels +void multi_voice_reset(); + +// process all voice details +void multi_voice_process(); + +// set the default voice quality and duration (if server passes -1, he just broadcasts the qos to all clients) +void multi_voice_set_vars(int qos,int duration); + +// voice settings debug console function +void multi_voice_dcf(); + +// update the qos and/or duration of recording if the current setting is different from the passed in value +void multi_voice_maybe_update_vars(int new_qos,int new_duration); + +// the status of the voice system - use this to determine what bitmaps to display, etc see above MULTI_VOICE_STATUS_* defines +int multi_voice_status(); + +// sends hit bitflag settings (who he'll receive sound from, etc) +void multi_voice_set_prefs(int pref_flags); + + +// -------------------------------------------------------------------------------------------------- +// MULTI VOICE / RTVOICE INTERFACE +// + +// process the "next" chunk of standalone valid sound data from the rtvoice system +void multi_voice_process_next_chunk(); + + +// -------------------------------------------------------------------------------------------------- +// MULTI VOICE PACKET HANDLERS +// + +// process an incoming voice packet of some kind or another +void multi_voice_process_packet(unsigned char *data, header *hinfo); + + +// -------------------------------------------------------------------------------------------------- +// MULTI VOICE TESTING FUNCTIONS +// + +// start recording voice locally for playback testing +void multi_voice_test_record_start(); + +// return if the test recording is going on +int multi_voice_test_recording(); + +// call this function if multi_voice_test_recording() is true to process various odds and ends of the test recording +void multi_voice_test_process(); + +// force stop any recording voice test +void multi_voice_test_record_stop(); + +// get a playback buffer handle (return -1 if none exist - bad) +int multi_voice_test_get_playback_buffer(); + +// return whether the last sampled chunk would have been too large to fit in a packet +int multi_voice_test_packet_tossed(); + +#endif + diff --git a/include/multi_xfer.h b/include/multi_xfer.h new file mode 100644 index 0000000..f87f684 --- /dev/null +++ b/include/multi_xfer.h @@ -0,0 +1,166 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_xfer.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 12/14/98 4:01p Dave + * Got multi_data stuff working well with new xfer stuff. + * + * 4 12/14/98 12:13p Dave + * Spiffed up xfer system a bit. Put in support for squad logo file xfer. + * Need to test now. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 21 5/21/98 3:45a Sandeep + * Make sure file xfer sender side uses correct directory type. + * + * 20 4/23/98 6:18p Dave + * Store ETS values between respawns. Put kick feature in the text + * messaging system. Fixed text messaging system so that it doesn't + * process or trigger ship controls. Other UI fixes. + * + * 19 4/01/98 11:19p Dave + * Put in auto-loading of xferred pilot pic files. Grey out background + * behind pinfo popup. Put a chatbox message in when players are kicked. + * Moved mission title down in briefing. Other ui fixes. + * + * 18 3/23/98 5:42p Dave + * Put in automatic xfer of pilot pic files. Changed multi_xfer system so + * that it can support multiplayer sends/received between client and + * server simultaneously. + * + * 17 3/21/98 7:14p Dave + * Fixed up standalone player slot switching. Made training missions not + * count towards player stats. + * + * 16 2/22/98 2:53p Dave + * Put in groundwork for advanced multiplayer campaign options. + * + * 15 2/20/98 4:43p Dave + * Finished support for multiplayer player data files. Split off + * multiplayer campaign functionality. + * + * 14 2/19/98 6:26p Dave + * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in + * initial support for player data uploading. + * + * 13 2/18/98 10:21p Dave + * Ripped out old file xfer system. Put in brand new xfer system. + * + * $NoKeywords: $ + */ + +#ifndef _FREESPACE_FILE_TRANSFER_HEADER +#define _FREESPACE_FILE_TRANSFER_HEADER + +// ------------------------------------------------------------------------------------------ +// MULTI XFER DEFINES/VARS +// + +typedef uint PSNET_SOCKET_RELIABLE; + +// status codes for transfers +#define MULTI_XFER_NONE -1 // nothing is happening - this is an invalid handle +#define MULTI_XFER_SUCCESS 0 // the xfer has successfully transferred +#define MULTI_XFER_FAIL 1 // the xfer has failed for one reason or another +#define MULTI_XFER_UNKNOWN 2 // the xfer has finished but its unknown if it was successful - wait a while longer +#define MULTI_XFER_TIMEDOUT 3 // the xfer has timed-out during some stage of the process +#define MULTI_XFER_IN_PROGRESS 4 // the xfer is in progress +#define MULTI_XFER_QUEUED 5 // queued up - hasn't started yet + +#define MULTI_XFER_FLAG_AUTODESTROY (1<<15) // automatically clear and free an xfer handle that is done +#define MULTI_XFER_FLAG_REJECT (1<<16) // set by the receive callback function if we want to disallow xfer of this file +// if this flag is set, the system will only xfer one file at a time to a given destination. +// so, suppose you start sending 3 files to one target, all which have this flag set. Only the first file will send. +// Once it is complete, the second one will go. Then the third. This is extremely useful for files where you don't +// _really_ care if it arrives or not (eg - sending multiple pilot pics or sounds or squad logos, etc). If you _do_ +// care about the file (eg - mission files), you probably shouldn't be using this flag +#define MULTI_XFER_FLAG_QUEUE (1<<17) + +// the xfer system is guaranteed never to spew data larger than this +#define MULTI_XFER_MAX_SIZE 500 + +// ------------------------------------------------------------------------------------------ +// MULTI XFER FUNCTIONS +// + +// initialize all file xfer transaction stuff, call in multi_level_init() +void multi_xfer_init(void (*multi_xfer_recv_callback)(int handle)); + +// do frame for all file xfers, call in multi_do_frame() +void multi_xfer_do(); + +// close down the file xfer system +void multi_xfer_close(); + +// reset the xfer system, including shutting down/killing all active xfers +void multi_xfer_reset(); + +// send a file to the specified player, return a handle +int multi_xfer_send_file(PSNET_SOCKET_RELIABLE who, char *filename, int cfile_flags, int flags = 0); + +// get the status of the current file xfer +int multi_xfer_get_status(int handle); + +// abort a transferring file +void multi_xfer_abort(int handle); + +// release an xfer handle +void multi_xfer_release_handle(int handle); + +// get the filename of the xfer for the given handle +char *multi_xfer_get_filename(int handle); + +// lock the xfer system (don't accept incoming files, don't allow outgoing files) +void multi_xfer_lock(); + +// unlock the xfer system +void multi_xfer_unlock(); + +// force all receives to go into the specified directory by cfile type +void multi_xfer_force_dir(int cf_type); + +// forces the given xfer entry to the specified directory type (only valid when called from the recv_callback function) +void multi_xfer_handle_force_dir(int handle,int cf_type); + +// xor the flag on a given entry +void multi_xfer_xor_flags(int handle,int flags); + +// get the flags for a given entry +int multi_xfer_get_flags(int handle); + +// if the passed filename is being xferred, return the xfer handle, otherwise return -1 +int multi_xfer_lookup(char *filename); + +// get the % of completion of the passed file handle, return < 0 if invalid +float multi_xfer_pct_complete(int handle); + +// get the socket of the file xfer (useful for identifying players) +uint multi_xfer_get_sock(int handle); + +// get the CF_TYPE of the directory this file is going to +int multi_xfer_get_force_dir(int handle); + +// ------------------------------------------------------------------------------------------ +// MULTI XFER PACKET HANDLERS +// + +// process an incoming file xfer data packet, return bytes processed, guaranteed to process the entire +// packet regardless of error conditions +int multi_xfer_process_packet(unsigned char *data, PSNET_SOCKET_RELIABLE who); + +#endif + diff --git a/include/multilag.h b/include/multilag.h new file mode 100644 index 0000000..4b18634 --- /dev/null +++ b/include/multilag.h @@ -0,0 +1,100 @@ +/* + * $Logfile: /Freespace2/code/Network/multilag.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 6/07/99 9:51p Dave + * Consolidated all multiplayer ports into one. + * + * 4 11/19/98 8:03a Dave + * Full support for D3-style reliable sockets. Revamped packet lag/loss + * system, made it receiver side and at the lowest possible level. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 12 4/27/98 6:02p Dave + * Modify how missile scoring works. Fixed a team select ui bug. Speed up + * multi_lag system. Put in new main hall. + * + * 11 4/04/98 8:42p Dave + * Tested and debugged UDP reliable socket layer. Modified lag system to + * take this into account. + * + * 10 4/01/98 11:19p Dave + * Put in auto-loading of xferred pilot pic files. Grey out background + * behind pinfo popup. Put a chatbox message in when players are kicked. + * Moved mission title down in briefing. Other ui fixes. + * + * 9 3/14/98 2:48p Dave + * Cleaned up observer joining code. Put in support for file xfers to + * ingame joiners (observers or not). Revamped and reinstalled pseudo + * lag/loss system. + * + * 8 1/15/98 6:12p Dave + * Fixed weapons loadout bugs with multiplayer respawning. Added + * multiplayer start screen. Fixed a few chatbox bugs. + * + * 7 1/11/98 10:03p Allender + * removed from headers which included it. Made psnet_socket + * type which is defined just as SOCKET type is. + * + * 6 12/29/97 5:21p Dave + * Put in object update sequencing for multiplayer. + * + * 5 12/16/97 6:17p Dave + * Put in primary weapon support for multiplayer weapon select screen. + * + * 4 12/10/97 4:46p Dave + * Added in more detailed support for multiplayer packet lag/loss. Fixed + * some multiplayer stuff. Added some controls to the standalone. + * + * 3 12/01/97 4:59p Dave + * Synchronized multiplayer debris objects. Put in pilot popup in main + * hall. Optimized simulated multiplayer lag module. Fixed a potential + * file_xfer bug. + * + * 2 11/28/97 5:06p Dave + * Put in facilities for simulating multiplayer lag. + * + * 1 11/28/97 4:38p Dave + * Initial Revision + * + * $NoKeywords: $ + */ + +#ifndef _MULTI_LAG_HEADER_FILE +#define _MULTI_LAG_HEADER_FILE + +#ifndef NDEBUG + // #define MULTI_USE_LAG +#endif + +struct fd_set; +struct timeval; + +// initialize multiplayer lagloss. in non-debug situations, this call does nothing +void multi_lag_init(); + +// shutdown multiplayer lag +void multi_lag_close(); + +// select for multi_lag +int multi_lag_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *except_fds, const timeval *timeout); + +// recvfrom for multilag +int multi_lag_recvfrom(uint s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen); + +#endif + diff --git a/include/multimsgs.h b/include/multimsgs.h new file mode 100644 index 0000000..486716a --- /dev/null +++ b/include/multimsgs.h @@ -0,0 +1,699 @@ +/* + * $Logfile: /Freespace2/code/Network/multimsgs.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the building and sending of multiplayer packets + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 31 8/26/99 8:51p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 30 8/25/99 4:38p Dave + * Updated PXO stuff. Make squad war report stuff much more nicely. + * + * 29 8/22/99 5:53p Dave + * Scoring fixes. Added self destruct key. Put callsigns in the logfile + * instead of ship designations for multiplayer players. + * + * 28 8/22/99 1:19p Dave + * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in + * which d3d cards are detected. + * + * 27 8/19/99 10:59a Dave + * Packet loss detection. + * + * 26 8/16/99 4:05p Dave + * Big honking checkin. + * + * 25 7/30/99 7:01p Dave + * Dogfight escort gauge. Fixed up laser rendering in Glide. + * + * 24 7/22/99 7:17p Dave + * Fixed excessive whacks in multiplayer. + * + * 23 7/08/99 10:53a Dave + * New multiplayer interpolation scheme. Not 100% done yet, but still + * better than the old way. + * + * 22 7/03/99 5:50p Dave + * Make rotated bitmaps draw properly in padlock views. + * + * 21 6/21/99 7:24p Dave + * netplayer pain packet. Added type E unmoving beams. + * + * 20 5/14/99 1:59p Andsager + * Multiplayer message for subsystem cargo revealed. + * + * 19 4/29/99 2:29p Dave + * Made flak work much better in multiplayer. + * + * 18 4/28/99 11:13p Dave + * Temporary checkin of artillery code. + * + * 17 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 16 4/02/99 9:55a Dave + * Added a few more options in the weapons.tbl for beam weapons. Attempt + * at putting "pain" packets into multiplayer. + * + * 15 3/10/99 6:50p Dave + * Changed the way we buffer packets for all clients. Optimized turret + * fired packets. Did some weapon firing optimizations. + * + * 14 3/09/99 6:24p Dave + * More work on object update revamping. Identified several sources of + * unnecessary bandwidth. + * + * 13 3/08/99 7:03p Dave + * First run of new object update system. Looks very promising. + * + * 12 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * 11 2/21/99 6:02p Dave + * Fixed standalone WSS packets. + * + * 10 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 9 1/14/99 12:48a Dave + * Todo list bug fixes. Made a pass at putting briefing icons back into + * FRED. Sort of works :( + * + * 8 1/12/99 5:45p Dave + * Moved weapon pipeline in multiplayer to almost exclusively client side. + * Very good results. Bandwidth goes down, playability goes up for crappy + * connections. Fixed object update problem for ship subsystems. + * + * 7 11/19/98 8:04a Dave + * Full support for D3-style reliable sockets. Revamped packet lag/loss + * system, made it receiver side and at the lowest possible level. + * + * 6 11/17/98 11:12a Dave + * Removed player identification by address. Now assign explicit id #'s. + * + * 5 11/12/98 12:13a Dave + * Tidied code up for multiplayer test. Put in network support for flak + * guns. + * + * 4 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 3 10/20/98 1:39p Andsager + * Make so sparks follow animated ship submodels. Modify + * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add + * submodel_num. Add submodel_num to multiplayer hit packet. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 169 9/20/98 7:20p Dave + * Added CHANGE_IFF packet. + * + * 168 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 167 9/11/98 2:05p Allender + * make reinforcements work correctly in multiplayer games. There still + * may be a team vs team issue that I haven't thought of yet :-( + * + * 166 8/28/98 3:29p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 165 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 164 6/22/98 8:36a Allender + * revamping of homing weapon system. don't send as object updates + * anymore + * + * 163 6/10/98 2:56p Dave + * Substantial changes to reduce bandwidth and latency problems. + * + * 162 5/25/98 10:58a Allender + * more object update stuff -- fix duplicate repair ship messages + * + * 161 5/20/98 2:24a Dave + * Fixed server side voice muting. Tweaked multi debrief/endgame + * sequencing a bit. Much friendlier for stats tossing/accepting now. + * + * 160 5/18/98 12:41a Allender + * fixed subsystem problems on clients (i.e. not reporting properly on + * damage indicator). Fixed ingame join problem with respawns. minor + * comm menu stuff + * + * 159 5/13/98 6:54p Dave + * More sophistication to PXO interface. Changed respawn checking so + * there's no window for desynchronization between the server and the + * clients. + * + * 158 5/11/98 11:40p Dave + * Stuff. + * + * 157 5/08/98 11:22a Allender + * fix ingame join trouble. Small messaging fix. Enable collisions for + * friendlies again + * + * 156 5/04/98 1:46p Allender + * new join procedure which should allow > 10 players accurately. Fixed a + * minor UI problem on join screen + * + * 155 4/30/98 12:49a Allender + * deal with asteroid problems in multiplayer + * + * 154 4/25/98 7:40p Allender + * fixd some small hotkey stuff. Worked on turret orientation being + * correct for multiplayer. new sexpression called end-campaign will will + * end the main campaign + * + * 153 4/22/98 5:53p Dave + * Large reworking of endgame sequencing. Updated multi host options + * screen for new artwork. Put in checks for host or team captains leaving + * midgame. + * + * 152 4/22/98 5:00p Allender + * new multiplayer dead popup. big changes to the comm menu system for * + * team vs. team. Start of debriefing stuff for team vs. team Make form + * on my wing work with individual ships who have high priority + * orders + * + * 151 4/21/98 4:44p Dave + * Implement Vasudan ships in multiplayer. Added a debug function to bash + * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui + * problem in options screen. + * + * 150 4/20/98 12:40a Allender + * fixed nasty problem where network read code was not reentrant. minor + * UI tweaks. ingame joiners now get netgame info correctly. + * + * $NoKeywords: $ + */ + + + +#ifndef MULTI_MSGS_H +#define MULTI_MSGS_H + +struct net_player; +struct net_addr; +struct object; +struct ship; +struct wing; +struct join_request; +struct button_info; +struct header; +struct beam_info; +struct ship_subsys; + +// macros for building up packets -- to save on time and typing. Important to note that local variables +// must be named correctly +// there are two flavors of sending orientation matrices, 16 bit and 32 bit. Just #define ORIENT_16 to use +// 16 bits, otherwise 32 bits is the default + +#define BUILD_HEADER(t) do { data[0]=t; packet_size = HEADER_LENGTH; } while(0) +#define ADD_DATA(d) do { Assert((packet_size + sizeof(d)) < MAX_PACKET_SIZE); memcpy(data+packet_size, &d, sizeof(d) ); packet_size += sizeof(d); } while (0) +#define ADD_STRING(s) do { Assert((packet_size + strlen(s) + 4) < MAX_PACKET_SIZE);int len = strlen(s); ADD_DATA(len); memcpy(data+packet_size, s, len ); packet_size += len; } while(0) +#define ADD_ORIENT(d) { Assert((packet_size + 17) < MAX_PACKET_SIZE); ubyte dt[17]; multi_pack_orient_matrix(dt,&d); memcpy(data+packet_size,dt,17); packet_size += 17; } + +#define GET_DATA(d) do { memcpy(&d, data+offset, sizeof(d) ); offset += sizeof(d); } while(0) +#define GET_STRING(s) do { int len; memcpy(&len, data+offset, sizeof(len)); offset += sizeof(len); memcpy(s, data+offset, len); offset += len; s[len] = '\0'; } while(0) +#define GET_ORIENT(d) { ubyte dt[17]; memcpy(dt,data+offset,17); offset+=17; multi_unpack_orient_matrix(dt,&d); } + +#define PACKET_SET_SIZE() do { hinfo->bytes_processed = offset; } while(0) + +// defines for weapon status changes. +#define MULTI_PRIMARY_CHANGED 1 +#define MULTI_SECONDARY_CHANGED 2 + +// data sending wrappers + +// send the specified data packet to all players +void multi_io_send(net_player *pl, ubyte *data, int length); +void multi_io_send_to_all(ubyte *data, int length, net_player *ignore = NULL); +void multi_io_send_force(net_player *pl); + +// send the data packet to all players via their reliable sockets +void multi_io_send_reliable(net_player *pl, ubyte *data, int length); +void multi_io_send_to_all_reliable(ubyte* data, int length, net_player *ignore = NULL); +void multi_io_send_reliable_force(net_player *pl); + +// send all buffered packets +void multi_io_send_buffered_packets(); + + +// packet handlers ------------------------------------------------------------------------------- + +// process an incoming join request packet +void process_join_packet( ubyte* data, header* hinfo ); + +// process an accept packet from the server +void process_accept_packet( ubyte* data, header* hinfo ); + +// process a notification for a new player who has joined the game +void process_new_player_packet(ubyte* data, header* hinfo); + +// process an incoming hud message packet +void process_hud_message(ubyte* data, header* hinfo); + +// process a notification the a player has left the game +void process_leave_game_packet(ubyte* data, header* hinfo); + +// process information about an active game +void process_game_active_packet(ubyte* data, header* hinfo); + +// process a query from a client looking for active freespace games +void process_game_query(ubyte* data, header* hinfo); + +// process a general game chat packet, if we're the standalone we should rebroadcast +void process_game_chat_packet( ubyte *data, header *hinfo ); + +// process a game information update +void process_game_info_packet( ubyte *data, header *hinfo ); + +void process_team_update_packet(ubyte *data, header *hinfo); + +// process a packet indicating a secondary weapon was fired +void process_secondary_fired_packet(ubyte* data, header* hinfo, int flag); + +// process a packet indicating a countermeasure was fired +void process_countermeasure_fired_packet( ubyte *data, header *hinfo ); + +// process information about the netgame sent from the server/host +void process_netgame_update_packet( ubyte *data, header *hinfo ); + +// process an incoming netgame description packet +void process_netgame_descript_packet( ubyte *data, header *hinfo ); + +// process an incoming netplayer state update. if we're the server, we should rebroadcast +void process_netplayer_update_packet( ubyte *data, header *hinfo ); + +void process_ship_status_packet(ubyte *data, header *hinfo); +void process_player_order_packet(ubyte *data, header *hinfo); + +// process an object update packet. See send_object_update for information on how +// this packet data should be interpreted. We send different information depending on +// on several variables (no change in orienation, etc). +void process_object_update_packet( ubyte *data, header *hinfo ); + +// process a packet indicating that a ship has been killed +void process_ship_kill_packet( ubyte *data, header *hinfo ); + +// process a packet saying that a wing should be created +void process_wing_create_packet( ubyte *data, header *hinfo ); + +// process a packet indicating a ship should be created +void process_ship_create_packet( ubyte *data, header *hinfo ); + +// process a packet indicating a ship is departing +void process_ship_depart_packet( ubyte *data, header *hinfo ); + +// process a mission log item packet +void process_mission_log_packet( ubyte *data, header *hinfo ); + +// process a mission message packet +void process_mission_message_packet( ubyte *data, header *hinfo ); + +// just send them a pong back as fast as possible +void process_ping_packet(ubyte *data, header *hinfo); + +// right now it just routes the pong through to the standalone gui, which is the only +// system which uses ping and pong right now. +void process_pong_packet(ubyte *data, header *hinfo); + +// process a request for a list of missions +void process_mission_request_packet(ubyte *data, header *hinfo); + +// process an individual mission file item +void process_mission_item_packet(ubyte *data, header *hinfo); + +// process a pause update packet (pause, unpause, etc) +void process_multi_pause_packet(ubyte *data, header *hinfo); + +// process an ingame nak packet +void process_ingame_nak(ubyte *data, header *hinfo); + +void process_ingame_ships_packet(ubyte *data, header *hinfo); +void process_ingame_wings_packet(ubyte *data, header *hinfo); + +// process a packet indicating we should end the current mission +void process_endgame_packet(ubyte *data, header *hinfo); + +// process a position/orientation update from an observer +void process_observer_update_packet(ubyte *data, header *hinfo); + +void process_netplayer_slot_packet(ubyte *data, header *hinfo); +void process_netplayer_class_packet(ubyte *data, header *hinfo); + +void process_subsys_update_packet(ubyte *data, header *hinfo); + +void process_ingame_ship_update_packet(ubyte *data, header *hinfo); + +void process_file_sig_packet(ubyte *data, header *hinfo); +void process_file_sig_request(ubyte *data, header *hinfo); + +void process_ingame_respawn_points_packet(ubyte *data, header *hinfo); + +void process_subsystem_destroyed_packet( ubyte *data, header *hinfo ); + +void process_netplayer_load_packet(ubyte *data, header *hinfo); + +void process_jump_into_mission_packet(ubyte *data, header *hinfo); + +void process_repair_info_packet(ubyte *data, header *hinfo); + +void process_mission_sync_packet(ubyte *data, header *hinfo); + +void process_store_stats_packet(ubyte *data, header *hinfo); + +void process_debris_update_packet(ubyte *data, header *hinfo); + +void process_ship_weapon_state_packet(ubyte *data, header *hinfo ); +void process_ship_weapon_change( ubyte *data, header *hinfo ); + +void process_firing_info_packet( ubyte *data, header *hinfo ); + +// process a cargo revealed packet +void process_cargo_revealed_packet( ubyte *data, header *hinfo ); + +void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo ); + +void process_mission_goal_info_packet( ubyte *data, header *hinfo ); + +void process_player_kick_packet(ubyte *data, header *hinfo); + +void process_player_settings_packet(ubyte *data, header *hinfo); + +void process_deny_packet(ubyte *data, header *hinfo); + +void process_post_sync_data_packet(ubyte *data, header *hinfo); + +void process_wss_slots_data_packet(ubyte *data, header *hinfo); + +void process_shield_explosion_packet( ubyte *data, header *hinfo ); + +void process_player_stats_block_packet(ubyte *data, header *hinfo); + +void process_host_restr_packet(ubyte *data, header *hinfo); + +void process_netgame_end_error_packet(ubyte *data, header *hinfo); + +void process_client_update_packet(ubyte *data, header *hinfo); + +void process_countdown_packet(ubyte *data, header *hinfo); + +// send a join packet request to the specified address (should be a server) +void send_join_packet(net_addr* addr,join_request *jr); + +// send an accept packet to a client in response to a request to join the game +void send_accept_packet(int new_player_num, int code, int ingame_join_team = -1); + +// send a general game chat packet (if msg_mode == MULTI_MSG_TARGET, need to pass in "to", if == MULTI_MSG_EXPR, need to pass in expr) +void send_game_chat_packet(net_player *from, char *msg, int msg_mode, net_player *to = NULL,char *expr = NULL,int server_msg = 0); + +// send a game information update +void send_game_info_packet( void ); + +// send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet) +void send_leave_game_packet(short player_id = -1,int kicked_reason = -1,net_player *target = NULL); + +// send a packet indicating a secondary weapon was fired +void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm ); + +// send a packet indicating a countermeasure was fired +void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val ); + + + +// send_game_update_packet sends an updated Netgame structure to all players currently connected. The update +// is used to change the current mission, current state, etc. +void send_netgame_update_packet(net_player *pl = NULL); + +// sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players +void send_netplayer_update_packet( net_player *pl = NULL ); + +void send_ship_status_packet(net_player *pl, button_info *bi, int id); +void send_player_order_packet(int type, int index, int command); + +// send a request or a reply for mission description, if code == 0, request, if code == 1, reply +void send_netgame_descript_packet(net_addr *addr, int code); + +// send object update packet sends object updates for all objects in the game. This function will be smart +// about sending only certain objects to certain players based on the players distance from an object, whether +// the object is behind the player, etc. +void send_object_update_packet(int force_all = 0); + +// send a packet indicating a ship has been killed +void send_ship_kill_packet( object *ship_obj, object *other_objp, float percent_killed, int self_destruct ); + +// send a packet indicating a wing of ships should be created +void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count ); + +// send a packet indicating a ship should be created +void send_ship_create_packet( object *objp, int is_support = 0 ); + +// packet indicating a ship is departing +void send_ship_depart_packet( object *objp ); + +// send a mission log item packet +void send_mission_log_packet( int entry ); + +// send a mission message packet +void send_mission_message_packet(int id, char *who_from, int priority, int timing, int source, int builtin_type, int multi_target, int multi_team_filter); + +// broadcast a query for active games. IPX will use net broadcast and TCP will either request from the MT or from the specified list +void broadcast_game_query(); + +// send an individual query to an address to see if there is an active game +void send_server_query(net_addr *addr); + +// broadcast a hud message to all players +void send_hud_msg_to_all( char* msg ); +void send_heartbeat(); + +// send a ping packet +void send_ping(net_addr *addr); + +// send a pong packet +void send_pong(net_addr *addr); + +// sent from host to master. give me the list of missions you have. +// this will be used only in a standalone mode +void send_mission_list_request( int what ); + +// send an individual mission file item +void send_mission_item(net_player *pl,char *file_name,char *mission_name); + +// send a request to the server to pause or unpause the game +void send_multi_pause_packet(int pause); + +// send an ack packet +void send_ingame_ack(int state,net_player *p); + +// send an ingame nak packet +void send_ingame_nak(int state,net_player *p); + +void send_ingame_ships_packet(net_player *pl); +void send_ingame_wings_packet(net_player *pl); + +// send a notification that a new player has joined the game (if target != NULL, broadcast the packet) +void send_new_player_packet(int new_player_num,net_player *target); + +// send a packet telling players to end the mission +void send_endgame_packet(net_player *pl = NULL); + +// send a position/orientation update for myself (if I'm an observer) +void send_observer_update_packet(); + +void send_netplayer_slot_packet(); + +void send_subsys_update_packet(net_player *p); + +void send_ingame_ship_update_packet(net_player *p,ship *sp); + +void send_ingame_final_packet(int net_sig); + +void send_file_sig_packet(ushort sum_sig,int length_sig); +void send_file_sig_request(char *file_name); + +void send_subsystem_destroyed_packet( ship *shipp, int index, vector worldpos ); + +void send_netplayer_load_packet(net_player *pl); + +void send_jump_into_mission_packet(net_player *pl = NULL); + +void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code ); + +void send_mission_sync_packet(int mode,int start_campaign = 0); + +void send_store_stats_packet(int accept); + +void send_debris_create_packet(object *objp, ushort net_signature, int model_num, vector exp_center ); +void send_debris_update_packet(object *objp,int code); + +void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status ); + +// ALAN BEGIN + +// send a request from the client to the host of the game (which is not necessarily the server in the case of the standalone) +// mode == WSS_WEAPON_SELECT or WSS_SHIP_SELECT +void send_wss_request_packet(short player_id, int from_slot,int from_index, int to_slot, int to_index, int wl_ship_slot, int ship_class, int mode,net_player *p = NULL); +void process_wss_request_packet(ubyte *data, header *hinfo); + +// send the update from the host to the clients +// wss_data is the pointer to a block of data returned by store_wss_stuff(...) +// +// I would reccomend : +// int store_wss_data(ubyte *block); // which returns bytes processed +// +// so you would say : +// +// ubyte block[MAX_PACKET_SIZE - 10 or so]; +// int processed = store_wss_data(block); +// send_wss_update_packet(block,processed); +// +// also : +// I would reccomend : +// int restore_wss_data(ubyte *block); // which returns bytes processed +// +// so I would say in the process_wss_update_packet() : +// +// int processed = restore_wss_data(block); +// do_other_lowlevel_packet_related_stuff_here(); +// +void send_wss_update_packet(int team_num,ubyte *wss_data,int size); +void process_wss_update_packet(ubyte *data, header *hinfo); +// ALAN END + +void send_firing_info_packet(void); + +void send_sh_transfer_complete_packet(int code); + +// packet to tell clients cargo of a ship was revealed to all +void send_cargo_revealed_packet(ship *shipp); + +void send_subsystem_cargo_revealed_packet(ship *shipp, int index); + +void send_mission_goal_info_packet(int goal_num, int new_status, int valid); + +void send_player_settings_packet(net_player *p = NULL); + +void send_deny_packet(net_addr *addr, int code); + +void send_post_sync_data_packet(net_player *p = NULL, int std_request = 1); + +void send_wss_slots_data_packet(int team_num, int final, net_player *p = NULL, int std_request = 1); + +void send_shield_explosion_packet(int objnum, int tri_num, vector hit_pos); + +void send_player_stats_block_packet(net_player *pl, int stats_type, net_player *target = NULL); + +void send_host_restr_packet(char *callsign, int code, int mode); + +void send_netgame_end_error_packet(int notify_code, int err_code); + +void send_client_update_packet(net_player *pl); + +// send information about this currently active game to the specified address +void send_game_active_packet(net_addr* addr); + +void send_ai_info_update_packet(object *objp, char what); +void process_ai_info_update_packet(ubyte *data, header *hinfo); + +void send_asteroid_create(object *new_objp, object *parent_objp, int asteroid_type, vector *relvec); +void send_asteroid_throw(object *objp); +void send_asteroid_hit(object *objp, object *other_objp, vector *hitpos, float damage); +void process_asteroid_info(ubyte *data, header *hinfo); + +void send_countermeasure_success_packet(int objnum); +void process_countermeasure_success_packet(ubyte *data, header *hinfo); + +// host sends a -1 to the server to begin the countdown. server sends an int "seconds until start" +void send_countdown_packet(int time); + +void send_debrief_info(int stage_count[], int *stage_active[]); +void process_debrief_info(ubyte *data, header *hinfo); + +void send_accept_player_data(net_player *npp, int is_ingame); +void process_accept_player_data(ubyte *data, header *hinfo); + +void send_homing_weapon_info(int num); +void process_homing_weapon_info(ubyte *data, header *hinfo); + +// emp effect stuff +void send_emp_effect(ushort net_sig, float intensity, float time); +void process_emp_effect(ubyte *data, header *hinfo); + +// for reinforcements +void send_reinforcement_avail( int rnum ); +void process_reinforcement_avail( ubyte *data, header *hinfo ); + +// change iff stuff +void send_change_iff_packet(ushort net_signature, int new_team); +void process_change_iff_packet( ubyte *data, header *hinfo); + +// new primary fired info +void send_NEW_primary_fired_packet(ship *shipp, int banks_fired); +void process_NEW_primary_fired_packet(ubyte *data, header *hinfo); + +// new countermeasure fired info +void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val); +void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo); + +// beam weapon packet +void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override); +void process_beam_fired_packet(ubyte *data, header *hinfo); + +// sw std query packet +void send_sw_query_packet(ubyte code, char *txt); +void process_sw_query_packet(ubyte *data, header *hinfo); + +// event update packet +void send_event_update_packet(int event); +void process_event_update_packet(ubyte *data, header *hinfo); + +// weapon detonate packet +void send_weapon_detonate_packet(object *objp); +void process_weapon_detonate_packet(ubyte *data, header *hinfo); + +// turret fired packet +void send_turret_fired_packet( int objnum, int subsys_index, int weapon_objnum ); +void process_turret_fired_packet( ubyte *data, header *hinfo ); + +// flak fired packet +void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range); +void process_flak_fired_packet(ubyte *data, header *hinfo); + +// player pain packet +void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos); +void process_player_pain_packet(ubyte *data, header *hinfo); + +// lightning packet +void send_lightning_packet(int bolt_type, vector *start, vector *strike); +void process_lightning_packet(ubyte *data, header *hinfo); + +// bytes sent +void send_bytes_recvd_packet(net_player *pl); +void process_bytes_recvd_packet(ubyte *data, header *hinfo); + +// host transfer +void send_host_captain_change_packet(short player_id, int captain_change); +void process_host_captain_change_packet(ubyte *data, header *hinfo); + +// self destruct +void send_self_destruct_packet(); +void process_self_destruct_packet(ubyte *data, header *hinfo); + +#endif + diff --git a/include/multiteamselect.h b/include/multiteamselect.h new file mode 100644 index 0000000..68644e8 --- /dev/null +++ b/include/multiteamselect.h @@ -0,0 +1,208 @@ +/* + * $Logfile: /Freespace2/code/Network/MultiTeamSelect.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Multiplayer Team Selection Code header + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 22 5/19/98 8:35p Dave + * Revamp PXO channel listing system. Send campaign goals/events to + * clients for evaluation. Made lock button pressable on all screens. + * + * 21 5/18/98 12:41a Allender + * fixed subsystem problems on clients (i.e. not reporting properly on + * damage indicator). Fixed ingame join problem with respawns. minor + * comm menu stuff + * + * 20 5/10/98 7:06p Dave + * Fix endgame sequencing ESC key. Changed how host options warning popups + * are done. Fixed pause/message scrollback/options screen problems in mp. + * Make sure observer HUD doesn't try to lock weapons. + * + * 19 5/06/98 8:06p Dave + * Made standalone reset properly under weird conditions. Tweak + * optionsmulti screen. Upped MAX_WEAPONS to 350. Put in new launch + * countdown anim. Minro ui fixes/tweaks. + * + * 18 4/22/98 7:24p Dave + * Made sure the "player/ships" locked button for multiplayer appears on + * all briefing screens. + * + * 17 4/20/98 4:56p Allender + * allow AI ships to respawn as many times as there are respawns in the + * mission. + * + * 16 4/01/98 11:19p Dave + * Put in auto-loading of xferred pilot pic files. Grey out background + * behind pinfo popup. Put a chatbox message in when players are kicked. + * Moved mission title down in briefing. Other ui fixes. + * + * 15 3/26/98 6:01p Dave + * Put in file checksumming routine in cfile. Made pilot pic xferring more + * robust. Cut header size of voice data packets in half. Put in + * restricted game host query system. + * + * 14 3/10/98 10:56a Dave + * Put support for deleting ships from starting wings back in. + * + * 13 3/09/98 5:55p Dave + * Fixed stats to take asteroid hits into account. Polished up UI stuff in + * team select. Finished up pilot info popup. Tracked down and fixed + * double click bug. + * + * 12 3/05/98 5:03p Dave + * More work on team vs. team support for multiplayer. Need to fix bugs in + * weapon select. + * + * 11 3/01/98 3:26p Dave + * Fixed a few team select bugs. Put in multiplayer intertface sounds. + * Corrected how ships are disabled/enabled in team select/weapon select + * screens. + * + * 10 2/23/98 11:09p Dave + * Finished up multiplayer campaign support. Seems bug-free. + * + * 9 2/19/98 6:26p Dave + * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in + * initial support for player data uploading. + * + * 8 2/18/98 3:56p Dave + * Several bugs fixed for mp team select screen. Put in standalone packet + * routing for team select. + * + * 7 2/17/98 6:08p Dave + * Tore out old multiplayer team select screen, installed new one. + * + * 6 10/16/97 4:58p Dave + * Finsihed up server side respawning issues. Knocked off a bunch of + * sequencing issues. + * + * 5 10/10/97 4:42p Dave + * Fixed up server transfer bugs. + * + * 4 10/09/97 4:58p Lawrance + * get short_callsign from player struct + * + * 3 10/03/97 4:58p Dave + * Put in client-side mimicing of host team selection changes. Some more + * stanalone hooks. + * + * 2 9/19/97 4:24p Allender + * start of team selection screen. + * + * 1 9/18/97 11:45a Allender + * + */ + +#ifndef _MULTITEAMSELECT_H +#define _MULTITEAMSELECT_H + +// ------------------------------------------------------------------------------------------------------ +// TEAM SELECT DEFINES/VARS +// + +struct header; + +// should be initialize to 0 inside of multi_vars_init +extern int Multi_ts_inited; + +#define MULTI_TS_MAX_TEAMS 2 // 2 teams max for now +#define MULTI_TS_NUM_SHIP_SLOTS 12 // # of ship slots in non team vs. team mode + +// deleted ship objnums +extern int Multi_ts_deleted_objnums[MULTI_TS_MAX_TEAMS * MULTI_TS_NUM_SHIP_SLOTS]; +extern int Multi_ts_num_deleted; + +// ------------------------------------------------------------------------------------------------------ +// TEAM SELECT FUNCTIONS +// + +// initialize the team select screen (always call, even when switching between weapon select, etc) +void multi_ts_init(); + +// initialize all critical internal data structures +void multi_ts_common_init(); + +// do frame for team select +void multi_ts_do(); + +// close the team select screen (always call, even when switching between weapon select, etc) +void multi_ts_close(); + +// drop a carried icon +void multi_ts_drop(int from_type,int from_index,int to_type,int to_index,int ship_class,int player_index = -1); + +// assign all players to appropriate default wings/slots +void multi_ts_assign_players_all(); + +// is the given slot disabled for the specified player +int multi_ts_disabled_slot(int slot_index,int player_index = -1); + +// is the given slot disabled for the specified player, _and_ it is his ship as well +int multi_ts_disabled_high_slot(int slot_index,int player_index = -1); + +// delete ships which have been removed from the game, tidy things +void multi_ts_create_wings(); + +// resynch all display/interface elements based upon all the ship/weapon pool values +void multi_ts_sync_interface(); + +// do any necessary processing for players who have left the game +void multi_ts_handle_player_drop(); + +// handle all details when the commit button is pressed (including possibly reporting errors/popups) +void multi_ts_commit_pressed(); + +// get the team # of the given ship +int multi_ts_get_team(char *ship_name); + +// function to get the team and slot of a particular ship +void multi_ts_get_team_and_slot(char *ship_name,int *team_index,int *slot_index); + +// function to return the shipname of the ship belonging in slot N +char *multi_ts_get_shipname( int team, int slot_index ); + +// blit the proper "locked" button - used for weapon select and briefing screens +void multi_ts_blit_locked_button(); + +// the "lock" button has been pressed +void multi_ts_lock_pressed(); + +// if i'm "locked" +int multi_ts_is_locked(); + +// show a popup saying "only host and team captains can modify, etc, etc" +void multi_ts_maybe_host_only_popup(); + +// ------------------------------------------------------------------------------------------------------ +// TEAM SELECT PACKET HANDLERS +// + +// send a player slot position update +void send_pslot_update_packet(int team,int code,int sound = -1); + +// process a player slot position update +void process_pslot_update_packet(ubyte *data, header *hinfo); + + + +// ------------------------------------------------------------------------------------------------------ +// TEAM SELECT STUBBED FUNCTIONS +// + +#endif + diff --git a/include/multiui.h b/include/multiui.h new file mode 100644 index 0000000..c28ce2e --- /dev/null +++ b/include/multiui.h @@ -0,0 +1,452 @@ +/* + * $Logfile: /Freespace2/code/Network/MultiUI.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the UI of the various multiplayer screens + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 8 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 7 5/04/99 5:20p Dave + * Fixed up multiplayer join screen and host options screen. Should both + * be at 100% now. + * + * 6 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * 5 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 4 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 68 9/17/98 3:08p Dave + * PXO to non-pxo game warning popup. Player icon stuff in create and join + * game screens. Upped server count refresh time in PXO to 35 secs (from + * 20). + * + * 67 9/10/98 1:17p Dave + * Put in code to flag missions and campaigns as being MD or not in Fred + * and Freespace. Put in multiplayer support for filtering out MD + * missions. Put in multiplayer popups for warning of non-valid missions. + * + * 66 9/04/98 3:51p Dave + * Put in validated mission updating and application during stats + * updating. + * + * 65 8/12/98 4:53p Dave + * Put in 32 bit checksumming for PXO missions. No validation on the + * actual tracker yet, though. + * + * 64 6/05/98 9:54a Lawrance + * OEM changes + * + * 63 5/23/98 3:31p Dave + * Tweaked pxo code. Fixed observer HUD stuff. + * + * 62 5/20/98 2:25a Dave + * Fixed server side voice muting. Tweaked multi debrief/endgame + * sequencing a bit. Much friendlier for stats tossing/accepting now. + * + * 61 5/15/98 12:09a Dave + * New tracker api code. New game tracker code. Finished up first run of + * the PXO screen. Fixed a few game server list exceptions. + * + * 60 5/11/98 11:40p Dave + * Stuff. + * + * 59 5/09/98 7:16p Dave + * Put in CD checking. Put in standalone host password. Made pilot into + * popup scrollable. + * + * 58 5/08/98 5:05p Dave + * Go to the join game screen when quitting multiplayer. Fixed mission + * text chat bugs. Put mission type symbols on the create game list. + * Started updating standalone gui controls. + * + * 57 5/04/98 10:39p Dave + * Put in endgame sequencing. Need to check campaign situations. + * Realigned ship info on team select screen. + * + * 56 5/04/98 1:44p Dave + * Fixed up a standalone resetting problem. Fixed multiplayer stats + * collection for clients. Make sure all multiplayer ui screens have the + * correct palette at all times. + * + * 55 4/30/98 12:57a Dave + * Put in new mode for ship/weapon selection. Rearranged how game querying + * is done a bit. + * + * 54 4/23/98 6:19p Dave + * Store ETS values between respawns. Put kick feature in the text + * messaging system. Fixed text messaging system so that it doesn't + * process or trigger ship controls. Other UI fixes. + * + * 53 4/23/98 1:28a Dave + * Seemingly nailed the current_primary_bank and current_secondary_bank -1 + * problem. Made sure non-critical button presses are _never_ sent to the + * server. + * + * 52 4/21/98 11:56p Dave + * Put in player deaths statskeeping. Use arrow keys in the ingame join + * ship select screen. Don't quit the game if in the debriefing and server + * leaves. + * + * 51 4/15/98 5:03p Dave + * Put in a rough countdown to mission start on final sync screen. Fixed + * several team vs. team bugs on the ship/team select screen. + * + * 50 4/14/98 12:19p Dave + * Revised the pause system yet again. Seperated into its own module. + * + * 49 4/13/98 4:50p Dave + * Maintain status of weapon bank/links through respawns. Put # players on + * create game mission list. Make observer not have engine sounds. Make + * oberver pivot point correct. Fixed respawn value getting reset every + * time host options screen started. + * + * 48 4/07/98 5:42p Dave + * Put in support for ui display of voice system status (recording, + * playing back, etc). Make sure main hall music is stopped before + * entering a multiplayer game via ingame join. + * + * 47 4/06/98 10:25p Dave + * Fixed up Netgame.respawn for the standalone case. + * + * 46 4/02/98 6:29p Lawrance + * compile out multilag code for demo + * + * 45 4/01/98 11:19p Dave + * Put in auto-loading of xferred pilot pic files. Grey out background + * behind pinfo popup. Put a chatbox message in when players are kicked. + * Moved mission title down in briefing. Other ui fixes. + * + * 44 3/28/98 3:20p Dave + * Made pilot select popup handle large #'s of pilots correctly. Made + * create game screen display/select missions correctly. Made temp + * observers get stats after a mission. Numerous small ui fixes. + * + * 43 3/26/98 5:24p Allender + * put in respawn edit box into mission notes dialog. Made loading of + * missions/campaign happen when first entering the game setup screen. + * + * 42 3/18/98 5:52p Dave + * Put in netgame password popup. Numerous ui changes. Laid groundwork for + * streamed multi_voice data. + * + * 41 2/23/98 11:09p Dave + * Finished up multiplayer campaign support. Seems bug-free. + * + * 40 2/20/98 4:47p Allender + * beefed up the multiplayer host interface when selection missions. + * Don't reload all the missions everytime a new filter is set + * + * 39 2/15/98 4:28p Dave + * Made multiplayer mission lists display mission titles and descriptions + * as well as filename. Removed some unneeded code in main hall. + * + * 38 2/11/98 5:35p Dave + * Standalone debugging. Changed how support ships warping in are handled. + * Some UI code tidying up. + * + * 37 2/10/98 8:39p Dave + * Fixed bugs. Made endgame sequencing more clear. + * + * 36 1/31/98 4:32p Dave + * Put in new support for VMT player validation, game logging in, and game + * logging out. Need to finish stats transfer. + * + * 35 1/23/98 5:43p Dave + * Finished bringing standalone up to speed. Coded in new host options + * screen. + * + * 34 1/22/98 5:26p Dave + * Modified some pregame sequencing packets. Starting to repair broken + * standalone stuff. + * + * 33 1/21/98 5:58p Dave + * Finished ingame join. Coded in multiplayer interface artwork changes. + * + * 32 1/20/98 5:42p Dave + * Moved ingame join to its own module. Improved it a bit. + * + * 31 1/16/98 2:34p Dave + * Made pause screen work properly (multiplayer). Changed how chat packets + * work. + * + * 30 1/15/98 6:12p Dave + * Fixed weapons loadout bugs with multiplayer respawning. Added + * multiplayer start screen. Fixed a few chatbox bugs. + * + * 29 12/18/97 8:59p Dave + * Finished putting in basic support for weapon select and ship select in + * multiplayer. + * + * 28 12/13/97 8:01p Dave + * Installed multiplayer create game screen. + * + * 27 12/13/97 3:01p Dave + * Finished polishing up multiplayer join screen. + * + * 26 12/13/97 1:59a Dave + * Installed multiplayer join screen + * + * 25 12/03/97 5:04p Sandeep + * Fixed TCP/IP connections and you can play games off master tracker now + * + * 24 11/15/97 2:37p Dave + * More multiplayer campaign support. + * + * 23 11/12/97 4:43p Hoffoss + * Implemented new pause screens, moved old pause to debug pause. + * + * 22 11/04/97 3:37p Dave + * More sequencing overhauls. Respawning, and server transfer. + * + * 21 10/30/97 5:47p Dave + * Smoothed transition between multiple multiplayer missions. Nailed a few + * timestamp problems. + * + * 20 10/29/97 5:18p Dave + * More debugging of server transfer. Put in debrief/brief + * transition for multiplayer (w/standalone) + * + * 19 10/22/97 6:26p Dave + * Cleanup up yet more pregame sequencing. Got standalone up to working + * condition. + * + * 18 10/21/97 5:21p Dave + * Fixed pregame mission load/file verify debacle. Added single vs. + * multiplayer stats system. + * + * 17 10/20/97 5:00p Dave + * Installed new de-luxe ack system. Fixed a few potential chat/stats + * bugs. Cleaned out some old code. + * + * 16 10/09/97 5:24p Allender + * fix misison time display. Pass frametime into UI menus + * + * 15 10/02/97 4:53p Dave + * Finished all leave/join problems. Fixed file xfer with new ingame and + * non-ingame situations. Fixed oddball timestamp problem. + * + * 14 9/30/97 5:07p Dave + * Finished up client-server remote commands. Finished up client-server + * squadmate messaging. Added periodic auto-subsystem updates. Began work + * on adapting ingame join to new player start system + * + * 13 9/30/97 4:58p Allender + * object updates on client side now happen according to different levels. + * prediction code needs to be put in still + * + * 12 9/15/97 4:43p Dave + * Got basic observer mode working. Seems bug free so far. + * + * 11 8/29/97 5:01p Dave + * Put in multiplayer pause screen/feartures. + * + * 10 8/20/97 4:21p Dave + * Spliced out busy ACCEPT wait loop to work within the existing state + * loop. Fixed display of mission description. + * + * 9 7/24/97 10:06a Dave + * Added scrollable game server list into multi_join_tracker_* + * + * 8 7/23/97 4:54p Dave + * Added multi_join_tracker_* functions for dealing with the tracker. + * + * 7 7/02/97 12:59p Allender + * chat hooks -- fixed a couple of chat bugs and reformetted the screen + * slightly + * + * 6 6/12/97 9:13a Allender + * added sequencing state to the end of ship selection. Changed some + * packet names and host sequencing + * + * 5 6/10/97 9:56p Allender + * get multiplayer mission selection working. Host can select mission and + * have himself and clients load the mission -- no sequencing past this + * point however + * + * 4 6/06/97 10:40a Allender + * added 'type' to mission (single/multi/etc). Added a couple of new game + * states for allowing to choose mission for multiplayer game + * + * 3 1/01/97 6:45p Lawrance + * added more multiplayer messages, improved code + * + * 2 12/30/96 10:18a Lawrance + * split up multiplayer code into manageable files + * + * $NoKeywords: $ + */ + + +#ifndef MULTI_UI_H +#define MULTI_UI_H + +#include "ui.h" +#include "missionparse.h" + +struct net_player; + +void multi_common_add_text(char *txt,int auto_scroll = 0); +void multi_common_set_text(char *str,int auto_scroll = 0); + +// time between sending refresh packets to known servers +#define MULTI_JOIN_REFRESH_TIME 45000 +#define MULTI_JOIN_REFRESH_TIME_LOCAL 5000 +// this time must be longer than the MULTI_JOIN_REFRESH_TIME but shorter than twice the MULTI_JOIN_REFRESH_TIME +// so that it does not time out between refresh times, but cannot last more than 2 complete refreshed without +// timing out. Just trust me - DB +#define MULTI_JOIN_SERVER_TIMEOUT (MULTI_JOIN_REFRESH_TIME + (MULTI_JOIN_REFRESH_TIME /2)) +#define MULTI_JOIN_SERVER_TIMEOUT_LOCAL (MULTI_JOIN_REFRESH_TIME_LOCAL + (MULTI_JOIN_REFRESH_TIME_LOCAL / 2)) + +// maximum number of items which can be on the list +#if defined(DEMO) || defined(OEM_BUILD) // not for FS2_DEMO + #define MULTI_CREATE_MAX_LIST_ITEMS 1 +#else + #define MULTI_CREATE_MAX_LIST_ITEMS 200 +#endif + +typedef struct { + char filename[MAX_FILENAME_LEN]; // filename of the mission + char name[NAME_LENGTH]; // name of the mission + int flags; // flags to tell what type of multiplayer game (coop, team v. team) + uint respawn; // mission specified respawn count + ubyte max_players; // max players allowed for this file + char valid_status; // see MVALID_* defines above +} multi_create_info; + +// load all common icons +#define MULTI_NUM_COMMON_ICONS 12 +#define MICON_VOICE_DENIED 0 +#define MICON_VOICE_RECORDING 1 +#define MICON_TEAM0 2 +#define MICON_TEAM0_SELECT 3 +#define MICON_TEAM1 4 +#define MICON_TEAM1_SELECT 5 +#define MICON_COOP 6 +#define MICON_TVT 7 +#define MICON_DOGFIGHT 8 +#define MICON_VOLITION 9 +#define MICON_VALID 10 +#define MICON_CD 11 + +// common icon stuff +extern int Multi_common_icons[MULTI_NUM_COMMON_ICONS]; +extern int Multi_common_icon_dims[MULTI_NUM_COMMON_ICONS][2]; +void multi_load_common_icons(); +void multi_unload_common_icons(); + +// initialize/display all bitmaps, etc related to displaying the voice system status +void multi_common_voice_display_status(); + +// multiplayer screen common palettes +void multi_common_load_palette(); +void multi_common_set_palette(); +void multi_common_unload_palette(); + +// call this to verify if we have a CD in the drive or not +void multi_common_verify_cd(); + +// variables to hold the mission and campaign lists +extern int Multi_create_mission_count; // how many we have +extern int Multi_create_campaign_count; +extern multi_create_info Multi_create_mission_list[MULTI_CREATE_MAX_LIST_ITEMS]; +extern multi_create_info Multi_create_campaign_list[MULTI_CREATE_MAX_LIST_ITEMS]; + +extern char Multi_create_files_array[MULTI_CREATE_MAX_LIST_ITEMS][MAX_FILENAME_LEN]; +extern int Multi_create_files_array_count; + +void multi_create_list_load_missions(); +void multi_create_list_load_campaigns(); + +// returns an index into Multi_create_mission_list +int multi_create_lookup_mission(char *fname); + +// returns an index into Multi_create_campaign_list +int multi_create_lookup_campaign(char *fname); + +void multi_sg_rank_build_name(char *in,char *out); + +void multi_join_game_init(); +void multi_join_game_close(); +void multi_join_game_do_frame(); +void multi_join_eval_pong(net_addr *addr, fix pong_time); +void multi_join_reset_join_stamp(); +void multi_join_clear_game_list(); +void multi_join_notify_new_game(); + +void multi_start_game_init(); +void multi_start_game_do(); +void multi_start_game_close(); + +void multi_create_game_init(); +void multi_create_game_do(); +void multi_create_game_close(); +void multi_create_game_add_mission(char *fname,char *name, int flags); + +#define MULTI_CREATE_SHOW_MISSIONS 0 +#define MULTI_CREATE_SHOW_CAMPAIGNS 1 +void multi_create_setup_list_data(int mode); + +void multi_create_handle_join(net_player *pl); + +void multi_jw_handle_join(net_player *pl); + +void multi_host_options_init(); +void multi_host_options_do(); +void multi_host_options_close(); + +void multi_game_client_setup_init(); +void multi_game_client_setup_do_frame(); +void multi_game_client_setup_close(); + +#define MULTI_SYNC_PRE_BRIEFING 0 // moving from the join to the briefing stage +#define MULTI_SYNC_POST_BRIEFING 1 // moving from the briefing to the gameplay stage +#define MULTI_SYNC_INGAME 2 // ingame joiners data sync +extern int Multi_sync_mode; // should always set this var before calling GS_EVENT_MULTI_MISSION_SYNC +extern int Multi_sync_countdown; // time in seconds until the mission is going to be launched +void multi_sync_init(); +void multi_sync_do(); +void multi_sync_close(); +void multi_sync_start_countdown(); // start the countdown to launch when the launch button is pressed + +// note : these functions are called from within missiondebrief.cpp - NOT from freespace.cpp +void multi_debrief_init(); +void multi_debrief_do_frame(); +void multi_debrief_close(); +void multi_debrief_accept_hit(); // handle the accept button being hit +void multi_debrief_esc_hit(); // handle the ESC button being hit +void multi_debrief_replay_hit(); // handle the replay button being hit +void multi_debrief_server_left(); // call this when the server has left and we would otherwise be saying "contact lost with server" +void multi_debrief_stats_accept(); // call this to insure that stats are not undone when we leave the debriefing +void multi_debrief_stats_toss(); // call this to "toss" the stats packet +int multi_debrief_stats_accept_code(); // call this to determine the status of multiplayer stats acceptance +void multi_debrief_server_process(); // process all details regarding moving the netgame to its next state + +// add a notification string, drawing appropriately depending on the state/screen we're in +void multi_common_add_notify(char *str); + +// bring up the password string popup, fill in passwd (return 1 if accept was pressed, 0 if cancel was pressed) +int multi_passwd_popup(char *passwd); + +#endif + diff --git a/include/multiutil.h b/include/multiutil.h new file mode 100644 index 0000000..cc0da2b --- /dev/null +++ b/include/multiutil.h @@ -0,0 +1,357 @@ +/* + * $Logfile: /Freespace2/code/Network/MultiUtil.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file to support misc. multiplayer support functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 13 9/15/99 1:45a Dave + * Don't init joystick on standalone. Fixed campaign mode on standalone. + * Fixed no-score-report problem in TvT + * + * 12 8/22/99 5:53p Dave + * Scoring fixes. Added self destruct key. Put callsigns in the logfile + * instead of ship designations for multiplayer players. + * + * 11 7/30/99 7:01p Dave + * Dogfight escort gauge. Fixed up laser rendering in Glide. + * + * 10 5/04/99 5:20p Dave + * Fixed up multiplayer join screen and host options screen. Should both + * be at 100% now. + * + * 9 3/08/99 7:03p Dave + * First run of new object update system. Looks very promising. + * + * 8 2/24/99 2:25p Dave + * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn + * bug for dogfight more. + * + * 7 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * 6 12/14/98 12:13p Dave + * Spiffed up xfer system a bit. Put in support for squad logo file xfer. + * Need to test now. + * + * 5 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 4 11/17/98 11:12a Dave + * Removed player identification by address. Now assign explicit id #'s. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 121 9/11/98 5:08p Dave + * More tweaks to kick notification system. + * + * 120 9/04/98 3:52p Dave + * Put in validated mission updating and application during stats + * updating. + * + * 119 8/12/98 4:53p Dave + * Put in 32 bit checksumming for PXO missions. No validation on the + * actual tracker yet, though. + * + * 118 7/24/98 11:15a Allender + * added 32bit checksumming stuff for validated missions + * + * 117 7/10/98 1:13a Allender + * lots of small multiplayer update changes. Code in launcher to specify + * connection speed. A couple of small fixes regarding empty mission + * files. Time out players after 10 second when they don't connect on + * their reliable socket. + * + * 116 6/30/98 2:17p Dave + * Revised object update system. Removed updates for all weapons. Put + * button info back into control info packet. + * + * 115 6/12/98 2:49p Dave + * Patch 1.02 changes. + * + * 114 6/10/98 2:56p Dave + * Substantial changes to reduce bandwidth and latency problems. + * + * 113 6/04/98 11:46a Dave + * Drastically reduce size/rate of client control info update packets. Put + * in rate limiting for object updating from server. + * + * 112 6/03/98 2:15p Dave + * Added special code from john to drastically reduce physics info packet + * fields. (30-50% reduction in bandwidth) + * + * 111 5/21/98 1:52a Dave + * Remove obsolete command line functions. Reduce shield explosion packets + * drastically. Tweak PXO screen even more. Fix file xfer system so that + * we can guarantee file uniqueness. + * + * 110 5/15/98 5:16p Dave + * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy + * status for team vs. team. Put in asserts to check for invalid team vs. + * team situations. + * + * 109 5/11/98 11:40p Dave + * Stuff. + * + * 108 5/10/98 7:06p Dave + * Fix endgame sequencing ESC key. Changed how host options warning popups + * are done. Fixed pause/message scrollback/options screen problems in mp. + * Make sure observer HUD doesn't try to lock weapons. + * + * 107 5/07/98 6:31p Dave + * Fix sticky situations where players are dying/respawning when the game + * is ended. + * + * 106 5/04/98 10:39p Dave + * Put in endgame sequencing. Need to check campaign situations. + * Realigned ship info on team select screen. + * + * 105 4/30/98 12:57a Dave + * Put in new mode for ship/weapon selection. Rearranged how game querying + * is done a bit. + * + * $NoKeywords: $ + */ + + +#ifndef MULTI_UTIL_H +#define MULTI_UTIL_H + +#include "psnet.h" + +// prototypes instead of headers :) +struct net_player; +struct net_addr; +struct player; +struct button_info; +struct join_request; +struct physics_info; +struct object; +struct active_game; +struct ship; +struct server_item; +struct ship_info; + +// two types of signatures that we can request, permanent signatures are all below 1000. non-permanent are above 1000 +#define MULTI_SIG_SHIP 1 +#define MULTI_SIG_ASTEROID 2 +#define MULTI_SIG_NON_PERMANENT 3 +#define MULTI_SIG_DEBRIS 4 + +extern ushort multi_assign_network_signature( int what_kind ); +extern ushort multi_get_next_network_signature( int what_kind ); +extern void multi_set_network_signature( ushort signature, int what_kind ); + +extern void stuff_netplayer_info( net_player *nplayer, net_addr *addr, int ship_class, player *pplayer ); +extern int find_player(net_addr* addr); +extern int find_player_no_port(net_addr *addr); +extern int find_player_id(short player_id); +extern int find_player_socket(PSNET_SOCKET_RELIABLE sock); // note this is only valid to do on a server! +extern int multi_find_player_by_object( object *obj ); +extern int multi_find_player_by_signature( int signature ); +extern int multi_find_player_by_callsign(char *callsign); +extern int multi_find_player_by_net_signature(ushort net_signature); +extern int multi_find_player_by_ship_name(char *ship_name); +extern int multi_create_player(int player_num, player *pl,char* name, net_addr* addr, int ship_class, short id); +extern int multi_find_open_netplayer_slot(); +extern int multi_find_open_player_slot(); +extern void delete_player(int player_num, int kicked_reason = -1); +extern int multi_get_player_ship(int np_index); + +extern int multi_num_players(); +extern int multi_num_observers(); +extern int multi_num_connections(); + +extern char* multi_random_death_word(); +extern char* multi_random_chat_start(); + +extern int multi_ship_class_lookup(char* ship_name); +extern ushort netmisc_calc_checksum( void * vptr, int len ); +extern void fill_net_addr(net_addr* addr, ubyte* address, ubyte* net_id, ushort port); +extern char* get_text_address( char * text, ubyte * address ); + +extern object *multi_get_network_object( ushort net_signature ); // find a network object + +void multi_find_ingame_join_pos(object *new_obj); + +// return size of packed matrix +void multi_pack_orient_matrix(ubyte *data,matrix *m); + +// return bytes processed +void multi_unpack_orient_matrix(ubyte *data,matrix *m); + +// catchall to do any necessary client-side simulation processing or master side process for menu pauses, etc. +void multi_do_client_warp(float flFrametime); + +void multi_assign_player_ship( int net_player, object *objp, int ship_class ); + +// ------------------------------------------------------------------- +// ship status change functions (used both client and server side) +int lookup_ship_status(net_player *p, int unique_id, int remove=0); // auto-remove if remove == 1 +void remove_ship_status_item(net_player *p, int id); +void add_net_button_info(net_player *p, button_info *bi, int unique_id); + +// called client-side every frame +void multi_maybe_send_ship_status(); + +// will be used server side _and_ client side. +void multi_apply_ship_status(net_player *p,button_info *bi, int locally); + +void multiplayer_match_target_speed(net_player *p); + +void multi_subsys_update_all(); + +void server_verify_filesig(short player_id, ushort sum_sig, int length_sig); +int server_all_filesigs_ok(); + +void multi_untag_player_ships(); + +// broadcast alltime stats to everyone in the game +void multi_broadcast_stats(int stats_code); + +int multi_netplayer_state_check(int state, int ignore_standalone = 0); +int multi_netplayer_state_check2(int state, int state2, int ignore_standalone = 0); +int multi_netplayer_state_check3(int state, int state2, int state3, int ignore_standalone = 0); +int multi_netplayer_flag_check(int flags, int ignore_standalone = 0); + +void multi_eval_socket_error(PSNET_SOCKET sock, int error); + +void multi_maybe_send_repair_info(object *dest_obj, object *source_objp, int code); + +int multi_is_valid_unknown_packet(ubyte type); + +// create a bogus object for the standalone +void multi_create_standalone_object(); + +// determine whether (as a server), you should be rebroadcasting certain messages to everyone in the game +int multi_message_should_broadcast(int type); + +// the active game list manager functions +active_game *multi_new_active_game( void ); +active_game *multi_update_active_games(active_game *ag); +void multi_free_active_games(); + +server_item *multi_new_server_item( void ); +void multi_free_server_list(); + +// netgame options evaluation stuff +int multi_can_message(net_player *p); +int multi_can_end_mission(net_player *p); + +int multi_eval_join_request(join_request *jr,net_addr *addr); + +// called by any machine (client, host, server, standalone, etc), to begin warping out all player objects +void multi_warpout_all_players(); + +// determine the highest rank of any of the players in the game +int multi_get_highest_rank(); + +// called on the machine of the player who hit alt+j +void multi_handle_end_mission_request(); + +// called to handle any special cases where a player is in some submenu when he needs to get pushed into some other state +void multi_handle_state_special(); + +// called by the file xfer subsytem when we start receiving a file +void multi_file_xfer_notify(int handle); + +// return the lag/disconnected status of the game +int multi_query_lag_status(); + +// process a valid join request +void multi_process_valid_join_request(join_request *jr, net_addr *who_from, int ingame_join_team = -1); + +// if a player is trying to join a restricted game, evaluate the keypress (accept or not, etc) +int multi_process_restricted_keys(int k); + +// determine the status of available player ships (use team_0 for non team vs. team situations) +void multi_player_ships_available(int *team_0, int *team_1); + +// server should update the player's bank/link status with the data in the passed ship +void multi_server_update_player_weapons(net_player *pl, ship *shipp); + +// flush the multidata cache directory +void multi_flush_multidata_cache(); + +// flush all data from a previous mission before starting the next +void multi_flush_mission_stuff(); + +// should we ignore all controls and keypresses because of some multiplayer +int multi_ignore_controls(int key = -1); + +// if the kill limit has been reached by any given player +int multi_kill_limit_reached(); + +// display a chat message (write to the correct spot - hud, standalone gui, chatbox, etc) +void multi_display_chat_msg(char *msg, int player_index, int add_id); + +// fill in Current_file_checksum and Current_file_length +void multi_get_mission_checksum(char *filename); + +// Packs/unpacks an object position. +// Returns number of bytes read or written. +#define OO_POS_RET_SIZE 9 +int multi_pack_unpack_position(int write, ubyte *data, vector *pos); + +// Packs/unpacks an orientation matrix. +// Returns number of bytes read or written. +#define OO_ORIENT_RET_SIZE 6 +int multi_pack_unpack_orient(int write, ubyte *data, matrix *orient); + +// Packs/unpacks velocity +// Returns number of bytes read or written. +#define OO_VEL_RET_SIZE 4 +int multi_pack_unpack_vel(int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi); + +// Packs/unpacks desired_velocity +// Returns number of bytes read or written. +#define OO_DESIRED_VEL_RET_SIZE 3 +int multi_pack_unpack_desired_vel(int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi, ship_info *sip); + +// Packs/unpacks rotational velocity +// Returns number of bytes read or written. +#define OO_ROTVEL_RET_SIZE 4 +int multi_pack_unpack_rotvel(int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi); + +// Packs/unpacks desired rotvel +// Returns number of bytes read or written. +#define OO_DESIRED_ROTVEL_RET_SIZE 3 +int multi_pack_unpack_desired_rotvel(int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi, ship_info *sip); + +char multi_unit_to_char(float unit); +float multi_char_to_unit(float val); + +// if we should render our ping time to the server in a multiplayer game +int multi_show_ingame_ping(); + +// if Game_current_mission_filename is a builtin multiplayer mission +int multi_is_builtin_mission(); + +int multi_get_connection_speed(); + +// if we're in tracker mode, do a validation update on all known missions +void multi_update_valid_missions(); + +// get a new id# for a player +short multi_get_new_id(); + +// make a bunch of fake players - don't rely on this to be very safe - its mostly used for interface testing +#ifndef NDEBUG +void multi_make_fake_players(int count); +#endif + +#endif + diff --git a/include/muzzleflash.h b/include/muzzleflash.h new file mode 100644 index 0000000..37650cb --- /dev/null +++ b/include/muzzleflash.h @@ -0,0 +1,69 @@ +/* + * $Logfile: /Freespace2/code/Weapon/MuzzleFlash.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * all sorts of cool stuff about ships + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 5/18/99 1:30p Dave + * Added muzzle flash table stuff. + * + * 3 3/19/99 9:52a Dave + * Checkin to repair massive source safe crash. Also added support for + * pof-style nebulae, and some new weapons code. + * + * 2 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * + * $NoKeywords: $ + */ + +#ifndef __FS2_MUZZLEFLASH_HEADER_FILE +#define __FS2_MUZZLEFLASH_HEADER_FILE + +// --------------------------------------------------------------------------------------------------------------------- +// MUZZLE FLASH DEFINES/VARS +// + +// prototypes +struct object; + +// muzzle flash types +#define MAX_MUZZLE_FLASH_TYPES 10 +extern int Num_mflash_types; + +// --------------------------------------------------------------------------------------------------------------------- +// MUZZLE FLASH FUNCTIONS +// + +// initialize muzzle flash stuff for the whole game +void mflash_game_init(); + +// initialize muzzle flash stuff for the level +void mflash_level_init(); + +// shutdown stuff for the level +void mflash_level_close(); + +// create a muzzle flash on the guy +void mflash_create(vector *gun_pos, vector *gun_dir, int mflash_type); + +// process muzzle flash stuff +void mflash_process_all(); + +// render all muzzle flashes +void mflash_render_all(); + +// lookup type by name +int mflash_lookup(char *name); + +#endif + diff --git a/include/neb.h b/include/neb.h new file mode 100644 index 0000000..afae8e3 --- /dev/null +++ b/include/neb.h @@ -0,0 +1,153 @@ +/* + * $Logfile: /Freespace2/code/Nebula/Neb.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Nebula effect + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 16 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 15 7/29/99 12:05a Dave + * Nebula speed optimizations. + * + * 14 7/26/99 10:12a Dave + * Upped the max # of nebula backgrounds. + * + * 13 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * $NoKeywords: $ + */ + +#ifndef _FS2_NEB2_EFFECT_HEADER_FILE +#define _FS2_NEB2_EFFECT_HEADER_FILE + +// -------------------------------------------------------------------------------------------------------- +// NEBULA DEFINES/VARS +// + +struct ship; +struct object; +struct vector; + +// fog near and far values for rendering the background nebula +extern float Neb_backg_fog_near; +extern float Neb_backg_fog_far; + +// nebula rendering mode +#define NEB2_RENDER_NONE 0 // no rendering +#define NEB2_RENDER_POLY 1 // background is the old-school polygons +#define NEB2_RENDER_POF 2 // background is the nice pof file +#define NEB2_RENDER_LAME 3 // super simple nebula effect +extern int Neb2_render_mode; + +// the AWACS suppresion level for the nebula +extern float Neb2_awacs; + +#define MAX_NEB2_POOFS 6 + +// poof names and flags (for fred) +extern char Neb2_poof_filenames[MAX_NEB2_POOFS][MAX_FILENAME_LEN]; +extern int Neb2_poof_flags; + +#define MAX_NEB2_BITMAPS 10 + +// pof texture filenames +extern char Neb2_bitmap_filenames[MAX_NEB2_BITMAPS][MAX_FILENAME_LEN]; + +// texture to use for this level +extern char Neb2_texture_name[MAX_FILENAME_LEN]; + +// how many "slices" are in the current player nebuls +extern int Neb2_slices; + +// nebula poofs +typedef struct cube_poof { + vector pt; // point in space + int bmap; // bitmap in space + float rot; // rotation angle + float rot_speed; // rotation speed + float flash; // lightning flash +} cube_poof; +#define MAX_CPTS 5 // should always be <= slices +extern cube_poof Neb2_cubes[MAX_CPTS][MAX_CPTS][MAX_CPTS]; + + +// -------------------------------------------------------------------------------------------------------- +// NEBULA FUNCTIONS +// + +// neb2 stuff (specific nebula densities) ----------------------------------- + +// initialize neb2 stuff at game startup +void neb2_init(); + +// set detail level +void neb2_set_detail_level(int level); + +// initialize nebula stuff - call from game_post_level_init(), so the mission has been loaded +void neb2_level_init(); + +// shutdown nebula stuff +void neb2_level_close(); + +// create a nebula object, return objnum of the nebula or -1 on fail +// NOTE : in most cases you will want to pass -1.0f for outer_radius. Trust me on this +int neb2_create(vector *offset, int num_poofs, float inner_radius, float outer_radius, float max_poof_radius); + +// delete a nebula object +void neb2_delete(object *objp); + +// call before beginning all rendering +void neb2_render_setup(vector *eye_pos, matrix *eye_orient); + +// renders a nebula object +void neb2_render(object *objp); + +// preprocess the nebula object before simulation +void neb2_process_pre(object *objp); + +// process the nebula object after simulating, but before rendering +void neb2_process_post(object *objp); + +// render the player nebula +void neb2_render_player(); + +// call this when the player's viewpoint has changed, this will cause the code to properly reset +// the eye's local poofs +void neb2_eye_changed(); + +// get near and far fog values based upon object type and rendering mode +void neb2_get_fog_values(float *fnear, float *ffar, object *obj); + +// given a position in space, return a value from 0.0 to 1.0 representing the fog level +float neb2_get_fog_intensity(object *obj); + +// should we not render this object because its obscured by the nebula? +int neb2_skip_render(object *objp, float z_depth); + +// extend LOD +float neb2_get_lod_scale(int objnum); + +// fogging stuff -------------------------------------------------- + +// get the color of the pixel in the small pre-rendered background nebula +void neb2_get_pixel(int x, int y, int *r, int *g, int *b); + +// set the background color +void neb2_set_backg_color(int r, int g, int b); + +// get the color to fog the background color to +void neb2_get_backg_color(int *r, int *g, int *b); + +#endif + diff --git a/include/neblightning.h b/include/neblightning.h new file mode 100644 index 0000000..1e044f9 --- /dev/null +++ b/include/neblightning.h @@ -0,0 +1,107 @@ +/* + * $Logfile: /Freespace2/code/Nebula/NebLightning.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Nebula effect + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 7/02/99 4:31p Dave + * Much more sophisticated lightning support. + * + * 3 5/26/99 11:46a Dave + * Added ship-blasting lighting and made the randomization of lighting + * much more customizable. + * + * 2 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * $NoKeywords: $ + */ + +#ifndef __FS2_NEBULA_LIGHTNING_HEADER_FILE +#define __FS2_NEBULA_LIGHTNING_HEADER_FILE + +// ------------------------------------------------------------------------------------------------------ +// NEBULA LIGHTNING DEFINES/VARS +// + +// lightning bolt types +#define MAX_BOLT_TYPES 10 +#define DEBUG_BOLT MAX_BOLT_TYPES + +// storm types - needs to be here for Fred. blech +#define MAX_STORM_TYPES 10 +typedef struct storm_type { + char name[NAME_LENGTH]; + + ubyte num_bolt_types; // how many different bolt types you'll see in the nebula + + char bolt_types[MAX_BOLT_TYPES]; // indices into the lightning types + + vector flavor; // flavor of the storm + + int min, max; // min and max delay between bolt firing. + int min_count, max_count; // # of bolts spewed +} storm_type; + +extern int Num_storm_types; +extern storm_type Storm_types[MAX_STORM_TYPES]; + +// nebula lightning intensity (0.0 to 1.0) +extern float Nebl_intensity; + +// min and max times for random lightning +extern int Nebl_random_min; // min random time +extern int Nebl_random_max; // max random time + +// min and max times for cruiser lightning +extern int Nebl_cruiser_min; // min cruiser time +extern int Nebl_cruiser_max; // max cruiser time + +// min and max times for cap ships +extern int Nebl_cap_min; // min cap time +extern int Nebl_cap_max; // max cap time + +// min and max time for super caps +extern int Nebl_supercap_min; // min supercap time +extern int Nebl_supercap_max; // max supercap time + +#define BOLT_TYPE_ANY -2 + +// ------------------------------------------------------------------------------------------------------ +// NEBULA LIGHTNING FUNCTIONS +// + +// initialize nebula lightning at game startup +void nebl_init(); + +// initialize lightning before entering a level +void nebl_level_init(); + +// render all lightning bolts +void nebl_render_all(); + +// create a lightning bolt - pass BOLT_TYPE_ANY to get a random bolt +void nebl_bolt(int bolt_type, vector *start, vector *strike); + +// process lightning (randomly generate bolts, etc, etc); +void nebl_process(); + +// get the current # of active lightning bolts +int nebl_get_active_bolts(); + +// get the current # of active nodes +int nebl_get_active_nodes(); + +// set the storm (call from mission parse) +void nebl_set_storm(char *name); + +#endif + diff --git a/include/nebula.h b/include/nebula.h new file mode 100644 index 0000000..ee3291f --- /dev/null +++ b/include/nebula.h @@ -0,0 +1,48 @@ +/* + * $Logfile: /Freespace2/code/Starfield/Nebula.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for nebula stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 4 11/25/97 11:40a Hoffoss + * Added support for nebula placement editing. + * + * 3 11/24/97 12:04p John + * + * 2 11/16/97 2:29p John + * added versioning to nebulas; put nebula code into freespace. + * + * 1 11/16/97 1:14p John + * + * $NoKeywords: $ + */ + +#ifndef _NEBULA_H +#define _NEBULA_H + +// mainly only needed by Fred +extern int Nebula_pitch; +extern int Nebula_bank; +extern int Nebula_heading; + +// You shouldn't pass the extension for filename. +// PBH = Pitch, Bank, Heading. Pass NULL for default orientation. +void nebula_init( char *filename, int pitch, int bank, int heading ); +void nebula_init( char *filename, angles *pbh = NULL ); +void nebula_close(); +void nebula_render(); + +#endif //_NEBULA_H + diff --git a/include/objcollide.h b/include/objcollide.h new file mode 100644 index 0000000..db8b793 --- /dev/null +++ b/include/objcollide.h @@ -0,0 +1,225 @@ +/* + * $Logfile: /Freespace2/code/Object/ObjCollide.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for all the Collide????.cpp modules + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 9 4/21/99 6:15p Dave + * Did some serious housecleaning in the beam code. Made it ready to go + * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added + * a handy macro for recalculating collision pairs for a given object. + * + * 8 11/19/98 11:08p Andsager + * Check in of physics and collision detection of rotating submodels + * + * 7 11/17/98 4:33p Andsager + * add submodel_rot_hit to collision_info struct + * + * 6 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 5 10/23/98 1:11p Andsager + * Make ship sparks emit correctly from rotating structures. + * + * 4 10/20/98 1:39p Andsager + * Make so sparks follow animated ship submodels. Modify + * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add + * submodel_num. Add submodel_num to multiplayer hit packet. + * + * 3 10/16/98 1:22p Andsager + * clean up header files + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 14 5/08/98 5:25p Lawrance + * Don't allow collision sounds too play over each so much + * + * 13 4/24/98 5:35p Andsager + * Fix sparks sometimes drawing not on model. If ship is sphere in + * collision, don't draw sparks. Modify ship_apply_local_damage() to take + * parameter no_spark. + * + * 12 4/01/98 1:48p Allender + * major changes to ship collision in multiplayer. Clients now do own + * ship/ship collisions (with their own ship only) Modifed the hull + * update packet to be sent quicker when object is target of player. + * + * 11 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 10 2/05/98 12:51a Mike + * Early asteroid stuff. + * + * 9 2/04/98 6:08p Lawrance + * Add a light collision sound, overlay a shield collide sound if + * applicable. + * + * 8 1/13/98 3:11p Allender + * new code to remove old weapons when no more weapon slots available. + * Currently not called anywhere pending further testing + * + * 7 1/12/98 9:26p Andsager + * Implement collisions from rotation. + * + * 6 1/08/98 12:12a Mike + * Make ships turn before warping out, if necessary, to avoid a collision. + * Warn player if his warpout will collide. Abort if in stage1. + * + * 5 1/05/98 9:07p Andsager + * Changed ship_shipor_debris_hit_info struct to more meaninful names + * + * 4 12/22/97 9:56p Andsager + * Implement ship:debris collisions. Generalize and move + * ship_ship_or_debris_hit struct from CollideShipShip to ObjCollide.h + * + * 3 10/28/97 4:57p John + * Put Andsager's new sphereline collide code officially into the code + * base and did a little restructuring. Fixed a few little bugs with it + * and added some simple bounding box elimination and did some timings. + * + * + * 2 10/25/97 10:13a Andsager + * Added SPHERE_POLY_CHECK to central location used by ModelCollide.cpp + * and Collide_ship_ship.cpp + * + * 1 9/17/97 2:14p John + * Initial revision + * + * $NoKeywords: $ + */ + +#ifndef _COLLIDESTUFF_H +#define _COLLIDESTUFF_H + +#include "pstypes.h" + +struct object; +struct CFILE; +struct mc_info; + +// used for ship:ship and ship:debris +typedef struct collision_info_struct { + object *heavy; + object *light; + vector heavy_collision_cm_pos; // should be zero + vector light_collision_cm_pos; // relative cm collision pos + vector r_heavy; // relative to A + vector r_light; // relative to B + vector hit_pos; // relative hit position in A's rf (r_heavy) + vector collision_normal; // normal outward from heavy + float hit_time; // time normalized [0,1] when sphere hits model + float impulse; // damage scales according to impulse + vector light_rel_vel; // velocity of light relative to heavy before collison + int collide_rotate; // if collision is detected purely from rotation + int submodel_num; // submodel of heavy object that is hit + int edge_hit; // if edge is hit, need to change collision normal + int submodel_rot_hit; // if collision is against rotating submodel +} collision_info_struct; + + +//=============================================================================== +// GENERAL COLLISION DETECTION HELPER FUNCTIONS +// These are in CollideGeneral.cpp and are used by one or more of the collision- +// type specific collision modules. +//=============================================================================== + +// Keeps track of pairs of objects for collision detection +typedef struct obj_pair { + object *a; + object *b; + int (*check_collision)( obj_pair * pair ); + int next_check_time; // a timestamp that when elapsed means to check for a collision + struct obj_pair *next; +} obj_pair; + + +#define COLLISION_OF(a,b) (((a)<<8)|(b)) + +#define COLLISION_TYPE_NONE 0 +#define COLLISION_TYPE_OLD 1 // checks all n objects with each other each frame +#define COLLISION_TYPE_NEW 2 // keeps track of collision pairs. throws out collisions that won't happen. + +extern int collision_type; + +#define SUBMODEL_NO_ROT_HIT 0 +#define SUBMODEL_ROT_HIT 1 +void set_hit_struct_info(collision_info_struct *hit, mc_info *mc, int submodel_rot_hit); + +void obj_reset_pairs(); +void obj_add_pair( object *A, object *B, int check_time = -1, int add_to_end = 0 ); + +void obj_check_all_collisions(); + +// Returns TRUE if the weapon will never hit the other object. +// If it can it predicts how long until these two objects need +// to be checked and fills the time in in current_pair. +// CODE is locatated in CollideGeneral.cpp +int weapon_will_never_hit( object *weapon, object *other, obj_pair * current_pair ); + + +// See if two lines intersect by doing recursive subdivision. +// Bails out if larger distance traveled is less than sum of radii + 1.0f. +// CODE is locatated in CollideGeneral.cpp +int collide_subdivide(vector *p0, vector *p1, float prad, vector *q0, vector *q1, float qrad); + + +//=============================================================================== +// SPECIFIC COLLISION DETECTION FUNCTIONS +//=============================================================================== + +// Checks weapon-weapon collisions. pair->a and pair->b are weapons. +// Returns 1 if all future collisions between these can be ignored +// CODE is locatated in CollideWeaponWeapon.cpp +int collide_weapon_weapon( obj_pair * pair ); + +// Checks ship-weapon collisions. pair->a is ship and pair->b is weapon. +// Returns 1 if all future collisions between these can be ignored +// CODE is locatated in CollideShipWeapon.cpp +int collide_ship_weapon( obj_pair * pair ); +void ship_weapon_do_hit_stuff(object *ship_obj, object *weapon_obj, vector *world_hitpos, vector *hitpos, int quadrant_num, int submodel_num = -1); + +// Checks debris-weapon collisions. pair->a is debris and pair->b is weapon. +// Returns 1 if all future collisions between these can be ignored +// CODE is locatated in CollideDebrisWeapon.cpp +int collide_debris_weapon( obj_pair * pair ); + +// Checks debris-ship collisions. pair->a is debris and pair->b is ship. +// Returns 1 if all future collisions between these can be ignored +// CODE is locatated in CollideDebrisShip.cpp +int collide_debris_ship( obj_pair * pair ); + +int collide_asteroid_ship(obj_pair *pair); +int collide_asteroid_weapon(obj_pair *pair); + +// Checks ship-ship collisions. pair->a and pair->b are ships. +// Returns 1 if all future collisions between these can be ignored +// CODE is locatated in CollideShipShip.cpp +int collide_ship_ship( obj_pair * pair ); + +// Predictive functions. +// Returns true if vector from curpos to goalpos with radius radius will collide with object goalobjp +int pp_collide(vector *curpos, vector *goalpos, object *goalobjp, float radius); + +// Return true if objp will collide with some large ship if it moves distance distance. +int collide_predict_large_ship(object *objp, float distance); + +// function to remove old weapons when no more weapon slots available. +int collide_remove_weapons(void); + +void collide_ship_ship_do_sound(vector *world_hit_pos, object *A, object *B, int player_involved); +void collide_ship_ship_sounds_init(); + +int get_ship_quadrant_from_global(vector *global_pos, object *objp); + +#endif + diff --git a/include/object.h b/include/object.h new file mode 100644 index 0000000..c632489 --- /dev/null +++ b/include/object.h @@ -0,0 +1,593 @@ +/* + * $Logfile: /Freespace2/code/Object/Object.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 17 8/16/99 3:53p Andsager + * Add special warp in interface in Fred and saving / reading. + * + * 16 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 15 7/01/99 11:44a Dave + * Updated object sound system to allow multiple obj sounds per ship. + * Added hit-by-beam sound. Added killed by beam sound. + * + * 14 6/28/99 4:51p Andsager + * Add ship-guardian sexp (does not allow ship to be killed) + * + * 13 5/18/99 11:50a Andsager + * Remove unused object type OBJ_GHOST_SAVE + * + * 12 4/26/99 10:58a Andsager + * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing. + * + * 11 4/23/99 5:53p Dave + * Started putting in new pof nebula support into Fred. + * + * 10 4/21/99 6:15p Dave + * Did some serious housecleaning in the beam code. Made it ready to go + * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added + * a handy macro for recalculating collision pairs for a given object. + * + * 9 3/29/99 6:17p Dave + * More work on demo system. Got just about everything in except for + * blowing ships up, secondary weapons and player death/warpout. + * + * 8 1/25/99 5:03a Dave + * First run of stealth, AWACS and TAG missile support. New mission type + * :) + * + * 7 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 6 1/12/99 12:53a Dave + * More work on beam weapons - made collision detection very efficient - + * collide against all object types properly - made 3 movement types + * smooth. Put in test code to check for possible non-darkening pixels on + * object textures. + * + * 5 11/14/98 5:33p Dave + * Lots of nebula work. Put in ship contrails. + * + * 4 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 3 10/16/98 3:42p Andsager + * increase MAX_WEAPONS and MAX_SHIPS and som header files + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 96 6/30/98 2:25p Dave + * Revamped object update system + * + * 95 6/30/98 2:23p Dave + * Revised object update system. Removed updates for all weapons. Put + * button info back into control info packet. + * + * 94 5/11/98 4:33p Allender + * fixed ingame join problems -- started to work on new object updating + * code (currently ifdef'ed out) + * + * 93 5/01/98 12:59a Dave + * Put in some test code for a new object update system. Found the problem + * with the current system (low-level packet buffering). Gonna fix it :) + * + * 92 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 91 4/01/98 9:20a Mike + * Reduce MAX_SHIPS, MAX_OBJECTS and make MAX_AI_INFO same as MAX_SHIPS + * + * 90 3/31/98 5:18p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * + * 89 3/26/98 5:43p Lawrance + * rename ship_team_from_obj(), obj_team() and move to object lib + * + * 88 3/12/98 1:24p Mike + * When weapons linked, increase firing delay. + * Free up weapon slots for AI ships, if necessary. + * Backside render shield effect. + * Remove shield hit triangle if offscreen. + * + * 87 3/09/98 10:56a Hoffoss + * Added jump node objects to Fred. + * + * 86 3/07/98 2:34p Allender + * more ingame join stuff. Works in a basic fashion in this new system. + * + * 85 2/27/98 4:48p John + * Made objects keep track of number of pairs they have associated with + * them. Then, I can early out of the obj_remove_all which was 2.5% of + * frametime at beginning of sm2-2 which then is 0% after this. + * + * 84 2/26/98 3:46p Hoffoss + * Externed object_inited variable for use in state restore. + * + * 83 2/23/98 5:08p Allender + * made net_signature an unsigned short. Now using permanent and + * non-permanent object "pools". + * + * 82 2/05/98 12:51a Mike + * Early asteroid stuff. + * + * 81 1/18/98 5:09p Lawrance + * Added support for TEAM_TRAITOR + * + * 80 12/12/97 5:23p Lawrance + * add TEAM_ANY #define... used for some targeting functions + * + * 79 12/11/97 5:46p Hoffoss + * Changed Fred to not display weapons that are not available to various + * ships. + * + * 78 11/03/97 5:38p Dave + * Cleaned up more multiplayer sequencing. Added OBJ_OBSERVER module/type. + * Restructured HUD_config structs/flags. + * + * 77 10/23/97 4:41p Allender + * lots of new rearm/repair code. Rearm requests now queue as goals for + * support ship. Warp in of new support ships functional. Support for + * stay-still and play-dead. + * + * 76 10/16/97 4:40p Allender + * removed object structure member (for multiplayer) -- moved to ship + * structure + * + * 75 9/30/97 5:05p Dave + * Added OF_COULD_BE_PLAYER flag for objects which ingame joiners can + * grab. + * + * 74 9/25/97 4:50p Mike + * Support ignoring wings. + * + * 73 9/17/97 5:12p John + * Restructured collision routines. Probably broke a lot of stuff. + * + * 72 9/15/97 4:43p Dave + * Got basic observer mode working. Seems bug free so far. + * + * 71 9/11/97 5:01p Dave + * Minor changes to handle ingame joining/dropping for multiplayer. + * + * 70 9/09/97 12:01a Mike + * Further support for new team numbering, switched from 0, 1, 2, 3 to 1, + * 2, 4, 8 to allow attacking of two simultaneous teams. + * + * 69 9/08/97 10:24p Mike + * Working on attacking multiple simultaneous teams. + * + * 68 9/08/97 5:20p Dave + * Added OF_IS_ORIGINAL flag + * + * 67 9/06/97 2:13p Mike + * Replace support for TEAM_NEUTRAL + * + * 66 9/05/97 5:02p Lawrance + * save/restore object pairs + * + * 65 8/29/97 10:13a Allender + * work on server/client prediction code -- doesn't work too well. Made + * all clients simulate their own orientation with the server giving + * corrections every so often. + * + * 64 8/20/97 7:54p Lawrance + * make obj_reset_pairs() external + * + * 63 8/16/97 3:53p Hoffoss + * Added OF_NO_SHIELDS define and support in Fred and mission load/save. + * + * 62 8/11/97 8:46p Hoffoss + * Added a new object type for Fred usage. + * + * 61 8/11/97 10:36a John + * added new function that you should call when changing object flags. + * + * 60 8/04/97 11:45a Lawrance + * split off initialization of CheckObject[] elements into separate + * function + * + * 59 7/31/97 5:55p John + * made so you pass flags to obj_create. + * Added new collision code that ignores any pairs that will never + * collide. + * + * 58 7/30/97 9:40a Allender + * added last_orient to object structure in anticipation of use for + * multiplayer + * + * 57 7/28/97 5:10p Hoffoss + * Removed all occurances of neutral team from Fred. + * + * 56 7/24/97 10:24a Mike + * Restore support for Unknown team + * + * 55 7/16/97 2:52p Lawrance + * make shockwaves objects + * + * 54 6/24/97 10:04a Allender + * major multiplayer improvements. Better sequencing before game. + * Dealing with weapon/fireball/counter measure objects between + * client/host. + * + * 53 6/23/97 10:12a Hoffoss + * Added new object type for Fred. + * + * 52 6/19/97 1:43p Allender + * basic object syncing when a mission loads. basic object update packets + * every frame + * + * 51 6/13/97 1:15p Allender + * use player positions for multiplayer games. No real error checking + * yet, but you can get people into a mutiplayer mission now. + * + * 50 6/06/97 4:13p Lawrance + * use an index instead of a pointer for object-linked sounds + * + * 49 6/05/97 6:08p Hoffoss + * Added an object flag, and rearranged them to make it more organized. + * + * 48 5/19/97 1:07p Mike + * Add OBJ_GHOST and deal with a dead player better. + * + * 47 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 46 5/12/97 6:00p Mike + * Add countermeasures. + * + * 45 5/09/97 4:34p Lawrance + * move out obj_snd typedef + * + * 44 5/08/97 4:30p Lawrance + * split off object sound stuff into separate file + * + * 43 5/06/97 9:36a Lawrance + * added object-linked persistant sounds + * + * 42 4/25/97 3:29p Mike + * Making shield multi-part. + * + * 41 4/24/97 4:39p Hoffoss + * Waypoint navbouys are now objects of type OBJ_WAYPOINT and displaying + * of them can be toggled on and off through DCF. + * + * 40 4/10/97 3:20p Mike + * Change hull damage to be like shields. + * + * 39 3/07/97 4:37p Mike + * Make rockeye missile home. + * Remove UNKNOWN and NEUTRAL teams. + * + * 38 3/05/97 12:49p John + * added Viewer_obj. Took out the interp_??? variables for turning + * outline,etc on and put them in flags you pass to model_render. + * Cleaned up model_interp code to fit new coding styles. + * + * 37 2/07/97 9:09a John + * Added new debris objects + * + * 36 2/04/97 8:31a Adam + * + * 35 1/21/97 1:37p Hoffoss + * Added an object flag for Fred. + * + * 34 1/20/97 7:58p John + * Fixed some link errors with testcode. + * + * 33 1/10/97 5:15p Mike + * Moved ship-specific parameters from obj_subsystem to ship_subsys. + * + * Added turret code to AI system. + * + * $NoKeywords: $ + */ + +#ifndef _OBJECT_H +#define _OBJECT_H + +#include "pstypes.h" +#include "vecmat.h" +#include "physics.h" +#include "cfile.h" + +/* + * CONSTANTS + */ + +#ifdef FS2_DEMO + #define MAX_OBJECTS 300 +#else + #define MAX_OBJECTS 1000 +#endif + +#define MAX_SHIELD_SECTIONS 4 // Number of sections in shield. + +#ifndef NDEBUG +#define OBJECT_CHECK +#endif + +// Team bitmasks. +#define TEAM_HOSTILE (1 << 0) +#define TEAM_FRIENDLY (1 << 1) +#define TEAM_NEUTRAL (1 << 2) +#define TEAM_UNKNOWN (1 << 3) +#define TEAM_TRAITOR (1 << 4) +#define TEAM_ANY (TEAM_HOSTILE|TEAM_FRIENDLY|TEAM_NEUTRAL|TEAM_UNKNOWN|TEAM_TRAITOR) +#define MAX_TEAM_NAMES_INDEX TEAM_TRAITOR + +//Object types +#define OBJ_NONE 0 //unused object +#define OBJ_SHIP 1 //a ship +#define OBJ_WEAPON 2 //a laser, missile, etc +#define OBJ_FIREBALL 3 //an explosion +#define OBJ_START 4 //a starting point marker (player start, etc) +#define OBJ_WAYPOINT 5 //a waypoint object, maybe only ever used by Fred +#define OBJ_DEBRIS 6 //a flying piece of ship debris +#define OBJ_CMEASURE 7 //a countermeasure, such as chaff +#define OBJ_GHOST 8 //so far, just a placeholder for when a player dies. +#define OBJ_POINT 9 //generic object type to display a point in Fred. +#define OBJ_SHOCKWAVE 10 // a shockwave +#define OBJ_WING 11 // not really a type used anywhere, but I need it for Fred. +#define OBJ_OBSERVER 12 // used for multiplayer observers (possibly single player later) +#define OBJ_ASTEROID 13 // An asteroid, you know, a big rock, like debris, sort of. +#define OBJ_JUMP_NODE 14 // A jump node object, used only in Fred. +#define OBJ_BEAM 15 // beam weapons. we have to roll them into the object system to get the benefits of the collision pairs + +//Make sure to change Object_type_names in Object.c when adding another type! +#define MAX_OBJECT_TYPES 16 + +#define UNUSED_OBJNUM (-MAX_OBJECTS*2) // Newer systems use this instead of -1 for invalid object. + +#ifndef NDEBUG +extern char *Object_type_names[MAX_OBJECT_TYPES]; +#endif + +// each object type should have these functions: (I will use weapon as example) +// +// int weapon_create( weapon specific parameters ) +// { +// ... +// objnum = obj_create(); +// ... Do some check to correctly handle obj_create returning which +// means that that object couldn't be created +// ... Initialize the weapon-specific info in Objects[objnum] +// return objnum; +// } +// +// void weapon_delete( object * obj ) +// { +// {Put a call to this in OBJECT.C, function obj_delete_all_that_should_be_dead } +// WARNING: To kill an object, set it's OF_SHOULD_BE_DEAD flag. Then, +// this function will get called when it's time to clean up the data. +// Assert( obj->flags & OF_SHOULD_BE_DEAD ); +// ... +// ... Free up all weapon-specfic data +// obj_delete(objnum); +// } +// +// void weapon_move( object * obj ) +// { +// {Put a call to this in ??? } +// ... Do whatever needs to be done each frame. Usually this amounts +// to setting the thrust, seeing if we've died, etc. +// } +// +// int weapon_check_collision( object * obj, object * other_obj, vector * hitpos ) +// { +// this should check if a vector from +// other_obj->last_pos to other_obj->pos with a radius of other_obj->radius +// collides with object obj. If it does, then fill in hitpos with the point +// of impact and return non-zero, otherwise return 0 if no impact. Note that +// this shouldn't take any action... that happens in weapon_hit. +// } + +// +// void weapon_hit( object * obj, object * other_obj, vector * hitpos ) +// { +// {Put a call to this in COLLIDE.C} +// ... Do what needs to be done when this object gets hit +// ... Reducing shields, etc +// } + +//Misc object flags +#define OF_RENDERS (1<<0) //It renders as something ( objtype_render gets called) +#define OF_COLLIDES (1<<1) //It collides with stuff (objtype_check_impact & objtype_hit gets called) +#define OF_PHYSICS (1<<2) //It moves with standard physics. +#define OF_SHOULD_BE_DEAD (1<<3) //this object should be dead, so next time we can, we should delete this object. +#define OF_INVULNERABLE (1<<4) // invulnerable +#define OF_PROTECTED (1<<5) // Don't kill this object, probably mission-critical. +#define OF_PLAYER_SHIP (1<<6) // this object under control of some player -- don't do ai stuff on it!!! +#define OF_NO_SHIELDS (1<<7) // object has no shield generator system (i.e. no shileds) +#define OF_JUST_UPDATED (1<<8) // for multiplayer -- indicates that we received object update this frame +#define OF_COULD_BE_PLAYER (1<<9) // for multiplayer -- indicates that it is selectable ingame joiners as their ship +#define OF_WAS_RENDERED (1<<10) // Set if this object was rendered this frame. Only gets set if OF_RENDERS set. Gets cleared or set in obj_render_all(). +#define OF_NOT_IN_COLL (1<<11) // object has not been added to collision list +#define OF_BEAM_PROTECTED (1<<12) // don't fire beam weapons at this type of object, probably mission critical. +#define OF_GUARDIAN (1<<13) // Don't allow ship to die, keep at least 1% hull +#define OF_SPECIAL_WARP (1<<14) // Object has special warp-in enabled. + +// Flags used by Fred +#define OF_MARKED (1<<16) //Object is marked (Fred). Can be reused in Freespace for anything that won't be used by Fred. +#define OF_TEMP_MARKED (1<<17) //Temporarily marked (Fred). +#define OF_REFERENCED (1<<18) // (Fred) Object is referenced by something somewhere +#define OF_HIDDEN (1<<19) // Object is hidden (not shown) and can't be manipulated + +// max # of object sounds per object +#define MAX_OBJECT_SOUNDS 4 + +typedef struct object { + struct object *next, *prev; // for linked lists of objects + int signature; // Every object ever has a unique signature... + char type; // what type of object this is... robot, weapon, hostage, powerup, fireball + int parent; // This object's parent. + int parent_sig; // This object's parent's signature + char parent_type; // This object's parent's type + int instance; // which instance. ie.. if type is Robot, then this indexes into the Robots array + uint flags; // misc flags. Call obj_set_flags to change this. + vector pos; // absolute x,y,z coordinate of center of object + matrix orient; // orientation of object in world + float radius; // 3d size of object - for collision detection + vector last_pos; // where object was last frame + matrix last_orient; // how the object was oriented last frame + physics_info phys_info; // a physics object + float shields[MAX_SHIELD_SECTIONS]; // Shield is broken into components. Quadrants on 4/24/97. + float hull_strength; // Remaining hull strength. + short objsnd_num[MAX_OBJECT_SOUNDS]; // Index of persistant sound struct. -1 if no persistant sound assigned. + ushort net_signature; + int num_pairs; // How many object pairs this is associated with. When 0 then there are no more. +} object; + +// object backup struct used by Fred. +typedef struct object_orient_pos { + vector pos; + matrix orient; +} object_orient_pos; + +/* + * VARIABLES + */ + +extern int Object_inited; +extern int Show_waypoints; + +// The next signature for the next newly created object. Zero is bogus +#define OBJECT_SIG_SHIP_START 300000; // ships start at this signature +extern int Object_next_ship_signature; +extern int Object_next_signature; +extern int num_objects; + +extern object Objects[]; +extern int Highest_object_index; //highest objnum +extern int Highest_ever_object_index; +extern object obj_free_list; +extern object obj_used_list; +extern object obj_create_list; + +extern int render_total; +extern int render_order[MAX_OBJECTS]; + +extern object *Viewer_obj; // Which object is the viewer. Can be NULL. +extern object *Player_obj; // Which object is the player. Has to be valid. + +// Use this instead of "objp - Objects" to get an object number +// given it's pointer. This way, we can replace it will a macro +// to check that the pointer is valid for debugging. +#define OBJ_INDEX(objp) (objp-Objects) + +/* + * FUNCTIONS + */ + +//do whatever setup needs to be done +void obj_init(); + +//initialize a new object. adds to the list for the given segment. +//returns the object number. The object will be a non-rendering, non-physics +//object. Returns 0 if failed, otherwise object index. +//You can pass 0 for parent if you don't care about that. +//You can pass null for orient and/or pos if you don't care. +int obj_create(ubyte type,int parent_obj, int instance, matrix * orient, vector * pos, float radius, uint flags ); + +//Render an object. Calls one of several routines based on type +void obj_render(object *obj); + +//Sorts and renders all the ojbects +void obj_render_all(void (*render_function)(object *objp) ); + +//move all objects for the current frame +void obj_move_all(float frametime); // moves all objects + +//move an object for the current frame +void obj_move_one(object * obj, float frametime); + +// function to delete an object -- should probably only be called directly from editor code +void obj_delete(int objnum); + +// should only be used by the editor! +void obj_merge_created_list(void); + +// recalculate object pairs for an object +#define OBJ_RECALC_PAIRS(obj_to_reset) do { obj_set_flags(obj_to_reset, obj_to_reset->flags & ~(OF_COLLIDES)); obj_set_flags(obj_to_reset, obj_to_reset->flags | OF_COLLIDES); } while(0); + +// Removes any occurances of object 'a' from the pairs list. +void obj_remove_pairs( object * a ); + +// add an object to the pairs list +void obj_add_pairs(int objnum); + +// Returns true if objects A and B are expected to collide in next duration seconds. +// For purposes of this check, the first object moves from current location to predicted +// location. The second object is assumed to be where it will be at time duration, NOT +// where it currently is. +// radius_scale: 0.0f means use polygon models, else scale sphere size by radius_scale +// radius_scale == 1.0f means Descent style collisions. +int objects_will_collide(object *A, object *B, float duration, float radius_scale); + +// Used for pausing. Seems hacked. Was in PHYSICS, but that broke the TestCode program, +// so I moved it into the object lib. -John +void obj_init_all_ships_physics(); + +float get_shield_strength(object *objp); +void set_shield_strength(object *objp, float strength); +void add_shield_strength(object *objp, float delta); + +// returns the average 3-space position of all ships. useful to find "center" of battle (sort of) +void obj_get_average_ship_pos(vector *pos); + +// function to deal with firing player things like lasers, missiles, etc. +// separated out because of multiplayer issues. +void obj_player_fire_stuff( object *objp, control_info ci ); + +// Call this if you want to change an object flag so that the +// object code knows what's going on. For instance if you turn +// off OF_COLLIDES, the object code needs to know this in order to +// actually turn the object collision detection off. By calling +// this you shouldn't get Int3's in the checkobject code. If you +// do, then put code in here to correctly handle the case. +void obj_set_flags(object *obj, uint new_flags); + +// get the Ship_info flags for a given ship (if you have the object) +int obj_get_SIF(object *objp); +int obj_get_SIF(int obj); + +// get the team for any object +int obj_team(object *objp); + +void obj_move_all_pre(object *objp, float frametime); +void obj_move_all_post(object *objp, float frametime); + +void obj_move_call_physics(object *objp, float frametime); + +// multiplayer object update stuff begins ------------------------------------------- + +// do client-side pre-interpolation object movement +void obj_client_pre_interpolate(); + +// do client-side post-interpolation object movement +void obj_client_post_interpolate(); + +// move an observer object in multiplayer +void obj_observer_move(float flFrametime); + + +#endif + diff --git a/include/objectsnd.h b/include/objectsnd.h new file mode 100644 index 0000000..6f19ed7 --- /dev/null +++ b/include/objectsnd.h @@ -0,0 +1,89 @@ +/* + * $Logfile: /Freespace2/code/Object/ObjectSnd.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for managing object-linked persistant sounds + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 7/01/99 4:23p Dave + * Full support for multiple linked ambient engine sounds. Added "big + * damage" flag. + * + * 3 7/01/99 11:44a Dave + * Updated object sound system to allow multiple obj sounds per ship. + * Added hit-by-beam sound. Added killed by beam sound. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 11 3/17/98 5:55p Lawrance + * Support object-linked sounds for asteroids. + * + * 10 9/03/97 5:02p Lawrance + * add engine stuttering when a ship is dying + * + * 9 7/14/97 12:04a Lawrance + * make Obj_snd_enabled visible + * + * 8 6/09/97 11:50p Lawrance + * integrating DirectSound3D + * + * 7 6/06/97 4:13p Lawrance + * use an index instead of a pointer for object-linked sounds + * + * 6 6/05/97 1:07a Lawrance + * changes to support sound interface + * + * 5 6/02/97 1:50p Lawrance + * supporting integration with Direct3D + * + * 4 5/09/97 4:33p Lawrance + * doppler effects + * + * 3 5/09/97 9:41a Lawrance + * added #ifndef to avoid multiple inclusions + * + * 2 5/08/97 4:30p Lawrance + * split off object sound stuff into separate file + * + * $NoKeywords: $ + */ + +#ifndef __OBJECTSND_H__ +#define __OBJECTSND_H__ + +#define OS_USED (1<<0) +#define OS_DS3D (1<<1) +#define OS_MAIN (1<<2) // "main" sound. attentuation does not apply until outside the radius of the object + +extern int Obj_snd_enabled; + +void obj_snd_level_init(); +void obj_snd_level_close(); +void obj_snd_do_frame(); + +// pos is the position of the sound source in the object's frame of reference. +// so, if the objp->pos was at the origin, the pos passed here would be the exact +// model coords of the location of the engine +// by passing vmd_zero_vector here, you get a sound centered directly on the object +// NOTE : if main is true, the attentuation factors don't apply if you're within the radius of the object +int obj_snd_assign(int objnum, int sndnum, vector *pos, int main); + +// if sndnum is not -1, deletes all instances of the given sound within the object +void obj_snd_delete(int objnum, int sndnum = -1); + +void obj_snd_delete_all(); +void obj_snd_stop_all(); +int obj_snd_is_playing(int index); +int obj_snd_return_instance(int index); + +#endif + diff --git a/include/objecttree.h b/include/objecttree.h new file mode 100644 index 0000000..d14f868 --- /dev/null +++ b/include/objecttree.h @@ -0,0 +1,66 @@ +// ObjectTree.h : header file +// +#ifndef _OBJECTTREE_H +#define _OBJECTTREE_H + +#include "model.h" + +///////////////////////////////////////////////////////////////////////////// +// CObjectTree dialog + +class CObjectTree : public CDialog +{ +// Construction +public: + char m_title[128]; + + void MyCreate(void * pv); + void add_model(polymodel *pm, int submodel, HTREEITEM parent ); + void ExpandAll(); + void CollapseAll(); + + CObjectTree(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CObjectTree) + enum { IDD = IDD_DIALOG1 }; + CStatic m_txt_pof_info; + CStatic m_txt_detail6; + CStatic m_txt_detail5; + CStatic m_txt_detail4; + CStatic m_txt_detail3; + CStatic m_txt_detail1; + CStatic m_txt_detail2; + CStatic m_txt_bspgen_version; + CStatic m_txt_movement_axis; + CStatic m_txt_movement_type; + CStatic m_txt_name; + CStatic m_txt_num_polies; + CStatic m_txt_num_verts; + CTreeCtrl m_tree; + //}}AFX_DATA + + + void * m_pv; + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CObjectTree) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CObjectTree) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#endif + diff --git a/include/observer.h b/include/observer.h new file mode 100644 index 0000000..60ff3c3 --- /dev/null +++ b/include/observer.h @@ -0,0 +1,43 @@ +/* + * $Logfile: /Freespace2/code/Observer/Observer.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $NoKeywords: $ + */ + +#ifndef _OBSERVER_HEADER_FILE +#define _OBSERVER_HEADER_FILE + +#include "object.h" +#include "vecmat.h" + +#define OBS_MAX_VEL_X (85.0f) // side to side +#define OBS_MAX_VEL_Y (85.0f) // side to side +#define OBS_MAX_VEL_Z (85.0f) // forwards and backwards + + +#define OBS_FLAG_USED (1<<1) + +typedef struct observer { + int objnum; + + int target_objnum; // not used as of yet + int flags; +} observer; + +#define MAX_OBSERVER_OBS 17 +extern observer Observers[MAX_OBSERVER_OBS]; + +extern int Num_observer_obs; + +void observer_init(); +int observer_create(matrix *orient, vector *pos); // returns objnum +void observer_delete(object *obj); + +// get the eye position and orientation for the passed observer object +void observer_get_eye(vector *eye_pos, matrix *eye_orient, object *obj); + +#endif + diff --git a/include/operatorargtypeselect.h b/include/operatorargtypeselect.h new file mode 100644 index 0000000..605ce2d --- /dev/null +++ b/include/operatorargtypeselect.h @@ -0,0 +1,67 @@ +/* + * $Logfile: /Freespace2/code/FRED2/OperatorArgTypeSelect.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Dialog box handling code for selecting an argument return type of an SEXP. + * Changes to SEXPs made this no longer needed, but just in case more changes + * cause it to be needed again, it's still around. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 3 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// OperatorArgTypeSelect dialog + +class OperatorArgTypeSelect : public CDialog +{ +// Construction +public: + OperatorArgTypeSelect(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(OperatorArgTypeSelect) + enum { IDD = IDD_OPERATOR_ARGUMENT_TYPES }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(OperatorArgTypeSelect) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(OperatorArgTypeSelect) + afx_msg void OnBoolean(); + afx_msg void OnNumbers(); + afx_msg void OnShips(); + afx_msg void OnWings(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/optionsmenu.h b/include/optionsmenu.h new file mode 100644 index 0000000..c9490e1 --- /dev/null +++ b/include/optionsmenu.h @@ -0,0 +1,87 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/OptionsMenu.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for code that controls the Options menu + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 6/25/99 11:59a Dave + * Multi options screen. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 10 1/28/98 6:21p Dave + * Made the standalone use ~8 megs less memory. Fixed multiplayer submenu + * endgame problem. + * + * 9 12/27/97 8:07p Lawrance + * remove old code + * + * 8 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 7 1/09/97 12:57p Lawrance + * supporting a new state where the player picks to either save or restore + * + * 6 11/21/96 7:14p Lawrance + * converted menu code to use a file (menu.tbl) to get the data for the + * menu + * + * 5 11/13/96 4:02p Lawrance + * complete over-haul of the menu system and the states associated with + * them + * + * 4 11/13/96 8:32a Lawrance + * streamlined menu code + * + * 3 11/06/96 8:54a Lawrance + * added revision templates, made more efficient + * + * $NoKeywords: $ + * +*/ + +#ifndef _OPTIONSMENU_H +#define _OPTIONSMENU_H + +#include "ui.h" + +struct op_sliders { + // base slider + char *filename; + int x, y, xt, yt; + int hotspot; + int dot_w; + int dots; + + // left and right buttons + char *left_filename; + int left_mask, left_x, left_y; + char *right_filename; + int right_mask, right_x, right_y; + + // slider control + UI_DOT_SLIDER_NEW slider; // because we have a class inside this struct, we need the constructor below.. + + op_sliders(char *name, int x1, int y1, int xt1, int yt1, int h, int _dot_w, int _dots, char *_left_filename, int _left_mask, int _left_x, int _left_y, char *_right_filename, int _right_mask, int _right_x, int _right_y) : + filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h), dot_w(_dot_w), dots(_dots), left_filename(_left_filename), left_mask(_left_mask), left_x(_left_x), left_y(_left_y), right_filename(_right_filename), right_mask(_right_mask), right_x(_right_x), right_y(_right_y) {} +}; + +void options_menu_init(); +void options_menu_close(); +void options_menu_do_frame(float frametime); + +// kill the options menu +void options_cancel_exit(); + +#endif + diff --git a/include/optionsmenumulti.h b/include/optionsmenumulti.h new file mode 100644 index 0000000..38c8f00 --- /dev/null +++ b/include/optionsmenumulti.h @@ -0,0 +1,70 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/OptionsMenuMulti.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 3 5/06/98 8:06p Dave + * Made standalone reset properly under weird conditions. Tweak + * optionsmulti screen. Upped MAX_WEAPONS to 350. Put in new launch + * countdown anim. Minro ui fixes/tweaks. + * + * 2 4/17/98 5:27p Dave + * More work on the multi options screen. Fixed many minor ui todo bugs. + * + * 1 4/16/98 11:39p Dave + * + * + * $NoKeywords: $ + */ + +#ifndef _OPTIONS_MENU_MULTI_HEADER_FILE +#define _OPTIONS_MENU_MULTI_HEADER_FILE + +// This file is basically just a sister module to the OptionsMenu file + +class UI_WINDOW; + +// called when the options screen is initialized, pass in the UI window +void options_multi_init(UI_WINDOW *options_window); + +// do frame for the multi options screen +void options_multi_do(int key); + +// called when the entire options screen is closed (note - does not do any settings updates. this is purely for ui shutdown) +void options_multi_close(); + +// called if the accept button on the main options screen was hit +void options_multi_accept(); + +// called when the multiplayer tab is hit - initializes/switches all necessary data. +// NOTE : this is different from the initialization function, which is called only when the options menu is started +void options_multi_select(); + +// called when the multiplayer tab has been switched from +void options_multi_unselect(); + +// return the bitmap handle of the current background bitmap, or -1 if the multiplayer tab is not active +int options_multi_background_bitmap(); + +// set voice sound buffer for display +void options_multi_set_voice_data(unsigned char *sound_buf,int buf_size,unsigned char *comp_buf, int comp_size, int uncomp_size, double gain); + +// process and blit any voice waveform if necessary +void options_multi_vox_process_waveform(); + +// return whether we want to eat a tabbed keypress +int options_multi_eat_tab(); + +#endif + diff --git a/include/orienteditor.h b/include/orienteditor.h new file mode 100644 index 0000000..4afa17f --- /dev/null +++ b/include/orienteditor.h @@ -0,0 +1,100 @@ +/* + * $Logfile: /Freespace2/code/FRED2/OrientEditor.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Object orientation editor (or just object editor) dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 8 5/20/97 2:28p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 7 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 6 3/12/97 4:33p Hoffoss + * added spin controls to orient editor, light intensity level can be + * specified in BG editor. + * + * 5 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 4 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "object.h" + +///////////////////////////////////////////////////////////////////////////// +// orient_editor dialog + +class orient_editor : public CDialog +{ +// Construction +public: + int query_modified(); + void OnCancel(); + void OnOK(); + orient_editor(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(orient_editor) + enum { IDD = IDD_ORIENT_EDITOR }; + CSpinButtonCtrl m_spin6; + CSpinButtonCtrl m_spin5; + CSpinButtonCtrl m_spin4; + CSpinButtonCtrl m_spin3; + CSpinButtonCtrl m_spin2; + CSpinButtonCtrl m_spin1; + int m_object_index; + BOOL m_point_to; + CString m_position_z; + CString m_position_y; + CString m_position_x; + CString m_location_x; + CString m_location_y; + CString m_location_z; + //}}AFX_DATA + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(orient_editor) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(orient_editor) + virtual BOOL OnInitDialog(); + afx_msg void OnClose(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +private: + float convert(CString &str); + int total; + int index[MAX_OBJECTS]; + void update_object(object *ptr); +}; + diff --git a/include/osapi.h b/include/osapi.h new file mode 100644 index 0000000..a0b231b --- /dev/null +++ b/include/osapi.h @@ -0,0 +1,67 @@ +#ifndef _OSAPI_H +#define _OSAPI_H + +#include +// -------------------------------------------------------------------------------------------------- +// OSAPI DEFINES/VARS +// + +// set if running under MsDev - done after os_init(...) has returned +extern int Os_debugger_running; + +// game-wide +// #define THREADED + +#ifdef THREADED + #define ENTER_CRITICAL_SECTION(csc) do { EnterCriticalSection(csc); } while(0); + #define LEAVE_CRITICAL_SECTION(csc) do { LeaveCriticalSection(csc); } while(0); +#else + #define ENTER_CRITICAL_SECTION(csc) do { } while(0); + #define LEAVE_CRITICAL_SECTION(csc) do { } while(0); +#endif + +// -------------------------------------------------------------------------------------------------- +// OSAPI FUNCTIONS +// + +// initialization/shutdown functions ----------------------------------------------- + +// If app_name is NULL or ommited, then TITLE is used +// for the app name, which is where registry keys are stored. +void os_init(char * wclass, char * title, char *app_name=NULL, char *version_string=NULL ); + +// set the main window title +void os_set_title( char * title ); + +// call at program end +void os_cleanup(); + + +// window management --------------------------------------------------------------- + +// toggle window size between full screen and windowed +void os_toggle_fullscreen(); + +// Returns 1 if app is not the foreground app. +int os_foreground(); + +// Returns the handle to the main window +uint os_get_window(); + + +// process management -------------------------------------------------------------- + +// call to process windows messages. only does something in non THREADED mode +void os_poll(); + +// Sleeps for n milliseconds or until app becomes active. +void os_sleep(int ms); + +// Used to stop message processing +void os_suspend(); + +// resume message processing +void os_resume(); + +#endif + diff --git a/include/osregistry.h b/include/osregistry.h new file mode 100644 index 0000000..82f19f3 --- /dev/null +++ b/include/osregistry.h @@ -0,0 +1,53 @@ +#ifndef _FS2_REGISTRY_HEADER_FILE +#define _FS2_REGISTRY_HEADER_FILE + +// ------------------------------------------------------------------------------------------------------------ +// REGISTRY DEFINES/VARS +// + +// exectuable defines +extern char *Osreg_company_name; +extern char *Osreg_class_name; +extern char *Osreg_app_name; +extern char *Osreg_title; + + +// ------------------------------------------------------------------------------------------------------------ +// REGISTRY FUNCTIONS +// + + +// initialize the registry. setup default keys to use +void os_init_registry_stuff(char *company, char *app, char *version); + +// Removes a value from to the INI file. Passing +// name=NULL will delete the section. +void os_config_remove( char *section, char *name ); + +// Writes a string to the registry +void os_config_write_string( char *section, char *name, char *value ); + +// same as previous function except we don't use the application name to build up the keyname +void os_config_write_string2( char *section, char *name, char *value ); + +// Writes an unsigned int to the INI file. +void os_config_write_uint( char *section, char *name, unsigned int value ); + +// Reads a string from the INI file. If default is passed, +// and the string isn't found, returns ptr to default otherwise +// returns NULL; Copy the return value somewhere before +// calling os_read_string again, because it might reuse the +// same buffer. +char * os_config_read_string( char *section, char *name, char *default_value=NULL ); + +// same as previous function except we don't use the application name to build up the keyname +char * os_config_read_string2( char *section, char *name, char *default_value=NULL ); + +// Reads a string from the INI file. Default_value must +// be passed, and if 'name' isn't found, then returns default_value +unsigned int os_config_read_uint( char *section, char *name, unsigned int default_value ); + +// uses Ex versions of Windows registry functions +char * os_config_read_string_ex( char *keyname, char *name, char *default_value ); + +#endif diff --git a/include/outwnd.h b/include/outwnd.h new file mode 100644 index 0000000..f24c362 --- /dev/null +++ b/include/outwnd.h @@ -0,0 +1,17 @@ +#ifndef _OUTWND_H +#define _OUTWND_H + +#define FILTER_NAME_LENGTH 30 + +void load_filter_info(void); +void outwnd_init(int display_under_freespace_window=0); +void outwnd_close(); +void outwnd_printf(char *id, char *format, ...); +void outwnd_printf2(char *format, ...); + +#ifndef NDEBUG + extern int Log_debug_output_to_file; +#endif + +#endif + diff --git a/include/packunpack.h b/include/packunpack.h new file mode 100644 index 0000000..ddf9a6d --- /dev/null +++ b/include/packunpack.h @@ -0,0 +1,242 @@ +/* + * $Logfile: /Freespace2/code/Anim/PackUnpack.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for handling packing and unpacking in Hoffoss's RLE format, used for + * Anim files. Also handles Anim loading, creating Anim instances (for + * utilizing an Anim), and getting getting frames of the Anim. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 7 7/16/99 1:49p Dave + * 8 bit aabitmaps. yay. + * + * 6 1/14/99 12:48a Dave + * Todo list bug fixes. Made a pass at putting briefing icons back into + * FRED. Sort of works :( + * + * 5 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 4 10/22/98 6:14p Dave + * Optimized some #includes in Anim folder. Put in the beginnings of + * parse/localization support for externalized strings and tstrings.tbl + * + * 3 10/16/98 3:42p Andsager + * increase MAX_WEAPONS and MAX_SHIPS and som header files + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 32 5/18/98 5:59p Hoffoss + * Made command briefing advanced now once the speech stops and animation + * has fully played once, whichever is longer. + * + * 31 5/07/98 3:11a Lawrance + * Implement custom streaming code + * + * 30 1/14/98 6:43p Lawrance + * Add ref_count to anim struct, so we don't free multiple times + * + * 29 11/19/97 8:28p Dave + * Hooked in Main Hall screen. Put in Anim support for ping ponging + * animations as well as general reversal of anim direction. + * + * 28 8/30/97 2:11p Lawrance + * allow animations to loop + * + * 27 8/25/97 11:13p Lawrance + * support framerate independent playback with the option of now advancing + * more than one frame at a time + * + * 26 8/21/97 5:11p Lawrance + * frame numbering for ANI's now is from 0 -> total_frames-1. + * + * 25 7/28/97 10:42p Lawrance + * re-did interface to unpack_frame() to make more general + * + * 24 7/21/97 11:41a Lawrance + * make playback time of .ani files keyed of frametime + * + * 23 7/20/97 6:57p Lawrance + * supporting new RLE format + * + * 22 6/27/97 4:36p Lawrance + * update pal translation table when gr_screen.signature changes + * + * 21 6/26/97 12:12a Lawrance + * supporting anti-aliased bitmap animations + * + * 20 6/25/97 3:03p Lawrance + * fix palette translation problem with anti-alised bitmaps + * + * 19 5/27/97 3:48p Lawrance + * don't re-create a bitmap if using the same frame of animation + * + * 18 5/21/97 11:06a Lawrance + * enabling a user-defined transparent value + * + * 17 5/19/97 3:21p Lawrance + * add fps parm, version num to anim header + * + * 16 5/19/97 2:28p Lawrance + * changes some variables to flags + * + * 15 5/15/97 4:42p Lawrance + * supporting animations in-game + * + * 14 3/01/97 2:08p Lawrance + * not using windows.h, since memory mapping details moved to cfile + * + * 13 2/28/97 12:17p Lawrance + * supporting mapping file to memory + * + * 12 2/25/97 11:06a Lawrance + * moved some higher level functions to from PackUnpack to AnimPlay + * + * 11 2/19/97 9:51p Lawrance + * made keyframe decompression more effecient, moved + * default anim FPS to header file + * + * 10 2/19/97 4:01p Lawrance + * load_anim returns int, not void + * + * 9 2/17/97 4:19p Lawrance + * changed stop and start to be actual frame numbers, not percentages + * + * 8 2/17/97 4:17p Hoffoss + * modified packing internal format and added random access function to an + * Anim frame. + * + * 7 2/17/97 2:59p Lawrance + * integrating into game + * + * 6 2/14/97 11:09p Hoffoss + * Made optimizations. + * + * 5 2/14/97 10:48p Hoffoss + * fixed bug. + * + * 4 2/14/97 10:38p Lawrance + * fixing bugs + * + * 3 2/14/97 3:29p Hoffoss + * Added header for MSDEV to fill in. + * + * $NoKeywords: $ + */ + +#ifndef __PACKUNPACK_H__ +#define __PACKUNPACK_H__ + +#include "pstypes.h" + +struct CFILE; + +#define ANI_STREAM_CACHE_SIZE 4096 + +#define PACKER_CODE 0xEE // Use'd by PACKING_METHOD_RLE +#define PACKING_METHOD_RLE 0 // Hoffoss's RLE format +#define PACKING_METHOD_RLE_KEY 1 // Hoffoss's key frame RLE format +#define PACKING_METHOD_STD_RLE 2 // Standard RLE format (high bit is count) +#define PACKING_METHOD_STD_RLE_KEY 3 // Standard RLE format key frame + +#define STD_RLE_CODE 0x80 + +typedef struct key_frame { + int frame_num; // which frame number this key frame is + int offset; // offset from the start of data block +} key_frame; + +#define ANF_MEM_MAPPED (1<<0) // animation is memory-mapped file +#define ANF_STREAMED (1<<1) +#define ANF_XPARENT (1<<2) // animation has transparency +#define ANF_ALL_KEYFRAMES (1<<3) // all the frames are keyframes (this is necessary if we want to play the file backwards) + +typedef struct anim { + anim *next; + char name[MAX_PATH_LEN]; + ubyte packer_code; + int width; + int height; + int total_frames; + int instance_count; // number of instances that are currently playing + int ref_count; // number of times this anim has been loaded + float time; // playback time in seconds + int num_keys; + key_frame *keys; + ubyte palette[768]; + ubyte palette_translation[256]; + ubyte *data; // points to compressed data + CFILE* cfile_ptr; + int version; + int fps; + ubyte xparent_r; // red component for the transparent color in source image + ubyte xparent_g; // green component for the transparent color in source image + ubyte xparent_b; // blue component for the transparent color in source image + int flags; + uint screen_sig; + int file_offset; // file offset to start of frame data + int cache_file_offset; + ubyte *cache; +} anim_t; + +// the direction to play the anim (forwards or backwards) +#define ANIM_DIRECT_FORWARD 0 +#define ANIM_DIRECT_REVERSE 1 + +typedef struct anim_instance { + anim_instance *next, *prev; + int x,y; // coordinates anim is played at (top left corner of anim) + vector *world_pos; // world (x,y,z) position of explosion + float radius; // radius of image, needed for scaling + int frame_num; // current frame, or last frame if between frames (first frame is 0) + int last_frame_num;// last frame rendered + anim *parent; // pointer to anim structure, which holds compressed data + ubyte *data; // pointer to next frame's compressed data + ubyte *frame; // uncompressed frame + float time_elapsed; // how long the anim has played for (in seconds) + int start_at; // frame anim playing should start + int stop_at; // frame anim playing should stop + int framerate_independent; // animation should play back in same amount of time, regardless + int skip_frames; // should anim skip frames during framerate independent playback + int looped; // should anim keep playing over and over... + int stop_now; // flag to indicate time to stop the animation + int last_bitmap; // id of last bitmap that was rendered out from animation + int screen_id; // 0 means all screens should render, otherwise screen specific + void *aa_color; // anti-aliased bitmap color + int xlate_pal; + int direction; // playing forwards or backwards ? + int ping_pong; // should be played ping-pong style + int paused; // pause the anim + int file_offset; // current offset into frame (like data, put offset into file) + int loop_count; // starts at 0, and is incremented each time it loops +} anim_instance_t; + +extern int packer_code; + +int pack_key_frame(ubyte *frame, ubyte *save, long size, long max, int compress_type); +int pack_frame(ubyte *frame, ubyte *frame2, ubyte *save, long size, long max, int compress_type); + +ubyte *unpack_frame(anim_instance *ai, ubyte *ptr, ubyte *frame, int size, ubyte *pal_translate, int aabitmap, int bpp); +int unpack_frame_from_file(anim_instance *ai, ubyte *frame, int size, ubyte *pal_translate, int aabitmap, int bpp); + +void anim_init(); +anim_instance *init_anim_instance(anim *ptr, int bpp); +void free_anim_instance(anim_instance *inst); +int anim_get_next_frame(anim_instance *inst); +int anim_get_frame(anim_instance *inst, int frame_num, int xlate_pal=1); +ubyte *anim_get_next_raw_buffer(anim_instance *inst, int xlate_pal, int aabitmap, int bpp); +void anim_set_palette(anim *a); +void anim_check_for_palette_change(anim_instance *inst); + + +#endif /* __PACKUNPACK_H__ */ + diff --git a/include/palman.h b/include/palman.h new file mode 100644 index 0000000..54f188e --- /dev/null +++ b/include/palman.h @@ -0,0 +1,108 @@ +/* + * $Logfile: /Freespace2/code/Palman/PalMan.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Palette Manager header file + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 3/31/99 8:24p Dave + * Beefed up all kinds of stuff, incluging beam weapons, nebula effects + * and background nebulae. Added per-ship non-dimming pixel colors. + * + * 3 2/05/99 12:52p Dave + * Fixed Glide nondarkening textures. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 14 5/20/98 9:46p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 13 12/02/97 4:00p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 12 11/21/97 11:32a John + * Added nebulas. Fixed some warpout bugs. + * + * 11 11/14/97 12:31p John + * Fixed some DirectX bugs. Moved the 8-16 xlat tables into Graphics + * libs. Made 16-bpp DirectX modes know what bitmap format they're in. + * + * 10 7/16/97 5:29p John + * added palette table caching and made scaler and liner no light tmapper + * do alpha blending in 8 bpp mode. + * + * 9 7/16/97 3:07p John + * + * 8 5/21/97 11:06a Lawrance + * added user_palette_find() + * + * 7 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 6 11/26/96 6:50p John + * Added some more hicolor primitives. Made windowed mode run as current + * bpp, if bpp is 8,16,or 32. + * + * 5 11/26/96 9:44a Allender + * allow for use of different bitmap palettes + * + * $NoKeywords: $ + */ + +#ifndef _PALMAN_H +#define _PALMAN_H + +#include "pstypes.h" + +// Calculate tables for this palette. +// Assumes gr_palette is filled in. +extern void palette_update(char *name, int restrict_colors_to_upper_128 ); + +// Writes current tables to disk. +extern void palette_flush(); + +// Functions to query a palette +extern uint palette_compute_checksum( ubyte *pal ); // computes checksum of palette +extern ubyte *palette_get_blend_table(float alpha); +extern ubyte *palette_get_fade_table(); + +extern uint palette_find( int r, int g, int b ); + +// Data used to query a palette +extern ubyte gr_palette[256*3]; +extern ubyte gr_fade_table[(256*34)*2]; +extern uint gr_palette_checksum; + +// Functions to deal with changing the palette. +// These just call gr_set_palette, which will in turn +// call palette_flush and palette_update. +extern void palette_load_table( char * filename ); +extern void palette_use_bm_palette(int n); +extern void palette_restore_palette( void ); + +// nondarkening texture pixel colors +#define MAX_NONDARK_COLORS 10 + +extern int Palman_num_nondarkening_default; +extern ubyte Palman_non_darkening_default[MAX_NONDARK_COLORS][3]; + +extern int Palman_num_nondarkening; +extern ubyte Palman_non_darkening[MAX_NONDARK_COLORS][3]; + +extern int palman_is_nondarkening(int r,int g, int b); +extern void palman_load_pixels(); +extern void palman_set_nondarkening(ubyte colors[MAX_NONDARK_COLORS][3], int size); + +#endif + diff --git a/include/parselo.h b/include/parselo.h new file mode 100644 index 0000000..608b6bc --- /dev/null +++ b/include/parselo.h @@ -0,0 +1,352 @@ +/* + * $Source$ + * $Revision$ + * $Author$ + * $Date$ + * + * Header for parselo.c + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 14 9/13/99 10:40a Mikeb + * Bumped up MISSION_TEXT_SIZE from 380000 to 390000 for ships.tbl ship + * descriptions. + * + * 13 8/02/99 4:19p Andsager + * Bump up MISSION_TEXT_SIZE to 380000 for ships.tbl + * + * 12 5/03/99 10:10a Davidg + * DA: Bump Mission_text_size to handle large ship.tbl + * + * 11 2/19/99 11:10a Jasen + * Upped MISSION_TEXT_SIZE to 300000 (for souper cap) + * + * 10 2/03/99 6:06p Dave + * Groundwork for FS2 PXO usertracker support. Gametracker support next. + * + * 9 1/07/99 1:52p Andsager + * Initial check in of Sexp_variables + * + * 8 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 7 10/26/98 9:42a Dave + * Early flak gun support. + * + * 6 10/22/98 6:14p Dave + * Optimized some #includes in Anim folder. Put in the beginnings of + * parse/localization support for externalized strings and tstrings.tbl + * + * 5 10/16/98 1:22p Andsager + * clean up header files + * + * 4 10/14/98 1:15p Andsager + * Fix fred + * + * 3 10/14/98 12:25p Andsager + * Cleaned up and removed unnecessary include files + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 54 6/10/98 6:47p Lawrance + * Increase allowed mission length to 512, to accommodate longer French + * text + * + * 53 5/20/98 2:27a Sandeep + * + * 52 4/30/98 4:53p John + * Restructured and cleaned up cfile code. Added capability to read off + * of CD-ROM drive and out of multiple pack files. + * + * 51 4/03/98 10:31a John + * Made briefing and debriefing arrays be malloc'd + * + * 50 3/30/98 6:22p Hoffoss + * Added a new parsing function. + * + * 49 3/29/98 12:55a Lawrance + * Get demo build working with limited set of data. + * + * 48 2/02/98 4:59p Hoffoss + * Added a promotion text field to rank.tbl and code to support it in + * FreeSpace. + * + * 47 11/03/97 10:11p Hoffoss + * Added new split_str_once() function. Works similar to split_str(), + * only it only splits a string into 2 lines. + * + * 46 10/29/97 9:02a Jasen + * Raised MISSION_TEXT_SIZE limit to 128000. + * + * 45 10/17/97 3:12p Hoffoss + * Added a whitespace elimination function, and utilized it in briefing + * icon label saving. + * + * 44 10/15/97 4:46p Lawrance + * add drop_leading_whitespace() function + * + * 43 10/08/97 4:48p Dave + * Moved file id function into missionparse (from parselo). Finished + * tracker logging in and out of games. Changed how file checksumming + * works. + * + * 42 10/07/97 5:08p Dave + * Added file versioning/checksumming for multiplayer situations. Began + * master tracker changes for finalized version. + * + * 41 10/01/97 5:07p Hoffoss + * Weapon loadout load and save from fsm files added. + * + * 40 9/22/97 5:47p Lawrance + * re-write split_str() to allow special control words that don't get + * considered as part of printable string + * + * 39 9/09/97 3:39p Sandeep + * warning level 4 bugs + * + * 38 8/17/97 12:47p Hoffoss + * Changed code so I can force missions to load from the missions + * directory regardless of it's extension. + * + * 37 8/13/97 10:54a Hoffoss + * Added function to help extract only parts of a mission file. + * + * 36 7/22/97 5:38p Jasen + * Fixed bug with sexp error checking code. Wasn't accounting for player + * names in ship name lookup. + * + * 35 6/23/97 12:02p Lawrance + * move split_str() here, support \n's. + * + * 34 6/11/97 6:14p Hoffoss + * Made messages able to be longer. + * + * 33 4/28/97 3:30p Hoffoss + * Changed mission saving to not rely on required strings being present in + * mission files being overwritten. + * + * 32 4/22/97 4:50p Hoffoss + * Added some flexibility to stuff_string(). + * + * 31 4/21/97 5:02p Hoffoss + * Player/player status editing supported, and both saved and loaded from + * Mission files. + * + * 30 3/09/97 2:23p Allender + * Major changes to player messaging system. Added messages.tbl. Made + * all currently player messages go through new system. Not done yet. + * + * 29 3/04/97 11:19a Lawrance + * added stuff_booleanI() that recognizes 1/0 and YES/NO + * + * 28 2/18/97 9:52a Adam + * Raised parsing text max size limit (Jason) + * + * 27 1/31/97 12:07p John + * Add turret gun type on end of $subsystem line. Changed match_and_find + * to return index rather than stuffing it. + * + * 26 1/30/97 5:15p Mike + * Skill levels. + * Better named AI classes. + * Optional override of AI class in fsm file. + * + * 25 1/29/97 2:59p Mike + * Table-driven support for John's laser rendering. + * Null hook for AVI weapons. + * + * 24 1/17/97 3:50p Hoffoss + * Fred bug fixes and slight improvements in order to be able to create + * mission 5. + * + * 23 12/12/96 3:06p Mike + * Fix bug in parsing code created by adding support for optional + * terminators in required_string. + * + * 22 12/12/96 2:35p Mike + * Subsystem support + * + * 21 12/11/96 3:31p Hoffoss + * Added support for more flexible ship cargos. + * + * 20 12/09/96 9:47a Hoffoss + * Fixed silly error. #define was in wrong file, so I moved it. + * + * 19 12/03/96 2:44p Mike + * Support for ship class and ship specific AI abilities: evasion, + * courage, patience, accuracy + * + * 18 11/15/96 1:43p Hoffoss + * Improvements to the Ship Dialog editor window. It is now an + * independant window that updates data correctly. + * + * 17 11/07/96 1:56p Allender + * externed function for use in sexp.cpp + * + * 16 10/30/96 9:05a Hoffoss + * Expanded mission file max size to 32k + * + * 15 10/29/96 3:28p Allender + * added stuff_string_list function. + * + * 14 10/28/96 12:18p Allender + * added a couple of functions to deal with getting and moving past + * strings on a whitespace only basis (instead ot to EOL) + * + * 13 10/23/96 11:06a Mike + * New weapon system. + * Revision 1.4 1996/09/24 13:50:41 mike + * Clean up some low level code in parselo.c. + * Add support for Player info in mission.txt. + * + * Revision 1.3 1996/09/23 20:42:03 allender + * added filename as parameter to read_mission_text + * + * Revision 1.2 1996/09/23 17:18:39 mike + * Split parse.h into parse.h and parselo.h + * + * Revision 1.1 1996/09/23 17:11:36 mike + * Initial revision + * + * + */ + +#ifndef _PARSELO_H +#define _PARSELO_H + +#include +#include "cfile.h" + +#define MISSION_TEXT_SIZE 390000 + +extern char Mission_text[MISSION_TEXT_SIZE]; +extern char Mission_text_raw[MISSION_TEXT_SIZE]; +extern char *Mp; +extern char *token_found; +extern int fred_parse_flag; +extern int Token_found_flag; +extern jmp_buf parse_abort; + + +#define COMMENT_CHAR (char)';' +#define EOF_CHAR (char)-128 +#define EOLN (char)0x0a + +#define F_NAME 1 +#define F_DATE 2 +#define F_NOTES 3 +#define F_FILESPEC 4 +#define F_MULTITEXTOLD 5 // needed for backwards compatability with old briefing format +#define F_SEXP 6 +#define F_PATHNAME 7 +#define F_SHIPCHOICE 8 +#define F_MESSAGE 9 // this is now obsolete for mission messages - all messages in missions should now use $MessageNew and stuff strings as F_MULTITEXT +#define F_MULTITEXT 10 + +#define PATHNAME_LENGTH 192 +#define NAME_LENGTH 32 +#define SEXP_LENGTH 128 +#define DATE_LENGTH 32 +#define TIME_LENGTH 16 +#define DATE_TIME_LENGTH 48 +#define NOTES_LENGTH 1024 +#define MULTITEXT_LENGTH 1024 +#define FILESPEC_LENGTH 64 +#define MESSAGE_LENGTH 512 + +#define SHIP_TYPE 0 // used to identify which kind of array to do a search for a name in +#define SHIP_INFO_TYPE 1 +#define WEAPON_LIST_TYPE 2 // to parse an int_list of weapons +#define RAW_INTEGER_TYPE 3 // to parse a list of integers +#define WEAPON_POOL_TYPE 4 + +#define SEXP_SAVE_MODE 1 +#define SEXP_ERROR_CHECK_MODE 2 + +// white space +extern int is_white_space(char ch); +extern void ignore_white_space(); +extern void drop_trailing_white_space(char *str); +extern void drop_leading_white_space(char *str); +extern char *drop_white_space(char *str); + +// gray space +void ignore_gray_space(); + +// error +extern int get_line_num(); +extern char *next_tokens(); +extern void diag_printf(char *format, ...); +extern void error_display(int error_level, char *format, ...); + +// skip +extern int skip_to_string(char *pstr, char *end = NULL); +extern int skip_to_start_of_strings(char *pstr1, char *pstr2); +extern void advance_to_eoln(char *terminators); +extern void skip_token(); + +// required +extern int required_string(char *pstr); +extern int optional_string(char *pstr); +extern int required_string_either(char *str1, char *str2); +extern int required_string_3(char *str1, char *str2, char *str3); + +// stuff +extern void copy_to_eoln(char *outstr, char *more_terminators, char *instr, int max); +extern void copy_text_until(char *outstr, char *instr, char *endstr, int max_chars); +extern void stuff_string_white(char *pstr); +extern void stuff_string(char *pstr, int type, char *terminators, int len = 0); +extern void stuff_string_line(char *pstr, int len); + +// Exactly the same as stuff string only Malloc's the buffer. +// Supports various FreeSpace primitive types. If 'len' is supplied, it will override +// the default string length if using the F_NAME case. +char *stuff_and_malloc_string( int type, char *terminators, int len); +extern void stuff_float(float *f); +extern void stuff_int(int *i); +extern void stuff_byte(ubyte *i); +extern int stuff_string_list(char slp[][NAME_LENGTH], int max_strings); +extern int stuff_int_list(int *ilp, int max_ints, int lookup_type); +extern int stuff_vector_list(vector *vlp, int max_vecs); +extern void stuff_vector(vector *vp); +extern void stuff_matrix(matrix *mp); +extern int string_lookup(char *str1, char *strlist[], int max, char *description = NULL, int say_errors = 0); +extern void find_and_stuff(char *id, int *addr, int f_type, char *strlist[], int max, char *description); +extern int match_and_stuff(int f_type, char *strlist[], int max, char *description); +extern void find_and_stuff_or_add(char *id, int *addr, int f_type, char *strlist[], int *total, + int max, char *description); +extern int get_string(char *str); +extern void stuff_parenthesized_vector(vector *vp); +void stuff_boolean(int *i); +int check_for_string(char *pstr); +int check_for_string_raw(char *pstr); + +// general +extern void init_parse(); +extern void reset_parse(); +extern void display_parse_diagnostics(); +extern void parse_main(); + +// utility +extern void mark_int_list(int *ilp, int max_ints, int lookup_type); +extern void compact_multitext_string(char *str); +extern void read_file_text(char *filename, int mode = CF_TYPE_ANY ); +extern void debug_show_mission_text(); +extern void convert_sexp_to_string(int cur_node, char *outstr, int mode); +char *split_str_once(char *src, int max_pixel_w); +int split_str(char *src, int max_pixel_w, int *n_chars, char **p_str, int max_lines, char ignore_char = -1); + +// fred +extern int required_string_fred(char *pstr, char *end = NULL); +extern int required_string_either_fred(char *str1, char *str2); +extern int optional_string_fred(char *pstr, char *end = NULL, char *end2 = NULL); +#endif + diff --git a/include/particle.h b/include/particle.h new file mode 100644 index 0000000..1bdbb3c --- /dev/null +++ b/include/particle.h @@ -0,0 +1,144 @@ +/* + * $Logfile: /Freespace2/code/Particle/Particle.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Includes for particle system + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 7 7/21/99 8:10p Dave + * First run of supernova effect. + * + * 6 1/29/99 12:47a Dave + * Put in sounds for beam weapon. A bunch of interface screens (tech + * database stuff). + * + * 5 1/27/99 9:56a Dave + * Temporary checkin of beam weapons for Dan to make cool sounds. + * + * 4 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 3 1/21/99 2:06p Dave + * Final checkin for multiplayer testing. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 6 5/13/98 3:25p John + * Added code to make explosion impacts not get removed by other + * particles. + * + * 5 5/11/98 10:06a John + * Added new particle for Adam + * + * 4 4/30/98 11:31a Andsager + * Added particles to big ship explosions. Modified particle_emit() to + * take optional range to increase range at which pariticles are created. + * + * 3 1/29/98 11:48a John + * Added new counter measure rendering as model code. Made weapons be + * able to have impact explosion. + * + * 2 1/02/98 5:04p John + * Several explosion related changes. Made fireballs not be used as + * ani's. Made ship spark system expell particles. Took away impact + * explosion for weapon hitting ship... this needs to get added to weapon + * info and makes shield hit more obvious. Only make sparks when hit + * hull, not shields. + * + * 1 12/23/97 8:26a John + * + * $NoKeywords: $ + */ + +#ifndef _PARTICLE_H +#define _PARTICLE_H + +//============================================================================ +//==================== PARTICLE SYSTEM GAME SEQUENCING CODE ================== +//============================================================================ + +// Resets particle system. Call between levels. +void particle_init(); + +// Moves the particles for each frame +void particle_move_all(float frametime); + +// Renders all the particles +void particle_render_all(); + +// kill all active particles +void particle_kill_all(); + + +//============================================================================ +//=============== LOW-LEVEL SINGLE PARTICLE CREATION CODE ==================== +//============================================================================ + +// The different types of particles... +#define PARTICLE_DEBUG 0 // A red sphere, no optional data required +#define PARTICLE_BITMAP 1 // A bitmap, optional data is the bitmap number. If bitmap is an animation, + // lifetime is calculated by the number of frames and fps. +#define PARTICLE_FIRE 2 // The vclip used for explosions, optional means nothing +#define PARTICLE_SMOKE 3 // The vclip used for smoke, optional means nothing +#define PARTICLE_SMOKE2 4 // The vclip used for smoke, optional means nothing +#define PARTICLE_BITMAP_PERSISTENT 5 // A bitmap, optional data is the bitmap number. If bitmap is an animation, + // lifetime is calculated by the number of frames and fps. + +// particle creation stuff +typedef struct particle_info { + // old-style particle info + vector pos; + vector vel; + float lifetime; + float rad; + int type; + uint optional_data; + + // new-style particle info + float tracer_length; + short attached_objnum; // if these are set, the pos is relative to the pos of the origin of the attached object + int attached_sig; // to make sure the object hasn't changed or died. velocity is ignored in this case + ubyte reverse; // play any animations in reverse +} particle_info; + +// Creates a single particle. See the PARTICLE_?? defines for types. +void particle_create( particle_info *pinfo ); +void particle_create( vector *pos, vector *vel, float lifetime, float rad, int type, uint optional_data = 0 ); + + +//============================================================================ +//============== HIGH-LEVEL PARTICLE SYSTEM CREATION CODE ==================== +//============================================================================ + +// Use a structure rather than pass a ton of parameters to particle_emit +typedef struct particle_emitter { + int num_low; // Lowest number of particles to create + int num_high; // Highest number of particles to create + vector pos; // Where the particles emit from + vector vel; // Initial velocity of all the particles + float min_life; // How long the particles live + float max_life; // How long the particles live + vector normal; // What normal the particle emit arond + float normal_variance; // How close they stick to that normal 0=good, 1=360 degree + float min_vel; // How fast the slowest particle can move + float max_vel; // How fast the fastest particle can move + float min_rad; // Min radius + float max_rad; // Max radius +} particle_emitter; + +// Creates a bunch of particles. You pass a structure +// rather than a bunch of parameters. +void particle_emit( particle_emitter *pe, int type, uint optional_data, float range=1.0 ); + +#endif // _PARTICLE_H + diff --git a/include/pcxutils.h b/include/pcxutils.h new file mode 100644 index 0000000..168729e --- /dev/null +++ b/include/pcxutils.h @@ -0,0 +1,89 @@ +/* + * $Logfile: /Freespace2/code/PcxUtils/pcxutils.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * header file for PCX utilities + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 8/10/99 6:54p Dave + * Mad optimizations. Added paging to the nebula effect. + * + * 5 2/05/99 12:52p Dave + * Fixed Glide nondarkening textures. + * + * 4 12/01/98 4:46p Dave + * Put in targa bitmap support (16 bit). + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 7 9/03/97 4:32p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 6 11/26/96 9:28a Allender + * get palette info when getting pcx info + * + * 5 11/18/96 12:36p John + * Added code to dump screen to a PCX file. + * + * 4 11/13/96 4:51p Allender + * started overhaul of bitmap manager. bm_load no longer actually load + * the data, only the info for the bitmap. Locking the bitmap now forces + * load when no data present (or will if bpp changes) + * + * $NoKeywords: $ + */ + +#ifndef _PCXUTILS_H +#define _PCXUTILS_H + +#include "pstypes.h" + +/* +#ifdef __cplusplus +extern "C" { +#endif +*/ + +#define PCX_ERROR_NONE 0 +#define PCX_ERROR_OPENING 1 +#define PCX_ERROR_NO_HEADER 2 +#define PCX_ERROR_WRONG_VERSION 3 +#define PCX_ERROR_READING 4 +#define PCX_ERROR_NO_PALETTE 5 +#define PCX_ERROR_WRITING 6 +#define PCX_ERROR_MEMORY 7 + +extern int pcx_read_header(char *filename, int *w, int *h, ubyte *pal ); +extern int pcx_read_bitmap_8bpp( char * filename, ubyte *org_data, ubyte *palette ); +extern int pcx_read_bitmap_16bpp( char * filename, ubyte *org_data ); +extern int pcx_read_bitmap_16bpp_aabitmap( char *filename, ubyte *org_data ); +extern int pcx_read_bitmap_16bpp_nondark( char *filename, ubyte *org_data ); + + +// Dumps a 8bpp bitmap to a file. +// Set rowoff to -w for upside down bitmaps. +extern int pcx_write_bitmap( char * filename, int w, int h, ubyte ** row_ptrs, ubyte * palette ); + + +/* +#ifdef __cplusplus +} +#endif +*/ + +#endif + diff --git a/include/physics.h b/include/physics.h new file mode 100644 index 0000000..f6e330f --- /dev/null +++ b/include/physics.h @@ -0,0 +1,268 @@ +/* + * $Logfile: /Freespace2/code/Physics/Physics.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Clues to the meaning of life on Shivan planet Sphlighesphlaightseh + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 8/13/99 10:49a Andsager + * Knossos and HUGE ship warp out. HUGE ship warp in. Stealth search + * modes dont collide big ships. + * + * 5 7/03/99 5:50p Dave + * Make rotated bitmaps draw properly in padlock views. + * + * 4 5/11/99 10:16p Andsager + * First pass on engine wash effect. Rotation (control input), damage, + * shake. + * + * 3 3/19/99 9:51a Dave + * Checkin to repair massive source safe crash. Also added support for + * pof-style nebulae, and some new weapons code. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 49 6/30/98 2:23p Dave + * Revised object update system. Removed updates for all weapons. Put + * button info back into control info packet. + * + * 48 3/12/98 5:21p Andsager + * Optimize physics for lasers and dumbfire missiles + * + * 47 2/03/98 6:01p Andsager + * Fix excessive rotvel in debris_create. Check using physics function + * check_rotvel_limit. + * + * 46 12/29/97 5:10p Allender + * fixed problems with speed not being reported properly in multiplayer + * games. Made read_flying_controls take frametime as a parameter. More + * ship/weapon select stuff + * + * 45 12/02/97 11:17p Andsager + * Added reduced_damp_decay timestamp. + * + * 44 11/17/97 5:15p Andsager + * + * 43 9/09/97 10:14p Andsager + * + * 42 9/04/97 5:09p Andsager + * implement physics using moment of inertia and mass (from BSPgen). + * Added to phys_info struct. Updated ship_info, polymodel structs. + * Updated weapon ($Mass and $Force) and ship ($Mass -> $Density) tables + * + * 41 8/29/97 10:13a Allender + * work on server/client prediction code -- doesn't work too well. Made + * all clients simulate their own orientation with the server giving + * corrections every so often. + * + * 40 8/19/97 9:56a John + * new thruster effect fairly functional + * + * 39 8/11/97 9:50a Allender + * fixed afterburner snafu + * + * 38 8/08/97 9:59a Allender + * added afterburner code into multiplayer. Required adding a new physics + * info flag to indicate afterburners ready to fire. (needed for + * multiplayer). Removed some now unused variables in afterburner.cpp + * + * 37 7/31/97 12:44p Andsager + * + * 36 7/27/97 5:14p Lawrance + * add afterburners to the player control info + * + * 35 7/25/97 9:07a Andsager + * modified apply_whack + * + * 34 7/23/97 5:10p Andsager + * Enhanced shockwave effect with shake based on blast force. + * + * 33 7/22/97 2:40p Andsager + * shockwaves now cause proper rotation of ships + * + * 32 7/21/97 4:12p Mike + * Two ships move as one while docked, including physics whacks. Spin of + * objects when killed based on speed. + * + * 31 7/16/97 11:53a Andsager + * + * 30 7/15/97 12:03p Andsager + * New physics stuff + * + * 29 7/11/97 11:54a John + * added rotated 3d bitmaps. + * + * 28 6/26/97 12:37p Allender + * added debug laser back in + * + * 27 5/08/97 11:42a Allender + * re-ordered game loop (again). Player weapons now created in simulation + * loop of code, not immediately. + * + * 26 4/17/97 10:02a Lawrance + * allow ship shaking for ABURN_DECAY_TIME after afterburners cut out + * + * 25 4/16/97 10:48p Mike + * Afterburner shake. + * Made afterburner engagement a bit in physics flags, removed from ship + * flags, removed a parameter to physics_read_flying_controls(). + * + * 24 4/10/97 3:20p Mike + * Change hull damage to be like shields. + * + * 23 3/04/97 3:10p Mike + * Intermediate checkin: Had to resolve some build errors. Working on two + * docked ships moving as one. + * + * $NoKeywords: $ + */ + +#ifndef _PHYSICS_H +#define _PHYSICS_H + +#include "vecmat.h" + + +#define PF_ACCELERATES (1 << 1) +#define PF_USE_VEL (1 << 2) // Use velocity present in physics_info struct, don't call physics_sim_vel. +#define PF_AFTERBURNER_ON (1 << 3) // Afterburner currently engaged. +#define PF_SLIDE_ENABLED (1 << 4) // Allow descent style sliding +#define PF_REDUCED_DAMP (1 << 5) // Allows reduced damping on z (for death, shockwave) (CAN be reset in physics) +#define PF_IN_SHOCKWAVE (1 << 6) // Indicates whether object has recently been hit by shockwave (used to enable shake) +#define PF_DEAD_DAMP (1 << 7) // Makes forward damping same as sideways (NOT reset in physics) +#define PF_AFTERBURNER_WAIT (1 << 8) // true when afterburner cannot be used. replaces variable used in afterburner code +#define PF_CONST_VEL (1 << 9) // Use velocity in phys_info struct. Optimize weapons in phys_sim +#define PF_WARP_IN (1 << 10) // Use when ship is warping in +#define PF_SPECIAL_WARP_IN (1 << 11) // Use when ship is warping in and we want to slow the ship faster than normal game physics +#define PF_WARP_OUT (1 << 12) // Use when ship is warping out +#define PF_SPECIAL_WARP_OUT (1 << 13) // Use when ship is warping out and we want to slow the ship faster than normal game physics + +//information for physics sim for an object +typedef struct physics_info { + uint flags; //misc physics flags + + float mass; //the mass of this object + + matrix I_body_inv; // inverse moment of inertia tensor (used to calculate rotational effects) + + float rotdamp; //rotational velocity damping + float side_slip_time_const; // time const for achieving desired velocity in the local sideways direction + // value should be zero for no sideslip and increase depending on desired slip + + vector max_vel; //maximum foward velocity in x,y,z + vector afterburner_max_vel; // maximum foward velocity in x,y,z while afterburner engaged + vector max_rotvel; //maximum p,b,h rotational velocity + float max_rear_vel; //maximum velocity in the backwards Z direction + + // Acceleration rates. Only used if flag PF_ACCELERATES is set + // starting from rest time to reach .50 v_max 0.69 time const + // time to reach .75 v_max 1.39 time const + // + float forward_accel_time_const; // forward acceleration time const + float afterburner_forward_accel_time_const; // forward acceleration time const while afterburner engaged + float forward_decel_time_const; // forward deceleration time const + float slide_accel_time_const; // slide acceleration time const + float slide_decel_time_const; // slide deceleration time const + float shockwave_shake_amp; // amplitude of shockwave shake at onset + + // These get changed by the control code. The physics uses these + // as input values when doing physics. + vector prev_ramp_vel; // follows the user's desired velocity + vector desired_vel; // in world coord + vector desired_rotvel; // in local coord + float forward_thrust; // How much the forward thruster is applied. 0-1. + + // Data that changes each frame. Physics fills these in each frame. + vector vel; // The current velocity vector of this object + vector rotvel; // The current rotational velecity (angles) + float speed; // Yes, this can be derived from velocity, but that's expensive! + float fspeed; // Speed in the forward direction. + float heading; + vector prev_fvec; // Used in AI for momentum. + matrix last_rotmat; // Used for moving two objects together and for editor. + + int afterburner_decay; // timestamp used to control how long ship shakes after afterburner released + int shockwave_decay; // timestamp used to control how long ship affected after hit by shockwave + int reduced_damp_decay; // timestamp used to control how long ship ship has reduced damp physics +} physics_info; + +// All of these are numbers from -1.0 to 1.0 indicating +// what percent of full velocity you want to go. +typedef struct control_info { + float pitch; // -1.0 to 1.0 + float vertical; + float heading; + float sideways; + float bank; + float forward; + float forward_cruise_percent; // percentage used for forward cruising + // This is a special case from -100 to 100 + + // below is information that are used by the player controls for firing information. + int fire_primary_count; + int fire_secondary_count; + int fire_countermeasure_count; + int fire_debug_count; // should this be around an NDEBUG #if/#endif? + + // afterburner control information + int afterburner_start; + int afterburner_stop; + +} control_info; + +extern int physics_paused; // Set means don't do physics, except for player. + +// To use the "Descent-ship" physics: +// controls_read_all(&ci, FrameSecs ); +// physics_read_flying_controls( &ViewerOrient, &ViewerPhysics, FrameSecs, &ci ); +// physics_sim(&ViewerPos, &ViewerOrient, &ViewerPhysics, FrameSecs ); +extern void physics_init( physics_info * pi ); +extern void physics_read_flying_controls( matrix * orient, physics_info * pi, control_info * ci, float sim_time, vector *wash_rot=NULL); +extern void physics_sim(vector *position, matrix * orient, physics_info * pi, float sim_time ); +extern void physics_sim_editor(vector *position, matrix * orient, physics_info * pi, float sim_time); + +extern void physics_sim_vel(vector * position, physics_info * pi, float sim_time, matrix * orient); +extern void physics_sim_rot(matrix * orient, physics_info * pi, float sim_time ); +extern void physics_apply_whack(vector *force, vector *pos, physics_info *pi, matrix *orient, float mass); +extern void physics_apply_shock(vector *direction_vec, float pressure, physics_info *pi, matrix *orient, vector *min, vector *max, float radius); +extern void physics_collide_whack(vector *impulse, vector *delta_rotvel, physics_info *pi, matrix *orient); +int check_rotvel_limit( physics_info *pi ); + + + +// functions which use physics calcs to predict position and velocity +void physics_predict_pos(physics_info *pi, float delta_time, vector *predicted_pos); +void physics_predict_vel(physics_info *pi, float delta_time, vector *predicted_vel); +void physics_predict_pos_and_vel(physics_info *pi, float delta_time, vector *predicted_vel, vector *predicted_pos); + +// If physics_set_viewer is called with the viewer's physics_info, then +// this variable tracks the viewer's bank. This is used for g3_draw_rotated_bitmap. +extern float Physics_viewer_bank; + +// If you would like Physics_viewer_bank to be tracked (Which is needed +// for rotating 3d bitmaps) call this and pass it a pointer to the +// viewer's physics info. +#define PHYSICS_VIEWER_FRONT 0 +#define PHYSICS_VIEWER_LEFT 1 +#define PHYSICS_VIEWER_RIGHT 2 +#define PHYSICS_VIEWER_REAR 3 +#define PHYSICS_VIEWER_UP 4 +void physics_set_viewer( physics_info * p, int dir ); + +/* +#ifdef __cplusplus +} +#endif +*/ + +#endif // PHYSICS_H + diff --git a/include/pixel.h b/include/pixel.h new file mode 100644 index 0000000..0be55f3 --- /dev/null +++ b/include/pixel.h @@ -0,0 +1,60 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Pixel.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for pixel routines. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 9 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 8 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 7 5/29/97 3:10p John + * Took out debug menu. + * Made software scaler draw larger bitmaps. + * Optimized Direct3D some. + * + * 6 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 5 1/09/97 11:35a John + * Added some 2d functions to get/put screen images. + * + * 4 11/07/96 6:19p John + * Added a bunch of 16bpp primitives so the game sort of runs in 16bpp + * mode. + * + * 3 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#ifndef _PIXEL_H +#define _PIXEL_H + +extern void gr8_pixel( int x, int y ); + +#endif + diff --git a/include/player.h b/include/player.h new file mode 100644 index 0000000..7daa304 --- /dev/null +++ b/include/player.h @@ -0,0 +1,388 @@ +/* + * $Logfile: /Freespace2/code/Playerman/Player.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for player information + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 18 9/12/99 1:27p Jefff + * upped debug player_mission_failure_limit to 5 + * + * 17 9/07/99 10:50a Jefff + * + * 16 9/06/99 9:43p Jefff + * skip mission support + * + * 15 8/29/99 4:18p Andsager + * New "burst" limit for friendly damage. Also credit more damage done + * against large friendly ships. + * + * 14 8/27/99 10:36a Dave + * Impose a 2% penalty for hitting the shield balance key. + * + * 13 8/02/99 9:13p Dave + * Added popup tips. + * + * 12 7/21/99 8:10p Dave + * First run of supernova effect. + * + * 11 7/19/99 7:20p Dave + * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula + * pre-rendering. + * + * 10 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 9 5/21/99 5:03p Andsager + * Add code to display engine wash death. Modify ship_kill_packet + * + * 8 5/18/99 10:08a Andsager + * Modified single maximum range before blown up to also be multi + * friendly. + * + * 7 3/28/99 12:37p Dave + * Tentative beginnings to warpin effect. + * + * 6 3/24/99 4:05p Dave + * Put in support for assigning the player to a specific squadron with a + * specific logo. Preliminary work for doing pos/orient checksumming in + * multiplayer to reduce bandwidth. + * + * 5 1/14/99 6:06p Dave + * 100% full squad logo support for single player and multiplayer. + * + * 4 12/14/98 12:13p Dave + * Spiffed up xfer system a bit. Put in support for squad logo file xfer. + * Need to test now. + * + * 3 11/12/98 12:13a Dave + * Tidied code up for multiplayer test. Put in network support for flak + * guns. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 96 9/17/98 3:08p Dave + * PXO to non-pxo game warning popup. Player icon stuff in create and join + * game screens. Upped server count refresh time in PXO to 35 secs (from + * 20). + * + * 95 5/19/98 12:19p Mike + * Cheat codes! + * + * 94 5/09/98 4:52p Lawrance + * Implement padlock view (up/rear/left/right) + * + * 93 5/04/98 5:52p Comet + * Fixed bug with Galatea/Bastion selection when finishing missions. + * + * 92 4/25/98 3:49p Lawrance + * Save briefing auto-advance pref + * + * 91 4/21/98 11:55p Dave + * Put in player deaths statskeeping. Use arrow keys in the ingame join + * ship select screen. Don't quit the game if in the debriefing and server + * leaves. + * + * 90 4/20/98 6:07p Hoffoss + * Added code to track if player is stationed on the galatea or the + * bastion. + * + * 89 4/08/98 10:34p Allender + * make threat indicators work in multiplayer. Fix socket problem (once + * and for all???) + * + * 88 4/05/98 7:43p Lawrance + * fix up saving/restoring of link status and auto-target/match-speed. + * + * 87 4/01/98 7:42p Lawrance + * Enable auto-targeting by default when a new pilot is created. + * + * 86 3/31/98 4:42p Allender + * mission objective support for team v. team mode. Chatbox changes to + * make input box be correct length when typing + * + * 85 3/30/98 6:27p Dave + * Put in a more official set of multiplayer options, including a system + * for distributing netplayer and netgame settings. + * + * 84 3/27/98 3:59p Hoffoss + * Due to everyone using different assumptions about what a length define + * means, changed code to account for safest assumption. + * + * 83 3/24/98 4:25p Lawrance + * Make finding out if player killed self easier and more reliable + * + * 82 3/19/98 5:35p Lawrance + * Correctly inform player if killed by ship explosion. + * + * 81 3/16/98 5:54p Lawrance + * Play cargo scanning sound + * + * 80 3/10/98 5:08p Allender + * fixed up multiplayer death messages (I hope). changes in object update + * packets + * + * 79 3/07/98 3:49p Lawrance + * store killer species in player struct + * + * 78 3/05/98 10:16p Lawrance + * Add 'save_flags' to player struct to save/restore certain player flags + * cleanly. + * + * $NoKeywords: $ + */ + +#ifndef _PLAYER_H +#define _PLAYER_H + +#include "object.h" +#include "physics.h" +#include "missionload.h" +#include "hudtarget.h" // for targeting hotkey lists +#include "ship.h" // for targeting subobjects +#include "missioncampaign.h" // for mission/campaign stuff +#include "scoring.h" // for scoring/stats +#include "keycontrol.h" // for button_info +#include "multi_options.h" + + +#define CALLSIGN_LEN 28 // shortened from 32 to allow .plr to be attached without exceeding MAX_FILENAME_LEN +#define SHORT_CALLSIGN_PIXEL_W 80 // max width of short_callsign[] in pixels +#define MAX_KEYED_TARGETS 8 // number of hot keys available to assign targets to + +// player image defines +#define PLAYER_PILOT_PIC_W 160 +#define PLAYER_PILOT_PIC_H 120 + +#define PLAYER_SQUAD_PIC_W 128 +#define PLAYER_SQUAD_PIC_H 128 + +// player flags follow +#define PLAYER_FLAGS_MATCH_TARGET (1<<0) // currently matching speed with selected target +#define PLAYER_FLAGS_MSG_MODE (1<<1) // is the player in messaging mode? +#define PLAYER_FLAGS_AUTO_TARGETING (1<<2) // is auto targeting on? +#define PLAYER_FLAGS_AUTO_MATCH_SPEED (1<<3) // is auto speed matching on? +#define PLAYER_FLAGS_STRUCTURE_IN_USE (1<<4) // is this structure in use -- for multiplayer games +#define PLAYER_FLAGS_PROMOTED (1<<5) // possibly set in mission to automatically give player promotion +#define PLAYER_FLAGS_IS_MULTI (1<<6) // this is a multiplayer pilot +#define PLAYER_FLAGS_DIST_WARNING (1<<7) // is this player under warning for being too far from battle +#define PLAYER_FLAGS_FORCE_MISSION_OVER (1<<8) // mission is being forced over for this player +#define PLAYER_FLAGS_LINK_PRIMARY (1<<9) // primary weapons were linked last mission +#define PLAYER_FLAGS_LINK_SECONDARY (1<<10) // secondary weapons were linked last mission +#define PLAYER_FLAGS_NO_CHECK_ALL_ALONE_MSG (1<<11) // player can't receive 'you're all alone...' message from Terran Command +#define PLAYER_FLAGS_KILLED_BY_EXPLOSION (1<<12) // player was killed by an instantaneous area-effect explosion +#define PLAYER_FLAGS_HAS_PLAYED_PXO (1<<13) // this pilot has at least played PXO once in the past. +#define PLAYER_FLAGS_DIST_TO_BE_KILLED (1<<14) // the pilot has been warned about distance and will be killed after message finishes playing +#define PLAYER_FLAGS_KILLED_BY_ENGINE_WASH (1<<15) // player was killed by engine wash +#define PLAYER_FLAGS_KILLED_SELF_UNKNOWN (1<<16) // player died by his own hand +#define PLAYER_FLAGS_KILLED_SELF_MISSILES (1<<17) // player died by his own missile +#define PLAYER_FLAGS_KILLED_SELF_SHOCKWAVE (1<<18) // player died by his own shockwave + +#define PLAYER_KILLED_SELF ( PLAYER_FLAGS_KILLED_SELF_MISSILES | PLAYER_FLAGS_KILLED_SELF_SHOCKWAVE ) + +#define PCM_NORMAL 0 // normal flying mode +#define PCM_WARPOUT_STAGE1 1 // speed up to 40 km/s +#define PCM_WARPOUT_STAGE2 2 // flying towards and through warp hole +#define PCM_WARPOUT_STAGE3 3 // through warp hole, waiting for it to disapper. +#define PCM_SUPERNOVA 4 // supernova. lock everything to where it is. + +// number of times dude can fail a mission in a session before +// having the opportunity to skip it +#ifdef RELEASE_REAL + #define PLAYER_MISSION_FAILURE_LIMIT 5 +#else + #define PLAYER_MISSION_FAILURE_LIMIT 5 +#endif // RELEASE_REAL + + +typedef struct campaign_stats { + char campaign_name[MAX_FILENAME_LEN+1]; // insurance + scoring_struct stats; +} campaign_stats; + +typedef struct player { + char callsign[CALLSIGN_LEN + 1]; + char short_callsign[CALLSIGN_LEN + 1]; // callsign truncated to SHORT_CALLSIGN_PIXEL_W pixels + int short_callsign_width; // useful for mutliplayer chat boxes. + char image_filename[MAX_FILENAME_LEN]; // filename of the image for this pilot + char squad_filename[MAX_FILENAME_LEN]; // filename of the squad image for this pilot + char squad_name[NAME_LENGTH + 1]; // pilot's squadron name + int num_campaigns; // tells how many array entries in the campaigns field + char current_campaign[MAX_FILENAME_LEN]; // Name of the currently active campaign, or zero-length string if none + campaign_info *campaigns; // holds information regarding all active campaigns the player is playing + int readyroom_listing_mode; + + ubyte on_bastion; + int flags; + int save_flags; + + htarget_list keyed_targets[MAX_KEYED_TARGETS]; // linked list of hot-keyed targets + int current_hotkey_set; // currently hotkey set in use, -1 if none + + vector lead_target_pos; // (x,y,z) of the lead target indicator + int lead_target_cheat; // whether cheat for firing at lead indicator is active + int lead_indicator_active; // flag that indicates the lead indicator is enabled + + int lock_indicator_x; // 2D screen x-coordinate of the lock indicator + int lock_indicator_y; // 2D screen y-coordinate of the lock indicator + int lock_indicator_start_x; // 2D screen x-coordinate of where lock indicator originated + int lock_indicator_start_y; // 2D screen y-coordinate of where lock indicator originated + int lock_indicator_visible; // flag indicating if the lock indicator is on screen or not + float lock_time_to_target; // time left (in milliseconds) before minimum time to lock elapsed + float lock_dist_to_target; // distance from lock indicator to target (in pixels) + + int last_ship_flown_si_index; // ship info index of ship most recently flown on a mission + + int objnum; // object number for this player + button_info bi; // structure that holds bit vectors for button presses + control_info ci; // control info structure for this player + scoring_struct stats; // scoring and stats info for the player (points to multi_stats or single_stats) + + int friendly_hits; // Number of times hit a friendly ship this mission. + float friendly_damage; // Total friendly damage done in mission. Diminishes over time. + fix friendly_last_hit_time; // Missiontime of last hit on friendly. Used to decay friendly damage. + fix last_warning_message_time; // Time at which last message to player was sent regarding friendly damage. + + int control_mode; // Used to determine what mode player control is in. For worm holes mainly. + int saved_viewer_mode; // used to save viewer mode when warping out + + int check_warn_timestamp; // Timestamp used to determine when to check for possible warning, + // done so we don't check each frame + + int distance_warning_count; // Number of distance warings + + int distance_warning_time; // Time at which distance warning was given + + int allow_warn_timestamp; // Timestamp used to regulate how often a player might receive + // warning messages about ships attacking. + int warn_count; // number of attack warnings player has received this mission + float damage_this_burst; // amount of damage done this frame to friendly craft + int repair_sound_loop; // Sound id for ship repair looping sound, this is in the player + // file since the repair sound only plays when Player ship is getting repaired + + int cargo_scan_loop; // Sound id for scanning cargo looping sound + + int praise_count; // number of praises received this mission + int allow_praise_timestamp; // timestamp marking time until next praise is allowed + int praise_delay_timestamp; // timestamp used to delay a praise by a second or two + + int ask_help_count; // number of praises received this mission + int allow_ask_help_timestamp; // timestamp marking time until next 'ask help' is allowed + + int scream_count; // number of wingman screams received this mission + int allow_scream_timestamp; // timestamp marking time until next wingman scream is allowed + + int subsys_in_view; // set to -1 when this information needs to be re-evaluated + int request_repair_timestamp; // timestamp marking time until next time we can be informed of a repair ship getting called in + + int cargo_inspect_time; // time that current cargo has been inspected for + int target_is_dying; // The player target is dying, set to -1 if no target + int current_target_sx; // Screen x-pos of current target (or subsystem if applicable) + int current_target_sy; // Screen y-pos of current target (or subsystem if applicable) + int target_in_lock_cone; // Is the current target in secondary weapon lock cone? + ship_subsys *locking_subsys; // Subsystem pointer that missile lock is trying to seek + int locking_subsys_parent; // objnum of the parent of locking_subsystem + int locking_on_center; // boolean, whether missile lock is trying for center of ship or not + + int killer_objtype; // type of object that killed player + int killer_species; // Species which killed player + int killer_weapon_index; // weapon used to kill player (if applicable) + char killer_parent_name[NAME_LENGTH]; // name of parent object that killed the player + + int check_for_all_alone_msg; // timestamp to check for playing of 'all alone' msg + + int update_dumbfire_time; // when to update dumbfire threat indicators + int update_lock_time; // when to update lock threat indicators + int threat_flags; // threat flags + int auto_advance; // auto-advance through briefing? + + multi_local_options m_local_options; // options for local player in multiplayer mode (ignore for single player pilots) + multi_server_options m_server_options; // options for netgame host/server in multiplayer mode + + int insignia_texture; // player's insignia bitmap (or -1 if none). should correspond to squad filename + // NOTE : this bitmap is in TEXTURE format. do not try to use this bitmap to + // render in screen format + int tips; // show tips or not + + int shield_penalty_stamp; // timestamp for when we can next apply a shield balance penalty + + int failures_this_session; // number of times dude has failed the mission he is on this session + ubyte show_skip_popup; // false if dude clicked "don't show this again" -- persists for current mission only +} player_t; + +extern player Players[MAX_PLAYERS]; + +extern int Player_num; // player num of person playing on this machine +extern player_t *Player; // pointer to my information +//extern control_info PlayerControls; + +extern void player_init(); // initialization per level +extern void player_level_init(); +extern void player_controls_init(); // initialize Descent style controls for use in various places +extern void player_match_target_speed(char *no_target_text=NULL, char *match_off_text=NULL, char *match_on_text=NULL); // call to continually match speed with selected target +extern void player_clear_speed_matching(); + +void player_set_pilot_defaults(player *p); + +int player_process_pending_praise(); +float player_farthest_weapon_range(); +void player_save_target_and_weapon_link_prefs(); +void player_restore_target_and_weapon_link_prefs(); + +// functions for controlling looping sounds associated with the player +void player_stop_looped_sounds(); +void player_maybe_start_repair_sound(); +void player_stop_repair_sound(); +void player_stop_cargo_scan_sound(); +void player_maybe_start_cargo_scan_sound(); + +// will attempt to load an insignia bitmap and set it as active for the player +void player_set_squad_bitmap(player *p, char *fname); + +// set squadron +void player_set_squad(player *p, char *squad_name); + +int player_inspect_cargo(float frametime, char *outstr); + +//#ifndef NDEBUG +extern int use_descent; // player is using descent-style physics +extern void toggle_player_object(); // toggles between descent-style ship and player ship +//#endif + +extern void read_player_controls( object *obj, float frametime); +extern void player_control_reset_ci( control_info *ci ); + +char *player_generate_death_text( player *player_p, char *text ); +void player_show_death_message(); +void player_maybe_fire_turret(object *objp); +void player_maybe_play_all_alone_msg(); +void player_set_next_all_alone_msg_timestamp(); + +void player_get_padlock_orient(matrix *eye_orient); +void player_display_packlock_view(); + +// get the player's eye position and orient +void player_get_eye(vector *eye_pos, matrix *eye_orient); + +//============================================================= +//===================== PLAYER WARPOUT STUFF ================== +#define TARGET_WARPOUT_SPEED 40.0f // speed you need to be going to warpout +#define TARGET_WARPOUT_MATCH_PERCENT 0.05f // how close to TARGET_WARPOUT_SPEED you need to be +#define MINIMUM_PLAYER_WARPOUT_TIME 3.0f // How long before you can press 'ESC' to abort warpout + +extern float Warpout_time; // Declared in Freespace.cpp +extern int Warpout_forced; // If non-zero, bash the player to speed and go through effect +//============================================================= + + +#endif + diff --git a/include/playermenu.h b/include/playermenu.h new file mode 100644 index 0000000..16104a5 --- /dev/null +++ b/include/playermenu.h @@ -0,0 +1,68 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/PlayerMenu.h $ + * $Revision$Date: 11/10/97 11:15a $ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 8/02/99 9:13p Dave + * Added popup tips. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 12 2/12/98 4:40p Dave + * Seperated multiplayer kick functionality into its own module. Tweaked + * some main hall values. Added default help overlay when adding new + * pilots. + * + * 11 1/26/98 5:23p Andsager + * Fixed a compile bug caused by Dave B. + * + * 10 1/26/98 4:44p Dave + * Changed how multiplayer messaging, endgame, and pausing are handled. + * Tidied up player select dialog thingie. + * + * 9 11/12/97 4:40p Dave + * Put in multiplayer campaign support parsing, loading and saving. Made + * command-line variables better named. Changed some things on the initial + * pilot select screen. + * + * 8 11/11/97 4:57p Dave + * Put in support for single vs. multiplayer pilots. Began work on + * multiplayer campaign saving. Put in initial player select screen + * + * $NoKeywords: $ + * + */ + +#ifndef _PLAYER_SELECT_MENU_HEADER_FILE +#define _PLAYER_SELECT_MENU_HEADER_FILE + +// general defines +#define PLAYER_SELECT_MODE_SINGLE 0 // looking through single player pilots +#define PLAYER_SELECT_MODE_MULTI 1 // looking through multi player pilots + +// flag indicating if this is the absolute first pilot created and selected. Used to determine +// if the main hall should display the help overlay screen +extern int Player_select_very_first_pilot; + +// functions for selecting single/multiplayer pilots at the very beginning of Freespace +void player_select_init(); +void player_select_do(); +void player_select_close(); + +// function to check whether we found a "last pilot". loads this pilot in if possible and returns true, or false otherwise +int player_select_get_last_pilot(); + +// tooltips +void player_tips_init(); +void player_tips_popup(); + +#endif + diff --git a/include/playerstarteditor.h b/include/playerstarteditor.h new file mode 100644 index 0000000..8c50fac --- /dev/null +++ b/include/playerstarteditor.h @@ -0,0 +1,141 @@ +/* + * $Logfile: /Freespace2/code/fred2/PlayerStartEditor.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Player starting point editor dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 2/23/99 7:03p Dave + * Rewrote a horribly mangled and evil team loadout dialog. Bugs gone. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 17 1/15/98 5:04p Hoffoss + * Fixed functionality of team loadout editor. + * + * 16 10/03/97 6:16p Hoffoss + * Added weapon loadout editing to player editor (now called team loadout + * editor) + * + * 15 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 14 8/16/97 2:16p Hoffoss + * Made changed to how checkboxes work. + * + * 13 8/13/97 10:17p Hoffoss + * Added player entry delay support to Fred, and moved the + * Player_entry_delay variable to a common library instead of FreeSpace. + * + * 12 5/20/97 2:28p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 11 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 10 3/11/97 5:41p Hoffoss + * Player start editor now displays wing ship counts. Removed default + * player ship selection box. + * + * 9 3/11/97 4:52p Hoffoss + * changed player start editor dialog functioning. + * + * 8 2/26/97 5:31p Hoffoss + * Added support for default player ship in player editor dialog box. + * + * 7 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 6 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 5 1/30/97 2:24p Hoffoss + * Added remaining mission file structures and implemented load/save of + * them. + * + * $NoKeywords: $ + */ + +#include "shipchecklistbox.h" + +///////////////////////////////////////////////////////////////////////////// +// player_start_editor dialog + +class player_start_editor : public CDialog +{ +// Construction +public: + player_start_editor(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(player_start_editor) + enum { IDD = IDD_LOADOUT_EDITOR }; + CSpinButtonCtrl m_pool_spin; + CSpinButtonCtrl m_delay_spin; + CSpinButtonCtrl m_spin1; + CCheckListBox m_ship_list; + CCheckListBox m_weapon_list; + int m_delay; + int m_weapon_pool; + int m_ship_pool; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(player_start_editor) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(player_start_editor) + virtual BOOL OnInitDialog(); + afx_msg void OnInitMenu(CMenu* pMenu); + afx_msg void OnSelchangeShipList(); + afx_msg void OnSelchangeWeaponList(); + afx_msg void OnUpdateShipPool(); + afx_msg void OnUpdateWeaponPool(); + void OnCancel(); + void OnOK(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: + // if we've finished initializing the dialog + int dlg_inited; + + // team we're currently working with + int selected_team; + + // ship pool info + int ship_pool[MAX_TEAMS][MAX_SHIP_TYPES]; + + // weapon pool info + int weapon_pool[MAX_TEAMS][MAX_WEAPON_TYPES]; + + // regenerate all controls + void reset_controls(); +}; + diff --git a/include/pofview.h b/include/pofview.h new file mode 100644 index 0000000..9d2dcc7 --- /dev/null +++ b/include/pofview.h @@ -0,0 +1,51 @@ +// PofView.h : main header file for the POFVIEW application +// + +#ifndef _POFVIEW_H +#define _POFVIEW_H + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols + +///////////////////////////////////////////////////////////////////////////// +// CPofViewApp: +// See PofView.cpp for the implementation of this class +// + +extern float model_thrust; +extern int model_afterburner; + +class CPofViewApp : public CWinApp +{ +public: + virtual BOOL OnIdle(LONG lCount); + CPofViewApp(); + + + int m_timer; + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPofViewApp) + public: + virtual BOOL InitInstance(); + virtual int ExitInstance(); + //}}AFX_VIRTUAL + +// Implementation + + //{{AFX_MSG(CPofViewApp) + afx_msg void OnAppAbout(); + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +///////////////////////////////////////////////////////////////////////////// +#endif + diff --git a/include/pofviewdoc.h b/include/pofviewdoc.h new file mode 100644 index 0000000..206a70a --- /dev/null +++ b/include/pofviewdoc.h @@ -0,0 +1,46 @@ +// PofViewDoc.h : interface of the CPofViewDoc class +// +///////////////////////////////////////////////////////////////////////////// + +class CPofViewDoc : public CDocument +{ +protected: // create from serialization only + CPofViewDoc(); + DECLARE_DYNCREATE(CPofViewDoc) + +// Attributes +public: + int m_model_num; + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPofViewDoc) + public: + virtual BOOL OnNewDocument(); + virtual void Serialize(CArchive& ar); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CPofViewDoc(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CPofViewDoc) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + diff --git a/include/pofviewview.h b/include/pofviewview.h new file mode 100644 index 0000000..00545a4 --- /dev/null +++ b/include/pofviewview.h @@ -0,0 +1,140 @@ +// PofViewView.h : interface of the CPofViewView class +// +///////////////////////////////////////////////////////////////////////////// + +#include "vecmat.h" +#include "physics.h" +#include "objecttree.h" + +class CPofViewView : public CView +{ +protected: // create from serialization only + CPofViewView(); + DECLARE_DYNCREATE(CPofViewView) + +// Attributes +public: + int m_dolighting; + CPofViewDoc* GetDocument(); + + void MoveViewer(float FrameTime, BOOL is_active, int key); + + vector m_ViewerPos; + matrix m_ViewerOrient; + matrix m_ObjectOrient; + float m_ViewerZoom; + physics_info m_ViewerPhysics; + control_info m_Viewer_ci; + int m_show_outline; + int m_show_pivots; + int m_current_detail_level; + int m_show_paths; + int m_show_radius; + int m_show_overwrite; + int m_show_destroyed_subobjects; + int m_show_shields; + int m_show_invisible_faces; + int m_show_bay_paths; + int m_autocenter; + int m_mouse_x, m_mouse_y; + int m_mouse_dx, m_mouse_dy; + int m_mouse_down; + int m_mouse_inited; + + int m_texturing; + int m_smoothing; + int m_lights_on; + + CObjectTree m_TreeDialog; + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPofViewView) + public: + virtual void OnDraw(CDC* pDC); // overridden to draw this view + virtual void OnInitialUpdate(); + protected: + virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); + virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); + virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~CPofViewView(); +#ifdef _DEBUG + virtual void AssertValid() const; + virtual void Dump(CDumpContext& dc) const; +#endif + +protected: + +// Generated message map functions +protected: + //{{AFX_MSG(CPofViewView) + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKillFocus(CWnd* pNewWnd); + afx_msg void OnSetFocus(CWnd* pOldWnd); + afx_msg void OnSize(UINT nType, int cx, int cy); + afx_msg void OnPofOutline(); + afx_msg void OnUpdatePofOutline(CCmdUI* pCmdUI); + afx_msg void OnPofLighting(); + afx_msg void OnUpdatePofLighting(CCmdUI* pCmdUI); + afx_msg void OnPofPivots(); + afx_msg void OnUpdatePofPivots(CCmdUI* pCmdUI); + afx_msg void OnUpdateDetail1(CCmdUI* pCmdUI); + afx_msg void OnDetail1(); + afx_msg void OnDetail2(); + afx_msg void OnUpdateDetail2(CCmdUI* pCmdUI); + afx_msg void OnDetail3(); + afx_msg void OnUpdateDetail3(CCmdUI* pCmdUI); + afx_msg void OnDetail4(); + afx_msg void OnUpdateDetail4(CCmdUI* pCmdUI); + afx_msg void OnDetail5(); + afx_msg void OnUpdateDetail5(CCmdUI* pCmdUI); + afx_msg void OnDetail6(); + afx_msg void OnUpdateDetail6(CCmdUI* pCmdUI); + afx_msg void OnShowTree(); + afx_msg void OnUpdateShowTree(CCmdUI* pCmdUI); + afx_msg void OnPofPaths(); + afx_msg void OnUpdatePofPaths(CCmdUI* pCmdUI); + afx_msg void OnPofRadius(); + afx_msg void OnUpdatePofRadius(CCmdUI* pCmdUI); + afx_msg void OnPofOverwrite(); + afx_msg void OnUpdatePofOverwrite(CCmdUI* pCmdUI); + afx_msg void OnShowDamagedSubobjects(); + afx_msg void OnUpdateShowDamagedSubobjects(CCmdUI* pCmdUI); + afx_msg void OnShowDebris(); + afx_msg void OnUpdateShowDebris(CCmdUI* pCmdUI); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnPofSmoothing(); + afx_msg void OnUpdatePofSmoothing(CCmdUI* pCmdUI); + afx_msg void OnPofTexturing(); + afx_msg void OnUpdatePofTexturing(CCmdUI* pCmdUI); + afx_msg void OnPofShields(); + afx_msg void OnUpdatePofShields(CCmdUI* pCmdUI); + afx_msg void OnToggleLighting(); + afx_msg void OnUpdateToggleLighting(CCmdUI* pCmdUI); + afx_msg void OnInvisiblefaces(); + afx_msg void OnUpdateInvisiblefaces(CCmdUI* pCmdUI); + afx_msg void OnBayPaths(); + afx_msg void OnUpdateBayPaths(CCmdUI* pCmdUI); + afx_msg void OnAutocenter(); + afx_msg void OnUpdateAutocenter(CCmdUI* pCmdUI); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#ifndef _DEBUG // debug version in PofViewView.cpp +inline CPofViewDoc* CPofViewView::GetDocument() + { return (CPofViewDoc*)m_pDocument; } +#endif + +///////////////////////////////////////////////////////////////////////////// + diff --git a/include/popup.h b/include/popup.h new file mode 100644 index 0000000..cc144a8 --- /dev/null +++ b/include/popup.h @@ -0,0 +1,191 @@ +/* + * $Logfile: /Freespace2/code/Popup/Popup.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for displaying pop-up dialog boxes + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 7 8/16/99 9:50a Jefff + * added mouseover webcursor options to user-defined popup buttons. + * + * 6 8/04/99 10:53a Dave + * Added title to the user tips popup. + * + * 5 8/02/99 9:13p Dave + * Added popup tips. + * + * 4 6/01/99 4:03p Dave + * Changed some popup flag #defines. Upped string count to 1336 + * + * 3 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 17 6/12/98 4:48p Hoffoss + * Externalized default popup choices. + * + * 16 2/05/98 11:09a Dave + * Fixed an ingame join bug. Fixed a read-only file problem with + * multiplauer file xfer. + * + * 15 2/03/98 8:18p Dave + * More MT stats transfer stuff + * + * 14 1/28/98 6:24p Dave + * Made standalone use ~8 megs less memory. Fixed multiplayer submenu + * sequencing problem. + * + * 13 1/27/98 5:52p Lawrance + * support new "tiny" popups + * + * 12 1/27/98 3:25p Hoffoss + * Made popups use the correct button icons for default positive and + * negative buttons. + * + * 11 1/26/98 6:28p Lawrance + * Use '&' meta char for underlining, change how keyboard usage works so + * fits better with mouse usage. + * + * 10 1/17/98 10:04p Lawrance + * fix errors in popup comments + * + * 9 1/14/98 6:55p Dave + * Fixed a slew of multiplayer bugs. Made certain important popups ignore + * the escape character. + * + * 8 1/13/98 5:37p Dave + * Reworked a lot of standalone interface code. Put in single and + * multiplayer popups for death sequence. Solidified multiplayer kick + * code. + * + * 7 1/13/98 4:06p Lawrance + * Add underline char for shortcuts. + * + * 6 1/02/98 9:08p Lawrance + * Integrated art for popups, expanded options. + * + * 5 12/30/97 4:30p Sandeep + * Added conditional popups + * + * 4 12/26/97 10:01p Lawrance + * Allow keyboard shortcuts for popup buttons + * + * 3 12/24/97 9:49p Lawrance + * ensure mouse gets drawn when popup menu is up + * + * 2 12/24/97 8:54p Lawrance + * Integrating new popup code + * + * $NoKeywords: $ + */ + +#ifndef __POPUP_H__ +#define __POPUP_H__ + +// standardized text used for common buttons +// Special note (JH): The leading '&' is expected for these 4 defines in the code +#define POPUP_OK XSTR("&Ok", 503) +#define POPUP_CANCEL XSTR("&Cancel", 504) +#define POPUP_YES XSTR("&Yes", 505) +#define POPUP_NO XSTR("&No", 506) + +/////////////////////////////////////////////////// +// flags +/////////////////////////////////////////////////// + +// NEVER ADD FLAGS LESS THAN 10 here. there are some internal flags which use those + +// font size +#define PF_TITLE (1<<10) // Draw title centered in regular font (title is first line) +#define PF_TITLE_BIG (1<<11) // Draw title centered in large font (title is first line) +#define PF_BODY_BIG (1<<12) // Draw message body in large font + +// color +#define PF_TITLE_RED (1<<13) // Color to draw title, if different from default +#define PF_TITLE_GREEN (1<<14) +#define PF_TITLE_BLUE (1<<15) +#define PF_TITLE_WHITE (1<<16) +#define PF_BODY_RED (1<<17) // Color to draw body, if different from title +#define PF_BODY_GREEN (1<<18) +#define PF_BODY_BLUE (1<<19) + +// icon choices +#define PF_USE_NEGATIVE_ICON (1<<20) // Always drawn as first icon if set +#define PF_USE_AFFIRMATIVE_ICON (1<<21) // Always drawn as second icon if two choices (if 1 choice, it is the only icon) + +// misc +#define PF_RUN_STATE (1<<22) // call the do frame of the current state underneath the popup +#define PF_IGNORE_ESC (1<<23) // ignore the escape character +#define PF_ALLOW_DEAD_KEYS (1<<24) // Allow player to use keyset that exists when player dies +#define PF_NO_NETWORKING (1<<25) // don't do any networking + +// no special buttons +#define PF_NO_SPECIAL_BUTTONS (1<<26) + +// special web mouseover cursor flags +#define PF_WEB_CURSOR_1 (1<<27) // button 1 will get web cursor +#define PF_WEB_CURSOR_2 (1<<28) // button 2 will get web cursor + +// input: flags => formatting specificatons (PF_... shown above) +// nchoices => number of choices popup has +// text_1 => text for first button +// ... => +// text_n => text for last button +// msg text => text msg for popup (can be of form "%s",pl->text) +// +// exit: choice selected (0..nchoices-1) +// will return -1 if there was an error or popup was aborted +// +// typical usage: +// +// rval = popup(0, 2, POPUP_YES, POPUP_NO, "Hey %s, do you want to quit", pl->callsign); +int popup(int flags, int nchoices, ... ); + +// popup with cancel button and conditional funcrion. +// input: condition => function to call every frame, if condition() returns FALSE, the popup +// continues waiting. If condition() returns anything else, the popup will +// return that value. +// text_1 => text for cancel button +// msg text => text msg for popup (can be of form "%s",pl->text) +// +// exit: condition occured (return value of condition) +// will return 0 if cancel was pressed or popup was aborted +// will return -1 if there was an error +// +// typical usage: +// +// int condition_function() { +// if (blah) return 1; +// return 0; +// } +// . +// . +// . +// rval = popup_till_condition( condition_function, "Cancel", "Checking to see if %s is an idiot.", pl->callsign); +int popup_till_condition( int(*condition)() , ...); + +// popup to return the value from an input box +char *popup_input(int flags, char *caption, int max_output_len = -1); + +int popup_active(); + +int popup_running_state(); + +// kill any active popup, forcing it to return -1 (similar to ESC) +void popup_kill_any_active(); + +// change the text inside of the popup +void popup_change_text(char *new_text); + +#endif + diff --git a/include/popupdead.h b/include/popupdead.h new file mode 100644 index 0000000..321fd8e --- /dev/null +++ b/include/popupdead.h @@ -0,0 +1,46 @@ +/* + * $Logfile: /Freespace2/code/Popup/PopupDead.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header for the death popup + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 3 4/22/98 4:59p Allender + * new multiplayer dead popup. big changes to the comm menu system for + * team vs. team. Start of debriefing stuff for team vs. team Make form + * on my wing work with individual ships who have high priority orders + * + * 2 2/10/98 11:20p Lawrance + * Implement separate dead popup system + * + * 1 2/10/98 6:02p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __POPUPDEAD_H__ +#define __POPUPDEAD_H__ + +// return values for popup_do_frame for multiplayer +#define POPUPDEAD_DO_RESPAWN 0 +#define POPUPDEAD_DO_OBSERVER 1 +#define POPUPDEAD_DO_MAIN_HALL 2 + +void popupdead_start(); +void popupdead_close(); +int popupdead_do_frame(float frametime); +int popupdead_is_active(); + +#endif + diff --git a/include/prefsdlg.h b/include/prefsdlg.h new file mode 100644 index 0000000..398ecd9 --- /dev/null +++ b/include/prefsdlg.h @@ -0,0 +1,73 @@ +/* + * $Logfile: /Freespace2/code/FRED2/PrefsDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Fred Preferences dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// CPrefsDlg dialog + +class CPrefsDlg : public CDialog +{ +// Construction +public: + CPrefsDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CPrefsDlg) + enum { IDD = IDD_PREFERENCES }; + BOOL m_ConfirmDeleting; + BOOL m_ShowCapitalShips; + BOOL m_ShowElevations; + BOOL m_ShowFighters; + BOOL m_ShowGrid; + BOOL m_ShowMiscObjects; + BOOL m_ShowPlanets; + BOOL m_ShowWaypoints; + BOOL m_ShowStarfield; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CPrefsDlg) + public: + virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CPrefsDlg) + afx_msg void OnSaveDefaultPrefs(); + afx_msg void OnClose(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/psnet.h b/include/psnet.h new file mode 100644 index 0000000..c1951c5 --- /dev/null +++ b/include/psnet.h @@ -0,0 +1,324 @@ +/* + * $Logfile: /Freespace2/code/Network/PsNet.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the application level network-interface. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 4 11/19/98 8:04a Dave + * Full support for D3-style reliable sockets. Revamped packet lag/loss + * system, made it receiver side and at the lowest possible level. + * + * 3 11/17/98 11:12a Dave + * Removed player identification by address. Now assign explicit id #'s. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 41 10/02/98 3:22p Allender + * fix up the -connect option and fix the -port option + * + * 40 9/14/98 11:28a Allender + * support for server bashing of address when received from client. Added + * a cmdline.cfg file to process command line arguments from a file + * + * 39 8/07/98 10:40a Allender + * new command line flags for starting netgames. Only starting currently + * works, and PXO isn't implemented yet + * + * 38 6/30/98 2:17p Dave + * Revised object update system. Removed updates for all weapons. Put + * button info back into control info packet. + * + * 37 6/04/98 11:04a Allender + * object update level stuff. Don't reset to high when becoming an + * observer of any type. default to low when guy is a dialup customer + * + * 36 5/22/98 10:54a Allender + * new dialog information for networking to tell user if connection type + * doesn't match the setup specified type + * + * 35 5/21/98 3:31p Allender + * more RAS stuff -- fix possible problem when unable to read data on + * reliable socket + * + * 34 5/18/98 4:10p Allender + * ditch any reference to IPX initialziation. put in (butt comment out) + * the RAS code + * + * 33 5/01/98 3:15a Dave + * Tweaked object update system. Put in new packet buffering system. + * + * 32 4/04/98 4:22p Dave + * First rev of UDP reliable sockets is done. Seems to work well if not + * overly burdened. + * + * 31 4/03/98 1:03a Dave + * First pass at unreliable guaranteed delivery packets. + * + * 30 3/11/98 11:42p Allender + * more ingame join stuff. Fix to networking code to possibly + * reinitialize reliable socket when entering join screen + * + * 29 2/05/98 11:44a Allender + * enhcanced network statistic collection. Made callback in debug console + * to do networking if player is in the console + * + * 28 2/04/98 6:35p Dave + * Changed psnet to use raw data with no headers. Started putting in + * support for master tracker security measures. + * + * 27 1/11/98 10:03p Allender + * removed from headers which included it. Made psnet_socket + * type which is defined just as SOCKET type is. + * + * 26 12/16/97 6:19p Dave + * Put in primary weapon support for multiplayer weapon select. + * + * 25 12/16/97 5:24p Allender + * changed to options menu to allow FQDN's. changes to player booting + * code (due to timeout). more work still needs to be done though. + * + * 24 12/11/97 8:15p Dave + * Put in network options screen. Xed out olf protocol selection screen. + * + * 23 12/03/97 11:48a Allender + * overhaul on reliable socket code. Made reliable sockets non-blocking + * (listen socket still blocks). Made player timeouts work correctly with + * certain winsock errors on the reliable sockets + * + * 22 11/11/97 11:55a Allender + * initialize network at beginning of application. create new call to set + * which network protocol to use + * + * 21 11/04/97 3:50p Allender + * more reliable socket stuff. Removed admin port. Cleaner sequencing, + * etc. + * + * 20 11/03/97 8:25p Dave + * Got client side reliable sockets working. Got reliable/unreliable + * pakcet interleaving done. + * + * 19 11/03/97 5:09p Allender + * added reliable transport system -- still in infancy. Each netplayer + * has reliable socket to server (and server to clients). + * + * 18 10/24/97 6:22p Sandeep + * added checknet + * + * 17 9/17/97 9:09a Dave + * Observer mode work, put in standalone controls. Fixed up some stuff for + * ingame join broken by recent code checkins. + * + * 16 8/26/97 5:02p Dave + * Put in admin socket handling thread. Modified all functions to support + * this. + * + * 15 8/20/97 4:34p Dave + * Added admin socket init to psnet_init() + * + * 14 8/13/97 4:55p Dave + * Moved PSNET_FLAG_CHECKSUM bit #define into psnet.h + * + * 13 8/11/97 3:18p Dave + * Added administration client socket. + * + * 12 8/04/97 9:41p Allender + * revamped packet structure for netgames. No more magic number. psnet* + * functions can now do checksumming at it's level. + * + * 11 7/30/97 9:39a Allender + * network debug stuff -- showing net read/write stats on hud + * + * 10 6/11/97 1:38p Allender + * added basic sequencing through ship selection. Put My_addr structure + * into psnet.cpp where it should be + * + * 9 1/03/97 12:01p Lawrance + * Now getting return address and port from the network layer header. + * + * 8 1/02/97 2:07p Lawrance + * fixed problem with reading socket data + * + * 7 1/01/97 6:45p Lawrance + * added support for IPX + * + * $NoKeywords: $ + */ + + +#ifndef _PSNET_H +#define _PSNET_H + +// use PSNET 2 +#define PSNET2 + +#ifdef PSNET2 + #include "psnet2.h" +#else + +// use Berkeley reliable sockets if defined - otherwise use Volition reliable sockets +#define PSNET_RELIABLE_OLD_SCHOOL + +#define NET_NONE 0 // if no protocol is active or none are selected +#define NET_TCP 1 +#define NET_IPX 2 +#define NET_VMT 3 + +#define MAX_PACKET_SIZE 512 + +#define PSNET_FLAG_CHECKSUM (1<<0) // this packet is checksummed +#define PSNET_FLAG_RAW (1<<1) // send or receive raw data. don't do any checksumming, sequencing, etc + +#define DEFAULT_GAME_PORT 7802 + +typedef struct net_addr { + uint type; // See NET_ defines above + ubyte net_id[4]; // used for IPX only + ubyte addr[6]; // address (first 4 used when IP, all 6 used when IPX) + short port; +} net_addr; + +// define these in such a manner that a call to psnet_send_reliable is exactly the same and the new code in unobtrusive +#ifdef PSNET_RELIABLE_OLD_SCHOOL + typedef uint PSNET_SOCKET; + typedef uint PSNET_SOCKET_RELIABLE; + + #undef INVALID_SOCKET + #define INVALID_SOCKET (PSNET_SOCKET)(~0) +#else + typedef net_addr* PSNET_SOCKET; + typedef net_addr* PSNET_SOCKET_RELIABLE + + #undef INVALID_SOCKET + #define INVALID_SOCKET NULL + +#endif + +// defines for protocol overheads +#define UDP_HEADER_SIZE 34 +#define TCP_HEADER_SIZE 40 +#define TCP_HEADER_SIZE_COMPRESSED 6 + +// define values for network errors when trying to enter the ready room +#define NETWORK_ERROR_NONE 0 +#define NETWORK_ERROR_NO_TYPE -1 +#define NETWORK_ERROR_NO_WINSOCK -2 +#define NETWORK_ERROR_NO_PROTOCOL -3 +#define NETWORK_ERROR_RELIABLE -4 +#define NETWORK_ERROR_CONNECT_TO_ISP -5 +#define NETWORK_ERROR_LAN_AND_RAS -6 + +extern net_addr Psnet_my_addr; // address information of this machine +extern uint Psnet_my_ip; +extern int Psnet_my_addr_valid; + +extern int Network_status; +extern int Tcp_failure_code; +extern int Ipx_failure_code; + +extern int Tcp_active; + +// specified their internet connnection type +#define NETWORK_CONNECTION_NONE 1 +#define NETWORK_CONNECTION_DIALUP 2 +#define NETWORK_CONNECTION_LAN 3 + +extern int Psnet_connection; + +extern ushort Psnet_default_port; + + +#ifndef NDEBUG + +extern int Psnet_bytes_read; // globally available numbers for printing on the hud +extern int Psnet_bytes_written; +extern void psnet_calc_socket_stats(); // routine to calc stats for this frame. +#endif + +void ipx_ntoa(net_addr *addr, char *text); // this is a HUGE hack right now. Just the 6 byte equivalent of inet_ntoa + +extern void psnet_init( int protocol, int default_port ); +extern void psnet_close(); +extern int psnet_use_protocol( int type ); +extern void psnet_rel_close_socket( PSNET_SOCKET *sockp ); +extern int psnet_rel_check(); +extern int psnet_get_network_status(); + +extern void psnet_whoami( net_addr * my_address ); +extern char* psnet_addr_to_string( char * text, net_addr * address ); +extern void psnet_string_to_addr( net_addr * address, char * text ); +extern int psnet_same( net_addr * a1, net_addr * a2 ); + +extern int psnet_send( net_addr * who_to, void * data, int len, int flags = PSNET_FLAG_RAW, int reliable_socket = 0 ); +extern int psnet_get( void * data, net_addr * from_addr, int flags = PSNET_FLAG_RAW ); +extern int psnet_broadcast( net_addr * who_to, void * data, int len,int flags = PSNET_FLAG_RAW ); + +// functions for reliable socket stuff +extern int psnet_rel_send( PSNET_SOCKET socket, ubyte *data, int length, int flags = PSNET_FLAG_RAW ); +extern int psnet_rel_get( PSNET_SOCKET socket, ubyte *buffer, int max_length, int flags = PSNET_FLAG_RAW); + +extern int psnet_rel_check_for_listen( net_addr *addr ); +extern void psnet_rel_connect_to_server( PSNET_SOCKET *s, net_addr *server_addr ); + +extern void psnet_flush(); +extern int psnet_is_valid_ip_string( char *ip_string, int allow_port=1 ); + +// initialize the buffering system +extern void psnet_buffer_init(); + +// buffer a packet (maintain order!) +extern void psnet_buffer_packet(ubyte *data, int length, net_addr *from); + +// get the index of the next packet in order! +extern int psnet_buffer_get_next(ubyte *data, int *length, net_addr *from); + + +// ------------------------------------------------------------------------------------- +// PSNET RELIABLE UDP STUFF +// + +// initialize the psnet reliable system (return 0 on fail, 1 on success) +int psnet_reliable_init(); + +// shutdown the reliable system (free up buffers, etc) +void psnet_reliable_close(); + +// notify the reliable system of a new address at index N +void psnet_reliable_notify_new_addr(net_addr *addr,int index); + +// notify the reliable system of a drop at index N +void psnet_reliable_notify_drop_addr(net_addr *addr); + +// send a reliable data packet +int psnet_reliable_send(ubyte *data,int packet_size,net_addr *addr); + +// process frame for all reliable stuff (call once per frame) +void psnet_reliable_process(); + +// determine if the passed in reliable data should be processed, and sends an ack if necessary +// return # of bytes which should be stripped off the data (reliable data header) +int psnet_reliable_should_process(net_addr *addr,ubyte *data,int packet_size); + + +#ifndef NDEBUG +extern void psnet_stats_init(); +extern int psnet_get_stats( net_addr *addr, int *tr, int *tw ); +#endif + +#endif // #ifdef PSNET2 + +#endif + diff --git a/include/psnet2.h b/include/psnet2.h new file mode 100644 index 0000000..7679d3c --- /dev/null +++ b/include/psnet2.h @@ -0,0 +1,224 @@ +/* + * $Logfile: /Freespace2/code/Network/Psnet2.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the application level network-interface. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 9 8/16/99 4:06p Dave + * Big honking checkin. + * + * 8 7/28/99 11:46a Dave + * Put in FS2_DEMO defines for port stuff. + * + * 7 6/25/99 11:59a Dave + * Multi options screen. + * + * 6 6/07/99 9:51p Dave + * Consolidated all multiplayer ports into one. + * + * 5 3/09/99 6:24p Dave + * More work on object update revamping. Identified several sources of + * unnecessary bandwidth. + * + * 4 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 3 11/19/98 8:04a Dave + * Full support for D3-style reliable sockets. Revamped packet lag/loss + * system, made it receiver side and at the lowest possible level. + * + * $NoKeywords: $ + */ + + +#ifndef _PSNET2_H +#define _PSNET2_H + +// ------------------------------------------------------------------------------------------------------- +// PSNET 2 DEFINES/VARS +// + +#define NET_NONE 0 // if no protocol is active or none are selected +#define NET_TCP 1 +#define NET_IPX 2 +#define NET_VMT 3 + +#define MAX_PACKET_SIZE 512 + +#ifdef FS2_DEMO + #define DEFAULT_GAME_PORT 7802 +#else + #define DEFAULT_GAME_PORT 7808 +#endif + +typedef struct net_addr { + uint type; // See NET_ defines above + ubyte net_id[4]; // used for IPX only + ubyte addr[6]; // address (first 4 used when IP, all 6 used when IPX) + short port; +} net_addr; + +// define these in such a manner that a call to psnet_send_reliable is exactly the same and the new code in unobtrusive +typedef uint PSNET_SOCKET; +typedef uint PSNET_SOCKET_RELIABLE; +#undef INVALID_SOCKET +#define INVALID_SOCKET (PSNET_SOCKET)(~0) + +// defines for protocol overheads +#define UDP_HEADER_SIZE 34 +#define TCP_HEADER_SIZE 40 +#define TCP_HEADER_SIZE_COMPRESSED 6 + +// define values for network errors when trying to enter the ready room +#define NETWORK_ERROR_NONE 0 +#define NETWORK_ERROR_NO_TYPE -1 +#define NETWORK_ERROR_NO_WINSOCK -2 +#define NETWORK_ERROR_NO_PROTOCOL -3 +#define NETWORK_ERROR_RELIABLE -4 +#define NETWORK_ERROR_CONNECT_TO_ISP -5 +#define NETWORK_ERROR_LAN_AND_RAS -6 + +// psnet packet types +#define PSNET_NUM_TYPES 5 +#define PSNET_TYPE_UNRELIABLE 0 +#define PSNET_TYPE_RELIABLE 1 +#define PSNET_TYPE_USER_TRACKER 2 +#define PSNET_TYPE_GAME_TRACKER 3 +#define PSNET_TYPE_VALIDATION 4 + +extern net_addr Psnet_my_addr; // address information of this machine +extern uint Psnet_my_ip; +extern int Psnet_my_addr_valid; + +extern int Network_status; +extern int Tcp_failure_code; +extern int Ipx_failure_code; + +extern int Tcp_active; +extern int Ipx_active; + +extern int Socket_type; // protocol type in use (see NET_* defines above) + +// specified their internet connnection type +#define NETWORK_CONNECTION_NONE 1 +#define NETWORK_CONNECTION_DIALUP 2 +#define NETWORK_CONNECTION_LAN 3 + +extern int Psnet_connection; + +extern ushort Psnet_default_port; + +// Reliable socket states +#define RNF_UNUSED 0 // Completely clean socket.. +#define RNF_CONNECTED 1 // Connected and running fine +#define RNF_BROKEN 2 // Broken - disconnected abnormally +#define RNF_DISCONNECTED 3 // Disconnected cleanly +#define RNF_CONNECTING 4 // We received the connecting message, but haven't told the game yet. +#define RNF_LIMBO 5 // between connecting and connected + +extern uint Unreliable_socket; // all PXO API modules should use this to send and receive on + +// ------------------------------------------------------------------------------------------------------- +// PSNET 2 TOP LAYER FUNCTIONS - these functions simply buffer and store packets based upon type (see PSNET_TYPE_* defines) +// + +struct sockaddr; +struct fd_set; +struct timeval; + +// wrappers around select() and recvfrom() for lagging/losing data, and for sorting through different packet types +int RECVFROM(uint s, char * buf, int len, int flags, sockaddr *from, int *fromlen, int psnet_type); +int SELECT(int nfds, fd_set *readfds, fd_set *writefds, fd_set*exceptfds, const timeval* timeout, int psnet_type); + +// wrappers around sendto to sorting through different packet types +int SENDTO(uint s, char * buf, int len, int flags, sockaddr * to, int tolen, int psnet_type); + +// call this once per frame to read everything off of our socket +void PSNET_TOP_LAYER_PROCESS(); + + +// ------------------------------------------------------------------------------------------------------- +// PSNET 2 FUNCTIONS +// + +// initialize psnet to use the specified port +void psnet_init(int protocol, int default_port); + +// shutdown psnet +void psnet_close(); + +// set the protocol to use +int psnet_use_protocol(int type); + +// get the status of the network +int psnet_get_network_status(); + +// convert a net_addr to a string +char *psnet_addr_to_string( char * text, net_addr * address ); + +// convert a string to a net addr +void psnet_string_to_addr( net_addr * address, char * text ); + +// compare 2 addresses +int psnet_same( net_addr * a1, net_addr * a2 ); + +// send data unreliably +int psnet_send( net_addr * who_to, void * data, int len, int np_index = -1 ); + +// get data from the unreliable socket +int psnet_get( void * data, net_addr * from_addr ); + +// broadcast data on unreliable socket +int psnet_broadcast( net_addr * who_to, void * data, int len ); + +// flush all sockets +void psnet_flush(); + +// if the passed string is a valid IP string +int psnet_is_valid_ip_string( char *ip_string, int allow_port=1 ); + +// mark a socket as having received data +void psnet_mark_received(PSNET_SOCKET_RELIABLE socket); + + +// ------------------------------------------------------------------------------------------------------- +// PSNET 2 RELIABLE SOCKET FUNCTIONS +// + +// shutdown a reliable socket +void psnet_rel_close_socket(PSNET_SOCKET_RELIABLE *sockp); + +// obsolete function - left in for compatibility sake +int psnet_rel_check(); + +// send data on the reliable socket +int psnet_rel_send(PSNET_SOCKET_RELIABLE socket, ubyte *data, int length, int np_index = -1); + +// Return codes: +// -1 socket not connected +// 0 No packet ready to receive +// >0 Buffer filled with the number of bytes recieved +int psnet_rel_get(PSNET_SOCKET_RELIABLE socket, ubyte *buffer, int max_length); + +// process all active reliable sockets +void psnet_rel_work(); + +// get the status of a reliable socket, see RNF_* defines above +int psnet_rel_get_status(PSNET_SOCKET_RELIABLE sock); + +// check the listen socket for pending reliable connections +int psnet_rel_check_for_listen(net_addr *addr); + +// perform a reliable socket connect to the specified server +void psnet_rel_connect_to_server(PSNET_SOCKET_RELIABLE *s, net_addr *server_addr); + +#endif + diff --git a/include/pstypes.h b/include/pstypes.h new file mode 100644 index 0000000..5812689 --- /dev/null +++ b/include/pstypes.h @@ -0,0 +1,693 @@ +/* + * $Logfile: /Freespace2/code/GlobalIncs/PsTypes.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file containg global typedefs, constants and macros + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 34 6/16/00 3:16p Jefff + * sim of the year dvd version changes, a few german soty localization + * fixes + * + * 33 10/05/99 2:29p Danw + * + * 32 10/01/99 9:10a Daveb + * V 1.1 PATCH + * + * 31 9/13/99 12:22a Dave + * Minor build update. + * + * 30 8/28/99 4:54p Dave + * Fixed directives display for multiplayer clients for wings with + * multiple waves. Fixed hud threat indicator rendering color. + * + * 29 8/09/99 4:18p Andsager + * Make french and german defines, needed specifically to enable language + * under launcher misc. tab + * + * 28 7/20/99 1:49p Dave + * Peter Drake build. Fixed some release build warnings. + * + * 27 7/18/99 5:19p Dave + * Jump node icon. Fixed debris fogging. Framerate warning stuff. + * + * 26 7/15/99 9:21a Andsager + * FS2_DEMO check in + * + * 25 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 24 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 23 6/14/99 10:45a Dave + * Made beam weapons specify accuracy by skill level in the weapons.tbl + * + * 22 6/03/99 10:15p Dave + * Put in temporary main hall screen. + * + * 21 5/09/99 8:57p Dave + * Final E3 build preparations. + * + * 20 4/25/99 7:43p Dave + * Misc small bug fixes. Made sun draw properly. + * + * 19 4/25/99 3:03p Dave + * Removed E3_BUILD from pstypes + * + * 18 4/25/99 3:02p Dave + * Build defines for the E3 build. + * + * 17 4/15/99 1:29p Dave + * Remove multiplayer beta build define. + * + * 16 4/15/99 1:24p Dave + * Final Beta 1 checkin. + * + * 15 4/14/99 5:28p Dave + * Minor bug fixes. + * + * 14 4/12/99 2:22p Dave + * More checks for dogfight stats. + * + * 13 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 12 2/25/99 4:19p Dave + * Added multiplayer_beta defines. Added cd_check define. Fixed a few + * release build warnings. Added more data to the squad war request and + * response packets. + * + * 11 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * 10 2/07/99 8:51p Andsager + * Add inner bound to asteroid field. Inner bound tries to stay astroid + * free. Wrap when within and don't throw at ships inside. + * + * 9 1/15/99 11:29a Neilk + * Fixed D3D screen/texture pixel formatting problem. + * + * 8 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * 7 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 6 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 5 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 4 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 3 10/07/98 11:28a Dave + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 75 8/20/98 5:30p Dave + * Put in handy multiplayer logfile system. Now need to put in useful + * applications of it all over the code. + * + * 74 6/17/98 11:01a Lawrance + * set correct offset for English special font chars + * + * 73 6/12/98 4:52p Hoffoss + * Added support for special characters in in forgeign languages. + * + * 72 6/09/98 6:49p Lawrance + * Comment out UK_BUILD + * + * 71 6/09/98 5:15p Lawrance + * French/German localization + * + * 70 6/09/98 12:12p Hoffoss + * Added XSTR localization code. + * + * 69 6/05/98 9:49a Lawrance + * OEM changes + * + * 68 5/22/98 3:13p Allender + * no Int3()'s and Asserts + * + * 67 5/20/98 12:59p John + * Turned optimizations on for debug builds. Also turning on automatic + * function inlining. Turned off the unreachable code warning. + * + * 66 5/04/98 1:49p Allender + * make Int3's do nothing when InterplayQA is defined + * + * 65 4/25/98 11:55p Lawrance + * compile out Int3() and Assert() for release demo build + * + * + * $NoKeywords: $ + * + */ + +#ifndef _PSTYPES_H +#define _PSTYPES_H + + +// Build defines. Comment in/out for whatever build is necessary: +// #define OEM_BUILD // enable for OEM builds +// #define MULTIPLAYER_BETA_BUILD // enable for multiplayer beta build +// #define E3_BUILD // enable for 3dfx E3 build +// #define PRESS_TOUR_BUILD // enable for press tour build +// #define FS2_DEMO // enable demo build for FS2 +// #define PD_BUILD // fred documentation/evaluation build +// #define FRENCH_BUILD // build for French (obsolete) +// #define GERMAN_BUILD // build for German (this is now used) +#define RELEASE_REAL // this means that it is an actual release candidate, not just an optimized/release build + +// uncomment this #define for DVD version (makes popups say DVD instead of CD 2 or whatever): JCF 5/10/2000 +// #define DVD_MESSAGE_HACK + + +#if defined(MULTIPLAYER_BETA_BUILD) || defined(E3_BUILD) || defined(RELEASE_REAL) + #define GAME_CD_CHECK +#endif + +// 4127 is constant conditional (assert) +// 4100 is unreferenced formal parameters, +// 4514 is unreferenced inline function removed, +// 4201 is nameless struct extension used. (used by windows header files) +// 4410 illegal size for operand... ie... fxch st(1) +// 4611 is _setjmp warning. Since we use setjmp alot, and we don't really use constructors or destructors, this warning doesn't really apply to us. +// 4725 is the pentium division bug warning, and I can't seem to get rid of it, even with this pragma. +// JS: I figured out the disabling 4725 works, but not on the first function in the module. +// So to disable this, I add in a stub function at the top of each module that does nothing. +// 4710 is inline function not expanded (who cares?) +// 4711 tells us an inline function was expanded (who cares?) +// 4702 unreachable code. I care, but too many to deal with +// 4201 nonstandard extension used : nameless struct/union (happens a lot in Windows include headers) +// 4390 emptry control statement (triggered by nprintf and mprintf's inside of one-line if's, etc) +#ifndef PLAT_UNIX +#pragma warning(disable: 4127 4100 4514 4201 4410 4611 4725 4710 4711 4702 4201 4390) +#endif + +#include // For NULL, etc +#include +#include +#include +#include + +#ifdef PLAT_UNIX +#include "unix.h" +#endif +// value to represent an uninitialized state in any int or uint +#define UNINITIALIZED 0x7f8e6d9c + +#if defined(DEMO) || defined(OEM_BUILD) // no change for FS2_DEMO + #define MAX_PLAYERS 1 +#else + #define MAX_PLAYERS 12 +#endif + +#define MAX_TEAMS 3 + +#define USE_INLINE_ASM 1 // Define this to use inline assembly +#define STRUCT_CMP(a, b) memcmp((void *) &a, (void *) &b, sizeof(a)) + +#define LOCAL static // make module local varilable static. + +typedef __int64 longlong; +typedef long fix; +typedef unsigned char ubyte; +typedef unsigned short ushort; +typedef unsigned int uint; + +#define HARDWARE_ONLY + +//Stucture to store clipping codes in a word +typedef struct ccodes { + ubyte vor,vand; //or is low byte, and is high byte +} ccodes; + +typedef struct vector { + union { + struct { + float x,y,z; + }; + float a1d[3]; + }; +} vector; + +// A vector referenced as an array +typedef struct vectora { + float xyz[3]; +} vectora; + +typedef struct vec2d { + float i,j; +} vec2d; + +// Used for some 2d primitives, like gr_poly +typedef struct vert2df { + float x, y; +} vert2df; + +typedef struct angles { + float p, b, h; +} angles_t; + +typedef struct matrix { + union { + struct { + vector rvec, uvec, fvec; + }; + float a2d[3][3]; + float a1d[9]; + }; +} matrix; + +typedef struct uv_pair { + float u,v; +} uv_pair; + +// Used to store rotated points for mines. +// Has flag to indicate if projected. +typedef struct vertex { + float x, y, z; // world space position + float sx, sy, sw; // screen space position (sw == 1/z) + float u, v; // texture position + ubyte r, g, b, a; // color. Use b for darkening; + ubyte codes; // what sides of view pyramid this point is on/off. 0 = Inside view pyramid. + ubyte flags; // Projection flags. Indicates whether it is projected or not or if projection overflowed. + ubyte pad[2]; // pad structure to be 4 byte aligned. +} vertex; + +#define BMP_AABITMAP (1<<0) // antialiased bitmap +#define BMP_TEX_XPARENT (1<<1) // transparent texture +#define BMP_TEX_NONDARK (1<<2) // nondarkening texture +#define BMP_TEX_OTHER (1<<3) // so we can identify all "normal" textures + +// any texture type +#define BMP_TEX_ANY ( BMP_TEX_XPARENT | BMP_TEX_NONDARK | BMP_TEX_OTHER ) + +// max res == 1024x768. max texture size == 256 +#define MAX_BMAP_SECTIONS_X 4 +#define MAX_BMAP_SECTIONS_Y 3 +#define MAX_BMAP_SECTION_SIZE 256 +typedef struct bitmap_section_info { + ushort sx[MAX_BMAP_SECTIONS_X]; // x offset of each section + ushort sy[MAX_BMAP_SECTIONS_Y]; // y offset of each section + + ubyte num_x, num_y; // number of x and y sections +} bitmap_section_info; + +typedef struct bitmap { + short w, h; // Width and height + short rowsize; // What you need to add to go to next row + ubyte bpp; // How many bits per pixel it is. (7,8,15,16,24,32) + ubyte flags; // See the BMP_???? defines for values + uint data; // Pointer to data, or maybe offset into VRAM. + ubyte *palette; // If bpp==8, this is pointer to palette. If the BMP_NO_PALETTE_MAP flag + // is not set, this palette just points to the screen palette. (gr_palette) + + bitmap_section_info sections; +} bitmap; + +//This are defined in MainWin.c +extern void _cdecl WinAssert(char * text,char *filename, int line); +extern void _cdecl Error( char * filename, int line, char * format, ... ); +extern void _cdecl Warning( char * filename, int line, char * format, ... ); + +#include "outwnd.h" + +// To debug printf do this: +// mprintf(( "Error opening %s\n", filename )); +#ifndef NDEBUG +#define mprintf(args) outwnd_printf2 args +#define nprintf(args) outwnd_printf args +#else +#define mprintf(args) +#define nprintf(args) +#endif + +#define LOCATION __FILE__,__LINE__ + +// To flag an error, you can do this: +// Error( __FILE__, __LINE__, "Error opening %s", filename ); +// or, +// Error( LOCATION, "Error opening %s", filename ); + +#if defined(NDEBUG) +#define Assert(x) do {} while (0) +#else +void gr_activate(int); +#define Assert(x) do { if (!(x)){ gr_activate(0); WinAssert(#x,__FILE__,__LINE__); gr_activate(1); } } while (0) +#endif + +//#define Int3() _asm { int 3 } + +#ifdef INTERPLAYQA + // Interplay QA version of Int3 + #define Int3() do { } while (0) + + // define to call from Warning function above since it calls Int3, so without this, we + // get put into infinite dialog boxes + #define AsmInt3() _asm { int 3 } + +#else + #if defined(NDEBUG) + // No debug version of Int3 + #define Int3() do { } while (0) + #else + void debug_int3(); + + // Debug version of Int3 + #define Int3() debug_int3() + #endif // NDEBUG && DEMO +#endif // INTERPLAYQA + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) > (b)) ? (a) : (b)) + +#define PI 3.141592654f +#define PI2 (3.141592654f*2.0f) // PI*2 +#define ANG_TO_RAD(x) ((x)*PI/180) + + +extern int Fred_running; // Is Fred running, or FreeSpace? +extern int Pofview_running; +extern int Nebedit_running; + + +//====================================================================================== +//====== D E B U G C O N S O L E S T U F F ======================== +//====================================================================================== + +// Here is a a sample command to toggle something that would +// be called by doing "toggle it" from the debug console command window/ + +/* +DCF(toggle_it,"description") +{ + if (Dc_command) { + This_var = !This_var; + } + + if (Dc_help) { + dc_printf( "Usage: sample\nToggles This_var on/off.\n" ); + } + + if (Dc_status) { + dc_printf( "The status is %d.\n", This_var ); + } +*/ + +class debug_command { + public: + char *name; + char *help; + void (*func)(); + debug_command(char *name,char *help,void (*func)()); // constructor +}; + +#define DCF(function_name,help_text) \ + void dcf_##function_name(); \ + debug_command dc_##function_name(#function_name,help_text,dcf_##function_name); \ + void dcf_##function_name() + +// Starts the debug console +extern void debug_console( void (*func)() = NULL ); + +// The next three variables tell your function what to do. It should +// only change something if the dc_command is set. A minimal function +// needs to process the dc_command. Usually, these will be called in +// these combinations: +// dc_command=true, dc_status=true means process it and show status +// dc_help=true means show help only +// dc_status=true means show status only +// I would recommend doing this in each function: +// if (dc_command) { process command } +// if (dc_help) { print out help } +// if (dc_status) { print out status } +// with the last two being optional + +extern int Dc_command; // If this is set, then process the command +extern int Dc_help; // If this is set, then print out the help text in the form, "usage: ... \nLong description\n" ); +extern int Dc_status; // If this is set, then print out the current status of the command. + +void dc_get_arg(uint flags); // Gets the next argument. If it doesn't match the flags, this function will print an error and not return. +extern char *Dc_arg; // The (lowercased) string value of the argument retrieved from dc_arg +extern char *Dc_arg_org; // Dc_arg before it got converted to lowercase +extern uint Dc_arg_type; // The type of dc_arg. +extern char *Dc_command_line; // The rest of the command line, from the end of the last processed arg on. +extern int Dc_arg_int; // If Dc_arg_type & ARG_INT or ARG_HEX is set, then this is the value +extern float Dc_arg_float; // If Dc_arg_type & ARG_FLOAT is set, then this is the value + +// Outputs text to the console +void dc_printf( char *format, ... ); + +// Each dc_arg_type can have one or more of these flags set. +// This is because some things can fit into two catagories. +// Like 1 can be an integer, a float, a string, or a true boolean +// value. +#define ARG_NONE (1<<0) // no argument +#define ARG_ANY 0xFFFFFFFF // Anything. +#define ARG_STRING (1<<1) // any valid string +#define ARG_QUOTE (1<<2) // a quoted string +#define ARG_INT (1<<3) // a valid integer +#define ARG_FLOAT (1<<4) // a valid floating point number + +// some specific commonly used predefined types. Can add up to (1<<31) +#define ARG_HEX (1<<5) // a valid hexadecimal integer. Note that ARG_INT will always be set also in this case. +#define ARG_TRUE (1<<6) // on, true, non-zero number +#define ARG_FALSE (1<<7) // off, false, zero +#define ARG_PLUS (1<<8) // Plus sign +#define ARG_MINUS (1<<9) // Minus sign +#define ARG_COMMA (1<<10) // a comma + +// A shortcut for boolean only variables. +// Example: +// DCF_BOOL( lighting, Show_lighting ) +// +#define DCF_BOOL( function_name, bool_variable ) \ + void dcf_##function_name(); \ + debug_command dc_##function_name(#function_name,"Toggles "#bool_variable,dcf_##function_name ); \ + void dcf_##function_name() { \ + if ( Dc_command ) { \ + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); \ + if ( Dc_arg_type & ARG_TRUE ) bool_variable = 1; \ + else if ( Dc_arg_type & ARG_FALSE ) bool_variable = 0; \ + else if ( Dc_arg_type & ARG_NONE ) bool_variable ^= 1; \ + } \ + if ( Dc_help ) dc_printf( "Usage: %s [bool]\nSets %s to true or false. If nothing passed, then toggles it.\n", #function_name, #bool_variable ); \ + if ( Dc_status ) dc_printf( "%s is %s\n", #function_name, (bool_variable?"TRUE":"FALSE") ); \ +} + + +//====================================================================================== +//====================================================================================== +//====================================================================================== + + + +#include "fix.h" +#include "floating.h" + +// Some constants for stuff +#define MAX_FILENAME_LEN 32 // Length for filenames, ie "title.pcx" +#define MAX_PATH_LEN 128 // Length for pathnames, ie "c:\bitmaps\title.pcx" + +// contants and defined for byteswapping routines (useful for mac) + +#define SWAPSHORT(x) ( \ + ((ubyte)x << 8) | \ + (((ushort)x) >> 8) \ + ) + +#define SWAPINT(x) ( \ + (x << 24) | \ + (((ulong)x) >> 24) | \ + ((x & 0x0000ff00) << 8) | \ + ((x & 0x00ff0000) >> 8) \ + ) + +#ifndef MACINTOSH +#define INTEL_INT(x) x +#define INTEL_SHORT(x) x +#else +#define INTEL_INT(x) SWAPINT(x) +#define INTEL_SHORT(x) SWAPSHORT(x) +#endif + +#define TRUE 1 +#define FALSE 0 + +int myrand(); + + + +// Callback Loading function. +// If you pass a function to this, that function will get called +// around 10x per second, so you can update the screen. +// Pass NULL to turn it off. +// Call this with the name of a function. That function will +// then get called around 10x per second. The callback function +// gets passed a 'count' which is how many times game_busy has +// been called since the callback was set. It gets called +// one last time with count=-1 when you turn off the callback +// by calling game_busy_callback(NULL). Game_busy_callback +// returns the current count, so you can tell how many times +// game_busy got called. +// If delta_step is above 0, then it will also make sure it +// calls the callback each time count steps 'delta_step' even +// if 1/10th of a second hasn't elapsed. +extern int game_busy_callback( void (*callback)(int count), int delta_step = -1 ); + +// Call whenever loading to display cursor +extern void game_busy(); + + +//========================================================= +// Functions to monitor performance +#ifndef NDEBUG + +class monitor { + public: + char *name; + int value; // Value that gets cleared to 0 each frame. + int min, max, sum, cnt; // Min & Max of value. Sum is used to calculate average + monitor(char *name); // constructor +}; + +// Creates a monitor variable +#define MONITOR(function_name) monitor mon_##function_name(#function_name) + +// Increments a monitor variable +#define MONITOR_INC(function_name,inc) do { mon_##function_name.value+=(inc); } while(0) + +// Call this once per frame to update monitor file +void monitor_update(); + +#else + +#define MONITOR(function_name) + +#define MONITOR_INC(function_name,inc) do { } while(0) + +// Call this once per frame to update monitor file +#define monitor_update() do { } while(0) + +#endif + +#define NOX(s) s + +char *XSTR(char *str, int index); + +// Caps V between MN and MX. +template void CAP( T& v, T mn, T mx ) +{ + if ( v < mn ) { + v = mn; + } else if ( v > mx ) { + v = mx; + } +} + +// ======================================================== +// stamp checksum stuff +// ======================================================== + +// here is the define for the stamp for this set of code +#define STAMP_STRING "\001\001\001\001\002\002\002\002Read the Foundation Novels from Asimov. I liked them." +#define STAMP_STRING_LENGTH 80 +#define DEFAULT_CHECKSUM_STRING "\001\001\001\001" +#define DEFAULT_TIME_STRING "\002\002\002\002" + +// macro to calculate the checksum for the stamp. Put here so that we can use different methods +// for different applications. Requires the local variable 'checksum' to be defined!!! +#define CALCULATE_STAMP_CHECKSUM() do { \ + int i, found; \ + \ + checksum = 0; \ + for ( i = 0; i < (int)strlen(ptr); i++ ) { \ + found = 0; \ + checksum += ptr[i]; \ + if ( checksum & 0x01 ) \ + found = 1; \ + checksum = checksum >> 1; \ + if (found) \ + checksum |= 0x80000000; \ + } \ + checksum |= 0x80000000; \ + } while (0) ; + +//========================================================= +// Memory management functions +//========================================================= + +#ifndef NDEBUG + // Debug versions + + // Returns 0 if not enough RAM. + int vm_init(int min_heap_size); + + // Allocates some RAM. + void *vm_malloc( int size, char *filename=NULL, int line=-1 ); + + // + char *vm_strdup( const char *ptr, char *filename, int line ); + + // Frees some RAM. + void vm_free( void *ptr, char *filename=NULL, int line=-1 ); + + // Frees all RAM. + void vm_free_all(); + + // Easy to use macros + #define VM_MALLOC(size) vm_malloc((size),__FILE__,__LINE__) + #define VM_FREE(ptr) vm_free((ptr),__FILE__,__LINE__) + + #define malloc(size) vm_malloc((size),__FILE__,__LINE__) + #define free(ptr) vm_free((ptr),__FILE__,__LINE__) + #define strdup(ptr) vm_strdup((ptr),__FILE__,__LINE__) + +#else + // Release versions + + // Returns 0 if not enough RAM. + int vm_init(int min_heap_size); + + // Allocates some RAM. + void *vm_malloc( int size ); + + // + char *vm_strdup( const char *ptr ); + + // Frees some RAM. + void vm_free( void *ptr ); + + // Frees all RAM. + void vm_free_all(); + + // Easy to use macros + #define VM_MALLOC(size) vm_malloc(size) + #define VM_FREE(ptr) vm_free(ptr) + + #define malloc(size) vm_malloc(size) + #define free(ptr) vm_free(ptr) + #define strdup(ptr) vm_strdup(ptr) +#endif + + +#endif // PS_TYPES_H + diff --git a/include/radar.h b/include/radar.h new file mode 100644 index 0000000..eb33bad --- /dev/null +++ b/include/radar.h @@ -0,0 +1,59 @@ +/* + * $Logfile: /Freespace2/code/Radar/Radar.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Prototypes for radar code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 10 12/29/97 4:22p Lawrance + * Draw NEUTRAL radar dots after FRIENDLY.. clean up some radar code. + * + * 9 11/06/97 5:02p Dave + * Finished reworking standalone multiplayer sequencing. Added + * configurable observer-mode HUD + * + * 8 11/05/97 11:21p Lawrance + * implement new radar gauge + * + * 7 10/11/97 6:38p Lawrance + * implementing damage effects to radar + * + * 6 3/25/97 3:55p Lawrance + * allowing debris to be targeted and shown on radar + * + * 5 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _RADAR_H +#define _RADAR_H + +extern int Radar_static_looping; + +extern void radar_init(); +extern void radar_plot_object( object *objp ); +extern void radar_frame_init(); +extern void radar_mission_init(); +extern void radar_frame_render(float frametime); + +// observer hud rendering code uses this function +void radar_draw_blips_sorted(int distort=0); +void radar_draw_range(); +void radar_blit_gauge(); + +#endif + diff --git a/include/rbaudio.h b/include/rbaudio.h new file mode 100644 index 0000000..5bdb7e8 --- /dev/null +++ b/include/rbaudio.h @@ -0,0 +1,74 @@ +/* + * $Logfile: /Freespace2/code/Sound/RBAudio.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * header for redbook audio playback + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 1 4/28/97 4:45p John + * Initial version of ripping sound & movie out of OsAPI. + * + * 2 1/30/97 9:57a Allender + * basic Redbook audio implemented. + * + * 1 1/28/97 9:54a Allender + * + * $NoKeywords: $ +*/ + +#ifndef _RBAUDIO_H +#define _RBAUDIO_H + +#define RBA_MEDIA_CHANGED -1 + +typedef struct _RBACHANNELCTL { + unsigned int out0in, out0vol; + unsigned int out1in, out1vol; + unsigned int out2in, out2vol; + unsigned int out3in, out3vol; +} RBACHANNELCTL; + + +// mwa ??#if defined(__NT__) +extern void RBAInit(void); //drive a == 0, drive b == 1 +// mwa ??#else +// mwa ?? extern void RBAInit(ubyte cd_drive_num); //drive a == 0, drive b == 1 +// mwa ??#endif +extern void RBClose(void); +extern void RBARegisterCD(void); +extern long RBAGetDeviceStatus(void); +extern int RBAPlayTrack(int track); +extern int RBAPlayTracks(int first, int last); //plays tracks first through last, inclusive +extern int RBACheckMediaChange(); +extern long RBAGetHeadLoc(int *min, int *sec, int *frame); +extern int RBAPeekPlayStatus(void); +extern void RBAStop(void); +extern void RBASetStereoAudio(RBACHANNELCTL *channels); +extern void RBASetQuadAudio(RBACHANNELCTL *channels); +extern void RBAGetAudioInfo(RBACHANNELCTL *channels); +extern void RBASetChannelVolume(int channel, int volume); +extern void RBASetVolume(int volume); +extern int RBAEnabled(void); +extern void RBADisable(void); +extern void RBAEnable(void); +extern int RBAGetNumberOfTracks(void); +extern void RBAPause(); +extern int RBAResume(); + +//return the track number currently playing. Useful if RBAPlayTracks() +//is called. Returns 0 if no track playing, else track number +int RBAGetTrackNum(); + +#endif + diff --git a/include/readyroom.h b/include/readyroom.h new file mode 100644 index 0000000..055c8db --- /dev/null +++ b/include/readyroom.h @@ -0,0 +1,46 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/ReadyRoom.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Ready Room code, which is the UI screen for selecting Campaign/mission to play next mainly. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 5 4/02/98 5:40p Hoffoss + * Added the Load Mission screen to FreeSpace. + * + * 4 3/02/98 5:22p Hoffoss + * Removed ready room and added campaign room. + * + * 3 3/02/98 3:39p Hoffoss + * Added new Campaign Room screen. + * + * 2 11/16/97 1:11p Hoffoss + * Coded up readyroom screen, first pass. + * + * 1 11/15/97 7:30p Hoffoss + * + * $NoKeywords: $ + */ + +void sim_room_init(); +void sim_room_close(); +void sim_room_do_frame(float frametime); + +// called by main menu to continue on with current campaign (if there is one). +int readyroom_continue_campaign(); + +void campaign_room_init(); +void campaign_room_close(); +void campaign_room_do_frame(float frametime); + diff --git a/include/rect.h b/include/rect.h new file mode 100644 index 0000000..5012998 --- /dev/null +++ b/include/rect.h @@ -0,0 +1,38 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Rect.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for Rect.h + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 2 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 1 10/26/96 1:32p John + * Initial rev + * + * $NoKeywords: $ + */ + +#ifndef _RECT_H +#define _RECT_H + +extern void grx_rect(int x,int y,int w,int h); + +#endif + diff --git a/include/redalert.h b/include/redalert.h new file mode 100644 index 0000000..b637074 --- /dev/null +++ b/include/redalert.h @@ -0,0 +1,66 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/RedAlert.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for Red Alert mission interface and code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 7 5/05/98 6:19p Lawrance + * Fix problems with "retry mission" for red alerts + * + * 6 5/04/98 6:06p Lawrance + * Make red alert mode work! + * + * 5 3/28/98 2:53p Allender + * added hud gauge when entering a red alert mission + * + * 4 3/09/98 4:30p Allender + * multiplayer secondary weapon changes. red-alert and cargo-known-delay + * sexpressions. Add time cargo revealed to ship structure + * + * 3 3/09/98 4:23p Lawrance + * Replay mission, full save/restore of wingman status + * + * 2 3/09/98 12:13a Lawrance + * Add support for Red Alert missions + * + * 1 3/08/98 4:54p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __REDALERT_H__ +#define __REDALERT_H__ + +#include "cfile.h" + +void red_alert_start_mission(); + +void red_alert_init(); +void red_alert_close(); +void red_alert_do_frame(float frametime); +int red_alert_mission(); +void red_alert_set_status(int status); +int red_alert_check_status(); + +void red_alert_store_wingman_status(); +void red_alert_bash_wingman_status(); +void red_alert_write_wingman_status(CFILE *fp); +void red_alert_read_wingman_status(CFILE *fp, int version); + +#endif + diff --git a/include/reinforcementeditordlg.h b/include/reinforcementeditordlg.h new file mode 100644 index 0000000..d192977 --- /dev/null +++ b/include/reinforcementeditordlg.h @@ -0,0 +1,147 @@ +/* + * $Logfile: /Freespace2/code/FRED2/ReinforcementEditorDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Reinforcements editor dialog handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 9 5/23/98 3:33p Hoffoss + * Removed unused code in reinforcements editor and make ships.tbl button + * in ship editor disappear in release build. + * + * 8 7/16/97 11:02p Allender + * added messaging for reinforcements. One (or one of several) can now + * play if reinforcement are not yet available, or when they are arriving + * + * 7 5/20/97 2:29p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 6 4/29/97 3:02p Hoffoss + * Reinforcement type is now automatically handled by Fred. + * + * 5 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 4 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 3 2/04/97 3:10p Hoffoss + * Reinforcements editor fully implemented. + * + * 2 2/03/97 1:32p Hoffoss + * Reinforcement editor functional, but still missing a few options. + * Checking in good code now prior to experimenting, so I can revert if + * needed. + * + * $NoKeywords: $ + */ + +#include "ship.h" + +///////////////////////////////////////////////////////////////////////////// +// reinforcement_editor_dlg dialog + +class reinforcement_editor_dlg : public CDialog +{ +// Construction +public: + int query_modified(); + void OnOK(); + void OnCancel(); + void save_data(); + void update_data(); + reinforcement_editor_dlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(reinforcement_editor_dlg) + enum { IDD = IDD_REINFORCEMENT_EDITOR }; + CSpinButtonCtrl m_delay_spin; + CSpinButtonCtrl m_uses_spin; + int m_uses; + int m_delay; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(reinforcement_editor_dlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + void remove_selected( CListBox *box ); + void move_messages( CListBox *box ); + + // Generated message map functions + //{{AFX_MSG(reinforcement_editor_dlg) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangeList(); + afx_msg void OnAdd(); + afx_msg void OnDelete(); + afx_msg void OnClose(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +private: + int m_num_reinforcements; + reinforcements m_reinforcements[MAX_REINFORCEMENTS]; + int cur; +}; + +///////////////////////////////////////////////////////////////////////////// +// reinforcement_select dialog + +class reinforcement_select : public CDialog +{ +// Construction +public: + int cur; + char name[NAME_LENGTH]; + reinforcement_select(CWnd* pParent = NULL); // standard constructor + void OnOK(); + void OnCancel(); + +// Dialog Data + //{{AFX_DATA(reinforcement_select) + enum { IDD = IDD_REINFORCEMENT_SELECT }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(reinforcement_select) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(reinforcement_select) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangeList(); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/resource.h b/include/resource.h new file mode 100644 index 0000000..ea8189d --- /dev/null +++ b/include/resource.h @@ -0,0 +1,61 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Pofview.rc +// +#define IDD_ABOUTBOX 100 +#define IDR_MAINFRAME 128 +#define IDR_POFTYPE 129 +#define IDD_DIALOG1 130 +#define IDC_TREE1 1000 +#define IDC_TXT_NUMPOLIES 1001 +#define IDC_TXT_NUM_VERTICES 1002 +#define IDC_TXT_NAME 1003 +#define IDC_TXT_MOVEMENT_TYPE 1004 +#define IDC_TXT_MOVEMENT_AXIS 1005 +#define IDC_TXT_BSPGEN_VERSION 1007 +#define IDC_TXT_DETAIIL1 1008 +#define IDC_TXT_DETAIL2 1009 +#define IDC_TXT_DETAIL3 1010 +#define IDC_TXT_DETAIL4 1011 +#define IDC_TXT_DETAIL5 1012 +#define IDC_TXT_DETAIL6 1013 +#define IDC_TXT_POF_INFO 1014 +#define ID_VIEW_OUTLINE 32772 +#define ID_POF_OUTLINE 32773 +#define ID_POF_LIGHTING 32774 +#define ID_POF_PIVOTS 32775 +#define ID_DETAIL_1 32776 +#define ID_DETAIL_2 32777 +#define ID_DETAIL_3 32778 +#define ID_DETAIL_4 32779 +#define ID_DETAIL_5 32780 +#define ID_DETAIL_6 32781 +#define ID_SHOW_TREE 32782 +#define ID_POF_PATHS 32783 +#define ID_POF_RADIUS 32784 +#define ID_POF_OVERWRITE 32785 +#define IDC_SHOW_DAMAGED_SUBOBJECTS 32786 +#define IDC_SHOW_DEBRIS 32787 +#define ID_POF_TEXTURING 32788 +#define ID_POF_SMOOTHING 32789 +#define ID_POF_SHIELDS 32790 +#define IDC_TOGGLE_LIGHTING 32791 +#define IDC_TURRENT_TURNING 32792 +#define ID_TURRET_TURNING 32793 +#define ID_INVISIBLEFACES 32794 +#define ID_TURRET_NORMALS 32795 +#define ID_BAY_PATHS 32796 +#define ID_AUTOCEN 32797 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_3D_CONTROLS 1 +#define _APS_NEXT_RESOURCE_VALUE 131 +#define _APS_NEXT_COMMAND_VALUE 32798 +#define _APS_NEXT_CONTROL_VALUE 1015 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif + diff --git a/include/rsx.h b/include/rsx.h new file mode 100644 index 0000000..fefe540 --- /dev/null +++ b/include/rsx.h @@ -0,0 +1,612 @@ +/* +// RSX.H +// +// INTEL CORPORATION PROPRIETARY INFORMATION +// This software is supplied under the terms of a license agreement or +// nondisclosure agreement with Intel Corporation and may not be copied +// or disclosed except in accordance with the terms of that agreement. +// +// Copyright (c) 1996, 1997 Intel Corporation. All Rights Reserved. +// +// PVCS: +// $Workfile: rsx.h $ +// $Revision$ +// $Modtime: 8/25/98 2:00p $ +// +// PURPOSE: +// RSX 3D Header +// +// CONTENTS: +// COM Interface declarations +// Data Structures +// Error Codes +// +*/ + + +#ifndef __RSX20_INCLUDED__ +#define __RSX20_INCLUDED__ +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#else +#define IUnknown void +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mmsystem.h" + +#ifdef _WIN32 + +/* {E78F7620-96CB-11cf-A00B-444553540000} */ +DEFINE_GUID(CLSID_RSX20, + 0xe78f7620, 0x96cb, 0x11cf, 0xa0, 0xb, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); + +/* {4B2CE920-1C45-11d0-985D-00AA003B43AF} */ +DEFINE_GUID(CLSID_RSXCACHEDEMITTER, + 0x4b2ce920, 0x1c45, 0x11d0, 0x98, 0x5d, 0x0, 0xaa, 0x0, 0x3b, 0x43, 0xaf); + +/* {4B2CE921-1C45-11d0-985D-00AA003B43AF} */ +DEFINE_GUID(CLSID_RSXSTREAMINGEMITTER, + 0x4b2ce921, 0x1c45, 0x11d0, 0x98, 0x5d, 0x0, 0xaa, 0x0, 0x3b, 0x43, 0xaf); + +/* {4B2CE922-1C45-11d0-985D-00AA003B43AF} */ +DEFINE_GUID(CLSID_RSXDIRECTLISTENER, + 0x4b2ce922, 0x1c45, 0x11d0, 0x98, 0x5d, 0x0, 0xaa, 0x0, 0x3b, 0x43, 0xaf); + +/* {4B2CE923-1C45-11d0-985D-00AA003B43AF} */ +DEFINE_GUID(CLSID_RSXSTREAMINGLISTENER, + 0x4b2ce923, 0x1c45, 0x11d0, 0x98, 0x5d, 0x0, 0xaa, 0x0, 0x3b, 0x43, 0xaf); + +/* {E78F7629-96CB-11cf-A00B-444553540000} */ +DEFINE_GUID(IID_IRSX20, + 0xe78f7629, 0x96cb, 0x11cf, 0xa0, 0xb, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); + +/* {B5B40681-1AC8-11d0-985D-00AA003B43AF} */ +DEFINE_GUID(IID_IRSX2, + 0xb5b40681, 0x1ac8, 0x11d0, 0x98, 0x5d, 0x0, 0xaa, 0x0, 0x3b, 0x43, 0xaf); + +/* {E78F762D-96CB-11cf-A00B-444553540000} */ +DEFINE_GUID(IID_IRSXCachedEmitter, + 0xe78f762d, 0x96cb, 0x11cf, 0xa0, 0xb, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); + + +/* {E78F7630-96CB-11cf-A00B-444553540000} */ +DEFINE_GUID(IID_IRSXStreamingEmitter, + 0xe78f7630, 0x96cb, 0x11cf, 0xa0, 0xb, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); + + +/* {E78F7634-96CB-11cf-A00B-444553540000} */ +DEFINE_GUID(IID_IRSXDirectListener, + 0xe78f7634, 0x96cb, 0x11cf, 0xa0, 0xb, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); + + +/* {E78F7638-96CB-11cf-A00B-444553540000} */ +DEFINE_GUID(IID_IRSXStreamingListener, + 0xe78f7638, 0x96cb, 0x11cf, 0xa0, 0xb, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); + + +#endif + +typedef struct IRSX FAR *LPRSX; +typedef struct IRSX2 FAR *LPRSX2; +typedef struct IRSXCachedEmitter FAR *LPRSXCACHEDEMITTER; +typedef struct IRSXDirectListener FAR *LPRSXDIRECTLISTENER; +typedef struct IRSXStreamingEmitter FAR *LPRSXSTREAMINGEMITTER; +typedef struct IRSXStreamingListener FAR *LPRSXSTREAMINGLISTENER; + +typedef struct _RSXBUFFERHDR FAR *LPRSXBUFFERHDR; +typedef struct _RSXCACHEDEMITTERDESC FAR *LPRSXCACHEDEMITTERDESC; +typedef struct _RSXDIRECTLISTENERDESC FAR *LPRSXDIRECTLISTENERDESC; +typedef struct _RSXEMITTERMODEL FAR *LPRSXEMITTERMODEL; +typedef struct _RSXENVIRONMENT FAR *LPRSXENVIRONMENT; +typedef struct _RSXQUERYMEDIAINFO FAR *LPRSXQUERYMEDIAINFO; +typedef struct _RSXREVERBMODEL FAR *LPRSXREVERBMODEL; +typedef struct _RSXSTREAMINGEMITTERDESC FAR *LPRSXSTREAMINGEMITTERDESC; +typedef struct _RSXSTREAMINGLISTENERDESC FAR *LPRSXSTREAMINGLISTENERDESC; +typedef struct _RSXVECTOR3D FAR *LPRSXVECTOR3D; + + +#define RSX_MAX_NAME_LEN (MAX_PATH) + + +/* +// Enumerated Types +*/ + +enum RSX_CPU_Budget { RSX_MIN_BUDGET, RSX_LOW_BUDGET, RSX_MODERATE_BUDGET }; + + +#ifdef _WIN32 +#undef INTERFACE +#define INTERFACE IRSX +DECLARE_INTERFACE_( IRSX, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG,Release) (THIS) PURE; + + + /*** IRSX methods ***/ + STDMETHOD(CreateCachedEmitter)(THIS_ LPRSXCACHEDEMITTERDESC lpCachedEmitterAttr, + LPRSXCACHEDEMITTER FAR *lpCachedEmitterInterface, + IUnknown FAR *reserved) PURE; + + STDMETHOD(CreateDirectListener)(THIS_ LPRSXDIRECTLISTENERDESC lpDirectListenerAttr, + LPRSXDIRECTLISTENER FAR *lpDirectListenerInterface, + IUnknown FAR *reserved) PURE; + + STDMETHOD(CreateStreamingEmitter)( THIS_ LPRSXSTREAMINGEMITTERDESC lpStreamingEmitterAttr, + LPRSXSTREAMINGEMITTER FAR *lpStreamingEmitterInterface, + IUnknown FAR *reserved) PURE; + + STDMETHOD(CreateStreamingListener)( THIS_ LPRSXSTREAMINGLISTENERDESC lpStreamingListenerAttr, + LPRSXSTREAMINGLISTENER FAR *lpStreamingListenerInterface, + IUnknown FAR *reserved) PURE; + + STDMETHOD(GetEnvironment)(THIS_ LPRSXENVIRONMENT lpEnvAttr) PURE; + + STDMETHOD(GetReverb)(THIS_ LPRSXREVERBMODEL lpReverbModel) PURE; + + STDMETHOD(SetEnvironment)(THIS_ LPRSXENVIRONMENT lpEnvAttr) PURE; + + STDMETHOD(SetReverb)(THIS_ LPRSXREVERBMODEL lpReverbModel) PURE; + +}; +#endif + +#ifdef _WIN32 +#undef INTERFACE +#define INTERFACE IRSX2 +DECLARE_INTERFACE_( IRSX2, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG,Release) (THIS) PURE; + + /* IRSX2 */ + STDMETHOD(GetEnvironment)(THIS_ LPRSXENVIRONMENT lpEnvAttr) PURE; + + STDMETHOD(GetReverb)(THIS_ LPRSXREVERBMODEL lpReverbModel) PURE; + + STDMETHOD(SetEnvironment)(THIS_ LPRSXENVIRONMENT lpEnvAttr) PURE; + + STDMETHOD(SetReverb)(THIS_ LPRSXREVERBMODEL lpReverbModel) PURE; + +}; +#endif + + + + +/* +// Flags for IRSXCachedEmitter::ControlMedia +// +*/ +#define RSX_PLAY 0x00000010 +#define RSX_PAUSE 0x00000020 +#define RSX_RESUME 0x00000040 +#define RSX_STOP 0x00000050 + + + +#ifdef _WIN32 +#undef INTERFACE +#define INTERFACE IRSXCachedEmitter +DECLARE_INTERFACE_( IRSXCachedEmitter, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG,Release) (THIS) PURE; + + + /*** IRSXEmitter methods ***/ + + STDMETHOD(GetCPUBudget)(THIS_ enum RSX_CPU_Budget* lpCPUBudget) PURE; + + STDMETHOD(GetModel)(THIS_ LPRSXEMITTERMODEL lpEmitterModel) PURE; + + STDMETHOD(GetMuteState)(THIS_ LPDWORD lpdwMuteState) PURE; + + STDMETHOD(GetOrientation)(THIS_ LPRSXVECTOR3D lpOrientation) PURE; + + STDMETHOD(GetPitch)(THIS_ PFLOAT lpfPitch) PURE; + + STDMETHOD(GetUserData)(THIS_ LPDWORD lpdwUser) PURE; + + STDMETHOD(GetPosition)(THIS_ LPRSXVECTOR3D lpPosition) PURE; + + STDMETHOD(QueryMediaState)(THIS_ LPRSXQUERYMEDIAINFO lpQueryMediaInfo) PURE; + + STDMETHOD(SetCPUBudget)(THIS_ enum RSX_CPU_Budget CPUBudget) PURE; + + STDMETHOD(SetModel)(THIS_ LPRSXEMITTERMODEL lpEmitterModel) PURE; + + STDMETHOD(SetMuteState)(THIS_ DWORD dwMuteState) PURE; + + STDMETHOD(SetOrientation)(THIS_ LPRSXVECTOR3D lpOrientation) PURE; + + STDMETHOD(SetPitch)(THIS_ FLOAT fPitch) PURE; + + STDMETHOD(SetPosition)(THIS_ LPRSXVECTOR3D lpPosition) PURE; + + + /*** IRSXCachedEmitter methods ***/ + STDMETHOD(ControlMedia)(THIS_ DWORD dwControl, + DWORD nLoops, + FLOAT fInitialStartTime) PURE; + + STDMETHOD(GetCacheTime)(THIS_ PFLOAT lpfCacheTime) PURE; + + STDMETHOD(GetMarkPosition)( THIS_ PFLOAT lpfBeginTime, + PFLOAT lpfEndTime) PURE; + + STDMETHOD(SetCacheTime)(THIS_ FLOAT fCacheTime) PURE; + + STDMETHOD(SetMarkPosition)(THIS_ FLOAT fBeginTime, FLOAT fEndTime) PURE; + + STDMETHOD(Initialize)( THIS_ LPRSXCACHEDEMITTERDESC lpCachedEmitterAttr, + LPUNKNOWN pUnk) PURE; + + +}; +#endif + + + +#ifdef _WIN32 +#undef INTERFACE +#define INTERFACE IRSXStreamingEmitter +DECLARE_INTERFACE_( IRSXStreamingEmitter, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG,Release) (THIS) PURE; + + /*** IRSXEmitter methods ***/ + STDMETHOD(GetCPUBudget)(THIS_ enum RSX_CPU_Budget* lpCPUBudget) PURE; + + STDMETHOD(GetModel)(THIS_ LPRSXEMITTERMODEL lpEmitterModel) PURE; + + STDMETHOD(GetMuteState)(THIS_ LPDWORD lpdwMuteState) PURE; + + STDMETHOD(GetOrientation)(THIS_ LPRSXVECTOR3D lpOrientation) PURE; + + STDMETHOD(GetPitch)(THIS_ PFLOAT lpfPitch) PURE; + + STDMETHOD(GetUserData)(THIS_ LPDWORD lpdwUser) PURE; + + STDMETHOD(GetPosition)(THIS_ LPRSXVECTOR3D lpPosition) PURE; + + STDMETHOD(QueryMediaState)(THIS_ LPRSXQUERYMEDIAINFO lpQueryMediaInfo) PURE; + + STDMETHOD(SetCPUBudget)(THIS_ enum RSX_CPU_Budget CPUBudget) PURE; + + STDMETHOD(SetModel)(THIS_ LPRSXEMITTERMODEL lpEmitterModel) PURE; + + STDMETHOD(SetMuteState)(THIS_ DWORD dwMuteState) PURE; + + STDMETHOD(SetOrientation)(THIS_ LPRSXVECTOR3D lpOrientation) PURE; + + STDMETHOD(SetPitch)(THIS_ FLOAT fPitch) PURE; + + STDMETHOD(SetPosition)(THIS_ LPRSXVECTOR3D lpPosition) PURE; + + /*** IRSXStreamingEmitter methods ***/ + STDMETHOD(Flush)(THIS) PURE; + + STDMETHOD(SubmitBuffer)(THIS_ LPRSXBUFFERHDR lpBufferHdr) PURE; + + STDMETHOD(Initialize)( THIS_ LPRSXSTREAMINGEMITTERDESC lpStreamingEmitterAttr, + LPUNKNOWN pUnk) PURE; + + +}; +#endif + + + +#ifdef _WIN32 +#undef INTERFACE +#define INTERFACE IRSXDirectListener +DECLARE_INTERFACE_( IRSXDirectListener, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG,Release) (THIS) PURE; + + + /*** IRSXListener methods ***/ + STDMETHOD(GetOrientation)(THIS_ LPRSXVECTOR3D lpDirection, LPRSXVECTOR3D lpUp) PURE; + + STDMETHOD(GetPosition)(THIS_ LPRSXVECTOR3D lpPosition) PURE; + + STDMETHOD(GetUserData)(THIS_ LPDWORD lpdwUser) PURE; + + STDMETHOD(SetOrientation)(THIS_ LPRSXVECTOR3D lpDirection, LPRSXVECTOR3D lpUp) PURE; + + STDMETHOD(SetPosition)(THIS_ LPRSXVECTOR3D lpPosition) PURE; + + + /*** IRSXDirectListener methods ***/ + STDMETHOD(Connect)(THIS) PURE; + + STDMETHOD(Disconnect)(THIS) PURE; + + STDMETHOD(Initialize)( THIS_ LPRSXDIRECTLISTENERDESC lpDirectListenerAttr, + LPUNKNOWN pUnk) PURE; + + +}; +#endif + + +#ifdef _WIN32 +#undef INTERFACE +#define INTERFACE IRSXStreamingListener +DECLARE_INTERFACE_( IRSXStreamingListener, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + + STDMETHOD_(ULONG,Release) (THIS) PURE; + + + /*** IRSXListener methods ***/ + STDMETHOD(GetOrientation)( THIS_ LPRSXVECTOR3D lpDirection, + LPRSXVECTOR3D lpUp) PURE; + + STDMETHOD(GetPosition)(THIS_ LPRSXVECTOR3D lpPosition) PURE; + + STDMETHOD(GetUserData)(THIS_ LPDWORD lpdwUser) PURE; + + STDMETHOD(SetOrientation)( THIS_ LPRSXVECTOR3D lpDirection, + LPRSXVECTOR3D lpUp) PURE; + + STDMETHOD(SetPosition)(THIS_ LPRSXVECTOR3D lpPosition) PURE; + + + /*** IRSXStreamingListener methods ***/ + STDMETHOD(RequestBuffer)( THIS_ LPSTR lpBufferA, + LPSTR lpBufferB, + DWORD dwSizeB) PURE; + + STDMETHOD(Initialize)( THIS_ LPRSXSTREAMINGLISTENERDESC lpStreamingListenerAttr, + LPUNKNOWN pUnk) PURE; + +}; +#endif + + + + +/* +// RSXBUFFERHDR +*/ +typedef struct _RSXBUFFERHDR { + DWORD cbSize; /* structure size */ + DWORD dwUser; /* user data */ + DWORD dwSize; /* size of data pointed to by lpData */ + LPSTR lpData; /* pointer to data */ + HANDLE hEventSignal; /* event handle signaled when buffer complete */ + DWORD dwReserved1; /* reserved for RSX 3D */ + DWORD dwReserved2; /* reserved for RSX 3D */ + DWORD dwReserved3; /* reserved for RSX 3D */ + DWORD dwReserved4; /* reserved for RSX 3D */ + DWORD dwReserved5; /* reserved for RSX 3D */ +} RSXBUFFERHDR; + + +/* +// Flags for RSXCACHEDEMITTERDESC and RSXSTREAMINGEMITTERDESC +*/ +#define RSXEMITTERDESC_NODOPPLER 0x00000001 +#define RSXEMITTERDESC_NOATTENUATE 0x00000002 +#define RSXEMITTERDESC_NOSPATIALIZE 0x00000004 +#define RSXEMITTERDESC_NOREVERB 0x00000008 + +/* +// Flags for RSXCACHEDEMITTERDESC +*/ +#define RSXEMITTERDESC_GROUPID 0x00000010 +#define RSXEMITTERDESC_PREPROCESS 0x00000020 +#define RSXEMITTERDESC_INMEMORY 0x00000040 + +/* +// RSXCACHEDEMITTERDESC +*/ + +typedef struct _RSXCACHEDEMITTERDESC { + DWORD cbSize; /* structure size */ + DWORD dwFlags; /* creation flags */ + DWORD dwGroupID; /* synchronization group ID, + use zero for no group */ + char szFilename[RSX_MAX_NAME_LEN]; /* path */ + HANDLE hEventSignal; /* Signaled when play completes */ + DWORD dwUser; /* User specific */ +} RSXCACHEDEMITTERDESC; + + + +/* +// RSXDIRECTLISTENERDESC +*/ +typedef struct _RSXDIRECTLISTENERDESC { + DWORD cbSize; /* structure size */ + LPWAVEFORMATEX lpwf; /* Requested output format */ + HWND hMainWnd; /* Main Window Handle DirectSound will use */ + DWORD dwUser; /* User specific */ +} RSXDIRECTLISTENERDESC; + + + +/* +// RSXEMITTERMODEL +*/ +typedef struct _RSXEMITTERMODEL { + DWORD cbSize; /* structure size */ + float fMinBack; /* min back -- ambient */ + float fMinFront; /* min front -- ambient */ + float fMaxBack; /* max back -- attenuation */ + float fMaxFront; /* max front -- attenuation */ + float fIntensity; /* static intensity */ +} RSXEMITTERMODEL; + + + +/* +// Flags for RSXENVIRONMENT +*/ +#define RSXENVIRONMENT_COORDINATESYSTEM 0x00000001 +#define RSXENVIRONMENT_SPEEDOFSOUND 0x00000002 +#define RSXENVIRONMENT_CPUBUDGET 0x00000004 + + +/* +// RSXENVIRONMENT +*/ +typedef struct _RSXENVIRONMENT +{ + DWORD cbSize; /* structure size */ + DWORD dwFlags; /* creation flags */ + BOOL bUseRightHand; /* coordinate system */ + FLOAT fSpeedOfSound; /* Speed of Sound for Doppler */ + enum RSX_CPU_Budget CPUBudget; /* CPU limit for localization */ + +} RSXENVIRONMENT; + + + +/* +// RSXQUERYMEDIAINFO +*/ +typedef struct _RSXQUERYMEDIAINFO { + + DWORD cbSize; /* structure size */ + DWORD dwControl; /* play state */ + FLOAT fSecondsPlayed; /* playback position in seconds */ + FLOAT fTotalSeconds; /* total play time */ + DWORD dwNumLoops; /* loop count */ + FLOAT fAudibleLevel; /* audible level */ + +} RSXQUERYMEDIAINFO; + + + +/* +// RSXREVERBMODEL +*/ +typedef struct _RSXREVERBMODEL +{ + DWORD cbSize; /* structure size */ + BOOL bUseReverb; /* is reverb applied */ + FLOAT fDecayTime; /* Decay in seconds */ + FLOAT fIntensity; /* Gain to model sound absorption */ +} RSXREVERBMODEL; + + + +/* +// RSXSTREAMINGEMITTERDESC +*/ + +typedef struct _RSXSTREAMINGEMITTERDESC { + DWORD cbSize; /* structure size */ + DWORD dwFlags; /* creation flags */ + DWORD dwType; /* unused -- must be zero */ + LPWAVEFORMATEX lpwf; /* buffer format */ + DWORD dwUser; /* User specific */ +} RSXSTREAMINGEMITTERDESC; + + +/* +// RSXSTREAMINGLISTENERDESC +*/ +typedef struct _RSXSTREAMINGLISTENERDESC { + DWORD cbSize; /* structure size */ + LPWAVEFORMATEX lpwf; /* Requested output format */ + DWORD dwRequestedBufferSize; /* Application requested buffer size */ + DWORD dwActualBufferSize; /* RSX specified buffer size */ + DWORD dwUser; /* User specific */ +} RSXSTREAMINGLISTENERDESC; + + + +/* +// RSXVECTOR3D +*/ +typedef struct _RSXVECTOR3D { + float x; + float y; + float z; +} RSXVECTOR3D; + + + +/* +// Error Codes +// +*/ + + + +/* no sound driver installed */ +#define RSXERR_NODRIVER MAKE_HRESULT( 1, FACILITY_ITF, 10 ) + +/* wave driver doesn't support format */ +#define RSXERR_BADFORMAT MAKE_HRESULT( 1, FACILITY_ITF, 20 ) + +/* a zero length vector was specified for the orientation */ +#define RSXERR_ZEROVECTOR MAKE_HRESULT( 1, FACILITY_ITF, 30 ) + +/* +// an error occurred opening the wave file specified +// when creating the emitter +*/ +#define RSXERR_FILENOTFOUND MAKE_HRESULT( 1, FACILITY_ITF, 40 ) + +#define RSXERR_FILESHARINGVIOLATION MAKE_HRESULT( 1, FACILITY_ITF, 41 ) + + +/* the wave file is corrupted(not valid) */ +#define RSXERR_CORRUPTFILE MAKE_HRESULT( 1, FACILITY_ITF, 50 ) + +/* the listeners orienation vectors are parallel */ +#define RSXERR_PARALLELVECTORS MAKE_HRESULT( 1, FACILITY_ITF, 60 ) + +/* sound resources are allocated or busy */ +#define RSXERR_ALLOCATED MAKE_HRESULT( 1, FACILITY_ITF, 70 ) + +/* An invalid operation was performed while RSX is playing +// a cached emitter +*/ +#define RSXERR_PLAYING MAKE_HRESULT( 1, FACILITY_ITF, 80 ) + + + +#ifdef __cplusplus +}; +#endif + +#endif + diff --git a/include/rsx_lib.h b/include/rsx_lib.h new file mode 100644 index 0000000..39b4900 --- /dev/null +++ b/include/rsx_lib.h @@ -0,0 +1,47 @@ +/* + * $Logfile: /Freespace2/code/Sound/rsx_lib.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for RSX sound lib + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 2 10/14/97 11:33p Lawrance + * get RSX implemented + * + * 1 10/14/97 12:58p Lawrance + * + * 1 10/14/97 10:35a Lawrance + * + * $NoKeywords: $ + */ + + +#ifndef __FREESPACE_RSX_H__ +#define __FREESPACE_RSX_H__ + +#include "pstypes.h" + +extern int rsx_initialized; + +int rsx_init(); +void rsx_update_listener(vector *pos, matrix *orient); +void rsx_close(); +int rsx_create_cached_emitter(char *filename, int is_3d, int use_doppler, int min, int max, float max_volume); +int rsx_play( int sid, float priority, float volume); +int rsx_play_3d( int sid, float priority, float volume, vector *pos, vector *sound_fvec); +void rsx_unload_buffer(int sid); + + +#endif + diff --git a/include/rtvoice.h b/include/rtvoice.h new file mode 100644 index 0000000..be4081a --- /dev/null +++ b/include/rtvoice.h @@ -0,0 +1,89 @@ +/* + * $Logfile: /Freespace2/code/Sound/rtvoice.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for real-time voice code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 12 4/21/98 4:44p Dave + * Implement Vasudan ships in multiplayer. Added a debug function to bash + * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui + * problem in options screen. + * + * 11 4/17/98 5:27p Dave + * More work on the multi options screen. Fixed many minor ui todo bugs. + * + * 10 3/25/98 9:56a Dave + * Increase buffer size to handle 8 seconds of voice data. + * + * 9 3/22/98 7:13p Lawrance + * Get streaming of recording voice working + * + * 8 2/24/98 11:56p Lawrance + * Change real-time voice code to provide the uncompressed size on decode. + * + * 7 2/24/98 10:13p Dave + * Put in initial support for multiplayer voice streaming. + * + * 6 2/23/98 6:54p Lawrance + * Make interface to real-time voice more generic and useful. + * + * 5 2/16/98 7:31p Lawrance + * get compression/decompression of voice working + * + * 4 2/15/98 4:43p Lawrance + * work on real-time voice + * + * 3 2/03/98 11:53p Lawrance + * Adding support for DirectSoundCapture + * + * 2 1/31/98 5:48p Lawrance + * Start on real-time voice recording + * + * $NoKeywords: $ + */ + +#ifndef __RTVOICE_H__ +#define __RTVOICE_H__ + +// general +void rtvoice_set_qos(int qos); + +// recording +int rtvoice_init_recording(int qos); +void rtvoice_close_recording(); +int rtvoice_start_recording( void (*user_callback)() = NULL, int callback_time = 175 ); +void rtvoice_stop_recording(); +void rtvoice_get_data(unsigned char **outbuf, int *compressed_size, int *uncompressed_size, double *gain, unsigned char **outbuf_raw = NULL, int *outbuf_size_raw = NULL); + +// playback +int rtvoice_init_playback(); +void rtvoice_close_playback(); +int rtvoice_get_decode_buffer_size(); + +int rtvoice_create_playback_buffer(); +void rtvoice_free_playback_buffer(int index); + +void rtvoice_uncompress(unsigned char *data_in, int size_in, double gain, unsigned char *data_out, int size_out); + +// return a sound handle, _NOT_ a buffer handle +int rtvoice_play_compressed(int handle, unsigned char *data, int compressed_size, int uncompressed_size, double gain); +int rtvoice_play_uncompressed(int handle, unsigned char *data, int size); + +// pass in buffer handle returned from rtvoice_create_playback_buffer(), kills the _sound_ only +void rtvoice_stop_playback(int handle); +void rtvoice_stop_playback_all(); + +#endif + diff --git a/include/scaler.h b/include/scaler.h new file mode 100644 index 0000000..69ca386 --- /dev/null +++ b/include/scaler.h @@ -0,0 +1,58 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Scaler.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for bitmap scaler. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 9 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 8 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 7 10/15/97 4:48p John + * added 16-bpp aascaler + * + * 6 8/04/97 4:47p John + * added gr_aascaler. + * + * 5 11/07/96 6:19p John + * Added a bunch of 16bpp primitives so the game sort of runs in 16bpp + * mode. + * + * 4 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + + +#ifndef _SCALER_H +#define _SCALER_H + +#include "pstypes.h" + + +// Scales current bitmap between va and vb +void gr8_scaler(vertex *va, vertex *vb ); +void gr8_aascaler(vertex *va, vertex *vb ); + +#endif + diff --git a/include/scoring.h b/include/scoring.h new file mode 100644 index 0000000..559a01c --- /dev/null +++ b/include/scoring.h @@ -0,0 +1,307 @@ +/* + * $Logfile: /Freespace2/code/Stats/Scoring.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Scoring system structures, medals, rank, etc. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 5 8/26/99 8:49p Jefff + * Updated medals screen and about everything that ever touches medals in + * one way or another. Sheesh. + * + * 4 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * 3 1/29/99 2:08a Dave + * Fixed beam weapon collisions with players. Reduced size of scoring + * struct for multiplayer. Disabled PXO. + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 32 9/15/98 11:44a Dave + * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring + * scale factors. Fixed standalone filtering of MD missions to non-MD + * hosts. + * + * 31 5/15/98 9:52p Dave + * Added new stats for freespace. Put in artwork for viewing stats on PXO. + * + * 30 5/15/98 4:12p Allender + * removed redbook code. Put back in ingame join timer. Major fixups for + * stats in multiplayer. Pass correct score, medals, etc when leaving + * game. Be sure clients display medals, badges, etc. + * + * 29 5/06/98 3:15p Allender + * fixed some ranking problems. Made vasudan support ship available in + * multiplayer mode. + * + * 28 5/05/98 2:10p Dave + * Verify campaign support for testing. More new tracker code. + * + * 27 4/27/98 6:02p Dave + * Modify how missile scoring works. Fixed a team select ui bug. Speed up + * multi_lag system. Put in new main hall. + * + * 26 4/21/98 11:55p Dave + * Put in player deaths statskeeping. Use arrow keys in the ingame join + * ship select screen. Don't quit the game if in the debriefing and server + * leaves. + * + * 25 4/21/98 4:44p Dave + * Implement Vasudan ships in multiplayer. Added a debug function to bash + * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui + * problem in options screen. + * + * 24 4/13/98 4:19p Hoffoss + * Added rank voice file support. + * + * 23 3/09/98 5:54p Dave + * Fixed stats to take asteroid hits into account. Polished up UI stuff in + * team select. Finished up pilot info popup. Tracked down and fixed + * double click bug. + * + * 22 3/09/98 4:30p Allender + * multiplayer secondary weapon changes. red-alert and cargo-known-delay + * sexpressions. Add time cargo revealed to ship structure + * + * 21 3/02/98 4:24p Allender + * change how scoring works. Call scoring level close when debriefing is + * initialized. Made a "backout" function when mission is not accepted. + * + * 20 2/05/98 5:34p Hoffoss + * Changed code to allow each rank promotion to have it's own specific + * debriefing text. + * + * 19 2/04/98 6:36p Dave + * Changed how stats are handled for net players. + * + * 18 2/02/98 4:59p Hoffoss + * Added a promotion text field to rank.tbl and code to support it in + * FreeSpace. + * + * 17 1/27/98 5:01p Dave + * Put in support for leaving multiplayer games in the pause state. Fixed + * a popup bug which freed saved screens incorrectly. Reworked scoring + * kill and assist evaluation. + * + * 16 1/27/98 4:23p Allender + * enhanced internal scoring mechanisms. + * + * 15 12/31/97 2:52p Hoffoss + * Added award window stuff to debriefing screen. + * + * 14 12/10/97 4:45p Dave + * Added in more detailed support for multiplayer packet lag/loss. Fixed + * some multiplayer stuff. Added some controls to the standalone. + * + * 13 11/18/97 5:30p Dave + * Reorganized some stats code. Made sure that mission stats (single and + * multi) are only merged with alltime stats at the proper times. + * + * 12 11/06/97 4:39p Allender + * a ton of medal work. Removed an uneeded elemen in the scoring + * structure. Fix up medals screen to apprioriate display medals (after + * mask was changed). Fix Fred to only display medals which may actually + * be granted. Added image_filename to player struct for Jason Hoffoss + * + * 11 11/06/97 9:54a Dave + * Dependant checkin + * + * 10 11/05/97 4:43p Allender + * reworked medal/rank system to read all data from tables. Made Fred + * read medals.tbl. Changed ai-warp to ai-warp-out which doesn't require + * waypoint for activation + * + * 9 11/04/97 11:21p Allender + * added gramt-promotion and grant-medal sexpressions. Changed MAX_MEDALS + * to NUM_MEDALS and made it be the correct number. Temporaritly move + * medal names to missionparse.cpp so Fred could get at them + * + * 8 9/25/97 4:57p Dave + * Finished up work on assigning kills and assists in player (and + * multiplayer) stats. + * + * 7 9/24/97 5:03p Dave + * Beginning changes on how kills/assists are evaluated in both single and + * multiplayer + * + * 6 9/18/97 10:14p Dave + * Updated the scoring system struct. Added rank.tbl. Modified how medal + * names are displayed. + * + * 5 9/18/97 9:21a Dave + * Added view medals state. Changed pilot scoring struct to reflect. + * + * 4 7/25/97 4:32p Dave + * Added bonehead stats management. + * + * 3 7/24/97 2:59p Dave + * modified the scoring_struct type, and its corresponding init function + * + * 2 5/01/97 10:24a Hoffoss + * Created new scoring structure, and added code to save/restore it + * to/from pilot file. + * + * 1 4/30/97 10:19a Hoffoss + * + * $NoKeywords: $ + */ + +#ifndef _SCORING_HEADER_FILE +#define _SCORING_HEADER_FILE + +#include "ship.h" +#include "weapon.h" + +struct player; + +// ARGH. IMPORTANT : do not change NUM_MEDALS without talking to DaveB first. It will affect the size of the scoring struct and hence, will break +// a lot of PXO related stuff. SEE ALSO : MAX_SHIP_TYPES +#ifdef FS2_DEMO + #define NUM_MEDALS 16 + #define NUM_MEDALS_FS1 16 +#else + #define NUM_MEDALS 18 + #define NUM_MEDALS_FS1 16 +#endif + +#define NUM_RANKS 10 + +#define RANK_ENSIGN 0 +#define RANK_LT_JUNIOR 1 +#define RANK_LT 2 +#define RANK_LT_CMDR 3 +#define RANK_CMDR 4 +#define RANK_CAPTAIN 5 +#define RANK_COMMODORE 6 +#define RANK_REAR_ADMIRAL 7 +#define RANK_VICE_ADMIRAL 8 +#define RANK_ADMIRAL 9 + +#define MAX_FREESPACE1_RANK RANK_COMMODORE +#define MAX_FREESPACE2_RANK RANK_ADMIRAL + +/* + The ins and outs of when/where stats are stored and retreived - BE SURE TO FOLLOW THESE GUIDELINES + + SINGLE PLAYER : + scoring_level_init() is called from game_level_init(). This zeroes out mission specific stats + scoring_level_close() is called from the debriefing screen when the player hits accept. This saves the mission + stats to alltime stats, and updates any campaign stats + NOTE : in single player mode, if the player is going back to replay an old mission, the stats shouldn't be + stored again + + MULTI PLAYER : + scoring_level_init() is called in game_level_init() again. + init_multiplayer_stats() is called on all computers in the game when moving _into_ the MISSION_SYNC state + + scoring_level_close() is called on all machines when the host selects accept. If the host is not on the standalone + he sends a packet to all players indicating that they should save their data. If he _is_ on the standalone, he should + send only to the standalone, and then it rebroadcasts the packet ot everyone else. +*/ + + +typedef struct rank_stuff { + char name[NAME_LENGTH]; // name of this rank + char *promotion_text; // text to display when promoted to this rank + int points; // points needed to reach this rank + char bitmap[NAME_LENGTH]; // bitmap of this rank medal + char promotion_voice_base[MAX_FILENAME_LEN - 1]; +} rank_stuff; + +#define STATS_FLAG_INVALID (1<<0) +#define STATS_FLAG_CAMPAIGN (1<<1) +#define STATS_FLAG_MULTIPLAYER (1<<2) + +typedef struct scoring_struct { + int flags; + + // All-time total + int score; // all time score + int rank; // all time rank + int medals[NUM_MEDALS]; // all time medal counts + + ushort kills[MAX_SHIP_TYPES]; // only valid kills (i.e. not on friendlies). + int assists; // alltime assists + int kill_count; // total alltime kills + int kill_count_ok; // total valid alltime kills (no friendlies) + unsigned int p_shots_fired; // primary weapon shots fired + unsigned int s_shots_fired; // secondary weapon shots fired + + unsigned int p_shots_hit; // primary weapon shots hit + unsigned int s_shots_hit; // secondary weapon shots hit + + unsigned int p_bonehead_hits; // alltime primary friendly hits + unsigned int s_bonehead_hits; // alltime secondary friendly hits + int bonehead_kills; // alltime friendly kills + + unsigned int missions_flown; // total # of missions flown + unsigned int flight_time; // total # of flight hours the player has + time_t last_flown; // last time the player has flown + time_t last_backup; // so we can easily call scoring_level_backout() + + // Mission total + int m_medal_earned; // which medal (if any) earned this mission + int m_badge_earned; // which badge was earned. Calculated after mission is over + int m_promotion_earned; // was a promotion earned. Calculated after mission is over + + int m_score; + ushort m_kills[MAX_SHIP_TYPES]; // this will represent all kills in the mission (bonehead or not) + ushort m_okKills[MAX_SHIP_TYPES]; // this will be only the "valid" kills the player made + int m_kill_count; // total kills for this mission + int m_kill_count_ok; // total (non-friendly) kills for this mission + int m_assists; // player assits for the mission + unsigned int mp_shots_fired; // primary shots fired for the mission + unsigned int ms_shots_fired; // secondary shots fired for the mission + unsigned int mp_shots_hit; // primary shots hit for the mission + unsigned int ms_shots_hit; // secondary shots hit for the mission + unsigned int mp_bonehead_hits; // primary friendly hits for the mission + unsigned int ms_bonehead_hits; // secondary friendly hits for the mission + int m_bonehead_kills; // # of friendly kills for the mission + int m_player_deaths; // player deaths for the mission (really only useful for multiplayer) + + // kills by player for multiplayer dogfight + ushort m_dogfight_kills[MAX_PLAYERS]; +} scoring_struct; + +extern rank_stuff Ranks[NUM_RANKS]; + +void init_scoring_element(scoring_struct *s); + +void parse_rank_tbl(); +void scoring_level_init( scoring_struct *score ); +void scoring_level_close(int accepted = 1); +void scoring_backout_accept( scoring_struct *score ); +void scoring_do_accept( scoring_struct *score ); + +// function to give a medal to a player if he earned it +void scoring_check_medal(scoring_struct *sc); + +void scoring_add_damage(object *ship_obj,object *other_obj,float damage); +void scoring_eval_kill(object *ship_obj); +void scoring_eval_assists(ship *sp,int killer_sig); + +// bash the passed player to the specified rank +void scoring_bash_rank(player *pl,int rank); + +// eval a hit on an object (for primary and secondary hit purposes) +void scoring_eval_hit(object *hit_obj, object *other_obj, int from_blast = 0); + +// get a scaling factor for adding/subtracting from mission score +float scoring_get_scale_factor(); + +#endif + diff --git a/include/scramble.h b/include/scramble.h new file mode 100644 index 0000000..013e67f --- /dev/null +++ b/include/scramble.h @@ -0,0 +1,42 @@ +/* + * $Logfile: /freespace2/code/Scramble/scramble.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for file scrambler + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/24/98 11:41p Dave + * + * 1 10/24/98 11:31p Dave + * + * 4 4/14/98 1:39p Lawrance + * Add command line switches to preprocess ship and weapon tables + * + * 3 3/31/98 1:14a Lawrance + * Get .tbl and mission file encryption working. + * + * 2 3/30/98 5:51p Lawrance + * file encryption and decryption + * + * 1 3/30/98 5:19p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __SCRAMBLE_H__ +#define __SCRAMBLE_H__ + +#define PREPROCESS_SHIPS_TBL 0 +#define PREPROCESS_WEAPONS_TBL 1 + +void scramble_file(char *src_filename, char *dest_filename = NULL, int preprocess = -1); +void unscramble_file(char *src_filename, char *dest_filename = NULL); + +#endif + diff --git a/include/sexp.h b/include/sexp.h new file mode 100644 index 0000000..619b892 --- /dev/null +++ b/include/sexp.h @@ -0,0 +1,824 @@ +/* + * $Source$ + * $Revision$ + * $Author$ + * $Date$ + * + * header for sexpression parsing + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 40 9/07/99 1:05a Andsager + * Added team-score sexp for multi team vs team missions + * + * 39 9/02/99 7:06p Dave + * Bumped MAX_SEXP_NODES to 2200 + * + * 38 8/27/99 4:07p Andsager + * Add is-ship-visible sexp. Make ship-vanish sexp SINGLE player only + * + * 37 8/24/99 4:25p Andsager + * Add ship-vanish sexp + * + * 36 8/16/99 10:04p Andsager + * Add special-warp-dist and special-warpout-name sexp for Knossos device + * warpout. + * + * 35 8/09/99 2:00p Dave + * 2 new sexpressions. + * + * 34 8/02/99 4:26p Dave + * Added 2 new sexpressions. + * + * 33 7/28/99 1:36p Andsager + * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp + * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack + * better. + * + * 32 7/24/99 4:56p Dave + * Added 3 new sexpressions. + * + * 31 7/21/99 8:10p Dave + * First run of supernova effect. + * + * 30 7/20/99 9:19p Andsager + * Added facing waypoint sexp + * + * 29 7/20/99 9:54a Andsager + * Add subsys-set-random sexp + * + * 28 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 27 7/13/99 3:37p Andsager + * Add secondaries-depleted sexp + * + * 26 7/08/99 12:06p Andsager + * Add turret-tagged-only and turret-tagged-clear sexp. + * + * 25 6/28/99 4:51p Andsager + * Add ship-guardian sexp (does not allow ship to be killed) + * + * 24 6/23/99 5:51p Andsager + * Add waypoint-cap-speed. Checkin stealth ai - inactive. + * + * 23 6/16/99 10:21a Dave + * Added send-message-list sexpression. + * + * 22 6/01/99 8:35p Dave + * Finished lockarm weapons. Added proper supercap weapons/damage. Added + * awacs-set-radius sexpression. + * + * 21 5/24/99 11:28a Dave + * Sexpression for adding/removing ships from the hud escort list. + * + * 20 4/28/99 9:33a Andsager + * Add turret-free and turret-lock (and -all) sexp. Stargger start time + * of beam weapons beam-free and beam-free-all. + * + * 19 4/26/99 2:14p Andsager + * Add beam-protect-ship and beam-unprotect-ship sexp. + * + * 18 4/23/99 9:45a Andsager + * Modify rand_sexp to return the same value each time it is called on a + * particular node. This prevents random time delay defaulting to min. + * + * 17 4/02/99 9:55a Dave + * Added a few more options in the weapons.tbl for beam weapons. Attempt + * at putting "pain" packets into multiplayer. + * + * 16 3/20/99 3:46p Dave + * Added support for model-based background nebulae. Added 3 new + * sexpressions. + * + * 15 3/04/99 6:09p Dave + * Added in sexpressions for firing beams and checking for if a ship is + * tagged. + * + * 14 2/26/99 6:01p Andsager + * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay + * + * 13 2/11/99 5:22p Andsager + * Fixed bugs, generalized block Sexp_variables + * + * 12 2/11/99 2:15p Andsager + * Add ship explosion modification to FRED + * + * 11 1/25/99 4:29p Andsager + * Modify sexp_modify_variable() to handle type string. added quick out + * for multiplayer_client + * + * 10 1/25/99 8:15a Andsager + * Format fix + * + * 9 1/25/99 8:14a Andsager + * Add sexp_modify_variable(). Changed syntax checking to allow, adding + * operator return type ambiguous + * + * 8 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 7 1/20/99 9:37a Andsager + * + * 6 1/19/99 3:57p Andsager + * Round 2 of variables + * + * 5 1/07/99 1:52p Andsager + * Initial check in of Sexp_variables + * + * 4 12/15/98 3:59p Dan + * DA: bumped sexp nodes to 2000 for dan + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 168 9/17/98 9:25p Dave + * _really_ upped max sexp nodes to 1800. + * + * 167 9/16/98 6:54p Dave + * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort + * the ship list box. Added code so that tracker stats are not stored with + * only 1 player. + * + * 166 9/16/98 10:42a Hoffoss + * Added sexp node counting to fsm files for end user designers. + * + * 165 4/25/98 7:40p Allender + * fixd some small hotkey stuff. Worked on turret orientation being + * correct for multiplayer. new sexpression called end-campaign will will + * end the main campaign + * + * 164 4/24/98 4:20p Hoffoss + * Made the Cond operator an unlisted operator, as using this in Fred with + * the sexp trees will cause them to be generated wrong, and then + * FreeSpace would pay the price. + * + * 163 4/23/98 5:50p Hoffoss + * Added tracking of techroom database list info in pilot files, added + * sexp to add more to list, made mouse usable on ship listing in tech + * room. + * + * 162 4/16/98 4:33p Hoffoss + * Added support for detecting instructor terminating training due to + * player shooting at him. + * + * 161 4/15/98 12:58p Lawrance + * increase MAX_SEXP_NODES for demo build, needed since training mission + * was added + * + * 160 4/14/98 5:46p Hoffoss + * Added special-check operator. + * + * 159 4/14/98 5:24p Hoffoss + * Added a custom operator for training handling for Mike K. + * + * 158 4/14/98 11:55a Allender + * add end-of-campaign sexpression to allow for mission replay at the end + * of campaigns + * + * 157 4/09/98 4:32p Hoffoss + * Fixed several bugs in debriefing. + * + * 156 4/06/98 5:37p Hoffoss + * Added sexp tree support to briefings in Fred. + * + * 155 4/03/98 2:47p Allender + * made directives act different when multiple waves of a wing take a long + * time to reappear + * + * 154 4/03/98 12:18a Allender + * new sexpression to detect departed or destroyed. optionally disallow + * support ships. Allow docking with escape pods + * + * 153 4/02/98 10:13p Lawrance + * increase MAX_SEXP_NODES to 1600 + * + * 152 4/02/98 6:32p Lawrance + * reduce MAX_SEXP_NODES if DEMO defined + * + * 151 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 150 4/01/98 8:30p Mike + * Reduce MAX_SHIP_SUBOBJECTS and MAX_SEXP_NODES + * + * 149 3/17/98 4:29p Allender + * allow the warp broken sexpressions + * + * 148 3/15/98 3:41p Allender + * new sexpression to gauge whether a ship warped out in proximity of jump + * node + * + * 147 3/09/98 4:31p Allender + * multiplayer secondary weapon changes. red-alert and cargo-known-delay + * sexpressions. Add time cargo revealed to ship structure + * + * 146 3/02/98 9:17p Allender + * four new sexpressions: ships-(in)vulnerable. + * percent-ships-(destroyed/departed) + * + * 145 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 144 2/19/98 4:54p Allender + * new sexpression to make ships visible/invisible on players sensors + * + * 143 2/10/98 8:58p Allender + * added hull damage to set/repair/sabotage subsystem + * + * 142 1/30/98 4:23p Hoffoss + * Added operators for debriefing checking. + * + * 141 1/23/98 4:09p Hoffoss + * Added a new operator format type to allow ships that aren't player + * ships. Made sabotage, repair and set-subsys-strength utilize it. + * + * 140 1/20/98 4:49p Allender + * new sexpression to help AI fire huge secondary weapons + * + * 139 1/07/98 2:08p Hoffoss + * Moved do-nothing operator so it appears last in the catagory's list. + * + * 138 1/07/98 2:02p Johnson + * Added do-nothing to change catagory. + * + * 137 1/07/98 1:48p Hoffoss + * Put do-nothing operator into change catagory so designers can make use + * of it. + * + * 136 1/05/98 4:26p Allender + * added sexpression to flash a hud gauge -- a training only operator + * + * 135 1/02/98 4:41p Allender + * new sexpressions (not allowed yet) for changing warp status of ships + * + * 134 12/19/97 2:59p Allender + * more stuff to get persistent ships/weapons across campaign missions + * + * 133 12/19/97 12:43p Hoffoss + * Changed code to allow counts in directives. + * + * 132 12/19/97 12:03p Allender + * Added GM_CAMPAIGN_MODE to indicate when player is in a campaign or not. + * Started adding FreeSpace support for carrying of ship/weapon types + * across missions in a campaign. + * + */ + +#ifndef _SEXP_H +#define _SEXP_H + +#include "cfile.h" +#include "ship.h" + +#define OPERATOR_LENGTH 24 // if this ever exceeds TOKEN_LENGTH, let JasonH know! +#define TOKEN_LENGTH 32 + +#ifdef FS2_DEMO + #define MAX_SEXP_NODES 1600 +#else + #define MAX_SEXP_NODES 2200 // Reduced from 2000 to 1200 by MK on 4/1/98. + // Most used nodes is 698 in sm1-10a. Sandeep thinks that's the most complex mission. + // AL 2-4-98: upped to 1600, btm03 ran out of sexps, since campaign took a bunch + // DA 12/15 bumped up to 2000 - Dan ran out +#endif + +#define MAX_SEXP_VARIABLES 100 + +#define MAX_SEXP_TEXT 2000 +#define MAX_OPERATORS 200 // Yes, this is used, but not by the Sexp code. + +// Operator argument formats (data types of an argument) +#define OPF_NONE 1 // argument cannot exist at this position if it's this +#define OPF_NULL 2 // no value. Can still be used for type matching, however +#define OPF_BOOL 3 +#define OPF_NUMBER 4 +#define OPF_SHIP 5 +#define OPF_WING 6 +#define OPF_SUBSYSTEM 7 +#define OPF_POINT 8 // either a 3d point in space, or a waypoint name +#define OPF_IFF 9 +#define OPF_AI_GOAL 10 // special to match ai goals +#define OPF_DOCKER_POINT 11 // docking point on docker ship +#define OPF_DOCKEE_POINT 12 // docking point on dockee ship +#define OPF_MESSAGE 13 // the name (id) of a message in Messages[] array +#define OPF_WHO_FROM 14 // who sent the message -- doesn't necessarily have to be a ship!!! +#define OPF_PRIORITY 15 // priority for messages +#define OPF_WAYPOINT_PATH 16 // name of a waypoint +#define OPF_POSITIVE 17 // positive number or zero +#define OPF_MISSION_NAME 18 // name of a mission for various mission related things +#define OPF_SHIP_POINT 19 // a waypoint or a ship +#define OPF_GOAL_NAME 20 // name of goal (or maybe event?) from a mission +#define OPF_SHIP_WING 21 // either a ship or wing name (they don't conflict) +#define OPF_SHIP_WING_POINT 22 // name of a ship, wing, or a point +#define OPF_SHIP_TYPE 23 // type of ship (fighter/bomber/etc) +#define OPF_KEYPRESS 24 // a default key +#define OPF_EVENT_NAME 25 // name of an event +#define OPF_AI_ORDER 26 // a squadmsg order player can give to a ship +#define OPF_SKILL_LEVEL 27 // current skill level of the game +#define OPF_MEDAL_NAME 28 // name of medals +#define OPF_WEAPON_NAME 29 // name of a weapon +#define OPF_SHIP_CLASS_NAME 30 // name of a ship class +#define OPF_HUD_GAUGE_NAME 31 // name of HUD gauge +#define OPF_HUGE_WEAPON 32 // name of a secondary bomb type weapon +#define OPF_SHIP_NOT_PLAYER 33 // a ship, but not a player ship +#define OPF_JUMP_NODE_NAME 34 // name of a jump node +#define OPF_VARIABLE_NAME 35 // variable name +#define OPF_AMBIGUOUS 36 // type used with variable +#define OPF_AWACS_SUBSYSTEM 37 // an awacs subsystem + +// Operand return types +#define OPR_NUMBER 1 // returns number +#define OPR_BOOL 2 // returns true/false value +#define OPR_NULL 3 // doesn't return a value +#define OPR_AI_GOAL 4 // is an ai operator (doesn't really return a value, but used for type matching) +#define OPR_POSITIVE 5 // returns a non-negative number +#define OPR_STRING 6 // not really a return type, but used for type matching. +#define OPR_AMBIGUOUS 7 // not really a return type, but used for type matching. + +#define OP_INSERT_FLAG 0x8000 +#define OP_REPLACE_FLAG 0x4000 +#define OP_NONCAMPAIGN_FLAG 0x2000 +#define OP_CAMPAIGN_ONLY_FLAG 0x1000 +#define FIRST_OP 0x0100 +#define OP_CATAGORY_MASK 0x0f00 + +#define OP_CATAGORY_OBJECTIVE 0x0100 +#define OP_CATAGORY_TIME 0x0200 +#define OP_CATAGORY_LOGICAL 0x0300 +#define OP_CATAGORY_ARITHMETIC 0x0400 +#define OP_CATAGORY_STATUS 0x0500 +#define OP_CATAGORY_CHANGE 0x0600 +#define OP_CATAGORY_CONDITIONAL 0x0700 +#define OP_CATAGORY_DEBUG 0x0800 +#define OP_CATAGORY_AI 0x0900 // used for AI goals +#define OP_CATAGORY_TRAINING 0x0a00 +#define OP_CATAGORY_UNLISTED 0x0b00 +#define OP_CATAGORY_GOAL_EVENT 0x0c00 + +#define OP_PLUS (0x0000 | OP_CATAGORY_ARITHMETIC) +#define OP_MINUS (0x0001 | OP_CATAGORY_ARITHMETIC) +#define OP_MOD (0x0002 | OP_CATAGORY_ARITHMETIC) +#define OP_MUL (0x0003 | OP_CATAGORY_ARITHMETIC) +#define OP_DIV (0x0004 | OP_CATAGORY_ARITHMETIC) +#define OP_RAND (0x0005 | OP_CATAGORY_ARITHMETIC) + +#define OP_TRUE (0x0000 | OP_CATAGORY_LOGICAL) +#define OP_FALSE (0x0001 | OP_CATAGORY_LOGICAL) +#define OP_AND (0x0002 | OP_CATAGORY_LOGICAL) +#define OP_AND_IN_SEQUENCE (0x0003 | OP_CATAGORY_LOGICAL) +#define OP_OR (0x0004 | OP_CATAGORY_LOGICAL) +#define OP_EQUALS (0x0005 | OP_CATAGORY_LOGICAL) +#define OP_GREATER_THAN (0x0006 | OP_CATAGORY_LOGICAL) +#define OP_LESS_THAN (0x0007 | OP_CATAGORY_LOGICAL) +#define OP_IS_IFF (0x0008 | OP_CATAGORY_LOGICAL | OP_NONCAMPAIGN_FLAG) +#define OP_HAS_TIME_ELAPSED (0x0009 | OP_CATAGORY_LOGICAL | OP_NONCAMPAIGN_FLAG) +#define OP_NOT (0x000a | OP_CATAGORY_LOGICAL) + +#define OP_GOAL_INCOMPLETE (0x0000 | OP_CATAGORY_GOAL_EVENT | OP_NONCAMPAIGN_FLAG) +#define OP_GOAL_TRUE_DELAY (0x0001 | OP_CATAGORY_GOAL_EVENT | OP_NONCAMPAIGN_FLAG) +#define OP_GOAL_FALSE_DELAY (0x0002 | OP_CATAGORY_GOAL_EVENT | OP_NONCAMPAIGN_FLAG) +#define OP_EVENT_INCOMPLETE (0x0003 | OP_CATAGORY_GOAL_EVENT | OP_NONCAMPAIGN_FLAG) +#define OP_EVENT_TRUE_DELAY (0x0004 | OP_CATAGORY_GOAL_EVENT | OP_NONCAMPAIGN_FLAG) +#define OP_EVENT_FALSE_DELAY (0x0005 | OP_CATAGORY_GOAL_EVENT | OP_NONCAMPAIGN_FLAG) +#define OP_PREVIOUS_EVENT_TRUE (0x0006 | OP_CATAGORY_GOAL_EVENT) +#define OP_PREVIOUS_EVENT_FALSE (0x0007 | OP_CATAGORY_GOAL_EVENT) +#define OP_PREVIOUS_GOAL_TRUE (0x0009 | OP_CATAGORY_GOAL_EVENT) +#define OP_PREVIOUS_GOAL_FALSE (0x000a | OP_CATAGORY_GOAL_EVENT) + +#define OP_IS_DESTROYED_DELAY (0x0008 | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_IS_SUBSYSTEM_DESTROYED_DELAY (0x0009 | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_IS_DISABLED_DELAY (0x000a | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_IS_DISARMED_DELAY (0x000b | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_HAS_DOCKED_DELAY (0x000c | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_HAS_UNDOCKED_DELAY (0x000d | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_HAS_ARRIVED_DELAY (0x000e | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_HAS_DEPARTED_DELAY (0x000f | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_WAYPOINTS_DONE_DELAY (0x0011 | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_SHIP_TYPE_DESTROYED (0x0012 | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_PERCENT_SHIPS_DEPARTED (0x0013 | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_PERCENT_SHIPS_DESTROYED (0x0014 | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_DEPART_NODE_DELAY (0x0015 | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) +#define OP_DESTROYED_DEPARTED_DELAY (0x0016 | OP_CATAGORY_OBJECTIVE | OP_NONCAMPAIGN_FLAG) + +#define OP_TIME_SHIP_DESTROYED (0x0000 | OP_CATAGORY_TIME | OP_NONCAMPAIGN_FLAG) +#define OP_TIME_SHIP_ARRIVED (0x0001 | OP_CATAGORY_TIME | OP_NONCAMPAIGN_FLAG) +#define OP_TIME_SHIP_DEPARTED (0x0002 | OP_CATAGORY_TIME | OP_NONCAMPAIGN_FLAG) +#define OP_TIME_WING_DESTROYED (0x0003 | OP_CATAGORY_TIME | OP_NONCAMPAIGN_FLAG) +#define OP_TIME_WING_ARRIVED (0x0004 | OP_CATAGORY_TIME | OP_NONCAMPAIGN_FLAG) +#define OP_TIME_WING_DEPARTED (0x0005 | OP_CATAGORY_TIME | OP_NONCAMPAIGN_FLAG) +#define OP_MISSION_TIME (0x0006 | OP_CATAGORY_TIME | OP_NONCAMPAIGN_FLAG) +#define OP_TIME_DOCKED (0x0007 | OP_CATAGORY_TIME | OP_NONCAMPAIGN_FLAG) +#define OP_TIME_UNDOCKED (0x0008 | OP_CATAGORY_TIME | OP_NONCAMPAIGN_FLAG) + +#define OP_SHIELDS_LEFT (0x0000 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_HITS_LEFT (0x0001 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_HITS_LEFT_SUBSYSTEM (0x0002 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_DISTANCE (0x0003 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_LAST_ORDER_TIME (0x0004 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_NUM_PLAYERS (0x0005 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_SKILL_LEVEL_AT_LEAST (0x0006 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_WAS_PROMOTION_GRANTED (0x0008 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_WAS_MEDAL_GRANTED (0x0009 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_CARGO_KNOWN_DELAY (0x000a | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_CAP_SUBSYS_CARGO_KNOWN_DELAY (0x000b | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_HAS_BEEN_TAGGED_DELAY (0x000c | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_IS_TAGGED (0x000d | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_NUM_KILLS (0x000e | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_NUM_TYPE_KILLS (0x000f | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_NUM_CLASS_KILLS (0x0010 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_SHIELD_RECHARGE_PCT (0x0011 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_ENGINE_RECHARGE_PCT (0x0012 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_WEAPON_RECHARGE_PCT (0x0013 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_SHIELD_QUAD_LOW (0x0014 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_SECONDARY_AMMO_PCT (0x0015 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_IS_SECONDARY_SELECTED (0x0016 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_IS_PRIMARY_SELECTED (0x0017 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_SPECIAL_WARP_DISTANCE (0X0018 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_IS_SHIP_VISIBLE (0X0019 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) +#define OP_TEAM_SCORE (0X0020 | OP_CATAGORY_STATUS | OP_NONCAMPAIGN_FLAG) + + + +// conditional sexpressions +#define OP_WHEN (0x0000 | OP_CATAGORY_CONDITIONAL) + +// sexpressions with side-effects +#define OP_CHANGE_IFF (0x0000 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_REPAIR_SUBSYSTEM (0x0001 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SABOTAGE_SUBSYSTEM (0x0002 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SET_SUBSYSTEM_STRNGTH (0x0003 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_PROTECT_SHIP (0x0004 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SEND_MESSAGE (0x0005 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SELF_DESTRUCT (0x0006 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_CLEAR_GOALS (0x0007 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_ADD_GOAL (0x0008 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_INVALIDATE_GOAL (0x0009 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_VALIDATE_GOAL (0x000a | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SEND_RANDOM_MESSAGE (0x000b | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_TRANSFER_CARGO (0x000c | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_EXCHANGE_CARGO (0x000d | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_UNPROTECT_SHIP (0x000e | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_GOOD_REARM_TIME (0x0010 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_BAD_REARM_TIME (0x0011 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_GRANT_PROMOTION (0x0012 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_GRANT_MEDAL (0x0013 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_ALLOW_SHIP (0x0014 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_ALLOW_WEAPON (0x0015 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_GOOD_SECONDARY_TIME (0x0016 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_WARP_BROKEN (0x0017 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_WARP_NOT_BROKEN (0x0018 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_WARP_NEVER (0x0019 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_WARP_ALLOWED (0x0020 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SHIP_INVISIBLE (0x0021 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SHIP_VISIBLE (0x0022 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SHIP_INVULNERABLE (0x0023 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SHIP_VULNERABLE (0x0024 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_RED_ALERT (0x0025 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_TECH_ADD_SHIP (0x0026 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_TECH_ADD_WEAPON (0x0027 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_END_CAMPAIGN (0x0028 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_JETTISON_CARGO (0x0029 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_MODIFY_VARIABLE (0X0030 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_NOP (0x0031 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_BEAM_FIRE (0x0032 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_BEAM_FREE (0x0033 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_BEAM_FREE_ALL (0x0034 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_BEAM_LOCK (0x0035 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_BEAM_LOCK_ALL (0x0036 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_BEAM_PROTECT_SHIP (0x0037 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_BEAM_UNPROTECT_SHIP (0x0038 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_TURRET_FREE (0x0039 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_TURRET_FREE_ALL (0x0040 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_TURRET_LOCK (0x0041 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_TURRET_LOCK_ALL (0x0042 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_ADD_REMOVE_ESCORT (0x0043 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_AWACS_SET_RADIUS (0x0044 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SEND_MESSAGE_LIST (0x0045 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_CAP_WAYPOINT_SPEED (0x0046 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SHIP_GUARDIAN (0x0047 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SHIP_NO_GUARDIAN (0x0048 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_TURRET_TAGGED_ONLY_ALL (0x0049 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_TURRET_TAGGED_CLEAR_ALL (0x0050 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SUBSYS_SET_RANDOM (0x0051 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SUPERNOVA_START (0x0052 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_CARGO_NO_DEPLETE (0x0053 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SET_SPECIAL_WARPOUT_NAME (0X0054 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) +#define OP_SHIP_VANISH (0X0055 | OP_CATAGORY_CHANGE | OP_NONCAMPAIGN_FLAG) + + +// debugging sexpressions +#define OP_INT3 (0x0000 | OP_CATAGORY_DEBUG) + +// defined for AI goals +#define OP_AI_CHASE (0x0000 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_DOCK (0x0001 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_UNDOCK (0x0002 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_WARP_OUT (0x0003 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_WAYPOINTS (0x0004 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_WAYPOINTS_ONCE (0x0005 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_DESTROY_SUBSYS (0x0006 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_DISABLE_SHIP (0x0008 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_DISARM_SHIP (0x0009 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_GUARD (0x000a | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_CHASE_ANY (0x000b | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_EVADE_SHIP (0x000d | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_STAY_NEAR_SHIP (0x000e | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_KEEP_SAFE_DISTANCE (0x000f | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_IGNORE (0x0010 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_STAY_STILL (0x0011 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) +#define OP_AI_PLAY_DEAD (0x0012 | OP_CATAGORY_AI | OP_NONCAMPAIGN_FLAG) + +#define OP_GOALS_ID (0x0001 | OP_CATAGORY_UNLISTED) +#define OP_NEXT_MISSION (0x0002 | OP_CATAGORY_UNLISTED) // used in campaign files for branching +#define OP_IS_DESTROYED (0x0003 | OP_CATAGORY_UNLISTED) +#define OP_IS_SUBSYSTEM_DESTROYED (0x0004 | OP_CATAGORY_UNLISTED) +#define OP_IS_DISABLED (0x0005 | OP_CATAGORY_UNLISTED) +#define OP_IS_DISARMED (0x0006 | OP_CATAGORY_UNLISTED) +#define OP_HAS_DOCKED (0x0007 | OP_CATAGORY_UNLISTED) +#define OP_HAS_UNDOCKED (0x0008 | OP_CATAGORY_UNLISTED) +#define OP_HAS_ARRIVED (0x0009 | OP_CATAGORY_UNLISTED) +#define OP_HAS_DEPARTED (0x000a | OP_CATAGORY_UNLISTED) +#define OP_WAYPOINTS_DONE (0x000b | OP_CATAGORY_UNLISTED) +#define OP_ADD_SHIP_GOAL (0x000c | OP_CATAGORY_UNLISTED) +#define OP_CLEAR_SHIP_GOALS (0x000d | OP_CATAGORY_UNLISTED) +#define OP_ADD_WING_GOAL (0x000e | OP_CATAGORY_UNLISTED) +#define OP_CLEAR_WING_GOALS (0x000f | OP_CATAGORY_UNLISTED) +#define OP_AI_CHASE_WING (0x0010 | OP_CATAGORY_UNLISTED) +#define OP_AI_GUARD_WING (0x0011 | OP_CATAGORY_UNLISTED) +#define OP_EVENT_TRUE (0x0012 | OP_CATAGORY_UNLISTED) +#define OP_EVENT_FALSE (0x0013 | OP_CATAGORY_UNLISTED) +#define OP_PREVIOUS_GOAL_INCOMPLETE (0x0014 | OP_CATAGORY_UNLISTED) +#define OP_PREVIOUS_EVENT_INCOMPLETE (0x0015 | OP_CATAGORY_UNLISTED) +#define OP_AI_WARP (0x0016 | OP_CATAGORY_UNLISTED) +#define OP_END_MISSION_DELAY (0x0017 | OP_CATAGORY_UNLISTED) +#define OP_IS_CARGO_KNOWN (0x001c | OP_CATAGORY_UNLISTED) +#define OP_COND (0x001e | OP_CATAGORY_UNLISTED) +#define OP_END_OF_CAMPAIGN (0x001f | OP_CATAGORY_UNLISTED) + +#define OP_KEY_PRESSED (0x0000 | OP_CATAGORY_TRAINING) +#define OP_KEY_RESET (0x0001 | OP_CATAGORY_TRAINING) +#define OP_TARGETED (0x0002 | OP_CATAGORY_TRAINING) +#define OP_SPEED (0x0003 | OP_CATAGORY_TRAINING) +#define OP_FACING (0x0004 | OP_CATAGORY_TRAINING) +#define OP_ORDER (0x0005 | OP_CATAGORY_TRAINING) +#define OP_WAYPOINT_MISSED (0x0006 | OP_CATAGORY_TRAINING) +#define OP_PATH_FLOWN (0x0007 | OP_CATAGORY_TRAINING) +#define OP_WAYPOINT_TWICE (0x0008 | OP_CATAGORY_TRAINING) +#define OP_TRAINING_MSG (0x0009 | OP_CATAGORY_TRAINING) +#define OP_FLASH_HUD_GAUGE (0x000a | OP_CATAGORY_TRAINING) +#define OP_SPECIAL_CHECK (0x000b | OP_CATAGORY_TRAINING) +#define OP_SECONDARIES_DEPLETED (0x000c | OP_CATAGORY_TRAINING) +#define OP_FACING2 (0x000d | OP_CATAGORY_TRAINING) + +#define OP_SET_TRAINING_CONTEXT_FLY_PATH (0x0080 | OP_CATAGORY_TRAINING) +#define OP_SET_TRAINING_CONTEXT_SPEED (0x0081 | OP_CATAGORY_TRAINING) + +// defines for string constants +#define SEXP_HULL_STRING "Hull" + +// macros for accessing sexpression atoms +#define CAR(n) (Sexp_nodes[n].first) +#define CDR(n) (Sexp_nodes[n].rest) +#define CADR(n) (Sexp_nodes[Sexp_nodes[n].rest].first) +// #define CTEXT(n) (Sexp_nodes[n].text) +char *CTEXT(int n); + +#define REF_TYPE_SHIP 1 +#define REF_TYPE_WING 2 +#define REF_TYPE_PLAYER 3 +#define REF_TYPE_WAYPOINT 4 +#define REF_TYPE_PATH 5 // waypoint path + +#define SRC_SHIP_ARRIVAL 0x10000 +#define SRC_SHIP_DEPARTURE 0x20000 +#define SRC_WING_ARRIVAL 0x30000 +#define SRC_WING_DEPARTURE 0x40000 +#define SRC_EVENT 0x50000 +#define SRC_MISSION_GOAL 0x60000 +#define SRC_SHIP_ORDER 0x70000 +#define SRC_WING_ORDER 0x80000 +#define SRC_DEBRIEFING 0x90000 +#define SRC_BRIEFING 0xa0000 +#define SRC_UNKNOWN 0xffff0000 +#define SRC_MASK 0xffff0000 +#define SRC_DATA_MASK 0xffff + +#define SEXP_MODE_GENERAL 0 +#define SEXP_MODE_CAMPAIGN 1 + +// defines for type field of sexp nodes. The actual type of the node will be stored in the lower +// two bytes of the field. The upper two bytes will be used for flags (bleah...) +// Be sure not to conflict with type field of sexp_variable +#define SEXP_NOT_USED 0 +#define SEXP_LIST 1 +#define SEXP_ATOM 2 + +// flags for sexpressions -- masked onto the end of the type field +#define SEXP_FLAG_PERSISTENT (1<<31) // should this sexp node be persistant across missions +#define SEXP_FLAG_VARIABLE (1<<30) + +// sexp variable definitions +#define SEXP_VARIABLE_CHAR ('@') +// defines for type field of sexp_variable. Be sure not to conflict with type field of sexp_node +#define SEXP_VARIABLE_NUMBER (0x0010) +#define SEXP_VARIABLE_STRING (0x0020) +#define SEXP_VARIABLE_UNKNOWN (0x0040) +#define SEXP_VARIABLE_NOT_USED (0x0080) + +#define SEXP_VARIABLE_BLOCK (0X0001) +#define SEXP_VARIABLE_BLOCK_EXP (0X0002) + +#define BLOCK_EXP_SIZE 6 +#define INNER_RAD 0 +#define OUTER_RAD 1 +#define DAMAGE 2 +#define BLAST 3 +#define PROPAGATE 4 +#define SHOCK_SPEED 5 + + +#define SEXP_VARIABLE_SET (0x0100) +#define SEXP_VARIABLE_MODIFIED (0x0200) + +#define SEXP_TYPE_MASK(t) (t & 0x00ff) +#define SEXP_NODE_TYPE(n) (Sexp_nodes[n].type & 0x00ff) + +// defines for subtypes of atoms +#define SEXP_ATOM_LIST 0 +#define SEXP_ATOM_OPERATOR 1 +#define SEXP_ATOM_NUMBER 2 +#define SEXP_ATOM_STRING 3 + +// defines to short circuit evaluation when possible. Also used then goals can't +// be satisfied yet because ship (or wing) hasn't been created yet. + +#define SEXP_TRUE 1 +#define SEXP_FALSE 0 +#define SEXP_KNOWN_FALSE -1 +#define SEXP_KNOWN_TRUE -2 +#define SEXP_UNKNOWN -3 +#define SEXP_NAN -4 // not a number -- used when ships/wing part of boolean and haven't arrived yet +#define SEXP_NAN_FOREVER -5 // not a number and will never change -- used to falsify boolean sexpressions +#define SEXP_CANT_EVAL -6 // can't evaluate yet for whatever reason (acts like false) +#define SEXP_NUM_EVAL -7 // already completed an arithmetic operation and result is stored + +// defines for check_sexp_syntax +#define SEXP_CHECK_NONOP_ARGS -1 // non-operator has arguments +#define SEXP_CHECK_OP_EXPTECTED -2 // operator expected, but found data instead +#define SEXP_CHECK_UNKNOWN_OP -3 // unrecognized operator +#define SEXP_CHECK_TYPE_MISMATCH -4 // return type or data type mismatch +#define SEXP_CHECK_BAD_ARG_COUNT -5 // argument count in incorrect +#define SEXP_CHECK_UNKNOWN_TYPE -6 // unrecognized return type of data type + +#define SEXP_CHECK_INVALID_NUM -101 // number is not valid +#define SEXP_CHECK_INVALID_SHIP -102 // invalid ship name +#define SEXP_CHECK_INVALID_WING -103 // invalid wing name +#define SEXP_CHECK_INVALID_SUBSYS -104 // invalid subsystem +#define SEXP_CHECK_INVALID_IFF -105 // invalid iff string +#define SEXP_CHECK_INVALID_POINT -106 // invalid point +#define SEXP_CHECK_NEGATIVE_NUM -107 // negative number wasn't allowed +#define SEXP_CHECK_INVALID_SHIP_WING -108 // invalid ship/wing +#define SEXP_CHECK_INVALID_SHIP_TYPE -109 // invalid ship type +#define SEXP_CHECK_UNKNOWN_MESSAGE -110 // invalid message +#define SEXP_CHECK_INVALID_PRIORITY -111 // invalid priority for a message +#define SEXP_CHECK_INVALID_MISSION_NAME -112 // invalid mission name +#define SEXP_CHECK_INVALID_GOAL_NAME -113 // invalid goal name +#define SEXP_CHECK_INVALID_LEVEL -114 // mission level too high in campaign +#define SEXP_CHECK_INVALID_MSG_SOURCE -115 // invalid 'who-from' for a message being sent +#define SEXP_CHECK_INVALID_DOCKER_POINT -116 +#define SEXP_CHECK_INVALID_DOCKEE_POINT -117 +#define SEXP_CHECK_ORDER_NOT_ALLOWED -118 // ship goal (order) isn't allowed for given ship +#define SEXP_CHECK_DOCKING_NOT_ALLOWED -119 +#define SEXP_CHECK_NUM_RANGE_INVALID -120 +#define SEXP_CHECK_INVALID_EVENT_NAME -121 +#define SEXP_CHECK_INVALID_SKILL_LEVEL -122 +#define SEXP_CHECK_INVALID_MEDAL_NAME -123 +#define SEXP_CHECK_INVALID_WEAPON_NAME -124 +#define SEXP_CHECK_INVALID_SHIP_CLASS_NAME -125 +#define SEXP_CHECK_INVALID_GAUGE_NAME -126 +#define SEXP_CHECK_INVALID_JUMP_NODE -127 +#define SEXP_CHECK_INVALID_VARIABLE -128 + +#define TRAINING_CONTEXT_SPEED (1<<0) +#define TRAINING_CONTEXT_FLY_PATH (1<<1) + +// numbers used in special_training_check() function +#define SPECIAL_CHECK_TRAINING_FAILURE 2000 + +typedef struct sexp_ai_goal_link { + int ai_goal; + int op_code; +} sexp_ai_goal_link; + +typedef struct sexp_oper { + char *text; + int value; + int min, max; +} sexp_oper; + +typedef struct sexp_node { + char text[TOKEN_LENGTH]; + int type; // atom, list, or not used + int subtype; // type of atom or list? + int first; // if first parameter is sexp, index into Sexp_nodes + int rest; // index into Sexp_nodes of rest of parameters + int value; // known to be true, known to be false, or not known +} sexp_node; + +typedef struct sexp_variable { + int type; + char text[TOKEN_LENGTH]; + char variable_name[TOKEN_LENGTH]; +} sexp_variable; + +// next define used to eventually mark a directive as satisfied even though there may be more +// waves for a wing. bascially a hack for the directives display. +#define DIRECTIVE_WING_ZERO -999 + +extern sexp_oper Operators[]; +extern sexp_node Sexp_nodes[MAX_SEXP_NODES]; +extern sexp_variable Sexp_variables[MAX_SEXP_VARIABLES]; +extern int Num_operators; +extern int Locked_sexp_true, Locked_sexp_false; +extern int Directive_count; +extern int Sexp_useful_number; // a variable to pass useful info in from external modules +extern char *Sexp_string; +extern int Training_context; +extern int Training_context_speed_min; +extern int Training_context_speed_max; +extern int Training_context_speed_set; +extern int Training_context_speed_timestamp; +extern int Training_context_path; +extern int Training_context_goal_waypoint; +extern int Training_context_at_waypoint; +extern float Training_context_distance; +extern int Players_target; +extern ship_subsys *Players_targeted_subsys; +extern int Players_target_timestamp; +extern int Sexp_clipboard; // used by Fred + +extern void init_sexp(); +extern int alloc_sexp(char *text, int type, int subtype, int first, int rest); +extern int find_free_sexp(); +extern int free_one_sexp(int num); +extern int free_sexp(int num); +extern int free_sexp2(int num); +extern int dup_sexp_chain(int node); +extern int cmp_sexp_chains(int node1, int node2); +extern int find_sexp_list(int num); +extern int find_parent_operator(int num); +extern int is_sexp_top_level( int node ); +extern int identify_operator(char *token); +extern int find_operator(char *token); +extern int query_sexp_args_count(int index); +extern int check_sexp_syntax(int index, int return_type = OPR_BOOL, int recursive = 0, int *bindex = NULL, int mode = 0); +extern int get_sexp_main(void); // Returns start node +extern int stuff_sexp_variable_list(); +extern int eval_sexp(int index); +extern int query_operator_return_type(int op); +extern int query_operator_argument_type(int op, int argnum); +extern void update_sexp_references(char *old_name, char *new_name); +extern void update_sexp_references(char *old_name, char *new_name, int format); +extern int query_referenced_in_sexp(int mode, char *name, int *node); +extern int verify_vector(char *text); +extern void skip_white(char **str); +extern int validate_float(char **str); +extern int build_sexp_string(int cur_node, int level, int mode); +extern int sexp_query_type_match(int opf, int opr); +extern char *sexp_error_message(int num); +extern int count_free_sexp_nodes(); + +// functions to change the attributes of an sexpression tree to persistent or not persistent +extern void sexp_unmark_persistent( int n ); +extern void sexp_mark_persistent( int n ); +extern int waypoint_lookup(char *name); +extern int verify_sexp_tree(int node); +extern int query_sexp_ai_goal_valid(int sexp_ai_goal, int ship); +int query_node_in_sexp(int node, int sexp); +void flush_sexp_tree(int node); + +// sexp_variable +void sexp_modify_variable(int); +void sexp_modify_variable(char *text, int index); +int get_index_sexp_variable_name(const char* temp_name); +int sexp_variable_count(); +void sexp_variable_delete(int index); +void sexp_variable_sort(); +void sexp_fred_modify_variable(const char *text, const char *var_name, int index, int type); +int sexp_add_variable(const char *text, const char *var_name, int type, int index=-1); +int sexp_variable_allocate_block(const char* block_name, int block_type); +void sexp_variable_condense_block(); +void sexp_variable_block_free(const char *ship_name, int start_index, int block_type); + +#endif + diff --git a/include/sexp_tree.h b/include/sexp_tree.h new file mode 100644 index 0000000..6c14843 --- /dev/null +++ b/include/sexp_tree.h @@ -0,0 +1,250 @@ +#ifndef _SEXP_TREE_H +#define _SEXP_TREE_H + +#include "sexp.h" +#include "parselo.h" + +//#define MAX_SEXP_TREE_SIZE 500 +#define MAX_SEXP_TREE_SIZE 1050 + +// tree_node type +#define SEXPT_UNUSED 0x0000 +#define SEXPT_UNINIT 0x0001 +#define SEXPT_UNKNOWN 0x0002 + +#define SEXPT_VALID 0x1000 +#define SEXPT_TYPE_MASK 0X00ff +#define SEXPT_TYPE(X) (SEXPT_TYPE_MASK & X) + +#define SEXPT_OPERATOR 0x0010 +#define SEXPT_NUMBER 0x0020 +#define SEXPT_STRING 0x0040 +#define SEXPT_VARIABLE 0X0080 + +// tree_node flag +#define NOT_EDITABLE 0X00 +#define OPERAND 0x01 +#define EDITABLE 0x02 +#define COMBINED 0x04 + +// Bitmaps +#define BITMAP_OPERATOR 0 +#define BITMAP_DATA 1 +#define BITMAP_VARIABLE 2 +#define BITMAP_ROOT 3 +#define BITMAP_ROOT_DIRECTIVE 4 +#define BITMAP_CHAIN 5 +#define BITMAP_CHAIN_DIRECTIVE 6 +#define BITMAP_GREEN_DOT 7 +#define BITMAP_BLACK_DOT 8 +#define BITMAP_BLUE_DOT BITMAP_ROOT +#define BITMAP_RED_DOT BITMAP_ROOT_DIRECTIVE + + + +// tree behavior modes (or tree subtype) +#define ST_LABELED_ROOT 0x10000 +#define ST_ROOT_DELETABLE 0x20000 +#define ST_ROOT_EDITABLE 0x40000 + +#define MODE_GOALS (1 | ST_LABELED_ROOT | ST_ROOT_DELETABLE) +#define MODE_EVENTS (2 | ST_LABELED_ROOT | ST_ROOT_DELETABLE | ST_ROOT_EDITABLE) +#define MODE_CAMPAIGN (3 | ST_LABELED_ROOT | ST_ROOT_DELETABLE) + +// various tree operations notification codes (to be handled by derived class) +#define ROOT_DELETED 1 +#define ROOT_RENAMED 2 + +#define SEXP_ITEM_F_DUP (1<<0) + +/* + * Notes: An sexp_tree_item is basically a node in a tree. The sexp_tree is an array of + * these node items. + */ + +class sexp_tree_item +{ +public: + int type; + int parent; // pointer to parent of this item + int child; // pointer to first child of this item + int next; // pointer to next sibling + int flags; + char text[2 * TOKEN_LENGTH + 2]; + HTREEITEM handle; +}; + +class sexp_list_item +{ +public: + int type; + int op; + char *text; + int flags; + sexp_list_item *next; + + sexp_list_item() : flags(0), next(NULL) {} + void set_op(int op_num); + void set_data(char *str, int t = (SEXPT_STRING | SEXPT_VALID)); + void add_op(int op_num); + void add_data(char *str, int t = (SEXPT_STRING | SEXPT_VALID)); + void add_data_dup(char *str, int t = (SEXPT_STRING | SEXPT_VALID)); + void add_list(sexp_list_item *list); + void destroy(); +}; + +class sexp_tree : public CTreeCtrl +{ +public: + int sexp_tree::find_text(char *text, int *find); + int query_restricted_opf_range(int opf); + void verify_and_fix_arguments(int node); + void post_load(); + void update_help(HTREEITEM h); + char *help(int code); + HTREEITEM insert(LPCTSTR lpszItem, int image = BITMAP_ROOT, int sel_image = BITMAP_ROOT, HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST); + HTREEITEM handle(int node); + int get_type(HTREEITEM h); + void setup(CEdit *ptr = NULL); + int query_false(int node = -1); + int add_default_operator(int op, int argnum); + int get_default_value(sexp_list_item *item, int op, int i); + int query_default_argument_available(int op); + int query_default_argument_available(int op, int i); + void swap_roots(HTREEITEM one, HTREEITEM two); + void move_branch(int source, int parent = -1); + HTREEITEM move_branch(HTREEITEM source, HTREEITEM parent = TVI_ROOT, HTREEITEM after = TVI_LAST); + void copy_branch(HTREEITEM source, HTREEITEM parent = TVI_ROOT, HTREEITEM after = TVI_LAST); + void setup_selected(HTREEITEM h = NULL); + void add_or_replace_operator(int op, int replace_flag = 0); + void replace_one_arg_operator(char *op, char *data, int type); + void replace_operator(char *op); + void replace_data(char *data, int type); + void replace_variable_data(int var_idx, int type); + void link_modified(int *ptr); + sexp_tree(); + void ensure_visible(int node); + int node_error(int node, char *msg, int *bypass); + void expand_branch(HTREEITEM h); + void expand_operator(int node); + void merge_operator(int node); + int end_label_edit(HTREEITEM h, char *str); + int edit_label(HTREEITEM h); + int identify_arg_type(int node); + int count_args(int node); + void right_clicked(int mode = 0); + int ctree_size; + virtual void build_tree(); + void set_node(int index, int type, char *text); + void free_node(int node, int cascade = 0); + int allocate_node(int parent, int after = -1); + int allocate_node(); + void clear_tree(char *op = NULL); + void reset_handles(); + int save_tree(int node = -1); + void load_tree(int index, char *deflt = "true"); + void add_one_arg_operator(char *op, char *data, int type); + void add_operator(char *op, HTREEITEM h = TVI_ROOT); + int add_data(char *data, int type); + int add_variable_data(char *data, int type); + void add_sub_tree(int node, HTREEITEM root); + void hilite_item(int node); + int check_operator_validity(int op, int type); + char *match_closest_operator(char *str, int node); + void delete_sexp_tree_variable(const char *var_name); + void modify_sexp_tree_variable(const char *old_name, int sexp_var_index); + int get_item_index_to_var_index(); + int get_tree_name_to_sexp_variable_index(const char *tree_name); + int get_modify_variable_first_arg_index(); + int get_ambiguous_type(int parent); + int get_modify_variable_type(); + int get_variable_count(const char *var_name); + + + sexp_list_item *get_listing_opf(int opf, int parent_node, int arg_index); + sexp_list_item *get_listing_opf_null(); + sexp_list_item *get_listing_opf_bool(int parent_node = -1); + sexp_list_item *get_listing_opf_positive(); + sexp_list_item *get_listing_opf_number(); + sexp_list_item *get_listing_opf_ship(int parent_node = -1); + sexp_list_item *get_listing_opf_wing(); + sexp_list_item *get_listing_opf_subsystem(int parent_node, int arg_index); + sexp_list_item *get_listing_opf_point(); + sexp_list_item *get_listing_opf_iff(); + sexp_list_item *get_listing_opf_ai_goal(int parent_node); + sexp_list_item *get_listing_opf_docker_point(int parent_node); + sexp_list_item *get_listing_opf_dockee_point(int parent_node); + sexp_list_item *get_listing_opf_message(); + sexp_list_item *get_listing_opf_who_from(); + sexp_list_item *get_listing_opf_priority(); + sexp_list_item *get_listing_opf_waypoint_path(); + sexp_list_item *get_listing_opf_ship_point(); + sexp_list_item *get_listing_opf_ship_wing_point(); + sexp_list_item *get_listing_opf_mission_name(); + sexp_list_item *get_listing_opf_goal_name(int parent_node); + sexp_list_item *get_listing_opf_ship_wing(); + sexp_list_item *get_listing_opf_ship_type(); + sexp_list_item *get_listing_opf_keypress(); + sexp_list_item *get_listing_opf_event_name(int parent_node); + sexp_list_item *get_listing_opf_ai_order(); + sexp_list_item *get_listing_opf_skill_level(); + sexp_list_item *get_listing_opf_medal_name(); + sexp_list_item *get_listing_opf_weapon_name(); + sexp_list_item *get_listing_opf_ship_class_name(); + sexp_list_item *get_listing_opf_hud_gauge_name(); + sexp_list_item *get_listing_opf_huge_weapon(); + sexp_list_item *get_listing_opf_ship_not_player(); + sexp_list_item *get_listing_opf_jump_nodes(); + sexp_list_item *get_listing_opf_variable_names(); + sexp_list_item *get_listing_opf_variable_type(); + + int m_mode; + int item_index; + int select_sexp_node; // used to select an sexp item on dialog box open. + BOOL m_dragging; + HTREEITEM m_h_drag; + HTREEITEM m_h_drop; + CImageList *m_p_image_list; + CEdit *help_box; + CPoint m_pt; + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(sexp_tree) + public: + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + //}}AFX_VIRTUAL + + // Generated message map functions +protected: + //{{AFX_MSG(sexp_tree) + afx_msg void OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnLButtonUp(UINT nFlags, CPoint point); + afx_msg void OnDestroy(); + afx_msg void OnLButtonDown(UINT nFlags, CPoint point); + afx_msg void OnKeydown(NMHDR* pNMHDR, LRESULT* pResult); + //}}AFX_MSG + + void load_branch(int index, int parent); + int save_branch(int cur, int at_root = 0); + void free_node2(int node); + + int flag; + int *modified; + sexp_tree_item nodes[MAX_SEXP_TREE_SIZE]; + int total; + HTREEITEM item_handle; + int root_item; + // these 2 variables are used to help location data sources. Sometimes looking up + // valid data can require complex code just to get to an index that is required to + // locate data. These are set up in right_clicked() to try and short circuit having + // to do the lookup again in the code that actually does the adding or replacing of + // the data if it's selected. + int add_instance; // a source reference index indicator for adding data + int replace_instance; // a source reference index indicator for replacing data + + DECLARE_MESSAGE_MAP() +}; + +#endif + diff --git a/include/shade.h b/include/shade.h new file mode 100644 index 0000000..e86409a --- /dev/null +++ b/include/shade.h @@ -0,0 +1,53 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Shade.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for Shade.cpp + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 7 3/12/98 5:36p John + * Took out any unused shaders. Made shader code take rgbc instead of + * matrix and vector since noone used it like a matrix and it would have + * been impossible to do in hardware. Made Glide implement a basic + * shader for online help. + * + * 6 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 5 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 4 11/18/96 1:48p Allender + * added 16 bit version of (very slow) shader + * + * 3 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#ifndef _SHADE_H +#define _SHADE_H + +extern void grx_create_shader(shader * shade, float r, float g, float b, float c ); +extern void grx_set_shader( shader * shade ); +extern void gr8_shade(int x,int y,int w,int h); + +#endif + diff --git a/include/shieldsysdlg.h b/include/shieldsysdlg.h new file mode 100644 index 0000000..98a357e --- /dev/null +++ b/include/shieldsysdlg.h @@ -0,0 +1,70 @@ +/* + * $Logfile: /Freespace2/code/FRED2/ShieldSysDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Shield generator system editor. This dialog allows one to indicate whether all ships + * (on a certain team or of a certain type) have a shield system or not. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 2 8/18/97 9:31p Hoffoss + * Added grid adjustment dialog and shield system editor dialog. + * + * $NoKeywords: $ + */ + +extern int Shield_sys_teams[MAX_TEAM_NAMES]; +extern int Shield_sys_types[MAX_SHIP_TYPES]; + +///////////////////////////////////////////////////////////////////////////// +// shield_sys_dlg dialog + +class shield_sys_dlg : public CDialog +{ +// Construction +public: + void set_team(); + void set_type(); + shield_sys_dlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(shield_sys_dlg) + enum { IDD = IDD_SHIELD_SYS }; + int m_team; + int m_type; + //}}AFX_DATA + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(shield_sys_dlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(shield_sys_dlg) + virtual void OnOK(); + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangeTeam(); + afx_msg void OnSelchangeType(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/ship.h b/include/ship.h new file mode 100644 index 0000000..aad3978 --- /dev/null +++ b/include/ship.h @@ -0,0 +1,1246 @@ +/* + * $Logfile: /Freespace2/code/Ship/Ship.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * all sorts of cool stuff about ships + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 73 9/01/99 10:15a Dave + * + * 72 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 71 8/27/99 9:07p Dave + * LOD explosions. Improved beam weapon accuracy. + * + * 70 8/26/99 8:52p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 69 8/18/99 12:09p Andsager + * Add debug if message has no anim for message. Make messages come from + * wing leader. + * + * 68 8/16/99 10:04p Andsager + * Add special-warp-dist and special-warpout-name sexp for Knossos device + * warpout. + * + * 67 8/16/99 2:01p Andsager + * Knossos warp-in warp-out. + * + * 66 8/13/99 10:49a Andsager + * Knossos and HUGE ship warp out. HUGE ship warp in. Stealth search + * modes dont collide big ships. + * + * 65 8/02/99 10:39p Dave + * Added colored shields. OoOoOoooOoo + * + * 64 7/28/99 1:36p Andsager + * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp + * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack + * better. + * + * 63 7/26/99 8:06a Andsager + * Consistent personas + * + * 62 7/19/99 8:56p Andsager + * Added Ship_exited red_alert_carry flag and hull strength for RED ALERT + * carry over of Exited_ships + * + * 61 7/18/99 5:20p Dave + * Jump node icon. Fixed debris fogging. Framerate warning stuff. + * + * 60 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 59 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 58 7/08/99 12:06p Andsager + * Add turret-tagged-only and turret-tagged-clear sexp. + * + * 57 7/08/99 11:45a Dave + * Bumped up max ship types. + * + * 56 7/08/99 10:53a Dave + * New multiplayer interpolation scheme. Not 100% done yet, but still + * better than the old way. + * + * 55 7/06/99 10:45a Andsager + * Modify engine wash to work on any ship that is not small. Add AWACS + * ask for help. + * + * 54 7/01/99 4:23p Dave + * Full support for multiple linked ambient engine sounds. Added "big + * damage" flag. + * + * 53 7/01/99 11:44a Dave + * Updated object sound system to allow multiple obj sounds per ship. + * Added hit-by-beam sound. Added killed by beam sound. + * + * 52 6/30/99 5:53p Dave + * Put in new anti-camper code. + * + * 51 6/14/99 3:21p Andsager + * Allow collisions between ship and its debris. Fix up collision pairs + * when large ship is warping out. + * + * 50 6/07/99 4:21p Andsager + * Add HUD color for tagged object. Apply to target and radar. + * + * 49 6/03/99 11:43a Dave + * Added the ability to use a different model when rendering to the HUD + * target box. + * + * 48 6/01/99 8:35p Dave + * Finished lockarm weapons. Added proper supercap weapons/damage. Added + * awacs-set-radius sexpression. + * + * 47 5/28/99 9:26a Andsager + * Added check_world_pt_in_expanded_ship_bbox() function + * + * 46 5/26/99 11:46a Dave + * Added ship-blasting lighting and made the randomization of lighting + * much more customizable. + * + * 45 5/21/99 5:03p Andsager + * Add code to display engine wash death. Modify ship_kill_packet + * + * 44 5/20/99 7:00p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 43 5/14/99 11:50a Andsager + * Added vaporize for SMALL ships hit by HUGE beams. Modified dying + * frame. Enlarged debris shards and range at which visible. + * + * 42 5/12/99 2:55p Andsager + * Implemented level 2 tag as priority in turret object selection + * + * 41 5/11/99 10:16p Andsager + * First pass on engine wash effect. Rotation (control input), damage, + * shake. + * + * 40 4/28/99 3:11p Andsager + * Stagger turret weapon fire times. Make turrets smarter when target is + * protected or beam protected. Add weaopn range to weapon info struct. + * + * 39 4/28/99 9:39a Andsager + * flag for ship_weapon turret lock + * + * 38 4/23/99 12:01p Johnson + * Added SIF_HUGE_SHIP + * + * 37 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 36 4/19/99 11:01p Dave + * More sophisticated targeting laser support. Temporary checkin. + * + * 35 4/19/99 12:21p Johnson + * Allow ships with invisible polygons which do not collide + * + * 34 4/16/99 5:54p Dave + * Support for on/off style "stream" weapons. Real early support for + * target-painting lasers. + * + * 33 4/12/99 10:07p Dave + * Made network startup more forgiving. Added checkmarks to dogfight + * screen for players who hit commit. + * + * 32 4/02/99 9:55a Dave + * Added a few more options in the weapons.tbl for beam weapons. Attempt + * at putting "pain" packets into multiplayer. + * + * 31 3/31/99 8:24p Dave + * Beefed up all kinds of stuff, incluging beam weapons, nebula effects + * and background nebulae. Added per-ship non-dimming pixel colors. + * + * 30 3/30/99 5:40p Dave + * Fixed reinforcements for TvT in multiplayer. + * + * 29 3/28/99 5:58p Dave + * Added early demo code. Make objects move. Nice and framerate + * independant, but not much else. Don't use yet unless you're me :) + * + * 28 3/20/99 3:46p Dave + * Added support for model-based background nebulae. Added 3 new + * sexpressions. + * + * 27 3/08/99 7:03p Dave + * First run of new object update system. Looks very promising. + * + * 26 3/04/99 6:09p Dave + * Added in sexpressions for firing beams and checking for if a ship is + * tagged. + * + * 25 3/02/99 9:25p Dave + * Added a bunch of model rendering debug code. Started work on fixing + * beam weapon wacky firing. + * + * 24 3/01/99 7:39p Dave + * Added prioritizing ship respawns. Also fixed respawns in TvT so teams + * don't mix respawn points. + * + * 23 2/26/99 6:01p Andsager + * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay + * + * 22 2/26/99 4:14p Dave + * Put in the ability to have multiple shockwaves for ships. + * + * 21 2/11/99 5:22p Andsager + * Fixed bugs, generalized block Sexp_variables + * + * 20 2/11/99 2:15p Andsager + * Add ship explosion modification to FRED + * + * 19 2/03/99 6:06p Dave + * Groundwork for FS2 PXO usertracker support. Gametracker support next. + * + * $NoKeywords: $ + */ + +#ifndef _SHIP_H +#define _SHIP_H + +#include "parselo.h" // for defintions of token lengths -- maybe move this elsewhere later +#include "model.h" +#include "2d.h" // for color def +#include "multi_obj.h" +#include "trails.h" +#include "palman.h" + +struct object; + +// Part of the player died system. +extern vector Dead_camera_pos, Original_vec_to_deader; + +// States for player death sequence, stuffed in Player_died_state. +#define PDS_NONE 1 +#define PDS_DIED 2 +#define PDS_EJECTED 3 + +#ifdef NDEBUG + #ifdef FRED + #define MAX_SHIPS 100 // max number of ship instances there can be. + #define SHIPS_LIMIT 100 // what MAX_SHIPS will be at release time (for error checking in debug mode) + #else + #define MAX_SHIPS 150 // max number of ship instances there can be. + #define SHIPS_LIMIT 150 // what MAX_SHIPS will be at release time (for error checking in debug mode) + #endif +#else +#define MAX_SHIPS 200 // max number of ship instances there can be. +#define SHIPS_LIMIT 200 // what MAX_SHIPS will be at release time (for error checking in debug mode) +#endif + +#define HULL_DAMAGE_THRESHOLD_PERCENT 0.25f // Apply damage to hull, not shield if shield < this +#define HP_SCALE 1.2 // 1.2 means die when 20% of hits remaining +#define MAX_SHIP_HITS 8 // hits to kill a ship +#define MAX_SHIP_DETAIL_LEVELS 5 // maximum detail levels that a ship can render at +#define MAX_REINFORCEMENTS 10 + +#define MAX_ESCORT_SHIPS 3 +#define MAX_SHIP_WEAPONS 5 +#define MAX_OBJECT_STATUS 10 + +#define MAX_PRIMARY_WEAPON_TYPES 10 +#define MAX_SECONDARY_WEAPON_TYPES 10 + +// defines for 'direction' parameter of ship_select_next_primary() +#define CYCLE_PRIMARY_NEXT 0 +#define CYCLE_PRIMARY_PREV 1 + +#define BANK_1 0 +#define BANK_2 1 +#define BANK_3 2 +#define BANK_4 3 +#define BANK_5 4 +#define BANK_6 5 +#define BANK_7 6 +#define BANK_8 7 +#define BANK_9 8 + +#define TYPE_ATTACK_PROTECT 0 +#define TYPE_REPAIR_REARM 1 + +#define MAX_REINFORCEMENT_MESSAGES 5 + +#define RF_IS_AVAILABLE (1<<0) // reinforcement is now available + +typedef struct { + char name[NAME_LENGTH]; // ship or wing name (ship and wing names don't collide) + int type; // what operations this reinforcement unit can perform + int uses; // number of times reinforcemnt unit can be used + int num_uses; // number of times this reinforcement was actually used + int arrival_delay; // how long after called does this reinforcement appear + int flags; + char no_messages[MAX_REINFORCEMENT_MESSAGES][NAME_LENGTH]; // list of messages to possibly send when calling for reinforcement not available + char yes_messages[MAX_REINFORCEMENT_MESSAGES][NAME_LENGTH]; // list of messages to acknowledge reinforcement on the way +} reinforcements; + +// ship weapon flags +#define SW_FLAG_BEAM_FREE (1<<0) // if this is a beam weapon, its free to fire +#define SW_FLAG_TURRET_LOCK (1<<1) // is this turret is free to fire or locked +#define SW_FLAG_TAGGED_ONLY (1<<2) // only fire if target is tagged + +typedef struct { + int num_primary_banks; // Number of primary banks (same as model) + int num_secondary_banks; // Number of secondary banks (same as model) + + int primary_bank_weapons[MAX_PRIMARY_BANKS]; // Weapon_info[] index for the weapon in the bank + int secondary_bank_weapons[MAX_SECONDARY_BANKS]; // Weapon_info[] index for the weapon in the bank + + int current_primary_bank; // currently selected primary bank + int current_secondary_bank; // currently selected secondary bank + + int next_primary_fire_stamp[MAX_PRIMARY_BANKS]; // next time this primary bank can fire + int next_secondary_fire_stamp[MAX_SECONDARY_BANKS]; // next time this secondary bank can fire + + int secondary_bank_ammo[MAX_SECONDARY_BANKS]; // Number of missiles left in secondary bank + int secondary_bank_start_ammo[MAX_SECONDARY_BANKS]; // Number of missiles starting in secondary bank + int secondary_bank_capacity[MAX_SECONDARY_BANKS]; // Max number of missiles in bank + int secondary_next_slot[MAX_SECONDARY_BANKS]; // Next slot to fire in the bank + int secondary_bank_rearm_time[MAX_SECONDARY_BANKS]; // timestamp which indicates when bank can get new missile + + int last_fired_weapon_index; // Index of last fired secondary weapon. Used for remote detonates. + int last_fired_weapon_signature; // Signature of last fired weapon. + int detonate_weapon_time; // time at which last fired weapon can be detonated + int ai_class; + + int flags; // see SW_FLAG_* defines above +} ship_weapon; + + +// structure definition for a linked list of subsystems for a ship. Each subsystem has a pointer +// to the static data for the subsystem. The obj_subsystem data is defined and read in the model +// code. Other dynamic data (such as current_hits) should remain in this structure. +typedef struct ship_subsys { + struct ship_subsys *next, *prev; // Index of next and previous objects in list. + model_subsystem *system_info; // pointer to static data for this subsystem -- see model.h for definition + float current_hits; // current number of hits this subsystem has left. + + // turret info + vector turret_last_fire_direction; // direction pointing last time this turret fired + int turret_next_enemy_check_stamp; // time at which to next look for a new enemy. + int turret_next_fire_stamp; // next time this turret can fire + int turret_enemy_objnum; // object index of ship this turret is firing upon + int turret_enemy_sig; // signature of object ship this turret is firing upon + int turret_next_fire_pos; // counter which tells us which gun position to fire from next + float turret_time_enemy_in_range; // Number of seconds enemy in view cone, accuracy improves over time. + ship_subsys *targeted_subsys; // subsystem this turret is attacking + + int turret_pick_big_attack_point_timestamp; // Next time to pick an attack point for this turret + vector turret_big_attack_point; // local coordinate of point for this turret to attack on enemy + + // swarm (rapid fire) info + int turret_swarm_info_index; + + // awacs info + float awacs_intensity; + float awacs_radius; + + ship_weapon weapons; + + // Data the renderer needs for ship instance specific data, like + // angles and if it is blown off or not. + // There are 2 of these because turrets need one for the turret and one for the barrel. + // Things like radar dishes would only use one. + submodel_instance_info submodel_info_1; // Instance data for main turret or main object + submodel_instance_info submodel_info_2; // Instance data for turret guns, if there is one + + int disruption_timestamp; // time at which subsystem isn't disrupted + + int subsys_cargo_name; // cap ship cargo on subsys + int subsys_cargo_revealed; +} ship_subsys; + +// structure for subsystems which tells us the total count of a particular type of subsystem (i.e. +// we might have 3 engines), and the relative strength of the subsystem. The #defines in model.h +// for SUBSYSTEM_xxx will be used as indices into this array. +typedef struct ship_subsys_info { + int num; // number of subsystems of type on this ship; + float total_hits; // total number of hits between all subsystems of this type. + float current_hits; // current count of hits for all subsystems of this type. +} ship_subsys_info; + +#define MAX_IFF_COLORS 7 + +#define IFF_COLOR_HOSTILE 0 +#define IFF_COLOR_FRIENDLY 1 +#define IFF_COLOR_NEUTRAL 2 +#define IFF_COLOR_UNKNOWN 3 +#define IFF_COLOR_SELECTION 4 +#define IFF_COLOR_MESSAGE 5 +#define IFF_COLOR_TAGGED 6 + +#define SET_COLOR_HOSTILE gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][0]) +#define SET_COLOR_FRIENDLY gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][0]) +#define SET_COLOR_NEUTRAL gr_set_color_fast(&IFF_colors[IFF_COLOR_NEUTRAL][0]) +#define SET_COLOR_UNKNOWN gr_set_color_fast(&IFF_colors[IFF_COLOR_UNKNOWN][0]) +#define SET_COLOR_TAGGED gr_set_color_fast(&IFF_colors[IFF_COLOR_TAGGED][0]) + +// reference an array to store the different IFF colors +extern color IFF_colors[MAX_IFF_COLORS][2]; + +#include "ai.h" // ship_subsys must be declared before we include this. +#include "weapon.h" // ship_subsys must be declared before we include this. + +//#define MAX_SHIP_SUBOBJECTS 50 + +//extern ship_subobj Ship_subsystems[MAX_SHIP_SUBOBJECTS]; + +// states for the flags variable within the ship structure +// low bits are for mission file savable flags.. +// FRED needs these to be the low-order bits with no holes, +// because it indexes into an array, Hoffoss says. +#define SF_IGNORE_COUNT (1 << 0) // ignore this ship when counting ship types for goals +#define SF_REINFORCEMENT (1 << 1) // this ship is a reinforcement ship +#define SF_ESCORT (1 << 2) // this ship is an escort ship +#define SF_NO_ARRIVAL_MUSIC (1 << 3) // don't play arrival music when ship arrives +#define SF_NO_ARRIVAL_WARP (1 << 4) // no arrival warp in effect +#define SF_NO_DEPARTURE_WARP (1 << 5) // no departure warp in effect +#define SF_LOCKED (1 << 6) // can't manipulate ship in loadout screens +#define SF_INVULNERABLE (1 << 7) + +// high bits are for internal flags not saved to mission files +// Go from bit 31 down to bit 3 +#define SF_KILL_BEFORE_MISSION (1 << 31) +#define SF_DYING (1 << 30) +#define SF_DISABLED (1 << 29) +#define SF_DEPART_WARP (1 << 28) // ship is departing via warp-out +#define SF_DEPART_DOCKBAY (1 << 27) // ship is departing via docking bay +#define SF_ARRIVING_STAGE_1 (1 << 26) // ship is arriving. In other words, doing warp in effect, stage 1 +#define SF_ARRIVING_STAGE_2 (1 << 25) // ship is arriving. In other words, doing warp in effect, stage 2 +#define SF_ARRIVING (SF_ARRIVING_STAGE_1|SF_ARRIVING_STAGE_2) +#define SF_ENGINES_ON (1 << 24) // engines sound should play if set +#define SF_INITIALLY_DOCKED (1 << 23) // used by Fred to tell if this ship is initially docked with something else +#define SF_CARGO_REVEALED (1 << 22) // ship's cargo is revealed to all friendly ships +#define SF_FROM_PLAYER_WING (1 << 21) // set for ships that are members of any player starting wing +#define SF_PRIMARY_LINKED (1 << 20) // ships primary weapons are linked together +#define SF_SECONDARY_DUAL_FIRE (1 << 19) // ship is firing two missiles from the current secondary bank +#define SF_WARP_BROKEN (1 << 18) // set when warp drive is not working, but is repairable +#define SF_WARP_NEVER (1 << 17) // set when ship can never warp +#define SF_TRIGGER_DOWN (1 << 16) // ship has its "trigger" held down +#define SF_AMMO_COUNT_RECORDED (1 << 15) // we've recorded the inital secondary weapon count (which is used to limit support ship rearming) +#define SF_HIDDEN_FROM_SENSORS (1 << 14) // ship doesn't show up on sensors, blinks in/out on radar +#define SF_SCANNABLE (1 << 13) // ship is "scannable". Play scan effect and report as "Scanned" or "not scanned". +#define SF_WARPED_SUPPORT (1 << 12) // set when this is a support ship which was warped in automatically +#define SF_EXPLODED (1 << 11) // ship has exploded (needed for kill messages) +#define SF_SHIP_HAS_SCREAMED (1 << 10) // ship has let out a death scream +#define SF_RED_ALERT_STORE_STATUS (1 << 9) // ship status should be stored/restored if red alert mission +#define SF_VAPORIZE (1<<8) // ship is vaporized by beam - alternative death sequence + +// MWA -- don't go below whatever bitfield is used for Fred above (currently 7)!!!! + +#define SF_DEPARTING (SF_DEPART_WARP | SF_DEPART_DOCKBAY) // ship is departing +#define SF_CANNOT_WARP (SF_WARP_BROKEN | SF_WARP_NEVER | SF_DISABLED) // ship cannot warp out + +#define MAX_DAMAGE_SLOTS 32 +#define MAX_SHIP_ARCS 2 // How many "arcs" can be active at once... Must be less than MAX_ARC_EFFECTS in model.h. +#define NUM_SUB_EXPL_HANDLES 2 // How many different big ship sub explosion sounds can be played. + +#define MAX_SHIP_CONTRAILS 6 + +typedef struct ship_spark { + vector pos; // position of spark in the submodel's RF + int submodel_num; // which submodel is making the spark + int end_time; +} ship_spark; + +#define AWACS_WARN_NONE (1 << 0) +#define AWACS_WARN_25 (1 << 1) +#define AWACS_WARN_75 (1 << 2) + + +typedef struct ship { + int objnum; + int ai_index; // Index in Ai_info of ai_info associated with this ship. + int ship_info_index; // Index in ship_info for this ship + int modelnum; + int hotkey; + int escort_priority; + int score; + int respawn_priority; + + // BEGIN PACK ubytes and chars + ubyte pre_death_explosion_happened; // If set, it means the 4 or 5 smaller explosions + ubyte wash_killed; + char cargo1; + + // ship wing status info + char wing_status_wing_index; // wing index (0-4) in wingman status gauge + char wing_status_wing_pos; // wing position (0-5) in wingman status gauge + + // alternate type name index + char alt_type_index; // only used for display purposes (read : safe) + + // targeting laser info + char targeting_laser_bank; // -1 if not firing, index into polymodel gun points if it _is_ firing + // corkscrew missile stuff + ubyte num_corkscrew_to_fire; // # of corkscrew missiles lef to fire + // END PACK + // targeting laser info + int targeting_laser_objnum; // -1 if invalid, beam object # otherwise + // corkscrew missile stuff + int next_corkscrew_fire; // next time to fire a corkscrew missile + + int final_death_time; // Time until big fireball starts + int really_final_death_time; // Time until ship breaks up and disappears + vector deathroll_rotvel; // Desired death rotational velocity + int dock_objnum_when_dead; // Objnum of ship docked to when ship died (-1 if none) + int final_warp_time; // pops when ship is completely warped out or warped in. Used for both warp in and out. + vector warp_effect_pos; // where the warp in effect comes in at + vector warp_effect_fvec; // The warp in effect's forward vector + int next_fireball; + + int next_hit_spark; + int num_hits; // Note, this is the number of spark emitter positions! + ship_spark sparks[MAX_SHIP_HITS]; + + int special_exp_index; + + char ship_name[NAME_LENGTH]; + + int team; // Which team it's on, HOSTILE, FRIENDLY, UNKNOWN, NEUTRAL + + fix time_cargo_revealed; // time at which the cargo was revealed + + int arrival_location; + int arrival_distance; // how far away this ship should arrive + int arrival_anchor; // name of object this ship arrives near (or in front of) + int arrival_cue; + int arrival_delay; + int departure_location; // depart to hyperspace or someplace else (like docking bay) + int departure_anchor; // when docking day -- index of ship to use + int departure_cue; // sexpression to eval when departing + int departure_delay; // time in seconds after sexp is true that we delay. + int determination; + int wingnum; // wing number this ship is in. -1 if in no wing, Wing array index otherwise + int orders_accepted; // set of orders this ship will accept from the player. + + // Subsystem fields. The subsys_list is a list of all subsystems (which might include multiple types + // of a particular subsystem, like engines). The subsys_info struct is information for particular + // types of subsystems. (i.e. the list might contain 3 engines. There will be one subsys_info entry + // describing the state of all engines combined) -- MWA 4/1/97 + ship_subsys subsys_list; // linked list of subsystems for this ship. + ship_subsys *last_targeted_subobject[MAX_PLAYERS]; // Last subobject that has been targeted. NULL if none;(player specific) + ship_subsys_info subsys_info[SUBSYSTEM_MAX]; // info on particular generic types of subsystems + + float *shield_integrity; // Integrity at each triangle in shield mesh. + + // ETS fields + int shield_recharge_index; // index into array holding the shield recharge rate + int weapon_recharge_index; // index into array holding the weapon recharge rate + int engine_recharge_index; // index into array holding the engine recharge rate + float weapon_energy; // Number of EUs in energy reserves + float current_max_speed; // Max ship speed (based on energy diverted to engines) + int next_manage_ets; // timestamp for when ai can next modify ets ( -1 means never ) + + uint flags; // flag variable to contain ship state (see SF_ #defines) + int reinforcement_index; // index into reinforcement struct or -1 + + float afterburner_fuel; // amount of afterburner fuel remaining (capacity is stored + // as afterburner_fuel_capacity in ship_info). + + int cmeasure_count; // Number of charges of countermeasures this ship can hold. + int current_cmeasure; // Currently selected countermeasure. + + int cmeasure_fire_stamp; // Time at which can fire countermeasure. + + float target_shields_delta; // Target for shield recharge system. + float target_weapon_energy_delta; // Target for recharge system. + ship_weapon weapons; + + int shield_hits; // Number of hits on shield this frame. + + float wash_intensity; + vector wash_rot_axis; + int wash_timestamp; + + // store blast information about shockwaves that hit the ship +// ship_shockwave sw; + + int num_swarm_missiles_to_fire; // number of swarm missiles that need to be launched + int next_swarm_fire; // timestamp of next swarm missile to fire + int next_swarm_path; // next path number for swarm missile to take + int num_turret_swarm_info; // number of turrets in process of launching swarm + + int group; // group ship is in, or -1 if none. Fred thing + int death_roll_snd; // id of death roll sound, may need to be stopped early + int ship_list_index; // index of ship in Ship_objs[] array + + int thruster_bitmap; // What frame the current thruster bitmap is at for this ship + float thruster_frame; // Used to keep track of which frame the animation should be on. + + int thruster_glow_bitmap; // What frame the current thruster engine glow bitmap is at for this ship + float thruster_glow_frame; // Used to keep track of which frame the engine glow animation should be on. + float thruster_glow_noise; // Noise for current frame + + int next_engine_stutter; // timestamp to time the engine stuttering when a ship dies + + float total_damage_received; // total damage received (for scoring purposes) + float damage_ship[MAX_DAMAGE_SLOTS]; // damage applied from each player + int damage_ship_id[MAX_DAMAGE_SLOTS]; // signature of the damager (corresponds to each entry in damage_ship) + int persona_index; // which persona is this guy. + + int subsys_disrupted_flags; // bitflags used to check if SUBYSTEM_* is disrupted or not + int subsys_disrupted_check_timestamp; // timer to control how oftern flags are set/cleared in subsys_disrupted_flags + + uint create_time; // time ship was created, set by gettime() + + // keep multiplayer specific stuff below this point + int ts_index; // index into the team select and Wss_slots array (or -1 if not in one of those arrays) + + int large_ship_blowup_index; // -1 if not a large ship exploding, else this is an index used by the shipfx large ship exploding code. + int sub_expl_sound_handle[NUM_SUB_EXPL_HANDLES]; + + + // Stuff for showing electrical arcs on damaged ships + vector arc_pts[MAX_SHIP_ARCS][2]; // The endpoints of each arc + int arc_timestamp[MAX_SHIP_ARCS]; // When this times out, the spark goes away. -1 is not used + ubyte arc_type[MAX_SHIP_ARCS]; // see MARC_TYPE_* defines in model.h + int arc_next_time; // When the next arc will be created. + + // emp missile stuff + float emp_intensity; // <= 0.0f if no emp effect present + float emp_decr; // how much to decrement EMP effect per second for this ship + + // contrail stuff + short trail_num[MAX_SHIP_CONTRAILS]; + + // tag stuff + float tag_total; // total tag time + float tag_left; // total tag remaining + fix time_first_tagged; + float level2_tag_total; // total tag time + float level2_tag_left; // total tag remaining + + // old-style object update stuff + np_update np_updates[MAX_PLAYERS]; // for both server and client + + // lightning timestamp + int lightning_stamp; + + // AWACS warning flag + ubyte awacs_warning_flag; + + // Special warpout objnum (warpout at knossos) + int special_warp_objnum; +} ship; + +// structure and array def for ships that have exited the game. Keeps track of certain useful +// information. +#define SEF_DESTROYED (1<<0) +#define SEF_DEPARTED (1<<1) +#define SEF_CARGO_KNOWN (1<<2) +#define SEF_PLAYER_DELETED (1<<3) // ship deleted by a player in ship select +#define SEF_BEEN_TAGGED (1<<4) +#define SEF_RED_ALERT_CARRY (1<<5) + +#define MAX_EXITED_SHIPS 200 + +typedef struct exited_ship { + char ship_name[NAME_LENGTH]; + int obj_signature; + int team; + int flags; + fix time; + int hull_strength; +} exited_ship; + +extern exited_ship Ships_exited[MAX_EXITED_SHIPS]; + +// a couple of functions to get at the data +extern void ship_add_exited_ship( ship *shipp, int reason ); +extern int ship_find_exited_ship_by_name( char *name ); +extern int ship_find_exited_ship_by_signature( int signature); + +#define SIF_DO_COLLISION_CHECK (1 << 0) +#define SIF_PLAYER_SHIP (1 << 1) +#define SIF_DEFAULT_PLAYER_SHIP (1 << 2) +#define SIF_PATH_FIXUP (1 << 3) // when set, path verts have been set for this ship's model +#define SIF_SUPPORT (1 << 4) // this ship can perform repair/rearm functions +#define SIF_AFTERBURNER (1 << 5) // this ship has afterburners + +// If you add a new ship type, then please add the appriopriate type in the ship_count +// structure later in this file!!! and let MWA know!! +#define SIF_CARGO (1 << 6) // is this ship a cargo type ship -- used for docking purposes +#define SIF_FIGHTER (1 << 7) // this ship is a fighter +#define SIF_BOMBER (1 << 8) // this ship is a bomber +#define SIF_CRUISER (1 << 9) // this ship is a cruiser +#define SIF_FREIGHTER (1 << 10) // this ship is a freighter +#define SIF_CAPITAL (1 << 11) // this ship is a capital/installation ship +#define SIF_TRANSPORT (1 << 12) // this ship is a transport +#define SIF_NAVBUOY (1 << 13) // AL 11-24-97: this is a navbuoy +#define SIF_SENTRYGUN (1 << 14) // AL 11-24-97: this is a navbuoy with turrets +#define SIF_ESCAPEPOD (1 << 15) // AL 12-09-97: escape pods that fire from big ships +#define SIF_NO_SHIP_TYPE (1 << 16) // made distinct to help trap errors + +#define SIF_SHIP_COPY (1 << 17) // this ship is a copy of another ship in the table -- meaningful for scoring and possible other things +#define SIF_IN_TECH_DATABASE (1 << 18) // is ship type to be listed in the tech database? +#define SIF_IN_TECH_DATABASE_M (1 << 19) // is ship type to be listed in the tech database for multiplayer? + +#define SIF_STEALTH (1 << 20) // the ship is a stealth ship +#define SIF_SUPERCAP (1 << 21) // the ship is a supercap +#define SIF_DRYDOCK (1 << 22) // the ship is a drydock +#define SIF_DONT_COLLIDE_INVIS (1 << 23) // Don't collide with this ship's invisible polygons + +#define SIF_BIG_DAMAGE (1 << 24) // this ship is classified as a big damage ship +#define SIF_HAS_AWACS (1 << 25) // ship has an awacs subsystem + +#define SIF_CORVETTE (1 << 26) // corvette class (currently this only means anything for briefing icons) +#define SIF_GAS_MINER (1 << 27) // also just for briefing icons +#define SIF_AWACS (1 << 28) // ditto + +#define SIF_KNOSSOS_DEVICE (1 << 29) // this is the knossos device + +#define SIF_NO_FRED (1 << 30) // not available in fred + +#define MAX_SHIP_FLAGS 8 // Number of flags for flags field in ship_info struct +#define SIF_DEFAULT_VALUE (SIF_DO_COLLISION_CHECK) +#define SIF_ALL_SHIP_TYPES (SIF_CARGO | SIF_FIGHTER | SIF_BOMBER | SIF_CRUISER | SIF_FREIGHTER | SIF_CAPITAL | SIF_TRANSPORT | SIF_SUPPORT | SIF_NO_SHIP_TYPE | SIF_NAVBUOY | SIF_SENTRYGUN | SIF_ESCAPEPOD | SIF_SUPERCAP | SIF_CORVETTE | SIF_GAS_MINER | SIF_AWACS | SIF_KNOSSOS_DEVICE) +#define SIF_SMALL_SHIP (SIF_FIGHTER | SIF_BOMBER | SIF_SUPPORT | SIF_ESCAPEPOD ) +#define SIF_BIG_SHIP (SIF_CRUISER | SIF_FREIGHTER | SIF_TRANSPORT | SIF_CORVETTE | SIF_GAS_MINER | SIF_AWACS) +#define SIF_HUGE_SHIP (SIF_CAPITAL | SIF_SUPERCAP | SIF_DRYDOCK | SIF_KNOSSOS_DEVICE) +#define SIF_NOT_FLYABLE (SIF_CARGO | SIF_NAVBUOY | SIF_SENTRYGUN) // AL 11-24-97: this useful to know for targeting reasons +#define SIF_HARMLESS (SIF_CARGO | SIF_NAVBUOY | SIF_ESCAPEPOD) // AL 12-3-97: ships that are not a threat +// for ships of this type, we make beam weapons miss a little bit otherwise they'd be way too powerful +#define SIF_BEAM_JITTER (SIF_CARGO | SIF_FIGHTER | SIF_BOMBER | SIF_FREIGHTER | SIF_TRANSPORT | SIF_SENTRYGUN | SIF_NAVBUOY | SIF_ESCAPEPOD) + +// defines for ship types. These defines are distinct from the flag values in the ship_info struct. These +// values are used for array lookups, etc. +#define MAX_SHIP_TYPE_COUNTS 20 + +#define SHIP_TYPE_NONE 0 +#define SHIP_TYPE_CARGO 1 +#define SHIP_TYPE_FIGHTER_BOMBER 2 +#define SHIP_TYPE_CRUISER 3 +#define SHIP_TYPE_FREIGHTER 4 +#define SHIP_TYPE_CAPITAL 5 +#define SHIP_TYPE_TRANSPORT 6 +#define SHIP_TYPE_REPAIR_REARM 7 +#define SHIP_TYPE_NAVBUOY 8 +#define SHIP_TYPE_SENTRYGUN 9 +#define SHIP_TYPE_ESCAPEPOD 10 +#define SHIP_TYPE_SUPERCAP 11 +#define SHIP_TYPE_STEALTH 12 +#define SHIP_TYPE_FIGHTER 13 +#define SHIP_TYPE_BOMBER 14 +#define SHIP_TYPE_DRYDOCK 15 +#define SHIP_TYPE_AWACS 16 +#define SHIP_TYPE_GAS_MINER 17 +#define SHIP_TYPE_CORVETTE 18 +#define SHIP_TYPE_KNOSSOS_DEVICE 19 + +// The real FreeSpace ship_info struct. +typedef struct ship_info { + char name[NAME_LENGTH]; // name for the ship + char short_name[NAME_LENGTH]; // short name, for use in the editor? + int species; // which species this craft belongs to + char *type_str; // type string used by tooltips + char *maneuverability_str; // string used by tooltips + char *armor_str; // string used by tooltips + char *manufacturer_str; // string used by tooltips + char *desc; // string used by tooltips + char *tech_desc; // string used by tech database + + char *ship_length; // string used by multiplayer ship desc + char *gun_mounts; // string used by multiplayer ship desc + char *missile_banks; // string used by multiplayer ship desc + + char pof_file[NAME_LENGTH]; // POF file to load/associate with ship + char pof_file_hud[NAME_LENGTH]; // POF file to load for the HUD target box + int num_detail_levels; // number of detail levels for this ship + int detail_distance[MAX_SHIP_DETAIL_LEVELS]; // distance to change detail levels at + int modelnum; // ship model + int modelnum_hud; // model to use when rendering to the HUD (eg, mini supercap) + float density; // density of the ship in g/cm^3 (water = 1) + float damp; // drag + float rotdamp; // rotational drag + vector max_vel; // max velocity of the ship in the linear directions -- read from ships.tbl + vector afterburner_max_vel; // max velocity of the ship in the linear directions when afterburners are engaged -- read from ships.tbl + vector max_rotvel; // maximum rotational velocity + vector rotation_time; // time to rotate in x/y/z dimension traveling at max rotvel + float srotation_time; // scalar, computed at runtime as (rotation_time.x + rotation_time.y)/2 + float max_rear_vel; // max speed ship can go backwards. + float forward_accel; + float afterburner_forward_accel; // forward acceleration with afterburner engaged + float forward_decel; + float slide_accel; + float slide_decel; + int flags; // See SIF_xxxx + int ai_class; // Index into Ai_classes[]. Defined in ai.tbl + float shields; + float max_speed, min_speed, max_accel; + + // ship explosion info + float inner_rad; // radius within which maximum damage is applied + float outer_rad; // radius at which no damage is applied + float damage; // maximum damage applied from ship explosion + float blast; // maximum blast impulse from ship explosion + int explosion_propagates; // If true, then the explosion propagates + float shockwave_speed; // speed at which shockwave expands, 0 means no shockwave + int shockwave_count; // the # of total shockwaves + + // subsystem information + int n_subsystems; // this number comes from ships.tbl + model_subsystem *subsystems; // see model.h for structure definition + + // Energy Transfer System fields + float power_output; // power output of ships reactor (EU/s) + float max_overclocked_speed; // max speed when 100% power output sent to engines + float max_weapon_reserve; // maximum energy that can be stored for primary weapon usage + + // Afterburner fields + float afterburner_fuel_capacity; // maximum afterburner fuel that can be stored + float afterburner_burn_rate; // rate in fuel/second that afterburner consumes fuel + float afterburner_recover_rate; // rate in fuel/second that afterburner recovers fuel + + int cmeasure_max; // Number of charges of countermeasures this ship can hold. + + int num_primary_banks; // Actual number of primary banks (property of model) + int num_secondary_banks; // Actual number of secondary banks (property of model) + int primary_bank_weapons[MAX_PRIMARY_BANKS]; // Weapon_info[] index for the weapon in the bank + int secondary_bank_weapons[MAX_SECONDARY_BANKS]; // Weapon_info[] index for the weapon in the bank + int secondary_bank_ammo_capacity[MAX_SECONDARY_BANKS]; // Capacity of bank (not number of missiles) + + float initial_hull_strength; // Initial hull strength of this class of ship. + int engine_snd; // handle to engine sound for ship (-1 if no engine sound) + + vector closeup_pos; // position for camera when using ship in closeup view (eg briefing and hud target monitor) + float closeup_zoom; // zoom when using ship in closeup view (eg briefing and hud target monitor) + + int allowed_weapons[MAX_WEAPON_TYPES]; // array which specifies which weapons can be loaded out by the + // player during weapons loadout. + + ubyte shield_icon_index; // index to locate ship-specific animation frames for the shield on HUD + char icon_filename[NAME_LENGTH]; // filename for icon that is displayed in ship selection + char anim_filename[NAME_LENGTH]; // filename for animation that plays in ship selection + char overhead_filename[NAME_LENGTH]; // filename for animation that plays weapons loadout + + int score; // default score for this ship + + int scan_time; // time to scan this ship (in ms) + + // contrail info + trail_info ct_info[MAX_SHIP_CONTRAILS]; + int ct_count; + + // rgb non-dimming pixels for this ship type + int num_nondark_colors; + ubyte nondark_colors[MAX_NONDARK_COLORS][3]; + + // rgb shield color + ubyte shield_color[3]; +} ship_info; + +extern int num_wings; +extern ship Ships[MAX_SHIPS]; +extern ship *Player_ship; + +// Data structure to track the active missiles +typedef struct ship_obj { + ship_obj *next, *prev; + int flags, objnum; +} ship_obj; +extern ship_obj Ship_obj_list; + +typedef struct engine_wash_info +{ + char name[32]; + float angle; // half angle of cone around engine thruster + float radius_mult; // multiplier for radius + float length; // length of engine wash, measured from thruster + float intensity; // intensity of engine wash +} engine_wash_info; + +#define MAX_ENGINE_WASH_TYPES 20 +extern engine_wash_info Engine_wash_info[MAX_ENGINE_WASH_TYPES]; + +// **************************************************************** +// DO NOT CHANGE THIS - IT WILL LIKELY BREAK FREESPACE2 PXO SUPPORT +// TALK TO DAVE B FIRST +// **************************************************************** +#define MAX_SHIP_TYPES 130 + +#define MAX_SHIPS_PER_WING 6 + +#ifdef FS2_DEMO + #define MAX_WINGS 15 +#else + #define MAX_WINGS 25 +#endif + +#define MAX_PLAYER_WINGS 3 // number of wings player can start a mission with + +// flags defined for wings +#define MAX_WING_FLAGS 8 // total number of flags in the wing structure -- used for parsing wing flags +#define WF_WING_GONE (1<<0) // all ships were either destroyed or departed +#define WF_WING_DEPARTING (1<<1) // wing's departure cue turned true +#define WF_IGNORE_COUNT (1<<2) // ignore all ships in this wing for goal counting purposes. +#define WF_REINFORCEMENT (1<<3) // is this wing a reinforcement wing +#define WF_RESET_REINFORCEMENT (1<<4) // needed when we need to reset the wing's reinforcement flag (after calling it in) +#define WF_NO_ARRIVAL_MUSIC (1<<5) // don't play arrival music when wing arrives +#define WF_EXPANDED (1<<6) // wing expanded in hotkey select screen +#define WF_NO_ARRIVAL_MESSAGE (1<<7) // don't play any arrival message +#define WF_NO_ARRIVAL_WARP (1<<8) // don't play warp effect for any arriving ships in this wing. +#define WF_NO_DEPARTURE_WARP (1<<9) // don't play warp effect for any departing ships in this wing. +#define WF_NO_DYNAMIC (1<<10) // members of this wing relentlessly pursue their ai goals +#define WF_DEPARTURE_ORDERED (1<<11) // departure of this wing was ordered by player +#define WF_NEVER_EXISTED (1<<12) // this wing never existed because something prevented it from being created (like its mother ship being destroyed) + +// Defines a wing of ships. +typedef struct wing { + char name[NAME_LENGTH]; + int reinforcement_index; // index in reinforcement struct or -1 + int hotkey; + + int num_waves, current_wave; // members for dealing with waves + int threshold; // when number of ships in the wing reaches this number -- new wave + + fix time_gone; // time into the mission when this wing is officiall gone. + + int wave_count; // max ships per wave (as defined by the number of ships in the ships list) + int total_arrived_count; // count of number of ships that we have created, regardless of wave + int current_count; // count of number of ships actually in this wing -- used for limit in next array + int ship_index[MAX_SHIPS_PER_WING]; // index into ships array of all ships currently in the wing + + int total_destroyed; // total number of ships destroyed in the wing (including all waves) + int total_departed; // total number of ships departed in this wing (including all waves) + + int special_ship; // the leader of the wing. An index into ship_index[]. + + int arrival_location; // arrival and departure information for wings -- similar to info for ships + int arrival_distance; // distance from some ship where this ship arrives + int arrival_anchor; // name of object this ship arrives near (or in front of) + int arrival_cue; + int arrival_delay; + + int departure_location; + int departure_anchor; // name of object that we depart to (in case of dock bays) + int departure_cue; + int departure_delay; + + int wave_delay_min; // minimum number of seconds before new wave can arrive + int wave_delay_max; // maximum number of seconds before new wave can arrive + int wave_delay_timestamp; // timestamp used for delaying arrival of next wave + + int flags; + + ai_goal ai_goals[MAX_AI_GOALS]; // goals for the wing -- converted to ai_goal struct + + ushort net_signature; // starting net signature for ships in this wing. assiged at mission load time + +} wing; + +#define STARTING_WING_ALPHA 0 +#define STARTING_WING_BETA 1 +#define STARTING_WING_GAMMA 2 +#define STARTING_WING_ZETA 3 + +extern wing Wings[MAX_WINGS]; +extern int Starting_wings[MAX_PLAYER_WINGS]; +extern int ai_paused; + +extern int Num_reinforcements; +extern int Num_ship_types; +extern ship_info Ship_info[MAX_SHIP_TYPES]; +extern reinforcements Reinforcements[MAX_REINFORCEMENTS]; + +// structure definition for ship type counts. Used to give a count of the number of ships +// of a particular type, and the number of times that a ship of that particular type has been +// killed. When changing any info here, be sure to update the ship_type_names array in Ship.cpp +// the order of the types here MUST match the order of the types in the array +typedef struct ship_counts { + int total; + int killed; +} ship_counts; + +extern char *Ship_type_names[MAX_SHIP_TYPE_COUNTS]; +extern int Ship_type_flags[MAX_SHIP_TYPE_COUNTS]; // SIF_* flags for each ship type +extern ship_counts Ship_counts[MAX_SHIP_TYPE_COUNTS]; + + +// Use the below macros when you want to find the index of an array element in the +// Wings[] or Ships[] arrays. +#define WING_INDEX(wingp) (wingp-Wings) +#define SHIP_INDEX(shipp) (shipp-Ships) + + +extern void ship_init(); // called once at game start +extern void ship_level_init(); // called before the start of each level + +//returns -1 if failed +extern int ship_create(matrix * orient, vector * pos, int ship_type); +extern void change_ship_type(int n, int ship_type); +extern void ship_model_change(int n, int ship_type); +extern void ship_process_pre( object * objp, float frametime ); +extern void ship_process_post( object * objp, float frametime ); +extern void ship_render( object * objp ); +extern void ship_delete( object * objp ); +extern int ship_check_collision_fast( object * obj, object * other_obj, vector * hitpos ); +extern int ship_get_num_ships(); + +extern int ship_fire_primary_debug(object *objp); // Fire the debug laser. +extern int ship_fire_primary(object * objp, int stream_weapons, int force = 0); +extern int ship_fire_secondary(object * objp, int allow_swarm = 0 ); +extern int ship_launch_countermeasure(object *objp, int rand_val = -1); + +// for special targeting lasers +extern void ship_start_targeting_laser(ship *shipp); +extern void ship_stop_targeting_laser(ship *shipp); +extern void ship_process_targeting_lasers(); + +extern int ship_select_next_primary(object *objp, int direction); +extern int ship_select_next_secondary(object *objp); +extern int get_available_secondary_weapons(object *objp, int *outlist, int *outbanklist); +extern void ship_recalc_subsys_strength( ship *shipp ); +extern void subsys_set(int objnum, int ignore_subsys_info = 0); +extern void physics_ship_init(object *objp); + +// Note: This is not a general purpose routine. +// It is specifically used for targeting. +// It only returns a subsystem position if it has shields. +// Return true/false for subsystem found/not found. +// Stuff vector *pos with absolute position. +extern int get_subsystem_pos(vector *pos, object *objp, ship_subsys *subsysp); + +extern int ship_info_lookup(char *name); +extern int ship_info_base_lookup(int si_index); +extern int ship_name_lookup(char *name, int inc_players = 0); // returns the index into Ship array of name +extern int ship_type_name_lookup(char *name); + +extern int wing_lookup(char *name); + +// returns 0 if no conflict, 1 if conflict, -1 on some kind of error with wing struct +extern int wing_has_conflicting_teams(int wing_index); + +// next function takes optional second parameter which says to ignore the current count of ships +// in the wing -- used to tell is the wing exists or not, not whether it exists and has ships currently +// present. +extern int wing_name_lookup(char *name, int ignore_count = 0); + +extern int Player_ship_class; + +#define MAX_PLAYER_SHIP_CHOICES 15 +extern int Num_player_ship_precedence; // Number of ship types in Player_ship_precedence +extern int Player_ship_precedence[MAX_PLAYER_SHIP_CHOICES]; // Array of ship types, precedence list for player ship/wing selection + + +// Do the special effect for energy dissipating into the shield for a hit. +// model_num = index in Polygon_models[] +// centerp = pos of object, sort of the center of the shield +// tcp = hit point, probably the global hit_point set in polygon_check_face +// tr0 = index of polygon in shield pointer in polymodel. +extern void create_shield_explosion(int objnum, int model_num, matrix *orient, vector *centerp, vector *tcp, int tr0); + +// Initialize shield hit system. +extern void shield_hit_init(); +extern void create_shield_explosion_all(object *objp); +extern void shield_frame_init(); +extern void add_shield_point(int objnum, int tri_num, vector *hit_pos); +extern void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos); +extern void shield_point_multi_setup(); +extern void shield_hit_close(); + +void ship_draw_shield( object *objp); + +float apply_damage_to_shield(object *objp, int shield_quadrant, float damage); +float compute_shield_strength(object *objp); + +// Returns true if the shield presents any opposition to something +// trying to force through it. +// If quadrant is -1, looks at entire shield, otherwise +// just one quadrant +int ship_is_shield_up( object *obj, int quadrant ); + +//================================================= +// These two functions transfer instance specific angle +// data into and out of the model structure, which contains +// angles, but not for each instance of model being used. See +// the actual functions in ship.cpp for more details. +extern void ship_model_start(object *objp); +extern void ship_model_stop(object *objp); + +//============================================ +extern int ship_find_num_crewpoints(object *objp); +extern int ship_find_num_turrets(object *objp); + +extern void ship_get_eye( vector *eye_pos, matrix *eye_orient, object *obj ); // returns in eye the correct viewing position for the given object +extern ship_subsys *ship_get_indexed_subsys( ship *sp, int index, vector *attacker_pos = NULL ); // returns index'th subsystem of this ship +extern int ship_get_index_from_subsys(ship_subsys *ssp, int objnum, int error_bypass = 0); +extern int ship_get_subsys_index(ship *sp, char *ss_name, int error_bypass = 0); // returns numerical index in linked list of subsystems +extern float ship_get_subsystem_strength( ship *shipp, int type ); +extern ship_subsys *ship_get_subsys(ship *shipp, char *subsys_name); + +// subsys disruption +extern int ship_subsys_disrupted(ship_subsys *ss); +extern int ship_subsys_disrupted(ship *sp, int type); +extern void ship_subsys_set_disrupted(ship_subsys *ss, int time); + +extern int ship_do_rearm_frame( object *objp, float frametime ); +extern void ship_wing_cleanup( int shipnum, wing *wingp ); + +extern object *ship_find_repair_ship( object *requester ); +extern void ship_close(); // called in game_shutdown() to free malloced memory + + +extern void ship_assign_sound_all(); +extern void ship_assign_sound(ship *sp); + +extern void ship_add_ship_type_count( int ship_info_flag, int num ); +extern void ship_add_ship_type_kill_count( int ship_info_flag ); + + +extern int ship_get_default_orders_accepted( ship_info *sip ); +extern int ship_query_general_type(int ship); +extern int ship_query_general_type(ship *shipp); +extern int ship_docking_valid(int docker, int dockee); +extern int get_quadrant(vector *hit_pnt); // Return quadrant num of last hit ponit. + +extern void ship_obj_list_rebuild(); // only called by save/restore code +extern int ship_query_state(char *name); + +int ship_secondary_bank_has_ammo(int shipnum); // check if current secondary bank has ammo + +void ship_departed( int num ); +int ship_can_warp(ship *sp); // check if ship has engine power to warp +int ship_return_subsys_path_normal(ship *sp, ship_subsys *ss, vector *gsubpos, vector *norm); +int ship_subsystem_in_sight(object* objp, ship_subsys* subsys, vector *eye_pos, vector* subsys_pos, int do_facing_check=1, float *dot_out=NULL, vector *vec_out=NULL); +ship_subsys *ship_return_next_subsys(ship *shipp, int type, vector *attacker_pos); + +// defines and definition for function to get a random ship of a particular team (any ship, +// any ship but player ships, or only players) +#define SHIP_GET_ANY_SHIP 0 +#define SHIP_GET_NO_PLAYERS 1 +#define SHIP_GET_ONLY_PLAYERS 2 + +extern int ship_get_random_team_ship( int team, int flags = SHIP_GET_ANY_SHIP, float max_dist=0.0f ); +extern int ship_get_random_player_wing_ship( int flags = SHIP_GET_ANY_SHIP, float max_dist=0.0f, int persona_index = -1, int get_first=0, int multi_team = -1 ); +extern int ship_get_random_ship_in_wing(int wingnum, int flags = SHIP_GET_ANY_SHIP, float max_dist=0.0f, int get_first=0 ); + +// return ship index +int ship_get_random_ship(); + +extern int ship_get_by_signature(int signature); + +#ifndef NDEBUG +extern int Ai_render_debug_flag; +extern int Show_shield_mesh, New_shield_system; +extern int Ship_auto_repair; // flag to indicate auto-repair of subsystem should occur +#endif + +void ship_subsystem_delete(ship *shipp); +void ship_set_default_weapons(ship *shipp, ship_info *sip); +float ship_quadrant_shield_strength(object *hit_objp, vector *hitpos); + +int ship_dumbfire_threat(ship *sp); +int ship_lock_threat(ship *sp); + +int bitmask_2_bitnum(int num); +char *ship_return_orders(char *outbuf, ship *sp); +char *ship_return_time_to_goal(char *outbuf, ship *sp); +void ship_check_cargo_all(); // called from game_simulation_frame + +void ship_maybe_warn_player(ship *enemy_sp, float dist); +void ship_maybe_praise_player(ship *deader_sp); +void ship_maybe_ask_for_help(ship *sp); +void ship_scream(ship *sp); +void ship_maybe_scream(ship *sp); +void ship_maybe_tell_about_rearm(ship *sp); +void ship_maybe_lament(); + +void ship_primary_changed(ship *sp); +void ship_secondary_changed(ship *sp); + +// get the Ship_info flags for a given ship +int ship_get_SIF(ship *shipp); +int ship_get_SIF(int sh); +extern void ship_do_cargo_revealed( ship *shipp, int from_network = 0 ); + +float ship_get_secondary_weapon_range(ship *shipp); +int get_max_ammo_count_for_bank(int ship_class, int bank, int ammo_type); + +int is_support_allowed(object *objp); + +// Given an object and a turret on that object, return the actual firing point of the gun +// and its normal. This uses the current turret angles. We are keeping track of which +// gun to fire next in the ship specific info for this turret subobject. Use this info +// to determine which position to fire from next. +// Stuffs: +// *gpos: absolute position of gun firing point +// *gvec: vector fro *gpos to *targetp +void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp); + +// Given an object and a turret on that object, return the global position and forward vector +// of the turret. The gun normal is the unrotated gun normal, (the center of the FOV cone), not +// the actual gun normal given using the current turret heading. But it _is_ rotated into the model's orientation +// in global space. +void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec); + +// return 1 if objp is in fov of the specified turret, tp. Otherwise return 0. +// dist = distance from turret to center point of object +int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist); + +// forcible jettison cargo from a ship +void ship_jettison_cargo(ship *shipp); + +// get damage done by exploding ship, takes into account mods for individual ship +float ship_get_exp_damage(object* objp); + +// get whether ship has shockwave, takes into account mods for individual ship +int ship_get_exp_propagates(ship *sp); + +// get outer radius of damage, takes into account mods for individual ship +float ship_get_exp_outer_rad(object *ship_objp); + +// returns whether subsys is allowed to have cargo +int valid_cap_subsys_cargo_list(char *subsys_name); + +// determine turret status of a given subsystem, returns 0 for no turret, 1 for "fixed turret", 2 for "rotating" turret +int ship_get_turret_type(ship_subsys *subsys); + +// get ship by object signature, returns OBJECT INDEX +int ship_get_by_signature(int sig); + +// get the team of a reinforcement item +int ship_get_reinforcement_team(int r_index); + +// determine if the given texture is used by a ship type. return ship info index, or -1 if not used by a ship +int ship_get_texture(int bitmap); + +// update artillery lock info +void ship_update_artillery_lock(); + +// checks if a world point is inside the extended bounding box of a ship +int check_world_pt_in_expanded_ship_bbox(vector *world_pt, object *objp, float delta_box); + +// returns true if objp is ship and is tagged +int ship_is_tagged(object *objp); + +// returns max normal speed of ship (overclocked / afterburned) +float ship_get_max_speed(ship *shipp); + +// returns warp speed of ship +float ship_get_warp_speed(object *objp); + +// returns true if ship is beginning to speed up in warpout +int ship_is_beginning_warpout_speedup(object *objp); + +// given a ship info type, return a species +int ship_get_species_by_type(int ship_info_index); + +// return the length of the ship +float ship_get_length(ship* shipp); + + +#endif + diff --git a/include/ship_select.h b/include/ship_select.h new file mode 100644 index 0000000..3c63b48 --- /dev/null +++ b/include/ship_select.h @@ -0,0 +1,114 @@ +/* + * $Logfile: /Freespace2/code/FRED2/ship_select.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Object selection (marking) dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 10 9/09/97 10:29a Hoffoss + * Added support for neutral team, and fixed changes made to how team is + * used in ship structure. + * + * 9 8/26/97 11:08a Hoffoss + * Added waypoint paths to object selection dialog. + * + * 8 7/28/97 5:10p Hoffoss + * Removed all occurances of neutral team from Fred. + * + * 7 7/24/97 2:43p Hoffoss + * Made changes whiteside requested. Double clicking acts as clicking ok + * button. + * + * 6 5/26/97 10:30a Hoffoss + * Added select wing to object select dialog. + * + * 5 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// ship_select dialog + +#include "shipchecklistbox.h" +#include "object.h" + +class ship_select : public CDialog +{ +// Construction +public: + void update_status(); + void OnOK(); + void create_list(); + ship_select(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(ship_select) + enum { IDD = IDD_SHIP_SELECT }; + CListBox m_wing_list; + CListBox m_ship_list; + BOOL m_filter_ships; + BOOL m_filter_starts; + BOOL m_filter_waypoints; + int m_filter_friendly; + int m_filter_hostile; + int m_filter_neutral; + int m_filter_unknown; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ship_select) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(ship_select) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangeWingDisplayFilter(); + afx_msg void OnFilterShips(); + afx_msg void OnFilterStarts(); + afx_msg void OnFilterWaypoints(); + afx_msg void OnFilterShipsFriendly(); + afx_msg void OnFilterShipsHostile(); + afx_msg void OnFilterShipsNeutral(); + afx_msg void OnFilterShipsUnknown(); + afx_msg void OnClear(); + afx_msg void OnAll(); + afx_msg void OnInvert(); + afx_msg void OnDblclkShipList(); + afx_msg void OnSelchangeWingList(); + afx_msg void OnSelchangeShipList(); + afx_msg void OnDblclkWingList(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: + int activity, list_size, wlist_size, wplist_size; + object *obj_index[MAX_OBJECTS]; + int wing_index[MAX_WINGS + MAX_WAYPOINT_LISTS]; + int wing_sel_last[MAX_WINGS + MAX_WAYPOINT_LISTS]; +}; + diff --git a/include/shipchecklistbox.h b/include/shipchecklistbox.h new file mode 100644 index 0000000..3c534aa --- /dev/null +++ b/include/shipchecklistbox.h @@ -0,0 +1,48 @@ +/* + * $Logfile: /Freespace2/code/FRED2/ShipCheckListBox.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * A custom check list box class that allows space bar to toggle the state of all + * the selected checkboxes. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 5 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#ifndef _SHIPCHECKLISTBOX_H +#define _SHIPCHECKLISTBOX_H + +class ShipCheckListBox : public CCheckListBox +{ +public: + BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + +protected: + //{{AFX_MSG(CCheckListBox) + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +#endif + diff --git a/include/shipclasseditordlg.h b/include/shipclasseditordlg.h new file mode 100644 index 0000000..30bf74f --- /dev/null +++ b/include/shipclasseditordlg.h @@ -0,0 +1,92 @@ +/* + * $Logfile: /Freespace2/code/FRED2/ShipClassEditorDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Ship class editor dialog handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// CShipClassEditorDlg dialog + +class CShipClassEditorDlg : public CDialog +{ +// Construction +public: + CShipClassEditorDlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(CShipClassEditorDlg) + enum { IDD = IDD_SHIP_CLASS_EDITOR }; + CButton m_SoundsEditor; + CStatic m_ShipClassWindow; + CButton m_ShipClassNew; + CButton m_ShipClassDelete; + CButton m_ModelsEditor; + CButton m_GoalsEditor; + BOOL m_ShipClassAfterburner; + int m_ShipClassAIClass; + CString m_ShipClassArmor; + BOOL m_ShipClassCloak; + int m_ShipClassDebrisModel; + int m_ShipClassModel; + CString m_ShipClassEngine; + CString m_ShipClassExplosion1; + CString m_ShipClassExplosion2; + CString m_ShipClassIFF; + CString m_ShipClassManufacturer; + int m_ShipClassMaxBank; + int m_ShipClassMaxPitch; + int m_ShipClassMaxRoll; + int m_ShipClassMaxSpeed; + CString m_ShipClassName; + CString m_ShipClassPowerPlant; + int m_ShipClassScore; + int m_ShipClassShields; + BOOL m_ShipClassWarpdrive; + CString m_ShipClassTurretWeapon1; + CString m_ShipClassTurretWeapon2; + CString m_ShipClassWeaponSpecial; + CString m_ShipClassWeapon1; + CString m_ShipClassWeapon2; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CShipClassEditorDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CShipClassEditorDlg) + // NOTE: the ClassWizard will add member functions here + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/shipcontrails.h b/include/shipcontrails.h new file mode 100644 index 0000000..d200f77 --- /dev/null +++ b/include/shipcontrails.h @@ -0,0 +1,55 @@ +/* + * $Logfile: /Freespace2/code/Ship/ShipContrails.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * all sorts of cool stuff about ships + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 11/14/98 5:33p Dave + * Lots of nebula work. Put in ship contrails. + * + * 1 11/14/98 3:40p Dave + * + * 1 11/13/98 3:28p Dave + * + * + * $NoKeywords: $ + */ + +#ifndef _FREESPACE2_SHIP_CONTRAIL_HEADER_FILE +#define _FREESPACE2_SHIP_CONTRAIL_HEADER_FILE + +// ---------------------------------------------------------------------------------------------- +// CONTRAIL DEFINES/VARS +// + +// prototypes +struct ship; + +// ---------------------------------------------------------------------------------------------- +// CONTRAIL FUNCTIONS +// + +// call during level initialization +void ct_level_init(); + +// call during level shutdown +void ct_level_close(); + +// call when a ship is created to initialize its contrail stuff +void ct_ship_create(ship *shipp); + +// call when a ship is deleted to free up its contrail stuff +void ct_ship_delete(ship *shipp); + +// call each frame for processing a ship's contrails +void ct_ship_process(ship *shipp); + +#endif + diff --git a/include/shipeditordlg.h b/include/shipeditordlg.h new file mode 100644 index 0000000..faa4319 --- /dev/null +++ b/include/shipeditordlg.h @@ -0,0 +1,258 @@ +/* + * $Logfile: /Freespace2/code/Fred2/ShipEditorDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Single ship editing dialog + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 5/20/99 7:00p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 3 2/11/99 2:15p Andsager + * Add ship explosion modification to FRED + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 65 4/07/98 9:42a Allender + * put in persona combo box into ship editor. Removed code to assign + * personas based on message + * + * 64 3/27/98 12:02p Sandeep + * + * 63 3/25/98 4:14p Hoffoss + * Split ship editor up into ship editor and a misc dialog, which tracks + * flags and such. + * + * 62 3/16/98 8:27p Allender + * Fred support for two new AI flags -- kamikaze and no dynamic goals. + * + * 61 3/09/98 4:30p Allender + * multiplayer secondary weapon changes. red-alert and cargo-known-delay + * sexpressions. Add time cargo revealed to ship structure + * + * 60 2/17/98 11:42a Hoffoss + * Added support for hidden from sensors condition. + * + * 59 2/06/98 2:54p Hoffoss + * Fixed some bugs in dialog init, and cleared up some of the confusion + * about how it works by renaming some variables and adding comments. + * + * 58 1/29/98 5:14p Hoffoss + * Added support for a SF_INVULNERABLE ship flag in Fred. + * + * 57 11/13/97 4:14p Allender + * automatic assignment of hotkeys for starting wings. Appripriate + * warnings when they are incorrectly used. hotkeys correctly assigned to + * ships/wing arriving after mission start + * + * 56 11/10/97 10:13p Allender + * added departure anchor to Fred and Freespace in preparation for using + * docking bays. Functional in Fred, not in FreeSpace. + * + * 55 10/21/97 4:49p Allender + * added flags to Fred and FreeSpace to forgo warp effect (toggle in ship + * editor in Fred) + * + * 54 10/14/97 5:33p Hoffoss + * Added Fred support (and fsm support) for the no_arrival_music flags in + * ships and wings. + * + * 53 9/17/97 5:43p Hoffoss + * Added Fred support for new player start information. + * + * 52 9/04/97 4:31p Hoffoss + * Fixed bug: Changed ship editor to not touch wing info (arrival or + * departure cues) to avoid conflicts with wing editor's changes. + * + * 51 8/30/97 9:52p Hoffoss + * Implemented arrival location, distance, and anchor in Fred. + * + * 50 8/25/97 5:56p Hoffoss + * Added multiple asteroid field support, loading and saving of asteroid + * fields, and ship score field to Fred. + * + * 49 8/20/97 6:53p Hoffoss + * Implemented escort flag support in Fred. + * + * 48 8/16/97 12:06p Hoffoss + * Fixed bug where a whole wing is deleted that is being referenced. + * + * 47 8/12/97 7:17p Hoffoss + * Added previous button to ship and wing editors. + * + * 46 8/12/97 6:32p Hoffoss + * Added code to allow hiding of arrival and departure cues in editors. + * + * 45 8/08/97 1:31p Hoffoss + * Added syncronization protection to cur_object_index changes. + * + * $NoKeywords: $ + */ + +#ifndef _SHIPEDITORDLG_H +#define _SHIPEDITORDLG_H + +#include "sexp_tree.h" +#include "shipgoalsdlg.h" +#include "management.h" + +///////////////////////////////////////////////////////////////////////////// +// CShipEditorDlg dialog + +#define WM_GOODBYE (WM_USER+5) +#define ID_ALWAYS_ON_TOP 0x0f00 + +class numeric_edit_control +{ + int value; + int unique; + int control_id; + CWnd *dlg; + +public: + void setup(int id, CWnd *wnd); + void blank() { unique = 0; } + void init(int n); + void set(int n); + void display(); + void save(int *n); + void fix(int n); +}; + +class CShipEditorDlg : public CDialog +{ +private: + int make_ship_list(int *arr); + int update_ship(int ship); + int initialized; + int multi_edit; + int always_on_top; + int cue_height; + int mission_type; // indicates if single player(1) or multiplayer(0) + CView* m_pSEView; + CCriticalSection CS_update; + +// Construction +public: + int player_ship, single_ship; + int editing; + int modified; + int select_sexp_node; + int bypass_errors; + int bypass_all; + + int enable; // used to enable(1)/disable(0) controls based on if any ship selected + int p_enable; // used to enable(1)/disable(0) controls based on if a player ship + + int tristate_set(int val, int cur_state); + void show_hide_sexp_help(); + void calc_cue_height(); + int verify(); + void OnInitMenu(CMenu *m); + void OnOK(); + int update_data(int redraw = 1); + void initialize_data(int full); + CShipEditorDlg(CWnd* pParent = NULL); // standard constructor + CShipEditorDlg(CView* pView); + + // alternate ship name stuff + void ship_alt_name_init(int base_ship); + void ship_alt_name_close(int base_ship); + + BOOL Create(); + +// Dialog Data + //{{AFX_DATA(CShipEditorDlg) + enum { IDD = IDD_SHIP_EDITOR }; + CButton m_no_departure_warp; + CButton m_no_arrival_warp; + CButton m_player_ship; + CSpinButtonCtrl m_destroy_spin; + CSpinButtonCtrl m_departure_delay_spin; + CSpinButtonCtrl m_arrival_delay_spin; + sexp_tree m_departure_tree; + sexp_tree m_arrival_tree; + CString m_ship_name; + CString m_cargo1; + int m_ship_class; + int m_team; + int m_arrival_location; + int m_departure_location; + int m_ai_class; + numeric_edit_control m_arrival_delay; + numeric_edit_control m_departure_delay; + int m_hotkey; + BOOL m_update_arrival; + BOOL m_update_departure; + numeric_edit_control m_destroy_value; + numeric_edit_control m_score; + numeric_edit_control m_arrival_dist; + numeric_edit_control m_kdamage; + int m_arrival_target; + int m_departure_target; + int m_persona; + //}}AFX_DATA + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CShipEditorDlg) + public: + virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(CShipEditorDlg) + afx_msg void OnClose(); + afx_msg void OnRclickArrivalTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnRclickDepartureTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBeginlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBeginlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnEndlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnEndlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnGoals(); + afx_msg void OnSelchangeShipClass(); + afx_msg void OnInitialStatus(); + afx_msg void OnWeapons(); + afx_msg void OnShipReset(); + afx_msg void OnDeleteShip(); + afx_msg void OnShipTbl(); + afx_msg void OnNext(); + afx_msg void OnSelchangedArrivalTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnSelchangedDepartureTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnHideCues(); + afx_msg void OnPrev(); + afx_msg void OnSelchangeArrivalLocation(); + afx_msg void OnPlayerShip(); + afx_msg void OnNoArrivalWarp(); + afx_msg void OnNoDepartureWarp(); + afx_msg void OnSelchangeDepartureLocation(); + afx_msg void OnSelchangeHotkey(); + afx_msg void OnFlags(); + afx_msg void OnIgnoreOrders(); + afx_msg void OnSpecialExp(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#endif + diff --git a/include/shipflagsdlg.h b/include/shipflagsdlg.h new file mode 100644 index 0000000..740e243 --- /dev/null +++ b/include/shipflagsdlg.h @@ -0,0 +1,76 @@ +#include "shipeditordlg.h" + +// ShipFlagsDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// ship_flags_dlg dialog + +class ship_flags_dlg : public CDialog +{ +// Construction +public: + ship_flags_dlg(CWnd* pParent = NULL); // standard constructor + void OnOK(); + void update_ship(int ship); + void setup(int n); + int tristate_set(int val, int cur_state); + void set_modified(); + +// Dialog Data + //{{AFX_DATA(ship_flags_dlg) + enum { IDD = IDD_SHIP_FLAGS }; + CButton m_red_alert_carry; + CButton m_scannable; + CButton m_reinforcement; + CButton m_protect_ship; + CButton m_beam_protect_ship; + CButton m_no_dynamic; + CButton m_no_arrival_music; + CButton m_kamikaze; + CButton m_invulnerable; + CButton m_ignore_count; + CButton m_hidden; + CButton m_escort; + CButton m_destroy; + CButton m_cargo_known; + CButton m_special_warp; + CSpinButtonCtrl m_destroy_spin; + numeric_edit_control m_kdamage; + numeric_edit_control m_destroy_value; + numeric_edit_control m_escort_value; + numeric_edit_control m_respawn_priority; + //}}AFX_DATA + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ship_flags_dlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + int p_enable; // used to enable(1)/disable(0) controls based on if a player ship + + // Generated message map functions + //{{AFX_MSG(ship_flags_dlg) + virtual BOOL OnInitDialog(); + afx_msg void OnCargoKnown(); + afx_msg void OnDestroyCheck(); + afx_msg void OnEscort(); + afx_msg void OnHiddenFromSensors(); + afx_msg void OnIgnoreCount(); + afx_msg void OnInvulnerable(); + afx_msg void OnKamikaze(); + afx_msg void OnNoArrivalMusic(); + afx_msg void OnNoDynamic(); + afx_msg void OnProtectShip(); + afx_msg void OnBeamProtectShip(); + afx_msg void OnReinforcement(); + afx_msg void OnScannable(); + afx_msg void OnRedalertcarry(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/shipfx.h b/include/shipfx.h new file mode 100644 index 0000000..502e854 --- /dev/null +++ b/include/shipfx.h @@ -0,0 +1,238 @@ +/* + * $Logfile: /Freespace2/code/Ship/ShipFX.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for ship effects (as in special) + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 10 7/02/99 9:55p Dave + * Player engine wash sound. + * + * 9 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * 8 5/18/99 1:30p Dave + * Added muzzle flash table stuff. + * + * 7 5/09/99 6:00p Dave + * Lots of cool new effects. E3 build tweaks. + * + * 6 3/23/99 2:29p Andsager + * Fix shockwaves for kamikazi and Fred defined. Collect together + * shockwave_create_info struct. + * + * 5 2/26/99 4:14p Dave + * Put in the ability to have multiple shockwaves for ships. + * + * 4 1/27/99 9:56a Dave + * Temporary checkin of beam weapons for Dan to make cool sounds. + * + * 3 10/20/98 1:39p Andsager + * Make so sparks follow animated ship submodels. Modify + * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add + * submodel_num. Add submodel_num to multiplayer hit packet. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 19 5/22/98 5:32p Andsager + * Make big ship explosion sounds play all the way through. remove + * cur_snd from ship struct. + * + * 18 5/19/98 8:43p Andsager + * Modify sound management (of big ship explosions). Turn on big ship + * explosions for release build. + * + * 17 5/12/98 10:54p Andsager + * Add new sound manager for big ship sub-explosion sounds + * + * 16 4/14/98 11:11p John + * Made ships with < 50% hull left show electrical damage arcs. + * + * 15 4/14/98 5:43p John + * Made large ship blowup system reinit between levels. + * + * 14 4/14/98 4:56p John + * Hooked in Andsager's large ship exploding code, but it is temporarily + * disabled. + * + * 13 4/10/98 12:16p Allender + * fix ship hit kill and debris packets + * + * 12 4/05/98 2:37p John + * Made sun on by default. Fixed some other sun tweaks. + * + * 11 1/02/98 5:04p John + * Several explosion related changes. Made fireballs not be used as + * ani's. Made ship spark system expell particles. Took away impact + * explosion for weapon hitting ship... this needs to get added to weapon + * info and makes shield hit more obvious. Only make sparks when hit + * hull, not shields. + * + * 10 12/17/97 7:53p John + * Fixed a bug where gunpoint for flashes were in world coordinates, + * should have been object. + * + * 9 12/17/97 5:11p John + * Added brightening back into fade table. Added code for doing the fast + * dynamic gun flashes and thruster flashes. + * + * 8 12/12/97 3:02p John + * First Rev of Ship Shadows + * + * 7 9/12/97 4:06p John + * put in ship warp out effect. + * put in dynamic lighting for warp in/out + * + * 6 9/09/97 4:52p John + * Almost done ship warp in code + * + * 5 2/28/97 11:07a John + * more fx + * + * 4 2/28/97 10:57a John + * Made so you can blow off any subsystems, not just radars. + * + * + * 3 2/10/97 12:38p John + * made all ships blow up into debris pieces when exploded. + * + * 2 2/07/97 11:49a John + * Some not-final explosions for turrets. + * + * 1 2/07/97 10:53a John + * + * $NoKeywords: $ + */ + + +#ifndef _SHIPFX_H +#define _SHIPFX_H + +struct object; +struct ship; +struct ship_subsys; + +// Make sparks fly off of ship n +// sn = spark number to spark, corrosponding to element in +// ship->hitpos array. If this isn't -1, it is a just +// got hit by weapon spark, otherwise pick one randomally. +void shipfx_emit_spark( int n, int sn ); + +// Does the special effects to blow a subsystem off a ship +extern void shipfx_blow_off_subsystem(object *ship_obj,ship *ship_p,ship_subsys *subsys, vector *exp_center); + + +// Creates "ndebris" pieces of debris on random verts of the the "submodel" in the +// ship's model. +extern void shipfx_blow_up_model(object *obj,int model, int submodel, int ndebris, vector *exp_center); + +// put here for multiplayer purposes +void shipfx_blow_up_hull(object *obj,int model, vector *exp_center ); + + +// ================================================= +// SHIP WARP IN EFFECT STUFF +// ================================================= + +// When a ship warps in, this gets called to start the effect +extern void shipfx_warpin_start( object *objp ); + +// During a ship warp in, this gets called each frame to move the ship +extern void shipfx_warpin_frame( object *objp, float frametime ); + +// When a ship warps out, this gets called to start the effect +extern void shipfx_warpout_start( object *objp ); + +// During a ship warp out, this gets called each frame to move the ship +extern void shipfx_warpout_frame( object *objp, float frametime ); + +// ================================================= +// SHIP SHADOW EFFECT STUFF +// ================================================= + +// Given point p0, in object's frame of reference, find if +// it can see the sun. +int shipfx_point_in_shadow( vector *p0, matrix *src_orient, vector *src_pos, float radius ); + +// Given an ship see if it is in a shadow. +int shipfx_in_shadow( object * src_obj ); + +// Given world point see if it is in a shadow. +int shipfx_eye_in_shadow( vector *eye_pos, object *src_obj, int sun_n); + + +// ================================================= +// SHIP GUN FLASH EFFECT STUFF +// ================================================= + +// Resets the ship flash stuff. Call before +// each level. +void shipfx_flash_init(); + +// Given that a ship fired a weapon, light up the model +// accordingly. +// Set is_primary to non-zero if this is a primary weapon. +// Gun_pos should be in object's frame of reference, not world!!! +void shipfx_flash_create(object *objp, ship * shipp, vector *gun_pos, vector *gun_dir, int is_primary, int weapon_info_index); + +// Sets the flash lights in the model used by this +// ship to the appropriate values. There might not +// be any flashes linked to this ship in which +// case this function does nothing. +void shipfx_flash_light_model(object *objp, ship * shipp ); + +// Does whatever processing needs to be done each frame. +void shipfx_flash_do_frame(float frametime); + + +// ================================================= +// LARGE SHIP EXPLOSION EFFECT STUFF +// ================================================= + +// Call between levels +void shipfx_large_blowup_level_init(); + +// Returns 0 if couldn't init +int shipfx_large_blowup_init(ship *shipp); + +// Returns 1 when explosion is done +int shipfx_large_blowup_do_frame(ship *shipp, float frametime); + +void shipfx_large_blowup_render(ship *shipp); + +// sound manager fore big ship sub explosions sounds +void do_sub_expl_sound(float radius, vector* sound_pos, int* sound_handle); + +// do all shockwaves for a ship blowing up +void shipfx_do_shockwave_stuff(ship *shipp, shockwave_create_info *sci); + + +// ================================================= +// ELECTRICAL SPARKS ON DAMAGED SHIPS EFFECT STUFF +// ================================================= +void shipfx_do_damaged_arcs_frame( ship *shipp ); + + +// ================================================= +// NEBULA LIGHTNING. +// ================================================= +void shipfx_do_lightning_frame( ship *shipp ); + +// engine wash level init +void shipfx_engine_wash_level_init(); + +// pause engine wash sounds +void shipfx_stop_engine_wash_sound(); + +#endif + diff --git a/include/shipgoalsdlg.h b/include/shipgoalsdlg.h new file mode 100644 index 0000000..843ff43 --- /dev/null +++ b/include/shipgoalsdlg.h @@ -0,0 +1,137 @@ +/* + * $Logfile: /Freespace2/code/FRED2/ShipGoalsDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Initial orders editor dialog box handling code. This dialog is used for both + * ship and wing initial orders, and can support more if need be without modification. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 13 8/26/97 4:18p Hoffoss + * Added error checking to initial orders dialog when ok is clicked. + * + * 12 7/30/97 12:31p Hoffoss + * Made improvements to ship goals editor (initial orders) to disallow + * illegal orders. + * + * 11 5/30/97 4:50p Hoffoss + * Added code to allow marked ship editing of data in child dialogs of + * ship editor dialog. + * + * 10 3/10/97 5:37p Hoffoss + * fixed bug in dock goal selection. + * + * 9 3/03/97 4:32p Hoffoss + * Initial orders supports new docking stuff Allender added. + * + * 8 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "management.h" + +#ifndef _SHIPGOALSDLG_H +#define _SHIPGOALSDLG_H + +#define ED_MAX_GOALS 10 + +///////////////////////////////////////////////////////////////////////////// +// ShipGoalsDlg dialog + +class ShipGoalsDlg : public CDialog +{ +// Construction +public: + int verify_orders(int ship = -1); + void initialize_multi(); + void OnOK(); + void update(); + void initialize(ai_goal *goals, int ship = cur_ship); + ShipGoalsDlg(CWnd* pParent = NULL); // standard constructor + + int self_ship, self_wing; + int m_behavior[ED_MAX_GOALS]; + int m_object[ED_MAX_GOALS]; + int m_priority[ED_MAX_GOALS]; + int m_subsys[ED_MAX_GOALS]; + int m_dock2[ED_MAX_GOALS]; + int m_data[ED_MAX_GOALS]; + + CComboBox *m_behavior_box[ED_MAX_GOALS]; + CComboBox *m_object_box[ED_MAX_GOALS]; + CComboBox *m_subsys_box[ED_MAX_GOALS]; + CComboBox *m_dock2_box[ED_MAX_GOALS]; + CComboBox *m_priority_box[ED_MAX_GOALS]; + +// Dialog Data + //{{AFX_DATA(ShipGoalsDlg) + enum { IDD = IDD_SHIP_GOALS_EDITOR }; + //}}AFX_DATA + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ShipGoalsDlg) + public: + virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL); + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(ShipGoalsDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangeBehavior1(); + afx_msg void OnSelchangeBehavior2(); + afx_msg void OnSelchangeBehavior3(); + afx_msg void OnSelchangeBehavior4(); + afx_msg void OnSelchangeBehavior5(); + afx_msg void OnSelchangeBehavior6(); + afx_msg void OnSelchangeBehavior7(); + afx_msg void OnSelchangeBehavior8(); + afx_msg void OnSelchangeBehavior9(); + afx_msg void OnSelchangeBehavior10(); + afx_msg void OnSelchangeObject1(); + afx_msg void OnSelchangeObject2(); + afx_msg void OnSelchangeObject3(); + afx_msg void OnSelchangeObject4(); + afx_msg void OnSelchangeObject5(); + afx_msg void OnSelchangeObject6(); + afx_msg void OnSelchangeObject7(); + afx_msg void OnSelchangeObject8(); + afx_msg void OnSelchangeObject9(); + afx_msg void OnSelchangeObject10(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: + void set_item(int item, int init = 0); + void update_item(int item, int multi = 0); + void set_object(int item); + + ai_goal *goalp; +}; + +extern char *goal_behaviors[]; + +#endif + diff --git a/include/shiphit.h b/include/shiphit.h new file mode 100644 index 0000000..2a5324f --- /dev/null +++ b/include/shiphit.h @@ -0,0 +1,141 @@ +/* + * $Logfile: /Freespace2/code/Ship/ShipHit.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to deal with a ship getting hit by something, be it a missile, dog, or ship. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 9 8/22/99 5:53p Dave + * Scoring fixes. Added self destruct key. Put callsigns in the logfile + * instead of ship designations for multiplayer players. + * + * 8 6/21/99 7:25p Dave + * netplayer pain packet. Added type E unmoving beams. + * + * 7 5/11/99 10:16p Andsager + * First pass on engine wash effect. Rotation (control input), damage, + * shake. + * + * 6 4/20/99 3:43p Andsager + * Added normal parameter to ship_apply_local_damage for case of ship_ship + * collision. + * + * 5 2/04/99 1:23p Andsager + * Apply max spark limit to ships created in mission parse + * + * 4 10/20/98 1:39p Andsager + * Make so sparks follow animated ship submodels. Modify + * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add + * submodel_num. Add submodel_num to multiplayer hit packet. + * + * 3 10/16/98 1:22p Andsager + * clean up header files + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 8 5/18/98 12:41a Allender + * fixed subsystem problems on clients (i.e. not reporting properly on + * damage indicator). Fixed ingame join problem with respawns. minor + * comm menu stuff + * + * 7 4/24/98 5:35p Andsager + * Fix sparks sometimes drawing not on model. If ship is sphere in + * collision, don't draw sparks. Modify ship_apply_local_damage() to take + * parameter no_spark. + * + * 6 4/09/98 5:44p Allender + * multiplayer network object fixes. debris and self destructed ships + * should all sync up. Fix problem where debris pieces (hull pieces) were + * not getting a net signature + * + * 5 2/13/98 2:22p Allender + * make ships spark when entering mission with < 100% hull + * + * 4 10/16/97 4:41p Allender + * new packet to tell clients when subsystem has been destroyed + * + * 3 9/18/97 4:08p John + * Cleaned up & restructured ship damage stuff. + * + * 2 9/17/97 5:12p John + * Restructured collision routines. Probably broke a lot of stuff. + * + * 1 9/17/97 3:42p John + * + * $NoKeywords: $ + */ + +#ifndef _SHIPHIT_H +#define _SHIPHIT_H + +struct ship; +struct ship_subsys; +struct object; + +#define NO_SPARKS 0 +#define CREATE_SPARKS 1 + +#define MISS_SHIELDS -1 + +// ===================== NOTE!! ========================= +// To apply damage to a ship, call either ship_apply_local_damage or ship_apply_global_damage. +// These replace the old calls to ship_hit and ship_do_damage... +// These functions do nothing to the ship's physics; that is the responsibility +// of whoever is calling these functions. These functions are strictly +// for damaging ship's hulls, shields, and subsystems. Nothing more. + +// function to destroy a subsystem. Called internally and from multiplayer messaging code +extern void do_subobj_destroyed_stuff( ship *ship_p, ship_subsys *subsys, vector *hitpos ); + + +// This gets called to apply damage when something hits a particular point on a ship. +// This assumes that whoever called this knows if the shield got hit or not. +// hitpos is in world coordinates. +// if shield_quadrant is not -1, then that part of the shield takes damage properly. +void ship_apply_local_damage(object *ship_obj, object *other_obj, vector *hitpos, float damage, int shield_quadrant, bool create_spark=true, int submodel_num=-1, vector *hit_normal=NULL); + +// This gets called to apply damage when a damaging force hits a ship, but at no +// point in particular. Like from a shockwave. This routine will see if the +// shield got hit and if so, apply damage to it. +// You can pass force_center==NULL if you the damage doesn't come from anywhere, +// like for debug keys to damage an object or something. It will +// assume damage is non-directional and will apply it correctly. +void ship_apply_global_damage(object *ship_obj, object *other_obj, vector *force_center, float damage ); + +// like above, but does not apply damage to shields +void ship_apply_wash_damage(object *ship_obj, object *other_obj, float damage); + +// next routine needed for multiplayer +void ship_hit_kill( object *ship_obj, object *other_obj, float percent_killed, int self_destruct); + +void ship_self_destruct( object *objp ); + +// Call this instead of physics_apply_whack directly to +// deal with two ships docking properly. +void ship_apply_whack(vector *force, vector *new_pos, object *objp); + +// externed for code in missionparse to create sparks on a ship with < 100% hull integrity. +void ship_hit_sparks_no_rotate(object *ship_obj, vector *hitpos); + +// externed so that ships which self destruct have the proper things done to them in multiplayer +void ship_generic_kill_stuff( object *objp, float percent_killed ); + +// find the max number of sparks allowed for ship +// limited for fighter by hull % others by radius. +int get_max_sparks(object* ship_obj); + +// player pain +void ship_hit_pain(float damage); + + +#endif //_SHIPHIT_H + diff --git a/include/shipspecialdamage.h b/include/shipspecialdamage.h new file mode 100644 index 0000000..df8329a --- /dev/null +++ b/include/shipspecialdamage.h @@ -0,0 +1,59 @@ +#if !defined(AFX_SHIPSPECIALDAMAGE_H__89610237_C0F4_11D2_A8B6_0060088FAE88__INCLUDED_) +#define AFX_SHIPSPECIALDAMAGE_H__89610237_C0F4_11D2_A8B6_0060088FAE88__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// ShipSpecialDamage.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// ShipSpecialDamage dialog + +class ShipSpecialDamage : public CDialog +{ +// Construction +public: + ShipSpecialDamage(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(ShipSpecialDamage) + enum { IDD = IDD_SPECIAL_DAMAGE }; + int m_shock_enabled; + BOOL m_special_exp_enabled; + int m_inner_rad; + int m_outer_rad; + int m_damage; + int m_shock_speed; + int m_blast; + int m_ship_num; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(ShipSpecialDamage) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(ShipSpecialDamage) + afx_msg void OnEnableShockwave(); + afx_msg void OnEnableSpecialExp(); + virtual BOOL OnInitDialog(); + afx_msg void DoGray(); + virtual void OnCancel(); + virtual void OnOK(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_SHIPSPECIALDAMAGE_H__89610237_C0F4_11D2_A8B6_0060088FAE88__INCLUDED_) + diff --git a/include/shockwave.h b/include/shockwave.h new file mode 100644 index 0000000..f7dad8f --- /dev/null +++ b/include/shockwave.h @@ -0,0 +1,138 @@ +/* + * $Logfile: /Freespace2/code/Weapon/Shockwave.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for creating and managing shockwaves + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 7 8/27/99 1:34a Andsager + * Modify damage by shockwaves for BIG|HUGE ships. Modify shockwave damge + * when weapon blows up. + * + * 6 7/18/99 12:32p Dave + * Randomly oriented shockwaves. + * + * 5 3/23/99 2:29p Andsager + * Fix shockwaves for kamikazi and Fred defined. Collect together + * shockwave_create_info struct. + * + * 4 2/26/99 4:14p Dave + * Put in the ability to have multiple shockwaves for ships. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 11 4/15/98 10:17p Mike + * Training mission #5. + * Fix application of subsystem damage. + * + * 10 2/26/98 10:08p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 9 2/20/98 8:33p Lawrance + * Add function to query shockwave max radius + * + * 8 2/14/98 4:43p Lawrance + * add shockwave_weapon_index() to interface + * + * 7 2/02/98 8:47a Andsager + * Ship death area damage applied as instantaneous damage for small ships + * and shockwaves for large (>50 radius) ships. + * + * 6 7/16/97 3:50p Lawrance + * render shockwaves first, to fake transparency + * + * 5 7/16/97 2:52p Lawrance + * make shockwaves objects + * + * 4 7/15/97 7:26p Lawrance + * make shockwave blast persist over time + * + * 3 7/09/97 1:56p Lawrance + * add savegame support for shockwaves + * + * 2 7/08/97 6:00p Lawrance + * implementing shockwaves + * + * 1 7/08/97 1:30p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __SHOCKWAVE_H__ +#define __SHOCKWAVE_H__ + +#include "cfile.h" +#include "object.h" + +#define SW_USED (1<<0) +#define SW_WEAPON (1<<1) +#define SW_SHIP_DEATH (1<<2) +#define SW_WEAPON_KILL (1<<3) // Shockwave created when weapon destroyed by another + +#define MAX_SHOCKWAVES 16 +#define MAX_SHOCKWAVE_TYPES 1 +#define SW_MAX_OBJS_HIT 64 + +typedef struct shockwave_info +{ + int bitmap_id; + int num_frames; + int fps; +} shockwave_info; + +typedef struct shockwave { + shockwave *next, *prev; + int flags; + int objnum; // index into Objects[] for shockwave + int num_objs_hit; + int obj_sig_hitlist[SW_MAX_OBJS_HIT]; + float speed, radius; + float inner_radius, outer_radius, damage; + int weapon_info_index; // -1 if shockwave not caused by weapon + vector pos; + float blast; // amount of blast to apply + int next_blast; // timestamp for when to apply next blast damage + int shockwave_info_index; + int current_bitmap; + float time_elapsed; // in seconds + float total_time; // total lifetime of animation in seconds + int delay_stamp; // for delayed shockwaves + float rot_angle; +} shockwave; + +typedef struct shockwave_create_info { + float inner_rad; + float outer_rad; + float damage; + float blast; + float speed; + float rot_angle; +} shockwave_create_info; + +extern shockwave Shockwaves[MAX_SHOCKWAVES]; +extern shockwave_info Shockwave_info[MAX_SHOCKWAVE_TYPES]; + +void shockwave_close(); +void shockwave_level_init(); +void shockwave_level_close(); +void shockwave_delete(object *objp); +void shockwave_move_all(float frametime); +int shockwave_create(int parent_objnum, vector *pos, shockwave_create_info *sci, int flag, int delay = -1); +void shockwave_render(object *objp); +int shockwave_weapon_index(int index); +float shockwave_max_radius(int index); + +#endif /* __SHOCKWAVE_H__ */ + diff --git a/include/snazzyui.h b/include/snazzyui.h new file mode 100644 index 0000000..667c52b --- /dev/null +++ b/include/snazzyui.h @@ -0,0 +1,100 @@ + +/* + * $Logfile: /Freespace2/code/MenuUI/SnazzyUI.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for the Snazzy User Interface routines. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 17 4/09/98 5:51p Lawrance + * Be able to disable sounds for certain menus + * + * 16 2/03/98 11:52p Lawrance + * call snazzy_flush() from game_flush() + * + * 15 12/23/97 5:28p Hoffoss + * Made enter key act the same as clicking the mouse button in main hall + * screen. + * + * 14 9/07/97 10:06p Lawrance + * let snazzy code keep track of mouse status + * + * 13 8/11/97 9:48p Lawrance + * don't poll keyboard if not requested + * + * 12 2/25/97 11:11a Lawrance + * adding more functionality needed for ship selection screen + * + * 11 12/10/96 4:18p Lawrance + * added snazzy_menu_close() call and integrated with existing menus + * + * 10 12/09/96 2:53p Lawrance + * fixed bug where both the snazzy code and ui code were reading the + * keyboard, and the keypress from the snazzy code was being lost + * + * 9 11/21/96 7:14p Lawrance + * converted menu code to use a file (menu.tbl) to get the data for the + * menu + * + * 8 11/15/96 12:09p John + * Added new UI code. Made mouse not return Enter when you click it. + * Changed the doSnazzyUI function and names to be snazzy_menu_xxx. + * + * 7 11/13/96 4:02p Lawrance + * complete over-haul of the menu system and the states associated with + * them + * + * 6 11/13/96 8:32a Lawrance + * streamlined menu code + * + * 5 11/06/96 8:54a Lawrance + * added revision templates, made more efficient + * + * $NoKeywords: $ + * +*/ + +#ifndef _SNAZZYUI_H +#define _SNAZZYUI_H + +#define MAX_CHAR 150 +#define ESC_PRESSED -2 + +#include "pstypes.h" + +typedef struct menu_region { + int mask; // mask color for the region + int key; // shortcut key for the region + char text[MAX_CHAR]; // The text associated with this item. + int click_sound; // Id of sound to play when mask area clicked on +} MENU_REGION; + +// These are the actions thare are returned in the action parameter. +#define SNAZZY_OVER 1 // mouse is over a region +#define SNAZZY_CLICKED 2 // mouse button has gone from down to up over a region + +int snazzy_menu_do(ubyte *data, int mask_w, int mask_h, int num_regions, MENU_REGION *regions, int *action, int poll_key = 1, int *key = NULL); +void read_menu_tbl(char *menu_name, char *bkg_filename, char *mask_filename, MENU_REGION *regions, int* num_regions, int play_sound=1); +void snazzy_menu_add_region(MENU_REGION *region, char* text, int mask, int key, int click_sound = -1); + +void snazzy_menu_init(); // Call the first time a snazzy menu is inited +void snazzy_menu_close(); +void snazzy_flush(); + +#endif + diff --git a/include/sound.h b/include/sound.h new file mode 100644 index 0000000..347f95a --- /dev/null +++ b/include/sound.h @@ -0,0 +1,360 @@ +/* + * $Logfile: /Freespace2/code/Sound/Sound.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 9/08/99 3:22p Dave + * Updated builtin mission list. + * + * 5 8/27/99 6:38p Alanl + * crush the blasted repeating messages bug + * + * 4 6/18/99 5:16p Dave + * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD + * dialog to PXO screen. + * + * 3 5/23/99 8:11p Alanl + * Added support for EAX + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 30 5/12/98 5:39p Lawrance + * Increase MAX_SOUNDS to 175.. to account for missions with lots of voice + * + * 29 5/12/98 2:43p Lawrance + * Make snd_time_remaining() work for arbitrary format + * + * 28 4/13/98 5:04p Lawrance + * Write functions to determine how many milliseconds are left in a sound + * + * 27 3/29/98 12:56a Lawrance + * preload the warp in and explosions sounds before a mission. + * + * 26 3/25/98 6:10p Lawrance + * Work on DirectSound3D + * + * 25 3/23/98 10:34a Lawrance + * fix parameter in snd_size() + * + * 24 3/23/98 10:32a Lawrance + * Add functions for extracting raw sound data + * + * 23 3/21/98 3:34p Lawrance + * Allow 3d sounds to have their ranges modified dynamically + * + * 22 3/19/98 5:36p Lawrance + * Add some sound debug functions to see how many sounds are playing, and + * to start/stop random looping sounds. + * + * 21 3/17/98 5:55p Lawrance + * Support object-linked sounds for asteroids. + * + * 20 2/20/98 8:32p Lawrance + * Add radius parm to sound_play_3d() + * + * 19 1/07/98 11:08a Lawrance + * pass priority to snd_play_raw() + * + * 18 12/05/97 5:19p Lawrance + * re-do sound priorities to make more general and extensible + * + * 17 11/20/97 5:36p Dave + * Hooked in a bunch of main hall changes (including sound). Made it + * possible to reposition (rewind/ffwd) + * sound buffer pointers. Fixed animation direction change framerate + * problem. + * + * 16 11/20/97 1:06a Lawrance + * Add Master_voice_volume, make voices play back at correctly scaled + * volumes + * + * 15 10/14/97 11:33p Lawrance + * get RSX implemented + * + * 14 10/13/97 7:41p Lawrance + * store duration of sound + * + * 13 10/01/97 5:55p Lawrance + * change call to snd_play_3d() to allow for arbitrary listening position + * + * 12 7/15/97 11:15a Lawrance + * limit the max instances of simultaneous sound effects, implement + * priorities to force critical sounds + * + * 11 6/12/97 5:16p Lawrance + * allow snd_play_looping to take just one parm + * + * 10 6/09/97 11:50p Lawrance + * integrating DirectSound3D + * + * 9 6/05/97 11:25a Lawrance + * use sound signatures to ensure correct sound is loaded + * + * 8 6/05/97 1:36a Lawrance + * using a new interface to play sounds + * + * 7 6/05/97 1:08a Lawrance + * new sound play interface + * + * 6 6/02/97 1:45p Lawrance + * implementing hardware mixing + * + * 5 5/29/97 3:32p Lawrance + * added call to snd_do_frame() + * + * 4 5/29/97 12:04p Lawrance + * split off acm, direct sound, and direct sound 3d portions to separate + * files + * + * 3 5/26/97 10:25a Lawrance + * extern ds_convert_volume() + * + * 2 5/06/97 9:36a Lawrance + * added support for min and max distances for 3d sounds + * + * 1 4/28/97 4:45p John + * Initial version of ripping sound & movie out of OsAPI. + * + * 18 4/24/97 4:14p Lawrance + * make ds_initialized extern + * + * 17 4/21/97 8:58a Lawrance + * keep track of how much mem is used for sounds + * + * 16 4/20/97 11:19a Lawrance + * sndman_ interface obsolete. Using snd_ functions to load, play, and + * manage static sound fx + * + * 15 4/18/97 2:54p Lawrance + * sounds now have a default volume, when playing, pass a scaling factor + * not the actual volume + * + * 14 4/09/97 4:34p Lawrance + * allow looped sounds to be cut off after they complete the full sample + * duration + * + * 13 4/03/97 4:27p Lawrance + * expanding functionality to support event driven music + * + * 12 3/25/97 10:49a Lawrance + * ADPCM -> PCM conversion working + * + * 11 3/24/97 5:50p Lawrance + * added code to convert from ADPCM -> PCM using Audio Compression Manager + * + * 10 3/19/97 11:48a Lawrance + * make each sound effect have a DirectSound secondary buffer + * + * 9 3/10/97 4:16p Allender + * new messaging system. builtin5 fixed to support it. made new sound + * function to determine if sound is still playing. + * + * 8 2/25/97 11:13a Lawrance + * added Master_sound_volume to scale volume passed to snd_play() + * + * 7 1/10/97 10:44a John + * added functions to dynamically set volume/pan + * + * 6 12/03/96 3:50p John + * Added some initial support for looping sounds. + * + * 5 11/05/96 11:02a Mike + * Add sound toggle to Delete-D menu. + * Make some global variables look global - ie, capitalize them. + * Fix bug in collision code to make ship:ship collision detection work. + * Fix setting of parent_obj in obj_create(). + * + * 4 11/04/96 3:16p John + * First rev of working sound. + * + * 3 11/04/96 10:37a John + * Closed the DirectSound object on program termination. + * + * 2 10/30/96 5:27p John + * some hacked in sound stuff so i can experiment with directsound. + * + * 1 10/28/96 11:36a John + * + * $NoKeywords: $ + */ + +#ifndef __SOUND_H__ +#define __SOUND_H__ + +#define MAX_SOUNDS 256 + +// Used for keeping track which low-level sound library is being used +#define SOUND_LIB_DIRECTSOUND 0 +#define SOUND_LIB_RSX 1 + +#define GAME_SND_USE_DS3D (1<<1) +#define GAME_SND_VOICE (1<<2) + +// Priorities that can be passed to snd_play() functions to limit how many concurrent sounds of a +// given type are played. +#define SND_PRIORITY_MUST_PLAY 0 +#define SND_PRIORITY_SINGLE_INSTANCE 1 +#define SND_PRIORITY_DOUBLE_INSTANCE 2 +#define SND_PRIORITY_TRIPLE_INSTANCE 3 + +typedef struct game_snd +{ + int sig; // number of sound in sounds.tbl (not used) + char filename[MAX_FILENAME_LEN]; + float default_volume; // range: 0.0 -> 1.0 + int min, max; // min: distance at which sound will stop getting louder max: distance at which sound is inaudible + int preload; // preload sound (ie read from disk before mission starts) + int id; // index into Sounds[], where sound data is stored + int id_sig; // signature of Sounds[] element + int flags; +} game_snd; + +typedef struct sound_env +{ + unsigned long id; + float volume; + float damping; + float decay; +} sound_env; + +extern int Sound_enabled; +extern float Master_sound_volume; // 0 -> 1.0 +extern float Master_voice_volume; // 0 -> 1.0 +extern int Snd_sram; // System memory consumed by sound data +extern int Snd_hram; // Soundcard memory consumed by sound data + +//int snd_load( char *filename, int hardware=0, int three_d=0, int *sig=NULL ); +int snd_load( game_snd *gs, int allow_hardware_load = 0); + +int snd_unload( int sndnum ); +void snd_unload_all(); + +// Plays a sound with volume between 0 and 1.0, where 0 is the +// inaudible and 1.0 is the loudest sound in the game. +// Pan goes from -1.0 all the way left to 0.0 in center to 1.0 all the way right. +int snd_play( game_snd *gs, float pan=0.0f, float vol_scale=1.0f, int priority = SND_PRIORITY_SINGLE_INSTANCE, bool voice_message = false ); + +// Play a sound directly from index returned from snd_load(). Bypasses +// the sound management process of using game_snd. +int snd_play_raw( int soundnum, float pan, float vol_scale=1.0f, int priority = SND_PRIORITY_MUST_PLAY ); + +// Plays a sound with volume between 0 and 1.0, where 0 is the +// inaudible and 1.0 is the loudest sound in the game. It scales +// the pan and volume relative to the current viewer's location. +int snd_play_3d(game_snd *gs, vector *source_pos, vector *listen_pos, float radius=0.0f, vector *vel = NULL, int looping = 0, float vol_scale=1.0f, int priority = SND_PRIORITY_SINGLE_INSTANCE, vector *sound_fvec = NULL, float range_factor = 1.0f, int force = 0 ); + +// update the given 3d sound with a new position +void snd_update_3d_pos(int soudnnum, game_snd *gs, vector *new_pos); + +// Use these for looping sounds. +// Returns the handle of the sound. -1 if failed. +// If startloop or stoploop are not -1, then then are used. +int snd_play_looping( game_snd *gs, float pan=0.0f, int start_loop=-1, int stop_loop=-1, float vol_scale=1.0f, int priority = SND_PRIORITY_MUST_PLAY, int force = 0 ); + +void snd_stop( int snd_handle ); + +// Sets the volume of a sound that is already playing. +// The volume is between 0 and 1.0, where 0 is the +// inaudible and 1.0 is the loudest sound in the game. +void snd_set_volume( int snd_handle, float volume ); + +// Sets the panning location of a sound that is already playing. +// Pan goes from -1.0 all the way left to 0.0 in center to 1.0 all the way right. +void snd_set_pan( int snd_handle, float pan ); + +// Sets the pitch (frequency) of a sound that is already playing +// Valid values for pitch are between 100 and 100000 +void snd_set_pitch( int snd_handle, int pitch ); +int snd_get_pitch( int snd_handle ); + +// Stops all sounds from playing, even looping ones. +void snd_stop_all(); + +// determines if the sound handle is still palying +int snd_is_playing( int snd_handle ); + +// change the looping status of a sound that is playing +void snd_chg_loop_status(int snd_handle, int loop); + +// return the time in ms for the duration of the sound +int snd_get_duration(int snd_id); + +// get a 3D vol and pan for a particular sound +int snd_get_3d_vol_and_pan(game_snd *gs, vector *pos, float* vol, float *pan, float radius=0.0f); + +int snd_init(int use_a3d, int use_eax); +void snd_close(); + +// Return 1 or 0 to show that sound system is inited ok +int snd_is_inited(); + +// Returns a pointer to the direct sound object +uint sound_get_ds(); + +void snd_update_listener(vector *pos, vector *vel, matrix *orient); + +void snd_use_lib(int lib_id); + +int snd_num_playing(); + +int snd_get_data(int handle, char *data); +int snd_size(int handle, int *size); +void snd_do_frame(); + +// repositioning of the sound buffer pointer +void snd_rewind(int snd_handle, game_snd *sg, float seconds); // rewind N seconds from the current position +void snd_ffwd(int snd_handle, game_snd *sg, float seconds); // fast forward N seconds from the current position +void snd_set_pos(int snd_handle, game_snd *sg, float val,int as_pct); // set the position val as either a percentage (if as_pct) or as a # of seconds into the sound + +void snd_get_format(int handle, int *bits_per_sample, int *frequency); +int snd_time_remaining(int handle, int bits_per_sample=8, int frequency=11025); + +// sound environment + +enum +{ + SND_ENV_GENERIC, + SND_ENV_PADDEDCELL, + SND_ENV_ROOM, + SND_ENV_BATHROOM, + SND_ENV_LIVINGROOM, + SND_ENV_STONEROOM, + SND_ENV_AUDITORIUM, + SND_ENV_CONCERTHALL, + SND_ENV_CAVE, + SND_ENV_ARENA, + SND_ENV_HANGAR, + SND_ENV_CARPETEDHALLWAY, + SND_ENV_HALLWAY, + SND_ENV_STONECORRIDOR, + SND_ENV_ALLEY, + SND_ENV_FOREST, + SND_ENV_CITY, + SND_ENV_MOUNTAINS, + SND_ENV_QUARRY, + SND_ENV_PLAIN, + SND_ENV_PARKINGLOT, + SND_ENV_SEWERPIPE, + SND_ENV_UNDERWATER, + SND_ENV_DRUGGED, + SND_ENV_DIZZY, + SND_ENV_PSYCHOTIC, +}; + +int sound_env_set(sound_env *se); +int sound_env_get(sound_env *se); +int sound_env_disable(); +int sound_env_supported(); + +#endif diff --git a/include/spline.h b/include/spline.h new file mode 100644 index 0000000..12a3fb4 --- /dev/null +++ b/include/spline.h @@ -0,0 +1,96 @@ +/* + * $Logfile: /Freespace2/code/Math/spline.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 7/08/99 10:53a Dave + * New multiplayer interpolation scheme. Not 100% done yet, but still + * better than the old way. + * + * 2 7/06/99 4:24p Dave + * Mid-level checkin. Starting on some potentially cool multiplayer + * smoothness crap. + * + * + * $NoKeywords: $ + */ + +#ifndef __FS2_SPLINE_HEADER_FILE +#define __FS2_SPLINE_HEADER_FILE + +#include "vecmat.h" + +// ------------------------------------------------------------------------------------------------- +// SPLINE DEFINES/VARS +// + +struct color; + +// max bezier degree - note the # of points directly corresponds to the degree (degree == n_points - 1). +// more points means more expensive! +#define MAX_BEZ_PTS 3 + +// bezier class. whee +class bez_spline { +public : + vector pts[MAX_BEZ_PTS]; + int num_pts; + +public : + // constructor + bez_spline(); + bez_spline(int _num_pts, vector *_pts[MAX_BEZ_PTS]); + + // set the points + void bez_set_points(int _num_pts, vector *_pts[MAX_BEZ_PTS]); + + // bezier blend function + float BEZ(int k, int n, float u); + + // get a point on the bez curve. u goes from 0.0 to 1.0 + void bez_get_point(vector *out, float u); + + // render a bezier + void bez_render(int divs, color *c); +}; + +// hermite splines. cool cubic stuff +#define MAX_HERM_PTS 3 +class herm_spline { +public : + vector pts[MAX_HERM_PTS]; // control points + vector d_pts[MAX_HERM_PTS]; // derivative of control points (think of as velocity) + int num_pts; +public : + // constructor + herm_spline(); + herm_spline(int _num_pts, vector *_pts[MAX_HERM_PTS], vector *_d_pts[MAX_HERM_PTS]); + + // set the points + void herm_set_points(int _num_pts, vector *_pts[MAX_HERM_PTS], vector *_d_pts[MAX_HERM_PTS]); + + // get a point on the hermite curve. + void herm_get_point(vector *out, float u, int k); + + // the derivative of a point on the hermite curve + void herm_get_deriv(vector *deriv, float u, int k); + + // render a bezier + void herm_render(int divs, color *c); +}; + + +// ------------------------------------------------------------------------------------------------- +// SPLINE FUNCTIONS +// + + +#endif + diff --git a/include/sst1vid.h b/include/sst1vid.h new file mode 100644 index 0000000..5be60c6 --- /dev/null +++ b/include/sst1vid.h @@ -0,0 +1,120 @@ +/* +** Copyright (c) 1995, 3Dfx Interactive, Inc. +** All Rights Reserved. +** +** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.; +** the contents of this file may not be disclosed to third parties, copied or +** duplicated in any form, in whole or in part, without the prior written +** permission of 3Dfx Interactive, Inc. +** +** RESTRICTED RIGHTS LEGEND: +** Use, duplication or disclosure by the Government is subject to restrictions +** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data +** and Computer Software clause at DFARS 252.227-7013, and/or in similar or +** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished - +** rights reserved under the Copyright Laws of the United States. +** +** $Header$ +** $Log$ +** Revision 1.1 2002/05/03 03:28:12 root +** Initial revision +** + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 2 2/27/98 3:25p Frank + * (John/Frank K.) updated to 2.43 headers + * + * 1 10/16/97 11:51a Klier + * + * 4 9/09/97 7:35p Sellers + * Added 400x300 resolution + * + * 3 8/24/97 9:31a Sellers + * moved new video timing to sst1vid.h + * redefined 1600x1280 to be 1600x1200 + * + * 2 6/05/97 11:14p Pgj + * + * 5 7/24/96 3:43p Sellers + * added 512x384 @ 60 Hz for arcade monitors + * added 512x256 @ 60 Hz for arcade monitors + * + * 4 7/18/96 10:58a Sellers + * fixed FT and TF clock delay values for lower frequencies with + * .5/.5 combos + * + * 3 6/18/96 6:54p Sellers + * added sst1InitShutdownSli() to fix Glide Splash screen problems with + * SLI + * + * 2 6/13/96 7:45p Sellers + * added "voodoo.ini" support + * added DirectX support + * misc cleanup + * + * 2 6/11/96 1:43p Sellers + * added support for 60, 75, 85, and 120 Hz refresh rates for "most" + * resolutions + * + * 1 5/08/96 5:43p Paik + * Video definitions +*/ +#ifndef __SST1VID_H__ +#define __SST1VID_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Video defines */ + +typedef FxI32 GrScreenRefresh_t; +#define GR_REFRESH_60Hz 0x0 +#define GR_REFRESH_70Hz 0x1 +#define GR_REFRESH_72Hz 0x2 +#define GR_REFRESH_75Hz 0x3 +#define GR_REFRESH_80Hz 0x4 +#define GR_REFRESH_90Hz 0x5 +#define GR_REFRESH_100Hz 0x6 +#define GR_REFRESH_85Hz 0x7 +#define GR_REFRESH_120Hz 0x8 +#define GR_REFRESH_NONE 0xff + +typedef FxI32 GrScreenResolution_t; +#define GR_RESOLUTION_320x200 0x0 +#define GR_RESOLUTION_320x240 0x1 +#define GR_RESOLUTION_400x256 0x2 +#define GR_RESOLUTION_512x384 0x3 +#define GR_RESOLUTION_640x200 0x4 +#define GR_RESOLUTION_640x350 0x5 +#define GR_RESOLUTION_640x400 0x6 +#define GR_RESOLUTION_640x480 0x7 +#define GR_RESOLUTION_800x600 0x8 +#define GR_RESOLUTION_960x720 0x9 +#define GR_RESOLUTION_856x480 0xa +#define GR_RESOLUTION_512x256 0xb +#define GR_RESOLUTION_1024x768 0xC +#define GR_RESOLUTION_1280x1024 0xD +#define GR_RESOLUTION_1600x1200 0xE +#define GR_RESOLUTION_400x300 0xF +#define GR_RESOLUTION_NONE 0xff + +#ifdef GR_RESOLUTION_MAX +#undef GR_RESOLUTION_MAX +#endif +#ifdef GR_RESOLUTION_MIN +#undef GR_RESOLUTION_MIN +#endif +#define GR_RESOLUTION_MIN GR_RESOLUTION_320x200 +#define GR_RESOLUTION_MAX GR_RESOLUTION_1600x1200 + +#ifdef __cplusplus +} +#endif + +#endif /* __SST1VID_H__ */ + diff --git a/include/stand_gui.h b/include/stand_gui.h new file mode 100644 index 0000000..0bfd63a --- /dev/null +++ b/include/stand_gui.h @@ -0,0 +1,322 @@ +/* + * $Logfile: /Freespace2/code/Network/stand_gui.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 24 9/04/98 3:52p Dave + * Put in validated mission updating and application during stats + * updating. + * + * 23 6/10/98 2:56p Dave + * Substantial changes to reduce bandwidth and latency problems. + * + * 22 5/24/98 3:45a Dave + * Minor object update fixes. Justify channel information on PXO. Add a + * bunch of configuration stuff for the standalone. + * + * 21 5/22/98 9:35p Dave + * Put in channel based support for PXO. Put in "shutdown" button for + * standalone. UI tweaks for TvT + * + * 20 5/18/98 9:15p Dave + * Put in network config file support. + * + * 19 5/15/98 5:16p Dave + * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy + * status for team vs. team. Put in asserts to check for invalid team vs. + * team situations. + * + * 18 5/09/98 7:16p Dave + * Put in CD checking. Put in standalone host password. Made pilot into + * popup scrollable. + * + * 17 5/08/98 5:05p Dave + * Go to the join game screen when quitting multiplayer. Fixed mission + * text chat bugs. Put mission type symbols on the create game list. + * Started updating standalone gui controls. + * + * 16 3/24/98 5:00p Dave + * Fixed several ui bugs. Put in pre and post voice stream playback sound + * fx. Put in error specific popups for clients getting dropped from games + * through actions other than their own. + * + * 15 1/31/98 4:32p Dave + * Put in new support for VMT player validation, game logging in, and game + * logging out. Need to finish stats transfer. + * + * 14 1/24/98 3:39p Dave + * Fixed numerous multiplayer bugs (last frame quit problem, weapon bank + * changing, deny packets). Add several controls to standalone server. + * + * 13 1/17/98 5:51p Dave + * Bug fixes for bugs generated by multiplayer testing. + * + * 12 1/13/98 5:37p Dave + * Reworked a lot of standalone interface code. Put in single and + * multiplayer popups for death sequence. Solidified multiplayer kick + * code. + * + * 11 1/11/98 10:03p Allender + * removed from headers which included it. Made psnet_socket + * type which is defined just as SOCKET type is. + * + * 10 12/10/97 4:46p Dave + * Added in more detailed support for multiplayer packet lag/loss. Fixed + * some multiplayer stuff. Added some controls to the standalone. + * + * 9 12/03/97 11:59a Dave + * Dependant merge checkin + * + * 8 10/03/97 4:57p Dave + * Added functions for new text controls. Added some more reset controls. + * Put in checks for all-players-gone. + * + * 7 9/24/97 5:04p Dave + * Removed an unnecessary function prototype. + * + * 6 8/29/97 5:03p Dave + * Added a ton of new gui controls/features. + * + * 5 8/26/97 5:03p Dave + * Added bunch of informational controls. Standardized some functions for + * external use. Put in godview mode (conditionaled out though). + * + * 4 8/23/97 11:31a Dave + * Put in new gui calls. Added a bunch of display controls. + * + * 3 8/20/97 4:20p Dave + * Added standalone state text box. + * + * 2 8/11/97 4:52p Dave + * Spliced out standalone GUI stuff from OsApi and WinMain.cpp to its own + * module. + * + * 1 8/11/97 4:21p Dave + * + * $NoKeywords: $ + */ + +#ifndef _FREESPACE_STANDALONE_GUI_HEADER_FILE +#define _FREESPACE_STANDALONE_GUI_HEADER_FILE + +// ---------------------------------------------------------------------------------------- +// external variables +// + +struct net_player; + +// ---------------------------------------------------------------------------------------- +// generic dialog functions +// + +// create the validate dialog +void std_create_gen_dialog(char *title); + +// kill the validate dialog(); +void std_destroy_gen_dialog(); + +// set the text in the filename of the validate dialog +// valid values for field_num == 0 .. 2 +void std_gen_set_text(char *str, int field_num); + +// is the validate dialog active +int std_gen_is_active(); + + +// ---------------------------------------------------------------------------------------- +// connection page/tab functions +// + +// set the text box indicating how many players are connected, returning the determined count +int std_connect_set_connect_count(); + +// set the connect status (connected or not) of the game host +void std_connect_set_host_connect_status(); + +// add an ip string to the connect page listbox +void std_connect_add_ip_string(char *string); + +// remove an ip string from the connect page listbox +void std_connect_remove_ip_string(char *string); + +// set an ip string on the connect page listbox +void std_connect_set_ip_string(char *lookup,char *string); + +// kick a player (the one currently selected in the listbox) +void std_connect_kick_player(); + +// update the ping for this particular player +void std_connect_update_ping(net_player *p); + +// clear all the controls for this page +void std_connect_clear_controls(); + +// set the game name for the standalone. passing NULL uses the default +void std_connect_set_gamename(char *name); + +// the user has changed the text in the server name text box. handle this +void std_connect_handle_name_change(); + +// the user has changed the text in the host password text box +void std_connect_handle_passwd_change(); + + +// ---------------------------------------------------------------------------------------- +// multiplayer page/tab functions +// + +// set the mission time in seconds +void std_multi_set_standalone_missiontime(float mission_time); + +// set the mission name +void std_multi_set_standalone_mission_name(char *mission_name); + +// initialize the goal tree for this mission +void std_multi_setup_goal_tree(); + +// add all the goals from the current mission to the tree control +void std_multi_add_goals(); + +// update all the goals in the goal tree based upon the mission status +void std_multi_update_goals(); + +// set the framerate text box for this tab +void std_multi_set_framerate(float f); + +// clear all the controls for this page +void std_multi_clear_controls(); + +// update the netgame information area controls with the current Netgame settings +void std_multi_update_netgame_info_controls(); + + +// --------------------------------------------------------------------------------------- +// player info page/tab functions +// + +// start displaying info for the passed player on this page +void std_pinfo_display_player_info(net_player *p); + +// check to see if this player is the one being displayed, and if so, then update the display info +// return 1 if the player was updated +int std_pinfo_maybe_update_player_info(net_player *p); + +// add a player to the list on the player info page +void std_pinfo_add_player_list_item(net_player *p); + +// remove a player from the list on the player info page +void std_pinfo_remove_player_list_item(net_player *p); + +// update the ping display for this player +void std_pinfo_update_ping(net_player *p); + +// clear all the controls for this page +void std_pinfo_clear_controls(); + + +// --------------------------------------------------------------------------------------- +// player god stuff page/tab functions +// + +// add a player to the listbox on the godstuff page +void std_gs_add_player(net_player *p); + +// remove a player from the listbox on the godstuff page +void std_gs_remove_player(net_player *p); + +// send a message as if the standalone were a player +void std_gs_send_godstuff_message(); + +// set the framerate text box for this page +void std_gs_set_framerate(float f); + +// clear all the controls for this page +void std_gs_clear_controls(); + + +// --------------------------------------------------------------------------------------- +// debug page/tab functions +// + +// set the text on the standalones state indicator box +void std_debug_set_standalone_state_string(char *str); + +// clear all the controls for this page +void std_debug_clear_controls(); + + +// --------------------------------------------------------------------------------------- +// general functions +// + +// add a player and take care of updating all gui/data details +void std_add_player(net_player *p); + +// remove a player and take care of updateing all gui/data details +int std_remove_player(net_player *p); + +// set any relevant controls which display the framerate of the standalone +void std_set_standalone_fps(float fps); + +// update any relveant controls which display the ping for the given player +void std_update_player_ping(net_player *p); + +// reset all gui stuff for the standalone +void std_reset_standalone_gui(); + +// reset all networking/gui stuff (calls reset_standalone_gui) for the standalone +void std_multi_standalone_reset_all(); + +// close down the standalone +void std_deinit_standalone(); + +// initialize the standalone +void std_init_standalone(); + +// do any gui related issues on the standalone (like periodically updating player stats, etc...) +void std_do_gui_frame(); + +// notify the user that the standalone has failed to login to the tracker on startup +void std_tracker_notify_login_fail(); + +// attempt to log the standalone into the tracker +void std_tracker_login(); + +// reset all stand gui timestamps +void std_reset_timestamps(); + +// add a line of text chat to the standalone +void std_add_chat_text(char *text,int player_index,int add_id); + +// if the standalone is host password protected +int std_is_host_passwd(); + +// change the default property sheet interface into something more useful +void std_mutate_sheet(); + +// if the given callsign is banned from the server +int std_player_is_banned(char *name); + +// add a callsign to the ban list +void std_add_ban(char *name); + +#endif + diff --git a/include/starfield.h b/include/starfield.h new file mode 100644 index 0000000..3fb96db --- /dev/null +++ b/include/starfield.h @@ -0,0 +1,180 @@ +/* + * $Logfile: /Freespace2/code/Starfield/StarField.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to handle and draw starfields, background space image bitmaps, floating + * debris, etc. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 11 7/21/99 8:10p Dave + * First run of supernova effect. + * + * 10 6/03/99 6:37p Dave + * More TNT fun. Made perspective bitmaps more flexible. + * + * 9 5/09/99 6:00p Dave + * Lots of cool new effects. E3 build tweaks. + * + * 8 5/07/99 1:59p Johnson + * Tweaked background bitmaps a bit. + * + * 7 4/23/99 5:53p Dave + * Started putting in new pof nebula support into Fred. + * + * 6 4/07/99 6:22p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 5 3/31/99 8:24p Dave + * Beefed up all kinds of stuff, incluging beam weapons, nebula effects + * and background nebulae. Added per-ship non-dimming pixel colors. + * + * 4 11/14/98 5:33p Dave + * Lots of nebula work. Put in ship contrails. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 21 4/11/98 6:53p John + * Added first rev of subspace effect. + * + * 20 4/07/98 4:17p John + * Made Fred be able to move suns. Made suns actually affect the lighting + * in the game. + * + * 19 3/21/98 7:36p Lawrance + * Move jump nodes to own lib. + * + * 18 3/19/98 12:29p Lawrance + * Fix jumpnode targeting bug on the target monitor + * + * 17 3/15/98 3:41p Allender + * new sexpression to gauge whether a ship warped out in proximity of jump + * node + * + * 16 3/11/98 5:33p Lawrance + * Support rendering and targeting of jump nodes + * + * 15 3/10/98 4:26p Hoffoss + * Changed jump node structure to include a name. Position is now taken + * from the object (each jump node has an associated object now). + * + * 14 3/06/98 5:30p Hoffoss + * Added jump node rendering code to FreeSpace. + * + * 13 2/26/98 10:08p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 12 2/06/98 3:08p Mike + * More asteroid stuff, including resolving conflicts between the two + * asteroid_field structs! + * + * 11 1/18/98 4:24p John + * Detect when view camera "cuts" and tell the star code about it so it + * won't blur stars and debris between the last frame and this frame. + * + * 10 8/25/97 5:56p Hoffoss + * Added multiple asteroid field support, loading and saving of asteroid + * fields, and ship score field to Fred. + * + * 9 8/05/97 10:18a Lawrance + * my_rand() being used temporarily instead of rand() + * + * 8 2/17/97 3:06p Hoffoss + * Changed header description. + * + * 7 2/14/97 3:29p Hoffoss + * Added header for MSDEV to fill in. + * + * $NoKeywords: $ + */ + +#ifndef _STARFIELD_H +#define _STARFIELD_H + +#include "parselo.h" +#include "cfile.h" + +#define MAX_STARFIELD_BITMAP_LISTS 1 +#define MAX_STARFIELD_BITMAPS 60 +#define MAX_ASTEROID_FIELDS 4 + +// nice low polygon background +#define BACKGROUND_MODEL_FILENAME "spherec.pof" + +// global info (not individual instances) +typedef struct starfield_bitmap { + char filename[MAX_FILENAME_LEN+1]; // bitmap filename + char glow_filename[MAX_FILENAME_LEN+1]; // only for suns + int bitmap; // bitmap handle + int glow_bitmap; // only for suns + int xparent; + float r, g, b, i; // only for suns +} starfield_bitmap; + +// starfield bitmap instance +typedef struct starfield_bitmap_instance { + char filename[MAX_FILENAME_LEN+1]; // used to match up into the starfield_bitmap array + float scale_x, scale_y; // x and y scale + int div_x, div_y; // # of x and y divisions + angles ang; // angles from fred +} starfield_bitmap_instance; + +// background bitmaps +extern starfield_bitmap Starfield_bitmaps[MAX_STARFIELD_BITMAPS]; +extern starfield_bitmap_instance Starfield_bitmap_instance[MAX_STARFIELD_BITMAPS]; +extern int Num_starfield_bitmaps; + +// sun bitmaps and sun glow bitmaps +extern starfield_bitmap Sun_bitmaps[MAX_STARFIELD_BITMAPS]; +extern starfield_bitmap_instance Suns[MAX_STARFIELD_BITMAPS]; +extern int Num_suns; + +extern const int MAX_STARS; +extern int Num_stars; + +// call on game startup +void stars_init(); + +// call this in game_post_level_init() so we know whether we're running in full nebula mode or not +void stars_level_init(); + +// This *must* be called to initialize the lighting. +// You can turn off all the stars and suns and nebulas, though. +void stars_draw(int show_stars, int show_suns, int show_nebulas, int show_subspace); +// void calculate_bitmap_matrix(starfield_bitmaps *bm, vector *v); +// void calculate_bitmap_points(starfield_bitmaps *bm, float bank = 0.0f); + +// draw the corresponding glow for sun_n +void stars_draw_sun_glow(int sun_n); + +// Call when the viewer camera "cuts" so stars and debris +// don't draw incorrect blurs between last frame and this frame. +void stars_camera_cut(); + +// call this to set a specific model as the background model +void stars_set_background_model(char *model_name, char *texture_name); + +// lookup a starfield bitmap, return index or -1 on fail +int stars_find_bitmap(char *name); + +// lookup a sun by bitmap filename, return index or -1 on fail +int stars_find_sun(char *name); + +// get the world coords of the sun pos on the unit sphere. +void stars_get_sun_pos(int sun_n, vector *pos); + +#endif + diff --git a/include/starfieldeditor.h b/include/starfieldeditor.h new file mode 100644 index 0000000..0d6930e --- /dev/null +++ b/include/starfieldeditor.h @@ -0,0 +1,80 @@ +/* + * $Logfile: /Freespace2/code/FRED2/StarfieldEditor.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Starfield editor dialog handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 7 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 6 4/16/97 5:18p Hoffoss + * Moved Asteroid field editor stuff to a seperate dialog box. + * + * 5 3/17/97 3:00p Hoffoss + * slider updates on the fly now. + * + * 4 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 3 1/31/97 3:16p Hoffoss + * Asteroid field management implemented. + * + * $NoKeywords: $ + */ + +///////////////////////////////////////////////////////////////////////////// +// starfield_editor dialog + +class starfield_editor : public CDialog +{ +// Construction +public: + void OnOK(); + void OnCancel(); + starfield_editor(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(starfield_editor) + enum { IDD = IDD_STARFIELD }; + CSliderCtrl m_slider; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(starfield_editor) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + int initialized; + + // Generated message map functions + //{{AFX_MSG(starfield_editor) + virtual BOOL OnInitDialog(); + afx_msg void OnEnableAsteroids(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/staticrand.h b/include/staticrand.h new file mode 100644 index 0000000..81bc3bb --- /dev/null +++ b/include/staticrand.h @@ -0,0 +1,54 @@ +/* + * $Logfile: /Freespace2/code/Math/StaticRand.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * header for Static Random functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 4 3/17/98 12:16a Allender + * asteroids in multiplayer -- minor problems with position being correct + * + * 3 12/30/97 5:46p Lawrance + * Rename rnd() to rand_alt(). + * + * 2 12/30/97 4:27p Lawrance + * Add new rnd() function that doesn't affect rand() sequence. + * + * 1 8/08/97 3:38p Allender + * +*/ + +#ifndef _STATIC_RAND_H + +#include "pstypes.h" + +#define SEMIRAND_MAX_LOG 4 +#define SEMIRAND_MAX (2 << SEMIRAND_MAX_LOG) // Do not change this! Change SEMIRAND_MAX_LOG! + +extern int Semirand[SEMIRAND_MAX]; // this array is saved by the ai code on save/restore + +extern void init_semirand(); +extern int static_rand(int num); +extern float static_randf(int num); +extern void static_randvec(int num, vector *vp); +extern float static_randf_range(int num, float min, float max); + +// Alternate random number generator that doesn't affect rand() sequence + +void srand_alt(int seed); // Seed the random number generator +int rand_alt(); // Get a random integer between 1 and RND_MAX +float frand_alt(); // Get a random float between 0 and 1.0 + +#endif + diff --git a/include/stats.h b/include/stats.h new file mode 100644 index 0000000..54741bc --- /dev/null +++ b/include/stats.h @@ -0,0 +1,82 @@ +/* + * $Logfile: /Freespace2/code/Stats/Stats.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * module for running the stats screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 13 5/18/98 5:25p Chad + * Removed old-ass stats display code. + * + * 12 1/06/98 5:08p Dave + * Put in stats display change to show alltime+mission stats during + * debriefing. Fixed a object update sequencing bug. + * + * 11 10/24/97 6:19p Dave + * More standalone testing/fixing. Added reliable endgame sequencing. + * Added reliable ingame joining. Added reliable stats transfer (endgame). + * Added support for dropping players in debriefing. Removed a lot of old + * unused code. + * + * 10 10/23/97 7:44p Hoffoss + * Added 2 defines to replace hard-coded values in the code. + * + * 9 10/08/97 4:47p Dave + * Fixed bugs turned up in testing. Added some brief comments. + * + * 8 9/30/97 8:48p Lawrance + * generalize some functions for displaying stats info + * + * 7 9/09/97 4:31p Dave + * Put in multiplayer post-game stats stuff. + * + * 6 8/15/97 2:20p Allender + * fix stats code to properly get connected players + * + * 5 8/15/97 9:28a Dave + * Fixed minor bug in multiplayer stats init. + * + * 4 8/14/97 5:20p Dave + * Made initialization/clearing of players stats more thorough. + * + * 3 7/25/97 4:33p Dave + * Spiffed up statistics screen considerably. Currently reports all + * statistics in the game (except mission/campaign related scores) + * + * 2 7/24/97 2:31p Dave + * Added basic screen, no interaction yet. + * + * 1 7/24/97 1:50p Dave + * + * $NoKeywords: $ + */ + +#ifndef _FS_STATISTICS_STATE_HEADER +#define _FS_STATISTICS_STATE_HEADER + +#define MISSION_STATS 0 +#define ALL_TIME_STATS 1 + +#include "scoring.h" + +void show_stats_init(); +void show_stats_close(); +void set_player_stats(int pid); +void init_multiplayer_stats( void ); // initializes all mission specific stats to be 0 + +void show_stats_numbers(int stage, int sx, int sy, int dy=10,int add_mission = 0); +void show_stats_label(int stage, int sx, int sy, int dy=10); + +#endif + diff --git a/include/stdafx.h b/include/stdafx.h new file mode 100644 index 0000000..999a8ae --- /dev/null +++ b/include/stdafx.h @@ -0,0 +1,13 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + diff --git a/include/subsysdamage.h b/include/subsysdamage.h new file mode 100644 index 0000000..a533d54 --- /dev/null +++ b/include/subsysdamage.h @@ -0,0 +1,76 @@ +/* + * $Logfile: /Freespace2/code/Ship/SubsysDamage.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for various subystem damage defines + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 4 4/07/98 5:30p Lawrance + * Player can't send/receive messages when comm is destroyed. Garble + * messages when comm is damaged. + * + * 3 10/13/97 7:42p Lawrance + * added MIN_COMM_STR_RECEIVE_OK + * + * 2 10/11/97 6:38p Lawrance + * move subsys damage #defines to SubsysDamage.h + * + * 1 10/10/97 7:57p Lawrance + * + * $NoKeywords: $ + */ + +#ifndef __SUBSYS_DAMAGE_H__ +#define __SUBSYS_DAMAGE_H__ + +///////////////////////////////////////// +// engines +///////////////////////////////////////// +#define SHIP_MIN_ENGINES_TO_WARP 0.3f // % engine strength required to engage warp +#define ENGINE_MIN_STR 0.15f // if engines are below this level, still contribute this percent to total + // (unless destroyed, then contribute none). + +///////////////////////////////////////// +// weapons +///////////////////////////////////////// +#define SUBSYS_WEAPONS_STR_FIRE_OK 0.7f // 70% strength or better, weapons always fire +#define SUBSYS_WEAPONS_STR_FIRE_FAIL 0.2f // below 20%, weapons will not fire + + +///////////////////////////////////////// +// sensors - targeting +///////////////////////////////////////// +#define SENSOR_STR_TARGET_NO_EFFECTS 0.3f // % strength of sensors at which no negative effects on targeting +#define MIN_SENSOR_STR_TO_TARGET 0.2f // % strength of sensors at which targeting ceases + // to function + +///////////////////////////////////////// +// sensors - radar +///////////////////////////////////////// +#define SENSOR_STR_RADAR_NO_EFFECTS 0.4f // % strength of sensors at which no negative effects on radar +#define MIN_SENSOR_STR_TO_RADAR 0.1f // % strength of sensors at which radar ceases to function + +///////////////////////////////////////// +// communications +///////////////////////////////////////// +#define MIN_COMM_STR_TO_MESSAGE 0.3 // % strength of communications at which player + // is unable to use squadmate messaging + +#define COMM_DESTROYED 0 +#define COMM_DAMAGED 1 +#define COMM_OK 2 + + +#endif + diff --git a/include/supernova.h b/include/supernova.h new file mode 100644 index 0000000..9da931d --- /dev/null +++ b/include/supernova.h @@ -0,0 +1,91 @@ +/* + * $Logfile: /Freespace2/code/Starfield/Supernova.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for nebula stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 9/09/99 11:40p Dave + * Handle an Assert() in beam code. Added supernova sounds. Play the right + * 2 end movies properly, based upon what the player did in the mission. + * + * 3 9/03/99 1:32a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 2 7/21/99 8:10p Dave + * First run of supernova effect. + * + * + * $NoKeywords: $ + */ + +#ifndef __FS2_SUPERNOVA_FUN_HEADER_FILE +#define __FS2_SUPERNOVA_FUN_HEADER_FILE + +// -------------------------------------------------------------------------------------------------------------------------- +// SUPERNOVA DEFINES/VARS +// + +struct vector; +struct matrix; + +// supernova timing stuff +#define SUPERNOVA_MIN_TIME 15.0f // must be at least 15 seconds out +#define SUPERNOVA_CUT_TIME 5.0f // note this is also the minimum time for the supernova sexpression +#define SUPERNOVA_CAMERA_MOVE_TIME 2.0f // this is the amount of time the camera will cut from the sun to the player +#define SUPERNOVA_FADE_TO_WHITE_TIME 1.0f // fade to white over this amount of time + +// how much bigger the sun will be when the effect hits +#define SUPERNOVA_SUN_SCALE 3.0f + +// status for the supernova this mission +#define SUPERNOVA_NONE 0 // nothing happened in this mission +#define SUPERNOVA_STARTED 1 // started, but the player never got hit by it +#define SUPERNOVA_HIT 2 // started and killed the player +extern int Supernova_status; + +// -------------------------------------------------------------------------------------------------------------------------- +// SUPERNOVA FUNCTIONS +// + +// level init +void supernova_level_init(); + +// start a supernova +void supernova_start(int seconds); + +// call once per frame +void supernova_process(); + +// is there a supernova active +// 0 : not active. +// 1 : player still in control. shockwave approaching. +// 2 : camera cut. player controls locked. letterbox +// 3 : tooltime. lots of particles +// 4 : player is effectively dead. fade to white. stop simulation +// 5 : give dead popup +int supernova_active(); + +// time left before the supernova hits +float supernova_time_left(); + +// pct complete the supernova (0.0 to 1.0) +float supernova_pct_complete(); + +// if the camera should cut to the "you-are-toast" cam +int supernova_camera_cut(); + +// get view params from supernova +void supernova_set_view(vector *eye_pos, matrix *eye_orient); + +#endif + diff --git a/include/sw_force.h b/include/sw_force.h new file mode 100644 index 0000000..0f17c58 --- /dev/null +++ b/include/sw_force.h @@ -0,0 +1,477 @@ +/**************************************************************************** + + MODULE: SW_Force.H + Tab settings: 5 9 + + Copyright 1995, 1996, Microsoft Corporation, All Rights Reserved. + + You have a royalty-free right to use, modify, reproduce and + distribute this header Files (and/or any modified version) in + any way you find useful, provided that you agree that + Microsoft has no warranty obligations or liability for any + Application Files which are created using the header Files. + + PURPOSE: Header for SideWinder Force Feedback Joystick + and interface to DirectInput Force Feedback API + + + Author(s): Name: + ---------- ---------------- + + + Revision History: + ----------------- + Version Date Author Comments + 1.0 24-Mar-97 MEA original + +****************************************************************************/ +#ifndef _SW_Force_SEEN +#define _SW_Force_SEEN +#include +#include +#include +#include +#define INC_OLE2 +#include +#include +#include +#undef INITGUIDS + +#include "sw_guid.hpp" +#include "vdinput.h" +#include "sw_error.hpp" + +/* +#include "sw_guid.h" +#include "vdinput.h" +#include "sw_error.h" +*/ + + +// +// --- Defines and macros for making DirectInput FF a little easier to work +// with. +// +#define SINE 1 +#define COSINE 2 +#define SQUARE_HIGH 3 +#define SQUARE_LOW 4 +#define TRIANGLE_UP 5 +#define TRIANGLE_DOWN 6 +#define SAWTOOTH_UP 7 +#define SAWTOOTH_DOWN 8 +#define RAMP_UP 9 +#define RAMP_DOWN 10 +#define SPRING 11 +#define INERTIA 12 +#define DAMPER 13 +#define FRICTION 14 +#define WALL 15 + +#define HZ_TO_uS(HZ) ((int)(1000000.0/(double)(HZ) + 0.5)) +#define uS_TO_HZ(uS) (max(1,(int)((double)(uS)/1000000.0 + 0.5))) + +#ifndef X_AXIS +#define X_AXIS 1 +#endif + +#ifndef Y_AXIS +#define Y_AXIS 2 +#endif + +//--------------------------------------------------------------------------- +// Function prototype declarations C-callable +//--------------------------------------------------------------------------- +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT SWFF_OpenDefaultFFJoystick( + IN HWND hWnd, + OUT LPDIRECTINPUT* ppDI, + OUT LPDIRECTINPUTDEVICE2* ppDIDevice2); + +HRESULT SWFF_OpenDefaultFFJoystickEx( + IN HWND hWnd, + IN HINSTANCE hInstance, + OUT LPDIRECTINPUT* ppDI, + OUT LPDIRECTINPUTDEVICE2* ppDIDevice, + DWORD dwFlags); + +HRESULT SWFF_DestroyEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN LPDIRECTINPUTEFFECT pDIEffect); + +HRESULT SWFF_DestroyAllEffects( + IN LPDIRECTINPUTDEVICE2 pDIDevice); + +HRESULT SWFF_SetGain( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN DWORD dwGain); + +HRESULT SWFF_SetDirection( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN DWORD dwAngle); + +HRESULT SWFF_SetDuration( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN DWORD dwDuration); + +HRESULT SWFF_SetDirectionGain( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN DWORD dwAngle, + IN DWORD dwMag); + +HRESULT SWFF_PutRawForce( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN LONG lMagnitude, + IN DWORD dwDirection); + +HRESULT SWFF_PutRawAxisForce( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN LONG lMagnitude); + +HRESULT SWFF_CreateRawForceEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT * ppDIEffect, + IN LONG lMagnitude, + IN DWORD dwDirection); + +HRESULT SWFF_CreateRawAxisForceEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT * ppDIEffect, + IN LONG lMagnitude, + IN DWORD dwAxis); + +HRESULT SWFF_CreateROMEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT * ppDIEffect, + IN REFGUID refGUID, + IN DWORD dwDuration, + IN DWORD dwGain, + IN DWORD dwDirection, + IN LONG lButton); + +HRESULT SWFF_CreatePeriodicEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwType, + IN DWORD dwDuration, + IN DWORD dwPeriod, + IN DWORD dwDirection, + IN DWORD dwMagnitude, + IN LONG lOffset, + IN DWORD dwAttackTime, + IN DWORD dwAttackLevel, + IN DWORD dwFadeTime, + IN DWORD dwFadeLevel, + IN LONG lButton); + +HRESULT SWFF_CreateSpringEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN LONG lKx, + IN LONG lCenterX, + IN LONG lKy, + IN LONG lCenterY, + IN LONG lButton); + +HRESULT SWFF_CreateDamperEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN LONG lBx, + IN LONG lV0x, + IN LONG lBy, + IN LONG lV0y, + IN LONG lButton); + +HRESULT SWFF_CreateInertiaEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN LONG lMx, + IN LONG lA0x, + IN LONG lMy, + IN LONG lA0y, + IN LONG lButton); + +HRESULT SWFF_CreateFrictionEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN LONG lFx, + IN LONG lFy, + IN LONG lButton); + +HRESULT SWFF_CreateConditionEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwType, + IN DWORD dwDuration, + IN LONG lXCoefficient, + IN LONG lXOffset, + IN LONG lYCoefficient, + IN LONG lYOffset, + IN LONG lButton); + +HRESULT SWFF_CreateRampEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN DWORD dwDirection, + IN LONG lStart, + IN LONG lEnd, + IN DWORD dwAttackTime, + IN DWORD dwAttackLevel, + IN DWORD dwFadeTime, + IN DWORD dwFadeLevel, + IN LONG lButtonMask); + +HRESULT SWFF_CreateConstantForceEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN DWORD dwDirection, + IN LONG lMagnitude, + IN DWORD dwAttackTime, + IN DWORD dwAttackLevel, + IN DWORD dwFadeTime, + IN DWORD dwFadeLevel, + IN LONG lButton); + +HRESULT SWFF_CreateWallEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN DWORD dwDirection, + IN DWORD dwDistance, + IN BOOL bInner, + IN LONG lWallCoefficient, + IN LONG lButton); + +HRESULT SWFF_CreateVFXEffectFromFile( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const TCHAR *pszFileName); + +HRESULT SWFF_CreateVFXEffectFromFileEx( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const TCHAR *pszFileName, + IN DWORD dwDuration, + IN DWORD dwGain, + IN DWORD dwDirection); + +HRESULT SWFF_CreateVFXEffectFromBuffer( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const LPVOID pBuffer, + IN DWORD dwBufferSize); + +HRESULT SWFF_CreateVFXEffectFromBufferEx( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const LPVOID pBuffer, + IN DWORD dwBufferSize, + IN DWORD dwDuration, + IN DWORD dwGain, + IN DWORD dwDirection); + +HRESULT SWFF_CreateDIEffectFromFile( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const TCHAR *pszFileName); + +HRESULT SWFF_CreateDIEffectFromFileEx( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT** pppDIEffect, + IN OUT PDWORD pdwEffectCount, + IN const TCHAR *pszFileName, + IN OUT void** ppUDBuffer, + IN OUT PDWORD pdwOutFlags); + +HRESULT SWFF_CreateDIEffectFromBuffer( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const LPVOID pBuffer, + IN DWORD dwBufferSize); + +HRESULT SWFF_CreateDIEffectFromBufferEx( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT** pppDIEffect, + IN OUT PDWORD pdwEffectCount, + IN const LPVOID pBuffer, + IN DWORD dwBufferSize, + IN OUT void** ppUDBuffer, + IN OUT PDWORD pdwOutFlags); + +BOOL SWFF_RegisterVFXObject(LPCTSTR pszVFXPath); + +BOOL SWFF_GetJoyData( + IN int nJoyID, + IN OUT JOYINFOEX * pjix, + OUT char *pszErr); +/* +HRESULT SWFF_GetJoyData2( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIJOYSTATE pjs); +*/ +void SWFF_ErrorCodeToString( + IN HRESULT hResult, + OUT TCHAR * pszCodeString); + + +#ifdef __cplusplus +} +#endif + +// +// --- IVFX Interface prototypes +// +#ifndef PPVOID +typedef LPVOID * PPVOID; +#endif //PPVOID + +typedef struct IVFX *PVFX; +typedef struct IVFX **PPVFX; + +#define VFXCE_CREATE_SINGLE 0x00001 +#define VFXCE_CREATE_MULTIPLE 0x00002 +#define VFXCE_CALC_BUFFER_SIZE 0x00004 +#define VFXCE_CALC_EFFECT_COUNT 0x00008 +#define VFXCE_CONCATENATE 0x00010 +#define VFXCE_SUPERIMPOSE 0x00020 + + +#undef INTERFACE +#define INTERFACE IVFX +DECLARE_INTERFACE_(IVFX, IUnknown) +{ + //IUnknown members + STDMETHOD(QueryInterface) (THIS_ REFIID, PPVOID) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + //IVFX Interface members + // General Methods + STDMETHOD_(HRESULT, CreateEffectFromFile) ( + THIS_ + LPDIRECTINPUTDEVICE2, + LPDIRECTINPUTEFFECT*, + LPDWORD, + const TCHAR*, + LPVOID, + LPDWORD, + DWORD, + LPDWORD) PURE; + + STDMETHOD_(HRESULT, CreateEffectFromBuffer) ( + THIS_ + LPDIRECTINPUTDEVICE2, + LPDIRECTINPUTEFFECT*, + LPDWORD, + PVOID, + DWORD, + LPVOID, + LPDWORD, + DWORD, + LPDWORD) PURE; +}; + +// +// --- SideWinder specific +// +#define DEFAULT_ROM_EFFECT_GAIN 100 // Set dwGain to this for Default + // ROM Effect gain +#define DEFAULT_ROM_EFFECT_DURATION 1000 // Set dwDuration to this for Default + // ROM Effect Duration +#define DEFAULT_ROM_EFFECT_OUTPUTRATE 1000 // Set dwSampleRate to this for + // Default ROM Effect output rate + +#define MIN_ANGLE 0 +#define MAX_ANGLE 36000 +#define MIN_FORCEOUTPUTRATE 1 +#define MIN_GAIN 1 +#define MAX_GAIN 10000 +#define MAX_FORCE 10000 +#define MIN_FORCE -10000 +#define MIN_TIME_PERIOD 1 +#define MAX_TIME_PERIOD 4294967296L // 4096 * 10^^6 usecs + +#define SCALE_GAIN 100 // DX is +/- 10000 +#define SCALE_TIME 1000 // DX is in microseconds +#define SCALE_POSITION 100 // DX is +/- 10000 +#define SCALE_CONSTANTS 100 // DX is +/- 10000 +#define SCALE_DIRECTION 100 // DX is 0 to 35900 + +// +// --- Default Values +// +#define DEFAULT_OFFSET 0 +#define DEFAULT_ATTACK_LEVEL 0 +#define DEFAULT_ATTACK_TIME 0 +#define DEFAULT_SUSTAIN_LEVEL 10000 +#define DEFAULT_FADE_LEVEL 0 +#define DEFAULT_FADE_TIME 0 + + +// +// The following are Type Specific parameters structures for SideWinder +// + +// +// --- WALL Effect +// +#define WALL_INNER 0 // Wall material:from center to Wall Distance +#define WALL_OUTER 1 // Wall material:greater than Wall Distance + +typedef struct _BE_WALL_PARAM { + ULONG m_Bytes; // Size of this structure + ULONG m_WallType; // WALL_INNER or WALL_OUTER + LONG m_WallConstant; // in +/- 10000% + ULONG m_WallAngle; // 0, 9000, 18000, 27000 + ULONG m_WallDistance; // Distance from Wall face normal to center. 0 to 10000 +} BE_WALL_PARAM, *PBE_WALL_PARAM; + +// +// --- EF_VFX_EFFECT = { FRC file effects } +// +// Subtypes: none + +#define VFX_FILENAME 0L +#define VFX_BUFFER 1L + +#define DEFAULT_VFX_EFFECT_GAIN 10000 // set dwGain to this for default gain +#define DEFAULT_VFX_EFFECT_DIRECTION 0 // set polar direction to this for default direction +#define DEFAULT_VFX_EFFECT_DURATION 1000 // set dwDuration to this for default duration + +typedef struct _VFX_PARAM +{ + ULONG m_Bytes; // Size of this structure + ULONG m_PointerType; // VFX_FILENAME or VFX_BUFFER + ULONG m_BufferSize; // number of bytes in buffer (if VFX_BUFFER) + PVOID m_pFileNameOrBuffer; // file name to open +} VFX_PARAM, *PVFX_PARAM; + +typedef struct { + DIEFFECT DIEffectStruct; + DICONDITION DIConditionStruct[2]; + LONG rglDirection[2]; +} di_condition_effect_struct; + +HRESULT SWFF_CreateConditionEffectStruct( + di_condition_effect_struct *ptr, + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwType, + IN DWORD dwDuration, + IN LONG lXCoefficient, + IN LONG lXOffset, + IN LONG lYCoefficient, + IN LONG lYOffset, + IN LONG lButton); + +#endif // of ifdef _SW_Force_SEEN + diff --git a/include/swarm.h b/include/swarm.h new file mode 100644 index 0000000..fa6d2c0 --- /dev/null +++ b/include/swarm.h @@ -0,0 +1,60 @@ +/* + * $Logfile: /Freespace2/code/Weapon/Swarm.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for managing swarm missiles + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 5/20/99 7:00p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 4 1/29/99 2:25p Andsager + * Added turret_swarm_missiles + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 3 2/26/98 10:08p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 2 8/10/97 6:16p Lawrance + * split off swarm missile code into a separate file + * + * $NoKeywords: $ + */ + + +#ifndef __FREESPACE_SWARM_H__ +#define __FREESPACE_SWARM_H__ + +#include "object.h" +#include "cfile.h" +#include "ship.h" + +#define SWARM_DEFAULT_NUM_MISSILES_FIRED 4 // number of swarm missiles that launch when fired + +void swarm_level_init(); +void swarm_delete(int index); +int swarm_create(); +void swarm_update_direction(object *objp, float frametime); +void swarm_maybe_fire_missile(int shipnum); + +int turret_swarm_create(); +void turret_swarm_delete(int i); +void turret_swarm_set_up_info(int parent_objnum, ship_subsys *turret, int turret_weapon_class); +void turret_swarm_check_validity(); + +#endif /* __FREESPACE_SWARM_H__ */ + diff --git a/include/systemvars.h b/include/systemvars.h new file mode 100644 index 0000000..637572e --- /dev/null +++ b/include/systemvars.h @@ -0,0 +1,339 @@ +/* + * $Logfile: /Freespace2/code/GlobalIncs/SystemVars.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Variables and constants common to FreeSpace and Fred. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 11 6/22/99 7:03p Dave + * New detail options screen. + * + * 10 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 9 6/14/99 10:45a Dave + * Made beam weapons specify accuracy by skill level in the weapons.tbl + * + * 8 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * 7 3/29/99 6:17p Dave + * More work on demo system. Got just about everything in except for + * blowing ships up, secondary weapons and player death/warpout. + * + * 6 3/28/99 5:58p Dave + * Added early demo code. Make objects move. Nice and framerate + * independant, but not much else. Don't use yet unless you're me :) + * + * 5 3/28/99 12:37p Dave + * Tentative beginnings to warpin effect. + * + * 4 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 3 10/07/98 6:27p Dave + * Globalized mission and campaign file extensions. Removed Silent Threat + * special code. Moved \cache \players and \multidata into the \data + * directory. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 37 9/21/98 8:46p Dave + * Put in special check in fred for identifying unknown ships. + * + * 36 8/17/98 5:07p Dave + * First rev of corkscrewing missiles. + * + * 35 5/13/98 11:34p Mike + * Model caching system. + * + * 34 5/09/98 4:52p Lawrance + * Implement padlock view (up/rear/left/right) + * + * 33 4/01/98 5:34p John + * Made only the used POFs page in for a level. Reduced some interp + * arrays. Made custom detail level work differently. + * + * 32 3/31/98 5:18p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * + * 31 3/30/98 2:38p Mike + * Add asteroid_density to detail level support. + * No force explosion damage in training missions. + * Make cargo deathrolls shorter. + * + * 30 3/23/98 5:19p John + * Upped number of default detail levels to 4 + * + * 29 3/22/98 2:41p John + * Added lighting into the detail structure. + * + * 28 3/22/98 11:06a John + * Changed default detail levels + * + * 27 3/22/98 11:02a John + * Made a bunch of the detail levels actually do something + * + * 26 3/22/98 9:53a John + * Added in first stage of new detail level stuff + * + * 25 3/17/98 3:53p Lawrance + * Fix warpout in chase view + * + * 24 2/28/98 7:03p Lawrance + * get slew working in any view + * + * 23 2/26/98 12:33a Lawrance + * Added back slew mode, lots of changes to external and chase views. + * + * 22 2/10/98 9:06a John + * Added variables for restoring + * + * 21 2/03/98 9:25p John + * Upgraded Direct3D code to new version 5.0 code. Separated the D3D code + * more. Added a global variable D3D_enabled flag. + * + * 20 1/13/98 2:20p John + * Added code to load palette based on hud color. Added code to turn off + * nebulas using detail. Added code in WinMain to time out after waiting + * too long for window creation. + * + * 19 12/19/97 12:03p Allender + * Added GM_CAMPAIGN_MODE to indicate when player is in a campaign or not. + * Started adding FreeSpace support for carrying of ship/weapon types + * across missions in a campaign. + * + * 18 12/16/97 6:20p Hoffoss + * Added more debugging code for demos, and fixed a bug in demo + * recording/playback. + * + * 17 12/05/97 3:46p John + * made ship thruster glow scale instead of being an animation. + * + * 16 9/22/97 5:12p Dave + * Added stats transfer game _mode_. Started work on multiplayer chat + * screens for weapon and ship select + * + * 15 8/04/97 5:28p Dave + * Moved Game bit GM_GAME_HOST to a netinfo flag NETINFO_FLAG_GAME_HOST + * + * 14 8/04/97 4:37p Dave + * Added GM_STANDALONE_SERVER and GM_GAME_HOST game mode bits + * + * 13 8/04/97 10:21a Dave + * Added Is_standalone global var + * + * 12 7/29/97 10:59a Dave + * Added demo recording and demo playback bit flags + * + * 11 6/24/97 6:21p John + * added detail flags. + * sped up motion debris system a bit. + * + * 10 6/09/97 10:38a Allender + * added GAME_MODE_IN_GAME flag. Probably to be used only for multiplayer + * + * 9 5/07/97 1:44p Mike + * Add viewing from other ships. + * + * 8 4/16/97 3:24p Mike + * New Camera system. + * + * 7 4/15/97 11:28p Mike + * External view system + * + * 6 4/15/97 4:00p Mike + * Intermediate checkin caused by getting other files. Working on camera + * slewing system. + * + * 5 4/11/97 5:02p Mike + * Improve death sequence, clean up sequencing. + * + * 4 4/08/97 8:47a John + * Added a global varible for detail level + * + * 3 4/02/97 6:03p Mike + * Make dying work through event driven code. + * + * 2 4/01/97 11:07p Mike + * Clean up game sequencing functions. Get rid of Multiplayer and add + * Game_mode. Add SystemVars.cpp + * + * 1 4/01/97 10:59p Mike + * + * $NoKeywords: $ + */ + +#ifndef _SYSTEMVARS_H +#define _SYSTEMVARS_H + +#include "math.h" +#include "pstypes.h" + +#define GM_MULTIPLAYER (1 << 0) +#define GM_NORMAL (1 << 1) +#define GM_DEAD_DIED (1 << 2) // Died, waiting to blow up. +#define GM_DEAD_BLEW_UP (1 << 3) // Blew up. +#define GM_DEAD_ABORTED (1 << 4) // Player pressed a key, aborting death sequence. +#define GM_IN_MISSION (1 << 5) // Player is actually in the mission -- not at a pre-mission menu + +#define GM_DEAD (GM_DEAD_DIED | GM_DEAD_BLEW_UP | GM_DEAD_ABORTED) + +#define GM_STANDALONE_SERVER (1 << 8) +#define GM_STATS_TRANSFER (1 << 9) // in the process of stats transfer +#define GM_CAMPAIGN_MODE (1 << 10) // are we currently in a campaign. + +#define GM_DEMO_RECORD (1 << 11) // recording a demo +#define GM_DEMO_PLAYBACK (1 << 12) // playing a demo back +#define GM_DEMO (GM_DEMO_RECORD | GM_DEMO_PLAYBACK) // true whenever a demo is being recorded or played back + +#define VM_EXTERNAL (1 << 0) // Set if not viewing from player position. +#define VM_SLEWED (1 << 1) // Set if viewer orientation is slewed. +#define VM_DEAD_VIEW (1 << 2) // Set if viewer is watching from dead view. +#define VM_CHASE (1 << 3) // Chase view. +#define VM_OTHER_SHIP (1 << 4) // View from another ship. +#define VM_EXTERNAL_CAMERA_LOCKED (1 << 5) // External camera is locked in place (ie controls move ship not camera) +#define VM_WARP_CHASE (1 << 6) // View while warping out (form normal view mode) +#define VM_PADLOCK_UP (1 << 7) +#define VM_PADLOCK_REAR (1 << 8) +#define VM_PADLOCK_LEFT (1 << 9) +#define VM_PADLOCK_RIGHT (1 << 10) +#define VM_WARPIN_ANCHOR (1 << 11) // special warpin camera mode + +#define VM_PADLOCK_ANY (VM_PADLOCK_UP|VM_PADLOCK_REAR|VM_PADLOCK_LEFT|VM_PADLOCK_RIGHT) + + +typedef struct vei { + angles_t angles; // Angles defining viewer location. + float distance; // Distance from which to view, plus 2x radius. +} vei; + +typedef struct vci { + angles_t angles; + float distance; // Distance from which to view, plus 3x radius +} vci; + +extern fix Missiontime; +extern fix Frametime; +extern int Framecount; + +extern int Game_mode; + +extern int Viewer_mode; +extern int Rand_count; + +extern int Game_restoring; // If set, this means we are restoring data from disk + +// The detail level. Anything below zero draws simple models earlier than it +// should. Anything above zero draws higher detail models longer than it should. +// -2=lowest +// -1=low +// 0=normal (medium) +// 1=high +// 2=extra high +extern int Game_detail_level; + +#define DETAIL_DEFAULT (0xFFFFFFFF) + +#define DETAIL_FLAG_STARS (1<<0) // draw the stars +#define DETAIL_FLAG_NEBULAS (1<<1) // draw the motion debris +#define DETAIL_FLAG_MOTION (1<<2) // draw the motion debris +#define DETAIL_FLAG_PLANETS (1<<3) // draw planets +#define DETAIL_FLAG_MODELS (1<<4) // draw models not as blobs +#define DETAIL_FLAG_LASERS (1<<5) // draw lasers not as pixels +#define DETAIL_FLAG_CLEAR (1<<6) // clear screen background after each frame +#define DETAIL_FLAG_HUD (1<<7) // draw hud stuff +#define DETAIL_FLAG_FIREBALLS (1<<8) // draw fireballs +#define DETAIL_FLAG_COLLISION (1<<9) // use good collision detection + + +extern uint Game_detail_flags; + +extern angles_t Viewer_slew_angles; +extern vei Viewer_external_info; +extern vci Viewer_chase_info; + +extern int Is_standalone; +extern int Interface_framerate; // show interface framerate during flips +extern int Interface_last_tick; // last timer tick on flip + +// for notifying players of unknown ship types +extern int Fred_found_unknown_ship_during_parsing; + +#define NOISE_NUM_FRAMES 15 + +// Noise numbers go from 0 to 1.0 +extern float Noise[NOISE_NUM_FRAMES]; + + +// If true, then we are using Direct3D hardware. This is used for game type stuff +// that changes when you're using hardware. +extern int D3D_enabled; + +// game skill levels +#define NUM_SKILL_LEVELS 5 + +//==================================================================================== +// DETAIL LEVEL STUFF +// If you change any of this, be sure to increment the player file version +// in Freespace\ManagePilot.cpp and change Detail_defaults in SystemVars.cpp +// or bad things will happen, I promise. +//==================================================================================== + +#define MAX_DETAIL_LEVEL 4 // The highest valid value for the "analog" detail level settings + +// If you change this, update player file in ManagePilot.cpp +typedef struct detail_levels { + + int setting; // Which default setting this was created from. 0=lowest... NUM_DEFAULT_DETAIL_LEVELS-1, -1=Custom + + // "Analogs" + int nebula_detail; // 0=lowest detail, MAX_DETAIL_LEVEL=highest detail + int detail_distance; // 0=lowest MAX_DETAIL_LEVEL=highest + int hardware_textures; // 0=max culling, MAX_DETAIL_LEVEL=no culling + int num_small_debris; // 0=min number, MAX_DETAIL_LEVEL=max number + int num_particles; // 0=min number, MAX_DETAIL_LEVEL=max number + int num_stars; // 0=min number, MAX_DETAIL_LEVEL=max number + int shield_effects; // 0=min, MAX_DETAIL_LEVEL=max + int lighting; // 0=min, MAX_DETAIL_LEVEL=max + + // Booleans + int targetview_model; // 0=off, 1=on + int planets_suns; // 0=off, 1=on + int weapon_extras; // extra weapon details. trails, glows +} detail_levels; + +// Global values used to access detail levels in game and libs +extern detail_levels Detail; + +#define NUM_DEFAULT_DETAIL_LEVELS 4 // How many "predefined" detail levels there are + +// Call this with: +// 0 - lowest +// NUM_DEFAULT_DETAIL_LEVELS - highest +// To set the parameters in Detail to some set of defaults +void detail_level_set(int level); + +// Returns the current detail level or -1 if custom. +int current_detail_level(); + +//==================================================================================== +// Memory stuff from WinDebug.cpp +extern int TotalRam; +void windebug_memwatch_init(); + +#endif + diff --git a/include/techmenu.h b/include/techmenu.h new file mode 100644 index 0000000..90f5e07 --- /dev/null +++ b/include/techmenu.h @@ -0,0 +1,89 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/TechMenu.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for code that controls the Tech Room menu + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 5 8/23/99 11:20a Jefff + * Increased TECH_INTEL_DESC_LEN + * + * 4 8/10/99 3:45p Jefff + * Put the smack down on the tech room. Its all new, but tastefully done. + * + * 3 10/13/98 2:47p Andsager + * Remove reference to Tech_shivan_species_avail + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 11 5/05/98 1:49a Lawrance + * Add in missing help overlays + * + * 10 4/23/98 10:42p Hoffoss + * Added species section to techroom. Still missing description text, + * because this hasn't been created yet. + * + * 9 4/14/98 10:24p Hoffoss + * Started on new tech room. + * + * 8 4/02/98 5:40p Hoffoss + * Added the Load Mission screen to FreeSpace. + * + * 7 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 6 11/21/96 7:14p Lawrance + * converted menu code to use a file (menu.tbl) to get the data for the + * menu + * + * 5 11/13/96 4:02p Lawrance + * complete over-haul of the menu system and the states associated with + * them + * + * 4 11/13/96 8:32a Lawrance + * streamlined menu code + * + * 3 11/06/96 8:54a Lawrance + * added revision templates, made more efficient + * + * $NoKeywords: $ + * +*/ + + +#ifndef _TECHMENU_H +#define _TECHMENU_H + +#define MAX_INTEL_ENTRIES 10 +#define TECH_INTEL_DESC_LEN 5120 + +typedef struct { + char name[32]; + char desc[TECH_INTEL_DESC_LEN]; + char anim_filename[32]; + int in_tech_db; // determines if visible in tech db or not +} intel_data; + + +extern intel_data Intel_info[MAX_INTEL_ENTRIES]; +extern int Intel_info_size; + + +// function prototypes +void techroom_init(); +void techroom_close(); +void techroom_do_frame(float frametime); +int techroom_on_ships_tab(); +void techroom_intel_init(); // called on startup so campaigns can manipulate tech room visibility + +#endif + diff --git a/include/textviewdlg.h b/include/textviewdlg.h new file mode 100644 index 0000000..cdb7a64 --- /dev/null +++ b/include/textviewdlg.h @@ -0,0 +1,37 @@ +// TextViewDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// text_view_dlg dialog + +class text_view_dlg : public CDialog +{ +// Construction +public: + void set(int ship); + text_view_dlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(text_view_dlg) + enum { IDD = IDD_TEXT_VIEW }; + CString m_edit; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(text_view_dlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(text_view_dlg) + afx_msg void OnSetfocusEdit1(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/tga.h b/include/tga.h new file mode 100644 index 0000000..9dd7e8a --- /dev/null +++ b/include/tga.h @@ -0,0 +1,21 @@ +// Tga.h: interface for the CTga class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_TGA_H__7A2F74A2_D0C9_11D2_9904_00A0CC39C0BE__INCLUDED_) +#define AFX_TGA_H__7A2F74A2_D0C9_11D2_9904_00A0CC39C0BE__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +class CTga : public CObject +{ +public: + CTga(); + virtual ~CTga(); + +}; + +#endif // !defined(AFX_TGA_H__7A2F74A2_D0C9_11D2_9904_00A0CC39C0BE__INCLUDED_) + diff --git a/include/tgautils.h b/include/tgautils.h new file mode 100644 index 0000000..bd5aa60 --- /dev/null +++ b/include/tgautils.h @@ -0,0 +1,52 @@ +/* + * $Logfile: /Freespace2/code/TgaUtils/TgaUtils.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 3/20/99 3:46p Dave + * Added support for model-based background nebulae. Added 3 new + * sexpressions. + * + * 2 12/01/98 4:46p Dave + * Put in targa bitmap support (16 bit). + * + * $NoKeywords: $ + */ + + +#ifndef __TARGA_H +#define __TARGA_H + +// -------------------- +// +// Defines +// +// -------------------- + +#define TARGA_ERROR_NONE 0 +#define TARGA_ERROR_READING 1 +#define TARGA_ERROR_WRITING 2 + +// -------------------- +// +// Prototypes +// +// -------------------- + +int targa_read_header(char *filename, int *w, int *h, int *bpp, ubyte *palette=NULL ); +int targa_read_bitmap(char *filename, ubyte *data, ubyte *palette, int dest_size ); +int targa_write_bitmap(char *filename, ubyte *data, ubyte *palette, int w, int h, int bpp); + +// The following are used by the tools\vani code. +int targa_compress(char *out, char *in, int outsize, int pixsize, int bytecount); +int targa_uncompress( ubyte *dst, ubyte *src, int bitmap_width, int bytes_per_pixel ); + +#endif // __TARGA_H + diff --git a/include/timer.h b/include/timer.h new file mode 100644 index 0000000..e9ac614 --- /dev/null +++ b/include/timer.h @@ -0,0 +1,179 @@ +/* + * $Logfile: /Freespace2/code/Io/Timer.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for timer stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 5/17/99 6:03p Dave + * Added new timing code. Put in code to allow camera speed zooming. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 11 4/14/98 2:26p Hoffoss + * Removed function prototype that doesn't exist anymore. + * + * 10 9/11/97 7:12p Hoffoss + * Added more functionality to training sexp handling code. + * + * 9 8/05/97 10:18a Lawrance + * my_rand() being used temporarily instead of rand() + * + * 8 7/29/97 5:30p Lawrance + * move gettime() from keyboard module to timer module + * + * 7 7/16/97 4:42p Mike + * Make afterburner shake viewer, not ship. + * Shake for limited time. + * Add timestamp_until() function to timer library. + * + * 6 6/26/97 12:05p Allender + * fixed timestamp_valid macro + * + * 5 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef _TIMER_H +#define _TIMER_H + +#include "pstypes.h" + +//========================================================================== +// This installs the timer services and interrupts at the rate specified by +// count_val. If 'function' isn't 0, the function pointed to by function will +// be called 'freq' times per second. Should be > 19 and anything around +// 2-3000 is gonna start slowing down the system. Count_val should be +// 1,193,180 divided by your target frequency. Use 0 for the normal 18.2 Hz +// interrupt rate. + +#define TIMER_FREQUENCY 1193180 + +extern void timer_init(); +extern void timer_close(); +extern void timer_set_rate(int count_val); +extern void timer_set_function( void * function ); + +//========================================================================== +// These functions return the time since the timer was initialized in +// some various units. The total length of reading time varies for each +// one. They will roll around after they read 2^32. +// There are milliseconds, milliseconds times 10, milliseconds times 100, +// and microseconds. They time out after 1000 hrs, 100 hrs, 10 hrs, and +// 1 hr, respectively. + +extern fix timer_get_fixed_seconds(); // Rolls about every 9 hours... +extern fix timer_get_fixed_secondsX(); // Assume interrupts already disabled +extern fix timer_get_approx_seconds(); // Returns time since program started... accurate to 1/120th of a second +extern int timer_get_milliseconds(); // +extern int timer_get_microseconds(); + +//========================================================================== +// Use to access the BIOS ticker... ie... i = TICKER +void timer_delay(fix seconds); + + +//================================================================= +//================================================================= +// T I M E S T A M P F U N C T I O N S +//================================================================= +//================================================================= + +// NEVER USE THIS DIRECTLY!!! IF YOU REALLY NEED IT, THEN: +// call timestamp(0) and use TIMESTAMP_FREQUENCY to scale it. +extern int timestamp_ticker; + +// You shouldn't use the output of timestamp() directly, +// but if you have to, use the TIMESTAMP_FREQUENCY to +// scale it correctly. +#define TIMESTAMP_FREQUENCY 1000 + +// Call this at least every 600 hours, I would +// say at the start of each level should do it. +extern void timestamp_reset(); + +// Call this once every frame with the frametime. +extern void timestamp_inc(float frametime); + +// To do timing, call this with the interval you +// want to check. Then, pass this to timestamp_elapsed +// to see if delta_ms time has elapsed. If delta_ms is +// zero, the next call to timestamp_elapsed will always +// return 1. If delta_ms is less than zero, then this is +// considered an invalid timestamp and all calls to +// timestamp_elapsed will return 0. +// In other words: +// pass -1 for an invalid timestamp that will never time out +// pass 0 for a timestamp that is instantly timed out +// pass n > 0 for timestamp n milliseconds in the future. +int timestamp(int delta_ms ); + +// use this call to get the current counter value (which represents the time at the time +// this function is called). I.e. it doesn't return a count that would be in the future, +// but the count that is right now. +int timestamp(); + +// gets a timestamp randomly between a and b milliseconds in +// the future. +#define timestamp_rand(a,b) timestamp((myrand()%((b)-(a)+1))+(a)) + +// Example that makes a ship fire in 1/2 second + +// ... +// ship->next_fire = timestamp(500); +// ... +// if (fire && timestamp_elapsed(ship->next_fire)) +// fire_laser(); + +#define timestamp_elapsed( stamp ) ( (stamp!=0) ? (timestamp_ticker >= (stamp) ? 1 : 0) : 0 ) + +#define timestamp_valid(stamp) ((stamp==0) ? 0 : 1 ) + +// Returns millliseconds until timestamp will elapse. +int timestamp_until(int stamp); + +// checks if a specified time (in milliseconds) has elapsed past the given timestamp (which +// should be obtained from timestamp() or timestamp(x) with a positive x) +int timestamp_has_time_elapsed(int stamp, int time); + + +// timing functions ------------------------------------------------------------------------------- + +// start timing frame stuff +void timing_frame_start(); + +// done timing the frame +void timing_frame_stop(); + +// get the total frame time in microseconds +int timing_frame_total(); + +// time an individual event +void timing_event_start(char *event_name); + +// stop timing an event +void timing_event_stop(char *event_name); + +// get the total time for an event in microseconds +int timing_event_total(char *event_name); + +// get the percentage of total frametime for the event (0.0 to 1.0) +float timing_event_pct(char *event_name); + +// display timing +void timing_display(int x, int y); + +#endif + diff --git a/include/tmapper.h b/include/tmapper.h new file mode 100644 index 0000000..f5fce7e --- /dev/null +++ b/include/tmapper.h @@ -0,0 +1,122 @@ +/* + * $Logfile: /Freespace2/code/Graphics/TMAPPER.H $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for Tmapper.h + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 4 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 3 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 17 4/10/98 5:20p John + * Changed RGB in lighting structure to be ubytes. Removed old + * not-necessary 24 bpp software stuff. + * + * 16 4/09/98 7:58p John + * Cleaned up tmapper code a bit. Put NDEBUG around some ndebug stuff. + * Took out XPARENT flag settings in all the alpha-blended texture stuff. + * + * 15 4/09/98 4:38p John + * Made non-darkening and transparent textures work under Glide. Fixed + * bug with Jim's computer not drawing any bitmaps. + * + * 14 3/23/98 5:00p John + * Improved missile trails. Made smooth alpha under hardware. Made end + * taper. Made trail touch weapon. + * + * 13 11/21/97 11:32a John + * Added nebulas. Fixed some warpout bugs. + * + * 12 10/15/97 5:08p John + * added flag for alpha tmap + * + * 11 3/10/97 5:20p John + * Differentiated between Gouraud and Flat shading. Since we only do flat + * shading as of now, we don't need to interpolate L in the outer loop. + * This should save a few percent. + * + * 10 3/05/97 7:15p John + * took out the old z stop tmapper used for briefing. + * + * 9 12/10/96 10:37a John + * Restructured texture mapper to remove some overhead from each scanline + * setup. This gave about a 30% improvement drawing trans01.pof, which is + * a really complex model. In the process, I cleaned up the scanline + * functions and separated them into different modules for each pixel + * depth. + * + * 8 11/07/96 2:17p John + * Took out the OldTmapper stuff. + * + * 7 11/05/96 4:05p John + * Added roller. Added code to draw a distant planet. Made bm_load + * return -1 if invalid bitmap. + * + * 6 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#ifndef _TMAPPER_H +#define _TMAPPER_H + +// call this to reinit the scanline function pointers. +extern void tmapper_setup(); + +// Used to tell the tmapper what the current lighting values are +// if the TMAP_FLAG_RAMP or TMAP_FLAG_RGB are set and the TMAP_FLAG_GOURAUD +// isn't set. +void tmapper_set_light(vertex *v, uint flags); + +// DO NOT CALL grx_tmapper DIRECTLY!!!! Only use the +// gr_tmapper equivalent!!!! +extern void grx_tmapper( int nv, vertex * verts[], uint flags ); + +#define TMAP_MAX_VERTS 25 // Max number of vertices per polygon + +// Flags to pass to g3_draw_??? routines +#define TMAP_FLAG_TEXTURED (1<<0) // Uses texturing (Interpolate uv's) +#define TMAP_FLAG_CORRECT (1<<1) // Perspective correct (Interpolate sw) +#define TMAP_FLAG_RAMP (1<<2) // Use RAMP lighting (interpolate L) +#define TMAP_FLAG_RGB (1<<3) // Use RGB lighting (interpolate RGB) +#define TMAP_FLAG_GOURAUD (1<<4) // Lighting values differ on each vertex. + // If this is not set, then the texture mapper will use + // the lighting parameters in each vertex, otherwise it + // will use the ones specified in tmapper_set_?? +#define TMAP_FLAG_XPARENT (1<<5) // texture could have transparency +#define TMAP_FLAG_TILED (1<<6) // This means uv's can be > 1.0 +#define TMAP_FLAG_NEBULA (1<<7) // Must be used with RAMP and GOURAUD. Means l 0-1 is 0-31 palette entries + + +#define TMAP_HIGHEST_FLAG_BIT 7 // The highest bit used in the TMAP_FLAGS +#define TMAP_MAX_SCANLINES (1<<(TMAP_HIGHEST_FLAG_BIT+1)) + +// Add any entries that don't work for software under here: +// Make sure to disable them at top of grx_tmapper +#define TMAP_FLAG_ALPHA (1<<8) // Has an alpha component +#define TMAP_FLAG_NONDARKENING (1<<9) // RGB=255,255,255 doesn't darken + +// flags for full nebula effect +#define TMAP_FLAG_PIXEL_FOG (1<<10) // fog the polygon based upon the average pixel colors of the backbuffer behind it + +// bitmap section +#define TMAP_FLAG_BITMAP_SECTION (1<<11) + +#endif + diff --git a/include/tmapscanline.h b/include/tmapscanline.h new file mode 100644 index 0000000..3663abf --- /dev/null +++ b/include/tmapscanline.h @@ -0,0 +1,190 @@ +/* + * $Logfile: /Freespace2/code/Graphics/TmapScanline.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for tmapscanline.cpp. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 25 5/13/98 2:53p John + * Made subspace effect work under software. Had to add new inner loop to + * tmapper. Added glows to end of subspace effect. Made subspace effect + * levels use gamepalette-subspace palette. + * + * 24 4/10/98 5:20p John + * Changed RGB in lighting structure to be ubytes. Removed old + * not-necessary 24 bpp software stuff. + * + * 23 3/22/98 2:33p John + * Took out fx_v/v_right. Made fx_u etc get calculated in tmapper. + * + * 22 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 21 12/15/97 11:32a John + * New Laser Code + * + * 20 12/02/97 4:00p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 19 11/30/97 3:57p John + * Made fixed 32-bpp translucency. Made BmpMan always map translucent + * color into 255 even if you aren't supposed to remap and make it's + * palette black. + * + * 18 11/21/97 11:32a John + * Added nebulas. Fixed some warpout bugs. + * + * 17 10/16/97 10:55a John + * added tmapper to draw a monochrome alpha blended bitmap. + * + * 16 3/14/97 3:55p John + * Made tiled tmapper not always be zbuffered. + * + * 15 3/13/97 10:32a John + * Added code for tiled 256x256 textures in certain models. + * + * 14 3/05/97 7:15p John + * took out the old z stop tmapper used for briefing. + * + * 13 1/20/97 4:17p John + * + * 12 1/06/97 2:44p John + * Added in slow (but correct) zbuffering + * + * 11 12/10/96 10:37a John + * Restructured texture mapper to remove some overhead from each scanline + * setup. This gave about a 30% improvement drawing trans01.pof, which is + * a really complex model. In the process, I cleaned up the scanline + * functions and separated them into different modules for each pixel + * depth. + * + * 10 11/26/96 6:50p John + * Added some more hicolor primitives. Made windowed mode run as current + * bpp, if bpp is 8,16,or 32. + * + * 9 11/07/96 6:19p John + * Added a bunch of 16bpp primitives so the game sort of runs in 16bpp + * mode. + * + * 8 11/05/96 4:05p John + * Added roller. Added code to draw a distant planet. Made bm_load + * return -1 if invalid bitmap. + * + * 7 10/31/96 7:20p John + * Added per,tiled tmapper. Made models tile if they use 64x64 textures. + * + * 6 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + + +#ifndef _TMAPSCANLINE_H +#define _TMAPSCANLINE_H + + +typedef struct tmapper_vertex { + float sx, sy, sw, u, v, b; +} tmapper_vertex; + +typedef struct tmapper_data { + // These are filled once for each texture map being drawn. + // The inner loop cannot change any of these!! + int nv; // number of vertices + ubyte *pixptr; + bitmap *bp; + int src_offset; + uint flags; + float FixedScale; // constant for asm inner loop + float FixedScale8; // constant for asm inner loop + float One; // constant for asm inner loop + + // This are filled in by the outer loop before each scan line. + int loop_count; + tmapper_vertex l, r, dl, dr, deltas; + int lx, rx, y; + float clipped_left; // how many pixels were clipped off the left in 2d. + + // These are used internally by the assembly texture mapper. + fix fx_l, fx_l_right, fx_dl_dx; + fix fx_u, fx_v, fx_du_dx, fx_dv_dx; + float fl_dudx_wide; + float fl_dvdx_wide; + float fl_dwdx_wide; + uint dest_row_data; + int num_big_steps; + uint uv_delta[2]; + float FloatTemp; + uint Subdivisions; + uint WidthModLength; + uint BlendLookup; + uint FadeLookup; + uint DeltaU; + uint DeltaV; + uint DeltaUFrac, DeltaVFrac; + uint UFixed; + uint VFixed; + ushort FPUCW; + ushort OldFPUCW; + int InnerLooper; + uint pScreenBits; + int fx_w; + int fx_dwdx; + + uint saved_esp; + uint lookup; + +} tmapper_data; + +extern tmapper_data Tmap; + +extern void tmapscan_generic(); +extern void tmapscan_generic8(); +//extern void tmapscan_pnn(); +extern void tmapscan_pln(); +extern void tmapscan_lln(); +extern void tmapscan_flat(); +extern void tmapscan_nebula8(); +extern void tmapscan_flat_z(); + +extern void tmapscan_flat8(); +extern void tmapscan_lln8(); +extern void tmapscan_lnt8(); +extern void tmapscan_lnn8(); +extern void tmapscan_lnt8(); +extern void tmapscan_lln8_tiled(); +extern void tmapscan_llt8_tiled(); +extern void tmapscan_pln8(); +extern void tmapscan_plt8(); + +extern void tmapscan_lnaa8(); + +extern void tmapscan_pln8_tiled(); + +extern void tmapscan_lnn8_tiled_256x256(); +extern void tmapscan_pnn8_tiled_256x256_subspace(); + +extern void tmapscan_flat_gouraud(); + +extern void tmapscan_nebula8(); + +#endif + diff --git a/include/trails.h b/include/trails.h new file mode 100644 index 0000000..d5e51f8 --- /dev/null +++ b/include/trails.h @@ -0,0 +1,76 @@ +/* + * $Logfile: /Freespace2/code/Weapon/Trails.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * External defs for missile trail stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 3 11/14/98 5:33p Dave + * Lots of nebula work. Put in ship contrails. + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 3 3/23/98 5:00p John + * Improved missile trails. Made smooth alpha under hardware. Made end + * taper. Made trail touch weapon. + * + * 2 12/21/97 6:15p John + * Made a seperate system for missile trails + * + * 1 12/21/97 5:30p John + * Initial version + * + * $NoKeywords: $ + */ + +#ifndef _TRAILS_H +#define _TRAILS_H + +#include "pstypes.h" + +#define NUM_TRAIL_SECTIONS 16 + +// contrail info - similar to that for missile trails +// place this inside of info structures instead of explicit structs (eg. ship_info instead of ship, or weapon_info instead of weapon) +typedef struct trail_info { + vector pt; // offset from the object's center + float w_start; // starting width + float w_end; // ending width + float a_start; // starting alpha + float a_end; // ending alpha + float max_life; // max_life for a section + int stamp; // spew timestamp + int bitmap; // bitmap to use +} trail_info; + +// Call at start of level to reinit all missilie trail stuff +void trail_level_init(); + +// Needs to be called from somewhere to move the trails each frame +void trail_move_all(float frametime); + +// Needs to be called from somewhere to render the trails each frame +void trail_render_all(); + +// The following functions are what the weapon code calls +// to deal with trails: + +// Returns -1 if failed +int trail_create(trail_info info); +void trail_add_segment( int trail_num, vector *pos ); +void trail_set_segment( int trail_num, vector *pos ); +void trail_object_died( int trail_num ); +int trail_stamp_elapsed( int trail_num ); +void trail_set_stamp( int trail_num ); + +#endif //_TRAILS_H + diff --git a/include/trainingmenu.h b/include/trainingmenu.h new file mode 100644 index 0000000..c416128 --- /dev/null +++ b/include/trainingmenu.h @@ -0,0 +1,57 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/TrainingMenu.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for code that controls the Training menu + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 7 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 6 11/21/96 7:14p Lawrance + * converted menu code to use a file (menu.tbl) to get the data for the + * menu + * + * 5 11/13/96 4:02p Lawrance + * complete over-haul of the menu system and the states associated with + * them + * + * 4 11/13/96 8:32a Lawrance + * streamlined menu code + * + * 3 11/06/96 8:54a Lawrance + * added revision templates, made more efficient + * + * $NoKeywords: $ + * +*/ + +#ifndef _TRAININGMENU_H +#define _TRAININGMENU_H + +#define TRAINING_MENU_MAX_CHOICES 3 // keep up to date if any more choices added! + +// these are the colour values of the pixels that form the different training menu regions +#define TRAINING_MENU_TRAINING_MISSIONS_MASK 1 +#define TRAINING_MENU_REPLAY_MISSIONS_MASK 2 +#define TRAINING_MENU_RETURN_MASK 3 + +// function prototypes +// +void training_menu_init(); +void training_menu_close(); +void training_menu_do_frame(float frametime); + +#endif + diff --git a/include/ui.h b/include/ui.h new file mode 100644 index 0000000..7f35388 --- /dev/null +++ b/include/ui.h @@ -0,0 +1,1041 @@ +/* + * $Logfile: /Freespace2/code/Ui/UI.H $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for our user interface. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 20 8/16/99 9:45a Jefff + * changes to cursor management to allow a 2nd temporary cursor + * + * 19 8/11/99 3:21p Jefff + * set_bmaps clarification by daveb + * + * 18 8/11/99 12:18p Jefff + * added option to slider2 class to not force slider reset on + * set_numberItems + * + * 17 8/10/99 6:54p Dave + * Mad optimizations. Added paging to the nebula effect. + * + * 16 8/05/99 2:44p Jefff + * added disabled callback to UI_BUTTON + * + * 15 6/25/99 11:59a Dave + * Multi options screen. + * + * 14 6/22/99 7:03p Dave + * New detail options screen. + * + * 13 5/21/99 6:45p Dave + * Sped up ui loading a bit. Sped up localization disk access stuff. Multi + * start game screen, multi password, and multi pxo-help screen. + * + * 12 5/04/99 5:20p Dave + * Fixed up multiplayer join screen and host options screen. Should both + * be at 100% now. + * + * 11 5/03/99 8:33p Dave + * New version of multi host options screen. + * + * 10 4/29/99 2:15p Neilk + * fixed slider so there is an extra callback for mouse locks + * + * 9 4/16/99 5:22p Neilk + * Added UI_SLIDER2 class + * + * 8 2/21/99 6:02p Dave + * Fixed standalone WSS packets. + * + * 7 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 6 2/01/99 5:55p Dave + * Removed the idea of explicit bitmaps for buttons. Fixed text + * highlighting for disabled gadgets. + * + * 5 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 4 12/02/98 5:47p Dave + * Put in interface xstr code. Converted barracks screen to new format. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 71 5/12/98 11:59p Dave + * Put in some more functionality for Parallax Online. + * + * 70 5/11/98 5:29p Hoffoss + * Added mouse button mapped to joystick button support. + * + * 69 5/05/98 1:49a Lawrance + * Add member function to disable processing for all gadgets in a window + * + * 68 5/03/98 1:55a Lawrance + * Add function call that forces button code to skip first callback for + * highlighing + * + * 67 4/17/98 12:22a Dave + * Bumped up MAX_TOOLTIPS from 350 to 500 so Freespace will start. + * + * 66 4/14/98 5:07p Dave + * Don't load or send invalid pilot pics. Fixed chatbox graphic errors. + * Made chatbox display team icons in a team vs. team game. Fixed up pause + * and endgame sequencing issues. + * + * 65 4/14/98 4:27p Hoffoss + * Changed the way tooltips render as requested. + * + * 64 4/13/98 4:30p Hoffoss + * Fixed a bunch of stupid little bugs in options screen. Also changed + * forced_draw() to work like it used to. + * + * 63 4/12/98 2:09p Dave + * Make main hall door text less stupid. Make sure inputbox focus in the + * multi host options screen is managed more intelligently. + * + * 62 4/10/98 5:36p Dave + * Put in user notification of illegal values in multi host options + * screen. Fixed server respawn ship class problem. + * + * 61 4/10/98 4:51p Hoffoss + * Made several changes related to tooltips. + * + * 60 4/09/98 7:14p Hoffoss + * Did some cool changes for tooltips. + * + * 59 4/09/98 5:57p Hoffoss + * Added custom tooltip handler functionality for callback. + * + * 58 4/09/98 12:12p Mike + * Separate versioning for demo and full versions. + * Fix inputbox bugs. + * + * 57 3/30/98 6:24p Hoffoss + * Added the tooltip (foreign language translation of text) system. + * + * 56 3/23/98 5:48p Hoffoss + * Improved listbox handling. Most notibly the scrollbar arrows work now. + * + * 55 3/22/98 10:50p Lawrance + * Allow sliders to not have end-buttons. + * + * 54 3/10/98 4:06p Hoffoss + * Removed unused variables. + * + * 53 3/09/98 5:55p Dave + * Fixed stats to take asteroid hits into account. Polished up UI stuff in + * team select. Finished up pilot info popup. Tracked down and fixed + * double click bug. + * + * 52 3/02/98 3:54p Lawrance + * make button draw function public + * + * 51 2/26/98 4:21p Dave + * More robust multiplayer voice. + * + * 50 2/11/98 6:24p Hoffoss + * Fixed bug where disabled and hidden buttons give failed sound when + * pressed. Shouldn't happen when they are hidden. + * + * 49 2/09/98 10:03a Hoffoss + * Made first_time variable public so I can clear the stupid thing in code + * that I want it to be cleared in. + * + * 48 1/26/98 6:28p Lawrance + * Add ability to for a button press event externally. + * + * 47 1/23/98 5:43p Dave + * Finished bringing standalone up to speed. Coded in new host options + * screen. + * + * 46 1/16/98 7:57p Lawrance + * support animating input box cursor + * + * 45 1/15/98 5:10p Allender + * ton of interface changes. chatbox in multiplayer now behaves + * differently than before. It's always active in any screen that uses + * it. Only non-printatble characters will get passed back out from + * chatbox + * + * 44 1/14/98 6:44p Hoffoss + * Massive changes to UI code. A lot cleaner and better now. Did all + * this to get the new UI_DOT_SLIDER to work properly, which the old code + * wasn't flexible enough to handle. + * + * 43 1/02/98 9:11p Lawrance + * Add button_hot() function + * + * 42 12/22/97 5:08p Hoffoss + * Changed inputbox class to be able to accept only certain keys, changed + * pilot screens to utilize this feature. Added to assert with pilot file + * saving. + * + * 41 12/11/97 8:15p Dave + * Put in network options screen. Xed out olf protocol selection screen. + * + * 40 12/10/97 3:14p Dave + * Added an overloaded set_mask_bmap(int) function for the UI_WINDOW + * + * 39 12/08/97 6:22p Lawrance + * blink cursor on inputbox + * + * 38 12/06/97 4:27p Dave + * Another load of interface and multiplayer bug fixes. + * + * 37 11/25/97 3:51p Hoffoss + * Changed edit background rect position slightly. + * + * 36 11/19/97 8:32p Hoffoss + * Changed UI buttons so they go back to unpressed when they are disabled. + * + * 35 10/29/97 7:25p Hoffoss + * Added crude support for UI button double click checking. + * + * 34 10/24/97 10:58p Hoffoss + * Made some changes to the UI code to do some things I need it to do. + * + * 33 10/01/97 4:40p Lawrance + * allow process() to have key input + * + * 32 9/18/97 10:31p Lawrance + * allow listbox to change text + * + * 31 9/09/97 4:32p Dave + * Added sel_changed() function to UI_LISTBOX to check is the selection + * has changed. + * + * 30 9/07/97 10:05p Lawrance + * add icon class + * + * 29 8/30/97 12:23p Lawrance + * add button function to reset the status of a button + * + * 28 8/29/97 7:33p Lawrance + * move cleanup code from destructor to destroy() method + * + * 27 8/24/97 5:25p Lawrance + * improve drawing of buttons + * + * 26 8/21/97 12:13p Dave + * Made it possible for input box to ignore esc to lose focus. + * + * 25 8/19/97 1:27p Dave + * Modified input box to allow character limitation by pixel width. + * Changed list box so that you can create an empty box and add items as + * you need to. + * + * 24 8/18/97 5:28p Lawrance + * integrating sounds for when mouse goes over an active control + * + * 23 8/17/97 2:42p Lawrance + * add code to have selected bitmap for buttons linger for a certain time + * + * 22 8/15/97 8:21p Dave + * Modified UI_INPUTBOX so that it is possible to draw it invisibly. That + * is, only the text is displayed. + * + * 21 8/14/97 5:23p Dave + * Added clear_all_items() to the UI_LISTBOX + * + * 20 6/13/97 5:51p Lawrance + * add in support for repeating buttons + * + * 19 6/11/97 1:32p Allender + * externed sort_filelist + * + * 18 5/26/97 10:26a Lawrance + * get slider control working 100% + * + * 17 5/22/97 5:36p Lawrance + * allowing custom art for scrollbars + * + * 16 5/21/97 11:07a Lawrance + * integrate masks and custom bitmaps + * + * 15 4/28/97 2:19p Lawrance + * added clear_focus() function + * + * 14 4/22/97 10:11a John + * Added checkbox lists to listboxes + * + * 13 4/15/97 3:47p Allender + * moved type selection of list box items into actual UI code. Made it + * behave more like windows listboxes do + * + * 12 1/28/97 4:58p Lawrance + * allowing hidden UI components + * + * 11 12/23/96 2:42p Lawrance + * allowing keys to select list box items in the mission load screen + * + * 10 12/04/96 3:00p John + * Added code to allow adjusting of HUD colors and saved it to the player + * config file. + * + * 9 12/03/96 3:46p Lawrance + * added ability to set contents of input box + * + * 8 12/03/96 11:29a John + * Made scroll buttons on listbox scroll once, then delay, then repeat + * when the buttons are held down. + * + * 7 12/02/96 2:17p John + * Made right button drag UI gadgets around and + * Ctrl+Shift+Alt+F12 dumps out where they are. + * + * 6 12/01/96 3:48a Lawrance + * added function set_current to UI_LISTBOX + * + * 5 11/29/96 6:08p Lawrance + * enabled check-boxes to be set to a specific value outside of the + * create() function + * + * 4 11/21/96 10:58a John + * Started adding code to drag buttons. + * + * 3 11/18/96 4:28p Jasen + * making class member process return an int + * + * 2 11/15/96 11:42a John + * + * 1 11/14/96 6:55p John + * + * $NoKeywords: $ + */ + +#ifndef _UI_H +#define _UI_H + +#include "2d.h" + +#define UI_KIND_BUTTON 1 +#define UI_KIND_KEYTRAP 2 +#define UI_KIND_CHECKBOX 3 +#define UI_KIND_RADIO 4 +#define UI_KIND_SCROLLBAR 5 +#define UI_KIND_LISTBOX 6 +#define UI_KIND_INPUTBOX 7 +#define UI_KIND_SLIDER 8 +#define UI_KIND_ICON 9 +#define UI_KIND_DOT_SLIDER 10 +#define UI_KIND_SLIDER2 11 +#define UI_KIND_DOT_SLIDER_NEW 12 + +#define MAX_KEY_BUFFER 32 // for listboxes + +#define MAX_BMAPS_PER_GADGET 15 + +#define UI_INPUTBOX_FLAG_INVIS (1 << 0) // don't draw the input box boarders +#define UI_INPUTBOX_FLAG_KEYTHRU (1 << 1) // pass all keypresses through to parent +#define UI_INPUTBOX_FLAG_ESC_CLR (1 << 2) // allow escape key to clear input box +#define UI_INPUTBOX_FLAG_ESC_FOC (1 << 3) // escape loses focus for the input box +#define UI_INPUTBOX_FLAG_PASSWD (1 << 4) // display all characters as special "password" characters +#define UI_INPUTBOX_FLAG_EAT_USED (1 << 5) // don't return any characters actually used by inputbox +#define UI_INPUTBOX_FLAG_LETTER_FIRST (1 << 6) // require input string to begin with a letter. +#define UI_INPUTBOX_FLAG_NO_LETTERS (1 << 7) // don't allow [a-z,A-Z] at all, no matter what +#define UI_INPUTBOX_FLAG_NO_NUMERALS (1 << 8) // don't allow [0-9] at all, no matter what +#define UI_INPUTBOX_FLAG_TEXT_CEN (1 << 9) // always draw text centered in the inputbox +#define UI_INPUTBOX_FLAG_NO_BACK (1 << 10) // don't draw a black background rectangle + +#define UI_GF_MOUSE_CAPTURED (1 << 31) // gadget has all rights to the mouse + +class UI_WINDOW; +class UI_BUTTON; +class UI_KEYTRAP; +class UI_CHECKBOX; +class UI_RADIO; +class UI_SCROLLBAR; +class UI_LISTBOX; +class UI_INPUTBOX; +// class UI_SLIDER; +class UI_DOT_SLIDER; +class UI_DOT_SLIDER_NEW; + +class UI_GADGET +{ + friend class UI_WINDOW; + friend class UI_BUTTON; + friend class UI_KEYTRAP; + friend class UI_CHECKBOX; + friend class UI_RADIO; + friend class UI_SCROLLBAR; + friend class UI_LISTBOX; + friend class UI_INPUTBOX; + // friend UI_SLIDER; + friend class UI_DOT_SLIDER; + friend class UI_DOT_SLIDER_NEW; + + protected: + char *bm_filename; + int kind; + int hotkey; + int x, y, w, h; + int m_flags; + void (*user_function)(void); + int disabled_flag; + int base_dragging; + int base_drag_x, base_drag_y; + int base_start_x, base_start_y; + int hidden; + + // Data for supporting linking controls to hotspots + int linked_to_hotspot; + int hotspot_num; + + // Data for supporting bitmaps associated with different states of the control + int uses_bmaps; + int m_num_frames; +// ubyte *bmap_storage[MAX_BMAPS_PER_GADGET]; + + void drag_with_children( int dx, int dy ); + void start_drag_with_children(); + void stop_drag_with_children(); + + UI_GADGET *parent; + UI_GADGET *children; + UI_GADGET *prev; + UI_GADGET *next; + + int is_mouse_on_children(); + void remove_from_family(); + void set_parent(UI_GADGET *_parent); + + UI_GADGET *get_next(); + UI_GADGET *get_prev(); + UI_WINDOW *my_wnd; + + virtual void process(int focus = 0); + virtual void destroy(); + int check_move(); + + public: + int bmap_ids[MAX_BMAPS_PER_GADGET]; + + UI_GADGET(); // constructor + ~UI_GADGET(); // destructor + + void base_create( UI_WINDOW *wnd, int kind, int x, int y, int w, int h ); + virtual void draw(); + void set_focus(); + void clear_focus(); + int has_focus(); + void set_hotkey(int keycode); + void set_callback(void (*user_function)(void)); + void disable(); + void enable(int n = 1); + void capture_mouse(); + int mouse_captured(UI_GADGET *gadget = NULL); + int disabled(); + int enabled(); + virtual void hide(int n = 1); + virtual void unhide(); + void update_dimensions(int x, int y, int w, int h); + void get_dimensions(int *x, int *y, int *w, int *h); + int is_mouse_on(); + void get_mouse_pos(int *x, int *y); + + void link_hotspot(int num); + int get_hotspot(); + int bmaps_used() { return uses_bmaps; } + + // loads nframes bitmaps, starting at index start_frame. + // anything < start_frame will not be loaded. + // this keeps the loading code from trying to load bitmaps which don't exist + // and taking an unnecessary disk hit. + int set_bmaps(char *ani_filename, int nframes = 3, int start_frame = 1); // extracts MAX_BMAPS_PER_GADGET from .ani file + + void reset(); // zero out m_flags + int is_hidden() { return hidden; } +}; + +// xstrings for a window +#define UI_NUM_XSTR_COLORS 2 +#define UI_XSTR_COLOR_GREEN 0 // shades of green/gray +#define UI_XSTR_COLOR_PINK 1 // pinkish hue +typedef struct UI_XSTR { + char *xstr; // base string + int xstr_id; // xstring id + int x, y; // coords of the string + int clr; // color to use + int font_id; // font id + UI_GADGET *assoc; // the associated gadget +} UI_XSTR; + +#define MAX_UI_XSTRS 100 + +// Button terminology: +// Up = button is in up state (also called pressed) +// Down = button is in down state (also called released) +// Just pressed = button has just gone from up to down state +// Just released = button has just gone from down to up state +// Clicked = a trigger type effect caused by 'just pressed' event or repeat while 'down' +// Double clicked = 2 'just pressed' events occuring within a short amount of time + +// Button flags +#define BF_UP (1<<0) +#define BF_DOWN (1<<1) +#define BF_JUST_PRESSED (1<<2) +#define BF_JUST_RELEASED (1<<3) +#define BF_CLICKED (1<<4) +#define BF_DOUBLE_CLICKED (1<<5) +#define BF_HIGHLIGHTED (1<<6) // button is not highlighted (ie mouse is not over) +#define BF_JUST_HIGHLIGHTED (1<<7) // button has just been highlighted, true for 1 frame +#define BF_IGNORE_FOCUS (1<<8) // button should not use focus to accept space/enter keypresses +#define BF_HOTKEY_JUST_PRESSED (1<<9) // button hotkey was just pressed +#define BF_REPEATS (1<<10) // if held down, generates repeating presses +#define BF_SKIP_FIRST_HIGHLIGHT_CALLBACK (1<<11) // skip first callback for mouse over event + +class UI_BUTTON : public UI_GADGET +{ + friend class UI_SCROLLBAR; + // friend UI_SLIDER; + friend class UI_DOT_SLIDER; + friend class UI_DOT_SLIDER_NEW; + + char *text; + int position; // indicates position of button (0 - up, 1 - down by mouse click 2 - down by keypress + int next_repeat; // timestamp for next repeat if held down + int m_press_linger; // timestamp for hold a pressed state animation + int hotkey_if_focus; // hotkey for button that only works if it has focus + int force_draw_frame; // frame number to draw next time (override default) + int first_callback; // true until first time callback function is called for button highlight + + // Used to index into bmap_ids[] array to locate right bitmap for button + enum { B_NORMAL = 0 }; + enum { B_HIGHLIGHT = 1 }; + enum { B_PRESSED = 2 }; + enum { B_DISABLED = 3 }; + enum { B_REPEAT_TIME = 100 }; // ms + + void (*m_just_highlighted_function)(void); // call-back that gets called when button gets highlighted + void (*m_disabled_function)(void); // callback that gets called when disabled button gets pressed (sound, popup, etc) + + void frame_reset(); + virtual void process(int focus = 0); + virtual void destroy(); + + int custom_cursor_bmap; // bmap handle of special cursor used on mouseovers + int previous_cursor_bmap; // store old cursor + void maybe_show_custom_cursor(); // call this in process() + void restore_previous_cursor(); // called in frame_reset() + + public: + virtual void draw(); + void set_hotkey_if_focus(int key); + int pressed(); // has it been selected (ie clicked on) + int double_clicked(); // button was double clicked on + int just_pressed(); // button has just been selected + int just_highlighted(); // button has just had mouse go over it + int button_down(); // is the button depressed? + int button_hilighted(); // is the mouse over this button? + void set_button_hilighted(); // force button to be highlighted + void press_button(); // force button to get pressed + void create(UI_WINDOW *wnd, char *_text, int _x, int _y, int _w, int _h, int do_repeat=0, int ignore_focus = 0); + void set_highlight_action( void (*user_function)(void) ); + void set_disabled_action( void (*user_function)(void) ); + void draw_forced(int frame_num); + void reset_status(); + void reset_timestamps(); + void skip_first_highlight_callback(); + void repeatable(int yes); + void set_custom_cursor_bmap(int bmap_id) { custom_cursor_bmap = bmap_id; }; +}; + +class UI_KEYTRAP : public UI_GADGET +{ + int pressed_down; + virtual void draw(); + virtual void process(int focus = 0); + + public: + int pressed(); + void create(UI_WINDOW *wnd, int hotkey, void (*user_function)(void) ); +}; + +class UI_USERBOX : public UI_GADGET +{ + int b1_held_down; + int b1_clicked; + int b1_double_clicked; + int b1_dragging; + int b1_drag_x1, b1_drag_y1; + int b1_drag_x2, b1_drag_y2; + int b1_done_dragging; + int keypress; + int mouse_onme; + int mouse_x, mouse_y; + int bitmap_number; +}; + +class UI_INPUTBOX : public UI_GADGET +{ + char *text; + char *passwd_text; + int length; + int position; + int oldposition; + int pressed_down; + int changed_flag; + int flags; + int pixel_limit; // base max characters on how wide the string is (-1 to ignore) in pixels + int locked; +// int should_reset; + int ignore_escape; + color *text_color; + char *valid_chars; + char *invalid_chars; + + // cursor drawing + int cursor_first_frame; + int cursor_nframes; + int cursor_fps; + int cursor_current_frame; + int cursor_elapsed_time; + + int validate_input(int chr); + void init_cursor(); + + virtual void draw(); + virtual void process(int focus = 0); + virtual void destroy(); + + public: +// int first_time; + + void create(UI_WINDOW *wnd, int _x, int _y, int _w, int _textlen, char *text, int _flags = 0, int pixel_lim = -1, color *clr = NULL); + void set_valid_chars(char *vchars); + void set_invalid_chars(char *ichars); + int changed(); + int pressed(); + void get_text(char *out); + void set_text(char *in); +}; + +// Icon flags +#define ICON_NOT_HIGHLIGHTED (1<<0) // icon is not highlighted (ie mouse is not over) +#define ICON_JUST_HIGHLIGHTED (1<<1) // icon has just been highlighted, true for 1 frame + +class UI_ICON : public UI_GADGET +{ + char *text; + + // Used to index into bmap_ids[] array to locate right bitmap for button + enum { ICON_NORMAL = 0 }; + enum { ICON_HIGHLIGHT = 1 }; + enum { ICON_SELECTED = 2 }; + enum { ICON_DISABLED = 3 }; + + virtual void draw(); + virtual void process(int focus = 0); + virtual void destroy(); + + public: + void create(UI_WINDOW *wnd, char *_text, int _x, int _y, int _w, int _h); +}; + +class UI_CHECKBOX : public UI_GADGET +{ + char *text; + int position; + int pressed_down; + int flag; + virtual void draw(); + virtual void process(int focus = 0); + virtual void destroy(); + + // Used to index into bmap_ids[] array to locate right bitmap for checkbox + enum { CBOX_UP_CLEAR = 0 }; + enum { CBOX_DOWN_CLEAR = 1 }; + enum { CBOX_UP_MARKED = 2 }; + enum { CBOX_DOWN_MARKED = 3 }; + enum { CBOX_DISABLED_CLEAR = 4 }; + enum { CBOX_DISABLED_MARKED = 5 }; + + public: + int changed(); + int checked(); + void create(UI_WINDOW *wnd, char *_text, int _x, int _y, int _state ); + void set_state(int _state); +}; + +class UI_RADIO : public UI_GADGET +{ + char *text; + int position; + int pressed_down; + int flag; + int group; + virtual void draw(); + virtual void process(int focus = 0); + virtual void destroy(); + + // Used to index into bmap_ids[] array to locate right bitmap for radio button + enum { RADIO_UP_CLEAR = 0 }; + enum { RADIO_DOWN_CLEAR = 1 }; + enum { RADIO_UP_MARKED = 2 }; + enum { RADIO_DOWN_MARKED = 3 }; + enum { RADIO_DISABLED_CLEAR = 4 }; + enum { RADIO_DISABLED_MARKED = 5 }; + + public: + int changed(); + int checked(); + void create(UI_WINDOW *wnd, char *_text, int _x, int _y, int _state, int _group ); +}; + +class UI_SCROLLBAR : public UI_GADGET +{ + friend class UI_LISTBOX; + friend class UI_BUTTON; + int horz; + int start; + int stop; + int position; + int window_size; + int bar_length; + int bar_position; + int bar_size; + UI_BUTTON up_button; + UI_BUTTON down_button; + int last_scrolled; + int drag_x, drag_y; + int drag_starting; + int dragging; + int moved; + + virtual void draw(); + virtual void process(int focus = 0); + + // Used to index into bmap_ids[] array to locate right bitmap for scrollbar + enum { SB_NORMAL = 0 }; + enum { SB_DISABLED = 1 }; + + public: + void create(UI_WINDOW *wnd, int _x, int _y, int _h,int _start, int _stop, int _position, int _window_size ); + int getpos(); + int changed(); + void hide(); + void unhide(); + int get_hidden(); + void link_hotspot(int up_button_num, int down_button_num); + int set_bmaps(char *up_button_fname, char *down_button_fname, char *line_fname); +}; + +class UI_SLIDER2 : public UI_GADGET +{ + friend class UI_BUTTON; + private: + int numberItems; // total range + int numberPositions; // total positions (height - bitmapbuttonheight) + int currentItem; // current item we are on + int currentPosition; // current position + int last_scrolled; + int mouse_locked; + int slider_mode; // + UI_BUTTON sliderBackground;// invisible button to detect clicks + int bitmapSliderControl; // this is the bitmap of the slider button itself + void (*upCallback)(); + void (*downCallback)(); + void (*captureCallback)(); // this is called when the mouse is released + UI_BUTTON *upButton, *downButton; + int slider_w, slider_h, slider_half_h; // this is the width and height and half height of the bitmap used for the slider + virtual void draw(); + virtual void process(int focus = 0); + + // Used to index into bmap_ids[] array to locate right bitmap for slider + enum { S2_NORMAL = 0 }; + enum { S2_HIGHLIGHT = 1 }; + enum { S2_PRESSED = 2 }; + enum { S2_DISABLED = 3 }; + + // Used for slider mode + enum { S2M_ON_ME = 0 }; + enum { S2M_MOVING = 1 }; + enum { S2M_DEFAULT = 2 }; + + public: + // create the slider + void create(UI_WINDOW *wnd, int _x, int _y, int _w, int _h, int _numberItems, char *_bitmapSliderControl, + void (*_upCallback)(), void (*_downCallback)(), void (*_captureCallback)()); + + // range management + int get_numberItems(); // return number of itmes + int get_currentPosition(); // return current position + int get_currentItem(); // return current item + void set_numberItems(int _numberItems, int reset = 1); // change range. maybe reset back to position 0 + void set_currentItem(int _currentItem); // force slider to new position manually + void force_currentItem(int _currentItem); // force slider to new position manually, _not_ calling any callbacks + + // force down + void forceDown(); + + // force up + void forceUp(); + + // general ui commands + void hide(); + void unhide(); + int get_hidden(); +}; + +// to be phased out eventually in FS2 +class UI_DOT_SLIDER : public UI_GADGET +{ + friend class UI_BUTTON; + + UI_BUTTON button; + UI_BUTTON up_button; + UI_BUTTON down_button; + int first_frame, total_frames; + int has_end_buttons; + int num_pos; + + public: + int pos; // 0 thru 10 + + void create(UI_WINDOW *wnd, int _x, int _y, char *bm, int id, int end_buttons = 1, int num_pos = 10); + virtual void draw(); + virtual void process(int focus = 0); + virtual void destroy(); +}; + +class UI_DOT_SLIDER_NEW : public UI_GADGET +{ + friend class UI_BUTTON; + UI_BUTTON button; + UI_BUTTON up_button; + UI_BUTTON down_button; + int has_end_buttons; + int num_pos; + int dot_width; + + public: + int pos; // 0 thru 10 + + void create(UI_WINDOW *wnd, int _x, int _y, int num_pos, char *bm_slider, int slider_mask, + char *bm_left = NULL, int left_mask = -1, int left_x = -1, int left_y = -1, + char *bm_right = NULL, int right_mask = -1, int right_x = -1, int right_y = -1, + int dot_width = 19); + virtual void draw(); + virtual void process(int focus = 0); +}; + +class UI_LISTBOX : public UI_GADGET +{ + private: + char **list; + char *check_list; + int max_items; + int num_items; + int num_items_displayed; + int first_item; + int old_first_item; + int current_item; + int selected_item; + int toggled_item; + int old_current_item; + int last_scrolled; + int dragging; + int textheight; + int has_scrollbar; + char key_buffer[MAX_KEY_BUFFER]; + int key_buffer_count; + int last_typed; + UI_SCROLLBAR scrollbar; + + virtual void draw(); + virtual void process(int focus = 0); + + // Used to index into bmap_ids[] array to locate right bitmap for listbox + enum { LBOX_NORMAL = 0 }; + enum { LBOX_DISABLED = 1 }; + + public: + void create(UI_WINDOW *wnd, int _x, int _y, int _w, int _h, int _numitem, char **_list, char *_check_list = NULL, int _max_items = -1); + int selected(); // selected: Returns >= 0 if an item was selected + int current(); // current: Returns which item listbox bar is currently on. This could be -1, if none selected! + int toggled(); // toggled: Returns which item was toggled with spacebr or mouse click of check_list is not NULL + void set_current(int _index); // sets the current item in the list box + void set_first_item(int _index); + char *get_string(int _index); + void clear_all_items(); // deletes all the items in the list (makes them empty strings) + int add_string(char *str); + int sel_changed(); // returns > 0 if the selected item has changed + void set_new_list(int _numitems, char **_list); + + int set_bmaps(char *lbox_fname, char *b_up_fname, char *b_down_fname, char *sb_fname); + void link_hotspot(int up_button_num, int down_button_num); +}; + +#define WIN_BORDER 1 +#define WIN_FILLED 2 +#define WIN_SAVE_BG 4 +#define WIN_DIALOG (4+2+1) + +class UI_WINDOW +{ + friend class UI_GADGET; + friend class UI_BUTTON; + friend class UI_KEYTRAP; + friend class UI_CHECKBOX; + friend class UI_RADIO; + friend class UI_SCROLLBAR; + friend class UI_LISTBOX; + friend class UI_INPUTBOX; + // friend UI_SLIDER; + friend class UI_SLIDER2; + friend class UI_DOT_SLIDER; + friend class UI_DOT_SLIDER_NEW; + friend class UI_ICON; + +protected: + int flags; + int x, y; + int w, h; + int f_id; // font id + int last_tooltip_hotspot; + uint last_tooltip_time; + int tt_group; // which tooltip group this window uses, or -1 if none + int ignore_gadgets; + + UI_GADGET *first_gadget; + UI_GADGET *selected_gadget; + UI_GADGET *mouse_captured_gadget; + + int mask_bmap_id; // bitmap id of the mask bitmap to define hotspots + int foreground_bmap_id; // bitmap id of the foreground bitmap to display + bitmap *mask_bmap_ptr; // pointer to bitmap of the mask + ushort *mask_data; // points to raw mask bitmap data + int mask_w, mask_h; // width and height of the mask + + UI_XSTR *xstrs[MAX_UI_XSTRS]; // strings for drawing in code instead of in artwork + + + int keypress; // filled in each frame + void capture_mouse(UI_GADGET *gadget = NULL); + void release_bitmaps(); // called internally when window destroys gadgets + void check_focus_switch_keys(); + void do_dump_check(); + void draw_xstrs(); // draw xstrs + void draw_one_xstr(UI_XSTR *xstr, int frame); + +public: + UI_WINDOW(); // constructor + ~UI_WINDOW(); // destructor + void set_mask_bmap(char *fname); + void set_mask_bmap(int bmap, char *name); + void set_foreground_bmap(char *fname); + void create( int x, int y, int w, int h, int flags ); + int process( int key_in = -1,int process_mouse = 1); + void draw(); + void draw_tooltip(); + void draw_XSTR_forced(UI_GADGET *owner, int frame); + int get_current_hotspot(); + void destroy(); + ushort *get_mask_data(int *w, int *h) { *w = mask_w; *h = mask_h; return mask_data; } + void render_tooltip(char *str); + void set_ignore_gadgets(int state); + void add_XSTR(char *string, int xstr_id, int x, int y, UI_GADGET *assoc, int color_type, int font_id = -1); + void add_XSTR(UI_XSTR *xstr); + + char *(*tooltip_handler)(char *text); + int last_keypress; // filled in each frame + int ttx, tty; + int use_hack_to_get_around_stupid_problem_flag; +}; + +// 2 extremely useful structs +typedef struct ui_button_info { + char *filename; + int x, y, xt, yt; + int hotspot; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + ui_button_info(char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {} +} ui_button_info; + + +/* +typedef struct { + char *mask; + int start; + int end; +} tooltip_group; + +typedef struct { + int hotspot; + char *text; +} tooltip; + +#define MAX_TOOLTIP_GROUPS 50 +#define MAX_TOOLTIPS 500 + +extern int Num_tooltip_groups; +extern tooltip_group Tooltip_groups[MAX_TOOLTIP_GROUPS]; +extern tooltip Tooltips[MAX_TOOLTIPS]; +*/ + +int ui_getfilelist( int MaxNum, char **list, char *filespec ); +void ui_sort_filenames( int n, char **list ); + +/* +class UI_SLIDER : public UI_GADGET +{ + friend UI_BUTTON; + int horz; + int position; + int window_size; + int fake_length; + int fake_position; + int fake_size; + UI_BUTTON left_button; + UI_BUTTON right_button; + int last_scrolled; + int drag_x, drag_y; + int drag_starting; + int dragging; + int moved; + + int marker_x, marker_y, marker_w, marker_h; + int n_positions, pixel_range, increment; + float start, stop, current; + int mouse_locked; + + virtual void draw(); + virtual void process(int focus = 0); + + // Used to index into bmap_ids[] array to locate right bitmap for slider + enum { SLIDER_BAR_NORMAL = 0 }; + enum { SLIDER_BAR_DISABLED = 1 }; + enum { SLIDER_MARKER_NORMAL = 2 }; + enum { SLIDER_MARKER_DISABLED = 3 }; + + public: + void create(UI_WINDOW *wnd, int _x, int _y, int _w, int _h, float _start, float _stop, float _pos, int n_positions); + int getpos(); + float getcurrent(); + int changed(); + void hide(); + void unhide(); + int get_hidden(); + void link_hotspot(int up_button_num, int down_button_num); + int set_bmaps(char *left_button_fname, char *right_button_fname, char *bar_fname, char *marker_fname); +}; +*/ + +#endif + diff --git a/include/uidefs.h b/include/uidefs.h new file mode 100644 index 0000000..85ebbaf --- /dev/null +++ b/include/uidefs.h @@ -0,0 +1,112 @@ +/* + * $Logfile: /Freespace2/code/UI/UiDefs.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for stuff used internally by the UI code. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 8 4/01/98 5:07p Hoffoss + * Changed mouse button handling for UI in order to track fast events + * (like button going down and released all between frames). + * + * 7 2/03/98 4:21p Hoffoss + * Made UI controls draw white text when disabled. + * + * 6 6/12/97 12:39p John + * made ui use freespace colors + * + * 5 6/11/97 1:13p John + * Started fixing all the text colors in the game. + * + * 4 12/08/96 1:58a Lawrance + * added some additional colors + * + * 3 12/02/96 2:17p John + * Made right button drag UI gadgets around and + * Ctrl+Shift+Alt+F12 dumps out where they are. + * + * 2 11/15/96 11:43a John + * + * 1 11/14/96 6:55p John + * + * $NoKeywords: $ + */ + +#ifndef _UIDEFS_H +#define _UIDEFS_H + +#include "pstypes.h" +#include "key.h" +#include "mouse.h" +#include "2d.h" +#include "freespace.h" + +#define CBLACK Color_black +#define CGREEN Color_green +#define CBRIGHT_GREEN Color_bright_green +#define CGRAY Color_grey +#define CDARK_GRAY Color_bright_white // since gray doesn't work with our current color system.. +#define CWHITE Color_white +#define CBRIGHT Color_bright_white + +#define BORDER_WIDTH 8 + +void ui_hline(int x1, int x2, int y ); +void ui_vline(int y1, int y2, int x ); +void ui_string_centered( int x, int y, char * s ); +void ui_draw_shad( int x1, int y1, int x2, int y2, int r1, int g1, int b1, int r2, int g2, int b2 ); +void ui_draw_frame( int x1, int y1, int x2, int y2 ); +void ui_rect( int x1, int y1, int x2, int y2 ); +void ui_draw_box_out( int x1, int y1, int x2, int y2 ); +void ui_draw_box_in( int x1, int y1, int x2, int y2 ); +void ui_draw_line_in( int x1, int y1, int x2, int y2 ); +void ui_draw_sunken_border( int x1, int y1, int x2, int y2 ); + +#define BUTTON_PRESSED 1 +#define BUTTON_RELEASED 2 +#define BUTTON_JUST_PRESSED 4 +#define BUTTON_JUST_RELEASED 8 +#define BUTTON_DOUBLE_CLICKED 16 + +#define B1_PRESSED (ui_mouse.b1_status & BUTTON_PRESSED) +#define B1_RELEASED (ui_mouse.b1_status & BUTTON_RELEASED) +#define B1_JUST_PRESSED (ui_mouse.b1_status & BUTTON_JUST_PRESSED) +#define B1_JUST_RELEASED (ui_mouse.b1_status & BUTTON_JUST_RELEASED) +#define B1_DOUBLE_CLICKED (ui_mouse.b1_status & BUTTON_DOUBLE_CLICKED) + +#define B2_PRESSED (ui_mouse.b2_status & BUTTON_PRESSED) +#define B2_RELEASED (ui_mouse.b2_status & BUTTON_RELEASED) +#define B2_JUST_PRESSED (ui_mouse.b2_status & BUTTON_JUST_PRESSED) +#define B2_JUST_RELEASED (ui_mouse.b2_status & BUTTON_JUST_RELEASED) + +typedef struct UI_MOUSE { + int x, y; + int dx, dy; + int b1_status; + int b1_last_status; + int b1_time_lastpressed; + int b2_status; + int b2_last_status; + int b2_time_lastpressed; + int timestamp; +} UI_MOUSE; + +extern UI_MOUSE ui_mouse; +extern void ui_mouse_process(); + + +#define Middle(x) ((x)/2) + +#endif + diff --git a/include/unix.h b/include/unix.h new file mode 100644 index 0000000..26d1fe0 --- /dev/null +++ b/include/unix.h @@ -0,0 +1,39 @@ +// unix.h - duplicates some MS defines + +#ifndef _UNIX_H +#define _UNIX_H + +#include +#include +#include +#include "SDL.h" + +#define DWORD int +#define _MAX_FNAME 255 +#define _MAX_PATH 255 +#define _MAX_DIR 256 +#define MAX_FILENAME_LENGTH 64 +#define _cdecl +#define __int64 long long +#define LARGE_INTEGER long long +#define stricmp strcasecmp +#define strnicmp strncasecmp +#define _strnicmp strncasecmp +#define _isnan isnan +#define HANDLE int +#define _getcwd getcwd +#define _chdir chdir +#define _strlwr strlwr +#define _unlink unlink +#define _mkdir mkdir +#define _hypot hypot +#define byte unsigned char + +extern void strlwr (char *str); +extern int filelength (int fd); +extern int MulDiv (int, int, int); +#define CRITICAL_SECTION SDL_mutex* + +#define STUB_FUNCTION fprintf(stderr,"STUB: %s at " __FILE__ ", line %d, thread %d\n",__FUNCTION__,__LINE__,getpid()) +#endif + diff --git a/include/vasync.h b/include/vasync.h new file mode 100644 index 0000000..acd7156 --- /dev/null +++ b/include/vasync.h @@ -0,0 +1,132 @@ +/*==========================================================================; + * + * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. + * + * File: async.h + * Content: AsyncData object include file + ***************************************************************************/ +#ifndef __ASYNC_INCLUDED__ +#define __ASYNC_INCLUDED__ + + +#include // for DECLARE_INTERFACE and HRESULT + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * GUIDS used by DPAsyncData objects + */ + +// {47BCD7E0-2E89-11d0-A889-00A0C905433C} +DEFINE_GUID(IID_IDPAsyncData, 0x47bcd7e0, 0x2e89, 0x11d0, 0xa8, 0x89, 0x0, 0xa0, 0xc9, 0x5, 0x43, 0x3c); + + +/**************************************************************************** + * + * IDPAsyncData Structures + * + * Various structures used to invoke DPAsyncData. + * + ****************************************************************************/ + +typedef struct IDPAsyncData FAR *LPDPASYNCDATA; + + +/**************************************************************************** + * + * IDPAsyncData Interface + * + ****************************************************************************/ + +#undef INTERFACE +#define INTERFACE IDPAsyncData +DECLARE_INTERFACE_( IDPAsyncData, IUnknown ) +{ + /* IUnknown Methods */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + /* IDPAsyncData Methods */ + STDMETHOD(AddItem) (THIS_ LPDWORD, LPVOID, DWORD) PURE; + STDMETHOD(Cancel) (THIS) PURE; + STDMETHOD(GetFlags) (THIS_ LPDWORD) PURE; + STDMETHOD(GetItem) (THIS_ DWORD, LPVOID *) PURE; + STDMETHOD(GetItemCount) (THIS_ LPDWORD) PURE; + STDMETHOD(GetStatus) (THIS_ LPDWORD, LPDWORD, HRESULT *) PURE; + STDMETHOD(IsSnapshotCurrent) (THIS) PURE; + STDMETHOD(RefreshSnapshot) (THIS) PURE; + STDMETHOD(RemoveItem) (THIS_ DWORD) PURE; + STDMETHOD(SetFlags) (THIS_ DWORD) PURE; + STDMETHOD(SetItem) (THIS_ DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetStatus) (THIS_ DWORD) PURE; + STDMETHOD(SetStatusEvent) (THIS_ DWORD, HANDLE) PURE; +}; + + +/**************************************************************************** + * + * IDPAsyncData interface macros + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDPAsyncData_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDPAsyncData_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDPAsyncData_Release(p) (p)->lpVtbl->Release(p) +#define IDPAsyncData_AddItem(p) (p)->lpVtbl->AddItem(p) +#define IDPAsyncData_Cancel(p) (p)->lpVtbl->Cancel(p) +#define IDPAsyncData_GetItem(p,a,b) (p)->lpVtbl->GetItem(p,a,b) +#define IDPAsyncData_GetItemCount(p,a) (p)->lpVtbl->GetItemCount(p,a) +#define IDPAsyncData_GetStatus(p,a,b,c) (p)->lpVtbl->GetStatus(p,a,b,c) +#define IDPAsyncData_IsSnapshotCurrent(p) (p)->lpVtbl->IsSnapshotCurrent(p) +#define IDPAsyncData_RefreshSnapshot(p) (p)->lpVtbl->RefreshSnapshot(p) +#define IDPAsyncData_RemoveItem(p) (p)->lpVtbl->RemoveItem(p) +#define IDPAsyncData_SetFlags(p) (p)->lpVtbl->SetFlags(p) +#define IDPAsyncData_SetItem(p) (p)->lpVtbl->SetItem(p) +#define IDPAsyncData_SetStatus(p) (p)->lpVtbl->SetStatus(p) +#define IDPAsyncData_SetStatusEvent(p,a,b) (p)->lpVtbl->SetStatusEvent(p,a,b) + +#else /* C++ */ + +#define IDPAsyncData_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDPAsyncData_AddRef(p) (p)->AddRef() +#define IDPAsyncData_Release(p) (p)->Release() +#define IDPAsyncData_AddItem(p) (p)->AddItem() +#define IDPAsyncData_Cancel(p) (p)->Cancel() +#define IDPAsyncData_GetItem(p,a,b) (p)->GetItem(a,b) +#define IDPAsyncData_GetItemCount(p,a) (p)->GetItemCount(a) +#define IDPAsyncData_GetStatus(p,a,b,c) (p)->GetStatus(a,b,c) +#define IDPAsyncData_IsSnapshotCurrent(p) (p)->IsSnapshotCurrent() +#define IDPAsyncData_RefreshSnapshot(p) (p)->RefreshSnapshot() +#define IDPAsyncData_RemoveItem(p) (p)->RemoveItem() +#define IDPAsyncData_SetFlags(p) (p)->SetFlags() +#define IDPAsyncData_SetItem(p) (p)->SetItem() +#define IDPAsyncData_SetStatus(p) (p)->SetStatus() +#define IDPAsyncData_SetStatusEvent(p,a,b) (p)->SetStatusEvent(a,b) + +#endif + + +/**************************************************************************** + * + * AsyncData Flags + * + ****************************************************************************/ + +/* + * This flag is set if the ItemData is ANSI. If it is not set, the ItemData + * is either Unicode or doesn't contain any string information. + */ +#define DPASYNCDATA_ANSI (0x00000001) + + +#ifdef __cplusplus +}; +#endif /* __cplusplus */ + +#endif /* __ASYNC_INCLUDED__ */ + diff --git a/include/vd3d.h b/include/vd3d.h new file mode 100644 index 0000000..c0954b8 --- /dev/null +++ b/include/vd3d.h @@ -0,0 +1,897 @@ +/*==========================================================================; + * + * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. + * + * File: d3d.h + * Content: Direct3D include file + * + ***************************************************************************/ + +#ifndef _D3D_H_ +#define _D3D_H_ + +#include + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#else +#include "d3dcom.h" +#endif + +#ifdef _WIN32 +#define D3DAPI WINAPI +#else +#define D3DAPI +#endif + +/* + * Interface IID's + */ +#if defined( _WIN32 ) && !defined( _NO_COM) +DEFINE_GUID( IID_IDirect3D, 0x3BBA0080,0x2421,0x11CF,0xA3,0x1A,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirect3D2, 0x6aae1ec1,0x662a,0x11d0,0x88,0x9d,0x00,0xaa,0x00,0xbb,0xb7,0x6a); + +DEFINE_GUID( IID_IDirect3DRampDevice, 0xF2086B20,0x259F,0x11CF,0xA3,0x1A,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirect3DRGBDevice, 0xA4665C60,0x2673,0x11CF,0xA3,0x1A,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirect3DHALDevice, 0x84E63dE0,0x46AA,0x11CF,0x81,0x6F,0x00,0x00,0xC0,0x20,0x15,0x6E ); +DEFINE_GUID( IID_IDirect3DMMXDevice, 0x881949a1,0xd6f3,0x11d0,0x89,0xab,0x00,0xa0,0xc9,0x05,0x41,0x29 ); + +DEFINE_GUID( IID_IDirect3DDevice, 0x64108800,0x957d,0X11d0,0x89,0xab,0x00,0xa0,0xc9,0x05,0x41,0x29 ); +DEFINE_GUID( IID_IDirect3DDevice2, 0x93281501, 0x8cf8, 0x11d0, 0x89, 0xab, 0x0, 0xa0, 0xc9, 0x5, 0x41, 0x29); +DEFINE_GUID( IID_IDirect3DTexture, 0x2CDCD9E0,0x25A0,0x11CF,0xA3,0x1A,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirect3DTexture2, 0x93281502, 0x8cf8, 0x11d0, 0x89, 0xab, 0x0, 0xa0, 0xc9, 0x5, 0x41, 0x29); +DEFINE_GUID( IID_IDirect3DLight, 0x4417C142,0x33AD,0x11CF,0x81,0x6F,0x00,0x00,0xC0,0x20,0x15,0x6E ); +DEFINE_GUID( IID_IDirect3DMaterial, 0x4417C144,0x33AD,0x11CF,0x81,0x6F,0x00,0x00,0xC0,0x20,0x15,0x6E ); +DEFINE_GUID( IID_IDirect3DMaterial2, 0x93281503, 0x8cf8, 0x11d0, 0x89, 0xab, 0x0, 0xa0, 0xc9, 0x5, 0x41, 0x29); +DEFINE_GUID( IID_IDirect3DExecuteBuffer,0x4417C145,0x33AD,0x11CF,0x81,0x6F,0x00,0x00,0xC0,0x20,0x15,0x6E ); +DEFINE_GUID( IID_IDirect3DViewport, 0x4417C146,0x33AD,0x11CF,0x81,0x6F,0x00,0x00,0xC0,0x20,0x15,0x6E ); +DEFINE_GUID( IID_IDirect3DViewport2, 0x93281500, 0x8cf8, 0x11d0, 0x89, 0xab, 0x0, 0xa0, 0xc9, 0x5, 0x41, 0x29); +#endif + +/* + * Data structures + */ +#ifdef __cplusplus + +/* 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined */ +struct IDirect3D; +struct IDirect3D2; +struct IDirect3DDevice; +struct IDirect3DDevice2; +struct IDirect3DExecuteBuffer; +struct IDirect3DLight; +struct IDirect3DMaterial; +struct IDirect3DMaterial2; +struct IDirect3DTexture; +struct IDirect3DTexture2; +struct IDirect3DViewport; +struct IDirect3DViewport2; +typedef struct IDirect3D *LPDIRECT3D; +typedef struct IDirect3D2 *LPDIRECT3D2; +typedef struct IDirect3DDevice *LPDIRECT3DDEVICE; +typedef struct IDirect3DDevice2 *LPDIRECT3DDEVICE2; +typedef struct IDirect3DExecuteBuffer *LPDIRECT3DEXECUTEBUFFER; +typedef struct IDirect3DLight *LPDIRECT3DLIGHT; +typedef struct IDirect3DMaterial *LPDIRECT3DMATERIAL; +typedef struct IDirect3DMaterial2 *LPDIRECT3DMATERIAL2; +typedef struct IDirect3DTexture *LPDIRECT3DTEXTURE; +typedef struct IDirect3DTexture2 *LPDIRECT3DTEXTURE2; +typedef struct IDirect3DViewport *LPDIRECT3DVIEWPORT; +typedef struct IDirect3DViewport2 *LPDIRECT3DVIEWPORT2; + +#else + +typedef struct IDirect3D *LPDIRECT3D; +typedef struct IDirect3D2 *LPDIRECT3D2; +typedef struct IDirect3DDevice *LPDIRECT3DDEVICE; +typedef struct IDirect3DDevice2 *LPDIRECT3DDEVICE2; +typedef struct IDirect3DExecuteBuffer *LPDIRECT3DEXECUTEBUFFER; +typedef struct IDirect3DLight *LPDIRECT3DLIGHT; +typedef struct IDirect3DMaterial *LPDIRECT3DMATERIAL; +typedef struct IDirect3DMaterial2 *LPDIRECT3DMATERIAL2; +typedef struct IDirect3DTexture *LPDIRECT3DTEXTURE; +typedef struct IDirect3DTexture2 *LPDIRECT3DTEXTURE2; +typedef struct IDirect3DViewport *LPDIRECT3DVIEWPORT; +typedef struct IDirect3DViewport2 *LPDIRECT3DVIEWPORT2; + +#endif + +#include "vd3dtypes.h" +#include "vd3dcaps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * IDirect3D + */ +#undef INTERFACE +#define INTERFACE IDirect3D +DECLARE_INTERFACE_(IDirect3D, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3D methods ***/ + STDMETHOD(Initialize) (THIS_ REFIID) PURE; + STDMETHOD(EnumDevices)(THIS_ LPD3DENUMDEVICESCALLBACK, LPVOID) PURE; + STDMETHOD(CreateLight) (THIS_ LPDIRECT3DLIGHT*, IUnknown*) PURE; + STDMETHOD(CreateMaterial) (THIS_ LPDIRECT3DMATERIAL*, IUnknown*) PURE; + STDMETHOD(CreateViewport) (THIS_ LPDIRECT3DVIEWPORT*, IUnknown*) PURE; + STDMETHOD(FindDevice)(THIS_ LPD3DFINDDEVICESEARCH, LPD3DFINDDEVICERESULT) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3D_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3D_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3D_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3D_Initialize(p, a) (p)->lpVtbl->Initialize(p, a) +#define IDirect3D_EnumDevices(p, a, b) (p)->lpVtbl->EnumDevices(p, a, b) +#define IDirect3D_CreateLight(p, a, b) (p)->lpVtbl->CreateLight(p, a, b) +#define IDirect3D_CreateMaterial(p, a, b) (p)->lpVtbl->CreateMaterial(p, a, b) +#define IDirect3D_CreateViewport(p, a, b) (p)->lpVtbl->CreateViewport(p, a, b) +#define IDirect3D_FindDevice(p, a, b) (p)->lpVtbl->FindDevice(p, a, b) +#else +#define IDirect3D_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3D_AddRef(p) (p)->AddRef() +#define IDirect3D_Release(p) (p)->Release() +#define IDirect3D_Initialize(p, a) (p)->Initialize(a) +#define IDirect3D_EnumDevices(p, a, b) (p)->EnumDevices(a, b) +#define IDirect3D_CreateLight(p, a, b) (p)->CreateLight(a, b) +#define IDirect3D_CreateMaterial(p, a, b) (p)->CreateMaterial(a, b) +#define IDirect3D_CreateViewport(p, a, b) (p)->CreateViewport(a, b) +#define IDirect3D_FindDevice(p, a, b) (p)->FindDevice(a, b) +#endif + +/* + * IDirect3D2 + */ +#undef INTERFACE +#define INTERFACE IDirect3D2 +DECLARE_INTERFACE_(IDirect3D2, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3D methods ***/ + STDMETHOD(EnumDevices)(THIS_ LPD3DENUMDEVICESCALLBACK, LPVOID) PURE; + STDMETHOD(CreateLight) (THIS_ LPDIRECT3DLIGHT*, IUnknown*) PURE; + STDMETHOD(CreateMaterial) (THIS_ LPDIRECT3DMATERIAL2*, IUnknown*) PURE; + STDMETHOD(CreateViewport) (THIS_ LPDIRECT3DVIEWPORT2*, IUnknown*) PURE; + STDMETHOD(FindDevice)(THIS_ LPD3DFINDDEVICESEARCH, LPD3DFINDDEVICERESULT) PURE; + + STDMETHOD(CreateDevice)(THIS_ REFCLSID, LPDIRECTDRAWSURFACE, LPDIRECT3DDEVICE2 *) PURE; + +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3D2_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3D2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3D2_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3D2_EnumDevices(p, a, b) (p)->lpVtbl->EnumDevices(p, a, b) +#define IDirect3D2_CreateLight(p, a, b) (p)->lpVtbl->CreateLight(p, a, b) +#define IDirect3D2_CreateMaterial(p, a, b) (p)->lpVtbl->CreateMaterial(p, a, b) +#define IDirect3D2_CreateViewport(p, a, b) (p)->lpVtbl->CreateViewport(p, a, b) +#define IDirect3D2_FindDevice(p, a, b) (p)->lpVtbl->FindDevice(p, a, b) +#define IDirect3D2_CreateDevice(p, a, b, c) (p)->lpVtbl->CreateDevice(p, a, b, c) +#else +#define IDirect3D2_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3D2_AddRef(p) (p)->AddRef() +#define IDirect3D2_Release(p) (p)->Release() +#define IDirect3D2_EnumDevices(p, a, b) (p)->EnumDevices(a, b) +#define IDirect3D2_CreateLight(p, a, b) (p)->CreateLight(a, b) +#define IDirect3D2_CreateMaterial(p, a, b) (p)->CreateMaterial(a, b) +#define IDirect3D2_CreateViewport(p, a, b) (p)->CreateViewport(a, b) +#define IDirect3D2_FindDevice(p, a, b) (p)->FindDevice(a, b) +#define IDirect3D2_CreateDevice(p, a, b, c) (p)->CreateDevice(a, b, c) +#endif + +/* + * IDirect3DDevice + */ +#undef INTERFACE +#define INTERFACE IDirect3DDevice +DECLARE_INTERFACE_(IDirect3DDevice, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3DDevice methods ***/ + STDMETHOD(Initialize) (THIS_ LPDIRECT3D, LPGUID, LPD3DDEVICEDESC) PURE; + STDMETHOD(GetCaps) (THIS_ LPD3DDEVICEDESC, LPD3DDEVICEDESC) PURE; + STDMETHOD(SwapTextureHandles) (THIS_ LPDIRECT3DTEXTURE, LPDIRECT3DTEXTURE) PURE; + STDMETHOD(CreateExecuteBuffer) (THIS_ LPD3DEXECUTEBUFFERDESC, LPDIRECT3DEXECUTEBUFFER*, IUnknown*) PURE; + STDMETHOD(GetStats) (THIS_ LPD3DSTATS) PURE; + STDMETHOD(Execute) (THIS_ LPDIRECT3DEXECUTEBUFFER, LPDIRECT3DVIEWPORT, DWORD) PURE; + STDMETHOD(AddViewport) (THIS_ LPDIRECT3DVIEWPORT) PURE; + STDMETHOD(DeleteViewport) (THIS_ LPDIRECT3DVIEWPORT) PURE; + STDMETHOD(NextViewport) (THIS_ LPDIRECT3DVIEWPORT, LPDIRECT3DVIEWPORT*, DWORD) PURE; + STDMETHOD(Pick) (THIS_ LPDIRECT3DEXECUTEBUFFER, LPDIRECT3DVIEWPORT, DWORD, LPD3DRECT) PURE; + STDMETHOD(GetPickRecords)(THIS_ LPDWORD, LPD3DPICKRECORD) PURE; + STDMETHOD(EnumTextureFormats) (THIS_ LPD3DENUMTEXTUREFORMATSCALLBACK, LPVOID) PURE; + STDMETHOD(CreateMatrix) (THIS_ LPD3DMATRIXHANDLE) PURE; + STDMETHOD(SetMatrix) (THIS_ D3DMATRIXHANDLE, const LPD3DMATRIX) PURE; + STDMETHOD(GetMatrix) (THIS_ D3DMATRIXHANDLE, LPD3DMATRIX) PURE; + STDMETHOD(DeleteMatrix) (THIS_ D3DMATRIXHANDLE) PURE; + STDMETHOD_(HRESULT, BeginScene) (THIS) PURE; + STDMETHOD_(HRESULT, EndScene) (THIS) PURE; + STDMETHOD(GetDirect3D) (THIS_ LPDIRECT3D*) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DDevice_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3DDevice_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DDevice_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DDevice_Initialize(p, a, b, c) (p)->lpVtbl->Initialize(p, a, b, c) +#define IDirect3DDevice_GetCaps(p, a, b) (p)->lpVtbl->GetCaps(p, a, b) +#define IDirect3DDevice_SwapTextureHandles(p, a, b) (p)->lpVtbl->SwapTextureHandles(p, a, b) +#define IDirect3DDevice_CreateExecuteBuffer(p, a, b, c) (p)->lpVtbl->CreateExecuteBuffer(p, a, b, c) +#define IDirect3DDevice_GetStats(p, a) (p)->lpVtbl->GetStats(p, a) +#define IDirect3DDevice_Execute(p, a, b, c) (p)->lpVtbl->Execute(p, a, b, c) +#define IDirect3DDevice_AddViewport(p, a) (p)->lpVtbl->AddViewport(p, a) +#define IDirect3DDevice_DeleteViewport(p, a) (p)->lpVtbl->DeleteViewport(p, a) +#define IDirect3DDevice_NextViewport(p, a, b) (p)->lpVtbl->NextViewport(p, a, b) +#define IDirect3DDevice_Pick(p, a, b, c, d) (p)->lpVtbl->Pick(p, a, b, c, d) +#define IDirect3DDevice_GetPickRecords(p, a, b) (p)->lpVtbl->GetPickRecords(p, a, b) +#define IDirect3DDevice_EnumTextureFormats(p, a, b) (p)->lpVtbl->EnumTextureFormats(p, a, b) +#define IDirect3DDevice_CreateMatrix(p, a) (p)->lpVtbl->CreateMatrix(p, a) +#define IDirect3DDevice_SetMatrix(p, a, b) (p)->lpVtbl->SetMatrix(p, a, b) +#define IDirect3DDevice_GetMatrix(p, a, b) (p)->lpVtbl->GetMatrix(p, a, b) +#define IDirect3DDevice_DeleteMatrix(p, a) (p)->lpVtbl->DeleteMatrix(p, a) +#define IDirect3DDevice_BeginScene(p) (p)->lpVtbl->BeginScene(p) +#define IDirect3DDevice_EndScene(p) (p)->lpVtbl->EndScene(p) +#define IDirect3DDevice_GetDirect3D(p, a) (p)->lpVtbl->GetDirect3D(p, a) +#else +#define IDirect3DDevice_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3DDevice_AddRef(p) (p)->AddRef() +#define IDirect3DDevice_Release(p) (p)->Release() +#define IDirect3DDevice_Initialize(p, a, b, c) (p)->Initialize(a, b, c) +#define IDirect3DDevice_GetCaps(p, a, b) (p)->GetCaps(a, b) +#define IDirect3DDevice_SwapTextureHandles(p, a, b) (p)->SwapTextureHandles(a, b) +#define IDirect3DDevice_CreateExecuteBuffer(p, a, b, c) (p)->CreateExecuteBuffer(a, b, c) +#define IDirect3DDevice_GetStats(p, a) (p)->GetStats(a) +#define IDirect3DDevice_Execute(p, a, b, c) (p)->Execute(a, b, c) +#define IDirect3DDevice_AddViewport(p, a) (p)->AddViewport(a) +#define IDirect3DDevice_DeleteViewport(p, a) (p)->DeleteViewport(a) +#define IDirect3DDevice_NextViewport(p, a, b) (p)->NextViewport(a, b) +#define IDirect3DDevice_Pick(p, a, b, c, d) (p)->Pick(a, b, c, d) +#define IDirect3DDevice_GetPickRecords(p, a, b) (p)->GetPickRecords(a, b) +#define IDirect3DDevice_EnumTextureFormats(p, a, b) (p)->EnumTextureFormats(a, b) +#define IDirect3DDevice_CreateMatrix(p, a) (p)->CreateMatrix(a) +#define IDirect3DDevice_SetMatrix(p, a, b) (p)->SetMatrix(a, b) +#define IDirect3DDevice_GetMatrix(p, a, b) (p)->GetMatrix(a, b) +#define IDirect3DDevice_DeleteMatrix(p, a) (p)->DeleteMatrix(a) +#define IDirect3DDevice_BeginScene(p) (p)->BeginScene() +#define IDirect3DDevice_EndScene(p) (p)->EndScene() +#define IDirect3DDevice_GetDirect3D(p, a) (p)->GetDirect3D(a) +#endif + +/* + * IDirect3DDevice2 + */ +#undef INTERFACE +#define INTERFACE IDirect3DDevice2 +DECLARE_INTERFACE_(IDirect3DDevice2, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3DDevice2 methods ***/ + STDMETHOD(GetCaps) (THIS_ LPD3DDEVICEDESC, LPD3DDEVICEDESC) PURE; + STDMETHOD(SwapTextureHandles) (THIS_ LPDIRECT3DTEXTURE2, LPDIRECT3DTEXTURE2) PURE; + STDMETHOD(GetStats) (THIS_ LPD3DSTATS) PURE; + STDMETHOD(AddViewport) (THIS_ LPDIRECT3DVIEWPORT2) PURE; + STDMETHOD(DeleteViewport) (THIS_ LPDIRECT3DVIEWPORT2) PURE; + STDMETHOD(NextViewport) (THIS_ LPDIRECT3DVIEWPORT2, LPDIRECT3DVIEWPORT2*, DWORD) PURE; + STDMETHOD(EnumTextureFormats) (THIS_ LPD3DENUMTEXTUREFORMATSCALLBACK, LPVOID) PURE; + STDMETHOD_(HRESULT, BeginScene) (THIS) PURE; + STDMETHOD_(HRESULT, EndScene) (THIS) PURE; + STDMETHOD(GetDirect3D) (THIS_ LPDIRECT3D2*) PURE; + + /*** DrawPrimitive API ***/ + STDMETHOD(SetCurrentViewport) (THIS_ LPDIRECT3DVIEWPORT2) PURE; + STDMETHOD(GetCurrentViewport) (THIS_ LPDIRECT3DVIEWPORT2 *) PURE; + + STDMETHOD(SetRenderTarget) (THIS_ LPDIRECTDRAWSURFACE, DWORD) PURE; + STDMETHOD(GetRenderTarget) (THIS_ LPDIRECTDRAWSURFACE *) PURE; + + STDMETHOD(Begin) (THIS_ D3DPRIMITIVETYPE, D3DVERTEXTYPE, DWORD) PURE; + STDMETHOD(BeginIndexed) (THIS_ D3DPRIMITIVETYPE, D3DVERTEXTYPE, LPVOID, DWORD, DWORD) PURE; + STDMETHOD(Vertex) (THIS_ LPVOID) PURE; + STDMETHOD(Index) (THIS_ WORD) PURE; + STDMETHOD(End) (THIS_ DWORD) PURE; + + STDMETHOD(GetRenderState) (THIS_ D3DRENDERSTATETYPE, LPDWORD) PURE; + STDMETHOD(SetRenderState) (THIS_ D3DRENDERSTATETYPE, DWORD) PURE; + STDMETHOD(GetLightState) (THIS_ D3DLIGHTSTATETYPE, LPDWORD) PURE; + STDMETHOD(SetLightState) (THIS_ D3DLIGHTSTATETYPE, DWORD) PURE; + STDMETHOD(SetTransform) (THIS_ D3DTRANSFORMSTATETYPE, LPD3DMATRIX) PURE; + STDMETHOD(GetTransform) (THIS_ D3DTRANSFORMSTATETYPE, LPD3DMATRIX) PURE; + STDMETHOD(MultiplyTransform) (THIS_ D3DTRANSFORMSTATETYPE, LPD3DMATRIX) PURE; + + STDMETHOD(DrawPrimitive) (THIS_ D3DPRIMITIVETYPE, D3DVERTEXTYPE, LPVOID, DWORD, DWORD) PURE; + STDMETHOD(DrawIndexedPrimitive) (THIS_ D3DPRIMITIVETYPE, D3DVERTEXTYPE, LPVOID, DWORD, LPWORD, DWORD, DWORD) PURE; + + STDMETHOD(SetClipStatus) (THIS_ LPD3DCLIPSTATUS) PURE; + STDMETHOD(GetClipStatus) (THIS_ LPD3DCLIPSTATUS) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DDevice2_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3DDevice2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DDevice2_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DDevice2_GetCaps(p, a, b) (p)->lpVtbl->GetCaps(p, a, b) +#define IDirect3DDevice2_SwapTextureHandles(p, a, b) (p)->lpVtbl->SwapTextureHandles(p, a, b) +#define IDirect3DDevice2_GetStats(p, a) (p)->lpVtbl->CreateViewport(p, a) +#define IDirect3DDevice2_AddViewport(p, a) (p)->lpVtbl->AddViewport(p, a) +#define IDirect3DDevice2_DeleteViewport(p, a) (p)->lpVtbl->DeleteViewport(p, a) +#define IDirect3DDevice2_NextViewport(p, a, b) (p)->lpVtbl->NextViewport(p, a, b) +#define IDirect3DDevice2_EnumTextureFormats(p, a, b) (p)->lpVtbl->EnumTextureFormats(p, a, b) +#define IDirect3DDevice2_BeginScene(p) (p)->lpVtbl->BeginScene(p) +#define IDirect3DDevice2_EndScene(p) (p)->lpVtbl->EndScene(p) +#define IDirect3DDevice2_GetDirect3D(p, a) (p)->lpVtbl->GetDirect3D(p, a) + +#define IDirect3DDevice2_SetCurrentViewport(p, a) (p)->lpVtbl->SetCurrentViewport(p, a) +#define IDirect3DDevice2_GetCurrentViewport(p, a) (p)->lpVtbl->GetCurrentViewport(p, a) + +#define IDirect3DDevice2_SetRenderTarget(p, a, b) (p)->lpVtbl->SetRenderTarget(p, a, b) +#define IDirect3DDevice2_GetRenderTarget(p, a) (p)->lpVtbl->GetRenderTarget(p, a) + +#define IDirect3DDevice2_Begin(p, a, b, c) (p)->lpVtbl->Begin(p, a, b, c) +#define IDirect3DDevice2_BeginIndexed(p, a, b, c, d, e) (p)->lpVtbl->Begin(p, a, b, c, d, e) +#define IDirect3DDevice2_Vertex(p, a) (p)->lpVtbl->Vertex(p, a) +#define IDirect3DDevice2_Index(p, a) (p)->lpVtbl->Index(p, a) +#define IDirect3DDevice2_End(p, a) (p)->lpVtbl->End(p, a) + +#define IDirect3DDevice2_GetRenderState(p, a, b) (p)->lpVtbl->GetRenderState(p, a, b) +#define IDirect3DDevice2_SetRenderState(p, a, b) (p)->lpVtbl->SetRenderState(p, a, b) +#define IDirect3DDevice2_GetLightState(p, a, b) (p)->lpVtbl->GetLightState(p, a, b) +#define IDirect3DDevice2_SetLightState(p, a, b) (p)->lpVtbl->SetLightState(p, a, b) +#define IDirect3DDevice2_SetTransform(p, a, b) (p)->lpVtbl->SetTransform(p, a, b) +#define IDirect3DDevice2_GetTransform(p, a, b) (p)->lpVtbl->GetTransform(p, a, b) +#define IDirect3DDevice2_MultiplyTransform(p, a, b) (p)->lpVtbl->MultiplyTransform(p, a, b) + +#define IDirect3DDevice2_DrawPrimitive(p, a, b, c, d, e) (p)->lpVtbl->DrawPrimitive(p, a, b, c, d, e) +#define IDirect3DDevice2_DrawIndexedPrimitive(p, a, b, c, d, e, f, g) \ + (p)->lpVtbl->DrawIndexedPrimitive(p, a, b, c, d, e, f, g) +#define IDirect3DDevice2_SetClipStatus(p, a) (p)->lpVtbl->SetClipStatus(p, a) +#define IDirect3DDevice2_GetClipStatus(p, a) (p)->lpVtbl->GetClipStatus(p, a) +#else +#define IDirect3DDevice2_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3DDevice2_AddRef(p) (p)->AddRef() +#define IDirect3DDevice2_Release(p) (p)->Release() +#define IDirect3DDevice2_GetCaps(p, a, b) (p)->GetCaps(a, b) +#define IDirect3DDevice2_SwapTextureHandles(p, a, b) (p)->SwapTextureHandles(a, b) +#define IDirect3DDevice2_GetStats(p, a) (p)->CreateViewport(a) +#define IDirect3DDevice2_AddViewport(p, a) (p)->AddViewport(a) +#define IDirect3DDevice2_DeleteViewport(p, a) (p)->DeleteViewport(a) +#define IDirect3DDevice2_NextViewport(p, a, b) (p)->NextViewport(a, b) +#define IDirect3DDevice2_EnumTextureFormats(p, a, b) (p)->EnumTextureFormats(a, b) +#define IDirect3DDevice2_BeginScene(p) (p)->BeginScene() +#define IDirect3DDevice2_EndScene(p) (p)->EndScene() +#define IDirect3DDevice2_GetDirect3D(p, a) (p)->GetDirect3D(a) + +#define IDirect3DDevice2_SetCurrentViewport(p, a) (p)->SetCurrentViewport(a) +#define IDirect3DDevice2_GetCurrentViewport(p, a) (p)->GetCurrentViewport(a) + +#define IDirect3DDevice2_SetRenderTarget(p, a, b) (p)->SetRenderTarget(a, b) +#define IDirect3DDevice2_GetRenderTarget(p, a) (p)->GetRenderTarget(a) + +#define IDirect3DDevice2_Begin(p, a, b, c) (p)->Begin(a, b, c) +#define IDirect3DDevice2_BeginIndexed(p, a, b, c, d, e) (p)->Begin(a, b, c, d, e) +#define IDirect3DDevice2_Vertex(p, a) (p)->Vertex(a) +#define IDirect3DDevice2_Index(p, a) (p)->Index(a) +#define IDirect3DDevice2_End(p, a) (p)->End(a) + +#define IDirect3DDevice2_GetRenderState(p, a, b) (p)->GetRenderState(a, b) +#define IDirect3DDevice2_SetRenderState(p, a, b) (p)->SetRenderState(a, b) +#define IDirect3DDevice2_GetLightState(p, a, b) (p)->GetLightState(a, b) +#define IDirect3DDevice2_SetLightState(p, a, b) (p)->SetLightState(a, b) +#define IDirect3DDevice2_SetTransform(p, a, b) (p)->SetTransform(a, b) +#define IDirect3DDevice2_GetTransform(p, a, b) (p)->GetTransform(a, b) +#define IDirect3DDevice2_MultiplyTransform(p, a, b) (p)->MultiplyTransform(a, b) + +#define IDirect3DDevice2_DrawPrimitive(p, a, b, c, d, e) (p)->DrawPrimitive(a, b, c, d, e) +#define IDirect3DDevice2_DrawIndexedPrimitive(p, a, b, c, d, e, f, g) \ + (p)->DrawIndexedPrimitive(a, b, c, d, e, f, g) +#define IDirect3DDevice2_SetClipStatus(p, a) (p)->SetClipStatus(a) +#define IDirect3DDevice2_GetClipStatus(p, a) (p)->GetClipStatus(a) + +#endif + +/* + * IDirect3DExecuteBuffer + */ +#undef INTERFACE +#define INTERFACE IDirect3DExecuteBuffer +DECLARE_INTERFACE_(IDirect3DExecuteBuffer, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3DExecuteBuffer methods ***/ + STDMETHOD(Initialize) (THIS_ LPDIRECT3DDEVICE, LPD3DEXECUTEBUFFERDESC) PURE; + STDMETHOD(Lock) (THIS_ LPD3DEXECUTEBUFFERDESC) PURE; + STDMETHOD_(HRESULT, Unlock) (THIS) PURE; + STDMETHOD(SetExecuteData) (THIS_ LPD3DEXECUTEDATA) PURE; + STDMETHOD(GetExecuteData) (THIS_ LPD3DEXECUTEDATA) PURE; + STDMETHOD(Validate) (THIS_ LPDWORD, LPD3DVALIDATECALLBACK, LPVOID, DWORD) PURE; + STDMETHOD(Optimize) (THIS_ DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DExecuteBuffer_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3DExecuteBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DExecuteBuffer_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DExecuteBuffer_Initialize(p, a, b) (p)->lpVtbl->Initialize(p, a, b) +#define IDirect3DExecuteBuffer_Lock(p, a) (p)->lpVtbl->Lock(p, a) +#define IDirect3DExecuteBuffer_Unlock(p) (p)->lpVtbl->Unlock(p) +#define IDirect3DExecuteBuffer_SetExecuteData(p, a) (p)->lpVtbl->SetExecuteData(p, a) +#define IDirect3DExecuteBuffer_GetExecuteData(p, a) (p)->lpVtbl->GetExecuteData(p, a) +#define IDirect3DExecuteBuffer_Validate(p, a, b, c, d) (p)->lpVtbl->Validate(p, a, b, c, d) +#define IDirect3DExecuteBuffer_Optimize(p, a) (p)->lpVtbl->Optimize(p, a) +#else +#define IDirect3DExecuteBuffer_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3DExecuteBuffer_AddRef(p) (p)->AddRef() +#define IDirect3DExecuteBuffer_Release(p) (p)->Release() +#define IDirect3DExecuteBuffer_Initialize(p, a, b) (p)->Initialize(a, b) +#define IDirect3DExecuteBuffer_Lock(p, a) (p)->Lock(a) +#define IDirect3DExecuteBuffer_Unlock(p) (p)->Unlock() +#define IDirect3DExecuteBuffer_SetExecuteData(p, a) (p)->SetExecuteData(a) +#define IDirect3DExecuteBuffer_GetExecuteData(p, a) (p)->GetExecuteData(a) +#define IDirect3DExecuteBuffer_Validate(p, a, b, c, d) (p)->Validate(a, b, c, d) +#define IDirect3DExecuteBuffer_Optimize(p, a) (p)->Optimize(a) +#endif + +/* + * IDirect3DLight + */ +#undef INTERFACE +#define INTERFACE IDirect3DLight +DECLARE_INTERFACE_(IDirect3DLight, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3DLight methods ***/ + STDMETHOD(Initialize) (THIS_ LPDIRECT3D) PURE; + STDMETHOD(SetLight) (THIS_ LPD3DLIGHT) PURE; + STDMETHOD(GetLight) (THIS_ LPD3DLIGHT) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DLight_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3DLight_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DLight_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DLight_Initialize(p, a) (p)->lpVtbl->Initialize(p, a) +#define IDirect3DLight_SetLight(p, a) (p)->lpVtbl->SetLight(p, a) +#define IDirect3DLight_GetLight(p, a) (p)->lpVtbl->GetLight(p, a) +#else +#define IDirect3DLight_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3DLight_AddRef(p) (p)->AddRef() +#define IDirect3DLight_Release(p) (p)->Release() +#define IDirect3DLight_Initialize(p, a) (p)->Initialize(a) +#define IDirect3DLight_SetLight(p, a) (p)->SetLight(a) +#define IDirect3DLight_GetLight(p, a) (p)->GetLight(a) +#endif + +/* + * IDirect3DMaterial + */ +#undef INTERFACE +#define INTERFACE IDirect3DMaterial +DECLARE_INTERFACE_(IDirect3DMaterial, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3DMaterial methods ***/ + STDMETHOD(Initialize) (THIS_ LPDIRECT3D) PURE; + STDMETHOD(SetMaterial) (THIS_ LPD3DMATERIAL) PURE; + STDMETHOD(GetMaterial) (THIS_ LPD3DMATERIAL) PURE; + STDMETHOD(GetHandle) (THIS_ LPDIRECT3DDEVICE, LPD3DMATERIALHANDLE) PURE; + STDMETHOD_(HRESULT, Reserve) (THIS) PURE; + STDMETHOD_(HRESULT, Unreserve) (THIS) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DMaterial_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3DMaterial_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DMaterial_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DMaterial_Initialize(p, a) (p)->lpVtbl->Initialize(p, a) +#define IDirect3DMaterial_SetMaterial(p, a) (p)->lpVtbl->SetMaterial(p, a) +#define IDirect3DMaterial_GetMaterial(p, a) (p)->lpVtbl->GetMaterial(p, a) +#define IDirect3DMaterial_GetHandle(p, a, b) (p)->lpVtbl->GetHandle(p, a, b) +#define IDirect3DMaterial_Reserve(p) (p)->lpVtbl->Reserve(p) +#define IDirect3DMaterial_Unreserve(p) (p)->lpVtbl->Unreserve(p) +#else +#define IDirect3DMaterial_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3DMaterial_AddRef(p) (p)->AddRef() +#define IDirect3DMaterial_Release(p) (p)->Release() +#define IDirect3DMaterial_Initialize(p, a) (p)->Initialize(a) +#define IDirect3DMaterial_SetMaterial(p, a) (p)->SetMaterial(a) +#define IDirect3DMaterial_GetMaterial(p, a) (p)->GetMaterial(a) +#define IDirect3DMaterial_GetHandle(p, a, b) (p)->GetHandle(a, b) +#define IDirect3DMaterial_Reserve(p) (p)->Reserve() +#define IDirect3DMaterial_Unreserve(p) (p)->Unreserve() +#endif + +/* + * IDirect3DMaterial2 + */ +#undef INTERFACE +#define INTERFACE IDirect3DMaterial2 +DECLARE_INTERFACE_(IDirect3DMaterial2, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3DMaterial2 methods ***/ + STDMETHOD(SetMaterial) (THIS_ LPD3DMATERIAL) PURE; + STDMETHOD(GetMaterial) (THIS_ LPD3DMATERIAL) PURE; + STDMETHOD(GetHandle) (THIS_ LPDIRECT3DDEVICE2, LPD3DMATERIALHANDLE) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DMaterial2_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3DMaterial2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DMaterial2_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DMaterial2_SetMaterial(p, a) (p)->lpVtbl->SetMaterial(p, a) +#define IDirect3DMaterial2_GetMaterial(p, a) (p)->lpVtbl->GetMaterial(p, a) +#define IDirect3DMaterial2_GetHandle(p, a, b) (p)->lpVtbl->GetHandle(p, a, b) +#else +#define IDirect3DMaterial2_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3DMaterial2_AddRef(p) (p)->AddRef() +#define IDirect3DMaterial2_Release(p) (p)->Release() +#define IDirect3DMaterial2_SetMaterial(p, a) (p)->SetMaterial(a) +#define IDirect3DMaterial2_GetMaterial(p, a) (p)->GetMaterial(a) +#define IDirect3DMaterial2_GetHandle(p, a, b) (p)->GetHandle(a, b) +#endif + +/* + * IDirect3DTexture + */ +#undef INTERFACE +#define INTERFACE IDirect3DTexture +DECLARE_INTERFACE_(IDirect3DTexture, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3DTexture methods ***/ + STDMETHOD(Initialize) (THIS_ LPDIRECT3DDEVICE, LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(GetHandle) (THIS_ LPDIRECT3DDEVICE, LPD3DTEXTUREHANDLE) PURE; + STDMETHOD(PaletteChanged) (THIS_ DWORD, DWORD) PURE; + STDMETHOD(Load) (THIS_ LPDIRECT3DTEXTURE) PURE; + STDMETHOD_(HRESULT, Unload) (THIS) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DTexture_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3DTexture_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DTexture_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DTexture_Initialize(p, a, b) (p)->lpVtbl->Initialize(p, a, b) +#define IDirect3DTexture_GetHandle(p, a, b) (p)->lpVtbl->GetHandle(p, a, b) +#define IDirect3DTexture_PaletteChanged(p, a, b) (p)->lpVtbl->PaletteChanged(p, a, b) +#define IDirect3DTexture_Load(p, a) (p)->lpVtbl->Load(p, a) +#define IDirect3DTexture_Unload(p) (p)->lpVtbl->Unload(p) +#else +#define IDirect3DTexture_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3DTexture_AddRef(p) (p)->AddRef() +#define IDirect3DTexture_Release(p) (p)->Release() +#define IDirect3DTexture_Initialize(p, a, b) (p)->Initialize(a, b) +#define IDirect3DTexture_GetHandle(p, a, b) (p)->GetHandle(a, b) +#define IDirect3DTexture_PaletteChanged(p, a, b) (p)->PaletteChanged(a, b) +#define IDirect3DTexture_Load(p, a) (p)->Load(a) +#define IDirect3DTexture_Unload(p) (p)->Unload() +#endif + +/* + * IDirect3DTexture2 + */ +#undef INTERFACE +#define INTERFACE IDirect3DTexture2 +DECLARE_INTERFACE_(IDirect3DTexture2, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3DTexture2 methods ***/ + STDMETHOD(GetHandle) (THIS_ LPDIRECT3DDEVICE2, LPD3DTEXTUREHANDLE) PURE; + STDMETHOD(PaletteChanged) (THIS_ DWORD, DWORD) PURE; + STDMETHOD(Load) (THIS_ LPDIRECT3DTEXTURE2) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DTexture2_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3DTexture2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DTexture2_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DTexture2_GetHandle(p, a, b) (p)->lpVtbl->GetHandle(p, a, b) +#define IDirect3DTexture2_PaletteChanged(p, a, b) (p)->lpVtbl->PaletteChanged(p, a, b) +#define IDirect3DTexture2_Load(p, a) (p)->lpVtbl->Load(p, a) +#else +#define IDirect3DTexture2_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3DTexture2_AddRef(p) (p)->AddRef() +#define IDirect3DTexture2_Release(p) (p)->Release() +#define IDirect3DTexture2_GetHandle(p, a, b) (p)->GetHandle(a, b) +#define IDirect3DTexture2_PaletteChanged(p, a, b) (p)->PaletteChanged(a, b) +#define IDirect3DTexture2_Load(p, a) (p)->Load(a) +#endif + +/* + * IDirect3DViewport + */ +#undef INTERFACE +#define INTERFACE IDirect3DViewport +DECLARE_INTERFACE_(IDirect3DViewport, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3DViewport methods ***/ + STDMETHOD(Initialize) (THIS_ LPDIRECT3D) PURE; + STDMETHOD(GetViewport) (THIS_ LPD3DVIEWPORT) PURE; + STDMETHOD(SetViewport) (THIS_ LPD3DVIEWPORT) PURE; + STDMETHOD(TransformVertices) (THIS_ DWORD, LPD3DTRANSFORMDATA, DWORD, LPDWORD) PURE; + STDMETHOD(LightElements) (THIS_ DWORD, LPD3DLIGHTDATA) PURE; + STDMETHOD(SetBackground) (THIS_ D3DMATERIALHANDLE) PURE; + STDMETHOD(GetBackground) (THIS_ LPD3DMATERIALHANDLE, LPBOOL) PURE; + STDMETHOD(SetBackgroundDepth) (THIS_ LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(GetBackgroundDepth) (THIS_ LPDIRECTDRAWSURFACE*, LPBOOL) PURE; + STDMETHOD(Clear) (THIS_ DWORD, LPD3DRECT, DWORD) PURE; + STDMETHOD(AddLight) (THIS_ LPDIRECT3DLIGHT) PURE; + STDMETHOD(DeleteLight) (THIS_ LPDIRECT3DLIGHT) PURE; + STDMETHOD(NextLight) (THIS_ LPDIRECT3DLIGHT, LPDIRECT3DLIGHT*, DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DViewport_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3DViewport_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DViewport_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DViewport_Initialize(p, a) (p)->lpVtbl->Initialize(p, a) +#define IDirect3DViewport_GetViewport(p, a) (p)->lpVtbl->GetViewport(p, a) +#define IDirect3DViewport_SetViewport(p, a) (p)->lpVtbl->SetViewport(p, a) +#define IDirect3DViewport_TransformVertices(p, a, b, c, d) (p)->lpVtbl->TransformVertices(p, a, b, c, d) +#define IDirect3DViewport_LightElements(p, a, b) (p)->lpVtbl->LightElements(p, a, b) +#define IDirect3DViewport_SetBackground(p, a) (p)->lpVtbl->SetBackground(p, a) +#define IDirect3DViewport_GetBackground(p, a, b) (p)->lpVtbl->GetBackground(p, a, b) +#define IDirect3DViewport_SetBackgroundDepth(p, a) (p)->lpVtbl->SetBackgroundDepth(p, a) +#define IDirect3DViewport_GetBackgroundDepth(p, a, b) (p)->lpVtbl->GetBackgroundDepth(p, a, b) +#define IDirect3DViewport_Clear(p, a, b, c) (p)->lpVtbl->Clear(p, a, b, c) +#define IDirect3DViewport_AddLight(p, a) (p)->lpVtbl->AddLight(p, a) +#define IDirect3DViewport_DeleteLight(p, a) (p)->lpVtbl->DeleteLight(p, a) +#define IDirect3DViewport_NextLight(p, a, b, c) (p)->lpVtbl->NextLight(p, a, b, c) +#else +#define IDirect3DViewport_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3DViewport_AddRef(p) (p)->AddRef() +#define IDirect3DViewport_Release(p) (p)->Release() +#define IDirect3DViewport_Initialize(p, a) (p)->Initialize(a) +#define IDirect3DViewport_GetViewport(p, a) (p)->GetViewport(a) +#define IDirect3DViewport_SetViewport(p, a) (p)->SetViewport(a) +#define IDirect3DViewport_TransformVertices(p, a, b, c, d) (p)->TransformVertices(a, b, c, d) +#define IDirect3DViewport_LightElements(p, a, b) (p)->LightElements(a, b) +#define IDirect3DViewport_SetBackground(p, a) (p)->SetBackground(a) +#define IDirect3DViewport_GetBackground(p, a, b) (p)->GetBackground(a, b) +#define IDirect3DViewport_SetBackgroundDepth(p, a) (p)->SetBackgroundDepth(a) +#define IDirect3DViewport_GetBackgroundDepth(p, a, b) (p)->GetBackgroundDepth(a, b) +#define IDirect3DViewport_Clear(p, a, b, c) (p)->Clear(a, b, c) +#define IDirect3DViewport_AddLight(p, a) (p)->AddLight(a) +#define IDirect3DViewport_DeleteLight(p, a) (p)->DeleteLight(a) +#define IDirect3DViewport_NextLight(p, a, b, c) (p)->NextLight(a, b, c) +#endif + +/* + * IDirect3DViewport2 + */ +#undef INTERFACE +#define INTERFACE IDirect3DViewport2 +DECLARE_INTERFACE_(IDirect3DViewport2, IDirect3DViewport) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID* ppvObj) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + /*** IDirect3DViewport methods ***/ + STDMETHOD(Initialize) (THIS_ LPDIRECT3D) PURE; + STDMETHOD(GetViewport) (THIS_ LPD3DVIEWPORT) PURE; + STDMETHOD(SetViewport) (THIS_ LPD3DVIEWPORT) PURE; + STDMETHOD(TransformVertices) (THIS_ DWORD, LPD3DTRANSFORMDATA, DWORD, LPDWORD) PURE; + STDMETHOD(LightElements) (THIS_ DWORD, LPD3DLIGHTDATA) PURE; + STDMETHOD(SetBackground) (THIS_ D3DMATERIALHANDLE) PURE; + STDMETHOD(GetBackground) (THIS_ LPD3DMATERIALHANDLE, LPBOOL) PURE; + STDMETHOD(SetBackgroundDepth) (THIS_ LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(GetBackgroundDepth) (THIS_ LPDIRECTDRAWSURFACE*, LPBOOL) PURE; + STDMETHOD(Clear) (THIS_ DWORD, LPD3DRECT, DWORD) PURE; + STDMETHOD(AddLight) (THIS_ LPDIRECT3DLIGHT) PURE; + STDMETHOD(DeleteLight) (THIS_ LPDIRECT3DLIGHT) PURE; + STDMETHOD(NextLight) (THIS_ LPDIRECT3DLIGHT, LPDIRECT3DLIGHT*, DWORD) PURE; + /*** IDirect3DViewport2 methods ***/ + STDMETHOD(GetViewport2) (THIS_ LPD3DVIEWPORT2) PURE; + STDMETHOD(SetViewport2) (THIS_ LPD3DVIEWPORT2) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirect3DViewport2_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirect3DViewport2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirect3DViewport2_Release(p) (p)->lpVtbl->Release(p) +#define IDirect3DViewport2_Initialize(p, a) (p)->lpVtbl->Initialize(p, a) +#define IDirect3DViewport2_GetViewport(p, a) (p)->lpVtbl->GetViewport(p, a) +#define IDirect3DViewport2_SetViewport(p, a) (p)->lpVtbl->SetViewport(p, a) +#define IDirect3DViewport2_TransformVertices(p, a, b, c, d) (p)->lpVtbl->TransformVertices(p, a, b, c, d) +#define IDirect3DViewport2_LightElements(p, a, b) (p)->lpVtbl->LightElements(p, a, b) +#define IDirect3DViewport2_SetBackground(p, a) (p)->lpVtbl->SetBackground(p, a) +#define IDirect3DViewport2_GetBackground(p, a, b) (p)->lpVtbl->GetBackground(p, a, b) +#define IDirect3DViewport2_SetBackgroundDepth(p, a) (p)->lpVtbl->SetBackgroundDepth(p, a) +#define IDirect3DViewport2_GetBackgroundDepth(p, a, b) (p)->lpVtbl->GetBackgroundDepth(p, a, b) +#define IDirect3DViewport2_Clear(p, a, b, c) (p)->lpVtbl->Clear(p, a, b, c) +#define IDirect3DViewport2_AddLight(p, a) (p)->lpVtbl->AddLight(p, a) +#define IDirect3DViewport2_DeleteLight(p, a) (p)->lpVtbl->DeleteLight(p, a) +#define IDirect3DViewport2_NextLight(p, a, b, c) (p)->lpVtbl->NextLight(p, a, b, c) +#define IDirect3DViewport2_GetViewport2(p, a) (p)->lpVtbl->GetViewport2(p, a) +#define IDirect3DViewport2_SetViewport2(p, a) (p)->lpVtbl->SetViewport2(p, a) +#else +#define IDirect3DViewport2_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirect3DViewport2_AddRef(p) (p)->AddRef() +#define IDirect3DViewport2_Release(p) (p)->Release() +#define IDirect3DViewport2_Initialize(p, a) (p)->Initialize(a) +#define IDirect3DViewport2_GetViewport(p, a) (p)->GetViewport(a) +#define IDirect3DViewport2_SetViewport(p, a) (p)->SetViewport(a) +#define IDirect3DViewport2_TransformVertices(p, a, b, c, d) (p)->TransformVertices(a, b, c, d) +#define IDirect3DViewport2_LightElements(p, a, b) (p)->LightElements(a, b) +#define IDirect3DViewport2_SetBackground(p, a) (p)->SetBackground(a) +#define IDirect3DViewport2_GetBackground(p, a, b) (p)->GetBackground(a, b) +#define IDirect3DViewport2_SetBackgroundDepth(p, a) (p)->SetBackgroundDepth(a) +#define IDirect3DViewport2_GetBackgroundDepth(p, a, b) (p)->GetBackgroundDepth(a, b) +#define IDirect3DViewport2_Clear(p, a, b, c) (p)->Clear(a, b, c) +#define IDirect3DViewport2_AddLight(p, a) (p)->AddLight(a) +#define IDirect3DViewport2_DeleteLight(p, a) (p)->DeleteLight(a) +#define IDirect3DViewport2_NextLight(p, a, b, c) (p)->NextLight(a, b, c) +#define IDirect3DViewport2_GetViewport2(p, a) (p)->GetViewport2(a) +#define IDirect3DViewport2_SetViewport2(p, a) (p)->SetViewport2(a) +#endif + + +/**************************************************************************** + * + * Flags for IDirect3DDevice::NextViewport + * + ****************************************************************************/ + +/* + * Return the next viewport + */ +#define D3DNEXT_NEXT 0x00000001l + +/* + * Return the first viewport + */ +#define D3DNEXT_HEAD 0x00000002l + +/* + * Return the last viewport + */ +#define D3DNEXT_TAIL 0x00000004l + + +/**************************************************************************** + * + * Flags for DrawPrimitive/DrawIndexedPrimitive + * Also valid for Begin/BeginIndexed + * + ****************************************************************************/ + +/* + * Wait until the device is ready to draw the primitive + * This will cause DP to not return DDERR_WASSTILLDRAWING + */ +#define D3DDP_WAIT 0x00000001l + + +/* + * Hint that the primitives have been clipped by the application. + */ +#define D3DDP_DONOTCLIP 0x00000004l + +/* + * Hint that the extents need not be updated. + */ +#define D3DDP_DONOTUPDATEEXTENTS 0x00000008l + +/* + * Direct3D Errors + * DirectDraw error codes are used when errors not specified here. + */ +#define D3D_OK DD_OK +#define D3DERR_BADMAJORVERSION MAKE_DDHRESULT(700) +#define D3DERR_BADMINORVERSION MAKE_DDHRESULT(701) + +/* + * An invalid device was requested by the application. + */ +#define D3DERR_INVALID_DEVICE MAKE_DDHRESULT(705) +#define D3DERR_INITFAILED MAKE_DDHRESULT(706) + +/* + * SetRenderTarget attempted on a device that was + * QI'd off the render target. + */ +#define D3DERR_DEVICEAGGREGATED MAKE_DDHRESULT(707) + +#define D3DERR_EXECUTE_CREATE_FAILED MAKE_DDHRESULT(710) +#define D3DERR_EXECUTE_DESTROY_FAILED MAKE_DDHRESULT(711) +#define D3DERR_EXECUTE_LOCK_FAILED MAKE_DDHRESULT(712) +#define D3DERR_EXECUTE_UNLOCK_FAILED MAKE_DDHRESULT(713) +#define D3DERR_EXECUTE_LOCKED MAKE_DDHRESULT(714) +#define D3DERR_EXECUTE_NOT_LOCKED MAKE_DDHRESULT(715) + +#define D3DERR_EXECUTE_FAILED MAKE_DDHRESULT(716) +#define D3DERR_EXECUTE_CLIPPED_FAILED MAKE_DDHRESULT(717) + +#define D3DERR_TEXTURE_NO_SUPPORT MAKE_DDHRESULT(720) +#define D3DERR_TEXTURE_CREATE_FAILED MAKE_DDHRESULT(721) +#define D3DERR_TEXTURE_DESTROY_FAILED MAKE_DDHRESULT(722) +#define D3DERR_TEXTURE_LOCK_FAILED MAKE_DDHRESULT(723) +#define D3DERR_TEXTURE_UNLOCK_FAILED MAKE_DDHRESULT(724) +#define D3DERR_TEXTURE_LOAD_FAILED MAKE_DDHRESULT(725) +#define D3DERR_TEXTURE_SWAP_FAILED MAKE_DDHRESULT(726) +#define D3DERR_TEXTURE_LOCKED MAKE_DDHRESULT(727) +#define D3DERR_TEXTURE_NOT_LOCKED MAKE_DDHRESULT(728) +#define D3DERR_TEXTURE_GETSURF_FAILED MAKE_DDHRESULT(729) + +#define D3DERR_MATRIX_CREATE_FAILED MAKE_DDHRESULT(730) +#define D3DERR_MATRIX_DESTROY_FAILED MAKE_DDHRESULT(731) +#define D3DERR_MATRIX_SETDATA_FAILED MAKE_DDHRESULT(732) +#define D3DERR_MATRIX_GETDATA_FAILED MAKE_DDHRESULT(733) +#define D3DERR_SETVIEWPORTDATA_FAILED MAKE_DDHRESULT(734) + +#define D3DERR_INVALIDCURRENTVIEWPORT MAKE_DDHRESULT(735) +#define D3DERR_INVALIDPRIMITIVETYPE MAKE_DDHRESULT(736) +#define D3DERR_INVALIDVERTEXTYPE MAKE_DDHRESULT(737) +#define D3DERR_TEXTURE_BADSIZE MAKE_DDHRESULT(738) +#define D3DERR_INVALIDRAMPTEXTURE MAKE_DDHRESULT(739) + +#define D3DERR_MATERIAL_CREATE_FAILED MAKE_DDHRESULT(740) +#define D3DERR_MATERIAL_DESTROY_FAILED MAKE_DDHRESULT(741) +#define D3DERR_MATERIAL_SETDATA_FAILED MAKE_DDHRESULT(742) +#define D3DERR_MATERIAL_GETDATA_FAILED MAKE_DDHRESULT(743) +#define D3DERR_INVALIDPALETTE MAKE_DDHRESULT(744) + +#define D3DERR_ZBUFF_NEEDS_SYSTEMMEMORY MAKE_DDHRESULT(745) +#define D3DERR_ZBUFF_NEEDS_VIDEOMEMORY MAKE_DDHRESULT(746) +#define D3DERR_SURFACENOTINVIDMEM MAKE_DDHRESULT(747) + +#define D3DERR_LIGHT_SET_FAILED MAKE_DDHRESULT(750) +#define D3DERR_LIGHTHASVIEWPORT MAKE_DDHRESULT(751) +#define D3DERR_LIGHTNOTINTHISVIEWPORT MAKE_DDHRESULT(752) + +#define D3DERR_SCENE_IN_SCENE MAKE_DDHRESULT(760) +#define D3DERR_SCENE_NOT_IN_SCENE MAKE_DDHRESULT(761) +#define D3DERR_SCENE_BEGIN_FAILED MAKE_DDHRESULT(762) +#define D3DERR_SCENE_END_FAILED MAKE_DDHRESULT(763) + +#define D3DERR_INBEGIN MAKE_DDHRESULT(770) +#define D3DERR_NOTINBEGIN MAKE_DDHRESULT(771) +#define D3DERR_NOVIEWPORTS MAKE_DDHRESULT(772) +#define D3DERR_VIEWPORTDATANOTSET MAKE_DDHRESULT(773) +#define D3DERR_VIEWPORTHASNODEVICE MAKE_DDHRESULT(774) +#define D3DERR_NOCURRENTVIEWPORT MAKE_DDHRESULT(775) + +#ifdef __cplusplus +}; +#endif + +#endif /* _D3D_H_ */ + diff --git a/include/vd3dcaps.h b/include/vd3dcaps.h new file mode 100644 index 0000000..660aabb --- /dev/null +++ b/include/vd3dcaps.h @@ -0,0 +1,314 @@ +/*==========================================================================; + * + * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. + * + * File: d3dcaps.h + * Content: Direct3D capabilities include file + * + ***************************************************************************/ + +#ifndef _D3DCAPS_H +#define _D3DCAPS_H + +/* + * Pull in DirectDraw include file automatically: + */ +#include "vddraw.h" + +#pragma pack(4) + +/* Description of capabilities of transform */ + +typedef struct _D3DTRANSFORMCAPS { + DWORD dwSize; + DWORD dwCaps; +} D3DTRANSFORMCAPS, *LPD3DTRANSFORMCAPS; + +#define D3DTRANSFORMCAPS_CLIP 0x00000001L /* Will clip whilst transforming */ + +/* Description of capabilities of lighting */ + +typedef struct _D3DLIGHTINGCAPS { + DWORD dwSize; + DWORD dwCaps; /* Lighting caps */ + DWORD dwLightingModel; /* Lighting model - RGB or mono */ + DWORD dwNumLights; /* Number of lights that can be handled */ +} D3DLIGHTINGCAPS, *LPD3DLIGHTINGCAPS; + +#define D3DLIGHTINGMODEL_RGB 0x00000001L +#define D3DLIGHTINGMODEL_MONO 0x00000002L + +#define D3DLIGHTCAPS_POINT 0x00000001L /* Point lights supported */ +#define D3DLIGHTCAPS_SPOT 0x00000002L /* Spot lights supported */ +#define D3DLIGHTCAPS_DIRECTIONAL 0x00000004L /* Directional lights supported */ +#define D3DLIGHTCAPS_PARALLELPOINT 0x00000008L /* Parallel point lights supported */ + +/* Description of capabilities for each primitive type */ + +typedef struct _D3DPrimCaps { + DWORD dwSize; + DWORD dwMiscCaps; /* Capability flags */ + DWORD dwRasterCaps; + DWORD dwZCmpCaps; + DWORD dwSrcBlendCaps; + DWORD dwDestBlendCaps; + DWORD dwAlphaCmpCaps; + DWORD dwShadeCaps; + DWORD dwTextureCaps; + DWORD dwTextureFilterCaps; + DWORD dwTextureBlendCaps; + DWORD dwTextureAddressCaps; + DWORD dwStippleWidth; /* maximum width and height of */ + DWORD dwStippleHeight; /* of supported stipple (up to 32x32) */ +} D3DPRIMCAPS, *LPD3DPRIMCAPS; + +/* D3DPRIMCAPS dwMiscCaps */ + +#define D3DPMISCCAPS_MASKPLANES 0x00000001L +#define D3DPMISCCAPS_MASKZ 0x00000002L +#define D3DPMISCCAPS_LINEPATTERNREP 0x00000004L +#define D3DPMISCCAPS_CONFORMANT 0x00000008L +#define D3DPMISCCAPS_CULLNONE 0x00000010L +#define D3DPMISCCAPS_CULLCW 0x00000020L +#define D3DPMISCCAPS_CULLCCW 0x00000040L + +/* D3DPRIMCAPS dwRasterCaps */ + +#define D3DPRASTERCAPS_DITHER 0x00000001L +#define D3DPRASTERCAPS_ROP2 0x00000002L +#define D3DPRASTERCAPS_XOR 0x00000004L +#define D3DPRASTERCAPS_PAT 0x00000008L +#define D3DPRASTERCAPS_ZTEST 0x00000010L +#define D3DPRASTERCAPS_SUBPIXEL 0x00000020L +#define D3DPRASTERCAPS_SUBPIXELX 0x00000040L +#define D3DPRASTERCAPS_FOGVERTEX 0x00000080L +#define D3DPRASTERCAPS_FOGTABLE 0x00000100L +#define D3DPRASTERCAPS_STIPPLE 0x00000200L +#define D3DPRASTERCAPS_ANTIALIASSORTDEPENDENT 0x00000400L +#define D3DPRASTERCAPS_ANTIALIASSORTINDEPENDENT 0x00000800L +#define D3DPRASTERCAPS_ANTIALIASEDGES 0x00001000L +#define D3DPRASTERCAPS_MIPMAPLODBIAS 0x00002000L +#define D3DPRASTERCAPS_ZBIAS 0x00004000L +#define D3DPRASTERCAPS_ZBUFFERLESSHSR 0x00008000L +#define D3DPRASTERCAPS_FOGRANGE 0x00010000L +#define D3DPRASTERCAPS_ANISOTROPY 0x00020000L + +/* D3DPRIMCAPS dwZCmpCaps, dwAlphaCmpCaps */ + +#define D3DPCMPCAPS_NEVER 0x00000001L +#define D3DPCMPCAPS_LESS 0x00000002L +#define D3DPCMPCAPS_EQUAL 0x00000004L +#define D3DPCMPCAPS_LESSEQUAL 0x00000008L +#define D3DPCMPCAPS_GREATER 0x00000010L +#define D3DPCMPCAPS_NOTEQUAL 0x00000020L +#define D3DPCMPCAPS_GREATEREQUAL 0x00000040L +#define D3DPCMPCAPS_ALWAYS 0x00000080L + +/* D3DPRIMCAPS dwSourceBlendCaps, dwDestBlendCaps */ + +#define D3DPBLENDCAPS_ZERO 0x00000001L +#define D3DPBLENDCAPS_ONE 0x00000002L +#define D3DPBLENDCAPS_SRCCOLOR 0x00000004L +#define D3DPBLENDCAPS_INVSRCCOLOR 0x00000008L +#define D3DPBLENDCAPS_SRCALPHA 0x00000010L +#define D3DPBLENDCAPS_INVSRCALPHA 0x00000020L +#define D3DPBLENDCAPS_DESTALPHA 0x00000040L +#define D3DPBLENDCAPS_INVDESTALPHA 0x00000080L +#define D3DPBLENDCAPS_DESTCOLOR 0x00000100L +#define D3DPBLENDCAPS_INVDESTCOLOR 0x00000200L +#define D3DPBLENDCAPS_SRCALPHASAT 0x00000400L +#define D3DPBLENDCAPS_BOTHSRCALPHA 0x00000800L +#define D3DPBLENDCAPS_BOTHINVSRCALPHA 0x00001000L + +/* D3DPRIMCAPS dwShadeCaps */ + +#define D3DPSHADECAPS_COLORFLATMONO 0x00000001L +#define D3DPSHADECAPS_COLORFLATRGB 0x00000002L +#define D3DPSHADECAPS_COLORGOURAUDMONO 0x00000004L +#define D3DPSHADECAPS_COLORGOURAUDRGB 0x00000008L +#define D3DPSHADECAPS_COLORPHONGMONO 0x00000010L +#define D3DPSHADECAPS_COLORPHONGRGB 0x00000020L + +#define D3DPSHADECAPS_SPECULARFLATMONO 0x00000040L +#define D3DPSHADECAPS_SPECULARFLATRGB 0x00000080L +#define D3DPSHADECAPS_SPECULARGOURAUDMONO 0x00000100L +#define D3DPSHADECAPS_SPECULARGOURAUDRGB 0x00000200L +#define D3DPSHADECAPS_SPECULARPHONGMONO 0x00000400L +#define D3DPSHADECAPS_SPECULARPHONGRGB 0x00000800L + +#define D3DPSHADECAPS_ALPHAFLATBLEND 0x00001000L +#define D3DPSHADECAPS_ALPHAFLATSTIPPLED 0x00002000L +#define D3DPSHADECAPS_ALPHAGOURAUDBLEND 0x00004000L +#define D3DPSHADECAPS_ALPHAGOURAUDSTIPPLED 0x00008000L +#define D3DPSHADECAPS_ALPHAPHONGBLEND 0x00010000L +#define D3DPSHADECAPS_ALPHAPHONGSTIPPLED 0x00020000L + +#define D3DPSHADECAPS_FOGFLAT 0x00040000L +#define D3DPSHADECAPS_FOGGOURAUD 0x00080000L +#define D3DPSHADECAPS_FOGPHONG 0x00100000L + +/* D3DPRIMCAPS dwTextureCaps */ + +#define D3DPTEXTURECAPS_PERSPECTIVE 0x00000001L +#define D3DPTEXTURECAPS_POW2 0x00000002L +#define D3DPTEXTURECAPS_ALPHA 0x00000004L +#define D3DPTEXTURECAPS_TRANSPARENCY 0x00000008L +#define D3DPTEXTURECAPS_BORDER 0x00000010L +#define D3DPTEXTURECAPS_SQUAREONLY 0x00000020L + +/* D3DPRIMCAPS dwTextureFilterCaps */ + +#define D3DPTFILTERCAPS_NEAREST 0x00000001L +#define D3DPTFILTERCAPS_LINEAR 0x00000002L +#define D3DPTFILTERCAPS_MIPNEAREST 0x00000004L +#define D3DPTFILTERCAPS_MIPLINEAR 0x00000008L +#define D3DPTFILTERCAPS_LINEARMIPNEAREST 0x00000010L +#define D3DPTFILTERCAPS_LINEARMIPLINEAR 0x00000020L + +/* D3DPRIMCAPS dwTextureBlendCaps */ + +#define D3DPTBLENDCAPS_DECAL 0x00000001L +#define D3DPTBLENDCAPS_MODULATE 0x00000002L +#define D3DPTBLENDCAPS_DECALALPHA 0x00000004L +#define D3DPTBLENDCAPS_MODULATEALPHA 0x00000008L +#define D3DPTBLENDCAPS_DECALMASK 0x00000010L +#define D3DPTBLENDCAPS_MODULATEMASK 0x00000020L +#define D3DPTBLENDCAPS_COPY 0x00000040L +#define D3DPTBLENDCAPS_ADD 0x00000080L + +/* D3DPRIMCAPS dwTextureAddressCaps */ +#define D3DPTADDRESSCAPS_WRAP 0x00000001L +#define D3DPTADDRESSCAPS_MIRROR 0x00000002L +#define D3DPTADDRESSCAPS_CLAMP 0x00000004L +#define D3DPTADDRESSCAPS_BORDER 0x00000008L +#define D3DPTADDRESSCAPS_INDEPENDENTUV 0x00000010L + +/* + * Description for a device. + * This is used to describe a device that is to be created or to query + * the current device. + */ +typedef struct _D3DDeviceDesc { + DWORD dwSize; /* Size of D3DDEVICEDESC structure */ + DWORD dwFlags; /* Indicates which fields have valid data */ + D3DCOLORMODEL dcmColorModel; /* Color model of device */ + DWORD dwDevCaps; /* Capabilities of device */ + D3DTRANSFORMCAPS dtcTransformCaps; /* Capabilities of transform */ + BOOL bClipping; /* Device can do 3D clipping */ + D3DLIGHTINGCAPS dlcLightingCaps; /* Capabilities of lighting */ + D3DPRIMCAPS dpcLineCaps; + D3DPRIMCAPS dpcTriCaps; + DWORD dwDeviceRenderBitDepth; /* One of DDBB_8, 16, etc.. */ + DWORD dwDeviceZBufferBitDepth;/* One of DDBD_16, 32, etc.. */ + DWORD dwMaxBufferSize; /* Maximum execute buffer size */ + DWORD dwMaxVertexCount; /* Maximum vertex count */ + // *** New fields for DX5 *** // + + // Width and height caps are 0 for legacy HALs. + DWORD dwMinTextureWidth, dwMinTextureHeight; + DWORD dwMaxTextureWidth, dwMaxTextureHeight; + DWORD dwMinStippleWidth, dwMaxStippleWidth; + DWORD dwMinStippleHeight, dwMaxStippleHeight; +} D3DDEVICEDESC, *LPD3DDEVICEDESC; + +#define D3DDEVICEDESCSIZE (sizeof(D3DDEVICEDESC)) + +typedef HRESULT (FAR PASCAL * LPD3DENUMDEVICESCALLBACK)(LPGUID lpGuid, LPSTR lpDeviceDescription, LPSTR lpDeviceName, LPD3DDEVICEDESC, LPD3DDEVICEDESC, LPVOID); + +/* D3DDEVICEDESC dwFlags indicating valid fields */ + +#define D3DDD_COLORMODEL 0x00000001L /* dcmColorModel is valid */ +#define D3DDD_DEVCAPS 0x00000002L /* dwDevCaps is valid */ +#define D3DDD_TRANSFORMCAPS 0x00000004L /* dtcTransformCaps is valid */ +#define D3DDD_LIGHTINGCAPS 0x00000008L /* dlcLightingCaps is valid */ +#define D3DDD_BCLIPPING 0x00000010L /* bClipping is valid */ +#define D3DDD_LINECAPS 0x00000020L /* dpcLineCaps is valid */ +#define D3DDD_TRICAPS 0x00000040L /* dpcTriCaps is valid */ +#define D3DDD_DEVICERENDERBITDEPTH 0x00000080L /* dwDeviceRenderBitDepth is valid */ +#define D3DDD_DEVICEZBUFFERBITDEPTH 0x00000100L /* dwDeviceZBufferBitDepth is valid */ +#define D3DDD_MAXBUFFERSIZE 0x00000200L /* dwMaxBufferSize is valid */ +#define D3DDD_MAXVERTEXCOUNT 0x00000400L /* dwMaxVertexCount is valid */ + +/* D3DDEVICEDESC dwDevCaps flags */ + +#define D3DDEVCAPS_FLOATTLVERTEX 0x00000001L /* Device accepts floating point */ + /* for post-transform vertex data */ +#define D3DDEVCAPS_SORTINCREASINGZ 0x00000002L /* Device needs data sorted for increasing Z*/ +#define D3DDEVCAPS_SORTDECREASINGZ 0X00000004L /* Device needs data sorted for decreasing Z*/ +#define D3DDEVCAPS_SORTEXACT 0x00000008L /* Device needs data sorted exactly */ + +#define D3DDEVCAPS_EXECUTESYSTEMMEMORY 0x00000010L /* Device can use execute buffers from system memory */ +#define D3DDEVCAPS_EXECUTEVIDEOMEMORY 0x00000020L /* Device can use execute buffers from video memory */ +#define D3DDEVCAPS_TLVERTEXSYSTEMMEMORY 0x00000040L /* Device can use TL buffers from system memory */ +#define D3DDEVCAPS_TLVERTEXVIDEOMEMORY 0x00000080L /* Device can use TL buffers from video memory */ +#define D3DDEVCAPS_TEXTURESYSTEMMEMORY 0x00000100L /* Device can texture from system memory */ +#define D3DDEVCAPS_TEXTUREVIDEOMEMORY 0x00000200L /* Device can texture from device memory */ +#define D3DDEVCAPS_DRAWPRIMTLVERTEX 0x00000400L /* Device can draw TLVERTEX primitives */ +#define D3DDEVCAPS_CANRENDERAFTERFLIP 0x00000800L /* Device can render without waiting for flip to complete */ +#define D3DDEVCAPS_TEXTURENONLOCALVIDMEM 0x00001000L /* Device can texture from nonlocal video memory */ + +#define D3DFDS_COLORMODEL 0x00000001L /* Match color model */ +#define D3DFDS_GUID 0x00000002L /* Match guid */ +#define D3DFDS_HARDWARE 0x00000004L /* Match hardware/software */ +#define D3DFDS_TRIANGLES 0x00000008L /* Match in triCaps */ +#define D3DFDS_LINES 0x00000010L /* Match in lineCaps */ +#define D3DFDS_MISCCAPS 0x00000020L /* Match primCaps.dwMiscCaps */ +#define D3DFDS_RASTERCAPS 0x00000040L /* Match primCaps.dwRasterCaps */ +#define D3DFDS_ZCMPCAPS 0x00000080L /* Match primCaps.dwZCmpCaps */ +#define D3DFDS_ALPHACMPCAPS 0x00000100L /* Match primCaps.dwAlphaCmpCaps */ +#define D3DFDS_SRCBLENDCAPS 0x00000200L /* Match primCaps.dwSourceBlendCaps */ +#define D3DFDS_DSTBLENDCAPS 0x00000400L /* Match primCaps.dwDestBlendCaps */ +#define D3DFDS_SHADECAPS 0x00000800L /* Match primCaps.dwShadeCaps */ +#define D3DFDS_TEXTURECAPS 0x00001000L /* Match primCaps.dwTextureCaps */ +#define D3DFDS_TEXTUREFILTERCAPS 0x00002000L /* Match primCaps.dwTextureFilterCaps */ +#define D3DFDS_TEXTUREBLENDCAPS 0x00004000L /* Match primCaps.dwTextureBlendCaps */ +#define D3DFDS_TEXTUREADDRESSCAPS 0x00008000L /* Match primCaps.dwTextureBlendCaps */ + +/* + * FindDevice arguments + */ +typedef struct _D3DFINDDEVICESEARCH { + DWORD dwSize; + DWORD dwFlags; + BOOL bHardware; + D3DCOLORMODEL dcmColorModel; + GUID guid; + DWORD dwCaps; + D3DPRIMCAPS dpcPrimCaps; +} D3DFINDDEVICESEARCH, *LPD3DFINDDEVICESEARCH; + +typedef struct _D3DFINDDEVICERESULT { + DWORD dwSize; + GUID guid; /* guid which matched */ + D3DDEVICEDESC ddHwDesc; /* hardware D3DDEVICEDESC */ + D3DDEVICEDESC ddSwDesc; /* software D3DDEVICEDESC */ +} D3DFINDDEVICERESULT, *LPD3DFINDDEVICERESULT; + + +/* + * Description of execute buffer. + */ +typedef struct _D3DExecuteBufferDesc { + DWORD dwSize; /* size of this structure */ + DWORD dwFlags; /* flags indicating which fields are valid */ + DWORD dwCaps; /* capabilities of execute buffer */ + DWORD dwBufferSize; /* size of execute buffer data */ + LPVOID lpData; /* pointer to actual data */ +} D3DEXECUTEBUFFERDESC, *LPD3DEXECUTEBUFFERDESC; + +/* D3DEXECUTEBUFFER dwFlags indicating valid fields */ + +#define D3DDEB_BUFSIZE 0x00000001l /* buffer size valid */ +#define D3DDEB_CAPS 0x00000002l /* caps valid */ +#define D3DDEB_LPDATA 0x00000004l /* lpData valid */ + +/* D3DEXECUTEBUFFER dwCaps */ + +#define D3DDEBCAPS_SYSTEMMEMORY 0x00000001l /* buffer in system memory */ +#define D3DDEBCAPS_VIDEOMEMORY 0x00000002l /* buffer in device memory */ +#define D3DDEBCAPS_MEM (D3DDEBCAPS_SYSTEMMEMORY|D3DDEBCAPS_VIDEOMEMORY) + +#pragma pack() + +#endif /* _D3DCAPS_H_ */ + diff --git a/include/vd3di.h b/include/vd3di.h new file mode 100644 index 0000000..cf5601a --- /dev/null +++ b/include/vd3di.h @@ -0,0 +1,794 @@ +/*==========================================================================; + * + * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. + * + * File: d3di.h + * Content: Direct3D internal include file + *@@BEGIN_MSINTERNAL + * + * $Id$ + * + * History: + * Date By Reason + * ==== == ====== + * 05/11/95 stevela Initial rev with this header. + * 11/11/95 stevela Light code changed. + * 21/11/95 colinmc Made Direct3D aggregatable + * (so it can be QI'd off DirectDraw). + * 23/11/95 colinmc Made Direct3D textures and devices aggregatable + * (QI'd off DirectDrawSurfaces). + * 07/12/95 stevela Merged in Colin's changes. + * 10/12/95 stevela Removed AGGREGATE_D3D. + * Removed Validate macros from here. Now in d3dpr.h + * 02/03/96 colinmc Minor build fix + * 17/04/96 stevela Use ddraw.h externally and ddrawp.h internally + *@@END_MSINTERNAL + * + ***************************************************************************/ + +#ifndef _D3DI_H +#define _D3DI_H + +//@@BEGIN_MSINTERNAL +#include "vddrawp.h" +#if 0 +//@@END_MSINTERNAL +#include "vddraw.h" +//@@BEGIN_MSINTERNAL +#endif +//@@END_MSINTERNAL +#include "vd3d.h" + +// @@BEGIN_MSINTERNAL +#if !defined(BUILD_RLAPI) && !defined(BUILD_DDDDK) +#include "vddrawi.h" + +#include "rlreg.h" +#include "lists.h" +#include "object.h" + + +/* + INDEX_BATCH_SCALE is the constant which is used by DrawIndexedPrim + to deterimine if the number of primitives being drawn is small + relative to the number of vertices being passed. If it is then + the prims are dereferenced in batches and sent to DrawPrim. +*/ +#define INDEX_BATCH_SCALE 2 + +/* +typedef D3DCOLORMODEL D3DCOLORMODEL; + +#define D3DCOLOR_RAMP D3DCOLOR_RAMP +#define D3DCOLOR_RGB D3DCOLOR_RGB +#define D3D_COLORMODEL D3D_COLORMODEL +*/ +#endif /* !BUILD_RLAPI */ +// @@END_MSINTERNAL + +typedef DWORD D3DI_BUFFERHANDLE, *LPD3DI_BUFFERHANDLE; + +/* + * Internal version of executedata + */ +typedef struct _D3DI_ExecuteData { + DWORD dwSize; + D3DI_BUFFERHANDLE dwHandle; /* Handle allocated by driver */ + DWORD dwVertexOffset; + DWORD dwVertexCount; + DWORD dwInstructionOffset; + DWORD dwInstructionLength; + DWORD dwHVertexOffset; + D3DSTATUS dsStatus; /* Status after execute */ +} D3DI_EXECUTEDATA, *LPD3DI_EXECUTEDATA; + +/* + * Internal version of lightdata and constants for flags + */ + +#define D3DLIGHTI_ATT0_IS_NONZERO (0x00010000) +#define D3DLIGHTI_ATT1_IS_NONZERO (0x00020000) +#define D3DLIGHTI_ATT2_IS_NONZERO (0x00040000) +#define D3DLIGHTI_LINEAR_FALLOFF (0x00080000) +#define D3DLIGHTI_UNIT_SCALE (0x00100000) +#define D3DLIGHTI_LIGHT_AT_EYE (0x00200000) + +typedef struct _D3DI_LIGHT { + D3DLIGHTTYPE type; + DWORD version; /* matches number on D3DLIGHT struct */ + BOOL valid; + D3DVALUE red, green, blue, shade; + D3DVECTOR position; + D3DVECTOR model_position; + D3DVECTOR direction; + D3DVECTOR model_direction; + D3DVECTOR halfway; + D3DVECTOR model_eye; /* direction from eye in model space */ + D3DVECTOR model_scale; /* model scale for proper range computations */ + D3DVALUE range; + D3DVALUE range_squared; + D3DVALUE falloff; + D3DVALUE attenuation0; + D3DVALUE attenuation1; + D3DVALUE attenuation2; + D3DVALUE cos_theta_by_2; + D3DVALUE cos_phi_by_2; + DWORD flags; +} D3DI_LIGHT, *LPD3DI_LIGHT; + +// @@BEGIN_MSINTERNAL +#if !defined(BUILD_RLAPI) && !defined(BUILD_DDDDK) +#ifndef BUILD_HEL +#ifdef BUILD_D3D_LAYER +#include "driver.h" +#endif + +typedef struct IUnknownVtbl D3DUNKNOWNCALLBACKS, *LPD3DUNKNOWNCALLBACKS; +typedef struct ID3DObjectVtbl D3DOBJECTVTBL, *LPD3DOBJECTVTBL; +typedef struct IDirect3DVtbl DIRECT3DCALLBACKS, *LPDIRECT3DCALLBACKS; +typedef struct IDirect3D2Vtbl DIRECT3D2CALLBACKS, *LPDIRECT3D2CALLBACKS; +typedef struct IDirect3DDeviceVtbl DIRECT3DDEVICECALLBACKS, *LPDIRECT3DDEVICECALLBACKS; +typedef struct IDirect3DDevice2Vtbl DIRECT3DDEVICE2CALLBACKS, *LPDIRECT3DDEVICE2CALLBACKS; +typedef struct IDirect3DExecuteBufferVtbl DIRECT3DEXECUTEBUFFERCALLBACKS, *LPDIRECT3DEXECUTEBUFFERCALLBACKS; +typedef struct IDirect3DLightVtbl DIRECT3DLIGHTCALLBACKS, *LPDIRECT3DLIGHTCALLBACKS; +typedef struct IDirect3DMaterialVtbl DIRECT3DMATERIALCALLBACKS, *LPDIRECT3DMATERIALCALLBACKS; +typedef struct IDirect3DMaterial2Vtbl DIRECT3DMATERIAL2CALLBACKS, *LPDIRECT3DMATERIAL2CALLBACKS; +typedef struct IDirect3DTextureVtbl DIRECT3DTEXTURECALLBACKS, *LPDIRECT3DTEXTURECALLBACKS; +typedef struct IDirect3DTexture2Vtbl DIRECT3DTEXTURE2CALLBACKS, *LPDIRECT3DTEXTURE2CALLBACKS; +typedef struct IDirect3DViewport2Vtbl DIRECT3DVIEWPORT2CALLBACKS, *LPDIRECT3DVIEWPORT2CALLBACKS; + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _DIRECT3DI *LPDIRECT3DI; +typedef struct _DIRECT3DDEVICEI *LPDIRECT3DDEVICEI; +typedef struct _DIRECT3DEXECUTEBUFFERI *LPDIRECT3DEXECUTEBUFFERI; +typedef struct _DIRECT3DLIGHTI *LPDIRECT3DLIGHTI; +typedef struct _DIRECT3DMATERIALI *LPDIRECT3DMATERIALI; +typedef struct _DIRECT3DTEXTUREI *LPDIRECT3DTEXTUREI; +typedef struct _DIRECT3DVIEWPORTI *LPDIRECT3DVIEWPORTI; + +/* + * If we have an aggregate Direct3D we need a structure to + * represent an interface distinct from the underlying + * object. This is that structure. None of this would be necessary + * in C++ + */ +typedef struct _DIRECT3DUNKNOWNI +{ + LPD3DUNKNOWNCALLBACKS lpVtbl; + LPDIRECT3DI lpObj; +} DIRECT3DUNKNOWNI; +typedef struct _DIRECT3DUNKNOWNI *LPDIRECT3DUNKNOWNI; + +typedef struct _DIRECT3DOLDI +{ + LPDIRECT3DCALLBACKS lpVtbl; + LPDIRECT3DI lpObj; +} DIRECT3DOLDI; +typedef struct _DIRECT3DOLDI *LPDIRECT3DOLDI; + +/* + * Internal version of Direct3D object; it has data after the vtable + */ +typedef struct _DIRECT3DI +{ + /*** Object Interface ***/ + LPDIRECT3D2CALLBACKS lpVtbl; /* Pointer to callbacks */ + int refCnt; /* Reference count object */ + + /*** Object Relations ***/ + /* Devices */ + int numDevs;/* Number of devices */ + LIST_ROOT(_devices, _DIRECT3DDEVICEI) devices; + /* Associated IDirect3DDevices */ + + /* Viewports */ + int numViewports; /* Number of viewports */ + LIST_ROOT(_viewports, _DIRECT3DVIEWPORTI) viewports; + /* Created IDirect3DViewports */ + + /* Lights */ + int numLights; /* Number of lights */ + LIST_ROOT(_lights, _DIRECT3DLIGHTI) lights; + /* Created IDirect3DLights */ + + /* Materials */ + int numMaterials; /* Number of materials */ + LIST_ROOT(_materials, _DIRECT3DMATERIALI) materials; + /* Created IDirect3DMaterials */ + + /*** Object Data ***/ + unsigned long v_next; /* id of next viewport to be created */ + RLDDIRegistry* lpReg; /* Registry */ + + /* + * DirectDraw Interface + */ + LPDDRAWI_DIRECTDRAW_INT lpDDInt; + + /* + * The special IUnknown interface for the aggregate that does + * not punt to the parent object. + */ + LPUNKNOWN lpOwningIUnknown; /* The owning IUnknown */ + DIRECT3DUNKNOWNI lpThisIUnknown; /* Our IUnknown interface */ + DIRECT3DOLDI lpThisIDirect3D; /* Our IDirect3D interface */ + + DWORD dwVersion; /* 1 for legacy D3D, 2 for D3D2 */ +} DIRECT3DI; + +/* + * If we have an aggreate Direct3DDevice we need a structure to + * represent an interface distinct from the underlying + * object. This is that structure. + */ +typedef struct _DIRECT3DDEVICEUNKNOWNI +{ + LPDIRECT3DDEVICE2CALLBACKS lpVtbl; + LPDIRECT3DDEVICEI lpObj; +} DIRECT3DDEVICEUNKNOWNI; +typedef struct _DIRECT3DDEVICEUNKNOWNI *LPDIRECT3DDEVICEUNKNOWNI; + +typedef struct _DIRECT3DDEVICEOLDI +{ + LPDIRECT3DDEVICECALLBACKS lpVtbl; + LPDIRECT3DDEVICEI lpObj; +} DIRECT3DDEVICEOLDI; +typedef struct _DIRECT3DDEVICEOLDI *LPDIRECT3DDEVICEOLDI; + +/* + * Internal version of Direct3DDevice object; it has data after the vtable + */ + +#include "vd3dhal.h" + +typedef RLDDIDriver* (*RLDDIDDrawCreateDriverFn)( + LPDDRAWI_DIRECTDRAW_INT lpDDInt, + LPDIRECTDRAWSURFACE lpDDS, + LPDIRECTDRAWSURFACE lpZ, + LPDIRECTDRAWPALETTE lpPal, + LPDIRECT3DDEVICEI); + +typedef HRESULT (*RLDDIGetCapsFn)(LPD3DDEVICEDESC*, LPD3DDEVICEDESC*); +typedef void (*RLDDIInitFn)(RLDDIMallocFn, RLDDIReallocFn, RLDDIFreeFn, RLDDIRaiseFn, RLDDIValue**, int, int); +typedef void (*RLDDIPushDriverFn)(RLDDIDriverStack*, RLDDIDriver*); +typedef void (*RLDDIPopDriverFn)(RLDDIDriverStack*); + +struct _RLDDIGenRasDriver; +typedef struct _RLDDIGenRasDriver* LPRLDDIGENRASDRIVER; +struct _RLDDIDDrawDriver; +typedef struct _RLDDIDDrawDriver* LPRLDDIDDRAWDRIVER; + +struct _RLDDITransformDriver; +typedef struct _RLDDITransformDriver* LPRLDDITRANSFORMDRIVER; + +typedef HRESULT (*PFNLOCKTEXTURE)(LPRLDDIGENRASDRIVER driver); +typedef void (*PFNUNLOCKTEXTURE)(LPRLDDIGENRASDRIVER driver); +typedef int (*PFNSETFILLPARAMS)(LPRLDDIGENRASDRIVER driver, D3DSTATE* in_params, + int count); +typedef HRESULT (*PFNREALLOCALIGNED)(void** p_inout, size_t size); +typedef HRESULT (*PFNMALLOCALIGNED)(void** p_inout, size_t size); +typedef HRESULT (*PFNFREEALIGNED)(void* p_inout); +typedef HRESULT (*PFNDRVSETRENDERTARGET)(LPDIRECTDRAWSURFACE, LPDIRECTDRAWSURFACE, + LPDIRECTDRAWPALETTE, LPDIRECT3DDEVICEI); +typedef HRESULT (*PFNDOFLUSHBEGINEND)(LPDIRECT3DDEVICE2, DWORD); +typedef HRESULT (*PFNDRAWPRIMITIVES)(LPDIRECT3DDEVICEI, LPD3DTLVERTEX, LPVOID, LPD3DINSTRUCTION, DWORD); + + +typedef struct _D3DI_TEXTUREBLOCK +{ + LIST_MEMBER(_D3DI_TEXTUREBLOCK) list; + /* Next block in IDirect3DTexture */ + LIST_MEMBER(_D3DI_TEXTUREBLOCK) devList; + /* Next block in IDirect3DDevice */ + LPDIRECT3DDEVICEI lpD3DDeviceI; + LPDIRECT3DTEXTUREI lpD3DTextureI; + D3DTEXTUREHANDLE hTex; + /* texture handle */ +} D3DI_TEXTUREBLOCK; +typedef struct _D3DI_TEXTUREBLOCK *LPD3DI_TEXTUREBLOCK; + +typedef struct _D3DI_MATERIALBLOCK +{ + LIST_MEMBER(_D3DI_MATERIALBLOCK) list; + /* Next block in IDirect3DMaterial */ + LIST_MEMBER(_D3DI_MATERIALBLOCK) devList; + /* Next block in IDirect3DDevice */ + LPDIRECT3DDEVICEI lpD3DDeviceI; + LPDIRECT3DMATERIALI lpD3DMaterialI; + D3DMATERIALHANDLE hMat; + /* material handle */ +} D3DI_MATERIALBLOCK; +typedef struct _D3DI_MATERIALBLOCK *LPD3DI_MATERIALBLOCK; + +#define D3D_RSTATEBUF_SIZE 128 +#define D3D_MAX_RSTATES 96 /* Last render state ID + 1 */ +#define D3D_MAX_MMX_VERTICES 1024 + +extern DWORD dwD3DTriBatchSize, dwTriBatchSize, dwLineBatchSize; +extern DWORD dwHWBufferSize, dwHWMaxTris; +extern DWORD dwHWFewVertices; + +typedef struct _D3DHAL_DRAWPRIMCOUNTS *LPD3DHAL_DRAWPRIMCOUNTS; + +// Legacy HAL batching is done with these structs. +typedef struct _D3DI_HWCOUNTS { + WORD wNumStateChanges; // Number of state changes batched + WORD wNumVertices; // Number of vertices in tri list + WORD wNumTriangles; // Number of triangles in tri list +} D3DI_HWCOUNTS, *LPD3DI_HWCOUNTS; + +typedef struct _DIRECT3DDEVICEI +{ + /*** Object Interface ***/ + LPDIRECT3DDEVICE2CALLBACKS lpVtbl; /* Pointer to callbacks */ + int refCnt; /* Reference count */ + + /*** Object Relations ***/ + LPDIRECT3DI lpDirect3DI; /* parent */ + LIST_MEMBER(_DIRECT3DDEVICEI)list; /* Next device IDirect3D */ + + /* Textures */ + LIST_ROOT(_textures, _D3DI_TEXTUREBLOCK) texBlocks; + /* Ref to created IDirect3DTextures */ + + /* Execute buffers */ + LIST_ROOT(_buffers, _DIRECT3DEXECUTEBUFFERI) buffers; + /* Created IDirect3DExecuteBuffers */ + + /* Viewports */ + int numViewports; + CIRCLE_QUEUE_ROOT(_dviewports, _DIRECT3DVIEWPORTI) viewports; + /* Associated IDirect3DViewports */ + LPDIRECT3DVIEWPORT2 lpCurrentViewport; + + /* Materials */ + LIST_ROOT(_dmmaterials, _D3DI_MATERIALBLOCK) matBlocks; + /* Ref to associated IDirect3DMaterials */ + + /*** Object Data ***/ + /* Private interfaces */ + LPD3DOBJECTVTBL lpClassVtbl; /* Private Vtbl */ + LPD3DOBJECTVTBL lpObjVtbl; /* Private Vtbl */ + + LPD3DHAL_CALLBACKS lpD3DHALCallbacks; /* HW specific */ + LPD3DHAL_GLOBALDRIVERDATA lpD3DHALGlobalDriverData; /* HW specific */ + + /* Viewports */ + unsigned long v_id; /* ID of last viewport rendered */ + + /* Lights */ + int numLights; + /* This indicates the maximum number + of lights that have been set in + the device. */ + + /* Device characteristics */ + int age; + int width; + int height; + int depth; + unsigned long red_mask, green_mask, blue_mask; + + int dither; + int ramp_size; /* SW specific */ + D3DCOLORMODEL color_model; + int wireframe_options; + D3DTEXTUREFILTER texture_quality; + D3DVALUE gamma; + unsigned char gamma_table[256]; + int aspectx, aspecty; + D3DVALUE perspective_tolerance; + + /* Library information */ +#ifdef WIN32 + HINSTANCE hDrvDll; + char dllname[MAXPATH]; + char base[256]; +#endif +#ifdef SHLIB + void* so; +#endif + + /* Are we in a scene? */ + BOOL bInScene; + + /* Our Device type */ + GUID guid; + + /* GetCaps function from the library */ + RLDDIGetCapsFn GetCapsFn; + + /* Functions required to build driver */ + RLDDIInitFn RLDDIInit; + RLDDIPushDriverFn RLDDIPushDriver; + RLDDIPopDriverFn RLDDIPopDriver; + RLDDIDDrawCreateDriverFn RLDDIDDrawCreateDriver; + + /* Device description */ + D3DDEVICEDESC d3dHWDevDesc; + D3DDEVICEDESC d3dHELDevDesc; + + /* Driver stack */ + RLDDIDriverStack* stack; + + /* + * The special IUnknown interface for the aggregate that does + * not punt to the parent object. + */ + LPUNKNOWN lpOwningIUnknown; /* The owning IUnknown */ + DIRECT3DDEVICEUNKNOWNI lpThisIUnknown; /* Our IUnknown interface */ + DIRECT3DDEVICEOLDI lpThisID3DDev; /* Our IDirect3DDevice interface */ + + LPD3DHAL_CALLBACKS2 lpD3DHALCallbacks2; /* HW specific */ + + /* + * Renderstate cache for DrawPrimitive + */ + WORD wRStateBufLevel; + D3DSTATE renStateBuf[D3D_RSTATEBUF_SIZE]; + + /* Pointers to drivers used by DrawPrimitive */ + LPRLDDIGENRASDRIVER rasDriver; /* SW specific */ + LPRLDDITRANSFORMDRIVER xfmDriver; /* Assumed to be NULL for HW */ + LPRLDDIDDRAWDRIVER drawDriver; /* Assumed to be NULL for HW */ + + /* + * Pointers to functions used by DrawPrim&Begin/End + */ + + PFNLOCKTEXTURE pfnLockTexture; /* SW specific */ + PFNUNLOCKTEXTURE pfnUnlockTexture; /* SW specific */ + PFNSETFILLPARAMS pfnSetFillParams; /* SW specific */ + PFNREALLOCALIGNED pfnReallocAligned; /* SW specific */ + PFNDRVSETRENDERTARGET pfnDrvSetRenderTarget; /* SW specific */ + HRESULT (*pfnFlushStates)(struct _DIRECT3DDEVICEI*); + PFNDOFLUSHBEGINEND pfnDoFlushBeginEnd; + PFNDRAWPRIMITIVES pfnDrawPrimitives; + + /* Are we between Begin/End? */ + BOOL bInBegin; + D3DVERTEXTYPE vtVertexType; + D3DPRIMITIVETYPE ptPrimitiveType; + CRITICAL_SECTION BeginEndCSect; + + // max number of vertices + #define BEGIN_DATA_BLOCK_SIZE 256 + + LPVOID lpvVertexBatch; + WORD *lpIndexBatch; + + // lpvVertexData is non-NULL if we are InBegin and indexed. + LPVOID lpvVertexData; + DWORD dwNumVertices; + DWORD dwMaxVertexCount; // current number of vertices there is space for + WORD *lpVertexIndices; + DWORD dwNumIndices; + DWORD dwMaxIndexCount; // current number of indices there is space for + + DWORD dwFlags; + WORD wFlushed; + + /* + * DrawPrimitives batching + */ + + + // Buffer to put DrawPrimitives stuff into + // Used for both legacy and DrawPrimitive HALs + WORD *lpwDPBuffer; + WORD *lpwDPBufferAlloced; + + /* Legacy HALs */ + // pointer to current prim counts struct + LPD3DHAL_DRAWPRIMCOUNTS lpDPPrimCounts; + + // Buffer of counts structures that keep track of the + // number of render states and vertices buffered + LPD3DI_HWCOUNTS lpHWCounts; + + // Buffer of triangle structures. + LPD3DTRIANGLE lpHWTris; + + // Buffer of interleaved render states and primitives. + LPD3DTLVERTEX lpHWVertices; + + // Byte offset into lpHWVertices. This gets incremented + // by 8 when a render state is batched and by 32*dwNumVertices + // when a primitive is batched. + DWORD dwHWOffset; + + // Max value of dwHWOffset. Used to decide whether to flush. + DWORD dwHWMaxOffset; + + // Index into lpHWTris. + DWORD dwHWTriIndex; + + // Number of counts structures used so far. This actually + // gives the number of primitives batched and the index of + // the counts structure to batch render states into. + DWORD dwHWNumCounts; + + /* DrawPrimitive-aware HALs */ + + // Byte offset into buffer (we are currently + // using the device's wTriIndex) + DWORD dwDPOffset; + + // Maximum offset. If dwDPOffset exceeds this, it is + // time to flush. + DWORD dwDPMaxOffset; + + WORD *wTriIndex; + + // buffers for TL and H vertices + LPD3DTLVERTEX lpTLVbuf; + LPD3DHVERTEX lpHVbuf; + DWORD dwVbufSize; + + LPD3DHAL_D3DEXTENDEDCAPS lpD3DExtendedCaps; /* HW specific */ + +} DIRECT3DDEVICEI; + +/* + * Internal version of Direct3DExecuteBuffer object; + * it has data after the vtable + */ +typedef struct _DIRECT3DEXECUTEBUFFERI +{ + /*** Object Interface ***/ + LPDIRECT3DEXECUTEBUFFERCALLBACKS lpVtbl; /* Pointer to callbacks */ + int refCnt; /* Reference count */ + + /*** Object Relations ***/ + LPDIRECT3DDEVICEI lpD3DDeviceI; /* Parent */ + LIST_MEMBER(_DIRECT3DEXECUTEBUFFERI)list; + /* Next buffer in IDirect3D */ + + /*** Object Data ***/ + DWORD pid; /* Process locking execute buffer */ + D3DEXECUTEBUFFERDESC debDesc; + /* Description of the buffer */ + D3DEXECUTEDATA exData; /* Execute Data */ + BOOL locked; /* Is the buffer locked */ + + D3DI_BUFFERHANDLE hBuf; + /* Execute buffer handle */ +} DIRECT3DEXECUTEBUFFERI; + +/* + * Internal version of Direct3DLight object; + * it has data after the vtable + */ +typedef struct _DIRECT3DLIGHTI +{ + /*** Object Interface ***/ + LPDIRECT3DLIGHTCALLBACKS lpVtbl; /* Pointer to callbacks */ + int refCnt; /* Reference count */ + + /*** Object Relations ***/ + LPDIRECT3DI lpDirect3DI; /* Parent */ + LIST_MEMBER(_DIRECT3DLIGHTI)list; + /* Next light in IDirect3D */ + + LPDIRECT3DVIEWPORTI lpD3DViewportI; /* Guardian */ + CIRCLE_QUEUE_MEMBER(_DIRECT3DLIGHTI)light_list; + /* Next light in IDirect3DViewport */ + + /*** Object Data ***/ + D3DLIGHT2 dlLight;/* Data describing light */ + D3DI_LIGHT diLightData; + /* Internal representation of light */ +} DIRECT3DLIGHTI; + +typedef struct _DIRECT3DMATERIALOLDI +{ + LPDIRECT3DMATERIALCALLBACKS lpVtbl; + LPDIRECT3DMATERIALI lpObj; +} DIRECT3DMATERIALOLDI, *LPDIRECT3DMATERIALOLDI; + +/* + * Internal version of Direct3DMaterial object; + * it has data after the vtable + */ +typedef struct _DIRECT3DMATERIALI +{ + /*** Object Interface ***/ + LPDIRECT3DMATERIAL2CALLBACKS lpVtbl; /* Pointer to callbacks */ + int refCnt; /* Reference count */ + + /*** Object Relations ***/ + LPDIRECT3DI lpDirect3DI; /* Parent */ + DIRECT3DMATERIALOLDI lpThisID3DMat; /* Our IDirect3DMaterial interface */ + LIST_MEMBER(_DIRECT3DMATERIALI)list; + /* Next MATERIAL in IDirect3D */ + + LIST_ROOT(_mblocks, _D3DI_MATERIALBLOCK)blocks; + /* devices we're associated with */ + + /*** Object Data ***/ + D3DMATERIAL dmMaterial; /* Data describing material */ + BOOL bRes; /* Is this material reserved in the driver */ +} DIRECT3DMATERIALI; + +/* + * If we have an aggreate Direct3DTexture we need a structure + * to represent an unknown interface distinct from the underlying + * object. This is that structure. + */ +typedef struct _DIRECT3DTEXTUREUNKNOWNI +{ + LPDIRECT3DTEXTURECALLBACKS lpVtbl; + LPDIRECT3DTEXTUREI lpObj; +} DIRECT3DTEXTUREUNKNOWNI; +typedef struct _DIRECT3DTEXTUREUNKNOWNI *LPDIRECT3DTEXTUREUNKNOWNI; + +typedef struct _DIRECT3DTEXTUREOLDI +{ + LPDIRECT3DTEXTURECALLBACKS lpVtbl; + LPDIRECT3DTEXTUREI lpObj; +} DIRECT3DTEXTUREOLDI, *LPDIRECT3DTEXTUREOLDI; + +/* + * Internal version of Direct3DTexture object; it has data after the vtable + */ +typedef struct _DIRECT3DTEXTUREI +{ + /*** Object Interface ***/ + LPDIRECT3DTEXTURE2CALLBACKS lpVtbl; /* Pointer to callbacks */ + int refCnt; /* Reference count */ + + + /*** Object Relations ***/ + LIST_ROOT(_blocks, _D3DI_TEXTUREBLOCK) blocks; + /* Devices we're associated with */ + + /*** Object Data ***/ + LPDIRECTDRAWSURFACE lpDDS; + + /* + * The special IUnknown interface for the aggregate that does + * not punt to the parent object. + */ + LPUNKNOWN lpOwningIUnknown; /* The owning IUnknown */ + DIRECT3DTEXTUREUNKNOWNI lpThisIUnknown; /* Our IUnknown interface */ + DIRECT3DTEXTUREOLDI lpThisID3DTex; /* Our IDirect3DTexture interface */ + BOOL bIsPalettized; + +} DIRECT3DTEXTUREI; + +/* + * Internal version of Direct3DViewport object; it has data after the vtable + */ +typedef struct _DIRECT3DVIEWPORTI +{ + /*** Object Interface ***/ + LPDIRECT3DVIEWPORT2CALLBACKS lpVtbl; /* Pointer to callbacks */ + int refCnt; /* Reference count */ + + /*** Object Relations */ + LPDIRECT3DI lpDirect3DI; /* Parent */ + LIST_MEMBER(_DIRECT3DVIEWPORTI)list; + /* Next viewport in IDirect3D */ + + LPDIRECT3DDEVICEI lpD3DDeviceI; /* Guardian */ + CIRCLE_QUEUE_MEMBER(_DIRECT3DVIEWPORTI)vw_list; + /* Next viewport in IDirect3DDevice */ + + /* Lights */ + int numLights; + CIRCLE_QUEUE_ROOT(_dlights, _DIRECT3DLIGHTI) lights; + /* Associated IDirect3DLights */ + + /*** Object Data ***/ + unsigned long v_id; /* Id for this viewport */ + D3DVIEWPORT2 v_data; + BOOL v_data_is_set; + + BOOL have_background; + D3DMATERIALHANDLE background; + /* Background material */ + BOOL have_depth; + LPDIRECTDRAWSURFACE depth; /* Background depth */ + + BOOL bLightsChanged; + /* Have the lights changed since they + were last collected? */ + DWORD clrCount; /* Number of rects allocated */ + LPD3DRECT clrRects; /* Rects used for clearing */ +} DIRECT3DVIEWPORTI; + +/* + * Picking stuff. + */ +typedef struct _D3DI_PICKDATA { + D3DI_EXECUTEDATA* exe; + D3DPICKRECORD* records; + int pick_count; + D3DRECT pick; +} D3DI_PICKDATA, *LPD3DI_PICKDATA; + +/* + * Direct3D memory allocation + */ + +/* + * Register a set of functions to be used in place of malloc, realloc + * and free for memory allocation. The functions D3DMalloc, D3DRealloc + * and D3DFree will use these functions. The default is to use the + * ANSI C library routines malloc, realloc and free. + */ +typedef LPVOID (*D3DMALLOCFUNCTION)(size_t); +typedef LPVOID (*D3DREALLOCFUNCTION)(LPVOID, size_t); +typedef VOID (*D3DFREEFUNCTION)(LPVOID); + +/* + * Allocate size bytes of memory and return a pointer to it in *p_return. + * Returns D3DERR_BADALLOC with *p_return unchanged if the allocation fails. + */ +extern __declspec (dllexport) HRESULT D3DAPI D3DMalloc(LPVOID* p_return, size_t size); + +/* + * Change the size of an allocated block of memory. A pointer to the + * block is passed in in *p_inout. If *p_inout is NULL then a new + * block is allocated. If the reallocation is successful, *p_inout is + * changed to point to the new block. If the allocation fails, + * *p_inout is unchanged and D3DERR_BADALLOC is returned. + */ +HRESULT D3DAPI D3DRealloc(LPVOID* p_inout, size_t size); + +/* + * Free a block of memory previously allocated with D3DMalloc or + * D3DRealloc. + */ +extern __declspec (dllexport) VOID D3DAPI D3DFree(LPVOID p); + +/* + * Used for raising errors from the driver. + */ +HRESULT D3DAPI D3DRaise(HRESULT); + +/* + * Convert RLDDI error codes to D3D error codes + */ +#define RLDDITOD3DERR(_errcode) (RLDDIToD3DErrors[_errcode]) +extern HRESULT RLDDIToD3DErrors[]; + +/* + * maths + */ +#if 1 /* defined(STACK_CALL) && defined(__WATCOMC__) */ +D3DVALUE D3DIPow(D3DVALUE, D3DVALUE); +#else +#define D3DIPow(v,p) DTOVAL(pow(VALTOD(v), VALTOD(p))) +#endif + +/* + * Light utils + */ +void D3DI_DeviceMarkLightEnd(LPDIRECT3DDEVICEI, int); +void D3DI_UpdateLightInternal(LPDIRECT3DLIGHTI); +void D3DI_VectorNormalise12(LPD3DVECTOR v); +D3DTEXTUREHANDLE D3DI_FindTextureHandle(LPDIRECT3DTEXTUREI, LPDIRECT3DDEVICEI); +void D3DI_SetTextureHandle(LPDIRECT3DTEXTUREI, LPDIRECT3DDEVICEI, D3DTEXTUREHANDLE); +void D3DI_RemoveTextureBlock(LPD3DI_TEXTUREBLOCK); +void D3DI_RemoveMaterialBlock(LPD3DI_MATERIALBLOCK); + +extern BOOL D3DI_isHALValid(LPD3DHAL_CALLBACKS); + +#ifdef BUILD_D3D_LAYER +extern RLDDIValue* RLDDIFInvSqrtTable; +#endif + +#ifdef __cplusplus +}; +#endif + +#endif /* BUILD_HEL */ +#endif /* !BUILD_RLAPI */ +// @@END_MSINTERNAL + +#endif /* _D3DI_H */ + diff --git a/include/vd3drm.h b/include/vd3drm.h new file mode 100644 index 0000000..4b5cc82 --- /dev/null +++ b/include/vd3drm.h @@ -0,0 +1,229 @@ +/*==========================================================================; + * + * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. + * + * File: d3drm.h + * Content: Direct3DRM include file + * + ***************************************************************************/ + +#ifndef __D3DRM_H__ +#define __D3DRM_H__ + +#include "vddraw.h" +#include "vd3drmobj.h" + +#ifdef __cplusplus +extern "C" { +struct IDirect3DRM; +#endif + + +DEFINE_GUID(IID_IDirect3DRM, 0x2bc49361, 0x8327, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRM2, 0x4516ecc8, 0x8f20, 0x11d0, 0x9b, 0x6d, 0x00, 0x00, 0xc0, 0x78, 0x1b, 0xc3); +WIN_TYPES(IDirect3DRM, DIRECT3DRM); +WIN_TYPES(IDirect3DRM2, DIRECT3DRM2); + + +/* Create a Direct3DRM API */ +STDAPI Direct3DRMCreate(LPDIRECT3DRM FAR *lplpDirect3DRM); + +#undef INTERFACE +#define INTERFACE IDirect3DRM + +DECLARE_INTERFACE_(IDirect3DRM, IUnknown) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD(CreateObject) + (THIS_ REFCLSID rclsid, LPUNKNOWN pUnkOuter, REFIID riid, LPVOID FAR* ppv) PURE; + STDMETHOD(CreateFrame) (THIS_ LPDIRECT3DRMFRAME, LPDIRECT3DRMFRAME *) PURE; + STDMETHOD(CreateMesh) (THIS_ LPDIRECT3DRMMESH *) PURE; + STDMETHOD(CreateMeshBuilder)(THIS_ LPDIRECT3DRMMESHBUILDER *) PURE; + STDMETHOD(CreateFace) (THIS_ LPDIRECT3DRMFACE *) PURE; + STDMETHOD(CreateAnimation) (THIS_ LPDIRECT3DRMANIMATION *) PURE; + STDMETHOD(CreateAnimationSet)(THIS_ LPDIRECT3DRMANIMATIONSET *) PURE; + STDMETHOD(CreateTexture) (THIS_ LPD3DRMIMAGE, LPDIRECT3DRMTEXTURE *) PURE; + STDMETHOD(CreateLight) (THIS_ D3DRMLIGHTTYPE, D3DCOLOR, LPDIRECT3DRMLIGHT *) PURE; + STDMETHOD(CreateLightRGB) + (THIS_ D3DRMLIGHTTYPE, D3DVALUE, D3DVALUE, D3DVALUE, LPDIRECT3DRMLIGHT *) PURE; + STDMETHOD(CreateMaterial) (THIS_ D3DVALUE, LPDIRECT3DRMMATERIAL *) PURE; + STDMETHOD(CreateDevice) (THIS_ DWORD, DWORD, LPDIRECT3DRMDEVICE *) PURE; + + /* Create a Windows Device using DirectDraw surfaces */ + STDMETHOD(CreateDeviceFromSurface) + ( THIS_ LPGUID lpGUID, LPDIRECTDRAW lpDD, + LPDIRECTDRAWSURFACE lpDDSBack, LPDIRECT3DRMDEVICE * + ) PURE; + + /* Create a Windows Device using D3D objects */ + STDMETHOD(CreateDeviceFromD3D) + ( THIS_ LPDIRECT3D lpD3D, LPDIRECT3DDEVICE lpD3DDev, + LPDIRECT3DRMDEVICE * + ) PURE; + + STDMETHOD(CreateDeviceFromClipper) + ( THIS_ LPDIRECTDRAWCLIPPER lpDDClipper, LPGUID lpGUID, + int width, int height, LPDIRECT3DRMDEVICE *) PURE; + + STDMETHOD(CreateTextureFromSurface)(THIS_ LPDIRECTDRAWSURFACE lpDDS, LPDIRECT3DRMTEXTURE *) PURE; + + STDMETHOD(CreateShadow) + ( THIS_ LPDIRECT3DRMVISUAL, LPDIRECT3DRMLIGHT, + D3DVALUE px, D3DVALUE py, D3DVALUE pz, + D3DVALUE nx, D3DVALUE ny, D3DVALUE nz, + LPDIRECT3DRMVISUAL * + ) PURE; + STDMETHOD(CreateViewport) + ( THIS_ LPDIRECT3DRMDEVICE, LPDIRECT3DRMFRAME, DWORD, DWORD, + DWORD, DWORD, LPDIRECT3DRMVIEWPORT * + ) PURE; + STDMETHOD(CreateWrap) + ( THIS_ D3DRMWRAPTYPE, LPDIRECT3DRMFRAME, + D3DVALUE ox, D3DVALUE oy, D3DVALUE oz, + D3DVALUE dx, D3DVALUE dy, D3DVALUE dz, + D3DVALUE ux, D3DVALUE uy, D3DVALUE uz, + D3DVALUE ou, D3DVALUE ov, + D3DVALUE su, D3DVALUE sv, + LPDIRECT3DRMWRAP * + ) PURE; + STDMETHOD(CreateUserVisual) (THIS_ D3DRMUSERVISUALCALLBACK, LPVOID lPArg, LPDIRECT3DRMUSERVISUAL *) PURE; + STDMETHOD(LoadTexture) (THIS_ const char *, LPDIRECT3DRMTEXTURE *) PURE; + STDMETHOD(LoadTextureFromResource) (THIS_ HRSRC rs, LPDIRECT3DRMTEXTURE *) PURE; + + STDMETHOD(SetSearchPath) (THIS_ LPCSTR) PURE; + STDMETHOD(AddSearchPath) (THIS_ LPCSTR) PURE; + STDMETHOD(GetSearchPath) (THIS_ DWORD *size_return, LPSTR path_return) PURE; + STDMETHOD(SetDefaultTextureColors)(THIS_ DWORD) PURE; + STDMETHOD(SetDefaultTextureShades)(THIS_ DWORD) PURE; + + STDMETHOD(GetDevices) (THIS_ LPDIRECT3DRMDEVICEARRAY *) PURE; + STDMETHOD(GetNamedObject) (THIS_ const char *, LPDIRECT3DRMOBJECT *) PURE; + + STDMETHOD(EnumerateObjects) (THIS_ D3DRMOBJECTCALLBACK, LPVOID) PURE; + + STDMETHOD(Load) + ( THIS_ LPVOID, LPVOID, LPIID *, DWORD, D3DRMLOADOPTIONS, + D3DRMLOADCALLBACK, LPVOID, D3DRMLOADTEXTURECALLBACK, LPVOID, + LPDIRECT3DRMFRAME + ) PURE; + STDMETHOD(Tick) (THIS_ D3DVALUE) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRM2 + +DECLARE_INTERFACE_(IDirect3DRM2, IUnknown) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD(CreateObject) + (THIS_ REFCLSID rclsid, LPUNKNOWN pUnkOuter, REFIID riid, LPVOID FAR* ppv) PURE; + STDMETHOD(CreateFrame) (THIS_ LPDIRECT3DRMFRAME, LPDIRECT3DRMFRAME2 *) PURE; + STDMETHOD(CreateMesh) (THIS_ LPDIRECT3DRMMESH *) PURE; + STDMETHOD(CreateMeshBuilder)(THIS_ LPDIRECT3DRMMESHBUILDER2 *) PURE; + STDMETHOD(CreateFace) (THIS_ LPDIRECT3DRMFACE *) PURE; + STDMETHOD(CreateAnimation) (THIS_ LPDIRECT3DRMANIMATION *) PURE; + STDMETHOD(CreateAnimationSet)(THIS_ LPDIRECT3DRMANIMATIONSET *) PURE; + STDMETHOD(CreateTexture) (THIS_ LPD3DRMIMAGE, LPDIRECT3DRMTEXTURE2 *) PURE; + STDMETHOD(CreateLight) (THIS_ D3DRMLIGHTTYPE, D3DCOLOR, LPDIRECT3DRMLIGHT *) PURE; + STDMETHOD(CreateLightRGB) + (THIS_ D3DRMLIGHTTYPE, D3DVALUE, D3DVALUE, D3DVALUE, LPDIRECT3DRMLIGHT *) PURE; + STDMETHOD(CreateMaterial) (THIS_ D3DVALUE, LPDIRECT3DRMMATERIAL *) PURE; + STDMETHOD(CreateDevice) (THIS_ DWORD, DWORD, LPDIRECT3DRMDEVICE2 *) PURE; + + /* Create a Windows Device using DirectDraw surfaces */ + STDMETHOD(CreateDeviceFromSurface) + ( THIS_ LPGUID lpGUID, LPDIRECTDRAW lpDD, + LPDIRECTDRAWSURFACE lpDDSBack, LPDIRECT3DRMDEVICE2 * + ) PURE; + + /* Create a Windows Device using D3D objects */ + STDMETHOD(CreateDeviceFromD3D) + ( THIS_ LPDIRECT3D2 lpD3D, LPDIRECT3DDEVICE2 lpD3DDev, + LPDIRECT3DRMDEVICE2 * + ) PURE; + + STDMETHOD(CreateDeviceFromClipper) + ( THIS_ LPDIRECTDRAWCLIPPER lpDDClipper, LPGUID lpGUID, + int width, int height, LPDIRECT3DRMDEVICE2 *) PURE; + + STDMETHOD(CreateTextureFromSurface)(THIS_ LPDIRECTDRAWSURFACE lpDDS, LPDIRECT3DRMTEXTURE2 *) PURE; + + STDMETHOD(CreateShadow) + ( THIS_ LPDIRECT3DRMVISUAL, LPDIRECT3DRMLIGHT, + D3DVALUE px, D3DVALUE py, D3DVALUE pz, + D3DVALUE nx, D3DVALUE ny, D3DVALUE nz, + LPDIRECT3DRMVISUAL * + ) PURE; + STDMETHOD(CreateViewport) + ( THIS_ LPDIRECT3DRMDEVICE, LPDIRECT3DRMFRAME, DWORD, DWORD, + DWORD, DWORD, LPDIRECT3DRMVIEWPORT * + ) PURE; + STDMETHOD(CreateWrap) + ( THIS_ D3DRMWRAPTYPE, LPDIRECT3DRMFRAME, + D3DVALUE ox, D3DVALUE oy, D3DVALUE oz, + D3DVALUE dx, D3DVALUE dy, D3DVALUE dz, + D3DVALUE ux, D3DVALUE uy, D3DVALUE uz, + D3DVALUE ou, D3DVALUE ov, + D3DVALUE su, D3DVALUE sv, + LPDIRECT3DRMWRAP * + ) PURE; + STDMETHOD(CreateUserVisual) (THIS_ D3DRMUSERVISUALCALLBACK, LPVOID lPArg, LPDIRECT3DRMUSERVISUAL *) PURE; + STDMETHOD(LoadTexture) (THIS_ const char *, LPDIRECT3DRMTEXTURE2 *) PURE; + STDMETHOD(LoadTextureFromResource) (THIS_ HMODULE hModule, LPCTSTR strName, LPCTSTR strType, LPDIRECT3DRMTEXTURE2 *) PURE; + + STDMETHOD(SetSearchPath) (THIS_ LPCSTR) PURE; + STDMETHOD(AddSearchPath) (THIS_ LPCSTR) PURE; + STDMETHOD(GetSearchPath) (THIS_ DWORD *size_return, LPSTR path_return) PURE; + STDMETHOD(SetDefaultTextureColors)(THIS_ DWORD) PURE; + STDMETHOD(SetDefaultTextureShades)(THIS_ DWORD) PURE; + + STDMETHOD(GetDevices) (THIS_ LPDIRECT3DRMDEVICEARRAY *) PURE; + STDMETHOD(GetNamedObject) (THIS_ const char *, LPDIRECT3DRMOBJECT *) PURE; + + STDMETHOD(EnumerateObjects) (THIS_ D3DRMOBJECTCALLBACK, LPVOID) PURE; + + STDMETHOD(Load) + ( THIS_ LPVOID, LPVOID, LPIID *, DWORD, D3DRMLOADOPTIONS, + D3DRMLOADCALLBACK, LPVOID, D3DRMLOADTEXTURECALLBACK, LPVOID, + LPDIRECT3DRMFRAME + ) PURE; + STDMETHOD(Tick) (THIS_ D3DVALUE) PURE; + + STDMETHOD(CreateProgressiveMesh)(THIS_ LPDIRECT3DRMPROGRESSIVEMESH *) PURE; +}; + +#define D3DRM_OK DD_OK +#define D3DRMERR_BADOBJECT MAKE_DDHRESULT(781) +#define D3DRMERR_BADTYPE MAKE_DDHRESULT(782) +#define D3DRMERR_BADALLOC MAKE_DDHRESULT(783) +#define D3DRMERR_FACEUSED MAKE_DDHRESULT(784) +#define D3DRMERR_NOTFOUND MAKE_DDHRESULT(785) +#define D3DRMERR_NOTDONEYET MAKE_DDHRESULT(786) +#define D3DRMERR_FILENOTFOUND MAKE_DDHRESULT(787) +#define D3DRMERR_BADFILE MAKE_DDHRESULT(788) +#define D3DRMERR_BADDEVICE MAKE_DDHRESULT(789) +#define D3DRMERR_BADVALUE MAKE_DDHRESULT(790) +#define D3DRMERR_BADMAJORVERSION MAKE_DDHRESULT(791) +#define D3DRMERR_BADMINORVERSION MAKE_DDHRESULT(792) +#define D3DRMERR_UNABLETOEXECUTE MAKE_DDHRESULT(793) +#define D3DRMERR_LIBRARYNOTFOUND MAKE_DDHRESULT(794) +#define D3DRMERR_INVALIDLIBRARY MAKE_DDHRESULT(795) +#define D3DRMERR_PENDING MAKE_DDHRESULT(796) +#define D3DRMERR_NOTENOUGHDATA MAKE_DDHRESULT(797) +#define D3DRMERR_REQUESTTOOLARGE MAKE_DDHRESULT(798) +#define D3DRMERR_REQUESTTOOSMALL MAKE_DDHRESULT(799) +#define D3DRMERR_CONNECTIONLOST MAKE_DDHRESULT(800) +#define D3DRMERR_LOADABORTED MAKE_DDHRESULT(801) +#define D3DRMERR_NOINTERNET MAKE_DDHRESULT(802) +#define D3DRMERR_BADCACHEFILE MAKE_DDHRESULT(803) +#define D3DRMERR_BOXNOTSET MAKE_DDHRESULT(804) +#define D3DRMERR_BADPMDATA MAKE_DDHRESULT(805) + +#ifdef __cplusplus +}; +#endif + +#endif /* _D3DRMAPI_H_ */ + diff --git a/include/vd3drmdef.h b/include/vd3drmdef.h new file mode 100644 index 0000000..e4f7ae3 --- /dev/null +++ b/include/vd3drmdef.h @@ -0,0 +1,473 @@ +/*==========================================================================; + * + * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. + * + * File: d3drm.h + * Content: Direct3DRM include file + * + ***************************************************************************/ + +#ifndef __D3DRMDEFS_H__ +#define __D3DRMDEFS_H__ + +#include +#include "vd3dtypes.h" + +#ifdef WIN32 +#define D3DRMAPI __stdcall +#else +#define D3DRMAPI +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +#ifndef TRUE +#define FALSE 0 +#define TRUE 1 +#endif + +typedef struct _D3DRMVECTOR4D +{ D3DVALUE x, y, z, w; +} D3DRMVECTOR4D, *LPD3DRMVECTOR4D; + +typedef D3DVALUE D3DRMMATRIX4D[4][4]; + +typedef struct _D3DRMQUATERNION +{ D3DVALUE s; + D3DVECTOR v; +} D3DRMQUATERNION, *LPD3DRMQUATERNION; + +typedef struct _D3DRMRAY +{ D3DVECTOR dvDir; + D3DVECTOR dvPos; +} D3DRMRAY, *LPD3DRMRAY; + +typedef struct _D3DRMBOX +{ D3DVECTOR min, max; +} D3DRMBOX, *LPD3DRMBOX; + +typedef void (*D3DRMWRAPCALLBACK) + (LPD3DVECTOR, int* u, int* v, LPD3DVECTOR a, LPD3DVECTOR b, LPVOID); + +typedef enum _D3DRMLIGHTTYPE +{ D3DRMLIGHT_AMBIENT, + D3DRMLIGHT_POINT, + D3DRMLIGHT_SPOT, + D3DRMLIGHT_DIRECTIONAL, + D3DRMLIGHT_PARALLELPOINT +} D3DRMLIGHTTYPE, *LPD3DRMLIGHTTYPE; + +typedef enum _D3DRMSHADEMODE { + D3DRMSHADE_FLAT = 0, + D3DRMSHADE_GOURAUD = 1, + D3DRMSHADE_PHONG = 2, + + D3DRMSHADE_MASK = 7, + D3DRMSHADE_MAX = 8 +} D3DRMSHADEMODE, *LPD3DRMSHADEMODE; + +typedef enum _D3DRMLIGHTMODE { + D3DRMLIGHT_OFF = 0 * D3DRMSHADE_MAX, + D3DRMLIGHT_ON = 1 * D3DRMSHADE_MAX, + + D3DRMLIGHT_MASK = 7 * D3DRMSHADE_MAX, + D3DRMLIGHT_MAX = 8 * D3DRMSHADE_MAX +} D3DRMLIGHTMODE, *LPD3DRMLIGHTMODE; + +typedef enum _D3DRMFILLMODE { + D3DRMFILL_POINTS = 0 * D3DRMLIGHT_MAX, + D3DRMFILL_WIREFRAME = 1 * D3DRMLIGHT_MAX, + D3DRMFILL_SOLID = 2 * D3DRMLIGHT_MAX, + + D3DRMFILL_MASK = 7 * D3DRMLIGHT_MAX, + D3DRMFILL_MAX = 8 * D3DRMLIGHT_MAX +} D3DRMFILLMODE, *LPD3DRMFILLMODE; + +typedef DWORD D3DRMRENDERQUALITY, *LPD3DRMRENDERQUALITY; + +#define D3DRMRENDER_WIREFRAME (D3DRMSHADE_FLAT+D3DRMLIGHT_OFF+D3DRMFILL_WIREFRAME) +#define D3DRMRENDER_UNLITFLAT (D3DRMSHADE_FLAT+D3DRMLIGHT_OFF+D3DRMFILL_SOLID) +#define D3DRMRENDER_FLAT (D3DRMSHADE_FLAT+D3DRMLIGHT_ON+D3DRMFILL_SOLID) +#define D3DRMRENDER_GOURAUD (D3DRMSHADE_GOURAUD+D3DRMLIGHT_ON+D3DRMFILL_SOLID) +#define D3DRMRENDER_PHONG (D3DRMSHADE_PHONG+D3DRMLIGHT_ON+D3DRMFILL_SOLID) + +#define D3DRMRENDERMODE_BLENDEDTRANSPARENCY 1 +#define D3DRMRENDERMODE_SORTEDTRANSPARENCY 2 + +typedef enum _D3DRMTEXTUREQUALITY +{ D3DRMTEXTURE_NEAREST, /* choose nearest texel */ + D3DRMTEXTURE_LINEAR, /* interpolate 4 texels */ + D3DRMTEXTURE_MIPNEAREST, /* nearest texel in nearest mipmap */ + D3DRMTEXTURE_MIPLINEAR, /* interpolate 2 texels from 2 mipmaps */ + D3DRMTEXTURE_LINEARMIPNEAREST, /* interpolate 4 texels in nearest mipmap */ + D3DRMTEXTURE_LINEARMIPLINEAR /* interpolate 8 texels from 2 mipmaps */ +} D3DRMTEXTUREQUALITY, *LPD3DRMTEXTUREQUALITY; + +typedef enum _D3DRMCOMBINETYPE +{ D3DRMCOMBINE_REPLACE, + D3DRMCOMBINE_BEFORE, + D3DRMCOMBINE_AFTER +} D3DRMCOMBINETYPE, *LPD3DRMCOMBINETYPE; + +typedef D3DCOLORMODEL D3DRMCOLORMODEL, *LPD3DRMCOLORMODEL; + +typedef enum _D3DRMPALETTEFLAGS +{ D3DRMPALETTE_FREE, /* renderer may use this entry freely */ + D3DRMPALETTE_READONLY, /* fixed but may be used by renderer */ + D3DRMPALETTE_RESERVED /* may not be used by renderer */ +} D3DRMPALETTEFLAGS, *LPD3DRMPALETTEFLAGS; + +typedef struct _D3DRMPALETTEENTRY +{ unsigned char red; /* 0 .. 255 */ + unsigned char green; /* 0 .. 255 */ + unsigned char blue; /* 0 .. 255 */ + unsigned char flags; /* one of D3DRMPALETTEFLAGS */ +} D3DRMPALETTEENTRY, *LPD3DRMPALETTEENTRY; + +typedef struct _D3DRMIMAGE +{ int width, height; /* width and height in pixels */ + int aspectx, aspecty; /* aspect ratio for non-square pixels */ + int depth; /* bits per pixel */ + int rgb; /* if false, pixels are indices into a + palette otherwise, pixels encode + RGB values. */ + int bytes_per_line; /* number of bytes of memory for a + scanline. This must be a multiple + of 4. */ + void* buffer1; /* memory to render into (first buffer). */ + void* buffer2; /* second rendering buffer for double + buffering, set to NULL for single + buffering. */ + unsigned long red_mask; + unsigned long green_mask; + unsigned long blue_mask; + unsigned long alpha_mask; /* if rgb is true, these are masks for + the red, green and blue parts of a + pixel. Otherwise, these are masks + for the significant bits of the + red, green and blue elements in the + palette. For instance, most SVGA + displays use 64 intensities of red, + green and blue, so the masks should + all be set to 0xfc. */ + int palette_size; /* number of entries in palette */ + D3DRMPALETTEENTRY* palette; /* description of the palette (only if + rgb is false). Must be (1< /* Use Windows header files */ +#define VIRTUAL + +#include "vd3drmdef.h" +#include "vd3d.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The methods for IUnknown + */ +#define IUNKNOWN_METHODS(kind) \ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID *ppvObj) kind; \ + STDMETHOD_(ULONG, AddRef) (THIS) kind; \ + STDMETHOD_(ULONG, Release) (THIS) kind + +/* + * The methods for IDirect3DRMObject + */ +#define IDIRECT3DRMOBJECT_METHODS(kind) \ + STDMETHOD(Clone) (THIS_ LPUNKNOWN pUnkOuter, REFIID riid, LPVOID *ppvObj) kind; \ + STDMETHOD(AddDestroyCallback) (THIS_ D3DRMOBJECTCALLBACK, LPVOID argument) kind; \ + STDMETHOD(DeleteDestroyCallback) (THIS_ D3DRMOBJECTCALLBACK, LPVOID argument) kind; \ + STDMETHOD(SetAppData) (THIS_ DWORD data) kind; \ + STDMETHOD_(DWORD, GetAppData) (THIS) kind; \ + STDMETHOD(SetName) (THIS_ LPCSTR) kind; \ + STDMETHOD(GetName) (THIS_ LPDWORD lpdwSize, LPSTR lpName) kind; \ + STDMETHOD(GetClassName) (THIS_ LPDWORD lpdwSize, LPSTR lpName) kind + + +#define WIN_TYPES(itype, ptype) \ + typedef interface itype FAR *LP##ptype, FAR **LPLP##ptype + +WIN_TYPES(IDirect3DRMObject, DIRECT3DRMOBJECT); +WIN_TYPES(IDirect3DRMDevice, DIRECT3DRMDEVICE); +WIN_TYPES(IDirect3DRMDevice2, DIRECT3DRMDEVICE2); +WIN_TYPES(IDirect3DRMViewport, DIRECT3DRMVIEWPORT); +WIN_TYPES(IDirect3DRMFrame, DIRECT3DRMFRAME); +WIN_TYPES(IDirect3DRMFrame2, DIRECT3DRMFRAME2); +WIN_TYPES(IDirect3DRMVisual, DIRECT3DRMVISUAL); +WIN_TYPES(IDirect3DRMMesh, DIRECT3DRMMESH); +WIN_TYPES(IDirect3DRMMeshBuilder, DIRECT3DRMMESHBUILDER); +WIN_TYPES(IDirect3DRMMeshBuilder2, DIRECT3DRMMESHBUILDER2); +WIN_TYPES(IDirect3DRMFace, DIRECT3DRMFACE); +WIN_TYPES(IDirect3DRMLight, DIRECT3DRMLIGHT); +WIN_TYPES(IDirect3DRMTexture, DIRECT3DRMTEXTURE); +WIN_TYPES(IDirect3DRMTexture2, DIRECT3DRMTEXTURE2); +WIN_TYPES(IDirect3DRMWrap, DIRECT3DRMWRAP); +WIN_TYPES(IDirect3DRMMaterial, DIRECT3DRMMATERIAL); +WIN_TYPES(IDirect3DRMInterpolator, DIRECT3DRMINTERPOLATOR); +WIN_TYPES(IDirect3DRMAnimation, DIRECT3DRMANIMATION); +WIN_TYPES(IDirect3DRMAnimationSet, DIRECT3DRMANIMATIONSET); +WIN_TYPES(IDirect3DRMUserVisual, DIRECT3DRMUSERVISUAL); +WIN_TYPES(IDirect3DRMShadow, DIRECT3DRMSHADOW); +WIN_TYPES(IDirect3DRMArray, DIRECT3DRMARRAY); +WIN_TYPES(IDirect3DRMObjectArray, DIRECT3DRMOBJECTARRAY); +WIN_TYPES(IDirect3DRMDeviceArray, DIRECT3DRMDEVICEARRAY); +WIN_TYPES(IDirect3DRMFaceArray, DIRECT3DRMFACEARRAY); +WIN_TYPES(IDirect3DRMViewportArray, DIRECT3DRMVIEWPORTARRAY); +WIN_TYPES(IDirect3DRMFrameArray, DIRECT3DRMFRAMEARRAY); +WIN_TYPES(IDirect3DRMVisualArray, DIRECT3DRMVISUALARRAY); +WIN_TYPES(IDirect3DRMPickedArray, DIRECT3DRMPICKEDARRAY); +WIN_TYPES(IDirect3DRMPicked2Array, DIRECT3DRMPICKED2ARRAY); +WIN_TYPES(IDirect3DRMLightArray, DIRECT3DRMLIGHTARRAY); +WIN_TYPES(IDirect3DRMProgressiveMesh, DIRECT3DRMPROGRESSIVEMESH); + +/* + * Direct3DRM Object classes + */ +DEFINE_GUID(CLSID_CDirect3DRMDevice, 0x4fa3568e, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMViewport, 0x4fa3568f, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMFrame, 0x4fa35690, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMMesh, 0x4fa35691, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMMeshBuilder, 0x4fa35692, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMFace, 0x4fa35693, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMLight, 0x4fa35694, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMTexture, 0x4fa35695, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMWrap, 0x4fa35696, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMMaterial, 0x4fa35697, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMAnimation, 0x4fa35698, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMAnimationSet, 0x4fa35699, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMUserVisual, 0x4fa3569a, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMShadow, 0x4fa3569b, 0x623f, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(CLSID_CDirect3DRMViewportInterpolator, +0xde9eaa1, 0x3b84, 0x11d0, 0x9b, 0x6d, 0x0, 0x0, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(CLSID_CDirect3DRMFrameInterpolator, +0xde9eaa2, 0x3b84, 0x11d0, 0x9b, 0x6d, 0x0, 0x0, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(CLSID_CDirect3DRMMeshInterpolator, +0xde9eaa3, 0x3b84, 0x11d0, 0x9b, 0x6d, 0x0, 0x0, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(CLSID_CDirect3DRMLightInterpolator, +0xde9eaa6, 0x3b84, 0x11d0, 0x9b, 0x6d, 0x0, 0x0, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(CLSID_CDirect3DRMMaterialInterpolator, +0xde9eaa7, 0x3b84, 0x11d0, 0x9b, 0x6d, 0x0, 0x0, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(CLSID_CDirect3DRMTextureInterpolator, +0xde9eaa8, 0x3b84, 0x11d0, 0x9b, 0x6d, 0x0, 0x0, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(CLSID_CDirect3DRMProgressiveMesh, 0x4516ec40, 0x8f20, 0x11d0, 0x9b, 0x6d, 0x00, 0x00, 0xc0, 0x78, 0x1b, 0xc3); + + +/* + * Direct3DRM Object interfaces + */ +DEFINE_GUID(IID_IDirect3DRMObject, 0xeb16cb00, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMDevice, 0xe9e19280, 0x6e05, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMDevice2, 0x4516ec78, 0x8f20, 0x11d0, 0x9b, 0x6d, 0x00, 0x00, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(IID_IDirect3DRMViewport, 0xeb16cb02, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMFrame, 0xeb16cb03, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMFrame2, 0xc3dfbd60, 0x3988, 0x11d0, 0x9e, 0xc2, 0x0, 0x0, 0xc0, 0x29, 0x1a, 0xc3); +DEFINE_GUID(IID_IDirect3DRMVisual, 0xeb16cb04, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMMesh, 0xa3a80d01, 0x6e12, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMMeshBuilder, 0xa3a80d02, 0x6e12, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMMeshBuilder2, 0x4516ec77, 0x8f20, 0x11d0, 0x9b, 0x6d, 0x0, 0x0, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(IID_IDirect3DRMFace, 0xeb16cb07, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMLight, 0xeb16cb08, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMTexture, 0xeb16cb09, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMTexture2, 0x120f30c0, 0x1629, 0x11d0, 0x94, 0x1c, 0x0, 0x80, 0xc8, 0xc, 0xfa, 0x7b); +DEFINE_GUID(IID_IDirect3DRMWrap, 0xeb16cb0a, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMMaterial, 0xeb16cb0b, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMAnimation, 0xeb16cb0d, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMAnimationSet, 0xeb16cb0e, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMObjectArray, 0x242f6bc2, 0x3849, 0x11d0, 0x9b, 0x6d, 0x0, 0x0, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(IID_IDirect3DRMDeviceArray, 0xeb16cb10, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMViewportArray, 0xeb16cb11, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMFrameArray, 0xeb16cb12, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMVisualArray, 0xeb16cb13, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMLightArray, 0xeb16cb14, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMPickedArray, 0xeb16cb16, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMFaceArray, 0xeb16cb17, 0xd271, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMUserVisual, 0x59163de0, 0x6d43, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMShadow, 0xaf359780, 0x6ba3, 0x11cf, 0xac, 0x4a, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); +DEFINE_GUID(IID_IDirect3DRMInterpolator, 0x242f6bc1, 0x3849, 0x11d0, 0x9b, 0x6d, 0x0, 0x0, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(IID_IDirect3DRMProgressiveMesh, 0x4516ec79, 0x8f20, 0x11d0, 0x9b, 0x6d, 0x00, 0x00, 0xc0, 0x78, 0x1b, 0xc3); +DEFINE_GUID(IID_IDirect3DRMPicked2Array, 0x4516ec7b, 0x8f20, 0x11d0, 0x9b, 0x6d, 0x00, 0x00, 0xc0, 0x78, 0x1b, 0xc3); + + +typedef void (CDECL *D3DRMOBJECTCALLBACK)(LPDIRECT3DRMOBJECT obj, LPVOID arg); +typedef void (CDECL *D3DRMFRAMEMOVECALLBACK)(LPDIRECT3DRMFRAME obj, LPVOID arg, D3DVALUE delta); +typedef void (CDECL *D3DRMUPDATECALLBACK)(LPDIRECT3DRMDEVICE obj, LPVOID arg, int, LPD3DRECT); +typedef int (CDECL *D3DRMUSERVISUALCALLBACK) + ( LPDIRECT3DRMUSERVISUAL obj, LPVOID arg, D3DRMUSERVISUALREASON reason, + LPDIRECT3DRMDEVICE dev, LPDIRECT3DRMVIEWPORT view + ); +typedef HRESULT (CDECL *D3DRMLOADTEXTURECALLBACK) + (char *tex_name, void *arg, LPDIRECT3DRMTEXTURE *); +typedef void (CDECL *D3DRMLOADCALLBACK) + (LPDIRECT3DRMOBJECT object, REFIID objectguid, LPVOID arg); + + +typedef struct _D3DRMPICKDESC +{ + ULONG ulFaceIdx; + LONG lGroupIdx; + D3DVECTOR vPosition; + +} D3DRMPICKDESC, *LPD3DRMPICKDESC; + +typedef struct _D3DRMPICKDESC2 +{ + ULONG ulFaceIdx; + LONG lGroupIdx; + D3DVECTOR dvPosition; + D3DVALUE tu; + D3DVALUE tv; + D3DVECTOR dvNormal; + D3DCOLOR dcColor; + +} D3DRMPICKDESC2, *LPD3DRMPICKDESC2; + +#undef INTERFACE +#define INTERFACE IDirect3DRMObject + +/* + * Base class + */ +DECLARE_INTERFACE_(IDirect3DRMObject, IUnknown) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMVisual + +DECLARE_INTERFACE_(IDirect3DRMVisual, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMDevice + +DECLARE_INTERFACE_(IDirect3DRMDevice, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMDevice methods + */ + STDMETHOD(Init)(THIS_ ULONG width, ULONG height) PURE; + STDMETHOD(InitFromD3D)(THIS_ LPDIRECT3D lpD3D, LPDIRECT3DDEVICE lpD3DDev) PURE; + STDMETHOD(InitFromClipper)(THIS_ LPDIRECTDRAWCLIPPER lpDDClipper, LPGUID lpGUID, int width, int height) PURE; + + STDMETHOD(Update)(THIS) PURE; + STDMETHOD(AddUpdateCallback)(THIS_ D3DRMUPDATECALLBACK, LPVOID arg) PURE; + STDMETHOD(DeleteUpdateCallback)(THIS_ D3DRMUPDATECALLBACK, LPVOID arg) PURE; + STDMETHOD(SetBufferCount)(THIS_ DWORD) PURE; + STDMETHOD_(DWORD, GetBufferCount)(THIS) PURE; + + STDMETHOD(SetDither)(THIS_ BOOL) PURE; + STDMETHOD(SetShades)(THIS_ DWORD) PURE; + STDMETHOD(SetQuality)(THIS_ D3DRMRENDERQUALITY) PURE; + STDMETHOD(SetTextureQuality)(THIS_ D3DRMTEXTUREQUALITY) PURE; + + STDMETHOD(GetViewports)(THIS_ LPDIRECT3DRMVIEWPORTARRAY *return_views) PURE; + + STDMETHOD_(BOOL, GetDither)(THIS) PURE; + STDMETHOD_(DWORD, GetShades)(THIS) PURE; + STDMETHOD_(DWORD, GetHeight)(THIS) PURE; + STDMETHOD_(DWORD, GetWidth)(THIS) PURE; + STDMETHOD_(DWORD, GetTrianglesDrawn)(THIS) PURE; + STDMETHOD_(DWORD, GetWireframeOptions)(THIS) PURE; + STDMETHOD_(D3DRMRENDERQUALITY, GetQuality)(THIS) PURE; + STDMETHOD_(D3DCOLORMODEL, GetColorModel)(THIS) PURE; + STDMETHOD_(D3DRMTEXTUREQUALITY, GetTextureQuality)(THIS) PURE; + STDMETHOD(GetDirect3DDevice)(THIS_ LPDIRECT3DDEVICE *) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMDevice2 + +DECLARE_INTERFACE_(IDirect3DRMDevice2, IDirect3DRMDevice) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMDevice methods + */ + STDMETHOD(Init)(THIS_ ULONG width, ULONG height) PURE; + STDMETHOD(InitFromD3D)(THIS_ LPDIRECT3D lpD3D, LPDIRECT3DDEVICE lpD3DDev) PURE; + STDMETHOD(InitFromClipper)(THIS_ LPDIRECTDRAWCLIPPER lpDDClipper, LPGUID lpGUID, int width, int height) PURE; + + STDMETHOD(Update)(THIS) PURE; + STDMETHOD(AddUpdateCallback)(THIS_ D3DRMUPDATECALLBACK, LPVOID arg) PURE; + STDMETHOD(DeleteUpdateCallback)(THIS_ D3DRMUPDATECALLBACK, LPVOID arg) PURE; + STDMETHOD(SetBufferCount)(THIS_ DWORD) PURE; + STDMETHOD_(DWORD, GetBufferCount)(THIS) PURE; + + STDMETHOD(SetDither)(THIS_ BOOL) PURE; + STDMETHOD(SetShades)(THIS_ DWORD) PURE; + STDMETHOD(SetQuality)(THIS_ D3DRMRENDERQUALITY) PURE; + STDMETHOD(SetTextureQuality)(THIS_ D3DRMTEXTUREQUALITY) PURE; + + STDMETHOD(GetViewports)(THIS_ LPDIRECT3DRMVIEWPORTARRAY *return_views) PURE; + + STDMETHOD_(BOOL, GetDither)(THIS) PURE; + STDMETHOD_(DWORD, GetShades)(THIS) PURE; + STDMETHOD_(DWORD, GetHeight)(THIS) PURE; + STDMETHOD_(DWORD, GetWidth)(THIS) PURE; + STDMETHOD_(DWORD, GetTrianglesDrawn)(THIS) PURE; + STDMETHOD_(DWORD, GetWireframeOptions)(THIS) PURE; + STDMETHOD_(D3DRMRENDERQUALITY, GetQuality)(THIS) PURE; + STDMETHOD_(D3DCOLORMODEL, GetColorModel)(THIS) PURE; + STDMETHOD_(D3DRMTEXTUREQUALITY, GetTextureQuality)(THIS) PURE; + STDMETHOD(GetDirect3DDevice)(THIS_ LPDIRECT3DDEVICE *) PURE; + + /* + * IDirect3DRMDevice2 methods + */ + STDMETHOD(InitFromD3D2)(THIS_ LPDIRECT3D2 lpD3D, LPDIRECT3DDEVICE2 lpD3DDev) PURE; + STDMETHOD(InitFromSurface)(THIS_ LPGUID lpGUID, LPDIRECTDRAW lpDD, LPDIRECTDRAWSURFACE lpDDSBack) PURE; + STDMETHOD(SetRenderMode)(THIS_ DWORD dwFlags) PURE; + STDMETHOD_(DWORD, GetRenderMode)(THIS) PURE; + STDMETHOD(GetDirect3DDevice2)(THIS_ LPDIRECT3DDEVICE2 *) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMViewport + +DECLARE_INTERFACE_(IDirect3DRMViewport, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMViewport methods + */ + STDMETHOD(Init) + ( THIS_ LPDIRECT3DRMDEVICE dev, LPDIRECT3DRMFRAME camera, + DWORD xpos, DWORD ypos, DWORD width, DWORD height + ) PURE; + STDMETHOD(Clear)(THIS) PURE; + STDMETHOD(Render)(THIS_ LPDIRECT3DRMFRAME) PURE; + + STDMETHOD(SetFront)(THIS_ D3DVALUE) PURE; + STDMETHOD(SetBack)(THIS_ D3DVALUE) PURE; + STDMETHOD(SetField)(THIS_ D3DVALUE) PURE; + STDMETHOD(SetUniformScaling)(THIS_ BOOL) PURE; + STDMETHOD(SetCamera)(THIS_ LPDIRECT3DRMFRAME) PURE; + STDMETHOD(SetProjection)(THIS_ D3DRMPROJECTIONTYPE) PURE; + STDMETHOD(Transform)(THIS_ D3DRMVECTOR4D *d, D3DVECTOR *s) PURE; + STDMETHOD(InverseTransform)(THIS_ D3DVECTOR *d, D3DRMVECTOR4D *s) PURE; + STDMETHOD(Configure)(THIS_ LONG x, LONG y, DWORD width, DWORD height) PURE; + STDMETHOD(ForceUpdate)(THIS_ DWORD x1, DWORD y1, DWORD x2, DWORD y2) PURE; + STDMETHOD(SetPlane)(THIS_ D3DVALUE left, D3DVALUE right, D3DVALUE bottom, D3DVALUE top) PURE; + + STDMETHOD(GetCamera)(THIS_ LPDIRECT3DRMFRAME *) PURE; + STDMETHOD(GetDevice)(THIS_ LPDIRECT3DRMDEVICE *) PURE; + STDMETHOD(GetPlane)(THIS_ D3DVALUE *left, D3DVALUE *right, D3DVALUE *bottom, D3DVALUE *top) PURE; + STDMETHOD(Pick)(THIS_ LONG x, LONG y, LPDIRECT3DRMPICKEDARRAY *return_visuals) PURE; + + STDMETHOD_(BOOL, GetUniformScaling)(THIS) PURE; + STDMETHOD_(LONG, GetX)(THIS) PURE; + STDMETHOD_(LONG, GetY)(THIS) PURE; + STDMETHOD_(DWORD, GetWidth)(THIS) PURE; + STDMETHOD_(DWORD, GetHeight)(THIS) PURE; + STDMETHOD_(D3DVALUE, GetField)(THIS) PURE; + STDMETHOD_(D3DVALUE, GetBack)(THIS) PURE; + STDMETHOD_(D3DVALUE, GetFront)(THIS) PURE; + STDMETHOD_(D3DRMPROJECTIONTYPE, GetProjection)(THIS) PURE; + STDMETHOD(GetDirect3DViewport)(THIS_ LPDIRECT3DVIEWPORT *) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMFrame + +DECLARE_INTERFACE_(IDirect3DRMFrame, IDirect3DRMVisual) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMFrame methods + */ + STDMETHOD(AddChild)(THIS_ LPDIRECT3DRMFRAME child) PURE; + STDMETHOD(AddLight)(THIS_ LPDIRECT3DRMLIGHT) PURE; + STDMETHOD(AddMoveCallback)(THIS_ D3DRMFRAMEMOVECALLBACK, VOID *arg) PURE; + STDMETHOD(AddTransform)(THIS_ D3DRMCOMBINETYPE, D3DRMMATRIX4D) PURE; + STDMETHOD(AddTranslation)(THIS_ D3DRMCOMBINETYPE, D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(AddScale)(THIS_ D3DRMCOMBINETYPE, D3DVALUE sx, D3DVALUE sy, D3DVALUE sz) PURE; + STDMETHOD(AddRotation)(THIS_ D3DRMCOMBINETYPE, D3DVALUE x, D3DVALUE y, D3DVALUE z, D3DVALUE theta) PURE; + STDMETHOD(AddVisual)(THIS_ LPDIRECT3DRMVISUAL) PURE; + STDMETHOD(GetChildren)(THIS_ LPDIRECT3DRMFRAMEARRAY *children) PURE; + STDMETHOD_(D3DCOLOR, GetColor)(THIS) PURE; + STDMETHOD(GetLights)(THIS_ LPDIRECT3DRMLIGHTARRAY *lights) PURE; + STDMETHOD_(D3DRMMATERIALMODE, GetMaterialMode)(THIS) PURE; + STDMETHOD(GetParent)(THIS_ LPDIRECT3DRMFRAME *) PURE; + STDMETHOD(GetPosition)(THIS_ LPDIRECT3DRMFRAME reference, LPD3DVECTOR return_position) PURE; + STDMETHOD(GetRotation)(THIS_ LPDIRECT3DRMFRAME reference, LPD3DVECTOR axis, LPD3DVALUE return_theta) PURE; + STDMETHOD(GetScene)(THIS_ LPDIRECT3DRMFRAME *) PURE; + STDMETHOD_(D3DRMSORTMODE, GetSortMode)(THIS) PURE; + STDMETHOD(GetTexture)(THIS_ LPDIRECT3DRMTEXTURE *) PURE; + STDMETHOD(GetTransform)(THIS_ D3DRMMATRIX4D return_matrix) PURE; + STDMETHOD(GetVelocity)(THIS_ LPDIRECT3DRMFRAME reference, LPD3DVECTOR return_velocity, BOOL with_rotation) PURE; + STDMETHOD(GetOrientation)(THIS_ LPDIRECT3DRMFRAME reference, LPD3DVECTOR dir, LPD3DVECTOR up) PURE; + STDMETHOD(GetVisuals)(THIS_ LPDIRECT3DRMVISUALARRAY *visuals) PURE; + STDMETHOD(GetTextureTopology)(THIS_ BOOL *wrap_u, BOOL *wrap_v) PURE; + STDMETHOD(InverseTransform)(THIS_ D3DVECTOR *d, D3DVECTOR *s) PURE; + STDMETHOD(Load)(THIS_ LPVOID filename, LPVOID name, D3DRMLOADOPTIONS loadflags, D3DRMLOADTEXTURECALLBACK, LPVOID lpArg)PURE; + STDMETHOD(LookAt)(THIS_ LPDIRECT3DRMFRAME target, LPDIRECT3DRMFRAME reference, D3DRMFRAMECONSTRAINT) PURE; + STDMETHOD(Move)(THIS_ D3DVALUE delta) PURE; + STDMETHOD(DeleteChild)(THIS_ LPDIRECT3DRMFRAME) PURE; + STDMETHOD(DeleteLight)(THIS_ LPDIRECT3DRMLIGHT) PURE; + STDMETHOD(DeleteMoveCallback)(THIS_ D3DRMFRAMEMOVECALLBACK, VOID *arg) PURE; + STDMETHOD(DeleteVisual)(THIS_ LPDIRECT3DRMVISUAL) PURE; + STDMETHOD_(D3DCOLOR, GetSceneBackground)(THIS) PURE; + STDMETHOD(GetSceneBackgroundDepth)(THIS_ LPDIRECTDRAWSURFACE *) PURE; + STDMETHOD_(D3DCOLOR, GetSceneFogColor)(THIS) PURE; + STDMETHOD_(BOOL, GetSceneFogEnable)(THIS) PURE; + STDMETHOD_(D3DRMFOGMODE, GetSceneFogMode)(THIS) PURE; + STDMETHOD(GetSceneFogParams)(THIS_ D3DVALUE *return_start, D3DVALUE *return_end, D3DVALUE *return_density) PURE; + STDMETHOD(SetSceneBackground)(THIS_ D3DCOLOR) PURE; + STDMETHOD(SetSceneBackgroundRGB)(THIS_ D3DVALUE red, D3DVALUE green, D3DVALUE blue) PURE; + STDMETHOD(SetSceneBackgroundDepth)(THIS_ LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(SetSceneBackgroundImage)(THIS_ LPDIRECT3DRMTEXTURE) PURE; + STDMETHOD(SetSceneFogEnable)(THIS_ BOOL) PURE; + STDMETHOD(SetSceneFogColor)(THIS_ D3DCOLOR) PURE; + STDMETHOD(SetSceneFogMode)(THIS_ D3DRMFOGMODE) PURE; + STDMETHOD(SetSceneFogParams)(THIS_ D3DVALUE start, D3DVALUE end, D3DVALUE density) PURE; + STDMETHOD(SetColor)(THIS_ D3DCOLOR) PURE; + STDMETHOD(SetColorRGB)(THIS_ D3DVALUE red, D3DVALUE green, D3DVALUE blue) PURE; + STDMETHOD_(D3DRMZBUFFERMODE, GetZbufferMode)(THIS) PURE; + STDMETHOD(SetMaterialMode)(THIS_ D3DRMMATERIALMODE) PURE; + STDMETHOD(SetOrientation) + ( THIS_ LPDIRECT3DRMFRAME reference, + D3DVALUE dx, D3DVALUE dy, D3DVALUE dz, + D3DVALUE ux, D3DVALUE uy, D3DVALUE uz + ) PURE; + STDMETHOD(SetPosition)(THIS_ LPDIRECT3DRMFRAME reference, D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(SetRotation)(THIS_ LPDIRECT3DRMFRAME reference, D3DVALUE x, D3DVALUE y, D3DVALUE z, D3DVALUE theta) PURE; + STDMETHOD(SetSortMode)(THIS_ D3DRMSORTMODE) PURE; + STDMETHOD(SetTexture)(THIS_ LPDIRECT3DRMTEXTURE) PURE; + STDMETHOD(SetTextureTopology)(THIS_ BOOL wrap_u, BOOL wrap_v) PURE; + STDMETHOD(SetVelocity)(THIS_ LPDIRECT3DRMFRAME reference, D3DVALUE x, D3DVALUE y, D3DVALUE z, BOOL with_rotation) PURE; + STDMETHOD(SetZbufferMode)(THIS_ D3DRMZBUFFERMODE) PURE; + STDMETHOD(Transform)(THIS_ D3DVECTOR *d, D3DVECTOR *s) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMFrame2 + +DECLARE_INTERFACE_(IDirect3DRMFrame2, IDirect3DRMFrame) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMFrame methods + */ + STDMETHOD(AddChild)(THIS_ LPDIRECT3DRMFRAME child) PURE; + STDMETHOD(AddLight)(THIS_ LPDIRECT3DRMLIGHT) PURE; + STDMETHOD(AddMoveCallback)(THIS_ D3DRMFRAMEMOVECALLBACK, VOID *arg) PURE; + STDMETHOD(AddTransform)(THIS_ D3DRMCOMBINETYPE, D3DRMMATRIX4D) PURE; + STDMETHOD(AddTranslation)(THIS_ D3DRMCOMBINETYPE, D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(AddScale)(THIS_ D3DRMCOMBINETYPE, D3DVALUE sx, D3DVALUE sy, D3DVALUE sz) PURE; + STDMETHOD(AddRotation)(THIS_ D3DRMCOMBINETYPE, D3DVALUE x, D3DVALUE y, D3DVALUE z, D3DVALUE theta) PURE; + STDMETHOD(AddVisual)(THIS_ LPDIRECT3DRMVISUAL) PURE; + STDMETHOD(GetChildren)(THIS_ LPDIRECT3DRMFRAMEARRAY *children) PURE; + STDMETHOD_(D3DCOLOR, GetColor)(THIS) PURE; + STDMETHOD(GetLights)(THIS_ LPDIRECT3DRMLIGHTARRAY *lights) PURE; + STDMETHOD_(D3DRMMATERIALMODE, GetMaterialMode)(THIS) PURE; + STDMETHOD(GetParent)(THIS_ LPDIRECT3DRMFRAME *) PURE; + STDMETHOD(GetPosition)(THIS_ LPDIRECT3DRMFRAME reference, LPD3DVECTOR return_position) PURE; + STDMETHOD(GetRotation)(THIS_ LPDIRECT3DRMFRAME reference, LPD3DVECTOR axis, LPD3DVALUE return_theta) PURE; + STDMETHOD(GetScene)(THIS_ LPDIRECT3DRMFRAME *) PURE; + STDMETHOD_(D3DRMSORTMODE, GetSortMode)(THIS) PURE; + STDMETHOD(GetTexture)(THIS_ LPDIRECT3DRMTEXTURE *) PURE; + STDMETHOD(GetTransform)(THIS_ D3DRMMATRIX4D return_matrix) PURE; + STDMETHOD(GetVelocity)(THIS_ LPDIRECT3DRMFRAME reference, LPD3DVECTOR return_velocity, BOOL with_rotation) PURE; + STDMETHOD(GetOrientation)(THIS_ LPDIRECT3DRMFRAME reference, LPD3DVECTOR dir, LPD3DVECTOR up) PURE; + STDMETHOD(GetVisuals)(THIS_ LPDIRECT3DRMVISUALARRAY *visuals) PURE; + STDMETHOD(GetTextureTopology)(THIS_ BOOL *wrap_u, BOOL *wrap_v) PURE; + STDMETHOD(InverseTransform)(THIS_ D3DVECTOR *d, D3DVECTOR *s) PURE; + STDMETHOD(Load)(THIS_ LPVOID filename, LPVOID name, D3DRMLOADOPTIONS loadflags, D3DRMLOADTEXTURECALLBACK, LPVOID lpArg)PURE; + STDMETHOD(LookAt)(THIS_ LPDIRECT3DRMFRAME target, LPDIRECT3DRMFRAME reference, D3DRMFRAMECONSTRAINT) PURE; + STDMETHOD(Move)(THIS_ D3DVALUE delta) PURE; + STDMETHOD(DeleteChild)(THIS_ LPDIRECT3DRMFRAME) PURE; + STDMETHOD(DeleteLight)(THIS_ LPDIRECT3DRMLIGHT) PURE; + STDMETHOD(DeleteMoveCallback)(THIS_ D3DRMFRAMEMOVECALLBACK, VOID *arg) PURE; + STDMETHOD(DeleteVisual)(THIS_ LPDIRECT3DRMVISUAL) PURE; + STDMETHOD_(D3DCOLOR, GetSceneBackground)(THIS) PURE; + STDMETHOD(GetSceneBackgroundDepth)(THIS_ LPDIRECTDRAWSURFACE *) PURE; + STDMETHOD_(D3DCOLOR, GetSceneFogColor)(THIS) PURE; + STDMETHOD_(BOOL, GetSceneFogEnable)(THIS) PURE; + STDMETHOD_(D3DRMFOGMODE, GetSceneFogMode)(THIS) PURE; + STDMETHOD(GetSceneFogParams)(THIS_ D3DVALUE *return_start, D3DVALUE *return_end, D3DVALUE *return_density) PURE; + STDMETHOD(SetSceneBackground)(THIS_ D3DCOLOR) PURE; + STDMETHOD(SetSceneBackgroundRGB)(THIS_ D3DVALUE red, D3DVALUE green, D3DVALUE blue) PURE; + STDMETHOD(SetSceneBackgroundDepth)(THIS_ LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(SetSceneBackgroundImage)(THIS_ LPDIRECT3DRMTEXTURE) PURE; + STDMETHOD(SetSceneFogEnable)(THIS_ BOOL) PURE; + STDMETHOD(SetSceneFogColor)(THIS_ D3DCOLOR) PURE; + STDMETHOD(SetSceneFogMode)(THIS_ D3DRMFOGMODE) PURE; + STDMETHOD(SetSceneFogParams)(THIS_ D3DVALUE start, D3DVALUE end, D3DVALUE density) PURE; + STDMETHOD(SetColor)(THIS_ D3DCOLOR) PURE; + STDMETHOD(SetColorRGB)(THIS_ D3DVALUE red, D3DVALUE green, D3DVALUE blue) PURE; + STDMETHOD_(D3DRMZBUFFERMODE, GetZbufferMode)(THIS) PURE; + STDMETHOD(SetMaterialMode)(THIS_ D3DRMMATERIALMODE) PURE; + STDMETHOD(SetOrientation) + ( THIS_ LPDIRECT3DRMFRAME reference, + D3DVALUE dx, D3DVALUE dy, D3DVALUE dz, + D3DVALUE ux, D3DVALUE uy, D3DVALUE uz + ) PURE; + STDMETHOD(SetPosition)(THIS_ LPDIRECT3DRMFRAME reference, D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(SetRotation)(THIS_ LPDIRECT3DRMFRAME reference, D3DVALUE x, D3DVALUE y, D3DVALUE z, D3DVALUE theta) PURE; + STDMETHOD(SetSortMode)(THIS_ D3DRMSORTMODE) PURE; + STDMETHOD(SetTexture)(THIS_ LPDIRECT3DRMTEXTURE) PURE; + STDMETHOD(SetTextureTopology)(THIS_ BOOL wrap_u, BOOL wrap_v) PURE; + STDMETHOD(SetVelocity)(THIS_ LPDIRECT3DRMFRAME reference, D3DVALUE x, D3DVALUE y, D3DVALUE z, BOOL with_rotation) PURE; + STDMETHOD(SetZbufferMode)(THIS_ D3DRMZBUFFERMODE) PURE; + STDMETHOD(Transform)(THIS_ D3DVECTOR *d, D3DVECTOR *s) PURE; + + /* + * IDirect3DRMFrame2 methods + */ + STDMETHOD(AddMoveCallback2)(THIS_ D3DRMFRAMEMOVECALLBACK, VOID *arg, DWORD dwFlags) PURE; + STDMETHOD(GetBox)(THIS_ LPD3DRMBOX) PURE; + STDMETHOD_(BOOL, GetBoxEnable)(THIS) PURE; + STDMETHOD(GetAxes)(THIS_ LPD3DVECTOR dir, LPD3DVECTOR up); + STDMETHOD(GetMaterial)(THIS_ LPDIRECT3DRMMATERIAL *) PURE; + STDMETHOD_(BOOL, GetInheritAxes)(THIS); + STDMETHOD(GetHierarchyBox)(THIS_ LPD3DRMBOX) PURE; + + STDMETHOD(SetBox)(THIS_ LPD3DRMBOX) PURE; + STDMETHOD(SetBoxEnable)(THIS_ BOOL) PURE; + STDMETHOD(SetAxes)(THIS_ D3DVALUE dx, D3DVALUE dy, D3DVALUE dz, + D3DVALUE ux, D3DVALUE uy, D3DVALUE uz); + STDMETHOD(SetInheritAxes)(THIS_ BOOL inherit_from_parent); + STDMETHOD(SetMaterial)(THIS_ LPDIRECT3DRMMATERIAL) PURE; + STDMETHOD(SetQuaternion)(THIS_ LPDIRECT3DRMFRAME reference, D3DRMQUATERNION *q) PURE; + + STDMETHOD(RayPick)(THIS_ LPDIRECT3DRMFRAME reference, LPD3DRMRAY ray, DWORD dwFlags, LPDIRECT3DRMPICKED2ARRAY *return_visuals) PURE; + STDMETHOD(Save)(THIS_ LPCSTR filename, D3DRMXOFFORMAT d3dFormat, + D3DRMSAVEOPTIONS d3dSaveFlags); +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMMesh + +DECLARE_INTERFACE_(IDirect3DRMMesh, IDirect3DRMVisual) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMMesh methods + */ + STDMETHOD(Scale)(THIS_ D3DVALUE sx, D3DVALUE sy, D3DVALUE sz) PURE; + STDMETHOD(Translate)(THIS_ D3DVALUE tx, D3DVALUE ty, D3DVALUE tz) PURE; + STDMETHOD(GetBox)(THIS_ D3DRMBOX *) PURE; + STDMETHOD(AddGroup)(THIS_ unsigned vCount, unsigned fCount, unsigned vPerFace, unsigned *fData, D3DRMGROUPINDEX *returnId) PURE; + STDMETHOD(SetVertices)(THIS_ D3DRMGROUPINDEX id, unsigned index, unsigned count, D3DRMVERTEX *values) PURE; + STDMETHOD(SetGroupColor)(THIS_ D3DRMGROUPINDEX id, D3DCOLOR value) PURE; + STDMETHOD(SetGroupColorRGB)(THIS_ D3DRMGROUPINDEX id, D3DVALUE red, D3DVALUE green, D3DVALUE blue) PURE; + STDMETHOD(SetGroupMapping)(THIS_ D3DRMGROUPINDEX id, D3DRMMAPPING value) PURE; + STDMETHOD(SetGroupQuality)(THIS_ D3DRMGROUPINDEX id, D3DRMRENDERQUALITY value) PURE; + STDMETHOD(SetGroupMaterial)(THIS_ D3DRMGROUPINDEX id, LPDIRECT3DRMMATERIAL value) PURE; + STDMETHOD(SetGroupTexture)(THIS_ D3DRMGROUPINDEX id, LPDIRECT3DRMTEXTURE value) PURE; + + STDMETHOD_(unsigned, GetGroupCount)(THIS) PURE; + STDMETHOD(GetGroup)(THIS_ D3DRMGROUPINDEX id, unsigned *vCount, unsigned *fCount, unsigned *vPerFace, DWORD *fDataSize, unsigned *fData) PURE; + STDMETHOD(GetVertices)(THIS_ D3DRMGROUPINDEX id, DWORD index, DWORD count, D3DRMVERTEX *returnPtr) PURE; + STDMETHOD_(D3DCOLOR, GetGroupColor)(THIS_ D3DRMGROUPINDEX id) PURE; + STDMETHOD_(D3DRMMAPPING, GetGroupMapping)(THIS_ D3DRMGROUPINDEX id) PURE; + STDMETHOD_(D3DRMRENDERQUALITY, GetGroupQuality)(THIS_ D3DRMGROUPINDEX id) PURE; + STDMETHOD(GetGroupMaterial)(THIS_ D3DRMGROUPINDEX id, LPDIRECT3DRMMATERIAL *returnPtr) PURE; + STDMETHOD(GetGroupTexture)(THIS_ D3DRMGROUPINDEX id, LPDIRECT3DRMTEXTURE *returnPtr) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMProgressiveMesh + +DECLARE_INTERFACE_(IDirect3DRMProgressiveMesh, IDirect3DRMVisual) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMProgressiveMesh methods + */ + STDMETHOD(Load) (THIS_ LPVOID lpObjLocation, LPVOID lpObjId, + D3DRMLOADOPTIONS dloLoadflags, D3DRMLOADTEXTURECALLBACK lpCallback, + LPVOID lpArg) PURE; + STDMETHOD(GetLoadStatus) (THIS_ LPD3DRMPMESHLOADSTATUS lpStatus) PURE; + STDMETHOD(SetMinRenderDetail) (THIS_ D3DVALUE d3dVal) PURE; + STDMETHOD(Abort) (THIS_ DWORD dwFlags) PURE; + + STDMETHOD(GetFaceDetail) (THIS_ LPDWORD lpdwCount) PURE; + STDMETHOD(GetVertexDetail) (THIS_ LPDWORD lpdwCount) PURE; + STDMETHOD(SetFaceDetail) (THIS_ DWORD dwCount) PURE; + STDMETHOD(SetVertexDetail) (THIS_ DWORD dwCount) PURE; + STDMETHOD(GetFaceDetailRange) (THIS_ LPDWORD lpdwMin, LPDWORD lpdwMax) PURE; + STDMETHOD(GetVertexDetailRange) (THIS_ LPDWORD lpdwMin, LPDWORD lpdwMax) PURE; + STDMETHOD(GetDetail) (THIS_ D3DVALUE *lpdvVal) PURE; + STDMETHOD(SetDetail) (THIS_ D3DVALUE d3dVal) PURE; + + STDMETHOD(RegisterEvents) (THIS_ HANDLE hEvent, DWORD dwFlags, DWORD dwReserved) PURE; + STDMETHOD(CreateMesh) (THIS_ LPDIRECT3DRMMESH *lplpD3DRMMesh) PURE; + STDMETHOD(Duplicate) (THIS_ LPDIRECT3DRMPROGRESSIVEMESH *lplpD3DRMPMesh) PURE; + STDMETHOD(GetBox) (THIS_ LPD3DRMBOX lpBBox) PURE; + STDMETHOD(SetQuality) (THIS_ D3DRMRENDERQUALITY) PURE; + STDMETHOD(GetQuality) (THIS_ LPD3DRMRENDERQUALITY lpdwquality) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMShadow + +DECLARE_INTERFACE_(IDirect3DRMShadow, IDirect3DRMVisual) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMShadow methods + */ + STDMETHOD(Init) + ( THIS_ LPDIRECT3DRMVISUAL visual, LPDIRECT3DRMLIGHT light, + D3DVALUE px, D3DVALUE py, D3DVALUE pz, + D3DVALUE nx, D3DVALUE ny, D3DVALUE nz + ) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMFace + +DECLARE_INTERFACE_(IDirect3DRMFace, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMFace methods + */ + STDMETHOD(AddVertex)(THIS_ D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(AddVertexAndNormalIndexed)(THIS_ DWORD vertex, DWORD normal) PURE; + STDMETHOD(SetColorRGB)(THIS_ D3DVALUE, D3DVALUE, D3DVALUE) PURE; + STDMETHOD(SetColor)(THIS_ D3DCOLOR) PURE; + STDMETHOD(SetTexture)(THIS_ LPDIRECT3DRMTEXTURE) PURE; + STDMETHOD(SetTextureCoordinates)(THIS_ DWORD vertex, D3DVALUE u, D3DVALUE v) PURE; + STDMETHOD(SetMaterial)(THIS_ LPDIRECT3DRMMATERIAL) PURE; + STDMETHOD(SetTextureTopology)(THIS_ BOOL wrap_u, BOOL wrap_v) PURE; + + STDMETHOD(GetVertex)(THIS_ DWORD index, D3DVECTOR *vertex, D3DVECTOR *normal) PURE; + STDMETHOD(GetVertices)(THIS_ DWORD *vertex_count, D3DVECTOR *coords, D3DVECTOR *normals); + STDMETHOD(GetTextureCoordinates)(THIS_ DWORD vertex, D3DVALUE *u, D3DVALUE *v) PURE; + STDMETHOD(GetTextureTopology)(THIS_ BOOL *wrap_u, BOOL *wrap_v) PURE; + STDMETHOD(GetNormal)(THIS_ D3DVECTOR *) PURE; + STDMETHOD(GetTexture)(THIS_ LPDIRECT3DRMTEXTURE *) PURE; + STDMETHOD(GetMaterial)(THIS_ LPDIRECT3DRMMATERIAL *) PURE; + + STDMETHOD_(int, GetVertexCount)(THIS) PURE; + STDMETHOD_(int, GetVertexIndex)(THIS_ DWORD which) PURE; + STDMETHOD_(int, GetTextureCoordinateIndex)(THIS_ DWORD which) PURE; + STDMETHOD_(D3DCOLOR, GetColor)(THIS) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMMeshBuilder + +DECLARE_INTERFACE_(IDirect3DRMMeshBuilder, IDirect3DRMVisual) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMMeshBuilder methods + */ + STDMETHOD(Load)(THIS_ LPVOID filename, LPVOID name, D3DRMLOADOPTIONS loadflags, D3DRMLOADTEXTURECALLBACK, LPVOID lpArg) PURE; + STDMETHOD(Save)(THIS_ const char *filename, D3DRMXOFFORMAT, D3DRMSAVEOPTIONS save) PURE; + STDMETHOD(Scale)(THIS_ D3DVALUE sx, D3DVALUE sy, D3DVALUE sz) PURE; + STDMETHOD(Translate)(THIS_ D3DVALUE tx, D3DVALUE ty, D3DVALUE tz) PURE; + STDMETHOD(SetColorSource)(THIS_ D3DRMCOLORSOURCE) PURE; + STDMETHOD(GetBox)(THIS_ D3DRMBOX *) PURE; + STDMETHOD(GenerateNormals)(THIS) PURE; + STDMETHOD_(D3DRMCOLORSOURCE, GetColorSource)(THIS) PURE; + + STDMETHOD(AddMesh)(THIS_ LPDIRECT3DRMMESH) PURE; + STDMETHOD(AddMeshBuilder)(THIS_ LPDIRECT3DRMMESHBUILDER) PURE; + STDMETHOD(AddFrame)(THIS_ LPDIRECT3DRMFRAME) PURE; + STDMETHOD(AddFace)(THIS_ LPDIRECT3DRMFACE) PURE; + STDMETHOD(AddFaces) + ( THIS_ DWORD vcount, D3DVECTOR *vertices, DWORD ncount, D3DVECTOR *normals, + DWORD *data, LPDIRECT3DRMFACEARRAY* + ) PURE; + STDMETHOD(ReserveSpace)(THIS_ DWORD vertex_Count, DWORD normal_count, DWORD face_count) PURE; + STDMETHOD(SetColorRGB)(THIS_ D3DVALUE red, D3DVALUE green, D3DVALUE blue) PURE; + STDMETHOD(SetColor)(THIS_ D3DCOLOR) PURE; + STDMETHOD(SetTexture)(THIS_ LPDIRECT3DRMTEXTURE) PURE; + STDMETHOD(SetMaterial)(THIS_ LPDIRECT3DRMMATERIAL) PURE; + STDMETHOD(SetTextureTopology)(THIS_ BOOL wrap_u, BOOL wrap_v) PURE; + STDMETHOD(SetQuality)(THIS_ D3DRMRENDERQUALITY) PURE; + STDMETHOD(SetPerspective)(THIS_ BOOL) PURE; + STDMETHOD(SetVertex)(THIS_ DWORD index, D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(SetNormal)(THIS_ DWORD index, D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(SetTextureCoordinates)(THIS_ DWORD index, D3DVALUE u, D3DVALUE v) PURE; + STDMETHOD(SetVertexColor)(THIS_ DWORD index, D3DCOLOR) PURE; + STDMETHOD(SetVertexColorRGB)(THIS_ DWORD index, D3DVALUE red, D3DVALUE green, D3DVALUE blue) PURE; + + STDMETHOD(GetFaces)(THIS_ LPDIRECT3DRMFACEARRAY*) PURE; + STDMETHOD(GetVertices) + ( THIS_ DWORD *vcount, D3DVECTOR *vertices, DWORD *ncount, D3DVECTOR *normals, DWORD *face_data_size, DWORD *face_data + ) PURE; + STDMETHOD(GetTextureCoordinates)(THIS_ DWORD index, D3DVALUE *u, D3DVALUE *v) PURE; + + STDMETHOD_(int, AddVertex)(THIS_ D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD_(int, AddNormal)(THIS_ D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(CreateFace)(THIS_ LPDIRECT3DRMFACE*) PURE; + STDMETHOD_(D3DRMRENDERQUALITY, GetQuality)(THIS) PURE; + STDMETHOD_(BOOL, GetPerspective)(THIS) PURE; + STDMETHOD_(int, GetFaceCount)(THIS) PURE; + STDMETHOD_(int, GetVertexCount)(THIS) PURE; + STDMETHOD_(D3DCOLOR, GetVertexColor)(THIS_ DWORD index) PURE; + + STDMETHOD(CreateMesh)(THIS_ LPDIRECT3DRMMESH*) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMMeshBuilder2 + +DECLARE_INTERFACE_(IDirect3DRMMeshBuilder2, IDirect3DRMMeshBuilder) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMMeshBuilder methods + */ + STDMETHOD(Load)(THIS_ LPVOID filename, LPVOID name, D3DRMLOADOPTIONS loadflags, D3DRMLOADTEXTURECALLBACK, LPVOID lpArg) PURE; + STDMETHOD(Save)(THIS_ const char *filename, D3DRMXOFFORMAT, D3DRMSAVEOPTIONS save) PURE; + STDMETHOD(Scale)(THIS_ D3DVALUE sx, D3DVALUE sy, D3DVALUE sz) PURE; + STDMETHOD(Translate)(THIS_ D3DVALUE tx, D3DVALUE ty, D3DVALUE tz) PURE; + STDMETHOD(SetColorSource)(THIS_ D3DRMCOLORSOURCE) PURE; + STDMETHOD(GetBox)(THIS_ D3DRMBOX *) PURE; + STDMETHOD(GenerateNormals)(THIS) PURE; + STDMETHOD_(D3DRMCOLORSOURCE, GetColorSource)(THIS) PURE; + + STDMETHOD(AddMesh)(THIS_ LPDIRECT3DRMMESH) PURE; + STDMETHOD(AddMeshBuilder)(THIS_ LPDIRECT3DRMMESHBUILDER) PURE; + STDMETHOD(AddFrame)(THIS_ LPDIRECT3DRMFRAME) PURE; + STDMETHOD(AddFace)(THIS_ LPDIRECT3DRMFACE) PURE; + STDMETHOD(AddFaces) + ( THIS_ DWORD vcount, D3DVECTOR *vertices, DWORD ncount, D3DVECTOR *normals, + DWORD *data, LPDIRECT3DRMFACEARRAY* + ) PURE; + STDMETHOD(ReserveSpace)(THIS_ DWORD vertex_Count, DWORD normal_count, DWORD face_count) PURE; + STDMETHOD(SetColorRGB)(THIS_ D3DVALUE red, D3DVALUE green, D3DVALUE blue) PURE; + STDMETHOD(SetColor)(THIS_ D3DCOLOR) PURE; + STDMETHOD(SetTexture)(THIS_ LPDIRECT3DRMTEXTURE) PURE; + STDMETHOD(SetMaterial)(THIS_ LPDIRECT3DRMMATERIAL) PURE; + STDMETHOD(SetTextureTopology)(THIS_ BOOL wrap_u, BOOL wrap_v) PURE; + STDMETHOD(SetQuality)(THIS_ D3DRMRENDERQUALITY) PURE; + STDMETHOD(SetPerspective)(THIS_ BOOL) PURE; + STDMETHOD(SetVertex)(THIS_ DWORD index, D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(SetNormal)(THIS_ DWORD index, D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(SetTextureCoordinates)(THIS_ DWORD index, D3DVALUE u, D3DVALUE v) PURE; + STDMETHOD(SetVertexColor)(THIS_ DWORD index, D3DCOLOR) PURE; + STDMETHOD(SetVertexColorRGB)(THIS_ DWORD index, D3DVALUE red, D3DVALUE green, D3DVALUE blue) PURE; + + STDMETHOD(GetFaces)(THIS_ LPDIRECT3DRMFACEARRAY*) PURE; + STDMETHOD(GetVertices) + ( THIS_ DWORD *vcount, D3DVECTOR *vertices, DWORD *ncount, D3DVECTOR *normals, DWORD *face_data_size, DWORD *face_data + ) PURE; + STDMETHOD(GetTextureCoordinates)(THIS_ DWORD index, D3DVALUE *u, D3DVALUE *v) PURE; + + STDMETHOD_(int, AddVertex)(THIS_ D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD_(int, AddNormal)(THIS_ D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(CreateFace)(THIS_ LPDIRECT3DRMFACE*) PURE; + STDMETHOD_(D3DRMRENDERQUALITY, GetQuality)(THIS) PURE; + STDMETHOD_(BOOL, GetPerspective)(THIS) PURE; + STDMETHOD_(int, GetFaceCount)(THIS) PURE; + STDMETHOD_(int, GetVertexCount)(THIS) PURE; + STDMETHOD_(D3DCOLOR, GetVertexColor)(THIS_ DWORD index) PURE; + + STDMETHOD(CreateMesh)(THIS_ LPDIRECT3DRMMESH*) PURE; + + /* + * IDirect3DRMMeshBuilder2 methods + */ + STDMETHOD(GenerateNormals2)(THIS_ D3DVALUE crease, DWORD dwFlags) PURE; + STDMETHOD(GetFace)(THIS_ DWORD index, LPDIRECT3DRMFACE*) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMLight + +DECLARE_INTERFACE_(IDirect3DRMLight, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMLight methods + */ + STDMETHOD(SetType)(THIS_ D3DRMLIGHTTYPE) PURE; + STDMETHOD(SetColor)(THIS_ D3DCOLOR) PURE; + STDMETHOD(SetColorRGB)(THIS_ D3DVALUE red, D3DVALUE green, D3DVALUE blue) PURE; + STDMETHOD(SetRange)(THIS_ D3DVALUE) PURE; + STDMETHOD(SetUmbra)(THIS_ D3DVALUE) PURE; + STDMETHOD(SetPenumbra)(THIS_ D3DVALUE) PURE; + STDMETHOD(SetConstantAttenuation)(THIS_ D3DVALUE) PURE; + STDMETHOD(SetLinearAttenuation)(THIS_ D3DVALUE) PURE; + STDMETHOD(SetQuadraticAttenuation)(THIS_ D3DVALUE) PURE; + + STDMETHOD_(D3DVALUE, GetRange)(THIS) PURE; + STDMETHOD_(D3DVALUE, GetUmbra)(THIS) PURE; + STDMETHOD_(D3DVALUE, GetPenumbra)(THIS) PURE; + STDMETHOD_(D3DVALUE, GetConstantAttenuation)(THIS) PURE; + STDMETHOD_(D3DVALUE, GetLinearAttenuation)(THIS) PURE; + STDMETHOD_(D3DVALUE, GetQuadraticAttenuation)(THIS) PURE; + STDMETHOD_(D3DCOLOR, GetColor)(THIS) PURE; + STDMETHOD_(D3DRMLIGHTTYPE, GetType)(THIS) PURE; + + STDMETHOD(SetEnableFrame)(THIS_ LPDIRECT3DRMFRAME) PURE; + STDMETHOD(GetEnableFrame)(THIS_ LPDIRECT3DRMFRAME*) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMTexture + +DECLARE_INTERFACE_(IDirect3DRMTexture, IDirect3DRMVisual) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMTexture methods + */ + STDMETHOD(InitFromFile)(THIS_ const char *filename) PURE; + STDMETHOD(InitFromSurface)(THIS_ LPDIRECTDRAWSURFACE lpDDS) PURE; + STDMETHOD(InitFromResource)(THIS_ HRSRC) PURE; + STDMETHOD(Changed)(THIS_ BOOL pixels, BOOL palette) PURE; + + STDMETHOD(SetColors)(THIS_ DWORD) PURE; + STDMETHOD(SetShades)(THIS_ DWORD) PURE; + STDMETHOD(SetDecalSize)(THIS_ D3DVALUE width, D3DVALUE height) PURE; + STDMETHOD(SetDecalOrigin)(THIS_ LONG x, LONG y) PURE; + STDMETHOD(SetDecalScale)(THIS_ DWORD) PURE; + STDMETHOD(SetDecalTransparency)(THIS_ BOOL) PURE; + STDMETHOD(SetDecalTransparentColor)(THIS_ D3DCOLOR) PURE; + + STDMETHOD(GetDecalSize)(THIS_ D3DVALUE *width_return, D3DVALUE *height_return) PURE; + STDMETHOD(GetDecalOrigin)(THIS_ LONG *x_return, LONG *y_return) PURE; + + STDMETHOD_(D3DRMIMAGE *, GetImage)(THIS) PURE; + STDMETHOD_(DWORD, GetShades)(THIS) PURE; + STDMETHOD_(DWORD, GetColors)(THIS) PURE; + STDMETHOD_(DWORD, GetDecalScale)(THIS) PURE; + STDMETHOD_(BOOL, GetDecalTransparency)(THIS) PURE; + STDMETHOD_(D3DCOLOR, GetDecalTransparentColor)(THIS) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMTexture2 + +DECLARE_INTERFACE_(IDirect3DRMTexture2, IDirect3DRMTexture) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMTexture methods + */ + STDMETHOD(InitFromFile)(THIS_ const char *filename) PURE; + STDMETHOD(InitFromSurface)(THIS_ LPDIRECTDRAWSURFACE lpDDS) PURE; + STDMETHOD(InitFromResource)(THIS_ HRSRC) PURE; + STDMETHOD(Changed)(THIS_ BOOL pixels, BOOL palette) PURE; + + STDMETHOD(SetColors)(THIS_ DWORD) PURE; + STDMETHOD(SetShades)(THIS_ DWORD) PURE; + STDMETHOD(SetDecalSize)(THIS_ D3DVALUE width, D3DVALUE height) PURE; + STDMETHOD(SetDecalOrigin)(THIS_ LONG x, LONG y) PURE; + STDMETHOD(SetDecalScale)(THIS_ DWORD) PURE; + STDMETHOD(SetDecalTransparency)(THIS_ BOOL) PURE; + STDMETHOD(SetDecalTransparentColor)(THIS_ D3DCOLOR) PURE; + + STDMETHOD(GetDecalSize)(THIS_ D3DVALUE *width_return, D3DVALUE *height_return) PURE; + STDMETHOD(GetDecalOrigin)(THIS_ LONG *x_return, LONG *y_return) PURE; + + STDMETHOD_(D3DRMIMAGE *, GetImage)(THIS) PURE; + STDMETHOD_(DWORD, GetShades)(THIS) PURE; + STDMETHOD_(DWORD, GetColors)(THIS) PURE; + STDMETHOD_(DWORD, GetDecalScale)(THIS) PURE; + STDMETHOD_(BOOL, GetDecalTransparency)(THIS) PURE; + STDMETHOD_(D3DCOLOR, GetDecalTransparentColor)(THIS) PURE; + + /* + * IDirect3DRMTexture2 methods + */ + STDMETHOD(InitFromImage)(THIS_ LPD3DRMIMAGE) PURE; + STDMETHOD(InitFromResource2)(THIS_ HMODULE hModule, LPCTSTR strName, LPCTSTR strType) PURE; + STDMETHOD(GenerateMIPMap)(THIS_ DWORD) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMWrap + +DECLARE_INTERFACE_(IDirect3DRMWrap, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMWrap methods + */ + STDMETHOD(Init) + ( THIS_ D3DRMWRAPTYPE, LPDIRECT3DRMFRAME ref, + D3DVALUE ox, D3DVALUE oy, D3DVALUE oz, + D3DVALUE dx, D3DVALUE dy, D3DVALUE dz, + D3DVALUE ux, D3DVALUE uy, D3DVALUE uz, + D3DVALUE ou, D3DVALUE ov, + D3DVALUE su, D3DVALUE sv + ) PURE; + STDMETHOD(Apply)(THIS_ LPDIRECT3DRMOBJECT) PURE; + STDMETHOD(ApplyRelative)(THIS_ LPDIRECT3DRMFRAME frame, LPDIRECT3DRMOBJECT) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMMaterial + +DECLARE_INTERFACE_(IDirect3DRMMaterial, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMMaterial methods + */ + STDMETHOD(SetPower)(THIS_ D3DVALUE power) PURE; + STDMETHOD(SetSpecular)(THIS_ D3DVALUE r, D3DVALUE g, D3DVALUE b) PURE; + STDMETHOD(SetEmissive)(THIS_ D3DVALUE r, D3DVALUE g, D3DVALUE b) PURE; + + STDMETHOD_(D3DVALUE, GetPower)(THIS) PURE; + STDMETHOD(GetSpecular)(THIS_ D3DVALUE* r, D3DVALUE* g, D3DVALUE* b) PURE; + STDMETHOD(GetEmissive)(THIS_ D3DVALUE* r, D3DVALUE* g, D3DVALUE* b) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMAnimation + +DECLARE_INTERFACE_(IDirect3DRMAnimation, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMAnimation methods + */ + STDMETHOD(SetOptions)(THIS_ D3DRMANIMATIONOPTIONS flags) PURE; + STDMETHOD(AddRotateKey)(THIS_ D3DVALUE time, D3DRMQUATERNION *q) PURE; + STDMETHOD(AddPositionKey)(THIS_ D3DVALUE time, D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(AddScaleKey)(THIS_ D3DVALUE time, D3DVALUE x, D3DVALUE y, D3DVALUE z) PURE; + STDMETHOD(DeleteKey)(THIS_ D3DVALUE time) PURE; + STDMETHOD(SetFrame)(THIS_ LPDIRECT3DRMFRAME frame) PURE; + STDMETHOD(SetTime)(THIS_ D3DVALUE time) PURE; + + STDMETHOD_(D3DRMANIMATIONOPTIONS, GetOptions)(THIS) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMAnimationSet + +DECLARE_INTERFACE_(IDirect3DRMAnimationSet, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMAnimationSet methods + */ + STDMETHOD(AddAnimation)(THIS_ LPDIRECT3DRMANIMATION aid) PURE; + STDMETHOD(Load)(THIS_ LPVOID filename, LPVOID name, D3DRMLOADOPTIONS loadflags, D3DRMLOADTEXTURECALLBACK, LPVOID lpArg, LPDIRECT3DRMFRAME parent)PURE; + STDMETHOD(DeleteAnimation)(THIS_ LPDIRECT3DRMANIMATION aid) PURE; + STDMETHOD(SetTime)(THIS_ D3DVALUE time) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMUserVisual + +DECLARE_INTERFACE_(IDirect3DRMUserVisual, IDirect3DRMVisual) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMUserVisual methods + */ + STDMETHOD(Init)(THIS_ D3DRMUSERVISUALCALLBACK fn, void *arg) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMArray + +DECLARE_INTERFACE_(IDirect3DRMArray, IUnknown) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD_(DWORD, GetSize)(THIS) PURE; + /* No GetElement method as it would get overloaded + * in derived classes, and overloading is + * a no-no in COM + */ +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMObjectArray + +DECLARE_INTERFACE_(IDirect3DRMObjectArray, IDirect3DRMArray) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD_(DWORD, GetSize)(THIS) PURE; + STDMETHOD(GetElement)(THIS_ DWORD index, LPDIRECT3DRMOBJECT *) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMDeviceArray + +DECLARE_INTERFACE_(IDirect3DRMDeviceArray, IDirect3DRMArray) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD_(DWORD, GetSize)(THIS) PURE; + STDMETHOD(GetElement)(THIS_ DWORD index, LPDIRECT3DRMDEVICE *) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMFrameArray + +DECLARE_INTERFACE_(IDirect3DRMFrameArray, IDirect3DRMArray) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD_(DWORD, GetSize)(THIS) PURE; + STDMETHOD(GetElement)(THIS_ DWORD index, LPDIRECT3DRMFRAME *) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMViewportArray + +DECLARE_INTERFACE_(IDirect3DRMViewportArray, IDirect3DRMArray) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD_(DWORD, GetSize)(THIS) PURE; + STDMETHOD(GetElement)(THIS_ DWORD index, LPDIRECT3DRMVIEWPORT *) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMVisualArray + +DECLARE_INTERFACE_(IDirect3DRMVisualArray, IDirect3DRMArray) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD_(DWORD, GetSize)(THIS) PURE; + STDMETHOD(GetElement)(THIS_ DWORD index, LPDIRECT3DRMVISUAL *) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMPickedArray + +DECLARE_INTERFACE_(IDirect3DRMPickedArray, IDirect3DRMArray) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD_(DWORD, GetSize)(THIS) PURE; + STDMETHOD(GetPick)(THIS_ DWORD index, LPDIRECT3DRMVISUAL *, LPDIRECT3DRMFRAMEARRAY *, LPD3DRMPICKDESC) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMLightArray + +DECLARE_INTERFACE_(IDirect3DRMLightArray, IDirect3DRMArray) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD_(DWORD, GetSize)(THIS) PURE; + STDMETHOD(GetElement)(THIS_ DWORD index, LPDIRECT3DRMLIGHT *) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMFaceArray + +DECLARE_INTERFACE_(IDirect3DRMFaceArray, IDirect3DRMArray) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD_(DWORD, GetSize)(THIS) PURE; + STDMETHOD(GetElement)(THIS_ DWORD index, LPDIRECT3DRMFACE *) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMPicked2Array + +DECLARE_INTERFACE_(IDirect3DRMPicked2Array, IDirect3DRMArray) +{ + IUNKNOWN_METHODS(PURE); + + STDMETHOD_(DWORD, GetSize)(THIS) PURE; + STDMETHOD(GetPick)(THIS_ DWORD index, LPDIRECT3DRMVISUAL *, LPDIRECT3DRMFRAMEARRAY *, LPD3DRMPICKDESC2) PURE; +}; + +#undef INTERFACE +#define INTERFACE IDirect3DRMInterpolator + +DECLARE_INTERFACE_(IDirect3DRMInterpolator, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMInterpolator methods + */ + STDMETHOD(AttachObject)(THIS_ LPDIRECT3DRMOBJECT) PURE; + STDMETHOD(GetAttachedObjects)(THIS_ LPDIRECT3DRMOBJECTARRAY *) PURE; + STDMETHOD(DetachObject)(THIS_ LPDIRECT3DRMOBJECT) PURE; + STDMETHOD(SetIndex)(THIS_ D3DVALUE) PURE; + STDMETHOD_(D3DVALUE, GetIndex)(THIS) PURE; + STDMETHOD(Interpolate)(THIS_ D3DVALUE, LPDIRECT3DRMOBJECT, D3DRMINTERPOLATIONOPTIONS) PURE; +}; + +#ifdef __cplusplus +}; +#endif +#endif /* _D3DRMOBJ_H_ */ + diff --git a/include/vd3drmwin.h b/include/vd3drmwin.h new file mode 100644 index 0000000..3391ef1 --- /dev/null +++ b/include/vd3drmwin.h @@ -0,0 +1,49 @@ +/*==========================================================================; + * + * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. + * + * File: d3drm.h + * Content: Direct3DRM include file + * + ***************************************************************************/ + +#ifndef __D3DRMWIN_H__ +#define __D3DRMWIN_H__ + +#ifndef WIN32 +#define WIN32 +#endif + +#include "vd3drm.h" +#include "vddraw.h" +#include "vd3d.h" + +/* + * GUIDS used by Direct3DRM Windows interface + */ +DEFINE_GUID(IID_IDirect3DRMWinDevice, 0xc5016cc0, 0xd273, 0x11ce, 0xac, 0x48, 0x0, 0x0, 0xc0, 0x38, 0x25, 0xa1); + +WIN_TYPES(IDirect3DRMWinDevice, DIRECT3DRMWINDEVICE); + +#undef INTERFACE +#define INTERFACE IDirect3DRMWinDevice + +DECLARE_INTERFACE_(IDirect3DRMWinDevice, IDirect3DRMObject) +{ + IUNKNOWN_METHODS(PURE); + IDIRECT3DRMOBJECT_METHODS(PURE); + + /* + * IDirect3DRMWinDevice methods + */ + + /* Repaint the window with the last frame which was rendered. */ + STDMETHOD(HandlePaint)(THIS_ HDC hdc) PURE; + + /* Respond to a WM_ACTIVATE message. */ + STDMETHOD(HandleActivate)(THIS_ WORD wparam) PURE; +}; + + +#endif + diff --git a/include/vd3dtypes.h b/include/vd3dtypes.h new file mode 100644 index 0000000..0e1302b --- /dev/null +++ b/include/vd3dtypes.h @@ -0,0 +1,1202 @@ +/*==========================================================================; + * + * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. + * + * File: d3dtypes.h + * Content: Direct3D types include file + * + ***************************************************************************/ + +#ifndef _D3DTYPES_H_ +#define _D3DTYPES_H_ + +#if (! defined WIN32) && (! defined WIN95) +#include "subwtype.h" +#else +#include +#endif + +#include +#include "vddraw.h" + +#pragma pack(4) + +/* D3DVALUE is the fundamental Direct3D fractional data type */ + +#define D3DVALP(val, prec) ((float)(val)) +#define D3DVAL(val) ((float)(val)) +typedef float D3DVALUE, *LPD3DVALUE; +#define D3DDivide(a, b) (float)((double) (a) / (double) (b)) +#define D3DMultiply(a, b) ((a) * (b)) + +typedef LONG D3DFIXED; + +#ifndef RGB_MAKE +/* + * Format of CI colors is + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | alpha | color index | fraction | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +#define CI_GETALPHA(ci) ((ci) >> 24) +#define CI_GETINDEX(ci) (((ci) >> 8) & 0xffff) +#define CI_GETFRACTION(ci) ((ci) & 0xff) +#define CI_ROUNDINDEX(ci) CI_GETINDEX((ci) + 0x80) +#define CI_MASKALPHA(ci) ((ci) & 0xffffff) +#define CI_MAKE(a, i, f) (((a) << 24) | ((i) << 8) | (f)) + +/* + * Format of RGBA colors is + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | alpha | red | green | blue | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +#define RGBA_GETALPHA(rgb) ((rgb) >> 24) +#define RGBA_GETRED(rgb) (((rgb) >> 16) & 0xff) +#define RGBA_GETGREEN(rgb) (((rgb) >> 8) & 0xff) +#define RGBA_GETBLUE(rgb) ((rgb) & 0xff) +#define RGBA_MAKE(r, g, b, a) ((D3DCOLOR) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))) + +/* D3DRGB and D3DRGBA may be used as initialisers for D3DCOLORs + * The float values must be in the range 0..1 + */ +#define D3DRGB(r, g, b) \ + (0xff000000L | ( ((long)((r) * 255)) << 16) | (((long)((g) * 255)) << 8) | (long)((b) * 255)) +#define D3DRGBA(r, g, b, a) \ + ( (((long)((a) * 255)) << 24) | (((long)((r) * 255)) << 16) \ + | (((long)((g) * 255)) << 8) | (long)((b) * 255) \ + ) + +/* + * Format of RGB colors is + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ignored | red | green | blue | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +#define RGB_GETRED(rgb) (((rgb) >> 16) & 0xff) +#define RGB_GETGREEN(rgb) (((rgb) >> 8) & 0xff) +#define RGB_GETBLUE(rgb) ((rgb) & 0xff) +#define RGBA_SETALPHA(rgba, x) (((x) << 24) | ((rgba) & 0x00ffffff)) +#define RGB_MAKE(r, g, b) ((D3DCOLOR) (((r) << 16) | ((g) << 8) | (b))) +#define RGBA_TORGB(rgba) ((D3DCOLOR) ((rgba) & 0xffffff)) +#define RGB_TORGBA(rgb) ((D3DCOLOR) ((rgb) | 0xff000000)) + +#endif + +/* + * Flags for Enumerate functions + */ + +/* + * Stop the enumeration + */ +#define D3DENUMRET_CANCEL DDENUMRET_CANCEL + +/* + * Continue the enumeration + */ +#define D3DENUMRET_OK DDENUMRET_OK + +typedef HRESULT (WINAPI* LPD3DVALIDATECALLBACK)(LPVOID lpUserArg, DWORD dwOffset); +typedef HRESULT (WINAPI* LPD3DENUMTEXTUREFORMATSCALLBACK)(LPDDSURFACEDESC lpDdsd, LPVOID lpContext); + +typedef DWORD D3DCOLOR, *LPD3DCOLOR; + +typedef DWORD D3DMATERIALHANDLE, *LPD3DMATERIALHANDLE; +typedef DWORD D3DTEXTUREHANDLE, *LPD3DTEXTUREHANDLE; +typedef DWORD D3DMATRIXHANDLE, *LPD3DMATRIXHANDLE; + +typedef struct _D3DCOLORVALUE { + union { + D3DVALUE r; + D3DVALUE dvR; + }; + union { + D3DVALUE g; + D3DVALUE dvG; + }; + union { + D3DVALUE b; + D3DVALUE dvB; + }; + union { + D3DVALUE a; + D3DVALUE dvA; + }; +} D3DCOLORVALUE, *LPD3DCOLORVALUE; + +typedef struct _D3DRECT { + union { + LONG x1; + LONG lX1; + }; + union { + LONG y1; + LONG lY1; + }; + union { + LONG x2; + LONG lX2; + }; + union { + LONG y2; + LONG lY2; + }; +} D3DRECT, *LPD3DRECT; + +typedef struct _D3DVECTOR { + union { + D3DVALUE x; + D3DVALUE dvX; + }; + union { + D3DVALUE y; + D3DVALUE dvY; + }; + union { + D3DVALUE z; + D3DVALUE dvZ; + }; +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + +public: + + // ===================================== + // Constructors + // ===================================== + + _D3DVECTOR() { } + _D3DVECTOR(D3DVALUE f); + _D3DVECTOR(D3DVALUE _x, D3DVALUE _y, D3DVALUE _z); + _D3DVECTOR(const D3DVALUE f[3]); + + // ===================================== + // Access grants + // ===================================== + + const D3DVALUE&operator[](int i) const; + D3DVALUE&operator[](int i); + + // ===================================== + // Assignment operators + // ===================================== + + _D3DVECTOR& operator += (const _D3DVECTOR& v); + _D3DVECTOR& operator -= (const _D3DVECTOR& v); + _D3DVECTOR& operator *= (const _D3DVECTOR& v); + _D3DVECTOR& operator /= (const _D3DVECTOR& v); + _D3DVECTOR& operator *= (D3DVALUE s); + _D3DVECTOR& operator /= (D3DVALUE s); + + // ===================================== + // Unary operators + // ===================================== + + friend _D3DVECTOR operator + (const _D3DVECTOR& v); + friend _D3DVECTOR operator - (const _D3DVECTOR& v); + + + // ===================================== + // Binary operators + // ===================================== + + // Addition and subtraction + friend _D3DVECTOR operator + (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + friend _D3DVECTOR operator - (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + // Scalar multiplication and division + friend _D3DVECTOR operator * (const _D3DVECTOR& v, D3DVALUE s); + friend _D3DVECTOR operator * (D3DVALUE s, const _D3DVECTOR& v); + friend _D3DVECTOR operator / (const _D3DVECTOR& v, D3DVALUE s); + // Memberwise multiplication and division + friend _D3DVECTOR operator * (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + friend _D3DVECTOR operator / (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + + // Vector dominance + friend int operator < (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + friend int operator <= (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + + // Bitwise equality + friend int operator == (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + + // Length-related functions + friend D3DVALUE SquareMagnitude (const _D3DVECTOR& v); + friend D3DVALUE Magnitude (const _D3DVECTOR& v); + + // Returns vector with same direction and unit length + friend _D3DVECTOR Normalize (const _D3DVECTOR& v); + + // Return min/max component of the input vector + friend D3DVALUE Min (const _D3DVECTOR& v); + friend D3DVALUE Max (const _D3DVECTOR& v); + + // Return memberwise min/max of input vectors + friend _D3DVECTOR Minimize (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + friend _D3DVECTOR Maximize (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + + // Dot and cross product + friend D3DVALUE DotProduct (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + friend _D3DVECTOR CrossProduct (const _D3DVECTOR& v1, const _D3DVECTOR& v2); + +#endif + +} D3DVECTOR, *LPD3DVECTOR; + +#if (defined __cplusplus) && (defined D3D_OVERLOADS) +#include "vd3dvec.inl" +#endif + +/* + * Vertex data types supported in an ExecuteBuffer. + */ + +/* + * Homogeneous vertices + */ + +typedef struct _D3DHVERTEX { + DWORD dwFlags; /* Homogeneous clipping flags */ + union { + D3DVALUE hx; + D3DVALUE dvHX; + }; + union { + D3DVALUE hy; + D3DVALUE dvHY; + }; + union { + D3DVALUE hz; + D3DVALUE dvHZ; + }; +} D3DHVERTEX, *LPD3DHVERTEX; + +/* + * Transformed/lit vertices + */ +typedef struct _D3DTLVERTEX { + union { + D3DVALUE sx; /* Screen coordinates */ + D3DVALUE dvSX; + }; + union { + D3DVALUE sy; + D3DVALUE dvSY; + }; + union { + D3DVALUE sz; + D3DVALUE dvSZ; + }; + union { + D3DVALUE rhw; /* Reciprocal of homogeneous w */ + D3DVALUE dvRHW; + }; + union { + D3DCOLOR color; /* Vertex color */ + D3DCOLOR dcColor; + }; + union { + D3DCOLOR specular; /* Specular component of vertex */ + D3DCOLOR dcSpecular; + }; + union { + D3DVALUE tu; /* Texture coordinates */ + D3DVALUE dvTU; + }; + union { + D3DVALUE tv; + D3DVALUE dvTV; + }; +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + _D3DTLVERTEX() { } + _D3DTLVERTEX(const D3DVECTOR& v, float _rhw, + D3DCOLOR _color, D3DCOLOR _specular, + float _tu, float _tv) + { sx = v.x; sy = v.y; sz = v.z; rhw = _rhw; + color = _color; specular = _specular; + tu = _tu; tv = _tv; + } +#endif +} D3DTLVERTEX, *LPD3DTLVERTEX; + +/* + * Untransformed/lit vertices + */ +typedef struct _D3DLVERTEX { + union { + D3DVALUE x; /* Homogeneous coordinates */ + D3DVALUE dvX; + }; + union { + D3DVALUE y; + D3DVALUE dvY; + }; + union { + D3DVALUE z; + D3DVALUE dvZ; + }; + DWORD dwReserved; + union { + D3DCOLOR color; /* Vertex color */ + D3DCOLOR dcColor; + }; + union { + D3DCOLOR specular; /* Specular component of vertex */ + D3DCOLOR dcSpecular; + }; + union { + D3DVALUE tu; /* Texture coordinates */ + D3DVALUE dvTU; + }; + union { + D3DVALUE tv; + D3DVALUE dvTV; + }; +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + _D3DLVERTEX() { } + _D3DLVERTEX(const D3DVECTOR& v, + D3DCOLOR _color, D3DCOLOR _specular, + float _tu, float _tv) + { x = v.x; y = v.y; z = v.z; dwReserved = 0; + color = _color; specular = _specular; + tu = _tu; tv = _tv; + } +#endif +} D3DLVERTEX, *LPD3DLVERTEX; + +/* + * Untransformed/unlit vertices + */ + +typedef struct _D3DVERTEX { + union { + D3DVALUE x; /* Homogeneous coordinates */ + D3DVALUE dvX; + }; + union { + D3DVALUE y; + D3DVALUE dvY; + }; + union { + D3DVALUE z; + D3DVALUE dvZ; + }; + union { + D3DVALUE nx; /* Normal */ + D3DVALUE dvNX; + }; + union { + D3DVALUE ny; + D3DVALUE dvNY; + }; + union { + D3DVALUE nz; + D3DVALUE dvNZ; + }; + union { + D3DVALUE tu; /* Texture coordinates */ + D3DVALUE dvTU; + }; + union { + D3DVALUE tv; + D3DVALUE dvTV; + }; +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + _D3DVERTEX() { } + _D3DVERTEX(const D3DVECTOR& v, const D3DVECTOR& n, float _tu, float _tv) + { x = v.x; y = v.y; z = v.z; + nx = n.x; ny = n.y; nz = n.z; + tu = _tu; tv = _tv; + } +#endif +} D3DVERTEX, *LPD3DVERTEX; + +/* + * Matrix, viewport, and tranformation structures and definitions. + */ + +typedef struct _D3DMATRIX { +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + union { + struct { +#endif + + D3DVALUE _11, _12, _13, _14; + D3DVALUE _21, _22, _23, _24; + D3DVALUE _31, _32, _33, _34; + D3DVALUE _41, _42, _43, _44; + +#if (defined __cplusplus) && (defined D3D_OVERLOADS) + }; + D3DVALUE m[4][4]; + }; + _D3DMATRIX() { } + _D3DMATRIX( D3DVALUE _m00, D3DVALUE _m01, D3DVALUE _m02, D3DVALUE _m03, + D3DVALUE _m10, D3DVALUE _m11, D3DVALUE _m12, D3DVALUE _m13, + D3DVALUE _m20, D3DVALUE _m21, D3DVALUE _m22, D3DVALUE _m23, + D3DVALUE _m30, D3DVALUE _m31, D3DVALUE _m32, D3DVALUE _m33 + ) + { + m[0][0] = _m00; m[0][1] = _m01; m[0][2] = _m02; m[0][3] = _m03; + m[1][0] = _m10; m[1][1] = _m11; m[1][2] = _m12; m[1][3] = _m13; + m[2][0] = _m20; m[2][1] = _m21; m[2][2] = _m22; m[2][3] = _m23; + m[3][0] = _m30; m[3][1] = _m31; m[3][2] = _m32; m[3][3] = _m33; + } + + D3DVALUE& operator()(int iRow, int iColumn) { return m[iRow][iColumn]; } + const D3DVALUE& operator()(int iRow, int iColumn) const { return m[iRow][iColumn]; } +#endif +} D3DMATRIX, *LPD3DMATRIX; + +typedef struct _D3DVIEWPORT { + DWORD dwSize; + DWORD dwX; + DWORD dwY; /* Top left */ + DWORD dwWidth; + DWORD dwHeight; /* Dimensions */ + D3DVALUE dvScaleX; /* Scale homogeneous to screen */ + D3DVALUE dvScaleY; /* Scale homogeneous to screen */ + D3DVALUE dvMaxX; /* Min/max homogeneous x coord */ + D3DVALUE dvMaxY; /* Min/max homogeneous y coord */ + D3DVALUE dvMinZ; + D3DVALUE dvMaxZ; /* Min/max homogeneous z coord */ +} D3DVIEWPORT, *LPD3DVIEWPORT; + +typedef struct _D3DVIEWPORT2 { + DWORD dwSize; + DWORD dwX; + DWORD dwY; /* Viewport Top left */ + DWORD dwWidth; + DWORD dwHeight; /* Viewport Dimensions */ + D3DVALUE dvClipX; /* Top left of clip volume */ + D3DVALUE dvClipY; + D3DVALUE dvClipWidth; /* Clip Volume Dimensions */ + D3DVALUE dvClipHeight; + D3DVALUE dvMinZ; /* Min/max of clip Volume */ + D3DVALUE dvMaxZ; +} D3DVIEWPORT2, *LPD3DVIEWPORT2; + +/* + * Values for clip fields. + */ +#define D3DCLIP_LEFT 0x00000001L +#define D3DCLIP_RIGHT 0x00000002L +#define D3DCLIP_TOP 0x00000004L +#define D3DCLIP_BOTTOM 0x00000008L +#define D3DCLIP_FRONT 0x00000010L +#define D3DCLIP_BACK 0x00000020L +#define D3DCLIP_GEN0 0x00000040L +#define D3DCLIP_GEN1 0x00000080L +#define D3DCLIP_GEN2 0x00000100L +#define D3DCLIP_GEN3 0x00000200L +#define D3DCLIP_GEN4 0x00000400L +#define D3DCLIP_GEN5 0x00000800L + +/* + * Values for d3d status. + */ +#define D3DSTATUS_CLIPUNIONLEFT D3DCLIP_LEFT +#define D3DSTATUS_CLIPUNIONRIGHT D3DCLIP_RIGHT +#define D3DSTATUS_CLIPUNIONTOP D3DCLIP_TOP +#define D3DSTATUS_CLIPUNIONBOTTOM D3DCLIP_BOTTOM +#define D3DSTATUS_CLIPUNIONFRONT D3DCLIP_FRONT +#define D3DSTATUS_CLIPUNIONBACK D3DCLIP_BACK +#define D3DSTATUS_CLIPUNIONGEN0 D3DCLIP_GEN0 +#define D3DSTATUS_CLIPUNIONGEN1 D3DCLIP_GEN1 +#define D3DSTATUS_CLIPUNIONGEN2 D3DCLIP_GEN2 +#define D3DSTATUS_CLIPUNIONGEN3 D3DCLIP_GEN3 +#define D3DSTATUS_CLIPUNIONGEN4 D3DCLIP_GEN4 +#define D3DSTATUS_CLIPUNIONGEN5 D3DCLIP_GEN5 + +#define D3DSTATUS_CLIPINTERSECTIONLEFT 0x00001000L +#define D3DSTATUS_CLIPINTERSECTIONRIGHT 0x00002000L +#define D3DSTATUS_CLIPINTERSECTIONTOP 0x00004000L +#define D3DSTATUS_CLIPINTERSECTIONBOTTOM 0x00008000L +#define D3DSTATUS_CLIPINTERSECTIONFRONT 0x00010000L +#define D3DSTATUS_CLIPINTERSECTIONBACK 0x00020000L +#define D3DSTATUS_CLIPINTERSECTIONGEN0 0x00040000L +#define D3DSTATUS_CLIPINTERSECTIONGEN1 0x00080000L +#define D3DSTATUS_CLIPINTERSECTIONGEN2 0x00100000L +#define D3DSTATUS_CLIPINTERSECTIONGEN3 0x00200000L +#define D3DSTATUS_CLIPINTERSECTIONGEN4 0x00400000L +#define D3DSTATUS_CLIPINTERSECTIONGEN5 0x00800000L +#define D3DSTATUS_ZNOTVISIBLE 0x01000000L +/* Do not use 0x80000000 for any status flags in future as it is reserved */ + +#define D3DSTATUS_CLIPUNIONALL ( \ + D3DSTATUS_CLIPUNIONLEFT | \ + D3DSTATUS_CLIPUNIONRIGHT | \ + D3DSTATUS_CLIPUNIONTOP | \ + D3DSTATUS_CLIPUNIONBOTTOM | \ + D3DSTATUS_CLIPUNIONFRONT | \ + D3DSTATUS_CLIPUNIONBACK | \ + D3DSTATUS_CLIPUNIONGEN0 | \ + D3DSTATUS_CLIPUNIONGEN1 | \ + D3DSTATUS_CLIPUNIONGEN2 | \ + D3DSTATUS_CLIPUNIONGEN3 | \ + D3DSTATUS_CLIPUNIONGEN4 | \ + D3DSTATUS_CLIPUNIONGEN5 \ + ) + +#define D3DSTATUS_CLIPINTERSECTIONALL ( \ + D3DSTATUS_CLIPINTERSECTIONLEFT | \ + D3DSTATUS_CLIPINTERSECTIONRIGHT | \ + D3DSTATUS_CLIPINTERSECTIONTOP | \ + D3DSTATUS_CLIPINTERSECTIONBOTTOM | \ + D3DSTATUS_CLIPINTERSECTIONFRONT | \ + D3DSTATUS_CLIPINTERSECTIONBACK | \ + D3DSTATUS_CLIPINTERSECTIONGEN0 | \ + D3DSTATUS_CLIPINTERSECTIONGEN1 | \ + D3DSTATUS_CLIPINTERSECTIONGEN2 | \ + D3DSTATUS_CLIPINTERSECTIONGEN3 | \ + D3DSTATUS_CLIPINTERSECTIONGEN4 | \ + D3DSTATUS_CLIPINTERSECTIONGEN5 \ + ) + +#define D3DSTATUS_DEFAULT ( \ + D3DSTATUS_CLIPINTERSECTIONALL | \ + D3DSTATUS_ZNOTVISIBLE) + + +/* + * Options for direct transform calls + */ +#define D3DTRANSFORM_CLIPPED 0x00000001l +#define D3DTRANSFORM_UNCLIPPED 0x00000002l + +typedef struct _D3DTRANSFORMDATA { + DWORD dwSize; + LPVOID lpIn; /* Input vertices */ + DWORD dwInSize; /* Stride of input vertices */ + LPVOID lpOut; /* Output vertices */ + DWORD dwOutSize; /* Stride of output vertices */ + LPD3DHVERTEX lpHOut; /* Output homogeneous vertices */ + DWORD dwClip; /* Clipping hint */ + DWORD dwClipIntersection; + DWORD dwClipUnion; /* Union of all clip flags */ + D3DRECT drExtent; /* Extent of transformed vertices */ +} D3DTRANSFORMDATA, *LPD3DTRANSFORMDATA; + +/* + * Structure defining position and direction properties for lighting. + */ +typedef struct _D3DLIGHTINGELEMENT { + D3DVECTOR dvPosition; /* Lightable point in model space */ + D3DVECTOR dvNormal; /* Normalised unit vector */ +} D3DLIGHTINGELEMENT, *LPD3DLIGHTINGELEMENT; + +/* + * Structure defining material properties for lighting. + */ +typedef struct _D3DMATERIAL { + DWORD dwSize; + union { + D3DCOLORVALUE diffuse; /* Diffuse color RGBA */ + D3DCOLORVALUE dcvDiffuse; + }; + union { + D3DCOLORVALUE ambient; /* Ambient color RGB */ + D3DCOLORVALUE dcvAmbient; + }; + union { + D3DCOLORVALUE specular; /* Specular 'shininess' */ + D3DCOLORVALUE dcvSpecular; + }; + union { + D3DCOLORVALUE emissive; /* Emissive color RGB */ + D3DCOLORVALUE dcvEmissive; + }; + union { + D3DVALUE power; /* Sharpness if specular highlight */ + D3DVALUE dvPower; + }; + D3DTEXTUREHANDLE hTexture; /* Handle to texture map */ + DWORD dwRampSize; +} D3DMATERIAL, *LPD3DMATERIAL; + +typedef enum _D3DLIGHTTYPE { + D3DLIGHT_POINT = 1, + D3DLIGHT_SPOT = 2, + D3DLIGHT_DIRECTIONAL = 3, + D3DLIGHT_PARALLELPOINT = 4, + D3DLIGHT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DLIGHTTYPE; + +/* + * Structure defining a light source and its properties. + */ +typedef struct _D3DLIGHT { + DWORD dwSize; + D3DLIGHTTYPE dltType; /* Type of light source */ + D3DCOLORVALUE dcvColor; /* Color of light */ + D3DVECTOR dvPosition; /* Position in world space */ + D3DVECTOR dvDirection; /* Direction in world space */ + D3DVALUE dvRange; /* Cutoff range */ + D3DVALUE dvFalloff; /* Falloff */ + D3DVALUE dvAttenuation0; /* Constant attenuation */ + D3DVALUE dvAttenuation1; /* Linear attenuation */ + D3DVALUE dvAttenuation2; /* Quadratic attenuation */ + D3DVALUE dvTheta; /* Inner angle of spotlight cone */ + D3DVALUE dvPhi; /* Outer angle of spotlight cone */ +} D3DLIGHT, *LPD3DLIGHT; + +/* + * Structure defining a light source and its properties. + */ + +/* flags bits */ +#define D3DLIGHT_ACTIVE 0x00000001 +#define D3DLIGHT_NO_SPECULAR 0x00000002 + +/* maximum valid light range */ +#define D3DLIGHT_RANGE_MAX ((float)sqrt(FLT_MAX)) + +typedef struct _D3DLIGHT2 { + DWORD dwSize; + D3DLIGHTTYPE dltType; /* Type of light source */ + D3DCOLORVALUE dcvColor; /* Color of light */ + D3DVECTOR dvPosition; /* Position in world space */ + D3DVECTOR dvDirection; /* Direction in world space */ + D3DVALUE dvRange; /* Cutoff range */ + D3DVALUE dvFalloff; /* Falloff */ + D3DVALUE dvAttenuation0; /* Constant attenuation */ + D3DVALUE dvAttenuation1; /* Linear attenuation */ + D3DVALUE dvAttenuation2; /* Quadratic attenuation */ + D3DVALUE dvTheta; /* Inner angle of spotlight cone */ + D3DVALUE dvPhi; /* Outer angle of spotlight cone */ + DWORD dwFlags; +} D3DLIGHT2, *LPD3DLIGHT2; + +typedef struct _D3DLIGHTDATA { + DWORD dwSize; + LPD3DLIGHTINGELEMENT lpIn; /* Input positions and normals */ + DWORD dwInSize; /* Stride of input elements */ + LPD3DTLVERTEX lpOut; /* Output colors */ + DWORD dwOutSize; /* Stride of output colors */ +} D3DLIGHTDATA, *LPD3DLIGHTDATA; + +/* + * Before DX5, these values were in an enum called + * D3DCOLORMODEL. This was not correct, since they are + * bit flags. A driver can surface either or both flags + * in the dcmColorModel member of D3DDEVICEDESC. + */ +#define D3DCOLOR_MONO 1 +#define D3DCOLOR_RGB 2 + +typedef DWORD D3DCOLORMODEL; + +/* + * Options for clearing + */ +#define D3DCLEAR_TARGET 0x00000001l /* Clear target surface */ +#define D3DCLEAR_ZBUFFER 0x00000002l /* Clear target z buffer */ + +/* + * Execute buffers are allocated via Direct3D. These buffers may then + * be filled by the application with instructions to execute along with + * vertex data. + */ + +/* + * Supported op codes for execute instructions. + */ +typedef enum _D3DOPCODE { + D3DOP_POINT = 1, + D3DOP_LINE = 2, + D3DOP_TRIANGLE = 3, + D3DOP_MATRIXLOAD = 4, + D3DOP_MATRIXMULTIPLY = 5, + D3DOP_STATETRANSFORM = 6, + D3DOP_STATELIGHT = 7, + D3DOP_STATERENDER = 8, + D3DOP_PROCESSVERTICES = 9, + D3DOP_TEXTURELOAD = 10, + D3DOP_EXIT = 11, + D3DOP_BRANCHFORWARD = 12, + D3DOP_SPAN = 13, + D3DOP_SETSTATUS = 14, + D3DOP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DOPCODE; + +typedef struct _D3DINSTRUCTION { + BYTE bOpcode; /* Instruction opcode */ + BYTE bSize; /* Size of each instruction data unit */ + WORD wCount; /* Count of instruction data units to follow */ +} D3DINSTRUCTION, *LPD3DINSTRUCTION; + +/* + * Structure for texture loads + */ +typedef struct _D3DTEXTURELOAD { + D3DTEXTUREHANDLE hDestTexture; + D3DTEXTUREHANDLE hSrcTexture; +} D3DTEXTURELOAD, *LPD3DTEXTURELOAD; + +/* + * Structure for picking + */ +typedef struct _D3DPICKRECORD { + BYTE bOpcode; + BYTE bPad; + DWORD dwOffset; + D3DVALUE dvZ; +} D3DPICKRECORD, *LPD3DPICKRECORD; + +/* + * The following defines the rendering states which can be set in the + * execute buffer. + */ + +typedef enum _D3DSHADEMODE { + D3DSHADE_FLAT = 1, + D3DSHADE_GOURAUD = 2, + D3DSHADE_PHONG = 3, + D3DSHADE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DSHADEMODE; + +typedef enum _D3DFILLMODE { + D3DFILL_POINT = 1, + D3DFILL_WIREFRAME = 2, + D3DFILL_SOLID = 3, + D3DFILL_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DFILLMODE; + +typedef struct _D3DLINEPATTERN { + WORD wRepeatFactor; + WORD wLinePattern; +} D3DLINEPATTERN; + +typedef enum _D3DTEXTUREFILTER { + D3DFILTER_NEAREST = 1, + D3DFILTER_LINEAR = 2, + D3DFILTER_MIPNEAREST = 3, + D3DFILTER_MIPLINEAR = 4, + D3DFILTER_LINEARMIPNEAREST = 5, + D3DFILTER_LINEARMIPLINEAR = 6, + D3DFILTER_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTEXTUREFILTER; + +typedef enum _D3DBLEND { + D3DBLEND_ZERO = 1, + D3DBLEND_ONE = 2, + D3DBLEND_SRCCOLOR = 3, + D3DBLEND_INVSRCCOLOR = 4, + D3DBLEND_SRCALPHA = 5, + D3DBLEND_INVSRCALPHA = 6, + D3DBLEND_DESTALPHA = 7, + D3DBLEND_INVDESTALPHA = 8, + D3DBLEND_DESTCOLOR = 9, + D3DBLEND_INVDESTCOLOR = 10, + D3DBLEND_SRCALPHASAT = 11, + D3DBLEND_BOTHSRCALPHA = 12, + D3DBLEND_BOTHINVSRCALPHA = 13, + D3DBLEND_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DBLEND; + +typedef enum _D3DTEXTUREBLEND { + D3DTBLEND_DECAL = 1, + D3DTBLEND_MODULATE = 2, + D3DTBLEND_DECALALPHA = 3, + D3DTBLEND_MODULATEALPHA = 4, + D3DTBLEND_DECALMASK = 5, + D3DTBLEND_MODULATEMASK = 6, + D3DTBLEND_COPY = 7, + D3DTBLEND_ADD = 8, + D3DTBLEND_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTEXTUREBLEND; + +typedef enum _D3DTEXTUREADDRESS { + D3DTADDRESS_WRAP = 1, + D3DTADDRESS_MIRROR = 2, + D3DTADDRESS_CLAMP = 3, + D3DTADDRESS_BORDER = 4, + D3DTADDRESS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTEXTUREADDRESS; + +typedef enum _D3DCULL { + D3DCULL_NONE = 1, + D3DCULL_CW = 2, + D3DCULL_CCW = 3, + D3DCULL_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DCULL; + +typedef enum _D3DCMPFUNC { + D3DCMP_NEVER = 1, + D3DCMP_LESS = 2, + D3DCMP_EQUAL = 3, + D3DCMP_LESSEQUAL = 4, + D3DCMP_GREATER = 5, + D3DCMP_NOTEQUAL = 6, + D3DCMP_GREATEREQUAL = 7, + D3DCMP_ALWAYS = 8, + D3DCMP_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DCMPFUNC; + +typedef enum _D3DFOGMODE { + D3DFOG_NONE = 0, + D3DFOG_EXP = 1, + D3DFOG_EXP2 = 2, + D3DFOG_LINEAR = 3, + D3DFOG_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DFOGMODE; + +typedef enum _D3DANTIALIASMODE { + D3DANTIALIAS_NONE = 0, + D3DANTIALIAS_SORTDEPENDENT = 1, + D3DANTIALIAS_SORTINDEPENDENT = 2, + D3DANTIALIAS_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DANTIALIASMODE; + +// Vertex types supported by Direct3D +typedef enum _D3DVERTEXTYPE { + D3DVT_VERTEX = 1, + D3DVT_LVERTEX = 2, + D3DVT_TLVERTEX = 3, + D3DVT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DVERTEXTYPE; + +// Primitives supported by draw-primitive API +typedef enum _D3DPRIMITIVETYPE { + D3DPT_POINTLIST = 1, + D3DPT_LINELIST = 2, + D3DPT_LINESTRIP = 3, + D3DPT_TRIANGLELIST = 4, + D3DPT_TRIANGLESTRIP = 5, + D3DPT_TRIANGLEFAN = 6, + D3DPT_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DPRIMITIVETYPE; + +/* + * Amount to add to a state to generate the override for that state. + */ +#define D3DSTATE_OVERRIDE_BIAS 256 + +/* + * A state which sets the override flag for the specified state type. + */ +#define D3DSTATE_OVERRIDE(type) ((DWORD) (type) + D3DSTATE_OVERRIDE_BIAS) + +typedef enum _D3DTRANSFORMSTATETYPE { + D3DTRANSFORMSTATE_WORLD = 1, + D3DTRANSFORMSTATE_VIEW = 2, + D3DTRANSFORMSTATE_PROJECTION = 3, + D3DTRANSFORMSTATE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DTRANSFORMSTATETYPE; + +typedef enum _D3DLIGHTSTATETYPE { + D3DLIGHTSTATE_MATERIAL = 1, + D3DLIGHTSTATE_AMBIENT = 2, + D3DLIGHTSTATE_COLORMODEL = 3, + D3DLIGHTSTATE_FOGMODE = 4, + D3DLIGHTSTATE_FOGSTART = 5, + D3DLIGHTSTATE_FOGEND = 6, + D3DLIGHTSTATE_FOGDENSITY = 7, + D3DLIGHTSTATE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DLIGHTSTATETYPE; + +typedef enum _D3DRENDERSTATETYPE { + D3DRENDERSTATE_TEXTUREHANDLE = 1, /* Texture handle */ + D3DRENDERSTATE_ANTIALIAS = 2, /* D3DANTIALIASMODE */ + D3DRENDERSTATE_TEXTUREADDRESS = 3, /* D3DTEXTUREADDRESS */ + D3DRENDERSTATE_TEXTUREPERSPECTIVE = 4, /* TRUE for perspective correction */ + D3DRENDERSTATE_WRAPU = 5, /* TRUE for wrapping in u */ + D3DRENDERSTATE_WRAPV = 6, /* TRUE for wrapping in v */ + D3DRENDERSTATE_ZENABLE = 7, /* TRUE to enable z test */ + D3DRENDERSTATE_FILLMODE = 8, /* D3DFILL_MODE */ + D3DRENDERSTATE_SHADEMODE = 9, /* D3DSHADEMODE */ + D3DRENDERSTATE_LINEPATTERN = 10, /* D3DLINEPATTERN */ + D3DRENDERSTATE_MONOENABLE = 11, /* TRUE to enable mono rasterization */ + D3DRENDERSTATE_ROP2 = 12, /* ROP2 */ + D3DRENDERSTATE_PLANEMASK = 13, /* DWORD physical plane mask */ + D3DRENDERSTATE_ZWRITEENABLE = 14, /* TRUE to enable z writes */ + D3DRENDERSTATE_ALPHATESTENABLE = 15, /* TRUE to enable alpha tests */ + D3DRENDERSTATE_LASTPIXEL = 16, /* TRUE for last-pixel on lines */ + D3DRENDERSTATE_TEXTUREMAG = 17, /* D3DTEXTUREFILTER */ + D3DRENDERSTATE_TEXTUREMIN = 18, /* D3DTEXTUREFILTER */ + D3DRENDERSTATE_SRCBLEND = 19, /* D3DBLEND */ + D3DRENDERSTATE_DESTBLEND = 20, /* D3DBLEND */ + D3DRENDERSTATE_TEXTUREMAPBLEND = 21, /* D3DTEXTUREBLEND */ + D3DRENDERSTATE_CULLMODE = 22, /* D3DCULL */ + D3DRENDERSTATE_ZFUNC = 23, /* D3DCMPFUNC */ + D3DRENDERSTATE_ALPHAREF = 24, /* D3DFIXED */ + D3DRENDERSTATE_ALPHAFUNC = 25, /* D3DCMPFUNC */ + D3DRENDERSTATE_DITHERENABLE = 26, /* TRUE to enable dithering */ + D3DRENDERSTATE_ALPHABLENDENABLE = 27, /* TRUE to enable alpha blending */ + D3DRENDERSTATE_FOGENABLE = 28, /* TRUE to enable fog */ + D3DRENDERSTATE_SPECULARENABLE = 29, /* TRUE to enable specular */ + D3DRENDERSTATE_ZVISIBLE = 30, /* TRUE to enable z checking */ + D3DRENDERSTATE_SUBPIXEL = 31, /* TRUE to enable subpixel correction */ + D3DRENDERSTATE_SUBPIXELX = 32, /* TRUE to enable correction in X only */ + D3DRENDERSTATE_STIPPLEDALPHA = 33, /* TRUE to enable stippled alpha */ + D3DRENDERSTATE_FOGCOLOR = 34, /* D3DCOLOR */ + D3DRENDERSTATE_FOGTABLEMODE = 35, /* D3DFOGMODE */ + D3DRENDERSTATE_FOGTABLESTART = 36, /* Fog table start */ + D3DRENDERSTATE_FOGTABLEEND = 37, /* Fog table end */ + D3DRENDERSTATE_FOGTABLEDENSITY = 38, /* Fog table density */ + D3DRENDERSTATE_STIPPLEENABLE = 39, /* TRUE to enable stippling */ + D3DRENDERSTATE_EDGEANTIALIAS = 40, /* TRUE to enable edge antialiasing */ + D3DRENDERSTATE_COLORKEYENABLE = 41, /* TRUE to enable source colorkeyed textures */ + D3DRENDERSTATE_BORDERCOLOR = 43, /* Border color for texturing w/border */ + D3DRENDERSTATE_TEXTUREADDRESSU = 44, /* Texture addressing mode for U coordinate */ + D3DRENDERSTATE_TEXTUREADDRESSV = 45, /* Texture addressing mode for V coordinate */ + D3DRENDERSTATE_MIPMAPLODBIAS = 46, /* D3DVALUE Mipmap LOD bias */ + D3DRENDERSTATE_ZBIAS = 47, /* LONG Z bias */ + D3DRENDERSTATE_RANGEFOGENABLE = 48, /* Enables range-based fog */ + D3DRENDERSTATE_ANISOTROPY = 49, /* Max. anisotropy. 1 = no anisotropy */ + D3DRENDERSTATE_FLUSHBATCH = 50, /* Explicit flush for DP batching (DX5 Only) */ + D3DRENDERSTATE_STIPPLEPATTERN00 = 64, /* Stipple pattern 01... */ + D3DRENDERSTATE_STIPPLEPATTERN01 = 65, + D3DRENDERSTATE_STIPPLEPATTERN02 = 66, + D3DRENDERSTATE_STIPPLEPATTERN03 = 67, + D3DRENDERSTATE_STIPPLEPATTERN04 = 68, + D3DRENDERSTATE_STIPPLEPATTERN05 = 69, + D3DRENDERSTATE_STIPPLEPATTERN06 = 70, + D3DRENDERSTATE_STIPPLEPATTERN07 = 71, + D3DRENDERSTATE_STIPPLEPATTERN08 = 72, + D3DRENDERSTATE_STIPPLEPATTERN09 = 73, + D3DRENDERSTATE_STIPPLEPATTERN10 = 74, + D3DRENDERSTATE_STIPPLEPATTERN11 = 75, + D3DRENDERSTATE_STIPPLEPATTERN12 = 76, + D3DRENDERSTATE_STIPPLEPATTERN13 = 77, + D3DRENDERSTATE_STIPPLEPATTERN14 = 78, + D3DRENDERSTATE_STIPPLEPATTERN15 = 79, + D3DRENDERSTATE_STIPPLEPATTERN16 = 80, + D3DRENDERSTATE_STIPPLEPATTERN17 = 81, + D3DRENDERSTATE_STIPPLEPATTERN18 = 82, + D3DRENDERSTATE_STIPPLEPATTERN19 = 83, + D3DRENDERSTATE_STIPPLEPATTERN20 = 84, + D3DRENDERSTATE_STIPPLEPATTERN21 = 85, + D3DRENDERSTATE_STIPPLEPATTERN22 = 86, + D3DRENDERSTATE_STIPPLEPATTERN23 = 87, + D3DRENDERSTATE_STIPPLEPATTERN24 = 88, + D3DRENDERSTATE_STIPPLEPATTERN25 = 89, + D3DRENDERSTATE_STIPPLEPATTERN26 = 90, + D3DRENDERSTATE_STIPPLEPATTERN27 = 91, + D3DRENDERSTATE_STIPPLEPATTERN28 = 92, + D3DRENDERSTATE_STIPPLEPATTERN29 = 93, + D3DRENDERSTATE_STIPPLEPATTERN30 = 94, + D3DRENDERSTATE_STIPPLEPATTERN31 = 95, + D3DRENDERSTATE_FORCE_DWORD = 0x7fffffff, /* force 32-bit size enum */ +} D3DRENDERSTATETYPE; + +// For back-compatibility with legacy compilations +#define D3DRENDERSTATE_BLENDENABLE D3DRENDERSTATE_ALPHABLENDENABLE + +#define D3DRENDERSTATE_STIPPLEPATTERN(y) (D3DRENDERSTATE_STIPPLEPATTERN00 + (y)) + +typedef struct _D3DSTATE { + union { + D3DTRANSFORMSTATETYPE dtstTransformStateType; + D3DLIGHTSTATETYPE dlstLightStateType; + D3DRENDERSTATETYPE drstRenderStateType; + }; + union { + DWORD dwArg[1]; + D3DVALUE dvArg[1]; + }; +} D3DSTATE, *LPD3DSTATE; + +/* + * Operation used to load matrices + * hDstMat = hSrcMat + */ +typedef struct _D3DMATRIXLOAD { + D3DMATRIXHANDLE hDestMatrix; /* Destination matrix */ + D3DMATRIXHANDLE hSrcMatrix; /* Source matrix */ +} D3DMATRIXLOAD, *LPD3DMATRIXLOAD; + +/* + * Operation used to multiply matrices + * hDstMat = hSrcMat1 * hSrcMat2 + */ +typedef struct _D3DMATRIXMULTIPLY { + D3DMATRIXHANDLE hDestMatrix; /* Destination matrix */ + D3DMATRIXHANDLE hSrcMatrix1; /* First source matrix */ + D3DMATRIXHANDLE hSrcMatrix2; /* Second source matrix */ +} D3DMATRIXMULTIPLY, *LPD3DMATRIXMULTIPLY; + +/* + * Operation used to transform and light vertices. + */ +typedef struct _D3DPROCESSVERTICES { + DWORD dwFlags; /* Do we transform or light or just copy? */ + WORD wStart; /* Index to first vertex in source */ + WORD wDest; /* Index to first vertex in local buffer */ + DWORD dwCount; /* Number of vertices to be processed */ + DWORD dwReserved; /* Must be zero */ +} D3DPROCESSVERTICES, *LPD3DPROCESSVERTICES; + +#define D3DPROCESSVERTICES_TRANSFORMLIGHT 0x00000000L +#define D3DPROCESSVERTICES_TRANSFORM 0x00000001L +#define D3DPROCESSVERTICES_COPY 0x00000002L +#define D3DPROCESSVERTICES_OPMASK 0x00000007L + +#define D3DPROCESSVERTICES_UPDATEEXTENTS 0x00000008L +#define D3DPROCESSVERTICES_NOCOLOR 0x00000010L + + +/* + * Triangle flags + */ + +/* + * Tri strip and fan flags. + * START loads all three vertices + * EVEN and ODD load just v3 with even or odd culling + * START_FLAT contains a count from 0 to 29 that allows the + * whole strip or fan to be culled in one hit. + * e.g. for a quad len = 1 + */ +#define D3DTRIFLAG_START 0x00000000L +#define D3DTRIFLAG_STARTFLAT(len) (len) /* 0 < len < 30 */ +#define D3DTRIFLAG_ODD 0x0000001eL +#define D3DTRIFLAG_EVEN 0x0000001fL + +/* + * Triangle edge flags + * enable edges for wireframe or antialiasing + */ +#define D3DTRIFLAG_EDGEENABLE1 0x00000100L /* v0-v1 edge */ +#define D3DTRIFLAG_EDGEENABLE2 0x00000200L /* v1-v2 edge */ +#define D3DTRIFLAG_EDGEENABLE3 0x00000400L /* v2-v0 edge */ +#define D3DTRIFLAG_EDGEENABLETRIANGLE \ + (D3DTRIFLAG_EDGEENABLE1 | D3DTRIFLAG_EDGEENABLE2 | D3DTRIFLAG_EDGEENABLE3) + +/* + * Primitive structures and related defines. Vertex offsets are to types + * D3DVERTEX, D3DLVERTEX, or D3DTLVERTEX. + */ + +/* + * Triangle list primitive structure + */ +typedef struct _D3DTRIANGLE { + union { + WORD v1; /* Vertex indices */ + WORD wV1; + }; + union { + WORD v2; + WORD wV2; + }; + union { + WORD v3; + WORD wV3; + }; + WORD wFlags; /* Edge (and other) flags */ +} D3DTRIANGLE, *LPD3DTRIANGLE; + +/* + * Line list structure. + * The instruction count defines the number of line segments. + */ +typedef struct _D3DLINE { + union { + WORD v1; /* Vertex indices */ + WORD wV1; + }; + union { + WORD v2; + WORD wV2; + }; +} D3DLINE, *LPD3DLINE; + +/* + * Span structure + * Spans join a list of points with the same y value. + * If the y value changes, a new span is started. + */ +typedef struct _D3DSPAN { + WORD wCount; /* Number of spans */ + WORD wFirst; /* Index to first vertex */ +} D3DSPAN, *LPD3DSPAN; + +/* + * Point structure + */ +typedef struct _D3DPOINT { + WORD wCount; /* number of points */ + WORD wFirst; /* index to first vertex */ +} D3DPOINT, *LPD3DPOINT; + + +/* + * Forward branch structure. + * Mask is logically anded with the driver status mask + * if the result equals 'value', the branch is taken. + */ +typedef struct _D3DBRANCH { + DWORD dwMask; /* Bitmask against D3D status */ + DWORD dwValue; + BOOL bNegate; /* TRUE to negate comparison */ + DWORD dwOffset; /* How far to branch forward (0 for exit)*/ +} D3DBRANCH, *LPD3DBRANCH; + +/* + * Status used for set status instruction. + * The D3D status is initialised on device creation + * and is modified by all execute calls. + */ +typedef struct _D3DSTATUS { + DWORD dwFlags; /* Do we set extents or status */ + DWORD dwStatus; /* D3D status */ + D3DRECT drExtent; +} D3DSTATUS, *LPD3DSTATUS; + +#define D3DSETSTATUS_STATUS 0x00000001L +#define D3DSETSTATUS_EXTENTS 0x00000002L +#define D3DSETSTATUS_ALL (D3DSETSTATUS_STATUS | D3DSETSTATUS_EXTENTS) + +typedef struct _D3DCLIPSTATUS { + DWORD dwFlags; /* Do we set 2d extents, 3D extents or status */ + DWORD dwStatus; /* Clip status */ + float minx, maxx; /* X extents */ + float miny, maxy; /* Y extents */ + float minz, maxz; /* Z extents */ +} D3DCLIPSTATUS, *LPD3DCLIPSTATUS; + +#define D3DCLIPSTATUS_STATUS 0x00000001L +#define D3DCLIPSTATUS_EXTENTS2 0x00000002L +#define D3DCLIPSTATUS_EXTENTS3 0x00000004L + +/* + * Statistics structure + */ +typedef struct _D3DSTATS { + DWORD dwSize; + DWORD dwTrianglesDrawn; + DWORD dwLinesDrawn; + DWORD dwPointsDrawn; + DWORD dwSpansDrawn; + DWORD dwVerticesProcessed; +} D3DSTATS, *LPD3DSTATS; + +/* + * Execute options. + * When calling using D3DEXECUTE_UNCLIPPED all the primitives + * inside the buffer must be contained within the viewport. + */ +#define D3DEXECUTE_CLIPPED 0x00000001l +#define D3DEXECUTE_UNCLIPPED 0x00000002l + +typedef struct _D3DEXECUTEDATA { + DWORD dwSize; + DWORD dwVertexOffset; + DWORD dwVertexCount; + DWORD dwInstructionOffset; + DWORD dwInstructionLength; + DWORD dwHVertexOffset; + D3DSTATUS dsStatus; /* Status after execute */ +} D3DEXECUTEDATA, *LPD3DEXECUTEDATA; + +/* + * Palette flags. + * This are or'ed with the peFlags in the PALETTEENTRYs passed to DirectDraw. + */ +#define D3DPAL_FREE 0x00 /* Renderer may use this entry freely */ +#define D3DPAL_READONLY 0x40 /* Renderer may not set this entry */ +#define D3DPAL_RESERVED 0x80 /* Renderer may not use this entry */ + +#pragma pack() + +#endif /* _D3DTYPES_H_ */ + diff --git a/include/vddraw.h b/include/vddraw.h new file mode 100644 index 0000000..3038828 --- /dev/null +++ b/include/vddraw.h @@ -0,0 +1,3792 @@ +/*==========================================================================; + * + * Copyright (C) 1994-1997 Microsoft Corporation. All Rights Reserved. + * + * File: ddraw.h + * Content: DirectDraw include file + * + ***************************************************************************/ + +#ifndef __DDRAW_INCLUDED__ +#define __DDRAW_INCLUDED__ + +/* + * If you wish an application built against the newest version of DirectDraw + * to run against an older DirectDraw run time then define DIRECTDRAW_VERSION + * to be the earlies version of DirectDraw you wish to run against. For, + * example if you wish an application to run against a DX 3 runtime define + * DIRECTDRAW_VERSION to be 0x0300. + */ +#ifndef DIRECTDRAW_VERSION +#define DIRECTDRAW_VERSION 0x0500 +#endif /* DIRECTDRAW_VERSION */ + +#if defined( _WIN32 ) && !defined( _NO_COM ) +#define COM_NO_WINDOWS_H +#include +#else +#define IUnknown void +#if !defined( NT_BUILD_ENVIRONMENT ) && !defined(WINNT) + #define CO_E_NOTINITIALIZED 0x800401F0L +#endif +#endif + +#define _FACDD 0x876 +#define MAKE_DDHRESULT( code ) MAKE_HRESULT( 1, _FACDD, code ) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * GUIDS used by DirectDraw objects + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +DEFINE_GUID( CLSID_DirectDraw, 0xD7B70EE0,0x4340,0x11CF,0xB0,0x63,0x00,0x20,0xAF,0xC2,0xCD,0x35 ); +DEFINE_GUID( CLSID_DirectDrawClipper, 0x593817A0,0x7DB3,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xb9,0x33,0x56 ); +DEFINE_GUID( IID_IDirectDraw, 0x6C14DB80,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 ); +DEFINE_GUID( IID_IDirectDraw2, 0xB3A6F3E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 ); +DEFINE_GUID( IID_IDirectDrawSurface, 0x6C14DB81,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 ); +DEFINE_GUID( IID_IDirectDrawSurface2, 0x57805885,0x6eec,0x11cf,0x94,0x41,0xa8,0x23,0x03,0xc1,0x0e,0x27 ); +DEFINE_GUID( IID_IDirectDrawSurface3, 0xDA044E00,0x69B2,0x11D0,0xA1,0xD5,0x00,0xAA,0x00,0xB8,0xDF,0xBB ); + +DEFINE_GUID( IID_IDirectDrawPalette, 0x6C14DB84,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 ); +DEFINE_GUID( IID_IDirectDrawClipper, 0x6C14DB85,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 ); +DEFINE_GUID( IID_IDirectDrawColorControl, 0x4B9F0EE0,0x0D7E,0x11D0,0x9B,0x06,0x00,0xA0,0xC9,0x03,0xA3,0xB8 ); + +#endif + +/*============================================================================ + * + * DirectDraw Structures + * + * Various structures used to invoke DirectDraw. + * + *==========================================================================*/ + +struct IDirectDraw; +struct IDirectDrawSurface; +struct IDirectDrawPalette; +struct IDirectDrawClipper; + +typedef struct IDirectDraw FAR *LPDIRECTDRAW; +typedef struct IDirectDraw2 FAR *LPDIRECTDRAW2; +typedef struct IDirectDrawSurface FAR *LPDIRECTDRAWSURFACE; +typedef struct IDirectDrawSurface2 FAR *LPDIRECTDRAWSURFACE2; +typedef struct IDirectDrawSurface3 FAR *LPDIRECTDRAWSURFACE3; + +typedef struct IDirectDrawPalette FAR *LPDIRECTDRAWPALETTE; +typedef struct IDirectDrawClipper FAR *LPDIRECTDRAWCLIPPER; +typedef struct IDirectDrawColorControl FAR *LPDIRECTDRAWCOLORCONTROL; + +typedef struct _DDFXROP FAR *LPDDFXROP; +typedef struct _DDSURFACEDESC FAR *LPDDSURFACEDESC; +typedef struct _DDCOLORCONTROL FAR *LPDDCOLORCONTROL; + +/* + * API's + */ +#if (defined (WIN32) || defined( _WIN32 ) ) && !defined( _NO_COM ) +//#if defined( _WIN32 ) && !defined( _NO_ENUM ) + typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKA)(GUID FAR *, LPSTR, LPSTR, LPVOID); + typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKW)(GUID FAR *, LPWSTR, LPWSTR, LPVOID); + extern HRESULT WINAPI DirectDrawEnumerateW( LPDDENUMCALLBACKW lpCallback, LPVOID lpContext ); + extern HRESULT WINAPI DirectDrawEnumerateA( LPDDENUMCALLBACKA lpCallback, LPVOID lpContext ); + #ifdef UNICODE + typedef LPDDENUMCALLBACKW LPDDENUMCALLBACK; + #define DirectDrawEnumerate DirectDrawEnumerateW + #else + typedef LPDDENUMCALLBACKA LPDDENUMCALLBACK; + #define DirectDrawEnumerate DirectDrawEnumerateA + #endif + extern HRESULT WINAPI DirectDrawCreate( GUID FAR *lpGUID, LPDIRECTDRAW FAR *lplpDD, IUnknown FAR *pUnkOuter ); + extern HRESULT WINAPI DirectDrawCreateClipper( DWORD dwFlags, LPDIRECTDRAWCLIPPER FAR *lplpDDClipper, IUnknown FAR *pUnkOuter ); +#endif + + +#define REGSTR_KEY_DDHW_DESCRIPTION "Description" +#define REGSTR_KEY_DDHW_DRIVERNAME "DriverName" +#define REGSTR_PATH_DDHW "Hardware\\DirectDrawDrivers" + +#define DDCREATE_HARDWAREONLY 0x00000001l +#define DDCREATE_EMULATIONONLY 0x00000002l + +#if defined(WINNT) || !defined(WIN32) +typedef long HRESULT; +#endif + +//#ifndef WINNT +typedef HRESULT (FAR PASCAL * LPDDENUMMODESCALLBACK)(LPDDSURFACEDESC, LPVOID); +typedef HRESULT (FAR PASCAL * LPDDENUMSURFACESCALLBACK)(LPDIRECTDRAWSURFACE, LPDDSURFACEDESC, LPVOID); +//#endif +/* + * DDCOLORKEY + */ +typedef struct _DDCOLORKEY +{ + DWORD dwColorSpaceLowValue; // low boundary of color space that is to + // be treated as Color Key, inclusive + DWORD dwColorSpaceHighValue; // high boundary of color space that is + // to be treated as Color Key, inclusive +} DDCOLORKEY; + +typedef DDCOLORKEY FAR* LPDDCOLORKEY; + +/* + * DDBLTFX + * Used to pass override information to the DIRECTDRAWSURFACE callback Blt. + */ +typedef struct _DDBLTFX +{ + DWORD dwSize; // size of structure + DWORD dwDDFX; // FX operations + DWORD dwROP; // Win32 raster operations + DWORD dwDDROP; // Raster operations new for DirectDraw + DWORD dwRotationAngle; // Rotation angle for blt + DWORD dwZBufferOpCode; // ZBuffer compares + DWORD dwZBufferLow; // Low limit of Z buffer + DWORD dwZBufferHigh; // High limit of Z buffer + DWORD dwZBufferBaseDest; // Destination base value + DWORD dwZDestConstBitDepth; // Bit depth used to specify Z constant for destination + union + { + DWORD dwZDestConst; // Constant to use as Z buffer for dest + LPDIRECTDRAWSURFACE lpDDSZBufferDest; // Surface to use as Z buffer for dest + }; + DWORD dwZSrcConstBitDepth; // Bit depth used to specify Z constant for source + union + { + DWORD dwZSrcConst; // Constant to use as Z buffer for src + LPDIRECTDRAWSURFACE lpDDSZBufferSrc; // Surface to use as Z buffer for src + }; + DWORD dwAlphaEdgeBlendBitDepth; // Bit depth used to specify constant for alpha edge blend + DWORD dwAlphaEdgeBlend; // Alpha for edge blending + DWORD dwReserved; + DWORD dwAlphaDestConstBitDepth; // Bit depth used to specify alpha constant for destination + union + { + DWORD dwAlphaDestConst; // Constant to use as Alpha Channel + LPDIRECTDRAWSURFACE lpDDSAlphaDest; // Surface to use as Alpha Channel + }; + DWORD dwAlphaSrcConstBitDepth; // Bit depth used to specify alpha constant for source + union + { + DWORD dwAlphaSrcConst; // Constant to use as Alpha Channel + LPDIRECTDRAWSURFACE lpDDSAlphaSrc; // Surface to use as Alpha Channel + }; + union + { + DWORD dwFillColor; // color in RGB or Palettized + DWORD dwFillDepth; // depth value for z-buffer + DWORD dwFillPixel; // pixel value for RGBA or RGBZ + LPDIRECTDRAWSURFACE lpDDSPattern; // Surface to use as pattern + }; + DDCOLORKEY ddckDestColorkey; // DestColorkey override + DDCOLORKEY ddckSrcColorkey; // SrcColorkey override +} DDBLTFX; + +typedef DDBLTFX FAR* LPDDBLTFX; + + +/* + * DDSCAPS + */ +typedef struct _DDSCAPS +{ + DWORD dwCaps; // capabilities of surface wanted +} DDSCAPS; + +typedef DDSCAPS FAR* LPDDSCAPS; + +/* + * DDCAPS + */ +#define DD_ROP_SPACE (256/32) // space required to store ROP array + +#if DIRECTDRAW_VERSION >= 0x0500 +/* + * This structure is the DDCAPS structure as it was in version 2 and 3 of Direct X. + * It is present for back compatability. + */ +typedef struct _DDCAPS_DX3 +{ + DWORD dwSize; // size of the DDDRIVERCAPS structure + DWORD dwCaps; // driver specific capabilities + DWORD dwCaps2; // more driver specific capabilites + DWORD dwCKeyCaps; // color key capabilities of the surface + DWORD dwFXCaps; // driver specific stretching and effects capabilites + DWORD dwFXAlphaCaps; // alpha driver specific capabilities + DWORD dwPalCaps; // palette capabilities + DWORD dwSVCaps; // stereo vision capabilities + DWORD dwAlphaBltConstBitDepths; // DDBD_2,4,8 + DWORD dwAlphaBltPixelBitDepths; // DDBD_1,2,4,8 + DWORD dwAlphaBltSurfaceBitDepths; // DDBD_1,2,4,8 + DWORD dwAlphaOverlayConstBitDepths; // DDBD_2,4,8 + DWORD dwAlphaOverlayPixelBitDepths; // DDBD_1,2,4,8 + DWORD dwAlphaOverlaySurfaceBitDepths; // DDBD_1,2,4,8 + DWORD dwZBufferBitDepths; // DDBD_8,16,24,32 + DWORD dwVidMemTotal; // total amount of video memory + DWORD dwVidMemFree; // amount of free video memory + DWORD dwMaxVisibleOverlays; // maximum number of visible overlays + DWORD dwCurrVisibleOverlays; // current number of visible overlays + DWORD dwNumFourCCCodes; // number of four cc codes + DWORD dwAlignBoundarySrc; // source rectangle alignment + DWORD dwAlignSizeSrc; // source rectangle byte size + DWORD dwAlignBoundaryDest; // dest rectangle alignment + DWORD dwAlignSizeDest; // dest rectangle byte size + DWORD dwAlignStrideAlign; // stride alignment + DWORD dwRops[DD_ROP_SPACE]; // ROPS supported + DDSCAPS ddsCaps; // DDSCAPS structure has all the general capabilities + DWORD dwMinOverlayStretch; // minimum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwMaxOverlayStretch; // maximum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwMinLiveVideoStretch; // minimum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwMaxLiveVideoStretch; // maximum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwMinHwCodecStretch; // minimum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwMaxHwCodecStretch; // maximum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 + DWORD dwReserved1; // reserved + DWORD dwReserved2; // reserved + DWORD dwReserved3; // reserved + DWORD dwSVBCaps; // driver specific capabilities for System->Vmem blts + DWORD dwSVBCKeyCaps; // driver color key capabilities for System->Vmem blts + DWORD dwSVBFXCaps; // driver FX capabilities for System->Vmem blts + DWORD dwSVBRops[DD_ROP_SPACE];// ROPS supported for System->Vmem blts + DWORD dwVSBCaps; // driver specific capabilities for Vmem->System blts + DWORD dwVSBCKeyCaps; // driver color key capabilities for Vmem->System blts + DWORD dwVSBFXCaps; // driver FX capabilities for Vmem->System blts + DWORD dwVSBRops[DD_ROP_SPACE];// ROPS supported for Vmem->System blts + DWORD dwSSBCaps; // driver specific capabilities for System->System blts + DWORD dwSSBCKeyCaps; // driver color key capabilities for System->System blts + DWORD dwSSBFXCaps; // driver FX capabilities for System->System blts + DWORD dwSSBRops[DD_ROP_SPACE];// ROPS supported for System->System blts + DWORD dwReserved4; // reserved + DWORD dwReserved5; // reserved + DWORD dwReserved6; // reserved +} DDCAPS_DX3; +typedef DDCAPS_DX3 FAR* LPDDCAPS_DX3; +#endif /* DIRECTDRAW_VERSION >= 0x0500 */ + +typedef struct _DDCAPS +{ +/* 0*/ DWORD dwSize; // size of the DDDRIVERCAPS structure +/* 4*/ DWORD dwCaps; // driver specific capabilities +/* 8*/ DWORD dwCaps2; // more driver specific capabilites +/* c*/ DWORD dwCKeyCaps; // color key capabilities of the surface +/* 10*/ DWORD dwFXCaps; // driver specific stretching and effects capabilites +/* 14*/ DWORD dwFXAlphaCaps; // alpha driver specific capabilities +/* 18*/ DWORD dwPalCaps; // palette capabilities +/* 1c*/ DWORD dwSVCaps; // stereo vision capabilities +/* 20*/ DWORD dwAlphaBltConstBitDepths; // DDBD_2,4,8 +/* 24*/ DWORD dwAlphaBltPixelBitDepths; // DDBD_1,2,4,8 +/* 28*/ DWORD dwAlphaBltSurfaceBitDepths; // DDBD_1,2,4,8 +/* 2c*/ DWORD dwAlphaOverlayConstBitDepths; // DDBD_2,4,8 +/* 30*/ DWORD dwAlphaOverlayPixelBitDepths; // DDBD_1,2,4,8 +/* 34*/ DWORD dwAlphaOverlaySurfaceBitDepths; // DDBD_1,2,4,8 +/* 38*/ DWORD dwZBufferBitDepths; // DDBD_8,16,24,32 +/* 3c*/ DWORD dwVidMemTotal; // total amount of video memory +/* 40*/ DWORD dwVidMemFree; // amount of free video memory +/* 44*/ DWORD dwMaxVisibleOverlays; // maximum number of visible overlays +/* 48*/ DWORD dwCurrVisibleOverlays; // current number of visible overlays +/* 4c*/ DWORD dwNumFourCCCodes; // number of four cc codes +/* 50*/ DWORD dwAlignBoundarySrc; // source rectangle alignment +/* 54*/ DWORD dwAlignSizeSrc; // source rectangle byte size +/* 58*/ DWORD dwAlignBoundaryDest; // dest rectangle alignment +/* 5c*/ DWORD dwAlignSizeDest; // dest rectangle byte size +/* 60*/ DWORD dwAlignStrideAlign; // stride alignment +/* 64*/ DWORD dwRops[DD_ROP_SPACE]; // ROPS supported +/* 84*/ DDSCAPS ddsCaps; // DDSCAPS structure has all the general capabilities +/* 88*/ DWORD dwMinOverlayStretch; // minimum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 8c*/ DWORD dwMaxOverlayStretch; // maximum overlay stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 90*/ DWORD dwMinLiveVideoStretch; // minimum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 94*/ DWORD dwMaxLiveVideoStretch; // maximum live video stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 98*/ DWORD dwMinHwCodecStretch; // minimum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* 9c*/ DWORD dwMaxHwCodecStretch; // maximum hardware codec stretch factor multiplied by 1000, eg 1000 == 1.0, 1300 == 1.3 +/* a0*/ DWORD dwReserved1; // reserved +/* a4*/ DWORD dwReserved2; // reserved +/* a8*/ DWORD dwReserved3; // reserved +/* ac*/ DWORD dwSVBCaps; // driver specific capabilities for System->Vmem blts +/* b0*/ DWORD dwSVBCKeyCaps; // driver color key capabilities for System->Vmem blts +/* b4*/ DWORD dwSVBFXCaps; // driver FX capabilities for System->Vmem blts +/* b8*/ DWORD dwSVBRops[DD_ROP_SPACE];// ROPS supported for System->Vmem blts +/* d8*/ DWORD dwVSBCaps; // driver specific capabilities for Vmem->System blts +/* dc*/ DWORD dwVSBCKeyCaps; // driver color key capabilities for Vmem->System blts +/* e0*/ DWORD dwVSBFXCaps; // driver FX capabilities for Vmem->System blts +/* e4*/ DWORD dwVSBRops[DD_ROP_SPACE];// ROPS supported for Vmem->System blts +/*104*/ DWORD dwSSBCaps; // driver specific capabilities for System->System blts +/*108*/ DWORD dwSSBCKeyCaps; // driver color key capabilities for System->System blts +/*10c*/ DWORD dwSSBFXCaps; // driver FX capabilities for System->System blts +/*110*/ DWORD dwSSBRops[DD_ROP_SPACE];// ROPS supported for System->System blts +#if DIRECTDRAW_VERSION >= 0x0500 +/*130*/ DWORD dwMaxVideoPorts; // maximum number of usable video ports +/*134*/ DWORD dwCurrVideoPorts; // current number of video ports used +/*138*/ DWORD dwSVBCaps2; // more driver specific capabilities for System->Vmem blts +/*13c*/ DWORD dwNLVBCaps; // driver specific capabilities for non-local->local vidmem blts +/*140*/ DWORD dwNLVBCaps2; // more driver specific capabilities non-local->local vidmem blts +/*144*/ DWORD dwNLVBCKeyCaps; // driver color key capabilities for non-local->local vidmem blts +/*148*/ DWORD dwNLVBFXCaps; // driver FX capabilities for non-local->local blts +/*14c*/ DWORD dwNLVBRops[DD_ROP_SPACE]; // ROPS supported for non-local->local blts +#else /* DIRECTDRAW_VERSION >= 0x0500 */ +/*130*/ DWORD dwReserved4; // reserved +/*134*/ DWORD dwReserved5; // reserved +/*138*/ DWORD dwReserved6; // reserved +#endif /* DIRECTDRAW_VERSION >= 0x0500 */ +} DDCAPS; + +typedef DDCAPS FAR* LPDDCAPS; + + + +/* + * DDPIXELFORMAT + */ +typedef struct _DDPIXELFORMAT +{ + DWORD dwSize; // size of structure + DWORD dwFlags; // pixel format flags + DWORD dwFourCC; // (FOURCC code) + union + { + DWORD dwRGBBitCount; // how many bits per pixel + DWORD dwYUVBitCount; // how many bits per pixel + DWORD dwZBufferBitDepth; // how many bits for z buffers + DWORD dwAlphaBitDepth; // how many bits for alpha channels + }; + union + { + DWORD dwRBitMask; // mask for red bit + DWORD dwYBitMask; // mask for Y bits + }; + union + { + DWORD dwGBitMask; // mask for green bits + DWORD dwUBitMask; // mask for U bits + }; + union + { + DWORD dwBBitMask; // mask for blue bits + DWORD dwVBitMask; // mask for V bits + }; + union + { + DWORD dwRGBAlphaBitMask; // mask for alpha channel + DWORD dwYUVAlphaBitMask; // mask for alpha channel + DWORD dwRGBZBitMask; // mask for Z channel + DWORD dwYUVZBitMask; // mask for Z channel + }; +} DDPIXELFORMAT; + +typedef DDPIXELFORMAT FAR* LPDDPIXELFORMAT; + +/* + * DDOVERLAYFX + */ +typedef struct _DDOVERLAYFX +{ + DWORD dwSize; // size of structure + DWORD dwAlphaEdgeBlendBitDepth; // Bit depth used to specify constant for alpha edge blend + DWORD dwAlphaEdgeBlend; // Constant to use as alpha for edge blend + DWORD dwReserved; + DWORD dwAlphaDestConstBitDepth; // Bit depth used to specify alpha constant for destination + union + { + DWORD dwAlphaDestConst; // Constant to use as alpha channel for dest + LPDIRECTDRAWSURFACE lpDDSAlphaDest; // Surface to use as alpha channel for dest + }; + DWORD dwAlphaSrcConstBitDepth; // Bit depth used to specify alpha constant for source + union + { + DWORD dwAlphaSrcConst; // Constant to use as alpha channel for src + LPDIRECTDRAWSURFACE lpDDSAlphaSrc; // Surface to use as alpha channel for src + }; + DDCOLORKEY dckDestColorkey; // DestColorkey override + DDCOLORKEY dckSrcColorkey; // DestColorkey override + DWORD dwDDFX; // Overlay FX + DWORD dwFlags; // flags +} DDOVERLAYFX; + +typedef DDOVERLAYFX FAR *LPDDOVERLAYFX; + +/* + * DDBLTBATCH: BltBatch entry structure + */ +typedef struct _DDBLTBATCH +{ + LPRECT lprDest; + LPDIRECTDRAWSURFACE lpDDSSrc; + LPRECT lprSrc; + DWORD dwFlags; + LPDDBLTFX lpDDBltFx; +} DDBLTBATCH; + +typedef DDBLTBATCH FAR * LPDDBLTBATCH; + +/* + * callbacks + */ +typedef DWORD (FAR PASCAL *LPCLIPPERCALLBACK)(LPDIRECTDRAWCLIPPER lpDDClipper, HWND hWnd, DWORD code, LPVOID lpContext ); +#ifdef STREAMING +typedef DWORD (FAR PASCAL *LPSURFACESTREAMINGCALLBACK)(DWORD); +#endif + + +/* + * INTERACES FOLLOW: + * IDirectDraw + * IDirectDrawClipper + * IDirectDrawPalette + * IDirectDrawSurface + */ + +/* + * IDirectDraw + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDraw +DECLARE_INTERFACE_( IDirectDraw, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDraw methods ***/ + STDMETHOD(Compact)(THIS) PURE; + STDMETHOD(CreateClipper)(THIS_ DWORD, LPDIRECTDRAWCLIPPER FAR*, IUnknown FAR * ) PURE; + STDMETHOD(CreatePalette)(THIS_ DWORD, LPPALETTEENTRY, LPDIRECTDRAWPALETTE FAR*, IUnknown FAR * ) PURE; + STDMETHOD(CreateSurface)(THIS_ LPDDSURFACEDESC, LPDIRECTDRAWSURFACE FAR *, IUnknown FAR *) PURE; + STDMETHOD(DuplicateSurface)( THIS_ LPDIRECTDRAWSURFACE, LPDIRECTDRAWSURFACE FAR * ) PURE; + STDMETHOD(EnumDisplayModes)( THIS_ DWORD, LPDDSURFACEDESC, LPVOID, LPDDENUMMODESCALLBACK ) PURE; + STDMETHOD(EnumSurfaces)(THIS_ DWORD, LPDDSURFACEDESC, LPVOID,LPDDENUMSURFACESCALLBACK ) PURE; + STDMETHOD(FlipToGDISurface)(THIS) PURE; + STDMETHOD(GetCaps)( THIS_ LPDDCAPS, LPDDCAPS) PURE; + STDMETHOD(GetDisplayMode)( THIS_ LPDDSURFACEDESC) PURE; + STDMETHOD(GetFourCCCodes)(THIS_ LPDWORD, LPDWORD ) PURE; + STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE FAR *) PURE; + STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD) PURE; + STDMETHOD(GetScanLine)(THIS_ LPDWORD) PURE; + STDMETHOD(GetVerticalBlankStatus)(THIS_ LPBOOL ) PURE; + STDMETHOD(Initialize)(THIS_ GUID FAR *) PURE; + STDMETHOD(RestoreDisplayMode)(THIS) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND, DWORD) PURE; + STDMETHOD(SetDisplayMode)(THIS_ DWORD, DWORD,DWORD) PURE; + STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD, HANDLE ) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDraw_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDraw_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDraw_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDraw_Compact(p) (p)->lpVtbl->Compact(p) +#define IDirectDraw_CreateClipper(p, a, b, c) (p)->lpVtbl->CreateClipper(p, a, b, c) +#define IDirectDraw_CreatePalette(p, a, b, c, d) (p)->lpVtbl->CreatePalette(p, a, b, c, d) +#define IDirectDraw_CreateSurface(p, a, b, c) (p)->lpVtbl->CreateSurface(p, a, b, c) +#define IDirectDraw_DuplicateSurface(p, a, b) (p)->lpVtbl->DuplicateSurface(p, a, b) +#define IDirectDraw_EnumDisplayModes(p, a, b, c, d) (p)->lpVtbl->EnumDisplayModes(p, a, b, c, d) +#define IDirectDraw_EnumSurfaces(p, a, b, c, d) (p)->lpVtbl->EnumSurfaces(p, a, b, c, d) +#define IDirectDraw_FlipToGDISurface(p) (p)->lpVtbl->FlipToGDISurface(p) +#define IDirectDraw_GetCaps(p, a, b) (p)->lpVtbl->GetCaps(p, a, b) +#define IDirectDraw_GetDisplayMode(p, a) (p)->lpVtbl->GetDisplayMode(p, a) +#define IDirectDraw_GetFourCCCodes(p, a, b) (p)->lpVtbl->GetFourCCCodes(p, a, b) +#define IDirectDraw_GetGDISurface(p, a) (p)->lpVtbl->GetGDISurface(p, a) +#define IDirectDraw_GetMonitorFrequency(p, a) (p)->lpVtbl->GetMonitorFrequency(p, a) +#define IDirectDraw_GetScanLine(p, a) (p)->lpVtbl->GetScanLine(p, a) +#define IDirectDraw_GetVerticalBlankStatus(p, a) (p)->lpVtbl->GetVerticalBlankStatus(p, a) +#define IDirectDraw_Initialize(p, a) (p)->lpVtbl->Initialize(p, a) +#define IDirectDraw_RestoreDisplayMode(p) (p)->lpVtbl->RestoreDisplayMode(p) +#define IDirectDraw_SetCooperativeLevel(p, a, b) (p)->lpVtbl->SetCooperativeLevel(p, a, b) +#define IDirectDraw_SetDisplayMode(p, a, b, c) (p)->lpVtbl->SetDisplayMode(p, a, b, c) +#define IDirectDraw_WaitForVerticalBlank(p, a, b) (p)->lpVtbl->WaitForVerticalBlank(p, a, b) +#else +#define IDirectDraw_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDraw_AddRef(p) (p)->AddRef() +#define IDirectDraw_Release(p) (p)->Release() +#define IDirectDraw_Compact(p) (p)->Compact() +#define IDirectDraw_CreateClipper(p, a, b, c) (p)->CreateClipper(a, b, c) +#define IDirectDraw_CreatePalette(p, a, b, c, d) (p)->CreatePalette(a, b, c, d) +#define IDirectDraw_CreateSurface(p, a, b, c) (p)->CreateSurface(a, b, c) +#define IDirectDraw_DuplicateSurface(p, a, b) (p)->DuplicateSurface(a, b) +#define IDirectDraw_EnumDisplayModes(p, a, b, c, d) (p)->EnumDisplayModes(a, b, c, d) +#define IDirectDraw_EnumSurfaces(p, a, b, c, d) (p)->EnumSurfaces(a, b, c, d) +#define IDirectDraw_FlipToGDISurface(p) (p)->FlipToGDISurface() +#define IDirectDraw_GetCaps(p, a, b) (p)->GetCaps(a, b) +#define IDirectDraw_GetDisplayMode(p, a) (p)->GetDisplayMode(a) +#define IDirectDraw_GetFourCCCodes(p, a, b) (p)->GetFourCCCodes(a, b) +#define IDirectDraw_GetGDISurface(p, a) (p)->GetGDISurface(a) +#define IDirectDraw_GetMonitorFrequency(p, a) (p)->GetMonitorFrequency(a) +#define IDirectDraw_GetScanLine(p, a) (p)->GetScanLine(a) +#define IDirectDraw_GetVerticalBlankStatus(p, a) (p)->GetVerticalBlankStatus(a) +#define IDirectDraw_Initialize(p, a) (p)->Initialize(a) +#define IDirectDraw_RestoreDisplayMode(p) (p)->RestoreDisplayMode() +#define IDirectDraw_SetCooperativeLevel(p, a, b) (p)->SetCooperativeLevel(a, b) +#define IDirectDraw_SetDisplayMode(p, a, b, c) (p)->SetDisplayMode(a, b, c) +#define IDirectDraw_WaitForVerticalBlank(p, a, b) (p)->WaitForVerticalBlank(a, b) +#endif + +#endif + +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDraw2 +DECLARE_INTERFACE_( IDirectDraw2, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDraw methods ***/ + STDMETHOD(Compact)(THIS) PURE; + STDMETHOD(CreateClipper)(THIS_ DWORD, LPDIRECTDRAWCLIPPER FAR*, IUnknown FAR * ) PURE; + STDMETHOD(CreatePalette)(THIS_ DWORD, LPPALETTEENTRY, LPDIRECTDRAWPALETTE FAR*, IUnknown FAR * ) PURE; + STDMETHOD(CreateSurface)(THIS_ LPDDSURFACEDESC, LPDIRECTDRAWSURFACE FAR *, IUnknown FAR *) PURE; + STDMETHOD(DuplicateSurface)( THIS_ LPDIRECTDRAWSURFACE, LPDIRECTDRAWSURFACE FAR * ) PURE; + STDMETHOD(EnumDisplayModes)( THIS_ DWORD, LPDDSURFACEDESC, LPVOID, LPDDENUMMODESCALLBACK ) PURE; + STDMETHOD(EnumSurfaces)(THIS_ DWORD, LPDDSURFACEDESC, LPVOID,LPDDENUMSURFACESCALLBACK ) PURE; + STDMETHOD(FlipToGDISurface)(THIS) PURE; + STDMETHOD(GetCaps)( THIS_ LPDDCAPS, LPDDCAPS) PURE; + STDMETHOD(GetDisplayMode)( THIS_ LPDDSURFACEDESC) PURE; + STDMETHOD(GetFourCCCodes)(THIS_ LPDWORD, LPDWORD ) PURE; + STDMETHOD(GetGDISurface)(THIS_ LPDIRECTDRAWSURFACE FAR *) PURE; + STDMETHOD(GetMonitorFrequency)(THIS_ LPDWORD) PURE; + STDMETHOD(GetScanLine)(THIS_ LPDWORD) PURE; + STDMETHOD(GetVerticalBlankStatus)(THIS_ LPBOOL ) PURE; + STDMETHOD(Initialize)(THIS_ GUID FAR *) PURE; + STDMETHOD(RestoreDisplayMode)(THIS) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND, DWORD) PURE; + STDMETHOD(SetDisplayMode)(THIS_ DWORD, DWORD,DWORD, DWORD, DWORD) PURE; + STDMETHOD(WaitForVerticalBlank)(THIS_ DWORD, HANDLE ) PURE; + /*** Added in the v2 interface ***/ + STDMETHOD(GetAvailableVidMem)(THIS_ LPDDSCAPS, LPDWORD, LPDWORD) PURE; +}; +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDraw2_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDraw2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDraw2_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDraw2_Compact(p) (p)->lpVtbl->Compact(p) +#define IDirectDraw2_CreateClipper(p, a, b, c) (p)->lpVtbl->CreateClipper(p, a, b, c) +#define IDirectDraw2_CreatePalette(p, a, b, c, d) (p)->lpVtbl->CreatePalette(p, a, b, c, d) +#define IDirectDraw2_CreateSurface(p, a, b, c) (p)->lpVtbl->CreateSurface(p, a, b, c) +#define IDirectDraw2_DuplicateSurface(p, a, b) (p)->lpVtbl->DuplicateSurface(p, a, b) +#define IDirectDraw2_EnumDisplayModes(p, a, b, c, d) (p)->lpVtbl->EnumDisplayModes(p, a, b, c, d) +#define IDirectDraw2_EnumSurfaces(p, a, b, c, d) (p)->lpVtbl->EnumSurfaces(p, a, b, c, d) +#define IDirectDraw2_FlipToGDISurface(p) (p)->lpVtbl->FlipToGDISurface(p) +#define IDirectDraw2_GetCaps(p, a, b) (p)->lpVtbl->GetCaps(p, a, b) +#define IDirectDraw2_GetDisplayMode(p, a) (p)->lpVtbl->GetDisplayMode(p, a) +#define IDirectDraw2_GetFourCCCodes(p, a, b) (p)->lpVtbl->GetFourCCCodes(p, a, b) +#define IDirectDraw2_GetGDISurface(p, a) (p)->lpVtbl->GetGDISurface(p, a) +#define IDirectDraw2_GetMonitorFrequency(p, a) (p)->lpVtbl->GetMonitorFrequency(p, a) +#define IDirectDraw2_GetScanLine(p, a) (p)->lpVtbl->GetScanLine(p, a) +#define IDirectDraw2_GetVerticalBlankStatus(p, a) (p)->lpVtbl->GetVerticalBlankStatus(p, a) +#define IDirectDraw2_Initialize(p, a) (p)->lpVtbl->Initialize(p, a) +#define IDirectDraw2_RestoreDisplayMode(p) (p)->lpVtbl->RestoreDisplayMode(p) +#define IDirectDraw2_SetCooperativeLevel(p, a, b) (p)->lpVtbl->SetCooperativeLevel(p, a, b) +#define IDirectDraw2_SetDisplayMode(p, a, b, c, d, e) (p)->lpVtbl->SetDisplayMode(p, a, b, c, d, e) +#define IDirectDraw2_WaitForVerticalBlank(p, a, b) (p)->lpVtbl->WaitForVerticalBlank(p, a, b) +#define IDirectDraw2_GetAvailableVidMem(p, a, b, c) (p)->lpVtbl->GetAvailableVidMem(p, a, b, c) +#else +#define IDirectDraw2_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDraw2_AddRef(p) (p)->AddRef() +#define IDirectDraw2_Release(p) (p)->Release() +#define IDirectDraw2_Compact(p) (p)->Compact() +#define IDirectDraw2_CreateClipper(p, a, b, c) (p)->CreateClipper(a, b, c) +#define IDirectDraw2_CreatePalette(p, a, b, c, d) (p)->CreatePalette(a, b, c, d) +#define IDirectDraw2_CreateSurface(p, a, b, c) (p)->CreateSurface(a, b, c) +#define IDirectDraw2_DuplicateSurface(p, a, b) (p)->DuplicateSurface(a, b) +#define IDirectDraw2_EnumDisplayModes(p, a, b, c, d) (p)->EnumDisplayModes(a, b, c, d) +#define IDirectDraw2_EnumSurfaces(p, a, b, c, d) (p)->EnumSurfaces(a, b, c, d) +#define IDirectDraw2_FlipToGDISurface(p) (p)->FlipToGDISurface() +#define IDirectDraw2_GetCaps(p, a, b) (p)->GetCaps(a, b) +#define IDirectDraw2_GetDisplayMode(p, a) (p)->GetDisplayMode(a) +#define IDirectDraw2_GetFourCCCodes(p, a, b) (p)->GetFourCCCodes(a, b) +#define IDirectDraw2_GetGDISurface(p, a) (p)->GetGDISurface(a) +#define IDirectDraw2_GetMonitorFrequency(p, a) (p)->GetMonitorFrequency(a) +#define IDirectDraw2_GetScanLine(p, a) (p)->GetScanLine(a) +#define IDirectDraw2_GetVerticalBlankStatus(p, a) (p)->GetVerticalBlankStatus(a) +#define IDirectDraw2_Initialize(p, a) (p)->Initialize(a) +#define IDirectDraw2_RestoreDisplayMode(p) (p)->RestoreDisplayMode() +#define IDirectDraw2_SetCooperativeLevel(p, a, b) (p)->SetCooperativeLevel(a, b) +#define IDirectDraw2_SetDisplayMode(p, a, b, c, d, e) (p)->SetDisplayMode(a, b, c, d, e) +#define IDirectDraw2_WaitForVerticalBlank(p, a, b) (p)->WaitForVerticalBlank(a, b) +#define IDirectDraw2_GetAvailableVidMem(p, a, b, c) (p)->GetAvailableVidMem(a, b, c) +#endif + +#endif + +/* + * IDirectDrawPalette + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDrawPalette +DECLARE_INTERFACE_( IDirectDrawPalette, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawPalette methods ***/ + STDMETHOD(GetCaps)(THIS_ LPDWORD) PURE; + STDMETHOD(GetEntries)(THIS_ DWORD,DWORD,DWORD,LPPALETTEENTRY) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, DWORD, LPPALETTEENTRY) PURE; + STDMETHOD(SetEntries)(THIS_ DWORD,DWORD,DWORD,LPPALETTEENTRY) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawPalette_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDrawPalette_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawPalette_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawPalette_GetCaps(p, a) (p)->lpVtbl->GetCaps(p, a) +#define IDirectDrawPalette_GetEntries(p, a, b, c, d) (p)->lpVtbl->GetEntries(p, a, b, c, d) +#define IDirectDrawPalette_Initialize(p, a, b, c) (p)->lpVtbl->Initialize(p, a, b, c) +#define IDirectDrawPalette_SetEntries(p, a, b, c, d) (p)->lpVtbl->SetEntries(p, a, b, c, d) +#else +#define IDirectDrawPalette_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDrawPalette_AddRef(p) (p)->AddRef() +#define IDirectDrawPalette_Release(p) (p)->Release() +#define IDirectDrawPalette_GetCaps(p, a) (p)->GetCaps(a) +#define IDirectDrawPalette_GetEntries(p, a, b, c, d) (p)->GetEntries(a, b, c, d) +#define IDirectDrawPalette_Initialize(p, a, b, c) (p)->Initialize(a, b, c) +#define IDirectDrawPalette_SetEntries(p, a, b, c, d) (p)->SetEntries(a, b, c, d) +#endif + +#endif + +/* + * IDirectDrawClipper + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDrawClipper +DECLARE_INTERFACE_( IDirectDrawClipper, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawClipper methods ***/ + STDMETHOD(GetClipList)(THIS_ LPRECT, LPRGNDATA, LPDWORD) PURE; + STDMETHOD(GetHWnd)(THIS_ HWND FAR *) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, DWORD) PURE; + STDMETHOD(IsClipListChanged)(THIS_ BOOL FAR *) PURE; + STDMETHOD(SetClipList)(THIS_ LPRGNDATA,DWORD) PURE; + STDMETHOD(SetHWnd)(THIS_ DWORD, HWND ) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawClipper_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDrawClipper_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawClipper_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawClipper_GetClipList(p, a, b, c) (p)->lpVtbl->GetClipList(p, a, b, c) +#define IDirectDrawClipper_GetHWnd(p, a) (p)->lpVtbl->GetHWnd(p, a) +#define IDirectDrawClipper_Initialize(p, a, b) (p)->lpVtbl->Initialize(p, a, b) +#define IDirectDrawClipper_IsClipListChanged(p, a) (p)->lpVtbl->IsClipListChanged(p, a) +#define IDirectDrawClipper_SetClipList(p, a, b) (p)->lpVtbl->SetClipList(p, a, b) +#define IDirectDrawClipper_SetHWnd(p, a, b) (p)->lpVtbl->SetHWnd(p, a, b) +#else +#define IDirectDrawClipper_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDrawClipper_AddRef(p) (p)->AddRef() +#define IDirectDrawClipper_Release(p) (p)->Release() +#define IDirectDrawClipper_GetClipList(p, a, b, c) (p)->GetClipList(a, b, c) +#define IDirectDrawClipper_GetHWnd(p, a) (p)->GetHWnd(a) +#define IDirectDrawClipper_Initialize(p, a, b) (p)->Initialize(a, b) +#define IDirectDrawClipper_IsClipListChanged(p, a) (p)->IsClipListChanged(a) +#define IDirectDrawClipper_SetClipList(p, a, b) (p)->SetClipList(a, b) +#define IDirectDrawClipper_SetHWnd(p, a, b) (p)->SetHWnd(a, b) +#endif + +#endif + +/* + * IDirectDrawSurface and related interfaces + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDrawSurface +DECLARE_INTERFACE_( IDirectDrawSurface, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawSurface methods ***/ + STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT) PURE; + STDMETHOD(Blt)(THIS_ LPRECT,LPDIRECTDRAWSURFACE, LPRECT,DWORD, LPDDBLTFX) PURE; + STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH, DWORD, DWORD ) PURE; + STDMETHOD(BltFast)(THIS_ DWORD,DWORD,LPDIRECTDRAWSURFACE, LPRECT,DWORD) PURE; + STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD,LPDIRECTDRAWSURFACE) PURE; + STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD,LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE, DWORD) PURE; + STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS, LPDIRECTDRAWSURFACE FAR *) PURE; + STDMETHOD(GetBltStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetCaps)(THIS_ LPDDSCAPS) PURE; + STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER FAR*) PURE; + STDMETHOD(GetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(GetDC)(THIS_ HDC FAR *) PURE; + STDMETHOD(GetFlipStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetOverlayPosition)(THIS_ LPLONG, LPLONG ) PURE; + STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE FAR*) PURE; + STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT) PURE; + STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, LPDDSURFACEDESC) PURE; + STDMETHOD(IsLost)(THIS) PURE; + STDMETHOD(Lock)(THIS_ LPRECT,LPDDSURFACEDESC,DWORD,HANDLE) PURE; + STDMETHOD(ReleaseDC)(THIS_ HDC) PURE; + STDMETHOD(Restore)(THIS) PURE; + STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER) PURE; + STDMETHOD(SetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(SetOverlayPosition)(THIS_ LONG, LONG ) PURE; + STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE) PURE; + STDMETHOD(Unlock)(THIS_ LPVOID) PURE; + STDMETHOD(UpdateOverlay)(THIS_ LPRECT, LPDIRECTDRAWSURFACE,LPRECT,DWORD, LPDDOVERLAYFX) PURE; + STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD) PURE; + STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD, LPDIRECTDRAWSURFACE) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawSurface_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectDrawSurface_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawSurface_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawSurface_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a) +#define IDirectDrawSurface_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a) +#define IDirectDrawSurface_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e) +#define IDirectDrawSurface_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c) +#define IDirectDrawSurface_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e) +#define IDirectDrawSurface_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b) +#define IDirectDrawSurface_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b) +#define IDirectDrawSurface_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c) +#define IDirectDrawSurface_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b) +#define IDirectDrawSurface_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b) +#define IDirectDrawSurface_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a) +#define IDirectDrawSurface_GetCaps(p,b) (p)->lpVtbl->GetCaps(p,b) +#define IDirectDrawSurface_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a) +#define IDirectDrawSurface_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b) +#define IDirectDrawSurface_GetDC(p,a) (p)->lpVtbl->GetDC(p,a) +#define IDirectDrawSurface_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a) +#define IDirectDrawSurface_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b) +#define IDirectDrawSurface_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a) +#define IDirectDrawSurface_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a) +#define IDirectDrawSurface_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a) +#define IDirectDrawSurface_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectDrawSurface_IsLost(p) (p)->lpVtbl->IsLost(p) +#define IDirectDrawSurface_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) +#define IDirectDrawSurface_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a) +#define IDirectDrawSurface_Restore(p) (p)->lpVtbl->Restore(p) +#define IDirectDrawSurface_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a) +#define IDirectDrawSurface_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b) +#define IDirectDrawSurface_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b) +#define IDirectDrawSurface_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a) +#define IDirectDrawSurface_Unlock(p,b) (p)->lpVtbl->Unlock(p,b) +#define IDirectDrawSurface_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e) +#define IDirectDrawSurface_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a) +#define IDirectDrawSurface_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b) +#else +#define IDirectDrawSurface_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectDrawSurface_AddRef(p) (p)->AddRef() +#define IDirectDrawSurface_Release(p) (p)->Release() +#define IDirectDrawSurface_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a) +#define IDirectDrawSurface_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a) +#define IDirectDrawSurface_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e) +#define IDirectDrawSurface_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c) +#define IDirectDrawSurface_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e) +#define IDirectDrawSurface_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b) +#define IDirectDrawSurface_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b) +#define IDirectDrawSurface_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c) +#define IDirectDrawSurface_Flip(p,a,b) (p)->Flip(a,b) +#define IDirectDrawSurface_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b) +#define IDirectDrawSurface_GetBltStatus(p,a) (p)->GetBltStatus(a) +#define IDirectDrawSurface_GetCaps(p,b) (p)->GetCaps(b) +#define IDirectDrawSurface_GetClipper(p,a) (p)->GetClipper(a) +#define IDirectDrawSurface_GetColorKey(p,a,b) (p)->GetColorKey(a,b) +#define IDirectDrawSurface_GetDC(p,a) (p)->GetDC(a) +#define IDirectDrawSurface_GetFlipStatus(p,a) (p)->GetFlipStatus(a) +#define IDirectDrawSurface_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b) +#define IDirectDrawSurface_GetPalette(p,a) (p)->GetPalette(a) +#define IDirectDrawSurface_GetPixelFormat(p,a) (p)->GetPixelFormat(a) +#define IDirectDrawSurface_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a) +#define IDirectDrawSurface_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectDrawSurface_IsLost(p) (p)->IsLost() +#define IDirectDrawSurface_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) +#define IDirectDrawSurface_ReleaseDC(p,a) (p)->ReleaseDC(a) +#define IDirectDrawSurface_Restore(p) (p)->Restore() +#define IDirectDrawSurface_SetClipper(p,a) (p)->SetClipper(a) +#define IDirectDrawSurface_SetColorKey(p,a,b) (p)->SetColorKey(a,b) +#define IDirectDrawSurface_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b) +#define IDirectDrawSurface_SetPalette(p,a) (p)->SetPalette(a) +#define IDirectDrawSurface_Unlock(p,b) (p)->Unlock(b) +#define IDirectDrawSurface_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e) +#define IDirectDrawSurface_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a) +#define IDirectDrawSurface_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b) +#endif + +/* + * IDirectDrawSurface2 and related interfaces + */ +#undef INTERFACE +#define INTERFACE IDirectDrawSurface2 +DECLARE_INTERFACE_( IDirectDrawSurface2, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawSurface methods ***/ + STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE2) PURE; + STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT) PURE; + STDMETHOD(Blt)(THIS_ LPRECT,LPDIRECTDRAWSURFACE2, LPRECT,DWORD, LPDDBLTFX) PURE; + STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH, DWORD, DWORD ) PURE; + STDMETHOD(BltFast)(THIS_ DWORD,DWORD,LPDIRECTDRAWSURFACE2, LPRECT,DWORD) PURE; + STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD,LPDIRECTDRAWSURFACE2) PURE; + STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD,LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE2, DWORD) PURE; + STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS, LPDIRECTDRAWSURFACE2 FAR *) PURE; + STDMETHOD(GetBltStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetCaps)(THIS_ LPDDSCAPS) PURE; + STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER FAR*) PURE; + STDMETHOD(GetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(GetDC)(THIS_ HDC FAR *) PURE; + STDMETHOD(GetFlipStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetOverlayPosition)(THIS_ LPLONG, LPLONG ) PURE; + STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE FAR*) PURE; + STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT) PURE; + STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, LPDDSURFACEDESC) PURE; + STDMETHOD(IsLost)(THIS) PURE; + STDMETHOD(Lock)(THIS_ LPRECT,LPDDSURFACEDESC,DWORD,HANDLE) PURE; + STDMETHOD(ReleaseDC)(THIS_ HDC) PURE; + STDMETHOD(Restore)(THIS) PURE; + STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER) PURE; + STDMETHOD(SetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(SetOverlayPosition)(THIS_ LONG, LONG ) PURE; + STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE) PURE; + STDMETHOD(Unlock)(THIS_ LPVOID) PURE; + STDMETHOD(UpdateOverlay)(THIS_ LPRECT, LPDIRECTDRAWSURFACE2,LPRECT,DWORD, LPDDOVERLAYFX) PURE; + STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD) PURE; + STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD, LPDIRECTDRAWSURFACE2) PURE; + /*** Added in the v2 interface ***/ + STDMETHOD(GetDDInterface)(THIS_ LPVOID FAR *) PURE; + STDMETHOD(PageLock)(THIS_ DWORD) PURE; + STDMETHOD(PageUnlock)(THIS_ DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawSurface2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectDrawSurface2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawSurface2_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawSurface2_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a) +#define IDirectDrawSurface2_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a) +#define IDirectDrawSurface2_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e) +#define IDirectDrawSurface2_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c) +#define IDirectDrawSurface2_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e) +#define IDirectDrawSurface2_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b) +#define IDirectDrawSurface2_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b) +#define IDirectDrawSurface2_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c) +#define IDirectDrawSurface2_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b) +#define IDirectDrawSurface2_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b) +#define IDirectDrawSurface2_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a) +#define IDirectDrawSurface2_GetCaps(p,b) (p)->lpVtbl->GetCaps(p,b) +#define IDirectDrawSurface2_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a) +#define IDirectDrawSurface2_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b) +#define IDirectDrawSurface2_GetDC(p,a) (p)->lpVtbl->GetDC(p,a) +#define IDirectDrawSurface2_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a) +#define IDirectDrawSurface2_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b) +#define IDirectDrawSurface2_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a) +#define IDirectDrawSurface2_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a) +#define IDirectDrawSurface2_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a) +#define IDirectDrawSurface2_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectDrawSurface2_IsLost(p) (p)->lpVtbl->IsLost(p) +#define IDirectDrawSurface2_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) +#define IDirectDrawSurface2_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a) +#define IDirectDrawSurface2_Restore(p) (p)->lpVtbl->Restore(p) +#define IDirectDrawSurface2_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a) +#define IDirectDrawSurface2_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b) +#define IDirectDrawSurface2_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b) +#define IDirectDrawSurface2_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a) +#define IDirectDrawSurface2_Unlock(p,b) (p)->lpVtbl->Unlock(p,b) +#define IDirectDrawSurface2_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e) +#define IDirectDrawSurface2_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a) +#define IDirectDrawSurface2_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b) +#define IDirectDrawSurface2_GetDDInterface(p,a) (p)->lpVtbl->GetDDInterface(p,a) +#define IDirectDrawSurface2_PageLock(p,a) (p)->lpVtbl->PageLock(p,a) +#define IDirectDrawSurface2_PageUnlock(p,a) (p)->lpVtbl->PageUnlock(p,a) +#else +#define IDirectDrawSurface2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectDrawSurface2_AddRef(p) (p)->AddRef() +#define IDirectDrawSurface2_Release(p) (p)->Release() +#define IDirectDrawSurface2_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a) +#define IDirectDrawSurface2_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a) +#define IDirectDrawSurface2_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e) +#define IDirectDrawSurface2_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c) +#define IDirectDrawSurface2_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e) +#define IDirectDrawSurface2_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b) +#define IDirectDrawSurface2_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b) +#define IDirectDrawSurface2_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c) +#define IDirectDrawSurface2_Flip(p,a,b) (p)->Flip(a,b) +#define IDirectDrawSurface2_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b) +#define IDirectDrawSurface2_GetBltStatus(p,a) (p)->GetBltStatus(a) +#define IDirectDrawSurface2_GetCaps(p,b) (p)->GetCaps(b) +#define IDirectDrawSurface2_GetClipper(p,a) (p)->GetClipper(a) +#define IDirectDrawSurface2_GetColorKey(p,a,b) (p)->GetColorKey(a,b) +#define IDirectDrawSurface2_GetDC(p,a) (p)->GetDC(a) +#define IDirectDrawSurface2_GetFlipStatus(p,a) (p)->GetFlipStatus(a) +#define IDirectDrawSurface2_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b) +#define IDirectDrawSurface2_GetPalette(p,a) (p)->GetPalette(a) +#define IDirectDrawSurface2_GetPixelFormat(p,a) (p)->GetPixelFormat(a) +#define IDirectDrawSurface2_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a) +#define IDirectDrawSurface2_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectDrawSurface2_IsLost(p) (p)->IsLost() +#define IDirectDrawSurface2_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) +#define IDirectDrawSurface2_ReleaseDC(p,a) (p)->ReleaseDC(a) +#define IDirectDrawSurface2_Restore(p) (p)->Restore() +#define IDirectDrawSurface2_SetClipper(p,a) (p)->SetClipper(a) +#define IDirectDrawSurface2_SetColorKey(p,a,b) (p)->SetColorKey(a,b) +#define IDirectDrawSurface2_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b) +#define IDirectDrawSurface2_SetPalette(p,a) (p)->SetPalette(a) +#define IDirectDrawSurface2_Unlock(p,b) (p)->Unlock(b) +#define IDirectDrawSurface2_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e) +#define IDirectDrawSurface2_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a) +#define IDirectDrawSurface2_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b) +#define IDirectDrawSurface2_GetDDInterface(p,a) (p)->GetDDInterface(a) +#define IDirectDrawSurface2_PageLock(p,a) (p)->PageLock(a) +#define IDirectDrawSurface2_PageUnlock(p,a) (p)->PageUnlock(a) +#endif + +/* + * IDirectDrawSurface3 and related interfaces + */ +#undef INTERFACE +#define INTERFACE IDirectDrawSurface3 +DECLARE_INTERFACE_( IDirectDrawSurface3, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawSurface methods ***/ + STDMETHOD(AddAttachedSurface)(THIS_ LPDIRECTDRAWSURFACE3) PURE; + STDMETHOD(AddOverlayDirtyRect)(THIS_ LPRECT) PURE; + STDMETHOD(Blt)(THIS_ LPRECT,LPDIRECTDRAWSURFACE3, LPRECT,DWORD, LPDDBLTFX) PURE; + STDMETHOD(BltBatch)(THIS_ LPDDBLTBATCH, DWORD, DWORD ) PURE; + STDMETHOD(BltFast)(THIS_ DWORD,DWORD,LPDIRECTDRAWSURFACE3, LPRECT,DWORD) PURE; + STDMETHOD(DeleteAttachedSurface)(THIS_ DWORD,LPDIRECTDRAWSURFACE3) PURE; + STDMETHOD(EnumAttachedSurfaces)(THIS_ LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(EnumOverlayZOrders)(THIS_ DWORD,LPVOID,LPDDENUMSURFACESCALLBACK) PURE; + STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE3, DWORD) PURE; + STDMETHOD(GetAttachedSurface)(THIS_ LPDDSCAPS, LPDIRECTDRAWSURFACE3 FAR *) PURE; + STDMETHOD(GetBltStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetCaps)(THIS_ LPDDSCAPS) PURE; + STDMETHOD(GetClipper)(THIS_ LPDIRECTDRAWCLIPPER FAR*) PURE; + STDMETHOD(GetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(GetDC)(THIS_ HDC FAR *) PURE; + STDMETHOD(GetFlipStatus)(THIS_ DWORD) PURE; + STDMETHOD(GetOverlayPosition)(THIS_ LPLONG, LPLONG ) PURE; + STDMETHOD(GetPalette)(THIS_ LPDIRECTDRAWPALETTE FAR*) PURE; + STDMETHOD(GetPixelFormat)(THIS_ LPDDPIXELFORMAT) PURE; + STDMETHOD(GetSurfaceDesc)(THIS_ LPDDSURFACEDESC) PURE; + STDMETHOD(Initialize)(THIS_ LPDIRECTDRAW, LPDDSURFACEDESC) PURE; + STDMETHOD(IsLost)(THIS) PURE; + STDMETHOD(Lock)(THIS_ LPRECT,LPDDSURFACEDESC,DWORD,HANDLE) PURE; + STDMETHOD(ReleaseDC)(THIS_ HDC) PURE; + STDMETHOD(Restore)(THIS) PURE; + STDMETHOD(SetClipper)(THIS_ LPDIRECTDRAWCLIPPER) PURE; + STDMETHOD(SetColorKey)(THIS_ DWORD, LPDDCOLORKEY) PURE; + STDMETHOD(SetOverlayPosition)(THIS_ LONG, LONG ) PURE; + STDMETHOD(SetPalette)(THIS_ LPDIRECTDRAWPALETTE) PURE; + STDMETHOD(Unlock)(THIS_ LPVOID) PURE; + STDMETHOD(UpdateOverlay)(THIS_ LPRECT, LPDIRECTDRAWSURFACE3,LPRECT,DWORD, LPDDOVERLAYFX) PURE; + STDMETHOD(UpdateOverlayDisplay)(THIS_ DWORD) PURE; + STDMETHOD(UpdateOverlayZOrder)(THIS_ DWORD, LPDIRECTDRAWSURFACE3) PURE; + /*** Added in the v2 interface ***/ + STDMETHOD(GetDDInterface)(THIS_ LPVOID FAR *) PURE; + STDMETHOD(PageLock)(THIS_ DWORD) PURE; + STDMETHOD(PageUnlock)(THIS_ DWORD) PURE; + /*** Added in the V3 interface ***/ + STDMETHOD(SetSurfaceDesc)(THIS_ LPDDSURFACEDESC, DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawSurface3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectDrawSurface3_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawSurface3_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawSurface3_AddAttachedSurface(p,a) (p)->lpVtbl->AddAttachedSurface(p,a) +#define IDirectDrawSurface3_AddOverlayDirtyRect(p,a) (p)->lpVtbl->AddOverlayDirtyRect(p,a) +#define IDirectDrawSurface3_Blt(p,a,b,c,d,e) (p)->lpVtbl->Blt(p,a,b,c,d,e) +#define IDirectDrawSurface3_BltBatch(p,a,b,c) (p)->lpVtbl->BltBatch(p,a,b,c) +#define IDirectDrawSurface3_BltFast(p,a,b,c,d,e) (p)->lpVtbl->BltFast(p,a,b,c,d,e) +#define IDirectDrawSurface3_DeleteAttachedSurface(p,a,b) (p)->lpVtbl->DeleteAttachedSurface(p,a,b) +#define IDirectDrawSurface3_EnumAttachedSurfaces(p,a,b) (p)->lpVtbl->EnumAttachedSurfaces(p,a,b) +#define IDirectDrawSurface3_EnumOverlayZOrders(p,a,b,c) (p)->lpVtbl->EnumOverlayZOrders(p,a,b,c) +#define IDirectDrawSurface3_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b) +#define IDirectDrawSurface3_GetAttachedSurface(p,a,b) (p)->lpVtbl->GetAttachedSurface(p,a,b) +#define IDirectDrawSurface3_GetBltStatus(p,a) (p)->lpVtbl->GetBltStatus(p,a) +#define IDirectDrawSurface3_GetCaps(p,b) (p)->lpVtbl->GetCaps(p,b) +#define IDirectDrawSurface3_GetClipper(p,a) (p)->lpVtbl->GetClipper(p,a) +#define IDirectDrawSurface3_GetColorKey(p,a,b) (p)->lpVtbl->GetColorKey(p,a,b) +#define IDirectDrawSurface3_GetDC(p,a) (p)->lpVtbl->GetDC(p,a) +#define IDirectDrawSurface3_GetFlipStatus(p,a) (p)->lpVtbl->GetFlipStatus(p,a) +#define IDirectDrawSurface3_GetOverlayPosition(p,a,b) (p)->lpVtbl->GetOverlayPosition(p,a,b) +#define IDirectDrawSurface3_GetPalette(p,a) (p)->lpVtbl->GetPalette(p,a) +#define IDirectDrawSurface3_GetPixelFormat(p,a) (p)->lpVtbl->GetPixelFormat(p,a) +#define IDirectDrawSurface3_GetSurfaceDesc(p,a) (p)->lpVtbl->GetSurfaceDesc(p,a) +#define IDirectDrawSurface3_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectDrawSurface3_IsLost(p) (p)->lpVtbl->IsLost(p) +#define IDirectDrawSurface3_Lock(p,a,b,c,d) (p)->lpVtbl->Lock(p,a,b,c,d) +#define IDirectDrawSurface3_ReleaseDC(p,a) (p)->lpVtbl->ReleaseDC(p,a) +#define IDirectDrawSurface3_Restore(p) (p)->lpVtbl->Restore(p) +#define IDirectDrawSurface3_SetClipper(p,a) (p)->lpVtbl->SetClipper(p,a) +#define IDirectDrawSurface3_SetColorKey(p,a,b) (p)->lpVtbl->SetColorKey(p,a,b) +#define IDirectDrawSurface3_SetOverlayPosition(p,a,b) (p)->lpVtbl->SetOverlayPosition(p,a,b) +#define IDirectDrawSurface3_SetPalette(p,a) (p)->lpVtbl->SetPalette(p,a) +#define IDirectDrawSurface3_Unlock(p,b) (p)->lpVtbl->Unlock(p,b) +#define IDirectDrawSurface3_UpdateOverlay(p,a,b,c,d,e) (p)->lpVtbl->UpdateOverlay(p,a,b,c,d,e) +#define IDirectDrawSurface3_UpdateOverlayDisplay(p,a) (p)->lpVtbl->UpdateOverlayDisplay(p,a) +#define IDirectDrawSurface3_UpdateOverlayZOrder(p,a,b) (p)->lpVtbl->UpdateOverlayZOrder(p,a,b) +#define IDirectDrawSurface3_GetDDInterface(p,a) (p)->lpVtbl->GetDDInterface(p,a) +#define IDirectDrawSurface3_PageLock(p,a) (p)->lpVtbl->PageLock(p,a) +#define IDirectDrawSurface3_PageUnlock(p,a) (p)->lpVtbl->PageUnlock(p,a) +#define IDirectDrawSurface3_SetSurfaceDesc(p,a,b) (p)->lpVtbl->SetSurfaceDesc(p,a,b) +#else +#define IDirectDrawSurface3_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectDrawSurface3_AddRef(p) (p)->AddRef() +#define IDirectDrawSurface3_Release(p) (p)->Release() +#define IDirectDrawSurface3_AddAttachedSurface(p,a) (p)->AddAttachedSurface(a) +#define IDirectDrawSurface3_AddOverlayDirtyRect(p,a) (p)->AddOverlayDirtyRect(a) +#define IDirectDrawSurface3_Blt(p,a,b,c,d,e) (p)->Blt(a,b,c,d,e) +#define IDirectDrawSurface3_BltBatch(p,a,b,c) (p)->BltBatch(a,b,c) +#define IDirectDrawSurface3_BltFast(p,a,b,c,d,e) (p)->BltFast(a,b,c,d,e) +#define IDirectDrawSurface3_DeleteAttachedSurface(p,a,b) (p)->DeleteAttachedSurface(a,b) +#define IDirectDrawSurface3_EnumAttachedSurfaces(p,a,b) (p)->EnumAttachedSurfaces(a,b) +#define IDirectDrawSurface3_EnumOverlayZOrders(p,a,b,c) (p)->EnumOverlayZOrders(a,b,c) +#define IDirectDrawSurface3_Flip(p,a,b) (p)->Flip(a,b) +#define IDirectDrawSurface3_GetAttachedSurface(p,a,b) (p)->GetAttachedSurface(a,b) +#define IDirectDrawSurface3_GetBltStatus(p,a) (p)->GetBltStatus(a) +#define IDirectDrawSurface3_GetCaps(p,b) (p)->GetCaps(b) +#define IDirectDrawSurface3_GetClipper(p,a) (p)->GetClipper(a) +#define IDirectDrawSurface3_GetColorKey(p,a,b) (p)->GetColorKey(a,b) +#define IDirectDrawSurface3_GetDC(p,a) (p)->GetDC(a) +#define IDirectDrawSurface3_GetFlipStatus(p,a) (p)->GetFlipStatus(a) +#define IDirectDrawSurface3_GetOverlayPosition(p,a,b) (p)->GetOverlayPosition(a,b) +#define IDirectDrawSurface3_GetPalette(p,a) (p)->GetPalette(a) +#define IDirectDrawSurface3_GetPixelFormat(p,a) (p)->GetPixelFormat(a) +#define IDirectDrawSurface3_GetSurfaceDesc(p,a) (p)->GetSurfaceDesc(a) +#define IDirectDrawSurface3_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectDrawSurface3_IsLost(p) (p)->IsLost() +#define IDirectDrawSurface3_Lock(p,a,b,c,d) (p)->Lock(a,b,c,d) +#define IDirectDrawSurface3_ReleaseDC(p,a) (p)->ReleaseDC(a) +#define IDirectDrawSurface3_Restore(p) (p)->Restore() +#define IDirectDrawSurface3_SetClipper(p,a) (p)->SetClipper(a) +#define IDirectDrawSurface3_SetColorKey(p,a,b) (p)->SetColorKey(a,b) +#define IDirectDrawSurface3_SetOverlayPosition(p,a,b) (p)->SetOverlayPosition(a,b) +#define IDirectDrawSurface3_SetPalette(p,a) (p)->SetPalette(a) +#define IDirectDrawSurface3_Unlock(p,b) (p)->Unlock(b) +#define IDirectDrawSurface3_UpdateOverlay(p,a,b,c,d,e) (p)->UpdateOverlay(a,b,c,d,e) +#define IDirectDrawSurface3_UpdateOverlayDisplay(p,a) (p)->UpdateOverlayDisplay(a) +#define IDirectDrawSurface3_UpdateOverlayZOrder(p,a,b) (p)->UpdateOverlayZOrder(a,b) +#define IDirectDrawSurface3_GetDDInterface(p,a) (p)->GetDDInterface(a) +#define IDirectDrawSurface3_PageLock(p,a) (p)->PageLock(a) +#define IDirectDrawSurface3_PageUnlock(p,a) (p)->PageUnlock(a) +#define IDirectDrawSurface3_SetSurfaceDesc(p,a,b) (p)->SetSurfaceDesc(a,b) +#endif + +/* + * IDirectDrawColorControl + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDrawColorControl +DECLARE_INTERFACE_( IDirectDrawColorControl, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawColorControl methods ***/ + STDMETHOD(GetColorControls)(THIS_ LPDDCOLORCONTROL) PURE; + STDMETHOD(SetColorControls)(THIS_ LPDDCOLORCONTROL) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectDrawColorControl_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IDirectDrawColorControl_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectDrawColorControl_Release(p) (p)->lpVtbl->Release(p) +#define IDirectDrawColorControl_GetColorControls(p, a) (p)->lpVtbl->GetColorControls(p, a) +#define IDirectDrawColorControl_SetColorControls(p, a) (p)->lpVtbl->SetColorControls(p, a) +#else +#define IDirectDrawColorControl_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IDirectDrawColorControl_AddRef(p) (p)->AddRef() +#define IDirectDrawColorControl_Release(p) (p)->Release() +#define IDirectDrawColorControl_GetColorControls(p, a) (p)->GetColorControls(a) +#define IDirectDrawColorControl_SetColorControls(p, a) (p)->SetColorControls(a) +#endif + +#endif + + + +#endif + + +/* + * DDSURFACEDESC + */ +typedef struct _DDSURFACEDESC +{ + DWORD dwSize; // size of the DDSURFACEDESC structure + DWORD dwFlags; // determines what fields are valid + DWORD dwHeight; // height of surface to be created + DWORD dwWidth; // width of input surface + union + { + LONG lPitch; // distance to start of next line (return value only) + DWORD dwLinearSize; // Formless late-allocated optimized surface size + }; + DWORD dwBackBufferCount; // number of back buffers requested + union + { + DWORD dwMipMapCount; // number of mip-map levels requested + DWORD dwZBufferBitDepth; // depth of Z buffer requested + DWORD dwRefreshRate; // refresh rate (used when display mode is described) + }; + DWORD dwAlphaBitDepth; // depth of alpha buffer requested + DWORD dwReserved; // reserved + LPVOID lpSurface; // pointer to the associated surface memory + DDCOLORKEY ddckCKDestOverlay; // color key for destination overlay use + DDCOLORKEY ddckCKDestBlt; // color key for destination blt use + DDCOLORKEY ddckCKSrcOverlay; // color key for source overlay use + DDCOLORKEY ddckCKSrcBlt; // color key for source blt use + DDPIXELFORMAT ddpfPixelFormat; // pixel format description of the surface + DDSCAPS ddsCaps; // direct draw surface capabilities +} DDSURFACEDESC; + +/* + * ddsCaps field is valid. + */ +#define DDSD_CAPS 0x00000001l // default + +/* + * dwHeight field is valid. + */ +#define DDSD_HEIGHT 0x00000002l + +/* + * dwWidth field is valid. + */ +#define DDSD_WIDTH 0x00000004l + +/* + * lPitch is valid. + */ +#define DDSD_PITCH 0x00000008l + +/* + * dwBackBufferCount is valid. + */ +#define DDSD_BACKBUFFERCOUNT 0x00000020l + +/* + * dwZBufferBitDepth is valid. + */ +#define DDSD_ZBUFFERBITDEPTH 0x00000040l + +/* + * dwAlphaBitDepth is valid. + */ +#define DDSD_ALPHABITDEPTH 0x00000080l + + +/* + * lpSurface is valid. + */ +#define DDSD_LPSURFACE 0x00000800l + +/* + * ddpfPixelFormat is valid. + */ +#define DDSD_PIXELFORMAT 0x00001000l + +/* + * ddckCKDestOverlay is valid. + */ +#define DDSD_CKDESTOVERLAY 0x00002000l + +/* + * ddckCKDestBlt is valid. + */ +#define DDSD_CKDESTBLT 0x00004000l + +/* + * ddckCKSrcOverlay is valid. + */ +#define DDSD_CKSRCOVERLAY 0x00008000l + +/* + * ddckCKSrcBlt is valid. + */ +#define DDSD_CKSRCBLT 0x00010000l + +/* + * dwMipMapCount is valid. + */ +#define DDSD_MIPMAPCOUNT 0x00020000l + + /* + * dwRefreshRate is valid + */ +#define DDSD_REFRESHRATE 0x00040000l + +/* + * dwLinearSize is valid + */ +#define DDSD_LINEARSIZE 0x00080000l + +/* + * All input fields are valid. + */ +#define DDSD_ALL 0x000ff9eel + + +/* + * DDCOLORCONTROL + */ +typedef struct _DDCOLORCONTROL +{ + DWORD dwSize; + DWORD dwFlags; + LONG lBrightness; + LONG lContrast; + LONG lHue; + LONG lSaturation; + LONG lSharpness; + LONG lGamma; + LONG lColorEnable; + DWORD dwReserved1; +} DDCOLORCONTROL; + + +/* + * lBrightness field is valid. + */ +#define DDCOLOR_BRIGHTNESS 0x00000001l + +/* + * lContrast field is valid. + */ +#define DDCOLOR_CONTRAST 0x00000002l + +/* + * lHue field is valid. + */ +#define DDCOLOR_HUE 0x00000004l + +/* + * lSaturation field is valid. + */ +#define DDCOLOR_SATURATION 0x00000008l + +/* + * lSharpness field is valid. + */ +#define DDCOLOR_SHARPNESS 0x00000010l + +/* + * lGamma field is valid. + */ +#define DDCOLOR_GAMMA 0x00000020l + +/* + * lColorEnable field is valid. + */ +#define DDCOLOR_COLORENABLE 0x00000040l + + + +/*============================================================================ + * + * Direct Draw Capability Flags + * + * These flags are used to describe the capabilities of a given Surface. + * All flags are bit flags. + * + *==========================================================================*/ + +/**************************************************************************** + * + * DIRECTDRAWSURFACE CAPABILITY FLAGS + * + ****************************************************************************/ + +/* + * This bit is reserved. It should not be specified. + */ +#define DDSCAPS_RESERVED1 0x00000001l + +/* + * Indicates that this surface contains alpha-only information. + * (To determine if a surface is RGBA/YUVA, the pixel format must be + * interrogated.) + */ +#define DDSCAPS_ALPHA 0x00000002l + +/* + * Indicates that this surface is a backbuffer. It is generally + * set by CreateSurface when the DDSCAPS_FLIP capability bit is set. + * It indicates that this surface is THE back buffer of a surface + * flipping structure. DirectDraw supports N surfaces in a + * surface flipping structure. Only the surface that immediately + * precedeces the DDSCAPS_FRONTBUFFER has this capability bit set. + * The other surfaces are identified as back buffers by the presence + * of the DDSCAPS_FLIP capability, their attachment order, and the + * absence of the DDSCAPS_FRONTBUFFER and DDSCAPS_BACKBUFFER + * capabilities. The bit is sent to CreateSurface when a standalone + * back buffer is being created. This surface could be attached to + * a front buffer and/or back buffers to form a flipping surface + * structure after the CreateSurface call. See AddAttachments for + * a detailed description of the behaviors in this case. + */ +#define DDSCAPS_BACKBUFFER 0x00000004l + +/* + * Indicates a complex surface structure is being described. A + * complex surface structure results in the creation of more than + * one surface. The additional surfaces are attached to the root + * surface. The complex structure can only be destroyed by + * destroying the root. + */ +#define DDSCAPS_COMPLEX 0x00000008l + +/* + * Indicates that this surface is a part of a surface flipping structure. + * When it is passed to CreateSurface the DDSCAPS_FRONTBUFFER and + * DDSCAP_BACKBUFFER bits are not set. They are set by CreateSurface + * on the resulting creations. The dwBackBufferCount field in the + * DDSURFACEDESC structure must be set to at least 1 in order for + * the CreateSurface call to succeed. The DDSCAPS_COMPLEX capability + * must always be set with creating multiple surfaces through CreateSurface. + */ +#define DDSCAPS_FLIP 0x00000010l + +/* + * Indicates that this surface is THE front buffer of a surface flipping + * structure. It is generally set by CreateSurface when the DDSCAPS_FLIP + * capability bit is set. + * If this capability is sent to CreateSurface then a standalonw front buffer + * is created. This surface will not have the DDSCAPS_FLIP capability. + * It can be attached to other back buffers to form a flipping structure. + * See AddAttachments for a detailed description of the behaviors in this + * case. + */ +#define DDSCAPS_FRONTBUFFER 0x00000020l + +/* + * Indicates that this surface is any offscreen surface that is not an overlay, + * texture, zbuffer, front buffer, back buffer, or alpha surface. It is used + * to identify plain vanilla surfaces. + */ +#define DDSCAPS_OFFSCREENPLAIN 0x00000040l + +/* + * Indicates that this surface is an overlay. It may or may not be directly visible + * depending on whether or not it is currently being overlayed onto the primary + * surface. DDSCAPS_VISIBLE can be used to determine whether or not it is being + * overlayed at the moment. + */ +#define DDSCAPS_OVERLAY 0x00000080l + +/* + * Indicates that unique DirectDrawPalette objects can be created and + * attached to this surface. + */ +#define DDSCAPS_PALETTE 0x00000100l + +/* + * Indicates that this surface is the primary surface. The primary + * surface represents what the user is seeing at the moment. + */ +#define DDSCAPS_PRIMARYSURFACE 0x00000200l + +/* + * Indicates that this surface is the primary surface for the left eye. + * The primary surface for the left eye represents what the user is seeing + * at the moment with the users left eye. When this surface is created the + * DDSCAPS_PRIMARYSURFACE represents what the user is seeing with the users + * right eye. + */ +#define DDSCAPS_PRIMARYSURFACELEFT 0x00000400l + +/* + * Indicates that this surface memory was allocated in system memory + */ +#define DDSCAPS_SYSTEMMEMORY 0x00000800l + +/* + * Indicates that this surface can be used as a 3D texture. It does not + * indicate whether or not the surface is being used for that purpose. + */ +#define DDSCAPS_TEXTURE 0x00001000l + +/* + * Indicates that a surface may be a destination for 3D rendering. This + * bit must be set in order to query for a Direct3D Device Interface + * from this surface. + */ +#define DDSCAPS_3DDEVICE 0x00002000l + +/* + * Indicates that this surface exists in video memory. + */ +#define DDSCAPS_VIDEOMEMORY 0x00004000l + +/* + * Indicates that changes made to this surface are immediately visible. + * It is always set for the primary surface and is set for overlays while + * they are being overlayed and texture maps while they are being textured. + */ +#define DDSCAPS_VISIBLE 0x00008000l + +/* + * Indicates that only writes are permitted to the surface. Read accesses + * from the surface may or may not generate a protection fault, but the + * results of a read from this surface will not be meaningful. READ ONLY. + */ +#define DDSCAPS_WRITEONLY 0x00010000l + +/* + * Indicates that this surface is a z buffer. A z buffer does not contain + * displayable information. Instead it contains bit depth information that is + * used to determine which pixels are visible and which are obscured. + */ +#define DDSCAPS_ZBUFFER 0x00020000l + +/* + * Indicates surface will have a DC associated long term + */ +#define DDSCAPS_OWNDC 0x00040000l + +/* + * Indicates surface should be able to receive live video + */ +#define DDSCAPS_LIVEVIDEO 0x00080000l + +/* + * Indicates surface should be able to have a stream decompressed + * to it by the hardware. + */ +#define DDSCAPS_HWCODEC 0x00100000l + +/* + * Surface is a ModeX surface. + * + */ +#define DDSCAPS_MODEX 0x00200000l + +/* + * Indicates surface is one level of a mip-map. This surface will + * be attached to other DDSCAPS_MIPMAP surfaces to form the mip-map. + * This can be done explicitly, by creating a number of surfaces and + * attaching them with AddAttachedSurface or by implicitly by CreateSurface. + * If this bit is set then DDSCAPS_TEXTURE must also be set. + */ +#define DDSCAPS_MIPMAP 0x00400000l + +/* + * This bit is reserved. It should not be specified. + */ +#define DDSCAPS_RESERVED2 0x00800000l + + +/* + * Indicates that memory for the surface is not allocated until the surface + * is loaded (via the Direct3D texture Load() function). + */ +#define DDSCAPS_ALLOCONLOAD 0x04000000l + +/* + * Indicates that the surface will recieve data from a video port. + */ +#define DDSCAPS_VIDEOPORT 0x08000000l + +/* + * Indicates that a video memory surface is resident in true, local video + * memory rather than non-local video memory. If this flag is specified then + * so must DDSCAPS_VIDEOMEMORY. This flag is mutually exclusive with + * DDSCAPS_NONLOCALVIDMEM. + */ +#define DDSCAPS_LOCALVIDMEM 0x10000000l + +/* + * Indicates that a video memory surface is resident in non-local video + * memory rather than true, local video memory. If this flag is specified + * then so must DDSCAPS_VIDEOMEMORY. This flag is mutually exclusive with + * DDSCAPS_LOCALVIDMEM. + */ +#define DDSCAPS_NONLOCALVIDMEM 0x20000000l + +/* + * Indicates that this surface is a standard VGA mode surface, and not a + * ModeX surface. (This flag will never be set in combination with the + * DDSCAPS_MODEX flag). + */ +#define DDSCAPS_STANDARDVGAMODE 0x40000000l + +/* + * Indicates that this surface will be an optimized surface. This flag is + * currently only valid in conjunction with the DDSCAPS_TEXTURE flag. The surface + * will be created without any underlying video memory until loaded. + */ +#define DDSCAPS_OPTIMIZED 0x80000000l + + + + /**************************************************************************** + * + * DIRECTDRAW DRIVER CAPABILITY FLAGS + * + ****************************************************************************/ + +/* + * Display hardware has 3D acceleration. + */ +#define DDCAPS_3D 0x00000001l + +/* + * Indicates that DirectDraw will support only dest rectangles that are aligned + * on DIRECTDRAWCAPS.dwAlignBoundaryDest boundaries of the surface, respectively. + * READ ONLY. + */ +#define DDCAPS_ALIGNBOUNDARYDEST 0x00000002l + +/* + * Indicates that DirectDraw will support only source rectangles whose sizes in + * BYTEs are DIRECTDRAWCAPS.dwAlignSizeDest multiples, respectively. READ ONLY. + */ +#define DDCAPS_ALIGNSIZEDEST 0x00000004l +/* + * Indicates that DirectDraw will support only source rectangles that are aligned + * on DIRECTDRAWCAPS.dwAlignBoundarySrc boundaries of the surface, respectively. + * READ ONLY. + */ +#define DDCAPS_ALIGNBOUNDARYSRC 0x00000008l + +/* + * Indicates that DirectDraw will support only source rectangles whose sizes in + * BYTEs are DIRECTDRAWCAPS.dwAlignSizeSrc multiples, respectively. READ ONLY. + */ +#define DDCAPS_ALIGNSIZESRC 0x00000010l + +/* + * Indicates that DirectDraw will create video memory surfaces that have a stride + * alignment equal to DIRECTDRAWCAPS.dwAlignStride. READ ONLY. + */ +#define DDCAPS_ALIGNSTRIDE 0x00000020l + +/* + * Display hardware is capable of blt operations. + */ +#define DDCAPS_BLT 0x00000040l + +/* + * Display hardware is capable of asynchronous blt operations. + */ +#define DDCAPS_BLTQUEUE 0x00000080l + +/* + * Display hardware is capable of color space conversions during the blt operation. + */ +#define DDCAPS_BLTFOURCC 0x00000100l + +/* + * Display hardware is capable of stretching during blt operations. + */ +#define DDCAPS_BLTSTRETCH 0x00000200l + +/* + * Display hardware is shared with GDI. + */ +#define DDCAPS_GDI 0x00000400l + +/* + * Display hardware can overlay. + */ +#define DDCAPS_OVERLAY 0x00000800l + +/* + * Set if display hardware supports overlays but can not clip them. + */ +#define DDCAPS_OVERLAYCANTCLIP 0x00001000l + +/* + * Indicates that overlay hardware is capable of color space conversions during + * the overlay operation. + */ +#define DDCAPS_OVERLAYFOURCC 0x00002000l + +/* + * Indicates that stretching can be done by the overlay hardware. + */ +#define DDCAPS_OVERLAYSTRETCH 0x00004000l + +/* + * Indicates that unique DirectDrawPalettes can be created for DirectDrawSurfaces + * other than the primary surface. + */ +#define DDCAPS_PALETTE 0x00008000l + +/* + * Indicates that palette changes can be syncd with the veritcal refresh. + */ +#define DDCAPS_PALETTEVSYNC 0x00010000l + +/* + * Display hardware can return the current scan line. + */ +#define DDCAPS_READSCANLINE 0x00020000l + +/* + * Display hardware has stereo vision capabilities. DDSCAPS_PRIMARYSURFACELEFT + * can be created. + */ +#define DDCAPS_STEREOVIEW 0x00040000l + +/* + * Display hardware is capable of generating a vertical blank interrupt. + */ +#define DDCAPS_VBI 0x00080000l + +/* + * Supports the use of z buffers with blt operations. + */ +#define DDCAPS_ZBLTS 0x00100000l + +/* + * Supports Z Ordering of overlays. + */ +#define DDCAPS_ZOVERLAYS 0x00200000l + +/* + * Supports color key + */ +#define DDCAPS_COLORKEY 0x00400000l + +/* + * Supports alpha surfaces + */ +#define DDCAPS_ALPHA 0x00800000l + +/* + * colorkey is hardware assisted(DDCAPS_COLORKEY will also be set) + */ +#define DDCAPS_COLORKEYHWASSIST 0x01000000l + +/* + * no hardware support at all + */ +#define DDCAPS_NOHARDWARE 0x02000000l + +/* + * Display hardware is capable of color fill with bltter + */ +#define DDCAPS_BLTCOLORFILL 0x04000000l + +/* + * Display hardware is bank switched, and potentially very slow at + * random access to VRAM. + */ +#define DDCAPS_BANKSWITCHED 0x08000000l + +/* + * Display hardware is capable of depth filling Z-buffers with bltter + */ +#define DDCAPS_BLTDEPTHFILL 0x10000000l + +/* + * Display hardware is capable of clipping while bltting. + */ +#define DDCAPS_CANCLIP 0x20000000l + +/* + * Display hardware is capable of clipping while stretch bltting. + */ +#define DDCAPS_CANCLIPSTRETCHED 0x40000000l + +/* + * Display hardware is capable of bltting to or from system memory + */ +#define DDCAPS_CANBLTSYSMEM 0x80000000l + + + /**************************************************************************** + * + * MORE DIRECTDRAW DRIVER CAPABILITY FLAGS (dwCaps2) + * + ****************************************************************************/ + +/* + * Display hardware is certified + */ +#define DDCAPS2_CERTIFIED 0x00000001l + +/* + * Driver cannot interleave 2D operations (lock and blt) to surfaces with + * Direct3D rendering operations between calls to BeginScene() and EndScene() + */ +#define DDCAPS2_NO2DDURING3DSCENE 0x00000002l + +/* + * Display hardware contains a video port + */ +#define DDCAPS2_VIDEOPORT 0x00000004l + +/* + * The overlay can be automatically flipped according to the video port + * VSYNCs, providing automatic doubled buffered display of video port + * data using an overlay + */ +#define DDCAPS2_AUTOFLIPOVERLAY 0x00000008l + +/* + * Overlay can display each field of interlaced data individually while + * it is interleaved in memory without causing jittery artifacts. + */ +#define DDCAPS2_CANBOBINTERLEAVED 0x00000010l + +/* + * Overlay can display each field of interlaced data individually while + * it is not interleaved in memory without causing jittery artifacts. + */ +#define DDCAPS2_CANBOBNONINTERLEAVED 0x00000020l + +/* + * The overlay surface contains color controls (brightness, sharpness, etc.) + */ +#define DDCAPS2_COLORCONTROLOVERLAY 0x00000040l + +/* + * The primary surface contains color controls (gamma, etc.) + */ +#define DDCAPS2_COLORCONTROLPRIMARY 0x00000080l + +/* + * RGBZ -> RGB supported for 16:16 RGB:Z + */ +#define DDCAPS2_CANDROPZ16BIT 0x00000100l + +/* + * Driver supports non-local video memory. + */ +#define DDCAPS2_NONLOCALVIDMEM 0x00000200l + +/* + * Dirver supports non-local video memory but has different capabilities for + * non-local video memory surfaces. If this bit is set then so must + * DDCAPS2_NONLOCALVIDMEM. + */ +#define DDCAPS2_NONLOCALVIDMEMCAPS 0x00000400l + +/* + * Driver neither requires nor prefers surfaces to be pagelocked when performing + * blts involving system memory surfaces + */ +#define DDCAPS2_NOPAGELOCKREQUIRED 0x00000800l + +/* + * Driver can create surfaces which are wider than the primary surface + */ +#define DDCAPS2_WIDESURFACES 0x00001000l + +/* + * Driver supports bob using software without using a video port + */ +#define DDCAPS2_CANFLIPODDEVEN 0x00002000l + +/**************************************************************************** + * + * DIRECTDRAW FX ALPHA CAPABILITY FLAGS + * + ****************************************************************************/ + +/* + * Supports alpha blending around the edge of a source color keyed surface. + * For Blt. + */ +#define DDFXALPHACAPS_BLTALPHAEDGEBLEND 0x00000001l + +/* + * Supports alpha information in the pixel format. The bit depth of alpha + * information in the pixel format can be 1,2,4, or 8. The alpha value becomes + * more opaque as the alpha value increases. (0 is transparent.) + * For Blt. + */ +#define DDFXALPHACAPS_BLTALPHAPIXELS 0x00000002l + +/* + * Supports alpha information in the pixel format. The bit depth of alpha + * information in the pixel format can be 1,2,4, or 8. The alpha value + * becomes more transparent as the alpha value increases. (0 is opaque.) + * This flag can only be set if DDCAPS_ALPHA is set. + * For Blt. + */ +#define DDFXALPHACAPS_BLTALPHAPIXELSNEG 0x00000004l + +/* + * Supports alpha only surfaces. The bit depth of an alpha only surface can be + * 1,2,4, or 8. The alpha value becomes more opaque as the alpha value increases. + * (0 is transparent.) + * For Blt. + */ +#define DDFXALPHACAPS_BLTALPHASURFACES 0x00000008l + +/* + * The depth of the alpha channel data can range can be 1,2,4, or 8. + * The NEG suffix indicates that this alpha channel becomes more transparent + * as the alpha value increases. (0 is opaque.) This flag can only be set if + * DDCAPS_ALPHA is set. + * For Blt. + */ +#define DDFXALPHACAPS_BLTALPHASURFACESNEG 0x00000010l + +/* + * Supports alpha blending around the edge of a source color keyed surface. + * For Overlays. + */ +#define DDFXALPHACAPS_OVERLAYALPHAEDGEBLEND 0x00000020l + +/* + * Supports alpha information in the pixel format. The bit depth of alpha + * information in the pixel format can be 1,2,4, or 8. The alpha value becomes + * more opaque as the alpha value increases. (0 is transparent.) + * For Overlays. + */ +#define DDFXALPHACAPS_OVERLAYALPHAPIXELS 0x00000040l + +/* + * Supports alpha information in the pixel format. The bit depth of alpha + * information in the pixel format can be 1,2,4, or 8. The alpha value + * becomes more transparent as the alpha value increases. (0 is opaque.) + * This flag can only be set if DDCAPS_ALPHA is set. + * For Overlays. + */ +#define DDFXALPHACAPS_OVERLAYALPHAPIXELSNEG 0x00000080l + +/* + * Supports alpha only surfaces. The bit depth of an alpha only surface can be + * 1,2,4, or 8. The alpha value becomes more opaque as the alpha value increases. + * (0 is transparent.) + * For Overlays. + */ +#define DDFXALPHACAPS_OVERLAYALPHASURFACES 0x00000100l + +/* + * The depth of the alpha channel data can range can be 1,2,4, or 8. + * The NEG suffix indicates that this alpha channel becomes more transparent + * as the alpha value increases. (0 is opaque.) This flag can only be set if + * DDCAPS_ALPHA is set. + * For Overlays. + */ +#define DDFXALPHACAPS_OVERLAYALPHASURFACESNEG 0x00000200l + +/**************************************************************************** + * + * DIRECTDRAW FX CAPABILITY FLAGS + * + ****************************************************************************/ + +/* + * Uses arithmetic operations to stretch and shrink surfaces during blt + * rather than pixel doubling techniques. Along the Y axis. + */ +#define DDFXCAPS_BLTARITHSTRETCHY 0x00000020l + +/* + * Uses arithmetic operations to stretch during blt + * rather than pixel doubling techniques. Along the Y axis. Only + * works for x1, x2, etc. + */ +#define DDFXCAPS_BLTARITHSTRETCHYN 0x00000010l + +/* + * Supports mirroring left to right in blt. + */ +#define DDFXCAPS_BLTMIRRORLEFTRIGHT 0x00000040l + +/* + * Supports mirroring top to bottom in blt. + */ +#define DDFXCAPS_BLTMIRRORUPDOWN 0x00000080l + +/* + * Supports arbitrary rotation for blts. + */ +#define DDFXCAPS_BLTROTATION 0x00000100l + +/* + * Supports 90 degree rotations for blts. + */ +#define DDFXCAPS_BLTROTATION90 0x00000200l + +/* + * DirectDraw supports arbitrary shrinking of a surface along the + * x axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSHRINKX 0x00000400l + +/* + * DirectDraw supports integer shrinking (1x,2x,) of a surface + * along the x axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSHRINKXN 0x00000800l + +/* + * DirectDraw supports arbitrary shrinking of a surface along the + * y axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSHRINKY 0x00001000l + +/* + * DirectDraw supports integer shrinking (1x,2x,) of a surface + * along the y axis (vertical direction) for blts. + */ +#define DDFXCAPS_BLTSHRINKYN 0x00002000l + +/* + * DirectDraw supports arbitrary stretching of a surface along the + * x axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSTRETCHX 0x00004000l + +/* + * DirectDraw supports integer stretching (1x,2x,) of a surface + * along the x axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSTRETCHXN 0x00008000l + +/* + * DirectDraw supports arbitrary stretching of a surface along the + * y axis (horizontal direction) for blts. + */ +#define DDFXCAPS_BLTSTRETCHY 0x00010000l + +/* + * DirectDraw supports integer stretching (1x,2x,) of a surface + * along the y axis (vertical direction) for blts. + */ +#define DDFXCAPS_BLTSTRETCHYN 0x00020000l + +/* + * Uses arithmetic operations to stretch and shrink surfaces during + * overlay rather than pixel doubling techniques. Along the Y axis + * for overlays. + */ +#define DDFXCAPS_OVERLAYARITHSTRETCHY 0x00040000l + +/* + * Uses arithmetic operations to stretch surfaces during + * overlay rather than pixel doubling techniques. Along the Y axis + * for overlays. Only works for x1, x2, etc. + */ +#define DDFXCAPS_OVERLAYARITHSTRETCHYN 0x00000008l + +/* + * DirectDraw supports arbitrary shrinking of a surface along the + * x axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSHRINKX 0x00080000l + +/* + * DirectDraw supports integer shrinking (1x,2x,) of a surface + * along the x axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSHRINKXN 0x00100000l + +/* + * DirectDraw supports arbitrary shrinking of a surface along the + * y axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSHRINKY 0x00200000l + +/* + * DirectDraw supports integer shrinking (1x,2x,) of a surface + * along the y axis (vertical direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSHRINKYN 0x00400000l + +/* + * DirectDraw supports arbitrary stretching of a surface along the + * x axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSTRETCHX 0x00800000l + +/* + * DirectDraw supports integer stretching (1x,2x,) of a surface + * along the x axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSTRETCHXN 0x01000000l + +/* + * DirectDraw supports arbitrary stretching of a surface along the + * y axis (horizontal direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSTRETCHY 0x02000000l + +/* + * DirectDraw supports integer stretching (1x,2x,) of a surface + * along the y axis (vertical direction) for overlays. + */ +#define DDFXCAPS_OVERLAYSTRETCHYN 0x04000000l + +/* + * DirectDraw supports mirroring of overlays across the vertical axis + */ +#define DDFXCAPS_OVERLAYMIRRORLEFTRIGHT 0x08000000l + +/* + * DirectDraw supports mirroring of overlays across the horizontal axis + */ +#define DDFXCAPS_OVERLAYMIRRORUPDOWN 0x10000000l + +/**************************************************************************** + * + * DIRECTDRAW STEREO VIEW CAPABILITIES + * + ****************************************************************************/ + +/* + * The stereo view is accomplished via enigma encoding. + */ +#define DDSVCAPS_ENIGMA 0x00000001l + +/* + * The stereo view is accomplished via high frequency flickering. + */ +#define DDSVCAPS_FLICKER 0x00000002l + +/* + * The stereo view is accomplished via red and blue filters applied + * to the left and right eyes. All images must adapt their colorspaces + * for this process. + */ +#define DDSVCAPS_REDBLUE 0x00000004l + +/* + * The stereo view is accomplished with split screen technology. + */ +#define DDSVCAPS_SPLIT 0x00000008l + +/**************************************************************************** + * + * DIRECTDRAWPALETTE CAPABILITIES + * + ****************************************************************************/ + +/* + * Index is 4 bits. There are sixteen color entries in the palette table. + */ +#define DDPCAPS_4BIT 0x00000001l + +/* + * Index is onto a 8 bit color index. This field is only valid with the + * DDPCAPS_1BIT, DDPCAPS_2BIT or DDPCAPS_4BIT capability and the target + * surface is in 8bpp. Each color entry is one byte long and is an index + * into destination surface's 8bpp palette. + */ +#define DDPCAPS_8BITENTRIES 0x00000002l + +/* + * Index is 8 bits. There are 256 color entries in the palette table. + */ +#define DDPCAPS_8BIT 0x00000004l + +/* + * Indicates that this DIRECTDRAWPALETTE should use the palette color array + * passed into the lpDDColorArray parameter to initialize the DIRECTDRAWPALETTE + * object. + */ +#define DDPCAPS_INITIALIZE 0x00000008l + +/* + * This palette is the one attached to the primary surface. Changing this + * table has immediate effect on the display unless DDPSETPAL_VSYNC is specified + * and supported. + */ +#define DDPCAPS_PRIMARYSURFACE 0x00000010l + +/* + * This palette is the one attached to the primary surface left. Changing + * this table has immediate effect on the display for the left eye unless + * DDPSETPAL_VSYNC is specified and supported. + */ +#define DDPCAPS_PRIMARYSURFACELEFT 0x00000020l + +/* + * This palette can have all 256 entries defined + */ +#define DDPCAPS_ALLOW256 0x00000040l + +/* + * This palette can have modifications to it synced with the monitors + * refresh rate. + */ +#define DDPCAPS_VSYNC 0x00000080l + +/* + * Index is 1 bit. There are two color entries in the palette table. + */ +#define DDPCAPS_1BIT 0x00000100l + +/* + * Index is 2 bit. There are four color entries in the palette table. + */ +#define DDPCAPS_2BIT 0x00000200l + + +/**************************************************************************** + * + * DIRECTDRAWPALETTE SETENTRY CONSTANTS + * + ****************************************************************************/ + + +/**************************************************************************** + * + * DIRECTDRAWPALETTE GETENTRY CONSTANTS + * + ****************************************************************************/ + +/* 0 is the only legal value */ + +/**************************************************************************** + * + * DIRECTDRAWSURFACE SETPALETTE CONSTANTS + * + ****************************************************************************/ + + +/**************************************************************************** + * + * DIRECTDRAW BITDEPTH CONSTANTS + * + * NOTE: These are only used to indicate supported bit depths. These + * are flags only, they are not to be used as an actual bit depth. The + * absolute numbers 1, 2, 4, 8, 16, 24 and 32 are used to indicate actual + * bit depths in a surface or for changing the display mode. + * + ****************************************************************************/ + +/* + * 1 bit per pixel. + */ +#define DDBD_1 0x00004000l + +/* + * 2 bits per pixel. + */ +#define DDBD_2 0x00002000l + +/* + * 4 bits per pixel. + */ +#define DDBD_4 0x00001000l + +/* + * 8 bits per pixel. + */ +#define DDBD_8 0x00000800l + +/* + * 16 bits per pixel. + */ +#define DDBD_16 0x00000400l + +/* + * 24 bits per pixel. + */ +#define DDBD_24 0X00000200l + +/* + * 32 bits per pixel. + */ +#define DDBD_32 0x00000100l + +/**************************************************************************** + * + * DIRECTDRAWSURFACE SET/GET COLOR KEY FLAGS + * + ****************************************************************************/ + +/* + * Set if the structure contains a color space. Not set if the structure + * contains a single color key. + */ +#define DDCKEY_COLORSPACE 0x00000001l + +/* + * Set if the structure specifies a color key or color space which is to be + * used as a destination color key for blt operations. + */ +#define DDCKEY_DESTBLT 0x00000002l + +/* + * Set if the structure specifies a color key or color space which is to be + * used as a destination color key for overlay operations. + */ +#define DDCKEY_DESTOVERLAY 0x00000004l + +/* + * Set if the structure specifies a color key or color space which is to be + * used as a source color key for blt operations. + */ +#define DDCKEY_SRCBLT 0x00000008l + +/* + * Set if the structure specifies a color key or color space which is to be + * used as a source color key for overlay operations. + */ +#define DDCKEY_SRCOVERLAY 0x00000010l + + +/**************************************************************************** + * + * DIRECTDRAW COLOR KEY CAPABILITY FLAGS + * + ****************************************************************************/ + +/* + * Supports transparent blting using a color key to identify the replaceable + * bits of the destination surface for RGB colors. + */ +#define DDCKEYCAPS_DESTBLT 0x00000001l + +/* + * Supports transparent blting using a color space to identify the replaceable + * bits of the destination surface for RGB colors. + */ +#define DDCKEYCAPS_DESTBLTCLRSPACE 0x00000002l + +/* + * Supports transparent blting using a color space to identify the replaceable + * bits of the destination surface for YUV colors. + */ +#define DDCKEYCAPS_DESTBLTCLRSPACEYUV 0x00000004l + +/* + * Supports transparent blting using a color key to identify the replaceable + * bits of the destination surface for YUV colors. + */ +#define DDCKEYCAPS_DESTBLTYUV 0x00000008l + +/* + * Supports overlaying using colorkeying of the replaceable bits of the surface + * being overlayed for RGB colors. + */ +#define DDCKEYCAPS_DESTOVERLAY 0x00000010l + +/* + * Supports a color space as the color key for the destination for RGB colors. + */ +#define DDCKEYCAPS_DESTOVERLAYCLRSPACE 0x00000020l + +/* + * Supports a color space as the color key for the destination for YUV colors. + */ +#define DDCKEYCAPS_DESTOVERLAYCLRSPACEYUV 0x00000040l + +/* + * Supports only one active destination color key value for visible overlay + * surfaces. + */ +#define DDCKEYCAPS_DESTOVERLAYONEACTIVE 0x00000080l + +/* + * Supports overlaying using colorkeying of the replaceable bits of the + * surface being overlayed for YUV colors. + */ +#define DDCKEYCAPS_DESTOVERLAYYUV 0x00000100l + +/* + * Supports transparent blting using the color key for the source with + * this surface for RGB colors. + */ +#define DDCKEYCAPS_SRCBLT 0x00000200l + +/* + * Supports transparent blting using a color space for the source with + * this surface for RGB colors. + */ +#define DDCKEYCAPS_SRCBLTCLRSPACE 0x00000400l + +/* + * Supports transparent blting using a color space for the source with + * this surface for YUV colors. + */ +#define DDCKEYCAPS_SRCBLTCLRSPACEYUV 0x00000800l + +/* + * Supports transparent blting using the color key for the source with + * this surface for YUV colors. + */ +#define DDCKEYCAPS_SRCBLTYUV 0x00001000l + +/* + * Supports overlays using the color key for the source with this + * overlay surface for RGB colors. + */ +#define DDCKEYCAPS_SRCOVERLAY 0x00002000l + +/* + * Supports overlays using a color space as the source color key for + * the overlay surface for RGB colors. + */ +#define DDCKEYCAPS_SRCOVERLAYCLRSPACE 0x00004000l + +/* + * Supports overlays using a color space as the source color key for + * the overlay surface for YUV colors. + */ +#define DDCKEYCAPS_SRCOVERLAYCLRSPACEYUV 0x00008000l + +/* + * Supports only one active source color key value for visible + * overlay surfaces. + */ +#define DDCKEYCAPS_SRCOVERLAYONEACTIVE 0x00010000l + +/* + * Supports overlays using the color key for the source with this + * overlay surface for YUV colors. + */ +#define DDCKEYCAPS_SRCOVERLAYYUV 0x00020000l + +/* + * there are no bandwidth trade-offs for using colorkey with an overlay + */ +#define DDCKEYCAPS_NOCOSTOVERLAY 0x00040000l + + +/**************************************************************************** + * + * DIRECTDRAW PIXELFORMAT FLAGS + * + ****************************************************************************/ + +/* + * The surface has alpha channel information in the pixel format. + */ +#define DDPF_ALPHAPIXELS 0x00000001l + +/* + * The pixel format contains alpha only information + */ +#define DDPF_ALPHA 0x00000002l + +/* + * The FourCC code is valid. + */ +#define DDPF_FOURCC 0x00000004l + +/* + * The surface is 4-bit color indexed. + */ +#define DDPF_PALETTEINDEXED4 0x00000008l + +/* + * The surface is indexed into a palette which stores indices + * into the destination surface's 8-bit palette. + */ +#define DDPF_PALETTEINDEXEDTO8 0x00000010l + +/* + * The surface is 8-bit color indexed. + */ +#define DDPF_PALETTEINDEXED8 0x00000020l + +/* + * The RGB data in the pixel format structure is valid. + */ +#define DDPF_RGB 0x00000040l + +/* + * The surface will accept pixel data in the format specified + * and compress it during the write. + */ +#define DDPF_COMPRESSED 0x00000080l + +/* + * The surface will accept RGB data and translate it during + * the write to YUV data. The format of the data to be written + * will be contained in the pixel format structure. The DDPF_RGB + * flag will be set. + */ +#define DDPF_RGBTOYUV 0x00000100l + +/* + * pixel format is YUV - YUV data in pixel format struct is valid + */ +#define DDPF_YUV 0x00000200l + +/* + * pixel format is a z buffer only surface + */ +#define DDPF_ZBUFFER 0x00000400l + +/* + * The surface is 1-bit color indexed. + */ +#define DDPF_PALETTEINDEXED1 0x00000800l + +/* + * The surface is 2-bit color indexed. + */ +#define DDPF_PALETTEINDEXED2 0x00001000l + +/* + * The surface contains Z information in the pixels + */ +#define DDPF_ZPIXELS 0x00002000l + +/*=========================================================================== + * + * + * DIRECTDRAW CALLBACK FLAGS + * + * + *==========================================================================*/ + +/**************************************************************************** + * + * DIRECTDRAW ENUMSURFACES FLAGS + * + ****************************************************************************/ + +/* + * Enumerate all of the surfaces that meet the search criterion. + */ +#define DDENUMSURFACES_ALL 0x00000001l + +/* + * A search hit is a surface that matches the surface description. + */ +#define DDENUMSURFACES_MATCH 0x00000002l + +/* + * A search hit is a surface that does not match the surface description. + */ +#define DDENUMSURFACES_NOMATCH 0x00000004l + +/* + * Enumerate the first surface that can be created which meets the search criterion. + */ +#define DDENUMSURFACES_CANBECREATED 0x00000008l + +/* + * Enumerate the surfaces that already exist that meet the search criterion. + */ +#define DDENUMSURFACES_DOESEXIST 0x00000010l + + +/**************************************************************************** + * + * DIRECTDRAW SETDISPLAYMODE FLAGS + * + ****************************************************************************/ + +/* + * The desired mode is a standard VGA mode + */ +#define DDSDM_STANDARDVGAMODE 0x00000001l + + + +/**************************************************************************** + * + * DIRECTDRAW ENUMDISPLAYMODES FLAGS + * + ****************************************************************************/ + +/* + * Enumerate Modes with different refresh rates. EnumDisplayModes guarantees + * that a particular mode will be enumerated only once. This flag specifies whether + * the refresh rate is taken into account when determining if a mode is unique. + */ +#define DDEDM_REFRESHRATES 0x00000001l + +/* + * Enumerate VGA modes. Specify this flag if you wish to enumerate supported VGA + * modes such as mode 0x13 in addition to the usual ModeX modes (which are always + * enumerated if the application has previously called SetCooperativeLevel with the + * DDSCL_ALLOWMODEX flag set). + */ +#define DDEDM_STANDARDVGAMODES 0x00000002L + + +/**************************************************************************** + * + * DIRECTDRAW SETCOOPERATIVELEVEL FLAGS + * + ****************************************************************************/ + +/* + * Exclusive mode owner will be responsible for the entire primary surface. + * GDI can be ignored. used with DD + */ +#define DDSCL_FULLSCREEN 0x00000001l + +/* + * allow CTRL_ALT_DEL to work while in fullscreen exclusive mode + */ +#define DDSCL_ALLOWREBOOT 0x00000002l + +/* + * prevents DDRAW from modifying the application window. + * prevents DDRAW from minimize/restore the application window on activation. + */ +#define DDSCL_NOWINDOWCHANGES 0x00000004l + +/* + * app wants to work as a regular Windows application + */ +#define DDSCL_NORMAL 0x00000008l + +/* + * app wants exclusive access + */ +#define DDSCL_EXCLUSIVE 0x00000010l + + +/* + * app can deal with non-windows display modes + */ +#define DDSCL_ALLOWMODEX 0x00000040l + + +/**************************************************************************** + * + * DIRECTDRAW BLT FLAGS + * + ****************************************************************************/ + +/* + * Use the alpha information in the pixel format or the alpha channel surface + * attached to the destination surface as the alpha channel for this blt. + */ +#define DDBLT_ALPHADEST 0x00000001l + +/* + * Use the dwConstAlphaDest field in the DDBLTFX structure as the alpha channel + * for the destination surface for this blt. + */ +#define DDBLT_ALPHADESTCONSTOVERRIDE 0x00000002l + +/* + * The NEG suffix indicates that the destination surface becomes more + * transparent as the alpha value increases. (0 is opaque) + */ +#define DDBLT_ALPHADESTNEG 0x00000004l + +/* + * Use the lpDDSAlphaDest field in the DDBLTFX structure as the alpha + * channel for the destination for this blt. + */ +#define DDBLT_ALPHADESTSURFACEOVERRIDE 0x00000008l + +/* + * Use the dwAlphaEdgeBlend field in the DDBLTFX structure as the alpha channel + * for the edges of the image that border the color key colors. + */ +#define DDBLT_ALPHAEDGEBLEND 0x00000010l + +/* + * Use the alpha information in the pixel format or the alpha channel surface + * attached to the source surface as the alpha channel for this blt. + */ +#define DDBLT_ALPHASRC 0x00000020l + +/* + * Use the dwConstAlphaSrc field in the DDBLTFX structure as the alpha channel + * for the source for this blt. + */ +#define DDBLT_ALPHASRCCONSTOVERRIDE 0x00000040l + +/* + * The NEG suffix indicates that the source surface becomes more transparent + * as the alpha value increases. (0 is opaque) + */ +#define DDBLT_ALPHASRCNEG 0x00000080l + +/* + * Use the lpDDSAlphaSrc field in the DDBLTFX structure as the alpha channel + * for the source for this blt. + */ +#define DDBLT_ALPHASRCSURFACEOVERRIDE 0x00000100l + +/* + * Do this blt asynchronously through the FIFO in the order received. If + * there is no room in the hardware FIFO fail the call. + */ +#define DDBLT_ASYNC 0x00000200l + +/* + * Uses the dwFillColor field in the DDBLTFX structure as the RGB color + * to fill the destination rectangle on the destination surface with. + */ +#define DDBLT_COLORFILL 0x00000400l + +/* + * Uses the dwDDFX field in the DDBLTFX structure to specify the effects + * to use for the blt. + */ +#define DDBLT_DDFX 0x00000800l + +/* + * Uses the dwDDROPS field in the DDBLTFX structure to specify the ROPS + * that are not part of the Win32 API. + */ +#define DDBLT_DDROPS 0x00001000l + +/* + * Use the color key associated with the destination surface. + */ +#define DDBLT_KEYDEST 0x00002000l + +/* + * Use the dckDestColorkey field in the DDBLTFX structure as the color key + * for the destination surface. + */ +#define DDBLT_KEYDESTOVERRIDE 0x00004000l + +/* + * Use the color key associated with the source surface. + */ +#define DDBLT_KEYSRC 0x00008000l + +/* + * Use the dckSrcColorkey field in the DDBLTFX structure as the color key + * for the source surface. + */ +#define DDBLT_KEYSRCOVERRIDE 0x00010000l + +/* + * Use the dwROP field in the DDBLTFX structure for the raster operation + * for this blt. These ROPs are the same as the ones defined in the Win32 API. + */ +#define DDBLT_ROP 0x00020000l + +/* + * Use the dwRotationAngle field in the DDBLTFX structure as the angle + * (specified in 1/100th of a degree) to rotate the surface. + */ +#define DDBLT_ROTATIONANGLE 0x00040000l + +/* + * Z-buffered blt using the z-buffers attached to the source and destination + * surfaces and the dwZBufferOpCode field in the DDBLTFX structure as the + * z-buffer opcode. + */ +#define DDBLT_ZBUFFER 0x00080000l + +/* + * Z-buffered blt using the dwConstDest Zfield and the dwZBufferOpCode field + * in the DDBLTFX structure as the z-buffer and z-buffer opcode respectively + * for the destination. + */ +#define DDBLT_ZBUFFERDESTCONSTOVERRIDE 0x00100000l + +/* + * Z-buffered blt using the lpDDSDestZBuffer field and the dwZBufferOpCode + * field in the DDBLTFX structure as the z-buffer and z-buffer opcode + * respectively for the destination. + */ +#define DDBLT_ZBUFFERDESTOVERRIDE 0x00200000l + +/* + * Z-buffered blt using the dwConstSrcZ field and the dwZBufferOpCode field + * in the DDBLTFX structure as the z-buffer and z-buffer opcode respectively + * for the source. + */ +#define DDBLT_ZBUFFERSRCCONSTOVERRIDE 0x00400000l + +/* + * Z-buffered blt using the lpDDSSrcZBuffer field and the dwZBufferOpCode + * field in the DDBLTFX structure as the z-buffer and z-buffer opcode + * respectively for the source. + */ +#define DDBLT_ZBUFFERSRCOVERRIDE 0x00800000l + +/* + * wait until the device is ready to handle the blt + * this will cause blt to not return DDERR_WASSTILLDRAWING + */ +#define DDBLT_WAIT 0x01000000l + +/* + * Uses the dwFillDepth field in the DDBLTFX structure as the depth value + * to fill the destination rectangle on the destination Z-buffer surface + * with. + */ +#define DDBLT_DEPTHFILL 0x02000000l + + + +/**************************************************************************** + * + * BLTFAST FLAGS + * + ****************************************************************************/ + +#define DDBLTFAST_NOCOLORKEY 0x00000000 +#define DDBLTFAST_SRCCOLORKEY 0x00000001 +#define DDBLTFAST_DESTCOLORKEY 0x00000002 +#define DDBLTFAST_WAIT 0x00000010 + +/**************************************************************************** + * + * FLIP FLAGS + * + ****************************************************************************/ + +#define DDFLIP_WAIT 0x00000001l + +/* + * Indicates that the target surface contains the even field of video data. + * This flag is only valid with an overlay surface. + */ +#define DDFLIP_EVEN 0x00000002l + +/* + * Indicates that the target surface contains the odd field of video data. + * This flag is only valid with an overlay surface. + */ +#define DDFLIP_ODD 0x00000004l + + + +/**************************************************************************** + * + * DIRECTDRAW SURFACE OVERLAY FLAGS + * + ****************************************************************************/ + +/* + * Use the alpha information in the pixel format or the alpha channel surface + * attached to the destination surface as the alpha channel for the + * destination overlay. + */ +#define DDOVER_ALPHADEST 0x00000001l + +/* + * Use the dwConstAlphaDest field in the DDOVERLAYFX structure as the + * destination alpha channel for this overlay. + */ +#define DDOVER_ALPHADESTCONSTOVERRIDE 0x00000002l + +/* + * The NEG suffix indicates that the destination surface becomes more + * transparent as the alpha value increases. + */ +#define DDOVER_ALPHADESTNEG 0x00000004l + +/* + * Use the lpDDSAlphaDest field in the DDOVERLAYFX structure as the alpha + * channel destination for this overlay. + */ +#define DDOVER_ALPHADESTSURFACEOVERRIDE 0x00000008l + +/* + * Use the dwAlphaEdgeBlend field in the DDOVERLAYFX structure as the alpha + * channel for the edges of the image that border the color key colors. + */ +#define DDOVER_ALPHAEDGEBLEND 0x00000010l + +/* + * Use the alpha information in the pixel format or the alpha channel surface + * attached to the source surface as the source alpha channel for this overlay. + */ +#define DDOVER_ALPHASRC 0x00000020l + +/* + * Use the dwConstAlphaSrc field in the DDOVERLAYFX structure as the source + * alpha channel for this overlay. + */ +#define DDOVER_ALPHASRCCONSTOVERRIDE 0x00000040l + +/* + * The NEG suffix indicates that the source surface becomes more transparent + * as the alpha value increases. + */ +#define DDOVER_ALPHASRCNEG 0x00000080l + +/* + * Use the lpDDSAlphaSrc field in the DDOVERLAYFX structure as the alpha channel + * source for this overlay. + */ +#define DDOVER_ALPHASRCSURFACEOVERRIDE 0x00000100l + +/* + * Turn this overlay off. + */ +#define DDOVER_HIDE 0x00000200l + +/* + * Use the color key associated with the destination surface. + */ +#define DDOVER_KEYDEST 0x00000400l + +/* + * Use the dckDestColorkey field in the DDOVERLAYFX structure as the color key + * for the destination surface + */ +#define DDOVER_KEYDESTOVERRIDE 0x00000800l + +/* + * Use the color key associated with the source surface. + */ +#define DDOVER_KEYSRC 0x00001000l + +/* + * Use the dckSrcColorkey field in the DDOVERLAYFX structure as the color key + * for the source surface. + */ +#define DDOVER_KEYSRCOVERRIDE 0x00002000l + +/* + * Turn this overlay on. + */ +#define DDOVER_SHOW 0x00004000l + +/* + * Add a dirty rect to an emulated overlayed surface. + */ +#define DDOVER_ADDDIRTYRECT 0x00008000l + +/* + * Redraw all dirty rects on an emulated overlayed surface. + */ +#define DDOVER_REFRESHDIRTYRECTS 0x00010000l + +/* + * Redraw the entire surface on an emulated overlayed surface. + */ +#define DDOVER_REFRESHALL 0x00020000l + + +/* + * Use the overlay FX flags to define special overlay FX + */ +#define DDOVER_DDFX 0x00080000l + +/* + * Autoflip the overlay when ever the video port autoflips + */ +#define DDOVER_AUTOFLIP 0x00100000l + +/* + * Display each field of video port data individually without + * causing any jittery artifacts + */ +#define DDOVER_BOB 0x00200000l + +/* + * Indicates that bob/weave decisions should not be overridden by other + * interfaces. + */ +#define DDOVER_OVERRIDEBOBWEAVE 0x00400000l + +/* + * Indicates that the surface memory is composed of interleaved fields. + */ +#define DDOVER_INTERLEAVED 0x00800000l + + +/**************************************************************************** + * + * DIRECTDRAWSURFACE LOCK FLAGS + * + ****************************************************************************/ + +/* + * The default. Set to indicate that Lock should return a valid memory pointer + * to the top of the specified rectangle. If no rectangle is specified then a + * pointer to the top of the surface is returned. + */ +#define DDLOCK_SURFACEMEMORYPTR 0x00000000L // default + +/* + * Set to indicate that Lock should wait until it can obtain a valid memory + * pointer before returning. If this bit is set, Lock will never return + * DDERR_WASSTILLDRAWING. + */ +#define DDLOCK_WAIT 0x00000001L + +/* + * Set if an event handle is being passed to Lock. Lock will trigger the event + * when it can return the surface memory pointer requested. + */ +#define DDLOCK_EVENT 0x00000002L + +/* + * Indicates that the surface being locked will only be read from. + */ +#define DDLOCK_READONLY 0x00000010L + +/* + * Indicates that the surface being locked will only be written to + */ +#define DDLOCK_WRITEONLY 0x00000020L + + +/* + * Indicates that a system wide lock should not be taken when this surface + * is locked. This has several advantages (cursor responsiveness, ability + * to call more Windows functions, easier debugging) when locking video + * memory surfaces. However, an application specifying this flag must + * comply with a number of conditions documented in the help file. + * Furthermore, this flag cannot be specified when locking the primary. + */ +#define DDLOCK_NOSYSLOCK 0x00000800L + + +/**************************************************************************** + * + * DIRECTDRAWSURFACE PAGELOCK FLAGS + * + ****************************************************************************/ + +/* + * No flags defined at present + */ + + +/**************************************************************************** + * + * DIRECTDRAWSURFACE PAGEUNLOCK FLAGS + * + ****************************************************************************/ + +/* + * No flags defined at present + */ + + +/**************************************************************************** + * + * DIRECTDRAWSURFACE BLT FX FLAGS + * + ****************************************************************************/ + +/* + * If stretching, use arithmetic stretching along the Y axis for this blt. + */ +#define DDBLTFX_ARITHSTRETCHY 0x00000001l + +/* + * Do this blt mirroring the surface left to right. Spin the + * surface around its y-axis. + */ +#define DDBLTFX_MIRRORLEFTRIGHT 0x00000002l + +/* + * Do this blt mirroring the surface up and down. Spin the surface + * around its x-axis. + */ +#define DDBLTFX_MIRRORUPDOWN 0x00000004l + +/* + * Schedule this blt to avoid tearing. + */ +#define DDBLTFX_NOTEARING 0x00000008l + +/* + * Do this blt rotating the surface one hundred and eighty degrees. + */ +#define DDBLTFX_ROTATE180 0x00000010l + +/* + * Do this blt rotating the surface two hundred and seventy degrees. + */ +#define DDBLTFX_ROTATE270 0x00000020l + +/* + * Do this blt rotating the surface ninety degrees. + */ +#define DDBLTFX_ROTATE90 0x00000040l + +/* + * Do this z blt using dwZBufferLow and dwZBufferHigh as range values + * specified to limit the bits copied from the source surface. + */ +#define DDBLTFX_ZBUFFERRANGE 0x00000080l + +/* + * Do this z blt adding the dwZBufferBaseDest to each of the sources z values + * before comparing it with the desting z values. + */ +#define DDBLTFX_ZBUFFERBASEDEST 0x00000100l + +/**************************************************************************** + * + * DIRECTDRAWSURFACE OVERLAY FX FLAGS + * + ****************************************************************************/ + +/* + * If stretching, use arithmetic stretching along the Y axis for this overlay. + */ +#define DDOVERFX_ARITHSTRETCHY 0x00000001l + +/* + * Mirror the overlay across the vertical axis + */ +#define DDOVERFX_MIRRORLEFTRIGHT 0x00000002l + +/* + * Mirror the overlay across the horizontal axis + */ +#define DDOVERFX_MIRRORUPDOWN 0x00000004l + +/**************************************************************************** + * + * DIRECTDRAW WAITFORVERTICALBLANK FLAGS + * + ****************************************************************************/ + +/* + * return when the vertical blank interval begins + */ +#define DDWAITVB_BLOCKBEGIN 0x00000001l + +/* + * set up an event to trigger when the vertical blank begins + */ +#define DDWAITVB_BLOCKBEGINEVENT 0x00000002l + +/* + * return when the vertical blank interval ends and display begins + */ +#define DDWAITVB_BLOCKEND 0x00000004l + +/**************************************************************************** + * + * DIRECTDRAW GETFLIPSTATUS FLAGS + * + ****************************************************************************/ + +/* + * is it OK to flip now? + */ +#define DDGFS_CANFLIP 0x00000001l + +/* + * is the last flip finished? + */ +#define DDGFS_ISFLIPDONE 0x00000002l + +/**************************************************************************** + * + * DIRECTDRAW GETBLTSTATUS FLAGS + * + ****************************************************************************/ + +/* + * is it OK to blt now? + */ +#define DDGBS_CANBLT 0x00000001l + +/* + * is the blt to the surface finished? + */ +#define DDGBS_ISBLTDONE 0x00000002l + + +/**************************************************************************** + * + * DIRECTDRAW ENUMOVERLAYZORDER FLAGS + * + ****************************************************************************/ + +/* + * Enumerate overlays back to front. + */ +#define DDENUMOVERLAYZ_BACKTOFRONT 0x00000000l + +/* + * Enumerate overlays front to back + */ +#define DDENUMOVERLAYZ_FRONTTOBACK 0x00000001l + +/**************************************************************************** + * + * DIRECTDRAW UPDATEOVERLAYZORDER FLAGS + * + ****************************************************************************/ + +/* + * Send overlay to front + */ +#define DDOVERZ_SENDTOFRONT 0x00000000l + +/* + * Send overlay to back + */ +#define DDOVERZ_SENDTOBACK 0x00000001l + +/* + * Move Overlay forward + */ +#define DDOVERZ_MOVEFORWARD 0x00000002l + +/* + * Move Overlay backward + */ +#define DDOVERZ_MOVEBACKWARD 0x00000003l + +/* + * Move Overlay in front of relative surface + */ +#define DDOVERZ_INSERTINFRONTOF 0x00000004l + +/* + * Move Overlay in back of relative surface + */ +#define DDOVERZ_INSERTINBACKOF 0x00000005l + +/*=========================================================================== + * + * + * DIRECTDRAW RETURN CODES + * + * The return values from DirectDraw Commands and Surface that return an HRESULT + * are codes from DirectDraw concerning the results of the action + * requested by DirectDraw. + * + *==========================================================================*/ + +/* + * Status is OK + * + * Issued by: DirectDraw Commands and all callbacks + */ +#define DD_OK 0 + +/**************************************************************************** + * + * DIRECTDRAW ENUMCALLBACK RETURN VALUES + * + * EnumCallback returns are used to control the flow of the DIRECTDRAW and + * DIRECTDRAWSURFACE object enumerations. They can only be returned by + * enumeration callback routines. + * + ****************************************************************************/ + +/* + * stop the enumeration + */ +#define DDENUMRET_CANCEL 0 + +/* + * continue the enumeration + */ +#define DDENUMRET_OK 1 + +/**************************************************************************** + * + * DIRECTDRAW ERRORS + * + * Errors are represented by negative values and cannot be combined. + * + ****************************************************************************/ + +/* + * This object is already initialized + */ +#define DDERR_ALREADYINITIALIZED MAKE_DDHRESULT( 5 ) + +/* + * This surface can not be attached to the requested surface. + */ +#define DDERR_CANNOTATTACHSURFACE MAKE_DDHRESULT( 10 ) + +/* + * This surface can not be detached from the requested surface. + */ +#define DDERR_CANNOTDETACHSURFACE MAKE_DDHRESULT( 20 ) + +/* + * Support is currently not available. + */ +#define DDERR_CURRENTLYNOTAVAIL MAKE_DDHRESULT( 40 ) + +/* + * An exception was encountered while performing the requested operation + */ +#define DDERR_EXCEPTION MAKE_DDHRESULT( 55 ) + +/* + * Generic failure. + */ +#define DDERR_GENERIC E_FAIL + +/* + * Height of rectangle provided is not a multiple of reqd alignment + */ +#define DDERR_HEIGHTALIGN MAKE_DDHRESULT( 90 ) + +/* + * Unable to match primary surface creation request with existing + * primary surface. + */ +#define DDERR_INCOMPATIBLEPRIMARY MAKE_DDHRESULT( 95 ) + +/* + * One or more of the caps bits passed to the callback are incorrect. + */ +#define DDERR_INVALIDCAPS MAKE_DDHRESULT( 100 ) + +/* + * DirectDraw does not support provided Cliplist. + */ +#define DDERR_INVALIDCLIPLIST MAKE_DDHRESULT( 110 ) + +/* + * DirectDraw does not support the requested mode + */ +#define DDERR_INVALIDMODE MAKE_DDHRESULT( 120 ) + +/* + * DirectDraw received a pointer that was an invalid DIRECTDRAW object. + */ +#define DDERR_INVALIDOBJECT MAKE_DDHRESULT( 130 ) + +/* + * One or more of the parameters passed to the callback function are + * incorrect. + */ +#define DDERR_INVALIDPARAMS E_INVALIDARG + +/* + * pixel format was invalid as specified + */ +#define DDERR_INVALIDPIXELFORMAT MAKE_DDHRESULT( 145 ) + +/* + * Rectangle provided was invalid. + */ +#define DDERR_INVALIDRECT MAKE_DDHRESULT( 150 ) + +/* + * Operation could not be carried out because one or more surfaces are locked + */ +#define DDERR_LOCKEDSURFACES MAKE_DDHRESULT( 160 ) + +/* + * There is no 3D present. + */ +#define DDERR_NO3D MAKE_DDHRESULT( 170 ) + +/* + * Operation could not be carried out because there is no alpha accleration + * hardware present or available. + */ +#define DDERR_NOALPHAHW MAKE_DDHRESULT( 180 ) + + +/* + * no clip list available + */ +#define DDERR_NOCLIPLIST MAKE_DDHRESULT( 205 ) + +/* + * Operation could not be carried out because there is no color conversion + * hardware present or available. + */ +#define DDERR_NOCOLORCONVHW MAKE_DDHRESULT( 210 ) + +/* + * Create function called without DirectDraw object method SetCooperativeLevel + * being called. + */ +#define DDERR_NOCOOPERATIVELEVELSET MAKE_DDHRESULT( 212 ) + +/* + * Surface doesn't currently have a color key + */ +#define DDERR_NOCOLORKEY MAKE_DDHRESULT( 215 ) + +/* + * Operation could not be carried out because there is no hardware support + * of the dest color key. + */ +#define DDERR_NOCOLORKEYHW MAKE_DDHRESULT( 220 ) + +/* + * No DirectDraw support possible with current display driver + */ +#define DDERR_NODIRECTDRAWSUPPORT MAKE_DDHRESULT( 222 ) + +/* + * Operation requires the application to have exclusive mode but the + * application does not have exclusive mode. + */ +#define DDERR_NOEXCLUSIVEMODE MAKE_DDHRESULT( 225 ) + +/* + * Flipping visible surfaces is not supported. + */ +#define DDERR_NOFLIPHW MAKE_DDHRESULT( 230 ) + +/* + * There is no GDI present. + */ +#define DDERR_NOGDI MAKE_DDHRESULT( 240 ) + +/* + * Operation could not be carried out because there is no hardware present + * or available. + */ +#define DDERR_NOMIRRORHW MAKE_DDHRESULT( 250 ) + +/* + * Requested item was not found + */ +#define DDERR_NOTFOUND MAKE_DDHRESULT( 255 ) + +/* + * Operation could not be carried out because there is no overlay hardware + * present or available. + */ +#define DDERR_NOOVERLAYHW MAKE_DDHRESULT( 260 ) + +/* + * Operation could not be carried out because there is no appropriate raster + * op hardware present or available. + */ +#define DDERR_NORASTEROPHW MAKE_DDHRESULT( 280 ) + +/* + * Operation could not be carried out because there is no rotation hardware + * present or available. + */ +#define DDERR_NOROTATIONHW MAKE_DDHRESULT( 290 ) + +/* + * Operation could not be carried out because there is no hardware support + * for stretching + */ +#define DDERR_NOSTRETCHHW MAKE_DDHRESULT( 310 ) + +/* + * DirectDrawSurface is not in 4 bit color palette and the requested operation + * requires 4 bit color palette. + */ +#define DDERR_NOT4BITCOLOR MAKE_DDHRESULT( 316 ) + +/* + * DirectDrawSurface is not in 4 bit color index palette and the requested + * operation requires 4 bit color index palette. + */ +#define DDERR_NOT4BITCOLORINDEX MAKE_DDHRESULT( 317 ) + +/* + * DirectDraw Surface is not in 8 bit color mode and the requested operation + * requires 8 bit color. + */ +#define DDERR_NOT8BITCOLOR MAKE_DDHRESULT( 320 ) + +/* + * Operation could not be carried out because there is no texture mapping + * hardware present or available. + */ +#define DDERR_NOTEXTUREHW MAKE_DDHRESULT( 330 ) + +/* + * Operation could not be carried out because there is no hardware support + * for vertical blank synchronized operations. + */ +#define DDERR_NOVSYNCHW MAKE_DDHRESULT( 335 ) + +/* + * Operation could not be carried out because there is no hardware support + * for zbuffer blting. + */ +#define DDERR_NOZBUFFERHW MAKE_DDHRESULT( 340 ) + +/* + * Overlay surfaces could not be z layered based on their BltOrder because + * the hardware does not support z layering of overlays. + */ +#define DDERR_NOZOVERLAYHW MAKE_DDHRESULT( 350 ) + +/* + * The hardware needed for the requested operation has already been + * allocated. + */ +#define DDERR_OUTOFCAPS MAKE_DDHRESULT( 360 ) + +/* + * DirectDraw does not have enough memory to perform the operation. + */ +#define DDERR_OUTOFMEMORY E_OUTOFMEMORY + +/* + * DirectDraw does not have enough memory to perform the operation. + */ +#define DDERR_OUTOFVIDEOMEMORY MAKE_DDHRESULT( 380 ) + +/* + * hardware does not support clipped overlays + */ +#define DDERR_OVERLAYCANTCLIP MAKE_DDHRESULT( 382 ) + +/* + * Can only have ony color key active at one time for overlays + */ +#define DDERR_OVERLAYCOLORKEYONLYONEACTIVE MAKE_DDHRESULT( 384 ) + +/* + * Access to this palette is being refused because the palette is already + * locked by another thread. + */ +#define DDERR_PALETTEBUSY MAKE_DDHRESULT( 387 ) + +/* + * No src color key specified for this operation. + */ +#define DDERR_COLORKEYNOTSET MAKE_DDHRESULT( 400 ) + +/* + * This surface is already attached to the surface it is being attached to. + */ +#define DDERR_SURFACEALREADYATTACHED MAKE_DDHRESULT( 410 ) + +/* + * This surface is already a dependency of the surface it is being made a + * dependency of. + */ +#define DDERR_SURFACEALREADYDEPENDENT MAKE_DDHRESULT( 420 ) + +/* + * Access to this surface is being refused because the surface is already + * locked by another thread. + */ +#define DDERR_SURFACEBUSY MAKE_DDHRESULT( 430 ) + +/* + * Access to this surface is being refused because no driver exists + * which can supply a pointer to the surface. + * This is most likely to happen when attempting to lock the primary + * surface when no DCI provider is present. + * Will also happen on attempts to lock an optimized surface. + */ +#define DDERR_CANTLOCKSURFACE MAKE_DDHRESULT( 435 ) + +/* + * Access to Surface refused because Surface is obscured. + */ +#define DDERR_SURFACEISOBSCURED MAKE_DDHRESULT( 440 ) + +/* + * Access to this surface is being refused because the surface is gone. + * The DIRECTDRAWSURFACE object representing this surface should + * have Restore called on it. + */ +#define DDERR_SURFACELOST MAKE_DDHRESULT( 450 ) + +/* + * The requested surface is not attached. + */ +#define DDERR_SURFACENOTATTACHED MAKE_DDHRESULT( 460 ) + +/* + * Height requested by DirectDraw is too large. + */ +#define DDERR_TOOBIGHEIGHT MAKE_DDHRESULT( 470 ) + +/* + * Size requested by DirectDraw is too large -- The individual height and + * width are OK. + */ +#define DDERR_TOOBIGSIZE MAKE_DDHRESULT( 480 ) + +/* + * Width requested by DirectDraw is too large. + */ +#define DDERR_TOOBIGWIDTH MAKE_DDHRESULT( 490 ) + +/* + * Action not supported. + */ +#define DDERR_UNSUPPORTED E_NOTIMPL + +/* + * FOURCC format requested is unsupported by DirectDraw + */ +#define DDERR_UNSUPPORTEDFORMAT MAKE_DDHRESULT( 510 ) + +/* + * Bitmask in the pixel format requested is unsupported by DirectDraw + */ +#define DDERR_UNSUPPORTEDMASK MAKE_DDHRESULT( 520 ) + +/* + * vertical blank is in progress + */ +#define DDERR_VERTICALBLANKINPROGRESS MAKE_DDHRESULT( 537 ) + +/* + * Informs DirectDraw that the previous Blt which is transfering information + * to or from this Surface is incomplete. + */ +#define DDERR_WASSTILLDRAWING MAKE_DDHRESULT( 540 ) + + +/* + * Rectangle provided was not horizontally aligned on reqd. boundary + */ +#define DDERR_XALIGN MAKE_DDHRESULT( 560 ) + +/* + * The GUID passed to DirectDrawCreate is not a valid DirectDraw driver + * identifier. + */ +#define DDERR_INVALIDDIRECTDRAWGUID MAKE_DDHRESULT( 561 ) + +/* + * A DirectDraw object representing this driver has already been created + * for this process. + */ +#define DDERR_DIRECTDRAWALREADYCREATED MAKE_DDHRESULT( 562 ) + +/* + * A hardware only DirectDraw object creation was attempted but the driver + * did not support any hardware. + */ +#define DDERR_NODIRECTDRAWHW MAKE_DDHRESULT( 563 ) + +/* + * this process already has created a primary surface + */ +#define DDERR_PRIMARYSURFACEALREADYEXISTS MAKE_DDHRESULT( 564 ) + +/* + * software emulation not available. + */ +#define DDERR_NOEMULATION MAKE_DDHRESULT( 565 ) + +/* + * region passed to Clipper::GetClipList is too small. + */ +#define DDERR_REGIONTOOSMALL MAKE_DDHRESULT( 566 ) + +/* + * an attempt was made to set a clip list for a clipper objec that + * is already monitoring an hwnd. + */ +#define DDERR_CLIPPERISUSINGHWND MAKE_DDHRESULT( 567 ) + +/* + * No clipper object attached to surface object + */ +#define DDERR_NOCLIPPERATTACHED MAKE_DDHRESULT( 568 ) + +/* + * Clipper notification requires an HWND or + * no HWND has previously been set as the CooperativeLevel HWND. + */ +#define DDERR_NOHWND MAKE_DDHRESULT( 569 ) + +/* + * HWND used by DirectDraw CooperativeLevel has been subclassed, + * this prevents DirectDraw from restoring state. + */ +#define DDERR_HWNDSUBCLASSED MAKE_DDHRESULT( 570 ) + +/* + * The CooperativeLevel HWND has already been set. + * It can not be reset while the process has surfaces or palettes created. + */ +#define DDERR_HWNDALREADYSET MAKE_DDHRESULT( 571 ) + +/* + * No palette object attached to this surface. + */ +#define DDERR_NOPALETTEATTACHED MAKE_DDHRESULT( 572 ) + +/* + * No hardware support for 16 or 256 color palettes. + */ +#define DDERR_NOPALETTEHW MAKE_DDHRESULT( 573 ) + +/* + * If a clipper object is attached to the source surface passed into a + * BltFast call. + */ +#define DDERR_BLTFASTCANTCLIP MAKE_DDHRESULT( 574 ) + +/* + * No blter. + */ +#define DDERR_NOBLTHW MAKE_DDHRESULT( 575 ) + +/* + * No DirectDraw ROP hardware. + */ +#define DDERR_NODDROPSHW MAKE_DDHRESULT( 576 ) + +/* + * returned when GetOverlayPosition is called on a hidden overlay + */ +#define DDERR_OVERLAYNOTVISIBLE MAKE_DDHRESULT( 577 ) + +/* + * returned when GetOverlayPosition is called on a overlay that UpdateOverlay + * has never been called on to establish a destionation. + */ +#define DDERR_NOOVERLAYDEST MAKE_DDHRESULT( 578 ) + +/* + * returned when the position of the overlay on the destionation is no longer + * legal for that destionation. + */ +#define DDERR_INVALIDPOSITION MAKE_DDHRESULT( 579 ) + +/* + * returned when an overlay member is called for a non-overlay surface + */ +#define DDERR_NOTAOVERLAYSURFACE MAKE_DDHRESULT( 580 ) + +/* + * An attempt was made to set the cooperative level when it was already + * set to exclusive. + */ +#define DDERR_EXCLUSIVEMODEALREADYSET MAKE_DDHRESULT( 581 ) + +/* + * An attempt has been made to flip a surface that is not flippable. + */ +#define DDERR_NOTFLIPPABLE MAKE_DDHRESULT( 582 ) + +/* + * Can't duplicate primary & 3D surfaces, or surfaces that are implicitly + * created. + */ +#define DDERR_CANTDUPLICATE MAKE_DDHRESULT( 583 ) + +/* + * Surface was not locked. An attempt to unlock a surface that was not + * locked at all, or by this process, has been attempted. + */ +#define DDERR_NOTLOCKED MAKE_DDHRESULT( 584 ) + +/* + * Windows can not create any more DCs + */ +#define DDERR_CANTCREATEDC MAKE_DDHRESULT( 585 ) + +/* + * No DC was ever created for this surface. + */ +#define DDERR_NODC MAKE_DDHRESULT( 586 ) + +/* + * This surface can not be restored because it was created in a different + * mode. + */ +#define DDERR_WRONGMODE MAKE_DDHRESULT( 587 ) + +/* + * This surface can not be restored because it is an implicitly created + * surface. + */ +#define DDERR_IMPLICITLYCREATED MAKE_DDHRESULT( 588 ) + +/* + * The surface being used is not a palette-based surface + */ +#define DDERR_NOTPALETTIZED MAKE_DDHRESULT( 589 ) + + +/* + * The display is currently in an unsupported mode + */ +#define DDERR_UNSUPPORTEDMODE MAKE_DDHRESULT( 590 ) + +/* + * Operation could not be carried out because there is no mip-map + * texture mapping hardware present or available. + */ +#define DDERR_NOMIPMAPHW MAKE_DDHRESULT( 591 ) + +/* + * The requested action could not be performed because the surface was of + * the wrong type. + */ +#define DDERR_INVALIDSURFACETYPE MAKE_DDHRESULT( 592 ) + + + +/* + * Device does not support optimized surfaces, therefore no video memory optimized surfaces + */ +#define DDERR_NOOPTIMIZEHW MAKE_DDHRESULT( 600 ) + +/* + * Surface is an optimized surface, but has not yet been allocated any memory + */ +#define DDERR_NOTLOADED MAKE_DDHRESULT( 601 ) + +/* + * A DC has already been returned for this surface. Only one DC can be + * retrieved per surface. + */ +#define DDERR_DCALREADYCREATED MAKE_DDHRESULT( 620 ) + +/* + * An attempt was made to allocate non-local video memory from a device + * that does not support non-local video memory. + */ +#define DDERR_NONONLOCALVIDMEM MAKE_DDHRESULT( 630 ) + +/* + * The attempt to page lock a surface failed. + */ +#define DDERR_CANTPAGELOCK MAKE_DDHRESULT( 640 ) + +/* + * The attempt to page unlock a surface failed. + */ +#define DDERR_CANTPAGEUNLOCK MAKE_DDHRESULT( 660 ) + +/* + * An attempt was made to page unlock a surface with no outstanding page locks. + */ +#define DDERR_NOTPAGELOCKED MAKE_DDHRESULT( 680 ) + +/* + * There is more data available than the specified buffer size could hold + */ +#define DDERR_MOREDATA MAKE_DDHRESULT( 690 ) + +/* + * The video port is not active + */ +#define DDERR_VIDEONOTACTIVE MAKE_DDHRESULT( 695 ) + +/* + * Surfaces created by one direct draw device cannot be used directly by + * another direct draw device. + */ +#define DDERR_DEVICEDOESNTOWNSURFACE MAKE_DDHRESULT( 699 ) + + +/* + * An attempt was made to invoke an interface member of a DirectDraw object + * created by CoCreateInstance() before it was initialized. + */ +#define DDERR_NOTINITIALIZED CO_E_NOTINITIALIZED + +/* Alpha bit depth constants */ + + +#ifdef __cplusplus +}; +#endif + +#endif + diff --git a/include/vdinput.h b/include/vdinput.h new file mode 100644 index 0000000..f53b7b7 --- /dev/null +++ b/include/vdinput.h @@ -0,0 +1,1850 @@ +/**************************************************************************** + * + * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. + * + * File: dinput.h + * Content: DirectInput include file + * + ****************************************************************************/ + +#ifndef __DINPUT_INCLUDED__ +#define __DINPUT_INCLUDED__ + +#ifndef DIJ_RINGZERO + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#endif + +#endif /* DIJ_RINGZERO */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DIRECTINPUT_VERSION +#define DIRECTINPUT_VERSION 0x0500 +#endif + +#ifndef DIJ_RINGZERO +/**************************************************************************** + * + * Class IDs + * + ****************************************************************************/ + +DEFINE_GUID(CLSID_DirectInput, 0x25E609E0,0xB259,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(CLSID_DirectInputDevice,0x25E609E1,0xB259,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +/**************************************************************************** + * + * Interfaces + * + ****************************************************************************/ + +DEFINE_GUID(IID_IDirectInputA, 0x89521360,0xAA8A,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInputW, 0x89521361,0xAA8A,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInput2A, 0x5944E662,0xAA8A,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInput2W, 0x5944E663,0xAA8A,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +DEFINE_GUID(IID_IDirectInputDeviceA, 0x5944E680,0xC92E,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInputDeviceW, 0x5944E681,0xC92E,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInputDevice2A,0x5944E682,0xC92E,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(IID_IDirectInputDevice2W,0x5944E683,0xC92E,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +DEFINE_GUID(IID_IDirectInputEffect, 0xE7E1F7C0,0x88D2,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); + +/**************************************************************************** + * + * Predefined object types + * + ****************************************************************************/ + +DEFINE_GUID(GUID_XAxis, 0xA36D02E0,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_YAxis, 0xA36D02E1,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_ZAxis, 0xA36D02E2,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RxAxis, 0xA36D02F4,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RyAxis, 0xA36D02F5,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RzAxis, 0xA36D02E3,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_Slider, 0xA36D02E4,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +DEFINE_GUID(GUID_Button, 0xA36D02F0,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_Key, 0x55728220,0xD33C,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +DEFINE_GUID(GUID_POV, 0xA36D02F2,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +DEFINE_GUID(GUID_Unknown, 0xA36D02F3,0xC9F3,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +/**************************************************************************** + * + * Predefined product GUIDs + * + ****************************************************************************/ + +DEFINE_GUID(GUID_SysMouse, 0x6F1D2B60,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_SysKeyboard,0x6F1D2B61,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_Joystick ,0x6F1D2B70,0xD5A0,0x11CF,0xBF,0xC7,0x44,0x45,0x53,0x54,0x00,0x00); + +/**************************************************************************** + * + * Predefined force feedback effects + * + ****************************************************************************/ + +DEFINE_GUID(GUID_ConstantForce,0x13541C20,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_RampForce, 0x13541C21,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Square, 0x13541C22,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Sine, 0x13541C23,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Triangle, 0x13541C24,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_SawtoothUp, 0x13541C25,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_SawtoothDown, 0x13541C26,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Spring, 0x13541C27,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Damper, 0x13541C28,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Inertia, 0x13541C29,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_Friction, 0x13541C2A,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); +DEFINE_GUID(GUID_CustomForce, 0x13541C2B,0x8E33,0x11D0,0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35); + + +#endif /* DIJ_RINGZERO */ + +/**************************************************************************** + * + * Interfaces and Structures... + * + ****************************************************************************/ + +#if(DIRECTINPUT_VERSION >= 0x0500) + +/**************************************************************************** + * + * IDirectInputEffect + * + ****************************************************************************/ + +#define DIEFT_ALL 0x00000000 + +#define DIEFT_CONSTANTFORCE 0x00000001 +#define DIEFT_RAMPFORCE 0x00000002 +#define DIEFT_PERIODIC 0x00000003 +#define DIEFT_CONDITION 0x00000004 +#define DIEFT_CUSTOMFORCE 0x00000005 +#define DIEFT_HARDWARE 0x000000FF + +#define DIEFT_FFATTACK 0x00000200 +#define DIEFT_FFFADE 0x00000400 +#define DIEFT_SATURATION 0x00000800 +#define DIEFT_POSNEGCOEFFICIENTS 0x00001000 +#define DIEFT_POSNEGSATURATION 0x00002000 +#define DIEFT_DEADBAND 0x00004000 + +#define DIEFT_GETTYPE(n) LOBYTE(n) + +#define DI_DEGREES 100 +#define DI_FFNOMINALMAX 10000 +#define DI_SECONDS 1000000 + +typedef struct DICONSTANTFORCE { + LONG lMagnitude; +} DICONSTANTFORCE, *LPDICONSTANTFORCE; +typedef const DICONSTANTFORCE *LPCDICONSTANTFORCE; + +typedef struct DIRAMPFORCE { + LONG lStart; + LONG lEnd; +} DIRAMPFORCE, *LPDIRAMPFORCE; +typedef const DIRAMPFORCE *LPCDIRAMPFORCE; + +typedef struct DIPERIODIC { + DWORD dwMagnitude; + LONG lOffset; + DWORD dwPhase; + DWORD dwPeriod; +} DIPERIODIC, *LPDIPERIODIC; +typedef const DIPERIODIC *LPCDIPERIODIC; + +typedef struct DICONDITION { + LONG lOffset; + LONG lPositiveCoefficient; + LONG lNegativeCoefficient; + DWORD dwPositiveSaturation; + DWORD dwNegativeSaturation; + LONG lDeadBand; +} DICONDITION, *LPDICONDITION; +typedef const DICONDITION *LPCDICONDITION; + +typedef struct DICUSTOMFORCE { + DWORD cChannels; + DWORD dwSamplePeriod; + DWORD cSamples; + LPLONG rglForceData; +} DICUSTOMFORCE, *LPDICUSTOMFORCE; +typedef const DICUSTOMFORCE *LPCDICUSTOMFORCE; + +typedef struct DIENVELOPE { + DWORD dwSize; /* sizeof(DIENVELOPE) */ + DWORD dwAttackLevel; + DWORD dwAttackTime; /* Microseconds */ + DWORD dwFadeLevel; + DWORD dwFadeTime; /* Microseconds */ +} DIENVELOPE, *LPDIENVELOPE; +typedef const DIENVELOPE *LPCDIENVELOPE; + +typedef struct DIEFFECT { + DWORD dwSize; /* sizeof(DIEFFECT) */ + DWORD dwFlags; /* DIEFF_* */ + DWORD dwDuration; /* Microseconds */ + DWORD dwSamplePeriod; /* Microseconds */ + DWORD dwGain; + DWORD dwTriggerButton; /* or DIEB_NOTRIGGER */ + DWORD dwTriggerRepeatInterval; /* Microseconds */ + DWORD cAxes; /* Number of axes */ + LPDWORD rgdwAxes; /* Array of axes */ + LPLONG rglDirection; /* Array of directions */ + LPDIENVELOPE lpEnvelope; /* Optional */ + DWORD cbTypeSpecificParams; /* Size of params */ + LPVOID lpvTypeSpecificParams; /* Pointer to params */ +} DIEFFECT, *LPDIEFFECT; +typedef const DIEFFECT *LPCDIEFFECT; + +#define DIEFF_OBJECTIDS 0x00000001 +#define DIEFF_OBJECTOFFSETS 0x00000002 +#define DIEFF_CARTESIAN 0x00000010 +#define DIEFF_POLAR 0x00000020 +#define DIEFF_SPHERICAL 0x00000040 + +#define DIEP_DURATION 0x00000001 +#define DIEP_SAMPLEPERIOD 0x00000002 +#define DIEP_GAIN 0x00000004 +#define DIEP_TRIGGERBUTTON 0x00000008 +#define DIEP_TRIGGERREPEATINTERVAL 0x00000010 +#define DIEP_AXES 0x00000020 +#define DIEP_DIRECTION 0x00000040 +#define DIEP_ENVELOPE 0x00000080 +#define DIEP_TYPESPECIFICPARAMS 0x00000100 +#define DIEP_ALLPARAMS 0x000001FF +#define DIEP_START 0x20000000 +#define DIEP_NORESTART 0x40000000 +#define DIEP_NODOWNLOAD 0x80000000 +#define DIEB_NOTRIGGER 0xFFFFFFFF + +#define DIES_SOLO 0x00000001 +#define DIES_NODOWNLOAD 0x80000000 + +#define DIEGES_PLAYING 0x00000001 +#define DIEGES_EMULATED 0x00000002 + +typedef struct DIEFFESCAPE { + DWORD dwSize; + DWORD dwCommand; + LPVOID lpvInBuffer; + DWORD cbInBuffer; + LPVOID lpvOutBuffer; + DWORD cbOutBuffer; +} DIEFFESCAPE, *LPDIEFFESCAPE; + +#ifndef DIJ_RINGZERO + +#undef INTERFACE +#define INTERFACE IDirectInputEffect + +DECLARE_INTERFACE_(IDirectInputEffect, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputEffect methods ***/ + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE; + STDMETHOD(GetEffectGuid)(THIS_ LPGUID) PURE; + STDMETHOD(GetParameters)(THIS_ LPDIEFFECT,DWORD) PURE; + STDMETHOD(SetParameters)(THIS_ LPCDIEFFECT,DWORD) PURE; + STDMETHOD(Start)(THIS_ DWORD,DWORD) PURE; + STDMETHOD(Stop)(THIS) PURE; + STDMETHOD(GetEffectStatus)(THIS_ LPDWORD) PURE; + STDMETHOD(Download)(THIS) PURE; + STDMETHOD(Unload)(THIS) PURE; + STDMETHOD(Escape)(THIS_ LPDIEFFESCAPE) PURE; +}; + +typedef struct IDirectInputEffect *LPDIRECTINPUTEFFECT; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectInputEffect_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectInputEffect_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectInputEffect_Release(p) (p)->lpVtbl->Release(p) +#define IDirectInputEffect_Initialize(p,a,b,c) (p)->lpVtbl->Initialize(p,a,b,c) +#define IDirectInputEffect_GetEffectGuid(p,a) (p)->lpVtbl->GetEffectGuid(p,a) +#define IDirectInputEffect_GetParameters(p,a,b) (p)->lpVtbl->GetParameters(p,a,b) +#define IDirectInputEffect_SetParameters(p,a,b) (p)->lpVtbl->SetParameters(p,a,b) +#define IDirectInputEffect_Start(p,a,b) (p)->lpVtbl->Start(p,a,b) +#define IDirectInputEffect_Stop(p) (p)->lpVtbl->Stop(p) +#define IDirectInputEffect_GetEffectStatus(p,a) (p)->lpVtbl->GetEffectStatus(p,a) +#define IDirectInputEffect_Download(p) (p)->lpVtbl->Download(p) +#define IDirectInputEffect_Unload(p) (p)->lpVtbl->Unload(p) +#define IDirectInputEffect_Escape(p,a) (p)->lpVtbl->Escape(p,a) +#else +#define IDirectInputEffect_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectInputEffect_AddRef(p) (p)->AddRef() +#define IDirectInputEffect_Release(p) (p)->Release() +#define IDirectInputEffect_Initialize(p,a,b,c) (p)->Initialize(a,b,c) +#define IDirectInputEffect_GetEffectGuid(p,a) (p)->GetEffectGuid(a) +#define IDirectInputEffect_GetParameters(p,a,b) (p)->GetParameters(a,b) +#define IDirectInputEffect_SetParameters(p,a,b) (p)->SetParameters(a,b) +#define IDirectInputEffect_Start(p,a,b) (p)->Start(a,b) +#define IDirectInputEffect_Stop(p) (p)->Stop() +#define IDirectInputEffect_GetEffectStatus(p,a) (p)->GetEffectStatus(a) +#define IDirectInputEffect_Download(p) (p)->Download() +#define IDirectInputEffect_Unload(p) (p)->Unload() +#define IDirectInputEffect_Escape(p,a) (p)->Escape(a) +#endif + +#endif /* DIJ_RINGZERO */ + +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +/**************************************************************************** + * + * IDirectInputDevice + * + ****************************************************************************/ + +#define DIDEVTYPE_DEVICE 1 +#define DIDEVTYPE_MOUSE 2 +#define DIDEVTYPE_KEYBOARD 3 +#define DIDEVTYPE_JOYSTICK 4 +#define DIDEVTYPE_HID 0x00010000 + +#define DIDEVTYPEMOUSE_UNKNOWN 1 +#define DIDEVTYPEMOUSE_TRADITIONAL 2 +#define DIDEVTYPEMOUSE_FINGERSTICK 3 +#define DIDEVTYPEMOUSE_TOUCHPAD 4 +#define DIDEVTYPEMOUSE_TRACKBALL 5 + +#define DIDEVTYPEKEYBOARD_UNKNOWN 0 +#define DIDEVTYPEKEYBOARD_PCXT 1 +#define DIDEVTYPEKEYBOARD_OLIVETTI 2 +#define DIDEVTYPEKEYBOARD_PCAT 3 +#define DIDEVTYPEKEYBOARD_PCENH 4 +#define DIDEVTYPEKEYBOARD_NOKIA1050 5 +#define DIDEVTYPEKEYBOARD_NOKIA9140 6 +#define DIDEVTYPEKEYBOARD_NEC98 7 +#define DIDEVTYPEKEYBOARD_NEC98LAPTOP 8 +#define DIDEVTYPEKEYBOARD_NEC98106 9 +#define DIDEVTYPEKEYBOARD_JAPAN106 10 +#define DIDEVTYPEKEYBOARD_JAPANAX 11 +#define DIDEVTYPEKEYBOARD_J3100 12 + +#define DIDEVTYPEJOYSTICK_UNKNOWN 1 +#define DIDEVTYPEJOYSTICK_TRADITIONAL 2 +#define DIDEVTYPEJOYSTICK_FLIGHTSTICK 3 +#define DIDEVTYPEJOYSTICK_GAMEPAD 4 +#define DIDEVTYPEJOYSTICK_RUDDER 5 +#define DIDEVTYPEJOYSTICK_WHEEL 6 +#define DIDEVTYPEJOYSTICK_HEADTRACKER 7 + +#define GET_DIDEVICE_TYPE(dwDevType) LOBYTE(dwDevType) +#define GET_DIDEVICE_SUBTYPE(dwDevType) HIBYTE(dwDevType) + +#if(DIRECTINPUT_VERSION >= 0x0500) +/* This structure is defined for DirectX 3.0 compatibility */ + +typedef struct DIDEVCAPS_DX3 { + DWORD dwSize; + DWORD dwFlags; + DWORD dwDevType; + DWORD dwAxes; + DWORD dwButtons; + DWORD dwPOVs; +} DIDEVCAPS_DX3, *LPDIDEVCAPS_DX3; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +typedef struct DIDEVCAPS { + DWORD dwSize; + DWORD dwFlags; + DWORD dwDevType; + DWORD dwAxes; + DWORD dwButtons; + DWORD dwPOVs; +#if(DIRECTINPUT_VERSION >= 0x0500) + DWORD dwFFSamplePeriod; + DWORD dwFFMinTimeResolution; + DWORD dwFirmwareRevision; + DWORD dwHardwareRevision; + DWORD dwFFDriverVersion; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ +} DIDEVCAPS, *LPDIDEVCAPS; + +#define DIDC_ATTACHED 0x00000001 +#define DIDC_POLLEDDEVICE 0x00000002 +#define DIDC_EMULATED 0x00000004 +#define DIDC_POLLEDDATAFORMAT 0x00000008 +#if(DIRECTINPUT_VERSION >= 0x0500) +#define DIDC_FORCEFEEDBACK 0x00000100 +#define DIDC_FFATTACK 0x00000200 +#define DIDC_FFFADE 0x00000400 +#define DIDC_SATURATION 0x00000800 +#define DIDC_POSNEGCOEFFICIENTS 0x00001000 +#define DIDC_POSNEGSATURATION 0x00002000 +#define DIDC_DEADBAND 0x00004000 +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +#define DIDFT_ALL 0x00000000 + +#define DIDFT_RELAXIS 0x00000001 +#define DIDFT_ABSAXIS 0x00000002 +#define DIDFT_AXIS 0x00000003 + +#define DIDFT_PSHBUTTON 0x00000004 +#define DIDFT_TGLBUTTON 0x00000008 +#define DIDFT_BUTTON 0x0000000C + +#define DIDFT_POV 0x00000010 + +#define DIDFT_COLLECTION 0x00000040 +#define DIDFT_NODATA 0x00000080 + +#define DIDFT_ANYINSTANCE 0x00FFFF00 +#define DIDFT_INSTANCEMASK DIDFT_ANYINSTANCE +#define DIDFT_MAKEINSTANCE(n) ((WORD)(n) << 8) +#define DIDFT_GETTYPE(n) LOBYTE(n) +#define DIDFT_GETINSTANCE(n) LOWORD((n) >> 8) +#define DIDFT_FFACTUATOR 0x01000000 +#define DIDFT_FFEFFECTTRIGGER 0x02000000 + +#define DIDFT_ENUMCOLLECTION(n) ((WORD)(n) << 8) +#define DIDFT_NOCOLLECTION 0x00FFFF00 + + +#ifndef DIJ_RINGZERO + +typedef struct _DIOBJECTDATAFORMAT { + const GUID *pguid; + DWORD dwOfs; + DWORD dwType; + DWORD dwFlags; +} DIOBJECTDATAFORMAT, *LPDIOBJECTDATAFORMAT; +typedef const DIOBJECTDATAFORMAT *LPCDIOBJECTDATAFORMAT; + +typedef struct _DIDATAFORMAT { + DWORD dwSize; + DWORD dwObjSize; + DWORD dwFlags; + DWORD dwDataSize; + DWORD dwNumObjs; + LPDIOBJECTDATAFORMAT rgodf; +} DIDATAFORMAT, *LPDIDATAFORMAT; +typedef const DIDATAFORMAT *LPCDIDATAFORMAT; + +#define DIDF_ABSAXIS 0x00000001 +#define DIDF_RELAXIS 0x00000002 + +extern const DIDATAFORMAT c_dfDIMouse; +extern const DIDATAFORMAT c_dfDIKeyboard; +extern const DIDATAFORMAT c_dfDIJoystick; +extern const DIDATAFORMAT c_dfDIJoystick2; + +#if(DIRECTINPUT_VERSION >= 0x0500) +/* These structures are defined for DirectX 3.0 compatibility */ + +typedef struct DIDEVICEOBJECTINSTANCE_DX3A { + DWORD dwSize; + GUID guidType; + DWORD dwOfs; + DWORD dwType; + DWORD dwFlags; + CHAR tszName[MAX_PATH]; +} DIDEVICEOBJECTINSTANCE_DX3A, *LPDIDEVICEOBJECTINSTANCE_DX3A; +typedef struct DIDEVICEOBJECTINSTANCE_DX3W { + DWORD dwSize; + GUID guidType; + DWORD dwOfs; + DWORD dwType; + DWORD dwFlags; + WCHAR tszName[MAX_PATH]; +} DIDEVICEOBJECTINSTANCE_DX3W, *LPDIDEVICEOBJECTINSTANCE_DX3W; +#ifdef UNICODE +typedef DIDEVICEOBJECTINSTANCE_DX3W DIDEVICEOBJECTINSTANCE_DX3; +typedef LPDIDEVICEOBJECTINSTANCE_DX3W LPDIDEVICEOBJECTINSTANCE_DX3; +#else +typedef DIDEVICEOBJECTINSTANCE_DX3A DIDEVICEOBJECTINSTANCE_DX3; +typedef LPDIDEVICEOBJECTINSTANCE_DX3A LPDIDEVICEOBJECTINSTANCE_DX3; +#endif // UNICODE +typedef const DIDEVICEOBJECTINSTANCE_DX3A *LPCDIDEVICEOBJECTINSTANCE_DX3A; +typedef const DIDEVICEOBJECTINSTANCE_DX3W *LPCDIDEVICEOBJECTINSTANCE_DX3W; +typedef const DIDEVICEOBJECTINSTANCE_DX3 *LPCDIDEVICEOBJECTINSTANCE_DX3; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +typedef struct DIDEVICEOBJECTINSTANCEA { + DWORD dwSize; + GUID guidType; + DWORD dwOfs; + DWORD dwType; + DWORD dwFlags; + CHAR tszName[MAX_PATH]; +#if(DIRECTINPUT_VERSION >= 0x0500) + DWORD dwFFMaxForce; + DWORD dwFFForceResolution; + WORD wCollectionNumber; + WORD wDesignatorIndex; + WORD wUsagePage; + WORD wUsage; + DWORD dwDimension; + WORD wExponent; + WORD wReserved; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ +} DIDEVICEOBJECTINSTANCEA, *LPDIDEVICEOBJECTINSTANCEA; +typedef struct DIDEVICEOBJECTINSTANCEW { + DWORD dwSize; + GUID guidType; + DWORD dwOfs; + DWORD dwType; + DWORD dwFlags; + WCHAR tszName[MAX_PATH]; +#if(DIRECTINPUT_VERSION >= 0x0500) + DWORD dwFFMaxForce; + DWORD dwFFForceResolution; + WORD wCollectionNumber; + WORD wDesignatorIndex; + WORD wUsagePage; + WORD wUsage; + DWORD dwDimension; + WORD wExponent; + WORD wReserved; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ +} DIDEVICEOBJECTINSTANCEW, *LPDIDEVICEOBJECTINSTANCEW; +#ifdef UNICODE +typedef DIDEVICEOBJECTINSTANCEW DIDEVICEOBJECTINSTANCE; +typedef LPDIDEVICEOBJECTINSTANCEW LPDIDEVICEOBJECTINSTANCE; +#else +typedef DIDEVICEOBJECTINSTANCEA DIDEVICEOBJECTINSTANCE; +typedef LPDIDEVICEOBJECTINSTANCEA LPDIDEVICEOBJECTINSTANCE; +#endif // UNICODE +typedef const DIDEVICEOBJECTINSTANCEA *LPCDIDEVICEOBJECTINSTANCEA; +typedef const DIDEVICEOBJECTINSTANCEW *LPCDIDEVICEOBJECTINSTANCEW; +typedef const DIDEVICEOBJECTINSTANCE *LPCDIDEVICEOBJECTINSTANCE; + +typedef BOOL (FAR PASCAL * LPDIENUMDEVICEOBJECTSCALLBACKA)(LPCDIDEVICEOBJECTINSTANCEA, LPVOID); +typedef BOOL (FAR PASCAL * LPDIENUMDEVICEOBJECTSCALLBACKW)(LPCDIDEVICEOBJECTINSTANCEW, LPVOID); +#ifdef UNICODE +#define LPDIENUMDEVICEOBJECTSCALLBACK LPDIENUMDEVICEOBJECTSCALLBACKW +#else +#define LPDIENUMDEVICEOBJECTSCALLBACK LPDIENUMDEVICEOBJECTSCALLBACKA +#endif // !UNICODE + +#if(DIRECTINPUT_VERSION >= 0x0500) +#define DIDOI_FFACTUATOR 0x00000001 +#define DIDOI_FFEFFECTTRIGGER 0x00000002 +#define DIDOI_POLLED 0x00008000 +#define DIDOI_ASPECTPOSITION 0x00000100 +#define DIDOI_ASPECTVELOCITY 0x00000200 +#define DIDOI_ASPECTACCEL 0x00000300 +#define DIDOI_ASPECTFORCE 0x00000400 +#define DIDOI_ASPECTMASK 0x00000F00 +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +typedef struct DIPROPHEADER { + DWORD dwSize; + DWORD dwHeaderSize; + DWORD dwObj; + DWORD dwHow; +} DIPROPHEADER, *LPDIPROPHEADER; +typedef const DIPROPHEADER *LPCDIPROPHEADER; + +#define DIPH_DEVICE 0 +#define DIPH_BYOFFSET 1 +#define DIPH_BYID 2 + +typedef struct DIPROPDWORD { + DIPROPHEADER diph; + DWORD dwData; +} DIPROPDWORD, *LPDIPROPDWORD; +typedef const DIPROPDWORD *LPCDIPROPDWORD; + +typedef struct DIPROPRANGE { + DIPROPHEADER diph; + LONG lMin; + LONG lMax; +} DIPROPRANGE, *LPDIPROPRANGE; +typedef const DIPROPRANGE *LPCDIPROPRANGE; + +#define DIPROPRANGE_NOMIN ((LONG)0x80000000) +#define DIPROPRANGE_NOMAX ((LONG)0x7FFFFFFF) + +#ifdef __cplusplus +#define MAKEDIPROP(prop) (*(const GUID *)(prop)) +#else +#define MAKEDIPROP(prop) ((REFGUID)(prop)) +#endif + +#define DIPROP_BUFFERSIZE MAKEDIPROP(1) + +#define DIPROP_AXISMODE MAKEDIPROP(2) + +#define DIPROPAXISMODE_ABS 0 +#define DIPROPAXISMODE_REL 1 + +#define DIPROP_GRANULARITY MAKEDIPROP(3) + +#define DIPROP_RANGE MAKEDIPROP(4) + +#define DIPROP_DEADZONE MAKEDIPROP(5) + +#define DIPROP_SATURATION MAKEDIPROP(6) + +#define DIPROP_FFGAIN MAKEDIPROP(7) + +#define DIPROP_FFLOAD MAKEDIPROP(8) + +#define DIPROP_AUTOCENTER MAKEDIPROP(9) + +#define DIPROPAUTOCENTER_OFF 0 +#define DIPROPAUTOCENTER_ON 1 + +#define DIPROP_CALIBRATIONMODE MAKEDIPROP(10) + +#define DIPROPCALIBRATIONMODE_COOKED 0 +#define DIPROPCALIBRATIONMODE_RAW 1 + +typedef struct DIDEVICEOBJECTDATA { + DWORD dwOfs; + DWORD dwData; + DWORD dwTimeStamp; + DWORD dwSequence; +} DIDEVICEOBJECTDATA, *LPDIDEVICEOBJECTDATA; +typedef const DIDEVICEOBJECTDATA *LPCDIDEVICEOBJECTDATA; + +#define DIGDD_PEEK 0x00000001 + +#define DISEQUENCE_COMPARE(dwSequence1, cmp, dwSequence2) \ + ((int)((dwSequence1) - (dwSequence2)) cmp 0) +#define DISCL_EXCLUSIVE 0x00000001 +#define DISCL_NONEXCLUSIVE 0x00000002 +#define DISCL_FOREGROUND 0x00000004 +#define DISCL_BACKGROUND 0x00000008 + +#if(DIRECTINPUT_VERSION >= 0x0500) +/* These structures are defined for DirectX 3.0 compatibility */ + +typedef struct DIDEVICEINSTANCE_DX3A { + DWORD dwSize; + GUID guidInstance; + GUID guidProduct; + DWORD dwDevType; + CHAR tszInstanceName[MAX_PATH]; + CHAR tszProductName[MAX_PATH]; +} DIDEVICEINSTANCE_DX3A, *LPDIDEVICEINSTANCE_DX3A; +typedef struct DIDEVICEINSTANCE_DX3W { + DWORD dwSize; + GUID guidInstance; + GUID guidProduct; + DWORD dwDevType; + WCHAR tszInstanceName[MAX_PATH]; + WCHAR tszProductName[MAX_PATH]; +} DIDEVICEINSTANCE_DX3W, *LPDIDEVICEINSTANCE_DX3W; +#ifdef UNICODE +typedef DIDEVICEINSTANCE_DX3W DIDEVICEINSTANCE_DX3; +typedef LPDIDEVICEINSTANCE_DX3W LPDIDEVICEINSTANCE_DX3; +#else +typedef DIDEVICEINSTANCE_DX3A DIDEVICEINSTANCE_DX3; +typedef LPDIDEVICEINSTANCE_DX3A LPDIDEVICEINSTANCE_DX3; +#endif // UNICODE +typedef const DIDEVICEINSTANCE_DX3A *LPCDIDEVICEINSTANCE_DX3A; +typedef const DIDEVICEINSTANCE_DX3W *LPCDIDEVICEINSTANCE_DX3W; +typedef const DIDEVICEINSTANCE_DX3 *LPCDIDEVICEINSTANCE_DX3; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +typedef struct DIDEVICEINSTANCEA { + DWORD dwSize; + GUID guidInstance; + GUID guidProduct; + DWORD dwDevType; + CHAR tszInstanceName[MAX_PATH]; + CHAR tszProductName[MAX_PATH]; +#if(DIRECTINPUT_VERSION >= 0x0500) + GUID guidFFDriver; + WORD wUsagePage; + WORD wUsage; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ +} DIDEVICEINSTANCEA, *LPDIDEVICEINSTANCEA; +typedef struct DIDEVICEINSTANCEW { + DWORD dwSize; + GUID guidInstance; + GUID guidProduct; + DWORD dwDevType; + WCHAR tszInstanceName[MAX_PATH]; + WCHAR tszProductName[MAX_PATH]; +#if(DIRECTINPUT_VERSION >= 0x0500) + GUID guidFFDriver; + WORD wUsagePage; + WORD wUsage; +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ +} DIDEVICEINSTANCEW, *LPDIDEVICEINSTANCEW; +#ifdef UNICODE +typedef DIDEVICEINSTANCEW DIDEVICEINSTANCE; +typedef LPDIDEVICEINSTANCEW LPDIDEVICEINSTANCE; +#else +typedef DIDEVICEINSTANCEA DIDEVICEINSTANCE; +typedef LPDIDEVICEINSTANCEA LPDIDEVICEINSTANCE; +#endif // UNICODE +typedef const DIDEVICEINSTANCEA *LPCDIDEVICEINSTANCEA; +typedef const DIDEVICEINSTANCEW *LPCDIDEVICEINSTANCEW; +typedef const DIDEVICEINSTANCE *LPCDIDEVICEINSTANCE; + +#undef INTERFACE +#define INTERFACE IDirectInputDeviceW + +DECLARE_INTERFACE_(IDirectInputDeviceW, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputDeviceW methods ***/ + STDMETHOD(GetCapabilities)(THIS_ LPDIDEVCAPS) PURE; + STDMETHOD(EnumObjects)(THIS_ LPDIENUMDEVICEOBJECTSCALLBACKW,LPVOID,DWORD) PURE; + STDMETHOD(GetProperty)(THIS_ REFGUID,LPDIPROPHEADER) PURE; + STDMETHOD(SetProperty)(THIS_ REFGUID,LPCDIPROPHEADER) PURE; + STDMETHOD(Acquire)(THIS) PURE; + STDMETHOD(Unacquire)(THIS) PURE; + STDMETHOD(GetDeviceState)(THIS_ DWORD,LPVOID) PURE; + STDMETHOD(GetDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; + STDMETHOD(SetDataFormat)(THIS_ LPCDIDATAFORMAT) PURE; + STDMETHOD(SetEventNotification)(THIS_ HANDLE) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(GetObjectInfo)(THIS_ LPDIDEVICEOBJECTINSTANCEW,DWORD,DWORD) PURE; + STDMETHOD(GetDeviceInfo)(THIS_ LPDIDEVICEINSTANCEW) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE; +}; + +typedef struct IDirectInputDeviceW *LPDIRECTINPUTDEVICEW; + +#undef INTERFACE +#define INTERFACE IDirectInputDeviceA + +DECLARE_INTERFACE_(IDirectInputDeviceA, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputDeviceA methods ***/ + STDMETHOD(GetCapabilities)(THIS_ LPDIDEVCAPS) PURE; + STDMETHOD(EnumObjects)(THIS_ LPDIENUMDEVICEOBJECTSCALLBACKA,LPVOID,DWORD) PURE; + STDMETHOD(GetProperty)(THIS_ REFGUID,LPDIPROPHEADER) PURE; + STDMETHOD(SetProperty)(THIS_ REFGUID,LPCDIPROPHEADER) PURE; + STDMETHOD(Acquire)(THIS) PURE; + STDMETHOD(Unacquire)(THIS) PURE; + STDMETHOD(GetDeviceState)(THIS_ DWORD,LPVOID) PURE; + STDMETHOD(GetDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; + STDMETHOD(SetDataFormat)(THIS_ LPCDIDATAFORMAT) PURE; + STDMETHOD(SetEventNotification)(THIS_ HANDLE) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(GetObjectInfo)(THIS_ LPDIDEVICEOBJECTINSTANCEA,DWORD,DWORD) PURE; + STDMETHOD(GetDeviceInfo)(THIS_ LPDIDEVICEINSTANCEA) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE; +}; + +typedef struct IDirectInputDeviceA *LPDIRECTINPUTDEVICEA; + +#ifdef UNICODE +#define IID_IDirectInputDevice IID_IDirectInputDeviceW +#define IDirectInputDevice IDirectInputDeviceW +#define IDirectInputDeviceVtbl IDirectInputDeviceWVtbl +#else +#define IID_IDirectInputDevice IID_IDirectInputDeviceA +#define IDirectInputDevice IDirectInputDeviceA +#define IDirectInputDeviceVtbl IDirectInputDeviceAVtbl +#endif +typedef struct IDirectInputDevice *LPDIRECTINPUTDEVICE; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectInputDevice_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectInputDevice_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectInputDevice_Release(p) (p)->lpVtbl->Release(p) +#define IDirectInputDevice_GetCapabilities(p,a) (p)->lpVtbl->GetCapabilities(p,a) +#define IDirectInputDevice_EnumObjects(p,a,b,c) (p)->lpVtbl->EnumObjects(p,a,b,c) +#define IDirectInputDevice_GetProperty(p,a,b) (p)->lpVtbl->GetProperty(p,a,b) +#define IDirectInputDevice_SetProperty(p,a,b) (p)->lpVtbl->SetProperty(p,a,b) +#define IDirectInputDevice_Acquire(p) (p)->lpVtbl->Acquire(p) +#define IDirectInputDevice_Unacquire(p) (p)->lpVtbl->Unacquire(p) +#define IDirectInputDevice_GetDeviceState(p,a,b) (p)->lpVtbl->GetDeviceState(p,a,b) +#define IDirectInputDevice_GetDeviceData(p,a,b,c,d) (p)->lpVtbl->GetDeviceData(p,a,b,c,d) +#define IDirectInputDevice_SetDataFormat(p,a) (p)->lpVtbl->SetDataFormat(p,a) +#define IDirectInputDevice_SetEventNotification(p,a) (p)->lpVtbl->SetEventNotification(p,a) +#define IDirectInputDevice_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) +#define IDirectInputDevice_GetObjectInfo(p,a,b,c) (p)->lpVtbl->GetObjectInfo(p,a,b,c) +#define IDirectInputDevice_GetDeviceInfo(p,a) (p)->lpVtbl->GetDeviceInfo(p,a) +#define IDirectInputDevice_RunControlPanel(p,a,b) (p)->lpVtbl->RunControlPanel(p,a,b) +#define IDirectInputDevice_Initialize(p,a,b,c) (p)->lpVtbl->Initialize(p,a,b,c) +#else +#define IDirectInputDevice_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectInputDevice_AddRef(p) (p)->AddRef() +#define IDirectInputDevice_Release(p) (p)->Release() +#define IDirectInputDevice_GetCapabilities(p,a) (p)->GetCapabilities(a) +#define IDirectInputDevice_EnumObjects(p,a,b,c) (p)->EnumObjects(a,b,c) +#define IDirectInputDevice_GetProperty(p,a,b) (p)->GetProperty(a,b) +#define IDirectInputDevice_SetProperty(p,a,b) (p)->SetProperty(a,b) +#define IDirectInputDevice_Acquire(p) (p)->Acquire() +#define IDirectInputDevice_Unacquire(p) (p)->Unacquire() +#define IDirectInputDevice_GetDeviceState(p,a,b) (p)->GetDeviceState(a,b) +#define IDirectInputDevice_GetDeviceData(p,a,b,c,d) (p)->GetDeviceData(a,b,c,d) +#define IDirectInputDevice_SetDataFormat(p,a) (p)->SetDataFormat(a) +#define IDirectInputDevice_SetEventNotification(p,a) (p)->SetEventNotification(a) +#define IDirectInputDevice_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b) +#define IDirectInputDevice_GetObjectInfo(p,a,b,c) (p)->GetObjectInfo(a,b,c) +#define IDirectInputDevice_GetDeviceInfo(p,a) (p)->GetDeviceInfo(a) +#define IDirectInputDevice_RunControlPanel(p,a,b) (p)->RunControlPanel(a,b) +#define IDirectInputDevice_Initialize(p,a,b,c) (p)->Initialize(a,b,c) +#endif + +#endif /* DIJ_RINGZERO */ + + +#if(DIRECTINPUT_VERSION >= 0x0500) + +#define DISFFC_RESET 0x00000001 +#define DISFFC_STOPALL 0x00000002 +#define DISFFC_PAUSE 0x00000004 +#define DISFFC_CONTINUE 0x00000008 +#define DISFFC_SETACTUATORSON 0x00000010 +#define DISFFC_SETACTUATORSOFF 0x00000020 + +#define DIGFFS_EMPTY 0x00000001 +#define DIGFFS_STOPPED 0x00000002 +#define DIGFFS_PAUSED 0x00000004 +#define DIGFFS_ACTUATORSON 0x00000010 +#define DIGFFS_ACTUATORSOFF 0x00000020 +#define DIGFFS_POWERON 0x00000040 +#define DIGFFS_POWEROFF 0x00000080 +#define DIGFFS_SAFETYSWITCHON 0x00000100 +#define DIGFFS_SAFETYSWITCHOFF 0x00000200 +#define DIGFFS_USERFFSWITCHON 0x00000400 +#define DIGFFS_USERFFSWITCHOFF 0x00000800 +#define DIGFFS_DEVICELOST 0x80000000 + +#ifndef DIJ_RINGZERO + +typedef struct DIEFFECTINFOA { + DWORD dwSize; + GUID guid; + DWORD dwEffType; + DWORD dwStaticParams; + DWORD dwDynamicParams; + CHAR tszName[MAX_PATH]; +} DIEFFECTINFOA, *LPDIEFFECTINFOA; +typedef struct DIEFFECTINFOW { + DWORD dwSize; + GUID guid; + DWORD dwEffType; + DWORD dwStaticParams; + DWORD dwDynamicParams; + WCHAR tszName[MAX_PATH]; +} DIEFFECTINFOW, *LPDIEFFECTINFOW; +#ifdef UNICODE +typedef DIEFFECTINFOW DIEFFECTINFO; +typedef LPDIEFFECTINFOW LPDIEFFECTINFO; +#else +typedef DIEFFECTINFOA DIEFFECTINFO; +typedef LPDIEFFECTINFOA LPDIEFFECTINFO; +#endif // UNICODE +typedef const DIEFFECTINFOA *LPCDIEFFECTINFOA; +typedef const DIEFFECTINFOW *LPCDIEFFECTINFOW; +typedef const DIEFFECTINFO *LPCDIEFFECTINFO; + +typedef BOOL (FAR PASCAL * LPDIENUMEFFECTSCALLBACKA)(LPCDIEFFECTINFOA, LPVOID); +typedef BOOL (FAR PASCAL * LPDIENUMEFFECTSCALLBACKW)(LPCDIEFFECTINFOW, LPVOID); +#ifdef UNICODE +#define LPDIENUMEFFECTSCALLBACK LPDIENUMEFFECTSCALLBACKW +#else +#define LPDIENUMEFFECTSCALLBACK LPDIENUMEFFECTSCALLBACKA +#endif // !UNICODE +typedef BOOL (FAR PASCAL * LPDIENUMCREATEDEFFECTOBJECTSCALLBACK)(LPDIRECTINPUTEFFECT, LPVOID); + +#undef INTERFACE +#define INTERFACE IDirectInputDevice2W + +DECLARE_INTERFACE_(IDirectInputDevice2W, IDirectInputDeviceW) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputDeviceW methods ***/ + STDMETHOD(GetCapabilities)(THIS_ LPDIDEVCAPS) PURE; + STDMETHOD(EnumObjects)(THIS_ LPDIENUMDEVICEOBJECTSCALLBACKW,LPVOID,DWORD) PURE; + STDMETHOD(GetProperty)(THIS_ REFGUID,LPDIPROPHEADER) PURE; + STDMETHOD(SetProperty)(THIS_ REFGUID,LPCDIPROPHEADER) PURE; + STDMETHOD(Acquire)(THIS) PURE; + STDMETHOD(Unacquire)(THIS) PURE; + STDMETHOD(GetDeviceState)(THIS_ DWORD,LPVOID) PURE; + STDMETHOD(GetDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; + STDMETHOD(SetDataFormat)(THIS_ LPCDIDATAFORMAT) PURE; + STDMETHOD(SetEventNotification)(THIS_ HANDLE) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(GetObjectInfo)(THIS_ LPDIDEVICEOBJECTINSTANCEW,DWORD,DWORD) PURE; + STDMETHOD(GetDeviceInfo)(THIS_ LPDIDEVICEINSTANCEW) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE; + + /*** IDirectInputDevice2W methods ***/ + STDMETHOD(CreateEffect)(THIS_ REFGUID,LPCDIEFFECT,LPDIRECTINPUTEFFECT *,LPUNKNOWN) PURE; + STDMETHOD(EnumEffects)(THIS_ LPDIENUMEFFECTSCALLBACKW,LPVOID,DWORD) PURE; + STDMETHOD(GetEffectInfo)(THIS_ LPDIEFFECTINFOW,REFGUID) PURE; + STDMETHOD(GetForceFeedbackState)(THIS_ LPDWORD) PURE; + STDMETHOD(SendForceFeedbackCommand)(THIS_ DWORD) PURE; + STDMETHOD(EnumCreatedEffectObjects)(THIS_ LPDIENUMCREATEDEFFECTOBJECTSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(Escape)(THIS_ LPDIEFFESCAPE) PURE; + STDMETHOD(Poll)(THIS) PURE; + STDMETHOD(SendDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; +}; + +typedef struct IDirectInputDevice2W *LPDIRECTINPUTDEVICE2W; + +#undef INTERFACE +#define INTERFACE IDirectInputDevice2A + +DECLARE_INTERFACE_(IDirectInputDevice2A, IDirectInputDeviceA) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputDeviceA methods ***/ + STDMETHOD(GetCapabilities)(THIS_ LPDIDEVCAPS) PURE; + STDMETHOD(EnumObjects)(THIS_ LPDIENUMDEVICEOBJECTSCALLBACKA,LPVOID,DWORD) PURE; + STDMETHOD(GetProperty)(THIS_ REFGUID,LPDIPROPHEADER) PURE; + STDMETHOD(SetProperty)(THIS_ REFGUID,LPCDIPROPHEADER) PURE; + STDMETHOD(Acquire)(THIS) PURE; + STDMETHOD(Unacquire)(THIS) PURE; + STDMETHOD(GetDeviceState)(THIS_ DWORD,LPVOID) PURE; + STDMETHOD(GetDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; + STDMETHOD(SetDataFormat)(THIS_ LPCDIDATAFORMAT) PURE; + STDMETHOD(SetEventNotification)(THIS_ HANDLE) PURE; + STDMETHOD(SetCooperativeLevel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(GetObjectInfo)(THIS_ LPDIDEVICEOBJECTINSTANCEA,DWORD,DWORD) PURE; + STDMETHOD(GetDeviceInfo)(THIS_ LPDIDEVICEINSTANCEA) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD,REFGUID) PURE; + + /*** IDirectInputDevice2A methods ***/ + STDMETHOD(CreateEffect)(THIS_ REFGUID,LPCDIEFFECT,LPDIRECTINPUTEFFECT *,LPUNKNOWN) PURE; + STDMETHOD(EnumEffects)(THIS_ LPDIENUMEFFECTSCALLBACKA,LPVOID,DWORD) PURE; + STDMETHOD(GetEffectInfo)(THIS_ LPDIEFFECTINFOA,REFGUID) PURE; + STDMETHOD(GetForceFeedbackState)(THIS_ LPDWORD) PURE; + STDMETHOD(SendForceFeedbackCommand)(THIS_ DWORD) PURE; + STDMETHOD(EnumCreatedEffectObjects)(THIS_ LPDIENUMCREATEDEFFECTOBJECTSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(Escape)(THIS_ LPDIEFFESCAPE) PURE; + STDMETHOD(Poll)(THIS) PURE; + STDMETHOD(SendDeviceData)(THIS_ DWORD,LPDIDEVICEOBJECTDATA,LPDWORD,DWORD) PURE; +}; + +typedef struct IDirectInputDevice2A *LPDIRECTINPUTDEVICE2A; + +#ifdef UNICODE +#define IID_IDirectInputDevice2 IID_IDirectInputDevice2W +#define IDirectInputDevice2 IDirectInputDevice2W +#define IDirectInputDevice2Vtbl IDirectInputDevice2WVtbl +#else +#define IID_IDirectInputDevice2 IID_IDirectInputDevice2A +#define IDirectInputDevice2 IDirectInputDevice2A +#define IDirectInputDevice2Vtbl IDirectInputDevice2AVtbl +#endif +typedef struct IDirectInputDevice2 *LPDIRECTINPUTDEVICE2; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectInputDevice2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectInputDevice2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectInputDevice2_Release(p) (p)->lpVtbl->Release(p) +#define IDirectInputDevice2_GetCapabilities(p,a) (p)->lpVtbl->GetCapabilities(p,a) +#define IDirectInputDevice2_EnumObjects(p,a,b,c) (p)->lpVtbl->EnumObjects(p,a,b,c) +#define IDirectInputDevice2_GetProperty(p,a,b) (p)->lpVtbl->GetProperty(p,a,b) +#define IDirectInputDevice2_SetProperty(p,a,b) (p)->lpVtbl->SetProperty(p,a,b) +#define IDirectInputDevice2_Acquire(p) (p)->lpVtbl->Acquire(p) +#define IDirectInputDevice2_Unacquire(p) (p)->lpVtbl->Unacquire(p) +#define IDirectInputDevice2_GetDeviceState(p,a,b) (p)->lpVtbl->GetDeviceState(p,a,b) +#define IDirectInputDevice2_GetDeviceData(p,a,b,c,d) (p)->lpVtbl->GetDeviceData(p,a,b,c,d) +#define IDirectInputDevice2_SetDataFormat(p,a) (p)->lpVtbl->SetDataFormat(p,a) +#define IDirectInputDevice2_SetEventNotification(p,a) (p)->lpVtbl->SetEventNotification(p,a) +#define IDirectInputDevice2_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) +#define IDirectInputDevice2_GetObjectInfo(p,a,b,c) (p)->lpVtbl->GetObjectInfo(p,a,b,c) +#define IDirectInputDevice2_GetDeviceInfo(p,a) (p)->lpVtbl->GetDeviceInfo(p,a) +#define IDirectInputDevice2_RunControlPanel(p,a,b) (p)->lpVtbl->RunControlPanel(p,a,b) +#define IDirectInputDevice2_Initialize(p,a,b,c) (p)->lpVtbl->Initialize(p,a,b,c) +#define IDirectInputDevice2_CreateEffect(p,a,b,c,d) (p)->lpVtbl->CreateEffect(p,a,b,c,d) +#define IDirectInputDevice2_EnumEffects(p,a,b,c) (p)->lpVtbl->EnumEffects(p,a,b,c) +#define IDirectInputDevice2_GetEffectInfo(p,a,b) (p)->lpVtbl->GetEffectInfo(p,a,b) +#define IDirectInputDevice2_GetForceFeedbackState(p,a) (p)->lpVtbl->GetForceFeedbackState(p,a) +#define IDirectInputDevice2_SendForceFeedbackCommand(p,a) (p)->lpVtbl->SendForceFeedbackCommand(p,a) +#define IDirectInputDevice2_EnumCreatedEffectObjects(p,a,b,c) (p)->lpVtbl->EnumCreatedEffectObjects(p,a,b,c) +#define IDirectInputDevice2_Escape(p,a) (p)->lpVtbl->Escape(p,a) +#define IDirectInputDevice2_Poll(p) (p)->lpVtbl->Poll(p) +#define IDirectInputDevice2_SendDeviceData(p,a,b,c,d) (p)->lpVtbl->SendDeviceData(p,a,b,c,d) +#else +#define IDirectInputDevice2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectInputDevice2_AddRef(p) (p)->AddRef() +#define IDirectInputDevice2_Release(p) (p)->Release() +#define IDirectInputDevice2_GetCapabilities(p,a) (p)->GetCapabilities(a) +#define IDirectInputDevice2_EnumObjects(p,a,b,c) (p)->EnumObjects(a,b,c) +#define IDirectInputDevice2_GetProperty(p,a,b) (p)->GetProperty(a,b) +#define IDirectInputDevice2_SetProperty(p,a,b) (p)->SetProperty(a,b) +#define IDirectInputDevice2_Acquire(p) (p)->Acquire() +#define IDirectInputDevice2_Unacquire(p) (p)->Unacquire() +#define IDirectInputDevice2_GetDeviceState(p,a,b) (p)->GetDeviceState(a,b) +#define IDirectInputDevice2_GetDeviceData(p,a,b,c,d) (p)->GetDeviceData(a,b,c,d) +#define IDirectInputDevice2_SetDataFormat(p,a) (p)->SetDataFormat(a) +#define IDirectInputDevice2_SetEventNotification(p,a) (p)->SetEventNotification(a) +#define IDirectInputDevice2_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b) +#define IDirectInputDevice2_GetObjectInfo(p,a,b,c) (p)->GetObjectInfo(a,b,c) +#define IDirectInputDevice2_GetDeviceInfo(p,a) (p)->GetDeviceInfo(a) +#define IDirectInputDevice2_RunControlPanel(p,a,b) (p)->RunControlPanel(a,b) +#define IDirectInputDevice2_Initialize(p,a,b,c) (p)->Initialize(a,b,c) +#define IDirectInputDevice2_CreateEffect(p,a,b,c,d) (p)->CreateEffect(a,b,c,d) +#define IDirectInputDevice2_EnumEffects(p,a,b,c) (p)->EnumEffects(a,b,c) +#define IDirectInputDevice2_GetEffectInfo(p,a,b) (p)->GetEffectInfo(a,b) +#define IDirectInputDevice2_GetForceFeedbackState(p,a) (p)->GetForceFeedbackState(a) +#define IDirectInputDevice2_SendForceFeedbackCommand(p,a) (p)->SendForceFeedbackCommand(a) +#define IDirectInputDevice2_EnumCreatedEffectObjects(p,a,b,c) (p)->EnumCreatedEffectObjects(a,b,c) +#define IDirectInputDevice2_Escape(p,a) (p)->Escape(a) +#define IDirectInputDevice2_Poll(p) (p)->Poll() +#define IDirectInputDevice2_SendDeviceData(p,a,b,c,d) (p)->SendDeviceData(a,b,c,d) +#endif + +#endif /* DIJ_RINGZERO */ + +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +/**************************************************************************** + * + * Mouse + * + ****************************************************************************/ + +#ifndef DIJ_RINGZERO + +typedef struct _DIMOUSESTATE { + LONG lX; + LONG lY; + LONG lZ; + BYTE rgbButtons[4]; +} DIMOUSESTATE, *LPDIMOUSESTATE; + +#define DIMOFS_X FIELD_OFFSET(DIMOUSESTATE, lX) +#define DIMOFS_Y FIELD_OFFSET(DIMOUSESTATE, lY) +#define DIMOFS_Z FIELD_OFFSET(DIMOUSESTATE, lZ) +#define DIMOFS_BUTTON0 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 0) +#define DIMOFS_BUTTON1 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 1) +#define DIMOFS_BUTTON2 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 2) +#define DIMOFS_BUTTON3 (FIELD_OFFSET(DIMOUSESTATE, rgbButtons) + 3) + +#endif /* DIJ_RINGZERO */ + +/**************************************************************************** + * + * Keyboard + * + ****************************************************************************/ + +#ifndef DIJ_RINGZERO + +/**************************************************************************** + * + * DirectInput keyboard scan codes + * + ****************************************************************************/ + +#define DIK_ESCAPE 0x01 +#define DIK_1 0x02 +#define DIK_2 0x03 +#define DIK_3 0x04 +#define DIK_4 0x05 +#define DIK_5 0x06 +#define DIK_6 0x07 +#define DIK_7 0x08 +#define DIK_8 0x09 +#define DIK_9 0x0A +#define DIK_0 0x0B +#define DIK_MINUS 0x0C /* - on main keyboard */ +#define DIK_EQUALS 0x0D +#define DIK_BACK 0x0E /* backspace */ +#define DIK_TAB 0x0F +#define DIK_Q 0x10 +#define DIK_W 0x11 +#define DIK_E 0x12 +#define DIK_R 0x13 +#define DIK_T 0x14 +#define DIK_Y 0x15 +#define DIK_U 0x16 +#define DIK_I 0x17 +#define DIK_O 0x18 +#define DIK_P 0x19 +#define DIK_LBRACKET 0x1A +#define DIK_RBRACKET 0x1B +#define DIK_RETURN 0x1C /* Enter on main keyboard */ +#define DIK_LCONTROL 0x1D +#define DIK_A 0x1E +#define DIK_S 0x1F +#define DIK_D 0x20 +#define DIK_F 0x21 +#define DIK_G 0x22 +#define DIK_H 0x23 +#define DIK_J 0x24 +#define DIK_K 0x25 +#define DIK_L 0x26 +#define DIK_SEMICOLON 0x27 +#define DIK_APOSTROPHE 0x28 +#define DIK_GRAVE 0x29 /* accent grave */ +#define DIK_LSHIFT 0x2A +#define DIK_BACKSLASH 0x2B +#define DIK_Z 0x2C +#define DIK_X 0x2D +#define DIK_C 0x2E +#define DIK_V 0x2F +#define DIK_B 0x30 +#define DIK_N 0x31 +#define DIK_M 0x32 +#define DIK_COMMA 0x33 +#define DIK_PERIOD 0x34 /* . on main keyboard */ +#define DIK_SLASH 0x35 /* / on main keyboard */ +#define DIK_RSHIFT 0x36 +#define DIK_MULTIPLY 0x37 /* * on numeric keypad */ +#define DIK_LMENU 0x38 /* left Alt */ +#define DIK_SPACE 0x39 +#define DIK_CAPITAL 0x3A +#define DIK_F1 0x3B +#define DIK_F2 0x3C +#define DIK_F3 0x3D +#define DIK_F4 0x3E +#define DIK_F5 0x3F +#define DIK_F6 0x40 +#define DIK_F7 0x41 +#define DIK_F8 0x42 +#define DIK_F9 0x43 +#define DIK_F10 0x44 +#define DIK_NUMLOCK 0x45 +#define DIK_SCROLL 0x46 /* Scroll Lock */ +#define DIK_NUMPAD7 0x47 +#define DIK_NUMPAD8 0x48 +#define DIK_NUMPAD9 0x49 +#define DIK_SUBTRACT 0x4A /* - on numeric keypad */ +#define DIK_NUMPAD4 0x4B +#define DIK_NUMPAD5 0x4C +#define DIK_NUMPAD6 0x4D +#define DIK_ADD 0x4E /* + on numeric keypad */ +#define DIK_NUMPAD1 0x4F +#define DIK_NUMPAD2 0x50 +#define DIK_NUMPAD3 0x51 +#define DIK_NUMPAD0 0x52 +#define DIK_DECIMAL 0x53 /* . on numeric keypad */ +#define DIK_F11 0x57 +#define DIK_F12 0x58 + +#define DIK_F13 0x64 /* (NEC PC98) */ +#define DIK_F14 0x65 /* (NEC PC98) */ +#define DIK_F15 0x66 /* (NEC PC98) */ + +#define DIK_KANA 0x70 /* (Japanese keyboard) */ +#define DIK_CONVERT 0x79 /* (Japanese keyboard) */ +#define DIK_NOCONVERT 0x7B /* (Japanese keyboard) */ +#define DIK_YEN 0x7D /* (Japanese keyboard) */ +#define DIK_NUMPADEQUALS 0x8D /* = on numeric keypad (NEC PC98) */ +#define DIK_CIRCUMFLEX 0x90 /* (Japanese keyboard) */ +#define DIK_AT 0x91 /* (NEC PC98) */ +#define DIK_COLON 0x92 /* (NEC PC98) */ +#define DIK_UNDERLINE 0x93 /* (NEC PC98) */ +#define DIK_KANJI 0x94 /* (Japanese keyboard) */ +#define DIK_STOP 0x95 /* (NEC PC98) */ +#define DIK_AX 0x96 /* (Japan AX) */ +#define DIK_UNLABELED 0x97 /* (J3100) */ +#define DIK_NUMPADENTER 0x9C /* Enter on numeric keypad */ +#define DIK_RCONTROL 0x9D +#define DIK_NUMPADCOMMA 0xB3 /* , on numeric keypad (NEC PC98) */ +#define DIK_DIVIDE 0xB5 /* / on numeric keypad */ +#define DIK_SYSRQ 0xB7 +#define DIK_RMENU 0xB8 /* right Alt */ +#define DIK_HOME 0xC7 /* Home on arrow keypad */ +#define DIK_UP 0xC8 /* UpArrow on arrow keypad */ +#define DIK_PRIOR 0xC9 /* PgUp on arrow keypad */ +#define DIK_LEFT 0xCB /* LeftArrow on arrow keypad */ +#define DIK_RIGHT 0xCD /* RightArrow on arrow keypad */ +#define DIK_END 0xCF /* End on arrow keypad */ +#define DIK_DOWN 0xD0 /* DownArrow on arrow keypad */ +#define DIK_NEXT 0xD1 /* PgDn on arrow keypad */ +#define DIK_INSERT 0xD2 /* Insert on arrow keypad */ +#define DIK_DELETE 0xD3 /* Delete on arrow keypad */ +#define DIK_LWIN 0xDB /* Left Windows key */ +#define DIK_RWIN 0xDC /* Right Windows key */ +#define DIK_APPS 0xDD /* AppMenu key */ + +/* + * Alternate names for keys, to facilitate transition from DOS. + */ +#define DIK_BACKSPACE DIK_BACK /* backspace */ +#define DIK_NUMPADSTAR DIK_MULTIPLY /* * on numeric keypad */ +#define DIK_LALT DIK_LMENU /* left Alt */ +#define DIK_CAPSLOCK DIK_CAPITAL /* CapsLock */ +#define DIK_NUMPADMINUS DIK_SUBTRACT /* - on numeric keypad */ +#define DIK_NUMPADPLUS DIK_ADD /* + on numeric keypad */ +#define DIK_NUMPADPERIOD DIK_DECIMAL /* . on numeric keypad */ +#define DIK_NUMPADSLASH DIK_DIVIDE /* / on numeric keypad */ +#define DIK_RALT DIK_RMENU /* right Alt */ +#define DIK_UPARROW DIK_UP /* UpArrow on arrow keypad */ +#define DIK_PGUP DIK_PRIOR /* PgUp on arrow keypad */ +#define DIK_LEFTARROW DIK_LEFT /* LeftArrow on arrow keypad */ +#define DIK_RIGHTARROW DIK_RIGHT /* RightArrow on arrow keypad */ +#define DIK_DOWNARROW DIK_DOWN /* DownArrow on arrow keypad */ +#define DIK_PGDN DIK_NEXT /* PgDn on arrow keypad */ + +#endif /* DIJ_RINGZERO */ + +/**************************************************************************** + * + * Joystick + * + ****************************************************************************/ + +#ifndef DIJ_RINGZERO + +typedef struct DIJOYSTATE { + LONG lX; /* x-axis position */ + LONG lY; /* y-axis position */ + LONG lZ; /* z-axis position */ + LONG lRx; /* x-axis rotation */ + LONG lRy; /* y-axis rotation */ + LONG lRz; /* z-axis rotation */ + LONG rglSlider[2]; /* extra axes positions */ + DWORD rgdwPOV[4]; /* POV directions */ + BYTE rgbButtons[32]; /* 32 buttons */ +} DIJOYSTATE, *LPDIJOYSTATE; + +typedef struct DIJOYSTATE2 { + LONG lX; /* x-axis position */ + LONG lY; /* y-axis position */ + LONG lZ; /* z-axis position */ + LONG lRx; /* x-axis rotation */ + LONG lRy; /* y-axis rotation */ + LONG lRz; /* z-axis rotation */ + LONG rglSlider[2]; /* extra axes positions */ + DWORD rgdwPOV[4]; /* POV directions */ + BYTE rgbButtons[128]; /* 128 buttons */ + LONG lVX; /* x-axis velocity */ + LONG lVY; /* y-axis velocity */ + LONG lVZ; /* z-axis velocity */ + LONG lVRx; /* x-axis angular velocity */ + LONG lVRy; /* y-axis angular velocity */ + LONG lVRz; /* z-axis angular velocity */ + LONG rglVSlider[2]; /* extra axes velocities */ + LONG lAX; /* x-axis acceleration */ + LONG lAY; /* y-axis acceleration */ + LONG lAZ; /* z-axis acceleration */ + LONG lARx; /* x-axis angular acceleration */ + LONG lARy; /* y-axis angular acceleration */ + LONG lARz; /* z-axis angular acceleration */ + LONG rglASlider[2]; /* extra axes accelerations */ + LONG lFX; /* x-axis force */ + LONG lFY; /* y-axis force */ + LONG lFZ; /* z-axis force */ + LONG lFRx; /* x-axis torque */ + LONG lFRy; /* y-axis torque */ + LONG lFRz; /* z-axis torque */ + LONG rglFSlider[2]; /* extra axes forces */ +} DIJOYSTATE2, *LPDIJOYSTATE2; + +#define DIJOFS_X FIELD_OFFSET(DIJOYSTATE, lX) +#define DIJOFS_Y FIELD_OFFSET(DIJOYSTATE, lY) +#define DIJOFS_Z FIELD_OFFSET(DIJOYSTATE, lZ) +#define DIJOFS_RX FIELD_OFFSET(DIJOYSTATE, lRx) +#define DIJOFS_RY FIELD_OFFSET(DIJOYSTATE, lRy) +#define DIJOFS_RZ FIELD_OFFSET(DIJOYSTATE, lRz) +#define DIJOFS_SLIDER(n) (FIELD_OFFSET(DIJOYSTATE, rglSlider) + \ + (n) * sizeof(LONG)) +#define DIJOFS_POV(n) (FIELD_OFFSET(DIJOYSTATE, rgdwPOV) + \ + (n) * sizeof(DWORD)) +#define DIJOFS_BUTTON(n) (FIELD_OFFSET(DIJOYSTATE, rgbButtons) + (n)) +#define DIJOFS_BUTTON0 DIJOFS_BUTTON(0) +#define DIJOFS_BUTTON1 DIJOFS_BUTTON(1) +#define DIJOFS_BUTTON2 DIJOFS_BUTTON(2) +#define DIJOFS_BUTTON3 DIJOFS_BUTTON(3) +#define DIJOFS_BUTTON4 DIJOFS_BUTTON(4) +#define DIJOFS_BUTTON5 DIJOFS_BUTTON(5) +#define DIJOFS_BUTTON6 DIJOFS_BUTTON(6) +#define DIJOFS_BUTTON7 DIJOFS_BUTTON(7) +#define DIJOFS_BUTTON8 DIJOFS_BUTTON(8) +#define DIJOFS_BUTTON9 DIJOFS_BUTTON(9) +#define DIJOFS_BUTTON10 DIJOFS_BUTTON(10) +#define DIJOFS_BUTTON11 DIJOFS_BUTTON(11) +#define DIJOFS_BUTTON12 DIJOFS_BUTTON(12) +#define DIJOFS_BUTTON13 DIJOFS_BUTTON(13) +#define DIJOFS_BUTTON14 DIJOFS_BUTTON(14) +#define DIJOFS_BUTTON15 DIJOFS_BUTTON(15) +#define DIJOFS_BUTTON16 DIJOFS_BUTTON(16) +#define DIJOFS_BUTTON17 DIJOFS_BUTTON(17) +#define DIJOFS_BUTTON18 DIJOFS_BUTTON(18) +#define DIJOFS_BUTTON19 DIJOFS_BUTTON(19) +#define DIJOFS_BUTTON20 DIJOFS_BUTTON(20) +#define DIJOFS_BUTTON21 DIJOFS_BUTTON(21) +#define DIJOFS_BUTTON22 DIJOFS_BUTTON(22) +#define DIJOFS_BUTTON23 DIJOFS_BUTTON(23) +#define DIJOFS_BUTTON24 DIJOFS_BUTTON(24) +#define DIJOFS_BUTTON25 DIJOFS_BUTTON(25) +#define DIJOFS_BUTTON26 DIJOFS_BUTTON(26) +#define DIJOFS_BUTTON27 DIJOFS_BUTTON(27) +#define DIJOFS_BUTTON28 DIJOFS_BUTTON(28) +#define DIJOFS_BUTTON29 DIJOFS_BUTTON(29) +#define DIJOFS_BUTTON30 DIJOFS_BUTTON(30) +#define DIJOFS_BUTTON31 DIJOFS_BUTTON(31) + + +#endif /* DIJ_RINGZERO */ + +/**************************************************************************** + * + * IDirectInput + * + ****************************************************************************/ + +#ifndef DIJ_RINGZERO + +#define DIENUM_STOP 0 +#define DIENUM_CONTINUE 1 + +typedef BOOL (FAR PASCAL * LPDIENUMDEVICESCALLBACKA)(LPCDIDEVICEINSTANCEA, LPVOID); +typedef BOOL (FAR PASCAL * LPDIENUMDEVICESCALLBACKW)(LPCDIDEVICEINSTANCEW, LPVOID); +#ifdef UNICODE +#define LPDIENUMDEVICESCALLBACK LPDIENUMDEVICESCALLBACKW +#else +#define LPDIENUMDEVICESCALLBACK LPDIENUMDEVICESCALLBACKA +#endif // !UNICODE + +#define DIEDFL_ALLDEVICES 0x00000000 +#define DIEDFL_ATTACHEDONLY 0x00000001 +#if(DIRECTINPUT_VERSION >= 0x0500) +#define DIEDFL_FORCEFEEDBACK 0x00000100 +#endif /* DIRECTINPUT_VERSION >= 0x0500 */ + +#undef INTERFACE +#define INTERFACE IDirectInputW + +DECLARE_INTERFACE_(IDirectInputW, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputW methods ***/ + STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEW *,LPUNKNOWN) PURE; + STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKW,LPVOID,DWORD) PURE; + STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE; +}; + +typedef struct IDirectInputW *LPDIRECTINPUTW; + +#undef INTERFACE +#define INTERFACE IDirectInputA + +DECLARE_INTERFACE_(IDirectInputA, IUnknown) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputA methods ***/ + STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEA *,LPUNKNOWN) PURE; + STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKA,LPVOID,DWORD) PURE; + STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE; +}; + +typedef struct IDirectInputA *LPDIRECTINPUTA; + +#ifdef UNICODE +#define IID_IDirectInput IID_IDirectInputW +#define IDirectInput IDirectInputW +#define IDirectInputVtbl IDirectInputWVtbl +#else +#define IID_IDirectInput IID_IDirectInputA +#define IDirectInput IDirectInputA +#define IDirectInputVtbl IDirectInputAVtbl +#endif +typedef struct IDirectInput *LPDIRECTINPUT; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectInput_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectInput_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectInput_Release(p) (p)->lpVtbl->Release(p) +#define IDirectInput_CreateDevice(p,a,b,c) (p)->lpVtbl->CreateDevice(p,a,b,c) +#define IDirectInput_EnumDevices(p,a,b,c,d) (p)->lpVtbl->EnumDevices(p,a,b,c,d) +#define IDirectInput_GetDeviceStatus(p,a) (p)->lpVtbl->GetDeviceStatus(p,a) +#define IDirectInput_RunControlPanel(p,a,b) (p)->lpVtbl->RunControlPanel(p,a,b) +#define IDirectInput_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#else +#define IDirectInput_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectInput_AddRef(p) (p)->AddRef() +#define IDirectInput_Release(p) (p)->Release() +#define IDirectInput_CreateDevice(p,a,b,c) (p)->CreateDevice(a,b,c) +#define IDirectInput_EnumDevices(p,a,b,c,d) (p)->EnumDevices(a,b,c,d) +#define IDirectInput_GetDeviceStatus(p,a) (p)->GetDeviceStatus(a) +#define IDirectInput_RunControlPanel(p,a,b) (p)->RunControlPanel(a,b) +#define IDirectInput_Initialize(p,a,b) (p)->Initialize(a,b) +#endif + +#undef INTERFACE +#define INTERFACE IDirectInput2W + +DECLARE_INTERFACE_(IDirectInput2W, IDirectInputW) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputW methods ***/ + STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEW *,LPUNKNOWN) PURE; + STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKW,LPVOID,DWORD) PURE; + STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE; + + /*** IDirectInput2W methods ***/ + STDMETHOD(FindDevice)(THIS_ REFGUID,LPCWSTR,LPGUID) PURE; +}; + +typedef struct IDirectInput2W *LPDIRECTINPUT2W; + +#undef INTERFACE +#define INTERFACE IDirectInput2A + +DECLARE_INTERFACE_(IDirectInput2A, IDirectInputA) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + + /*** IDirectInputA methods ***/ + STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEA *,LPUNKNOWN) PURE; + STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKA,LPVOID,DWORD) PURE; + STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE; + STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE; + STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE; + + /*** IDirectInput2A methods ***/ + STDMETHOD(FindDevice)(THIS_ REFGUID,LPCSTR,LPGUID) PURE; +}; + +typedef struct IDirectInput2A *LPDIRECTINPUT2A; + +#ifdef UNICODE +#define IID_IDirectInput2 IID_IDirectInput2W +#define IDirectInput2 IDirectInput2W +#define IDirectInput2Vtbl IDirectInput2WVtbl +#else +#define IID_IDirectInput2 IID_IDirectInput2A +#define IDirectInput2 IDirectInput2A +#define IDirectInput2Vtbl IDirectInput2AVtbl +#endif +typedef struct IDirectInput2 *LPDIRECTINPUT2; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectInput2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectInput2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectInput2_Release(p) (p)->lpVtbl->Release(p) +#define IDirectInput2_CreateDevice(p,a,b,c) (p)->lpVtbl->CreateDevice(p,a,b,c) +#define IDirectInput2_EnumDevices(p,a,b,c,d) (p)->lpVtbl->EnumDevices(p,a,b,c,d) +#define IDirectInput2_GetDeviceStatus(p,a) (p)->lpVtbl->GetDeviceStatus(p,a) +#define IDirectInput2_RunControlPanel(p,a,b) (p)->lpVtbl->RunControlPanel(p,a,b) +#define IDirectInput2_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectInput2_FindDevice(p,a,b,c) (p)->lpVtbl->FindDevice(p,a,b,c) +#else +#define IDirectInput2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectInput2_AddRef(p) (p)->AddRef() +#define IDirectInput2_Release(p) (p)->Release() +#define IDirectInput2_CreateDevice(p,a,b,c) (p)->CreateDevice(a,b,c) +#define IDirectInput2_EnumDevices(p,a,b,c,d) (p)->EnumDevices(a,b,c,d) +#define IDirectInput2_GetDeviceStatus(p,a) (p)->GetDeviceStatus(a) +#define IDirectInput2_RunControlPanel(p,a,b) (p)->RunControlPanel(a,b) +#define IDirectInput2_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectInput2_FindDevice(p,a,b,c) (p)->FindDevice(a,b,c) +#endif + +extern HRESULT WINAPI DirectInputCreateA(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA *ppDI, LPUNKNOWN punkOuter); +extern HRESULT WINAPI DirectInputCreateW(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTW *ppDI, LPUNKNOWN punkOuter); +#ifdef UNICODE +#define DirectInputCreate DirectInputCreateW +#else +#define DirectInputCreate DirectInputCreateA +#endif // !UNICODE + +#endif /* DIJ_RINGZERO */ + + +/**************************************************************************** + * + * Return Codes + * + ****************************************************************************/ + +/* + * The operation completed successfully. + */ +#define DI_OK S_OK + +/* + * The device exists but is not currently attached. + */ +#define DI_NOTATTACHED S_FALSE + +/* + * The device buffer overflowed. Some input was lost. + */ +#define DI_BUFFEROVERFLOW S_FALSE + +/* + * The change in device properties had no effect. + */ +#define DI_PROPNOEFFECT S_FALSE + +/* + * The operation had no effect. + */ +#define DI_NOEFFECT S_FALSE + +/* + * The device is a polled device. As a result, device buffering + * will not collect any data and event notifications will not be + * signalled until GetDeviceState is called. + */ +#define DI_POLLEDDEVICE ((HRESULT)0x00000002L) + +/* + * The parameters of the effect were successfully updated by + * IDirectInputEffect::SetParameters, but the effect was not + * downloaded because the device is not exclusively acquired + * or because the DIEP_NODOWNLOAD flag was passed. + */ +#define DI_DOWNLOADSKIPPED ((HRESULT)0x00000003L) + +/* + * The parameters of the effect were successfully updated by + * IDirectInputEffect::SetParameters, but in order to change + * the parameters, the effect needed to be restarted. + */ +#define DI_EFFECTRESTARTED ((HRESULT)0x00000004L) + +/* + * The parameters of the effect were successfully updated by + * IDirectInputEffect::SetParameters, but some of them were + * beyond the capabilities of the device and were truncated. + */ +#define DI_TRUNCATED ((HRESULT)0x00000008L) + +/* + * Equal to DI_EFFECTRESTARTED | DI_TRUNCATED. + */ +#define DI_TRUNCATEDANDRESTARTED ((HRESULT)0x0000000CL) + +/* + * The application requires a newer version of DirectInput. + */ +#define DIERR_OLDDIRECTINPUTVERSION \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_OLD_WIN_VERSION) + +/* + * The application was written for an unsupported prerelease version + * of DirectInput. + */ +#define DIERR_BETADIRECTINPUTVERSION \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_RMODE_APP) + +/* + * The object could not be created due to an incompatible driver version + * or mismatched or incomplete driver components. + */ +#define DIERR_BADDRIVERVER \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_BAD_DRIVER_LEVEL) + +/* + * The device or device instance or effect is not registered with DirectInput. + */ +#define DIERR_DEVICENOTREG REGDB_E_CLASSNOTREG + +/* + * The requested object does not exist. + */ +#define DIERR_NOTFOUND \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND) + +/* + * The requested object does not exist. + */ +#define DIERR_OBJECTNOTFOUND \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_FILE_NOT_FOUND) + +/* + * An invalid parameter was passed to the returning function, + * or the object was not in a state that admitted the function + * to be called. + */ +#define DIERR_INVALIDPARAM E_INVALIDARG + +/* + * The specified interface is not supported by the object + */ +#define DIERR_NOINTERFACE E_NOINTERFACE + +/* + * An undetermined error occured inside the DInput subsystem + */ +#define DIERR_GENERIC E_FAIL + +/* + * The DInput subsystem couldn't allocate sufficient memory to complete the + * caller's request. + */ +#define DIERR_OUTOFMEMORY E_OUTOFMEMORY + +/* + * The function called is not supported at this time + */ +#define DIERR_UNSUPPORTED E_NOTIMPL + +/* + * This object has not been initialized + */ +#define DIERR_NOTINITIALIZED \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_NOT_READY) + +/* + * This object is already initialized + */ +#define DIERR_ALREADYINITIALIZED \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_ALREADY_INITIALIZED) + +/* + * This object does not support aggregation + */ +#define DIERR_NOAGGREGATION CLASS_E_NOAGGREGATION + +/* + * Another app has a higher priority level, preventing this call from + * succeeding. + */ +#define DIERR_OTHERAPPHASPRIO E_ACCESSDENIED + +/* + * Access to the device has been lost. It must be re-acquired. + */ +#define DIERR_INPUTLOST \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_READ_FAULT) + +/* + * The operation cannot be performed while the device is acquired. + */ +#define DIERR_ACQUIRED \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_BUSY) + +/* + * The operation cannot be performed unless the device is acquired. + */ +#define DIERR_NOTACQUIRED \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_INVALID_ACCESS) + +/* + * The specified property cannot be changed. + */ +#define DIERR_READONLY E_ACCESSDENIED + +/* + * The device already has an event notification associated with it. + */ +#define DIERR_HANDLEEXISTS E_ACCESSDENIED + +/* + * Data is not yet available. + */ +#ifndef E_PENDING +#define E_PENDING 0x80070007L +#endif + +/* + * Unable to IDirectInputJoyConfig_Acquire because the user + * does not have sufficient privileges to change the joystick + * configuration. + */ +#define DIERR_INSUFFICIENTPRIVS 0x80040200L + +/* + * The device is full. + */ +#define DIERR_DEVICEFULL 0x80040201L + +/* + * Not all the requested information fit into the buffer. + */ +#define DIERR_MOREDATA 0x80040202L + +/* + * The effect is not downloaded. + */ +#define DIERR_NOTDOWNLOADED 0x80040203L + +/* + * The device cannot be reinitialized because there are still effects + * attached to it. + */ +#define DIERR_HASEFFECTS 0x80040204L + +/* + * The operation cannot be performed unless the device is acquired + * in DISCL_EXCLUSIVE mode. + */ +#define DIERR_NOTEXCLUSIVEACQUIRED 0x80040205L + +/* + * The effect could not be downloaded because essential information + * is missing. For example, no axes have been associated with the + * effect, or no type-specific information has been created. + */ +#define DIERR_INCOMPLETEEFFECT 0x80040206L + +/* + * Attempted to read buffered device data from a device that is + * not buffered. + */ +#define DIERR_NOTBUFFERED 0x80040207L + +/* + * An attempt was made to modify parameters of an effect while it is + * playing. Not all hardware devices support altering the parameters + * of an effect while it is playing. + */ +#define DIERR_EFFECTPLAYING 0x80040208L + +#ifdef __cplusplus +}; +#endif + +#endif /* __DINPUT_INCLUDED__ */ + +/**************************************************************************** + * + * Definitions for non-IDirectInput (VJoyD) features defined more recently + * than the current sdk files + * + ****************************************************************************/ + +#ifdef _INC_MMSYSTEM +#ifndef MMNOJOY + +#ifndef __VJOYDX_INCLUDED__ +#define __VJOYDX_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Flag to indicate that the dwReserved2 field of the JOYINFOEX structure + * contains mini-driver specific data to be passed by VJoyD to the mini- + * driver instead of doing a poll. + */ +#define JOY_PASSDRIVERDATA 0x10000000l + +/* + * Informs the joystick driver that the configuration has been changed + * and should be reloaded from the registery. + * dwFlags is reserved and should be set to zero + */ +WINMMAPI MMRESULT WINAPI joyConfigChanged( DWORD dwFlags ); + +/* + * Hardware Setting indicating that the device is a headtracker + */ +#define JOY_HWS_ISHEADTRACKER 0x02000000l + +/* + * Hardware Setting indicating that the VxD is used to replace + * the standard analog polling + */ +#define JOY_HWS_ISGAMEPORTDRIVER 0x04000000l + +/* + * Hardware Setting indicating that the driver needs a standard + * gameport in order to communicate with the device. + */ +#define JOY_HWS_ISANALOGPORTDRIVER 0x08000000l + +/* + * Hardware Setting indicating that VJoyD should not load this + * driver, it will be loaded externally and will register with + * VJoyD of it's own accord. + */ +#define JOY_HWS_AUTOLOAD 0x10000000l + +/* + * Hardware Setting indicating that the driver acquires any + * resources needed without needing a devnode through VJoyD. + */ +#define JOY_HWS_NODEVNODE 0x20000000l + +/* + * Hardware Setting indicating that the VxD can be used as + * a port 201h emulator. + */ +#define JOY_HWS_ISGAMEPORTEMULATOR 0x40000000l + + +/* + * Usage Setting indicating that the settings are volatile and + * should be removed if still present on a reboot. + */ +#define JOY_US_VOLATILE 0x00000008L + +#ifdef __cplusplus +}; +#endif + +#endif /* __VJOYDX_INCLUDED__ */ + +#endif /* not MMNOJOY */ +#endif /* _INC_MMSYSTEM */ + +/**************************************************************************** + * + * Definitions for non-IDirectInput (VJoyD) features defined more recently + * than the current ddk files + * + ****************************************************************************/ + +#ifndef DIJ_RINGZERO + +#ifdef _INC_MMDDK +#ifndef MMNOJOYDEV + +#ifndef __VJOYDXD_INCLUDED__ +#define __VJOYDXD_INCLUDED__ +/* + * Poll type in which the do_other field of the JOYOEMPOLLDATA + * structure contains mini-driver specific data passed from an app. + */ +#define JOY_OEMPOLL_PASSDRIVERDATA 7 + +#endif /* __VJOYDXD_INCLUDED__ */ + +#endif /* not MMNOJOYDEV */ +#endif /* _INC_MMDDK */ + +#endif /* DIJ_RINGZERO */ + diff --git a/include/vdplay.h b/include/vdplay.h new file mode 100644 index 0000000..2d94ae9 --- /dev/null +++ b/include/vdplay.h @@ -0,0 +1,1711 @@ +/*==========================================================================; + * + * Copyright (C) 1994-1997 Microsoft Corporation. All Rights Reserved. + * + * File: dplay.h + * Content: DirectPlay include file + * + ***************************************************************************/ + +#ifndef __DPLAY_INCLUDED__ +#define __DPLAY_INCLUDED__ + +#include // for DECLARE_INTERFACE and HRESULT + +#define _FACDP 0x877 +#define MAKE_DPHRESULT( code ) MAKE_HRESULT( 1, _FACDP, code ) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * GUIDS used by DirectPlay objects + */ +DEFINE_GUID(IID_IDirectPlay2, 0x2b74f7c0, 0x9154, 0x11cf, 0xa9, 0xcd, 0x0, 0xaa, 0x0, 0x68, 0x86, 0xe3); +DEFINE_GUID(IID_IDirectPlay2A,0x9d460580, 0xa822, 0x11cf, 0x96, 0xc, 0x0, 0x80, 0xc7, 0x53, 0x4e, 0x82); + +DEFINE_GUID(IID_IDirectPlay3, 0x133efe40, 0x32dc, 0x11d0, 0x9c, 0xfb, 0x0, 0xa0, 0xc9, 0xa, 0x43, 0xcb); +DEFINE_GUID(IID_IDirectPlay3A,0x133efe41, 0x32dc, 0x11d0, 0x9c, 0xfb, 0x0, 0xa0, 0xc9, 0xa, 0x43, 0xcb); + +// {D1EB6D20-8923-11d0-9D97-00A0C90A43CB} +DEFINE_GUID(CLSID_DirectPlay,0xd1eb6d20, 0x8923, 0x11d0, 0x9d, 0x97, 0x0, 0xa0, 0xc9, 0xa, 0x43, 0xcb); + +/* + * GUIDS used by Service Providers shipped with DirectPlay + * Use these to identify Service Provider returned by EnumConnections + */ + +// GUID for IPX service provider +// {685BC400-9D2C-11cf-A9CD-00AA006886E3} +DEFINE_GUID(DPSPGUID_IPX, +0x685bc400, 0x9d2c, 0x11cf, 0xa9, 0xcd, 0x0, 0xaa, 0x0, 0x68, 0x86, 0xe3); + +// GUID for TCP/IP service provider +// 36E95EE0-8577-11cf-960C-0080C7534E82 +DEFINE_GUID(DPSPGUID_TCPIP, +0x36E95EE0, 0x8577, 0x11cf, 0x96, 0xc, 0x0, 0x80, 0xc7, 0x53, 0x4e, 0x82); + +// GUID for Serial service provider +// {0F1D6860-88D9-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPSPGUID_SERIAL, +0xf1d6860, 0x88d9, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +// GUID for Modem service provider +// {44EAA760-CB68-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPSPGUID_MODEM, +0x44eaa760, 0xcb68, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/**************************************************************************** + * + * DirectPlay Structures + * + * Various structures used to invoke DirectPlay. + * + ****************************************************************************/ + +#ifndef IDIRECTPLAY2_OR_GREATER +typedef struct IDirectPlay FAR *LPDIRECTPLAY; +#else +typedef struct IUnknown FAR *LPDIRECTPLAY; +#endif + +typedef struct IDirectPlay2 FAR *LPDIRECTPLAY2; +typedef struct IDirectPlay2 FAR *LPDIRECTPLAY2A; +typedef struct IDirectPlay2 IDirectPlay2A; + +typedef struct IDirectPlay3 FAR *LPDIRECTPLAY3; +typedef struct IDirectPlay3 FAR *LPDIRECTPLAY3A; +typedef struct IDirectPlay3 IDirectPlay3A; + +/* + * DPID + * DirectPlay player and group ID + */ +typedef DWORD DPID, FAR *LPDPID; + +/* + * DPID that system messages come from + */ +#define DPID_SYSMSG 0 + +/* + * DPID representing all players in the session + */ +#define DPID_ALLPLAYERS 0 + +/* + * DPID representing the server player + */ +#define DPID_SERVERPLAYER 1 + +/* + * The player ID is unknown (used with e.g. DPSESSION_NOMESSAGEID) + */ +#define DPID_UNKNOWN 0xFFFFFFFF + +/* + * DPCAPS + * Used to obtain the capabilities of a DirectPlay object + */ +typedef struct +{ + DWORD dwSize; // Size of structure, in bytes + DWORD dwFlags; // DPCAPS_xxx flags + DWORD dwMaxBufferSize; // Maximum message size, in bytes, for this service provider + DWORD dwMaxQueueSize; // Obsolete. + DWORD dwMaxPlayers; // Maximum players/groups (local + remote) + DWORD dwHundredBaud; // Bandwidth in 100 bits per second units; + // i.e. 24 is 2400, 96 is 9600, etc. + DWORD dwLatency; // Estimated latency; 0 = unknown + DWORD dwMaxLocalPlayers; // Maximum # of locally created players allowed + DWORD dwHeaderLength; // Maximum header length, in bytes, on messages + // added by the service provider + DWORD dwTimeout; // Service provider's suggested timeout value + // This is how long DirectPlay will wait for + // responses to system messages +} DPCAPS, FAR *LPDPCAPS; + +/* + * This DirectPlay object is the session host. If the host exits the + * session, another application will become the host and receive a + * DPSYS_HOST system message. + */ +#define DPCAPS_ISHOST 0x00000002 + +/* + * The service provider bound to this DirectPlay object can optimize + * group messaging. + */ +#define DPCAPS_GROUPOPTIMIZED 0x00000008 + +/* + * The service provider bound to this DirectPlay object can optimize + * keep alives (see DPSESSION_KEEPALIVE) + */ +#define DPCAPS_KEEPALIVEOPTIMIZED 0x00000010 + +/* + * The service provider bound to this DirectPlay object can optimize + * guaranteed message delivery. + */ +#define DPCAPS_GUARANTEEDOPTIMIZED 0x00000020 + +/* + * This DirectPlay object supports guaranteed message delivery. + */ +#define DPCAPS_GUARANTEEDSUPPORTED 0x00000040 + +/* + * This DirectPlay object supports digital signing of messages. + */ +#define DPCAPS_SIGNINGSUPPORTED 0x00000080 + +/* + * This DirectPlay object supports encryption of messages. + */ +#define DPCAPS_ENCRYPTIONSUPPORTED 0x00000100 + + +/* + * DPSESSIONDESC2 + * Used to describe the properties of a DirectPlay + * session instance + */ +typedef struct +{ + DWORD dwSize; // Size of structure + DWORD dwFlags; // DPSESSION_xxx flags + GUID guidInstance; // ID for the session instance + GUID guidApplication; // GUID of the DirectPlay application. + // GUID_NULL for all applications. + DWORD dwMaxPlayers; // Maximum # players allowed in session + DWORD dwCurrentPlayers; // Current # players in session (read only) + union + { // Name of the session + LPWSTR lpszSessionName; // Unicode + LPSTR lpszSessionNameA; // ANSI + }; + union + { // Password of the session (optional) + LPWSTR lpszPassword; // Unicode + LPSTR lpszPasswordA; // ANSI + }; + DWORD dwReserved1; // Reserved for future MS use. + DWORD dwReserved2; + DWORD dwUser1; // For use by the application + DWORD dwUser2; + DWORD dwUser3; + DWORD dwUser4; +} DPSESSIONDESC2, FAR *LPDPSESSIONDESC2; + +/* + * LPCDPSESSIONDESC2 + * A constant pointer to DPSESSIONDESC2 + */ +typedef const DPSESSIONDESC2 FAR *LPCDPSESSIONDESC2; + +/* + * Applications cannot create new players in this session. + */ +#define DPSESSION_NEWPLAYERSDISABLED 0x00000001 + +/* + * If the DirectPlay object that created the session, the host, + * quits, then the host will attempt to migrate to another + * DirectPlay object so that new players can continue to be created + * and new applications can join the session. + */ +#define DPSESSION_MIGRATEHOST 0x00000004 + +/* + * This flag tells DirectPlay not to set the idPlayerTo and idPlayerFrom + * fields in player messages. This cuts two DWORD's off the message + * overhead. + */ +#define DPSESSION_NOMESSAGEID 0x00000008 + + +/* + * This flag tells DirectPlay to not allow any new applications to + * join the session. Applications already in the session can still + * create new players. + */ +#define DPSESSION_JOINDISABLED 0x00000020 + +/* + * This flag tells DirectPlay to detect when remote players + * exit abnormally (e.g. their computer or modem gets unplugged) + */ +#define DPSESSION_KEEPALIVE 0x00000040 + +/* + * This flag tells DirectPlay not to send a message to all players + * when a players remote data changes + */ +#define DPSESSION_NODATAMESSAGES 0x00000080 + +/* + * This flag indicates that the session belongs to a secure server + * and needs user authentication + */ +#define DPSESSION_SECURESERVER 0x00000100 + +/* + * This flag indicates that the session is private and requirs a password + * for EnumSessions as well as Open. + */ +#define DPSESSION_PRIVATE 0x00000200 + +/* + * This flag indicates that the session requires a password for joining. + */ +#define DPSESSION_PASSWORDREQUIRED 0x00000400 + +/* + * This flag tells DirectPlay to route all messages through the server + */ +#define DPSESSION_MULTICASTSERVER 0x00000800 + +/* + * This flag tells DirectPlay to only download information about the + * DPPLAYER_SERVERPLAYER. + */ +#define DPSESSION_CLIENTSERVER 0x00001000 + +/* + * DPNAME + * Used to hold the name of a DirectPlay entity + * like a player or a group + */ +typedef struct +{ + DWORD dwSize; // Size of structure + DWORD dwFlags; // Not used. Must be zero. + union + { // The short or friendly name + LPWSTR lpszShortName; // Unicode + LPSTR lpszShortNameA; // ANSI + }; + union + { // The long or formal name + LPWSTR lpszLongName; // Unicode + LPSTR lpszLongNameA; // ANSI + }; + +} DPNAME, FAR *LPDPNAME; + +/* + * LPCDPNAME + * A constant pointer to DPNAME + */ +typedef const DPNAME FAR *LPCDPNAME; + +/* + * DPCREDENTIALS + * Used to hold the user name and password of a DirectPlay user + */ +typedef struct +{ + DWORD dwSize; // Size of structure + DWORD dwFlags; // Not used. Must be zero. + union + { // User name of the account + LPWSTR lpszUsername; // Unicode + LPSTR lpszUsernameA; // ANSI + }; + union + { // Password of the account + LPWSTR lpszPassword; // Unicode + LPSTR lpszPasswordA; // ANSI + }; + union + { // Domain name of the account + LPWSTR lpszDomain; // Unicode + LPSTR lpszDomainA; // ANSI + }; +} DPCREDENTIALS, FAR *LPDPCREDENTIALS; + +typedef const DPCREDENTIALS FAR *LPCDPCREDENTIALS; + +/* + * DPSECURITYDESC + * Used to describe the security properties of a DirectPlay + * session instance + */ +typedef struct +{ + DWORD dwSize; // Size of structure + DWORD dwFlags; // Not used. Must be zero. + union + { // SSPI provider name + LPWSTR lpszSSPIProvider; // Unicode + LPSTR lpszSSPIProviderA; // ANSI + }; + union + { // CAPI provider name + LPWSTR lpszCAPIProvider; // Unicode + LPSTR lpszCAPIProviderA; // ANSI + }; + DWORD dwCAPIProviderType; // Crypto Service Provider type + DWORD dwEncryptionAlgorithm; // Encryption Algorithm type +} DPSECURITYDESC, FAR *LPDPSECURITYDESC; + +typedef const DPSECURITYDESC FAR *LPCDPSECURITYDESC; + +/* + * DPACCOUNTDESC + * Used to describe a user membership account + */ +typedef struct +{ + DWORD dwSize; // Size of structure + DWORD dwFlags; // Not used. Must be zero. + union + { // Account identifier + LPWSTR lpszAccountID; // Unicode + LPSTR lpszAccountIDA; // ANSI + }; +} DPACCOUNTDESC, FAR *LPDPACCOUNTDESC; + +typedef const DPACCOUNTDESC FAR *LPCDPACCOUNTDESC; + +/* + * LPCGUID + * A constant pointer to a guid + */ +typedef const GUID FAR *LPCGUID; + +/* + * DPLCONNECTION + * Used to hold all in the informaion needed to connect + * an application to a session or create a session + */ +typedef struct +{ + DWORD dwSize; // Size of this structure + DWORD dwFlags; // Flags specific to this structure + LPDPSESSIONDESC2 lpSessionDesc; // Pointer to session desc to use on connect + LPDPNAME lpPlayerName; // Pointer to Player name structure + GUID guidSP; // GUID of the DPlay SP to use + LPVOID lpAddress; // Address for service provider + DWORD dwAddressSize; // Size of address data +} DPLCONNECTION, FAR *LPDPLCONNECTION; + +/* + * LPCDPLCONNECTION + * A constant pointer to DPLCONNECTION + */ +typedef const DPLCONNECTION FAR *LPCDPLCONNECTION; + +/* + * DPCHAT + * Used to hold the a DirectPlay chat message + */ +typedef struct +{ + DWORD dwSize; + DWORD dwFlags; + union + { // Message string + LPWSTR lpszMessage; // Unicode + LPSTR lpszMessageA; // ANSI + }; +} DPCHAT, FAR * LPDPCHAT; + +/**************************************************************************** + * + * Prototypes for DirectPlay callback functions + * + ****************************************************************************/ + +/* + * Callback for IDirectPlay2::EnumSessions + */ +typedef BOOL (FAR PASCAL * LPDPENUMSESSIONSCALLBACK2)( + LPCDPSESSIONDESC2 lpThisSD, + LPDWORD lpdwTimeOut, + DWORD dwFlags, + LPVOID lpContext ); + +/* + * This flag is set on the EnumSessions callback dwFlags parameter when + * the time out has occurred. There will be no session data for this + * callback. If *lpdwTimeOut is set to a non-zero value and the + * EnumSessionsCallback function returns TRUE then EnumSessions will + * continue waiting until the next timeout occurs. Timeouts are in + * milliseconds. + */ +#define DPESC_TIMEDOUT 0x00000001 + + +/* + * Callback for IDirectPlay2::EnumPlayers + * IDirectPlay2::EnumGroups + * IDirectPlay2::EnumGroupPlayers + */ +typedef BOOL (FAR PASCAL *LPDPENUMPLAYERSCALLBACK2)( + DPID dpId, + DWORD dwPlayerType, + LPCDPNAME lpName, + DWORD dwFlags, + LPVOID lpContext ); + + +/* + * Unicode callback for DirectPlayEnumerate + * This callback prototype will be used if compiling + * for Unicode strings + */ +typedef BOOL (FAR PASCAL * LPDPENUMDPCALLBACK)( + LPGUID lpguidSP, + LPWSTR lpSPName, + DWORD dwMajorVersion, + DWORD dwMinorVersion, + LPVOID lpContext); + +/* + * ANSI callback for DirectPlayEnumerate + * This callback prototype will be used if compiling + * for ANSI strings + */ +typedef BOOL (FAR PASCAL * LPDPENUMDPCALLBACKA)( + LPGUID lpguidSP, + LPSTR lpSPName, + DWORD dwMajorVersion, + DWORD dwMinorVersion, + LPVOID lpContext); + +/* + * Callback for IDirectPlay3(A)::EnumConnections + */ +typedef BOOL (FAR PASCAL * LPDPENUMCONNECTIONSCALLBACK)( + LPCGUID lpguidSP, + LPVOID lpConnection, + DWORD dwConnectionSize, + LPCDPNAME lpName, + DWORD dwFlags, + LPVOID lpContext); + + +/* + * API's + */ + +#ifdef UNICODE +#define DirectPlayEnumerate DirectPlayEnumerateW +#else +#define DirectPlayEnumerate DirectPlayEnumerateA +#endif // UNICODE + +extern HRESULT WINAPI DirectPlayEnumerateA( LPDPENUMDPCALLBACKA, LPVOID ); +extern HRESULT WINAPI DirectPlayEnumerateW( LPDPENUMDPCALLBACK, LPVOID ); +extern HRESULT WINAPI DirectPlayCreate( LPGUID lpGUID, LPDIRECTPLAY *lplpDP, IUnknown *pUnk); + +/**************************************************************************** + * + * IDirectPlay2 (and IDirectPlay2A) Interface + * + ****************************************************************************/ + +#undef INTERFACE +#define INTERFACE IDirectPlay2 +DECLARE_INTERFACE_( IDirectPlay2, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectPlay2 methods ***/ + STDMETHOD(AddPlayerToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(Close) (THIS) PURE; + STDMETHOD(CreateGroup) (THIS_ LPDPID,LPDPNAME,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(CreatePlayer) (THIS_ LPDPID,LPDPNAME,HANDLE,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(DeletePlayerFromGroup)(THIS_ DPID,DPID) PURE; + STDMETHOD(DestroyGroup) (THIS_ DPID) PURE; + STDMETHOD(DestroyPlayer) (THIS_ DPID) PURE; + STDMETHOD(EnumGroupPlayers) (THIS_ DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroups) (THIS_ LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumPlayers) (THIS_ LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumSessions) (THIS_ LPDPSESSIONDESC2,DWORD,LPDPENUMSESSIONSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(GetCaps) (THIS_ LPDPCAPS,DWORD) PURE; + STDMETHOD(GetGroupData) (THIS_ DPID,LPVOID,LPDWORD,DWORD) PURE; + STDMETHOD(GetGroupName) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetMessageCount) (THIS_ DPID, LPDWORD) PURE; + STDMETHOD(GetPlayerAddress) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetPlayerCaps) (THIS_ DPID,LPDPCAPS,DWORD) PURE; + STDMETHOD(GetPlayerData) (THIS_ DPID,LPVOID,LPDWORD,DWORD) PURE; + STDMETHOD(GetPlayerName) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetSessionDesc) (THIS_ LPVOID,LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; + STDMETHOD(Open) (THIS_ LPDPSESSIONDESC2,DWORD) PURE; + STDMETHOD(Receive) (THIS_ LPDPID,LPDPID,DWORD,LPVOID,LPDWORD) PURE; + STDMETHOD(Send) (THIS_ DPID, DPID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetGroupData) (THIS_ DPID,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(SetGroupName) (THIS_ DPID,LPDPNAME,DWORD) PURE; + STDMETHOD(SetPlayerData) (THIS_ DPID,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(SetPlayerName) (THIS_ DPID,LPDPNAME,DWORD) PURE; + STDMETHOD(SetSessionDesc) (THIS_ LPDPSESSIONDESC2,DWORD) PURE; +}; + +/**************************************************************************** + * + * IDirectPlay2 interface macros + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlay2_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectPlay2_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectPlay2_Release(p) (p)->lpVtbl->Release(p) +#define IDirectPlay2_AddPlayerToGroup(p,a,b) (p)->lpVtbl->AddPlayerToGroup(p,a,b) +#define IDirectPlay2_Close(p) (p)->lpVtbl->Close(p) +#define IDirectPlay2_CreateGroup(p,a,b,c,d,e) (p)->lpVtbl->CreateGroup(p,a,b,c,d,e) +#define IDirectPlay2_CreatePlayer(p,a,b,c,d,e,f) (p)->lpVtbl->CreatePlayer(p,a,b,c,d,e,f) +#define IDirectPlay2_DeletePlayerFromGroup(p,a,b) (p)->lpVtbl->DeletePlayerFromGroup(p,a,b) +#define IDirectPlay2_DestroyGroup(p,a) (p)->lpVtbl->DestroyGroup(p,a) +#define IDirectPlay2_DestroyPlayer(p,a) (p)->lpVtbl->DestroyPlayer(p,a) +#define IDirectPlay2_EnumGroupPlayers(p,a,b,c,d,e) (p)->lpVtbl->EnumGroupPlayers(p,a,b,c,d,e) +#define IDirectPlay2_EnumGroups(p,a,b,c,d) (p)->lpVtbl->EnumGroups(p,a,b,c,d) +#define IDirectPlay2_EnumPlayers(p,a,b,c,d) (p)->lpVtbl->EnumPlayers(p,a,b,c,d) +#define IDirectPlay2_EnumSessions(p,a,b,c,d,e) (p)->lpVtbl->EnumSessions(p,a,b,c,d,e) +#define IDirectPlay2_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b) +#define IDirectPlay2_GetMessageCount(p,a,b) (p)->lpVtbl->GetMessageCount(p,a,b) +#define IDirectPlay2_GetGroupData(p,a,b,c,d) (p)->lpVtbl->GetGroupData(p,a,b,c,d) +#define IDirectPlay2_GetGroupName(p,a,b,c) (p)->lpVtbl->GetGroupName(p,a,b,c) +#define IDirectPlay2_GetPlayerAddress(p,a,b,c) (p)->lpVtbl->GetPlayerAddress(p,a,b,c) +#define IDirectPlay2_GetPlayerCaps(p,a,b,c) (p)->lpVtbl->GetPlayerCaps(p,a,b,c) +#define IDirectPlay2_GetPlayerData(p,a,b,c,d) (p)->lpVtbl->GetPlayerData(p,a,b,c,d) +#define IDirectPlay2_GetPlayerName(p,a,b,c) (p)->lpVtbl->GetPlayerName(p,a,b,c) +#define IDirectPlay2_GetSessionDesc(p,a,b) (p)->lpVtbl->GetSessionDesc(p,a,b) +#define IDirectPlay2_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirectPlay2_Open(p,a,b) (p)->lpVtbl->Open(p,a,b) +#define IDirectPlay2_Receive(p,a,b,c,d,e) (p)->lpVtbl->Receive(p,a,b,c,d,e) +#define IDirectPlay2_Send(p,a,b,c,d,e) (p)->lpVtbl->Send(p,a,b,c,d,e) +#define IDirectPlay2_SetGroupData(p,a,b,c,d) (p)->lpVtbl->SetGroupData(p,a,b,c,d) +#define IDirectPlay2_SetGroupName(p,a,b,c) (p)->lpVtbl->SetGroupName(p,a,b,c) +#define IDirectPlay2_SetPlayerData(p,a,b,c,d) (p)->lpVtbl->SetPlayerData(p,a,b,c,d) +#define IDirectPlay2_SetPlayerName(p,a,b,c) (p)->lpVtbl->SetPlayerName(p,a,b,c) +#define IDirectPlay2_SetSessionDesc(p,a,b) (p)->lpVtbl->SetSessionDesc(p,a,b) + +#else /* C++ */ + +#define IDirectPlay2_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectPlay2_AddRef(p) (p)->AddRef() +#define IDirectPlay2_Release(p) (p)->Release() +#define IDirectPlay2_AddPlayerToGroup(p,a,b) (p)->AddPlayerToGroup(a,b) +#define IDirectPlay2_Close(p) (p)->Close() +#define IDirectPlay2_CreateGroup(p,a,b,c,d,e) (p)->CreateGroup(a,b,c,d,e) +#define IDirectPlay2_CreatePlayer(p,a,b,c,d,e,f) (p)->CreatePlayer(a,b,c,d,e,f) +#define IDirectPlay2_DeletePlayerFromGroup(p,a,b) (p)->DeletePlayerFromGroup(a,b) +#define IDirectPlay2_DestroyGroup(p,a) (p)->DestroyGroup(a) +#define IDirectPlay2_DestroyPlayer(p,a) (p)->DestroyPlayer(a) +#define IDirectPlay2_EnumGroupPlayers(p,a,b,c,d,e) (p)->EnumGroupPlayers(a,b,c,d,e) +#define IDirectPlay2_EnumGroups(p,a,b,c,d) (p)->EnumGroups(a,b,c,d) +#define IDirectPlay2_EnumPlayers(p,a,b,c,d) (p)->EnumPlayers(a,b,c,d) +#define IDirectPlay2_EnumSessions(p,a,b,c,d,e) (p)->EnumSessions(a,b,c,d,e) +#define IDirectPlay2_GetCaps(p,a,b) (p)->GetCaps(a,b) +#define IDirectPlay2_GetMessageCount(p,a,b) (p)->GetMessageCount(a,b) +#define IDirectPlay2_GetGroupData(p,a,b,c,d) (p)->GetGroupData(a,b,c,d) +#define IDirectPlay2_GetGroupName(p,a,b,c) (p)->GetGroupName(a,b,c) +#define IDirectPlay2_GetPlayerAddress(p,a,b,c) (p)->GetPlayerAddress(a,b,c) +#define IDirectPlay2_GetPlayerCaps(p,a,b,c) (p)->GetPlayerCaps(a,b,c) +#define IDirectPlay2_GetPlayerData(p,a,b,c,d) (p)->GetPlayerData(a,b,c,d) +#define IDirectPlay2_GetPlayerName(p,a,b,c) (p)->GetPlayerName(a,b,c) +#define IDirectPlay2_GetSessionDesc(p,a,b) (p)->GetSessionDesc(a,b) +#define IDirectPlay2_Initialize(p,a) (p)->Initialize(a) +#define IDirectPlay2_Open(p,a,b) (p)->Open(a,b) +#define IDirectPlay2_Receive(p,a,b,c,d,e) (p)->Receive(a,b,c,d,e) +#define IDirectPlay2_Send(p,a,b,c,d,e) (p)->Send(a,b,c,d,e) +#define IDirectPlay2_SetGroupData(p,a,b,c,d) (p)->SetGroupData(a,b,c,d) +#define IDirectPlay2_SetGroupName(p,a,b,c) (p)->SetGroupName(a,b,c) +#define IDirectPlay2_SetPlayerData(p,a,b,c,d) (p)->SetPlayerData(a,b,c,d) +#define IDirectPlay2_SetPlayerName(p,a,b,c) (p)->SetPlayerName(a,b,c) +#define IDirectPlay2_SetSessionDesc(p,a,b) (p)->SetSessionDesc(a,b) + +#endif + +/**************************************************************************** + * + * IDirectPlay3 (and IDirectPlay3A) Interface + * + ****************************************************************************/ + +#undef INTERFACE +#define INTERFACE IDirectPlay3 +DECLARE_INTERFACE_( IDirectPlay3, IDirectPlay2 ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectPlay2 methods ***/ + STDMETHOD(AddPlayerToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(Close) (THIS) PURE; + STDMETHOD(CreateGroup) (THIS_ LPDPID,LPDPNAME,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(CreatePlayer) (THIS_ LPDPID,LPDPNAME,HANDLE,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(DeletePlayerFromGroup)(THIS_ DPID,DPID) PURE; + STDMETHOD(DestroyGroup) (THIS_ DPID) PURE; + STDMETHOD(DestroyPlayer) (THIS_ DPID) PURE; + STDMETHOD(EnumGroupPlayers) (THIS_ DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroups) (THIS_ LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumPlayers) (THIS_ LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(EnumSessions) (THIS_ LPDPSESSIONDESC2,DWORD,LPDPENUMSESSIONSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(GetCaps) (THIS_ LPDPCAPS,DWORD) PURE; + STDMETHOD(GetGroupData) (THIS_ DPID,LPVOID,LPDWORD,DWORD) PURE; + STDMETHOD(GetGroupName) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetMessageCount) (THIS_ DPID, LPDWORD) PURE; + STDMETHOD(GetPlayerAddress) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetPlayerCaps) (THIS_ DPID,LPDPCAPS,DWORD) PURE; + STDMETHOD(GetPlayerData) (THIS_ DPID,LPVOID,LPDWORD,DWORD) PURE; + STDMETHOD(GetPlayerName) (THIS_ DPID,LPVOID,LPDWORD) PURE; + STDMETHOD(GetSessionDesc) (THIS_ LPVOID,LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; + STDMETHOD(Open) (THIS_ LPDPSESSIONDESC2,DWORD) PURE; + STDMETHOD(Receive) (THIS_ LPDPID,LPDPID,DWORD,LPVOID,LPDWORD) PURE; + STDMETHOD(Send) (THIS_ DPID, DPID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetGroupData) (THIS_ DPID,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(SetGroupName) (THIS_ DPID,LPDPNAME,DWORD) PURE; + STDMETHOD(SetPlayerData) (THIS_ DPID,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(SetPlayerName) (THIS_ DPID,LPDPNAME,DWORD) PURE; + STDMETHOD(SetSessionDesc) (THIS_ LPDPSESSIONDESC2,DWORD) PURE; + /*** IDirectPlay3 methods ***/ + STDMETHOD(AddGroupToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(CreateGroupInGroup) (THIS_ DPID,LPDPID,LPDPNAME,LPVOID,DWORD,DWORD) PURE; + STDMETHOD(DeleteGroupFromGroup) (THIS_ DPID,DPID) PURE; + STDMETHOD(EnumConnections) (THIS_ LPCGUID,LPDPENUMCONNECTIONSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroupsInGroup) (THIS_ DPID,LPGUID,LPDPENUMPLAYERSCALLBACK2,LPVOID,DWORD) PURE; + STDMETHOD(GetGroupConnectionSettings)(THIS_ DWORD, DPID, LPVOID, LPDWORD) PURE; + STDMETHOD(InitializeConnection) (THIS_ LPVOID,DWORD) PURE; + STDMETHOD(SecureOpen) (THIS_ LPCDPSESSIONDESC2,DWORD,LPCDPSECURITYDESC,LPCDPCREDENTIALS) PURE; + STDMETHOD(SendChatMessage) (THIS_ DPID,DPID,DWORD,LPDPCHAT); + STDMETHOD(SetGroupConnectionSettings)(THIS_ DWORD,DPID,LPDPLCONNECTION) PURE; + STDMETHOD(StartSession) (THIS_ DWORD,DPID); + STDMETHOD(GetGroupFlags) (THIS_ DPID,LPDWORD); + STDMETHOD(GetGroupParent) (THIS_ DPID,LPDPID); + STDMETHOD(GetPlayerAccount) (THIS_ DPID, DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(GetPlayerFlags) (THIS_ DPID,LPDWORD); +}; + +/**************************************************************************** + * + * IDirectPlay3 interface macros + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlay3_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectPlay3_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectPlay3_Release(p) (p)->lpVtbl->Release(p) +#define IDirectPlay3_AddPlayerToGroup(p,a,b) (p)->lpVtbl->AddPlayerToGroup(p,a,b) +#define IDirectPlay3_Close(p) (p)->lpVtbl->Close(p) +#define IDirectPlay3_CreateGroup(p,a,b,c,d,e) (p)->lpVtbl->CreateGroup(p,a,b,c,d,e) +#define IDirectPlay3_CreatePlayer(p,a,b,c,d,e,f) (p)->lpVtbl->CreatePlayer(p,a,b,c,d,e,f) +#define IDirectPlay3_DeletePlayerFromGroup(p,a,b) (p)->lpVtbl->DeletePlayerFromGroup(p,a,b) +#define IDirectPlay3_DestroyGroup(p,a) (p)->lpVtbl->DestroyGroup(p,a) +#define IDirectPlay3_DestroyPlayer(p,a) (p)->lpVtbl->DestroyPlayer(p,a) +#define IDirectPlay3_EnumGroupPlayers(p,a,b,c,d,e) (p)->lpVtbl->EnumGroupPlayers(p,a,b,c,d,e) +#define IDirectPlay3_EnumGroups(p,a,b,c,d) (p)->lpVtbl->EnumGroups(p,a,b,c,d) +#define IDirectPlay3_EnumPlayers(p,a,b,c,d) (p)->lpVtbl->EnumPlayers(p,a,b,c,d) +#define IDirectPlay3_EnumSessions(p,a,b,c,d,e) (p)->lpVtbl->EnumSessions(p,a,b,c,d,e) +#define IDirectPlay3_GetCaps(p,a,b) (p)->lpVtbl->GetCaps(p,a,b) +#define IDirectPlay3_GetMessageCount(p,a,b) (p)->lpVtbl->GetMessageCount(p,a,b) +#define IDirectPlay3_GetGroupData(p,a,b,c,d) (p)->lpVtbl->GetGroupData(p,a,b,c,d) +#define IDirectPlay3_GetGroupName(p,a,b,c) (p)->lpVtbl->GetGroupName(p,a,b,c) +#define IDirectPlay3_GetPlayerAddress(p,a,b,c) (p)->lpVtbl->GetPlayerAddress(p,a,b,c) +#define IDirectPlay3_GetPlayerCaps(p,a,b,c) (p)->lpVtbl->GetPlayerCaps(p,a,b,c) +#define IDirectPlay3_GetPlayerData(p,a,b,c,d) (p)->lpVtbl->GetPlayerData(p,a,b,c,d) +#define IDirectPlay3_GetPlayerName(p,a,b,c) (p)->lpVtbl->GetPlayerName(p,a,b,c) +#define IDirectPlay3_GetSessionDesc(p,a,b) (p)->lpVtbl->GetSessionDesc(p,a,b) +#define IDirectPlay3_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirectPlay3_Open(p,a,b) (p)->lpVtbl->Open(p,a,b) +#define IDirectPlay3_Receive(p,a,b,c,d,e) (p)->lpVtbl->Receive(p,a,b,c,d,e) +#define IDirectPlay3_Send(p,a,b,c,d,e) (p)->lpVtbl->Send(p,a,b,c,d,e) +#define IDirectPlay3_SetGroupData(p,a,b,c,d) (p)->lpVtbl->SetGroupData(p,a,b,c,d) +#define IDirectPlay3_SetGroupName(p,a,b,c) (p)->lpVtbl->SetGroupName(p,a,b,c) +#define IDirectPlay3_SetPlayerData(p,a,b,c,d) (p)->lpVtbl->SetPlayerData(p,a,b,c,d) +#define IDirectPlay3_SetPlayerName(p,a,b,c) (p)->lpVtbl->SetPlayerName(p,a,b,c) +#define IDirectPlay3_SetSessionDesc(p,a,b) (p)->lpVtbl->SetSessionDesc(p,a,b) +#define IDirectPlay3_AddGroupToGroup(p,a,b) (p)->lpVtbl->AddGroupToGroup(p,a,b) +#define IDirectPlay3_CreateGroupInGroup(p,a,b,c,d,e,f) (p)->lpVtbl->CreateGroupInGroup(p,a,b,c,d,e,f) +#define IDirectPlay3_DeleteGroupFromGroup(p,a,b) (p)->lpVtbl->DeleteGroupFromGroup(p,a,b) +#define IDirectPlay3_EnumConnections(p,a,b,c,d) (p)->lpVtbl->EnumConnections(p,a,b,c,d) +#define IDirectPlay3_EnumGroupsInGroup(p,a,b,c,d,e) (p)->lpVtbl->EnumGroupsInGroup(p,a,b,c,d,e) +#define IDirectPlay3_GetGroupConnectionSettings(p,a,b,c,d) (p)->lpVtbl->GetGroupConnectionSettings(p,a,b,c,d) +#define IDirectPlay3_InitializeConnection(p,a,b) (p)->lpVtbl->InitializeConnection(p,a,b) +#define IDirectPlay3_SecureOpen(p,a,b,c,d) (p)->lpVtbl->SecureOpen(p,a,b,c,d) +#define IDirectPlay3_SendChatMessage(p,a,b,c,d) (p)->lpVtbl->SendChatMessage(p,a,b,c,d) +#define IDirectPlay3_SetGroupConnectionSettings(p,a,b,c) (p)->lpVtbl->SetGroupConnectionSettings(p,a,b,c) +#define IDirectPlay3_StartSession(p,a,b) (p)->lpVtbl->StartSession(p,a,b) +#define IDirectPlay3_GetGroupFlags(p,a,b) (p)->lpVtbl->GetGroupFlags(p,a,b) +#define IDirectPlay3_GetGroupParent(p,a,b) (p)->lpVtbl->GetGroupParent(p,a,b) +#define IDirectPlay3_GetPlayerAccount(p,a,b,c,d) (p)->lpVtbl->GetPlayerAccount(p,a,b,c,d) +#define IDirectPlay3_GetPlayerFlags(p,a,b) (p)->lpVtbl->GetPlayerFlags(p,a,b) + +#else /* C++ */ + +#define IDirectPlay3_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectPlay3_AddRef(p) (p)->AddRef() +#define IDirectPlay3_Release(p) (p)->Release() +#define IDirectPlay3_AddPlayerToGroup(p,a,b) (p)->AddPlayerToGroup(a,b) +#define IDirectPlay3_Close(p) (p)->Close() +#define IDirectPlay3_CreateGroup(p,a,b,c,d,e) (p)->CreateGroup(a,b,c,d,e) +#define IDirectPlay3_CreatePlayer(p,a,b,c,d,e,f) (p)->CreatePlayer(a,b,c,d,e,f) +#define IDirectPlay3_DeletePlayerFromGroup(p,a,b) (p)->DeletePlayerFromGroup(a,b) +#define IDirectPlay3_DestroyGroup(p,a) (p)->DestroyGroup(a) +#define IDirectPlay3_DestroyPlayer(p,a) (p)->DestroyPlayer(a) +#define IDirectPlay3_EnumGroupPlayers(p,a,b,c,d,e) (p)->EnumGroupPlayers(a,b,c,d,e) +#define IDirectPlay3_EnumGroups(p,a,b,c,d) (p)->EnumGroups(a,b,c,d) +#define IDirectPlay3_EnumPlayers(p,a,b,c,d) (p)->EnumPlayers(a,b,c,d) +#define IDirectPlay3_EnumSessions(p,a,b,c,d,e) (p)->EnumSessions(a,b,c,d,e) +#define IDirectPlay3_GetCaps(p,a,b) (p)->GetCaps(a,b) +#define IDirectPlay3_GetMessageCount(p,a,b) (p)->GetMessageCount(a,b) +#define IDirectPlay3_GetGroupData(p,a,b,c,d) (p)->GetGroupData(a,b,c,d) +#define IDirectPlay3_GetGroupName(p,a,b,c) (p)->GetGroupName(a,b,c) +#define IDirectPlay3_GetPlayerAddress(p,a,b,c) (p)->GetPlayerAddress(a,b,c) +#define IDirectPlay3_GetPlayerCaps(p,a,b,c) (p)->GetPlayerCaps(a,b,c) +#define IDirectPlay3_GetPlayerData(p,a,b,c,d) (p)->GetPlayerData(a,b,c,d) +#define IDirectPlay3_GetPlayerName(p,a,b,c) (p)->GetPlayerName(a,b,c) +#define IDirectPlay3_GetSessionDesc(p,a,b) (p)->GetSessionDesc(a,b) +#define IDirectPlay3_Initialize(p,a) (p)->Initialize(a) +#define IDirectPlay3_Open(p,a,b) (p)->Open(a,b) +#define IDirectPlay3_Receive(p,a,b,c,d,e) (p)->Receive(a,b,c,d,e) +#define IDirectPlay3_Send(p,a,b,c,d,e) (p)->Send(a,b,c,d,e) +#define IDirectPlay3_SetGroupData(p,a,b,c,d) (p)->SetGroupData(a,b,c,d) +#define IDirectPlay3_SetGroupName(p,a,b,c) (p)->SetGroupName(a,b,c) +#define IDirectPlay3_SetPlayerData(p,a,b,c,d) (p)->SetPlayerData(a,b,c,d) +#define IDirectPlay3_SetPlayerName(p,a,b,c) (p)->SetPlayerName(a,b,c) +#define IDirectPlay3_SetSessionDesc(p,a,b) (p)->SetSessionDesc(a,b) +#define IDirectPlay3_AddGroupToGroup(p,a,b) (p)->AddGroupToGroup(a,b) +#define IDirectPlay3_CreateGroupInGroup(p,a,b,c,d,e,f) (p)->CreateGroupInGroup(a,b,c,d,e,f) +#define IDirectPlay3_DeleteGroupFromGroup(p,a,b) (p)->DeleteGroupFromGroup(a,b) +#define IDirectPlay3_EnumConnections(p,a,b,c,d) (p)->EnumConnections(a,b,c,d) +#define IDirectPlay3_EnumGroupsInGroup(p,a,b,c,d,e) (p)->EnumGroupsInGroup(a,b,c,d,e) +#define IDirectPlay3_GetGroupConnectionSettings(p,a,b,c,d) (p)->GetGroupConnectionSettings(a,b,c,d) +#define IDirectPlay3_InitializeConnection(p,a,b) (p)->InitializeConnection(a,b) +#define IDirectPlay3_SecureOpen(p,a,b,c,d) (p)->SecureOpen(a,b,c,d) +#define IDirectPlay3_SendChatMessage(p,a,b,c,d) (p)->SendChatMessage(a,b,c,d) +#define IDirectPlay3_SetGroupConnectionSettings(p,a,b,c) (p)->SetGroupConnectionSettings(a,b,c) +#define IDirectPlay3_StartSession(p,a,b) (p)->StartSession(a,b) +#define IDirectPlay3_GetGroupFlags(p,a,b) (p)->GetGroupFlags(a,b) +#define IDirectPlay3_GetGroupParent(p,a,b) (p)->GetGroupParent(a,b) +#define IDirectPlay3_GetPlayerAccount(p,a,b,c,d) (p)->GetPlayerAccount(a,b,c,d) +#define IDirectPlay3_GetPlayerFlags(p,a,b) (p)->GetPlayerFlags(a,b) + +#endif + +/**************************************************************************** + * + * EnumConnections API flags + * + ****************************************************************************/ + +/* + * Enumerate Service Providers + */ +#define DPCONNECTION_DIRECTPLAY 0x00000001 + +/* + * Enumerate Lobby Providers + */ +#define DPCONNECTION_DIRECTPLAYLOBBY 0x00000002 + + +/**************************************************************************** + * + * EnumPlayers API flags + * + ****************************************************************************/ + +/* + * Enumerate all players in the current session + */ +#define DPENUMPLAYERS_ALL 0x00000000 +#define DPENUMGROUPS_ALL DPENUMPLAYERS_ALL + + +/* + * Enumerate only local (created by this application) players + * or groups + */ +#define DPENUMPLAYERS_LOCAL 0x00000008 +#define DPENUMGROUPS_LOCAL DPENUMPLAYERS_LOCAL + +/* + * Enumerate only remote (non-local) players + * or groups + */ +#define DPENUMPLAYERS_REMOTE 0x00000010 +#define DPENUMGROUPS_REMOTE DPENUMPLAYERS_REMOTE + +/* + * Enumerate groups along with the players + */ +#define DPENUMPLAYERS_GROUP 0x00000020 + +/* + * Enumerate players or groups in another session + * (must supply lpguidInstance) + */ +#define DPENUMPLAYERS_SESSION 0x00000080 +#define DPENUMGROUPS_SESSION DPENUMPLAYERS_SESSION + +/* + * Enumerate server players + */ +#define DPENUMPLAYERS_SERVERPLAYER 0x00000100 + +/* + * Enumerate spectator players + */ +#define DPENUMPLAYERS_SPECTATOR 0x00000200 + +/* + * Enumerate shortcut groups + */ +#define DPENUMGROUPS_SHORTCUT 0x00000400 + +/* + * Enumerate staging area groups + */ +#define DPENUMGROUPS_STAGINGAREA 0x00000800 + +/**************************************************************************** + * + * CreatePlayer API flags + * + ****************************************************************************/ + +/* + * This flag indicates that this player should be designated + * the server player. The app should specify this at CreatePlayer. + */ +#define DPPLAYER_SERVERPLAYER DPENUMPLAYERS_SERVERPLAYER + +/* + * This flag indicates that this player should be designated + * a spectator. The app should specify this at CreatePlayer. + */ +#define DPPLAYER_SPECTATOR DPENUMPLAYERS_SPECTATOR + +/* + * This flag indicates that this player was created locally. + * (returned from GetPlayerFlags) + */ +#define DPPLAYER_LOCAL DPENUMPLAYERS_LOCAL + +/**************************************************************************** + * + * CreateGroup API flags + * + ****************************************************************************/ + + +/* + * This flag indicates that the StartSession can be called on the group. + * The app should specify this at CreateGroup, or CreateGroupInGroup. + */ +#define DPGROUP_STAGINGAREA DPENUMGROUPS_STAGINGAREA + +/* + * This flag indicates that this group was created locally. + * (returned from GetGroupFlags) + */ +#define DPGROUP_LOCAL DPENUMGROUPS_LOCAL + +/**************************************************************************** + * + * EnumSessions API flags + * + ****************************************************************************/ + +/* + * Enumerate sessions which can be joined + */ +#define DPENUMSESSIONS_AVAILABLE 0x00000001 + +/* + * Enumerate all sessions even if they can't be joined. + */ +#define DPENUMSESSIONS_ALL 0x00000002 + + + + +/* + * Start an asynchronous enum sessions + */ + #define DPENUMSESSIONS_ASYNC 0x00000010 + +/* + * Stop an asynchronous enum sessions + */ + #define DPENUMSESSIONS_STOPASYNC 0x00000020 + +/* + * Enumerate sessions even if they require a password + */ + #define DPENUMSESSIONS_PASSWORDREQUIRED 0x00000040 + +/* + * Return status about progress of enumeration instead of + * showing any status dialogs. + */ + #define DPENUMSESSIONS_RETURNSTATUS 0x00000080 + +/**************************************************************************** + * + * GetCaps and GetPlayerCaps API flags + * + ****************************************************************************/ + +/* + * The latency returned should be for guaranteed message sending. + * Default is non-guaranteed messaging. + */ +#define DPGETCAPS_GUARANTEED 0x00000001 + + +/**************************************************************************** + * + * GetGroupData, GetPlayerData API flags + * Remote and local Group/Player data is maintained separately. + * Default is DPGET_REMOTE. + * + ****************************************************************************/ + +/* + * Get the remote data (set by any DirectPlay object in + * the session using DPSET_REMOTE) + */ +#define DPGET_REMOTE 0x00000000 + +/* + * Get the local data (set by this DirectPlay object + * using DPSET_LOCAL) + */ +#define DPGET_LOCAL 0x00000001 + + +/**************************************************************************** + * + * Open API flags + * + ****************************************************************************/ + +/* + * Join the session that is described by the DPSESSIONDESC2 structure + */ +#define DPOPEN_JOIN 0x00000001 + +/* + * Create a new session as described by the DPSESSIONDESC2 structure + */ +#define DPOPEN_CREATE 0x00000002 + +/* + * Return status about progress of open instead of showing + * any status dialogs. + */ + #define DPOPEN_RETURNSTATUS DPENUMSESSIONS_RETURNSTATUS + +/**************************************************************************** + * + * DPLCONNECTION flags + * + ****************************************************************************/ + +/* + * This application should create a new session as + * described by the DPSESIONDESC structure + */ +#define DPLCONNECTION_CREATESESSION DPOPEN_CREATE + +/* + * This application should join the session described by + * the DPSESIONDESC structure with the lpAddress data + */ +#define DPLCONNECTION_JOINSESSION DPOPEN_JOIN + +/**************************************************************************** + * + * Receive API flags + * Default is DPRECEIVE_ALL + * + ****************************************************************************/ + +/* + * Get the first message in the queue + */ +#define DPRECEIVE_ALL 0x00000001 + +/* + * Get the first message in the queue directed to a specific player + */ +#define DPRECEIVE_TOPLAYER 0x00000002 + +/* + * Get the first message in the queue from a specific player + */ +#define DPRECEIVE_FROMPLAYER 0x00000004 + +/* + * Get the message but don't remove it from the queue + */ +#define DPRECEIVE_PEEK 0x00000008 + + +/**************************************************************************** + * + * Send API flags + * + ****************************************************************************/ + +/* + * Send the message using a guaranteed send method. + * Default is non-guaranteed. + */ +#define DPSEND_GUARANTEED 0x00000001 + + +/* + * This flag is obsolete. It is ignored by DirectPlay + */ +#define DPSEND_HIGHPRIORITY 0x00000002 + +/* + * This flag is obsolete. It is ignored by DirectPlay + */ +#define DPSEND_OPENSTREAM 0x00000008 + +/* + * This flag is obsolete. It is ignored by DirectPlay + */ +#define DPSEND_CLOSESTREAM 0x00000010 + +/* + * Send the message digitally signed to ensure authenticity. + */ +#define DPSEND_SIGNED 0x00000020 + +/* + * Send the message with encryption to ensure privacy. + */ +#define DPSEND_ENCRYPTED 0x00000040 + + +/**************************************************************************** + * + * SetGroupData, SetGroupName, SetPlayerData, SetPlayerName, + * SetSessionDesc API flags. + * Default is DPSET_REMOTE. + * + ****************************************************************************/ + +/* + * Propagate the data to all players in the session + */ +#define DPSET_REMOTE 0x00000000 + +/* + * Do not propagate the data to other players + */ +#define DPSET_LOCAL 0x00000001 + +/* + * Used with DPSET_REMOTE, use guaranteed message send to + * propagate the data + */ +#define DPSET_GUARANTEED 0x00000002 + + +/**************************************************************************** + * + * DirectPlay system messages and message data structures + * + * All system message come 'From' player DPID_SYSMSG. To determine what type + * of message it is, cast the lpData from Receive to DPMSG_GENERIC and check + * the dwType member against one of the following DPSYS_xxx constants. Once + * a match is found, cast the lpData to the corresponding of the DPMSG_xxx + * structures to access the data of the message. + * + ****************************************************************************/ + +/* + * A new player or group has been created in the session + * Use DPMSG_CREATEPLAYERORGROUP. Check dwPlayerType to see if it + * is a player or a group. + */ +#define DPSYS_CREATEPLAYERORGROUP 0x0003 + +/* + * A player has been deleted from the session + * Use DPMSG_DESTROYPLAYERORGROUP + */ +#define DPSYS_DESTROYPLAYERORGROUP 0x0005 + +/* + * A player has been added to a group + * Use DPMSG_ADDPLAYERTOGROUP + */ +#define DPSYS_ADDPLAYERTOGROUP 0x0007 + +/* + * A player has been removed from a group + * Use DPMSG_DELETEPLAYERFROMGROUP + */ +#define DPSYS_DELETEPLAYERFROMGROUP 0x0021 + +/* + * This DirectPlay object lost its connection with all the + * other players in the session. + * Use DPMSG_SESSIONLOST. + */ +#define DPSYS_SESSIONLOST 0x0031 + +/* + * The current host has left the session. + * This DirectPlay object is now the host. + * Use DPMSG_HOST. + */ +#define DPSYS_HOST 0x0101 + +/* + * The remote data associated with a player or + * group has changed. Check dwPlayerType to see + * if it is a player or a group + * Use DPMSG_SETPLAYERORGROUPDATA + */ +#define DPSYS_SETPLAYERORGROUPDATA 0x0102 + +/* + * The name of a player or group has changed. + * Check dwPlayerType to see if it is a player + * or a group. + * Use DPMSG_SETPLAYERORGROUPNAME + */ +#define DPSYS_SETPLAYERORGROUPNAME 0x0103 + +/* + * The session description has changed. + * Use DPMSG_SETSESSIONDESC + */ +#define DPSYS_SETSESSIONDESC 0x0104 + +/* + * A group has been added to a group + * Use DPMSG_ADDGROUPTOGROUP + */ +#define DPSYS_ADDGROUPTOGROUP 0x0105 + +/* + * A group has been removed from a group + * Use DPMSG_DELETEGROUPFROMGROUP + */ +#define DPSYS_DELETEGROUPFROMGROUP 0x0106 + +/* + * A secure player-player message has arrived. + * Use DPMSG_SECUREMESSAGE + */ +#define DPSYS_SECUREMESSAGE 0x0107 + +/* + * Start a new session. + * Use DPMSG_STARTSESSION + */ +#define DPSYS_STARTSESSION 0x0108 + +/* + * A chat message has arrived + * Use DPMSG_CHAT + */ +#define DPSYS_CHAT 0x0109 + +/* + * Used in the dwPlayerType field to indicate if it applies to a group + * or a player + */ +#define DPPLAYERTYPE_GROUP 0x00000000 +#define DPPLAYERTYPE_PLAYER 0x00000001 + + +/* + * DPMSG_GENERIC + * Generic message structure used to identify the message type. + */ +typedef struct +{ + DWORD dwType; // Message type +} DPMSG_GENERIC, FAR *LPDPMSG_GENERIC; + +/* + * DPMSG_CREATEPLAYERORGROUP + * System message generated when a new player or group + * created in the session with information about it. + */ +typedef struct +{ + DWORD dwType; // Message type + DWORD dwPlayerType; // Is it a player or group + DPID dpId; // ID of the player or group + DWORD dwCurrentPlayers; // current # players & groups in session + LPVOID lpData; // pointer to remote data + DWORD dwDataSize; // size of remote data + DPNAME dpnName; // structure with name info + // the following fields are only available when using + // the IDirectPlay3 interface or greater + DPID dpIdParent; // id of parent group + DWORD dwFlags; // player or group flags +} DPMSG_CREATEPLAYERORGROUP, FAR *LPDPMSG_CREATEPLAYERORGROUP; + +/* + * DPMSG_DESTROYPLAYERORGROUP + * System message generated when a player or group is being + * destroyed in the session with information about it. + */ +typedef struct +{ + DWORD dwType; // Message type + DWORD dwPlayerType; // Is it a player or group + DPID dpId; // player ID being deleted + LPVOID lpLocalData; // copy of players local data + DWORD dwLocalDataSize; // sizeof local data + LPVOID lpRemoteData; // copy of players remote data + DWORD dwRemoteDataSize; // sizeof remote data + // the following fields are only available when using + // the IDirectPlay3 interface or greater + DPNAME dpnName; // structure with name info + DPID dpIdParent; // id of parent group + DWORD dwFlags; // player or group flags +} DPMSG_DESTROYPLAYERORGROUP, FAR *LPDPMSG_DESTROYPLAYERORGROUP; + +/* + * DPMSG_ADDPLAYERTOGROUP + * System message generated when a player is being added + * to a group. + */ +typedef struct +{ + DWORD dwType; // Message type + DPID dpIdGroup; // group ID being added to + DPID dpIdPlayer; // player ID being added +} DPMSG_ADDPLAYERTOGROUP, FAR *LPDPMSG_ADDPLAYERTOGROUP; + +/* + * DPMSG_DELETEPLAYERFROMGROUP + * System message generated when a player is being + * removed from a group + */ +typedef DPMSG_ADDPLAYERTOGROUP DPMSG_DELETEPLAYERFROMGROUP; +typedef DPMSG_DELETEPLAYERFROMGROUP FAR *LPDPMSG_DELETEPLAYERFROMGROUP; + +/* + * DPMSG_ADDGROUPTOGROUP + * System message generated when a group is being added + * to a group. + */ +typedef struct +{ + DWORD dwType; // Message type + DPID dpIdParentGroup; // group ID being added to + DPID dpIdGroup; // group ID being added +} DPMSG_ADDGROUPTOGROUP, FAR *LPDPMSG_ADDGROUPTOGROUP; + +/* + * DPMSG_DELETEGROUPFROMGROUP + * System message generated when a GROUP is being + * removed from a group + */ +typedef DPMSG_ADDGROUPTOGROUP DPMSG_DELETEGROUPFROMGROUP; +typedef DPMSG_DELETEGROUPFROMGROUP FAR *LPDPMSG_DELETEGROUPFROMGROUP; + +/* + * DPMSG_SETPLAYERORGROUPDATA + * System message generated when remote data for a player or + * group has changed. + */ +typedef struct +{ + DWORD dwType; // Message type + DWORD dwPlayerType; // Is it a player or group + DPID dpId; // ID of player or group + LPVOID lpData; // pointer to remote data + DWORD dwDataSize; // size of remote data +} DPMSG_SETPLAYERORGROUPDATA, FAR *LPDPMSG_SETPLAYERORGROUPDATA; + +/* + * DPMSG_SETPLAYERORGROUPNAME + * System message generated when the name of a player or + * group has changed. + */ +typedef struct +{ + DWORD dwType; // Message type + DWORD dwPlayerType; // Is it a player or group + DPID dpId; // ID of player or group + DPNAME dpnName; // structure with new name info +} DPMSG_SETPLAYERORGROUPNAME, FAR *LPDPMSG_SETPLAYERORGROUPNAME; + +/* + * DPMSG_SETSESSIONDESC + * System message generated when session desc has changed + */ +typedef struct +{ + DWORD dwType; // Message type + DPSESSIONDESC2 dpDesc; // Session desc +} DPMSG_SETSESSIONDESC, FAR *LPDPMSG_SETSESSIONDESC; + +/* + * DPMSG_HOST + * System message generated when the host has migrated to this + * DirectPlay object. + * + */ +typedef DPMSG_GENERIC DPMSG_HOST; +typedef DPMSG_HOST FAR *LPDPMSG_HOST; + +/* + * DPMSG_SESSIONLOST + * System message generated when the connection to the session is lost. + * + */ +typedef DPMSG_GENERIC DPMSG_SESSIONLOST; +typedef DPMSG_SESSIONLOST FAR *LPDPMSG_SESSIONLOST; + +/* + * DPMSG_SECUREMESSAGE + * System message generated when a player requests a secure send + */ +typedef struct +{ + DWORD dwType; // Message Type + DWORD dwFlags; // Signed/Encrypted + DPID dpIdFrom; // ID of Sending Player + LPVOID lpData; // Player message + DWORD dwDataSize; // Size of player message +} DPMSG_SECUREMESSAGE, FAR *LPDPMSG_SECUREMESSAGE; + +/* + * DPMSG_STARTSESSION + * System message containing all information required to + * start a new session + */ +typedef struct +{ + DWORD dwType; // Message type + LPDPLCONNECTION lpConn; // DPLCONNECTION structure +} DPMSG_STARTSESSION, FAR *LPDPMSG_STARTSESSION; + +/* + * DPMSG_CHAT + * System message containing a chat message + */ +typedef struct +{ + DWORD dwType; // Message type + DWORD dwFlags; // Message flags + DPID idFromPlayer; // ID of the Sending Player + DPID idToPlayer; // ID of the To Player + DPID idToGroup; // ID of the To Group + LPDPCHAT lpChat; // Pointer to a structure containing the chat message +} DPMSG_CHAT, FAR *LPDPMSG_CHAT; + +/**************************************************************************** + * + * DIRECTPLAY ERRORS + * + * Errors are represented by negative values and cannot be combined. + * + ****************************************************************************/ +#define DP_OK S_OK +#define DPERR_ALREADYINITIALIZED MAKE_DPHRESULT( 5 ) +#define DPERR_ACCESSDENIED MAKE_DPHRESULT( 10 ) +#define DPERR_ACTIVEPLAYERS MAKE_DPHRESULT( 20 ) +#define DPERR_BUFFERTOOSMALL MAKE_DPHRESULT( 30 ) +#define DPERR_CANTADDPLAYER MAKE_DPHRESULT( 40 ) +#define DPERR_CANTCREATEGROUP MAKE_DPHRESULT( 50 ) +#define DPERR_CANTCREATEPLAYER MAKE_DPHRESULT( 60 ) +#define DPERR_CANTCREATESESSION MAKE_DPHRESULT( 70 ) +#define DPERR_CAPSNOTAVAILABLEYET MAKE_DPHRESULT( 80 ) +#define DPERR_EXCEPTION MAKE_DPHRESULT( 90 ) +#define DPERR_GENERIC E_FAIL +#define DPERR_INVALIDFLAGS MAKE_DPHRESULT( 120 ) +#define DPERR_INVALIDOBJECT MAKE_DPHRESULT( 130 ) +#define DPERR_INVALIDPARAM E_INVALIDARG +#define DPERR_INVALIDPARAMS DPERR_INVALIDPARAM +#define DPERR_INVALIDPLAYER MAKE_DPHRESULT( 150 ) +#define DPERR_INVALIDGROUP MAKE_DPHRESULT( 155 ) +#define DPERR_NOCAPS MAKE_DPHRESULT( 160 ) +#define DPERR_NOCONNECTION MAKE_DPHRESULT( 170 ) +#define DPERR_NOMEMORY E_OUTOFMEMORY +#define DPERR_OUTOFMEMORY DPERR_NOMEMORY +#define DPERR_NOMESSAGES MAKE_DPHRESULT( 190 ) +#define DPERR_NONAMESERVERFOUND MAKE_DPHRESULT( 200 ) +#define DPERR_NOPLAYERS MAKE_DPHRESULT( 210 ) +#define DPERR_NOSESSIONS MAKE_DPHRESULT( 220 ) +#define DPERR_PENDING E_PENDING +#define DPERR_SENDTOOBIG MAKE_DPHRESULT( 230 ) +#define DPERR_TIMEOUT MAKE_DPHRESULT( 240 ) +#define DPERR_UNAVAILABLE MAKE_DPHRESULT( 250 ) +#define DPERR_UNSUPPORTED E_NOTIMPL +#define DPERR_BUSY MAKE_DPHRESULT( 270 ) +#define DPERR_USERCANCEL MAKE_DPHRESULT( 280 ) +#define DPERR_NOINTERFACE E_NOINTERFACE +#define DPERR_CANNOTCREATESERVER MAKE_DPHRESULT( 290 ) +#define DPERR_PLAYERLOST MAKE_DPHRESULT( 300 ) +#define DPERR_SESSIONLOST MAKE_DPHRESULT( 310 ) +#define DPERR_UNINITIALIZED MAKE_DPHRESULT( 320 ) +#define DPERR_NONEWPLAYERS MAKE_DPHRESULT( 330 ) +#define DPERR_INVALIDPASSWORD MAKE_DPHRESULT( 340 ) +#define DPERR_CONNECTING MAKE_DPHRESULT( 350 ) + + +#define DPERR_BUFFERTOOLARGE MAKE_DPHRESULT( 1000 ) +#define DPERR_CANTCREATEPROCESS MAKE_DPHRESULT( 1010 ) +#define DPERR_APPNOTSTARTED MAKE_DPHRESULT( 1020 ) +#define DPERR_INVALIDINTERFACE MAKE_DPHRESULT( 1030 ) +#define DPERR_NOSERVICEPROVIDER MAKE_DPHRESULT( 1040 ) +#define DPERR_UNKNOWNAPPLICATION MAKE_DPHRESULT( 1050 ) +#define DPERR_NOTLOBBIED MAKE_DPHRESULT( 1070 ) +#define DPERR_SERVICEPROVIDERLOADED MAKE_DPHRESULT( 1080 ) +#define DPERR_ALREADYREGISTERED MAKE_DPHRESULT( 1090 ) +#define DPERR_NOTREGISTERED MAKE_DPHRESULT( 1100 ) + +// +// Security related errors +// +#define DPERR_AUTHENTICATIONFAILED MAKE_DPHRESULT( 2000 ) +#define DPERR_CANTLOADSSPI MAKE_DPHRESULT( 2010 ) +#define DPERR_ENCRYPTIONFAILED MAKE_DPHRESULT( 2020 ) +#define DPERR_SIGNFAILED MAKE_DPHRESULT( 2030 ) +#define DPERR_CANTLOADSECURITYPACKAGE MAKE_DPHRESULT( 2040 ) +#define DPERR_ENCRYPTIONNOTSUPPORTED MAKE_DPHRESULT( 2050 ) +#define DPERR_CANTLOADCAPI MAKE_DPHRESULT( 2060 ) +#define DPERR_NOTLOGGEDIN MAKE_DPHRESULT( 2070 ) +#define DPERR_LOGONDENIED MAKE_DPHRESULT( 2080 ) + + +/**************************************************************************** + * + * dplay 1.0 obsolete structures + interfaces + * Included for compatibility only. New apps should + * use IDirectPlay2 + * + ****************************************************************************/ + +// define this to ignore obsolete interfaces and constants +#ifndef IDIRECTPLAY2_OR_GREATER + +#define DPOPEN_OPENSESSION DPOPEN_JOIN +#define DPOPEN_CREATESESSION DPOPEN_CREATE + +#define DPENUMSESSIONS_PREVIOUS 0x00000004 + +#define DPENUMPLAYERS_PREVIOUS 0x00000004 + +#define DPSEND_GUARANTEE DPSEND_GUARANTEED +#define DPSEND_TRYONCE 0x00000004 + +#define DPCAPS_NAMESERVICE 0x00000001 +#define DPCAPS_NAMESERVER DPCAPS_ISHOST +#define DPCAPS_GUARANTEED 0x00000004 + +#define DPLONGNAMELEN 52 +#define DPSHORTNAMELEN 20 +#define DPSESSIONNAMELEN 32 +#define DPPASSWORDLEN 16 +#define DPUSERRESERVED 16 + +#define DPSYS_ADDPLAYER 0x0003 +#define DPSYS_DELETEPLAYER 0x0005 + +#define DPSYS_DELETEGROUP 0x0020 +#define DPSYS_DELETEPLAYERFROMGRP 0x0021 +#define DPSYS_CONNECT 0x484b + +typedef struct +{ + DWORD dwType; + DWORD dwPlayerType; + DPID dpId; + char szLongName[DPLONGNAMELEN]; + char szShortName[DPSHORTNAMELEN]; + DWORD dwCurrentPlayers; +} DPMSG_ADDPLAYER; + +typedef DPMSG_ADDPLAYER DPMSG_ADDGROUP; + +typedef struct +{ + DWORD dwType; + DPID dpIdGroup; + DPID dpIdPlayer; +} DPMSG_GROUPADD; + +typedef DPMSG_GROUPADD DPMSG_GROUPDELETE; +typedef struct +{ + DWORD dwType; + DPID dpId; +} DPMSG_DELETEPLAYER; + +typedef BOOL (PASCAL *LPDPENUMPLAYERSCALLBACK)( + DPID dpId, + LPSTR lpFriendlyName, + LPSTR lpFormalName, + DWORD dwFlags, + LPVOID lpContext ); + +typedef struct +{ + DWORD dwSize; + GUID guidSession; + DWORD dwSession; + DWORD dwMaxPlayers; + DWORD dwCurrentPlayers; + DWORD dwFlags; + char szSessionName[DPSESSIONNAMELEN]; + char szUserField[DPUSERRESERVED]; + DWORD dwReserved1; + char szPassword[DPPASSWORDLEN]; + DWORD dwReserved2; + DWORD dwUser1; + DWORD dwUser2; + DWORD dwUser3; + DWORD dwUser4; +} DPSESSIONDESC,*LPDPSESSIONDESC; + +typedef BOOL (PASCAL * LPDPENUMSESSIONSCALLBACK)( + LPDPSESSIONDESC lpDPSessionDesc, + LPVOID lpContext, + LPDWORD lpdwTimeOut, + DWORD dwFlags); + +/* + * IDirectPlay + */ +#undef INTERFACE +#define INTERFACE IDirectPlay +DECLARE_INTERFACE_( IDirectPlay, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectPlay methods ***/ + STDMETHOD(AddPlayerToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(Close) (THIS) PURE; + STDMETHOD(CreatePlayer) (THIS_ LPDPID,LPSTR,LPSTR,LPHANDLE) PURE; + STDMETHOD(CreateGroup) (THIS_ LPDPID,LPSTR,LPSTR) PURE; + STDMETHOD(DeletePlayerFromGroup)(THIS_ DPID,DPID) PURE; + STDMETHOD(DestroyPlayer) (THIS_ DPID) PURE; + STDMETHOD(DestroyGroup) (THIS_ DPID) PURE; + STDMETHOD(EnableNewPlayers) (THIS_ BOOL) PURE; + STDMETHOD(EnumGroupPlayers) (THIS_ DPID, LPDPENUMPLAYERSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroups) (THIS_ DWORD, LPDPENUMPLAYERSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumPlayers) (THIS_ DWORD, LPDPENUMPLAYERSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumSessions) (THIS_ LPDPSESSIONDESC,DWORD,LPDPENUMSESSIONSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(GetCaps) (THIS_ LPDPCAPS) PURE; + STDMETHOD(GetMessageCount) (THIS_ DPID, LPDWORD) PURE; + STDMETHOD(GetPlayerCaps) (THIS_ DPID, LPDPCAPS) PURE; + STDMETHOD(GetPlayerName) (THIS_ DPID,LPSTR,LPDWORD,LPSTR,LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; + STDMETHOD(Open) (THIS_ LPDPSESSIONDESC) PURE; + STDMETHOD(Receive) (THIS_ LPDPID,LPDPID,DWORD,LPVOID,LPDWORD) PURE; + STDMETHOD(SaveSession) (THIS_ LPSTR) PURE; + STDMETHOD(Send) (THIS_ DPID, DPID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetPlayerName) (THIS_ DPID,LPSTR,LPSTR) PURE; +}; + +/**************************************************************************** + * + * IDirectPlay interface macros + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlay_AddPlayerToGroup(p,a,b) (p)->lpVtbl->AddPlayerToGroup(p,a,b) +#define IDirectPlay_Close(p) (p)->lpVtbl->Close(p) +#define IDirectPlay_CreateGroup(p,a,b,c) (p)->lpVtbl->CreateGroup(p,a,b,c) +#define IDirectPlay_CreatePlayer(p,a,b,c,d) (p)->lpVtbl->CreatePlayer(p,a,b,c,d) +#define IDirectPlay_DeletePlayerFromGroup(p,a,b) (p)->lpVtbl->DeletePlayerFromGroup(p,a,b) +#define IDirectPlay_DestroyGroup(p,a) (p)->lpVtbl->DestroyGroup(p,a) +#define IDirectPlay_DestroyPlayer(p,a) (p)->lpVtbl->DestroyPlayer(p,a) +#define IDirectPlay_EnableNewPlayers(p,a) (p)->lpVtbl->EnableNewPlayers(p,a) +#define IDirectPlay_EnumGroupPlayers(p,a,b,c,d) (p)->lpVtbl->EnumGroupPlayers(p,a,b,c,d) +#define IDirectPlay_EnumGroups(p,a,b,c,d) (p)->lpVtbl->EnumGroups(p,a,b,c,d) +#define IDirectPlay_EnumPlayers(p,a,b,c,d) (p)->lpVtbl->EnumPlayers(p,a,b,c,d) +#define IDirectPlay_EnumSessions(p,a,b,c,d,e) (p)->lpVtbl->EnumSessions(p,a,b,c,d,e) +#define IDirectPlay_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectPlay_GetMessageCount(p,a,b) (p)->lpVtbl->GetMessageCount(p,a,b) +#define IDirectPlay_GetPlayerCaps(p,a,b) (p)->lpVtbl->GetPlayerCaps(p,a,b) +#define IDirectPlay_GetPlayerName(p,a,b,c,d,e) (p)->lpVtbl->GetPlayerName(p,a,b,c,d,e) +#define IDirectPlay_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#define IDirectPlay_Open(p,a) (p)->lpVtbl->Open(p,a) +#define IDirectPlay_Receive(p,a,b,c,d,e) (p)->lpVtbl->Receive(p,a,b,c,d,e) +#define IDirectPlay_SaveSession(p,a) (p)->lpVtbl->SaveSession(p,a) +#define IDirectPlay_Send(p,a,b,c,d,e) (p)->lpVtbl->Send(p,a,b,c,d,e) +#define IDirectPlay_SetPlayerName(p,a,b,c) (p)->lpVtbl->SetPlayerName(p,a,b,c) + +#else /* C++ */ + +#define IDirectPlay_AddPlayerToGroup(p,a,b) (p)->AddPlayerToGroup(a,b) +#define IDirectPlay_Close(p) (p)->Close() +#define IDirectPlay_CreateGroup(p,a,b,c) (p)->CreateGroup(a,b,c) +#define IDirectPlay_CreatePlayer(p,a,b,c,d) (p)->CreatePlayer(a,b,c,d) +#define IDirectPlay_DeletePlayerFromGroup(p,a,b) (p)->DeletePlayerFromGroup(a,b) +#define IDirectPlay_DestroyGroup(p,a) (p)->DestroyGroup(a) +#define IDirectPlay_DestroyPlayer(p,a) (p)->DestroyPlayer(a) +#define IDirectPlay_EnableNewPlayers(p,a) (p)->EnableNewPlayers(a) +#define IDirectPlay_EnumGroupPlayers(p,a,b,c,d) (p)->EnumGroupPlayers(a,b,c,d) +#define IDirectPlay_EnumGroups(p,a,b,c,d) (p)->EnumGroups(a,b,c,d) +#define IDirectPlay_EnumPlayers(p,a,b,c,d) (p)->EnumPlayers(a,b,c,d) +#define IDirectPlay_EnumSessions(p,a,b,c,d,e) (p)->EnumSessions(a,b,c,d,e) +#define IDirectPlay_GetCaps(p,a) (p)->GetCaps(a) +#define IDirectPlay_GetMessageCount(p,a,b) (p)->GetMessageCount(a,b) +#define IDirectPlay_GetPlayerCaps(p,a,b) (p)->GetPlayerCaps(a,b) +#define IDirectPlay_GetPlayerName(p,a,b,c,d,e) (p)->GetPlayerName(a,b,c,d,e) +#define IDirectPlay_Initialize(p,a) (p)->Initialize(a) +#define IDirectPlay_Open(p,a) (p)->Open(a) +#define IDirectPlay_Receive(p,a,b,c,d,e) (p)->Receive(a,b,c,d,e) +#define IDirectPlay_SaveSession(p,a) (p)->SaveSession(a) +#define IDirectPlay_Send(p,a,b,c,d,e) (p)->Send(a,b,c,d,e) +#define IDirectPlay_SetPlayerName(p,a,b,c) (p)->SetPlayerName(a,b,c) + +#endif + +DEFINE_GUID(IID_IDirectPlay, 0x5454e9a0, 0xdb65, 0x11ce, 0x92, 0x1c, 0x00, 0xaa, 0x00, 0x6c, 0x49, 0x72); + +#endif // IDIRECTPLAY2_OR_GREATER + +/**************************************************************************** + * + * IDirectPlay macros (included regardless of IDIRECTPLAY2_OR_GREATER flag) + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlay_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectPlay_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectPlay_Release(p) (p)->lpVtbl->Release(p) + +#else + +#define IDirectPlay_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectPlay_AddRef(p) (p)->AddRef() +#define IDirectPlay_Release(p) (p)->Release() + +#endif // IDirectPlay interface macros + +#ifdef __cplusplus +}; +#endif + +#endif + diff --git a/include/vdplobby.h b/include/vdplobby.h new file mode 100644 index 0000000..e6448b4 --- /dev/null +++ b/include/vdplobby.h @@ -0,0 +1,628 @@ +/*==========================================================================; + * + * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. + * + * File: dplobby.h + * Content: DirectPlayLobby include file + ***************************************************************************/ +#ifndef __DPLOBBY_INCLUDED__ +#define __DPLOBBY_INCLUDED__ + +#include "vdplay.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * GUIDS used by DirectPlay objects + */ + +/* {AF465C71-9588-11cf-A020-00AA006157AC} */ +DEFINE_GUID(IID_IDirectPlayLobby, 0xaf465c71, 0x9588, 0x11cf, 0xa0, 0x20, 0x0, 0xaa, 0x0, 0x61, 0x57, 0xac); +/* {26C66A70-B367-11cf-A024-00AA006157AC} */ +DEFINE_GUID(IID_IDirectPlayLobbyA, 0x26c66a70, 0xb367, 0x11cf, 0xa0, 0x24, 0x0, 0xaa, 0x0, 0x61, 0x57, 0xac); +/* {0194C220-A303-11d0-9C4F-00A0C905425E} */ +DEFINE_GUID(IID_IDirectPlayLobby2, 0x194c220, 0xa303, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); +/* {1BB4AF80-A303-11d0-9C4F-00A0C905425E} */ +DEFINE_GUID(IID_IDirectPlayLobby2A, 0x1bb4af80, 0xa303, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); +/* {2FE8F810-B2A5-11d0-A787-0000F803ABFC} */ +DEFINE_GUID(CLSID_DirectPlayLobby, 0x2fe8f810, 0xb2a5, 0x11d0, 0xa7, 0x87, 0x0, 0x0, 0xf8, 0x3, 0xab, 0xfc); + + +/**************************************************************************** + * + * IDirectPlayLobby Structures + * + * Various structures used to invoke DirectPlayLobby. + * + ****************************************************************************/ + +typedef struct IDirectPlayLobby FAR *LPDIRECTPLAYLOBBY; +typedef struct IDirectPlayLobby FAR *LPDIRECTPLAYLOBBYA; +typedef struct IDirectPlayLobby IDirectPlayLobbyA; + +typedef struct IDirectPlayLobby2 FAR *LPDIRECTPLAYLOBBY2; +typedef struct IDirectPlayLobby2 FAR *LPDIRECTPLAYLOBBY2A; +typedef struct IDirectPlayLobby2 IDirectPlayLobby2A; + + +/* + * DPLAPPINFO + * Used to hold information about a registered DirectPlay + * application + */ +typedef struct DPLAPPINFO +{ + DWORD dwSize; // Size of this structure + GUID guidApplication; // GUID of the Application + union + { + LPSTR lpszAppNameA; // Pointer to the Application Name + LPWSTR lpszAppName; + }; + +} DPLAPPINFO, FAR *LPDPLAPPINFO; + +/* + * LPCDPLAPPINFO + * A constant pointer to DPLAPPINFO + */ +typedef const DPLAPPINFO FAR *LPCDPLAPPINFO; + +/* + * DPCOMPOUNDADDRESSELEMENT + * + * An array of these is passed to CreateCompoundAddresses() + */ +typedef struct DPCOMPOUNDADDRESSELEMENT +{ + GUID guidDataType; + DWORD dwDataSize; + LPVOID lpData; +} DPCOMPOUNDADDRESSELEMENT, FAR *LPDPCOMPOUNDADDRESSELEMENT; + +/* + * LPCDPCOMPOUNDADDRESSELEMENT + * A constant pointer to DPCOMPOUNDADDRESSELEMENT + */ +typedef const DPCOMPOUNDADDRESSELEMENT FAR *LPCDPCOMPOUNDADDRESSELEMENT; + + +/**************************************************************************** + * + * Enumeration Method Callback Prototypes + * + ****************************************************************************/ + +/* + * Callback for EnumAddress() + */ +typedef BOOL (FAR PASCAL *LPDPENUMADDRESSCALLBACK)( + REFGUID guidDataType, + DWORD dwDataSize, + LPCVOID lpData, + LPVOID lpContext); + +/* + * Callback for EnumAddressTypes() + */ +typedef BOOL (FAR PASCAL *LPDPLENUMADDRESSTYPESCALLBACK)( + REFGUID guidDataType, + LPVOID lpContext, + DWORD dwFlags); + +/* + * Callback for EnumLocalApplications() + */ +typedef BOOL (FAR PASCAL * LPDPLENUMLOCALAPPLICATIONSCALLBACK)( + LPCDPLAPPINFO lpAppInfo, + LPVOID lpContext, + DWORD dwFlags); + + +/**************************************************************************** + * + * DirectPlayLobby API Prototypes + * + ****************************************************************************/ +#ifdef UNICODE +#define DirectPlayLobbyCreate DirectPlayLobbyCreateW +#else +#define DirectPlayLobbyCreate DirectPlayLobbyCreateA +#endif /* UNICODE */ + +extern HRESULT WINAPI DirectPlayLobbyCreateW(LPGUID, LPDIRECTPLAYLOBBY *, IUnknown *, LPVOID, DWORD ); +extern HRESULT WINAPI DirectPlayLobbyCreateA(LPGUID, LPDIRECTPLAYLOBBYA *, IUnknown *, LPVOID, DWORD ); + + +/**************************************************************************** + * + * IDirectPlayLobby (and IDirectPlayLobbyA) Interface + * + ****************************************************************************/ +#undef INTERFACE +#define INTERFACE IDirectPlayLobby +DECLARE_INTERFACE_( IDirectPlayLobby, IUnknown ) +{ + /* IUnknown Methods */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + /* IDirectPlayLobby Methods */ + STDMETHOD(Connect) (THIS_ DWORD, LPDIRECTPLAY2 *, IUnknown FAR *) PURE; + STDMETHOD(CreateAddress) (THIS_ REFGUID, REFGUID, LPCVOID, DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(EnumAddress) (THIS_ LPDPENUMADDRESSCALLBACK, LPCVOID, DWORD, LPVOID) PURE; + STDMETHOD(EnumAddressTypes) (THIS_ LPDPLENUMADDRESSTYPESCALLBACK, REFGUID, LPVOID, DWORD) PURE; + STDMETHOD(EnumLocalApplications)(THIS_ LPDPLENUMLOCALAPPLICATIONSCALLBACK, LPVOID, DWORD) PURE; + STDMETHOD(GetConnectionSettings)(THIS_ DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(ReceiveLobbyMessage) (THIS_ DWORD, DWORD, LPDWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(RunApplication) (THIS_ DWORD, LPDWORD, LPDPLCONNECTION, HANDLE) PURE; + STDMETHOD(SendLobbyMessage) (THIS_ DWORD, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetConnectionSettings)(THIS_ DWORD, DWORD, LPDPLCONNECTION) PURE; + STDMETHOD(SetLobbyMessageEvent) (THIS_ DWORD, DWORD, HANDLE) PURE; + +}; + +/**************************************************************************** + * + * IDirectPlayLobby2 (and IDirectPlayLobby2A) Interface + * + ****************************************************************************/ +#undef INTERFACE +#define INTERFACE IDirectPlayLobby2 +DECLARE_INTERFACE_( IDirectPlayLobby2, IDirectPlayLobby ) +{ + /* IUnknown Methods */ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + /* IDirectPlayLobby Methods */ + STDMETHOD(Connect) (THIS_ DWORD, LPDIRECTPLAY2 *, IUnknown FAR *) PURE; + STDMETHOD(CreateAddress) (THIS_ REFGUID, REFGUID, LPCVOID, DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(EnumAddress) (THIS_ LPDPENUMADDRESSCALLBACK, LPCVOID, DWORD, LPVOID) PURE; + STDMETHOD(EnumAddressTypes) (THIS_ LPDPLENUMADDRESSTYPESCALLBACK, REFGUID, LPVOID, DWORD) PURE; + STDMETHOD(EnumLocalApplications)(THIS_ LPDPLENUMLOCALAPPLICATIONSCALLBACK, LPVOID, DWORD) PURE; + STDMETHOD(GetConnectionSettings)(THIS_ DWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(ReceiveLobbyMessage) (THIS_ DWORD, DWORD, LPDWORD, LPVOID, LPDWORD) PURE; + STDMETHOD(RunApplication) (THIS_ DWORD, LPDWORD, LPDPLCONNECTION, HANDLE) PURE; + STDMETHOD(SendLobbyMessage) (THIS_ DWORD, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetConnectionSettings)(THIS_ DWORD, DWORD, LPDPLCONNECTION) PURE; + STDMETHOD(SetLobbyMessageEvent) (THIS_ DWORD, DWORD, HANDLE) PURE; + + /* IDirectPlayLobby2 Methods */ + STDMETHOD(CreateCompoundAddress)(THIS_ LPCDPCOMPOUNDADDRESSELEMENT,DWORD,LPVOID,LPDWORD) PURE; +}; + +/**************************************************************************** + * + * IDirectPlayLobby interface macros + * + ****************************************************************************/ + +#if !defined(__cplusplus) || defined(CINTERFACE) + +#define IDirectPlayLobby_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectPlayLobby_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectPlayLobby_Release(p) (p)->lpVtbl->Release(p) +#define IDirectPlayLobby_Connect(p,a,b,c) (p)->lpVtbl->Connect(p,a,b,c) +#define IDirectPlayLobby_CreateAddress(p,a,b,c,d,e,f) (p)->lpVtbl->CreateAddress(p,a,b,c,d,e,f) +#define IDirectPlayLobby_CreateCompoundAddress(p,a,b,c,d) (p)->lpVtbl->CreateCompoundAddress(p,a,b,c,d) +#define IDirectPlayLobby_EnumAddress(p,a,b,c,d) (p)->lpVtbl->EnumAddress(p,a,b,c,d) +#define IDirectPlayLobby_EnumAddressTypes(p,a,b,c,d) (p)->lpVtbl->EnumAddressTypes(p,a,b,c,d) +#define IDirectPlayLobby_EnumLocalApplications(p,a,b,c) (p)->lpVtbl->EnumLocalApplications(p,a,b,c) +#define IDirectPlayLobby_GetConnectionSettings(p,a,b,c) (p)->lpVtbl->GetConnectionSettings(p,a,b,c) +#define IDirectPlayLobby_ReceiveLobbyMessage(p,a,b,c,d,e) (p)->lpVtbl->ReceiveLobbyMessage(p,a,b,c,d,e) +#define IDirectPlayLobby_RunApplication(p,a,b,c,d) (p)->lpVtbl->RunApplication(p,a,b,c,d) +#define IDirectPlayLobby_SendLobbyMessage(p,a,b,c,d) (p)->lpVtbl->SendLobbyMessage(p,a,b,c,d) +#define IDirectPlayLobby_SetConnectionSettings(p,a,b,c) (p)->lpVtbl->SetConnectionSettings(p,a,b,c) +#define IDirectPlayLobby_SetLobbyMessageEvent(p,a,b,c) (p)->lpVtbl->SetLobbyMessageEvent(p,a,b,c) + +#else /* C++ */ + +#define IDirectPlayLobby_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectPlayLobby_AddRef(p) (p)->AddRef() +#define IDirectPlayLobby_Release(p) (p)->Release() +#define IDirectPlayLobby_Connect(p,a,b,c) (p)->Connect(a,b,c) +#define IDirectPlayLobby_CreateAddress(p,a,b,c,d,e,f) (p)->CreateAddress(a,b,c,d,e,f) +#define IDirectPlayLobby_CreateCompoundAddress(p,a,b,c,d) (p)->lpVtbl->CreateCompoundAddress(a,b,c,d) +#define IDirectPlayLobby_EnumAddress(p,a,b,c,d) (p)->EnumAddress(a,b,c,d) +#define IDirectPlayLobby_EnumAddressTypes(p,a,b,c,d) (p)->EnumAddressTypes(a,b,c,d) +#define IDirectPlayLobby_EnumLocalApplications(p,a,b,c) (p)->EnumLocalApplications(a,b,c) +#define IDirectPlayLobby_GetConnectionSettings(p,a,b,c) (p)->GetConnectionSettings(a,b,c) +#define IDirectPlayLobby_ReceiveLobbyMessage(p,a,b,c,d,e) (p)->ReceiveLobbyMessage(a,b,c,d,e) +#define IDirectPlayLobby_RunApplication(p,a,b,c,d) (p)->RunApplication(a,b,c,d) +#define IDirectPlayLobby_SendLobbyMessage(p,a,b,c,d) (p)->SendLobbyMessage(a,b,c,d) +#define IDirectPlayLobby_SetConnectionSettings(p,a,b,c) (p)->SetConnectionSettings(a,b,c) +#define IDirectPlayLobby_SetLobbyMessageEvent(p,a,b,c) (p)->SetLobbyMessageEvent(a,b,c) + +#endif + +/**************************************************************************** + * + * DirectPlayLobby Flags + * + ****************************************************************************/ + +/* + * This is a message flag used by ReceiveLobbyMessage. It can be + * returned in the dwMessageFlags parameter to indicate a message from + * the system. + */ +#define DPLMSG_SYSTEM 0x00000001 + +/* + * This is a message flag used by ReceiveLobbyMessage and SendLobbyMessage. + * It is used to indicate that the message is a standard lobby message. + * DPLMSG_SETPROPERTY, DPLMSG_SETPROPERTYRESPONSE, DPLMSG_GETPROPERTY, + * DPLMSG_GETPROPERTYRESPONSE + */ +#define DPLMSG_STANDARD 0x00000002 + + +/**************************************************************************** + * + * DirectPlayLobby messages and message data structures + * + * All system messages have a dwMessageFlags value of DPLMSG_SYSTEM returned + * from a call to ReceiveLobbyMessage. + * + * All standard messages have a dwMessageFlags value of DPLMSG_STANDARD returned + * from a call to ReceiveLobbyMessage. + * + ****************************************************************************/ + +/* + * DPLMSG_GENERIC + * Generic message structure used to identify the message type. + */ +typedef struct _DPLMSG_GENERIC +{ + DWORD dwType; // Message type +} DPLMSG_GENERIC, FAR *LPDPLMSG_GENERIC; + +/* + * DPLMSG_SETPROPERTY + * Standard message sent by an application to a lobby to set a + * property + */ +typedef struct _DPLMSG_SETPROPERTY +{ + DWORD dwType; // Message type + DWORD dwRequestID; // Request ID (DPL_NOCONFIRMATION if no confirmation desired) + GUID guidPlayer; // Player GUID + GUID guidPropertyTag; // Property GUID + DWORD dwDataSize; // Size of data + DWORD dwPropertyData[1]; // Buffer containing data +} DPLMSG_SETPROPERTY, FAR *LPDPLMSG_SETPROPERTY; + +#define DPL_NOCONFIRMATION 0 + +/* + * DPLMSG_SETPROPERTYRESPONSE + * Standard message returned by a lobby to confirm a + * DPLMSG_SETPROPERTY message. + */ +typedef struct _DPLMSG_SETPROPERTYRESPONSE +{ + DWORD dwType; // Message type + DWORD dwRequestID; // Request ID + GUID guidPlayer; // Player GUID + GUID guidPropertyTag; // Property GUID + HRESULT hr; // Return Code +} DPLMSG_SETPROPERTYRESPONSE, FAR *LPDPLMSG_SETPROPERTYRESPONSE; + +/* + * DPLMSG_GETPROPERTY + * Standard message sent by an application to a lobby to request + * the current value of a property + */ +typedef struct _DPLMSG_GETPROPERTY +{ + DWORD dwType; // Message type + DWORD dwRequestID; // Request ID + GUID guidPlayer; // Player GUID + GUID guidPropertyTag; // Property GUID +} DPLMSG_GETPROPERTY, FAR *LPDPLMSG_GETPROPERTY; + +/* + * DPLMSG_GETPROPERTYRESPONSE + * Standard message returned by a lobby in response to a + * DPLMSG_GETPROPERTY message. + */ +typedef struct _DPLMSG_GETPROPERTYRESPONSE +{ + DWORD dwType; // Message type + DWORD dwRequestID; // Request ID + GUID guidPlayer; // Player GUID + GUID guidPropertyTag; // Property GUID + HRESULT hr; // Return Code + DWORD dwDataSize; // Size of data + DWORD dwPropertyData[1]; // Buffer containing data +} DPLMSG_GETPROPERTYRESPONSE, FAR *LPDPLMSG_GETPROPERTYRESPONSE; + + +/****************************************** + * + * DirectPlay Lobby message dwType values + * + *****************************************/ + +/* + * The application has read the connection settings. + * It is now O.K. for the lobby client to release + * its IDirectPlayLobby interface. + */ +#define DPLSYS_CONNECTIONSETTINGSREAD 0x00000001 + +/* + * The application's call to DirectPlayConnect failed + */ +#define DPLSYS_DPLAYCONNECTFAILED 0x00000002 + +/* + * The application has created a DirectPlay session. + */ +#define DPLSYS_DPLAYCONNECTSUCCEEDED 0x00000003 + +/* + * The application has terminated. + */ +#define DPLSYS_APPTERMINATED 0x00000004 + +/* + * The message is a DPLMSG_SETPROPERTY message. + */ +#define DPLSYS_SETPROPERTY 0x00000005 + +/* + * The message is a DPLMSG_SETPROPERTYRESPONSE message. + */ +#define DPLSYS_SETPROPERTYRESPONSE 0x00000006 + +/* + * The message is a DPLMSG_GETPROPERTY message. + */ +#define DPLSYS_GETPROPERTY 0x00000007 + +/* + * The message is a DPLMSG_GETPROPERTYRESPONSE message. + */ +#define DPLSYS_GETPROPERTYRESPONSE 0x00000008 + + +/**************************************************************************** + * + * DirectPlay defined property GUIDs and associated data structures + * + ****************************************************************************/ + +/* + * DPLPROPERTY_MessagesSupported + * + * Request whether the lobby supports standard. Lobby with respond with either + * TRUE or FALSE or may not respond at all. + * + * Property data is a single BOOL with TRUE or FALSE + */ +// {762CCDA1-D916-11d0-BA39-00C04FD7ED67} +DEFINE_GUID(DPLPROPERTY_MessagesSupported, +0x762ccda1, 0xd916, 0x11d0, 0xba, 0x39, 0x0, 0xc0, 0x4f, 0xd7, 0xed, 0x67); + +/* + * DPLPROPERTY_LobbyGuid + * + * Request the GUID that identifies the lobby software that the application + * is communicating with. + * + * Property data is a single GUID. + */ +// {F56920A0-D218-11d0-BA39-00C04FD7ED67} +DEFINE_GUID(DPLPROPERTY_LobbyGuid, +0xf56920a0, 0xd218, 0x11d0, 0xba, 0x39, 0x0, 0xc0, 0x4f, 0xd7, 0xed, 0x67); + +/* + * DPLPROPERTY_PlayerGuid + * + * Request the GUID that identifies the player on this machine for sending + * property data back to the lobby. + * + * Property data is the DPLDATA_PLAYERDATA structure + */ +// {B4319322-D20D-11d0-BA39-00C04FD7ED67} +DEFINE_GUID(DPLPROPERTY_PlayerGuid, +0xb4319322, 0xd20d, 0x11d0, 0xba, 0x39, 0x0, 0xc0, 0x4f, 0xd7, 0xed, 0x67); + +/* + * DPLDATA_PLAYERGUID + * + * Data structure to hold the GUID of the player and player creation flags + * from the lobby. + */ +typedef struct _DPLDATA_PLAYERGUID +{ + GUID guidPlayer; + DWORD dwPlayerFlags; +} DPLDATA_PLAYERGUID, FAR *LPDPLDATA_PLAYERGUID; + +/* + * DPLPROPERTY_PlayerScore + * + * Used to send an array of long integers to the lobby indicating the + * score of a player. + * + * Property data is the DPLDATA_PLAYERSCORE structure. + */ +// {48784000-D219-11d0-BA39-00C04FD7ED67} +DEFINE_GUID(DPLPROPERTY_PlayerScore, +0x48784000, 0xd219, 0x11d0, 0xba, 0x39, 0x0, 0xc0, 0x4f, 0xd7, 0xed, 0x67); + +/* + * DPLDATA_PLAYERSCORE + * + * Data structure to hold an array of long integers representing a player score. + * Application must allocate enough memory to hold all the scores. + */ +typedef struct _DPLDATA_PLAYERSCORE +{ + DWORD dwScoreCount; + LONG Score[1]; +} DPLDATA_PLAYERSCORE, FAR *LPDPLDATA_PLAYERSCORE; + +/**************************************************************************** + * + * DirectPlay Address ID's + * + ****************************************************************************/ + +/* DirectPlay Address + * + * A DirectPlay address consists of multiple chunks of data, each tagged + * with a GUID signifying the type of data in the chunk. The chunk also + * has a length so that unknown chunk types can be skipped. + * + * The EnumAddress() function is used to parse these address data chunks. + */ + +/* + * DPADDRESS + * + * Header for block of address data elements + */ +typedef struct _DPADDRESS +{ + GUID guidDataType; + DWORD dwDataSize; +} DPADDRESS; + +typedef DPADDRESS FAR *LPDPADDRESS; + +/* + * DPAID_TotalSize + * + * Chunk is a DWORD containing size of entire DPADDRESS structure + */ + +// {1318F560-912C-11d0-9DAA-00A0C90A43CB} +DEFINE_GUID(DPAID_TotalSize, +0x1318f560, 0x912c, 0x11d0, 0x9d, 0xaa, 0x0, 0xa0, 0xc9, 0xa, 0x43, 0xcb); + +/* + * DPAID_ServiceProvider + * + * Chunk is a GUID describing the service provider that created the chunk. + * All addresses must contain this chunk. + */ + +// {07D916C0-E0AF-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPAID_ServiceProvider, +0x7d916c0, 0xe0af, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/* + * DPAID_LobbyProvider + * + * Chunk is a GUID describing the lobby provider that created the chunk. + * All addresses must contain this chunk. + */ + +// {59B95640-9667-11d0-A77D-0000F803ABFC} +DEFINE_GUID(DPAID_LobbyProvider, +0x59b95640, 0x9667, 0x11d0, 0xa7, 0x7d, 0x0, 0x0, 0xf8, 0x3, 0xab, 0xfc); + +/* + * DPAID_Phone and DPAID_PhoneW + * + * Chunk is a string containing a phone number (i.e. "1-800-555-1212") + * in ANSI or UNICODE format + */ + +// {78EC89A0-E0AF-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPAID_Phone, +0x78ec89a0, 0xe0af, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +// {BA5A7A70-9DBF-11d0-9CC1-00A0C905425E} +DEFINE_GUID(DPAID_PhoneW, +0xba5a7a70, 0x9dbf, 0x11d0, 0x9c, 0xc1, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/* + * DPAID_Modem and DPAID_ModemW + * + * Chunk is a string containing a modem name registered with TAPI + * in ANSI or UNICODE format + */ + +// {F6DCC200-A2FE-11d0-9C4F-00A0C905425E} +DEFINE_GUID(DPAID_Modem, +0xf6dcc200, 0xa2fe, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +// {01FD92E0-A2FF-11d0-9C4F-00A0C905425E} +DEFINE_GUID(DPAID_ModemW, +0x1fd92e0, 0xa2ff, 0x11d0, 0x9c, 0x4f, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/* + * DPAID_Inet and DPAID_InetW + * + * Chunk is a string containing a TCP/IP host name or an IP address + * (i.e. "dplay.microsoft.com" or "137.55.100.173") in ANSI or UNICODE format + */ + +// {C4A54DA0-E0AF-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPAID_INet, +0xc4a54da0, 0xe0af, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +// {E63232A0-9DBF-11d0-9CC1-00A0C905425E} +DEFINE_GUID(DPAID_INetW, +0xe63232a0, 0x9dbf, 0x11d0, 0x9c, 0xc1, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/* + * DPCOMPORTADDRESS + * + * Used to specify com port settings. The constants that define baud rate, + * stop bits and parity are defined in WINBASE.H. The constants for flow + * control are given below. + */ + +#define DPCPA_NOFLOW 0 // no flow control +#define DPCPA_XONXOFFFLOW 1 // software flow control +#define DPCPA_RTSFLOW 2 // hardware flow control with RTS +#define DPCPA_DTRFLOW 3 // hardware flow control with DTR +#define DPCPA_RTSDTRFLOW 4 // hardware flow control with RTS and DTR + +typedef struct _DPCOMPORTADDRESS +{ + DWORD dwComPort; // COM port to use (1-4) + DWORD dwBaudRate; // baud rate (100-256k) + DWORD dwStopBits; // no. stop bits (1-2) + DWORD dwParity; // parity (none, odd, even, mark) + DWORD dwFlowControl; // flow control (none, xon/xoff, rts, dtr) +} DPCOMPORTADDRESS; + +typedef DPCOMPORTADDRESS FAR *LPDPCOMPORTADDRESS; + +/* + * DPAID_ComPort + * + * Chunk contains a DPCOMPORTADDRESS structure defining the serial port. + */ + +// {F2F0CE00-E0AF-11cf-9C4E-00A0C905425E} +DEFINE_GUID(DPAID_ComPort, +0xf2f0ce00, 0xe0af, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e); + +/**************************************************************************** + * + * dplobby 1.0 obsolete definitions + * Included for compatibility only. + * + ****************************************************************************/ +#define DPLAD_SYSTEM DPLMSG_SYSTEM + + +#ifdef __cplusplus +}; +#endif /* __cplusplus */ + +#endif /* __DPLOBBY_INCLUDED__ */ + diff --git a/include/vdsetup.h b/include/vdsetup.h new file mode 100644 index 0000000..566def1 --- /dev/null +++ b/include/vdsetup.h @@ -0,0 +1,268 @@ +/*========================================================================== + * + * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. + * + * File: dsetup.h + * Content: DirectXSetup, error codes and flags + ***************************************************************************/ + +#ifndef __DSETUP_H__ +#define __DSETUP_H__ + +#include // windows stuff + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#else +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +// DSETUP Error Codes, must remain compatible with previous setup. +#define DSETUPERR_SUCCESS_RESTART 1 +#define DSETUPERR_SUCCESS 0 +#define DSETUPERR_BADWINDOWSVERSION -1 +#define DSETUPERR_SOURCEFILENOTFOUND -2 +#define DSETUPERR_BADSOURCESIZE -3 +#define DSETUPERR_BADSOURCETIME -4 +#define DSETUPERR_NOCOPY -5 +#define DSETUPERR_OUTOFDISKSPACE -6 +#define DSETUPERR_CANTFINDINF -7 +#define DSETUPERR_CANTFINDDIR -8 +#define DSETUPERR_INTERNAL -9 +#define DSETUPERR_NTWITHNO3D -10 /* REM: obsolete, you'll never see this */ +#define DSETUPERR_UNKNOWNOS -11 +#define DSETUPERR_USERHITCANCEL -12 +#define DSETUPERR_NOTPREINSTALLEDONNT -13 + +// DSETUP flags. DirectX 5.0 apps should use these flags only. +#define DSETUP_DDRAWDRV 0x00000008 /* install DirectDraw Drivers */ +#define DSETUP_DSOUNDDRV 0x00000010 /* install DirectSound Drivers */ +#define DSETUP_DXCORE 0x00010000 /* install DirectX runtime */ +#define DSETUP_DIRECTX (DSETUP_DXCORE|DSETUP_DDRAWDRV|DSETUP_DSOUNDDRV) +#define DSETUP_TESTINSTALL 0x00020000 /* just test install, don't do anything */ + +// These OBSOLETE flags are here for compatibility with pre-DX5 apps only. +// They are present to allow DX3 apps to be recompiled with DX5 and still work. +// DO NOT USE THEM for DX5. They will go away in future DX releases. +#define DSETUP_DDRAW 0x00000001 /* OBSOLETE. install DirectDraw */ +#define DSETUP_DSOUND 0x00000002 /* OBSOLETE. install DirectSound */ +#define DSETUP_DPLAY 0x00000004 /* OBSOLETE. install DirectPlay */ +#define DSETUP_DPLAYSP 0x00000020 /* OBSOLETE. install DirectPlay Providers */ +#define DSETUP_DVIDEO 0x00000040 /* OBSOLETE. install DirectVideo */ +#define DSETUP_D3D 0x00000200 /* OBSOLETE. install Direct3D */ +#define DSETUP_DINPUT 0x00000800 /* OBSOLETE. install DirectInput */ +#define DSETUP_DIRECTXSETUP 0x00001000 /* OBSOLETE. install DirectXSetup DLL's */ +#define DSETUP_NOUI 0x00002000 /* OBSOLETE. install DirectX with NO UI */ +#define DSETUP_PROMPTFORDRIVERS 0x10000000 /* OBSOLETE. prompt when replacing display/audio drivers */ +#define DSETUP_RESTOREDRIVERS 0x20000000 /* OBSOLETE. restore display/audio drivers */ + + + +//****************************************************************** +// DirectX Setup Callback mechanism +//****************************************************************** + +// DSETUP Message Info Codes, passed to callback as Reason parameter. +#define DSETUP_CB_MSG_NOMESSAGE 0 +#define DSETUP_CB_MSG_CANTINSTALL_UNKNOWNOS 1 +#define DSETUP_CB_MSG_CANTINSTALL_NT 2 +#define DSETUP_CB_MSG_CANTINSTALL_BETA 3 +#define DSETUP_CB_MSG_CANTINSTALL_NOTWIN32 4 +#define DSETUP_CB_MSG_CANTINSTALL_WRONGLANGUAGE 5 +#define DSETUP_CB_MSG_CANTINSTALL_WRONGPLATFORM 6 +#define DSETUP_CB_MSG_PREINSTALL_NT 7 +#define DSETUP_CB_MSG_NOTPREINSTALLEDONNT 8 +#define DSETUP_CB_MSG_SETUP_INIT_FAILED 9 +#define DSETUP_CB_MSG_INTERNAL_ERROR 10 +#define DSETUP_CB_MSG_CHECK_DRIVER_UPGRADE 11 +#define DSETUP_CB_MSG_OUTOFDISKSPACE 12 +#define DSETUP_CB_MSG_BEGIN_INSTALL 13 +#define DSETUP_CB_MSG_BEGIN_INSTALL_RUNTIME 14 +#define DSETUP_CB_MSG_BEGIN_INSTALL_DRIVERS 15 +#define DSETUP_CB_MSG_BEGIN_RESTORE_DRIVERS 16 +#define DSETUP_CB_MSG_FILECOPYERROR 17 + + +#define DSETUP_CB_UPGRADE_TYPE_MASK 0x000F +#define DSETUP_CB_UPGRADE_KEEP 0x0001 +#define DSETUP_CB_UPGRADE_SAFE 0x0002 +#define DSETUP_CB_UPGRADE_FORCE 0x0004 +#define DSETUP_CB_UPGRADE_UNKNOWN 0x0008 + +#define DSETUP_CB_UPGRADE_HASWARNINGS 0x0100 +#define DSETUP_CB_UPGRADE_CANTBACKUP 0x0200 + +#define DSETUP_CB_UPGRADE_DEVICE_ACTIVE 0x0800 + +#define DSETUP_CB_UPGRADE_DEVICE_DISPLAY 0x1000 +#define DSETUP_CB_UPGRADE_DEVICE_MEDIA 0x2000 + + +typedef struct _DSETUP_CB_UPGRADEINFO +{ + DWORD UpgradeFlags; +} DSETUP_CB_UPGRADEINFO; + +typedef struct _DSETUP_CB_FILECOPYERROR +{ + DWORD dwError; +} DSETUP_CB_FILECOPYERROR; + + +#ifdef _WIN32 +// +// Data Structures +// +#ifndef UNICODE_ONLY +typedef struct _DIRECTXREGISTERAPPA { + DWORD dwSize; + DWORD dwFlags; + LPSTR lpszApplicationName; + LPGUID lpGUID; + LPSTR lpszFilename; + LPSTR lpszCommandLine; + LPSTR lpszPath; + LPSTR lpszCurrentDirectory; +} DIRECTXREGISTERAPPA, *PDIRECTXREGISTERAPPA, *LPDIRECTXREGISTERAPPA; +#endif //!UNICODE_ONLY +#ifndef ANSI_ONLY +typedef struct _DIRECTXREGISTERAPPW { + DWORD dwSize; + DWORD dwFlags; + LPWSTR lpszApplicationName; + LPGUID lpGUID; + LPWSTR lpszFilename; + LPWSTR lpszCommandLine; + LPWSTR lpszPath; + LPWSTR lpszCurrentDirectory; +} DIRECTXREGISTERAPPW, *PDIRECTXREGISTERAPPW, *LPDIRECTXREGISTERAPPW; +#endif //!ANSI_ONLY +#ifdef UNICODE +typedef DIRECTXREGISTERAPPW DIRECTXREGISTERAPP; +typedef PDIRECTXREGISTERAPPW PDIRECTXREGISTERAPP; +typedef LPDIRECTXREGISTERAPPW LPDIRECTXREGISTERAPP; +#else +typedef DIRECTXREGISTERAPPA DIRECTXREGISTERAPP; +typedef PDIRECTXREGISTERAPPA PDIRECTXREGISTERAPP; +typedef LPDIRECTXREGISTERAPPA LPDIRECTXREGISTERAPP; +#endif // UNICODE + + +// +// API +// +#ifndef UNICODE_ONLY +INT +WINAPI +DirectXSetupA( + HWND hWnd, + LPSTR lpszRootPath, + DWORD dwFlags + ); +#endif //!UNICODE_ONLY +#ifndef ANSI_ONLY +INT +WINAPI +DirectXSetupW( + HWND hWnd, + LPWSTR lpszRootPath, + DWORD dwFlags + ); +#endif //!ANSI_ONLY +#ifdef UNICODE +#define DirectXSetup DirectXSetupW +#else +#define DirectXSetup DirectXSetupA +#endif // !UNICODE + +#ifndef UNICODE_ONLY +INT +WINAPI +DirectXDeviceDriverSetupA( + HWND hWnd, + LPSTR lpszDriverClass, + LPSTR lpszDriverPath, + DWORD dwFlags + ); +#endif //!UNICODE_ONLY +#ifndef ANSI_ONLY +INT +WINAPI +DirectXDeviceDriverSetupW( + HWND hWnd, + LPWSTR lpszDriverClass, + LPWSTR lpszDriverPath, + DWORD dwFlags + ); +#endif //!ANSI_ONLY +#ifdef UNICODE +#define DirectXDeviceDriverSetup DirectXDeviceDriverSetupW +#else +#define DirectXDeviceDriverSetup DirectXDeviceDriverSetupA +#endif // !UNICODE + +#ifndef UNICODE_ONLY +INT +WINAPI +DirectXRegisterApplicationA( + HWND hWnd, + LPDIRECTXREGISTERAPPA lpDXRegApp + ); +#endif //!UNICODE_ONLY +#ifndef ANSI_ONLY +INT +WINAPI +DirectXRegisterApplicationW( + HWND hWnd, + LPDIRECTXREGISTERAPPW lpDXRegApp + ); +#endif //!ANSI_ONLY +#ifdef UNICODE +#define DirectXRegisterApplication DirectXRegisterApplicationW +#else +#define DirectXRegisterApplication DirectXRegisterApplicationA +#endif // !UNICODE + +INT +WINAPI +DirectXUnRegisterApplication( + HWND hWnd, + LPGUID lpGUID + ); + +// +// Function Pointers +// +#ifdef UNICODE +typedef INT (WINAPI * LPDIRECTXSETUP)(HWND, LPWSTR, DWORD); +typedef INT (WINAPI * LPDIRECTXDEVICEDRIVERSETUP)(HWND, LPWSTR, LPSTR, DWORD); +typedef INT (WINAPI * LPDIRECTXREGISTERAPPLICATION)(HWND, LPDIRECTXREGISTERAPPW); +#else +typedef INT (WINAPI * LPDIRECTXSETUP)(HWND, LPSTR, DWORD); +typedef INT (WINAPI * LPDIRECTXDEVICEDRIVERSETUP)(HWND, LPSTR, LPSTR, DWORD); +typedef INT (WINAPI * LPDIRECTXREGISTERAPPLICATION)(HWND, LPDIRECTXREGISTERAPPA); +#endif // UNICODE + +typedef DWORD (FAR PASCAL * DSETUP_CALLBACK)(DWORD Reason, + DWORD MsgType, /* Same as flags to MessageBox */ + LPSTR szMessage, + LPSTR szName, + void *pInfo); + +INT WINAPI DirectXSetupSetCallback(DSETUP_CALLBACK Callback); +INT WINAPI DirectXSetupGetVersion(DWORD *lpdwVersion, DWORD *lpdwMinorVersion); + +#endif // WIN32 + + +#ifdef __cplusplus +}; +#endif + +#endif + diff --git a/include/vdsound.h b/include/vdsound.h new file mode 100644 index 0000000..878cd5a --- /dev/null +++ b/include/vdsound.h @@ -0,0 +1,864 @@ +/*==========================================================================; + * + * Copyright (C) 1995,1996 Microsoft Corporation. All Rights Reserved. + * + * File: dsound.h + * Content: DirectSound include file + * + **************************************************************************/ + +#ifndef __DSOUND_INCLUDED__ +#define __DSOUND_INCLUDED__ + +#include "vd3dtypes.h" + +#define COM_NO_WINDOWS_H +#include + +#define _FACDS 0x878 +#define MAKE_DSHRESULT(code) MAKE_HRESULT(1, _FACDS, code) + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Direct Sound Component GUID {47D4D946-62E8-11cf-93BC-444553540000} +DEFINE_GUID(CLSID_DirectSound, 0x47d4d946, 0x62e8, 0x11cf, 0x93, 0xbc, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); + +// DirectSound Capture Component GUID {B0210780-89CD-11d0-AF08-00A0C925CD16} +DEFINE_GUID(CLSID_DirectSoundCapture, 0xb0210780, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); + +// +// Structures +// + +#ifdef __cplusplus +// 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined +struct IDirectSound; +struct IDirectSoundBuffer; +struct IDirectSound3DListener; +struct IDirectSound3DBuffer; +struct IDirectSoundCapture; +struct IDirectSoundCaptureBuffer; +struct IDirectSoundNotify; +#endif // __cplusplus + +typedef struct IDirectSound *LPDIRECTSOUND; +typedef struct IDirectSoundBuffer *LPDIRECTSOUNDBUFFER; +typedef struct IDirectSound3DListener *LPDIRECTSOUND3DLISTENER; +typedef struct IDirectSound3DBuffer *LPDIRECTSOUND3DBUFFER; +typedef struct IDirectSoundCapture *LPDIRECTSOUNDCAPTURE; +typedef struct IDirectSoundCaptureBuffer *LPDIRECTSOUNDCAPTUREBUFFER; +typedef struct IDirectSoundNotify *LPDIRECTSOUNDNOTIFY; + +typedef struct _DSCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwMinSecondarySampleRate; + DWORD dwMaxSecondarySampleRate; + DWORD dwPrimaryBuffers; + DWORD dwMaxHwMixingAllBuffers; + DWORD dwMaxHwMixingStaticBuffers; + DWORD dwMaxHwMixingStreamingBuffers; + DWORD dwFreeHwMixingAllBuffers; + DWORD dwFreeHwMixingStaticBuffers; + DWORD dwFreeHwMixingStreamingBuffers; + DWORD dwMaxHw3DAllBuffers; + DWORD dwMaxHw3DStaticBuffers; + DWORD dwMaxHw3DStreamingBuffers; + DWORD dwFreeHw3DAllBuffers; + DWORD dwFreeHw3DStaticBuffers; + DWORD dwFreeHw3DStreamingBuffers; + DWORD dwTotalHwMemBytes; + DWORD dwFreeHwMemBytes; + DWORD dwMaxContigFreeHwMemBytes; + DWORD dwUnlockTransferRateHwBuffers; + DWORD dwPlayCpuOverheadSwBuffers; + DWORD dwReserved1; + DWORD dwReserved2; +} DSCAPS, *LPDSCAPS; + +typedef const DSCAPS *LPCDSCAPS; + +typedef struct _DSBCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwUnlockTransferRate; + DWORD dwPlayCpuOverhead; +} DSBCAPS, *LPDSBCAPS; + +typedef const DSBCAPS *LPCDSBCAPS; + +typedef struct _DSBUFFERDESC +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + LPWAVEFORMATEX lpwfxFormat; +} DSBUFFERDESC, *LPDSBUFFERDESC; + +typedef const DSBUFFERDESC *LPCDSBUFFERDESC; + +typedef struct _DS3DBUFFER +{ + DWORD dwSize; + D3DVECTOR vPosition; + D3DVECTOR vVelocity; + DWORD dwInsideConeAngle; + DWORD dwOutsideConeAngle; + D3DVECTOR vConeOrientation; + LONG lConeOutsideVolume; + D3DVALUE flMinDistance; + D3DVALUE flMaxDistance; + DWORD dwMode; +} DS3DBUFFER, *LPDS3DBUFFER; + +typedef const DS3DBUFFER *LPCDS3DBUFFER; + +typedef struct _DS3DLISTENER +{ + DWORD dwSize; + D3DVECTOR vPosition; + D3DVECTOR vVelocity; + D3DVECTOR vOrientFront; + D3DVECTOR vOrientTop; + D3DVALUE flDistanceFactor; + D3DVALUE flRolloffFactor; + D3DVALUE flDopplerFactor; +} DS3DLISTENER, *LPDS3DLISTENER; + +typedef const DS3DLISTENER *LPCDS3DLISTENER; + +typedef struct _DSCCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwFormats; + DWORD dwChannels; +} DSCCAPS, *LPDSCCAPS; + +typedef const DSCCAPS *LPCDSCCAPS; + +typedef struct _DSCBUFFERDESC +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + LPWAVEFORMATEX lpwfxFormat; +} DSCBUFFERDESC, *LPDSCBUFFERDESC; + +typedef const DSCBUFFERDESC *LPCDSCBUFFERDESC; + +typedef struct _DSCBCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; +} DSCBCAPS, *LPDSCBCAPS; + +typedef const DSCBCAPS *LPCDSCBCAPS; + +typedef struct _DSBPOSITIONNOTIFY +{ + DWORD dwOffset; + HANDLE hEventNotify; +} DSBPOSITIONNOTIFY, *LPDSBPOSITIONNOTIFY; + +typedef const DSBPOSITIONNOTIFY *LPCDSBPOSITIONNOTIFY; + +// +// Compatibility typedefs +// + +typedef LPDIRECTSOUND *LPLPDIRECTSOUND; +typedef LPDIRECTSOUNDBUFFER *LPLPDIRECTSOUNDBUFFER; +typedef LPDIRECTSOUND3DLISTENER *LPLPDIRECTSOUND3DLISTENER; +typedef LPDIRECTSOUND3DBUFFER *LPLPDIRECTSOUND3DBUFFER; +typedef LPDIRECTSOUNDCAPTURE *LPLPDIRECTSOUNDCAPTURE; +typedef LPDIRECTSOUNDCAPTUREBUFFER *LPLPDIRECTSOUNDCAPTUREBUFFER; +typedef LPDIRECTSOUNDNOTIFY *LPLPDIRECTSOUNDNOTIFY; +typedef LPVOID *LPLPVOID; +typedef const WAVEFORMATEX *LPCWAVEFORMATEX; + +// +// DirectSound API +// + +typedef BOOL (CALLBACK *LPDSENUMCALLBACKW)(LPGUID, LPCWSTR, LPCWSTR, LPVOID); +typedef BOOL (CALLBACK *LPDSENUMCALLBACKA)(LPGUID, LPCSTR, LPCSTR, LPVOID); + +extern HRESULT WINAPI DirectSoundCreate(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); +extern HRESULT WINAPI DirectSoundEnumerateW(LPDSENUMCALLBACKW, LPVOID); +extern HRESULT WINAPI DirectSoundEnumerateA(LPDSENUMCALLBACKA, LPVOID); + +extern HRESULT WINAPI DirectSoundCaptureCreate(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN); +extern HRESULT WINAPI DirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW, LPVOID); +extern HRESULT WINAPI DirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA, LPVOID); + +#ifdef UNICODE +#define LPDSENUMCALLBACK LPDSENUMCALLBACKW +#define DirectSoundEnumerate DirectSoundEnumerateW +#define DirectSoundCaptureEnumerate DirectSoundCaptureEnumerateW +#else // UNICODE +#define LPDSENUMCALLBACK LPDSENUMCALLBACKA +#define DirectSoundEnumerate DirectSoundEnumerateA +#define DirectSoundCaptureEnumerate DirectSoundCaptureEnumerateA +#endif // UNICODE + +// +// IDirectSound +// + +DEFINE_GUID(IID_IDirectSound, 0x279AFA83, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); + +#undef INTERFACE +#define INTERFACE IDirectSound + +DECLARE_INTERFACE_(IDirectSound, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSound methods + STDMETHOD(CreateSoundBuffer) (THIS_ LPCDSBUFFERDESC, LPDIRECTSOUNDBUFFER *, LPUNKNOWN) PURE; + STDMETHOD(GetCaps) (THIS_ LPDSCAPS) PURE; + STDMETHOD(DuplicateSoundBuffer) (THIS_ LPDIRECTSOUNDBUFFER, LPDIRECTSOUNDBUFFER *) PURE; + STDMETHOD(SetCooperativeLevel) (THIS_ HWND, DWORD) PURE; + STDMETHOD(Compact) (THIS) PURE; + STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD) PURE; + STDMETHOD(SetSpeakerConfig) (THIS_ DWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSound_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSound_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSound_CreateSoundBuffer(p,a,b,c) (p)->lpVtbl->CreateSoundBuffer(p,a,b,c) +#define IDirectSound_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectSound_DuplicateSoundBuffer(p,a,b) (p)->lpVtbl->DuplicateSoundBuffer(p,a,b) +#define IDirectSound_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) +#define IDirectSound_Compact(p) (p)->lpVtbl->Compact(p) +#define IDirectSound_GetSpeakerConfig(p,a) (p)->lpVtbl->GetSpeakerConfig(p,a) +#define IDirectSound_SetSpeakerConfig(p,b) (p)->lpVtbl->SetSpeakerConfig(p,b) +#define IDirectSound_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSound_AddRef(p) (p)->AddRef() +#define IDirectSound_Release(p) (p)->Release() +#define IDirectSound_CreateSoundBuffer(p,a,b,c) (p)->CreateSoundBuffer(a,b,c) +#define IDirectSound_GetCaps(p,a) (p)->GetCaps(a) +#define IDirectSound_DuplicateSoundBuffer(p,a,b) (p)->DuplicateSoundBuffer(a,b) +#define IDirectSound_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b) +#define IDirectSound_Compact(p) (p)->Compact() +#define IDirectSound_GetSpeakerConfig(p,a) (p)->GetSpeakerConfig(a) +#define IDirectSound_SetSpeakerConfig(p,b) (p)->SetSpeakerConfig(b) +#define IDirectSound_Initialize(p,a) (p)->Initialize(a) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSoundBuffer +// + +DEFINE_GUID(IID_IDirectSoundBuffer, 0x279AFA85, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); + +#undef INTERFACE +#define INTERFACE IDirectSoundBuffer + +DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSoundBuffer methods + STDMETHOD(GetCaps) (THIS_ LPDSBCAPS) PURE; + STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD) PURE; + STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD) PURE; + STDMETHOD(GetVolume) (THIS_ LPLONG) PURE; + STDMETHOD(GetPan) (THIS_ LPLONG) PURE; + STDMETHOD(GetFrequency) (THIS_ LPDWORD) PURE; + STDMETHOD(GetStatus) (THIS_ LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPDIRECTSOUND, LPCDSBUFFERDESC) PURE; + STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID *, LPDWORD, LPVOID *, LPDWORD, DWORD) PURE; + STDMETHOD(Play) (THIS_ DWORD, DWORD, DWORD) PURE; + STDMETHOD(SetCurrentPosition) (THIS_ DWORD) PURE; + STDMETHOD(SetFormat) (THIS_ LPCWAVEFORMATEX) PURE; + STDMETHOD(SetVolume) (THIS_ LONG) PURE; + STDMETHOD(SetPan) (THIS_ LONG) PURE; + STDMETHOD(SetFrequency) (THIS_ DWORD) PURE; + STDMETHOD(Stop) (THIS) PURE; + STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(Restore) (THIS) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSoundBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSoundBuffer_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSoundBuffer_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectSoundBuffer_GetCurrentPosition(p,a,b) (p)->lpVtbl->GetCurrentPosition(p,a,b) +#define IDirectSoundBuffer_GetFormat(p,a,b,c) (p)->lpVtbl->GetFormat(p,a,b,c) +#define IDirectSoundBuffer_GetVolume(p,a) (p)->lpVtbl->GetVolume(p,a) +#define IDirectSoundBuffer_GetPan(p,a) (p)->lpVtbl->GetPan(p,a) +#define IDirectSoundBuffer_GetFrequency(p,a) (p)->lpVtbl->GetFrequency(p,a) +#define IDirectSoundBuffer_GetStatus(p,a) (p)->lpVtbl->GetStatus(p,a) +#define IDirectSoundBuffer_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectSoundBuffer_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g) +#define IDirectSoundBuffer_Play(p,a,b,c) (p)->lpVtbl->Play(p,a,b,c) +#define IDirectSoundBuffer_SetCurrentPosition(p,a) (p)->lpVtbl->SetCurrentPosition(p,a) +#define IDirectSoundBuffer_SetFormat(p,a) (p)->lpVtbl->SetFormat(p,a) +#define IDirectSoundBuffer_SetVolume(p,a) (p)->lpVtbl->SetVolume(p,a) +#define IDirectSoundBuffer_SetPan(p,a) (p)->lpVtbl->SetPan(p,a) +#define IDirectSoundBuffer_SetFrequency(p,a) (p)->lpVtbl->SetFrequency(p,a) +#define IDirectSoundBuffer_Stop(p) (p)->lpVtbl->Stop(p) +#define IDirectSoundBuffer_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d) +#define IDirectSoundBuffer_Restore(p) (p)->lpVtbl->Restore(p) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSoundBuffer_AddRef(p) (p)->AddRef() +#define IDirectSoundBuffer_Release(p) (p)->Release() +#define IDirectSoundBuffer_GetCaps(p,a) (p)->GetCaps(a) +#define IDirectSoundBuffer_GetCurrentPosition(p,a,b) (p)->GetCurrentPosition(a,b) +#define IDirectSoundBuffer_GetFormat(p,a,b,c) (p)->GetFormat(a,b,c) +#define IDirectSoundBuffer_GetVolume(p,a) (p)->GetVolume(a) +#define IDirectSoundBuffer_GetPan(p,a) (p)->GetPan(a) +#define IDirectSoundBuffer_GetFrequency(p,a) (p)->GetFrequency(a) +#define IDirectSoundBuffer_GetStatus(p,a) (p)->GetStatus(a) +#define IDirectSoundBuffer_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectSoundBuffer_Lock(p,a,b,c,d,e,f,g) (p)->Lock(a,b,c,d,e,f,g) +#define IDirectSoundBuffer_Play(p,a,b,c) (p)->Play(a,b,c) +#define IDirectSoundBuffer_SetCurrentPosition(p,a) (p)->SetCurrentPosition(a) +#define IDirectSoundBuffer_SetFormat(p,a) (p)->SetFormat(a) +#define IDirectSoundBuffer_SetVolume(p,a) (p)->SetVolume(a) +#define IDirectSoundBuffer_SetPan(p,a) (p)->SetPan(a) +#define IDirectSoundBuffer_SetFrequency(p,a) (p)->SetFrequency(a) +#define IDirectSoundBuffer_Stop(p) (p)->Stop() +#define IDirectSoundBuffer_Unlock(p,a,b,c,d) (p)->Unlock(a,b,c,d) +#define IDirectSoundBuffer_Restore(p) (p)->Restore() +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSound3DListener +// + +DEFINE_GUID(IID_IDirectSound3DListener, 0x279AFA84, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); + +#undef INTERFACE +#define INTERFACE IDirectSound3DListener + +DECLARE_INTERFACE_(IDirectSound3DListener, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID FAR *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSound3D methods + STDMETHOD(GetAllParameters) (THIS_ LPDS3DLISTENER) PURE; + STDMETHOD(GetDistanceFactor) (THIS_ LPD3DVALUE) PURE; + STDMETHOD(GetDopplerFactor) (THIS_ LPD3DVALUE) PURE; + STDMETHOD(GetOrientation) (THIS_ LPD3DVECTOR, LPD3DVECTOR) PURE; + STDMETHOD(GetPosition) (THIS_ LPD3DVECTOR) PURE; + STDMETHOD(GetRolloffFactor) (THIS_ LPD3DVALUE) PURE; + STDMETHOD(GetVelocity) (THIS_ LPD3DVECTOR) PURE; + STDMETHOD(SetAllParameters) (THIS_ LPCDS3DLISTENER, DWORD) PURE; + STDMETHOD(SetDistanceFactor) (THIS_ D3DVALUE, DWORD) PURE; + STDMETHOD(SetDopplerFactor) (THIS_ D3DVALUE, DWORD) PURE; + STDMETHOD(SetOrientation) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; + STDMETHOD(SetPosition) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; + STDMETHOD(SetRolloffFactor) (THIS_ D3DVALUE, DWORD) PURE; + STDMETHOD(SetVelocity) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; + STDMETHOD(CommitDeferredSettings) (THIS) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound3DListener_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSound3DListener_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSound3DListener_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSound3DListener_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a) +#define IDirectSound3DListener_GetDistanceFactor(p,a) (p)->lpVtbl->GetDistanceFactor(p,a) +#define IDirectSound3DListener_GetDopplerFactor(p,a) (p)->lpVtbl->GetDopplerFactor(p,a) +#define IDirectSound3DListener_GetOrientation(p,a,b) (p)->lpVtbl->GetOrientation(p,a,b) +#define IDirectSound3DListener_GetPosition(p,a) (p)->lpVtbl->GetPosition(p,a) +#define IDirectSound3DListener_GetRolloffFactor(p,a) (p)->lpVtbl->GetRolloffFactor(p,a) +#define IDirectSound3DListener_GetVelocity(p,a) (p)->lpVtbl->GetVelocity(p,a) +#define IDirectSound3DListener_SetAllParameters(p,a,b) (p)->lpVtbl->SetAllParameters(p,a,b) +#define IDirectSound3DListener_SetDistanceFactor(p,a,b) (p)->lpVtbl->SetDistanceFactor(p,a,b) +#define IDirectSound3DListener_SetDopplerFactor(p,a,b) (p)->lpVtbl->SetDopplerFactor(p,a,b) +#define IDirectSound3DListener_SetOrientation(p,a,b,c,d,e,f,g) (p)->lpVtbl->SetOrientation(p,a,b,c,d,e,f,g) +#define IDirectSound3DListener_SetPosition(p,a,b,c,d) (p)->lpVtbl->SetPosition(p,a,b,c,d) +#define IDirectSound3DListener_SetRolloffFactor(p,a,b) (p)->lpVtbl->SetRolloffFactor(p,a,b) +#define IDirectSound3DListener_SetVelocity(p,a,b,c,d) (p)->lpVtbl->SetVelocity(p,a,b,c,d) +#define IDirectSound3DListener_CommitDeferredSettings(p) (p)->lpVtbl->CommitDeferredSettings(p) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound3DListener_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSound3DListener_AddRef(p) (p)->AddRef() +#define IDirectSound3DListener_Release(p) (p)->Release() +#define IDirectSound3DListener_GetAllParameters(p,a) (p)->GetAllParameters(a) +#define IDirectSound3DListener_GetDistanceFactor(p,a) (p)->GetDistanceFactor(a) +#define IDirectSound3DListener_GetDopplerFactor(p,a) (p)->GetDopplerFactor(a) +#define IDirectSound3DListener_GetOrientation(p,a,b) (p)->GetOrientation(a,b) +#define IDirectSound3DListener_GetPosition(p,a) (p)->GetPosition(a) +#define IDirectSound3DListener_GetRolloffFactor(p,a) (p)->GetRolloffFactor(a) +#define IDirectSound3DListener_GetVelocity(p,a) (p)->GetVelocity(a) +#define IDirectSound3DListener_SetAllParameters(p,a,b) (p)->SetAllParameters(a,b) +#define IDirectSound3DListener_SetDistanceFactor(p,a,b) (p)->SetDistanceFactor(a,b) +#define IDirectSound3DListener_SetDopplerFactor(p,a,b) (p)->SetDopplerFactor(a,b) +#define IDirectSound3DListener_SetOrientation(p,a,b,c,d,e,f,g) (p)->SetOrientation(a,b,c,d,e,f,g) +#define IDirectSound3DListener_SetPosition(p,a,b,c,d) (p)->SetPosition(a,b,c,d) +#define IDirectSound3DListener_SetRolloffFactor(p,a,b) (p)->SetRolloffFactor(a,b) +#define IDirectSound3DListener_SetVelocity(p,a,b,c,d) (p)->SetVelocity(a,b,c,d) +#define IDirectSound3DListener_CommitDeferredSettings(p) (p)->CommitDeferredSettings() +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSound3DBuffer +// + +DEFINE_GUID(IID_IDirectSound3DBuffer, 0x279AFA86, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60); + +#undef INTERFACE +#define INTERFACE IDirectSound3DBuffer + +DECLARE_INTERFACE_(IDirectSound3DBuffer, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSoundBuffer3D methods + STDMETHOD(GetAllParameters) (THIS_ LPDS3DBUFFER) PURE; + STDMETHOD(GetConeAngles) (THIS_ LPDWORD, LPDWORD) PURE; + STDMETHOD(GetConeOrientation) (THIS_ LPD3DVECTOR) PURE; + STDMETHOD(GetConeOutsideVolume) (THIS_ LPLONG) PURE; + STDMETHOD(GetMaxDistance) (THIS_ LPD3DVALUE) PURE; + STDMETHOD(GetMinDistance) (THIS_ LPD3DVALUE) PURE; + STDMETHOD(GetMode) (THIS_ LPDWORD) PURE; + STDMETHOD(GetPosition) (THIS_ LPD3DVECTOR) PURE; + STDMETHOD(GetVelocity) (THIS_ LPD3DVECTOR) PURE; + STDMETHOD(SetAllParameters) (THIS_ LPCDS3DBUFFER, DWORD) PURE; + STDMETHOD(SetConeAngles) (THIS_ DWORD, DWORD, DWORD) PURE; + STDMETHOD(SetConeOrientation) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; + STDMETHOD(SetConeOutsideVolume) (THIS_ LONG, DWORD) PURE; + STDMETHOD(SetMaxDistance) (THIS_ D3DVALUE, DWORD) PURE; + STDMETHOD(SetMinDistance) (THIS_ D3DVALUE, DWORD) PURE; + STDMETHOD(SetMode) (THIS_ DWORD, DWORD) PURE; + STDMETHOD(SetPosition) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; + STDMETHOD(SetVelocity) (THIS_ D3DVALUE, D3DVALUE, D3DVALUE, DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound3DBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSound3DBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSound3DBuffer_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSound3DBuffer_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a) +#define IDirectSound3DBuffer_GetConeAngles(p,a,b) (p)->lpVtbl->GetConeAngles(p,a,b) +#define IDirectSound3DBuffer_GetConeOrientation(p,a) (p)->lpVtbl->GetConeOrientation(p,a) +#define IDirectSound3DBuffer_GetConeOutsideVolume(p,a) (p)->lpVtbl->GetConeOutsideVolume(p,a) +#define IDirectSound3DBuffer_GetPosition(p,a) (p)->lpVtbl->GetPosition(p,a) +#define IDirectSound3DBuffer_GetMinDistance(p,a) (p)->lpVtbl->GetMinDistance(p,a) +#define IDirectSound3DBuffer_GetMaxDistance(p,a) (p)->lpVtbl->GetMaxDistance(p,a) +#define IDirectSound3DBuffer_GetMode(p,a) (p)->lpVtbl->GetMode(p,a) +#define IDirectSound3DBuffer_GetVelocity(p,a) (p)->lpVtbl->GetVelocity(p,a) +#define IDirectSound3DBuffer_SetAllParameters(p,a,b) (p)->lpVtbl->SetAllParameters(p,a,b) +#define IDirectSound3DBuffer_SetConeAngles(p,a,b,c) (p)->lpVtbl->SetConeAngles(p,a,b,c) +#define IDirectSound3DBuffer_SetConeOrientation(p,a,b,c,d) (p)->lpVtbl->SetConeOrientation(p,a,b,c,d) +#define IDirectSound3DBuffer_SetConeOutsideVolume(p,a,b)(p)->lpVtbl->SetConeOutsideVolume(p,a,b) +#define IDirectSound3DBuffer_SetPosition(p,a,b,c,d) (p)->lpVtbl->SetPosition(p,a,b,c,d) +#define IDirectSound3DBuffer_SetMinDistance(p,a,b) (p)->lpVtbl->SetMinDistance(p,a,b) +#define IDirectSound3DBuffer_SetMaxDistance(p,a,b) (p)->lpVtbl->SetMaxDistance(p,a,b) +#define IDirectSound3DBuffer_SetMode(p,a,b) (p)->lpVtbl->SetMode(p,a,b) +#define IDirectSound3DBuffer_SetVelocity(p,a,b,c,d) (p)->lpVtbl->SetVelocity(p,a,b,c,d) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound3DBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSound3DBuffer_AddRef(p) (p)->AddRef() +#define IDirectSound3DBuffer_Release(p) (p)->Release() +#define IDirectSound3DBuffer_GetAllParameters(p,a) (p)->GetAllParameters(a) +#define IDirectSound3DBuffer_GetConeAngles(p,a,b) (p)->GetConeAngles(a,b) +#define IDirectSound3DBuffer_GetConeOrientation(p,a) (p)->GetConeOrientation(a) +#define IDirectSound3DBuffer_GetConeOutsideVolume(p,a) (p)->GetConeOutsideVolume(a) +#define IDirectSound3DBuffer_GetPosition(p,a) (p)->GetPosition(a) +#define IDirectSound3DBuffer_GetMinDistance(p,a) (p)->GetMinDistance(a) +#define IDirectSound3DBuffer_GetMaxDistance(p,a) (p)->GetMaxDistance(a) +#define IDirectSound3DBuffer_GetMode(p,a) (p)->GetMode(a) +#define IDirectSound3DBuffer_GetVelocity(p,a) (p)->GetVelocity(a) +#define IDirectSound3DBuffer_SetAllParameters(p,a,b) (p)->SetAllParameters(a,b) +#define IDirectSound3DBuffer_SetConeAngles(p,a,b,c) (p)->SetConeAngles(a,b,c) +#define IDirectSound3DBuffer_SetConeOrientation(p,a,b,c,d) (p)->SetConeOrientation(a,b,c,d) +#define IDirectSound3DBuffer_SetConeOutsideVolume(p,a,b)(p)->SetConeOutsideVolume(a,b) +#define IDirectSound3DBuffer_SetPosition(p,a,b,c,d) (p)->SetPosition(a,b,c,d) +#define IDirectSound3DBuffer_SetMinDistance(p,a,b) (p)->SetMinDistance(a,b) +#define IDirectSound3DBuffer_SetMaxDistance(p,a,b) (p)->SetMaxDistance(a,b) +#define IDirectSound3DBuffer_SetMode(p,a,b) (p)->SetMode(a,b) +#define IDirectSound3DBuffer_SetVelocity(p,a,b,c,d) (p)->SetVelocity(a,b,c,d) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSoundCapture +// + +DEFINE_GUID(IID_IDirectSoundCapture, 0xb0210781, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); + +#undef INTERFACE +#define INTERFACE IDirectSoundCapture + +DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSoundCapture methods + STDMETHOD(CreateCaptureBuffer) (THIS_ LPCDSCBUFFERDESC, LPDIRECTSOUNDCAPTUREBUFFER *, LPUNKNOWN) PURE; + STDMETHOD(GetCaps) (THIS_ LPDSCCAPS ) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundCapture_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSoundCapture_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSoundCapture_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSoundCapture_CreateCaptureBuffer(p,a,b,c) (p)->lpVtbl->CreateCaptureBuffer(p,a,b,c) +#define IDirectSoundCapture_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectSoundCapture_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundCapture_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSoundCapture_AddRef(p) (p)->AddRef() +#define IDirectSoundCapture_Release(p) (p)->Release() +#define IDirectSoundCapture_CreateCaptureBuffer(p,a,b,c) (p)->CreateCaptureBuffer(a,b,c) +#define IDirectSoundCapture_GetCaps(p,a) (p)->GetCaps(a) +#define IDirectSoundCapture_Initialize(p,a) (p)->Initialize(a) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSoundCaptureBuffer +// + +DEFINE_GUID(IID_IDirectSoundCaptureBuffer, 0xb0210782, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); + +#undef INTERFACE +#define INTERFACE IDirectSoundCaptureBuffer + +DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSoundCaptureBuffer methods + STDMETHOD(GetCaps) (THIS_ LPDSCBCAPS ) PURE; + STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD, LPDWORD ) PURE; + STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX, DWORD, LPDWORD ) PURE; + STDMETHOD(GetStatus) (THIS_ LPDWORD ) PURE; + STDMETHOD(Initialize) (THIS_ LPDIRECTSOUNDCAPTURE, LPCDSCBUFFERDESC) PURE; + STDMETHOD(Lock) (THIS_ DWORD, DWORD, LPVOID *, LPDWORD, LPVOID *, LPDWORD, DWORD) PURE; + STDMETHOD(Start) (THIS_ DWORD) PURE; + STDMETHOD(Stop) (THIS) PURE; + STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundCaptureBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSoundCaptureBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSoundCaptureBuffer_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSoundCaptureBuffer_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectSoundCaptureBuffer_GetCurrentPosition(p,a,b) (p)->lpVtbl->GetCurrentPosition(p,a,b) +#define IDirectSoundCaptureBuffer_GetFormat(p,a,b,c) (p)->lpVtbl->GetFormat(p,a,b,c) +#define IDirectSoundCaptureBuffer_GetStatus(p,a) (p)->lpVtbl->GetStatus(p,a) +#define IDirectSoundCaptureBuffer_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectSoundCaptureBuffer_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g) +#define IDirectSoundCaptureBuffer_Start(p,a) (p)->lpVtbl->Start(p,a) +#define IDirectSoundCaptureBuffer_Stop(p) (p)->lpVtbl->Stop(p) +#define IDirectSoundCaptureBuffer_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundCaptureBuffer_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSoundCaptureBuffer_AddRef(p) (p)->AddRef() +#define IDirectSoundCaptureBuffer_Release(p) (p)->Release() +#define IDirectSoundCaptureBuffer_GetCaps(p,a) (p)->GetCaps(a) +#define IDirectSoundCaptureBuffer_GetCurrentPosition(p,a,b) (p)->GetCurrentPosition(a,b) +#define IDirectSoundCaptureBuffer_GetFormat(p,a,b,c) (p)->GetFormat(a,b,c) +#define IDirectSoundCaptureBuffer_GetStatus(p,a) (p)->GetStatus(a) +#define IDirectSoundCaptureBuffer_Initialize(p,a,b) (p)->Initialize(a,b) +#define IDirectSoundCaptureBuffer_Lock(p,a,b,c,d,e,f,g) (p)->Lock(a,b,c,d,e,f,g) +#define IDirectSoundCaptureBuffer_Start(p,a) (p)->Start(a) +#define IDirectSoundCaptureBuffer_Stop(p) (p)->Stop() +#define IDirectSoundCaptureBuffer_Unlock(p,a,b,c,d) (p)->Unlock(a,b,c,d) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IDirectSoundNotify +// + +DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16); + +#undef INTERFACE +#define INTERFACE IDirectSoundNotify + +DECLARE_INTERFACE_(IDirectSoundNotify, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IDirectSoundNotify methods + STDMETHOD(SetNotificationPositions) (THIS_ DWORD, LPCDSBPOSITIONNOTIFY) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundNotify_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSoundNotify_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSoundNotify_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSoundNotify_SetNotificationPositions(p,a,b) (p)->lpVtbl->SetNotificationPositions(p,a,b) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundNotify_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IDirectSoundNotify_AddRef(p) (p)->AddRef() +#define IDirectSoundNotify_Release(p) (p)->Release() +#define IDirectSoundNotify_SetNotificationPositions(p,a,b) (p)->SetNotificationPositions(a,b) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +// +// IKsPropertySet +// + +#ifndef _IKsPropertySet_ +#define _IKsPropertySet_ + +#ifdef __cplusplus +// 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined +struct IKsPropertySet; +#endif // __cplusplus + +typedef struct IKsPropertySet *LPKSPROPERTYSET; + +#define KSPROPERTY_SUPPORT_GET 0x00000001 +#define KSPROPERTY_SUPPORT_SET 0x00000002 + +DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93); + +#undef INTERFACE +#define INTERFACE IKsPropertySet + +DECLARE_INTERFACE_(IKsPropertySet, IUnknown) +{ + // IUnknown methods + STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + + // IKsPropertySet methods + STDMETHOD(Get) (THIS_ REFGUID, ULONG, LPVOID, ULONG, LPVOID, ULONG, PULONG) PURE; + STDMETHOD(Set) (THIS_ REFGUID, ULONG, LPVOID, ULONG, LPVOID, ULONG) PURE; + STDMETHOD(QuerySupport) (THIS_ REFGUID, ULONG, PULONG) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IKsPropertySet_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IKsPropertySet_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IKsPropertySet_Release(p) (p)->lpVtbl->Release(p) +#define IKsPropertySet_Get(p,a,b,c,d,e,f,g) (p)->lpVtbl->Get(p,a,b,c,d,e,f,g) +#define IKsPropertySet_Set(p,a,b,c,d,e,f) (p)->lpVtbl->Set(p,a,b,c,d,e,f) +#define IKsPropertySet_QuerySupport(p,a,b,c) (p)->lpVtbl->QuerySupport(p,a,b,c) +#else // !defined(__cplusplus) || defined(CINTERFACE) +#define IKsPropertySet_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IKsPropertySet_AddRef(p) (p)->AddRef() +#define IKsPropertySet_Release(p) (p)->Release() +#define IKsPropertySet_Get(p,a,b,c,d,e,f,g) (p)->Get(a,b,c,d,e,f,g) +#define IKsPropertySet_Set(p,a,b,c,d,e,f) (p)->Set(a,b,c,d,e,f) +#define IKsPropertySet_QuerySupport(p,a,b,c) (p)->QuerySupport(a,b,c) +#endif // !defined(__cplusplus) || defined(CINTERFACE) + +#endif // _IKsPropertySet_ + +// +// Return Codes +// + +#define DS_OK 0 + +// The call failed because resources (such as a priority level) +// were already being used by another caller. +#define DSERR_ALLOCATED MAKE_DSHRESULT(10) + +// The control (vol,pan,etc.) requested by the caller is not available. +#define DSERR_CONTROLUNAVAIL MAKE_DSHRESULT(30) + +// An invalid parameter was passed to the returning function +#define DSERR_INVALIDPARAM E_INVALIDARG + +// This call is not valid for the current state of this object +#define DSERR_INVALIDCALL MAKE_DSHRESULT(50) + +// An undetermined error occured inside the DirectSound subsystem +#define DSERR_GENERIC E_FAIL + +// The caller does not have the priority level required for the function to +// succeed. +#define DSERR_PRIOLEVELNEEDED MAKE_DSHRESULT(70) + +// Not enough free memory is available to complete the operation +#define DSERR_OUTOFMEMORY E_OUTOFMEMORY + +// The specified WAVE format is not supported +#define DSERR_BADFORMAT MAKE_DSHRESULT(100) + +// The function called is not supported at this time +#define DSERR_UNSUPPORTED E_NOTIMPL + +// No sound driver is available for use +#define DSERR_NODRIVER MAKE_DSHRESULT(120) + +// This object is already initialized +#define DSERR_ALREADYINITIALIZED MAKE_DSHRESULT(130) + +// This object does not support aggregation +#define DSERR_NOAGGREGATION CLASS_E_NOAGGREGATION + +// The buffer memory has been lost, and must be restored. +#define DSERR_BUFFERLOST MAKE_DSHRESULT(150) + +// Another app has a higher priority level, preventing this call from +// succeeding. +#define DSERR_OTHERAPPHASPRIO MAKE_DSHRESULT(160) + +// This object has not been initialized +#define DSERR_UNINITIALIZED MAKE_DSHRESULT(170) + +// The requested COM interface is not available +#define DSERR_NOINTERFACE E_NOINTERFACE + +// +// Flags +// + +#define DSCAPS_PRIMARYMONO 0x00000001 +#define DSCAPS_PRIMARYSTEREO 0x00000002 +#define DSCAPS_PRIMARY8BIT 0x00000004 +#define DSCAPS_PRIMARY16BIT 0x00000008 +#define DSCAPS_CONTINUOUSRATE 0x00000010 +#define DSCAPS_EMULDRIVER 0x00000020 +#define DSCAPS_CERTIFIED 0x00000040 +#define DSCAPS_SECONDARYMONO 0x00000100 +#define DSCAPS_SECONDARYSTEREO 0x00000200 +#define DSCAPS_SECONDARY8BIT 0x00000400 +#define DSCAPS_SECONDARY16BIT 0x00000800 + +#define DSBPLAY_LOOPING 0x00000001 + +#define DSBSTATUS_PLAYING 0x00000001 +#define DSBSTATUS_BUFFERLOST 0x00000002 +#define DSBSTATUS_LOOPING 0x00000004 + +#define DSBLOCK_FROMWRITECURSOR 0x00000001 +#define DSBLOCK_ENTIREBUFFER 0x00000002 + +#define DSSCL_NORMAL 0x00000001 +#define DSSCL_PRIORITY 0x00000002 +#define DSSCL_EXCLUSIVE 0x00000003 +#define DSSCL_WRITEPRIMARY 0x00000004 + +#define DS3DMODE_NORMAL 0x00000000 +#define DS3DMODE_HEADRELATIVE 0x00000001 +#define DS3DMODE_DISABLE 0x00000002 + +#define DS3D_IMMEDIATE 0x00000000 +#define DS3D_DEFERRED 0x00000001 + +#define DS3D_MINDISTANCEFACTOR 0.0f +#define DS3D_MAXDISTANCEFACTOR 10.0f +#define DS3D_DEFAULTDISTANCEFACTOR 1.0f + +#define DS3D_MINROLLOFFFACTOR 0.0f +#define DS3D_MAXROLLOFFFACTOR 10.0f +#define DS3D_DEFAULTROLLOFFFACTOR 1.0f + +#define DS3D_MINDOPPLERFACTOR 0.0f +#define DS3D_MAXDOPPLERFACTOR 10.0f +#define DS3D_DEFAULTDOPPLERFACTOR 1.0f + +#define DS3D_DEFAULTMINDISTANCE 1.0f +#define DS3D_DEFAULTMAXDISTANCE 1000000000.0f + +#define DS3D_MINCONEANGLE 0 +#define DS3D_MAXCONEANGLE 360 +#define DS3D_DEFAULTCONEANGLE 360 + +#define DS3D_DEFAULTCONEOUTSIDEVOLUME 0 + +#define DSBCAPS_PRIMARYBUFFER 0x00000001 +#define DSBCAPS_STATIC 0x00000002 +#define DSBCAPS_LOCHARDWARE 0x00000004 +#define DSBCAPS_LOCSOFTWARE 0x00000008 +#define DSBCAPS_CTRL3D 0x00000010 +#define DSBCAPS_CTRLFREQUENCY 0x00000020 +#define DSBCAPS_CTRLPAN 0x00000040 +#define DSBCAPS_CTRLVOLUME 0x00000080 +#define DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 +#define DSBCAPS_CTRLDEFAULT 0x000000E0 +#define DSBCAPS_CTRLALL 0x000001F0 +#define DSBCAPS_STICKYFOCUS 0x00004000 +#define DSBCAPS_GLOBALFOCUS 0x00008000 +#define DSBCAPS_GETCURRENTPOSITION2 0x00010000 +#define DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 + +#define DSCBCAPS_WAVEMAPPED 0x80000000 + +#define DSSPEAKER_HEADPHONE 0x00000001 +#define DSSPEAKER_MONO 0x00000002 +#define DSSPEAKER_QUAD 0x00000003 +#define DSSPEAKER_STEREO 0x00000004 +#define DSSPEAKER_SURROUND 0x00000005 + +#define DSSPEAKER_GEOMETRY_MIN 0x00000005 // 5 degrees +#define DSSPEAKER_GEOMETRY_NARROW 0x0000000A // 10 degrees +#define DSSPEAKER_GEOMETRY_WIDE 0x00000014 // 20 degrees +#define DSSPEAKER_GEOMETRY_MAX 0x000000B4 // 180 degrees + +#define DSSPEAKER_COMBINED(c, g) ((DWORD)(((BYTE)(c)) | ((DWORD)((BYTE)(g))) << 16)) +#define DSSPEAKER_CONFIG(a) ((BYTE)(a)) +#define DSSPEAKER_GEOMETRY(a) ((BYTE)(((DWORD)(a) >> 16) & 0x00FF)) + +#define DSCCAPS_EMULDRIVER 0x00000020 + +#define DSCBLOCK_ENTIREBUFFER 0x00000001 + +#define DSCBSTATUS_CAPTURING 0x00000001 +#define DSCBSTATUS_LOOPING 0x00000002 + +#define DSCBSTART_LOOPING 0x00000001 + +#define DSBFREQUENCY_MIN 100 +#define DSBFREQUENCY_MAX 100000 +#define DSBFREQUENCY_ORIGINAL 0 + +#define DSBPAN_LEFT -10000 +#define DSBPAN_CENTER 0 +#define DSBPAN_RIGHT 10000 + +#define DSBVOLUME_MIN -10000 +#define DSBVOLUME_MAX 0 + +#define DSBSIZE_MIN 4 +#define DSBSIZE_MAX 0x0FFFFFFF + +#define DSBPN_OFFSETSTOP 0xFFFFFFFF + +#ifdef __cplusplus +}; +#endif // __cplusplus + +#endif // __DSOUND_INCLUDED__ + diff --git a/include/vdvp.h b/include/vdvp.h new file mode 100644 index 0000000..66061b8 --- /dev/null +++ b/include/vdvp.h @@ -0,0 +1,832 @@ +/*==========================================================================; + * + * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. + * + * File: dvp.h + * Content: DirectDrawVideoPort include file + * + ***************************************************************************/ + +#ifndef __DVP_INCLUDED__ +#define __DVP_INCLUDED__ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#define COM_NO_WINDOWS_H +#include +#else +#define IUnknown void +#undef CO_E_NOTINITIALIZED +#define CO_E_NOTINITIALIZED 0x800401F0L +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * GUIDS used by DirectDrawVideoPort objects + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +DEFINE_GUID( IID_IDDVideoPortContainer, 0x6C142760,0xA733,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60 ); +DEFINE_GUID( IID_IDirectDrawVideoPort, 0xB36D93E0,0x2B43,0x11CF,0xA2,0xDE,0x00,0xAA,0x00,0xB9,0x33,0x56 ); + +DEFINE_GUID( DDVPTYPE_E_HREFH_VREFH, 0x54F39980L,0xDA60,0x11CF,0x9B,0x06,0x00,0xA0,0xC9,0x03,0xA3,0xB8); +DEFINE_GUID( DDVPTYPE_E_HREFH_VREFL, 0x92783220L,0xDA60,0x11CF,0x9B,0x06,0x00,0xA0,0xC9,0x03,0xA3,0xB8); +DEFINE_GUID( DDVPTYPE_E_HREFL_VREFH, 0xA07A02E0L,0xDA60,0x11CF,0x9B,0x06,0x00,0xA0,0xC9,0x03,0xA3,0xB8); +DEFINE_GUID( DDVPTYPE_E_HREFL_VREFL, 0xE09C77E0L,0xDA60,0x11CF,0x9B,0x06,0x00,0xA0,0xC9,0x03,0xA3,0xB8); +DEFINE_GUID( DDVPTYPE_CCIR656, 0xFCA326A0L,0xDA60,0x11CF,0x9B,0x06,0x00,0xA0,0xC9,0x03,0xA3,0xB8); +DEFINE_GUID( DDVPTYPE_BROOKTREE, 0x1352A560L,0xDA61,0x11CF,0x9B,0x06,0x00,0xA0,0xC9,0x03,0xA3,0xB8); +DEFINE_GUID( DDVPTYPE_PHILIPS, 0x332CF160L,0xDA61,0x11CF,0x9B,0x06,0x00,0xA0,0xC9,0x03,0xA3,0xB8); + +/* + * GUIDS used to describe connections + */ + +#endif + +/*============================================================================ + * + * DirectDraw Structures + * + * Various structures used to invoke DirectDraw. + * + *==========================================================================*/ + +struct IDirectDraw; +struct IDirectDrawSurface; +struct IDirectDrawPalette; +struct IDirectDrawClipper; + +typedef struct IDDVideoPortContainer FAR *LPDDVIDEOPORTCONTAINER; +typedef struct IDirectDrawVideoPort FAR *LPDIRECTDRAWVIDEOPORT; + +typedef struct _DDVIDEOPORTCONNECT FAR *LPDDVIDEOPORTCONNECT; +typedef struct _DDVIDEOPORTCAPS FAR *LPDDVIDEOPORTCAPS; +typedef struct _DDVIDEOPORTDESC FAR *LPDDVIDEOPORTDESC; +typedef struct _DDVIDEOPORTINFO FAR *LPDDVIDEOPORTINFO; +typedef struct _DDVIDEOPORTBANDWIDTH FAR *LPDDVIDEOPORTBANDWIDTH; +typedef struct _DDVIDEOPORTSTATUS FAR *LPDDVIDEOPORTSTATUS; + +typedef struct IDDVideoPortContainerVtbl DDVIDEOPORTCONTAINERCALLBACKS; +typedef struct IDirectDrawVideoPortVtbl DIRECTDRAWVIDEOPORTCALLBACKS; + + +/* + * API's + */ +typedef HRESULT (FAR PASCAL * LPDDENUMVIDEOCALLBACK)(LPDDVIDEOPORTCAPS, LPVOID); + + +/* + * INTERACES FOLLOW: + * IDirectDrawVideoPort + * IVideoPort + */ + +/* + * IDirectDrawVideoPortContainer + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDDVideoPortContainer +DECLARE_INTERFACE_( IDDVideoPortContainer, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectDrawVideoPort methods ***/ + STDMETHOD(CreateVideoPort)(THIS_ DWORD, LPDDVIDEOPORTDESC, LPDIRECTDRAWVIDEOPORT FAR *, IUnknown FAR *) PURE; + STDMETHOD(EnumVideoPorts)(THIS_ DWORD, LPDDVIDEOPORTCAPS, LPVOID,LPDDENUMVIDEOCALLBACK ) PURE; + STDMETHOD(GetVideoPortConnectInfo)(THIS_ DWORD, LPDWORD, LPDDVIDEOPORTCONNECT ) PURE; + STDMETHOD(QueryVideoPortStatus)(THIS_ DWORD, LPDDVIDEOPORTSTATUS ) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IVideoPortContainer_QueryInterface(p, a, b) (p)->lpVtbl->QueryInterface(p, a, b) +#define IVideoPortContainer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IVideoPortContainer_Release(p) (p)->lpVtbl->Release(p) +#define IVideoPortContainer_CreateVideoPort(p, a, b, c, d) (p)->lpVtbl->CreateVideoPort(p, a, b, c, d) +#define IVideoPortContainer_EnumVideoPorts(p, a, b, c, d) (p)->lpVtbl->EnumVideoPorts(p, a, b, c, d) +#define IVideoPortContainer_GetVideoPortConnectInfo(p, a, b, c) (p)->lpVtbl->GetVideoPortConnectInfo(p, a, b, c) +#define IVideoPortContainer_QueryVideoPortStatus(p, a, b) (p)->lpVtbl->QueryVideoPortStatus(p, a, b) +#else +#define IVideoPortContainer_QueryInterface(p, a, b) (p)->QueryInterface(a, b) +#define IVideoPortContainer_AddRef(p) (p)->AddRef() +#define IVideoPortContainer_Release(p) (p)->Release() +#define IVideoPortContainer_CreateVideoPort(p, a, b, c, d) (p)->CreateVideoPort(a, b, c, d) +#define IVideoPortContainer_EnumVideoPorts(p, a, b, c, d) (p)->EnumVideoPorts(a, b, c, d) +#define IVideoPortContainer_GetVideoPortConnectInfo(p, a, b, c) (p)->GetVideoPortConnectInfo(a, b, c) +#define IVideoPortContainer_QueryVideoPortStatus(p, a, b) (p)->QueryVideoPortStatus(a, b) +#endif + +#endif + + +/* + * IDirectDrawVideoPort + */ +#if defined( _WIN32 ) && !defined( _NO_COM ) +#undef INTERFACE +#define INTERFACE IDirectDrawVideoPort +DECLARE_INTERFACE_( IDirectDrawVideoPort, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID FAR * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IVideoPort methods ***/ + STDMETHOD(Flip)(THIS_ LPDIRECTDRAWSURFACE, DWORD) PURE; + STDMETHOD(GetBandwidthInfo)(THIS_ LPDDPIXELFORMAT, DWORD, DWORD, DWORD, LPDDVIDEOPORTBANDWIDTH) PURE; + STDMETHOD(GetColorControls)(THIS_ LPDDCOLORCONTROL) PURE; + STDMETHOD(GetInputFormats)(THIS_ LPDWORD, LPDDPIXELFORMAT, DWORD) PURE; + STDMETHOD(GetOutputFormats)(THIS_ LPDDPIXELFORMAT, LPDWORD, LPDDPIXELFORMAT, DWORD) PURE; + STDMETHOD(GetFieldPolarity)(THIS_ LPBOOL) PURE; + STDMETHOD(GetVideoLine)(THIS_ LPDWORD) PURE; + STDMETHOD(GetVideoSignalStatus)(THIS_ LPDWORD) PURE; + STDMETHOD(SetColorControls)(THIS_ LPDDCOLORCONTROL) PURE; + STDMETHOD(SetTargetSurface)(THIS_ LPDIRECTDRAWSURFACE, DWORD) PURE; + STDMETHOD(StartVideo)(THIS_ LPDDVIDEOPORTINFO) PURE; + STDMETHOD(StopVideo)(THIS) PURE; + STDMETHOD(UpdateVideo)(THIS_ LPDDVIDEOPORTINFO) PURE; + STDMETHOD(WaitForSync)(THIS_ DWORD, DWORD, DWORD) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IVideoPort_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IVideoPort_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IVideoPort_Release(p) (p)->lpVtbl->Release(p) +#define IVideoPort_SetTargetSurface(p,a,b) (p)->lpVtbl->SetTargetSurface(p,a,b) +#define IVideoPort_Flip(p,a,b) (p)->lpVtbl->Flip(p,a,b) +#define IVideoPort_GetBandwidthInfo(p,a,b,c,d,e) (p)->lpVtbl->GetBandwidthInfo(p,a,b,c,d,e) +#define IVideoPort_GetColorControls(p,a) (p)->lpVtbl->GetColorControls(p,a) +#define IVideoPort_GetInputFormats(p,a,b,c) (p)->lpVtbl->GetInputFormats(p,a,b,c) +#define IVideoPort_GetOutputFormats(p,a,b,c,d) (p)->lpVtbl->GetOutputFormats(p,a,b,c,d) +#define IVideoPort_GetFieldPolarity(p,a) (p)->lpVtbl->GetFieldPolarity(p,a) +#define IVideoPort_GetVideoLine(p,a) (p)->lpVtbl->GetVideoLine(p,a) +#define IVideoPort_GetVideoSignalStatus(p,a) (p)->lpVtbl->GetVideoSignalStatus(p,a) +#define IVideoPort_SetColorControls(p,a) (p)->lpVtbl->SetColorControls(p,a) +#define IVideoPort_StartVideo(p,a) (p)->lpVtbl->StartVideo(p,a) +#define IVideoPort_StopVideo(p) (p)->lpVtbl->StopVideo(p) +#define IVideoPort_UpdateVideo(p,a) (p)->lpVtbl->UpdateVideo(p,a) +#define IVideoPort_WaitForSync(p,a,b,c) (p)->lpVtbl->WaitForSync(p,a,b,c) +#else +#define IVideoPort_QueryInterface(p,a,b) (p)->QueryInterface(a,b) +#define IVideoPort_AddRef(p) (p)->AddRef() +#define IVideoPort_Release(p) (p)->Release() +#define IVideoPort_SetTargetSurface(p,a,b) (p)->SetTargetSurface(a,b) +#define IVideoPort_Flip(p,a,b) (p)->Flip(a,b) +#define IVideoPort_GetBandwidthInfo(p,a,b,c,d,e) (p)->GetBandwidthInfo(a,b,c,d,e) +#define IVideoPort_GetColorControls(p,a) (p)->GetColorControls(a) +#define IVideoPort_GetInputFormats(p,a,b,c) (p)->GetInputFormats(a,b,c) +#define IVideoPort_GetOutputFormats(p,a,b,c,d) (p)->GetOutputFormats(a,b,c,d) +#define IVideoPort_GetFieldPolarity(p,a) (p)->GetFieldPolarity(a) +#define IVideoPort_GetVideoLine(p,a) (p)->GetVideoLine(a) +#define IVideoPort_GetVideoSignalStatus(p,a) (p)->GetVideoSignalStatus(a) +#define IVideoPort_SetColorControls(p,a) (p)->SetColorControls(a) +#define IVideoPort_StartVideo(p,a) (p)->StartVideo(a) +#define IVideoPort_StopVideo(p) (p)->StopVideo() +#define IVideoPort_UpdateVideo(p,a) (p)->UpdateVideo(a) +#define IVideoPort_WaitForSync(p,a,b,c) (p)->WaitForSync(a,b,c) +#endif + +#endif + + +/* + * DDVIDEOPORTCONNECT + */ +typedef struct _DDVIDEOPORTCONNECT +{ + DWORD dwSize; // size of the DDVIDEOPORTCONNECT structure + DWORD dwPortWidth; // Width of the video port + GUID guidTypeID; // Description of video port connection + DWORD dwFlags; // Connection flags + DWORD dwReserved1; // Reserved, set to zero. +} DDVIDEOPORTCONNECT; + + +/* + * DDVIDEOPORTCAPS + */ +typedef struct _DDVIDEOPORTCAPS +{ + DWORD dwSize; // size of the DDVIDEOPORTCAPS structure + DWORD dwFlags; // indicates which fields contain data + DWORD dwMaxWidth; // max width of the video port field + DWORD dwMaxVBIWidth; // max width of the VBI data + DWORD dwMaxHeight; // max height of the video port field + DWORD dwVideoPortID; // Video port ID (0 - (dwMaxVideoPorts -1)) + DWORD dwCaps; // Video port capabilities + DWORD dwFX; // More video port capabilities + DWORD dwNumAutoFlipSurfaces; // Number of autoflippable surfaces + DWORD dwAlignVideoPortBoundary; // Byte restriction of placement within the surface + DWORD dwAlignVideoPortPrescaleWidth;// Byte restriction of width after prescaling + DWORD dwAlignVideoPortCropBoundary; // Byte restriction of left cropping + DWORD dwAlignVideoPortCropWidth; // Byte restriction of cropping width + DWORD dwPreshrinkXStep; // Width can be shrunk in steps of 1/x + DWORD dwPreshrinkYStep; // Height can be shrunk in steps of 1/x + DWORD dwNumVBIAutoFlipSurfaces; // Number of VBI autoflippable surfaces + DWORD dwReserved1; // Reserved for future use + DWORD dwReserved2; // Reserved for future use +} DDVIDEOPORTCAPS; + +/* + * The dwMaxWidth and dwMaxVBIWidth members are valid + */ +#define DDVPD_WIDTH 0x00000001l + +/* + * The dwMaxHeight member is valid + */ +#define DDVPD_HEIGHT 0x00000002l + +/* + * The dwVideoPortID member is valid + */ +#define DDVPD_ID 0x00000004l + +/* + * The dwCaps member is valid + */ +#define DDVPD_CAPS 0x00000008l + +/* + * The dwFX member is valid + */ +#define DDVPD_FX 0x00000010l + +/* + * The dwNumAutoFlipSurfaces member is valid + */ +#define DDVPD_AUTOFLIP 0x00000020l + +/* + * All of the alignment members are valid + */ +#define DDVPD_ALIGN 0x00000040l + + +/* + * DDVIDEOPORTDESC + */ +typedef struct _DDVIDEOPORTDESC +{ + DWORD dwSize; // size of the DDVIDEOPORTDESC structure + DWORD dwFieldWidth; // width of the video port field + DWORD dwVBIWidth; // width of the VBI data + DWORD dwFieldHeight; // height of the video port field + DWORD dwMicrosecondsPerField; // Microseconds per video field + DWORD dwMaxPixelsPerSecond; // Maximum pixel rate per second + DWORD dwVideoPortID; // Video port ID (0 - (dwMaxVideoPorts -1)) + DWORD dwReserved1; // Reserved for future use - set to zero + DDVIDEOPORTCONNECT VideoPortType; // Description of video port connection + DWORD dwReserved2; // Reserved for future use - set to zero + DWORD dwReserved3; // Reserved for future use - set to zero +} DDVIDEOPORTDESC; + + +/* + * DDVIDEOPORTINFO + */ +typedef struct _DDVIDEOPORTINFO +{ + DWORD dwSize; // Size of the structure + DWORD dwOriginX; // Placement of the video data within the surface. + DWORD dwOriginY; // Placement of the video data within the surface. + DWORD dwVPFlags; // Video port options + RECT rCrop; // Cropping rectangle (optional). + DWORD dwPrescaleWidth; // Determines pre-scaling/zooming in the X direction (optional). + DWORD dwPrescaleHeight; // Determines pre-scaling/zooming in the Y direction (optional). + LPDDPIXELFORMAT lpddpfInputFormat; // Video format written to the video port + LPDDPIXELFORMAT lpddpfVBIInputFormat; // Input format of the VBI data + LPDDPIXELFORMAT lpddpfVBIOutputFormat;// Output format of the data + DWORD dwVBIHeight; // Specifies the number of lines of data within the vertical blanking interval. + DWORD dwReserved1; // Reserved for future use - set to zero + DWORD dwReserved2; // Reserved for future use - set to zero +} DDVIDEOPORTINFO; + + +/* + * DDVIDEOPORTBANDWIDTH + */ +typedef struct _DDVIDEOPORTBANDWIDTH +{ + DWORD dwSize; // Size of the structure + DWORD dwCaps; + DWORD dwOverlay; // Zoom factor at which overlay is supported + DWORD dwColorkey; // Zoom factor at which overlay w/ colorkey is supported + DWORD dwYInterpolate; // Zoom factor at which overlay w/ Y interpolation is supported + DWORD dwYInterpAndColorkey; // Zoom factor at which ovelray w/ Y interpolation and colorkeying is supported + DWORD dwReserved1; // Reserved for future use - set to zero + DWORD dwReserved2; // Reserved for future use - set to zero +} DDVIDEOPORTBANDWIDTH; + + +/* + * DDVIDEOPORTSTATUS + */ +typedef struct _DDVIDEOPORTSTATUS +{ + DWORD dwSize; // Size of the structure + BOOL bInUse; // TRUE if video port is currently being used + DWORD dwFlags; // Currently not used + DWORD dwReserved1; // Reserved for future use + DDVIDEOPORTCONNECT VideoPortType; // Information about the connection + DWORD dwReserved2; // Reserved for future use + DWORD dwReserved3; // Reserved for future use +} DDVIDEOPORTSTATUS; + +/*============================================================================ + * + * Video Port Flags + * + * All flags are bit flags. + * + *==========================================================================*/ + +/**************************************************************************** + * + * VIDEOPORT DDVIDEOPORTCONNECT FLAGS + * + ****************************************************************************/ + +/* + * When this is set by the driver and passed to the client, this + * indicates that the video port is capable of double clocking the data. + * When this is set by the client, this indicates that the video port + * should enable double clocking. This flag is only valid with external + * syncs. + */ +#define DDVPCONNECT_DOUBLECLOCK 0x00000001l + +/* + * When this is set by the driver and passed to the client, this + * indicates that the video port is capable of using an external VACT + * signal. When this is set by the client, this indicates that the + * video port should use the external VACT signal. + */ +#define DDVPCONNECT_VACT 0x00000002l + +/* + * When this is set by the driver and passed to the client, this + * indicates that the video port is capable of treating even fields + * like odd fields and visa versa. When this is set by the client, + * this indicates that the video port should treat even fields like odd + * fields. + */ +#define DDVPCONNECT_INVERTPOLARITY 0x00000004l + +/* + * Indicates that any data written to the video port during the VREF + * period will not be written into the frame buffer. This flag is read only. + */ +#define DDVPCONNECT_DISCARDSVREFDATA 0x00000008l + +/* + * Device will write half lines into the frame buffer, sometimes causing + * the data to not be displayed correctly. + */ +#define DDVPCONNECT_HALFLINE 0x00000010l + +/* + * Indicates that the signal is interlaced. This flag is only + * set by the client. + */ +#define DDVPCONNECT_INTERLACED 0x00000020l + +/* + * Indicates that video port is shareable and that this video port + * will use the even fields. This flag is only set by the client. + */ +#define DDVPCONNECT_SHAREEVEN 0x00000040l + +/* + * Indicates that video port is shareable and that this video port + * will use the odd fields. This flag is only set by the client. + */ +#define DDVPCONNECT_SHAREODD 0x00000080l + +/**************************************************************************** + * + * VIDEOPORT DDVIDEOPORTDESC CAPS + * + ****************************************************************************/ + +/* + * Flip can be performed automatically to avoid tearing. + */ +#define DDVPCAPS_AUTOFLIP 0x00000001l + +/* + * Supports interlaced video + */ +#define DDVPCAPS_INTERLACED 0x00000002l + +/* + * Supports non-interlaced video + */ +#define DDVPCAPS_NONINTERLACED 0x00000004l + +/* + * Indicates that the device can return whether the current field + * of an interlaced signal is even or odd. + */ +#define DDVPCAPS_READBACKFIELD 0x00000008l + +/* + * Indicates that the device can return the current line of video + * being written into the frame buffer. + */ +#define DDVPCAPS_READBACKLINE 0x00000010l + +/* + * Allows two gen-locked video streams to share a single video port, + * where one stream uses the even fields and the other uses the odd + * fields. Separate parameters (including address, scaling, + * cropping, etc.) are maintained for both fields.) + */ +#define DDVPCAPS_SHAREABLE 0x00000020l + +/* + * Even fields of video can be automatically discarded. + */ +#define DDVPCAPS_SKIPEVENFIELDS 0x00000040l + +/* + * Odd fields of video can be automatically discarded. + */ +#define DDVPCAPS_SKIPODDFIELDS 0x00000080l + +/* + * Indicates that the device is capable of driving the graphics + * VSYNC with the video port VSYNC. + */ +#define DDVPCAPS_SYNCMASTER 0x00000100l + +/* + * Indicates that data within the vertical blanking interval can + * be written to a different surface. + */ +#define DDVPCAPS_VBISURFACE 0x00000200l + +/* + * Indicates that the video port can perform color operations + * on the incoming data before it is written to the frame buffer. + */ +#define DDVPCAPS_COLORCONTROL 0x00000400l + +/* + * Indicates that the video port can accept VBI data in a different + * width or format than the regular video data. + */ +#define DDVPCAPS_OVERSAMPLEDVBI 0x00000800l + +/* + * Indicates that the video port can write data directly to system memory + */ +#define DDVPCAPS_SYSTEMMEMORY 0x00001000l + + +/**************************************************************************** + * + * VIDEOPORT DDVIDEOPORTDESC FX + * + ****************************************************************************/ + +/* + * Limited cropping is available to crop out the vertical interval data. + */ +#define DDVPFX_CROPTOPDATA 0x00000001l + +/* + * Incoming data can be cropped in the X direction before it is written + * to the surface. + */ +#define DDVPFX_CROPX 0x00000002l + +/* + * Incoming data can be cropped in the Y direction before it is written + * to the surface. + */ +#define DDVPFX_CROPY 0x00000004l + +/* + * Supports interleaving interlaced fields in memory. + */ +#define DDVPFX_INTERLEAVE 0x00000008l + +/* + * Supports mirroring left to right as the video data is written + * into the frame buffer. + */ +#define DDVPFX_MIRRORLEFTRIGHT 0x00000010l + +/* + * Supports mirroring top to bottom as the video data is written + * into the frame buffer. + */ +#define DDVPFX_MIRRORUPDOWN 0x00000020l + +/* + * Data can be arbitrarily shrunk in the X direction before it + * is written to the surface. + */ +#define DDVPFX_PRESHRINKX 0x00000040l + +/* + * Data can be arbitrarily shrunk in the Y direction before it + * is written to the surface. + */ +#define DDVPFX_PRESHRINKY 0x00000080l + +/* + * Data can be binary shrunk (1/2, 1/4, 1/8, etc.) in the X + * direction before it is written to the surface. + */ +#define DDVPFX_PRESHRINKXB 0x00000100l + +/* + * Data can be binary shrunk (1/2, 1/4, 1/8, etc.) in the Y + * direction before it is written to the surface. + */ +#define DDVPFX_PRESHRINKYB 0x00000200l + +/* + * Data can be shrunk in increments of 1/x in the X direction + * (where X is specified in the DDVIDEOPORTCAPS.dwPreshrinkXStep) + * before it is written to the surface. + */ +#define DDVPFX_PRESHRINKXS 0x00000400l + +/* + * Data can be shrunk in increments of 1/x in the Y direction + * (where X is specified in the DDVIDEOPORTCAPS.dwPreshrinkYStep) + * before it is written to the surface. + */ +#define DDVPFX_PRESHRINKYS 0x00000800l + +/* + * Data can be arbitrarily stretched in the X direction before + * it is written to the surface. + */ +#define DDVPFX_PRESTRETCHX 0x00001000l + +/* + * Data can be arbitrarily stretched in the Y direction before + * it is written to the surface. + */ +#define DDVPFX_PRESTRETCHY 0x00002000l + +/* + * Data can be integer stretched in the X direction before it is + * written to the surface. + */ +#define DDVPFX_PRESTRETCHXN 0x00004000l + +/* + * Data can be integer stretched in the Y direction before it is + * written to the surface. + */ +#define DDVPFX_PRESTRETCHYN 0x00008000l + +/* + * Indicates that data within the vertical blanking interval can + * be converted independently of the remaining video data. + */ +#define DDVPFX_VBICONVERT 0x00010000l + +/* + * Indicates that scaling can be disabled for data within the + * vertical blanking interval. + */ +#define DDVPFX_VBINOSCALE 0x00020000l + +/* + * Indicates that the video data can ignore the left and right + * cropping coordinates when cropping oversampled VBI data. + */ +#define DDVPFX_IGNOREVBIXCROP 0x00040000l + + +/**************************************************************************** + * + * VIDEOPORT DDVIDEOPORTINFO FLAGS + * + ****************************************************************************/ + +/* + * Perform automatic flipping. Auto-flipping is performed between + * the overlay surface that was attached to the video port using + * IDirectDrawVideoPort::AttachSurface and the overlay surfaces that + * are attached to the surface via the IDirectDrawSurface::AttachSurface + * method. The flip order is the order in which the overlay surfaces + * were. attached. + */ +#define DDVP_AUTOFLIP 0x00000001l + +/* + * Perform conversion using the ddpfOutputFormat information. + */ +#define DDVP_CONVERT 0x00000002l + +/* + * Perform cropping using the specified rectangle. + */ +#define DDVP_CROP 0x00000004l + +/* + * Indicates that interlaced fields should be interleaved in memory. + */ +#define DDVP_INTERLEAVE 0x00000008l + +/* + * Indicates that the data should be mirrored left to right as it's + * written into the frame buffer. + */ +#define DDVP_MIRRORLEFTRIGHT 0x00000010l + +/* + * Indicates that the data should be mirrored top to bottom as it's + * written into the frame buffer. + */ +#define DDVP_MIRRORUPDOWN 0x00000020l + +/* + * Perform pre-scaling/zooming based on the pre-scale parameters. + */ +#define DDVP_PRESCALE 0x00000040l + +/* + * Ignore input of even fields. + */ +#define DDVP_SKIPEVENFIELDS 0x00000080l + +/* + * Ignore input of odd fields. + */ +#define DDVP_SKIPODDFIELDS 0x00000100l + +/* + * Drive the graphics VSYNCs using the video port VYSNCs. + */ +#define DDVP_SYNCMASTER 0x00000200l + +/* + * The ddpfVBIOutputFormatFormat member contains data that should be used + * to convert the data within the vertical blanking interval. + */ +#define DDVP_VBICONVERT 0x00000400l + +/* + * Indicates that data within the vertical blanking interval + * should not be scaled. + */ +#define DDVP_VBINOSCALE 0x00000800l + +/* + * Indicates that these bob/weave decisions should not be + * overriden by other interfaces. + */ +#define DDVP_OVERRIDEBOBWEAVE 0x00001000l + +/* + * Indicates that the video data should ignore the left and right + * cropping coordinates when cropping the VBI data. + */ +#define DDVP_IGNOREVBIXCROP 0x00002000l + + +/**************************************************************************** + * + * DIRIRECTDRAWVIDEOPORT GETINPUTFORMAT/GETOUTPUTFORMAT FLAGS + * + ****************************************************************************/ + +/* + * Return formats for the video data + */ +#define DDVPFORMAT_VIDEO 0x00000001l + +/* + * Return formats for the VBI data + */ +#define DDVPFORMAT_VBI 0x00000002l + + +/**************************************************************************** + * + * DIRIRECTDRAWVIDEOPORT SETTARGETSURFACE FLAGS + * + ****************************************************************************/ + +/* + * Surface should receive video data (and VBI data if a surface + * is not explicitly attached for that purpose) + */ +#define DDVPTARGET_VIDEO 0x00000001l + +/* + * Surface should receive VBI data + */ +#define DDVPTARGET_VBI 0x00000002l + + +/**************************************************************************** + * + * DIRIRECTDRAWVIDEOPORT WAITFORSYNC FLAGS + * + ****************************************************************************/ + +/* + * Waits until the beginning of the next VSYNC + */ +#define DDVPWAIT_BEGIN 0x00000001l + +/* + * Waits until the end of the next/current VSYNC + */ +#define DDVPWAIT_END 0x00000002l + +/* + * Waits until the beginning of the specified line + */ +#define DDVPWAIT_LINE 0x00000003l + +/**************************************************************************** + * + * DIRECTDRAWVIDEOPORT FLIP FLAGS + * + ****************************************************************************/ + +/* + * Flips the normal video surface + */ +#define DDVPFLIP_VIDEO 0x00000001l + +/* + * Flips the VBI surface + */ +#define DDVPFLIP_VBI 0x00000002l + +/**************************************************************************** + * + * DIRIRECTDRAWVIDEOPORT GETVIDEOSIGNALSTATUS VALUES + * + ****************************************************************************/ + +/* + * No video signal is present at the video port + */ +#define DDVPSQ_NOSIGNAL 0x00000001l + +/* + * A valid video signal is present at the video port + */ +#define DDVPSQ_SIGNALOK 0x00000002l + +/**************************************************************************** + * + * VIDEOPORTBANDWIDTH Flags + * + ****************************************************************************/ + +/* + * The specified height/width refer to the size of the video port data + * written into memory, after prescaling has occured. + */ +#define DDVPB_VIDEOPORT 0x00000001l + +/* + * The specified height/width refer to the source size of the overlay. + */ +#define DDVPB_OVERLAY 0x00000002l + +/* + * This is a query for the device to return which caps this device requires. + */ +#define DDVPB_TYPE 0x00000004l + +/**************************************************************************** + * + * VIDEOPORTBANDWIDTH Caps + * + ****************************************************************************/ + +/* + * The bandwidth for this device is dependant on the overlay source size. + */ +#define DDVPBCAPS_SOURCE 0x00000001l + +/* + * The bandwidth for this device is dependant on the overlay destination + * size. + */ +#define DDVPBCAPS_DESTINATION 0x00000002l + + +#ifdef __cplusplus +}; +#endif + +#endif + diff --git a/include/vecmat.h b/include/vecmat.h new file mode 100644 index 0000000..035cfa4 --- /dev/null +++ b/include/vecmat.h @@ -0,0 +1,640 @@ +/* + * $Logfile: /Freespace2/code/Math/VecMat.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for functions that manipulate vectors and matricies + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 6 6/18/99 5:16p Dave + * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD + * dialog to PXO screen. + * + * 5 4/28/99 11:13p Dave + * Temporary checkin of artillery code. + * + * 4 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 3 1/12/99 12:53a Dave + * More work on beam weapons - made collision detection very efficient - + * collide against all object types properly - made 3 movement types + * smooth. Put in test code to check for possible non-darkening pixels on + * object textures. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 43 9/11/98 10:10a Andsager + * Optimize and rename matrix_decomp to vm_matrix_to_rot_axis_and_angle, + * rename quatern_rot to vm_quaternion_rotate + * + * 42 3/09/98 3:51p Mike + * More error checking. + * + * 41 12/17/97 5:44p Andsager + * Change vm_matrix_interpolate so that it does not overshoot if optional + * last parameter is 1 + * + * 40 9/30/97 8:03p Lawrance + * add missing semi-colon to function prototype + * + * 39 9/30/97 5:04p Andsager + * add vm_estimate_next_orientation + * + * 38 9/28/97 2:17p Andsager + * added vm_project_point_onto_plane + * + * 37 9/25/97 5:57p Andsager + * improved function description for matrix interpolate + * + * 36 9/09/97 10:15p Andsager + * added vm_rotate_vec_to_body() and vm_rotate_vec_to_world() + * + * 35 8/20/97 5:33p Andsager + * added vm_vec_projection_parallel and vm_vec_projection_onto_surface + * + * 34 8/19/97 11:42p Lawrance + * use atan2_safe() instead of atan2() + * + * 33 8/18/97 4:46p Hoffoss + * Added global default axis vector constants. + * + * 32 8/03/97 3:54p Lawrance + * added vm_find_bounding_sphere() + * + * 31 7/30/97 2:20p Dan + * from allender: fixed vm_is_vec_nan to work properly with address-of + * operator by adding parens around macro variables + * + * 30 7/29/97 2:48p Hoffoss + * Added vm_is_vec_nan(). + * + * 29 7/28/97 2:21p John + * changed vecmat functions to not return src. Started putting in code + * for inline vector math. Fixed some bugs with optimizer. + * + * 28 7/28/97 3:25p Andsager + * + * 27 7/28/97 2:41p Mike + * Replace vm_forward_interpolate(). + * + * 26 7/28/97 1:18p Andsager + * implement vm_fvec_matrix_interpolate(), which interpolates matrices on + * xy and then z + * + * 25 7/24/97 5:24p Andsager + * implement forward vector interpolation + * + * 24 7/02/97 4:25p Mike + * Add matrix_interpolate(), but don't call it. + * + * 23 7/01/97 3:27p Mike + * Improve skill level support. + * + * 22 6/25/97 12:27p Hoffoss + * Added some functions I needed for Fred. + * + * 21 5/21/97 8:49a Lawrance + * added vm_vec_same() + * + * 20 4/15/97 4:00p Mike + * Intermediate checkin caused by getting other files. Working on camera + * slewing system. + * + * 19 3/17/97 1:55p Hoffoss + * Added function for error checking matrices. + * + * 18 3/06/97 10:56a Mike + * Write error checking version of vm_vec_normalize(). + * Fix resultant problems. + * + * 17 3/04/97 3:30p John + * added function to interpolate an angle. + * + * 16 2/25/97 5:12p John + * Added functions to see if two matrices or vectors are close. + * + * 15 2/03/97 1:30p John + * Put a clearer comment in for vm_vec_unrotate + * + * 14 2/03/97 1:14p John + * Added vm_vec_unrotate function + * + * 13 1/27/97 11:57a John + * added a function to rotate a point around an arbritary line. + * + * 12 11/26/96 12:18p Hoffoss + * Added the vm_vec_dist_squared() function. + * + * 11 11/16/96 2:38p Mike + * Waypoint code, under construction and a painful mess. + * + * 10 11/05/96 3:42p Mike + * Make AI use accuracy parameter, though not yet specified in ships.tbl + * or *.fsm. + * + * Add vm_vec_rand_vec_quick. + * + * Add frand() which returns a rand in 0.0..1.0. + * + * 9 10/30/96 2:35p Mike + * Docking behavior. + * Changed quick versions of vecmat routines to not return 1/mag. They + * return mag, just like non-quick versions. + * + * 8 10/24/96 10:17a Hoffoss + * Moved function 'compute_point_on_plane()' to vecmat. + * + * 7 10/23/96 10:32p Lawrance + * added function vm_vect_mag_squared() + * + * $NoKeywords: $ + * +*/ + +#ifndef _VECMAT_H +#define _VECMAT_H + +#include +#include "pstypes.h" + +//#define _INLINE_VECMAT + +#define vm_is_vec_nan(v) (_isnan((v)->x) || _isnan((v)->y) || _isnan((v)->z)) + +//Macros/functions to fill in fields of structures + +//macro to check if vector is zero +#define IS_VEC_NULL(v) (((v)->x == (float)0.0) && ((v)->y == (float)0.0) && ((v)->z == (float)0.0)) + +//macro to set a vector to zero. we could do this with an in-line assembly +//macro, but it's probably better to let the compiler optimize it. +//Note: NO RETURN VALUE +#define vm_vec_zero(v) (v)->x=(v)->y=(v)->z=(float)0.0 + +/* +//macro set set a matrix to the identity. Note: NO RETURN VALUE +#define vm_set_identity(m) do {m->rvec.x = m->uvec.y = m->fvec.z = (float)1.0; \ + m->rvec.y = m->rvec.z = \ + m->uvec.x = m->uvec.z = \ + m->fvec.x = m->fvec.y = (float)0.0;} while (0) +*/ +extern void vm_set_identity(matrix *m); + +#define vm_vec_make(v,_x,_y,_z) (((v)->x=(_x), (v)->y=(_y), (v)->z=(_z)), (v)) + +//Global constants + +extern vector vmd_zero_vector; +extern vector vmd_x_vector; +extern vector vmd_y_vector; +extern vector vmd_z_vector; +extern matrix vmd_identity_matrix; + +//Here's a handy constant + +#define ZERO_VECTOR {(float)0.0,(float)0.0,(float)0.0} +#define IDENTITY_MATRIX {(float)1.0,(float)0.0,(float)0.0, \ + (float)0.0,(float)1.0,(float)0.0, \ + (float)0.0,(float)0.0,(float)1.0 } + +//fills in fields of an angle vector +#define vm_angvec_make(v,_p,_b,_h) (((v)->p=(_p), (v)->b=(_b), (v)->h=(_h)), (v)) + +//negate a vector +#define vm_vec_negate(v) do {(v)->x = - (v)->x; (v)->y = - (v)->y; (v)->z = - (v)->z;} while (0); + +typedef struct plane { + float A, B, C, D; +} plane; + +//Functions in library + +//adds two vectors, fills in dest, returns ptr to dest +//ok for dest to equal either source, but should use vm_vec_add2() if so +#ifdef _INLINE_VECMAT +#define vm_vec_add( dst, src0, src1 ) do { \ + (dst)->x = (src0)->x + (src1)->x; \ + (dst)->y = (src0)->y + (src1)->y; \ + (dst)->z = (src0)->z + (src1)->z; \ +} while(0) +#else +void vm_vec_add(vector *dest,vector *src0,vector *src1); +#endif + +//adds src onto dest vector, returns ptr to dest +#ifdef _INLINE_VECMAT +#define vm_vec_add2( dst, src ) do { \ + (dst)->x += (src)->x; \ + (dst)->y += (src)->y; \ + (dst)->z += (src)->z; \ +} while(0) +#else +void vm_vec_add2(vector *dest,vector *src); +#endif + + +//scales a vector and subs from to another +//dest -= k * src +#ifdef _INLINE_VECMAT +#define vm_vec_scale_sub2( dst, src, k ) do { \ + float tmp_k = (k); \ + (dst)->x -= (src)->x*tmp_k; \ + (dst)->y -= (src)->y*tmp_k; \ + (dst)->z -= (src)->z*tmp_k; \ +} while(0) +#else +void vm_vec_scale_sub2(vector *dest,vector *src, float k); +#endif + +//subs two vectors, fills in dest, returns ptr to dest +//ok for dest to equal either source, but should use vm_vec_sub2() if so +#ifdef _INLINE_VECMAT +#define vm_vec_sub( dst, src0, src1 ) do { \ + (dst)->x = (src0)->x - (src1)->x; \ + (dst)->y = (src0)->y - (src1)->y; \ + (dst)->z = (src0)->z - (src1)->z; \ +} while(0) +#else +void vm_vec_sub(vector *dest,vector *src0,vector *src1); +#endif + + +//subs one vector from another, returns ptr to dest +//dest can equal source +#ifdef _INLINE_VECMAT +#define vm_vec_sub2( dst, src ) do { \ + (dst)->x -= (src)->x; \ + (dst)->y -= (src)->y; \ + (dst)->z -= (src)->z; \ +} while(0) +#else +void vm_vec_sub2(vector *dest,vector *src); +#endif + + +//averages two vectors. returns ptr to dest +//dest can equal either source +vector *vm_vec_avg(vector *dest,vector *src0,vector *src1); + +//averages four vectors. returns ptr to dest +//dest can equal any source +vector *vm_vec_avg4(vector *dest,vector *src0,vector *src1,vector *src2,vector *src3); + +//scales a vector in place. returns ptr to vector +#ifdef _INLINE_VECMAT +#define vm_vec_scale( dst, k ) do { \ + float tmp_k = (k); \ + (dst)->x *= tmp_k; \ + (dst)->y *= tmp_k; \ + (dst)->z *= tmp_k; \ +} while(0) +#else +void vm_vec_scale(vector *dest,float s); +#endif + +//scales and copies a vector. returns ptr to dest +#ifdef _INLINE_VECMAT +#define vm_vec_copy_scale( dst, src, k ) do { \ + float tmp_k = (k); \ + (dst)->x = (src)->x * tmp_k; \ + (dst)->y = (src)->y * tmp_k; \ + (dst)->z = (src)->z * tmp_k; \ +} while(0) +#else +void vm_vec_copy_scale(vector *dest,vector *src,float s); +#endif + +//scales a vector, adds it to another, and stores in a 3rd vector +//dest = src1 + k * src2 +#ifdef _INLINE_VECMAT +#define vm_vec_scale_add( dst, src1, src2, k ) do { \ + float tmp_k = (k); \ + (dst)->x = (src1)->x + (src2)->x * tmp_k; \ + (dst)->y = (src1)->y + (src2)->y * tmp_k; \ + (dst)->z = (src1)->z + (src2)->z * tmp_k; \ +} while(0) +#else +void vm_vec_scale_add(vector *dest,vector *src1,vector *src2,float k); +#endif + + +//scales a vector and adds it to another +//dest += k * src +#ifdef _INLINE_VECMAT +#define vm_vec_scale_add2( dst, src, k ) do { \ + float tmp_k = (k); \ + (dst)->x += (src)->x * tmp_k; \ + (dst)->y += (src)->y * tmp_k; \ + (dst)->z += (src)->z * tmp_k; \ +} while(0) +#else +void vm_vec_scale_add2(vector *dest,vector *src,float k); +#endif + +//scales a vector in place, taking n/d for scale. returns ptr to vector +//dest *= n/d +#ifdef _INLINE_VECMAT +#define vm_vec_scale2( dst, n, d ) do { \ + float tmp_k = (n)/(d); \ + (dst)->x *= tmp_k; \ + (dst)->y *= tmp_k; \ + (dst)->z *= tmp_k; \ +} while(0) +#else +void vm_vec_scale2(vector *dest,float n,float d); +#endif + +// finds the projection of source vector along a unit vector +// returns the magnitude of the component +float vm_vec_projection_parallel (vector *component, vector *src, vector *unit_vector); + +// finds the projection of source vector onto a surface given by surface normal +void vm_vec_projection_onto_plane (vector *projection, vector *src, vector *normal); + +//returns magnitude of a vector +float vm_vec_mag(vector *v); + +// returns the square of the magnitude of a vector (useful if comparing distances) +float vm_vec_mag_squared(vector* v); + +// returns the square of the distance between two points (fast and exact) +float vm_vec_dist_squared(vector *v0, vector *v1); + +//computes the distance between two points. (does sub and mag) +float vm_vec_dist(vector *v0,vector *v1); + +//computes an approximation of the magnitude of the vector +//uses dist = largest + next_largest*3/8 + smallest*3/16 +float vm_vec_mag_quick(vector *v); + +//computes an approximation of the distance between two points. +//uses dist = largest + next_largest*3/8 + smallest*3/16 +float vm_vec_dist_quick(vector *v0,vector *v1); + + +//normalize a vector. returns mag of source vec +float vm_vec_copy_normalize(vector *dest,vector *src); +float vm_vec_normalize(vector *v); + +// This version of vector normalize checks for the null vector before normalization. +// If it is detected, it generates a Warning() and returns the vector 1, 0, 0. +float vm_vec_normalize_safe(vector *v); + +//normalize a vector. returns mag of source vec. uses approx mag +float vm_vec_copy_normalize_quick(vector *dest,vector *src); +float vm_vec_normalize_quick(vector *v); + +//normalize a vector. returns mag of source vec. uses approx mag +float vm_vec_copy_normalize_quick_mag(vector *dest,vector *src); +float vm_vec_normalize_quick_mag(vector *v); + +//return the normalized direction vector between two points +//dest = normalized(end - start). Returns mag of direction vector +//NOTE: the order of the parameters matches the vector subtraction +float vm_vec_normalized_dir(vector *dest,vector *end,vector *start); +float vm_vec_normalized_dir_quick_mag(vector *dest,vector *end,vector *start); +// Returns mag of direction vector +float vm_vec_normalized_dir_quick(vector *dest,vector *end,vector *start); + +////returns dot product of two vectors +#ifdef _INLINE_VECMAT +#define vm_vec_dotprod( v0, v1 ) (((v1)->x*(v0)->x)+((v1)->y*(v0)->y)+((v1)->z*(v0)->z)) +#define vm_vec_dot( v0, v1 ) (((v1)->x*(v0)->x)+((v1)->y*(v0)->y)+((v1)->z*(v0)->z)) +#else +float vm_vec_dotprod(vector *v0,vector *v1); +#define vm_vec_dot vm_vec_dotprod +#endif + +#ifdef _INLINE_VECMAT +#define vm_vec_dot3( x1, y1, z1, v ) (((x1)*(v)->x)+((y1)*(v)->y)+((z1)*(v)->z)) +#else +float vm_vec_dot3(float x,float y,float z,vector *v); +#endif + +//computes cross product of two vectors. returns ptr to dest +//dest CANNOT equal either source +vector *vm_vec_crossprod(vector *dest,vector *src0,vector *src1); +#define vm_vec_cross vm_vec_crossprod + +// test if 2 vectors are parallel or not. +int vm_test_parallel(vector *src0, vector *src1); + +//computes surface normal from three points. result is normalized +//returns ptr to dest +//dest CANNOT equal either source +vector *vm_vec_normal(vector *dest,vector *p0,vector *p1,vector *p2); + +//computes non-normalized surface normal from three points. +//returns ptr to dest +//dest CANNOT equal either source +vector *vm_vec_perp(vector *dest,vector *p0,vector *p1,vector *p2); + +//computes the delta angle between two vectors. +//vectors need not be normalized. if they are, call vm_vec_delta_ang_norm() +//the forward vector (third parameter) can be NULL, in which case the absolute +//value of the angle in returned. Otherwise the angle around that vector is +//returned. +float vm_vec_delta_ang(vector *v0,vector *v1,vector *fvec); + +//computes the delta angle between two normalized vectors. +float vm_vec_delta_ang_norm(vector *v0,vector *v1,vector *fvec); + +//computes a matrix from a set of three angles. returns ptr to matrix +matrix *vm_angles_2_matrix(matrix *m,angles *a); + +// Computes a matrix from a single angle. +// angle_index = 0,1,2 for p,b,h +matrix *vm_angle_2_matrix(matrix *m, float a, int angle_index); + +//computes a matrix from a forward vector and an angle +matrix *vm_vec_ang_2_matrix(matrix *m,vector *v,float a); + +//computes a matrix from one or more vectors. The forward vector is required, +//with the other two being optional. If both up & right vectors are passed, +//the up vector is used. If only the forward vector is passed, a bank of +//zero is assumed +//returns ptr to matrix +matrix *vm_vector_2_matrix(matrix *m,vector *fvec,vector *uvec,vector *rvec); + +//this version of vector_2_matrix requires that the vectors be more-or-less +//normalized and close to perpendicular +matrix *vm_vector_2_matrix_norm(matrix *m,vector *fvec,vector *uvec,vector *rvec); + +//rotates a vector through a matrix. returns ptr to dest vector +//dest CANNOT equal either source +vector *vm_vec_rotate(vector *dest,vector *src,matrix *m); + +//rotates a vector through the transpose of the given matrix. +//returns ptr to dest vector +//dest CANNOT equal source +// This is a faster replacement for this common code sequence: +// vm_copy_transpose_matrix(&tempm,src_matrix); +// vm_vec_rotate(dst_vec,src_vect,&tempm); +// Replace with: +// vm_vec_unrotate(dst_vec,src_vect, src_matrix) +// +// THIS DOES NOT ACTUALLY TRANSPOSE THE SOURCE MATRIX!!! So if +// you need it transposed later on, you should use the +// vm_vec_transpose() / vm_vec_rotate() technique. +vector *vm_vec_unrotate(vector *dest,vector *src,matrix *m); + +//transpose a matrix in place. returns ptr to matrix +matrix *vm_transpose_matrix(matrix *m); +#define vm_transpose(m) vm_transpose_matrix(m) + +//copy and transpose a matrix. returns ptr to matrix +//dest CANNOT equal source. use vm_transpose_matrix() if this is the case +matrix *vm_copy_transpose_matrix(matrix *dest,matrix *src); +#define vm_copy_transpose(dest,src) vm_copy_transpose_matrix((dest),(src)) + +//mulitply 2 matrices, fill in dest. returns ptr to dest +//dest CANNOT equal either source +matrix *vm_matrix_x_matrix(matrix *dest,matrix *src0,matrix *src1); + +//extract angles from a matrix +angles *vm_extract_angles_matrix(angles *a,matrix *m); + +//extract heading and pitch from a vector, assuming bank==0 +angles *vm_extract_angles_vector(angles *a,vector *v); + +//make sure matrix is orthogonal +void vm_orthogonalize_matrix(matrix *m_src); + +// like vm_orthogonalize_matrix(), except that zero vectors can exist within the +// matrix without causing problems. Valid vectors will be created where needed. +void vm_fix_matrix(matrix *m); + +//Rotates the orient matrix by the angles in tangles and then +//makes sure that the matrix is orthogonal. +void vm_rotate_matrix_by_angles( matrix *orient, angles *tangles ); + +//compute the distance from a point to a plane. takes the normalized normal +//of the plane (ebx), a point on the plane (edi), and the point to check (esi). +//returns distance in eax +//distance is signed, so negative dist is on the back of the plane +float vm_dist_to_plane(vector *checkp,vector *norm,vector *planep); + +// Given mouse movement in dx, dy, returns a 3x3 rotation matrix in RotMat. +// Taken from Graphics Gems III, page 51, "The Rolling Ball" +// Example: +//if ( (Mouse.dx!=0) || (Mouse.dy!=0) ) { +// vm_trackball( Mouse.dx, Mouse.dy, &MouseRotMat ); +// vm_matrix_x_matrix(&tempm,&LargeView.ev_matrix,&MouseRotMat); +// LargeView.ev_matrix = tempm; +//} +void vm_trackball( int idx, int idy, matrix * RotMat ); + +// Find the point on the line between p0 and p1 that is nearest to int_pnt. +// Stuff result in nearest_point. +// Return value indicated where on the line *nearest_point lies. Between 0.0f and 1.0f means it's +// in the line segment. Positive means beyond *p1, negative means before *p0. 2.0f means it's +// beyond *p1 by 2x. +float find_nearest_point_on_line(vector *nearest_point, vector *p0, vector *p1, vector *int_pnt); + +float vm_vec_dot_to_point(vector *dir, vector *p1, vector *p2); + +void compute_point_on_plane(vector *q, plane *planep, vector *p); + +// ---------------------------------------------------------------------------- +// computes the point on a plane closest to a given point (which may be on the plane) +// +// inputs: new_point => point on the plane [result] +// point => point to compute closest plane point +// plane_normal => plane normal +// plane_point => plane point +void vm_project_point_onto_plane(vector *new_point, vector *point, vector *plane_normal, vector *plane_point); + + +// Returns fairly random vector, "quick" normalized +void vm_vec_rand_vec_quick(vector *rvec); + +// Given an point "in" rotate it by "angle" around an +// arbritary line defined by a point on the line "line_point" +// and the normalized line direction, "line_dir" +// Returns the rotated point in "out". +void vm_rot_point_around_line(vector *out, vector *in, float angle, vector *line_point, vector *line_dir); + +// Given two position vectors, return 0 if the same, else non-zero. +int vm_vec_cmp( vector * a, vector * b ); + +// Given two orientation matrices, return 0 if the same, else non-zero. +int vm_matrix_cmp( matrix * a, matrix * b ); + +// Moves angle 'h' towards 'desired_angle', taking the shortest +// route possible. It will move a maximum of 'step_size' radians +// each call. All angles in radians. +void vm_interp_angle( float *h, float desired_angle, float step_size ); + +// check a matrix for zero rows and columns +int vm_check_matrix_for_zeros(matrix *m); + +// see if two vectors are identical +int vm_vec_same(vector *v1, vector *v2); + +// Interpolate from a start matrix toward a goal matrix, minimizing time between orientations. +// Moves at maximum rotational acceleration toward the goal when far and then max deceleration when close. +// Subject to constaints on rotational velocity and angular accleleration. +// Returns next_orientation valid at time delta_t. +void vm_matrix_interpolate(matrix *goal_orient, matrix *start_orient, vector *rotvel_in, float delta_t, + matrix *next_orient, vector *rotvel_out, vector *rotvel_limit, vector *acc_limit, int no_overshoot=0); + +// Interpolate from a start forward vec toward a goal forward vec, minimizing time between orientations. +// Moves at maximum rotational acceleration toward the goal when far and then max deceleration when close. +// Subject to constaints on rotational velocity and angular accleleration. +// Returns next forward vec valid at time delta_t. +void vm_forward_interpolate(vector *goal_fvec, matrix *orient, vector *rotvel_in, float delta_t, float delta_bank, + matrix *next_orient, vector *rotvel_out, vector *vel_limit, vector *acc_limit, int no_overshoot=0); + +// Find the bounding sphere for a set of points (center and radius are output parameters) +void vm_find_bounding_sphere(vector *pnts, int num_pnts, vector *center, float *radius); + +// Version of atan2() that is safe for optimized builds +float atan2_safe(float x, float y); + +// Translates from world coordinates to body coordinates +vector* vm_rotate_vec_to_body(vector *body_vec, vector *world_vec, matrix *orient); + +// Translates from body coordinates to world coordiantes +vector* vm_rotate_vec_to_world(vector *world_vec, vector *body_vec, matrix *orient); + +// estimate next orientation matrix as extrapolation of last and current +void vm_estimate_next_orientation(matrix *last_orient, matrix *current_orient, matrix *next_orient); + +// Return true if all elements of *vec are legal, that is, not a NAN. +int is_valid_vec(vector *vec); + +// Return true if all elements of *m are legal, that is, not a NAN. +int is_valid_matrix(matrix *m); + +// Finds the rotation matrix corresponding to a rotation of theta about axis u +void vm_quaternion_rotate(matrix *m, float theta, vector *u); + +// Takes a rotation matrix and returns the axis and angle needed to generate it +void vm_matrix_to_rot_axis_and_angle(matrix *m, float *theta, vector *rot_axis); + +// interpolate between 2 vectors. t goes from 0.0 to 1.0. at +void vm_vec_interp_constant(vector *out, vector *v1, vector *v2, float t); + +// randomly perturb a vector around a given (normalized vector) or optional orientation matrix +void vm_vec_random_cone(vector *out, vector *in, float max_angle, matrix *orient = NULL); + +// given a start vector, an orientation and a radius, give a point on the plane of the circle +// if on_edge is 1, the point is on the very edge of the circle +void vm_vec_random_in_circle(vector *out, vector *in, matrix *orient, float radius, int on_edge); + +// find the nearest point on the line to p. if dist is non-NULL, it is filled in +// returns 0 if the point is inside the line segment, -1 if "before" the line segment and 1 ir "after" the line segment +int vm_vec_dist_to_line(vector *p, vector *l0, vector *l1, vector *nearest, float *dist); + +#endif + diff --git a/include/verifya3d.h b/include/verifya3d.h new file mode 100644 index 0000000..99a7135 --- /dev/null +++ b/include/verifya3d.h @@ -0,0 +1,30 @@ +///////////////////////////////////////////////////////////////////////////// +// +// VerifyA3D.h : header file for Aureal A3D verification handling. +// +///////////////////////////////////////////////////////////////////////////// + +#ifndef __VERIFYA3D_H +#define __VERIFYA3D_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +// Public verification function. +// Return TRUE if Aureal, FALSE if clone, -1 if check could not succeed. +extern int WINAPI VerifyAurealA3D(void); + +#ifdef __cplusplus +} +#endif + +#endif + + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..cccc267 --- /dev/null +++ b/include/version.h @@ -0,0 +1,152 @@ +/* + * $Logfile: /Freespace2/code/GlobalIncs/version.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 23 11/03/99 11:07a Jefff + * version num -> 1.2 + * + * 22 10/27/99 3:05p Jefff + * upped version to 1.1 for german + * + * 21 10/25/99 5:40p Jefff + * bump version to 1.02 for German version + * + * 20 10/06/99 10:24a Jefff + * upped OEM version to 1.0 + * + * 19 10/01/99 9:10a Daveb + * V 1.1 PATCH + * + * 18 9/14/99 7:34a Dave + * + * 17 9/13/99 12:00a Dave + * Bumped to 0.07 + * + * 16 9/11/99 11:01p Dave + * Upped to 0.06 + * + * 15 9/10/99 9:44p Dave + * Bumped version # up. Make server reliable connects not have such a huge + * timeout. + * + * 14 8/16/99 4:04p Dave + * Big honking checkin. + * + * 13 8/09/99 2:21p Andsager + * Fix patching from multiplayer direct to launcher update tab. + * + * 12 8/09/99 10:36a Dave + * Version info for game. + * + * 11 8/06/99 4:37p Andsager + * demo patch to 1.10 + * + * 10 8/06/99 4:35p Andsager + * test patching - up demo version to 1.01 + * + * 9 8/06/99 10:33a Andsager + * Demo release canditate version number. + * + * 8 8/06/99 12:33a Andsager + * up demo to 1.2 + * + * 7 8/05/99 5:31p Andsager + * Up demo version 1.01 + * + * 6 7/30/99 11:59a Andsager + * + * 5 7/28/99 2:51p Andsager + * Update demo build to 1.0.0 + * + * 4 7/27/99 8:59a Andsager + * Make major, minor version consistent for all builds. Only show major + * and minor for launcher update window. + * + * 3 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 2 5/19/99 4:07p Dave + * Moved versioning code into a nice isolated common place. Fixed up + * updating code on the pxo screen. Fixed several stub problems. + * + * 1 5/18/99 4:28p Dave + * + * $NoKeywords: $ + */ + +#ifndef __FS2_VERSIONING_HEADER_FILE +#define __FS2_VERSIONING_HEADER_FILE + +// ---------------------------------------------------------------------------------------------------------------- +// VERSION DEFINES/VARS +// + +// keep this defined to build a standalone only executable +// STANDALONE_ONLY +// #define STANDALONE_ONLY_BUILD +// #define STANDALONE_ONLY_RELEASE_VERSION 0 + +// Here are the version defines. +// Gets displayed as MAJOR.MINOR, or 1.21 if MAJOR = 1, MINOR = 21. +// Prior to release, MAJOR should be zero. After release, it should be 1. Probably never increase to 2 as that could +// cause confusion with a sequel. +// MINOR should increase by 1 for each minor upgrade and by 10 for major upgrades. +// With each rev we send, we should increase MINOR. +// Version history (full version): +// 1.0 Initial US/UK release +// 1.01 Patch for Win95 volume label bug +// 1.20 German release version + +#if defined (FS2_DEMO) + #define FS_VERSION_MAJOR 01 // Major is 0 for pre-release and 1 for 1.0 release + #define FS_VERSION_MINOR 10 // Increase by 1 for minor revs, 10 for major. + #define FS_VERSION_BUILD 00 // Build version +#elif defined(OEM_BUILD) + #define FS_VERSION_MAJOR 01 // Major is 0 for pre-release and 1 for 1.0 release + #define FS_VERSION_MINOR 00 // Increase by 1 for minor revs, 10 for major. + #define FS_VERSION_BUILD 00 // Build version +#else + #define FS_VERSION_MAJOR 01 // Major is 0 for pre-release and 1 for 1.0 release + #define FS_VERSION_MINOR 20 // Increase by 1 for minor revs, 10 for major. + #define FS_VERSION_BUILD 00 // Build version +#endif + +#define VERSION_LOC_FNAME "version.nfo" +#define MOTD_LOC_FNAME "motd.txt" + +#ifdef MULTIPLAYER_BETA_BUILD + #define MOTD_URL "http://www.pxo.net/files/fs2beta/motd.txt" + #define VERSION_URL "http://www.pxo.net/files/fs2beta/version.nfo" +#elif defined(FS2_DEMO) + #define MOTD_URL "http://www.pxo.net/files/fs2demo/motd.txt" + #define VERSION_URL "http://www.pxo.net/files/fs2demo/version.nfo" +#else + #define MOTD_URL "http://www.pxo.net/files/fs2/motd.txt" + #define VERSION_URL "http://www.pxo.net/files/fs2/version.nfo" +#endif + +#define VERSION_FILE_COMMENT_CHAR ';' +#define NO_VERSION_IN_REGISTRY -2 + +// ---------------------------------------------------------------------------------------------------------------- +// VERSION FUNCTIONS +// + +// compare version against the passed version file +// returns -1 on error +// 0 if we are an earlier version +// 1 if same version +// 2 if higher version +// fills in user version and latest version values if non-NULL +int version_compare(char *filename, int *u_major, int *u_minor, int *u_build, int *l_major, int *l_minor, int *l_build); + +#endif + diff --git a/include/waypointpathdlg.h b/include/waypointpathdlg.h new file mode 100644 index 0000000..9c80014 --- /dev/null +++ b/include/waypointpathdlg.h @@ -0,0 +1,83 @@ +/* + * $Logfile: /Freespace2/code/FRED2/WaypointPathDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Waypoint editor + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 4 5/23/97 1:53p Hoffoss + * Fixed problems with modeless dialog updating. It won't get caught in + * an infinate loop anymore, but still gives an error warning 3 times when + * using cancel and trying to switch window focus to main window. Don't + * know if I can fix that, but it's not too critical right now. + * + * 3 3/13/97 12:09p Hoffoss + * Waypoint path editor finished (apparently I didn't get around to + * completing it before). + * + * 2 2/12/97 12:26p Hoffoss + * Expanded on global error checker, added initial orders conflict + * checking and warning, added waypoint editor dialog and code. + * + * $NoKeywords: $ + */ + +#ifndef _WAYPOINTPATHDLG_H +#define _WAYPOINTPATHDLG_H + +///////////////////////////////////////////////////////////////////////////// +// waypoint_path_dlg dialog + +class waypoint_path_dlg : public CDialog +{ +// Construction +public: + int bypass_errors; + int update_data(int redraw = 1); + void initialize_data(int full_update); + void OnOK(); + BOOL Create(); + waypoint_path_dlg(CWnd* pParent = NULL); // standard constructor + +// Dialog Data + //{{AFX_DATA(waypoint_path_dlg) + enum { IDD = IDD_WAYPOINT_PATH_EDITOR }; + CString m_name; + //}}AFX_DATA + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(waypoint_path_dlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(waypoint_path_dlg) + afx_msg void OnInitMenu(CMenu* pMenu); + afx_msg void OnClose(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +#endif + diff --git a/include/weapon.h b/include/weapon.h new file mode 100644 index 0000000..dcbc833 --- /dev/null +++ b/include/weapon.h @@ -0,0 +1,606 @@ +/* + * $Logfile: /Freespace2/code/Weapon/Weapon.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 34 9/06/99 12:46a Andsager + * Add weapon_explosion_ani LOD + * + * 33 8/27/99 1:34a Andsager + * Modify damage by shockwaves for BIG|HUGE ships. Modify shockwave damge + * when weapon blows up. + * + * 32 8/24/99 10:47a Jefff + * tech room weapon anims. added tech anim field to weapons.tbl + * + * 31 8/10/99 5:30p Jefff + * Added tech_title string to weapon_info. Changed parser accordingly. + * + * 30 8/02/99 5:16p Dave + * Bumped up weapon title string length from 32 to 48 + * + * 29 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 28 6/30/99 5:53p Dave + * Put in new anti-camper code. + * + * 27 6/21/99 7:25p Dave + * netplayer pain packet. Added type E unmoving beams. + * + * 26 6/14/99 10:45a Dave + * Made beam weapons specify accuracy by skill level in the weapons.tbl + * + * 25 6/04/99 2:16p Dave + * Put in shrink effect for beam weapons. + * + * 24 6/01/99 8:35p Dave + * Finished lockarm weapons. Added proper supercap weapons/damage. Added + * awacs-set-radius sexpression. + * + * 23 5/27/99 6:17p Dave + * Added in laser glows. + * + * 22 5/20/99 7:00p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 21 4/28/99 11:13p Dave + * Temporary checkin of artillery code. + * + * 20 4/28/99 3:11p Andsager + * Stagger turret weapon fire times. Make turrets smarter when target is + * protected or beam protected. Add weaopn range to weapon info struct. + * + * 19 4/22/99 11:06p Dave + * Final pass at beam weapons. Solidified a lot of stuff. All that remains + * now is to tweak and fix bugs as they come up. No new beam weapon + * features. + * + * 18 4/19/99 11:01p Dave + * More sophisticated targeting laser support. Temporary checkin. + * + * 17 4/16/99 5:54p Dave + * Support for on/off style "stream" weapons. Real early support for + * target-painting lasers. + * + * 16 4/02/99 9:55a Dave + * Added a few more options in the weapons.tbl for beam weapons. Attempt + * at putting "pain" packets into multiplayer. + * + * 15 3/31/99 8:24p Dave + * Beefed up all kinds of stuff, incluging beam weapons, nebula effects + * and background nebulae. Added per-ship non-dimming pixel colors. + * + * 14 3/23/99 11:03a Dave + * Added a few new fields and fixed parsing code for new weapon stuff. + * + * 13 3/19/99 9:52a Dave + * Checkin to repair massive source safe crash. Also added support for + * pof-style nebulae, and some new weapons code. + * + * 12 1/29/99 12:47a Dave + * Put in sounds for beam weapon. A bunch of interface screens (tech + * database stuff). + * + * 11 1/27/99 9:56a Dave + * Temporary checkin of beam weapons for Dan to make cool sounds. + * + * 10 1/25/99 5:03a Dave + * First run of stealth, AWACS and TAG missile support. New mission type + * :) + * + * 9 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 8 1/21/99 10:45a Dave + * More beam weapon stuff. Put in warmdown time. + * + * 7 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * 6 11/14/98 5:33p Dave + * Lots of nebula work. Put in ship contrails. + * + * 5 10/26/98 9:42a Dave + * Early flak gun support. + * + * 4 10/16/98 3:42p Andsager + * increase MAX_WEAPONS and MAX_SHIPS and som header files + * + * 3 10/07/98 6:27p Dave + * Globalized mission and campaign file extensions. Removed Silent Threat + * special code. Moved \cache \players and \multidata into the \data + * directory. + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 100 9/04/98 3:52p Dave + * Put in validated mission updating and application during stats + * updating. + * + * 99 9/01/98 4:25p Dave + * Put in total (I think) backwards compatibility between mission disk + * freespace and non mission disk freespace, including pilot files and + * campaign savefiles. + * + * 98 8/28/98 3:29p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 97 8/25/98 1:49p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 96 8/18/98 10:15a Dave + * Touchups on the corkscrew missiles. Added particle spewing weapons. + * + * 95 8/17/98 5:07p Dave + * First rev of corkscrewing missiles. + * + * 94 5/18/98 1:58a Mike + * Make Phoenix not be fired at fighters (but yes bombers). + * Improve positioning of ships in guard mode. + * Make turrets on player ship not fire near end of support ship docking. + * + * 93 5/15/98 5:37p Hoffoss + * Added new 'tech description' fields to ship and weapon tables, and + * added usage of it in tech room. + * + * 92 5/06/98 8:06p Dave + * Made standalone reset properly under weird conditions. Tweak + * optionsmulti screen. Upped MAX_WEAPONS to 350. Put in new launch + * countdown anim. Minro ui fixes/tweaks. + * + * 91 5/05/98 11:15p Lawrance + * Optimize weapon flyby sound determination + * + * 90 5/05/98 4:08p Hoffoss + * Added WIF_PLAYER_ALLOWED to check what is valid for the weaponry pool + * in Fred. + * + * 89 4/23/98 5:50p Hoffoss + * Added tracking of techroom database list info in pilot files, added + * sexp to add more to list, made mouse usable on ship listing in tech + * room. + * + * 88 4/19/98 9:21p Sandeep + * + * 87 4/13/98 5:11p Mike + * More improvement to countermeasure code. + * + * 86 4/10/98 11:02p Mike + * Make countermeasures less effective against aspect seekers than against + * heat seekers. + * Make AI ships match bank with each other when attacking a faraway + * target. + * Make ships not do silly loop-de-loop sometimes when attacking a faraway + * target. + * + * 85 4/10/98 4:52p Hoffoss + * Made several changes related to tooltips. + * + * 84 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 83 4/01/98 5:35p John + * Made only the used POFs page in for a level. Reduced some interp + * arrays. Made custom detail level work differently. + * + * 82 3/21/98 3:37p Mike + * Fix/optimize attacking of big ships. + * + * 81 3/15/98 9:44p Lawrance + * Get secondary weapons for turrets working + * + * 79 3/12/98 1:24p Mike + * When weapons linked, increase firing delay. + * Free up weapon slots for AI ships, if necessary. + * Backside render shield effect. + * Remove shield hit triangle if offscreen. + * + * 78 3/11/98 4:34p John + * Made weapons have glow & thrusters. + * + * 77 3/04/98 8:51a Mike + * Working on making ships better about firing Synaptic. + * + * 76 3/03/98 5:12p Dave + * 50% done with team vs. team interface issues. Added statskeeping to + * secondary weapon blasts. Numerous multiplayer ui bug fixes. + * + * 75 3/02/98 10:00a Sandeep + * + * 74 2/15/98 11:28p Allender + * allow increase in MAX_WEAPON_TYPES by chaning all bitfield references + * + * 73 2/11/98 1:52p Sandeep + * + * 72 2/02/98 8:47a Andsager + * Ship death area damage applied as instantaneous damage for small ships + * and shockwaves for large (>50 radius) ships. + * + * 71 1/29/98 11:48a John + * Added new counter measure rendering as model code. Made weapons be + * able to have impact explosion. + * + * 70 1/23/98 5:08p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 69 1/21/98 7:20p Lawrance + * removed unused mount_size member + * + * 68 1/19/98 10:01p Lawrance + * Implement "Electronics" missiles + * + * 67 1/17/98 10:04p Lawrance + * Implementing "must lock to fire" missiles. Fix couple bugs with rearm + * time display. + * + * 66 1/17/98 4:45p Mike + * Better support for AI selection of secondary weapons. + * + * 65 1/16/98 11:43a Mike + * Fix countermeasures. + * + * 64 1/15/98 11:13a John + * Added code for specifying weapon trail bitmaps in weapons.tbl + * + * 63 1/06/98 6:58p Lawrance + * Add wp->turret_subsys, which gets set when a turret fires a weapon. + * + * $NoKeywords: $ + */ + +#ifndef _WEAPON_H +#define _WEAPON_H + +// define moved to before includes so that we can have it available when ship.h is included below +#define MAX_WEAPON_TYPES 200 + +// define to compile corkscrew missiles in + +#include "object.h" +#include "parselo.h" +#include "ship.h" /* needed for ship_subsys* */ +#include "2d.h" // needed for color +#include "shockwave.h" +#include "trails.h" +#include "ai.h" + +#define WP_UNUSED -1 +#define WP_LASER 0 +#define WP_MISSILE 1 +#define WP_BEAM 2 + +#define WRT_LASER 1 +#define WRT_POF 2 + +// Bitflags controlling weapon behavior +#define MAX_WEAPON_FLAGS 18 // Maximum number of different bit flags legal to specify in a single weapons.tbl Flags line + +#define WIF_HOMING_HEAT (1 << 0) // if set, this weapon homes via seeking heat +#define WIF_HOMING_ASPECT (1 << 1) // if set, this weapon homes via chasing aspect +#define WIF_ELECTRONICS (1 << 2) // Takes out electronics systems. +#define WIF_SPAWN (1 << 3) // Spawns projectiles on detonation. +#define WIF_REMOTE (1 << 4) // Can be remotely detonated by parent. +#define WIF_PUNCTURE (1 << 5) // Punctures armor, damaging subsystems. +#define WIF_SUPERCAP (1 << 6) // This is a weapon which does supercap class damage (meaning, it applies real damage to supercap ships) +#define WIF_AREA_EFFECT (1 << 7) // Explosion has an area effect +#define WIF_SHOCKWAVE (1 << 8) // Explosion has a shockwave +#define WIF_TURNS (1 << 9) // Set this if the weapon ever changes heading. If you + // don't set this and the weapon turns, collision detection + // won't work, I promise! +#define WIF_SWARM (1 << 10) // Missile "swarms".. ie changes heading and twists on way to target +#define WIF_TRAIL (1 << 11) // Has a trail +#define WIF_BIG_ONLY (1 << 12) // Only big ships (cruiser, capital, etc.) can arm this weapon +#define WIF_CHILD (1 << 13) // No ship can have this weapon. It gets created by weapon detonations. +#define WIF_BOMB (1 << 14) // Bomb-type missile, can be targeted +#define WIF_HUGE (1 << 15) // Huge damage (generally 500+), probably only fired at huge ships. +#define WIF_NO_DUMBFIRE (1 << 16) // Missile cannot be fired dumbfire (ie requires aspect lock) +#define WIF_THRUSTER (1 << 17) // Has thruster cone and/or glow +#define WIF_IN_TECH_DATABASE (1 << 18) +#define WIF_PLAYER_ALLOWED (1 << 19) // allowed to be on starting wing ships/in weaponry pool +#define WIF_BOMBER_PLUS (1 << 20) // Fire this missile only at a bomber or big ship. But not a fighter. + +#define WIF_CORKSCREW (1 << 21) // corkscrew style missile +#define WIF_PARTICLE_SPEW (1 << 22) // spews particles as it travels +#define WIF_EMP (1 << 23) // weapon explodes with a serious EMP effect +#define WIF_ENERGY_SUCK (1 << 24) // energy suck primary (impact effect) +#define WIF_FLAK (1 << 25) // use for big-ship turrets - flak gun +#define WIF_BEAM (1 << 26) // if this is a beam weapon : NOTE - VERY SPECIAL CASE +#define WIF_TAG (1 << 27) // this weapon has a tag effect when it hits +#define WIF_SHUDDER (1 << 28) // causes the weapon to shudder. shudder is proportional to the mass and damage of the weapon +#define WIF_MFLASH (1 << 29) // has muzzle flash +#define WIF_LOCKARM (1 << 30) // if the missile was fired without a lock, it does significanlty less damage on impact +#define WIF_STREAM (1 << 31) // handled by "trigger down/trigger up" instead of "fire - wait - fire - wait" + +#define WIF_HOMING (WIF_HOMING_HEAT | WIF_HOMING_ASPECT) +#define WIF_HURTS_BIG_SHIPS (WIF_BOMB | WIF_BEAM | WIF_HUGE | WIF_BIG_ONLY) + +#define WEAPON_EXHAUST_DELTA_TIME 75 // Delay in milliseconds between exhaust blobs + +#define WF_LOCK_WARNING_PLAYED (1<<0) // set when a lock warning sound is played for the player + // (needed since we don't want to play multiple lock sounds) +#define WF_ALREADY_APPLIED_STATS (1<<1) // for use in ship_apply_local and ship_apply_global damage functions + // so that we don't record multiple hits (stats) for one impact +#define WF_PLAYED_FLYBY_SOUND (1<<2) // flyby sound has been played for this weapon +#define WF_CONSIDER_FOR_FLYBY_SOUND (1<<3) // consider for flyby +#define WF_DEAD_IN_WATER (1<<4) // a missiles engines have died +#define WF_LOCKED_WHEN_FIRED (1<<5) // fired with a lock +#define WF_DESTROYED_BY_WEAPON (1<<6) // destroyed by damage from other weapon + +typedef struct weapon { + int weapon_info_index; // index into weapon_info array + int objnum; // object number for this weapon + int team; // The team of the ship that fired this + int species; // The species of the ship that fired this + float lifeleft; // life left on this weapon + int target_num; // Object index of target + int target_sig; // So we know if the target is the same one we've been tracking + float nearest_dist; // nearest distance yet attained to target + fix creation_time; // time at which created, stuffed Missiontime + int weapon_flags; // bit flags defining behavior, see WF_xxxx + object* homing_object; // object this weapon is homing on. + ship_subsys* homing_subsys; // subsystem this weapon is homing on + vector homing_pos; // world position missile is homing on + short swarm_index; // index into swarm missile info, -1 if not WIF_SWARM + int missile_list_index; // index for missiles into Missile_obj_list, -1 weapon not missile + int trail_num; // -1 if no trail, else index into Trails array + ship_subsys *turret_subsys; // points to turret that fired weapon, otherwise NULL + int group_id; // Which group this is in. + + // Stuff for thruster glows + int thruster_bitmap; // What frame the current thruster bitmap is at for this weapon + float thruster_frame; // Used to keep track of which frame the animation should be on. + int thruster_glow_bitmap; // What frame the current thruster engine glow bitmap is at for this weapon + float thruster_glow_frame; // Used to keep track of which frame the engine glow animation should be on. + float thruster_glow_noise; // Noise for current frame + + int pick_big_attack_point_timestamp; // Timestamp at which to pick a new point to attack. + vector big_attack_point; // Target-relative location of attack point. + + int cmeasure_ignore_objnum; // Ignoring this countermeasure. It's failed to attract this weapon. + int cmeasure_chase_objnum; // Chasing this countermeasure. Don't maybe ignore in future. + + // corkscrew info (taken out for now) + short cscrew_index; // corkscrew info index + + // particle spew info + int particle_spew_time; // time to spew next bunch of particles + + // flak info + short flak_index; // flak info index +} weapon; + + +#ifdef FS2_DEMO + #define MAX_WEAPONS 100 +#else + // upped 5/6/98 from 200 - DB + #define MAX_WEAPONS 350 +#endif + +// info specific to beam weapons +#define MAX_BEAM_SECTIONS 5 +typedef struct beam_weapon_section_info { + float width; // width of the section + int texture; // texture bitmap + ubyte rgba_inner[4]; // for non-textured beams + ubyte rgba_outer[4]; // for non-textured beams + float flicker; // how much it flickers (0.0 to 1.0) + float z_add; // is this necessary? +} beam_weapon_section_info; + +typedef struct beam_weapon_info { + int beam_type; // beam type + float beam_life; // how long it lasts + int beam_warmup; // how long it takes to warmup (in ms) + int beam_warmdown; // how long it takes to warmdown (in ms) + float beam_muzzle_radius; // muzzle glow radius + int beam_particle_count; // beam spew particle count + float beam_particle_radius; // radius of beam particles + float beam_particle_angle; // angle of beam particle spew cone + int beam_particle_ani; // particle ani + float beam_miss_factor[NUM_SKILL_LEVELS]; // magic # which makes beams miss more. by skill level + int beam_loop_sound; // looping beam sound + int beam_warmup_sound; // warmup sound + int beam_warmdown_sound; // warmdown sound + int beam_num_sections; // the # of visible "sections" on the beam + int beam_glow_bitmap; // muzzle glow bitmap + int beam_shots; // # of shots the beam takes + float beam_shrink_factor; // what percentage of total beam lifetime when the beam starts shrinking + float beam_shrink_pct; // what percent/second the beam shrinks at + beam_weapon_section_info sections[MAX_BEAM_SECTIONS]; // info on the visible sections of the beam +} beam_weapon_info; + +extern weapon Weapons[MAX_WEAPONS]; + +#define WEAPON_TITLE_LEN 48 + +typedef struct weapon_info { + char name[NAME_LENGTH]; // name of this weapon + char title[WEAPON_TITLE_LEN]; // official title of weapon (used by tooltips) + char *desc; // weapon's description (used by tooltips) + int subtype; // one of the WP_* macros above + int render_type; // rendering method, laser, pof, avi + char pofbitmap_name[NAME_LENGTH]; // Name of the pof representing this if POF, or bitmap filename if bitmap + int model_num; // modelnum of weapon -- -1 if no model + + char *tech_desc; // weapon's description (in tech database) + char tech_anim_filename[NAME_LENGTH]; // weapon's tech room animation + char tech_title[NAME_LENGTH]; // weapon's name (in tech database) + + int laser_bitmap; // Which bitmap renders for laser, -1 if none + int laser_glow_bitmap; // optional bitmap for laser glow + float laser_length; + color laser_color_1; // for cycling between glow colors + color laser_color_2; // for cycling between glow colors + float laser_head_radius, laser_tail_radius; + + float max_speed; // initial speed of the weapon + float mass; // mass of the weapon + float fire_wait; // fire rate -- amount of time before you can refire the weapon + float blast_force; // force this weapon exhibits when hitting an object + float damage; // damage of weapon (for missile, damage within inner radius) + float inner_radius, outer_radius; // damage radii for missiles (0 means impact only) + float shockwave_speed; // speed of shockwave ( 0 means none ) + float armor_factor, shield_factor, subsystem_factor; // in 0.0..2.0, scale of damage done to type of thing + float lifetime; // How long this thing lives. + float energy_consumed; // Energy used up when weapon is fired + int wi_flags; // bit flags defining behavior, see WIF_xxxx + float turn_time; + float cargo_size; // cargo space taken up by individual weapon (missiles only) + float rearm_rate; // rate per second at which secondary weapons are loaded during rearming + float weapon_range; // max range weapon can be effectively fired. (May be less than life * speed) + + // spawn weapons + short spawn_type; // Type of weapon to spawn when detonated. + short spawn_count; // Number of weapons of spawn_type to spawn. + + // swarm count + short swarm_count; // how many swarm missiles are fired for this weapon + + // Specific to ASPECT homing missiles. + float min_lock_time; // minimum time (in seconds) to achieve lock + int lock_pixels_per_sec; // pixels/sec moved while locking + int catchup_pixels_per_sec; // pixels/sec moved while catching-up for a lock + int catchup_pixel_penalty; // number of extra pixels to move while locking as a penalty for catching up for a lock + + // Specific to HEAT homing missiles. + float fov; + + int launch_snd; + int impact_snd; + int flyby_snd; // whizz-by sound, transmitted through weapon's portable atmosphere. + + // Specific to weapons with TRAILS: + trail_info tr_info; + + char icon_filename[NAME_LENGTH]; // filename for icon that is displayed in weapon selection + char anim_filename[NAME_LENGTH]; // filename for animation that plays in weapon selection + + int impact_weapon_expl_index; // Index into Weapon_expl_info of which ANI should play when this thing impacts something + float impact_explosion_radius; // How big the explosion should be + + // EMP effect + float emp_intensity; // intensity of the EMP effect + float emp_time; // time of the EMP effect + + // Energy suck effect + float weapon_reduce; // how much energy removed from weapons systems + float afterburner_reduce; // how much energy removed from weapons systems + + // Beam weapon effect + beam_weapon_info b_info; // this must be valid if the weapon is a beam weapon WIF_BEAM or WIF_BEAM_SMALL + + // tag stuff + float tag_time; // how long the tag lasts + int tag_level; // tag level (1 - 3) + + // muzzle flash + int muzzle_flash; // muzzle flash stuff +} weapon_info; + +// Data structure to track the active missiles +typedef struct missile_obj { + missile_obj *next, *prev; + int flags, objnum; +} missile_obj; +extern missile_obj Missile_obj_list; + +extern weapon_info Weapon_info[MAX_WEAPON_TYPES]; + +extern int Num_weapon_types; // number of weapons in the game +extern int First_secondary_index; +extern int Num_weapons; + +extern int Num_player_weapon_precedence; // Number of weapon types in Player_weapon_precedence +extern int Player_weapon_precedence[MAX_WEAPON_TYPES]; // Array of weapon types, precedence list for player weapon selection +extern char *Weapon_names[MAX_WEAPON_TYPES]; + +#define WEAPON_INDEX(wp) (wp-Weapons) +#define WEAPON_INFO_INDEX(wip) (wip-Weapon_info) + + +int weapon_info_lookup(char *name); +void weapon_init(); // called at game startup +void weapon_level_init(); // called before the start of each level +void weapon_render(object * obj); +void weapon_delete( object * obj ); +void weapon_process_pre( object *obj, float frame_time); +void weapon_process_post( object *obj, float frame_time); + +// Group_id: If you should quad lasers, they should all have the same group id. +// This will be used to optimize lighting, since each group only needs to cast one light. +// Call this to get a new group id, then pass it to each weapon_create call for all the +// weapons in the group. Number will be between 0 and WEAPON_MAX_GROUP_IDS and will +// get reused. +int weapon_create_group_id(); + +// How many unique groups of weapons there can be at one time. +#define WEAPON_MAX_GROUP_IDS 256 + +// Passing a group_id of -1 means it isn't in a group. See weapon_create_group_id for more +// help on weapon groups. +int weapon_create( vector * pos, matrix * orient, int weapon_type, int parent_obj, int secondary_flag, int group_id=-1, int is_locked = 0); +void weapon_set_tracking_info(int weapon_objnum, int parent_objnum, int target_objnum, int target_is_locked = 0, ship_subsys *target_subsys = NULL); + +// for weapons flagged as particle spewers, spew particles. wheee +void weapon_maybe_spew_particle(object *obj); + + +void weapon_hit( object * weapon_obj, object * other_obj, vector * hitpos ); +int weapon_name_lookup(char *name); +void spawn_child_weapons( object *objp ); + +// call to detonate a weapon. essentially calls weapon_hit() with other_obj as NULL, and sends a packet in multiplayer +void weapon_detonate(object *objp); + +void weapon_area_apply_blast(vector *force_apply_pos, object *ship_objp, vector *blast_pos, float blast, int make_shockwave); +int weapon_area_calc_damage(object *objp, vector *pos, float inner_rad, float outer_rad, float max_blast, float max_damage, + float *blast, float *damage, float limit); + +void missile_obj_list_rebuild(); // called by save/restore code only +missile_obj *missile_obj_return_address(int index); +void find_homing_object_cmeasures(); + +// THE FOLLOWING FUNCTION IS IN SHIP.CPP!!!! +// JAS - figure out which thruster bitmap will get rendered next +// time around. ship_render needs to have shipp->thruster_bitmap set to +// a valid bitmap number, or -1 if we shouldn't render thrusters. +// This does basically the same thing as ship_do_thruster_frame, except it +// operates on a weapon. This is in the ship code because it needs +// the same thruster animation info as the ship stuff, and I would +// rather extern this one function than all the thruster animation stuff. +void ship_do_weapon_thruster_frame( weapon *weaponp, object *objp, float frametime ); + +// call to get the "color" of the laser at the given moment (since glowing lasers can cycle colors) +void weapon_get_laser_color(color *c, object *objp); + +void weapon_hit_do_sound(object *hit_obj, weapon_info *wip, vector *hitpos); + +// return a scale factor for damage which should be applied for 2 collisions +float weapon_get_damage_scale(weapon_info *wip, object *wep, object *target); + +// return handle to explosion ani +int weapon_get_expl_handle(int weapon_expl_index, vector *pos, float size); + +#endif + diff --git a/include/weaponeditordlg.h b/include/weaponeditordlg.h new file mode 100644 index 0000000..d3791d7 --- /dev/null +++ b/include/weaponeditordlg.h @@ -0,0 +1,124 @@ +/* + * $Logfile: /Freespace2/code/FRED2/WeaponEditorDlg.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Weapon editor dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 8 2/22/98 1:32a Hoffoss + * Changed editor to use raw ammo counts isntead of percentages, and make + * it select first item in list by default. + * + * 7 5/30/97 4:50p Hoffoss + * Added code to allow marked ship editing of data in child dialogs of + * ship editor dialog. + * + * 6 4/21/97 5:02p Hoffoss + * Player/player status editing supported, and both saved and loaded from + * Mission files. + * + * 5 4/16/97 1:59p Hoffoss + * Weapon editor now fully functional. + * + * 4 4/09/97 11:48a Hoffoss + * Initial work to weapon editor. + * + * 3 3/31/97 6:07p Hoffoss + * Fixed several errors, including BG editor not graying fields, BG editor + * not updating image when changed, Removed obsolete data from Weapon + * editor, priority not being saved when missions saved, priority not + * editable in initial orders editor. + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "ship.h" + +///////////////////////////////////////////////////////////////////////////// +// WeaponEditorDlg dialog + +class WeaponEditorDlg : public CDialog +{ +// Construction +public: + void update_pilot(); + void OnCancel(); + void change_selection(); + void OnOK(); + WeaponEditorDlg(CWnd* pParent = NULL); // standard constructor + + int m_ammo_max1; + int m_ammo_max2; + int m_ammo_max3; + int m_ammo_max4; + int m_last_item; + int m_ship; + int m_ship_class; + int m_multi_edit; + ship_weapon pilot, *cur_weapon; + +// Dialog Data + //{{AFX_DATA(WeaponEditorDlg) + enum { IDD = IDD_WEAPON_EDITOR }; + CSpinButtonCtrl m_spin4; + CSpinButtonCtrl m_spin3; + CSpinButtonCtrl m_spin2; + CSpinButtonCtrl m_spin1; + int m_ai_class; + int m_ammo1; + int m_ammo2; + int m_ammo3; + int m_ammo4; + int m_gun1; + int m_gun2; + int m_gun3; + int m_missile1; + int m_missile2; + int m_missile3; + int m_missile4; + int m_cur_item; + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(WeaponEditorDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(WeaponEditorDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnSelchangeList(); + afx_msg void OnClose(); + afx_msg void OnSelchangeMissile1(); + afx_msg void OnSelchangeMissile2(); + afx_msg void OnSelchangeMissile3(); + afx_msg void OnSelchangeMissile4(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/wing.h b/include/wing.h new file mode 100644 index 0000000..471aff1 --- /dev/null +++ b/include/wing.h @@ -0,0 +1,56 @@ +/* + * $Logfile: /Freespace2/code/FRED2/wing.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Wing management functions for dealing with wing related operations + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 7 8/16/97 4:51p Hoffoss + * Fixed bugs with wing deletion and removing ships from a wing. + * + * 6 8/12/97 1:55a Hoffoss + * Made extensive changes to object reference checking and handling for + * object deletion call. + * + * 5 6/18/97 2:36p Hoffoss + * Wing ship numbering starts at 1 instead of 0, and changed form wing to + * allow reforming a wing. + * + * 4 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 3 1/24/97 4:14p Hoffoss + * Added error checking to Fred. + * + * 2 11/26/96 2:40p Hoffoss + * Wing toolbar buttons supported. + * + * 1 11/05/96 9:14a Hoffoss + * + * $NoKeywords: $ + */ + +#include "management.h" + +int create_wing(); +void remove_wing(int wing_num); +void remove_ship_from_wing(int ship, int min = 1); +void mark_wing(int wing); +int delete_wing(int wing = cur_wing, int bypass = 0); + diff --git a/include/wing_editor.h b/include/wing_editor.h new file mode 100644 index 0000000..d3131e0 --- /dev/null +++ b/include/wing_editor.h @@ -0,0 +1,201 @@ +/* + * $Logfile: /Freespace2/code/FRED2/wing_editor.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Wing editor dialog box handler code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 35 3/16/98 8:27p Allender + * Fred support for two new AI flags -- kamikaze and no dynamic goals. + * + * 34 2/23/98 9:48p Allender + * added no arrival/departure warps to wings + * + * 33 11/25/97 10:03a Allender + * added no arrival message checkbox to wing editor + * + * 32 11/25/97 9:42a Hoffoss + * Removed starting wing checkbox from wing editor. + * + * 31 11/13/97 4:14p Allender + * automatic assignment of hotkeys for starting wings. Appripriate + * warnings when they are incorrectly used. hotkeys correctly assigned to + * ships/wing arriving after mission start + * + * 30 11/11/97 2:13p Allender + * docking bay support for Fred and Freespace. Added hook to ai code for + * arrival/departure from dock bays. Fred support now sufficient. + * + * 29 11/10/97 10:13p Allender + * added departure anchor to Fred and Freespace in preparation for using + * docking bays. Functional in Fred, not in FreeSpace. + * + * 28 10/14/97 5:33p Hoffoss + * Added Fred support (and fsm support) for the no_arrival_music flags in + * ships and wings. + * + * 27 8/30/97 9:52p Hoffoss + * Implemented arrival location, distance, and anchor in Fred. + * + * 26 8/22/97 4:16p Hoffoss + * added support for arrival and departure info in ship editor using + * wing's info if editing marked ships in a wing instead of using ship's. + * + * 25 8/13/97 11:22p Hoffoss + * Implemented wave delay min and max in Fred. + * + * 24 8/12/97 7:17p Hoffoss + * Added previous button to ship and wing editors. + * + * 23 8/12/97 6:32p Hoffoss + * Added code to allow hiding of arrival and departure cues in editors. + * + * 22 7/25/97 2:40p Hoffoss + * Fixed bug in sexp tree selection updating handling. + * + * 21 7/09/97 2:38p Allender + * organized ship/wing editor dialogs. Added protect ship and ignore + * count checkboxes to those dialogs. Changed flag code for + * parse_objects. Added unprotect sexpressions + * + * 20 6/05/97 6:10p Hoffoss + * Added features: Autosaving, object hiding. Also fixed some minor bugs. + * + * 19 5/30/97 11:33a Allender + * more hotkey combo box stuff + * + * 18 5/23/97 1:53p Hoffoss + * Fixed problems with modeless dialog updating. It won't get caught in + * an infinate loop anymore, but still gives an error warning 3 times when + * using cancel and trying to switch window focus to main window. Don't + * know if I can fix that, but it's not too critical right now. + * + * 17 4/28/97 2:37p Hoffoss + * Added hotkey editing to Fred for ships and wings. + * + * 16 3/20/97 3:55p Hoffoss + * Major changes to how dialog boxes initialize (load) and update (save) + * their internal data. This should simplify things and create less + * problems. + * + * 15 2/20/97 4:03p Hoffoss + * Several ToDo items: new reinforcement clears arrival cue, reinforcement + * control from ship and wing dialogs, show grid toggle. + * + * 14 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "sexp_tree.h" + +///////////////////////////////////////////////////////////////////////////// +// wing_editor dialog + +class wing_editor : public CDialog +{ +// Construction +public: + int cue_height; + int bypass_errors; + int modified; + int select_sexp_node; + + void initialize_data_safe(int full_update); + void update_data_safe(); + void show_hide_sexp_help(); + void calc_cue_height(); + int verify(); + wing_editor(CWnd* pParent = NULL); // standard constructor + BOOL Create(); + void OnOK(); + int update_data(int redraw = 1); + void initialize_data(int full); + +// Dialog Data + //{{AFX_DATA(wing_editor) + enum { IDD = IDD_WING_EDITOR }; + CSpinButtonCtrl m_departure_delay_spin; + CSpinButtonCtrl m_arrival_delay_spin; + sexp_tree m_departure_tree; + sexp_tree m_arrival_tree; + CSpinButtonCtrl m_threshold_spin; + CSpinButtonCtrl m_waves_spin; + CString m_wing_name; + int m_special_ship; + int m_waves; + int m_threshold; + int m_arrival_location; + int m_departure_location; + int m_arrival_delay; + int m_departure_delay; + BOOL m_reinforcement; + int m_hotkey; + BOOL m_ignore_count; + int m_arrival_delay_max; + int m_arrival_delay_min; + int m_arrival_dist; + int m_arrival_target; + BOOL m_no_arrival_music; + int m_departure_target; + BOOL m_no_arrival_message; + BOOL m_no_arrival_warp; + BOOL m_no_departure_warp; + BOOL m_no_dynamic; + //}}AFX_DATA + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(wing_editor) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(wing_editor) + afx_msg void OnInitMenu(CMenu* pMenu); + afx_msg void OnDeltaposSpinWaves(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnRclickArrivalTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnRclickDepartureTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBeginlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnBeginlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnEndlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnEndlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnDeleteWing(); + afx_msg void OnDisbandWing(); + afx_msg void OnClose(); + afx_msg void OnGoals2(); + afx_msg void OnReinforcement(); + afx_msg void OnNext(); + afx_msg void OnSelchangedArrivalTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnSelchangedDepartureTree(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnHideCues(); + afx_msg void OnPrev(); + afx_msg void OnSelchangeArrivalLocation(); + afx_msg void OnSelchangeDepartureLocation(); + afx_msg void OnSelchangeHotkey(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + diff --git a/include/winmidi.h b/include/winmidi.h new file mode 100644 index 0000000..948527d --- /dev/null +++ b/include/winmidi.h @@ -0,0 +1,30 @@ +/* + * $Logfile: /Freespace2/code/Sound/WinMIDI.h $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for playing and managing MIDI files + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:12 root + * Initial revision + * + * + * 2 10/07/98 10:54a Dave + * Initial checkin. + * + * 1 10/07/98 10:51a Dave + * + * 3 1/19/98 11:37p Lawrance + * Fixing Optimization build warnings + * + * + * $NoKeywords: $ + */ + +#ifndef __WINMIDI_H__ +#define __WINMIDI_H__ + +#endif /* __WINMIDI_H__ */ + diff --git a/src/ac/ac.cpp b/src/ac/ac.cpp new file mode 100644 index 0000000..fefb85f --- /dev/null +++ b/src/ac/ac.cpp @@ -0,0 +1,218 @@ +/* + * $Logfile: /Freespace2/code/AC/ac.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for console version of anim converter + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 2 10/23/98 6:03p Dave + * + * 12 6/24/98 10:43a Hoffoss + * Changed default to 15 FPS instead of 30 FPS. + * + * 11 6/23/98 4:18p Hoffoss + * Fixed some bugs with AC release build. + * + * 10 7/20/97 6:59p Lawrance + * added new -i and -x switches + * + * 9 5/21/97 11:06a Lawrance + * enabling a user-defined transparent value + * + * 8 5/19/97 3:21p Lawrance + * add fps parm, version num to anim header + * + * 7 2/25/97 5:18p Lawrance + * add carriage return after finished, so DOS prompt at a new line + * + * 6 2/20/97 3:03p Lawrance + * initialize force_key_frame global to -1 + * + * 5 2/20/97 1:59p Lawrance + * sourcesafe sucks + * + * 4 2/20/97 1:58p Adam + * + * 2 2/19/97 9:26p Lawrance + * console version of converter working + * + * 1 2/19/97 7:26p Lawrance + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include +#include "convert.h" +#include "ac.h" +#include "animplay.h" +#include "packunpack.h" + +#ifdef NDEBUG +char *help_text = "AC ANI Converter Copyright (C) 1998, Volition, Inc. All Rights Reserved.\n" + "For exclusive use in FreeSpace missions only.\n\n" + "Usage: ac [-fps n] [-v] filename\n\n" \ + "-i => display information and statistics about the ani file\n" \ + "-x => extract pcx files from ani (named filename0000.pcx etc)\n" \ + "-fps n => n frames per second (default 15)\n" \ + "-v => display the version number\n" \ + "filename => a Microsoft RLE AVI || PCX file in form nameXXXX.pcx\n\n" \ + "Examples:\n" \ + "ac test.avi => converts test.avi to test.ani\n" \ + "ac test0000.pcx => converts test0000.pcx up to highest testxxxx.pcx to test.ani\n"; + +#else +char *help_text = "Usage: ac [-c n] [-k n] [-ke n] [-fps n] [-to] [-v] filename\n\n" \ + "-c n => which kind of compression to use (default is 1):\n" \ + " -c 0 => compression using up to 255 count, takes 3 bytes for a run\n" \ + " -c 1 => compression using up to 127 count, takes 2 bytes for a run\n" \ + "-i => display information and statistics about the ani file\n" \ + "-x => extract pcx files from ani (named filename0000.pcx etc)\n" \ + "-k n => keyframe at frame n\n" \ + "-ke n => keyframe every n frames\n" \ + "-fps n => n frames per second (default 15)\n" \ + "-to => transparent override. Use pixel in top-left corner of first frame as transparent color. Otherwise RGB value 0.255.0 is used.\n" \ + "-v => display the version number\n" \ + "filename => a Microsoft RLE AVI || PCX file in form nameXXXX.pcx\n\n" \ + "Examples:\n" \ + "ac test.avi => converts test.avi to test.ani with no keyframes\n" \ + "ac -k 10 test.avi => converts test.avi to test.ani with keyframe at 10th frame\n" \ + "ac -ke 10 test.avi => every 10th frame (frame 1, 10, 20 etc) is a keyframe\n" \ + "ac test0000.pcx => converts test0000.pcx up to highest testxxxx.pcx to test.ani\n"\ + "ac -k 10 test0000.pcx => same as above, but test009.pcx is a keyframe\n" \ + "ac -ke 5 test0000.pcx => makes test0004.pcx, test0009.pcx, etc a keyframe\n"; +#endif + +// Global to file +static char buffer[255]; + +// Internal function prototypes +void ac_error(char *msg); +void start_convert_with(char* filename); + +int main(int argc, char *argv[]) +{ + int i; + + key_frame_rate = 0; // assume no regular keyframes + force_key_frame = -1; // assume no force key frame + + Default_fps = ANIM_DEFAULT_FPS; // assume default fps value + Use_custom_xparent_color = 0; // assume xparent RGB is 0.255.0 + + Compression_type = CUSTOM_DELTA_RLE; + + vm_init(16*1024*1024); + + if ( argc <= 1 ) { + printf(help_text); + exit(0); + } + + for ( i = 1; i < argc; i++ ) { + if ( !stricmp(argv[i], "-h" ) ) { + printf(help_text); + exit(0); + +#ifndef NDEBUG + } else if ( !stricmp(argv[i], "-c" ) ) { + int mode; + if ( i+1 >= argc ) ac_error("-c switch requires a parameter\n"); + mode = atoi(argv[i+1]); + i++; + switch ( mode ) { + case 0: + Compression_type = CUSTOM_DELTA_RLE; + break; + case 1: + Compression_type = STD_DELTA_RLE; + break; + default: + ac_error("-c only supports compression types 0 and 1\n"); + break; + } // end switch + + } else if ( !stricmp(argv[i], "-k" ) ) { + if ( i+1 >= argc ) ac_error("-k switch requires a parameter\n"); + force_key_frame = atoi(argv[i+1]); + i++; + + } else if ( !stricmp(argv[i], "-ke" ) ) { + if ( i+1 >= argc ) ac_error("-ke switch requires a parameter\n"); + key_frame_rate = atoi(argv[i+1]); + i++; + + } else if ( !stricmp(argv[i], "-to" ) ) { + Use_custom_xparent_color = 1; +#endif + + } else if ( !stricmp(argv[i], "-fps" ) ) { + if ( i+1 >= argc ) ac_error("-fps switch requires a parameter\n"); + Default_fps = atoi(argv[i+1]); + i++; + + } else if ( !stricmp(argv[i], "-v" ) ) { + printf("AC version: %.2f\n", float(ANIM_VERSION)); + + } else if ( !stricmp(argv[i], "-i" ) ) { + if ( i+1 >= argc ) ac_error("-i switch requires filename of ani\n"); + anim_display_info(argv[i+1]); + exit(0); + + } else if ( !stricmp(argv[i], "-x" ) ) { + if ( i+1 >= argc ) ac_error("-x switch requires filename of ani\n"); + anim_write_frames_out(argv[i+1]); + exit(0); + + } else { + start_convert_with(argv[i]); + } + + } + + return 0; +} + +void ac_error(char *msg) +{ + fprintf(stderr, msg); + exit(1); +} + + +void start_convert_with(char* filename) +{ + char *extension; + int rc; + + if (strlen(filename) < 4 || filename[strlen(filename) - 4] != '.') + ac_error("Extension must be specified for file to convert"); + + extension = filename + strlen(filename) - 3; + if (!stricmp(extension, "avi")) { + if ( key_frame_rate > 0 ) + key_frame_rate--; + rc = convert_avi_to_anim(filename); + if (rc) { + sprintf(buffer,"Could not convert %s to ani format\n", filename); + ac_error(buffer); + } + } + else if (!stricmp(extension, "pcx")) { + rc = convert_frames_to_anim(filename); + if (rc) { + sprintf(buffer,"Could not convert %s to ani format\n", filename); + ac_error(buffer); + } + } + else + ac_error("Type of file to convert is not supported"); +} \ No newline at end of file diff --git a/src/ac/ac_stubs.cpp b/src/ac/ac_stubs.cpp new file mode 100644 index 0000000..0e34cd2 --- /dev/null +++ b/src/ac/ac_stubs.cpp @@ -0,0 +1,135 @@ +/* + * $Logfile: /Freespace2/code/ac/ac_stubs.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Stubs to allow compiling of AC project + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 7 6/21/99 10:24a Andsager + * Fix stubs. + * + * 6 3/19/99 9:52a Dave + * Checkin to repair massive source safe crash. Also added support for + * pof-style nebulae, and some new weapons code. + * + * 5 2/17/99 2:17p Dave + * a few new stubs + * + * 4 1/08/99 2:08p Dave + * Fixed software mode for pofview. + * + * 3 1/06/99 2:24p Dave + * Stub fixes. + * + * 2 10/23/98 6:03p Dave + * + * 1 10/23/98 5:04p Dave + * + * 1 10/23/98 4:32p Dave + * + * 6 6/23/98 2:52p Hoffoss + * Changed code so AC compiles once again. + * + * 5 7/20/97 6:59p Lawrance + * added new -i and -x switches + * + * 4 5/19/97 3:21p Lawrance + * add fps parm, version num to anim header + * + * 3 4/18/97 9:20a Lawrance + * added os_force_windowed() stub + * + * 2 2/19/97 9:26p Lawrance + * console version of converter working + * + * $NoKeywords: $ + */ + +#include "pstypes.h" + +struct bitmap; + +void gr_palette_load(unsigned char*) +{ +} + +void os_force_windowed() +{ +} + +void gr_set_palette(char *, unsigned char *) +{ +} + +int tga_read_bitmap( char *, bitmap *) { return 0; }; +int tga_read_header(char *, bitmap *, unsigned char *) { return 0; } +long fixdiv(long, long) { return 0; } +int game_cd_changed() { return 0; } +int Font1; +void freespace_menu_background() {} +int Fred_running; +int Pofview_running = 0; +char *Game_CDROM_dir; +void game_stop_looped_sounds() {} +void game_enter_state(int, int){} +void game_leave_state(int, int){} +void game_do_state(int){} +int set_cdrom_path(int){return 0;} +int find_freespace_cd(char*){return 0;} +void game_flush(){} +void game_process_event(int, int); +int Game_skill_level; +float flFrametime; +int game_check_key(){return 0;} +void game_stop_time(){} +void game_do_state_common(int, int){} +void game_start_time(){} +void game_set_frametime(int){} +int Test_begin; +long Game_time_compression; +int Framerate_delay; +int game_poll() {return 0;} +char *Game_current_mission_filename; +void game_process_event(int, int){} +void game_increase_skill_level(){} +int Warpout_forced; +float Warpout_time; +int game_start_mission(){return 0;} +void game_level_close(){} +void game_whack_apply(float, float){} +void game_flash(float, float, float){} +int game_do_cd_check(char*){return 0;} +struct fs_builtin_mission *game_find_builtin_mission(char*){return 0;} +int Game_do_state_should_skip; +int Show_target_weapons; +int Show_target_debug_info; +int Game_subspace_effect; +void game_load_palette(){} +void game_format_time(long, char*){} +int game_get_default_skill_level(){return 0;} +float Freespace_gamma; +void get_version_string(char*){} +int Sun_drew; +int Interface_framerate; +void game_set_view_clip(){} +float Viewer_zoom; +int Game_weapons_tbl_valid; +int Game_ships_tbl_valid; +vector Camera_pos; +vector Dead_player_last_vel; +int Debug_octant; +void game_shudder_apply(int, float){} +int game_hacked_data() {return 0;} +int game_single_step; +int last_single_step; +int Nebedit_running; +void game_tst_mark(struct object*, struct ship*){} +int game_do_cd_mission_check(char*){return 0;} +int Player_multi_died_check; +int tst; \ No newline at end of file diff --git a/src/ac/convert.cpp b/src/ac/convert.cpp new file mode 100644 index 0000000..c0ec3f6 --- /dev/null +++ b/src/ac/convert.cpp @@ -0,0 +1,782 @@ +/* + * $Logfile: /Freespace2/code/AC/convert.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for the conversion of standard animation files to our own ANIM format. + * This is where all the real code for this application is located really. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 2 10/23/98 6:03p Dave + * + * 1 10/23/98 5:34p Dave + * + * 3 10/22/98 6:14p Dave + * Optimized some #includes in Anim folder. Put in the beginnings of + * parse/localization support for externalized strings and tstrings.tbl + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 12 6/23/98 2:52p Hoffoss + * Changed code so AC compiles once again. + * + * 11 7/20/97 6:57p Lawrance + * supporting new RLE format + * + * 10 6/25/97 11:57a Lawrance + * forgot linefeed + * + * 9 6/25/97 11:49a Lawrance + * don't assert if PCX read fails, print warning + * + * 8 5/21/97 2:26p Lawrance + * bug fix: not using correct header size + * + * 7 5/21/97 11:06a Lawrance + * enabling a user-defined transparent value + * + * 6 5/19/97 3:21p Lawrance + * add fps parm, version num to anim header + * + * 5 2/25/97 5:18p Lawrance + * force_key_frame now numbering from 1 for PCXs as well as AVIs + * + * 4 2/20/97 1:47p Lawrance + * adding dots when making frames + * + * 3 2/20/97 10:30a Hoffoss + * Added in support for forcing a key frame. + * + * 2 2/19/97 9:26p Lawrance + * added force_key_frame global + * + * 1 2/19/97 7:14p Lawrance + * + * 14 2/19/97 4:21p Lawrance + * took out unnecessary #include BmpMan.h + * + * 13 2/19/97 3:59p Lawrance + * using pcxutils to load bitmaps, not bmpman + * + * 12 2/17/97 2:59p Lawrance + * integrating into game + * + * 11 2/17/97 3:02p Hoffoss + * Added headers to files, and implemented key frame dialog stuff. + * + * 10 2/14/97 10:38p Lawrance + * fixing bugs + * + * 9 2/14/97 9:47p Hoffoss + * fixed bugs. + * + * 8 2/14/97 8:44p Lawrance + * fixed bug with saving header + * + * 7 2/14/97 8:04p Hoffoss + * Fixed bug. + * + * 6 2/14/97 7:44p Lawrance + * fixed some bugs in loading an AVI + * + * 5 2/14/97 7:33p Lawrance + * added convert_avi_to_anim() function + * + * 4 2/14/97 5:38p Hoffoss + * Changes to get AnimCoverter project to compile and link. + * + * 3 2/14/97 3:28p Hoffoss + * Wrote functions to save an anim file. + * + * 2 2/13/97 5:55p Lawrance + * reading AVI / decompressing AVI functions in + * + * $NoKeywords: $ + */ + +#pragma warning(disable: 4201) + +#include +#include /* Video For Windows header file */ +#include "pstypes.h" +#include "convert.h" +#include "pcxutils.h" +#include "animplay.h" +#include "packunpack.h" + +#define AVI_STREAM_F_USED ( 1 << 0 ) + +typedef struct AVI_STREAM_TYPE { + PAVISTREAM pstream; + PAVIFILE pfile; + int num_frames; + int current_frame; + int w,h,bpp; + int min_compressed_buffer_size; + ubyte palette[768]; + char filename[255]; + //ubyte pal_translation[256]; // palette translation look-up table + int flags; +} AVI_STREAM_TYPE; + +// Internal function prototypes +int AVI_stream_open(char* filename); +void AVI_stream_close(); +int AVI_stream_get_frame(ubyte* buffer, int frame_number); +void AVI_decompress_RLE8(ubyte* src, ubyte* dest, int w, int h); + +// Global to file +static AVI_STREAM_TYPE AVI_stream; +static int AVI_stream_inited = 0; + +// Globals +char *anim_save_filename; +ubyte *cur_frame, *last_frame; +ubyte anim_buffer[ANIM_BUFFER_MAX]; +int key_frame_rate; +int force_key_frame; +int total_key_frames; +int anim_offset; +int cur_frame_num; +int Default_fps; +int Use_custom_xparent_color; +rgb_triple Xparent_color; + +int Compression_type; // what kind of RLE compression is going to be used +int Key_frame_compression, Regular_frame_compression; + +key_frame *first_frame; +FILE *anim_fp = NULL; +anim Anim; + +// AVI_stream_init() is called only to clear USED flag of the AVI_stream structure +// and reset the current frame. +// +// This does not need to be called explicity, since it will be called by AVI_stream_open() +// anyways. +// +void AVI_stream_init() +{ + AVI_stream.flags &= ~AVI_STREAM_F_USED; + AVI_stream.current_frame = 0; + AVI_stream_inited = 1; + + AVIFileInit(); // opens AVIFile library +} + +// AVI_stream_open() will open the AVI file and prepare it for reading, but will not +// store any of the frame data. +// +// returns: 0 ==> success +// !0 ==> could not open the AVI stream +// +// The filename is expected to be an absolute pathname (or file in the current working directory) +// +int AVI_stream_open(char* filename) +{ + if ( !AVI_stream_inited ) + AVI_stream_init(); + + int hr; + PAVIFILE pfile; + PAVISTREAM pstream; + AVISTREAMINFO avi_stream_info; + + Assert( !(AVI_stream.flags & AVI_STREAM_F_USED) ); + + // Open the AVI file + hr = AVIFileOpen(&pfile, filename, OF_SHARE_DENY_WRITE, 0); + if (hr != 0){ +// nprintf(("Warning", "AVI ==> Unable to open %s", filename)); + return -1; + } + + strcpy(AVI_stream.filename, filename); + + // Get a handle to the video stream within the AVI file + hr = AVIFileGetStream(pfile, &pstream, streamtypeVIDEO, 0); + if (hr != 0){ + //nprintf(("Warning", "AVI ==> Unable to open video stream in %s", filename)); + return -1; + } + + // Store the pointer to stream, since we'll need it later to read from disk + AVI_stream.pstream = pstream; + AVI_stream.pfile = pfile; + + // Get information on the stream + hr = AVIStreamInfo( pstream, &avi_stream_info, sizeof(AVISTREAMINFO) ); + if (hr != 0){ + //nprintf(("Warning", "AVI ==> Unable to retreive stream info in %s", filename)); + return -1; + } + + + int buffer_size; + + int start_sample = AVIStreamStart(pstream); + Assert( start_sample == 0 ); + + int end_sample = AVIStreamEnd(pstream); + Assert( end_sample >= start_sample ); + + // store the number of frames in the AVI_info[] structure + AVI_stream.num_frames = end_sample; // start sample must be 0 + Assert(AVI_stream.num_frames == AVIStreamLength(pstream) ); + + + // Get information on the stream + hr = AVIStreamInfo( pstream, &avi_stream_info, sizeof(AVISTREAMINFO) ); + if (hr != 0){ + //nprintf(("Warning", "AVI ==> Unable to retreive stream info in %s", filename)); + return -1; + } + + buffer_size = avi_stream_info.dwSuggestedBufferSize; + Assert( buffer_size > 0 ); + AVI_stream.min_compressed_buffer_size = buffer_size; + + // determine the format of the AVI image data + ubyte* format_buffer; + long format_buffer_size; + BITMAPINFO* bitmap_info; + + hr = AVIStreamFormatSize(pstream, 0, &format_buffer_size); + Assert( format_buffer_size > 0 ); + + format_buffer = (ubyte*) malloc(format_buffer_size); + Assert(format_buffer != NULL); // format_buffer is free'ed when AVI is free'ed, since memory is used by b_info member in AVI_info[] structure + + hr = AVIStreamReadFormat(pstream, 0, format_buffer, &format_buffer_size); + bitmap_info = (BITMAPINFO*)format_buffer; + + + switch ( bitmap_info->bmiHeader.biCompression ) { + case BI_RLE8: + break; + + default: + Assert(0); + break; + } + + AVI_stream.w = bitmap_info->bmiHeader.biWidth; + AVI_stream.h = bitmap_info->bmiHeader.biHeight; + AVI_stream.bpp = bitmap_info->bmiHeader.biBitCount; + + // create the palette translation look-up table + // + // Transparency: If the palette color is full green, then treat as transparent + // + RGBQUAD* pal; + pal = (RGBQUAD*)(bitmap_info->bmiColors); + + // Store the palette in the AVI stream structure + for ( int i = 0; i < 256; i++ ) { + AVI_stream.palette[i*3] = pal[i].rgbRed; + AVI_stream.palette[i*3+1] = pal[i].rgbGreen; + AVI_stream.palette[i*3+2] = pal[i].rgbBlue; + } + + + // memcpy(AVI_stream.palette, pal, 256*3); + +/* + int transparent_found = 0; + for ( i = 0; i < 256; i++ ) { + + //nprintf(("AVI", "AVI ==> R: %d G: %d B: %d\n", pal[i].rgbRed, pal[i].rgbGreen, pal[i].rgbBlue)); + if ( pal[i].rgbRed < 5 && pal[i].rgbGreen > 250 && pal[i].rgbBlue < 5 ) { + avi_stream->pal_translation[i] = TRANSPARENT_INDEX; + break; // found transparent, continue in j for loop, since don't need check any more + } + else + avi_stream->pal_translation[i] = palette_find( pal[i].rgbRed, pal[i].rgbGreen, pal[i].rgbBlue ); + } + + for ( j = i+1; j < 256; j++ ) { + avi_stream->pal_translation[j] = palette_find( pal[j].rgbRed, pal[j].rgbGreen, pal[j].rgbBlue ); + } +*/ + + free(format_buffer); + + // set the flag to used, so to make sure we only process one AVI stream at a time + AVI_stream.flags |= AVI_STREAM_F_USED; + + + return 0; +} + + +// AVI_stream_close() should be called when you are finished reading all the frames of an AVI +// +void AVI_stream_close() +{ +// Assert( AVI_stream.flags & AVI_STREAM_F_USED); + + AVIStreamRelease(AVI_stream.pstream); // closes the video stream + AVIFileRelease(AVI_stream.pfile); // closes the file + AVI_stream.flags &= ~AVI_STREAM_F_USED; // clear the used flag + + AVIFileExit(); // releases AVIFile library + AVI_stream_inited = 0; + +} + + + + +// AVI_stream_get_next_frame() will take the next RLE'd AVI frame and return the +// uncompressed data in the buffer pointer supplied as a parameter. The caller is +// responsible for allocating the memory before-hand (the memory required is easily +// calculated by looking at the w and h members in AVI_stream). +// +// returns: 0 ==> success +// !0 ==> error +// +int AVI_stream_get_frame(ubyte* buffer, int frame_number) +{ + if ( frame_number > AVI_stream.num_frames ) { + buffer = NULL; + return -1; + } + + Assert( (frame_number - 1) >= 0 ); + + ubyte* compressed_frame = (ubyte*)malloc(AVI_stream.min_compressed_buffer_size); + Assert( compressed_frame != NULL ); + + long num_bytes_used; + long num_samples_used; + + AVIStreamRead( AVI_stream.pstream, frame_number-1, 1, compressed_frame, AVI_stream.min_compressed_buffer_size, &num_bytes_used, &num_samples_used); + Assert(num_samples_used == 1); + + AVI_decompress_RLE8(compressed_frame, buffer, AVI_stream.w, AVI_stream.h); + + free( compressed_frame ); + return 0; + +} + + + +// ------------------------------------------------------------------------------------------------- +// AVI_decompress_RLE8() will decompress the data pointed to by src, and store in dest. +// +// NOTE: 1. memory for dest must be already allocated before calling function +// + +void AVI_decompress_RLE8(ubyte* src, ubyte* dest, int w, int h) +{ + int src_index = 0; + int dest_index = 0; + int i; + + Assert( src != NULL); + Assert( dest != NULL); + Assert( w > 0 ); + Assert( h > 0 ); + + ubyte count; + ubyte run; + ubyte control_code; + + int size_src = w * h + 1; + + int scan_line = h-1; + int height_offset = scan_line * w; + + while ( src_index < size_src ) { + + count = src[src_index]; + + if ( count == 0 ) { // control code follows + src_index++; + control_code = src[src_index]; + if ( control_code == 1 ) { + src_index++; +// nprintf(("AVI","AVI ==> Reached end of compressed image\n")); + break; + } + else if ( control_code == 0 ) { + src_index++; + scan_line--; + height_offset = scan_line * w; // only need to calc once per scanline + dest_index = 0; + //nprintf(("AVI","AVI ==> Reached end of line in compressed image\n")); + } + else if ( control_code == 2 ) { + Assert(0); + } + else { + // in absolute mode + src_index++; + for ( i = 0; i < control_code; i++ ) { + + dest[height_offset + dest_index] = src[src_index]; + dest_index++; + src_index++; + } + // run must end on a word boundry + if ( control_code & 1 ) + src_index++; + } + } + else { + src_index++; + run = src[src_index]; + // nprintf(("AVI","AVI ==> Got %d pixel run of %d\n", src[src_index], count)); + memset( &dest[height_offset + dest_index], run, count ); + dest_index += count; + src_index++; + } + } // end while + +} + +int save_anim_header() +{ + int i, new_format_id = 0; + + Assert(anim_fp); + fclose(anim_fp); + anim_fp = fopen(anim_save_filename, "r+b"); + + if (!fwrite(&new_format_id, 2, 1, anim_fp)) + return -1; + if (!fwrite(&Anim.version, 2, 1, anim_fp)) + return -1; + if (!fwrite(&Anim.fps, 2, 1, anim_fp)) + return -1; + if (!fwrite(&Anim.xparent_r, 1, 1, anim_fp)) + return -1; + if (!fwrite(&Anim.xparent_g, 1, 1, anim_fp)) + return -1; + if (!fwrite(&Anim.xparent_b, 1, 1, anim_fp)) + return -1; + if (!fwrite(&Anim.width, 2, 1, anim_fp)) + return -1; + if (!fwrite(&Anim.height, 2, 1, anim_fp)) + return -1; + if (!fwrite(&Anim.total_frames, 2, 1, anim_fp)) + return -1; + if (!fwrite(&Anim.packer_code, 1, 1, anim_fp)) + return -1; + if (fwrite(&Anim.palette, 3, 256, anim_fp) != 256) + return -1; + if (!fwrite(&total_key_frames, 2, 1, anim_fp)) + return -1; + + for (i=0; i= 0) + count++; + + if (count) + Anim.keys = (key_frame *) malloc(count * sizeof(key_frame)); + + count = 0; + frame = last_frame = 1; + while (frame <= total_frames) { + if ((force_key_frame > last_frame) && (force_key_frame < frame)) + Anim.keys[count++].frame_num = force_key_frame; + + Anim.keys[count++].frame_num = frame; + frame += rate; + } + + if (force_key_frame > last_frame) + Anim.keys[count++].frame_num = force_key_frame; + + Anim.num_keys = count; + return count; // number of key frames +} + +int anim_save_init(char *file, int width, int height, int frames) +{ + Assert(file); + anim_save_filename = file; + anim_fp = fopen(file, "wb"); + if (!anim_fp) + return -1; + + Anim.version = ANIM_VERSION; + Anim.fps = Default_fps; + Anim.width = width; + Anim.height = height; + Anim.packer_code = PACKER_CODE; + Anim.xparent_r = Xparent_color.r; + Anim.xparent_g = Xparent_color.g; + Anim.xparent_b = Xparent_color.b; + Anim.total_frames = frames; + anim_offset = 0; + cur_frame_num = 0; + total_key_frames = allocate_key_frames(frames); + fseek(anim_fp, ANIM_HEADER_SIZE + total_key_frames * 6, SEEK_SET); + + switch ( Compression_type ) { + case CUSTOM_DELTA_RLE: + Key_frame_compression = PACKING_METHOD_RLE_KEY; + Regular_frame_compression = PACKING_METHOD_RLE; + break; + + case STD_DELTA_RLE: + Key_frame_compression = PACKING_METHOD_STD_RLE_KEY; + Regular_frame_compression = PACKING_METHOD_STD_RLE; + break; + + default: + Int3(); + return -1; + break; + } // end switch + + return 0; +} + +int anim_save_frame() +{ + ubyte *temp; + int i, size; + key_frame *keyp = NULL; + + Assert(anim_fp); + cur_frame_num++; + Assert(cur_frame_num <= Anim.total_frames); + + for (i=0; ioffset = anim_offset; + size = pack_key_frame(cur_frame, anim_buffer, Anim.width * Anim.height, ANIM_BUFFER_MAX, Key_frame_compression); + + } else { + fprintf(stdout, "."); + fflush(stdout); + size = pack_frame(cur_frame, last_frame, anim_buffer, Anim.width * Anim.height, ANIM_BUFFER_MAX, Regular_frame_compression); + } + + if (size < 0) + return -1; + + if ((int) fwrite(anim_buffer, 1, size, anim_fp) != size) + return -1; + + anim_offset += size; + temp = cur_frame; + cur_frame = last_frame; + last_frame = temp; + return 0; +} + + +// converts an avi file to an anim file +// +// returns: 0 ==> success +// !0 ==> failure +// +int convert_avi_to_anim(char* filename) +{ + char ani_filename[255]; + int ret = 1; + int rc, i, xparent_pal_index; + int avi_stream_opened = 0; + + rc = AVI_stream_open(filename); + if ( rc != 0 ) { + // could not open the AVI stream + goto Finish; + } + avi_stream_opened = 1; + + Assert(AVI_stream.bpp == 8); + cur_frame = (ubyte*) malloc(AVI_stream.w * AVI_stream.h); + last_frame = (ubyte*) malloc(AVI_stream.w * AVI_stream.h); + Assert(cur_frame && last_frame); + + strcpy(ani_filename, AVI_stream.filename); + strcpy(ani_filename + strlen(ani_filename) - 3, "ani"); + + memcpy(Anim.palette, AVI_stream.palette, 768); + + if (Use_custom_xparent_color) { + // Need to look at pixel in top-left + rc = AVI_stream_get_frame(cur_frame, 1); + xparent_pal_index = cur_frame[0]; + Xparent_color.r = Anim.palette[xparent_pal_index * 3]; + Xparent_color.g = Anim.palette[xparent_pal_index * 3 + 1]; + Xparent_color.b = Anim.palette[xparent_pal_index * 3 + 2]; + + } else { + Xparent_color.r = 0; + Xparent_color.g = 255; + Xparent_color.b = 0; + } + + rc = anim_save_init(ani_filename, AVI_stream.w, AVI_stream.h, AVI_stream.num_frames); + if (rc == -1) + goto Finish; + + for ( i=1; i <= AVI_stream.num_frames; i++ ) { + // get uncompressed frame from the AVI + rc = AVI_stream_get_frame(cur_frame, i); + if ( rc != 0 ) + goto Finish; + + // pass to the anim compression + rc = anim_save_frame(); + if ( rc != 0 ) + goto Finish; + } + + rc = save_anim_header(); + if ( rc != 0 ) + goto Finish; + + ret = 0; + + Finish: + // done with the AVI.. close the stream + if ( avi_stream_opened ) + AVI_stream_close(); + + if ( anim_fp ) + fclose(anim_fp); + + free(cur_frame); + free(last_frame); + fprintf(stdout,"\n"); + fflush(stdout); + return ret; +} + +int convert_frames_to_anim(char *filename) +{ + int first_frame, frame, pos, width, height, xparent_pal_index, r = -1; + char ani_filename[255], name[255], temp[8]; + int rc; + FILE *fp; + + Assert(strlen(filename) < 254); + strcpy(name, filename); + strcpy(ani_filename, filename); + strcpy(ani_filename + strlen(ani_filename) - 8, ".ani"); + pos = strlen(name) - 8; + frame = first_frame = atoi(&name[pos]); + force_key_frame -= frame; + + // first file + fp = fopen(name, "rb"); + if(fp != NULL){ + do { + fclose(fp); + frame++; + sprintf(temp, "%04.4d", frame); + strncpy(&name[pos], temp, 4); + + // next file + fp = fopen(name, "rb"); + } while(fp != NULL); + } + + rc = pcx_read_header(filename, &width, &height, NULL); + if (rc != PCX_ERROR_NONE) { + fprintf(stdout, "An error reading the PCX file %s. It may not exist.\n", filename); + return -1; + } + + cur_frame = (ubyte *) malloc(width * height); + last_frame = (ubyte *) malloc(width * height); + + rc = pcx_read_bitmap_8bpp(filename, cur_frame, Anim.palette); + if (rc != PCX_ERROR_NONE) { + fprintf(stdout, "An error reading the PCX file %s. It may not exist.\n", filename); + return -1; + } + + if (Use_custom_xparent_color) { + // Need to look at pixel in top-left + xparent_pal_index = ((ubyte *) cur_frame)[0]; + Xparent_color.r = Anim.palette[xparent_pal_index * 3]; + Xparent_color.g = Anim.palette[xparent_pal_index * 3 + 1]; + Xparent_color.b = Anim.palette[xparent_pal_index * 3 + 2]; + + } else { + Xparent_color.r = 0; + Xparent_color.g = 255; + Xparent_color.b = 0; + } + + if (anim_save_init(ani_filename, width, height, frame - first_frame)) + goto done; + + while (first_frame < frame) { + sprintf(temp, "%04.4d", first_frame); + strncpy(&name[pos], temp, 4); + rc = pcx_read_bitmap_8bpp(name, cur_frame, Anim.palette); + if (rc != PCX_ERROR_NONE) + goto done; + + if (anim_save_frame()) + goto done; + + first_frame++; + } + + if (save_anim_header()) + goto done; + + r = 0; + +done: + fclose(anim_fp); + free(cur_frame); + free(last_frame); + fprintf(stdout, "\n"); + fflush(stdout); + return r; +} + diff --git a/src/anim/animplay.cpp b/src/anim/animplay.cpp new file mode 100644 index 0000000..6e04eba --- /dev/null +++ b/src/anim/animplay.cpp @@ -0,0 +1,1306 @@ +/* + * $Logfile: /Freespace2/code/Anim/AnimPlay.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for playing back anim files + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 9 9/13/99 11:26p Andsager + * Add debug code to check for poorly sized anis + * + * 8 8/26/99 9:45a Dave + * First pass at easter eggs and cheats. + * + * 7 7/16/99 1:49p Dave + * 8 bit aabitmaps. yay. + * + * 6 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 5 6/10/99 10:34a Dave + * Removed unnecessary assert. + * + * 4 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 3 10/22/98 6:14p Dave + * Optimized some #includes in Anim folder. Put in the beginnings of + * parse/localization support for externalized strings and tstrings.tbl + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 38 6/23/98 4:18p Hoffoss + * Fixed some bugs with AC release build. + * + * 37 5/18/98 5:59p Hoffoss + * Made command briefing advanced now once the speech stops and animation + * has fully played once, whichever is longer. + * + * 36 5/14/98 6:29p Hoffoss + * Fixed some warnings a release rebuild all turned up. + * + * 35 5/07/98 3:11a Lawrance + * Implement custom streaming code + * + * 34 4/27/98 3:36p Dave + * + * 33 3/25/98 8:43p Hoffoss + * Changed anim_play() to not be so damn complex when you try and call it. + * + * 32 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 31 1/19/98 11:37p Lawrance + * Fixing Optimization build warnings + * + * 30 1/19/98 3:09p Hoffoss + * Only free an anim instance if it is actually playing. + * + * 29 1/14/98 6:43p Lawrance + * Add ref_count to anim struct, so we don't free multiple times + * + * 28 12/30/97 6:44p John + * Made g3_Draw_bitmap functions account for aspect of bitmap. + * + * 27 12/27/97 2:35p John + * Restructed some code so that if the memory-mapped file open fails, it + * will still read it the old-fashioned, non-memory mapped way. + * + * 26 12/24/97 9:10p Lawrance + * take out some debugging statements + * + * 25 12/24/97 8:57p Lawrance + * Added anim_ignore_next_frametime() + * + * 24 12/07/97 2:06p Dave + * Removed some debug frame-checking code. + * + * 23 12/06/97 2:55p Dave + * + * 22 11/29/97 2:05p John + * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2, + * like they used to incorrectly assume. Added code to model to read in + * thruster radius's. + * + * 21 11/20/97 5:36p Dave + * Hooked in a bunch of main hall changes (including sound). Made it + * possible to reposition (rewind/ffwd) + * sound buffer pointers. Fixed animation direction change framerate + * problem. + * + * 20 11/20/97 4:33p Sandeep + * ALAN: ensure instance->paused gets initialized + * + * 19 11/20/97 4:12p Lawrance + * when paused, don't increment time_elapsed + * + * 18 11/19/97 8:28p Dave + * Hooked in Main Hall screen. Put in Anim support for ping ponging + * animations as well as general reversal of anim direction. + * + * 17 9/11/97 4:17p Allender + * use _MAX_PATH instead of MAX_FILENAME_LENGTH for anim_play since it is + * called from MoviePlayer with full path + * + * 16 9/11/97 10:49a Lawrance + * improve anim_show_next_frame() + * + * 15 9/10/97 4:59p Lawrance + * improve comments to anim_play() function + * + * 14 9/03/97 4:19p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 13 8/30/97 2:11p Lawrance + * allow animations to loop + * + * 12 8/25/97 11:13p Lawrance + * support framerate independent playback with the option of now advancing + * more than one frame at a time + * + * 11 8/22/97 8:20a Lawrance + * display scrolling text properly, with indicator that text exitsts to be + * scrolled + * + * 10 8/21/97 5:11p Lawrance + * frame numbering for ANI's now is from 0 -> total_frames-1. + * + * 9 8/19/97 9:30a Lawrance + * print out reason if file doesn't open right + * + * 8 7/28/97 10:42p Lawrance + * re-did interface to unpack_frame() to make more general + * + * 7 7/21/97 11:41a Lawrance + * make playback time of .ani files keyed of frametime + * + * 6 7/20/97 6:57p Lawrance + * supporting new RLE format + * + * 5 7/11/97 11:54a John + * added rotated 3d bitmaps. + * + * 4 6/27/97 4:36p Lawrance + * update pal translation table when gr_screen.signature changes + * + * 3 6/26/97 3:00p Lawrance + * fix bug with playing 3d anims + * + * 2 6/26/97 12:12a Lawrance + * supporting anti-aliased bitmap animations + * + * 1 6/23/97 5:09p Lawrance + * + * 24 6/03/97 5:53p Lawrance + * don't unload bitmap after bm_create + * + * $NoKeywords: $ + */ + +#include "animplay.h" +#include "linklist.h" +#include "timer.h" +#include "bmpman.h" +#include "2d.h" +#include "3d.h" +#include "grinternal.h" +#include "pcxutils.h" +#include "packunpack.h" +#include "cfile.h" + +static color Color_xparent; + +anim *first_anim = NULL; +anim_instance anim_free_list; +anim_instance anim_render_list; + +#define MAX_ANIM_INSTANCES 25 +anim_instance anim_render_instance[MAX_ANIM_INSTANCES]; + +int Anim_paused; // global variable to pause the playing back of anims +int Anim_inited = FALSE; + +fix t1,t2; + +int Anim_ignore_frametime=0; // flag used to ignore frametime... useful when need to avoid saturated frametimes + +// ------------------------------------------------------------------------------------------------- +// anim_init() will queue all the anim_render_instance[] elements onto the anim_free_list +// +void anim_init() +{ + int i; + + if ( Anim_inited == TRUE ) + return; + + list_init( &anim_free_list ); + list_init( &anim_render_list ); + + // Link all anim render slots into the free list + for (i=1; i < MAX_ANIM_INSTANCES; i++) { + list_append(&anim_free_list, &anim_render_instance[i]); + } + + Anim_paused = 0; + Anim_inited = TRUE; +} + +// ------------------------------------------------------------------------------------------------- +// anim_render_all() will display the frames for the currently playing anims +// +void anim_render_all(int screen_id, float frametime) +{ + anim_instance* A; + anim_instance* temp; + + A = GET_FIRST(&anim_render_list); + while( A !=END_OF_LIST(&anim_render_list) ) { + temp = GET_NEXT(A); + if ( A->screen_id == screen_id ) { + if ( Anim_ignore_frametime ) { + frametime = 0.0f; + Anim_ignore_frametime=0; + } + if ( anim_show_next_frame(A, frametime) == -1 ) { + A->data = NULL; + anim_release_render_instance(A); + } + } + A = temp; + } +} + +// ------------------------------------------------------------------------------------------------- +// anim_render_one() will display the frames for the passed animation, it will ignore animations which +// do not have the same id as the passed screen_id +// +void anim_render_one(int screen_id, anim_instance *ani, float frametime) +{ + // make sure this guy's screen id matches the passed one + if(screen_id != ani->screen_id){ + return; + } + + // otherwise render it + if ( Anim_ignore_frametime ) { + frametime = 0.0f; + Anim_ignore_frametime=0; + } + if ( anim_show_next_frame(ani, frametime) == -1 ) { + ani->data = NULL; + anim_release_render_instance(ani); + } +} + +MONITOR(NumANIPlayed); + +// Setup an anim_play_struct for passing into anim_play(). Will fill in default values, which you +// can then change before calling anim_play(). +// +void anim_play_init(anim_play_struct *aps, anim *a_info, int x, int y) +{ + aps->anim_info = a_info; + aps->x = x; + aps->y = y; + aps->start_at = 0; + aps->stop_at = a_info->total_frames - 1; + aps->screen_id = 0; + aps->world_pos = NULL; + aps->radius = 0.0f; + aps->framerate_independent = 0; + aps->color = NULL; + aps->skip_frames = 1; + aps->looped = 0; + aps->ping_pong = 0; +} + +// ------------------------------------------------------------------------------------------------- +// anim_play() will add an anim instance to the anim_render_list. This will cause the +// anim to be played at the x,y position specified in the parameter list. +// +// input: +// +// anim_info => the compressed animation that we should make an instance from +// x => x position of animation to play at (top left corner) +// y => y position of animation to play at ( top left corner) +// start_at => frame number to start at (note: numbering is from 0->num_frames-1) +// stop_at => frame number to stop at (note: numbering is from 0->num_frames-1) +// screen_id => OPTIONAL (default value 0): screen signature so animation only plays when +// anim_render_all() called with that same signature +// world_pos => OPTIONAL (default value NULL): only give a world pos when you want to +// play the animation at a 3D location. You must specify radius when +// this is non-null. +// radius => OPTIONAL (default value 0): only needed when the animation is playing +// as a 3D animation (this is only when world_pos in not NULL). +// fi => OPTIONAL (default value 0): framerate indepentdent flag, when set TRUE +// the animation will skip frames if necessary to maintain the fps value +// associated with the animation +// color => OPTIONAL (default value NULL) address of an alpha color struct. Only +// required when the animation should be drawn with an alpha color. +// skip_frames => OPTIONAL (default value 1) should anim skip frames when doing framerate +// independent playback +// looped => OPTIONAL (default value 0) should anim play looped (ie forever) +// +// returns: +// +// pointer to instance => success +// NULL => if anim anim could not be played +// +anim_instance *anim_play(anim_play_struct *aps) +{ + Assert( aps->anim_info != NULL ); + Assert( aps->start_at >= 0 ); + Assert( aps->stop_at < aps->anim_info->total_frames ); + // Assert( aps->stop_at >= aps->start_at ); + Assert( !(aps->looped && aps->ping_pong) ); // shouldn't have these both set at once + + MONITOR_INC(NumANIPlayed, 1); + + // if (aps->ping_pong && !(aps->anim_info->flags & ANF_ALL_KEYFRAMES)); + + anim_instance *instance; + + // Find next free anim instance slot on queue + instance = GET_FIRST(&anim_free_list); + Assert( instance != &anim_free_list ); // shouldn't have the dummy element + + // remove instance from the free list + list_remove( &anim_free_list, instance ); + + // insert instance onto the end of anim_render_list + list_append( &anim_render_list, instance ); + + aps->anim_info->instance_count++; + instance->frame_num = -1; + instance->last_frame_num = -99; + instance->parent = aps->anim_info; + instance->data = aps->anim_info->data; + if ( anim_instance_is_streamed(instance) ) { + instance->file_offset = instance->parent->file_offset; + } + instance->frame = (ubyte *) malloc(instance->parent->width * instance->parent->height * 2); + Assert( instance->frame != NULL ); + instance->time_elapsed = 0.0f; + instance->stop_at = aps->stop_at; + instance->x = aps->x; + instance->y = aps->y; + instance->world_pos = aps->world_pos; + instance->radius = aps->radius; + instance->framerate_independent = aps->framerate_independent; + instance->last_bitmap = -1; + instance->stop_now = FALSE; + instance->screen_id = aps->screen_id; + instance->aa_color = aps->color; + instance->skip_frames = aps->skip_frames; + instance->looped = aps->looped; + instance->ping_pong = aps->ping_pong; + instance->direction = ANIM_DIRECT_FORWARD; + instance->paused = 0; + instance->loop_count = 0; + if ( aps->color == NULL ){ + instance->xlate_pal = 1; + } else { + instance->xlate_pal = 0; + } + + // determining the start_at frame is more complicated, since it must be a key-frame. + // Futhermore, need to subtract 1 from key-frame number, since frame number is always + // incremented the first time anim_show_next_frame() is called + + instance->start_at = aps->start_at; + + if ( aps->start_at > 0 ) { + key_frame *keyp; + int idx; + int key = 0; + int offset = 0; + int frame_num = aps->start_at; + + keyp = instance->parent->keys; + idx = 0; + while (idx < instance->parent->num_keys) { + if (key == frame_num) + break; + + key = keyp[idx].frame_num - 1; + offset = keyp[idx].offset; + + idx++; + } + /*while (keyp) { + if (( (keyp->frame_num-1) <= frame_num) && ( (keyp->frame_num-1) > key)) { // find closest key + key = keyp->frame_num-1; + offset = keyp->offset; + if ( key == frame_num ) + break; + } + + keyp = keyp->next; + }*/ + + if (key > instance->frame_num) { // best key is closer than current position + instance->frame_num = key; + if ( anim_instance_is_streamed(instance) ) { + instance->file_offset = instance->parent->file_offset + offset; + } else { + instance->data = instance->parent->data + offset; + } + + } + + instance->frame_num--; // required + } + + return instance; +} + +// ----------------------------------------------------------------------------- +// anim_show_next_frame() +// +// This function is called to blit the next frame of an anim instance to the +// screen. This is normally called by the anim_render_all() function. +// +// input: instance => pointer to animation instance +// frametime => time elapsed since last call, in seconds +// +int anim_show_next_frame(anim_instance *instance, float frametime) +{ + int bitmap_id, bitmap_flags=0, new_frame_num, frame_diff=0, i, n_frames=0,frame_save; + float percent_through, decompress_time, render_time, time; + vertex image_vertex; + int aabitmap = 0; + int bpp = 16; + + Assert( instance != NULL ); + + instance->time_elapsed += frametime; + + // Advance to the next frame, if we determine enough time has elapsed. + if(instance->direction == ANIM_DIRECT_FORWARD) + n_frames = instance->stop_at - instance->start_at + 1; + else if(instance->direction == ANIM_DIRECT_REVERSE) + n_frames = instance->start_at - instance->stop_at + 1; + time = n_frames / i2fl(instance->parent->fps); + + percent_through = instance->time_elapsed / time; + + if(instance->direction == ANIM_DIRECT_FORWARD) + new_frame_num = instance->start_at - 1 + fl2i(percent_through * n_frames + 0.5f); + else + new_frame_num = instance->start_at - 1 - fl2i(percent_through * n_frames + 0.5f); + + frame_save = instance->frame_num; + + // If framerate independent, use the new_frame_num... unless instance->skip_frames is + // FALSE, then only advance a maximum of one frame (this is needed since some big animations + // should just play slower rather than taking the hit of decompressing multiple frames and + // creating an even greater slowdown + if (instance->framerate_independent) { + if(instance->direction == ANIM_DIRECT_FORWARD){ + if ( new_frame_num > instance->last_frame_num) { + if ( instance->skip_frames ) + instance->frame_num = new_frame_num; + else + instance->frame_num++; + } + } else if(instance->direction == ANIM_DIRECT_REVERSE){ + if( new_frame_num < instance->last_frame_num) { + if ( instance->skip_frames ) + instance->frame_num = new_frame_num; + else + instance->frame_num--; + } + } + } + else { + if(instance->direction == ANIM_DIRECT_FORWARD){ + if ( new_frame_num > instance->last_frame_num) { + instance->frame_num++; + } + } else if(instance->direction == ANIM_DIRECT_REVERSE){ + if ( new_frame_num < instance->last_frame_num) { + instance->frame_num--; + } + } + } + + if(instance->direction == ANIM_DIRECT_FORWARD){ + if ( instance->frame_num < instance->start_at ) { + instance->frame_num = instance->start_at; + } + } else if(instance->direction == ANIM_DIRECT_REVERSE){ + if ( instance->frame_num > instance->start_at ) { + instance->frame_num = instance->start_at; + } + } + + if ( instance->stop_now == TRUE ) { + return -1; + } + + // If past the last frame, clamp to the last frame and then set the stop_now flag in the + // anim instance. The next iteration, the animation will stop. + if(instance->direction == ANIM_DIRECT_FORWARD){ + if (instance->frame_num >= instance->stop_at ) { + if (instance->looped) { // looped animations + instance->frame_num = instance->stop_at; + instance->time_elapsed = 0.0f; + } else if(instance->ping_pong) { // pingponged animations + instance->frame_num = instance->stop_at; + // instance->time_elapsed = 0.0f; + anim_reverse_direction(instance); + } else { // one-shot animations + instance->frame_num = instance->stop_at; + instance->last_frame_num = instance->frame_num; + instance->stop_now = TRUE; + } + } + } else if(instance->direction == ANIM_DIRECT_REVERSE){ + if (instance->frame_num <= instance->stop_at ) { + if (instance->looped) { // looped animations + instance->frame_num = instance->stop_at; + instance->time_elapsed = 0.0f; + } else if(instance->ping_pong) { // pingponged animations + instance->frame_num = instance->stop_at; + // instance->time_elapsed = 0.0f; + anim_reverse_direction(instance); + } else { // one-shot animations + instance->frame_num = instance->stop_at+1; + instance->last_frame_num = instance->frame_num; + instance->stop_now = TRUE; + } + } + } + + if(instance->direction == ANIM_DIRECT_FORWARD){ + if( instance->last_frame_num >= instance->start_at ) { + frame_diff = instance->frame_num - instance->last_frame_num; + } else { + frame_diff = 1; + } + } else if(instance->direction == ANIM_DIRECT_REVERSE){ + if( instance->last_frame_num <= instance->start_at ) { + frame_diff = instance->last_frame_num - instance->frame_num; + } else { + frame_diff = 1; + } + } + Assert(frame_diff >= 0); + // nprintf(("Alan","FRAME DIFF: %d\n",frame_diff)); + Assert( instance->frame_num >= 0 && instance->frame_num < instance->parent->total_frames ); + + // if the anim is paused, ignore all the above changes and still display this frame + if(instance->paused || Anim_paused){ + instance->frame_num = frame_save; + instance->time_elapsed -= frametime; + frame_diff = 0; + } + + if (instance->parent->flags & ANF_XPARENT){ + // bitmap_flags = BMP_XPARENT; + bitmap_flags = 0; + } + bpp = 16; + if(instance->aa_color != NULL){ + bitmap_flags |= BMP_AABITMAP; + aabitmap = 1; + bpp = 8; + } + + if ( frame_diff > 0 ) { + instance->last_frame_num = instance->frame_num; + + t1 = timer_get_fixed_seconds(); + for ( i = 0; i < frame_diff; i++ ) { + anim_check_for_palette_change(instance); + + // if we're playing backwards, every frame must be a keyframe and we set the data ptr here + if(instance->direction == ANIM_DIRECT_REVERSE){ + if ( anim_instance_is_streamed(instance) ) { + instance->file_offset = instance->parent->file_offset + instance->parent->keys[instance->frame_num-1].offset; + } else { + instance->data = instance->parent->data + instance->parent->keys[instance->frame_num-1].offset; + } + } + + ubyte *temp = NULL; + int temp_file_offset = 0; + + // if we're using bitmap polys + if(Gr_bitmap_poly){ + BM_SELECT_TEX_FORMAT(); + } + + if ( anim_instance_is_streamed(instance) ) { + if ( instance->xlate_pal ){ + temp_file_offset = unpack_frame_from_file(instance, instance->frame, instance->parent->width*instance->parent->height, instance->parent->palette_translation, aabitmap, bpp); + } else { + temp_file_offset = unpack_frame_from_file(instance, instance->frame, instance->parent->width*instance->parent->height, NULL, aabitmap, bpp); + } + } else { + if ( instance->xlate_pal ){ + temp = unpack_frame(instance, instance->data, instance->frame, instance->parent->width*instance->parent->height, instance->parent->palette_translation, aabitmap, bpp); + } else { + temp = unpack_frame(instance, instance->data, instance->frame, instance->parent->width*instance->parent->height, NULL, aabitmap, bpp); + } + } + + // always go back to screen format + BM_SELECT_SCREEN_FORMAT(); + + if(instance->direction == ANIM_DIRECT_FORWARD){ + if ( anim_instance_is_streamed(instance) ) { + instance->file_offset = temp_file_offset; + } else { + instance->data = temp; + } + } + } + t2 = timer_get_fixed_seconds(); + } + else { + t2=t1=0; + } + + // this only happens when the anim is being looped, we need to reset the last_frame_num + if ( (instance->time_elapsed == 0) && (instance->looped) ) { + instance->last_frame_num = -1; + instance->frame_num = -1; + instance->data = instance->parent->data; + instance->file_offset = instance->parent->file_offset; + instance->loop_count++; + } + + decompress_time = f2fl(t2-t1); + + t1 = timer_get_fixed_seconds(); + if ( frame_diff == 0 && instance->last_bitmap != -1 ) { + bitmap_id = instance->last_bitmap; + } + else { + if ( instance->last_bitmap != -1 ){ + bm_release(instance->last_bitmap); + } + bitmap_id = bm_create(16, instance->parent->width, instance->parent->height, instance->frame, bitmap_flags); + } + + if ( bitmap_id == -1 ) { + // anim has finsished playing, free the instance frame data + anim_release_render_instance(instance); + return -1; + + // NOTE: there is no need to free the instance, since it was pre-allocated as + // part of the anim_free_list + } + else { + gr_set_bitmap(bitmap_id); + + // determine x,y to display the bitmap at + if ( instance->world_pos == NULL ) { + if ( instance->aa_color == NULL ) { + gr_bitmap(instance->x, instance->y); + } + else { + gr_set_color_fast( (color*)instance->aa_color ); + gr_aabitmap(instance->x, instance->y); + } + } + else { + g3_rotate_vertex(&image_vertex,instance->world_pos); + Assert(instance->radius != 0.0f); + g3_draw_bitmap(&image_vertex, 0, instance->radius*1.5f, TMAP_FLAG_TEXTURED ); + } + + //bm_release(bitmap_id); + instance->last_bitmap = bitmap_id; + } + + t2 = timer_get_fixed_seconds(); + render_time = f2fl(t2-t1); + +// nprintf(("Alan","DECOMPRESS: %.3fms RENDER: %.3fms\n", decompress_time*1000, render_time*1000)); + + return 0; +} + +// ----------------------------------------------------------------------------- +// anim_stop_playing() +// +// Stop an anim instance that is on the anim_render_list from playing +// +int anim_stop_playing(anim_instance* instance) +{ + Assert(instance != NULL); + + if ( anim_playing(instance) ) { + anim_release_render_instance(instance); + } + return 0; +} + +// ----------------------------------------------------------------------------- +// anim_release_render_instance() +// +// Free a particular animation instance that is on the anim_render_list. Do +// not call this function to free an animation instance in general (use +// free_anim_instance() for that), only when you want to free an instance +// that is on the anim_render_list +// +void anim_release_render_instance(anim_instance* instance) +{ + Assert( instance != NULL ); + Assert(instance->frame); + free(instance->frame); + instance->frame = NULL; + instance->parent->instance_count--; + + if ( instance->last_bitmap != -1 ) { + bm_release(instance->last_bitmap); + instance->last_bitmap = -1; + } + + // remove instance from anim_render_list + list_remove( &anim_render_list, instance ); + + // insert instance into the anim_free_list + list_append( &anim_free_list, instance ); +} + +// ----------------------------------------------------------------------------- +// anim_release_all_instances() +// +// Free all anim instances that are on the anim_render_list. +// +// input: screen_id => optional parameter that lets you only free a subset +// of the anim instances. A screen_id of 0 is the default +// value, and this is used for animations that always play +// when they are placed on the aim_render_list. +// +void anim_release_all_instances(int screen_id) +{ + anim_instance* A; + anim_instance* temp; + + if ( Anim_inited == FALSE ) + return; + + A = GET_FIRST(&anim_render_list); + while( A !=END_OF_LIST(&anim_render_list) ) { + temp = GET_NEXT(A); + if ( A->screen_id == screen_id || screen_id == 0 ) { + anim_release_render_instance(A); + } + A = temp; + } +} + +// ----------------------------------------------------------------------------- +// anim_read_header() +// +// Read the header of a .ani file. Below is the format of a .ani header +// +// #bytes | description +// 2 | obsolete, kept for compatibility with old versions +// 2 | version number +// 2 | fps +// 1 | transparent red value +// 1 | transparent green value +// 1 | transparent blue value +// 2 | width +// 2 | height +// 2 | number of frames +// 2 | packer code +// 763 | palette +// 2 | number of key frames +// 2 | key frame number } repeats +// 4 | key frame offset } repeats +// 4 | compressed data length +// +void anim_read_header(anim *ptr, CFILE *fp) +{ + ptr->width = cfread_short(fp); + // If first 2 bytes are zero, this means we are using a new format, which includes + // a version, and fps values. This is only done since a version number was not included + // in the original header. + + // default + Color_xparent.red = 0; + Color_xparent.green = 255; + Color_xparent.blue = 0; + + if ( ptr->width == 0 ) { + ptr->version = cfread_short(fp); + ptr->fps = cfread_short(fp); + + // version 2 added a custom transparency color + if ( ptr->version >= 2 ) { + cfread(&Color_xparent.red, 1, 1, fp); + cfread(&Color_xparent.green, 1, 1, fp); + cfread(&Color_xparent.blue, 1, 1, fp); + } + + ptr->width = cfread_short(fp); + } + else { + ptr->version = 0; + ptr->fps = 30; + } + + ptr->height = cfread_short(fp); + +#ifndef NDEBUG + // get size of ani compared to power of 2 + int r, floor_pow; + r = ptr->height; + floor_pow = 0; + + while(r >= 2) { + r /= 2; + floor_pow++; + } + + int floor_size = (int) pow(2, floor_pow); + int diff = ptr->height - floor_size; + float waste = 100.0f * float((floor_size - diff))/(2.0f *(float)floor_size); + + if (diff != 0) { + if (ptr->height > 16) { + mprintf(("ANI %s with size %dx%d (%.1f%% wasted)\n", ptr->name, ptr->height, ptr->height, waste)); + } + } +#endif + + ptr->total_frames = cfread_short(fp); + cfread(&ptr->packer_code, 1, 1, fp); + cfread(&ptr->palette, 256, 3, fp); + ptr->num_keys = cfread_short(fp); + + // store xparent colors + ptr->xparent_r = Color_xparent.red; + ptr->xparent_g = Color_xparent.green; + ptr->xparent_b = Color_xparent.blue; + + if(ptr->total_frames == ptr->num_keys){ + ptr->flags |= ANF_ALL_KEYFRAMES; + } +} + +// ----------------------------------------------------------------------------- +// anim_load() +// +// Load an animation. This stores the compressed data, which instances +// of the animation can reference. Must be free'ed later with anim_free() +// +// input: name => filename of animation +// file_mapped => boolean, whether to use memory-mapped file or not. +// Memory-mapped files will page in the animation from disk +// as it is needed, but performance is not as good +// +// returns: pointer to anim that is loaded => sucess +// NULL => failure +// +anim *anim_load(char *real_filename, int file_mapped) +{ + anim *ptr; + CFILE *fp; + int count,idx; + char name[_MAX_PATH]; + +// file_mapped = 0; + + Assert ( real_filename != NULL ); + + strcpy( name, real_filename ); + char *p = strchr( name, '.' ); + if ( p ) { + *p = 0; + } + strcat( name, ".ani" ); + + ptr = first_anim; + while (ptr) { + if (!stricmp(name, ptr->name)) + break; + + ptr = ptr->next; + } + + if (!ptr) { + fp = cfopen(name, "rb"); + if ( !fp ) + return NULL; + + ptr = (anim *) malloc(sizeof(anim)); + Assert(ptr); + + ptr->flags = 0; + ptr->next = first_anim; + first_anim = ptr; + Assert(strlen(name) < _MAX_PATH - 1); + strcpy(ptr->name, name); + ptr->instance_count = 0; + ptr->width = 0; + ptr->height = 0; + ptr->total_frames = 0; + ptr->keys = NULL; + ptr->ref_count=0; + + anim_read_header(ptr, fp); + + if(ptr->num_keys > 0){ + ptr->keys = (key_frame*)malloc(sizeof(key_frame) * ptr->num_keys); + Assert(ptr->keys != NULL); + } + + // store how long the anim should take on playback (in seconds) + ptr->time = i2fl(ptr->total_frames)/ptr->fps; + + for(idx=0;idxnum_keys;idx++){ + ptr->keys[idx].frame_num = 0; + cfread(&ptr->keys[idx].frame_num, 2, 1, fp); + cfread(&ptr->keys[idx].offset, 4, 1, fp); + } + + /*prev_keyp = &ptr->keys; + count = ptr->num_keys; + while (count--) { + keyp = (key_frame *) malloc(sizeof(key_frame)); + keyp->next = *prev_keyp; + *prev_keyp = keyp; + prev_keyp = &keyp->next; + + keyp->frame_num = 0; + cfread(&keyp->frame_num, 2, 1, fp); + cfread(&keyp->offset, 4, 1, fp); + }*/ + cfread(&count, 4, 1, fp); // size of compressed data + + ptr->cfile_ptr = NULL; + + if ( file_mapped ) { + // Try mapping the file to memory + ptr->flags |= ANF_MEM_MAPPED; + ptr->cfile_ptr = cfopen(name, "rb", CFILE_MEMORY_MAPPED); + } + + // couldn't memory-map file... must be in a packfile, so stream manually + if ( file_mapped && !ptr->cfile_ptr ) { + ptr->flags &= ~ANF_MEM_MAPPED; + ptr->flags |= ANF_STREAMED; + ptr->cfile_ptr = cfopen(name, "rb"); + } + + ptr->cache = NULL; + + // If it opened properly as mem-mapped (or streamed) + if (ptr->cfile_ptr != NULL) { + // VERY IMPORTANT STEP + // Set the data pointer to the compressed data (which is not at the start of the + // file). Use ftell() to find out how far we've already parsed into the file + // + int offset; + offset = cftell(fp); + ptr->file_offset = offset; + if ( ptr->flags & ANF_STREAMED ) { + ptr->data = NULL; + ptr->cache_file_offset = ptr->file_offset; + ptr->cache = (ubyte*)malloc(ANI_STREAM_CACHE_SIZE+2); + Assert(ptr->cache); + cfseek(ptr->cfile_ptr, offset, CF_SEEK_SET); + cfread(ptr->cache, ANI_STREAM_CACHE_SIZE, 1, ptr->cfile_ptr); + } else { + ptr->data = (ubyte*)cf_returndata(ptr->cfile_ptr) + offset; + } + } else { + // Not a memory mapped file (or streamed) + ptr->flags &= ~ANF_MEM_MAPPED; + ptr->flags &= ~ANF_STREAMED; + ptr->data = (ubyte *) malloc(count); + ptr->file_offset = -1; + cfread(ptr->data, count, 1, fp); + } + + cfclose(fp); + + // store screen signature, so we can tell if palette changes + ptr->screen_sig = gr_screen.signature; + + anim_set_palette(ptr); + } + + ptr->ref_count++; + return ptr; +} + +// --------------------------------------------------- +// anim_free() +// +// Free an animation that was loaded with anim_load(). All instances +// referencing this animation must be free'ed or get an assert. +// +int anim_free(anim *ptr) +{ + Assert ( ptr != NULL ); + anim *list, **prev_anim; + + list = first_anim; + prev_anim = &first_anim; + while (list && (list != ptr)) { + prev_anim = &list->next; + list = list->next; + } + + if ( !list ) + return -1; + + // only free when ref_count is 0 + ptr->ref_count--; + if ( ptr->ref_count > 0 ) + return -1; + + // only free if there are no playing instances + if ( ptr->instance_count > 0 ) + return -1; + + if(ptr->keys != NULL){ + free(ptr->keys); + ptr->keys = NULL; + } + + if ( ptr->flags & (ANF_MEM_MAPPED|ANF_STREAMED) ) { + cfclose(ptr->cfile_ptr); + if ( ptr->cache ) { + free(ptr->cache); + } + } + else { + Assert(ptr->data); + free(ptr->data); + } + + *prev_anim = ptr->next; + free(ptr); + return 0; +} + + +// --------------------------------------------------------------------- +// anim_playing() +// +// Return if an anim is playing or not. +// +int anim_playing(anim_instance *ai) +{ + Assert(ai != NULL); + if ( ai->frame == NULL ) + return 0; + else + return 1; +} + + +// --------------------------------------------------------------------- +// anim_level_init() +// +// Called at the beginning of a mission to initialize any mission dependent +// anim data. +// +void anim_level_init() +{ +} + +// --------------------------------------------------------------------- +// anim_level_close() +// +// Called after the end of a mission to clean up any mission dependent +// anim data. +// +void anim_level_close() +{ + anim_release_all_instances(); +} + +// --------------------------------------------------- +// anim_write_frames_out() +// +// Write the frames of a .ani file out to disk as .pcx files. +// Use naming convention: filename0000.pcx, filename0001.pcx etc. +// +// return: 0 => success +// -1 => failed +// +int anim_write_frames_out(char *filename) +{ + anim *source_anim; + anim_instance *ai; + char root_name[256], pcxname[256]; + char buf[64]; + int i,j; + ubyte **row_data; + + strcpy(root_name, filename); + root_name[strlen(filename)-4] = 0; + + source_anim = anim_load(filename); + if ( source_anim == NULL ) + return -1; + + ai = init_anim_instance(source_anim, 16); + + row_data = (ubyte**)malloc((source_anim->height+1) * 4); + + for ( i = 0; i < source_anim->total_frames; i++ ) { + anim_get_next_raw_buffer(ai, 0, 0, 16); + strcpy(pcxname, root_name); + sprintf(buf,"%04d",i); + strcat(pcxname, buf); + + for ( j = 0; j < source_anim->height; j++ ) { + row_data[j] = &ai->frame[j*source_anim->width]; + } + + + pcx_write_bitmap( pcxname, + source_anim->width, + source_anim->height, + row_data, + source_anim->palette); + + printf("."); + + } + printf("\n"); + free(row_data); + return 0; +} + +// --------------------------------------------------- +// anim_display_info() +// +// Display information and statistics about a .ani file. +// This is called when -i switch is on when running ac.exe +// +void anim_display_info(char *real_filename) +{ + CFILE *fp; + anim A; + float percent; + int i, uncompressed, compressed, *key_frame_nums=NULL, tmp; + char filename[MAX_FILENAME_LEN]; + + strcpy( filename, real_filename ); + char *p = strchr( filename, '.' ); + if ( p ) { + *p = 0; + } + strcat( filename, ".ani" ); + + fp = cfopen(filename, "rb"); + if ( !fp ) { + printf("Fatal error opening %s", filename); + return; + } + + anim_read_header(&A, fp); + // read the keyframe frame nums and offsets + key_frame_nums = (int*)malloc(sizeof(int)*A.num_keys); + Assert(key_frame_nums != NULL); + for ( i = 0; i < A.num_keys; i++ ) { + key_frame_nums[i] = 0; + cfread(&key_frame_nums[i], 2, 1, fp); + cfread(&tmp, 4, 1, fp); +//printf("key frame num: %d,%d\n", key_frame_nums[i],tmp); + } + + cfread(&compressed, 4, 1, fp); + + uncompressed = A.width * A.height * A.total_frames; // 8 bits per pixel + percent = i2fl(compressed) / uncompressed * 100.0f; + + printf("%% of uncompressed size: %.0f%%\n", percent); + printf("Width: %d\n", A.width); + printf("Height: %d\n", A.height); + printf("Total Frames: %d\n", A.total_frames); + +#ifndef NDEBUG + printf("Key Frames: %d\n", A.num_keys); + if ( A.num_keys > 1 && (A.total_frames != A.num_keys) ) { + printf("key list: ("); + for ( i = 0; i < A.num_keys; i++ ) { + if ( i < A.num_keys-1 ) + printf("%d, ", key_frame_nums[i]); + else + printf("%d)\n", key_frame_nums[i]); + } + } +#endif + + printf("FPS: %d\n", A.fps); + +#ifndef NDEBUG + printf("Transparent RGB: (%d,%d,%d)\n", A.xparent_r, A.xparent_g, A.xparent_b); +#endif + + printf("ac version: %d\n", A.version); + + if ( key_frame_nums != NULL ) { + free(key_frame_nums); + } + + cfclose(fp); +} + +void anim_reverse_direction(anim_instance *ai) +{ + int temp; + + if(!(ai->parent->flags & ANF_ALL_KEYFRAMES)){ + // you're not allowed to call anim_reverse_direction(...) unless every frame is a keyframe!!!! + // The God of Delta-RLE demands it be thus. + Int3(); + } + + // flip the animation direction + if(ai->direction == ANIM_DIRECT_FORWARD){ + ai->direction = ANIM_DIRECT_REVERSE; + } else if(ai->direction == ANIM_DIRECT_REVERSE){ + ai->direction = ANIM_DIRECT_FORWARD; + } + + // flip frame_num and last_frame_num + temp = ai->frame_num; + ai->frame_num = ai->last_frame_num; + ai->last_frame_num = temp; + + // flip the start and stop at frames + temp = ai->stop_at; + ai->stop_at = ai->start_at; + ai->start_at = temp; + + // make sure to sync up the time correctly + if(ai->direction == ANIM_DIRECT_FORWARD){ + ai->time_elapsed = ((float)ai->frame_num - (float)ai->start_at - 1.0f) / (float)ai->parent->fps; + } else if(ai->direction == ANIM_DIRECT_REVERSE) { + ai->time_elapsed = ((float)ai->start_at - (float)ai->frame_num - 1.0f) / (float)ai->parent->fps; + } +} + +void anim_pause(anim_instance *ai) +{ + ai->paused = 1; +} + +void anim_unpause(anim_instance *ai) +{ + ai->paused = 0; +} + +void anim_ignore_next_frametime() +{ + Anim_ignore_frametime=1; +} + +int anim_instance_is_streamed(anim_instance *ai) +{ + Assert(ai); + return ( ai->parent->flags & ANF_STREAMED ); +} + +unsigned char anim_instance_get_byte(anim_instance *ai, int offset) +{ + int absolute_offset; + anim *parent; + + Assert(ai); + Assert(ai->parent->cfile_ptr); + Assert(ai->parent->flags & ANF_STREAMED); + + parent = ai->parent; + absolute_offset = ai->file_offset + offset; + + // maybe in cache? + int cache_offset; + cache_offset = absolute_offset - parent->cache_file_offset; + if ( (cache_offset >= 0) && (cache_offset < ANI_STREAM_CACHE_SIZE) ) { + return parent->cache[cache_offset]; + } else { + // fill cache + cfseek(parent->cfile_ptr, absolute_offset, CF_SEEK_SET); + cfread(parent->cache, ANI_STREAM_CACHE_SIZE, 1, parent->cfile_ptr); + parent->cache_file_offset = absolute_offset; + return parent->cache[0]; + } +} diff --git a/src/anim/packunpack.cpp b/src/anim/packunpack.cpp new file mode 100644 index 0000000..8cbd94e --- /dev/null +++ b/src/anim/packunpack.cpp @@ -0,0 +1,1270 @@ +/* + * $Logfile: /Freespace2/code/Anim/PackUnpack.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for handling packing and unpacking in Hoffoss's RLE format, used for + * Anim files. Also handles Anim loading, creating Anim instances (for + * utilizing an Anim), and getting getting frames of the Anim. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 12 7/30/99 10:10p Dave + * Fixed loading bar in 32 bit mode. + * + * 11 7/18/99 1:59p Johnson + * Fixed potential anim locking problem. + * + * 10 7/16/99 1:49p Dave + * 8 bit aabitmaps. yay. + * + * 9 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 8 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 7 1/14/99 12:48a Dave + * Todo list bug fixes. Made a pass at putting briefing icons back into + * FRED. Sort of works :( + * + * 6 12/01/98 5:53p Dave + * Simplified the way pixel data is swizzled. Fixed tga bitmaps to work + * properly in D3D and Glide. + * + * 5 12/01/98 8:06a Dave + * Temporary checkin to fix some texture transparency problems in d3d. + * + * 4 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 3 10/22/98 6:14p Dave + * Optimized some #includes in Anim folder. Put in the beginnings of + * parse/localization support for externalized strings and tstrings.tbl + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 30 5/14/98 3:38p John + * Added in more non-darkening colors for Adam. Had to fix some bugs in + * BmpMan and Ani stuff to get this to work. + * + * 29 5/07/98 3:11a Lawrance + * Implement custom streaming code + * + * 28 11/19/97 8:28p Dave + * Hooked in Main Hall screen. Put in Anim support for ping ponging + * animations as well as general reversal of anim direction. + * + * 27 9/09/97 3:39p Sandeep + * warning level 4 bugs + * + * 26 8/22/97 8:21a Lawrance + * short circuit key frame check if keyframe matches frame we are + * searching for + * + * 25 8/21/97 5:11p Lawrance + * frame numbering for ANI's now is from 0 -> total_frames-1. + * + * 24 8/19/97 10:59a Lawrance + * fix problem with accessing key frames + * + * 23 7/28/97 10:42p Lawrance + * re-did interface to unpack_frame() to make more general + * + * 22 7/28/97 10:52a Lawrance + * correctly set bitmap flags in anim_get_frame() + * + * 21 7/21/97 5:10p Lawrance + * fix problem that was causing infinite recursion + * + * 20 7/20/97 6:57p Lawrance + * supporting new RLE format + * + * 19 6/27/97 4:36p Lawrance + * update pal translation table when gr_screen.signature changes + * + * 18 6/26/97 12:12a Lawrance + * supporting anti-aliased bitmap animations + * + * 17 6/25/97 3:03p Lawrance + * fix palette translation problem with anti-alised bitmaps + * + * 16 5/19/97 2:28p Lawrance + * changes some variables to flags + * + * 15 5/15/97 4:42p Lawrance + * supporting animations in-game + * + * 14 2/25/97 11:06a Lawrance + * moved some higher level functions to from PackUnpack to AnimPlay + * + * 13 2/19/97 9:51p Lawrance + * made keyframe decompression more effecient, moved + * default anim FPS to header file + * + * 12 2/19/97 4:00p Lawrance + * don't assert when cannot find anim filename, return a NULL instead + * + * 11 2/17/97 4:17p Hoffoss + * modified packing internal format and added random access function to an + * Anim frame. + * + * 10 2/17/97 2:59p Lawrance + * integrating into game + * + * 9 2/14/97 11:27p Lawrance + * optimized unpacking some more (Jason) + * + * 8 2/14/97 11:09p Hoffoss + * Made optimizations. + * + * 7 2/14/97 10:48p Hoffoss + * fixed bug. + * + * 6 2/14/97 10:38p Lawrance + * fixing bugs + * + * 5 2/14/97 5:38p Hoffoss + * Changes to get AnimCoverter project to compile and link. + * + * 4 2/14/97 3:29p Hoffoss + * Added header for MSDEV to fill in. + * + * $NoKeywords: $ + */ + +#include "grinternal.h" +#include "bmpman.h" +#include "palman.h" +#include "2d.h" +#include "animplay.h" +#include "packunpack.h" + +int packer_code = PACKER_CODE; +int transparent_code = 254; + +void anim_check_for_palette_change(anim_instance *instance) { + if ( instance->parent->screen_sig != gr_screen.signature ) { + instance->parent->screen_sig = gr_screen.signature; + anim_set_palette(instance->parent); + } +} + +anim_instance *init_anim_instance(anim *ptr, int bpp) +{ + anim_instance *inst; + + if (!ptr) { + Int3(); + return NULL; + } + + if ( ptr->flags & ANF_STREAMED ) { + if ( ptr->file_offset < 0 ) { + Int3(); + return NULL; + } + } else { + if ( !ptr->data ) { + Int3(); + return NULL; + } + } + + ptr->instance_count++; + inst = (anim_instance *) malloc(sizeof(anim_instance)); + Assert(inst); + inst->frame_num = -1; + inst->last_frame_num = -1; + inst->parent = ptr; + inst->data = ptr->data; + inst->file_offset = ptr->file_offset; + inst->stop_now = FALSE; + inst->aa_color = NULL; + + if(bpp == 16){ + inst->frame = (ubyte *) malloc(inst->parent->width * inst->parent->height * 2); + } else { + inst->frame = (ubyte *) malloc(inst->parent->width * inst->parent->height); + } + return inst; +} + +void free_anim_instance(anim_instance *inst) +{ + Assert(inst->frame); + free(inst->frame); + inst->frame = NULL; + inst->parent->instance_count--; + inst->parent = NULL; + inst->data = NULL; + inst->file_offset = -1; + + free(inst); +} + +int anim_get_next_frame(anim_instance *inst) +{ + int bm, bitmap_flags; + int aabitmap = 0; + int bpp = 16; + + if ( anim_instance_is_streamed(inst) ) { + if ( inst->file_offset <= 0 ) { + return -1; + } + } else { + if (!inst->data) + return -1; + } + + inst->frame_num++; + if (inst->frame_num >= inst->parent->total_frames) { + inst->data = NULL; + inst->file_offset = inst->parent->file_offset; + return -1; + } + + if (inst->parent->flags & ANF_XPARENT) { + // bitmap_flags = BMP_XPARENT; + bitmap_flags = 0; + } else { + bitmap_flags = 0; + } + + bpp = 16; + if(inst->aa_color != NULL){ + bitmap_flags |= BMP_AABITMAP; + aabitmap = 1; + bpp = 8; + } + + anim_check_for_palette_change(inst); + + // if we're using bitmap polys + if(Gr_bitmap_poly){ + BM_SELECT_TEX_FORMAT(); + } + + if ( anim_instance_is_streamed(inst) ) { + inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation, aabitmap, bpp); + } else { + inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation, aabitmap, bpp); + } + + bm = bm_create(bpp, inst->parent->width, inst->parent->height, inst->frame, bitmap_flags); + bm_unload(bm); + return bm; +} + +ubyte *anim_get_next_raw_buffer(anim_instance *inst, int xlate_pal, int aabitmap, int bpp) +{ + if ( anim_instance_is_streamed(inst) ) { + if ( inst->file_offset < 0 ) { + return NULL; + } + } else { + if (!inst->data){ + return NULL; + } + } + + inst->frame_num++; + if (inst->frame_num >= inst->parent->total_frames) { + inst->data = NULL; + inst->file_offset = inst->parent->file_offset; + return NULL; + } + + anim_check_for_palette_change(inst); + + if ( anim_instance_is_streamed(inst) ) { + if ( xlate_pal ){ + inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation, aabitmap, bpp); + } else { + inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, NULL, aabitmap, bpp); + } + } else { + if ( xlate_pal ){ + inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation, aabitmap, bpp); + } else { + inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, NULL, aabitmap, bpp); + } + } + + return inst->frame; +} + +// -------------------------------------------------------------------- +// anim_get_frame() +// +// Get a bitmap id from the anim_instance for the specified frame_num +// +// input: *inst => pointer to anim instance +// frame_num => frame number to get (first frame is 0) +// xlate_pal => DEFAULT PARM (value 1): whether to translate the palette +// to the current game palette +// +int anim_get_frame(anim_instance *inst, int frame_num, int xlate_pal) +{ + /* + int bm, bitmap_flags, key = 0, offset = 0; + int idx; + + if ((frame_num < 0) || (frame_num >= inst->parent->total_frames)) // illegal frame number + return -1; + + int need_reset = 0; + if ( anim_instance_is_streamed(inst) ) { + if ( inst->file_offset < 0 ) { + need_reset = 1; + } + } else { + if ( !inst->data ) { + need_reset = 1; + } + } + + if (need_reset || (inst->frame_num >= inst->parent->total_frames)) { // reset to valid info + inst->data = inst->parent->data; + inst->file_offset = inst->parent->file_offset; + inst->frame_num = 0; + } + + bitmap_flags = 0; + if (inst->parent->flags & ANF_XPARENT) { + // bitmap_flags = BMP_XPARENT; + bitmap_flags = 0; + } + + if ( inst->frame_num == frame_num ) { + bm = bm_create(16, inst->parent->width, inst->parent->height, inst->frame, bitmap_flags); + bm_unload(bm); + return bm; + } + + if (inst->parent->flags & ANF_XPARENT){ + // bitmap_flags = BMP_XPARENT; + bitmap_flags = 0; + } else { + bitmap_flags = 0; + } + + idx = 0; + key = 0; + while(idx < inst->parent->num_keys){ + if (( (inst->parent->keys[idx].frame_num-1) <= frame_num) && ( (inst->parent->keys[idx].frame_num-1) > key)) { // find closest key + key = inst->parent->keys[idx].frame_num - 1; + offset = inst->parent->keys[idx].offset; + + if ( key == frame_num ) + break; + } + idx++; + } + + if ( key == frame_num ) { + inst->frame_num = key; + + if ( anim_instance_is_streamed(inst) ) { + inst->file_offset = inst->parent->file_offset + offset; + } else { + inst->data = inst->parent->data + offset; + } + + anim_check_for_palette_change(inst); + + if ( anim_instance_is_streamed(inst) ) { + if ( xlate_pal ){ + inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation); + } else { + inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, NULL); + } + } else { + if ( xlate_pal ){ + inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation); + } else { + inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, NULL); + } + } + + goto create_bitmap; + } + + if (key > inst->frame_num) // best key is closer than current position + { + inst->frame_num = key; + + if ( anim_instance_is_streamed(inst) ) { + inst->file_offset = inst->parent->file_offset + offset; + } else { + inst->data = inst->parent->data + offset; + } + + anim_check_for_palette_change(inst); + + if ( anim_instance_is_streamed(inst) ) { + if ( xlate_pal ) + inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation); + else + inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, NULL); + } else { + if ( xlate_pal ) + inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation); + else + inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, NULL); + } + } + + while (inst->frame_num != frame_num) { + anim_check_for_palette_change(inst); + + if ( anim_instance_is_streamed(inst) ) { + if ( xlate_pal ) + inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation); + else + inst->file_offset = unpack_frame_from_file(inst, inst->frame, inst->parent->width*inst->parent->height, NULL); + } else { + if ( xlate_pal ) + inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, inst->parent->palette_translation); + else + inst->data = unpack_frame(inst, inst->data, inst->frame, inst->parent->width*inst->parent->height, NULL); + } + inst->frame_num++; + } + + create_bitmap: + + bm = bm_create(16, inst->parent->width, inst->parent->height, inst->frame, bitmap_flags); + bm_unload(bm); + return bm; + */ + Int3(); + return -1; +} + +// frame = frame pixel data to pack +// save = memory to store packed data to +// size = number of bytes to pack +// max = maximum number of packed bytes (size of buffer) +// returns: actual number of bytes data packed to or -1 if error +int pack_key_frame(ubyte *frame, ubyte *save, long size, long max, int compress_type) +{ + int last = -32768, count = 0; + long packed_size = 1; + + switch ( compress_type ) { + case PACKING_METHOD_RLE_KEY: + *save++ = PACKING_METHOD_RLE_KEY; + while (size--) { + if (*frame != last || count > 255) { + if (packed_size + 3 >= max) + return -1; + + if (count < 3) { + if (last == packer_code) { + *save++ = (ubyte)packer_code; + *save++ = (ubyte)(count - 1); + packed_size += 2; + + } else + while (count--) { + *save++ = (ubyte)last; + packed_size++; + } + + } else { + *save++ = (ubyte)packer_code; + *save++ = (ubyte)(count - 1); + *save++ = (ubyte)last; + packed_size += 3; + } + + count = 0; + last = *frame; + } + + count++; + frame++; + } + + if (packed_size + 3 >= max) + return -1; + + if (count < 3) { + if (last == packer_code) { + *save++ = (ubyte)packer_code; + *save++ = (ubyte)(count - 1); + packed_size += 2; + + } else + while (count--) { + *save++ = (ubyte)last; + packed_size++; + } + + } else { + *save++ = (ubyte)packer_code; + *save++ = (ubyte)(count - 1); + *save++ = (ubyte)last; + packed_size += 3; + } + break; + + case PACKING_METHOD_STD_RLE_KEY: { + ubyte *dest_start; + int i; + + dest_start = save; + count = 1; + + last = *frame++; + *save++ = PACKING_METHOD_STD_RLE_KEY; + for (i=1; i < size; i++ ) { + + if ( *frame != last ) { + if ( count ) { + + if (packed_size + 2 >= max) + return -1; + + if ( (count == 1) && !(last & STD_RLE_CODE) ) { + *save++ = (ubyte)last; + packed_size++; + Assert( last != STD_RLE_CODE ); +// printf("Just packed %d 1 times, since pixel change, no count included\n",last); + } + else { + count |= STD_RLE_CODE; + *save++ = (ubyte)count; + *save++ = (ubyte)last; + packed_size += 2; +// printf("Just packed %d %d times, since pixel change\n",last,count); + } + } + + last = *frame; + count = 0; + } + + count++; + frame++; + + if ( count == 127 ) { + count |= STD_RLE_CODE; + *save++ = (ubyte)count; + *save++ = (ubyte)last; + packed_size += 2; + count = 0; +// printf("Just packed %d %d times, since count overflow\n",last,count); + + } + } // end for + + if (count) { + + if (packed_size + 2 >= max) + return -1; + + if ( (count == 1) && !(last & STD_RLE_CODE) ) { + *save++ = (ubyte)last; + packed_size++; +// printf("Just packed %d 1 times, at end since single pixel, no count\n",last); + Assert( last != STD_RLE_CODE ); + } + else { + count |= STD_RLE_CODE; + *save++ = (ubyte)count; + *save++ = (ubyte)last; + packed_size += 2; +// printf("Just packed %d %d times, at end since pixel change\n",last,count); + } + } + + Assert(packed_size == (save-dest_start) ); + return packed_size; + break; + } + + default: + Assert(0); + return -1; + break; + } // end switch + + return packed_size; +} + +// frame = frame pixel data to pack +// frame2 = previous frame's pixel data +// save = memory to store packed data to +// size = number of bytes to pack +// max = maximum number of packed bytes (size of buffer) +// returns: actual number of bytes data packed to or -1 if error +int pack_frame(ubyte *frame, ubyte *frame2, ubyte *save, long size, long max, int compress_type) +{ + int pixel, last = -32768, count = 0, i; + long packed_size = 1; + + switch ( compress_type ) { + case PACKING_METHOD_RLE: // Hoffoss RLE regular frame + *save++ = PACKING_METHOD_RLE; + while (size--) { + if (*frame != *frame2++) + pixel = *frame; + else + pixel = transparent_code; + + if (pixel != last || count > 255) { + if (packed_size + 3 >= max) + return -1; + + if (count < 3) { + if (last == packer_code) { + *save++ = (ubyte)packer_code; + *save++ = (ubyte)(count - 1); + packed_size += 2; + + } else + while (count--) { + *save++ = (ubyte)last; + packed_size++; + } + + } else { + *save++ = (ubyte)packer_code; + *save++ = (ubyte)(count - 1); + *save++ = (ubyte)last; + packed_size += 3; + } + + count = 0; + last = pixel; + } + + frame++; + count++; + } + + if (packed_size + 3 >= max) + return -1; + + if (count < 3) { + if (last == packer_code) { + *save++ = (ubyte)packer_code; + *save++ = (ubyte)(count - 1); + packed_size += 2; + + } else + while (count--) { + *save++ = (ubyte)last; + packed_size++; + } + + } else { + *save++ = (ubyte)(packer_code); + *save++ = (ubyte)(count - 1); + *save++ = (ubyte)(last); + packed_size += 3; + } + break; + + case PACKING_METHOD_STD_RLE: { // high bit count regular RLE frame + + ubyte *dest_start; + + dest_start = save; + count = 1; + + if (*frame++ != *frame2++) + last = *frame; + else + last = transparent_code; + + *save++ = PACKING_METHOD_STD_RLE; + for (i=1; i < size; i++ ) { + + if (*frame != *frame2++) + pixel = *frame; + else + pixel = transparent_code; + + if ( pixel != last ) { + if ( count ) { + + if (packed_size + 2 >= max) + return -1; + + if ( (count == 1) && !(last & STD_RLE_CODE) ) { + *save++ = (ubyte)last; + packed_size++; + Assert( last != STD_RLE_CODE ); + } + else { + count |= STD_RLE_CODE; + *save++ = (ubyte)count; + *save++ = (ubyte)last; + packed_size += 2; + } + } + + last = pixel; + count = 0; + } + + count++; + frame++; + + if ( count == 127 ) { + count |= STD_RLE_CODE; + *save++ = (ubyte)count; + *save++ = (ubyte)last; + packed_size += 2; + count = 0; + } + } // end for + + if (count) { + + if (packed_size + 2 >= max) + return -1; + + if ( (count == 1) && !(last & STD_RLE_CODE) ) { + *save++ = (ubyte)last; + packed_size++; + Assert( last != STD_RLE_CODE ); + } + else { + count |= STD_RLE_CODE; + *save++ = (ubyte)count; + *save++ = (ubyte)last; + packed_size += 2; + } + } + + Assert(packed_size == (save-dest_start) ); + return packed_size; + break; + } + + default: + Assert(0); + return -1; + break; + } // end switch + + return packed_size; +} + +// unpack a pixel given the passed index and the anim_instance's palette, return bytes stuffed +int unpack_pixel(anim_instance *ai, ubyte *data, ubyte pix, int aabitmap, int bpp) +{ + int bit_24; + ushort bit_16 = 0; + ubyte bit_8 = 0; + ubyte al = 0; + ubyte r, g, b; + anim *a = ai->parent; + Assert(a); + + // if this is an aabitmap, don't run through the palette + if(aabitmap){ + switch(bpp){ + case 16 : + bit_16 = (ushort)pix; + break; + case 8: + bit_8 = pix; + break; + default: + Int3(); + } + } else { + // if the pixel value is 255, or is the xparent color, make it so + if(((a->palette[pix*3] == a->xparent_r) && (a->palette[pix*3+1] == a->xparent_g) && (a->palette[pix*3+2] == a->xparent_b)) ){ + r = b = 0; + g = 255; + bm_set_components((ubyte*)&bit_16, &r, &g, &b, &al); + } else { + // stuff the 24 bit value + memcpy(&bit_24, &ai->parent->palette[pix * 3], 3); + + // convert to 16 bit + bm_24_to_16(bit_24, &bit_16); + } + } + + // stuff the pixel + switch(bpp){ + case 16 : + memcpy(data, &bit_16, sizeof(ushort)); + return sizeof(ushort); + + case 8 : + *data = bit_8; + return sizeof(ubyte); + } + + Int3(); + return 0; +} + +// unpack a pixel given the passed index and the anim_instance's palette, return bytes stuffed +int unpack_pixel_count(anim_instance *ai, ubyte *data, ubyte pix, int count, int aabitmap, int bpp) +{ + int bit_24; + int idx; + ubyte al = 0; + ushort bit_16 = 0; + ubyte bit_8 = 0; + anim *a = ai->parent; + ubyte r, g, b; + Assert(a); + + // if this is an aabitmap, don't run through the palette + if(aabitmap){ + switch(bpp){ + case 16 : + bit_16 = (ushort)pix; + break; + case 8 : + bit_8 = pix; + break; + default : + Int3(); + } + } else { + // if the pixel value is 255, or is the xparent color, make it so + if(((a->palette[pix*3] == a->xparent_r) && (a->palette[pix*3+1] == a->xparent_g) && (a->palette[pix*3+2] == a->xparent_b)) ){ + r = b = 0; + g = 255; + bm_set_components((ubyte*)&bit_16, &r, &g, &b, &al); + } else { + // stuff the 24 bit value + memcpy(&bit_24, &ai->parent->palette[pix * 3], 3); + + // convert to 16 bit + bm_24_to_16(bit_24, &bit_16); + } + } + + // stuff the pixel + for(idx=0; idx 0) { + value = *ptr++; + if (value != packer_code) { + if ( xlate_pal ){ + stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); + } else { + stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); + } + frame += stuffed; + size--; + } else { + count = *ptr++; + if (count < 2){ + value = packer_code; + } else { + value = *ptr++; + } + + if (++count > size){ + count = size; + } + + if ( xlate_pal ){ + stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); + } else { + stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); + } + + frame += stuffed; + size -= count; + } + } + } + else if ( *ptr == PACKING_METHOD_STD_RLE_KEY) { // key frame, with high bit as count + ptr++; + while (size > 0) { + value = *ptr++; + if ( !(value & STD_RLE_CODE) ) { + if ( xlate_pal ){ + stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); + } else { + stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); + } + + frame += stuffed; + size--; + } else { + count = value & (~STD_RLE_CODE); + value = *ptr++; + + size -= count; + Assert(size >= 0); + + if ( xlate_pal ){ + stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); + } else { + stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); + } + + frame += stuffed; + } + } + } + else if (*ptr == PACKING_METHOD_RLE) { // normal frame, Hoffoss's RLE format + +// test code, to show unused pixels +// memset(frame, 255, size); + + ptr++; + while (size > 0) { + value = *ptr++; + if (value != packer_code) { + if (value != transparent_code) { + if ( xlate_pal ){ + stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); + } else { + stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); + } + } else { + // temporary pixel + stuffed = pixel_size; + } + + frame += stuffed; + size--; + } else { + count = *ptr++; + if (count < 2){ + value = packer_code; + } else { + value = *ptr++; + } + + if (++count > size){ + count = size; + } + + size -= count; + Assert(size >= 0); + + if (value != transparent_code ) { + if ( xlate_pal ) { + stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); + } else { + stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); + } + } else { + stuffed = count * pixel_size; + } + + frame += stuffed; + } + } + + } + else if ( *ptr == PACKING_METHOD_STD_RLE) { // normal frame, with high bit as count + ptr++; + while (size > 0) { + value = *ptr++; + if ( !(value & STD_RLE_CODE) ) { + if (value != transparent_code) { + if ( xlate_pal ){ + stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); + } else { + stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); + } + } else { + stuffed = pixel_size; + } + + frame += stuffed; + size--; + } else { + count = value & (~STD_RLE_CODE); + value = *ptr++; + + size -= count; + Assert(size >= 0); + + if (value != transparent_code) { + if ( xlate_pal ){ + stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); + } else { + stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); + } + } else { + stuffed = pixel_size * count; + } + + frame += stuffed; + } + } + } + else { + Assert(0); // unknown packing method + } + + return ptr; +} + +// ptr = packed data to unpack +// frame = where to store unpacked data to +// size = total number of unpacked pixels requested +// pal_translate = color translation lookup table (NULL if no palette translation desired) +int unpack_frame_from_file(anim_instance *ai, ubyte *frame, int size, ubyte *pal_translate, int aabitmap, int bpp) +{ + int xlate_pal, value, count = 0; + int offset = 0; + int stuffed; + int pixel_size = (bpp == 16) ? 2 : 1; + + if ( pal_translate == NULL ) { + xlate_pal = 0; + } + else { + xlate_pal = 1; + } + + if (anim_instance_get_byte(ai,offset) == PACKING_METHOD_RLE_KEY) { // key frame, Hoffoss's RLE format + offset++; + while (size > 0) { + value = anim_instance_get_byte(ai,offset); + offset++; + if (value != packer_code) { + if ( xlate_pal ){ + stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); + } else { + stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); + } + + frame += stuffed; + size--; + } else { + count = anim_instance_get_byte(ai,offset); + offset++; + if (count < 2) { + value = packer_code; + } else { + value = anim_instance_get_byte(ai,offset); + offset++; + } + + if (++count > size){ + count = size; + } + + if ( xlate_pal ){ + stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); + } else { + stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); + } + + frame += stuffed; + size -= count; + } + } + } + else if ( anim_instance_get_byte(ai,offset) == PACKING_METHOD_STD_RLE_KEY) { // key frame, with high bit as count + offset++; + while (size > 0) { + value = anim_instance_get_byte(ai,offset); + offset++; + if ( !(value & STD_RLE_CODE) ) { + if ( xlate_pal ){ + stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); + } else { + stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); + } + + frame += stuffed; + size--; + } else { + count = value & (~STD_RLE_CODE); + value = anim_instance_get_byte(ai,offset); + offset++; + + size -= count; + Assert(size >= 0); + + if ( xlate_pal ){ + stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); + } else { + stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); + } + + frame += stuffed; + } + } + } + else if (anim_instance_get_byte(ai,offset) == PACKING_METHOD_RLE) { // normal frame, Hoffoss's RLE format + +// test code, to show unused pixels +// memset(frame, 255, size); + + offset++; + while (size > 0) { + value = anim_instance_get_byte(ai,offset); + offset++; + if (value != packer_code) { + if (value != transparent_code) { + if ( xlate_pal ){ + stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); + } else { + stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); + } + } else { + stuffed = pixel_size; + } + + frame += stuffed; + size--; + } else { + count = anim_instance_get_byte(ai,offset); + offset++; + + if (count < 2) { + value = packer_code; + } else { + value = anim_instance_get_byte(ai,offset); + offset++; + } + if (++count > size){ + count = size; + } + + size -= count; + Assert(size >= 0); + + if (value != transparent_code ) { + if ( xlate_pal ) { + stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); + } else { + stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); + } + } else { + stuffed = pixel_size * count; + } + + frame += stuffed; + } + } + + } + else if ( anim_instance_get_byte(ai,offset) ) { // normal frame, with high bit as count + offset++; + while (size > 0) { + value = anim_instance_get_byte(ai,offset); + offset++; + if ( !(value & STD_RLE_CODE) ) { + if (value != transparent_code) { + if ( xlate_pal ){ + stuffed = unpack_pixel(ai, frame, pal_translate[value], aabitmap, bpp); + } else { + stuffed = unpack_pixel(ai, frame, (ubyte)value, aabitmap, bpp); + } + } else { + stuffed = pixel_size; + } + + frame += stuffed; + size--; + } else { + count = value & (~STD_RLE_CODE); + value = anim_instance_get_byte(ai,offset); + offset++; + + size -= count; + Assert(size >= 0); + + if (value != transparent_code) { + if ( xlate_pal ){ + stuffed = unpack_pixel_count(ai, frame, pal_translate[value], count, aabitmap, bpp); + } else { + stuffed = unpack_pixel_count(ai, frame, (ubyte)value, count, aabitmap, bpp); + } + } else { + stuffed = pixel_size * count; + } + + frame += stuffed; + } + } + } + else { + Int3(); // unknown packing method + } + + return ai->file_offset + offset; +} + + +// TODO: actually convert the frame data to correct palette at this point +void anim_set_palette(anim *ptr) +{ + int i, xparent_found = 0; + + // create the palette translation look-up table + for ( i = 0; i < 256; i++ ) { + + //if ( (ptr->palette[i*3] == ptr->xparent_r) && (ptr->palette[i*3+1] == ptr->xparent_g) && (ptr->palette[i*3+2] == ptr->xparent_b) ) { + // ptr->palette_translation[i] = 255; + // xparent_found = 1; + //} else { + // ptr->palette_translation[i] = (ubyte)palette_find( ptr->palette[i*3], ptr->palette[i*3+1], ptr->palette[i*3+2] ); + ptr->palette_translation[i] = (ubyte)i; + //} + } + + if ( xparent_found ) { + ptr->flags |= ANF_XPARENT; + } + else { + ptr->flags &= ~ANF_XPARENT; + } +} + diff --git a/src/asteroid/asteroid.cpp b/src/asteroid/asteroid.cpp new file mode 100644 index 0000000..143720d --- /dev/null +++ b/src/asteroid/asteroid.cpp @@ -0,0 +1,2269 @@ +/* + * $Logfile: /Freespace2/code/Asteroid/Asteroid.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for asteroid code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:11 root + * Initial revision + * + * + * 31 9/08/99 10:52a Mikek + * Reduce the number of asteroids that can be thrown at a target at + * Medium+. + * + * 30 9/06/99 12:46a Andsager + * Add weapon_explosion_ani LOD + * + * 29 9/05/99 3:13p Mikek + * Slightly decrease number of asteroids thrown at escorted ships. + * + * 28 8/26/99 8:51p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 27 7/30/99 7:01p Dave + * Dogfight escort gauge. Fixed up laser rendering in Glide. + * + * 26 7/19/99 8:58p Andsager + * Don't try to throw at objnum -1 + * + * 25 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 24 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 23 6/10/99 11:06a Andsager + * Mission designed selection of asteroid types. + * + * 22 6/09/99 2:55p Andsager + * Allow multiple asteroid subtypes (of large, medium, small) and follow + * family. + * + * 21 6/08/99 7:19p Dave + * Fixed asteroid lighting. Was because on non-darkening textures in Glide + * only. Dumb. + * + * 20 6/07/99 1:18p Andsager + * Make asteroids choose consistent texture from large to small. Modify + * fireball radius of dying asteroids. + * + * 19 5/03/99 10:50p Andsager + * Make Asteroid_obj_list. Change get_nearest_turret_objnum() to use + * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs. + * obj_used_list. + * + * 18 4/23/99 12:01p Johnson + * Added SIF_HUGE_SHIP + * + * 17 4/21/99 6:15p Dave + * Did some serious housecleaning in the beam code. Made it ready to go + * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added + * a handy macro for recalculating collision pairs for a given object. + * + * 16 4/16/99 2:34p Andsager + * Second pass on debris fields + * + * 15 4/15/99 5:27p Andsager + * Fix explosion sound for Ships debris + * + * 14 4/15/99 5:00p Andsager + * Frist pass on Debris field + * + * 13 2/07/99 8:51p Andsager + * Add inner bound to asteroid field. Inner bound tries to stay astroid + * free. Wrap when within and don't throw at ships inside. + * + * 12 1/20/99 6:04p Dave + * Another bit of stuff for beam weapons. Ships will properly use them + * now, although they're really deadly. + * + * 11 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 10 12/03/98 3:14p Andsager + * Check in code that checks rotating submodel actually has ship subsystem + * + * 9 11/19/98 11:07p Andsager + * Check in of physics and collision detection of rotating submodels + * + * 8 11/13/98 10:12a Andsager + * simplify collision code + * + * 7 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 6 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 5 10/23/98 1:11p Andsager + * Make ship sparks emit correctly from rotating structures. + * + * 4 10/16/98 1:22p Andsager + * clean up header files + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 80 7/14/98 3:30p Dave + * + * 79 5/18/98 12:05p Mike + * Make sm1-06a a little harder. + * + * 78 5/06/98 12:36p Lawrance + * disable ambient asteroid sound for now + * + * 77 4/30/98 12:49a Allender + * deal with asteroid problems in multiplayer + * + * 76 4/27/98 1:46p Mike + * Make asteroid_density have no meaning. + * + * 75 4/24/98 11:59a Dan + * fix bug where a null vector would occur when death_hit_pos was assinged + * to asteroid_pos + * + * 74 4/23/98 4:43p Andsager + * Don't call asteroid_update_collide() on creating asteroids since it + * creates sets of collisions pairs. Pairs are created when objects are + * merged in obj_merge_created_list() + * + * 73 4/21/98 1:23a Mike + * Tone down difficulty in asteroid toss missions. + * + * 72 4/16/98 11:57a John + * Removed debug and unused registry values. + * + * 71 4/08/98 1:27a Lawrance + * Fix typo. + * + * 70 4/08/98 1:17a Lawrance + * Fix bug with red and white brackets being drawn on top of one another. + * + * 69 4/02/98 6:28p Lawrance + * remove asteroid code from demo + * + * 68 4/02/98 5:11p Mike + * Quick out of asteroid code if none present. + * + * 67 4/02/98 1:34p Mike + * Minor difficulty balance, made a bit easier. + * + * 66 4/02/98 1:29p Andsager + * + * 65 4/01/98 5:34p John + * Made only the used POFs page in for a level. Reduced some interp + * arrays. Made custom detail level work differently. + * + * 64 3/31/98 5:11p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * 63 3/30/98 4:02p John + * Made machines with < 32 MB of RAM use every other frame of certain + * bitmaps. Put in code to keep track of how much RAM we've malloc'd. + * + * 62 3/30/98 2:38p Mike + * Add asteroid_density to detail level support. + * No force explosion damage in training missions. + * Make cargo deathrolls shorter. + * + * 61 3/30/98 10:07a Lawrance + * Um, make asteroids work again. + * + * 60 3/29/98 12:55a Lawrance + * Get demo build working with limited set of data. + * + * 59 3/26/98 9:19a Lawrance + * Support multiple asteroid pofs + * + * 58 3/25/98 3:44p Andsager + * Fixed bug in asteroid_sub_create, where last_pos field was invalid in + * its creation frame but needed for collision in that frame. + * + * 57 3/25/98 2:03p Sandeep + * Remove "f" from top of file. + * + * 56 3/25/98 1:50p Mike + * Asteroid destruction (sm1-06a) balancing. + * + * 55 3/25/98 10:43a Andsager + * Hack for ship_asteroid collisions when erroneous relative_vel >150 + * + * 54 3/23/98 12:20p Andsager + * Enable collision from rotation in ship_debris and ship_debris + * collisions. + * + * 53 3/22/98 4:09p Andsager + * Make moment of inertia based on model. + * + * 52 3/21/98 3:33p Lawrance + * When asteroid collides with an escort ship, make the sound carry + * farther + * + * 51 3/19/98 12:09p John + * Fixed a bug using 6 characters. r_heavy was using local coordinates + * instead of world so all asteroid-ship and debris-ship hitpos's were in + * the wrong spot. Ok, I rearreanged some code to make it clearer also. + * + * 50 3/17/98 5:55p Lawrance + * Support object linked sounds for asteroids. + * + * 49 3/17/98 3:44p Mike + * Make asteroids-thrown-at-target not only happen when an asteroid wraps. + * + * 48 3/17/98 9:53a Lawrance + * Asteroid area effect only affect ships + * + * 47 3/17/98 9:25a Allender + * some minor asteroid changes for multiplayer + * + * 46 3/17/98 12:16a Allender + * asteroids in multiplayer -- minor problems with position being correct + * + * 45 3/14/98 1:44p Mike + * Todolist items 3365..3368. Make child asteroids collide properly. + * Make asteroid throwing less bunchy, toss asteroids earlier, make + * facing-ness not break mission balance. + * + * 44 3/13/98 9:06a John + * Made missile thrusters use a different min dist for scaling. Made + * missilies not use lighting. + * + * 43 3/12/98 4:01p Lawrance + * Only show warning brackets for cruiser/capital ships on the same team + * + * $NoKeywords: $ + */ + +#include "asteroid.h" +#include "object.h" +#include "objcollide.h" +#include "freespace.h" +#include "timer.h" +#include "3d.h" +#include "fireballs.h" +#include "gamesnd.h" +#include "bmpman.h" +#include "particle.h" +#include "linklist.h" +#include "hudescort.h" +#include "shiphit.h" +#include "multiutil.h" +#include "staticrand.h" +#include "multimsgs.h" +#include "systemvars.h" +#include "localize.h" +#include "multi.h" + +#ifndef FS2_DEMO + +#define ASTEROID_OBJ_USED (1<<0) // flag used in asteroid_obj struct +#define MAX_ASTEROID_OBJS MAX_ASTEROIDS // max number of asteroids tracked in asteroid list +asteroid_obj Asteroid_objs[MAX_ASTEROID_OBJS]; // array used to store asteroid object indexes +asteroid_obj Asteroid_obj_list; // head of linked list of asteroid_obj structs + +// Asteroid editor requires first set of entries to be "None" and then "Asteroid XXX" +// Any changes to this will require changes to the asteroid editor +debris_struct Field_debris_info[] = { + { -1, "None", }, + { ASTEROID_TYPE_SMALL, "Asteroid Small", }, + { ASTEROID_TYPE_MEDIUM, "Asteroid Medium", }, + { ASTEROID_TYPE_BIG, "Asteroid Large", }, + + { DEBRIS_TERRAN_SMALL, "Terran Small", }, + { DEBRIS_TERRAN_MEDIUM, "Terran Medium", }, + { DEBRIS_TERRAN_LARGE, "Terran Large", }, + + { DEBRIS_VASUDAN_SMALL, "Vasudan Small", }, + { DEBRIS_VASUDAN_MEDIUM, "Vasudan Medium", }, + { DEBRIS_VASUDAN_LARGE, "Vasudan Large", }, + + { DEBRIS_SHIVAN_SMALL, "Shivan Small", }, + { DEBRIS_SHIVAN_MEDIUM, "Shivan Medium", }, + { DEBRIS_SHIVAN_LARGE, "Shivan Large" }, +}; + +// used for randomly generating debris type when there are multiple sizes. +#define SMALL_DEBRIS_WEIGHT 8 +#define MEDIUM_DEBRIS_WEIGHT 4 +#define LARGE_DEBRIS_WEIGHT 1 + +int Asteroids_enabled = 1; +int Num_asteroid_types; +int Num_asteroids = 0; +int Asteroid_throw_objnum = -1; // Object index of ship to throw asteroids at. +int Next_asteroid_throw; + +asteroid_info Asteroid_info[MAX_DEBRIS_TYPES]; +asteroid Asteroids[MAX_ASTEROIDS]; +asteroid_field Asteroid_field; + +static int Asteroid_impact_explosion_ani; +static float Asteroid_impact_explosion_radius; + +#define ASTEROID_CHECK_WRAP_TIMESTAMP 2000 // how often an asteroid gets checked for wrapping +#define ASTEROID_UPDATE_COLLIDE_TIMESTAMP 2000 // how often asteroid is checked for impending collisions with escort ships +#define ASTEROID_MIN_COLLIDE_TIME 24 // time in seconds to check for asteroid colliding + +// Force updating of pair stuff for asteroid *objp. +void asteroid_update_collide(object *objp) +{ + // Asteroid has wrapped, update collide objnum and flags + Asteroids[objp->instance].collide_objnum = -1; + Asteroids[objp->instance].collide_objsig = -1; + OBJ_RECALC_PAIRS(objp); +} + +// Clear out the Asteroid_obj_list +// +void asteroid_obj_list_init() +{ + int i; + + list_init(&Asteroid_obj_list); + for ( i = 0; i < MAX_ASTEROID_OBJS; i++ ) { + Asteroid_objs[i].flags = 0; + } +} + +// --------------------------------------------------- +// asteroid_obj_list_add() +// +// Function to add a node from the Asteroid_obj_list. Only +// called from weapon_create() +int asteroid_obj_list_add(int objnum) +{ + int index; + + asteroid *cur_asteroid = &Asteroids[Objects[objnum].instance]; + index = cur_asteroid - Asteroids; + + Assert(index >= 0 && index < MAX_ASTEROID_OBJS); + Assert(!Asteroid_objs[index].flags & ASTEROID_OBJ_USED); + + Asteroid_objs[index].flags = 0; + Asteroid_objs[index].objnum = objnum; + list_append(&Asteroid_obj_list, &Asteroid_objs[index]); + Asteroid_objs[index].flags |= ASTEROID_OBJ_USED; + + return index; +} + +// --------------------------------------------------- +// missle_obj_list_remove() +// +// Function to remove a node from the Asteroid_obj_list. Only +// called from weapon_delete() +void asteroid_obj_list_remove(object * obj) +{ + int index = obj->instance; + + Assert(index >= 0 && index < MAX_ASTEROID_OBJS); + Assert(Asteroid_objs[index].flags & ASTEROID_OBJ_USED); + + list_remove(&Asteroid_obj_list, &Asteroid_objs[index]); + Asteroid_objs[index].flags = 0; +} + + +// Prevent speed from getting too huge so it's hard to catch up to an asteroid. +float asteroid_cap_speed(int asteroid_info_index, float speed) +{ + float max, double_max; + + max = Asteroid_info[asteroid_info_index].max_speed; + double_max = max * 2; + + while (speed > double_max){ + speed *= 0.5f; + } + + if (speed > max){ + speed *= 0.75f; + } + + return speed; +} + +// Returns whether position is inside inner bounding volume +// sum together the following: 1 inside x, 2 inside y, 4 inside z +// inside only when sum = 7 +int asteroid_in_inner_bound_with_axes(asteroid_field *asfieldp, vector *pos, float delta) +{ + Assert(asfieldp->has_inner_bound); + + int rval = 0; + if ( (pos->x > asfieldp->inner_min_bound.x - delta) && (pos->x < asfieldp->inner_max_bound.x + delta) ) { + rval += 1; + } + + if ( (pos->y > asfieldp->inner_min_bound.y - delta) && (pos->y < asfieldp->inner_max_bound.y + delta) ) { + rval += 2; + } + + if ( (pos->z > asfieldp->inner_min_bound.z - delta) && (pos->z < asfieldp->inner_max_bound.z + delta) ) { + rval += 4; + } + + return rval; +} + +// check if asteroid is within inner bound +// return 0 if not inside or no inner bound, 1 if inside inner bound +int asteroid_in_inner_bound(asteroid_field *asfieldp, vector *pos, float delta) { + + if (!asfieldp->has_inner_bound) { + return 0; + } + + return (asteroid_in_inner_bound_with_axes(asfieldp, pos, delta) == 7); +} + +// repositions asteroid outside the inner box on all 3 axes +// moves to the other side of the inner box a distance delta from edge of box +void inner_bound_pos_fixup(asteroid_field *asfieldp, vector *pos) +{ + if (!asteroid_in_inner_bound(asfieldp, pos, 0)) { + return; + } + + float dist1, dist2; + int axis; + + for (axis=0; axis<3; axis++) { + dist1 = pos->a1d[axis] - asfieldp->inner_min_bound.a1d[axis]; + dist2 = asfieldp->inner_max_bound.a1d[axis] - pos->a1d[axis]; + Assert(dist1 >= 0 && dist2 >= 0); + + if (dist1 < dist2) { + pos->a1d[axis] = asfieldp->inner_max_bound.a1d[axis] + dist1; + } else { + pos->a1d[axis] = asfieldp->inner_min_bound.a1d[axis] - dist2; + } + } +} + + +// Create a single asteroid +object *asteroid_create(asteroid_field *asfieldp, int asteroid_type, int asteroid_subtype) +{ + int n, objnum; + matrix orient; + object *objp; + asteroid *asp; + asteroid_info *asip; + vector pos, delta_bound; + angles angs; + float radius; + ushort signature; + int rand_base; + + // bogus + if(asfieldp == NULL){ + return NULL; + } + + for (n=0; n= MAX_ASTEROIDS) { + nprintf(("Warning","Could not create asteroid, no more slots left\n")); + return NULL; + } + + if((asteroid_type < 0) || (asteroid_type >= Num_asteroid_types)){ + return NULL; + } + + // HACK: multiplayer asteroid subtype always 0 to keep subtype in sync + if ( Game_mode & GM_MULTIPLAYER) { + asteroid_subtype = 0; + } + + if( (asteroid_subtype < 0) || (asteroid_subtype >= MAX_ASTEROID_POFS)){ + return NULL; + } + + asip = &Asteroid_info[asteroid_type]; + + // bogus + if(asip->modelp[asteroid_subtype] == NULL){ + return NULL; + } + + asp = &Asteroids[n]; + asp->type = asteroid_type; + asp->asteroid_subtype = asteroid_subtype; + asp->flags = 0; + asp->flags |= AF_USED; + asp->check_for_wrap = timestamp_rand(0, ASTEROID_CHECK_WRAP_TIMESTAMP); + asp->check_for_collide = timestamp_rand(0, ASTEROID_UPDATE_COLLIDE_TIMESTAMP); + asp->final_death_time = timestamp(-1); + asp->collide_objnum = -1; + asp->collide_objsig = -1; + asp->target_objnum = -1; + + radius = model_get_radius(asip->model_num[asteroid_subtype]); + + vm_vec_sub(&delta_bound, &asfieldp->max_bound, &asfieldp->min_bound); + + // for multiplayer, we want to do a static_rand so that everything behaves the same on all machines + signature = 0; + rand_base = 0; + if ( Game_mode & GM_NORMAL ) { + pos.x = asfieldp->min_bound.x + delta_bound.x * frand(); + pos.y = asfieldp->min_bound.y + delta_bound.y * frand(); + pos.z = asfieldp->min_bound.z + delta_bound.z * frand(); + + inner_bound_pos_fixup(asfieldp, &pos); + // vm_set_identity(&orient); + angs.p = frand() * 2*PI; + angs.b = frand() * 2*PI; + angs.h = frand() * 2*PI; + } else { + signature = multi_assign_network_signature( MULTI_SIG_ASTEROID ); + rand_base = signature; + + pos.x = asfieldp->min_bound.x + delta_bound.x * static_randf( rand_base++ ); + pos.y = asfieldp->min_bound.y + delta_bound.y * static_randf( rand_base++ ); + pos.z = asfieldp->min_bound.z + delta_bound.z * static_randf( rand_base++ ); + + inner_bound_pos_fixup(asfieldp, &pos); + // vm_set_identity(&orient); + angs.p = static_randf( rand_base++ ) * 2*PI; + angs.b = static_randf( rand_base++ ) * 2*PI; + angs.h = static_randf( rand_base++ ) * 2*PI; + } + + vm_angles_2_matrix(&orient, &angs); + + objnum = obj_create( OBJ_ASTEROID, -1, n, &orient, &pos, radius, OF_RENDERS | OF_PHYSICS | OF_COLLIDES); + // mprintf(("Framecount: %d asteroid create: obj = %d\n", Framecount, objnum)); + + if ( (objnum == -1) || (objnum >= MAX_OBJECTS) ) { + mprintf(("Couldn't create asteroid -- out of object slots\n")); + return NULL; + } + + asp->objnum = objnum; + + // Add to Asteroid_used_list + asteroid_obj_list_add(objnum); + + objp = &Objects[objnum]; + + if ( Game_mode & GM_MULTIPLAYER ){ + objp->net_signature = signature; + } + + Num_asteroids++; + + if (radius < 1.0) { + radius = 1.0f; + } + + vector rotvel; + if ( Game_mode & GM_NORMAL ) { + vm_vec_rand_vec_quick(&rotvel); + vm_vec_scale(&rotvel, frand()/4.0f + 0.1f); + objp->phys_info.rotvel = rotvel; + vm_vec_rand_vec_quick(&objp->phys_info.vel); + } else { + static_randvec( rand_base++, &rotvel ); + vm_vec_scale(&rotvel, static_randf(rand_base++)/4.0f + 0.1f); + objp->phys_info.rotvel = rotvel; + static_randvec( rand_base++, &objp->phys_info.vel ); + } + + + float speed; + + if ( Game_mode & GM_NORMAL ) { + speed = asteroid_cap_speed(asteroid_type, asfieldp->speed*frand_range(0.5f + (float) Game_skill_level/NUM_SKILL_LEVELS, 2.0f + (float) (2*Game_skill_level)/NUM_SKILL_LEVELS)); + } else { + speed = asteroid_cap_speed(asteroid_type, asfieldp->speed*static_randf_range(rand_base++, 0.5f + (float) Game_skill_level/NUM_SKILL_LEVELS, 2.0f + (float) (2*Game_skill_level)/NUM_SKILL_LEVELS)); + } + + vm_vec_scale(&objp->phys_info.vel, speed); + objp->phys_info.desired_vel = objp->phys_info.vel; + + // blow out his reverse thrusters. Or drag, same thing. + objp->phys_info.rotdamp = 10000.0f; + objp->phys_info.side_slip_time_const = 10000.0f; + objp->phys_info.flags |= (PF_REDUCED_DAMP | PF_DEAD_DAMP); // set damping equal for all axis and not changable + + // Fill in the max_vel field, so the collision pair stuff knows + // how fast this can move maximum in order to throw out collisions. + // This is in local coordinates, so Z is forward velocity. + objp->phys_info.max_vel.x = 0.0f; + objp->phys_info.max_vel.y = 0.0f; + objp->phys_info.max_vel.z = vm_vec_mag(&objp->phys_info.desired_vel); + + objp->phys_info.mass = asip->modelp[asteroid_subtype]->rad * 700.0f; + objp->phys_info.I_body_inv.rvec.x = 1.0f / (objp->phys_info.mass*asip->modelp[asteroid_subtype]->rad); + objp->phys_info.I_body_inv.uvec.y = objp->phys_info.I_body_inv.rvec.x; + objp->phys_info.I_body_inv.fvec.z = objp->phys_info.I_body_inv.rvec.x; + objp->hull_strength = asip->initial_hull_strength * (0.8f + (float)Game_skill_level/NUM_SKILL_LEVELS)/2.0f; + + // ensure vel is valid + Assert( !vm_is_vec_nan(&objp->phys_info.vel) ); + + // assign a persistant sound to the asteroid +// obj_snd_assign(objnum, SND_ASTEROID); + + return objp; +} + +// Create asteroids when parent_objp blows up. +void asteroid_sub_create(object *parent_objp, int asteroid_type, vector *relvec) +{ + object *new_objp; + float speed; + + Assert(parent_objp->type == OBJ_ASTEROID); + int subtype = Asteroids[parent_objp->instance].asteroid_subtype; + new_objp = asteroid_create(&Asteroid_field, asteroid_type, subtype); + + if (new_objp == NULL) + return; + + if ( MULTIPLAYER_MASTER ){ + send_asteroid_create( new_objp, parent_objp, asteroid_type, relvec ); + } + + // Now, bash some values. + vm_vec_scale_add(&new_objp->pos, &parent_objp->pos, relvec, 0.5f * parent_objp->radius); + float parent_speed = vm_vec_mag_quick(&parent_objp->phys_info.vel); + + if ( parent_speed < 0.1f ) { + parent_speed = vm_vec_mag_quick(&Asteroid_field.vel); + } + + new_objp->phys_info.vel = parent_objp->phys_info.vel; + if ( Game_mode & GM_NORMAL ) + speed = asteroid_cap_speed(asteroid_type, (frand() + 2.0f) * parent_speed); + else + speed = asteroid_cap_speed(asteroid_type, (static_randf(new_objp->net_signature)+2.0f) * parent_speed); + + vm_vec_scale_add2(&new_objp->phys_info.vel, relvec, speed); + if (vm_vec_mag_quick(&new_objp->phys_info.vel) > 80.0f) + vm_vec_scale(&new_objp->phys_info.vel, 0.5f); + + new_objp->phys_info.desired_vel = new_objp->phys_info.vel; + vm_vec_scale_add(&new_objp->last_pos, &new_objp->pos, &new_objp->phys_info.vel, -flFrametime); + // DA: 4/22/98 We get next line for free when new object (in obj_create_list) is merged. + // this line gives too many collision pairs. + // asteroid_update_collide(new_objp); +} + +// Load in an asteroid model +void asteroid_load(int asteroid_info_index, int asteroid_subtype) +{ + asteroid_info *asip; +// int pof_index; + + asip = &Asteroid_info[asteroid_info_index]; + + // pick one of MAX_ASTEROID_POFS models + // LOAD ALL TEXTURES USED +// static int asteroid_pof_index = rand() % MAX_ASTEROID_POFS; +// if (Asteroid_field.debris_genre == DG_ASTEROID) { +// pof_index = asteroid_pof_index; +// } else { +// // only 1 pof for ship debris type +// pof_index = 0; +// } + + asip->model_num[asteroid_subtype] = model_load( asip->pof_files[asteroid_subtype], 0, NULL ); + + if (asip->model_num[asteroid_subtype] > -1) { + asip->modelp[asteroid_subtype] = model_get(asip->model_num[asteroid_subtype]); + + // Stuff detail level distances. + for (int i = 0; i < asip->num_detail_levels; i++) { + asip->modelp[asteroid_subtype]->detail_depth[i] = i2fl(asip->detail_distance[i]); + } + } +} + +// randomly choose a debris model within the current group +int get_debris_from_same_group(int index) { + int group_base, group_offset; + + group_base = (index / 3) * 3; + group_offset = index - group_base; + + // group base + offset + // offset is formed by adding 1 or 2 (since 3 in model group) and mapping back in the 0-2 range + return group_base + ((group_offset + rand()%2 + 1) % 3); +} + +// returns a weight that depends on asteroid size. +// the weight is then used to determine the frequencty of different sizes of ship debris +int get_debris_weight(int ship_debris_index) +{ + switch (ship_debris_index) { + case DEBRIS_TERRAN_SMALL: + case DEBRIS_VASUDAN_SMALL: + case DEBRIS_SHIVAN_SMALL: + return SMALL_DEBRIS_WEIGHT; + break; + + case DEBRIS_TERRAN_MEDIUM: + case DEBRIS_VASUDAN_MEDIUM: + case DEBRIS_SHIVAN_MEDIUM: + return MEDIUM_DEBRIS_WEIGHT; + break; + + case DEBRIS_TERRAN_LARGE: + case DEBRIS_VASUDAN_LARGE: + case DEBRIS_SHIVAN_LARGE: + return LARGE_DEBRIS_WEIGHT; + break; + + default: + Int3(); + return 1; + break; + } +} + +// Create all the asteroids for the mission, called from +void asteroid_create_all() +{ + int i, idx; + + // ship_debris_odds_table keeps track of debris type of the next debris piece + // each different type (size) of debris piece has a diffenent weight, smaller weighted more heavily than larger. + // choose next type from table ship_debris_odds_table by rand()%max_weighted_range, + // the first column in ship_debris_odds_table random number *below* which the debris type in the second column is selected. + int ship_debris_odds_table[3][2]; + int max_weighted_range = 0; + + if (!Asteroids_enabled) + return; + + if (Asteroid_field.num_initial_asteroids <= 0 ) { + return; + } + + int max_asteroids = Asteroid_field.num_initial_asteroids; // * (1.0f - 0.1f*(MAX_DETAIL_LEVEL-Detail.asteroid_density))); + + int num_debris_types = 0; + + // get number of ship debris types + if (Asteroid_field.debris_genre == DG_SHIP) { + for (idx=0; idx<3; idx++) { + if (Asteroid_field.field_debris_type[idx] != -1) { + num_debris_types++; + } + } + + // Calculate the odds table + for (idx=0; idx 0); + + int rand_choice = rand() % max_weighted_range; + + for (idx=0; idx<3; idx++) { + // for ship debris, choose type according to odds table + if (rand_choice < ship_debris_odds_table[idx][0]) { + asteroid_create(&Asteroid_field, ship_debris_odds_table[idx][1], 0); + break; + } + } + } + } +} + +// Init asteriod system for the level, called from game_level_init() +void asteroid_level_init() +{ + Asteroid_field.num_initial_asteroids=0; + Num_asteroids = 0; + Next_asteroid_throw = timestamp(1); + asteroid_obj_list_init(); +} + +// return !0 if asteroid should be wrapped, 0 otherwise. Multiplayer clients will always return +// 0 from this function. We will force a wrap on the clients when server tells us +int asteroid_should_wrap(object *objp, asteroid_field *asfieldp) +{ + if ( MULTIPLAYER_CLIENT ) + return 0; + + if (objp->pos.x < asfieldp->min_bound.x) { + return 1; + } + + if (objp->pos.y < asfieldp->min_bound.y) { + return 1; + } + + if (objp->pos.z < asfieldp->min_bound.z) { + return 1; + } + + if (objp->pos.x > asfieldp->max_bound.x) { + return 1; + } + + if (objp->pos.y > asfieldp->max_bound.y) { + return 1; + } + + if (objp->pos.z > asfieldp->max_bound.z) { + return 1; + } + + // check against inner bound + if (asfieldp->has_inner_bound) { + if ( (objp->pos.x > asfieldp->inner_min_bound.x) && (objp->pos.x < asfieldp->inner_max_bound.x) + && (objp->pos.y > asfieldp->inner_min_bound.y) && (objp->pos.y < asfieldp->inner_max_bound.y) + && (objp->pos.z > asfieldp->inner_min_bound.z) && (objp->pos.z < asfieldp->inner_max_bound.z) ) { + + return 1; + } + } + + return 0; +} + +// Wrap an asteroid from one end of the asteroid field to the other +void asteroid_wrap_pos(object *objp, asteroid_field *asfieldp) +{ + if (objp->pos.x < asfieldp->min_bound.x) { + objp->pos.x = asfieldp->max_bound.x + (objp->pos.x - asfieldp->min_bound.x); + } + + if (objp->pos.y < asfieldp->min_bound.y) { + objp->pos.y = asfieldp->max_bound.y + (objp->pos.y - asfieldp->min_bound.y); + } + + if (objp->pos.z < asfieldp->min_bound.z) { + objp->pos.z = asfieldp->max_bound.z + (objp->pos.z - asfieldp->min_bound.z); + } + + if (objp->pos.x > asfieldp->max_bound.x) { + objp->pos.x = asfieldp->min_bound.x + (objp->pos.x - asfieldp->max_bound.x); + } + + if (objp->pos.y > asfieldp->max_bound.y) { + objp->pos.y = asfieldp->min_bound.y + (objp->pos.y - asfieldp->max_bound.y); + } + + if (objp->pos.z > asfieldp->max_bound.z) { + objp->pos.z = asfieldp->min_bound.z + (objp->pos.z - asfieldp->max_bound.z); + } + + // wrap on inner bound, check all 3 axes as needed, use of rand ok for multiplayer with send_asteroid_throw() + inner_bound_pos_fixup(asfieldp, &objp->pos); + +} + + +// return !0 if this asteroid is a target for any ship, otherwise return 0 +int asteroid_is_targeted(object *objp) +{ + ship_obj *so; + object *ship_objp; + int asteroid_obj_index; + + asteroid_obj_index=OBJ_INDEX(objp); + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + ship_objp = &Objects[so->objnum]; + if ( Ai_info[Ships[ship_objp->instance].ai_index].target_objnum == asteroid_obj_index ) { + return 1; + } + } + + return 0; +} + +// Create an asteroid that will hit object *objp in delta_time seconds +void asteroid_aim_at_target(object *objp, object *asteroid_objp, float delta_time) +{ + vector predicted_center_pos; + vector rand_vec; + float speed; + + vm_vec_scale_add(&predicted_center_pos, &objp->pos, &objp->phys_info.vel, delta_time); + vm_vec_rand_vec_quick(&rand_vec); + vm_vec_scale_add2(&predicted_center_pos, &rand_vec, objp->radius/2.0f); + + vm_vec_add2(&rand_vec, &objp->orient.fvec); + if (vm_vec_mag_quick(&rand_vec) < 0.1f) + vm_vec_add2(&rand_vec, &objp->orient.rvec); + vm_vec_normalize(&rand_vec); + + speed = Asteroid_info[0].max_speed * (frand()/2.0f + 0.5f); + + vm_vec_copy_scale(&asteroid_objp->phys_info.vel, &rand_vec, -speed); + asteroid_objp->phys_info.desired_vel = asteroid_objp->phys_info.vel; + vm_vec_scale_add(&asteroid_objp->pos, &predicted_center_pos, &asteroid_objp->phys_info.vel, -delta_time); + vm_vec_scale_add(&asteroid_objp->last_pos, &asteroid_objp->pos, &asteroid_objp->phys_info.vel, -flFrametime); +} + +int Max_incoming_asteroids[NUM_SKILL_LEVELS] = {3, 4, 5, 7, 10}; + +// Call once per frame to maybe throw an asteroid at a ship. +// "count" asteroids already targeted on +void maybe_throw_asteroid(int count) +{ + if (!timestamp_elapsed(Next_asteroid_throw)) { + return; + } + + if (Asteroid_throw_objnum == -1) { + return; + } + + nprintf(("AI", "Incoming asteroids: %i\n", count)); + + if (count > Max_incoming_asteroids[Game_skill_level]) + return; + + Next_asteroid_throw = timestamp(1000 + 1200 * count/(Game_skill_level+1)); + + ship_obj *so; + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + object *A = &Objects[so->objnum]; + if (so->objnum == Asteroid_throw_objnum) { + int subtype = rand() % 3; + while (Asteroid_field.field_debris_type[subtype] == -1) { + subtype = (subtype + 1) % 3; + } + object *objp = asteroid_create(&Asteroid_field, ASTEROID_TYPE_BIG, subtype); + if (objp != NULL) { + asteroid_aim_at_target(A, objp, ASTEROID_MIN_COLLIDE_TIME + frand() * 20.0f); + + // if asteroid is inside inner bound, kill it + if (asteroid_in_inner_bound(&Asteroid_field, &objp->pos, 0.0f)) { + objp->flags |= OF_SHOULD_BE_DEAD; + } else { + Asteroids[objp->instance].target_objnum = so->objnum; + // DA: 4/22/98 We get next line for free when new object (in obj_create_list) is merged. + // this line gives too many collision pairs. + // asteroid_update_collide(objp); + + if ( MULTIPLAYER_MASTER ) { + send_asteroid_throw( objp ); + } + } + } + + return; + } + } + +} + +void asteroid_delete( object * obj ) +{ + int num; + asteroid *asp; + + num = obj->instance; + Assert( Asteroids[num].objnum == OBJ_INDEX(obj)); + + asp = &Asteroids[num]; + + Assert( Num_asteroids >= 0 ); + + asp->flags = 0; + Num_asteroids--; + + // Delete asteroid from Asteroid_used_list + asteroid_obj_list_remove( obj ); +} + +// See if we should reposition the asteroid. Only reposition if oustide the bounding volume and +// the player isn't looking towards the asteroid. +void asteroid_maybe_reposition(object *objp, asteroid_field *asfieldp) +{ + // passive field does not wrap + if (asfieldp->field_type == FT_PASSIVE) { + return; + } + + if ( asteroid_should_wrap(objp, asfieldp) ) { + vector vec_to_asteroid, old_asteroid_pos, old_vel; + float dot, dist; + + old_asteroid_pos = objp->pos; + old_vel = objp->phys_info.vel; + + //nprintf(("AI", "Frame %i, reposition #%i\n", Framecount, objp-Objects)); + + // don't wrap asteroid if it is a target of some ship + if ( !asteroid_is_targeted(objp) ) { + + // only wrap if player won't see asteroid disappear/reverse direction + dist = vm_vec_normalized_dir(&vec_to_asteroid, &objp->pos, &Eye_position); + dot = vm_vec_dot(&Eye_matrix.fvec, &vec_to_asteroid); + + if ((dot < 0.7f) || (dist > 3000.0f)) { + if (Num_asteroids > MAX_ASTEROIDS-10) { + objp->flags |= OF_SHOULD_BE_DEAD; + } else { + // check to ensure player won't see asteroid appear either + asteroid_wrap_pos(objp, asfieldp); + Asteroids[objp->instance].target_objnum = -1; + + vm_vec_normalized_dir(&vec_to_asteroid, &objp->pos, &Eye_position); + dot = vm_vec_dot(&Eye_matrix.fvec, &vec_to_asteroid); + dist = vm_vec_dist_quick(&objp->pos, &Eye_position); + + if (( dot > 0.7f) && (dist < 3000.0f)) { + // player would see asteroid pop out other side, so reverse velocity instead of wrapping + objp->pos = old_asteroid_pos; + vm_vec_copy_scale(&objp->phys_info.vel, &old_vel, -1.0f); + objp->phys_info.desired_vel = objp->phys_info.vel; + Asteroids[objp->instance].target_objnum = -1; + } + + // update last pos (after vel is known) + vm_vec_scale_add(&objp->last_pos, &objp->pos, &objp->phys_info.vel, -flFrametime); + + asteroid_update_collide(objp); + + if ( MULTIPLAYER_MASTER ) + send_asteroid_throw( objp ); + } + } + } + } +} + +void lerp(float *goal, float f1, float f2, float scale) +{ + *goal = (f2 - f1) * scale + f1; +} + +void asteroid_process_pre( object *objp, float frame_time) +{ + if (Asteroids_enabled) { + vector *v, *vv; + + v = &objp->phys_info.vel; + vv = &objp->phys_info.desired_vel; + + //nprintf(("AI", "Frm %i: Obj #%2i: Hull: %5.1f Vel: %5.1f %5.1f %5.1f Des: %5.1f %5.1f %5.1f\n", Framecount, objp-Objects, objp->hull_strength, v->x, v->y, v->z, vv->x, vv->y, vv->z)); + + // Make vel chase desired_vel + lerp(&objp->phys_info.vel.x, objp->phys_info.vel.x, objp->phys_info.desired_vel.x, flFrametime); + lerp(&objp->phys_info.vel.y, objp->phys_info.vel.y, objp->phys_info.desired_vel.y, flFrametime); + lerp(&objp->phys_info.vel.z, objp->phys_info.vel.z, objp->phys_info.desired_vel.z, flFrametime); + } +} + +#ifndef PLAT_UNIX +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +#endif +int asteroid_check_collision(object *pasteroid, object *other_obj, vector *hitpos, collision_info_struct *asteroid_hit_info) +{ + if (!Asteroids_enabled) { + return 0; + } + + mc_info mc; + int num, asteroid_subtype; + + Assert( pasteroid->type == OBJ_ASTEROID ); + + num = pasteroid->instance; + Assert( num >= 0 ); + + Assert( Asteroids[num].objnum == OBJ_INDEX(pasteroid)); + asteroid_subtype = Asteroids[num].asteroid_subtype; + + // asteroid_hit_info NULL -- asteroid-weapon collision + if ( asteroid_hit_info == NULL ) { + // asteroid weapon collision + Assert( other_obj->type == OBJ_WEAPON ); + mc.model_num = Asteroid_info[Asteroids[num].type].model_num[asteroid_subtype]; // Fill in the model to check + model_clear_instance( mc.model_num ); + mc.orient = &pasteroid->orient; // The object's orient + mc.pos = &pasteroid->pos; // The object's position + mc.p0 = &other_obj->last_pos; // Point 1 of ray to check + mc.p1 = &other_obj->pos; // Point 2 of ray to check + mc.flags = (MC_CHECK_MODEL); + + if (model_collide(&mc)) + *hitpos = mc.hit_point_world; + + return mc.num_hits; + } + + // asteroid ship collision -- use asteroid_hit_info to calculate physics + object *ship_obj = other_obj; + Assert( ship_obj->type == OBJ_SHIP ); + + object* heavy = asteroid_hit_info->heavy; + object* light = asteroid_hit_info->light; + object *heavy_obj = heavy; + object *light_obj = light; + + vector zero, p0, p1; + vm_vec_zero( &zero ); + vm_vec_sub( &p0, &light->last_pos, &heavy->last_pos ); + vm_vec_sub( &p1, &light->pos, &heavy->pos ); + + mc.pos = &zero; // The object's position + mc.p0 = &p0; // Point 1 of ray to check + mc.p1 = &p1; // Point 2 of ray to check + + // find the light object's position in the heavy object's reference frame at last frame and also in this frame. + vector p0_temp, p0_rotated; + + // Collision detection from rotation enabled if at rotaion is less than 30 degree in frame + // This should account for all ships + if ( (vm_vec_mag_squared( &heavy->phys_info.rotvel ) * flFrametime*flFrametime) < (PI*PI/36) ) { + // collide_rotate calculate (1) start position and (2) relative velocity + asteroid_hit_info->collide_rotate = 1; + vm_vec_rotate( &p0_temp, &p0, &heavy->last_orient ); + vm_vec_unrotate( &p0_rotated, &p0_temp, &heavy->orient ); + mc.p0 = &p0_rotated; // Point 1 of ray to check + vm_vec_sub( &asteroid_hit_info->light_rel_vel, &p1, &p0_rotated ); + vm_vec_scale( &asteroid_hit_info->light_rel_vel, 1/flFrametime ); + // HACK - this applies to big ships warping in/out of asteroid fields - not sure what it does + if (vm_vec_mag(&asteroid_hit_info->light_rel_vel) > 300) { + // nprintf(("Physics", "Asteroid type %d\n", Asteroids[asteroid_hit_info->light->instance].type)); + asteroid_hit_info->collide_rotate = 0; + vm_vec_sub( &asteroid_hit_info->light_rel_vel, &light->phys_info.vel, &heavy->phys_info.vel ); + } + } else { + asteroid_hit_info->collide_rotate = 0; + vm_vec_sub( &asteroid_hit_info->light_rel_vel, &light->phys_info.vel, &heavy->phys_info.vel ); + } + + int mc_ret_val = 0; + + if ( asteroid_hit_info->heavy == ship_obj ) { // ship is heavier, so asteroid is sphere. Check sphere collision against ship poly model + mc.model_num = Ships[ship_obj->instance].modelnum; // Fill in the model to check + mc.orient = &ship_obj->orient; // The object's orient + mc.radius = pasteroid->radius; + mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE); + + // copy important data + int copy_flags = mc.flags; // make a copy of start end positions of sphere in big ship RF + vector copy_p0, copy_p1; + copy_p0 = *mc.p0; + copy_p1 = *mc.p1; + + // first test against the sphere - if this fails then don't do any submodel tests + mc.flags = MC_ONLY_SPHERE | MC_CHECK_SPHERELINE; + + int submodel_list[MAX_ROTATING_SUBMODELS]; + int num_rotating_submodels = 0; + polymodel *pm; + + ship_model_start(ship_obj); + + if (model_collide(&mc)) { + + // Set earliest hit time + asteroid_hit_info->hit_time = FLT_MAX; + + // Do collision the cool new way + if ( asteroid_hit_info->collide_rotate ) { + // We collide with the sphere, find the list of rotating submodels and test one at a time + model_get_rotating_submodel_list(submodel_list, &num_rotating_submodels, heavy_obj); + + // Get polymodel and turn off all rotating submodels, collide against only 1 at a time. + pm = model_get(Ships[heavy_obj->instance].modelnum); + + // turn off all rotating submodels and test for collision + for (int i=0; isubmodel[submodel_list[i]].blown_off = 1; + } + + // reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE + mc.flags = copy_flags | MC_SUBMODEL_INSTANCE; + + + // check each submodel in turn + for (int i=0; isubmodel[submodel_list[i]].blown_off = 0; + + // set angles for last frame (need to set to prev to get p0) + angles copy_angles = pm->submodel[submodel_list[i]].angs; + + // find the start and end positions of the sphere in submodel RF + pm->submodel[submodel_list[i]].angs = pm->submodel[submodel_list[i]].sii->prev_angs; + world_find_model_point(&p0, &light_obj->last_pos, pm, submodel_list[i], &heavy_obj->last_orient, &heavy_obj->last_pos); + + pm->submodel[submodel_list[i]].angs = copy_angles; + world_find_model_point(&p1, &light_obj->pos, pm, submodel_list[i], &heavy_obj->orient, &heavy_obj->pos); + + mc.p0 = &p0; + mc.p1 = &p1; + // mc.pos = zero // in submodel RF + + mc.orient = &vmd_identity_matrix; + mc.submodel_num = submodel_list[i]; + + if ( model_collide(&mc) ) { + if ( mc.hit_dist < asteroid_hit_info->hit_time ) { + mc_ret_val = 1; + + // set up asteroid_hit_info common + set_hit_struct_info(asteroid_hit_info, &mc, SUBMODEL_ROT_HIT); + + // set up asteroid_hit_info for rotating submodel + if (asteroid_hit_info->edge_hit == 0) { + model_find_obj_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel); + } + + // find position in submodel RF of light object at collison + vector int_light_pos, diff; + vm_vec_sub(&diff, mc.p1, mc.p0); + vm_vec_scale_add(&int_light_pos, mc.p0, &diff, mc.hit_dist); + model_find_world_point(&asteroid_hit_info->light_collision_cm_pos, &int_light_pos, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero); + } + } + // Don't look at this submodel again + pm->submodel[submodel_list[i]].blown_off = 1; + } + + } + + // Recover and do usual ship_ship collision, but without rotating submodels + mc.flags = copy_flags; + *mc.p0 = copy_p0; + *mc.p1 = copy_p1; + mc.orient = &heavy_obj->orient; + + // usual ship_ship collision test + if ( model_collide(&mc) ) { + // check if this is the earliest hit + if (mc.hit_dist < asteroid_hit_info->hit_time) { + mc_ret_val = 1; + + set_hit_struct_info(asteroid_hit_info, &mc, SUBMODEL_NO_ROT_HIT); + + // get collision normal if not edge hit + if (asteroid_hit_info->edge_hit == 0) { + model_find_obj_dir(&asteroid_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel); + } + + // find position in submodel RF of light object at collison + vector diff; + vm_vec_sub(&diff, mc.p1, mc.p0); + vm_vec_scale_add(&asteroid_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist); + + } + } + + ship_model_stop( ship_obj ); + } + + } else { + // Asteroid is heavier obj + mc.model_num = Asteroid_info[Asteroids[num].type].model_num[asteroid_subtype]; // Fill in the model to check + model_clear_instance( mc.model_num ); + mc.orient = &pasteroid->orient; // The object's orient + mc.radius = model_get_core_radius( Ships[ship_obj->instance].modelnum ); + + // check for collision between asteroid model and ship sphere + mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE); + + mc_ret_val = model_collide(&mc); + + if (mc_ret_val) { + set_hit_struct_info(asteroid_hit_info, &mc, SUBMODEL_NO_ROT_HIT); + + // set normal if not edge hit + if ( !asteroid_hit_info->edge_hit ) { + vm_vec_unrotate(&asteroid_hit_info->collision_normal, &mc.hit_normal, &heavy->orient); + } + + // find position in submodel RF of light object at collison + vector diff; + vm_vec_sub(&diff, mc.p1, mc.p0); + vm_vec_scale_add(&asteroid_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist); + + } + } + + + if ( mc_ret_val ) { + + // SET PHYSICS PARAMETERS + // already have (hitpos - heavy) and light_cm_pos + // get heavy cm pos - already have light_cm_pos + asteroid_hit_info->heavy_collision_cm_pos = zero; + + // get r_heavy and r_light + asteroid_hit_info->r_heavy = asteroid_hit_info->hit_pos; + vm_vec_sub(&asteroid_hit_info->r_light, &asteroid_hit_info->hit_pos, &asteroid_hit_info->light_collision_cm_pos); + + // set normal for edge hit + if ( asteroid_hit_info->edge_hit ) { + vm_vec_copy_normalize(&asteroid_hit_info->collision_normal, &asteroid_hit_info->r_light); + vm_vec_negate(&asteroid_hit_info->collision_normal); + } + + // get world hitpos + vm_vec_add(hitpos, &asteroid_hit_info->heavy->pos, &asteroid_hit_info->r_heavy); + + return 1; + } else { + // no hit + return 0; + } +} +#ifndef PLAT_UNIX +#pragma warning ( pop ) +#endif + + +void asteroid_render(object * obj) +{ + if (Asteroids_enabled) { + int num; + polymodel *pm; + asteroid *asp; + + pm = NULL; + num = obj->instance; + + Assert((num >= 0) && (num < MAX_ASTEROIDS)); + asp = &Asteroids[num]; + + Assert( asp->flags & AF_USED ); + + model_clear_instance( Asteroid_info[asp->type].model_num[asp->asteroid_subtype]); + model_render(Asteroid_info[asp->type].model_num[asp->asteroid_subtype], &obj->orient, &obj->pos, MR_NORMAL|MR_IS_ASTEROID, OBJ_INDEX(obj) ); // Replace MR_NORMAL with 0x07 for big yellow blobs + } +} + +// Create a normalized vector generally in the direction from *hitpos to other_obj->pos +void asc_get_relvec(vector *relvec, object *other_obj, vector *hitpos) +{ + vector tvec, rand_vec; + int count = 0; + + vm_vec_normalized_dir(&tvec, &other_obj->pos, hitpos); + + // Try up to three times to get a good vector. + while (count++ < 3) { + vm_vec_rand_vec_quick(&rand_vec); + vm_vec_add(relvec, &tvec, &rand_vec); + float mag = vm_vec_mag_quick(relvec); + if ((mag > 0.2f) && (mag < 1.7f)) + break; + } + + vm_vec_normalize_quick(relvec); +} + +// return multiplier on asteroid radius for fireball +float asteroid_get_fireball_scale_multiplier(int num) +{ + if (Asteroids[num].flags & AF_USED) { + + switch(Asteroids[num].type) { + case ASTEROID_TYPE_BIG: + return 1.5f; + break; + + default: + return 1.0f; + break; + } + } + + Int3(); // this should not happen. asteroid should be used. + return 1.0f; +} + + +// create asteroid explosion +// exit: expected time for explosion anim to last, in seconds +float asteroid_create_explosion(object *objp) +{ + int fireball_objnum; + float explosion_life, fireball_scale_multiplier; + + fireball_scale_multiplier = asteroid_get_fireball_scale_multiplier(objp->instance); + + fireball_objnum = fireball_create( &objp->pos, FIREBALL_ASTEROID, OBJ_INDEX(objp), objp->radius*fireball_scale_multiplier, 0, &objp->phys_info.vel ); + if ( fireball_objnum > -1 ) { + explosion_life = fireball_lifeleft(&Objects[fireball_objnum]); + } else { + explosion_life = 0.0f; + } + + return explosion_life; +} + +// play sound when asteroid explodes +void asteriod_explode_sound(object *objp, int type, int play_loud) +{ + int sound_index = -1; + float range_factor = 1.0f; // how many times sound should traver farther than normal + + switch (type) { + case ASTEROID_TYPE_SMALL: + case ASTEROID_TYPE_MEDIUM: + case DEBRIS_TERRAN_SMALL: + case DEBRIS_TERRAN_MEDIUM: + case DEBRIS_VASUDAN_SMALL: + case DEBRIS_VASUDAN_MEDIUM: + case DEBRIS_SHIVAN_SMALL: + case DEBRIS_SHIVAN_MEDIUM: + sound_index = SND_ASTEROID_EXPLODE_SMALL; + range_factor = 5.0f; + break; + + case ASTEROID_TYPE_BIG: + case DEBRIS_TERRAN_LARGE: + case DEBRIS_VASUDAN_LARGE: + case DEBRIS_SHIVAN_LARGE: + sound_index = SND_ASTEROID_EXPLODE_BIG; + range_factor = 10.0f; + break; + + default: + Int3(); + return; + } + + Assert(sound_index != -1); + + if ( !play_loud ) { + range_factor = 1.0f; + } + + snd_play_3d( &Snds[sound_index], &objp->pos, &Eye_position, objp->radius, NULL, 0, 1.0f, SND_PRIORITY_MUST_PLAY, NULL, range_factor ); +} + +// asteroid_do_area_effect() +// +// Do the area effect for an asteroid exploding +// +// input: asteroid_objp => object pointer to asteriod causing explosion +void asteroid_do_area_effect(object *asteroid_objp) +{ + object *ship_objp; + float damage, blast; + ship_obj *so; + asteroid *asp; + asteroid_info *asip; + + asp = &Asteroids[asteroid_objp->instance]; + asip = &Asteroid_info[asp->type]; + + if ( asip->damage <= 0 ) { // do a quick out if there is no damage to apply + return; + } + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + ship_objp = &Objects[so->objnum]; + + // don't blast navbuoys + if ( ship_get_SIF(ship_objp->instance) & SIF_NAVBUOY ) { + continue; + } + + if ( weapon_area_calc_damage(ship_objp, &asteroid_objp->pos, asip->inner_rad, asip->outer_rad, asip->blast, asip->damage, &blast, &damage, asip->outer_rad) == -1 ) + continue; + + ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage ); + weapon_area_apply_blast(NULL, ship_objp, &asteroid_objp->pos, blast, 0); + } // end for +} + +// Asteroid asteroid_obj was hit. +// Apply damage. Maybe make it break into smaller asteroids. +// input: asteroid_obj => pointer to asteroid object getting hit +// other_obj => object that hit asteroid, can be NULL if asteroid hit by area effect +// hitpos => world position asteroid was hit, can be NULL if hit by area effect +// damage => amount of damage to apply to asteroid +void asteroid_hit( object * asteroid_obj, object * other_obj, vector * hitpos, float damage ) +{ + float explosion_life; + asteroid *asp; + + asp = &Asteroids[asteroid_obj->instance]; + + if (asteroid_obj->flags & OF_SHOULD_BE_DEAD){ + return; + } + + if ( MULTIPLAYER_MASTER ){ + send_asteroid_hit( asteroid_obj, other_obj, hitpos, damage ); + } + + asteroid_obj->hull_strength -= damage; + + //nprintf(("AI", "Asteroid collided with %s, hull = %.2f\n", Object_type_names[other_obj->type], asteroid_obj->hull_strength)); + + if (asteroid_obj->hull_strength < 0.0f) { + if ( asp->final_death_time <= 0 ) { + int play_loud_collision = 0; + + explosion_life = asteroid_create_explosion(asteroid_obj); + if ( asp->collide_objnum == OBJ_INDEX(other_obj) ) { +// play_loud_collision = 1; + } + asteriod_explode_sound(asteroid_obj, asp->type, play_loud_collision); + asteroid_do_area_effect(asteroid_obj); + + asp->final_death_time = timestamp( fl2i(explosion_life*1000.0f)/5 ); // Wait till 30% of vclip time before breaking the asteroid up. + if ( hitpos ) { + asp->death_hit_pos = *hitpos; + } else { + asp->death_hit_pos = asteroid_obj->pos; + // randomize hit pos a bit, otherwise we will get a NULL vector when trying to find direction to toss child asteroids + vector rand_vec; + vm_vec_rand_vec_quick(&rand_vec); + vm_vec_add2(&asp->death_hit_pos, &rand_vec); + } + } + } else if ( other_obj ) { + if ( other_obj->type == OBJ_WEAPON ) { + weapon_info *wip; + wip = &Weapon_info[Weapons[other_obj->instance].weapon_info_index]; + // If the weapon didn't play any impact animation, play custom asteroid impact animation + if ( wip->impact_weapon_expl_index < 0 ) { + particle_create( hitpos, &vmd_zero_vector, 0.0f, Asteroid_impact_explosion_radius, PARTICLE_BITMAP, Asteroid_impact_explosion_ani ); + } + } + } + + // evaluate any relevant player scoring implications + scoring_eval_hit(asteroid_obj,other_obj); +} + +// De-init asteroids, called from game_level_close() +void asteroid_level_close() +{ + int i; + + for (i=0; i=0 && Asteroids[i].objnum < MAX_OBJECTS); + Objects[Asteroids[i].objnum].flags |= OF_SHOULD_BE_DEAD; + } + } + + Asteroid_field.num_initial_asteroids=0; +} + +DCF(asteroids,"Turns asteroids on/off") +{ + if ( Dc_command ) { + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); + if ( Dc_arg_type & ARG_TRUE ) + Asteroids_enabled = 1; + else if ( Dc_arg_type & ARG_FALSE ) + Asteroids_enabled = 0; + else if ( Dc_arg_type & ARG_NONE ) + Asteroids_enabled ^= 1; + } + if ( Dc_help ) + dc_printf( "Usage: asteroids [bool]\nTurns asteroid system on/off. If nothing passed, then toggles it.\n" ); + + if ( Dc_status ) + dc_printf( "asteroids are %s\n", (Asteroids_enabled?"ON":"OFF") ); + +/* + if ((old_asteroids_enabled == 0) && (Asteroids_enabled == 1)) { + asteroid_init(); + } else if ((old_asteroids_enabled == 1) && (Asteroids_enabled == 0)) { + asteroid_uninit(); + } +*/ +} + +void hud_target_asteroid() +{ + int i; + int start_index = 0, end_index = MAX_ASTEROIDS; + + if (Player_ai->target_objnum != -1) { + if (Objects[Player_ai->target_objnum].type == OBJ_ASTEROID) { + start_index = Objects[Player_ai->target_objnum].instance+1; + end_index = start_index-1; + if (end_index < 0) + end_index = MAX_ASTEROIDS; + } + } + + i = start_index; + while (i != end_index) { + if (i == MAX_ASTEROIDS) + i = 0; + + if (Asteroids[i].flags & AF_USED) { + Assert(Objects[Asteroids[i].objnum].type == OBJ_ASTEROID); + set_target_objnum( Player_ai, Asteroids[i].objnum); + break; + } + + i++; + } +} + +// Return the number of active asteroids +int asteroid_count() +{ + return Num_asteroids; +} + +// See if asteroid should split up. We delay splitting up to allow the explosion animation +// to play for a bit. +void asteroid_maybe_break_up(object *asteroid_obj) +{ + asteroid *asp; + + asp = &Asteroids[asteroid_obj->instance]; + + if ( timestamp_elapsed(asp->final_death_time) ) { + vector relvec, vfh, tvec; + + asteroid_obj->flags |= OF_SHOULD_BE_DEAD; + + // multiplayer clients won't go through the following code. asteroid_sub_create will send + // a create packet to the client in the above named function + if ( !MULTIPLAYER_CLIENT ) { + + switch (asp->type) { + case ASTEROID_TYPE_SMALL: + break; + case ASTEROID_TYPE_MEDIUM: + asc_get_relvec(&relvec, asteroid_obj, &asp->death_hit_pos); + asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_SMALL, &relvec); + + vm_vec_normalized_dir(&vfh, &asteroid_obj->pos, &asp->death_hit_pos); + vm_vec_copy_scale(&tvec, &vfh, 2.0f); + vm_vec_sub2(&tvec, &relvec); + asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_SMALL, &tvec); + + break; + case ASTEROID_TYPE_BIG: + asc_get_relvec(&relvec, asteroid_obj, &asp->death_hit_pos); + asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_MEDIUM, &relvec); + + vm_vec_normalized_dir(&vfh, &asteroid_obj->pos, &asp->death_hit_pos); + vm_vec_copy_scale(&tvec, &vfh, 2.0f); + vm_vec_sub2(&tvec, &relvec); + asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_MEDIUM, &tvec); + + while (frand() > 0.6f) { + vector rvec, tvec2; + vm_vec_rand_vec_quick(&rvec); + vm_vec_scale_add(&tvec2, &vfh, &rvec, 0.75f); + asteroid_sub_create(asteroid_obj, ASTEROID_TYPE_SMALL, &tvec2); + } + + break; + + // ship debris does not break up + case DEBRIS_TERRAN_SMALL: + case DEBRIS_TERRAN_MEDIUM: + case DEBRIS_TERRAN_LARGE: + case DEBRIS_VASUDAN_SMALL: + case DEBRIS_VASUDAN_MEDIUM: + case DEBRIS_VASUDAN_LARGE: + case DEBRIS_SHIVAN_SMALL: + case DEBRIS_SHIVAN_MEDIUM: + case DEBRIS_SHIVAN_LARGE: + break; + + default: + Int3(); + } + } + + asp->final_death_time = timestamp(-1); + } +} + +int asteroid_get_random_in_cone(vector *pos, vector *dir, float ang, int danger) +{ + int idx; + vector asteroid_dir; + + // just pick the first asteroid which satisfies our condition + for(idx=0; idx= ang){ + return idx; + } + } + + return -1; +} + +void asteroid_test_collide(object *asteroid_obj, object *ship_obj, mc_info *mc) +{ + float asteroid_ray_dist; + vector asteroid_fvec, terminus; + + // See if ray from asteroid intersects bounding box of escort ship + asteroid_ray_dist = vm_vec_mag_quick(&asteroid_obj->phys_info.desired_vel) * ASTEROID_MIN_COLLIDE_TIME; + asteroid_fvec = asteroid_obj->phys_info.desired_vel; + + if(IS_VEC_NULL(&asteroid_fvec)){ + terminus = asteroid_obj->pos; + } else { + vm_vec_normalize(&asteroid_fvec); + vm_vec_scale_add(&terminus, &asteroid_obj->pos, &asteroid_fvec, asteroid_ray_dist); + } + + Assert(ship_obj->type == OBJ_SHIP); + + ship_model_start(ship_obj); + + mc->model_num = Ships[ship_obj->instance].modelnum; // Fill in the model to check + mc->orient = &ship_obj->orient; // The object's orientation + mc->pos = &ship_obj->pos; // The object's position + mc->p0 = &asteroid_obj->pos; // Point 1 of ray to check + mc->p1 = &terminus; // Point 2 of ray to check +// mc->flags = MC_CHECK_MODEL | MC_ONLY_BOUND_BOX; + mc->flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE; + mc->radius = asteroid_obj->radius; + + model_collide(mc); + + ship_model_stop(ship_obj); +} + +// Return !0 is the asteroid will collide with the escort ship within ASTEROID_MIN_COLLIDE_TIME +// seconds +int asteroid_will_collide(object *asteroid_obj, object *escort_objp) +{ + mc_info mc; + + asteroid_test_collide(asteroid_obj, escort_objp, &mc); + + if ( !mc.num_hits ) { + return 0; + } + + return 1; +} + +// return !0 if we should warn about asteroid hitting ship, otherwise return 0 +int asteroid_valid_ship_to_warn_collide(ship *shipp) +{ + if ( !(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) { + return 0; + } + + if ( shipp->flags & (SF_DYING|SF_DEPART_WARP) ) { + return 0; + } + + if ( (shipp->team != Player_ship->team) || (Player_ship->team == TEAM_TRAITOR) ) { + return 0; + } + + return 1; +} + +// See if asteroid will collide with a large ship on the escort list in the next +// ASTEROID_MIN_COLLIDE_TIME seconds. +void asteroid_update_collide_flag(object *asteroid_objp) +{ + int i, num_escorts, escort_objnum, will_collide=0; + ship *escort_shipp; + asteroid *asp; + + asp = &Asteroids[asteroid_objp->instance]; + asp->collide_objnum = -1; + asp->collide_objsig = -1; + + // multiplayer dogfight + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){ + return; + } + + num_escorts = hud_escort_num_ships_on_list(); + + if ( num_escorts <= 0 ) { + return; + } + + for ( i = 0; i < num_escorts; i++ ) { + escort_objnum = hud_escort_return_objnum(i); + if ( escort_objnum >= 0 ) { + escort_shipp = &Ships[Objects[escort_objnum].instance]; + if ( asteroid_valid_ship_to_warn_collide(escort_shipp) ) { + will_collide = asteroid_will_collide(asteroid_objp, &Objects[escort_objnum]); + if ( will_collide ) { + asp->collide_objnum = escort_objnum; + asp->collide_objsig = Objects[escort_objnum].signature; + } + } + } + } +} + +// ensure that the collide objnum for the asteroid is still valid +void asteroid_verify_collide_objnum(asteroid *asp) +{ + if ( asp->collide_objnum >= 0 ) { + if ( Objects[asp->collide_objnum].signature != asp->collide_objsig ) { + asp->collide_objnum = -1; + asp->collide_objsig = -1; + } + } +} + +void asteroid_process_post(object * obj, float frame_time) +{ + if (Asteroids_enabled) { + int num; + num = obj->instance; + + //Assert( Asteroids[num].objnum == objnum ); + asteroid *asp = &Asteroids[num]; + + // Only wrap if active field + if (Asteroid_field.field_type == FT_ACTIVE) { + if ( timestamp_elapsed(asp->check_for_wrap) ) { + asteroid_maybe_reposition(obj, &Asteroid_field); + asp->check_for_wrap = timestamp(ASTEROID_CHECK_WRAP_TIMESTAMP); + } + } + + asteroid_verify_collide_objnum(asp); + + if ( timestamp_elapsed(asp->check_for_collide) ) { + asteroid_update_collide_flag(obj); + asp->check_for_collide = timestamp(ASTEROID_UPDATE_COLLIDE_TIMESTAMP); + } + + asteroid_maybe_break_up(obj); + } +} + +// return the object number that the asteroid is about to impact +int asteroid_collide_objnum(object *asteroid_objp) +{ + return Asteroids[asteroid_objp->instance].collide_objnum; +} + +// return the time until the asteroid will impact its collide_objnum +float asteroid_time_to_impact(object *asteroid_objp) +{ + float time=-1.0f, total_dist, speed; + asteroid *asp; + mc_info mc; + + asp = &Asteroids[asteroid_objp->instance]; + + if ( asp->collide_objnum < 0 ) { + return time; + } + + asteroid_test_collide(asteroid_objp, &Objects[asp->collide_objnum], &mc); + + if ( mc.num_hits ) { + total_dist = vm_vec_dist(&mc.hit_point_world, &asteroid_objp->pos) - asteroid_objp->radius; + if ( total_dist < 0 ) { + total_dist = 0.0f; + } + speed = vm_vec_mag(&asteroid_objp->phys_info.vel); + time = total_dist/speed; + } + + return time; +} + +// read in a single asteroid section from asteroid.tbl +void asteroid_parse_section() +{ + asteroid_info *asip; + + asip = &Asteroid_info[Num_asteroid_types]; + + required_string("$Name:"); + stuff_string(asip->name, F_NAME, NULL); + + required_string( "$POF file1:" ); + stuff_string_white( asip->pof_files[0] ); + + required_string( "$POF file2:" ); + stuff_string_white( asip->pof_files[1] ); + + if ( (strstr(asip->name,"Asteroid") != NULL) || (strstr(asip->name, "asteroid") != NULL) ) { + required_string( "$POF file3:" ); + stuff_string_white( asip->pof_files[2] ); + } + + asip->num_detail_levels = 0; + + required_string("$Detail distance:"); + asip->num_detail_levels = stuff_int_list(asip->detail_distance, MAX_SHIP_DETAIL_LEVELS, RAW_INTEGER_TYPE); + + required_string("$Max Speed:"); + stuff_float(&asip->max_speed); + + required_string("$Expl inner rad:"); + stuff_float(&asip->inner_rad); + + required_string("$Expl outer rad:"); + stuff_float(&asip->outer_rad); + + required_string("$Expl damage:"); + stuff_float(&asip->damage); + + required_string("$Expl blast:"); + stuff_float(&asip->blast); + + required_string("$Hitpoints:"); + stuff_float(&asip->initial_hull_strength); +} + +// read in data from asteroid.tbl into Asteroid_info[] array +void asteroid_parse_tbl() +{ + char impact_ani_file[FILESPEC_LENGTH]; + + Num_asteroid_types = 0; + + // open localization + lcl_ext_open(); + + read_file_text("asteroid.tbl"); + reset_parse(); + + required_string("#Asteroid Types"); + + while (required_string_either("#End","$Name:")) { + Assert( Num_asteroid_types < MAX_DEBRIS_TYPES ); + asteroid_parse_section(); + Num_asteroid_types++; + } + + required_string("#End"); + + // check all read in + Assert(Num_asteroid_types == MAX_DEBRIS_TYPES); + + Asteroid_impact_explosion_ani = -1; + required_string("$Impact Explosion:"); + stuff_string(impact_ani_file, F_NAME, NULL); + if ( stricmp(impact_ani_file,NOX("none"))) { + int num_frames; + Asteroid_impact_explosion_ani = bm_load_animation( impact_ani_file, &num_frames, NULL, 1); + } + + required_string("$Impact Explosion Radius:"); + stuff_float(&Asteroid_impact_explosion_radius); + + // close localization + lcl_ext_close(); +} + +// Return number of asteroids expected to collide with a ship. +int count_incident_asteroids() +{ + object *asteroid_objp; + int count; + + count = 0; + + for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp !=END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) { + if (asteroid_objp->type == OBJ_ASTEROID ) { + asteroid *asp = &Asteroids[asteroid_objp->instance]; + + if ( asp->target_objnum >= 0 ) { + count++; + } + } + } + + return count; +} + +// Pick object to throw asteroids at. +// Pick any capital or big ship inside the bounds of the asteroid field. +int set_asteroid_throw_objnum() +{ + if (Asteroid_field.num_initial_asteroids < 1) + return -1; + + ship_obj *so; + object *ship_objp; + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + ship_objp = &Objects[so->objnum]; + float radius = ship_objp->radius*2.0f; + + if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP)) { + if (ship_objp->pos.x + radius > Asteroid_field.min_bound.x) + if (ship_objp->pos.y + radius > Asteroid_field.min_bound.y) + if (ship_objp->pos.z + radius > Asteroid_field.min_bound.z) + if (ship_objp->pos.x - radius < Asteroid_field.max_bound.x) + if (ship_objp->pos.y - radius < Asteroid_field.max_bound.y) + if (ship_objp->pos.z - radius < Asteroid_field.max_bound.z) + if (!asteroid_in_inner_bound(&Asteroid_field, &ship_objp->pos, radius)) + return so->objnum; + } + } + return -1; + +} + +void asteroid_frame() +{ + if (Num_asteroids < 1) + return; + + // Only throw if active field + if (Asteroid_field.field_type == FT_PASSIVE) { + return; + } + + Asteroid_throw_objnum = set_asteroid_throw_objnum(); + + maybe_throw_asteroid(count_incident_asteroids()); +} + +// Called once, at game start. Do any one-time initializations here +void asteroid_init() +{ + asteroid_parse_tbl(); +} + +// Draw brackets around on-screen asteroids that are about to collide, otherwise draw an offscreen indicator +void asteroid_show_brackets() +{ + vertex asteroid_vertex; + object *asteroid_objp, *player_target; + asteroid *asp; + + // get pointer to player target, so we don't need to take OBJ_INDEX() of asteroid_objp to compare to target_objnum + if ( Player_ai->target_objnum >= 0 ) { + player_target = &Objects[Player_ai->target_objnum]; + } else { + player_target = NULL; + } + + for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp !=END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) { + if (asteroid_objp->type != OBJ_ASTEROID ) { + continue; + } + + asp = &Asteroids[asteroid_objp->instance]; + + if ( asp->collide_objnum < 0 ) { + continue; + } + + if ( asteroid_objp == player_target ) { + continue; + } + + g3_rotate_vertex(&asteroid_vertex,&asteroid_objp->pos); + g3_project_vertex(&asteroid_vertex); + + if (!(asteroid_vertex.flags & PF_OVERFLOW)) { + gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]); + hud_show_brackets(asteroid_objp, &asteroid_vertex); + } + + // if asteroid is not on screen, draw an offscreen indicator + if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) { + if (asteroid_vertex.codes != 0) { + float dist; +// dist = vm_vec_dist_quick(&Player_obj->pos, &asteroid_objp->pos); + dist = hud_find_target_distance( asteroid_objp, Player_obj ); + gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]); + hud_draw_offscreen_indicator(&asteroid_vertex, &asteroid_objp->pos, dist); + } + } + } +} + +// target the closest danger asteroid to the player +void asteroid_target_closest_danger() +{ + object *asteroid_objp, *closest_asteroid_objp = NULL; + asteroid *asp; + float dist, closest_dist = 999999.0f; + + for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp !=END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) { + if (asteroid_objp->type != OBJ_ASTEROID ) { + continue; + } + + asp = &Asteroids[asteroid_objp->instance]; + + if ( asp->collide_objnum < 0 ) { + continue; + } + + dist = vm_vec_dist_quick(&Player_obj->pos, &asteroid_objp->pos); + + if ( dist < closest_dist ) { + closest_dist = dist; + closest_asteroid_objp = asteroid_objp; + } + } + + if ( closest_asteroid_objp ) { + set_target_objnum( Player_ai, OBJ_INDEX(closest_asteroid_objp) ); + } +} + +void asteroid_page_in() +{ + if (Asteroid_field.num_initial_asteroids > 0 ) { + int i, j, k; + + nprintf(( "Paging", "Paging in asteroids\n" )); + + + // max of 3 possible debris field models + for (i=0; i<3; i++) { + asteroid_info *asip; + + if (Asteroid_field.debris_genre == DG_ASTEROID) { + // asteroid + asip = &Asteroid_info[i]; + } else { + // ship debris - always full until empty + if (Asteroid_field.field_debris_type[i] != -1) { + asip = &Asteroid_info[Asteroid_field.field_debris_type[i]]; + } else { + break; + } + } + + + for (k=0; k<3; k++) { + + // SHIP DEBRIS - use subtype 0 + if (Asteroid_field.debris_genre == DG_SHIP) { + if (k > 0) { + break; + } + } else { + // ASTEROID DEBRIS - use subtype (Asteroid_field.field_debris_type[] != -1) + if (Asteroid_field.field_debris_type[k] == -1) { + continue; + } + } + + asip->modelp[k] = model_get(asip->model_num[k]); + + // Page in textures + for (j=0; jmodelp[k]->n_textures; j++ ) { + int bitmap_num = asip->modelp[k]->original_textures[j]; + + + // if we're in Glide (and maybe later with D3D), use nondarkening textures + if ( bitmap_num > -1 ) { + if(gr_screen.mode == GR_GLIDE){ + bm_page_in_nondarkening_texture( bitmap_num ); + } else { + bm_page_in_texture( bitmap_num ); + } + } + } + + } + } + } +} + +#else + +// stubbed out functions not used in the demo +void asteroid_init() {} +void asteroid_level_init() {} +void asteroid_level_close() {} +void asteroid_create_all() {} +void asteroid_render( object *asteroid_objp ) {} +void asteroid_delete( object *asteroid_objp ) {} +void asteroid_process_pre( object *asteroid_objp, float frame_time) {} +void asteroid_process_post( object *asteroid_objp, float frame_time) {} +int asteroid_check_collision( object *asteroid_objp, object * other_obj, vector * hitpos, collision_info_struct *asteroid_hit_info ) {return 0;} +void asteroid_hit( object *asteroid_objp, object *other_objp, vector *hitpos, float damage ) {} +void asteroid_save_restore(CFILE *fp) {} +int asteroid_count() {return 0;} +int asteroid_collide_objnum(object *asteroid_objp) {return 0;} +float asteroid_time_to_impact(object *asteroid_objp) {return 0.0f;} +void asteroid_show_brackets() {} +void asteroid_target_closest_danger() {} +void asteroid_page_in() {} +void asteroid_sub_create(object *parent_objp, int asteroid_type, vector *relvec) {} +void asteroid_frame() {} + + +#endif diff --git a/src/bmpman/bmpman.cpp b/src/bmpman/bmpman.cpp new file mode 100644 index 0000000..f80f00b --- /dev/null +++ b/src/bmpman/bmpman.cpp @@ -0,0 +1,2556 @@ +/* + * $Logfile: /Freespace2/code/Bmpman/BmpMan.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to load and manage all bitmaps for the game + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 37 9/13/99 11:26p Andsager + * Add debug code to check for poorly sized anis + * + * 36 9/05/99 11:19p Dave + * Made d3d texture cache much more safe. Fixed training scoring bug where + * it would backout scores without ever having applied them in the first + * place. + * + * 35 8/20/99 2:09p Dave + * PXO banner cycling. + * + * 34 8/10/99 6:54p Dave + * Mad optimizations. Added paging to the nebula effect. + * + * 33 8/06/99 1:52p Dave + * Bumped up MAX_BITMAPS for the demo. + * + * 32 8/02/99 1:49p Dave + * Fixed low-mem animation problem. Whee! + * + * 31 7/16/99 1:49p Dave + * 8 bit aabitmaps. yay. + * + * 30 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 29 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 28 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 27 5/05/99 9:02p Dave + * Fixed D3D aabitmap rendering. Spiffed up nebula effect a bit (added + * rotations, tweaked values, made bitmap selection more random). Fixed + * D3D beam weapon clipping problem. Added D3d frame dumping. + * + * 26 4/27/99 12:16a Dave + * Fixed beam weapon muzzle glow problem. Fixed premature timeout on the + * pxo server list screen. Fixed secondary firing for hosts on a + * standalone. Fixed wacky multiplayer weapon "shuddering" problem. + * + * 25 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 24 4/08/99 10:42a Johnson + * Don't try to swizzle a texture to transparent in Fred. + * + * 23 3/31/99 8:24p Dave + * Beefed up all kinds of stuff, incluging beam weapons, nebula effects + * and background nebulae. Added per-ship non-dimming pixel colors. + * + * 22 3/20/99 3:46p Dave + * Added support for model-based background nebulae. Added 3 new + * sexpressions. + * + * 21 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 20 2/08/99 5:07p Dave + * FS2 chat server support. FS2 specific validated missions. + * + * 19 2/05/99 12:52p Dave + * Fixed Glide nondarkening textures. + * + * 18 2/04/99 6:29p Dave + * First full working rev of FS2 PXO support. Fixed Glide lighting + * problems. + * + * 17 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 16 1/15/99 11:29a Neilk + * Fixed D3D screen/texture pixel formatting problem. + * + * 15 1/14/99 12:48a Dave + * Todo list bug fixes. Made a pass at putting briefing icons back into + * FRED. Sort of works :( + * + * 14 1/12/99 12:53a Dave + * More work on beam weapons - made collision detection very efficient - + * collide against all object types properly - made 3 movement types + * smooth. Put in test code to check for possible non-darkening pixels on + * object textures. + * + * 13 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * 12 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 11 12/14/98 4:01p Dave + * Got multi_data stuff working well with new xfer stuff. + * + * 10 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 9 12/01/98 5:53p Dave + * Simplified the way pixel data is swizzled. Fixed tga bitmaps to work + * properly in D3D and Glide. + * + * 8 12/01/98 4:46p Dave + * Put in targa bitmap support (16 bit). + * + * 7 12/01/98 10:32a Johnson + * Fixed direct3d font problems. Fixed sun bitmap problem. Fixed direct3d + * starfield problem. + * + * 6 12/01/98 8:06a Dave + * Temporary checkin to fix some texture transparency problems in d3d. + * + * 5 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 4 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 3 10/22/98 6:14p Dave + * Optimized some #includes in Anim folder. Put in the beginnings of + * parse/localization support for externalized strings and tstrings.tbl + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 106 5/23/98 4:43p John + * Took out debugging sleep + * + * 105 5/23/98 4:14p John + * Added code to preload textures to video card for AGP. Added in code + * to page in some bitmaps that weren't getting paged in at level start. + * + * 104 5/20/98 12:59p John + * Turned optimizations on for debug builds. Also turning on automatic + * function inlining. Turned off the unreachable code warning. + * + * 103 5/20/98 10:20a Hoffoss + * Fixed warning in the code. + * + * 102 5/19/98 3:45p John + * fixed bug causing lowmem to drop half of the frames. Also halved fps + * during lowmem. + * + * 101 5/14/98 3:38p John + * Added in more non-darkening colors for Adam. Had to fix some bugs in + * BmpMan and Ani stuff to get this to work. + * + * 100 4/22/98 9:13p John + * Added code to replace frames of animations in vram if so desired. + * + * 99 4/17/98 6:56a John + * Fixed bug where RLE'd user bitmaps caused data to not get freed. + * (Turned off RLE for use bitmaps). Made lossy animations reduce + * resolution by a factor of 2 in low memory conditions. + * + * 98 4/16/98 6:31p Hoffoss + * Added function to get filename of a bitmap handle, which we don't have + * yet and I need. + * + * 97 4/01/98 9:27p John + * Fixed debug info in bmpman. + * + * 96 4/01/98 5:34p John + * Made only the used POFs page in for a level. Reduced some interp + * arrays. Made custom detail level work differently. + * + * 95 3/31/98 9:55a Lawrance + * JOHN: get xparency working for user bitmaps + * + * 94 3/30/98 4:02p John + * Made machines with < 32 MB of RAM use every other frame of certain + * bitmaps. Put in code to keep track of how much RAM we've malloc'd. + * + * 93 3/29/98 4:05p John + * New paging code that loads everything necessary at level startup. + * + * 92 3/27/98 11:20a John + * commented back in some debug code. + * + * 91 3/26/98 5:21p John + * Added new code to preload all bitmaps at the start of a level. + * Commented it out, though. + * + * 90 3/26/98 4:56p Jasen + * AL: Allow find_block_of() to allocate a series of bitmaps from index 0 + * + * 89 3/26/98 10:21a John + * Made no palette-mapped bitmaps use 0,255,0 as transparent. + * + * 88 3/24/98 5:39p John + * Added debug code to show bitmap fragmentation. Made user bitmaps + * allocate from top of array. + * + * 87 3/22/98 3:28p John + * Added in stippled alpha for lower details. Made medium detail use + * engine glow. + * + * 86 3/11/98 1:55p John + * Fixed warnings + * + * 85 3/06/98 4:09p John + * Changed the way we do bitmap RLE'ing... this speds up HUD bitmaps by + * about 2x + * + * 84 3/02/98 6:00p John + * Moved MAX_BITMAPS into BmpMan.h so the stuff in the graphics code that + * is dependent on it won't break if it changes. Made ModelCache slots + * be equal to MAX_OBJECTS which is what it is. + * + * 83 3/02/98 9:51a John + * Added code to print the number of bitmap slots in use between levels. + * + * 82 2/16/98 3:54p John + * Changed a bunch of mprintfs to catagorize to BmpInfo + * + * 81 2/13/98 5:00p John + * Made user bitmaps not get wrote to level cache file. + * + * 80 2/06/98 8:25p John + * Added code for new bitmaps since last frame + * + * 79 2/06/98 8:10p John + * Added code to show amout of texture usage each frame. + * + * 78 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 77 1/29/98 11:48a John + * Added new counter measure rendering as model code. Made weapons be + * able to have impact explosion. + * + * 76 1/28/98 6:19p Dave + * Reduced standalone memory usage ~8 megs. Put in support for handling + * multiplayer submenu handling for endgame, etc. + * + * 75 1/17/98 12:55p John + * Fixed bug that I just created that loaded all ani frames. + * + * 74 1/17/98 12:33p John + * Made the game_busy function be called a constant amount of times per + * level load, making the bar prediction easier. + * + * 73 1/17/98 12:14p John + * Added loading... bar to freespace. + * + * 72 1/11/98 3:20p John + * Made so that if no .clt exists, it will load all the bitmaps + * + * 71 1/11/98 3:06p John + * Made bitmap loading stop when cache gets full. + * + * 70 1/11/98 2:45p John + * Changed .lst to .clt + * + * 69 1/11/98 2:14p John + * Changed a lot of stuff that had to do with bitmap loading. Made cfile + * not do callbacks, I put that in global code. Made only bitmaps that + * need to load for a level load. + * + * 67 1/09/98 4:07p John + * Made all bitmap functions return a bitmap "Handle" not number. This + * helps to find bm_release errors. + * + * 66 1/09/98 1:38p John + * Fixed some bugs from previous comment + * + * 65 1/09/98 1:32p John + * Added some debugging code to track down a weird error. Namely I fill + * in the be structure with bogus values when someone frees it. + * + * 64 12/28/97 2:00p John + * put in another assert checking for invalid lock/unlock sequencing + * + * 63 12/24/97 2:02p John + * Changed palette translation to be a little faster for unoptimized + * builds + * + * 62 12/18/97 8:59p Dave + * Finished putting in basic support for weapon select and ship select in + * multiplayer. + * + * 61 12/15/97 10:27p John + * fixed bug where 2 bm_loads of same file both open the header. + * + * 60 12/08/97 2:17p John + * fixed bug with bmpman and cf_callback. + * made cf_callback in Freespace set small font back when done. + * + * 59 12/03/97 5:01p Lawrance + * bump up MAX_BITMAPS to 1500. People have reached 1000 bitmaps while + * playing multiple missions. + * + * 58 12/02/97 3:59p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 57 11/30/97 3:57p John + * Made fixed 32-bpp translucency. Made BmpMan always map translucent + * color into 255 even if you aren't supposed to remap and make it's + * palette black. + * + * 56 10/05/97 10:39a John + * fixed bug with palette on unmapped bitmaps. Made unmapped bitmaps get + * marked with xparent. + * + * 55 9/23/97 11:46a Lawrance + * fixed bug with rle'ing with spans get big + * + * 54 9/23/97 10:45a John + * made so you can tell bitblt code to rle a bitmap by passing flag to + * gr_set_bitmap + * + * 53 9/19/97 10:18a John + * fixed bug with aa animations being re-rle'd every + * frame. + * + * + * 52 9/09/97 10:08a Sandeep + * Fixed Compiler Level 4 warnings + * + * 51 9/08/97 2:02p John + * fixed typo in nprintf + * + * 50 9/08/97 1:56p John + * fixed some memory housekeeping bugs + * + * 49 9/03/97 4:19p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 48 8/29/97 7:35p Lawrance + * check if .ani animation is already loaded in bm_load_animation() + * + * 47 8/25/97 11:14p Lawrance + * added support for .ani files in bm_load_animation() + * + * 46 8/17/97 2:42p Lawrance + * only flag PCX files as xparent if they have xparent pixels in them + * + * 45 8/15/97 9:57a Lawrance + * support multiple xparent entries for PCX files + * + * 44 8/05/97 10:18a Lawrance + * my_rand() being used temporarily instead of rand() + * + * 43 8/01/97 4:30p John + * + * 42 7/29/97 8:34a John + * took out png stuff + * + * 41 7/18/97 3:27p Lawrance + * have pcx files use (0,255,0) for transparency + * + * 40 7/16/97 3:07p John + * + * 39 7/10/97 8:34a John + * Added code to read TGA files. + * + * 38 6/20/97 1:50p John + * added rle code to bmpman. made gr8_aabitmap use it. + * + * 37 6/18/97 12:07p John + * fixed some color bugs + * + * 36 6/17/97 8:58p Lawrance + * fixed bug with not nulling bm.data with USER bitmaps + * + * 35 6/12/97 2:44a Lawrance + * changed bm_unlock() to take an index into bm_bitmaps(). Added + * ref_count to bitmap_entry struct + * + * 34 5/20/97 10:36a John + * Fixed problem with user bitmaps and direct3d caching. + * + * 33 5/14/97 1:59p John + * fixed a palette bug with vclips. + * + * 32 3/24/97 4:43p John + * speed up chunked collision detection by only checking cubes the vector + * goes through. + * + * 31 3/24/97 3:25p John + * Cleaned up and restructured model_collide code and fvi code. In fvi + * made code that finds uvs work.. Added bm_get_pixel to BmpMan. + * + * 30 3/11/97 2:49p Allender + * + * 29 2/18/97 9:43a Lawrance + * added Assert() in bm_release + * + * 28 1/22/97 4:29p John + * maybe fixed bug with code that counts total bytes of texture ram used. + * + * 27 1/22/97 4:19p Lawrance + * added flags to bm_create to allow transparency + * + * 26 1/21/97 5:24p John + * fixed another bug with bm_release. + * + * 25 1/21/97 5:12p John + * fixed bug with case + * + * 24 1/21/97 5:02p John + * Added code for 8bpp user bitmaps. + * + * 23 1/09/97 11:35a John + * Added some 2d functions to get/put screen images. + * + * 22 11/26/96 6:50p John + * Added some more hicolor primitives. Made windowed mode run as current + * bpp, if bpp is 8,16,or 32. + * + * 21 11/26/96 9:44a Allender + * Allow for use of different bitmap palettes + * + * 20 11/25/96 10:36a Allender + * started working on 32 bpp support. Added 15 bpp. + * + * 19 11/18/96 1:51p Allender + * fix up manager code to reread bitmaps if needed in newer bit depth + * + * 18 11/15/96 4:24p Allender + * more bmpman stuff -- only free bitmap slots when releasing copied + * texture -- otherwise, only release the data for the bitmap + * + * 17 11/15/96 3:33p Allender + * added support for converting to 16 bit textures when requested with + * bm_load. Added some other management functions + * + * 16 11/13/96 4:51p Allender + * started overhaul of bitmap manager. bm_load no longer actually load + * the data, only the info for the bitmap. Locking the bitmap now forces + * load when no data present (or will if bpp changes) + * + * $NoKeywords: $ + */ + +#include +#include "pstypes.h" +#include "pcxutils.h" +#include "bmpman.h" +#include "palman.h" +#include "2d.h" +#include "animplay.h" +#include "timer.h" +#include "systemvars.h" +#include "key.h" +#include "packunpack.h" +#include "cfile.h" +#include "grinternal.h" +#include "tgautils.h" +#include "ship.h" + +#ifndef NDEBUG +#define BMPMAN_NDEBUG +#endif + +// keep this defined to use per-ship nondarkening pixels +#define BMPMAN_SPECIAL_NONDARK + +int bm_inited = 0; + +#define BM_TYPE_NONE 0 +#define BM_TYPE_PCX 1 +#define BM_TYPE_USER 2 +#define BM_TYPE_ANI 3 // in-house ANI format +#define BM_TYPE_TGA 4 // 16 bit targa + +typedef union bm_extra_info { + struct { + // Stuff needed for animations + int first_frame; // used for animations -- points to index of first frame + ubyte num_frames; // used for animation -- number of frames in the animation + ubyte fps; // used for animation -- frames per second + } ani; + struct { + // Stuff needed for user bitmaps + void *data; // For user bitmaps, this is where the data comes from + ubyte bpp; // For user bitmaps, this is what format the data is + ubyte flags; // Flags passed to bm_create + } user; +} bm_extra_info; + +typedef struct bitmap_entry { + // identification + char filename[MAX_FILENAME_LEN]; // filename for this bitmap + + uint signature; // a unique signature identifying the data + uint palette_checksum; // checksum used to be sure bitmap is in current palette + int handle; // Handle = id*MAX_BITMAPS + bitmapnum + int last_used; // When this bitmap was last used + + ubyte type; // PCX, USER, ANI, etc + signed char ref_count; // Number of locks on bitmap. Can't unload unless ref_count is 0. + + // Stuff to keep track of usage + ubyte preloaded; // If set, then this was loaded from the lst file + ubyte used_flags; // What flags it was accessed thru + + // Bitmap info + bitmap bm; + + // Data for animations and user bitmaps + bm_extra_info info; + +#ifdef BMPMAN_NDEBUG + // bookeeping + ubyte used_last_frame; // If set, then it was used last frame + ubyte used_this_frame; // If set, then it was used this frame + int data_size; // How much data this bitmap uses + int used_count; // How many times it was accessed +#endif +} bitmap_entry; + +uint Bm_next_signature = 0x1234; + +bitmap_entry bm_bitmaps[MAX_BITMAPS]; + +int bm_texture_ram = 0; + +// Bm_max_ram - How much RAM bmpman can use for textures. +// Set to <1 to make it use all it wants. +int Bm_max_ram = 0; //16*1024*1024; // Only use 16 MB for textures + +int bm_next_handle = 1; + +int Bm_paging = 0; + +static int Bm_low_mem = 0; + +// 16 bit pixel formats +int Bm_pixel_format = BM_PIXEL_FORMAT_ARGB; + +// get and put functions for 16 bit pixels - neat bit slinging, huh? +#define BM_SET_R_ARGB(p, r) { p[1] &= ~(0x7c); p[1] |= ((r & 0x1f) << 2); } +#define BM_SET_G_ARGB(p, g) { p[0] &= ~(0xe0); p[1] &= ~(0x03); p[0] |= ((g & 0x07) << 5); p[1] |= ((g & 0x18) >> 3); } +#define BM_SET_B_ARGB(p, b) { p[0] &= ~(0x1f); p[0] |= b & 0x1f; } +#define BM_SET_A_ARGB(p, a) { p[1] &= ~(0x80); p[1] |= ((a & 0x01) << 7); } + +#define BM_SET_R_D3D(p, r) { *p |= (ushort)(( (int)r / Gr_current_red->scale ) << Gr_current_red->shift); } +#define BM_SET_G_D3D(p, g) { *p |= (ushort)(( (int)g / Gr_current_green->scale ) << Gr_current_green->shift); } +#define BM_SET_B_D3D(p, b) { *p |= (ushort)(( (int)b / Gr_current_blue->scale ) << Gr_current_blue->shift); } +#define BM_SET_A_D3D(p, a) { if(a == 0){ *p = (ushort)Gr_current_green->mask; } } + +#define BM_SET_R(p, r) { switch(Bm_pixel_format){ case BM_PIXEL_FORMAT_ARGB: BM_SET_R_ARGB(((char*)p), r); break; case BM_PIXEL_FORMAT_D3D: BM_SET_R_D3D(p, r); break; default: Int3(); } } +#define BM_SET_G(p, g) { switch(Bm_pixel_format){ case BM_PIXEL_FORMAT_ARGB: BM_SET_G_ARGB(((char*)p), g); break; case BM_PIXEL_FORMAT_D3D: BM_SET_G_D3D(p, g); break; default: Int3(); } } +#define BM_SET_B(p, b) { switch(Bm_pixel_format){ case BM_PIXEL_FORMAT_ARGB: BM_SET_B_ARGB(((char*)p), b); break; case BM_PIXEL_FORMAT_D3D: BM_SET_B_D3D(p, b); break; default: Int3(); } } +#define BM_SET_A(p, a) { switch(Bm_pixel_format){ case BM_PIXEL_FORMAT_ARGB: BM_SET_A_ARGB(((char*)p), a); break; case BM_PIXEL_FORMAT_D3D: BM_SET_A_D3D(p, a); break; default: Int3(); } } + +// =========================================== +// Mode: 0 = High memory +// 1 = Low memory ( every other frame of ani's) +// 2 = Debug low memory ( only use first frame of each ani ) +void bm_set_low_mem( int mode ) +{ + Assert( (mode >= 0) && (mode<=2 )); + Bm_low_mem = mode; +} + + +static int bm_get_next_handle() +{ + int n = bm_next_handle; + bm_next_handle++; + if ( bm_next_handle > 30000 ) { + bm_next_handle = 1; + } + return n; +} + +// Frees a bitmaps data if it should, and +// Returns true if bitmap n can free it's data. +static void bm_free_data(int n) +{ + bitmap_entry *be; + bitmap *bmp; + + Assert( n >= 0 && n < MAX_BITMAPS ); + + be = &bm_bitmaps[n]; + bmp = &be->bm; + + // If there isn't a bitmap in this structure, don't + // do anything but clear out the bitmap info + if ( be->type==BM_TYPE_NONE) + goto SkipFree; + + // If this bitmap doesn't have any data to free, skip + // the freeing it part of this. + if ( bmp->data == 0 ) + goto SkipFree; + + // Don't free up memory for user defined bitmaps, since + // BmpMan isn't the one in charge of allocating/deallocing them. + if ( ( be->type==BM_TYPE_USER ) ) + goto SkipFree; + + // Free up the data now! + + // mprintf(( "Bitmap %d freed %d bytes\n", n, bm_bitmaps[n].data_size )); + #ifdef BMPMAN_NDEBUG + bm_texture_ram -= be->data_size; + #endif + free((void *)bmp->data); + +SkipFree: + + // Clear out & reset the bitmap data structure + bmp->flags = 0; + bmp->bpp = 0; + bmp->data = 0; + bmp->palette = NULL; + #ifdef BMPMAN_NDEBUG + be->data_size = 0; + #endif + be->signature = Bm_next_signature++; +} + + +#ifdef BMPMAN_NDEBUG + +int Bm_ram_freed = 0; + +static void bm_free_some_ram( int n, int size ) +{ +/* + if ( Bm_max_ram < 1 ) return; + if ( bm_texture_ram + size < Bm_max_ram ) return; + + int current_time = timer_get_milliseconds(); + + while( bm_texture_ram + size > Bm_max_ram ) { + Bm_ram_freed++; + + // Need to free some RAM up! + int i, oldest=-1, best_val=0; + for (i = 0; i < MAX_BITMAPS; i++) { + if ( (bm_bitmaps[i].type != BM_TYPE_NONE) && (bm_bitmaps[i].first_frame!=bm_bitmaps[n].first_frame) && (bm_bitmaps[i].ref_count==0) && (bm_bitmaps[i].data_size>0) ) { + int page_func = ( current_time-bm_bitmaps[i].last_used)*bm_bitmaps[i].data_size; + if ( (oldest==-1) || (page_func>best_val) ) { + oldest=i; + best_val = page_func; + } + } + } + + if ( oldest > -1 ) { + //mprintf(( "Freeing bitmap '%s'\n", bm_bitmaps[oldest].filename )); + for (i=0; i= 0 && n < MAX_BITMAPS ); +// mprintf(( "Bitmap %d allocated %d bytes\n", n, size )); + #ifdef BMPMAN_NDEBUG + bm_free_some_ram( n, size ); + Assert( bm_bitmaps[n].data_size == 0 ); + bm_bitmaps[n].data_size += size; + bm_texture_ram += size; + #endif + return malloc(size); +} + +void bm_close() +{ + int i; + if ( bm_inited ) { + for (i=0; isections.num_x = (ubyte)(be->w / MAX_BMAP_SECTION_SIZE); + if((be->sections.num_x * MAX_BMAP_SECTION_SIZE) < be->w){ + be->sections.num_x++; + } + be->sections.num_y = (ubyte)(be->h / MAX_BMAP_SECTION_SIZE); + if((be->sections.num_y * MAX_BMAP_SECTION_SIZE) < be->h){ + be->sections.num_y++; + } + + // calculate the offsets for each section + for(idx=0; idxsections.num_x; idx++){ + be->sections.sx[idx] = (ushort)(MAX_BMAP_SECTION_SIZE * idx); + } + for(idx=0; idxsections.num_y; idx++){ + be->sections.sy[idx] = (ushort)(MAX_BMAP_SECTION_SIZE * idx); + } +} + +// Creates a bitmap that exists in RAM somewhere, instead +// of coming from a disk file. You pass in a pointer to a +// block of 32 (or 8)-bit-per-pixel data. Right now, the only +// bpp you can pass in is 32 or 8. On success, it returns the +// bitmap number. You cannot free that RAM until bm_release +// is called on that bitmap. +int bm_create( int bpp, int w, int h, void * data, int flags ) +{ + int i, n, first_slot = MAX_BITMAPS; + + // Assert((bpp==32)||(bpp==8)); + if(bpp != 16){ + Assert(flags & BMP_AABITMAP); + } else { + Assert(bpp == 16); + } + + if ( !bm_inited ) bm_init(); + + for (i = MAX_BITMAPS-1; i >= 0; i-- ) { + if ( bm_bitmaps[i].type == BM_TYPE_NONE ) { + first_slot = i; + break; + } + } + + n = first_slot; + Assert( n > -1 ); + + // Out of bitmap slots + if ( n == -1 ) return -1; + + memset( &bm_bitmaps[n], 0, sizeof(bitmap_entry) ); + + sprintf( bm_bitmaps[n].filename, "TMP%dx%d", w, h ); + bm_bitmaps[n].type = BM_TYPE_USER; + bm_bitmaps[n].palette_checksum = 0; + + bm_bitmaps[n].bm.w = short(w); + bm_bitmaps[n].bm.h = short(h); + bm_bitmaps[n].bm.rowsize = short(w); + bm_bitmaps[n].bm.bpp = (unsigned char)(bpp); + bm_bitmaps[n].bm.flags = 0; + bm_bitmaps[n].bm.flags |= flags; + bm_bitmaps[n].bm.data = 0; + bm_bitmaps[n].bm.palette = NULL; + + bm_bitmaps[n].info.user.bpp = ubyte(bpp); + bm_bitmaps[n].info.user.data = data; + bm_bitmaps[n].info.user.flags = ubyte(flags); + + bm_bitmaps[n].signature = Bm_next_signature++; + + bm_bitmaps[n].handle = bm_get_next_handle()*MAX_BITMAPS + n; + bm_bitmaps[n].last_used = -1; + + // fill in section info + bm_calc_sections(&bm_bitmaps[n].bm); + + return bm_bitmaps[n].handle; +} + +// sub helper function. Given a raw filename and an extension, try and find the bitmap +// returns -1 if it could not be found +// 0 if it was found as a file +// 1 if it already exists, fills in handle +int Bm_ignore_duplicates = 0; +int bm_load_sub(char *real_filename, char *ext, int *handle) +{ + int i; + char filename[MAX_FILENAME_LEN] = ""; + + strcpy( filename, real_filename ); + strcat( filename, ext ); + for (i=0; i<(int)strlen(filename); i++ ){ + filename[i] = char(tolower(filename[i])); + } + + // try to find given filename to see if it has been loaded before + if(!Bm_ignore_duplicates){ + for (i = 0; i < MAX_BITMAPS; i++) { + if ( (bm_bitmaps[i].type != BM_TYPE_NONE) && !stricmp(filename, bm_bitmaps[i].filename) ) { + nprintf (("BmpMan", "Found bitmap %s -- number %d\n", filename, i)); + *handle = bm_bitmaps[i].handle; + return 1; + } + } + } + + // try and find the file + /* + CFILE *test = cfopen(filename, "rb"); + if(test != NULL){ + cfclose(test); + return 0; + } + */ + + // could not be found + return 0; +} + +// This loads a bitmap so we can draw with it later. +// It returns a negative number if it couldn't load +// the bitmap. On success, it returns the bitmap +// number. Function doesn't acutally load the data, only +// width, height, and possibly flags. +int bm_load( char * real_filename ) +{ + int i, n, first_slot = MAX_BITMAPS; + int w, h, bpp; + char filename[MAX_FILENAME_LEN]; + int tga = 0; + int handle; + int found = 0; + + if ( !bm_inited ) bm_init(); + + // nice little trick for keeping standalone memory usage way low - always return a bogus bitmap + if(Game_mode & GM_STANDALONE_SERVER){ + strcpy(filename,"test128"); + } + + // make sure no one passed an extension + strcpy( filename, real_filename ); + char *p = strchr( filename, '.' ); + if ( p ) { + mprintf(( "Someone passed an extension to bm_load for file '%s'\n", real_filename )); + //Int3(); + *p = 0; + } + + // try and find the pcx file + switch(bm_load_sub(filename, ".pcx", &handle)){ + // error + case -1: + break; + + // found as a file + case 0: + found = 1; + strcat(filename, ".pcx"); + break; + + // found as pre-existing + case 1: + found = 1; + return handle; + } + + if(!found){ + // try and find the tga file + switch(bm_load_sub(filename, ".tga", &handle)){ + // error + case -1: + return -1; + break; + + // found as a file + case 0: + strcat(filename, ".tga"); + tga = 1; + break; + + // found as pre-existing + case 1: + return handle; + } + } + + // if its a tga file + if(tga){ + int tga_error=targa_read_header( filename, &w, &h, &bpp, NULL ); + if ( tga_error != TARGA_ERROR_NONE ) { + mprintf(( "Couldn't open '%s'\n", filename )); + return -1; + } + } + // if its a pcx file + else { + int pcx_error=pcx_read_header( filename, &w, &h, NULL ); + if ( pcx_error != PCX_ERROR_NONE ) { + mprintf(( "Couldn't open '%s'\n", filename )); + return -1; + } + } + + // Error( LOCATION, "Unknown bitmap type %s\n", filename ); + + // Find an open slot + for (i = 0; i < MAX_BITMAPS; i++) { + if ( (bm_bitmaps[i].type == BM_TYPE_NONE) && (first_slot == MAX_BITMAPS) ){ + first_slot = i; + } + } + + n = first_slot; + Assert( n < MAX_BITMAPS ); + + if ( n == MAX_BITMAPS ) return -1; + + // ensure fields are cleared out from previous bitmap +// Assert(bm_bitmaps[n].bm.data == 0); +// Assert(bm_bitmaps[n].bm.palette == NULL); +// Assert(bm_bitmaps[n].ref_count == 0 ); +// Assert(bm_bitmaps[n].user_data == NULL); + memset( &bm_bitmaps[n], 0, sizeof(bitmap_entry) ); + + // Mark the slot as filled, because cf_read might load a new bitmap + // into this slot. + bm_bitmaps[n].type = tga ? (ubyte)BM_TYPE_TGA : (ubyte)BM_TYPE_PCX; + bm_bitmaps[n].signature = Bm_next_signature++; + Assert ( strlen(filename) < MAX_FILENAME_LEN ); + strncpy(bm_bitmaps[n].filename, filename, MAX_FILENAME_LEN-1 ); + bm_bitmaps[n].bm.w = short(w); + bm_bitmaps[n].bm.rowsize = short(w); + bm_bitmaps[n].bm.h = short(h); + bm_bitmaps[n].bm.bpp = 0; + bm_bitmaps[n].bm.flags = 0; + bm_bitmaps[n].bm.data = 0; + bm_bitmaps[n].bm.palette = NULL; + + bm_bitmaps[n].palette_checksum = 0; + bm_bitmaps[n].handle = bm_get_next_handle()*MAX_BITMAPS + n; + bm_bitmaps[n].last_used = -1; + + // fill in section info + bm_calc_sections(&bm_bitmaps[n].bm); + + return bm_bitmaps[n].handle; +} + +// special load function. basically allows you to load a bitmap which already exists (by filename). +// this is useful because in some cases we need to have a bitmap which is locked in screen format +// _and_ texture format, such as pilot pics and squad logos +int bm_load_duplicate(char *filename) +{ + int ret; + + // ignore duplicates + Bm_ignore_duplicates = 1; + + // load + ret = bm_load(filename); + + // back to normal + Bm_ignore_duplicates = 0; + + return ret; +} + +DCF(bm_frag,"Shows BmpMan fragmentation") +{ + if ( Dc_command ) { + + gr_clear(); + + int x=0, y=0; + int xs=2, ys=2; + int w=4, h=4; + + for (int i=0; i 639 ) { + x = 0; + y += h + ys + ys; + } + + } + + gr_flip(); + key_getch(); + } +} + +static int find_block_of(int n) +{ + int i, cnt, nstart; + + cnt=0; + nstart = 0; + for (i=0; i filename of animation +// nframes => OUTPUT parameter: number of frames in the animation +// fps => OUTPUT/OPTIONAL parameter: intended fps for the animation +// +// returns: bitmap number of first frame in the animation +// +int bm_load_animation( char *real_filename, int *nframes, int *fps, int can_drop_frames) +{ + int i, n; + anim the_anim; + CFILE *fp; + char filename[MAX_FILENAME_LEN]; + + if ( !bm_inited ) bm_init(); + + strcpy( filename, real_filename ); + char *p = strchr( filename, '.' ); + if ( p ) { + mprintf(( "Someone passed an extension to bm_load_animation for file '%s'\n", real_filename )); + //Int3(); + *p = 0; + } + strcat( filename, ".ani" ); + + if ( (fp = cfopen(filename, "rb")) == NULL ) { +// Error(LOCATION,"Could not open filename %s in bm_load_ani()\n", filename); + return -1; + } + + int reduced = 0; +#ifndef NDEBUG + // for debug of ANI sizes + strcpy(the_anim.name, real_filename); +#endif + anim_read_header(&the_anim, fp); + if ( can_drop_frames ) { + if ( Bm_low_mem == 1 ) { + reduced = 1; + the_anim.total_frames = ( the_anim.total_frames+1)/2; + } else if ( Bm_low_mem == 2 ) { + the_anim.total_frames = 1; + } + } + cfclose(fp); + + *nframes = the_anim.total_frames; + if ( fps != NULL ) { + if ( reduced ) { + *fps = the_anim.fps / 2; + } else { + *fps = the_anim.fps; + } + } + + // first check to see if this ani already has it's frames loaded + for (i = 0; i < MAX_BITMAPS; i++) { + if ( (bm_bitmaps[i].type == BM_TYPE_ANI) && !stricmp(filename, bm_bitmaps[i].filename) ) { + break; + } + } + + if ( i < MAX_BITMAPS ) { + // in low memory modes this can happen + if(!Bm_low_mem){ + Assert(bm_bitmaps[i].info.ani.num_frames == *nframes); + } + return bm_bitmaps[i].handle; + } + + n = find_block_of(*nframes); + if(n < 0){ + return -1; + } + // Assert( n >= 0 ); + + int first_handle = bm_get_next_handle(); + + Assert ( strlen(filename) < MAX_FILENAME_LEN ); + for ( i = 0; i < *nframes; i++ ) { + memset( &bm_bitmaps[n+i], 0, sizeof(bitmap_entry) ); + bm_bitmaps[n+i].info.ani.first_frame = n; + bm_bitmaps[n+i].info.ani.num_frames = ubyte(the_anim.total_frames); + bm_bitmaps[n+i].info.ani.fps = ubyte(the_anim.fps); + bm_bitmaps[n+i].bm.w = short(the_anim.width); + bm_bitmaps[n+i].bm.rowsize = short(the_anim.width); + bm_bitmaps[n+i].bm.h = short(the_anim.height); + if ( reduced ) { + bm_bitmaps[n+i].bm.w /= 2; + bm_bitmaps[n+i].bm.rowsize /= 2; + bm_bitmaps[n+i].bm.h /= 2; + } + bm_bitmaps[n+i].bm.flags = 0; + bm_bitmaps[n+i].bm.bpp = 0; + bm_bitmaps[n+i].bm.data = 0; + bm_bitmaps[n+i].bm.palette = NULL; + bm_bitmaps[n+i].type = BM_TYPE_ANI; + bm_bitmaps[n+i].palette_checksum = 0; + bm_bitmaps[n+i].signature = Bm_next_signature++; + bm_bitmaps[n+i].handle = first_handle*MAX_BITMAPS + n+i; + bm_bitmaps[n+i].last_used = -1; + + // fill in section info + bm_calc_sections(&bm_bitmaps[n+i].bm); + + if ( i == 0 ) { + sprintf( bm_bitmaps[n+i].filename, "%s", filename ); + } else { + sprintf( bm_bitmaps[n+i].filename, "%s[%d]", filename, i ); + } + } + + return bm_bitmaps[n].handle; +} + +// Gets info. w,h,or flags,nframes or fps can be NULL if you don't care. +void bm_get_info( int handle, int *w, int * h, ubyte * flags, int *nframes, int *fps, bitmap_section_info **sections ) +{ + bitmap * bmp; + + if ( !bm_inited ) return; + + int bitmapnum = handle % MAX_BITMAPS; + Assert( bm_bitmaps[bitmapnum].handle == handle ); // INVALID BITMAP HANDLE! + + if ( (bm_bitmaps[bitmapnum].type == BM_TYPE_NONE) || (bm_bitmaps[bitmapnum].handle != handle) ) { + if (w) *w = 0; + if (h) *h = 0; + if (flags) *flags = 0; + if (nframes) *nframes=0; + if (fps) *fps=0; + if (sections != NULL) *sections = NULL; + return; + } + + bmp = &(bm_bitmaps[bitmapnum].bm); + + if (w) *w = bmp->w; + if (h) *h = bmp->h; + if (flags) *flags = bmp->flags; + if ( bm_bitmaps[bitmapnum].type == BM_TYPE_ANI ) { + if (nframes) { + *nframes = bm_bitmaps[bitmapnum].info.ani.num_frames; + } + if (fps) { + *fps= bm_bitmaps[bitmapnum].info.ani.fps; + } + } else { + if (nframes) { + *nframes = 1; + } + if (fps) { + *fps= 0; + } + } + if(sections != NULL){ + *sections = &bm_bitmaps[bitmapnum].bm.sections; + } +} + +uint bm_get_signature( int handle ) +{ + if ( !bm_inited ) bm_init(); + + int bitmapnum = handle % MAX_BITMAPS; + Assert( bm_bitmaps[bitmapnum].handle == handle ); // INVALID BITMAP HANDLE + + return bm_bitmaps[bitmapnum].signature; +} + +extern int palman_is_nondarkening(int r,int g, int b); +static void bm_convert_format( int bitmapnum, bitmap *bmp, ubyte bpp, ubyte flags ) +{ + int idx; + int r, g, b, a; + + if(Fred_running || Pofview_running || Is_standalone){ + Assert(bmp->bpp == 8); + + return; + } else { + if(flags & BMP_AABITMAP){ + Assert(bmp->bpp == 8); + } else { + Assert(bmp->bpp == 16); + } + } + + // maybe swizzle to be an xparent texture + if(!(bmp->flags & BMP_TEX_XPARENT) && (flags & BMP_TEX_XPARENT)){ + for(idx=0; idxw*bmp->h; idx++){ + + // if the pixel is transparent + if ( ((ushort*)bmp->data)[idx] == Gr_t_green.mask) { + switch(Bm_pixel_format){ + // 1555, all we need to do is zero the whole thing + case BM_PIXEL_FORMAT_ARGB: + case BM_PIXEL_FORMAT_ARGB_D3D: + ((ushort*)bmp->data)[idx] = 0; + break; + // d3d format + case BM_PIXEL_FORMAT_D3D: + r = g = b = a = 0; + r /= Gr_t_red.scale; + g /= Gr_t_green.scale; + b /= Gr_t_blue.scale; + a /= Gr_t_alpha.scale; + ((ushort*)bmp->data)[idx] = (unsigned short)((a<flags |= BMP_TEX_XPARENT; + } +} + +// basically, map the bitmap into the current palette. used to be done for all pcx's, now just for +// Fred, since its the only thing that uses the software tmapper +void bm_swizzle_8bit_for_fred(bitmap_entry *be, bitmap *bmp, ubyte *data, ubyte *palette) +{ + int pcx_xparent_index = -1; + int i; + int r, g, b; + ubyte palxlat[256]; + + for (i=0; i<256; i++ ) { + r = palette[i*3]; + g = palette[i*3+1]; + b = palette[i*3+2]; + if ( g == 255 && r == 0 && b == 0 ) { + palxlat[i] = 255; + pcx_xparent_index = i; + } else { + palxlat[i] = (ubyte)(palette_find( r, g, b )); + } + } + for (i=0; iw * bmp->h; i++ ) { + ubyte c = palxlat[data[i]]; + data[i] = c; + } + be->palette_checksum = gr_palette_checksum; +} + +void bm_lock_pcx( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags ) +{ + ubyte *data, *palette; + ubyte pal[768]; + palette = NULL; + + // Unload any existing data + bm_free_data( bitmapnum ); + + // allocate bitmap data + if(bpp == 8){ + // Assert(Fred_running || Pofview_running || Is_standalone); + data = (ubyte *)bm_malloc(bitmapnum, bmp->w * bmp->h ); + #ifdef BMPMAN_NDEBUG + Assert( be->data_size == bmp->w * bmp->h ); + #endif + palette = pal; + bmp->data = (uint)data; + bmp->bpp = 8; + bmp->palette = gr_palette; + memset( data, 0, bmp->w * bmp->h); + } else { + data = (ubyte*)bm_malloc(bitmapnum, bmp->w * bmp->h * 2); + bmp->bpp = 16; + bmp->data = (uint)data; + bmp->palette = NULL; + memset( data, 0, bmp->w * bmp->h * 2); + } + + Assert( &be->bm == bmp ); + #ifdef BMPMAN_NDEBUG + Assert( be->data_size > 0 ); + #endif + + // some sanity checks on flags + Assert(!((flags & BMP_AABITMAP) && (flags & BMP_TEX_ANY))); // no aabitmap textures + Assert(!((flags & BMP_TEX_XPARENT) && (flags & BMP_TEX_NONDARK))); // can't be a transparent texture and a nondarkening texture + Assert(!((flags & BMP_TEX_NONDARK) && (gr_screen.mode == GR_DIRECT3D))); // D3D should never be trying to get nondarkening textures + + if(bpp == 8){ + int pcx_error=pcx_read_bitmap_8bpp( be->filename, data, palette ); + if ( pcx_error != PCX_ERROR_NONE ) { + // Error( LOCATION, "Couldn't open '%s'\n", be->filename ); + //Error( LOCATION, "Couldn't open '%s'\n", filename ); + //return -1; + } + + // now swizzle the thing into the proper format + if(Fred_running || Pofview_running){ + bm_swizzle_8bit_for_fred(be, bmp, data, palette); + } + } else { + int pcx_error; + + // load types + if(flags & BMP_AABITMAP){ + pcx_error = pcx_read_bitmap_16bpp_aabitmap( be->filename, data ); + } else if(flags & BMP_TEX_NONDARK){ + pcx_error = pcx_read_bitmap_16bpp_nondark( be->filename, data ); + } else { + pcx_error = pcx_read_bitmap_16bpp( be->filename, data ); + } + if ( pcx_error != PCX_ERROR_NONE ) { + // Error( LOCATION, "Couldn't open '%s'\n", be->filename ); + //Error( LOCATION, "Couldn't open '%s'\n", filename ); + //return -1; + } + } + + #ifdef BMPMAN_NDEBUG + Assert( be->data_size > 0 ); + #endif + + bmp->flags = 0; + bm_convert_format( bitmapnum, bmp, bpp, flags ); +} + +void bm_lock_ani( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags ) +{ + anim *the_anim; + anim_instance *the_anim_instance; + bitmap *bm; + ubyte *frame_data; + int size, i; + int first_frame, nframes; + + first_frame = be->info.ani.first_frame; + nframes = bm_bitmaps[first_frame].info.ani.num_frames; + + if ( (the_anim = anim_load(bm_bitmaps[first_frame].filename)) == NULL ) { + // Error(LOCATION, "Error opening %s in bm_lock\n", be->filename); + } + + if ( (the_anim_instance = init_anim_instance(the_anim, bpp)) == NULL ) { + // Error(LOCATION, "Error opening %s in bm_lock\n", be->filename); + anim_free(the_anim); + } + + int can_drop_frames = 0; + + if ( the_anim->total_frames != bm_bitmaps[first_frame].info.ani.num_frames ) { + can_drop_frames = 1; + } + bm = &bm_bitmaps[first_frame].bm; + if(bpp == 16){ + size = bm->w * bm->h * 2; + } else { + size = bm->w * bm->h; + } + + for ( i=0; iflags = 0; + // briefing editor in Fred2 uses aabitmaps (ani's) - force to 8 bit + if(Fred_running || Is_standalone){ + bm->bpp = 8; + } else { + bm->bpp = bpp; + } + bm->data = (uint)bm_malloc(first_frame + i, size); + + frame_data = anim_get_next_raw_buffer(the_anim_instance, 0 ,flags & BMP_AABITMAP ? 1 : 0, bm->bpp); + + if ( frame_data == NULL ) { + // Error(LOCATION,"Fatal error locking .ani file: %s\n", be->filename); + } + + ubyte *dptr, *sptr; + + sptr = frame_data; + dptr = (ubyte *)bm->data; + + if ( (bm->w!=the_anim->width) || (bm->h!=the_anim->height) ) { + // Scale it down + // Int3(); // not ready yet - should only be ingame + + // 8 bit + if(bpp == 8){ + int w,h; + fix u, utmp, v, du, dv; + + u = v = 0; + + du = ( the_anim->width*F1_0 ) / bm->w; + dv = ( the_anim->height*F1_0 ) / bm->h; + + for (h = 0; h < bm->h; h++) { + ubyte *drow = &dptr[bm->w * h]; + ubyte *srow = &sptr[f2i(v)*the_anim->width]; + + utmp = u; + + for (w = 0; w < bm->w; w++) { + *drow++ = srow[f2i(utmp)]; + utmp += du; + } + v += dv; + } + } + // 16 bpp + else { + int w,h; + fix u, utmp, v, du, dv; + + u = v = 0; + + du = ( the_anim->width*F1_0 ) / bm->w; + dv = ( the_anim->height*F1_0 ) / bm->h; + + for (h = 0; h < bm->h; h++) { + ushort *drow = &((ushort*)dptr)[bm->w * h]; + ushort *srow = &((ushort*)sptr)[f2i(v)*the_anim->width]; + + utmp = u; + + for (w = 0; w < bm->w; w++) { + *drow++ = srow[f2i(utmp)]; + utmp += du; + } + v += dv; + } + } + } else { + // 1-to-1 mapping + memcpy(dptr, sptr, size); + } + + bm_convert_format( first_frame+i, bm, bpp, flags ); + + // Skip a frame + if ( (i < nframes-1) && can_drop_frames ) { + frame_data = anim_get_next_raw_buffer(the_anim_instance, 0, flags & BMP_AABITMAP ? 1 : 0, bm->bpp); + } + + //mprintf(( "Checksum = %d\n", be->palette_checksum )); + } + + free_anim_instance(the_anim_instance); + anim_free(the_anim); +} + + +void bm_lock_user( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags ) +{ + // int idx; + // ushort bit_16; + + // Unload any existing data + bm_free_data( bitmapnum ); + + switch( be->info.user.bpp ) { + case 16: // user 16 bit bitmap + bmp->bpp = bpp; + bmp->flags = be->info.user.flags; + bmp->data = (uint)be->info.user.data; + break; + + case 8: // Going from 8 bpp to something (probably only for aabitmaps) + /* + Assert(flags & BMP_AABITMAP); + bmp->bpp = 16; + bmp->data = (uint)malloc(bmp->w * bmp->h * 2); + bmp->flags = be->info.user.flags; + bmp->palette = NULL; + + // go through and map the pixels + for(idx=0; idxw * bmp->h; idx++){ + bit_16 = (ushort)((ubyte*)be->info.user.data)[idx]; + Assert(bit_16 <= 255); + + // stuff the final result + memcpy((char*)bmp->data + (idx * 2), &bit_16, sizeof(ushort)); + } + */ + Assert(flags & BMP_AABITMAP); + bmp->bpp = bpp; + bmp->flags = be->info.user.flags; + bmp->data = (uint)be->info.user.data; + break; + + // default: + // Error( LOCATION, "Unhandled user bitmap conversion from %d to %d bpp", be->info.user.bpp, bmp->bpp ); + } + + bm_convert_format( bitmapnum, bmp, bpp, flags ); +} + +void bm_lock_tga( int handle, int bitmapnum, bitmap_entry *be, bitmap *bmp, ubyte bpp, ubyte flags ) +{ + ubyte *data; + + // Unload any existing data + bm_free_data( bitmapnum ); + + if(Fred_running || Is_standalone){ + Assert(bpp == 8); + } else { + Assert(bpp == 16); + } + + // should never try to make an aabitmap out of a targa + Assert(!(flags & BMP_AABITMAP)); + + // allocate bitmap data + if(bpp == 16){ + data = (ubyte*)bm_malloc(bitmapnum, bmp->w * bmp->h * 2); + } else { + data = (ubyte*)bm_malloc(bitmapnum, bmp->w * bmp->h); + } + bmp->bpp = bpp; + bmp->data = (uint)data; + bmp->palette = NULL; + if(bpp == 16){ + memset( data, 0, bmp->w * bmp->h * 2); + } else { + memset( data, 0, bmp->w * bmp->h ); + } + + Assert( &be->bm == bmp ); + #ifdef BMPMAN_NDEBUG + Assert( be->data_size > 0 ); + #endif + + int tga_error=targa_read_bitmap( be->filename, data, NULL, (bpp == 16) ? 2 : 1); + if ( tga_error != TARGA_ERROR_NONE ) { + // Error( LOCATION, "Couldn't open '%s'\n", be->filename ); + //Error( LOCATION, "Couldn't open '%s'\n", filename ); + //return -1; + } + + #ifdef BMPMAN_NDEBUG + Assert( be->data_size > 0 ); + #endif + + bmp->flags = 0; + bm_convert_format( bitmapnum, bmp, bpp, flags ); +} + +MONITOR( NumBitmapPage ); +MONITOR( SizeBitmapPage ); + +// This locks down a bitmap and returns a pointer to a bitmap +// that can be accessed until you call bm_unlock. Only lock +// a bitmap when you need it! This will convert it into the +// appropriate format also. +bitmap * bm_lock( int handle, ubyte bpp, ubyte flags ) +{ + bitmap *bmp; + bitmap_entry *be; + + + if ( !bm_inited ) bm_init(); + + int bitmapnum = handle % MAX_BITMAPS; + Assert( bm_bitmaps[bitmapnum].handle == handle ); // INVALID BITMAP HANDLE + +// flags &= (~BMP_RLE); + + // if we're on a standalone server, aways for it to lock to 8 bits + if(Is_standalone){ + bpp = 8; + flags = 0; + } + // otherwise do it as normal + else { + if(Fred_running || Pofview_running){ + Assert( bpp == 8 ); + Assert( (bm_bitmaps[bitmapnum].type == BM_TYPE_PCX) || (bm_bitmaps[bitmapnum].type == BM_TYPE_ANI) || (bm_bitmaps[bitmapnum].type == BM_TYPE_TGA)); + } else { + if(flags & BMP_AABITMAP){ + Assert( bpp == 8 ); + } else { + Assert( bpp == 16 ); + } + } + } + + be = &bm_bitmaps[bitmapnum]; + bmp = &be->bm; + + // If you hit this assert, chances are that someone freed the + // wrong bitmap and now someone is trying to use that bitmap. + // See John. + Assert( be->type != BM_TYPE_NONE ); + + // Increment ref count for bitmap since lock was made on it. + Assert(be->ref_count >= 0); + be->ref_count++; // Lock it before we page in data; this prevents a callback from freeing this + // as it gets read in + + // Mark this bitmap as used this frame + #ifdef BMPMAN_NDEBUG + if ( be->used_this_frame < 255 ) { + be->used_this_frame++; + } + #endif + + // if bitmap hasn't been loaded yet, then load it from disk + // reread the bitmap from disk under certain conditions + int pal_changed = 0; + int rle_changed = 0; + int fake_xparent_changed = 0; + if ( (bmp->data == NULL) || (bpp != bmp->bpp) || pal_changed || rle_changed || fake_xparent_changed ) { + Assert(be->ref_count == 1); + + if ( be->type != BM_TYPE_USER ) { + if ( bmp->data == NULL ) { + nprintf (("BmpMan","Loading %s for the first time.\n", be->filename)); + } else if ( bpp != bmp->bpp ) { + nprintf (("BmpMan","Reloading %s from bitdepth %d to bitdepth %d\n", be->filename, bmp->bpp, bpp)); + } else if ( pal_changed ) { + nprintf (("BmpMan","Reloading %s to remap palette\n", be->filename)); + } else if ( rle_changed ) { + nprintf (("BmpMan","Reloading %s to change RLE.\n", be->filename)); + } else if ( fake_xparent_changed ) { + nprintf (("BmpMan","Reloading %s to change fake xparency.\n", be->filename)); + } + } + + MONITOR_INC( NumBitmapPage, 1 ); + MONITOR_INC( SizeBitmapPage, bmp->w*bmp->h ); + + if ( !Bm_paging ) { + if ( be->type != BM_TYPE_USER ) { + char flag_text[64]; + strcpy( flag_text, "--" ); + nprintf(( "Paging", "Loading %s (%dx%dx%dx%s)\n", be->filename, bmp->w, bmp->h, bpp, flag_text )); + } + } + + // select proper format + if(flags & BMP_AABITMAP){ + BM_SELECT_ALPHA_TEX_FORMAT(); + } else if(flags & BMP_TEX_ANY){ + BM_SELECT_TEX_FORMAT(); + } else { + BM_SELECT_SCREEN_FORMAT(); + } + + switch ( be->type ) { + case BM_TYPE_PCX: + bm_lock_pcx( handle, bitmapnum, be, bmp, bpp, flags ); + break; + + case BM_TYPE_ANI: + bm_lock_ani( handle, bitmapnum, be, bmp, bpp, flags ); + break; + + case BM_TYPE_USER: + bm_lock_user( handle, bitmapnum, be, bmp, bpp, flags ); + break; + + case BM_TYPE_TGA: + bm_lock_tga( handle, bitmapnum, be, bmp, bpp, flags ); + break; + + default: + Warning(LOCATION, "Unsupported type in bm_lock -- %d\n", be->type ); + return NULL; + } + + // always go back to screen format + BM_SELECT_SCREEN_FORMAT(); + } + + if ( be->type == BM_TYPE_ANI ) { + int i,first = bm_bitmaps[bitmapnum].info.ani.first_frame; + + for ( i=0; i< bm_bitmaps[first].info.ani.num_frames; i++ ) { + // Mark all the bitmaps in this bitmap or animation as recently used + bm_bitmaps[first+i].last_used = timer_get_milliseconds(); + + // Mark all the bitmaps in this bitmap or animation as used for the usage tracker. + #ifdef BMPMAN_NDEBUG + bm_bitmaps[first+i].used_count++; + #endif + bm_bitmaps[first+i].used_flags = flags; + } + } else { + // Mark all the bitmaps in this bitmap or animation as recently used + be->last_used = timer_get_milliseconds(); + + // Mark all the bitmaps in this bitmap or animation as used for the usage tracker. + #ifdef BMPMAN_NDEBUG + be->used_count++; + #endif + be->used_flags = flags; + } + + return bmp; +} + +// Unlocks a bitmap +// +// Decrements the ref_count member of the bitmap_entry struct. A bitmap can only be unloaded +// when the ref_count is 0. +// +void bm_unlock( int handle ) +{ + bitmap_entry *be; + bitmap *bmp; + + int bitmapnum = handle % MAX_BITMAPS; + Assert( bm_bitmaps[bitmapnum].handle == handle ); // INVALID BITMAP HANDLE + + Assert(bitmapnum >= 0 && bitmapnum < MAX_BITMAPS); + if ( !bm_inited ) bm_init(); + + be = &bm_bitmaps[bitmapnum]; + bmp = &be->bm; + + be->ref_count--; + Assert(be->ref_count >= 0); // Trying to unlock data more times than lock was called!!! + +} + + +void bm_update() +{ +} + +char *bm_get_filename(int handle) +{ + int n; + + n = handle % MAX_BITMAPS; + Assert(bm_bitmaps[n].handle == handle); // INVALID BITMAP HANDLE + return bm_bitmaps[n].filename; +} + +void bm_get_palette(int handle, ubyte *pal, char *name) +{ + char *filename; + int w,h; + + int n= handle % MAX_BITMAPS; + Assert( bm_bitmaps[n].handle == handle ); // INVALID BITMAP HANDLE + + filename = bm_bitmaps[n].filename; + + if (name) { + strcpy( name, filename ); + } + + int pcx_error=pcx_read_header( filename, &w, &h, pal ); + if ( pcx_error != PCX_ERROR_NONE ){ + // Error(LOCATION, "Couldn't open '%s'\n", filename ); + } +} + +// -------------------------------------------------------------------------------------- +// bm_release() - unloads the bitmap's data and entire slot, so bitmap 'n' won't be valid anymore +// +// parameters: n => index into bm_bitmaps ( index returned from bm_load() or bm_create() ) +// +// returns: nothing + +void bm_release(int handle) +{ + bitmap_entry *be; + + int n = handle % MAX_BITMAPS; + + Assert(n >= 0 && n < MAX_BITMAPS); + be = &bm_bitmaps[n]; + + if ( bm_bitmaps[n].type == BM_TYPE_NONE ) { + return; // Already been released? + } + + if ( bm_bitmaps[n].type != BM_TYPE_USER ) { + return; + } + + Assert( be->handle == handle ); // INVALID BITMAP HANDLE + + // If it is locked, cannot free it. + if (be->ref_count != 0) { + nprintf(("BmpMan", "tried to unload %s that has a lock count of %d.. not unloading\n", be->filename, be->ref_count)); + return; + } + + bm_free_data(n); + + if ( bm_bitmaps[n].type == BM_TYPE_USER ) { + bm_bitmaps[n].info.user.data = NULL; + bm_bitmaps[n].info.user.bpp = 0; + } + + + bm_bitmaps[n].type = BM_TYPE_NONE; + + // Fill in bogus structures! + + // For debugging: + strcpy( bm_bitmaps[n].filename, "IVE_BEEN_RELEASED!" ); + bm_bitmaps[n].signature = 0xDEADBEEF; // a unique signature identifying the data + bm_bitmaps[n].palette_checksum = 0xDEADBEEF; // checksum used to be sure bitmap is in current palette + + // bookeeping + #ifdef BMPMAN_NDEBUG + bm_bitmaps[n].data_size = -1; // How much data this bitmap uses + #endif + bm_bitmaps[n].ref_count = -1; // Number of locks on bitmap. Can't unload unless ref_count is 0. + + // Bitmap info + bm_bitmaps[n].bm.w = bm_bitmaps[n].bm.h = -1; + + // Stuff needed for animations + // Stuff needed for user bitmaps + memset( &bm_bitmaps[n].info, 0, sizeof(bm_extra_info) ); + + bm_bitmaps[n].handle = -1; +} + + + + + + +// -------------------------------------------------------------------------------------- +// bm_unload() - unloads the data, but not the bitmap info. +// +// parameters: n => index into bm_bitmaps ( index returned from bm_load() or bm_create() ) +// +// returns: 0 => unload failed +// 1 => unload successful +// +int bm_unload( int handle ) +{ + bitmap_entry *be; + bitmap *bmp; + + int n = handle % MAX_BITMAPS; + + Assert(n >= 0 && n < MAX_BITMAPS); + be = &bm_bitmaps[n]; + bmp = &be->bm; + + if ( be->type == BM_TYPE_NONE ) { + return 0; // Already been released + } + + Assert( be->handle == handle ); // INVALID BITMAP HANDLE! + + // If it is locked, cannot free it. + if (be->ref_count != 0) { + nprintf(("BmpMan", "tried to unload %s that has a lock count of %d.. not unloading\n", be->filename, be->ref_count)); + return 0; + } + + nprintf(("BmpMan", "unloading %s. %dx%dx%d\n", be->filename, bmp->w, bmp->h, bmp->bpp)); + bm_free_data(n); // clears flags, bbp, data, etc + + return 1; +} + + +// unload all used bitmaps +void bm_unload_all() +{ + int i; + + for (i = 0; i < MAX_BITMAPS; i++) { + if ( bm_bitmaps[i].type != BM_TYPE_NONE ) { + bm_unload(bm_bitmaps[i].handle); + } + } +} + + +DCF(bmpman,"Shows/changes bitmap caching parameters and usage") +{ + if ( Dc_command ) { + dc_get_arg(ARG_STRING); + if ( !strcmp( Dc_arg, "flush" )) { + dc_printf( "Total RAM usage before flush: %d bytes\n", bm_texture_ram ); + int i; + for (i = 0; i < MAX_BITMAPS; i++) { + if ( bm_bitmaps[i].type != BM_TYPE_NONE ) { + bm_free_data(i); + } + } + dc_printf( "Total RAM after flush: %d bytes\n", bm_texture_ram ); + } else if ( !strcmp( Dc_arg, "ram" )) { + dc_get_arg(ARG_INT); + Bm_max_ram = Dc_arg_int*1024*1024; + } else { + // print usage, not stats + Dc_help = 1; + } + } + + if ( Dc_help ) { + dc_printf( "Usage: BmpMan keyword\nWhere keyword can be in the following forms:\n" ); + dc_printf( "BmpMan flush Unloads all bitmaps.\n" ); + dc_printf( "BmpMan ram x Sets max mem usage to x MB. (Set to 0 to have no limit.)\n" ); + dc_printf( "\nUse '? BmpMan' to see status of Bitmap manager.\n" ); + Dc_status = 0; // don't print status if help is printed. Too messy. + } + + if ( Dc_status ) { + dc_printf( "Total RAM usage: %d bytes\n", bm_texture_ram ); + + + if ( Bm_max_ram > 1024*1024 ) + dc_printf( "Max RAM allowed: %.1f MB\n", i2fl(Bm_max_ram)/(1024.0f*1024.0f) ); + else if ( Bm_max_ram > 1024 ) + dc_printf( "Max RAM allowed: %.1f KB\n", i2fl(Bm_max_ram)/(1024.0f) ); + else if ( Bm_max_ram > 0 ) + dc_printf( "Max RAM allowed: %d bytes\n", Bm_max_ram ); + else + dc_printf( "No RAM limit\n" ); + + + } +} + +// Marks a texture as being used for this level +void bm_page_in_texture( int bitmapnum, int nframes ) +{ + int i; + for (i=0; i= 0) && (Ship_info[ship_info_index].num_nondark_colors > 0)){ + // mprintf(("Using custom pixels for %s\n", Ship_info[ship_info_index].name)); + palman_set_nondarkening(Ship_info[ship_info_index].nondark_colors, Ship_info[ship_info_index].num_nondark_colors); + } + // use the colors from the default table + else { + // mprintf(("Using default pixels\n")); + palman_set_nondarkening(Palman_non_darkening_default, Palman_num_nondarkening_default); + } +#endif + + // if preloaded == 3, load it as an xparent texture + if(bm_bitmaps[i].used_flags == BMP_AABITMAP){ + bm_lock( bm_bitmaps[i].handle, 8, bm_bitmaps[i].used_flags ); + } else { + bm_lock( bm_bitmaps[i].handle, 16, bm_bitmaps[i].used_flags ); + } + bm_unlock( bm_bitmaps[i].handle ); + + if ( d3d_preloading ) { + if ( !gr_d3d_preload(bm_bitmaps[i].handle, (bm_bitmaps[i].preloaded==2) ) ) { + mprintf(( "Out of VRAM. Done preloading.\n" )); + d3d_preloading = 0; + } + } + + n++; + #ifdef BMPMAN_NDEBUG + if ( Bm_ram_freed ) { + nprintf(( "BmpInfo","BMPMAN: Not enough cache memory to load all level bitmaps\n" )); + break; + } + #endif + } + } + game_busy(); + } + nprintf(( "BmpInfo","BMPMAN: Loaded %d bitmaps that are marked as used for this level.\n", n )); + + int total_bitmaps = 0; + for (i = 0; i < MAX_BITMAPS; i++) { + if ( bm_bitmaps[i].type != BM_TYPE_NONE ) { + total_bitmaps++; + } + if ( bm_bitmaps[i].type == BM_TYPE_USER ) { + mprintf(( "User bitmap '%s'\n", bm_bitmaps[i].filename )); + } + } + + mprintf(( "Bmpman: %d/%d bitmap slots in use.\n", total_bitmaps, MAX_BITMAPS )); + //mprintf(( "Bmpman: Usage went from %d KB to %d KB.\n", usage_before/1024, usage_after/1024 )); + + Bm_paging = 0; +} + +int bm_get_cache_slot( int bitmap_id, int separate_ani_frames ) +{ + int n = bitmap_id % MAX_BITMAPS; + + Assert( bm_bitmaps[n].handle == bitmap_id ); // INVALID BITMAP HANDLE + + bitmap_entry *be = &bm_bitmaps[n]; + + if ( (!separate_ani_frames) && (be->type == BM_TYPE_ANI) ) { + return be->info.ani.first_frame; + } + + return n; + +} + +// convert a 24 bit value to a 16 bit value +void bm_24_to_16(int bit_24, ushort *bit_16) +{ + ubyte *pixel = (ubyte*)&bit_24; + ubyte alpha = 1; + + bm_set_components((ubyte*)bit_16, (ubyte*)&pixel[0], (ubyte*)&pixel[1], (ubyte*)&pixel[2], &alpha); +} + +extern int D3D_32bit; + +void (*bm_set_components)(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a) = NULL; + +void bm_set_components_argb(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) +{ + // rgba + *((ushort*)pixel) |= (ushort)(( (int)*rv / Gr_current_red->scale ) << Gr_current_red->shift); + *((ushort*)pixel) |= (ushort)(( (int)*gv / Gr_current_green->scale ) << Gr_current_green->shift); + *((ushort*)pixel) |= (ushort)(( (int)*bv / Gr_current_blue->scale ) << Gr_current_blue->shift); + *((ushort*)pixel) &= ~(0x8000); + if(*av){ + *((ushort*)pixel) |= 0x8000; + } +} + +void bm_set_components_d3d(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) +{ + // rgba + *((ushort*)pixel) |= (ushort)(( (int)*rv / Gr_current_red->scale ) << Gr_current_red->shift); + *((ushort*)pixel) |= (ushort)(( (int)*gv / Gr_current_green->scale ) << Gr_current_green->shift); + *((ushort*)pixel) |= (ushort)(( (int)*bv / Gr_current_blue->scale ) << Gr_current_blue->shift); + if(*av == 0){ + *((ushort*)pixel) = (ushort)Gr_current_green->mask; + } +} + +void bm_set_components_argb_d3d_16_screen(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) +{ + *((ushort*)pixel) |= (ushort)(( (int)*rv / Gr_current_red->scale ) << Gr_current_red->shift); + *((ushort*)pixel) |= (ushort)(( (int)*gv / Gr_current_green->scale ) << Gr_current_green->shift); + *((ushort*)pixel) |= (ushort)(( (int)*bv / Gr_current_blue->scale ) << Gr_current_blue->shift); + if(*av == 0){ + *((ushort*)pixel) = (ushort)Gr_current_green->mask; + } +} + +void bm_set_components_argb_d3d_32_screen(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) +{ + *((uint*)pixel) |= (uint)(( (int)*rv / Gr_current_red->scale ) << Gr_current_red->shift); + *((uint*)pixel) |= (uint)(( (int)*gv / Gr_current_green->scale ) << Gr_current_green->shift); + *((uint*)pixel) |= (uint)(( (int)*bv / Gr_current_blue->scale ) << Gr_current_blue->shift); + if(*av == 0){ + *((uint*)pixel) = (uint)Gr_current_green->mask; + } +} + +void bm_set_components_argb_d3d_16_tex(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) +{ + *((ushort*)pixel) |= (ushort)(( (int)*rv / Gr_current_red->scale ) << Gr_current_red->shift); + *((ushort*)pixel) |= (ushort)(( (int)*gv / Gr_current_green->scale ) << Gr_current_green->shift); + *((ushort*)pixel) |= (ushort)(( (int)*bv / Gr_current_blue->scale ) << Gr_current_blue->shift); + *((ushort*)pixel) &= ~(Gr_current_alpha->mask); + if(*av){ + *((ushort*)pixel) |= (ushort)(Gr_current_alpha->mask); + } else { + *((ushort*)pixel) = 0; + } +} + +void bm_set_components_argb_d3d_32_tex(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) +{ + *((ushort*)pixel) |= (ushort)(( (int)*rv / Gr_current_red->scale ) << Gr_current_red->shift); + *((ushort*)pixel) |= (ushort)(( (int)*gv / Gr_current_green->scale ) << Gr_current_green->shift); + *((ushort*)pixel) |= (ushort)(( (int)*bv / Gr_current_blue->scale ) << Gr_current_blue->shift); + *((ushort*)pixel) &= ~(Gr_current_alpha->mask); + if(*av){ + *((ushort*)pixel) |= (ushort)(Gr_current_alpha->mask); + } else { + *((ushort*)pixel) = 0; + } +} + +// for selecting pixel formats +void BM_SELECT_SCREEN_FORMAT() +{ + Gr_current_red = &Gr_red; + Gr_current_green = &Gr_green; + Gr_current_blue = &Gr_blue; + Gr_current_alpha = &Gr_alpha; + + // setup pointers + if(gr_screen.mode == GR_GLIDE){ + bm_set_components = bm_set_components_argb; + } else if(gr_screen.mode == GR_DIRECT3D){ + if(Bm_pixel_format == BM_PIXEL_FORMAT_D3D){ + bm_set_components = bm_set_components_d3d; + } else { + if(D3D_32bit){ + bm_set_components = bm_set_components_argb_d3d_32_screen; + } else { + bm_set_components = bm_set_components_argb_d3d_16_screen; + } + } + } +} + +void BM_SELECT_TEX_FORMAT() +{ + Gr_current_red = &Gr_t_red; + Gr_current_green = &Gr_t_green; + Gr_current_blue = &Gr_t_blue; + Gr_current_alpha = &Gr_t_alpha; + + // setup pointers + if(gr_screen.mode == GR_GLIDE){ + bm_set_components = bm_set_components_argb; + } else if(gr_screen.mode == GR_DIRECT3D){ + if(Bm_pixel_format == BM_PIXEL_FORMAT_D3D){ + bm_set_components = bm_set_components_d3d; + } else { + if(D3D_32bit){ + bm_set_components = bm_set_components_argb_d3d_32_tex; + } else { + bm_set_components = bm_set_components_argb_d3d_16_tex; + } + } + } +} + +void BM_SELECT_ALPHA_TEX_FORMAT() +{ + Gr_current_red = &Gr_ta_red; + Gr_current_green = &Gr_ta_green; + Gr_current_blue = &Gr_ta_blue; + Gr_current_alpha = &Gr_ta_alpha; + + // setup pointers + if(gr_screen.mode == GR_GLIDE){ + bm_set_components = bm_set_components_argb; + } else if(gr_screen.mode == GR_DIRECT3D){ + if(Bm_pixel_format == BM_PIXEL_FORMAT_D3D){ + bm_set_components = bm_set_components_d3d; + } else { + if(D3D_32bit){ + bm_set_components = bm_set_components_argb_d3d_32_tex; + } else { + bm_set_components = bm_set_components_argb_d3d_16_tex; + } + } + } +} + +// set the rgba components of a pixel, any of the parameters can be -1 +/* +void bm_set_components(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) +{ + int bit_32 = 0; + + // pick a byte size - 32 bits only if 32 bit mode d3d and screen format + if(D3D_32bit && (Gr_current_red == &Gr_red)){ + bit_32 = 1; + } + + if(bit_32){ + *((uint*)pixel) |= (uint)(( (int)*rv / Gr_current_red->scale ) << Gr_current_red->shift); + } else { + *((ushort*)pixel) |= (ushort)(( (int)*rv / Gr_current_red->scale ) << Gr_current_red->shift); + } + if(bit_32){ + *((uint*)pixel) |= (uint)(( (int)*gv / Gr_current_green->scale ) << Gr_current_green->shift); + } else { + *((ushort*)pixel) |= (ushort)(( (int)*gv / Gr_current_green->scale ) << Gr_current_green->shift); + } + if(bit_32){ + *((uint*)pixel) |= (uint)(( (int)*bv / Gr_current_blue->scale ) << Gr_current_blue->shift); + } else { + *((ushort*)pixel) |= (ushort)(( (int)*bv / Gr_current_blue->scale ) << Gr_current_blue->shift); + } + + // NOTE - this is a semi-hack. For direct3d we don't use an alpha bit, so if *av == 0, we just set the whole pixel to be Gr_green.mask + // ergo, we need to do this _last_ + switch(Bm_pixel_format){ + // glide has an alpha channel so we have to unset ir or set it each time + case BM_PIXEL_FORMAT_ARGB: + Assert(!bit_32); + *((ushort*)pixel) &= ~(0x8000); + if(*av){ + *((ushort*)pixel) |= 0x8000; + } + break; + + // this d3d format has no alpha channel, so only make it "transparent", never make it "non-transparent" + case BM_PIXEL_FORMAT_D3D: + Assert(!bit_32); + if(*av == 0){ + *((ushort*)pixel) = (ushort)Gr_current_green->mask; + } + break; + + // nice 1555 texture format + case BM_PIXEL_FORMAT_ARGB_D3D: + // if we're writing to normal texture format + if(Gr_current_red == &Gr_t_red){ + Assert(!bit_32); + *((ushort*)pixel) &= ~(Gr_current_alpha->mask); + if(*av){ + *((ushort*)pixel) |= (ushort)(Gr_current_alpha->mask); + } else { + *((ushort*)pixel) = 0; + } + } + // otherwise if we're writing to screen format, still do it the green mask way + else { + if(*av == 0){ + if(bit_32){ + *((uint*)pixel) = (uint)Gr_current_green->mask; + } else { + *((ushort*)pixel) = (ushort)Gr_current_green->mask; + } + } + } + break; + } +} +*/ + +// get the rgba components of a pixel, any of the parameters can be NULL +void bm_get_components(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a) +{ + int bit_32 = 0; + + // pick a byte size - 32 bits only if 32 bit mode d3d and screen format + if(D3D_32bit && (Gr_current_red == &Gr_red)){ + bit_32 = 1; + } + + if(r != NULL){ + if(bit_32){ + *r = ubyte(( (*((uint*)pixel) & Gr_current_red->mask)>>Gr_current_red->shift)*Gr_current_red->scale); + } else { + *r = ubyte(( ( ((ushort*)pixel)[0] & Gr_current_red->mask)>>Gr_current_red->shift)*Gr_current_red->scale); + } + } + if(g != NULL){ + if(bit_32){ + *g = ubyte(( (*((uint*)pixel) & Gr_current_green->mask) >>Gr_current_green->shift)*Gr_current_green->scale); + } else { + *g = ubyte(( ( ((ushort*)pixel)[0] & Gr_current_green->mask) >>Gr_current_green->shift)*Gr_current_green->scale); + } + } + if(b != NULL){ + if(bit_32){ + *b = ubyte(( (*((uint*)pixel) & Gr_current_blue->mask)>>Gr_current_blue->shift)*Gr_current_blue->scale); + } else { + *b = ubyte(( ( ((ushort*)pixel)[0] & Gr_current_blue->mask)>>Gr_current_blue->shift)*Gr_current_blue->scale); + } + } + + // get the alpha value + if(a != NULL){ + *a = 1; + + switch(Bm_pixel_format){ + // glide has an alpha channel so we have to unset ir or set it each time + case BM_PIXEL_FORMAT_ARGB: + Assert(!bit_32); + if(!( ((ushort*)pixel)[0] & 0x8000)){ + *a = 0; + } + break; + + // this d3d format has no alpha channel, so only make it "transparent", never make it "non-transparent" + case BM_PIXEL_FORMAT_D3D: + Assert(!bit_32); + if( *((ushort*)pixel) == Gr_current_green->mask){ + *a = 0; + } + break; + + // nice 1555 texture format mode + case BM_PIXEL_FORMAT_ARGB_D3D: + // if we're writing to a normal texture, use nice alpha bits + if(Gr_current_red == &Gr_t_red){ + Assert(!bit_32); + + if(!(*((ushort*)pixel) & Gr_current_alpha->mask)){ + *a = 0; + } + } + // otherwise do it as normal + else { + if(bit_32){ + if(*((int*)pixel) == Gr_current_green->mask){ + *a = 0; + } + } else { + if(*((ushort*)pixel) == Gr_current_green->mask){ + *a = 0; + } + } + } + } + } +} + +// get filename +void bm_get_filename(int bitmapnum, char *filename) +{ + int n = bitmapnum % MAX_BITMAPS; + + // return filename + strcpy(filename, bm_bitmaps[n].filename); +} + +// given a bitmap and a section, return the size (w, h) +void bm_get_section_size(int bitmapnum, int sx, int sy, int *w, int *h) +{ + int bw, bh; + bitmap_section_info *sections; + + // bogus input? + Assert((w != NULL) && (h != NULL)); + if((w == NULL) || (h == NULL)){ + return; + } + + // get bitmap info + bm_get_info(bitmapnum, &bw, &bh, NULL, NULL, NULL, §ions); + + // determine the width and height of this section + *w = sx < (sections->num_x - 1) ? MAX_BMAP_SECTION_SIZE : bw - sections->sx[sx]; + *h = sy < (sections->num_y - 1) ? MAX_BMAP_SECTION_SIZE : bh - sections->sy[sy]; +} diff --git a/src/cfile/cfile.cpp b/src/cfile/cfile.cpp new file mode 100644 index 0000000..c919e02 --- /dev/null +++ b/src/cfile/cfile.cpp @@ -0,0 +1,1686 @@ +/* + * $Logfile: /Freespace2/code/CFile/cfile.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Utilities for operating on files + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 20 9/08/99 10:01p Dave + * Make sure game won't run in a drive's root directory. Make sure + * standalone routes suqad war messages properly to the host. + * + * 19 9/08/99 12:03a Dave + * Make squad logos render properly in D3D all the time. Added intel anim + * directory. + * + * 18 9/06/99 2:35p Dave + * Rename briefing and debriefing voice direcrory paths properly. + * + * 17 9/03/99 1:31a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 16 8/31/99 9:46a Dave + * Support for new cfile cbanims directory. + * + * 15 8/06/99 11:20a Johns + * Don't call game_cd_changed() in a demo or multiplayer beta build. + * + * 14 6/08/99 2:33p Dave + * Fixed release build warning. Put in hud config stuff. + * + * 13 5/19/99 4:07p Dave + * Moved versioning code into a nice isolated common place. Fixed up + * updating code on the pxo screen. Fixed several stub problems. + * + * 12 4/30/99 12:18p Dave + * Several minor bug fixes. + * + * 11 4/07/99 5:54p Dave + * Fixes for encryption in updatelauncher. + * + * 10 3/28/99 5:58p Dave + * Added early demo code. Make objects move. Nice and framerate + * independant, but not much else. Don't use yet unless you're me :) + * + * 9 2/22/99 10:31p Andsager + * Get rid of unneeded includes. + * + * 8 1/12/99 3:15a Dave + * Barracks screen support for selecting squad logos. We need real artwork + * :) + * + * 7 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 6 10/29/98 10:41a Dave + * Change the way cfile initializes exe directory. + * + * 5 10/13/98 9:19a Andsager + * Add localization support to cfile. Optional parameter with cfopen that + * looks for localized files. + * + * 4 10/12/98 9:54a Dave + * Fixed a few file organization things. + * + * 3 10/07/98 6:27p Dave + * Globalized mission and campaign file extensions. Removed Silent Threat + * special code. Moved \cache \players and \multidata into the \data + * directory. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 110 9/17/98 10:31a Hoffoss + * Changed code to use location of executable as root rather than relying + * on the cwd being set correctly. This is most helpful in the case of + * Fred launching due to the user double-clicking on an .fsm file in + * explorer or something (where the cwd is probably the location of the + * .fsm file). + * + * 109 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 108 9/09/98 5:53p Dave + * Put in new tracker packets in API. Change cfile to be able to checksum + * portions of a file. + * + * 107 9/04/98 3:51p Dave + * Put in validated mission updating and application during stats + * updating. + * + * 106 8/31/98 2:06p Dave + * Make cfile sort the ordering or vp files. Added support/checks for + * recognizing "mission disk" players. + * + * 105 8/20/98 5:30p Dave + * Put in handy multiplayer logfile system. Now need to put in useful + * applications of it all over the code. + * + * 104 8/12/98 4:53p Dave + * Put in 32 bit checksumming for PXO missions. No validation on the + * actual tracker yet, though. + * + * 103 6/17/98 9:30a Allender + * fixed red alert replay stats clearing problem + * + * 102 6/05/98 9:46a Lawrance + * check for CD change on cfopen() + * + * 101 5/19/98 1:19p Allender + * new low level reliable socket reading code. Make all missions/campaign + * load/save to data missions folder (i.e. we are rid of the player + * missions folder) + * + * 100 5/13/98 10:22p John + * Added cfile functions to read/write rle compressed blocks of data. + * Made palman use it for .clr files. Made alphacolors calculate on the + * fly rather than caching to/from disk. + * + * 99 5/11/98 10:59a John + * Moved the low-level file reading code into cfilearchive.cpp. + * + * 98 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 97 5/03/98 11:53a John + * Fixed filename case mangling. + * + * 96 5/03/98 8:33a John + * Made sure there weren't any more broken function lurking around like + * the one Allender fixed. + * + * 95 5/02/98 11:05p Allender + * fix cfilelength for pack files + * + * + * $NoKeywords: $ + */ +#define _CFILE_INTERNAL + +#include +#include +#include +#include +#ifndef PLAT_UNIX +#include +#include +#include +#include /* needed for memory mapping of file functions */ +#else +#include +#include +#include +#endif + +#include "pstypes.h" +#include "cfile.h" +#include "encrypt.h" +//#include "outwnd.h" +//#include "vecmat.h" +//#include "timer.h" +#include "cfilesystem.h" +#include "cfilearchive.h" +#include "osapi.h" + +char Cfile_root_dir[CFILE_ROOT_DIRECTORY_LEN] = ""; + +// During cfile_init, verify that Pathtypes[n].index == n for each item +// Each path must have a valid parent that can be tracable all the way back to the root +// so that we can create directories when we need to. +// +cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = { + // What type this is Path Extensions Parent type + { CF_TYPE_INVALID, NULL, NULL, CF_TYPE_INVALID }, + // Root must be index 1!! + { CF_TYPE_ROOT, "", ".mve", CF_TYPE_ROOT }, + { CF_TYPE_DATA, "Data", ".cfg .log .txt", CF_TYPE_ROOT }, + { CF_TYPE_MAPS, "Data\\Maps", ".pcx .ani .tga", CF_TYPE_DATA }, + { CF_TYPE_TEXT, "Data\\Text", ".txt .net", CF_TYPE_DATA }, + { CF_TYPE_MISSIONS, "Data\\Missions", ".fs2 .fc2 .ntl .ssv", CF_TYPE_DATA }, + { CF_TYPE_MODELS, "Data\\Models", ".pof", CF_TYPE_DATA }, + { CF_TYPE_TABLES, "Data\\Tables", ".tbl", CF_TYPE_DATA }, + { CF_TYPE_SOUNDS, "Data\\Sounds", ".wav", CF_TYPE_DATA }, + { CF_TYPE_SOUNDS_8B22K, "Data\\Sounds\\8b22k", ".wav", CF_TYPE_SOUNDS }, + { CF_TYPE_SOUNDS_16B11K, "Data\\Sounds\\16b11k", ".wav", CF_TYPE_SOUNDS }, + { CF_TYPE_VOICE, "Data\\Voice", "", CF_TYPE_DATA }, + { CF_TYPE_VOICE_BRIEFINGS, "Data\\Voice\\Briefing", ".wav", CF_TYPE_VOICE }, + { CF_TYPE_VOICE_CMD_BRIEF, "Data\\Voice\\Command_briefings",".wav", CF_TYPE_VOICE }, + { CF_TYPE_VOICE_DEBRIEFINGS, "Data\\Voice\\Debriefing", ".wav", CF_TYPE_VOICE }, + { CF_TYPE_VOICE_PERSONAS, "Data\\Voice\\Personas", ".wav", CF_TYPE_VOICE }, + { CF_TYPE_VOICE_SPECIAL, "Data\\Voice\\Special", ".wav", CF_TYPE_VOICE }, + { CF_TYPE_VOICE_TRAINING, "Data\\Voice\\Training", ".wav", CF_TYPE_VOICE }, + { CF_TYPE_MUSIC, "Data\\Music", ".wav", CF_TYPE_VOICE }, + { CF_TYPE_MOVIES, "Data\\Movies", ".mve .msb", CF_TYPE_DATA }, + { CF_TYPE_INTERFACE, "Data\\Interface", ".pcx .ani .tga", CF_TYPE_DATA }, + { CF_TYPE_FONT, "Data\\Fonts", ".vf", CF_TYPE_DATA }, + { CF_TYPE_EFFECTS, "Data\\Effects", ".ani .pcx .neb .tga", CF_TYPE_DATA }, + { CF_TYPE_HUD, "Data\\Hud", ".ani .pcx .tga", CF_TYPE_DATA }, + { CF_TYPE_PLAYER_MAIN, "Data\\Players", "", CF_TYPE_DATA }, + { CF_TYPE_PLAYER_IMAGES_MAIN, "Data\\Players\\Images", ".pcx", CF_TYPE_PLAYER_MAIN }, + { CF_TYPE_CACHE, "Data\\Cache", ".clr .tmp", CF_TYPE_DATA }, //clr=cached color + { CF_TYPE_PLAYERS, "Data\\Players", ".hcf", CF_TYPE_DATA }, + { CF_TYPE_SINGLE_PLAYERS, "Data\\Players\\Single", ".plr .csg .css", CF_TYPE_PLAYERS }, + { CF_TYPE_MULTI_PLAYERS, "Data\\Players\\Multi", ".plr", CF_TYPE_DATA }, + { CF_TYPE_MULTI_CACHE, "Data\\MultiData", ".pcx .fs2", CF_TYPE_DATA }, + { CF_TYPE_CONFIG, "Data\\Config", ".cfg", CF_TYPE_DATA }, + { CF_TYPE_SQUAD_IMAGES_MAIN, "Data\\Players\\Squads", ".pcx", CF_TYPE_DATA }, + { CF_TYPE_DEMOS, "Data\\Demos", ".fsd", CF_TYPE_DATA }, + { CF_TYPE_CBANIMS, "Data\\CBAnims", ".ani", CF_TYPE_DATA }, + { CF_TYPE_INTEL_ANIMS, "Data\\IntelAnims", ".ani", CF_TYPE_DATA }, +}; + + +#define CFILE_STACK_MAX 8 + +int cfile_inited = 0; +int Cfile_stack_pos = 0; + +char Cfile_stack[128][CFILE_STACK_MAX]; + +Cfile_block Cfile_block_list[MAX_CFILE_BLOCKS]; +CFILE Cfile_list[MAX_CFILE_BLOCKS]; + +char *Cfile_cdrom_dir = NULL; + +// +// Function prototypes for internally-called functions +// +int cfget_cfile_block(); +CFILE *cf_open_fill_cfblock(FILE * fp, int type); +CFILE *cf_open_packed_cfblock(FILE *fp, int type, int offset, int size); +CFILE *cf_open_mapped_fill_cfblock(HANDLE hFile, int type); +void cf_chksum_long_init(); + +void cfile_close() +{ + cf_free_secondary_filelist(); +} + +// determine if the given path is in a root directory (c:\ or c:\freespace2.exe or c:\fred2.exe etc) +int cfile_in_root_dir(char *exe_path) +{ + int token_count = 0; + char path_copy[2048] = ""; + char *tok; + + // bogus + if(exe_path == NULL){ + return 1; + } + + // copy the path + memset(path_copy, 0, 2048); + strncpy(path_copy, exe_path, 2047); + + // count how many slashes there are in the path + tok = strtok(path_copy, "\\"); + if(tok == NULL){ + return 1; + } + do { + token_count++; + tok = strtok(NULL, "\\"); + } while(tok != NULL); + + // root directory if we have <= 1 slash + if(token_count <= 2){ + return 1; + } + + // not-root directory + return 0; +} + +// cfile_init() initializes the cfile system. Called once at application start. +// +// returns: success ==> 0 +// error ==> non-zero +// +int cfile_init(char *exe_dir, char *cdrom_dir) +{ + int i; + + // initialize encryption + encrypt_init(); + + if ( !cfile_inited ) { + char buf[128]; + + cfile_inited = 1; + + strcpy(buf, exe_dir); + i = strlen(buf); + + // are we in a root directory? + if(cfile_in_root_dir(buf)){ +#ifdef PLAT_UNIX + fprintf (stderr, "ERROR: Freespace2/Fred2 cannot be run from a drive root directory!"); +#else + MessageBox((HWND)NULL, "Freespace2/Fred2 cannot be run from a drive root directory!", "Error", MB_OK); +#endif + return 1; + } + + while (i--) { + if (buf[i] == '\\'){ + break; + } + } + + if (i >= 2) { + buf[i] = 0; + cfile_chdir(buf); + } else { +#ifdef PLAT_UNIX + fprintf (stderr, "Error trying to determine executable root directory!"); +#else + MessageBox((HWND)NULL, "Error trying to determine executable root directory!", "Error", MB_OK); +#endif + return 1; + } + + // set root directory + strncpy(Cfile_root_dir, buf, CFILE_ROOT_DIRECTORY_LEN-1); + + for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) { + Cfile_block_list[i].type = CFILE_BLOCK_UNUSED; + } + + Cfile_cdrom_dir = cdrom_dir; + cf_build_secondary_filelist(Cfile_cdrom_dir); + + // 32 bit CRC table init + cf_chksum_long_init(); + + atexit( cfile_close ); + } + + return 0; +} + +// Call this if pack files got added or removed or the +// cdrom changed. This will refresh the list of filenames +// stored in packfiles and on the cdrom. +void cfile_refresh() +{ + cf_build_secondary_filelist(Cfile_cdrom_dir); +} + + +// Changes to a drive if valid.. 1=A, 2=B, etc +// If flag, then changes to it. +// Returns 0 if not-valid, 1 if valid. +int cfile_chdrive( int DriveNum, int flag ) +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; + return 0; +#else + int n, org; + int Valid = 0; + + org = -1; + if (!flag) + org = _getdrive(); + + _chdrive( DriveNum ); + n = _getdrive(); + + + if (n == DriveNum ) + Valid = 1; + + if ( (!flag) && (n != org) ) + _chdrive( org ); + return Valid; +#endif +} + +// push current directory on a 'stack' (so we can restore it) and change the directory +int cfile_push_chdir(int type) +{ + int e; + char dir[128]; + char OriginalDirectory[128]; + char *Drive, *Path; + char NoDir[] = "\\."; + + _getcwd(OriginalDirectory, 127); + Assert(Cfile_stack_pos < CFILE_STACK_MAX); + strcpy(Cfile_stack[Cfile_stack_pos++], OriginalDirectory); + + cf_create_default_path_string( dir, type, NULL ); + _strlwr(dir); + Drive = strchr(dir, ':'); + + if (Drive) { + if (!cfile_chdrive( *(Drive - 1) - 'a' + 1, 1)) + return 1; + + Path = Drive+1; + + } else { + Path = dir; + } + + if (!(*Path)) { + Path = NoDir; + } + + // This chdir might get a critical error! + e = _chdir( Path ); + if (e) { + cfile_chdrive( OriginalDirectory[0] - 'a' + 1, 1 ); + return 2; + } + + return 0; +} + + +int cfile_chdir(char *dir) +{ + int e; + char OriginalDirectory[128]; + char *Drive, *Path; + char NoDir[] = "\\."; + + _getcwd(OriginalDirectory, 127); + _strlwr(dir); + + Drive = strchr(dir, ':'); + if (Drive) { + if (!cfile_chdrive( *(Drive - 1) - 'a' + 1, 1)) + return 1; + + Path = Drive+1; + + } else { + Path = dir; + } + + if (!(*Path)) { + Path = NoDir; + } + + // This chdir might get a critical error! + e = _chdir( Path ); + if (e) { + cfile_chdrive( OriginalDirectory[0] - 'a' + 1, 1 ); + return 2; + } + + return 0; +} + +int cfile_pop_dir() +{ + Assert(Cfile_stack_pos); + Cfile_stack_pos--; + return cfile_chdir(Cfile_stack[Cfile_stack_pos]); +} + +// flush (delete all files in) the passed directory (by type), return the # of files deleted +// NOTE : WILL NOT DELETE READ-ONLY FILES +int cfile_flush_dir(int dir_type) +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; + return 0; +#else + int find_handle; + int del_count; + _finddata_t find; + + Assert( CF_TYPE_SPECIFIED(dir_type) ); + + // attempt to change the directory to the passed type + if(cfile_push_chdir(dir_type)){ + return 0; + } + + // proceed to delete the files + find_handle = _findfirst( "*", &find ); + del_count = 0; + if (find_handle != -1) { + do { + if (!(find.attrib & _A_SUBDIR) && !(find.attrib & _A_RDONLY)) { + // delete the file + cf_delete(find.name,dir_type); + + // increment the deleted count + del_count++; + } + } while (!_findnext(find_handle, &find)); + _findclose( find_handle ); + } + + // pop the directory back + cfile_pop_dir(); + + // return the # of files deleted + return del_count; +#endif +} + + +// add the given extention to a filename (or filepath) if it doesn't already have this +// extension. +// filename = name of filename or filepath to process +// ext = extension to add. Must start with the period +// Returns: new filename or filepath with extension. +char *cf_add_ext(char *filename, char *ext) +{ + int flen, elen; + static char path[MAX_PATH_LEN]; + + flen = strlen(filename); + elen = strlen(ext); + Assert(flen < MAX_PATH_LEN); + strcpy(path, filename); + if ((flen < 4) || stricmp(path + flen - elen, ext)) { + Assert(flen + elen < MAX_PATH_LEN); + strcat(path, ext); + } + + return path; +} + +// Deletes a file. +void cf_delete( char *filename, int dir_type ) +{ + char longname[MAX_PATH_LEN]; + + Assert( CF_TYPE_SPECIFIED(dir_type) ); + + cf_create_default_path_string( longname, dir_type, filename ); + + FILE *fp = fopen(longname, "rb"); + if (fp) { + // delete the file + fclose(fp); + _unlink(longname); + } + +} + + +// Same as _access function to read a file's access bits +int cf_access( char *filename, int dir_type, int mode ) +{ + char longname[MAX_PATH_LEN]; + + Assert( CF_TYPE_SPECIFIED(dir_type) ); + + cf_create_default_path_string( longname, dir_type, filename ); + + return access(longname,mode); +} + + +// Returns 1 if file exists, 0 if not. +int cf_exist( char *filename, int dir_type ) +{ + char longname[MAX_PATH_LEN]; + + Assert( CF_TYPE_SPECIFIED(dir_type) ); + + cf_create_default_path_string( longname, dir_type, filename ); + + FILE *fp = fopen(longname, "rb"); + if (fp) { + return 1; + fclose(fp); + } + + return 0; +} + +void cf_attrib(char *filename, int set, int clear, int dir_type) +{ + char longname[MAX_PATH_LEN]; + + Assert( CF_TYPE_SPECIFIED(dir_type) ); + + cf_create_default_path_string( longname, dir_type, filename ); + + FILE *fp = fopen(longname, "rb"); + if (fp) { + fclose(fp); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + DWORD z = GetFileAttributes(longname); + SetFileAttributes(longname, z | set & ~clear); +#endif + } + +} + +int cf_rename(char *old_name, char *name, int dir_type) +{ + Assert( CF_TYPE_SPECIFIED(dir_type) ); + + int ret_code; + char old_longname[_MAX_PATH]; + char new_longname[_MAX_PATH]; + + cf_create_default_path_string( old_longname, dir_type, old_name ); + cf_create_default_path_string( new_longname, dir_type, name ); + + ret_code = rename(old_longname, new_longname ); + if(ret_code != 0){ + switch(errno){ + case EACCES : + return CF_RENAME_FAIL_ACCESS; + case ENOENT : + default: + return CF_RENAME_FAIL_EXIST; + } + } + + return CF_RENAME_SUCCESS; + + +} + +// Creates the directory path if it doesn't exist. Even creates all its +// parent paths. +void cf_create_directory( int dir_type ) +{ + int num_dirs = 0; + int dir_tree[CF_MAX_PATH_TYPES]; + char longname[MAX_PATH_LEN]; + + Assert( CF_TYPE_SPECIFIED(dir_type) ); + + int current_dir = dir_type; + + do { + Assert( num_dirs < CF_MAX_PATH_TYPES ); // Invalid Pathtypes data? + + dir_tree[num_dirs++] = current_dir; + current_dir = Pathtypes[current_dir].parent_index; + + } while( current_dir != CF_TYPE_ROOT ); + + + int i; + + for (i=num_dirs-1; i>=0; i-- ) { + cf_create_default_path_string( longname, dir_tree[i], NULL ); + +#ifdef PLAT_UNIX + if ( _mkdir(longname, 0777)==0 ) { + mprintf(( "CFILE: Created new directory '%s'\n", longname )); + } +#else + if ( _mkdir(longname)==0 ) { + mprintf(( "CFILE: Created new directory '%s'\n", longname )); + } +#endif + } + + +} + + +extern int game_cd_changed(); + +// cfopen() +// +// parameters: *filepath ==> name of file to open (may be path+name) +// *mode ==> specifies how file should be opened (eg "rb" for read binary) +// passing NULL to mode deletes the file if it exists and returns NULL +// type ==> one of: CFILE_NORMAL +// CFILE_MEMORY_MAPPED +// dir_type => override extension check, value is one of CF_TYPE* #defines +// +// NOTE: type parameter is an optional parameter. The default value is CFILE_NORMAL +// +// +// returns: success ==> address of CFILE structure +// error ==> NULL +// + +CFILE *cfopen(char *file_path, char *mode, int type, int dir_type, bool localize) +{ + char longname[_MAX_PATH]; + +// nprintf(("CFILE", "CFILE -- trying to open %s\n", file_path )); +// #if !defined(MULTIPLAYER_BETA_BUILD) && !defined(FS2_DEMO) + +// we no longer need to do this, and on machines with crappy-ass drivers it can slow things down horribly. +#if 0 + if ( game_cd_changed() ) { + cfile_refresh(); + } +#endif + + //================================================ + // Check that all the parameters make sense + Assert(file_path && strlen(file_path)); + Assert( mode != NULL ); + + // Can only open read-only binary files in memory mapped mode. + if ( (type & CFILE_MEMORY_MAPPED) && strcmp(mode,"rb") ) { + Int3(); + return NULL; + } + + //=========================================================== + // If in write mode, just try to open the file straight off + // the harddisk. No fancy packfile stuff here! + + if ( strchr(mode,'w') ) { + // For write-only files, require a full path or a path type + if ( strpbrk(file_path,"/\\:") ) { + // Full path given? + strcpy(longname, file_path ); + } else { + // Path type given? + Assert( dir_type != CF_TYPE_ANY ); + + // Create the directory if necessary + cf_create_directory( dir_type ); + + cf_create_default_path_string( longname, dir_type, file_path ); + } + Assert( !(type & CFILE_MEMORY_MAPPED) ); + + // JOHN: TODO, you should create the path if it doesn't exist. + + FILE *fp = fopen(longname, mode); + if (fp) { + return cf_open_fill_cfblock(fp, dir_type); + } + return NULL; + } + + + //================================================ + // Search for file on disk, on cdrom, or in a packfile + + int offset, size; + char copy_file_path[MAX_PATH_LEN]; // FIX change in memory from cf_find_file_location + strcpy(copy_file_path, file_path); + + + if ( cf_find_file_location( copy_file_path, dir_type, longname, &size, &offset, localize ) ) { + + // Fount it, now create a cfile out of it + + if ( type & CFILE_MEMORY_MAPPED ) { + + // Can't open memory mapped files out of pack files + if ( offset == 0 ) { +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + HANDLE hFile; + + hFile = CreateFile(longname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (hFile != INVALID_HANDLE_VALUE) { + return cf_open_mapped_fill_cfblock(hFile, dir_type); + } +#endif + } + + } else { + + FILE *fp = fopen( longname, "rb" ); + + if ( fp ) { + if ( offset ) { + // Found it in a pack file + return cf_open_packed_cfblock(fp, dir_type, offset, size ); + } else { + // Found it in a normal file + return cf_open_fill_cfblock(fp, dir_type); + } + } + } + + } + + return NULL; +} + + +// ------------------------------------------------------------------------ +// ctmpfile() +// +// Open up a temporary file. A unique name is automatically generated. The +// file will be automatically deleted when file is closed. +// +// return: NULL => tmp file could not be opened +// pointer to CFILE => tmp file successfully opened +// +CFILE *ctmpfile() +{ + FILE *fp; + fp = tmpfile(); + if ( fp ) + return cf_open_fill_cfblock(fp, 0); + else + return NULL; +} + + + +// cfget_cfile_block() will try to find an empty Cfile_block structure in the +// Cfile_block_list[] array and return the index. +// +// returns: success ==> index in Cfile_block_list[] array +// failure ==> -1 +// +int cfget_cfile_block() +{ + int i; + Cfile_block *cb; + + for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) { + cb = &Cfile_block_list[i]; + if ( cb->type == CFILE_BLOCK_UNUSED ) { + cb->data = NULL; + cb->fp = NULL; + cb->type = CFILE_BLOCK_USED; + return i; + } + } + + // If we've reached this point, a free Cfile_block could not be found + nprintf(("Warning","A free Cfile_block could not be found.\n")); + Assert(0); // out of free cfile blocks + return -1; +} + + +// cfclose() closes the file +// +// returns: success ==> 0 +// failure ==> EOF +// +int cfclose( CFILE * cfile ) +{ + int result; + + Assert(cfile != NULL); + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + + result = 0; + if ( cb->data ) { + // close memory mapped file +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + result = UnmapViewOfFile((void*)cb->data); + Assert(result); + result = CloseHandle(cb->hInFile); + Assert(result); // Ensure file handle is closed properly + result = CloseHandle(cb->hMapFile); + Assert(result); // Ensure file handle is closed properly +#endif + result = 0; + + } else if ( cb->fp != NULL ) { + Assert(cb->fp != NULL); + result = fclose(cb->fp); + } else { + // VP do nothing + } + + cb->type = CFILE_BLOCK_UNUSED; + return result; +} + + + + +// cf_open_fill_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array +// for the case of a file being opened by cf_open(); +// +// returns: success ==> ptr to CFILE structure. +// error ==> NULL +// +CFILE *cf_open_fill_cfblock(FILE *fp, int type) +{ + int cfile_block_index; + + cfile_block_index = cfget_cfile_block(); + if ( cfile_block_index == -1 ) { + return NULL; + } else { + CFILE *cfp; + Cfile_block *cfbp; + cfbp = &Cfile_block_list[cfile_block_index]; + cfp = &Cfile_list[cfile_block_index];; + cfp->id = cfile_block_index; + cfp->version = 0; + cfbp->data = NULL; + cfbp->fp = fp; + cfbp->dir_type = type; + + cf_init_lowlevel_read_code(cfp,0,filelength(fileno(fp)) ); + + return cfp; + } +} + + +// cf_open_packed_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array +// for the case of a file being opened by cf_open(); +// +// returns: success ==> ptr to CFILE structure. +// error ==> NULL +// +CFILE *cf_open_packed_cfblock(FILE *fp, int type, int offset, int size) +{ + // Found it in a pack file + int cfile_block_index; + + cfile_block_index = cfget_cfile_block(); + if ( cfile_block_index == -1 ) { + return NULL; + } else { + CFILE *cfp; + Cfile_block *cfbp; + cfbp = &Cfile_block_list[cfile_block_index]; + + cfp = &Cfile_list[cfile_block_index]; + cfp->id = cfile_block_index; + cfp->version = 0; + cfbp->data = NULL; + cfbp->fp = fp; + cfbp->dir_type = type; + + cf_init_lowlevel_read_code(cfp,offset, size ); + + return cfp; + } + +} + + + +// cf_open_mapped_fill_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array +// for the case of a file being opened by cf_open_mapped(); +// +// returns: ptr CFILE structure. +// +CFILE *cf_open_mapped_fill_cfblock(HANDLE hFile, int type) +{ + int cfile_block_index; + + cfile_block_index = cfget_cfile_block(); + if ( cfile_block_index == -1 ) { + return NULL; + } + else { + CFILE *cfp; + Cfile_block *cfbp; + cfbp = &Cfile_block_list[cfile_block_index]; + + cfp = &Cfile_list[cfile_block_index]; + cfp->id = cfile_block_index; + cfbp->fp = NULL; + cfbp->hInFile = hFile; + cfbp->dir_type = type; + + cf_init_lowlevel_read_code(cfp,0 , 0 ); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + cfbp->hMapFile = CreateFileMapping(cfbp->hInFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (cfbp->hMapFile == NULL) { + nprintf(("Error", "Could not create file-mapping object.\n")); + return NULL; + } + + cfbp->data = (ubyte*)MapViewOfFile(cfbp->hMapFile, FILE_MAP_READ, 0, 0, 0); + Assert( cfbp->data != NULL ); +#endif + return cfp; + } +} + +int cf_get_dir_type(CFILE *cfile) +{ + return Cfile_block_list[cfile->id].dir_type; +} + +// cf_returndata() returns the data pointer for a memory-mapped file that is associated +// with the CFILE structure passed as a parameter +// +// + +void *cf_returndata(CFILE *cfile) +{ + Assert(cfile != NULL); + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + Assert(cb->data != NULL); + return cb->data; +} + + + +// version number of opened file. Will be 0 unless you put something else here after you +// open a file. Once set, you can use minimum version numbers with the read functions. +void cf_set_version( CFILE * cfile, int version ) +{ + Assert(cfile != NULL); + + cfile->version = version; +} + +// routines to read basic data types from CFILE's. Put here to +// simplify mac/pc reading from cfiles. + +float cfread_float(CFILE *file, int ver, float deflt) +{ + float f; + + if (file->version < ver) + return deflt; + + if (cfread( &f, sizeof(f), 1, file) != 1) + return deflt; + +// i = INTEL_INT(i); // hmm, not sure what to do here + return f; +} + +int cfread_int(CFILE *file, int ver, int deflt) +{ + int i; + + if (file->version < ver) + return deflt; + + if (cfread( &i, sizeof(i), 1, file) != 1) + return deflt; + + i = INTEL_INT(i); + return i; +} + +uint cfread_uint(CFILE *file, int ver, uint deflt) +{ + uint i; + + if (file->version < ver) + return deflt; + + if (cfread( &i, sizeof(i), 1, file) != 1) + return deflt; + + i = INTEL_INT(i); + return i; +} + +short cfread_short(CFILE *file, int ver, short deflt) +{ + short s; + + if (file->version < ver) + return deflt; + + if (cfread( &s, sizeof(s), 1, file) != 1) + return deflt; + + s = INTEL_SHORT(s); + return s; +} + +ushort cfread_ushort(CFILE *file, int ver, ushort deflt) +{ + ushort s; + + if (file->version < ver) + return deflt; + + if (cfread( &s, sizeof(s), 1, file) != 1) + return deflt; + + s = INTEL_SHORT(s); + return s; +} + +ubyte cfread_ubyte(CFILE *file, int ver, ubyte deflt) +{ + ubyte b; + + if (file->version < ver) + return deflt; + + if (cfread( &b, sizeof(b), 1, file) != 1) + return deflt; + + return b; +} + +void cfread_vector(vector *vec, CFILE *file, int ver, vector *deflt) +{ + if (file->version < ver) { + if (deflt) + *vec = *deflt; + else + vec->x = vec->y = vec->z = 0.0f; + + return; + } + + vec->x = cfread_float(file, ver, deflt ? deflt->x : NULL); + vec->y = cfread_float(file, ver, deflt ? deflt->y : NULL); + vec->z = cfread_float(file, ver, deflt ? deflt->z : NULL); +} + +void cfread_angles(angles *ang, CFILE *file, int ver, angles *deflt) +{ + if (file->version < ver) { + if (deflt) + *ang = *deflt; + else + ang->p = ang->b = ang->h = 0.0f; + + return; + } + + ang->p = cfread_float(file, ver, deflt ? deflt->p : NULL); + ang->b = cfread_float(file, ver, deflt ? deflt->b : NULL); + ang->h = cfread_float(file, ver, deflt ? deflt->h : NULL); +} + +char cfread_char(CFILE *file, int ver, char deflt) +{ + char b; + + if (file->version < ver) + return deflt; + + if (cfread( &b, sizeof(b), 1, file) != 1) + return deflt; + + return b; +} + +void cfread_string(char *buf, int n, CFILE *file) +{ + char c; + + do { + c = cfread_char(file); + if ( n > 0 ) { + *buf++ = c; + n--; + } + } while (c != 0 ); +} + +void cfread_string_len(char *buf,int n, CFILE *file) +{ + int len; + len = cfread_int(file); + Assert( len < n ); + if (len) + cfread(buf, len, 1, file); + + buf[len] = 0; +} + +// equivalent write functions of above read functions follow + +int cfwrite_float(float f, CFILE *file) +{ +// i = INTEL_INT(i); // hmm, not sure what to do here + return cfwrite(&f, sizeof(f), 1, file); +} + +int cfwrite_int(int i, CFILE *file) +{ + i = INTEL_INT(i); + return cfwrite(&i, sizeof(i), 1, file); +} + +int cfwrite_uint(uint i, CFILE *file) +{ + i = INTEL_INT(i); + return cfwrite(&i, sizeof(i), 1, file); +} + +int cfwrite_short(short s, CFILE *file) +{ + s = INTEL_SHORT(s); + return cfwrite(&s, sizeof(s), 1, file); +} + +int cfwrite_ushort(ushort s, CFILE *file) +{ + s = INTEL_SHORT(s); + return cfwrite(&s, sizeof(s), 1, file); +} + +int cfwrite_ubyte(ubyte b, CFILE *file) +{ + return cfwrite(&b, sizeof(b), 1, file); +} + +int cfwrite_vector(vector *vec, CFILE *file) +{ + if(!cfwrite_float(vec->x, file)){ + return 0; + } + if(!cfwrite_float(vec->y, file)){ + return 0; + } + return cfwrite_float(vec->z, file); +} + +int cfwrite_angles(angles *ang, CFILE *file) +{ + if(!cfwrite_float(ang->p, file)){ + return 0; + } + if(!cfwrite_float(ang->b, file)){ + return 0; + } + return cfwrite_float(ang->h, file); +} + +int cfwrite_char(char b, CFILE *file) +{ + return cfwrite( &b, sizeof(b), 1, file); +} + +int cfwrite_string(char *buf, CFILE *file) +{ + if ( (!buf) || (buf && !buf[0]) ) { + return cfwrite_char(0, file); + } + int len = strlen(buf); + if(!cfwrite(buf, len, 1, file)){ + return 0; + } + return cfwrite_char(0, file); // write out NULL termination +} + +int cfwrite_string_len(char *buf, CFILE *file) +{ + int len = strlen(buf); + + if(!cfwrite_int(len, file)){ + return 0; + } + if (len){ + return cfwrite(buf,len,1,file); + } + + return 1; +} + +// Get the filelength +int cfilelength( CFILE * cfile ) +{ + Assert(cfile != NULL); + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + + // TODO: return length of memory mapped file + Assert( !cb->data ); + + Assert(cb->fp != NULL); + + // cb->size gets set at cfopen + return cb->size; +} + +// cfwrite() writes to the file +// +// returns: number of full elements actually written +// +// +int cfwrite(void *buf, int elsize, int nelem, CFILE *cfile) +{ + Assert(cfile != NULL); + Assert(buf != NULL); + Assert(elsize > 0); + Assert(nelem > 0); + + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + + int result = 0; + + // cfwrite() not supported for memory-mapped files + Assert( !cb->data ); + + Assert(cb->fp != NULL); + Assert(cb->lib_offset == 0 ); + result = fwrite(buf, elsize, nelem, cb->fp); + + return result; +} + + +// cfputc() writes a character to a file +// +// returns: success ==> returns character written +// error ==> EOF +// +int cfputc(int c, CFILE *cfile) +{ + int result; + + Assert(cfile != NULL); + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + + result = 0; + // cfputc() not supported for memory-mapped files + Assert( !cb->data ); + + Assert(cb->fp != NULL); + result = fputc(c, cb->fp); + + return result; +} + + +// cfgetc() reads a character from a file +// +// returns: success ==> returns character read +// error ==> EOF +// +int cfgetc(CFILE *cfile) +{ + Assert(cfile != NULL); + + char tmp; + + int result = cfread(&tmp, 1, 1, cfile ); + if ( result == 1 ) { + result = char(tmp); + } else { + result = CF_EOF; + } + + return result; +} + + + + + +// cfgets() reads a string from a file +// +// returns: success ==> returns pointer to string +// error ==> NULL +// +char *cfgets(char *buf, int n, CFILE *cfile) +{ + Assert(cfile != NULL); + Assert(buf != NULL); + Assert(n > 0 ); + + char * t = buf; + int i, c; + + for (i=0; i t ) { + return t; + } else { + return NULL; + } + } + c = int(tmp_c); + } while ( c == 13 ); + *buf++ = char(c); + if ( c=='\n' ) break; + } + *buf++ = 0; + + return t; +} + +// cfputs() writes a string to a file +// +// returns: success ==> non-negative value +// error ==> EOF +// +int cfputs(char *str, CFILE *cfile) +{ + Assert(cfile != NULL); + Assert(str != NULL); + + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + + int result; + + result = 0; + // cfputs() not supported for memory-mapped files + Assert( !cb->data ); + Assert(cb->fp != NULL); + result = fputs(str, cb->fp); + + return result; +} + + +// 16 and 32 bit checksum stuff ---------------------------------------------------------- + +// CRC code for mission validation. given to us by Kevin Bentley on 7/20/98. Some sort of +// checksumming code that he wrote a while ago. +#define CRC32_POLYNOMIAL 0xEDB88320L +unsigned long CRCTable[256]; + +#define CF_CHKSUM_SAMPLE_SIZE 512 + +// update cur_chksum with the chksum of the new_data of size new_data_size +ushort cf_add_chksum_short(ushort seed, char *buffer, int size) +{ + ubyte * ptr = (ubyte *)buffer; + unsigned int sum1,sum2; + + sum1 = sum2 = (int)(seed); + + while(size--) { + sum1 += *ptr++; + if (sum1 >= 255 ) sum1 -= 255; + sum2 += sum1; + } + sum2 %= 255; + + return (unsigned short)((sum1<<8)+ sum2); +} + +// update cur_chksum with the chksum of the new_data of size new_data_size +unsigned long cf_add_chksum_long(unsigned long seed, char *buffer, int size) +{ + unsigned long crc; + unsigned char *p; + unsigned long temp1; + unsigned long temp2; + + p = (unsigned char*)buffer; + crc = seed; + + while (size--!=0){ + temp1 = (crc>>8)&0x00FFFFFFL; + temp2 = CRCTable[((int)crc^*p++)&0xff]; + crc = temp1^temp2; + } + + return crc; +} + +void cf_chksum_long_init() +{ + int i,j; + unsigned long crc; + + for( i=0;i<=255;i++) { + crc=i; + for(j=8;j>0;j--) { + if(crc&1) + crc=(crc>>1)^CRC32_POLYNOMIAL; + else + crc>>=1; + } + CRCTable[i]=crc; + } +} + +// single function convenient to use for both short and long checksums +// NOTE : only one of chk_short or chk_long must be non-NULL (indicating which checksum to perform) +int cf_chksum_do(CFILE *cfile, ushort *chk_short, uint *chk_long, int max_size) +{ + char cf_buffer[CF_CHKSUM_SAMPLE_SIZE]; + int is_long; + int cf_len = 0; + int cf_total; + int read_size; + + // determine whether we're doing a short or long checksum + is_long = 0; + if(chk_short){ + Assert(!chk_long); + *chk_short = 0; + } else { + Assert(chk_long); + is_long = 1; + *chk_long = 0; + } + + // if max_size is -1, set it to be the size of the file + if(max_size < 0){ + cfseek(cfile, 0, SEEK_SET); + max_size = cfilelength(cfile); + } + + cf_total = 0; + do { + // determine how much we want to read + if((max_size - cf_total) >= CF_CHKSUM_SAMPLE_SIZE){ + read_size = CF_CHKSUM_SAMPLE_SIZE; + } else { + read_size = max_size - cf_total; + } + + // read in some buffer + cf_len = cfread(cf_buffer, 1, read_size, cfile); + + // total we've read so far + cf_total += cf_len; + + // add the checksum + if(cf_len > 0){ + // do the proper short or long checksum + if(is_long){ + *chk_long = cf_add_chksum_long(*chk_long, cf_buffer, cf_len); + } else { + *chk_short = cf_add_chksum_short(*chk_short, cf_buffer, cf_len); + } + } + } while((cf_len > 0) && (cf_total < max_size)); + + return 1; +} + +// get the 2 byte checksum of the passed filename - return 0 if operation failed, 1 if succeeded +int cf_chksum_short(char *filename, ushort *chksum, int max_size, int cf_type) +{ + int ret_val; + CFILE *cfile = NULL; + + // zero the checksum + *chksum = 0; + + // attempt to open the file + cfile = cfopen(filename,"rt",CFILE_NORMAL,cf_type); + if(cfile == NULL){ + return 0; + } + + // call the overloaded cf_chksum function() + ret_val = cf_chksum_do(cfile, chksum, NULL, max_size); + + // close the file down + cfclose(cfile); + cfile = NULL; + + // return the result + return ret_val; +} + +// get the 2 byte checksum of the passed file - return 0 if operation failed, 1 if succeeded +// NOTE : preserves current file position +int cf_chksum_short(CFILE *file, ushort *chksum, int max_size) +{ + int ret_code; + int start_pos; + + // Returns current position of file. + start_pos = cftell(file); + if(start_pos == -1){ + return 0; + } + + // move to the beginning of the file + if(cfseek(file, 0, CF_SEEK_SET)){ + return 0; + } + ret_code = cf_chksum_do(file, chksum, NULL, max_size); + // move back to the start position + cfseek(file, start_pos, CF_SEEK_SET); + + return ret_code; +} + +// get the 32 bit CRC checksum of the passed filename - return 0 if operation failed, 1 if succeeded +int cf_chksum_long(char *filename, uint *chksum, int max_size, int cf_type) +{ + int ret_val; + CFILE *cfile = NULL; + + // zero the checksum + *chksum = 0; + + // attempt to open the file + cfile = cfopen(filename,"rt",CFILE_NORMAL,cf_type); + if(cfile == NULL){ + return 0; + } + + // call the overloaded cf_chksum function() + ret_val = cf_chksum_do(cfile, NULL, chksum, max_size); + + // close the file down + cfclose(cfile); + cfile = NULL; + + // return the result + return ret_val; +} + +// get the 32 bit CRC checksum of the passed file - return 0 if operation failed, 1 if succeeded +// NOTE : preserves current file position +int cf_chksum_long(CFILE *file, uint *chksum, int max_size) +{ + int ret_code; + int start_pos; + + // Returns current position of file. + start_pos = cftell(file); + if(start_pos == -1){ + return 0; + } + + // move to the beginning of the file + if(cfseek(file, 0, CF_SEEK_SET)){ + return 0; + } + ret_code = cf_chksum_do(file, NULL, chksum, max_size); + // move back to the start position + cfseek(file, start_pos, CF_SEEK_SET); + + return ret_code; +} + + +// Flush the open file buffer +// +// exit: 0 - success +// 1 - failure +int cflush(CFILE *cfile) +{ + Assert(cfile != NULL); + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + + // not supported for memory mapped files + Assert( !cb->data ); + + Assert(cb->fp != NULL); + return fflush(cb->fp); +} + + + + + + + diff --git a/src/cfile/cfilearchive.cpp b/src/cfile/cfilearchive.cpp new file mode 100644 index 0000000..f3e8eeb --- /dev/null +++ b/src/cfile/cfilearchive.cpp @@ -0,0 +1,274 @@ +/* + * $Logfile: /Freespace2/code/CFile/CfileArchive.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Low-level code for reading data out of large archive files or normal files. All + * reads/seeks come through here. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 4 2/22/99 10:31p Andsager + * Get rid of unneeded includes. + * + * 3 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 12 5/14/98 8:10p Lawrance + * Fix bug with trying to read past the end of a file (when from a + * packfile). + * + * 11 5/11/98 11:48a John + * fixed bug with memory mapped files + * + * 10 5/11/98 10:59a John + * added in debug code + * + * 9 5/11/98 10:59a John + * Moved the low-level file reading code into cfilearchive.cpp. + * + * 8 4/30/98 4:53p John + * Restructured and cleaned up cfile code. Added capability to read off + * of CD-ROM drive and out of multiple pack files. + * + * 7 4/20/98 11:49a Hoffoss + * Fixed bug with directory name getting. + * + * 6 4/10/98 12:18a Hoffoss + * Make pilot image search in pack file possible. + * + * 5 1/19/98 9:37p Allender + * Great Compiler Warning Purge of Jan, 1998. Used pragma's in a couple + * of places since I was unsure of what to do with code. + * + * 4 12/30/97 5:31p Lawrance + * fixed problem for people that didn't have a VP file + * + * 3 12/30/97 4:31p Sandeep + * Changed pakfile format + * + * 2 12/28/97 12:42p John + * Put in support for reading archive files; Made missionload use the + * cf_get_file_list function. Moved demos directory out of data tree. + * + * 1 12/28/97 11:48a John + * + * $NoKeywords: $ + */ + +#define _CFILE_INTERNAL + +#include +#include +#include +#ifndef PLAT_UNIX +#include +#include +#include +#include /* needed for memory mapping of file functions */ +#endif + +#include "pstypes.h" +#include "cfile.h" +//#include "outwnd.h" +//#include "vecmat.h" +//#include "timer.h" +#include "cfilearchive.h" + +#define CHECK_POSITION + +// Called once to setup the low-level reading code. + +void cf_init_lowlevel_read_code( CFILE * cfile, int offset, int size ) +{ + Assert(cfile != NULL); + + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + + cb->lib_offset = offset; + cb->raw_position = 0; + cb->size = size; + + if ( cb->fp ) { + if ( cb->lib_offset ) { + fseek( cb->fp, cb->lib_offset, SEEK_SET ); + } + + #if defined(CHECK_POSITION) && !defined(NDEBUG) + int raw_position = ftell(cb->fp) - cb->lib_offset; + Assert(raw_position == cb->raw_position); + #endif + } +} + + + +// cfeof() Tests for end-of-file on a stream +// +// returns a nonzero value after the first read operation that attempts to read +// past the end of the file. It returns 0 if the current position is not end of file. +// There is no error return. + +int cfeof(CFILE *cfile) +{ + Assert(cfile != NULL); + + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + + int result; + + result = 0; + + // cfeof() not supported for memory-mapped files + Assert( !cb->data ); + + Assert(cb->fp != NULL); + + #if defined(CHECK_POSITION) && !defined(NDEBUG) + int raw_position = ftell(cb->fp) - cb->lib_offset; + Assert(raw_position == cb->raw_position); + #endif + + if (cb->raw_position >= cb->size ) { + result = 1; + } else { + result = 0; + } + + return result; +} + +// cftell() returns offset into file +// +// returns: success ==> offset into the file +// error ==> -1 +// +int cftell( CFILE * cfile ) +{ + Assert(cfile != NULL); + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + + // Doesn't work for memory mapped files + Assert( !cb->data ); + + Assert(cb->fp != NULL); + + #if defined(CHECK_POSITION) && !defined(NDEBUG) + int raw_position = ftell(cb->fp) - cb->lib_offset; + Assert(raw_position == cb->raw_position); + #endif + + return cb->raw_position; +} + + +// cfseek() moves the file pointer +// +// returns: success ==> 0 +// error ==> non-zero +// +int cfseek( CFILE *cfile, int offset, int where ) +{ + + Assert(cfile != NULL); + Cfile_block *cb; + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + cb = &Cfile_block_list[cfile->id]; + + + // TODO: seek to offset in memory mapped file + Assert( !cb->data ); + Assert( cb->fp != NULL ); + + int goal_position; + + switch( where ) { + case CF_SEEK_SET: + goal_position = offset+cb->lib_offset; + break; + case CF_SEEK_CUR: + { + goal_position = cb->raw_position+offset+cb->lib_offset; + } + break; + case CF_SEEK_END: + goal_position = cb->size+offset+cb->lib_offset; + break; + default: + Int3(); + return 1; + } + + int result = fseek(cb->fp, goal_position, SEEK_SET ); + cb->raw_position = goal_position - cb->lib_offset; + + #if defined(CHECK_POSITION) && !defined(NDEBUG) + int tmp_offset = ftell(cb->fp) - cb->lib_offset; + Assert(tmp_offset==cb->raw_position); + #endif + + return result; +} + + +// cfread() reads from a file +// +// returns: returns the number of full elements read +// +// +int cfread(void *buf, int elsize, int nelem, CFILE *cfile) +{ + Assert(cfile != NULL); + Assert(buf != NULL); + Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS); + + Cfile_block *cb; + cb = &Cfile_block_list[cfile->id]; + + // cfread() not supported for memory-mapped files + Assert( !cb->data ); + Assert(cb->fp != NULL); + + int size = elsize*nelem; + + Assert(nelem > 0); + Assert(elsize > 0); + Assert(size > 0); + + if ( (cb->raw_position+size) > cb->size ) { + size = cb->size - cb->raw_position; + if ( size < 1 ) { + return 0; + } + //mprintf(( "CFILE: EOF encountered in file\n" )); + } + + int bytes_read = fread( buf, 1, size, cb->fp ); + if ( bytes_read > 0 ) { + cb->raw_position += bytes_read; + } + + #if defined(CHECK_POSITION) && !defined(NDEBUG) + int tmp_offset = ftell(cb->fp) - cb->lib_offset; + Assert(tmp_offset==cb->raw_position); + #endif + + return bytes_read / elsize; + +} + diff --git a/src/cfile/cfilelist.cpp b/src/cfile/cfilelist.cpp new file mode 100644 index 0000000..6b2491e --- /dev/null +++ b/src/cfile/cfilelist.cpp @@ -0,0 +1,480 @@ +/* + * $Logfile: /Freespace2/code/CFile/CfileList.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for doing directory lists and sorts + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 3 2/22/99 10:31p Andsager + * Get rid of unneeded includes. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 6 5/13/98 10:22p John + * Added cfile functions to read/write rle compressed blocks of data. + * Made palman use it for .clr files. Made alphacolors calculate on the + * fly rather than caching to/from disk. + * + * 5 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 4 4/30/98 4:53p John + * Restructured and cleaned up cfile code. Added capability to read off + * of CD-ROM drive and out of multiple pack files. + * + * 3 4/10/98 12:18a Hoffoss + * Make pilot image search in pack file possible. + * + * 2 3/30/98 10:36p Allender + * be sure to call _findclose() when done reading a file list + * + * 1 12/28/97 11:48a John + * + * $NoKeywords: $ + */ + +#include +#include +#include +#ifndef PLAT_UNIX +#include +#include +#include +#include /* needed for memory mapping of file functions */ +#endif + +#include "pstypes.h" +#include "cfile.h" +//#include "outwnd.h" +//#include "vecmat.h" +//#include "timer.h" + +// Sorts a list of filenames using the specified sorting method (CF_SORT_*). +// n = number of filenames in list to sort +// list = list of filenames to be sorted +// sort = sorting method to use (one of the CF_SORT_* defines) +// info = extra info for each file. Only required if sorting by time, however if you +// have extra file info, you should pass it as well to get it sorted too (so an +// index into list is the same index for info for that file +void cf_sort_filenames( int n, char **list, int sort, file_list_info *info ) +{ + int i, j, incr; + char *t; + file_list_info tt; + + if (sort == CF_SORT_NAME) { + incr = n / 2; + while (incr > 0) { + for (i=incr; i= 0) { + if (stricmp(list[j], list[j + incr]) > 0) { + t = list[j]; + list[j] = list[j + incr]; + list[j + incr] = t; + + if (info) { + tt = info[j]; + info[j] = info[j + incr]; + info[j + incr] = tt; + } + + j -= incr; + + } else + break; + } + } + + incr /= 2; + } + + return; + + } else if (sort == CF_SORT_TIME) { + Assert(info); + incr = n / 2; + while (incr > 0) { + for (i=incr; i= 0) { + if (info[j].write_time < info[j + incr].write_time) { + t = list[j]; + list[j] = list[j + incr]; + list[j + incr] = t; + + tt = info[j]; + info[j] = info[j + incr]; + info[j + incr] = tt; + j -= incr; + + } else + break; + } + } + + incr /= 2; + } + + return; + } + + nprintf(("Error", "Unknown sorting method %d passed to cf_sort_filenames()\n", sort)); +} + + +// cf_compress - Do Run Length Compression on a block of data. Targa format. +// +// Usage: +// out Buffer to write it out to +// in Buffer to compress +// bytecount Number of bytes input +int cf_compress(char *out, char *in, int bytecount ) +{ + int pixcount; // number of pixels in the current packet + char *inputpixel=NULL; // current input pixel position + char *matchpixel=NULL; // pixel value to match for a run + char *flagbyte=NULL; // location of last flag byte to set + int rlcount; // current count in r.l. string + int rlthresh; // minimum valid run length + char *copyloc; // location to begin copying at + + // set the threshold -- the minimum valid run length + rlthresh = 2; // Require a 2 pixel span before rle'ing + + // set the first pixel up + + flagbyte = out; // place to put next flag if run + inputpixel = in; + pixcount = 1; + rlcount = 0; + copyloc = (char *)0; + + // loop till data processing complete + do { + + // if we have accumulated a 128-byte packet, process it + if ( pixcount == 129 ) { + *flagbyte = 127; + + // set the run flag if this is a run + + if ( rlcount >= rlthresh ) { + *flagbyte |= 0x80; + pixcount = 2; + } + + // copy the data into place + ++flagbyte; + memmove( flagbyte, copyloc, pixcount-1 ); + flagbyte += pixcount-1; + pixcount = 1; + + // set up for next packet + continue; + } + + // if zeroth byte, handle as special case + if ( pixcount == 1 ) { + rlcount = 0; + copyloc = inputpixel; /* point to 1st guy in packet */ + matchpixel = inputpixel; /* set pointer to pix to match */ + pixcount = 2; + inputpixel += 1; + continue; + } + + // assembling a packet -- look at next pixel + + // current pixel == match pixel? + if ( *inputpixel == *matchpixel ) { + + // establishing a run of enough length to + // save space by doing it + // -- write the non-run length packet + // -- start run-length packet + + if ( ++rlcount == rlthresh ) { + + // close a non-run packet + + if ( pixcount > (rlcount+1) ) { + // write out length and do not set run flag + + *flagbyte++ = (char)(pixcount - 2 - rlthresh); + + memmove(flagbyte, copyloc, (pixcount-1-rlcount) ); + flagbyte += (pixcount-1-rlcount); + + copyloc = inputpixel; + pixcount = rlcount + 1; + } + } + } else { + + // no match -- either break a run or continue without one + // if a run exists break it: + // write the bytes in the string (1+1) + // start the next string + + if ( rlcount >= rlthresh ) { + + *flagbyte++ = (char)(0x80 | rlcount); + memmove(flagbyte, copyloc, 1 ); + flagbyte += 1; + pixcount = 1; + continue; + } else { + + // not a match and currently not a run + // - save the current pixel + // - reset the run-length flag + rlcount = 0; + matchpixel = inputpixel; + } + } + pixcount++; + inputpixel += 1; + } while ( inputpixel < (in + bytecount)); + + // quit this buffer without loosing any data + if ( --pixcount >= 1 ) { + *flagbyte = (char)(pixcount - 1); + if ( rlcount >= rlthresh ) { + *flagbyte |= 0x80; + pixcount = 1; + } + + // copy the data into place + ++flagbyte; + memmove(flagbyte, copyloc, pixcount ); + flagbyte += pixcount; + } + return(flagbyte-out); +} + + +// cf_decompress - Do Decompression on a run-length encoded block of data. Targa format. +// +// Usage: +// out Buffer to write it out to +// in Buffer to compress +// bytecount Number of bytes input +int cf_decompress(char *out, char *in ) +{ + int count; + + char *param_out = out; + + while(1) { + + count = int(*in++); + int run_span = count & 0x80; + + count &= (~0x80); + + if ( count > 0 ) { + if ( run_span ) { + // RLE'd data + ubyte c = *in++; + + memset( out, c, count ); + out += count; + } else { + memmove( out, in, count ); + in += count; + out += count; + } + } + } + + return out - param_out; + +} + + +// cfread() reads from a file and decompresses it +// +// returns: returns the number of full elements read +// +// +int cfread_compressed(void *buf, int elsize, int nelem, CFILE *cfile) +{ + char *out = (char *)buf; + + while(1) { + + byte count; + + if ( cfread( &count, 1, 1, cfile ) != 1 ) { + break; + } + + int run_span = count & 0x80; + count &= (~0x80); + count++; + + if ( count > 0 ) { + if ( run_span ) { + // RLE'd data + byte c; + if ( cfread( &c, 1, 1, cfile ) != 1 ) { + break; + } + memset( out, c, count ); + } else { + if ( cfread( out, 1, count, cfile ) != count ) { + break; + } + } + out += count; + if ( out >= (char *)buf + (elsize*nelem)) { + break; + } + } else { + break; + } + } + + return (out - (char *)buf)/elsize; +} + +int cfwrite_compressed(void *param_buf, int param_elsize, int param_nelem, CFILE *cfile) +{ + char *in = (char *)param_buf; + int bytecount = (param_elsize * param_nelem ); + + int pixcount; // number of pixels in the current packet + char *inputpixel=NULL; // current input pixel position + char *matchpixel=NULL; // pixel value to match for a run + int rlcount; // current count in r.l. string + int rlthresh; // minimum valid run length + char *copyloc; // location to begin copying at + + // set the threshold -- the minimum valid run length + rlthresh = 2; // Require a 2 pixel span before rle'ing + + // set the first pixel up + + inputpixel = in; + pixcount = 1; + rlcount = 0; + copyloc = (char *)0; + + // loop till data processing complete + do { + + // if we have accumulated a 128-byte packet, process it + if ( pixcount == 129 ) { + ubyte code = 127; + + // set the run flag if this is a run + + if ( rlcount >= rlthresh ) { + code |= 0x80; + pixcount = 2; + } + + cfwrite( &code, 1, 1, cfile ); + + // copy the data into place + cfwrite( copyloc, 1, pixcount-1, cfile ); + pixcount = 1; + + // set up for next packet + continue; + } + + // if zeroth byte, handle as special case + if ( pixcount == 1 ) { + rlcount = 0; + copyloc = inputpixel; /* point to 1st guy in packet */ + matchpixel = inputpixel; /* set pointer to pix to match */ + pixcount = 2; + inputpixel += 1; + continue; + } + + // assembling a packet -- look at next pixel + + // current pixel == match pixel? + if ( *inputpixel == *matchpixel ) { + + // establishing a run of enough length to + // save space by doing it + // -- write the non-run length packet + // -- start run-length packet + + if ( ++rlcount == rlthresh ) { + + // close a non-run packet + + if ( pixcount > (rlcount+1) ) { + // write out length and do not set run flag + + ubyte code = (ubyte)(pixcount - 2 - rlthresh); + cfwrite( &code, 1, 1, cfile ); + + cfwrite( copyloc, 1, (pixcount-1-rlcount), cfile ); + copyloc = inputpixel; + pixcount = rlcount + 1; + } + } + } else { + + // no match -- either break a run or continue without one + // if a run exists break it: + // write the bytes in the string (1+1) + // start the next string + + if ( rlcount >= rlthresh ) { + + ubyte code = (ubyte)(0x80 | rlcount); + cfwrite( &code, 1, 1, cfile ); + cfwrite( copyloc, 1, 1, cfile ); + pixcount = 1; + continue; + } else { + + // not a match and currently not a run + // - save the current pixel + // - reset the run-length flag + rlcount = 0; + matchpixel = inputpixel; + } + } + pixcount++; + inputpixel += 1; + } while ( inputpixel < (in + bytecount)); + + // quit this buffer without loosing any data + if ( --pixcount >= 1 ) { + ubyte code = ubyte(pixcount - 1); + + // set the run flag if this is a run + + if ( rlcount >= rlthresh ) { + code |= 0x80; + pixcount = 1; + } + + cfwrite( &code, 1, 1, cfile ); + + // copy the data into place + cfwrite( copyloc, 1, pixcount, cfile ); + } + + return param_nelem; +} diff --git a/src/cfile/cfilesystem.cpp b/src/cfile/cfilesystem.cpp new file mode 100644 index 0000000..e3cef49 --- /dev/null +++ b/src/cfile/cfilesystem.cpp @@ -0,0 +1,1141 @@ +/* + * $Logfile: /Freespace2/code/CFile/CfileSystem.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Functions to keep track of and find files that can exist + * on the harddrive, cd-rom, or in a pack file on either of those. + * This keeps a list of all the files in packfiles or on CD-rom + * and when you need a file you call one function which then searches + * all those locations, inherently enforcing precedence orders. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 6 9/08/99 10:01p Dave + * Make sure game won't run in a drive's root directory. Make sure + * standalone routes suqad war messages properly to the host. + * + * 5 9/03/99 1:31a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 4 2/22/99 10:31p Andsager + * Get rid of unneeded includes. + * + * 3 10/13/98 9:19a Andsager + * Add localization support to cfile. Optional parameter with cfopen that + * looks for localized files. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 14 8/31/98 2:06p Dave + * Make cfile sort the ordering or vp files. Added support/checks for + * recognizing "mission disk" players. + * + * 13 6/23/98 4:18p Hoffoss + * Fixed some bugs with AC release build. + * + * 12 5/20/98 10:46p John + * Added code that doesn't include duplicate filenames in any file list + * functions. + * + * 11 5/14/98 2:14p Lawrance2 + * Use filespec filtering for packfiles + * + * 10 5/03/98 11:53a John + * Fixed filename case mangling. + * + * 9 5/02/98 11:06p Allender + * correctly deal with pack pathnames + * + * 8 5/01/98 11:41a Allender + * Fixed bug with mission saving in Fred. + * + * 7 5/01/98 10:21a John + * Added code to find all pack files in all trees. Added code to create + * any directories that we write to. + * + * 6 4/30/98 10:21p John + * Added code to cleanup cfilesystem + * + * 5 4/30/98 10:18p John + * added source safe header + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include +#ifndef PLAT_UNIX +#include +#include +#include +#include /* needed for memory mapping of file functions */ +#endif + +#include "pstypes.h" +//#include "outwnd.h" +//#include "vecmat.h" +//#include "timer.h" +#include "cfile.h" +#include "cfilesystem.h" +#include "localize.h" + + +#define CF_ROOTTYPE_PATH 0 +#define CF_ROOTTYPE_PACK 1 + +// Created by: +// specifying hard drive tree +// searching for pack files on hard drive // Found by searching all known paths +// specifying cd-rom tree +// searching for pack files on CD-rom tree +typedef struct cf_root { + char path[CF_MAX_PATHNAME_LENGTH]; // Contains something like c:\projects\freespace or c:\projects\freespace\freespace.vp + int roottype; // CF_ROOTTYPE_PATH = Path, CF_ROOTTYPE_PACK =Pack file +} cf_root; + +// convenient type for sorting (see cf_build_pack_list()) +typedef struct cf_root_sort { + char path[CF_MAX_PATHNAME_LENGTH]; + int roottype; + int cf_type; +} cf_root_sort; + +#define CF_NUM_ROOTS_PER_BLOCK 32 +#define CF_MAX_ROOT_BLOCKS 256 // Can store 32*256 = 8192 Roots +#define CF_MAX_ROOTS (CF_NUM_ROOTS_PER_BLOCK * CF_MAX_ROOT_BLOCKS) + +typedef struct cf_root_block { + cf_root roots[CF_NUM_ROOTS_PER_BLOCK]; +} cf_root_block; + +static int Num_roots = 0; +static cf_root_block *Root_blocks[CF_MAX_ROOT_BLOCKS]; + + +// Created by searching all roots in order. This means Files is then sorted by precedence. +typedef struct cf_file { + char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension + int root_index; // Where in Roots this is located + int pathtype_index; // Where in Paths this is located + time_t write_time; // When it was last written + int size; // How big it is in bytes + int pack_offset; // For pack files, where it is at. 0 if not in a pack file. This can be used to tell if in a pack file. +} cf_file; + +#define CF_NUM_FILES_PER_BLOCK 256 +#define CF_MAX_FILE_BLOCKS 128 // Can store 256*128 = 32768 files + +typedef struct cf_file_block { + cf_file files[CF_NUM_FILES_PER_BLOCK]; +} cf_file_block; + +static int Num_files = 0; +static cf_file_block *File_blocks[CF_MAX_FILE_BLOCKS]; + + +// Return a pointer to to file 'index'. +cf_file *cf_get_file(int index) +{ + int block = index / CF_NUM_FILES_PER_BLOCK; + int offset = index % CF_NUM_FILES_PER_BLOCK; + + return &File_blocks[block]->files[offset]; +} + +// Create a new file and return a pointer to it. +cf_file *cf_create_file() +{ + int block = Num_files / CF_NUM_FILES_PER_BLOCK; + int offset = Num_files % CF_NUM_FILES_PER_BLOCK; + + if ( File_blocks[block] == NULL ) { + File_blocks[block] = (cf_file_block *)malloc( sizeof(cf_file_block) ); + Assert( File_blocks[block] != NULL); + } + + Num_files++; + + return &File_blocks[block]->files[offset]; +} + +extern int cfile_inited; + +// Create a new root and return a pointer to it. The structure is assumed unitialized. +cf_root *cf_get_root(int n) +{ + int block = n / CF_NUM_ROOTS_PER_BLOCK; + int offset = n % CF_NUM_ROOTS_PER_BLOCK; + + if (!cfile_inited) + return NULL; + + return &Root_blocks[block]->roots[offset]; +} + + +// Create a new root and return a pointer to it. The structure is assumed unitialized. +cf_root *cf_create_root() +{ + int block = Num_roots / CF_NUM_ROOTS_PER_BLOCK; + int offset = Num_roots % CF_NUM_ROOTS_PER_BLOCK; + + if ( Root_blocks[block] == NULL ) { + Root_blocks[block] = (cf_root_block *)malloc( sizeof(cf_root_block) ); + Assert(Root_blocks[block] != NULL); + } + + Num_roots++; + + return &Root_blocks[block]->roots[offset]; +} + +// return the # of packfiles which exist +int cf_get_packfile_count(cf_root *root) +{ + char filespec[MAX_PATH_LEN]; + int i; + int packfile_count; + + // count up how many packfiles we're gonna have + packfile_count = 0; + for (i=CF_TYPE_ROOT; ipath ); + + if(strlen(Pathtypes[i].path)){ + strcat( filespec, Pathtypes[i].path ); + strcat( filespec, "\\" ); + } + + strcat( filespec, "*.vp" ); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + int find_handle; + _finddata_t find; + + find_handle = _findfirst( filespec, &find ); + + if (find_handle != -1) { + do { + if (!(find.attrib & _A_SUBDIR)) { + packfile_count++; + } + + } while (!_findnext(find_handle, &find)); + + _findclose( find_handle ); + } +#endif + } + + return packfile_count; +} + +// packfile sort function +int cf_packfile_sort_func(const void *elem1, const void *elem2) +{ + cf_root_sort *r1, *r2; + r1 = (cf_root_sort*)elem1; + r2 = (cf_root_sort*)elem2; + + // if the 2 directory types are the same, do a string compare + if(r1->cf_type == r2->cf_type){ + return stricmp(r1->path, r2->path); + } + + // otherwise return them in order of CF_TYPE_* precedence + return (r1->cf_type < r2->cf_type) ? -1 : 1; +} + +// Go through a root and look for pack files +void cf_build_pack_list( cf_root *root ) +{ + char filespec[MAX_PATH_LEN]; + int i; + cf_root_sort *temp_roots_sort, *rptr_sort; + int temp_root_count, root_index; + + // determine how many packfiles there are + temp_root_count = cf_get_packfile_count(root); + if(temp_root_count <= 0){ + return; + } + + // allocate a temporary array of temporary roots so we can easily sort them + temp_roots_sort = (cf_root_sort*)malloc(sizeof(cf_root_sort) * temp_root_count); + if(temp_roots_sort == NULL){ + Int3(); + return; + } + + // now just setup all the root info + root_index = 0; + for (i=CF_TYPE_ROOT; ipath ); + + if(strlen(Pathtypes[i].path)){ + strcat( filespec, Pathtypes[i].path ); + strcat( filespec, "\\" ); + } + strcat( filespec, "*.vp" ); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + int find_handle; + _finddata_t find; + + find_handle = _findfirst( filespec, &find ); + + if (find_handle != -1) { + do { + // add the new item + if (!(find.attrib & _A_SUBDIR)) { + Assert(root_index < temp_root_count); + + // get a temp pointer + rptr_sort = &temp_roots_sort[root_index++]; + + // fill in all the proper info + strcpy(rptr_sort->path, root->path); + + if(strlen(Pathtypes[i].path)){ + strcat(rptr_sort->path, Pathtypes[i].path ); + strcat(rptr_sort->path, "\\"); + } + + strcat(rptr_sort->path, find.name ); + rptr_sort->roottype = CF_ROOTTYPE_PACK; + rptr_sort->cf_type = i; + } + + } while (!_findnext(find_handle, &find)); + + _findclose( find_handle ); + } +#endif + } + + // these should always be the same + Assert(root_index == temp_root_count); + + // sort tht roots + qsort(temp_roots_sort, temp_root_count, sizeof(cf_root_sort), cf_packfile_sort_func); + + // now insert them all into the real root list properly + cf_root *new_root; + for(i=0; ipath, root->path ); + + // mwa -- 4/2/98 put in the next 2 lines because the path name needs to be there + // to find the files. + strcpy(new_root->path, temp_roots_sort[i].path); + new_root->roottype = CF_ROOTTYPE_PACK; + } + + // free up the temp list + free(temp_roots_sort); +} + + +void cf_build_root_list(char *cdrom_dir) +{ + Num_roots = 0; + + cf_root *root; + + //====================================================== + // First, check the current directory. + // strcpy( root->path, "d:\\projects\\freespace\\" ); + + root = cf_create_root(); + + if ( !_getcwd(root->path, CF_MAX_PATHNAME_LENGTH ) ) { + Error(LOCATION, "Can't get current working directory -- %d", errno ); + } + + // do we already have a slash? as in the case of a root directory install + if(strlen(root->path) && (root->path[strlen(root->path)-1] != '\\')){ + strcat(root->path, "\\"); // put trailing backslash on for easier path construction + } + root->roottype = CF_ROOTTYPE_PATH; + + //====================================================== + // Next, check any VP files under the current directory. + cf_build_pack_list(root); + + + //====================================================== + // Check the real CD if one... + if ( cdrom_dir && strlen(cdrom_dir) ) { + root = cf_create_root(); + strcpy( root->path, cdrom_dir ); + root->roottype = CF_ROOTTYPE_PATH; + + //====================================================== + // Next, check any VP files in the CD-ROM directory. + cf_build_pack_list(root); + + } + +} + +// Given a lower case list of file extensions +// separated by spaces, return zero if ext is +// not in the list. +int is_ext_in_list( char *ext_list, char *ext ) +{ + char tmp_ext[128]; + + strncpy( tmp_ext, ext, 127 ); + strlwr(tmp_ext); + if ( strstr(ext_list, tmp_ext )) { + return 1; + } + + return 0; +} + +void cf_search_root_path(int root_index) +{ + int i; + + cf_root *root = cf_get_root(root_index); + + mprintf(( "Searching root '%s'\n", root->path )); + + char search_path[CF_MAX_PATHNAME_LENGTH]; + + for (i=CF_TYPE_ROOT; ipath ); + + if(strlen(Pathtypes[i].path)){ + strcat( search_path, Pathtypes[i].path ); + strcat( search_path, "\\" ); + } + + strcat( search_path, "*.*" ); + +#ifdef STUB_FUNCTION + STUB_FUNCTION; +#else + int find_handle; + _finddata_t find; + + find_handle = _findfirst( search_path, &find ); + + if (find_handle != -1) { + do { + if (!(find.attrib & _A_SUBDIR)) { + + char *ext = strchr( find.name, '.' ); + if ( ext ) { + if ( is_ext_in_list( Pathtypes[i].extensions, ext ) ) { + // Found a file!!!! + cf_file *file = cf_create_file(); + + strcpy( file->name_ext, find.name ); + file->root_index = root_index; + file->pathtype_index = i; + file->write_time = find.time_write; + file->size = find.size; + file->pack_offset = 0; // Mark as a non-packed file + + //mprintf(( "Found file '%s'\n", file->name_ext )); + + } + } + + } + + } while (!_findnext(find_handle, &find)); + + _findclose( find_handle ); + } +#endif + + } +} + + +typedef struct VP_FILE_HEADER { + char id[4]; + int version; + int index_offset; + int num_files; +} VP_FILE_HEADER; + +typedef struct VP_FILE { + int offset; + int size; + char filename[32]; + time_t write_time; +} VP_FILE; + +void cf_search_root_pack(int root_index) +{ + int i; + + cf_root *root = cf_get_root(root_index); + + //mprintf(( "Searching root pack '%s'\n", root->path )); + + // Open data + + FILE *fp = fopen( root->path, "rb" ); + // Read the file header + if (!fp) { + return; + } + + VP_FILE_HEADER VP_header; + + Assert( sizeof(VP_header) == 16 ); + fread(&VP_header, 1, sizeof(VP_header), fp); + + // Read index info + fseek(fp, VP_header.index_offset, SEEK_SET); + + char search_path[CF_MAX_PATHNAME_LENGTH]; + + strcpy( search_path, "" ); + + // Go through all the files + for (i=0; i search_path) && (*p != '\\') ) { + p--; + } + *p = 0; + } else { + if ( strlen(search_path) ) { + strcat( search_path, "\\" ); + } + strcat( search_path, find.filename ); + } + + //mprintf(( "Current dir = '%s'\n", search_path )); + } else { + + int j; + for (j=CF_TYPE_ROOT; jname_ext, find.filename ); + file->root_index = root_index; + file->pathtype_index = j; + file->write_time = find.write_time; + file->size = find.size; + file->pack_offset = find.offset; // Mark as a non-packed file + + //mprintf(( "Found pack file '%s'\n", file->name_ext )); + } + } + + + } + } + + } + } + fclose(fp); +} + + +void cf_build_file_list() +{ + int i; + + Num_files = 0; + + // For each root, find all files... + for (i=1; iroottype == CF_ROOTTYPE_PATH ) { + cf_search_root_path(i); + } else if ( root->roottype == CF_ROOTTYPE_PACK ) { + cf_search_root_pack(i); + } + } + +} + + +void cf_build_secondary_filelist(char *cdrom_dir) +{ + int i; + + // Assume no files + Num_roots = 0; + Num_files = 0; + + // Init the path types + for (i=0; ipathtype_index) ) { + continue; + } + + if (localize) { + // create localized filespec + char temp[MAX_PATH_LEN]; + strcpy(temp, filespec); + lcl_add_dir_to_path_with_filename(filespec); + + if ( !stricmp(filespec, f->name_ext) ) { + if ( size ) *size = f->size; + if ( offset ) *offset = f->pack_offset; + if ( pack_filename ) { + cf_root * r = cf_get_root(f->root_index); + + strcpy( pack_filename, r->path ); + if ( f->pack_offset < 1 ) { + strcat( pack_filename, Pathtypes[f->pathtype_index].path ); + strcat( pack_filename, "\\" ); + strcat( pack_filename, f->name_ext ); + } + } + return 1; + } + // restore original filespec + strcpy(filespec, temp); + } + + // file either not localized or localized version not found + if ( !stricmp(filespec, f->name_ext) ) { + if ( size ) *size = f->size; + if ( offset ) *offset = f->pack_offset; + if ( pack_filename ) { + cf_root * r = cf_get_root(f->root_index); + + strcpy( pack_filename, r->path ); + if ( f->pack_offset < 1 ) { + + if(strlen(Pathtypes[f->pathtype_index].path)){ + strcat( pack_filename, Pathtypes[f->pathtype_index].path ); + strcat( pack_filename, "\\" ); + } + + strcat( pack_filename, f->name_ext ); + } + } + return 1; + } + } + + return 0; +} + + +// Returns true if filename matches filespec, else zero if not +int cf_matches_spec(char *filespec, char *filename) +{ + char *src_ext, *dst_ext; + + src_ext = strchr(filespec, '.'); + if (!src_ext) + return 1; + if (*src_ext == '*') + return 1; + + dst_ext = strchr(filename, '.'); + if (!dst_ext) + return 1; + + return !stricmp(dst_ext, src_ext); +} + +int (*Get_file_list_filter)(char *filename) = NULL; +int Skip_packfile_search = 0; + +int cf_file_already_in_list( int num_files, char **list, char *filename ) +{ + int i; + + char name_no_extension[MAX_PATH_LEN]; + + strcpy(name_no_extension, filename ); + char *p = strchr( name_no_extension, '.' ); + if ( p ) *p = 0; + + for (i=0; i= max) + break; + + if (!(find.attrib & _A_SUBDIR)) { + if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) { + ptr = strrchr(find.name, '.'); + if (ptr) + l = ptr - find.name; + else + l = strlen(find.name); + + list[num_files] = (char *)malloc(l + 1); + strncpy(list[num_files], find.name, l); + list[num_files][l] = 0; + if (info) + info[num_files].write_time = find.time_write; + + num_files++; + } + } + + } while (!_findnext(find_handle, &find)); + + _findclose( find_handle ); + } +#endif + + + // Search all the packfiles and CD. + if ( !Skip_packfile_search ) { + for (i=0; ipathtype_index) ) { + continue; + } + + if (num_files >= max) + break; + + if ( !cf_matches_spec( filter,f->name_ext)) { + continue; + } + + if ( cf_file_already_in_list(num_files,list,f->name_ext)) { + continue; + } + + if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) { + + //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index )); + + ptr = strrchr(f->name_ext, '.'); + if (ptr) + l = ptr - f->name_ext; + else + l = strlen(f->name_ext); + + list[num_files] = (char *)malloc(l + 1); + strncpy(list[num_files], f->name_ext, l); + list[num_files][l] = 0; + + if (info) { + info[num_files].write_time = f->write_time; + } + + num_files++; + } + + } + } + + + if (sort != CF_SORT_NONE) { + cf_sort_filenames( num_files, list, sort, info ); + } + + if (own_flag) { + free(info); + } + + Get_file_list_filter = NULL; + return num_files; +} + +int cf_file_already_in_list_preallocated( int num_files, char arr[][MAX_FILENAME_LEN], char *filename ) +{ + int i; + + char name_no_extension[MAX_PATH_LEN]; + + strcpy(name_no_extension, filename ); + char *p = strchr( name_no_extension, '.' ); + if ( p ) *p = 0; + + for (i=0; i= max) + break; + + if (!(find.attrib & _A_SUBDIR)) { + + if ( !Get_file_list_filter || (*Get_file_list_filter)(find.name) ) { + + strncpy(arr[num_files], find.name, MAX_FILENAME_LEN - 1 ); + char *ptr = strrchr(arr[num_files], '.'); + if ( ptr ) { + *ptr = 0; + } + + if (info) { + info[num_files].write_time = find.time_write; + } + + num_files++; + } + } + + } while (!_findnext(find_handle, &find)); + + _findclose( find_handle ); + } +#endif + + + // Search all the packfiles and CD. + if ( !Skip_packfile_search ) { + for (i=0; ipathtype_index) ) { + continue; + } + + if (num_files >= max) + break; + + if ( !cf_matches_spec( filter,f->name_ext)) { + continue; + } + + if ( cf_file_already_in_list_preallocated( num_files, arr, f->name_ext )) { + continue; + } + + if ( !Get_file_list_filter || (*Get_file_list_filter)(f->name_ext) ) { + + //mprintf(( "Found '%s' in root %d path %d\n", f->name_ext, f->root_index, f->pathtype_index )); + + strncpy(arr[num_files], f->name_ext, MAX_FILENAME_LEN - 1 ); + char *ptr = strrchr(arr[num_files], '.'); + if ( ptr ) { + *ptr = 0; + } + + if (info) { + info[num_files].write_time = f->write_time; + } + + num_files++; + } + + } + } + + if (sort != CF_SORT_NONE) { + Assert(list); + cf_sort_filenames( num_files, list, sort, info ); + } + + if (own_flag) { + free(info); + } + + Get_file_list_filter = NULL; + return num_files; +} + +// Returns the default storage path for files given a +// particular pathtype. In other words, the path to +// the unpacked, non-cd'd, stored on hard drive path. +// If filename isn't null it will also tack the filename +// on the end, creating a completely valid filename. +// Input: pathtype - CF_TYPE_?? +// filename - optional, if set, tacks the filename onto end of path. +// Output: path - Fully qualified pathname. +void cf_create_default_path_string( char *path, int pathtype, char *filename, bool localize ) +{ + if ( filename && strpbrk(filename,"/\\:") ) { + // Already has full path + strcpy( path, filename ); + + } else { + cf_root *root = cf_get_root(0); + + if (!root) { + strcpy(path, filename); + return; + } + + Assert(CF_TYPE_SPECIFIED(pathtype)); + + strcpy(path, root->path); + strcat(path, Pathtypes[pathtype].path); + + // Don't add slash for root directory + if (Pathtypes[pathtype].path[0] != '\0') { + strcat(path, "\\"); + } + + // add filename + if (filename) { + strcat(path, filename); + + // localize filename + if (localize) { + // create copy of path + char temp_path[MAX_PATH_LEN]; + strcpy(temp_path, path); + + // localize the path + lcl_add_dir_to_path_with_filename(path); + + // verify localized path + FILE *fp = fopen(path, "rb"); + if (fp) { + fclose(fp); + } else { + strcpy(path, temp_path); + } + } + } + } +} + diff --git a/src/cfilearchiver/cfilearchiver.cpp b/src/cfilearchiver/cfilearchiver.cpp new file mode 100644 index 0000000..33856d6 --- /dev/null +++ b/src/cfilearchiver/cfilearchiver.cpp @@ -0,0 +1,273 @@ +/* + * $Logfile: /Freespace2/code/Cfilearchiver/CfileArchiver.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Program to create an archive file for use with cfile stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/23/98 6:15p Dave + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include +#include +#include + +unsigned int Total_size=16; // Start with size of header +unsigned int Num_files =0; +FILE * fp_out = NULL; +FILE * fp_out_hdr = NULL; + +typedef struct vp_header { + char id[4]; + int version; + int index_offset; + int num_files; +} vp_header; + +//vp_header Vp_header; + +char archive_dat[1024]; +char archive_hdr[1024]; + +#define BLOCK_SIZE (1024*1024) +#define VERSION_NUMBER 2; + +char tmp_data[BLOCK_SIZE]; // 1 MB + +void write_header() +{ + fseek(fp_out, 0, SEEK_SET); + fwrite("VPVP", 1, 4, fp_out); + int ver = VERSION_NUMBER; + fwrite(&ver, 1, 4, fp_out); + fwrite(&Total_size, 1, 4, fp_out); + fwrite(&Num_files, 1, 4, fp_out); +} + +int write_index(char *hf, char *df) +{ + FILE *h = fopen(hf, "rb"); + if (!h) return 0; + FILE *d = fopen(df, "a+b"); + if (!d) return 0; + for (unsigned int i=0;i31 ) { + printf( "Filename '%s' too long\n", filename ); + exit(1); + } + fwrite( &Total_size, 1, 4, fp_out_hdr ); + fwrite( &filesize, 1, 4, fp_out_hdr ); + fwrite( &path, 1, 32, fp_out_hdr ); + fwrite( &time_write, 1, sizeof(time_t), fp_out_hdr); + + Total_size += filesize; + Num_files++; + printf( "Packing %s\\%s...", filespec, filename ); + + sprintf( path, "%s\\%s", filespec, filename ); + + FILE *fp = fopen( path, "rb" ); + + if ( fp == NULL ) { + printf( "Error opening '%s'\n", path ); + exit(1); + } + + int nbytes, nbytes_read=0; + + do { + nbytes = fread( tmp_data, 1, BLOCK_SIZE, fp ); + if ( nbytes > 0 ) { + fwrite( tmp_data, 1, nbytes, fp_out ); + nbytes_read += nbytes; + + } + } while( nbytes > 0 ); + + fclose(fp); + + printf( " %d bytes\n", nbytes_read ); +} + +// This function adds a directory marker to the header file +void add_directory( char * dirname) +{ + char path[256]; + char *pathptr = path; + char *tmpptr; + + strcpy(path, dirname); + fwrite(&Total_size, 1, 4, fp_out_hdr); + int i = 0; + fwrite(&i, 1, 4, fp_out_hdr); + // strip out any directories that this dir is a subdir of + while ((tmpptr = strchr(pathptr, '\\')) != NULL) { + pathptr = tmpptr+1; + } + fwrite(pathptr, 1, 32, fp_out_hdr); + fwrite(&i, 1, 4, fp_out_hdr); // timestamp = 0 + Num_files++; +} + +void pack_directory( char * filespec) +{ + int find_handle; + _finddata_t find; + char tmp[512]; + char tmp1[512]; + +/* + char dir_name[512]; + char *last_slash; + + last_slash = strrchr(filespec, '\\'); + if ( last_slash ) { + strcpy(dir_name, last_slash+1); + } else { + strcpy(dir_name, filespec); + } + + if ( !stricmp(dir_name, "voice") ) { + return; + } +*/ + + strcpy( tmp1, filespec ); + add_directory(filespec); + strcat( tmp1, "\\*.*" ); + + printf( "In dir '%s'\n", tmp1 ); + + find_handle = _findfirst( tmp1, &find ); + if( find_handle != -1 ) { + if ( find.attrib & _A_SUBDIR ) { + if (strcmp( "..", find.name) && strcmp( ".", find.name)) { + strcpy( tmp, filespec ); + strcat( tmp, "\\" ); + strcat( tmp, find.name ); + pack_directory(tmp); + } + } else { + pack_file( filespec, find.name, find.size, find.time_write ); + } + + while( !_findnext( find_handle, &find ) ) { + if ( find.attrib & _A_SUBDIR ) { + if (strcmp( "..", find.name) && strcmp( ".", find.name)) { + strcpy( tmp, filespec ); + strcat( tmp, "\\" ); + strcat( tmp, find.name ); + pack_directory(tmp); + + } + } else { + pack_file( filespec, find.name, find.size, find.time_write ); + } + } + } + add_directory(".."); +} + + + +int main(int argc, char *argv[] ) +{ + char archive[1024]; + char *p; + + if ( argc < 3 ) { + printf( "Usage: %s archive_name src_dir\n", argv[0] ); + printf( "Example: %s freespace c:\\freespace\\data\n", argv[0] ); + printf( "Creates an archive named freespace out of the\nfreespace data tree\n" ); + printf( "Press any key to exit...\n" ); + getch(); + return 1; + } + + strcpy( archive, argv[1] ); + p = strchr( archive, '.' ); + if (p) *p = 0; // remove extension + + strcpy( archive_dat, archive ); + strcat( archive_dat, ".vp" ); + + strcpy( archive_hdr, archive ); + strcat( archive_hdr, ".hdr" ); + + fp_out = fopen( archive_dat, "wb" ); + if ( !fp_out ) { + printf( "Couldn't open '%s'!\n", archive_dat ); + printf( "Press any key to exit...\n" ); + getch(); + return 1; + } + + fp_out_hdr = fopen( archive_hdr, "wb" ); + if ( !fp_out_hdr ) { + printf( "Couldn't open '%s'!\n", archive_hdr ); + printf( "Press any key to exit...\n" ); + getch(); + return 1; + } + + write_header(); + + pack_directory( argv[2] ); + + write_header(); + + fclose(fp_out); + fclose(fp_out_hdr); + + printf( "Data files written, appending index...\n" ); + + if (!write_index(archive_hdr, archive_dat)) { + printf("Error appending index!\n"); + printf("Press any key to exit...\n"); + getch(); + return 1; + } + + printf( "%d total KB.\n", Total_size/1024 ); + return 0; +} diff --git a/src/cmdline/cmdline.cpp b/src/cmdline/cmdline.cpp new file mode 100644 index 0000000..0f5d51c --- /dev/null +++ b/src/cmdline/cmdline.cpp @@ -0,0 +1,586 @@ +/* + * $Logfile: /Freespace2/code/Cmdline/cmdline.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 8 8/26/99 8:51p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 7 7/15/99 3:07p Dave + * 32 bit detection support. Mouse coord commandline. + * + * 6 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 5 6/22/99 9:37p Dave + * Put in pof spewing. + * + * 4 1/12/99 5:45p Dave + * Moved weapon pipeline in multiplayer to almost exclusively client side. + * Very good results. Bandwidth goes down, playability goes up for crappy + * connections. Fixed object update problem for ship subsystems. + * + * 3 11/17/98 11:12a Dave + * Removed player identification by address. Now assign explicit id #'s. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 38 10/02/98 3:22p Allender + * fix up the -connect option and fix the -port option + * + * 37 9/15/98 4:04p Allender + * added back in the -ip_addr command line switch because it needs to be + * in the standalone server only executable + * + * 36 9/14/98 11:52a Allender + * don't use cfile + * + * 35 9/14/98 11:28a Allender + * support for server bashing of address when received from client. Added + * a cmdline.cfg file to process command line arguments from a file + * + * 34 9/08/98 2:20p Allender + * temporary code to force IP address to a specific value. + * + * 33 8/20/98 5:30p Dave + * Put in handy multiplayer logfile system. Now need to put in useful + * applications of it all over the code. + * + * 32 8/07/98 10:39a Allender + * fixed debug standalone problem where stats would continually get sent + * to tracker. more debug code to help find stats problem + * + * 31 7/24/98 11:14a Allender + * start of new command line options for version 1.04 + * + * 30 5/21/98 1:50a Dave + * Remove obsolete command line functions. Reduce shield explosion packets + * drastically. Tweak PXO screen even more. Fix file xfer system so that + * we can guarantee file uniqueness. + * + * 29 5/18/98 9:10p Dave + * Put in many new PXO features. Fixed skill level bashing in multiplayer. + * Removed several old command line options. Put in network config files. + * + * 28 5/09/98 7:16p Dave + * Put in CD checking. Put in standalone host password. Made pilot into + * popup scrollable. + * + * 27 4/23/98 8:27p Allender + * basic support for cutscene playback. Into movie code in place. Tech + * room can view cutscenes stored in CDROM_dir variable + * + * 26 4/09/98 5:43p Dave + * Remove all command line processing from the demo. Began work fixing up + * the new multi host options screen. + * + * 25 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 24 4/01/98 5:56p Dave + * Fixed a messaging bug which caused msg_all mode in multiplayer not to + * work. Compile out a host of multiplayer options not available in the + * demo. + * + * 23 3/14/98 2:48p Dave + * Cleaned up observer joining code. Put in support for file xfers to + * ingame joiners (observers or not). Revamped and reinstalled pseudo + * lag/loss system. + * + * 22 2/22/98 12:19p John + * Externalized some strings + * + * 21 1/31/98 4:32p Dave + * Put in new support for VMT player validation, game logging in and game + * logging out. + * + * 20 12/10/97 4:45p Dave + * Added in more detailed support for multiplayer packet lag/loss. Fixed + * some multiplayer stuff. Added some controls to the standalone. + * + * 19 12/09/97 6:14p Lawrance + * add -nomusic flag + * + * 18 12/01/97 5:10p Dave + * Fixed a syntax bug. + * + * 17 12/01/97 4:59p Dave + * Synchronized multiplayer debris objects. Put in pilot popup in main + * hall. Optimized simulated multiplayer lag module. Fixed a potential + * file_xfer bug. + * + * 16 11/28/97 7:04p Dave + * Emergency checkin due to big system crash. + * + * 15 11/28/97 5:06p Dave + * Put in facilities for simulating multiplayer lag. + * + * 14 11/24/97 5:42p Dave + * Fixed a file xfer buffer free/malloc problem. Lengthened command line + * switch string parse length. + * + * 13 11/12/97 4:39p Dave + * Put in multiplayer campaign support parsing, loading and saving. Made + * command-line variables better named. Changed some things on the initial + * pilot select screen. + * + * 12 11/11/97 4:54p Dave + * Put in support for single vs. multiplayer pilots. Put in initial player + * selection screen (no command line option yet). Started work on + * multiplayer campaign file save gaming. + * + * 11 11/11/97 11:55a Allender + * initialize network at beginning of application. create new call to set + * which network protocol to use + * + * 10 9/18/97 10:12p Dave + * Added -gimmemedals, which gives the current pilot all the medals in the + * game (debug) + * + * 9 9/18/97 9:20a Dave + * Minor modifications + * + * 8 9/15/97 11:40p Lawrance + * remove demo granularity switch + * + * 7 9/09/97 3:39p Sandeep + * warning level 4 bugs + * + * 6 9/03/97 5:03p Lawrance + * add support for -nosound command line parm + * + * 5 8/22/97 8:52a Dave + * Removed a return statement which would have broken the parser out too + * early. + * + * 4 8/21/97 4:55p Dave + * Added a switch for multiplayer chat streaming. Added a section for + * global command line vars. + * + * 3 8/06/97 2:26p Dave + * Made the command line parse more robust. Made it easier to add and + * process new command-line switches. + * + * 2 8/04/97 3:13p Dave + * Added command line functions. See cmdline.cpp for directions on adding + * new switches + * + * 1 8/04/97 9:58a Dave + * + * $NoKeywords: $ + */ + +#include +#include +#include "cmdline.h" +#include "linklist.h" +#include "systemvars.h" +#include "multi.h" +#include "cfile.h" + +// variables +class cmdline_parm { +public: + cmdline_parm *next, *prev; + char *name; // name of parameter, must start with '-' char + char *help; // help text for this parameter + char *args; // string value for parameter arguements (NULL if no arguements) + int name_found; // true if parameter on command line, otherwise false + + cmdline_parm(char *name, char *help); + ~cmdline_parm(); + int found(); + int get_int(); + float get_float(); + char *str(); +}; + +// here are the command line parameters that we will be using for FreeSpace +cmdline_parm standalone_arg("-standalone", NULL); +cmdline_parm nosound_arg("-nosound", NULL); +cmdline_parm nomusic_arg("-nomusic", NULL); +cmdline_parm startgame_arg("-startgame", NULL); +cmdline_parm gamename_arg("-gamename", NULL); +cmdline_parm gamepassword_arg("-password", NULL); +cmdline_parm gameclosed_arg("-closed", NULL); +cmdline_parm gamerestricted_arg("-restricted", NULL); +cmdline_parm allowabove_arg("-allowabove", NULL); +cmdline_parm allowbelow_arg("-allowbelow", NULL); +cmdline_parm port_arg("-port", NULL); +cmdline_parm connect_arg("-connect", NULL); +cmdline_parm multilog_arg("-multilog", NULL); +cmdline_parm server_firing_arg("-oldfire", NULL); +cmdline_parm client_dodamage("-clientdamage", NULL); +cmdline_parm pof_spew("-pofspew", NULL); +cmdline_parm d3d_32bit("-32bit", NULL); +cmdline_parm mouse_coords("-coords", NULL); +cmdline_parm timeout("-timeout", NULL); +cmdline_parm d3d_window("-window", NULL); + +int Cmdline_multi_stream_chat_to_file = 0; +int Cmdline_freespace_no_sound = 0; +int Cmdline_freespace_no_music = 0; +int Cmdline_gimme_all_medals = 0; +int Cmdline_use_last_pilot = 0; +int Cmdline_multi_protocol = -1; +int Cmdline_cd_check = 1; +int Cmdline_start_netgame = 0; +int Cmdline_closed_game = 0; +int Cmdline_restricted_game = 0; +int Cmdline_network_port = -1; +char *Cmdline_game_name = NULL; +char *Cmdline_game_password = NULL; +char *Cmdline_rank_above= NULL; +char *Cmdline_rank_below = NULL; +char *Cmdline_connect_addr = NULL; +int Cmdline_multi_log = 0; +int Cmdline_server_firing = 0; +int Cmdline_client_dodamage = 0; +int Cmdline_spew_pof_info = 0; +int Cmdline_force_32bit = 0; +int Cmdline_mouse_coords = 0; +int Cmdline_timeout = -1; + +int Cmdline_window = 0; + +static cmdline_parm Parm_list(NULL, NULL); +static int Parm_list_inited = 0; + + +// Return true if this character is an extra char (white space and quotes) +int is_extra_space(char ch) +{ + return ((ch == ' ') || (ch == '\t') || (ch == 0x0a) || (ch == '\'') || (ch == '\"')); +} + + +// eliminates all leading and trailing extra chars from a string. Returns pointer passed in. +char *drop_extra_chars(char *str) +{ + int s, e; + + s = 0; + while (str[s] && is_extra_space(str[s])) + s++; + + e = strlen(str) - 1; + while (e > s) { + if (!is_extra_space(str[e])){ + break; + } + + e--; + } + + if (e > s){ + memmove(str, str + s, e - s + 1); + } + + str[e - s + 1] = 0; + return str; +} + + +// internal function - copy the value for a parameter agruement into the cmdline_parm arg field +void parm_stuff_args(cmdline_parm *parm, char *cmdline) +{ + char buffer[1024]; + memset(buffer, 0, 1024); + char *dest = buffer; + + cmdline += strlen(parm->name); + + while ((*cmdline != 0) && (*cmdline != '-')) { + *dest++ = *cmdline++; + } + + drop_extra_chars(buffer); + + // mwa 9/14/98 -- made it so that newer command line arguments found will overwrite + // the old arguments +// Assert(parm->args == NULL); + if ( parm->args != NULL ) { + delete( parm->args ); + parm->args = NULL; + } + + int size = strlen(buffer) + 1; + if (size > 0) { + parm->args = new char[size]; + memset(parm->args, 0, size); + strcpy(parm->args, buffer); + } +} + + +// internal function - parse the command line, extracting parameter arguements if they exist +// cmdline - command line string passed to the application +void os_parse_parms(char *cmdline) +{ + // locate command line parameters + cmdline_parm *parmp; + char *cmdline_offset; + + for (parmp = GET_FIRST(&Parm_list); parmp !=END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp) ) { + cmdline_offset = strstr(cmdline, parmp->name); + if (cmdline_offset) { + parmp->name_found = 1; + parm_stuff_args(parmp, cmdline_offset); + } + } +} + + +// validate the command line parameters. Display an error if an unrecognized parameter is located. +void os_validate_parms(char *cmdline) +{ + cmdline_parm *parmp; + char seps[] = " ,\t\n"; + char *token; + int parm_found; + + token = strtok(cmdline, seps); + while(token != NULL) { + + if (token[0] == '-') { + parm_found = 0; + for (parmp = GET_FIRST(&Parm_list); parmp !=END_OF_LIST(&Parm_list); parmp = GET_NEXT(parmp) ) { + if (!stricmp(parmp->name, token)) { + parm_found = 1; + break; + } + } + + if (parm_found == 0) { + Error(LOCATION,"Unrecogzined command line parameter %s", token); + } + } + + token = strtok(NULL, seps); + } +} + + +// Call once to initialize the command line system +// +// cmdline - command line string passed to the application +void os_init_cmdline(char *cmdline) +{ + FILE *fp; + + // read the cmdline.cfg file from the data folder, and pass the command line arguments to + // the the parse_parms and validate_parms line. Read these first so anything actually on + // the command line will take precedence + fp = fopen("data\\cmdline.cfg", "rt"); + + // if the file exists, get a single line, and deal with it + if ( fp ) { + char buf[1024], *p; + + fgets(buf, 1024, fp); + + // replace the newline character with a NUL: + if ( (p = strrchr(buf, '\n')) != NULL ) { + *p = '\0'; + } + + os_parse_parms(buf); + os_validate_parms(buf); + fclose(fp); + } + + + + os_parse_parms(cmdline); + os_validate_parms(cmdline); + +} + + +// arg constructor +// name_ - name of the parameter, must start with '-' character +// help_ - help text for this parameter +cmdline_parm::cmdline_parm(char *name_, char *help_) +{ + name = name_; + help = help_; + args = NULL; + name_found = 0; + + if (Parm_list_inited == 0) { + list_init(&Parm_list); + Parm_list_inited = 1; + } + + if (name != NULL) { + list_append(&Parm_list, this); + } +} + + +// destructor - frees any allocated memory +cmdline_parm::~cmdline_parm() +{ + if (args) { + delete [] args; + args = NULL; + } +} + + +// returns - true if the parameter exists on the command line, otherwise false +int cmdline_parm::found() +{ + return name_found; +} + + +// returns - the interger representation for the parameter arguement +int cmdline_parm::get_int() +{ + Assert(args); + return atoi(args); +} + + +// returns - the float representation for the parameter arguement +float cmdline_parm::get_float() +{ + Assert(args); + return (float)atof(args); +} + + +// returns - the string value for the parameter arguement +char *cmdline_parm::str() +{ + Assert(args); + return args; +} + +// external entry point into this modules +int parse_cmdline(char *cmdline) +{ + os_init_cmdline(cmdline); + + // is this a standalone server?? + if (standalone_arg.found()) { + Is_standalone = 1; + } + + // run with no sound + if ( nosound_arg.found() ) { + Cmdline_freespace_no_sound = 1; + } + + // run with no music + if ( nomusic_arg.found() ) { + Cmdline_freespace_no_music = 1; + } + + // should we start a network game + if ( startgame_arg.found() ) { + Cmdline_use_last_pilot = 1; + Cmdline_start_netgame = 1; + } + + // closed network game + if ( gameclosed_arg.found() ) { + Cmdline_closed_game = 1; + } + + // restircted network game + if ( gamerestricted_arg.found() ) { + Cmdline_restricted_game = 1; + } + + // get the name of the network game + if ( gamename_arg.found() ) { + Cmdline_game_name = gamename_arg.str(); + + // be sure that this string fits in our limits + if ( strlen(Cmdline_game_name) > MAX_GAMENAME_LEN ) { + Cmdline_game_name[MAX_GAMENAME_LEN-1] = '\0'; + } + } + + // get the password for a pssword game + if ( gamepassword_arg.found() ) { + Cmdline_game_password = gamepassword_arg.str(); + + // be sure that this string fits in our limits + if ( strlen(Cmdline_game_name) > MAX_PASSWD_LEN ) { + Cmdline_game_name[MAX_PASSWD_LEN-1] = '\0'; + } + } + + // set the rank above/below arguments + if ( allowabove_arg.found() ) { + Cmdline_rank_above = allowabove_arg.str(); + } + if ( allowbelow_arg.found() ) { + Cmdline_rank_below = allowbelow_arg.str(); + } + + // get the port number for games + if ( port_arg.found() ) { + Cmdline_network_port = port_arg.get_int(); + } + + // the connect argument specifies to join a game at this particular address + if ( connect_arg.found() ) { + Cmdline_use_last_pilot = 1; + Cmdline_connect_addr = connect_arg.str(); + } + + // see if the multilog flag was set + if ( multilog_arg.found() ){ + Cmdline_multi_log = 1; + } + + // maybe use old-school server-side firing + if (server_firing_arg.found() ){ + Cmdline_server_firing = 1; + } + + // maybe use old-school client damage + if(client_dodamage.found()){ + Cmdline_client_dodamage = 1; + } + + // spew pof info + if(pof_spew.found()){ + Cmdline_spew_pof_info = 1; + } + + // 32 bit + if(d3d_32bit.found()){ + Cmdline_force_32bit = 1; + } + + // mouse coords + if(mouse_coords.found()){ + Cmdline_mouse_coords = 1; + } + + // net timeout + if(timeout.found()){ + Cmdline_timeout = timeout.get_int(); + } + + // d3d windowed + if(d3d_window.found()){ + Cmdline_window = 1; + } + + return 1; +} diff --git a/src/cmeasure/cmeasure.cpp b/src/cmeasure/cmeasure.cpp new file mode 100644 index 0000000..f6da69f --- /dev/null +++ b/src/cmeasure/cmeasure.cpp @@ -0,0 +1,480 @@ +/* + * $Logfile: /Freespace2/code/CMeasure/CMeasure.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Counter measures. Created by Mike Kulas, May 12, 1997. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 5 9/05/99 11:25p Mikek + * Debug code (only on NDEBUG). Don't drop countermeasures if + * Ai_firing_enabled not set. + * + * 4 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 44 4/29/98 9:36p Allender + * ingame join tweaks. added network message for countermeasures + * + * 43 4/13/98 5:11p Mike + * More improvement to countermeasure code. + * + * 42 4/13/98 2:14p Mike + * Countermeasure balance testing for Jim. + * + * 41 4/10/98 11:02p Mike + * Make countermeasures less effective against aspect seekers than against + * heat seekers. + * Make AI ships match bank with each other when attacking a faraway + * target. + * Make ships not do silly loop-de-loop sometimes when attacking a faraway + * target. + * + * 40 3/31/98 5:11p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * 39 3/17/98 3:49p John + * Turned off lighting on counter measures. + * + * 38 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 37 2/23/98 5:44p Johnson + * Resolve build error. + * + * 36 2/23/98 4:30p Mike + * Make homing missiles detonate after they pass up their target. Make + * countermeasures less effective. + * + * 35 2/09/98 8:04p Lawrance + * Add objnum to cmeasure struct, correctly set source_objnum + * + * 34 2/05/98 11:20p Lawrance + * save/restore countermeasure data + * + * 33 2/05/98 12:51a Mike + * Early asteroid stuff. + * + * 32 2/04/98 12:20p Mike + * Make countermeasures detonate in a smaller radius. Make all in radius, + * not just homing one, detonate. + * + * 31 1/29/98 11:48a John + * Added new counter measure rendering as model code. Made weapons be + * able to have impact explosion. + * + * 30 1/29/98 11:11a John + * Put in code to show dummy counter measure object. + * + * 29 1/23/98 5:06p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 28 1/23/98 9:43a Mike + * Debug-C to disallow countermeasure firing. + * Fix bug in countermeasure tracking by aspect seekers. + * + * 27 1/20/98 9:47a Mike + * Suppress optimized compiler warnings. + * Some secondary weapon work. + * + * 26 1/16/98 11:43a Mike + * Fix countermeasures. + * + * 25 12/30/97 6:44p John + * Made g3_Draw_bitmap functions account for aspect of bitmap. + * + * 24 11/29/97 2:05p John + * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2, + * like they used to incorrectly assume. Added code to model to read in + * thruster radius's. + * + * 23 10/27/97 3:24p Lawrance + * ensure countermeasures keep their initial velocity + * + * 22 9/14/97 4:50p Lawrance + * added some demo debugging code + * + * 21 9/04/97 5:09p Andsager + * implement physics using moment of inertia and mass (from BSPgen). + * Added to phys_info struct. Updated ship_info, polymodel structs. + * Updated weapon ($Mass and $Force) and ship ($Mass -> $Density) tables + * + * 20 8/20/97 11:09a Mike + * Make countermeasure lifetime based on skill level. + * + * 19 8/13/97 9:50p Allender + * split *_move into *_process_pre and *_process_post functions. + * process_pre functions called before object is moved. _process_post + * functions called after object is moved. Reordered code in ship_post + * and weapon_post for multiplayer + * + * 18 8/13/97 4:45p Allender + * fixed ship_fire_primary and fire_secondary to not take parameter for + * determining whether to count ammo or not. Fixed countermeasure firing + * for multiplayer + * + * 17 8/13/97 12:06p Lawrance + * supporting multiple types of fireball explosions + * + * 16 8/11/97 6:03p Mike + * Make ships with no shields not claim to have shields in target box. + * + * 15 8/08/97 4:29p Allender + * countermeasure stuff for multiplayer + * + * 14 7/31/97 5:55p John + * made so you pass flags to obj_create. + * Added new collision code that ignores any pairs that will never + * collide. + * + * 13 7/15/97 12:03p Andsager + * New physics stuff + * + * 12 7/11/97 11:54a John + * added rotated 3d bitmaps. + * + * 11 6/24/97 10:04a Allender + * major multiplayer improvements. Better sequencing before game. + * Dealing with weapon/fireball/counter measure objects between + * client/host. + * + * 10 6/24/97 12:38a Lawrance + * fix minor bug with cycling cmeasure + * + * 9 6/05/97 1:37a Lawrance + * change syntax of a snd_play() + * + * 8 5/22/97 5:45p Mike + * Better countermeasure firing, key off availability, specify in + * weapons.tbl + * + * 7 5/22/97 12:06p Lawrance + * include Sound.h for playing sound fx + * + * 6 5/22/97 12:04p Lawrance + * added soundhook for cmeasure cycle + * + * 5 5/15/97 5:05p Mike + * In the midst of changing subsystem targetnig from type-based to + * pointer-based. + * Also allowed you to view a target while dead. + * + * 4 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 3 5/14/97 10:50a Mike + * More countermeasure stuff. + * + * 2 5/12/97 5:58p Mike + * Add countermeasures. + * + * 1 5/12/97 2:23p Mike + * + * $NoKeywords: $ + */ + +#include "pstypes.h" +#include "systemvars.h" +#include "cmeasure.h" +#include "freespace.h" +#include "vecmat.h" +#include "2d.h" +#include "3d.h" +#include "model.h" +#include "physics.h" +#include "floating.h" +#include "model.h" +#include "ship.h" +#include "timer.h" +#include "fireballs.h" +#include "radar.h" +#include "missionparse.h" // For MAX_SPECIES_NAMES +#include "gamesnd.h" +#include "objectsnd.h" +#include "sound.h" +#include "staticrand.h" + +cmeasure_info Cmeasure_info[MAX_CMEASURE_TYPES]; +cmeasure Cmeasures[MAX_CMEASURES]; + +int Num_cmeasure_types = 0; +int Num_cmeasures = 0; +int Cmeasure_inited = 0; +int Cmeasures_homing_check = 0; +int Countermeasures_enabled = 1; // Debug, set to 0 means no one can fire countermeasures. + +// This will get called at the start of each level. +void cmeasure_init() +{ + int i; + + if ( !Cmeasure_inited ) { + Cmeasure_inited = 1; + +/* // Do all the processing that happens only once + if ( Debris_model < 0 ) { + if (Debris_model>-1) { + polymodel * pm; + pm = model_get(Debris_model); + Debris_num_submodels = pm->n_models; + } + } + + for (i=0; iinstance]; + cmip = &Cmeasure_info[cmp->subtype]; + + if ( cmp->subtype == CMEASURE_UNUSED ) { + Int3(); // Hey, what are we doing in here? + return; + } + +// float size = -1.0f; +// vertex p; +// g3_rotate_vertex(&p, &objp->pos ); +// if ( rand() > RAND_MAX/2 ) { +// gr_set_color( 255, 0, 0 ); +// } else { +// gr_set_color( 255, 255, 255 ); +// } +// g3_draw_sphere(&p, 100.0f ); + + if ( cmip->model_num > -1 ) { + model_clear_instance(cmip->model_num); + model_render(cmip->model_num, &objp->orient, &objp->pos, MR_NO_LIGHTING ); + } else { + mprintf(( "Not rendering countermeasure because model_num is negative\n" )); + } + + +/* + // JAS TODO: Replace with proper fireball + int framenum = -1; + float size = -1.0f; + vertex p; + cmeasure *cmp; + + fireball_data *fd; + + cmp = &Cmeasures[objp->instance]; + fd = &Fireball_data[FIREBALL_SHIP_EXPLODE1]; + + switch (cmp->subtype) { + case CMEASURE_UNUSED: + Int3(); // Hey, what are we doing in here? + break; + default: + framenum = (int) (fd->num_frames * Cmeasures[objp->instance].lifeleft*4) % fd->num_frames; + size = objp->radius; + break; + } + + Assert(framenum != -1); + Assert(size != -1.0f); + + gr_set_bitmap(fd->bitmap_id + framenum); + g3_rotate_vertex(&p, &objp->pos ); + g3_draw_bitmap(&p, 0, size*0.5f, TMAP_FLAG_TEXTURED ); +*/ +} + +void cmeasure_delete( object * objp ) +{ + int num; + + num = objp->instance; + +// Assert( Cmeasures[num].objnum == OBJ_INDEX(objp)); + + Cmeasures[num].subtype = CMEASURE_UNUSED; + Num_cmeasures--; + Assert( Num_cmeasures >= 0 ); +} + +// broke cmeasure_move into two functions -- process_pre and process_post (as was done with +// all *_move functions). Nothing to do for process_pre + +void cmeasure_process_pre( object *objp, float frame_time) +{ +} + +void cmeasure_process_post(object * objp, float frame_time) +{ + int num; + num = objp->instance; + +// Assert( Cmeasures[num].objnum == objnum ); + cmeasure *cmp = &Cmeasures[num]; + + if ( cmp->lifeleft >= 0.0f) { + cmp->lifeleft -= frame_time; + if ( cmp->lifeleft < 0.0f ) { + objp->flags |= OF_SHOULD_BE_DEAD; +// demo_do_flag_dead(OBJ_INDEX(objp)); + } + } + +} + +float Skill_level_cmeasure_life_scale[NUM_SKILL_LEVELS] = {3.0f, 2.0f, 1.5f, 1.25f, 1.0f}; + +// creates one countermeasure. A ship fires 1 of these per launch. rand_val is used +// in multiplayer. If -1, then create a random number. If non-negative, use this +// number for static_rand functions +int cmeasure_create( object * source_obj, vector * pos, int cm_type, int rand_val ) +{ + int n, objnum, parent_objnum, arand; + object * obj; + ship *shipp; + cmeasure *cmp; + cmeasure_info *cmeasurep; + +#ifndef NDEBUG + if (!Countermeasures_enabled || !Ai_firing_enabled) + return -1; +#endif + + Cmeasures_homing_check = 2; // Tell homing code to scan everything for two frames. If only one frame, get sync problems due to objects being created at end of frame! + + parent_objnum = OBJ_INDEX(source_obj); + + Assert( source_obj->type == OBJ_SHIP ); + Assert( source_obj->instance >= 0 && source_obj->instance < MAX_SHIPS ); + + shipp = &Ships[source_obj->instance]; + + if ( Num_cmeasures >= MAX_CMEASURES) + return -1; + + for (n=0; ninstance].ship_name)); + + cmp = &Cmeasures[n]; + cmeasurep = &Cmeasure_info[cm_type]; + + if ( pos == NULL ) + pos = &source_obj->pos; + + objnum = obj_create( OBJ_CMEASURE, parent_objnum, n, &source_obj->orient, pos, 1.0f, OF_RENDERS | OF_PHYSICS ); + + Assert( objnum >= 0 && objnum < MAX_OBJECTS ); + + // Create Debris piece n! + if ( rand_val == -1 ) + arand = myrand(); // use a random number to get lifeleft, and random vector for displacement from ship + else + arand = rand_val; + + cmp->lifeleft = static_randf(arand) * (cmeasurep->life_max - cmeasurep->life_min) / cmeasurep->life_min; + if (source_obj->flags & OF_PLAYER_SHIP){ + cmp->lifeleft *= Skill_level_cmeasure_life_scale[Game_skill_level]; + } + cmp->lifeleft = cmeasurep->life_min + cmp->lifeleft * (cmeasurep->life_max - cmeasurep->life_min); + + // cmp->objnum = objnum; + cmp->team = shipp->team; + cmp->subtype = cm_type; + cmp->objnum = objnum; + cmp->source_objnum = parent_objnum; + cmp->source_sig = Objects[objnum].signature; + + cmp->flags = 0; + + nprintf(("Jim", "Frame %i: Launching countermeasure #%i\n", Framecount, Objects[objnum].signature)); + + obj = &Objects[objnum]; + + Num_cmeasures++; + + vector vel, rand_vec; + + vm_vec_scale_add(&vel, &source_obj->phys_info.vel, &source_obj->orient.fvec, -25.0f); + + static_randvec(arand+1, &rand_vec); + + vm_vec_scale_add2(&vel, &rand_vec, 2.0f); + + obj->phys_info.vel = vel; + + vm_vec_zero(&obj->phys_info.rotvel); + + // blow out his reverse thrusters. Or drag, same thing. + obj->phys_info.rotdamp = 10000.0f; + obj->phys_info.side_slip_time_const = 10000.0f; + + vm_vec_zero(&obj->phys_info.max_vel); // make so he can't turn on his own VOLITION anymore. + obj->phys_info.max_vel.z = -25.0f; + vm_vec_copy_scale(&obj->phys_info.desired_vel, &obj->orient.fvec, obj->phys_info.max_vel.z ); + + vm_vec_zero(&obj->phys_info.max_rotvel); // make so he can't change speed on his own VOLITION anymore. + +// obj->phys_info.flags |= PF_USE_VEL; + + return arand; // need to return this value for multiplayer purposes +} + +void cmeasure_select_next(object *objp) +{ + ship *shipp; + + Assert(objp->type == OBJ_SHIP); + + shipp = &Ships[objp->instance]; + shipp->current_cmeasure++; + + if (shipp->current_cmeasure >= Num_cmeasure_types) + shipp->current_cmeasure = 0; + + //snd_play( &Snds[SND_CMEASURE_CYCLE] ); + + mprintf(("Countermeasure type set to %i in frame %i\n", shipp->current_cmeasure, Framecount)); +} + diff --git a/src/code.dsp b/src/code.dsp new file mode 100644 index 0000000..e53ffff --- /dev/null +++ b/src/code.dsp @@ -0,0 +1,2503 @@ +# Microsoft Developer Studio Project File - Name="code" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=code - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "code.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "code.mak" CFG="code - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "code - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "code - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Freespace2/code", UCACAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "code - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\Release" +# PROP Intermediate_Dir "..\Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /G5 /MT /W4 /GX /Zi /Ot /Ow /Og /Oi /Oy /Ob2 /I ".." /I "anim" /I "asteroid" /I "bmpman" /I "cfile" /I "cmdline" /I "cmeasure" /I "controlconfig" /I "cutscene" /I "debris" /I "debugconsole" /I "directx" /I "fireball" /I "fred" /I "freespace2" /I "fs2launch" /I "gamehelp" /I "gamesequence" /I "gamesnd" /I "glide" /I "globalincs" /I "graphics" /I "hud" /I "io" /I "jumpnode" /I "lighting" /I "math" /I "menuui" /I "mission" /I "missionui" /I "model" /I "movie" /I "network" /I "object" /I "observer" /I "osapi" /I "palman" /I "parse" /I "particle" /I "pcxutils" /I "physics" /I "playerman" /I "popup" /I "radar" /I "render" /I "ship" /I "sndman" /I "sound" /I "starfield" /I "stats" /I "ui" /I "vcodec" /I "weapon" /I "localization" /I "fred2" /I "nebula" /I "TgaUtils" /I "demo" /I "inetfile" /I "exceptionhandler" /I "3dnow" /FI"PSTypes.h" /D "_WINDOWS" /D "WIN32" /D "NDEBUG" /U "_DEBUG" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\Debug" +# PROP Intermediate_Dir "..\Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /G5 /MTd /W4 /GX /Zi /Od /Ob2 /I ".." /I "anim" /I "asteroid" /I "bmpman" /I "cfile" /I "cmdline" /I "cmeasure" /I "controlconfig" /I "cutscene" /I "debris" /I "debugconsole" /I "directx" /I "fireball" /I "fred" /I "freespace2" /I "fs2launch" /I "gamehelp" /I "gamesequence" /I "gamesnd" /I "glide" /I "globalincs" /I "graphics" /I "hud" /I "io" /I "jumpnode" /I "lighting" /I "math" /I "menuui" /I "mission" /I "missionui" /I "model" /I "movie" /I "network" /I "object" /I "observer" /I "osapi" /I "palman" /I "parse" /I "particle" /I "pcxutils" /I "physics" /I "playerman" /I "popup" /I "radar" /I "render" /I "ship" /I "sndman" /I "sound" /I "starfield" /I "stats" /I "ui" /I "vcodec" /I "weapon" /I "localization" /I "fred2" /I "nebula" /I "TgaUtils" /I "demo" /I "inetfile" /I "exceptionhandler" /I "3dnow" /FI"PSTypes.h" /D "_WINDOWS" /D "WIN32" /D "_DEBUG" /U "NDEBUG" /FR /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "code - Win32 Release" +# Name "code - Win32 Debug" +# Begin Group "Anim" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Anim\AnimPlay.cpp +# End Source File +# Begin Source File + +SOURCE=.\Anim\AnimPlay.h +# End Source File +# Begin Source File + +SOURCE=.\Anim\PackUnpack.cpp +# End Source File +# Begin Source File + +SOURCE=.\Anim\PackUnpack.h +# End Source File +# End Group +# Begin Group "Asteroid" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Asteroid\Asteroid.cpp +# End Source File +# Begin Source File + +SOURCE=.\Asteroid\Asteroid.h +# End Source File +# End Group +# Begin Group "Bmpman" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Bmpman\BmpMan.cpp +# End Source File +# Begin Source File + +SOURCE=.\Bmpman\BmpMan.h +# End Source File +# End Group +# Begin Group "CFile" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\CFile\cfile.cpp +# End Source File +# Begin Source File + +SOURCE=.\CFile\cfile.h +# End Source File +# Begin Source File + +SOURCE=.\CFile\CfileArchive.cpp +# End Source File +# Begin Source File + +SOURCE=.\CFile\CfileArchive.h +# End Source File +# Begin Source File + +SOURCE=.\CFile\CfileList.cpp +# End Source File +# Begin Source File + +SOURCE=.\CFile\CfileSystem.cpp +# End Source File +# Begin Source File + +SOURCE=.\CFile\CfileSystem.h +# End Source File +# End Group +# Begin Group "Cmdline" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Cmdline\cmdline.cpp +# End Source File +# Begin Source File + +SOURCE=.\Cmdline\cmdline.h +# End Source File +# End Group +# Begin Group "CMeasure" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\CMeasure\CMeasure.cpp +# End Source File +# Begin Source File + +SOURCE=.\CMeasure\CMeasure.h +# End Source File +# End Group +# Begin Group "ControlConfig" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\ControlConfig\ControlsConfig.cpp +# End Source File +# Begin Source File + +SOURCE=.\ControlConfig\ControlsConfig.h +# End Source File +# Begin Source File + +SOURCE=.\ControlConfig\ControlsConfigCommon.cpp +# End Source File +# End Group +# Begin Group "Cutscene" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Cutscene\Cutscenes.cpp +# End Source File +# Begin Source File + +SOURCE=.\Cutscene\Cutscenes.h +# End Source File +# End Group +# Begin Group "Debris" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Debris\Debris.cpp +# End Source File +# Begin Source File + +SOURCE=.\Debris\Debris.h +# End Source File +# End Group +# Begin Group "DebugConsole" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\DebugConsole\Console.cpp +# End Source File +# End Group +# Begin Group "DirectX" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\DirectX\vasync.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vD3d.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vD3dcaps.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vd3di.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vd3drm.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vd3drmdef.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vd3drmobj.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vd3drmwin.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vD3dtypes.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vd3dvec.inl +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vDdraw.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vDinput.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vdplay.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vdplobby.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vdsetup.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vDsound.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vdvp.h +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vDdraw.lib +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vDinput.lib +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vDsound.lib +# End Source File +# Begin Source File + +SOURCE=.\DirectX\vDxguid.lib +# End Source File +# End Group +# Begin Group "Fireball" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Fireball\FireBalls.cpp +# End Source File +# Begin Source File + +SOURCE=.\Fireball\FireBalls.h +# End Source File +# Begin Source File + +SOURCE=.\Fireball\WarpInEffect.cpp +# End Source File +# End Group +# Begin Group "GameHelp" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\GameHelp\ContextHelp.cpp +# End Source File +# Begin Source File + +SOURCE=.\GameHelp\ContextHelp.h +# End Source File +# Begin Source File + +SOURCE=.\GameHelp\GameplayHelp.cpp +# End Source File +# Begin Source File + +SOURCE=.\GameHelp\GameplayHelp.h +# End Source File +# End Group +# Begin Group "GameSequence" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\GameSequence\GameSequence.cpp +# End Source File +# Begin Source File + +SOURCE=.\GameSequence\GameSequence.h +# End Source File +# End Group +# Begin Group "GameSnd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Gamesnd\EventMusic.cpp +# End Source File +# Begin Source File + +SOURCE=.\Gamesnd\EventMusic.h +# End Source File +# Begin Source File + +SOURCE=.\Gamesnd\GameSnd.cpp +# End Source File +# Begin Source File + +SOURCE=.\Gamesnd\GameSnd.h +# End Source File +# End Group +# Begin Group "Glide" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Glide\3DFX.H +# End Source File +# Begin Source File + +SOURCE=.\Glide\FXDLL.H +# End Source File +# Begin Source File + +SOURCE=.\Glide\FXGLOB.H +# End Source File +# Begin Source File + +SOURCE=.\Glide\FXOS.H +# End Source File +# Begin Source File + +SOURCE=.\Glide\Glide.cpp +# End Source File +# Begin Source File + +SOURCE=.\Glide\glide.h +# End Source File +# Begin Source File + +SOURCE=.\Glide\glidesys.h +# End Source File +# Begin Source File + +SOURCE=.\Glide\glideutl.h +# End Source File +# Begin Source File + +SOURCE=.\Glide\SST1VID.H +# End Source File +# End Group +# Begin Group "GlobalIncs" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\GlobalIncs\AlphaColors.cpp +# End Source File +# Begin Source File + +SOURCE=.\GlobalIncs\AlphaColors.h +# End Source File +# Begin Source File + +SOURCE=.\GlobalIncs\crypt.cpp +# End Source File +# Begin Source File + +SOURCE=.\GlobalIncs\crypt.h +# End Source File +# Begin Source File + +SOURCE=.\GlobalIncs\LinkList.h +# End Source File +# Begin Source File + +SOURCE=.\GlobalIncs\PsTypes.h +# End Source File +# Begin Source File + +SOURCE=.\GlobalIncs\SystemVars.cpp +# End Source File +# Begin Source File + +SOURCE=.\GlobalIncs\SystemVars.h +# End Source File +# Begin Source File + +SOURCE=.\GlobalIncs\version.cpp +# End Source File +# Begin Source File + +SOURCE=.\GlobalIncs\version.h +# End Source File +# Begin Source File + +SOURCE=.\GlobalIncs\WinDebug.cpp +# End Source File +# End Group +# Begin Group "Graphics" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Graphics\2d.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\2d.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\aaline.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Bitblt.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Bitblt.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Circle.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Circle.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Colors.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Colors.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Font.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Font.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Gradient.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Gradient.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrD3D.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrD3D.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrD3DInternal.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrD3DRender.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrD3DTexture.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrDirectDraw.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrDirectDraw.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrGlide.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrGlide.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrGlideInternal.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrGlideTexture.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrInternal.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrOpenGL.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrOpenGL.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrSoft.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrSoft.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrZbuffer.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\GrZbuffer.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Line.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Line.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Pixel.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Pixel.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Rect.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Rect.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Scaler.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Scaler.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Shade.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Shade.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\TmapGenericScans.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\Tmapper.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\TMAPPER.H +# End Source File +# Begin Source File + +SOURCE=.\Graphics\TmapScanline.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\TmapScanline.h +# End Source File +# Begin Source File + +SOURCE=.\Graphics\TmapScanTiled128x128.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\TmapScanTiled16x16.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\TmapScanTiled256x256.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\TmapScanTiled32x32.cpp +# End Source File +# Begin Source File + +SOURCE=.\Graphics\TmapScanTiled64x64.cpp +# End Source File +# End Group +# Begin Group "Hud" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Hud\HUD.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUD.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HudArtillery.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HudArtillery.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDbrackets.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDbrackets.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDconfig.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDconfig.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDescort.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDescort.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDets.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDets.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDgauges.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDlock.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDlock.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDmessage.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDmessage.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDObserver.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDObserver.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDreticle.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDreticle.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDshield.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDshield.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDsquadmsg.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDsquadmsg.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDtarget.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDtarget.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDtargetbox.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDtargetbox.h +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDWingmanStatus.cpp +# End Source File +# Begin Source File + +SOURCE=.\Hud\HUDWingmanStatus.h +# End Source File +# End Group +# Begin Group "Io" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Io\Joy.cpp +# End Source File +# Begin Source File + +SOURCE=.\Io\Joy.h +# End Source File +# Begin Source File + +SOURCE=.\Io\Joy_ff.cpp +# End Source File +# Begin Source File + +SOURCE=.\Io\Joy_ff.h +# End Source File +# Begin Source File + +SOURCE=.\Io\Key.cpp +# End Source File +# Begin Source File + +SOURCE=.\Io\Key.h +# End Source File +# Begin Source File + +SOURCE=.\Io\KeyControl.cpp +# End Source File +# Begin Source File + +SOURCE=.\Io\KeyControl.h +# End Source File +# Begin Source File + +SOURCE=.\Io\Mouse.cpp +# End Source File +# Begin Source File + +SOURCE=.\Io\Mouse.h +# End Source File +# Begin Source File + +SOURCE=.\Io\sw_error.hpp +# End Source File +# Begin Source File + +SOURCE=.\Io\sw_force.h +# End Source File +# Begin Source File + +SOURCE=.\Io\sw_guid.hpp +# End Source File +# Begin Source File + +SOURCE=.\Io\swff_lib.cpp +# End Source File +# Begin Source File + +SOURCE=.\Io\Timer.cpp +# End Source File +# Begin Source File + +SOURCE=.\Io\Timer.h +# End Source File +# End Group +# Begin Group "JumpNode" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\JumpNode\JumpNode.cpp +# End Source File +# Begin Source File + +SOURCE=.\JumpNode\JumpNode.h +# End Source File +# End Group +# Begin Group "Lighting" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Lighting\Lighting.cpp +# End Source File +# Begin Source File + +SOURCE=.\Lighting\Lighting.h +# End Source File +# End Group +# Begin Group "Math" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Math\Fix.cpp +# End Source File +# Begin Source File + +SOURCE=.\Math\fix.h +# End Source File +# Begin Source File + +SOURCE=.\Math\Floating.cpp +# End Source File +# Begin Source File + +SOURCE=.\Math\Floating.h +# End Source File +# Begin Source File + +SOURCE=.\Math\Fvi.cpp +# End Source File +# Begin Source File + +SOURCE=.\Math\Fvi.h +# End Source File +# Begin Source File + +SOURCE=.\Math\spline.cpp +# End Source File +# Begin Source File + +SOURCE=.\Math\spline.h +# End Source File +# Begin Source File + +SOURCE=.\Math\StaticRand.cpp +# End Source File +# Begin Source File + +SOURCE=.\Math\StaticRand.h +# End Source File +# Begin Source File + +SOURCE=.\Math\VecMat.cpp +# End Source File +# Begin Source File + +SOURCE=.\Math\VecMat.h +# End Source File +# End Group +# Begin Group "MenuUI" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\MenuUI\Barracks.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\Barracks.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\Credits.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\Credits.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\fishtank.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\fishtank.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\MainHallMenu.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\MainHallMenu.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\MainHallTemp.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\MainHallTemp.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\OptionsMenu.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\OptionsMenu.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\OptionsMenuMulti.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\OptionsMenuMulti.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\PlayerMenu.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\PlayerMenu.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\ReadyRoom.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\ReadyRoom.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\SnazzyUI.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\SnazzyUI.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\TechMenu.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\TechMenu.h +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\TrainingMenu.cpp +# End Source File +# Begin Source File + +SOURCE=.\MenuUI\TrainingMenu.h +# End Source File +# End Group +# Begin Group "Mission" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Mission\MissionBriefCommon.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionBriefCommon.h +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionCampaign.cpp +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionCampaign.h +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionGoals.cpp +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionGoals.h +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionGrid.cpp +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionGrid.h +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionHotKey.cpp +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionHotKey.h +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionLoad.cpp +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionLoad.h +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionLog.cpp +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionLog.h +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionMessage.cpp +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionMessage.h +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionParse.cpp +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionParse.h +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionTraining.cpp +# End Source File +# Begin Source File + +SOURCE=.\Mission\MissionTraining.h +# End Source File +# End Group +# Begin Group "MissionUI" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\MissionUI\Chatbox.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\Chatbox.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionBrief.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionBrief.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionCmdBrief.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionCmdBrief.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionDebrief.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionDebrief.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionLoopBrief.cpp +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionLoopBrief.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionPause.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionPause.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionRecommend.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionRecommend.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionScreenCommon.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionScreenCommon.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionShipChoice.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionShipChoice.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionStats.cpp +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionStats.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionWeaponChoice.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\MissionWeaponChoice.h +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\RedAlert.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\MissionUI\RedAlert.h +# End Source File +# End Group +# Begin Group "Model" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Model\MODEL.H +# End Source File +# Begin Source File + +SOURCE=.\Model\ModelCollide.cpp +# End Source File +# Begin Source File + +SOURCE=.\Model\ModelInterp.cpp +# End Source File +# Begin Source File + +SOURCE=.\Model\ModelOctant.cpp +# End Source File +# Begin Source File + +SOURCE=.\Model\ModelRead.cpp +# End Source File +# Begin Source File + +SOURCE=.\Model\ModelsInc.h +# End Source File +# End Group +# Begin Group "Object" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Object\CollideDebrisShip.cpp +# End Source File +# Begin Source File + +SOURCE=.\Object\CollideDebrisWeapon.cpp +# End Source File +# Begin Source File + +SOURCE=.\Object\CollideShipShip.cpp +# End Source File +# Begin Source File + +SOURCE=.\Object\CollideShipWeapon.cpp +# End Source File +# Begin Source File + +SOURCE=.\Object\CollideWeaponWeapon.cpp +# End Source File +# Begin Source File + +SOURCE=.\Object\ObjCollide.cpp +# End Source File +# Begin Source File + +SOURCE=.\Object\ObjCollide.h +# End Source File +# Begin Source File + +SOURCE=.\Object\Object.cpp +# End Source File +# Begin Source File + +SOURCE=.\Object\Object.h +# End Source File +# Begin Source File + +SOURCE=.\Object\ObjectSnd.cpp +# End Source File +# Begin Source File + +SOURCE=.\Object\ObjectSnd.h +# End Source File +# Begin Source File + +SOURCE=.\Object\ObjectSort.cpp +# End Source File +# End Group +# Begin Group "Observer" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Observer\Observer.cpp +# End Source File +# Begin Source File + +SOURCE=.\Observer\Observer.h +# End Source File +# End Group +# Begin Group "OsApi" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\OsApi\MONOPUB.H +# End Source File +# Begin Source File + +SOURCE=.\OsApi\OsApi.cpp +# End Source File +# Begin Source File + +SOURCE=.\OsApi\OsApi.h +# End Source File +# Begin Source File + +SOURCE=.\OsApi\OsRegistry.cpp +# End Source File +# Begin Source File + +SOURCE=.\OsApi\OsRegistry.h +# End Source File +# Begin Source File + +SOURCE=.\OsApi\OutWnd.cpp +# End Source File +# Begin Source File + +SOURCE=.\OsApi\OutWnd.h +# End Source File +# End Group +# Begin Group "Palman" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Palman\PalMan.cpp +# End Source File +# Begin Source File + +SOURCE=.\Palman\PalMan.h +# End Source File +# End Group +# Begin Group "Parse" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Parse\Encrypt.cpp +# End Source File +# Begin Source File + +SOURCE=.\Parse\Encrypt.h +# End Source File +# Begin Source File + +SOURCE=.\Parse\PARSELO.CPP +# End Source File +# Begin Source File + +SOURCE=.\Parse\parselo.h +# End Source File +# Begin Source File + +SOURCE=.\Parse\SEXP.CPP +# End Source File +# Begin Source File + +SOURCE=.\Parse\SEXP.H +# End Source File +# End Group +# Begin Group "Particle" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Particle\Particle.cpp +# End Source File +# Begin Source File + +SOURCE=.\Particle\Particle.h +# End Source File +# End Group +# Begin Group "PcxUtils" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\PcxUtils\pcxutils.cpp +# End Source File +# Begin Source File + +SOURCE=.\PcxUtils\pcxutils.h +# End Source File +# End Group +# Begin Group "Physics" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Physics\Physics.cpp +# End Source File +# Begin Source File + +SOURCE=.\Physics\Physics.h +# End Source File +# End Group +# Begin Group "Playerman" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Playerman\ManagePilot.cpp +# End Source File +# Begin Source File + +SOURCE=.\Playerman\ManagePilot.h +# End Source File +# Begin Source File + +SOURCE=.\Playerman\Player.h +# End Source File +# Begin Source File + +SOURCE=.\Playerman\PlayerControl.cpp +# End Source File +# End Group +# Begin Group "Popup" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Popup\Popup.cpp +# End Source File +# Begin Source File + +SOURCE=.\Popup\Popup.h +# End Source File +# Begin Source File + +SOURCE=.\Popup\PopupDead.cpp +# End Source File +# Begin Source File + +SOURCE=.\Popup\PopupDead.h +# End Source File +# End Group +# Begin Group "Radar" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Radar\Radar.cpp +# End Source File +# Begin Source File + +SOURCE=.\Radar\Radar.h +# End Source File +# End Group +# Begin Group "Render" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Render\3D.H +# End Source File +# Begin Source File + +SOURCE=.\Render\3dClipper.cpp +# End Source File +# Begin Source File + +SOURCE=.\Render\3ddraw.cpp +# End Source File +# Begin Source File + +SOURCE=.\Render\3dInternal.h +# End Source File +# Begin Source File + +SOURCE=.\Render\3dLaser.cpp +# End Source File +# Begin Source File + +SOURCE=.\Render\3dMath.cpp +# End Source File +# Begin Source File + +SOURCE=.\Render\3dSetup.cpp +# End Source File +# End Group +# Begin Group "Ship" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Ship\Afterburner.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\Afterburner.h +# End Source File +# Begin Source File + +SOURCE=.\Ship\ai.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\ai.h +# End Source File +# Begin Source File + +SOURCE=.\Ship\AiBig.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\AiBig.h +# End Source File +# Begin Source File + +SOURCE=.\Ship\AiCode.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\AiGoals.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\AiGoals.h +# End Source File +# Begin Source File + +SOURCE=.\Ship\AiLocal.h +# End Source File +# Begin Source File + +SOURCE=.\Ship\AWACS.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\AWACS.h +# End Source File +# Begin Source File + +SOURCE=.\Ship\Shield.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\Ship.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\Ship.h +# End Source File +# Begin Source File + +SOURCE=.\Ship\ShipContrails.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\ShipContrails.h +# End Source File +# Begin Source File + +SOURCE=.\Ship\ShipFX.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\ShipFX.h +# End Source File +# Begin Source File + +SOURCE=.\Ship\ShipHit.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Ship\ShipHit.h +# End Source File +# Begin Source File + +SOURCE=.\Ship\SubsysDamage.h +# End Source File +# End Group +# Begin Group "Sound" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Sound\acm.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\acm.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\AudioStr.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\AudioStr.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\channel.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\ds.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\ds.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\ds3d.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\ds3d.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\dscap.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\dscap.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\Ia3d.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\midifile.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\midifile.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\midiseq.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\RBAudio.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\RBAudio.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\rsx.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\rsx_lib.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\rsx_lib.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\rtvoice.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\rtvoice.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\Sound.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\Sound.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\VerifyA3D.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\WinMIDI.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\WinMIDI.h +# End Source File +# Begin Source File + +SOURCE=.\Sound\winmidi_base.cpp +# End Source File +# Begin Source File + +SOURCE=.\Sound\VerifyA3D.lib +# End Source File +# End Group +# Begin Group "Starfield" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Starfield\Nebula.cpp +# End Source File +# Begin Source File + +SOURCE=.\Starfield\Nebula.h +# End Source File +# Begin Source File + +SOURCE=.\Starfield\StarField.cpp +# End Source File +# Begin Source File + +SOURCE=.\Starfield\StarField.h +# End Source File +# Begin Source File + +SOURCE=.\Starfield\Supernova.cpp +# End Source File +# Begin Source File + +SOURCE=.\Starfield\Supernova.h +# End Source File +# End Group +# Begin Group "Stats" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Stats\Medals.cpp +# End Source File +# Begin Source File + +SOURCE=.\Stats\Medals.h +# End Source File +# Begin Source File + +SOURCE=.\Stats\Scoring.cpp +# End Source File +# Begin Source File + +SOURCE=.\Stats\Scoring.h +# End Source File +# Begin Source File + +SOURCE=.\Stats\Stats.cpp +# End Source File +# Begin Source File + +SOURCE=.\Stats\Stats.h +# End Source File +# End Group +# Begin Group "Ui" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\UI\BUTTON.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\CHECKBOX.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\GADGET.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\icon.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\INPUTBOX.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\KEYTRAP.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\LISTBOX.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\RADIO.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\SCROLL.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\slider.cpp +# End Source File +# Begin Source File + +SOURCE=.\Ui\SLIDER2.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\UI.H +# End Source File +# Begin Source File + +SOURCE=.\UI\UiDefs.h +# End Source File +# Begin Source File + +SOURCE=.\UI\UIDRAW.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\UIMOUSE.cpp +# End Source File +# Begin Source File + +SOURCE=.\UI\WINDOW.cpp +# End Source File +# End Group +# Begin Group "VCodec" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\VCodec\CODEC1.CPP +# End Source File +# Begin Source File + +SOURCE=.\VCodec\CODEC1.H +# End Source File +# End Group +# Begin Group "Weapon" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Weapon\Beam.cpp +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Beam.h +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Corkscrew.cpp +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Corkscrew.h +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Emp.cpp +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Emp.h +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Flak.cpp +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Flak.h +# End Source File +# Begin Source File + +SOURCE=.\Weapon\MuzzleFlash.cpp +# End Source File +# Begin Source File + +SOURCE=.\Weapon\MuzzleFlash.h +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Shockwave.cpp +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Shockwave.h +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Swarm.cpp +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Swarm.h +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Trails.cpp +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Trails.h +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Weapon.h +# End Source File +# Begin Source File + +SOURCE=.\Weapon\Weapons.cpp +# End Source File +# End Group +# Begin Group "Nebula" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Nebula\Neb.cpp +# End Source File +# Begin Source File + +SOURCE=.\Nebula\Neb.h +# End Source File +# Begin Source File + +SOURCE=.\Nebula\NebLightning.cpp + +!IF "$(CFG)" == "code - Win32 Release" + +# ADD CPP /Od + +!ELSEIF "$(CFG)" == "code - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\Nebula\NebLightning.h +# End Source File +# End Group +# Begin Group "Localization" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Localization\fhash.cpp +# End Source File +# Begin Source File + +SOURCE=.\Localization\fhash.h +# End Source File +# Begin Source File + +SOURCE=.\Localization\localize.cpp +# End Source File +# Begin Source File + +SOURCE=.\Localization\localize.h +# End Source File +# End Group +# Begin Group "TgaUtils" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\TgaUtils\TgaUtils.cpp +# End Source File +# Begin Source File + +SOURCE=.\TgaUtils\TgaUtils.h +# End Source File +# End Group +# Begin Group "Demo" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Demo\Demo.cpp +# End Source File +# Begin Source File + +SOURCE=.\Demo\Demo.h +# End Source File +# End Group +# Begin Group "InetFile" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Inetfile\CFtp.cpp +# End Source File +# Begin Source File + +SOURCE=.\Inetfile\CFtp.h +# End Source File +# Begin Source File + +SOURCE=.\Inetfile\Chttpget.cpp +# End Source File +# Begin Source File + +SOURCE=.\Inetfile\Chttpget.h +# End Source File +# Begin Source File + +SOURCE=.\Inetfile\inetgetfile.cpp +# End Source File +# Begin Source File + +SOURCE=.\Inetfile\inetgetfile.h +# End Source File +# End Group +# Begin Group "ExceptionHandler" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\ExceptionHandler\ExceptionHandler.cpp +# End Source File +# Begin Source File + +SOURCE=.\ExceptionHandler\ExceptionHandler.h +# End Source File +# End Group +# Begin Group "Network" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\Network\Multi.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\Multi.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_campaign.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_campaign.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_data.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_data.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_dogfight.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_dogfight.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_endgame.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_endgame.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_ingame.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_ingame.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_kick.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_kick.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_log.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_log.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_obj.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_obj.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_observer.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_observer.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_oo.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_oo.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_options.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_options.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_pause.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_pause.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_pinfo.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_pinfo.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_ping.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_ping.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_pmsg.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_pmsg.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_rate.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_rate.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_respawn.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_respawn.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_team.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_team.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_update.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_update.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_voice.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_voice.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_xfer.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multi_xfer.h +# End Source File +# Begin Source File + +SOURCE=.\Network\multilag.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multilag.h +# End Source File +# Begin Source File + +SOURCE=.\Network\MultiMsgs.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\multimsgs.h +# End Source File +# Begin Source File + +SOURCE=.\Network\MultiTeamSelect.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\MultiTeamSelect.h +# End Source File +# Begin Source File + +SOURCE=.\Network\MultiUI.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\MultiUI.h +# End Source File +# Begin Source File + +SOURCE=.\Network\MultiUtil.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\MultiUtil.h +# End Source File +# Begin Source File + +SOURCE=.\Network\PsNet.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\PsNet.h +# End Source File +# Begin Source File + +SOURCE=.\Network\Psnet2.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\Psnet2.h +# End Source File +# Begin Source File + +SOURCE=.\Network\stand_gui.cpp +# End Source File +# Begin Source File + +SOURCE=.\Network\stand_gui.h +# End Source File +# End Group +# End Target +# End Project diff --git a/src/controlconfig/controlsconfig.cpp b/src/controlconfig/controlsconfig.cpp new file mode 100644 index 0000000..b895427 --- /dev/null +++ b/src/controlconfig/controlsconfig.cpp @@ -0,0 +1,2529 @@ +/* + * $Logfile: /Freespace2/code/ControlConfig/ControlsConfig.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for keyboard, joystick and mouse configuration + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 13 10/14/99 2:50p Jefff + * localization fixes + * + * 12 8/16/99 9:50a Jefff + * fixed loading of tab bitmaps + * + * 11 8/11/99 3:21p Jefff + * added tab highlights on conflict + * + * 10 7/26/99 5:25p Jefff + * removed invalidation of key binding for demo builds + * + * 9 7/19/99 2:13p Dave + * Added some new strings for Heiko. + * + * 8 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 7 6/19/99 2:46p Dave + * New control config screen. + * + * 6 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 5 1/15/99 11:29a Neilk + * Fixed D3D screen/texture pixel formatting problem. + * + * 4 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 61 8/09/98 11:55a Lawrance + * if GRAVIS_OEM is defined, map the throttle axis by default + * + * 60 6/19/98 3:51p Lawrance + * localize control text + * + * 59 6/17/98 11:04a Lawrance + * localize the control config strings + * + * 58 6/13/98 5:19p Hoffoss + * externalized control config texts. + * + * 57 6/09/98 5:15p Lawrance + * French/German localization + * + * 56 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 55 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 54 5/26/98 11:10a Lawrance + * Fix bug where window controls get disabled when F1 pressed twice + * + * 53 5/20/98 10:35p Hoffoss + * Fixed bug with mouse buttons not working when action isn't continuous. + * + * 52 5/19/98 4:08p Allender + * kill default binding for Z axis + * + * 51 5/19/98 12:56p Hoffoss + * Added some code to help prevent triple-clicking for binding the mouse + * button to an action (why the hell people triple click is beyond me, + * though). + * + * 50 5/19/98 11:11a Lawrance + * Ensure X and Y axis have defaults! + * + * 49 5/18/98 4:53p Hoffoss + * Some force feedback tweaks and pilot initializations there should have + * been happening, but weren't, and not are! + * + * 48 5/18/98 10:15a Lawrance + * Only do hud squad msg key check when necessary + * + * 47 5/18/98 10:08a Lawrance + * deal with overlap between hud squad msg number keys and CC_CONTINUOUS + * keys + * + * 46 5/17/98 5:44p Hoffoss + * Made throttle never bound by default (ask Sandeep why if interested). + * + * 45 5/14/98 5:32p Hoffoss + * Improved axis binding code some more. + * + * 44 5/13/98 7:15p Hoffoss + * Fixed remaining bugs with axis binding. + * + * 43 5/13/98 1:17a Hoffoss + * Added joystick axes configurability. + * + * 42 5/12/98 3:49p Hoffoss + * Fixed bug where double mouse click would bind mouse button right away. + * + * 41 5/11/98 5:43p Hoffoss + * Made num lock not bindable. + * + * 40 5/11/98 5:29p Hoffoss + * Added mouse button mapped to joystick button support. + * + * 39 5/07/98 6:25p Dave + * Fix strange boundary conditions which arise when players die/respawn + * while the game is being ended. Spiff up the chatbox doskey thing a bit. + * + * 38 5/05/98 1:48a Lawrance + * Add in missing help overlays + * + * 37 4/27/98 10:11a Lawrance + * Add in disabled beep for missing buttons + * + * 36 4/25/98 2:59p Hoffoss + * Fixed typo that was causing a bug. + * + * 35 4/22/98 1:51a Lawrance + * Take out multiplayer key from demo key config + * + * 34 4/16/98 4:29p Hoffoss + * Fixed bank_when_pressed functionality when using alt or shift for it. + * + * 33 4/15/98 11:06a Lawrance + * fix bug with a multi key showing up in demo, remove obsolete bindings + * from demo and full version + * + * 32 4/14/98 2:45p Hoffoss + * Made hitting escape to exit screen not play failed sound. + * + * 31 4/14/98 2:27p Hoffoss + * Made certain actions be hidden in demo build. + * + * 30 4/13/98 2:38p Hoffoss + * Added a tooltip handler and make binding attempts with illegal keys + * show a popup. + * + * 29 4/11/98 7:59p Lawrance + * Add support for help overlays + * + * 28 4/09/98 4:12p Hoffoss + * Changed check_control() to automatically register a control as used if + * it detects it being used. + * + * 27 4/08/98 11:11a Hoffoss + * Fixed some bugs that showed up due to fixing other bugs the other day + * with controls. + * + * 26 4/07/98 3:47p Hoffoss + * Fixed continuous controls checking with respect to modifiers. + * + * 25 4/06/98 11:17a Hoffoss + * Fixed num lock/pause interplay bug. + * + * 24 4/03/98 3:51p Hoffoss + * Fixed some bugs, and made changed Interplay requested regarding search + * mode. + * + * 23 3/31/98 4:12p Hoffoss + * Made control used status clear at mission init time. + * + * 22 3/23/98 11:28a Hoffoss + * Fixed flashing question mark bug. + * + * 21 3/21/98 11:30a John + * Fixed bug where joymouse caused you to stay in binding mode when + * binding joystick button 1 to something. + * + * 20 3/20/98 3:37p Hoffoss + * Tried to fix mitri's bug, failed miserably. + * + * 19 3/19/98 5:04p Dave + * Put in support for targeted multiplayer text and voice messaging (all, + * friendly, hostile, individual). + * + * 18 3/18/98 12:03p John + * Marked all the new strings as externalized or not. + * + * 17 3/18/98 10:16a Hoffoss + * Fixed warning. + * + * 16 3/17/98 11:15a Hoffoss + * Made question mark that appears when you are in bind mode flash. + * + * 15 3/17/98 10:48a Hoffoss + * Allowed a special hack for "bank while pressed" action to use alt and + * shift keys standalone. + * + * 14 3/12/98 3:22p Hoffoss + * Fixed 2 bugs with one solution! Yay! Failed sound on bind fixed and + * pad enter now not translated to enter. + * + * 13 3/11/98 5:28p Hoffoss + * Added control config debug display info to possibly aid in tracking + * down a bug. + * + * 12 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 11 2/22/98 12:19p John + * Externalized some strings + * + * 10 2/20/98 3:39p Hoffoss + * Updated code for new control config screen artwork. + * + * 9 2/09/98 2:50p Hoffoss + * Made 'none' show up as gray instead of normal color, to distinguish it + * from actions with bound keys. + * + * 8 2/07/98 10:04p Hoffoss + * Changed color and placement of "more" indicator. + * + * 7 2/05/98 10:42a Hoffoss + * Fixed bug where while in bind mode, you could change the line selected + * using the mouse, and binding would work on the new line instead. + * + * 6 2/03/98 5:05p Hoffoss + * Added "clear other" button to clear all conflicting bindings with + * selected action. + * + * 5 1/22/98 4:53p Hoffoss + * Made training messages/directives display a joystick button in place of + * a keypress if there is no keypress bound to the action. + * + * 4 1/20/98 4:20p Hoffoss + * Removed confusing behavior of clear button clearing out the other + * binding in a conflict. + * + * 3 1/08/98 12:11p Hoffoss + * Changed Rudder axis to Roll axis, added new function we can use to + * check what joystick axes are valid with. + * + * 2 12/24/97 3:37p Hoffoss + * Moved control config stuff to seperate library to Fred can access it as + * well. + * + * 1 12/24/97 11:58a Hoffoss + * + * 98 12/22/97 2:15p Hoffoss + * Fixed bug where joystick axis lines weren't being displayed. + * + * 97 12/16/97 2:44p Hoffoss + * Added clear button to control config screen. + * + * 96 12/12/97 3:07p Hoffoss + * Changed how deleting bindings work. Each control of an action can be + * deleted independently or both at once. + * + * 95 12/07/97 2:36p John + * Made warp out be Alt+J instead of J + * + * 94 12/03/97 4:59p Hoffoss + * Added reset sound and change control config sounds around. + * + * 93 12/03/97 4:16p Hoffoss + * Changed sound stuff used in interface screens for interface purposes. + * + * $NoKeywords: $ + * +*/ + +#include "freespace.h" +#include "controlsconfig.h" +#include "gamesequence.h" +#include "player.h" +#include "2d.h" +#include "hudsquadmsg.h" +#include "key.h" +#include "timer.h" +#include "math.h" +#include "mouse.h" +#include "ui.h" +#include "joy.h" +#include "bmpman.h" +#include "sound.h" +#include "gamesnd.h" +#include "missionscreencommon.h" +#include "font.h" +#include "hud.h" +#include "managepilot.h" +#include "multi_pmsg.h" +#include "contexthelp.h" +#include "popup.h" +#include "uidefs.h" +#include "multiutil.h" +#include "alphacolors.h" + +#define NUM_SYSTEM_KEYS 14 +#define NUM_BUTTONS 19 +#define NUM_TABS 4 + +// coordinate indicies +#define CONTROL_X_COORD 0 +#define CONTROL_Y_COORD 1 +#define CONTROL_W_COORD 2 +#define CONTROL_H_COORD 3 + +char* Conflict_background_bitmap_fname[GR_NUM_RESOLUTIONS] = { + "ControlConfig", // GR_640 + "2_ControlConfig" // GR_1024 +}; + +char* Conflict_background_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = { + "ControlConfig-m", // GR_640 + "2_ControlConfig-m" // GR_1024 +}; + +// control list area +int Control_list_coords[GR_NUM_RESOLUTIONS][4] = { + { + 32, 58, 198, 259 // GR_640 + }, + { + 32, 94, 904, 424 // GR_1024 + } +}; + +// width of the control name section of the list +int Control_list_ctrl_w[GR_NUM_RESOLUTIONS] = { + 350, // GR_640 + 600 // GR_1024 +}; + +// x start position of the binding area section of the list +int Control_list_key_x[GR_NUM_RESOLUTIONS] = { + 397, // GR_640 + 712 // GR_1024 +}; + +// width of the binding area section of the list +int Control_list_key_w[GR_NUM_RESOLUTIONS] = { + 198, // GR_640 + 230 // GR_1024 +}; + +// display the "more..." text under the control list +int Control_more_coords[GR_NUM_RESOLUTIONS][2] = { + { + 320, 326 // GR_640 + }, + { + 500, 542 // GR_1024 + } +}; + +// area to display "conflicts with..." text +int Conflict_wnd_coords[GR_NUM_RESOLUTIONS][4] = { + { + 32, 313, 250, 32 // GR_640 + }, + { + 48, 508, 354, 46 // GR_1024 + } +}; + +// conflict warning anim coords +int Conflict_warning_coords[GR_NUM_RESOLUTIONS][2] = { + { + -1, 420 // GR_640 + }, + { + -1, 669 // GR_1024 + } +}; + +// for flashing the conflict text +#define CONFLICT_FLASH_TIME 250 +int Conflict_stamp = -1; +int Conflict_bright = 0; + +#define LIST_BUTTONS_MAX 40 +#define JOY_AXIS 0x80000 + +static int Num_cc_lines; +static struct { + char *label; + int cc_index; // index into Control_config of item + int y; // Y coordinate of line + int kx, kw, jx, jw; // x start and width of keyboard and joystick bound text +} Cc_lines[CCFG_MAX]; + +// struct to hold backup config_item elements so we can undo them +struct config_item_undo { + int size; + int *index; // array (size) of Control_config indices of replaced elements + config_item *list; // array (size) of original elements + config_item_undo *next; +}; + +config_item Control_config_backup[CCFG_MAX]; + +#ifdef GRAVIS_OEM +int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 }; +int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, JOY_Z_AXIS, -1 }; +#else +int Axis_map_to[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 }; +int Axis_map_to_defaults[] = { JOY_X_AXIS, JOY_Y_AXIS, JOY_RX_AXIS, -1, -1 }; +#endif + +// all this stuff is localized/externalized +#define NUM_AXIS_TEXT 6 +#define NUM_MOUSE_TEXT 5 +#define NUM_MOUSE_AXIS_TEXT 2 +#define NUM_INVERT_TEXT 2 +char *Joy_axis_action_text[NUM_JOY_AXIS_ACTIONS]; +char *Joy_axis_text[NUM_AXIS_TEXT]; +char *Mouse_button_text[NUM_MOUSE_TEXT]; +char *Mouse_axis_text[NUM_MOUSE_AXIS_TEXT]; +char *Invert_text[NUM_INVERT_TEXT]; + +ubyte System_keys[NUM_SYSTEM_KEYS] = { + KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, + KEY_F11, KEY_F12, KEY_PRINT_SCRN +}; + +int Control_check_count = 0; + +static int Tab; // which tab we are currently in +static int Binding_mode = 0; // are we waiting for a key to bind it? +static int Bind_time = 0; +static int Search_mode = 0; // are we waiting for a key to bind it? +static int Last_key = -1; +static int Selected_line = 0; // line that is currently selected for binding +static int Selected_item = -1; // -1 = none, 0 = key, 1 = button +static int Scroll_offset; +static int Axis_override = -1; +static int Background_bitmap; +static int Conflicts_tabs[NUM_TABS]; +static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list +static UI_WINDOW Ui_window; + +static struct { + int key; // index of other control in conflict with this one + int joy; // index of other control in conflict with this one +} Conflicts[CCFG_MAX]; + +int Conflicts_axes[NUM_JOY_AXIS_ACTIONS]; + +#define TARGET_TAB 0 +#define SHIP_TAB 1 +#define WEAPON_TAB 2 +#define COMPUTER_TAB 3 +#define SCROLL_UP_BUTTON 4 +#define SCROLL_DOWN_BUTTON 5 +#define ALT_TOGGLE 6 +#define SHIFT_TOGGLE 7 +#define INVERT_AXIS 8 +#define CANCEL_BUTTON 9 +#define UNDO_BUTTON 10 +#define RESET_BUTTON 11 +#define SEARCH_MODE 12 +#define BIND_BUTTON 13 +#define HELP_BUTTON 14 +#define ACCEPT_BUTTON 15 +#define CLEAR_OTHER_BUTTON 16 +#define CLEAR_ALL_BUTTON 17 +#define CLEAR_BUTTON 18 + +ui_button_info CC_Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { + { // GR_640 + ui_button_info("CCB_00", 32, 348, 17, 384, 0), // target tab + ui_button_info("CCB_01", 101, 348, 103, 384, 1), // ship tab + ui_button_info("CCB_02", 173, 352, 154, 384, 2), // weapon tab + ui_button_info("CCB_03", 242, 347, 244, 384, 3), // computer/misc tab + ui_button_info("CCB_04", 614, 73, -1, -1, 4), // scroll up + ui_button_info("CCB_05", 614, 296, -1, -1, 5), // scroll down + ui_button_info("CCB_06", 17, 452, 12, 440, 6), // alt toggle + ui_button_info("CCB_07", 56, 452, 50, 440, 7), // shift toggle + ui_button_info("CCB_09", 162, 452, 155, 440, 9), // invert + ui_button_info("CCB_10", 404, 1, 397, 45, 10), // cancel + ui_button_info("CCB_11", 582, 347, 586, 386, 11), // undo + ui_button_info("CCB_12", 576, 1, 578, 45, 12), // default + ui_button_info("CCB_13", 457, 4, 453, 45, 13), // search + ui_button_info("CCB_14", 516, 4, 519, 45, 14), // bind + ui_button_info("CCB_15", 540, 428, 500, 440, 15), // help + ui_button_info("CCB_16", 574, 432, 571, 412, 16), // accept + ui_button_info("CCB_18", 420, 346, 417, 386, 18), // clear other + ui_button_info("CCB_19", 476, 346, 474, 386, 19), // clear all + ui_button_info("CCB_20", 524, 346, 529, 386, 20), // clear button + }, + { // GR_1024 + ui_button_info("2_CCB_00", 51, 557, 27, 615, 0), // target tab + ui_button_info("2_CCB_01", 162, 557, 166, 615, 1), // ship tab + ui_button_info("2_CCB_02", 277, 563, 246, 615, 2), // weapon tab + ui_button_info("2_CCB_03", 388, 555, 391, 615, 3), // computer/misc tab + ui_button_info("2_CCB_04", 982, 117, -1, -1, 4), // scroll up + ui_button_info("2_CCB_05", 982, 474, -1, -1, 5), // scroll down + ui_button_info("2_CCB_06", 28, 723, 24, 704, 6), // alt toggle + ui_button_info("2_CCB_07", 89, 723, 80, 704, 7), // shift toggle + ui_button_info("2_CCB_09", 260, 723, 249, 704, 9), // invert + ui_button_info("2_CCB_10", 646, 2, 635, 71, 10), // cancel + ui_button_info("2_CCB_11", 932, 555, 938, 619, 11), // undo + ui_button_info("2_CCB_12", 921, 1, 923, 71, 12), // default + ui_button_info("2_CCB_13", 732, 6, 726, 71, 13), // search + ui_button_info("2_CCB_14", 825, 6, 831, 71, 14), // bind + ui_button_info("2_CCB_15", 864, 685, 800, 704, 15), // help + ui_button_info("2_CCB_16", 919, 692, 914, 660, 16), // accept + ui_button_info("2_CCB_18", 672, 553, 668, 619, 18), // clear other + ui_button_info("2_CCB_19", 761, 553, 749, 619, 19), // clear all + ui_button_info("2_CCB_20", 838, 553, 846, 619, 20), // clear button + } +}; + +// strings +#define CC_NUM_TEXT 20 +UI_XSTR CC_text[GR_NUM_RESOLUTIONS][CC_NUM_TEXT] = { + { // GR_640 + { "Targeting", 1340, 17, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][TARGET_TAB].button }, + { "Ship", 1341, 103, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIP_TAB].button }, + { "Weapons", 1065, 154, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][WEAPON_TAB].button }, + { "Misc", 1411, 244, 384, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][COMPUTER_TAB].button }, + { "Alt", 1510, 12, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][ALT_TOGGLE].button }, + { "Shift", 1511, 50, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SHIFT_TOGGLE].button }, + { "Invert", 1342, 155, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][INVERT_AXIS].button }, + { "Cancel", 641, 397, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CANCEL_BUTTON].button }, + { "Undo", 1343, 586, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][UNDO_BUTTON].button }, + { "Defaults", 1344, 568, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][RESET_BUTTON].button }, + { "Search", 1345, 453, 45, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][SEARCH_MODE].button }, + { "Bind", 1346, 519, 45, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][BIND_BUTTON].button }, + { "Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][HELP_BUTTON].button }, + { "Accept", 1035, 571, 412, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][ACCEPT_BUTTON].button }, + { "Clear", 1347, 417, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button }, + { "Conflict", 1348, 406, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_OTHER_BUTTON].button }, + { "Clear", 1413, 474, 386, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button }, + { "All", 1349, 483, 396, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[0][CLEAR_ALL_BUTTON].button }, + { "Clear", 1414, 529, 388, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button }, + { "Selected", 1350, 517, 396, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[0][CLEAR_BUTTON].button }, + }, + { // GR_1024 + { "Targeting", 1340, 47, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][TARGET_TAB].button }, + { "Ship", 1341, 176, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIP_TAB].button }, + { "Weapons", 1065, 266, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][WEAPON_TAB].button }, + { "Misc", 1411, 401, 615, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][COMPUTER_TAB].button }, + { "Alt", 1510, 29, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][ALT_TOGGLE].button }, + { "Shift", 1511, 85, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SHIFT_TOGGLE].button }, + { "Invert", 1342, 254, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][INVERT_AXIS].button }, + { "Cancel", 641, 655, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CANCEL_BUTTON].button }, + { "Undo", 1343, 938, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][UNDO_BUTTON].button }, + { "Defaults", 1344, 923, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][RESET_BUTTON].button }, + { "Search", 1345, 746, 71, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][SEARCH_MODE].button }, + { "Bind", 1346, 846, 71, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][BIND_BUTTON].button }, + { "Help", 928, 800, 704, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][HELP_BUTTON].button }, + { "Accept", 1035, 914, 660, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][ACCEPT_BUTTON].button }, + { "Clear", 1347, 683, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button }, + { "Conflict", 1348, 666, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_OTHER_BUTTON].button }, + { "Clear", 1413, 759, 619, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button }, + { "All", 1349, 772, 634, UI_XSTR_COLOR_GREEN, -1, &CC_Buttons[1][CLEAR_ALL_BUTTON].button }, + { "Clear", 1414, 871, 619, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button }, + { "Selected", 1350, 852, 634, UI_XSTR_COLOR_PINK, -1, &CC_Buttons[1][CLEAR_BUTTON].button }, + } +}; + +// linked list head of undo items +config_item_undo *Config_item_undo; + +// same indices as Scan_code_text[]. Indicates if a scancode is allowed to be bound. +int Config_allowed[] = { + 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 0, 1, 0, 1, 0, 1, + 1, 1, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + + +/* +// old invalid demo keys +#define INVALID_DEMO_KEYS_MAX 14 +int Invalid_demo_keys[] = { + INCREASE_SHIELD, + DECREASE_SHIELD, + SHIELD_EQUALIZE, + SHIELD_XFER_TOP, + SHIELD_XFER_BOTTOM, + SHIELD_XFER_LEFT, + SHIELD_XFER_RIGHT, + XFER_SHIELD, + XFER_LASER, + MULTI_MESSAGE_ALL, + MULTI_MESSAGE_FRIENDLY, + MULTI_MESSAGE_HOSTILE, + MULTI_MESSAGE_TARGET, + MULTI_OBSERVER_ZOOM_TO +}; +*/ +#define INVALID_DEMO_KEYS_MAX 0 +int Invalid_demo_keys[INVALID_DEMO_KEYS_MAX+1]; // +1 is only to prevent a 0-size array; + + +#ifndef NDEBUG +int Show_controls_info = 0; + +DCF_BOOL(show_controls_info, Show_controls_info) +#endif + +static int Axes_origin[JOY_NUM_AXES]; + +void control_config_detect_axis_reset() +{ + joystick_read_raw_axis(JOY_NUM_AXES, Axes_origin); +} + +int control_config_detect_axis() +{ + int i, d, axis = -1, delta = 16384; + int axes_values[JOY_NUM_AXES]; + + joystick_read_raw_axis(JOY_NUM_AXES, axes_values); + for (i=0; i delta) { + axis = i; + delta = d; + } + } + + return axis; +} + +int control_config_valid_action(int n) +{ +#ifdef FS2_DEMO + int i; + + for (i=0; i= 0) { + c = 0; + a = Control_config[i].key_id; + b = Control_config[j].key_id; + if (a == b) { + Conflicts[i].key = j; + Conflicts[j].key = i; + Conflicts_tabs[ Control_config[i].tab ] = 1; + Conflicts_tabs[ Control_config[j].tab ] = 1; + } + + /* if ((a >= 0) && (a & KEY_SHIFTED) && (shift >= 0)) { + Conflicts[i].key = shift; + Conflicts[shift].key = i; + Conflicts_tabs[ Control_config[i].tab ] = 1; + Conflicts_tabs[ Control_config[shift].tab ] = 1; + } + + if ((b >= 0) && (b & KEY_SHIFTED) && (shift >= 0)) { + Conflicts[j].key = shift; + Conflicts[shift].key = j; + Conflicts_tabs[ Control_config[j].tab ] = 1; + Conflicts_tabs[ Control_config[shift].tab ] = 1; + } + + if ((a >= 0) && (a & KEY_ALTED) && (alt >= 0)) { + Conflicts[i].key = alt; + Conflicts[alt].key = i; + Conflicts_tabs[ Control_config[i].tab ] = 1; + Conflicts_tabs[ Control_config[alt].tab ] = 1; + } + + if ((b >= 0) && (b & KEY_ALTED) && (alt >= 0)) { + Conflicts[j].key = alt; + Conflicts[alt].key = j; + Conflicts_tabs[ Control_config[j].tab ] = 1; + Conflicts_tabs[ Control_config[alt].tab ] = 1; + }*/ + } + + if ((Control_config[i].joy_id >= 0) && (Control_config[i].joy_id == Control_config[j].joy_id)) { + Conflicts[i].joy = j; + Conflicts[j].joy = i; + Conflicts_tabs[ Control_config[i].tab ] = 1; + Conflicts_tabs[ Control_config[j].tab ] = 1; + } + } + } + } + } + + for (i=0; i= 0) && (Axis_map_to[i] == Axis_map_to[j])) { + Conflicts_axes[i] = j; + Conflicts_axes[j] = i; + Conflicts_tabs[SHIP_TAB] = 1; + } + } + } +} + +// do list setup required prior to rendering and checking for the controls listing. Called when list changes +void control_config_list_prepare() +{ + int j, k, y, z; + int font_height = gr_get_font_height(); + + Num_cc_lines = y = z = 0; + while (z < CCFG_MAX) { + if ((Control_config[z].tab == Tab) && control_config_valid_action(z)) { + k = Control_config[z].key_id; + j = Control_config[z].joy_id; + Cc_lines[Num_cc_lines].label = XSTR(Control_config[z].text, CONTROL_CONFIG_XSTR + z); + Cc_lines[Num_cc_lines].cc_index = z; + Cc_lines[Num_cc_lines++].y = y; + y += font_height + 2; + } + + z++; + } + + if (Tab == SHIP_TAB) { + for (j=0; j= Num_cc_lines)) + return 0; + + y = Cc_lines[n].y - Cc_lines[Scroll_offset].y; + if ((y < 0) || (y + gr_get_font_height() > Control_list_coords[gr_screen.res][CONTROL_H_COORD])){ + return 0; + } + + return 1; +} + +// allocates the required space for one undo block and put it in the beginning of the linked list (top of a stack). +// Returns a pointer to this newly allocated block +config_item_undo *get_undo_block(int size) +{ + config_item_undo *ptr; + + ptr = (config_item_undo *) malloc( sizeof(config_item_undo) ); + Assert(ptr); + ptr->next = Config_item_undo; + Config_item_undo = ptr; + + ptr->size = size; + if (size) { + ptr->index = (int *) malloc( sizeof(int) * size ); + Assert(ptr->index); + ptr->list = (config_item *) malloc( sizeof(config_item) * size ); + Assert(ptr->list); + + } else { + ptr->index = NULL; + ptr->list = NULL; + } + + return ptr; +} + +// frees one undo block. The first one in the list (top of the stack) to be precise. +void free_undo_block() +{ + config_item_undo *ptr; + + ptr = Config_item_undo; + if (!ptr) + return; + + Config_item_undo = ptr->next; + if (ptr->size) { + free(ptr->list); + free(ptr->index); + } + + free(ptr); +} + +// undo the most recent binding changes +int control_config_undo_last() +{ + int i, z, tab; + + if (!Config_item_undo) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return -1; + } + + if (Config_item_undo->index[0] & JOY_AXIS) + tab = SHIP_TAB; + else + tab = Control_config[Config_item_undo->index[0]].tab; + + for (i=1; isize; i++) { + if (Config_item_undo->index[i] & JOY_AXIS) { + if (tab != SHIP_TAB) + tab = -1; + + } else { + if (Control_config[Config_item_undo->index[i]].tab != tab) + tab = -1; + } + } + + if (tab >= 0) + Tab = tab; + + for (i=0; isize; i++) { + z = Config_item_undo->index[i]; + if (z & JOY_AXIS) { + config_item *ptr; + + z &= ~JOY_AXIS; + ptr = &Config_item_undo->list[i]; + Axis_map_to[z] = ptr->joy_id; + Invert_axis[z] = ptr->used; + + } else { + Control_config[z] = Config_item_undo->list[i]; + } + } + + free_undo_block(); + control_config_conflict_check(); + control_config_list_prepare(); + gamesnd_play_iface(SND_USER_SELECT); + return 0; +} + +void control_config_save_axis_undo(int axis) +{ + config_item_undo *ptr; + config_item item; + + item.joy_id = (short) Axis_map_to[axis]; + item.used = 0; + item.used = Invert_axis[axis]; + + ptr = get_undo_block(1); + ptr->index[0] = axis | JOY_AXIS; + ptr->list[0] = item; +} + +void control_config_bind_key(int i, int key) +{ + config_item_undo *ptr; + + ptr = get_undo_block(1); + ptr->index[0] = i; + ptr->list[0] = Control_config[i]; + Control_config[i].key_id = (short) key; +} + +void control_config_bind_joy(int i, int joy) +{ + config_item_undo *ptr; + + ptr = get_undo_block(1); + ptr->index[0] = i; + ptr->list[0] = Control_config[i]; + Control_config[i].joy_id = (short) joy; +} + +void control_config_bind_axis(int i, int axis) +{ + control_config_save_axis_undo(i); + Axis_map_to[i] = axis; +} + +int control_config_remove_binding() +{ + int z, j, k; + config_item_undo *ptr; + + if (Selected_line < 0) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return -1; + } + + z = Cc_lines[Selected_line].cc_index; + if (z & JOY_AXIS) { + z &= ~JOY_AXIS; + if (Axis_map_to[z] < 0) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return -1; + } + + control_config_save_axis_undo(z); + Axis_map_to[z] = -1; + control_config_conflict_check(); + control_config_list_prepare(); + gamesnd_play_iface(SND_USER_SELECT); + Selected_item = -1; + return 0; + } + + if ((Control_config[z].joy_id < 0) && (Control_config[z].key_id < 0)) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return -1; + } + + j = k = -1; + ptr = get_undo_block(1); + ptr->index[0] = z; + ptr->list[0] = Control_config[z]; + + if (Selected_item && (Control_config[z].joy_id >= 0)) // if not just key selected (which would be 0) + Control_config[z].joy_id = (short) -1; + + if ((Selected_item != 1) && (Control_config[z].key_id >= 0)) // if not just joy button selected (1) + Control_config[z].key_id = (short) -1; + + control_config_conflict_check(); + control_config_list_prepare(); + gamesnd_play_iface(SND_USER_SELECT); + Selected_item = -1; + return 0; +} + +int control_config_clear_other() +{ + int z, i, j, total = 0; + config_item_undo *ptr; + + if (Selected_line < 0) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return -1; + } + + z = Cc_lines[Selected_line].cc_index; + if (z & JOY_AXIS) { + config_item item; + + z &= ~JOY_AXIS; + if (Axis_map_to[z] < 0) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return -1; + } + + for (i=0; iindex[j] = i | JOY_AXIS; + ptr->list[j] = item; + j++; + + Axis_map_to[i] = -1; + } + + control_config_conflict_check(); + control_config_list_prepare(); + gamesnd_play_iface(SND_USER_SELECT); + return 0; + } + + for (i=0; iindex[j] = i; + ptr->list[j] = Control_config[i]; + j++; + + if (Control_config[i].key_id == Control_config[z].key_id) + Control_config[i].key_id = (short) -1; + if (Control_config[i].joy_id == Control_config[z].joy_id) + Control_config[i].joy_id = (short) -1; + } + + control_config_conflict_check(); + control_config_list_prepare(); + gamesnd_play_iface(SND_USER_SELECT); + return 0; +} + +int control_config_clear_all() +{ + int i, j, total = 0; + config_item_undo *ptr; + + // first, determine how many bindings need to be changed + for (i=0; i= 0) || (Control_config[i].joy_id >= 0)) + total++; + + if (!total) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return -1; + } + + // now, back up the old bindings so we can undo if we want to + ptr = get_undo_block(total); + for (i=j=0; i= 0) || (Control_config[i].joy_id >= 0)) { + ptr->index[j] = i; + ptr->list[j] = Control_config[i]; + j++; + } + } + + Assert(j == total); + for (i=0; i= 0); + + if ( axis > 1 ) { + if (Axis_map_to_defaults[axis] < 0) + return -1; + + if (!joystick.axis_valid[Axis_map_to_defaults[axis]]) + return -1; + } + + return Axis_map_to_defaults[axis]; +} + +int control_config_do_reset() +{ + int i, j, total = 0; + config_item_undo *ptr; + config_item item; + + // first, determine how many bindings need to be changed + for (i=0; iindex[j] = i; + ptr->list[j] = Control_config[i]; + j++; + } + } + + for (i=0; iindex[j] = i | JOY_AXIS; + ptr->list[j] = item; + j++; + } + + Assert(j == total); + control_config_reset_defaults(); + control_config_conflict_check(); + control_config_list_prepare(); + gamesnd_play_iface(SND_RESET_PRESSED); + return 0; +} + +// This sets all the controls to their default values +void control_config_reset_defaults() +{ + int i; + + // Reset keyboard defaults + for (i=0; i Scroll_offset); + while (!cc_line_query_visible(Selected_line)) + Selected_line--; + + Selected_item = -1; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void control_config_scroll_line_up() +{ + if (Selected_line) { + Selected_line--; + if (Selected_line < Scroll_offset) + Scroll_offset = Selected_line; + + Selected_item = -1; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void control_config_scroll_screen_down() +{ + if (Cc_lines[Num_cc_lines - 1].y + gr_get_font_height() > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) { + Scroll_offset++; + while (!cc_line_query_visible(Selected_line)) { + Selected_line++; + Assert(Selected_line < Num_cc_lines); + } + + Selected_item = -1; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void control_config_scroll_line_down() +{ + if (Selected_line < Num_cc_lines - 1) { + Selected_line++; + Assert(Selected_line > Scroll_offset); + while (!cc_line_query_visible(Selected_line)) + Scroll_offset++; + + Selected_item = -1; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void control_config_toggle_modifier(int bit) +{ + int k, z; + + z = Cc_lines[Selected_line].cc_index; + Assert(!(z & JOY_AXIS)); + k = Control_config[z].key_id; + if (k < 0) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + control_config_bind_key(z, k ^ bit); + control_config_conflict_check(); + gamesnd_play_iface(SND_USER_SELECT); +} + +void control_config_toggle_invert() +{ + int z; + + z = Cc_lines[Selected_line].cc_index; + Assert(z & JOY_AXIS); + z &= ~JOY_AXIS; + control_config_save_axis_undo(z); + Invert_axis[z] = !Invert_axis[z]; +} + +void control_config_do_bind() +{ + int i; + + game_flush(); +// if ((Selected_line < 0) || (Cc_lines[Selected_line].cc_index & JOY_AXIS)) { + if (Selected_line < 0) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + for (i=0; ihotspot < 0) { // temporary + b->button.create(&Ui_window, NOX("Clear other"), b->x, b->y, 150, 30, 0, 1); // temporary + b->button.set_highlight_action(common_play_highlight_sound); + continue; + } + + b->button.create(&Ui_window, "", b->x, b->y, 60, 30, ((i == SCROLL_UP_BUTTON) || (i == SCROLL_DOWN_BUTTON)), 1); + + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + if (i<4) { + b->button.set_bmaps(b->filename, 5, 1); // a bit of a hack here, but buttons 0-3 need 4 frames loaded + } else { + b->button.set_bmaps(b->filename); + } + b->button.link_hotspot(b->hotspot); + } + + // create all text + for(i=0; i= 0) { + Axis_override = i; + bind = 1; + } + + k = game_poll(); + Ui_window.use_hack_to_get_around_stupid_problem_flag = 1; + Ui_window.process(0); + + if (k == KEY_ESC) { + strcpy(bound_string, XSTR( "Canceled", 206)); + bound_timestamp = timestamp(2500); + control_config_do_cancel(); + + } else { + if (k == KEY_ENTER) + bind = 1; + + for (i=0; i= 0) { + control_config_bind_axis(z, Axis_override); + strcpy(bound_string, Joy_axis_text[Axis_override]); + gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]); + bound_timestamp = timestamp(2500); + control_config_conflict_check(); + control_config_list_prepare(); + control_config_do_cancel(); + + } else { + control_config_do_cancel(1); + } + } + } + + } else { + if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) { + CC_Buttons[gr_screen.res][HELP_BUTTON].button.reset_status(); + Ui_window.set_ignore_gadgets(1); + } + + k = game_poll(); + Ui_window.use_hack_to_get_around_stupid_problem_flag = 1; + Ui_window.process(0); + + if ( (k > 0) || B1_JUST_RELEASED ) { + if (help_overlay_active(CONTROL_CONFIG_OVERLAY)) { + help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0); + Ui_window.set_ignore_gadgets(0); + k = 0; + } + } + + if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) { + Ui_window.set_ignore_gadgets(0); + } + + if (k == KEY_ESC) { + strcpy(bound_string, XSTR( "Canceled", 206)); + bound_timestamp = timestamp(2500); + control_config_do_cancel(); + + } else { + switch (k & KEY_MASK) { + case KEY_LSHIFT: + case KEY_RSHIFT: + case KEY_LALT: + case KEY_RALT: + Last_key = k & KEY_MASK; + k = 0; + break; + } + + if (Cc_lines[Selected_line].cc_index == BANK_WHEN_PRESSED) // a special hack just for Mike K. + if ( (Last_key >= 0) && (k <= 0) && !keyd_pressed[Last_key] ) + k = Last_key; + + if ((k > 0) && !Config_allowed[k & KEY_MASK]) { + popup(0, 1, POPUP_OK, XSTR( "That is a non-bindable key. Please try again.", 207)); + k = 0; + } + + k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED); + if (k > 0) { + z = Cc_lines[Selected_line].cc_index; + Assert(!(z & JOY_AXIS)); + control_config_bind_key(z, k); + + strcpy(bound_string, textify_scancode(k)); + gr_force_fit_string(bound_string, 39, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]); + bound_timestamp = timestamp(2500); + control_config_conflict_check(); + control_config_list_prepare(); + control_config_do_cancel(); + } + + for (i=0; i 0) || B1_JUST_RELEASED ) { + if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) { + help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0); + Ui_window.set_ignore_gadgets(0); + k = 0; + } + } + + if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) { + Ui_window.set_ignore_gadgets(0); + } + + if (k == KEY_ESC) { + control_config_do_cancel(); + + } else { + if ((k > 0) && !Config_allowed[k & KEY_MASK]) + k = 0; + + k &= (KEY_MASK | KEY_SHIFTED | KEY_ALTED); + z = -1; + if (k > 0) { + for (i=0; i= 0) { + Tab = Control_config[z].tab; + control_config_list_prepare(); + Selected_line = Scroll_offset = 0; + for (i=0; i 0) || B1_JUST_RELEASED ) { + if ( help_overlay_active(CONTROL_CONFIG_OVERLAY) ) { + help_overlay_set_state(CONTROL_CONFIG_OVERLAY, 0); + Ui_window.set_ignore_gadgets(0); + k = 0; + } + } + + if ( !help_overlay_active(CONTROL_CONFIG_OVERLAY) ) { + Ui_window.set_ignore_gadgets(0); + } + + switch (k) { + case KEY_DOWN: // select next line + control_config_scroll_line_down(); + break; + + case KEY_UP: // select previous line + control_config_scroll_line_up(); + break; + + case KEY_SHIFTED | KEY_TAB: // activate previous tab + Tab--; + if (Tab < 0) + Tab = NUM_TABS - 1; + + Scroll_offset = Selected_line = 0; + control_config_list_prepare(); + gamesnd_play_iface(SND_SCREEN_MODE_PRESSED); + break; + + case KEY_TAB: // activate next tab + Tab++; + if (Tab >= NUM_TABS) + Tab = 0; + + Scroll_offset = Selected_line = 0; + control_config_list_prepare(); + gamesnd_play_iface(SND_SCREEN_MODE_PRESSED); + break; + + case KEY_LEFT: + Selected_item--; + if (Selected_item == -2) { + Selected_item = 1; + if (Cc_lines[Selected_line].jw < 1) { + Selected_item = 0; + if (Cc_lines[Selected_line].kw < 1) + Selected_item = -1; + } + } + + gamesnd_play_iface(SND_SCROLL); + break; + + case KEY_RIGHT: + Selected_item++; + if ((Selected_item == 1) && (Cc_lines[Selected_line].jw < 1)) + Selected_item = -1; + else if (!Selected_item && (Cc_lines[Selected_line].kw < 1)) + Selected_item = -1; + else if (Selected_item > 1) + Selected_item = -1; + + gamesnd_play_iface(SND_SCROLL); + break; + + case KEY_BACKSP: // undo + control_config_undo_last(); + break; + + case KEY_ESC: + control_config_cancel_exit(); + break; + } // end switch + } + + for (i=0; i= Cc_lines[Selected_line].kx) && (x < Cc_lines[Selected_line].kx + Cc_lines[Selected_line].kw)) + Selected_item = 0; + + if ((x >= Cc_lines[Selected_line].jx) && (x < Cc_lines[Selected_line].jx + Cc_lines[Selected_line].jw)) + Selected_item = 1; + + gamesnd_play_iface(SND_USER_SELECT); + } + + if (List_buttons[i].double_clicked()) + control_config_do_bind(); + } + + GR_MAYBE_CLEAR_RES(Background_bitmap); + if (Background_bitmap >= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + // highlight tab with conflict + Ui_window.draw(); + for (i=z=0; i= 0) { + z = Cc_lines[Selected_line].cc_index; + if (z & JOY_AXIS) { + if (Invert_axis[z & ~JOY_AXIS]) { + CC_Buttons[gr_screen.res][INVERT_AXIS].button.draw_forced(2); + } + + } else { + z = Control_config[z].key_id; + if (z >= 0) { + if (z & KEY_SHIFTED) { + CC_Buttons[gr_screen.res][SHIFT_TOGGLE].button.draw_forced(2); + } + if (z & KEY_ALTED) { + CC_Buttons[gr_screen.res][ALT_TOGGLE].button.draw_forced(2); + } + } + } + } + + if (Binding_mode) { + CC_Buttons[gr_screen.res][BIND_BUTTON].button.draw_forced(2); + } + + z = Cc_lines[Selected_line].cc_index; + x = Conflict_wnd_coords[gr_screen.res][CONTROL_X_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD] / 2; + y = Conflict_wnd_coords[gr_screen.res][CONTROL_Y_COORD] + Conflict_wnd_coords[gr_screen.res][CONTROL_H_COORD] / 2; + if (Binding_mode) { + int t; + + t = (int) (timer * 3); + if (t % 2) { + gr_set_color_fast(&Color_text_normal); + gr_get_string_size(&w, NULL, XSTR( "?", 208)); + gr_printf(x - w / 2, y - font_height / 2, XSTR( "?", 208)); + } + + } else if (!(z & JOY_AXIS) && ((Conflicts[z].key >= 0) || (Conflicts[z].joy >= 0))) { + i = Conflicts[z].key; + if (i < 0) + i = Conflicts[z].joy; + + gr_set_color_fast(&Color_text_normal); + str = XSTR( "Control conflicts with:", 209); + gr_get_string_size(&w, NULL, str); + gr_printf(x - w / 2, y - font_height, str); + + strcpy(buf, XSTR(Control_config[i].text, CONTROL_CONFIG_XSTR + i)); + gr_force_fit_string(buf, 255, Conflict_wnd_coords[gr_screen.res][CONTROL_W_COORD]); + gr_get_string_size(&w, NULL, buf); + gr_printf(x - w / 2, y, buf); + + } else if (*bound_string) { + gr_set_color_fast(&Color_text_normal); + gr_get_string_size(&w, NULL, bound_string); + gr_printf(x - w / 2, y - font_height / 2, bound_string); + if (timestamp_elapsed(bound_timestamp)) + *bound_string = 0; + } + +// gr_set_color_fast(&Color_text_heading); +// gr_printf(LIST_X + 20, HEADING_Y, Heading[Tab]); + +// gr_get_string_size(&w, &h, Heading[Tab]); +// y = HEADING_Y + h / 2 - 1; +// gr_line(LIST_X, y, LIST_X + 18, y); +// gr_line(LIST_X + w + 21, y, LIST_X + LIST_W, y); + + if (Cc_lines[Num_cc_lines - 1].y + font_height > Cc_lines[Scroll_offset].y + Control_list_coords[gr_screen.res][CONTROL_H_COORD]) { + gr_set_color_fast(&Color_white); + gr_printf(Control_more_coords[gr_screen.res][CONTROL_X_COORD], Control_more_coords[gr_screen.res][CONTROL_Y_COORD], XSTR( "More...", 210)); + } + + conflict = 0; + line = Scroll_offset; + while (cc_line_query_visible(line)) { + z = Cc_lines[line].cc_index; + y = Control_list_coords[gr_screen.res][CONTROL_Y_COORD] + Cc_lines[line].y - Cc_lines[Scroll_offset].y; + + List_buttons[line - Scroll_offset].update_dimensions(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, Control_list_coords[gr_screen.res][CONTROL_W_COORD], font_height); + List_buttons[line - Scroll_offset].enable(!Binding_mode); + + Cc_lines[line].kw = Cc_lines[line].jw = 0; + + if (line == Selected_line){ + c = &Color_text_selected; + } else if (line == select_tease_line) { + c = &Color_text_subselected; + } else { + c = &Color_text_normal; + } + + gr_set_color_fast(c); + if (Cc_lines[line].label) { + strcpy(buf, Cc_lines[line].label); + gr_force_fit_string(buf, 255, Control_list_ctrl_w[gr_screen.res]); + gr_printf(Control_list_coords[gr_screen.res][CONTROL_X_COORD], y, buf); + } + + if (!(z & JOY_AXIS)) { + k = Control_config[z].key_id; + j = Control_config[z].joy_id; + x = Control_list_key_x[gr_screen.res]; + jptr = NULL; + *buf = 0; + + if ((k < 0) && (j < 0)) { + gr_set_color_fast(&Color_grey); + gr_printf(x, y, XSTR( "None", 211)); + + } else { + if (k >= 0) { + strcpy(buf, textify_scancode(k)); + if (Conflicts[z].key >= 0) { + if (c == &Color_text_normal) + gr_set_color_fast(&Color_text_error); + else { + gr_set_color_fast(&Color_text_error_hi); + conflict++; + } + + } else if (Selected_item == 1) { + gr_set_color_fast(&Color_text_normal); + + } else + gr_set_color_fast(c); + + gr_printf(x, y, buf); + + len = strlen(buf); + Cc_lines[line].kx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD]; + gr_get_string_size(&w, NULL, buf); + Cc_lines[line].kw = w; + x += w; + + if (j >= 0) { + gr_set_color_fast(&Color_text_normal); + gr_printf(x, y, XSTR( ", ", 212)); + gr_get_string_size(&w, NULL, XSTR( ", ", 212)); + x += w; + } + } + + if (j >= 0) { + strcpy(buf, Joy_button_text[j]); + if (Conflicts[z].joy >= 0) { + if (c == &Color_text_normal) + gr_set_color_fast(&Color_text_error); + else { + gr_set_color_fast(&Color_text_error_hi); + conflict++; + } + + } else if (!Selected_item) { + gr_set_color_fast(&Color_text_normal); + + } else + gr_set_color_fast(c); + + gr_force_fit_string(buf, 255, Control_list_key_w[gr_screen.res] + Control_list_key_x[gr_screen.res] - x); + gr_printf(x, y, buf); + + Cc_lines[line].jx = x - Control_list_coords[gr_screen.res][CONTROL_X_COORD]; + gr_get_string_size(&Cc_lines[line].jw, NULL, buf); + } + } + + } else { + x = Control_list_key_x[gr_screen.res]; + j = Axis_map_to[z & ~JOY_AXIS]; + if (Binding_mode && (line == Selected_line)) + j = Axis_override; + + if (j < 0) { + gr_set_color_fast(&Color_grey); + gr_printf(x, y, XSTR( "None", 211)); + + } else { + if (Conflicts_axes[z & ~JOY_AXIS] >= 0) { + if (c == &Color_text_normal) + gr_set_color_fast(&Color_text_error); + + else { + gr_set_color_fast(&Color_text_error_hi); + conflict++; + } + + } else if (!Selected_item) { + gr_set_color_fast(&Color_text_normal); + + } else + gr_set_color_fast(c); + + gr_string(x, y, Joy_axis_text[j]); + } + } + + line++; + } + + CC_Buttons[gr_screen.res][CLEAR_OTHER_BUTTON].button.enable(conflict); + + i = line - Scroll_offset; + while (i < LIST_BUTTONS_MAX) + List_buttons[i++].disable(); + + // blit help overlay if active + help_overlay_maybe_blit(CONTROL_CONFIG_OVERLAY); + + gr_flip(); +} + +void clear_key_binding(short key) +{ + int i; + + for (i=0; i= 0) && (Control_config[id].joy_id < MOUSE_NUM_BUTTONS)) + if (mouse_down(1 << Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) { + control_used(id); + return 1; + } + + // check what current modifiers are pressed + mask = 0; + if (keyd_pressed[KEY_LSHIFT] || key_down_count(KEY_LSHIFT) || keyd_pressed[KEY_RSHIFT] || key_down_count(KEY_RSHIFT)) + mask |= KEY_SHIFTED; + + if (keyd_pressed[KEY_LALT] || key_down_count(KEY_LALT) || keyd_pressed[KEY_RALT] || key_down_count(KEY_RALT)) + mask |= KEY_ALTED; + + z = Control_config[id].key_id; + if (z >= 0) { + if ( (z != KEY_LALT) && (z != KEY_RALT) && (z != KEY_LSHIFT) && (z != KEY_RSHIFT) ) { + // if current modifiers don't match action's modifiers, don't register control active. + if ((z & (KEY_SHIFTED | KEY_ALTED)) != mask) + return 0; + } + + z &= KEY_MASK; + + if (keyd_pressed[z] || key_down_count(z)) { + if ( !hud_squadmsg_read_key(z) ) { + control_used(id); + return 1; + } + } + } + + return 0; + } + + if ((Control_config[id].key_id == key) || joy_down_count(Control_config[id].joy_id) || mouse_down_count(1 << Control_config[id].joy_id)) { + control_used(id); + return 1; + } + + return 0; +} + +// get heading, pitch, bank, throttle abs. and throttle rel. values. +void control_get_axes_readings(int *h, int *p, int *b, int *ta, int *tr) +{ + int axes_values[JOY_NUM_AXES]; + + joystick_read_raw_axis(JOY_NUM_AXES, axes_values); + + // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 (fixed point) + *h = 0; + if (Axis_map_to[0] >= 0) + *h = joy_get_scaled_reading(axes_values[Axis_map_to[0]], Axis_map_to[0]); + + *p = 0; + if (Axis_map_to[1] >= 0) + *p = joy_get_scaled_reading(axes_values[Axis_map_to[1]], Axis_map_to[1]); + + *b = 0; + if (Axis_map_to[2] >= 0) + *b = joy_get_scaled_reading(axes_values[Axis_map_to[2]], Axis_map_to[2]); + + *ta = 0; + if (Axis_map_to[3] >= 0) + *ta = joy_get_unscaled_reading(axes_values[Axis_map_to[3]], Axis_map_to[3]); + + *tr = 0; + if (Axis_map_to[4] >= 0) + *tr = joy_get_scaled_reading(axes_values[Axis_map_to[4]], Axis_map_to[4]); + + if (Invert_axis[0]) + *h = -(*h); + if (Invert_axis[1]) + *p = -(*p); + if (Invert_axis[2]) + *b = -(*b); + if (Invert_axis[3]) + *ta = F1_0 - *ta; + if (Invert_axis[4]) + *tr = -(*tr); + + return; +} + +void control_used(int id) +{ + Control_config[id].used = timestamp(); +} + +void control_config_clear_used_status() +{ + int i; + + for (i=0; i= 0) || (Conflicts[z].joy >= 0))) { // we are deleting a conflict + j = Conflicts[z].joy; + if ((j >= 0) && (Control_config[j].joy_id < 0)) + j = -1; + + k = Conflicts[z].key; + if ((k >= 0) && (Control_config[k].key_id < 0)) + k = -1; + + if ((j >= 0) && (k >= 0) && (j != k)) { // deleting 2 conflicts, each in different actions + ptr = get_undo_block(2); + ptr->index[0] = j; + ptr->list[0] = Control_config[j]; + Control_config[j].joy_id = (short) -1; + + ptr->index[1] = k; + ptr->list[1] = Control_config[k]; + Control_config[k].key_id = (short) -1; + + } else { // only 1 action in conflict with selected action (might be both controls, though) + z = j; + if (j < 0) + z = k; + + Assert(z >= 0); + ptr = get_undo_block(1); + ptr->index[0] = z; + ptr->list[0] = Control_config[z]; + + if (j >= 0) + Control_config[z].joy_id = (short) -1; + + if (k >= 0) + Control_config[z].key_id = (short) -1; + }*/ + + return 0; +} \ No newline at end of file diff --git a/src/controlconfig/controlsconfigcommon.cpp b/src/controlconfig/controlsconfigcommon.cpp new file mode 100644 index 0000000..18cfb18 --- /dev/null +++ b/src/controlconfig/controlsconfigcommon.cpp @@ -0,0 +1,858 @@ +/* + * $Logfile: /Freespace2/code/ControlConfig/ControlsConfigCommon.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for keyboard, joystick and mouse configuration common stuff (between Fred and FreeSpace) + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 14 11/01/99 3:36p Jefff + * had scan code texts for y and z in german swapped + * + * 13 11/01/99 2:16p Jefff + * minor key name change in german + * + * 12 10/29/99 6:10p Jefff + * squashed the damned y/z german issues once and for all + * + * 11 10/28/99 11:16p Jefff + * Changed some german key names. Made key translations always use the + * english table. + * + * 10 10/28/99 2:05a Jefff + * revised some german key names. changed some y/z switch stuff. + * + * 9 10/25/99 5:39p Jefff + * swap init binding for y and z keys in German builds + * + * 8 9/01/99 2:56p Jefff + * a few control key description changes + * + * 7 8/22/99 5:53p Dave + * Scoring fixes. Added self destruct key. Put callsigns in the logfile + * instead of ship designations for multiplayer players. + * + * 6 8/19/99 10:59a Dave + * Packet loss detection. + * + * 5 8/01/99 12:39p Dave + * Added HUD contrast control key (for nebula). + * + * 4 7/23/99 2:57p Andsager + * fix translate_key_to_index to work with localization + * + * 3 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 41 6/19/98 3:51p Lawrance + * localize control text + * + * 40 6/17/98 11:05a Lawrance + * localize the control config strings + * + * 39 5/24/98 2:28p Hoffoss + * Because we never really care about if the left or the right shift or + * alt key was used, but rather than either shift or alt was used, made + * both map to the left one. Solves some problems, causes none. + * + * 38 5/22/98 11:21a Hoffoss + * Added bank when pressed back in. + * + * 37 5/15/98 8:37p Lawrance + * Add 'equalize recharge rates' and 'target ship that sent last + * transmission' key bindings + * + * 36 5/13/98 7:15p Hoffoss + * Fixed remaining bugs with axis binding. + * + * 35 5/13/98 1:17a Hoffoss + * Added joystick axes configurability. + * + * 34 5/12/98 11:25a Hoffoss + * Disabled bank when pressed action. + * + * 33 5/09/98 10:48p Lawrance + * change text for view bindings + * + * 32 5/09/98 4:52p Lawrance + * Implement padlock view (up/rear/left/right) + * + * 31 4/30/98 9:43p Hoffoss + * Added new actions for free look movement which are default bounded to + * the hat. + * + * 30 4/25/98 1:25p Lawrance + * Add time compression keys to key config + * + * 29 4/25/98 12:43p Allender + * added new shortcut key for attack my subsystem + * + * 28 4/18/98 5:00p Dave + * Put in observer zoom key. Made mission sync screen more informative. + * + * 27 4/17/98 1:24a Lawrance + * fix spelling error + * + * 26 4/15/98 11:06a Lawrance + * fix bug with a multi key showing up in demo, remove obsolete bindings + * from demo and full version + * + * 25 4/10/98 12:47p Allender + * changed working on replay popup. Don't reference repair in comm menu. + * Added Shift-R for repair me + * + * 24 4/08/98 11:11a Hoffoss + * Fixed some bugs that showed up due to fixing other bugs the other day + * with controls. + * + * 23 4/07/98 4:06p Lawrance + * Change binding next for bomb targeting + * + * 22 4/07/98 1:52p Lawrance + * fix error in binding text for energy keys + * + * 21 4/06/98 11:17a Hoffoss + * Fixed num lock/pause interplay bug. + * + * 20 4/01/98 6:46p Lawrance + * change text for Alt+J + * + * 19 3/31/98 4:15p Hoffoss + * Disabled the show objectives action. + * + * 18 3/25/98 2:16p Dave + * Select random default image for newly created pilots. Fixed several + * multi-pause messaging bugs. Begin work on online help for multiplayer + * keys. + * + * 17 3/19/98 5:04p Dave + * Put in support for targeted multiplayer text and voice messaging (all, + * friendly, hostile, individual). + * + * 16 3/17/98 11:25a Hoffoss + * Fixed some of the action's names. + * + * 15 3/17/98 10:48a Hoffoss + * Allowed a special hack for "bank while pressed" action to use alt and + * shift keys standalone. + * + * 14 2/28/98 9:47p Lawrance + * change some binding text + * + * 13 2/28/98 7:02p Lawrance + * overhaul on-line help + * + * 12 2/26/98 12:33a Lawrance + * Added back slew mode, lots of changes to external and chase views. + * + * 11 2/23/98 11:25a Sandeep + * added default keys + * + * 10 2/22/98 6:27p Lawrance + * External mode takes precedence over chase mode. Fix bug with repair + * ships docking in chase mode. + * + * 9 2/22/98 12:19p John + * Externalized some strings + * + * 8 2/02/98 6:59p Lawrance + * Adding new targeting keys (bomb, uninspected cargo, new ship, live + * turrets). + * + * 7 1/27/98 4:24p Allender + * moved hotkey selection to F3 instead of X. Made palette flash + * disappear in multiplayer pause menu + * + * 6 1/22/98 4:53p Hoffoss + * Made training messages/directives display a joystick button in place of + * a keypress if there is no keypress bound to the action. + * + * 5 1/22/98 4:15p Hoffoss + * Added code to allow popup to tell player he needs to bind a key for the + * training mission. + * + * 4 1/02/98 4:42p Allender + * removed unused key bindings from control config list + * + * 3 12/30/97 4:47p Allender + * work with ignore my target command. Added new keyboard hotkey. Made + * it work globally + * + * 2 12/24/97 3:37p Hoffoss + * Moved control config stuff to seperate library to Fred can access it as + * well. + * + * 1 12/24/97 3:27p Hoffoss + * + * 1 12/24/97 11:58a Hoffoss + * + * 98 12/22/97 2:15p Hoffoss + * Fixed bug where joystick axis lines weren't being displayed. + * + * 97 12/16/97 2:44p Hoffoss + * Added clear button to control config screen. + * + * 96 12/12/97 3:07p Hoffoss + * Changed how deleting bindings work. Each control of an action can be + * deleted independently or both at once. + * + * 95 12/07/97 2:36p John + * Made warp out be Alt+J instead of J + * + * 94 12/03/97 4:59p Hoffoss + * Added reset sound and change control config sounds around. + * + * 93 12/03/97 4:16p Hoffoss + * Changed sound stuff used in interface screens for interface purposes. + * + * 92 12/01/97 5:25p Hoffoss + * Routed interface sound playing through a special function that will + * only allow one instance of the sound to play at a time, avoiding + * over-mixing problems. + * + * 91 12/01/97 3:38p Hoffoss + * Changed placement of 'More' indicator. + * + * 90 11/25/97 3:49p Hoffoss + * Changed subhilighting to happen when mouse is over line, rather than + * mouse button down but not up again yet. + * + * 89 11/24/97 10:20p Lawrance + * Add key 'KEY_N' to target next ship on monitoring view + * + * 88 11/24/97 6:15p Lawrance + * fix button scroll problem + * + * 87 11/23/97 6:15p Hoffoss + * Make exiting the controls config screen save the pilot, so changes keep + * even if programs crashes or something. + * + * 86 11/21/97 5:57p Hoffoss + * Fixed bug where timef type controls weren't checking joystick buttons. + * + * 85 11/21/97 4:06p Hoffoss + * Fixed bug with enabling of axis are reversed. + * + * 84 11/20/97 4:47p Hoffoss + * Swapped throttle and rudder, which apparently were backwards. + * + * 83 11/20/97 4:00p Lawrance + * set z to a valid value + * + * 82 11/20/97 3:52p Hoffoss + * Made "None" appear if no control is bound to function. + * + * 81 11/20/97 2:10p Hoffoss + * Added defaults for joystick buttons 2 and 3. + * + * 80 11/20/97 1:08a Lawrance + * add support for 'R' key - target closest friendly repair ship + * + * 79 11/19/97 8:33p Hoffoss + * New controls config screen baby! + * + * 78 11/17/97 7:09p Hoffoss + * Made chase view control a trigger type. + * + * 77 11/17/97 6:40p Lawrance + * Changed 'I' key to toggle of extended target info, moved target closest + * locked missile to 'L' + * + * 76 11/17/97 3:26p Jasen + * Adjusted coordinates for the help button .ani location + * + * 75 11/16/97 3:51p Hoffoss + * Added more button to config screen when scrolling allowed. + * + * 74 11/16/97 3:20p Hoffoss + * Added various suggestions from the Todolist. + * + * 73 11/14/97 4:33p Mike + * Change Debug key to backquote (from F11). + * Balance a ton of subsystems in ships.tbl. + * Change "Heavy Laser" to "Disruptor". + * + * 72 11/12/97 2:54p Hoffoss + * Made modifiers non-stand alone, added support for red tabs on + * conflicts. + * + * 71 10/30/97 6:01p Hoffoss + * Changed screen to utilize the new text color standards. + * + * 70 10/29/97 7:25p Hoffoss + * Added crude support for UI button double click checking. + * + * 69 10/29/97 6:32p Hoffoss + * Changed some interface apperances. + * + * 68 10/29/97 4:54p Hoffoss + * Changed scan_code_text element for Enter to 'Enter' instead of 'a' with + * a caret over it (what's that all about?) + * + * 67 10/28/97 10:42a Hoffoss + * Fixed handling of continuous controls that have modifiers to the key. + * + * 66 10/28/97 10:03a Hoffoss + * Fixed bug with continuous type controls not registering if pressed and + * released too quickly. Also some other little changes. + * + * 65 10/28/97 12:12a Lawrance + * remove unused keys (Alt-H and Alt-F) + * + * 64 10/27/97 12:23p Hoffoss + * Improved warning system and fixed coloring bug. + * + * 63 10/27/97 11:39a Hoffoss + * Added control conflicts checking. + * + * 62 10/26/97 3:20p Hoffoss + * Added many missing features to control config screen. + * + * 61 10/25/97 5:41p Hoffoss + * More functionality added to the controls config screen. + * + * 60 10/24/97 11:00p Hoffoss + * Controls config screen much improved. + * + * 59 10/22/97 4:50p Hoffoss + * Disabled throttle by default. + * + * 58 10/22/97 11:00a Hoffoss + * Changed VIEW_SLEW and VIEW_EXTERNAL to be continuous rather than + * trigger actions. + * + * 57 10/21/97 7:05p Hoffoss + * Overhauled the key/joystick control structure and usage throughout the + * entire FreeSpace code. The whole system is very different now. + * + * 56 10/18/97 7:19p Hoffoss + * Added timestamp recording when a key is pressed. + * + * 55 10/13/97 4:33p Hoffoss + * Made training messages go away after time. + * + * 54 10/13/97 3:24p Hoffoss + * Made it so training message text can be arbitrarily bolded. + * + * 53 9/24/97 4:52p Hoffoss + * Changed training message key token handling, and implemented a new + * training message system method to test out for a while. + * + * 52 9/14/97 10:24p Lawrance + * add damage screen popup window + * + * 51 9/10/97 6:02p Hoffoss + * Added code to check for key-pressed sexp operator in FreeSpace as part + * of training mission stuff. + * + * 50 9/09/97 3:39p Sandeep + * warning level 4 bugs + * $NoKeywords: $ + * +*/ + +#include "controlsconfig.h" +#include "key.h" +#include "joy.h" +#include "localize.h" + +#define TARGET_TAB 0 +#define SHIP_TAB 1 +#define WEAPON_TAB 2 +#define COMPUTER_TAB 3 + +int Failed_key_index; + +// assume control keys are used as modifiers until we find out +int Shift_is_modifier; +int Ctrl_is_modifier; +int Alt_is_modifier; + +int Axis_enabled[JOY_NUM_AXES] = { 1, 1, 1, 0, 0, 0 }; +int Axis_enabled_defaults[JOY_NUM_AXES] = { 1, 1, 1, 0, 0, 0 }; +int Invert_axis[JOY_NUM_AXES] = { 0, 0, 0, 0, 0, 0 }; +int Invert_axis_defaults[JOY_NUM_AXES] = { 0, 0, 0, 0, 0, 0 }; + +// arrays which hold the key mappings. The array index represents a key-independent action. +// +//XSTR:OFF +config_item Control_config[CCFG_MAX + 1] = { + // targeting a ship + { KEY_T, -1, TARGET_TAB, "Target Next Ship" }, + { KEY_SHIFTED | KEY_T, -1, TARGET_TAB, "Target Previous Ship" }, + { KEY_H, 2, TARGET_TAB, "Target Next Closest Hostile Ship" }, + { KEY_SHIFTED | KEY_H, -1, TARGET_TAB, "Target Previous Closest Hostile Ship" }, + { KEY_ALTED | KEY_H, -1, TARGET_TAB, "Toggle Auto Targeting" }, + { KEY_F, -1, TARGET_TAB, "Target Next Closest Friendly Ship" }, + { KEY_SHIFTED | KEY_F, -1, TARGET_TAB, "Target Previous Closest Friendly Ship" }, + { KEY_Y, 4, TARGET_TAB, "Target Ship in Reticle" }, + { KEY_G, -1, TARGET_TAB, "Target Target's Nearest Attacker" }, + { KEY_ALTED | KEY_Y, -1, TARGET_TAB, "Target Last Ship to Send Transmission" }, + { KEY_ALTED | KEY_T, -1, TARGET_TAB, "Turn Off Auto-Targeting" }, + + // targeting a ship's subsystem + { KEY_V, -1, TARGET_TAB, "Target Subsystem in Reticle" }, + { KEY_S, -1, TARGET_TAB, "Target Next Subsystem" }, + { KEY_SHIFTED | KEY_S, -1, TARGET_TAB, "Target Previous Subsystem" }, + { KEY_ALTED | KEY_S, -1, TARGET_TAB, "Turn Off Auto-Targeting of Subsystems" }, + + // matching speed + { KEY_M, -1, COMPUTER_TAB, "Match Target Speed" }, + { KEY_ALTED | KEY_M, -1, COMPUTER_TAB, "Toggle Auto Speed Matching" }, + + // weapons + { KEY_LCTRL, 0, WEAPON_TAB, "Fire Primary Weapon", CC_TYPE_CONTINUOUS }, + { KEY_SPACEBAR, 1, WEAPON_TAB, "Fire Secondary Weapon", CC_TYPE_CONTINUOUS }, + { KEY_PERIOD, -1, WEAPON_TAB, "Cycle Forward Primary Weapon" }, + { KEY_COMMA, -1, WEAPON_TAB, "Cycle Backward Primary Weapon" }, + { KEY_DIVIDE, -1, WEAPON_TAB, "Cycle Secondary Weapon Bank" }, + { KEY_SHIFTED | KEY_DIVIDE, -1, WEAPON_TAB, "Cycle Secondary Weapon Firing Rate" }, + { KEY_X, 3, WEAPON_TAB, "Launch Countermeasure" }, + + // controls + { KEY_A, -1, SHIP_TAB, "Forward Thrust", CC_TYPE_CONTINUOUS }, + { KEY_Z, -1, SHIP_TAB, "Reverse Thrust", CC_TYPE_CONTINUOUS }, + { KEY_PAD7, -1, SHIP_TAB, "Bank Left", CC_TYPE_CONTINUOUS }, + { KEY_PAD9, -1, SHIP_TAB, "Bank Right", CC_TYPE_CONTINUOUS }, + { KEY_PAD8, -1, SHIP_TAB, "Pitch Forward", CC_TYPE_CONTINUOUS }, + { KEY_PAD2, -1, SHIP_TAB, "Pitch Backward", CC_TYPE_CONTINUOUS }, + { KEY_PAD4, -1, SHIP_TAB, "Turn Left", CC_TYPE_CONTINUOUS }, + { KEY_PAD6, -1, SHIP_TAB, "Turn Right", CC_TYPE_CONTINUOUS }, + + // throttle controls + { KEY_BACKSP, -1, SHIP_TAB, "Set Throttle to Zero" }, + { KEY_SLASH, -1, SHIP_TAB, "Set Throttle to Max" }, + { KEY_LBRACKET, -1, SHIP_TAB, "Set Throttle to One-Third" }, + { KEY_RBRACKET, -1, SHIP_TAB, "Set Throttle to Two-Thirds" }, + { KEY_EQUAL, -1, SHIP_TAB, "Increase Throttle 5 Percent" }, + { KEY_MINUS, -1, SHIP_TAB, "Decrease Throttle 5 Percent" }, + + // squadmate messaging + { KEY_SHIFTED | KEY_A, -1, COMPUTER_TAB, "Attack My Target" }, + { KEY_SHIFTED | KEY_Z, -1, COMPUTER_TAB, "Disarm My Target" }, + { KEY_SHIFTED | KEY_D, -1, COMPUTER_TAB, "Disable My Target" }, + { KEY_SHIFTED | KEY_V, -1, COMPUTER_TAB, "Attack my Subsystem" }, + { KEY_SHIFTED | KEY_X, -1, COMPUTER_TAB, "Capture My Target" }, + { KEY_SHIFTED | KEY_E, -1, COMPUTER_TAB, "Engage Enemy" }, + { KEY_SHIFTED | KEY_W, -1, COMPUTER_TAB, "Form on my Wing" }, + { KEY_SHIFTED | KEY_I, -1, COMPUTER_TAB, "Ignore my Target" }, + { KEY_SHIFTED | KEY_P, -1, COMPUTER_TAB, "Protect my Target" }, + { KEY_SHIFTED | KEY_C, -1, COMPUTER_TAB, "Cover me" }, + { KEY_SHIFTED | KEY_J, -1, COMPUTER_TAB, "Return to base" }, + { KEY_SHIFTED | KEY_R, -1, COMPUTER_TAB, "Rearm me" }, + + { KEY_R, 6, TARGET_TAB, "Target Closest Attacking Ship" }, + + // Views + { KEY_PADMULTIPLY, -1, COMPUTER_TAB, "Chase View" }, + { KEY_PADPERIOD, -1, COMPUTER_TAB, "External View"}, + { KEY_PADENTER, -1, COMPUTER_TAB, "Toggle External Camera Lock"}, + { KEY_PAD0, -1, COMPUTER_TAB, "Free Look View", CC_TYPE_CONTINUOUS }, + { KEY_PADDIVIDE, -1, COMPUTER_TAB, "Current Target View" }, + { KEY_PADPLUS, -1, COMPUTER_TAB, "Increase View Distance", CC_TYPE_CONTINUOUS }, + { KEY_PADMINUS, -1, COMPUTER_TAB, "Decrease View Distance", CC_TYPE_CONTINUOUS }, + { KEY_PAD5, -1, COMPUTER_TAB, "Center View", CC_TYPE_CONTINUOUS }, + { -1, 33, COMPUTER_TAB, "View Up", CC_TYPE_CONTINUOUS }, + { -1, 32, COMPUTER_TAB, "View Rear", CC_TYPE_CONTINUOUS }, + { -1, 34, COMPUTER_TAB, "View Left", CC_TYPE_CONTINUOUS }, + { -1, 35, COMPUTER_TAB, "View Right", CC_TYPE_CONTINUOUS }, + + { KEY_RAPOSTRO, -1, COMPUTER_TAB, "Cycle Radar Range" }, + { KEY_C, -1, COMPUTER_TAB, "Communications Menu" }, + { -1, -1, -1, "Show Objectives" }, + { KEY_ALTED | KEY_J, -1, COMPUTER_TAB, "Enter Subspace (End Mission)" }, + { KEY_J, -1, TARGET_TAB, "Target Target's Target" }, + { KEY_TAB, 5, SHIP_TAB, "Afterburner", CC_TYPE_CONTINUOUS }, + + { KEY_INSERT, -1, COMPUTER_TAB, "Increase Weapon Energy" }, + { KEY_DELETE, -1, COMPUTER_TAB, "Decrease Weapon Energy" }, + { KEY_HOME, -1, COMPUTER_TAB, "Increase Shield Energy" }, + { KEY_END, -1, COMPUTER_TAB, "Decrease Shield Energy" }, + { KEY_PAGEUP, -1, COMPUTER_TAB, "Increase Engine Energy" }, + { KEY_PAGEDOWN, -1, COMPUTER_TAB, "Decrease Engine Energy" }, + { KEY_ALTED | KEY_D, -1, COMPUTER_TAB, "Equalize Energy Settings" }, + + { KEY_Q, 7, COMPUTER_TAB, "Equalize Shield" }, + { KEY_UP, -1, COMPUTER_TAB, "Augment Forward Shield" }, + { KEY_DOWN, -1, COMPUTER_TAB, "Augment Rear Shield" }, + { KEY_LEFT, -1, COMPUTER_TAB, "Augment Left Shield" }, + { KEY_RIGHT, -1, COMPUTER_TAB, "Augment Right Shield" }, + { KEY_SCROLLOCK, -1, COMPUTER_TAB, "Transfer Energy Laser->Shield" }, + { KEY_SHIFTED | KEY_SCROLLOCK, -1, COMPUTER_TAB, "Transfer Energy Shield->Laser" }, + { -1, -1, -1, "Show Damage Popup Window" }, + + { -1, -1, SHIP_TAB, "Bank When Pressed", CC_TYPE_CONTINUOUS }, + { -1, -1, -1, "Show NavMap" }, + { KEY_ALTED | KEY_E, -1, COMPUTER_TAB, "Add or Remove Escort" }, + { KEY_ALTED | KEY_SHIFTED | KEY_E, -1, COMPUTER_TAB, "Clear Escort List" }, + { KEY_E, -1, TARGET_TAB, "Target Next Escort Ship" }, + { KEY_ALTED | KEY_R, -1, TARGET_TAB, "Target Closest Repair Ship" }, + + { KEY_U, -1, TARGET_TAB, "Target Next Uninspected Cargo" }, + { KEY_SHIFTED | KEY_U, -1, TARGET_TAB, "Target Previous Uninspected Cargo" }, + { KEY_N, -1, TARGET_TAB, "Target Newest Ship In Area" }, + { KEY_K, -1, TARGET_TAB, "Target Next Live Turret" }, + { KEY_SHIFTED | KEY_K, -1, TARGET_TAB, "Target Previous Live Turret" }, + + { KEY_B, -1, TARGET_TAB, "Target Next Hostile Bomb or Bomber" }, + { KEY_SHIFTED | KEY_B, -1, TARGET_TAB, "Target Previous Hostile Bomb or Bomber" }, + + // multiplayer messaging keys + { KEY_1, -1, COMPUTER_TAB, "(Multiplayer) Message All", CC_TYPE_CONTINUOUS }, + { KEY_2, -1, COMPUTER_TAB, "(Multiplayer) Message Friendly", CC_TYPE_CONTINUOUS }, + { KEY_3, -1, COMPUTER_TAB, "(Multiplayer) Message Hostile", CC_TYPE_CONTINUOUS }, + { KEY_4, -1, COMPUTER_TAB, "(Multiplayer) Message Target", CC_TYPE_CONTINUOUS }, + { KEY_ALTED | KEY_X, -1, COMPUTER_TAB, "(Multiplayer) Observer zoom to target"}, + + { KEY_SHIFTED | KEY_PERIOD, -1, COMPUTER_TAB, "Increase time compression" }, + { KEY_SHIFTED | KEY_COMMA, -1, COMPUTER_TAB, "Decrease time compression" }, + + { KEY_L, -1, COMPUTER_TAB, "Toggle high HUD contrast" }, + + { KEY_SHIFTED | KEY_N, -1, COMPUTER_TAB, "(Multiplayer) Toggle network info"}, + { KEY_SHIFTED | KEY_END, -1, COMPUTER_TAB, "(Multiplayer) Self destruct"}, + + { -1, -1, -1, "" } +}; + +char *Scan_code_text_german[] = { + "", "Esc", "1", "2", "3", "4", "5", "6", + "7", "8", "9", "0", "Akzent '", "\xE1", "R\x81""cktaste", "Tab", + "Q", "W", "E", "R", "T", "Z", "U", "I", + "O", "P", "\x9A", "+", "Eingabe", "Strg Links", "A", "S", + + "D", "F", "G", "H", "J", "K", "L", "\x99", + "\xAE", "`", "Shift", "#", "Y", "X", "C", "V", + "B", "N", "M", ",", ".", "-", "Shift", "Num *", + "Alt", "Leertaste", "Hochstell", "F1", "F2", "F3", "F4", "F5", + + "F6", "F7", "F8", "F9", "F10", "Pause", "Rollen", "Num 7", + "Num 8", "Num 9", "Num -", "Num 4", "Num 5", "Num 6", "Num +", "Num 1", + "Num 2", "Num 3", "Num 0", "Num ,", "", "", "", "F11", + "F12", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "Num Eingabe", "Strg Rechts", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "Num /", "", "Druck", + "Alt", "", "", "", "", "", "", "", + + "", "", "", "", "", "Num Lock", "", "Pos 1", + "Pfeil Hoch", "Bild Hoch", "", "Pfeil Links", "", "Pfeil Rechts", "", "Ende", + "Pfeil Runter", "Bild Runter", "Einfg", "Entf", "", "", "", "", + "", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", +}; + +char *Joy_button_text_german[] = { + "Knopf 1", "Knopf 2", "Knopf 3", "Knopf 4", "Knopf 5", "Knopf 6", + "Knopf 7", "Knopf 8", "Knopf 9", "Knopf 10", "Knopf 11", "Knopf 12", + "Knopf 13", "Knopf 14", "Knopf 15", "Knopf 16", "Knopf 17", "Knopf 18", + "Knopf 19", "Knopf 20", "Knopf 21", "Knopf 22", "Knopf 23", "Knopf 24", + "Knopf 25", "Knopf 26", "Knopf 27", "Knopf 28", "Knopf 29", "Knopf 30", + "Knopf 31", "Knopf 32", "Hut Hinten", "Hut Vorne", "Hut Links", "Hut Rechts" +}; + +char *Scan_code_text_french[] = { + "", "\x90""chap", "1", "2", "3", "4", "5", "6", + "7", "8", "9", "0", "-", "=", "Fl\x82""che Ret.", "Tab", + "Q", "W", "E", "R", "T", "Y", "U", "I", + "O", "P", "[", "]", "Entr\x82""e", "Ctrl Gauche", "A", "S", + + "D", "F", "G", "H", "J", "K", "L", ";", + "'", "`", "Maj.", "\\", "Z", "X", "C", "V", + "B", "N", "M", ",", ".", "/", "Maj.", "Pav\x82 *", + "Alt", "Espace", "Verr. Maj.", "F1", "F2", "F3", "F4", "F5", + + "F6", "F7", "F8", "F9", "F10", "Pause", "Arret defil", "Pav\x82 7", + "Pav\x82 8", "Pav\x82 9", "Pav\x82 -", "Pav\x82 4", "Pav\x82 5", "Pav\x82 6", "Pav\x82 +", "Pav\x82 1", + "Pav\x82 2", "Pav\x82 3", "Pav\x82 0", "Pav\x82 .", "", "", "", "F11", + "F12", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "Pav\x82 Entr", "Ctrl Droite", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "Pav\x82 /", "", "Impr \x82""cran", + "Alt", "", "", "", "", "", "", "", + + "", "", "", "", "", "Verr num", "", "Orig.", + "Fl\x82""che Haut", "Page Haut", "", "Fl\x82""che Gauche", "", "Fl\x82""che Droite", "", "Fin", + "Fl\x82""che Bas", "Page Bas", "Inser", "Suppr", "", "", "", "", + "", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", +}; + +char *Joy_button_text_french[] = { + "Bouton 1", "Bouton 2", "Bouton 3", "Bouton 4", "Bouton 5", "Bouton 6", + "Bouton 7", "Bouton 8", "Bouton 9", "Bouton 10", "Bouton 11", "Bouton 12", + "Bouton 13", "Bouton 14", "Bouton 15", "Bouton 16", "Bouton 17", "Bouton 18", + "Bouton 19", "Bouton 20", "Bouton 21", "Bouton 22", "Bouton 23", "Bouton 24", + "Bouton 25", "Bouton 26", "Bouton 27", "Bouton 28", "Bouton 29", "Bouton 30", + "Bouton 31", "Bouton 32", "Chapeau Arrière", "Chapeau Avant", "Chapeau Gauche", "Chapeau Droite" +}; + +// This is the text that is displayed on the screen for the keys a player selects +char *Scan_code_text_english[] = { + "", "Esc", "1", "2", "3", "4", "5", "6", + "7", "8", "9", "0", "-", "=", "Backspace", "Tab", + "Q", "W", "E", "R", "T", "Y", "U", "I", + "O", "P", "[", "]", "Enter", "Left Ctrl", "A", "S", + + "D", "F", "G", "H", "J", "K", "L", ";", + "'", "`", "Shift", "\\", "Z", "X", "C", "V", + "B", "N", "M", ",", ".", "/", "Shift", "Pad *", + "Alt", "Spacebar", "Caps Lock", "F1", "F2", "F3", "F4", "F5", + + "F6", "F7", "F8", "F9", "F10", "Pause", "Scroll Lock", "Pad 7", + "Pad 8", "Pad 9", "Pad -", "Pad 4", "Pad 5", "Pad 6", "Pad +", "Pad 1", + "Pad 2", "Pad 3", "Pad 0", "Pad .", "", "", "", "F11", + "F12", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "Pad Enter", "Right Ctrl", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "Pad /", "", "Print Scrn", + "Alt", "", "", "", "", "", "", "", + + "", "", "", "", "", "Num Lock", "", "Home", + "Up Arrow", "Page Up", "", "Left Arrow", "", "Right Arrow", "", "End", + "Down Arrow", "Page Down", "Insert", "Delete", "", "", "", "", + "", "", "", "", "", "", "", "", + + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", +}; + +char *Joy_button_text_english[] = { + "Button 1", "Button 2", "Button 3", "Button 4", "Button 5", "Button 6", + "Button 7", "Button 8", "Button 9", "Button 10", "Button 11", "Button 12", + "Button 13", "Button 14", "Button 15", "Button 16", "Button 17", "Button 18", + "Button 19", "Button 20", "Button 21", "Button 22", "Button 23", "Button 24", + "Button 25", "Button 26", "Button 27", "Button 28", "Button 29", "Button 30", + "Button 31", "Button 32", "Hat Back", "Hat Forward", "Hat Left", "Hat Right" +}; + +char **Scan_code_text = Scan_code_text_english; +char **Joy_button_text = Joy_button_text_english; + +void set_modifier_status() +{ + int i; + + Alt_is_modifier = 0; + Shift_is_modifier = 0; + Ctrl_is_modifier = 0; + + for (i=0; i= 0) + return Joy_button_text[code]; + } + + return textify_scancode(code); +} + +char *textify_scancode(int code) +{ + static char text[40]; + + if (code < 0) + return "None"; + + *text = 0; + if (code & KEY_ALTED) { + if(Lcl_gr){ + strcat(text, "Alt-"); + } else if(Lcl_fr){ + strcat(text, "Alt-"); + } else { + strcat(text, "Alt-"); + } + } + + if (code & KEY_SHIFTED) { + if(Lcl_gr){ + strcat(text, "Shift-"); + } else if(Lcl_fr){ + strcat(text, "Maj.-"); + } else { + strcat(text, "Shift-"); + } + } + + strcat(text, Scan_code_text[code & KEY_MASK]); + return text; +} +//XSTR:ON + +// initialize common control config stuff - call at game startup after localization has been initialized +void control_config_common_init() +{ + if(Lcl_gr){ + Scan_code_text = Scan_code_text_german; + Joy_button_text = Joy_button_text_german; + + // swap init bindings for y and z keys + Control_config[TARGET_SHIP_IN_RETICLE].key_default = KEY_Z; + Control_config[TARGET_LAST_TRANMISSION_SENDER].key_default = KEY_ALTED | KEY_Z; + Control_config[REVERSE_THRUST].key_default = KEY_Y; + Control_config[DISARM_MESSAGE].key_default = KEY_SHIFTED | KEY_Y; + } else if(Lcl_fr){ + Scan_code_text = Scan_code_text_french; + Joy_button_text = Joy_button_text_french; + } else { + Scan_code_text = Scan_code_text_english; + Joy_button_text = Joy_button_text_english; + } +} \ No newline at end of file diff --git a/src/cryptstring/cryptstring.cpp b/src/cryptstring/cryptstring.cpp new file mode 100644 index 0000000..c802f23 --- /dev/null +++ b/src/cryptstring/cryptstring.cpp @@ -0,0 +1,72 @@ +/* + * $Logfile: /Freespace2/code/Cryptstring/cryptstring.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Applet for crypting strings. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/23/98 6:21p Dave + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include "crypt.h" + +int main(int argc, char **argv) +{ + int i; + char *crypt_string; + + if ( argc == 1 ) { + printf("Usage: cryptstring ...\n"); + printf("Output will be \n"); + exit(1); + } + + for ( i = 1; i < argc; i++ ) { + char *s; + + s = argv[i]; + // if the length of the string is greater than the number of crypted symbols we + // return, then pass only the maximum length + if ( strlen(s) > CRYPT_STRING_LENGTH ) + s += (strlen(s) - CRYPT_STRING_LENGTH); + + crypt_string = jcrypt(s); + printf("%s\n", crypt_string); + } + + return 0; +} + +char *jcrypt (char *plainstring) +{ + int i,t,len; + static char cryptstring[CRYPT_STRING_LENGTH + 1]; + + len=strlen (plainstring); + if (len > CRYPT_STRING_LENGTH) + len = CRYPT_STRING_LENGTH; + + for (i = 0;i < len; i++) { + cryptstring[i]=0; + + for (t = 0; t < len; t++) { + cryptstring[i]^=(plainstring[t] ^ plainstring[i%(t+1)]); + cryptstring[i]%=90; + cryptstring[i]+=33; + } + } + + cryptstring[i]=0; + return ((char *)cryptstring); +} diff --git a/src/cutscene/cutscenes.cpp b/src/cutscene/cutscenes.cpp new file mode 100644 index 0000000..f390cd0 --- /dev/null +++ b/src/cutscene/cutscenes.cpp @@ -0,0 +1,741 @@ +/* + * $Logfile: /Freespace2/code/Cutscene/Cutscenes.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for the cutscenes viewer screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 15 6/16/00 3:15p Jefff + * sim of the year dvd version changes, a few german soty localization + * fixes + * + * 14 10/13/99 10:20a Jefff + * fixed bug where intro cutscene showed 2x after getting bad ending + * + * 13 10/06/99 10:30a Jefff + * OEM updates + * + * 12 9/30/99 6:01p Jefff + * OEM updates + * + * 11 9/07/99 1:10p Mikek + * Make movie check not hang in ~RELEASE_REAL build. + * + * 10 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 9 9/06/99 1:16a Dave + * Make sure the user sees the intro movie. + * + * 8 9/03/99 1:31a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 7 7/19/99 2:13p Dave + * Added some new strings for Heiko. + * + * 6 6/01/99 3:52p Dave + * View footage screen. Fixed xstrings to not display the & symbol. Popup, + * dead popup, pxo find player popup, pxo private room popup. + * + * 5 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 4 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 18 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 17 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 16 5/24/98 9:01p Lawrance + * Add commit sounds when accept is pressed + * + * 15 5/21/98 8:05p Allender + * fix possible bug with number of cutscenes shown in the list + * + * 14 5/21/98 12:35a Lawrance + * Tweak how CD is checked for + * + * 13 5/20/98 1:34p Hoffoss + * Added cutscene description rendering. + * + * 12 5/19/98 12:19p Mike + * Cheat codes! + * + * 11 5/12/98 4:17p Hoffoss + * Make ctrl-arrows (up/down) switch between tech room screens. + * + * 10 5/11/98 8:04p Hoffoss + * Fixed minor bugs. + * + * 9 5/10/98 10:05p Allender + * only show cutscenes which have been seen before. Made Fred able to + * write missions anywhere, defaulting to player misison folder, not data + * mission folder. Fix FreeSpace code to properly read missions from + * correct locations + * + * 8 5/08/98 5:30p Lawrance + * add CD checks for movie playing + * + * 7 5/08/98 4:07p Allender + * more cutscene stuff + * + * 6 5/07/98 2:33p Hoffoss + * Removed help and options buttons. + * + * 5 4/30/98 4:53p John + * Restructured and cleaned up cfile code. Added capability to read off + * of CD-ROM drive and out of multiple pack files. + * + * 4 4/23/98 8:27p Allender + * basic support for cutscene playback. Into movie code in place. Tech + * room can view cutscenes stored in CDROM_dir variable + * + * 3 4/21/98 7:07p Hoffoss + * Fixed problem where when switching screens flashes old tab hilight once + * before switching to new state. + * + * 2 4/17/98 6:33p Hoffoss + * Made changes to the tech room group of screens. Cutscenes screen is + * now in a new file. + * + * $NoKeywords: $ + */ + +#include "cutscenes.h" +#include "ui.h" +#include "cfile.h" +#include "gamesnd.h" +#include "gamesequence.h" +#include "freespace.h" +#include "key.h" +#include "bmpman.h" +// #include "movie.h" +#include "popup.h" +#include "mainhallmenu.h" +#include "alphacolors.h" +#include "localize.h" + +char *Cutscene_bitmap_name[GR_NUM_RESOLUTIONS] = { + "ViewFootage", + "2_ViewFootage" +}; +char *Cutscene_mask_name[GR_NUM_RESOLUTIONS] = { + "ViewFootage-m", + "2_ViewFootage-m" +}; + +int Num_cutscenes; +int Cutscenes_viewable; +int Description_index; +cutscene_info Cutscenes[MAX_CUTSCENES]; + +extern int All_movies_enabled; // If set, all movies may be viewed. Keyed off cheat code. + +// initialization stuff for cutscenes +void cutscene_init() +{ + char buf[MULTITEXT_LENGTH]; + int rval; + + if ((rval = setjmp(parse_abort)) != 0) { + Error(LOCATION, "Error parsing 'rank.tbl'\r\nError code = %i.\r\n", rval); + } + + // open localization + lcl_ext_open(); + + read_file_text("cutscenes.tbl"); + reset_parse(); + + // parse in all the rank names + Num_cutscenes = 0; + skip_to_string("#Cutscenes"); + ignore_white_space(); + while ( required_string_either("#End", "$Filename:") ) { + Assert ( Num_cutscenes < MAX_CUTSCENES ); + required_string("$Filename:"); + stuff_string( Cutscenes[Num_cutscenes].filename, F_PATHNAME, NULL ); + required_string("$Name:"); + stuff_string( Cutscenes[Num_cutscenes].name, F_NAME, NULL ); + required_string("$Description:"); + stuff_string(buf, F_MULTITEXT, NULL); + drop_white_space(buf); + compact_multitext_string(buf); + Cutscenes[Num_cutscenes].description = strdup(buf); + required_string("$cd:"); + stuff_int( &Cutscenes[Num_cutscenes].cd ); + + Num_cutscenes++; + } + + required_string("#End"); + + Cutscenes_viewable = INTRO_CUTSCENE_FLAG; + + // close localization + lcl_ext_close(); +} + +// function to return 0 based index of which CD a particular movie is on +// returns -1 on failure. +int cutscenes_get_cd_num( char *filename ) +{ +#if defined(OEM_BUILD) + return 0; // only 1 cd for OEM +#else + int i; + + for (i = 0; i < Num_cutscenes; i++ ) { + if ( !stricmp(Cutscenes[i].filename, filename) ) { + return (Cutscenes[i].cd - 1); + } + } + + return -1; +#endif // defined(OEM_BUILD) +} + +// marks a cutscene as viewable +void cutscene_mark_viewable(char *filename) +{ + int i; + + for (i = 0; i < Num_cutscenes; i++ ) { + if ( !stricmp(Cutscenes[i].filename, filename) ) { + Cutscenes_viewable |= (1< 5 ) { + cd_present = 0; + break; + } +#else + cd_present = 0; + break; +#endif + + } + + return cd_present; +} + +void cutscenes_screen_play() +{ + char name[MAX_FILENAME_LEN], *full_name; + int which_cutscene; + + Assert( (Selected_line >= 0) && (Selected_line < Num_files) ); + which_cutscene = Cutscene_list[Selected_line]; + + strcpy(name, Cutscenes[which_cutscene].filename ); + full_name = cf_add_ext(name, NOX(".mve")); + + // no soup for you! + /* + int rval = movie_play(full_name); + if ( !rval ) { + char str[256]; + + sprintf(str, XSTR( "Unable to play movie %s.", 204), Cutscenes[which_cutscene].name ); + popup(0, 1, POPUP_OK, str ); + } + */ +} + +void cutscenes_screen_scroll_line_up() +{ + if (Selected_line) { + Selected_line--; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); + + if (Selected_line < Scroll_offset) + Scroll_offset = Selected_line; +} + +void cutscenes_screen_scroll_line_down() +{ + int h; + + if (Selected_line < Num_files - 1) { + Selected_line++; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); + + h = Cutscene_list_coords[gr_screen.res][3] / gr_get_font_height(); + if (Selected_line >= Scroll_offset + h){ + Scroll_offset++; + } +} + +void cutscenes_screen_scroll_screen_up() +{ + int h; + + if (Scroll_offset) { + Scroll_offset--; + Assert(Selected_line > Scroll_offset); + h = Cutscene_list_coords[gr_screen.res][3] / gr_get_font_height(); + while (Selected_line >= Scroll_offset + h){ + Selected_line--; + } + + gamesnd_play_iface(SND_SCROLL); + + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +void cutscenes_screen_scroll_screen_down() +{ + int h; + + h = Cutscene_list_coords[gr_screen.res][3] / gr_get_font_height(); + if (Scroll_offset + h < Num_files) { + Scroll_offset++; + if (Selected_line < Scroll_offset){ + Selected_line = Scroll_offset; + } + + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +int cutscenes_screen_button_pressed(int n) +{ + switch (n) { + case TECH_DATABASE_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_TECH_MENU); + return 1; + + case SIMULATOR_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_SIMULATOR_ROOM); + return 1; + + case CREDITS_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_CREDITS); + return 1; + + case SCROLL_UP_BUTTON: + cutscenes_screen_scroll_screen_up(); + break; + + case SCROLL_DOWN_BUTTON: + cutscenes_screen_scroll_screen_down(); + break; + + case PLAY_BUTTON: + cutscenes_screen_play(); + break; + + case EXIT_BUTTON: + gamesnd_play_iface(SND_COMMIT_PRESSED); + gameseq_post_event(GS_EVENT_MAIN_MENU); + game_flush(); + break; + } + + return 0; +} + +void cutscenes_screen_init() +{ + int i; + ui_button_info *b; + + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Ui_window.set_mask_bmap(Cutscene_mask_name[gr_screen.res]); + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // add xstrs + for(i=0; i= 0) && (z < Num_files)) + select_tease_line = z; + } + + if (List_region.pressed()) { + List_region.get_mouse_pos(NULL, &y); + z = Scroll_offset + y / font_height; + if ((z >= 0) && (z < Num_files)) + Selected_line = z; + } + + GR_MAYBE_CLEAR_RES(Background_bitmap); + if (Background_bitmap >= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + Ui_window.draw(); + + for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){ + if (Buttons[gr_screen.res][i].button.button_down()){ + break; + } + } + + if (i > CREDITS_BUTTON){ + Buttons[gr_screen.res][CUTSCENES_BUTTON].button.draw_forced(2); + } + + y = 0; + z = Scroll_offset; + while (y + font_height <= Cutscene_list_coords[gr_screen.res][3]) { + if (z >= Num_files){ + break; + } + + if (z == Selected_line){ + gr_set_color_fast(&Color_text_selected); + } else if (z == select_tease_line) { + gr_set_color_fast(&Color_text_subselected); + } else { + gr_set_color_fast(&Color_text_normal); + } + + gr_printf(Cutscene_list_coords[gr_screen.res][0], Cutscene_list_coords[gr_screen.res][1] + y, Cutscenes[Cutscene_list[z]].name); + + y += font_height; + z++; + } + + if (Description_index != Selected_line) { + char *src; + + Description_index = Selected_line; + Text_size = 0; + src = Cutscenes[Cutscene_list[Description_index]].description; + if (src) { + Text_size = split_str(src, Cutscene_desc_coords[gr_screen.res][2], Text_line_size, Text_lines, Cutscene_max_text_lines[gr_screen.res]); + Assert(Text_size >= 0 && Text_size < Cutscene_max_text_lines[gr_screen.res]); + } + } + + if (Description_index >= 0) { + int len; + char line[MAX_TEXT_LINE_LEN + 1]; + + gr_set_color_fast(&Color_text_normal); + + y = 0; + z = Text_offset; + while (y + font_height <= Cutscene_desc_coords[gr_screen.res][3]) { + if (z >= Text_size) + break; + + len = Text_line_size[z]; + if (len > MAX_TEXT_LINE_LEN) + len = MAX_TEXT_LINE_LEN; + + strncpy(line, Text_lines[z], len); + line[len] = 0; + gr_string(Cutscene_desc_coords[gr_screen.res][0], Cutscene_desc_coords[gr_screen.res][1] + y, line); + + y += font_height; + z++; + } + } + + gr_flip(); +} diff --git a/src/debris/debris.cpp b/src/debris/debris.cpp new file mode 100644 index 0000000..3b5d781 --- /dev/null +++ b/src/debris/debris.cpp @@ -0,0 +1,1262 @@ +/* + * $Logfile: /Freespace2/code/Debris/Debris.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for the pieces of exploding object debris. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 16 8/01/99 1:13p Dave + * Fixed objsnd problem with hull debris pieces. + * + * 15 7/01/99 4:23p Dave + * Full support for multiple linked ambient engine sounds. Added "big + * damage" flag. + * + * 14 7/01/99 11:44a Dave + * Updated object sound system to allow multiple obj sounds per ship. + * Added hit-by-beam sound. Added killed by beam sound. + * + * 13 5/18/99 11:50a Andsager + * Remove unused object type OBJ_GHOST_SAVE + * + * 12 5/14/99 11:50a Andsager + * Added vaporize for SMALL ships hit by HUGE beams. Modified dying + * frame. Enlarged debris shards and range at which visible. + * + * 11 4/23/99 12:01p Johnson + * Added SIF_HUGE_SHIP + * + * 10 2/26/99 4:14p Dave + * Put in the ability to have multiple shockwaves for ships. + * + * 9 1/20/99 6:04p Dave + * Another bit of stuff for beam weapons. Ships will properly use them + * now, although they're really deadly. + * + * 8 12/03/98 3:14p Andsager + * Check in code that checks rotating submodel actually has ship subsystem + * + * 7 11/19/98 11:07p Andsager + * Check in of physics and collision detection of rotating submodels + * + * 6 11/13/98 10:13a Andsager + * simplify collision code + * + * 5 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 4 10/23/98 1:11p Andsager + * Make ship sparks emit correctly from rotating structures. + * + * 3 10/16/98 1:22p Andsager + * clean up header files + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 119 8/28/98 3:28p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 118 5/03/98 5:41p Mike + * Add Framecount to nprintf. + * + * 117 4/15/98 10:00p Allender + * make debris have own signature set. + * + * 116 4/15/98 9:42a Adam + * added 2 more explosion types (1, actually, but placeholder for 2) + * + * 115 4/13/98 4:52p Allender + * remove AI_frametime and us flFrametime instead. Make lock warning work + * in multiplayer for aspect seeking missiles. Debris fixups + * + * 114 4/10/98 12:16p Allender + * fix ship hit kill and debris packets + * + * 113 4/09/98 5:43p Allender + * multiplayer network object fixes. debris and self destructed ships + * should all sync up. Fix problem where debris pieces (hull pieces) were + * not getting a net signature + * + * 112 4/01/98 5:34p John + * Made only the used POFs page in for a level. Reduced some interp + * arrays. Made custom detail level work differently. + * + * 111 3/31/98 5:11p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * 110 3/26/98 5:21p John + * Added new code to preload all bitmaps at the start of a level. + * Commented it out, though. + * + * 109 3/23/98 12:20p Andsager + * Enable collision from rotation in ship_debris and ship_debris + * collisions. + * + * 108 3/19/98 12:09p John + * Fixed a bug using 6 characters. r_heavy was using local coordinates + * instead of world so all asteroid-ship and debris-ship hitpos's were in + * the wrong spot. Ok, I rearreanged some code to make it clearer also. + * + * + * 107 3/12/98 6:47p John + * MAde arcs on debris further than objrad*50 not render. + * + * 106 3/09/98 2:10p Andsager + * Put in checks for debris (other) with excessive velocity. + * + * 105 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 104 2/22/98 12:19p John + * Externalized some strings + * + * 103 2/20/98 8:31p Lawrance + * Add radius parm to sound_play_3d() + * + * 102 2/10/98 6:43p Lawrance + * Moved asteroid code to a separate lib. + * + * 101 2/07/98 2:14p Mike + * Improve asteroid splitting. Add ship:asteroid collisions. Timestamp + * ship:debris collisions. + * + * 100 2/06/98 7:45p John + * Reversed order of asteroid models so blown up ones are smaller. + * + * 99 2/06/98 7:28p John + * Made debris and particles not get created if > 200 m from eye. Added + * max_velocity to asteroid's physics info for aiding in throwing out + * collision pairs. + * + * 98 2/06/98 3:08p Mike + * More asteroid stuff, including resolving conflicts between the two + * asteroid_field structs! + * + * 97 2/06/98 12:25a Mike + * More asteroid stuff. + * + * 96 2/05/98 9:41p Mike + * Asteroid work, intermediate checkin to resolve compile errors. + * + * 95 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 94 2/05/98 12:51a Mike + * Early asteroid stuff. + * + * 93 2/03/98 6:01p Andsager + * Fix excessive rotvel in debris_create. Check using physics function + * check_rotvel_limit. + * + * 92 2/03/98 11:14a Andsager + * Temp check in to stop debris being created with excess rotvel + * + * 91 2/02/98 4:45p Mike + * Increase translational and rotational velocity imparted to debris + * pieces at request of Adam. + * + * 90 1/30/98 2:56p John + * Made debris arcs jump around. Made only 2/3 of the chunks have arcing + * + * 89 1/30/98 11:48a John + * Made debris arcs cast light. Added sound effects for them. + * + * 88 1/29/98 5:50p John + * Made electrical arcing on debris pieces be persistent from frame to + * frame + * + * 87 1/29/98 8:39a Andsager + * Changed mass and moment of intertia based area vs. volume + * + * 86 1/27/98 11:02a John + * Added first rev of sparks. Made all code that calls model_render call + * clear_instance first. Made debris pieces not render by default when + * clear_instance is called. + * + * 85 1/24/98 4:49p Lawrance + * Only delete hull piece if you can find an old one that isn't already + * about to die. + * + * 84 1/23/98 5:06p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * $NoKeywords: $ + */ + +#include "3d.h" +#include "bmpman.h" +#include "object.h" +#include "debris.h" +#include "fireballs.h" +#include "radar.h" +#include "missionparse.h" // For MAX_SPECIES_NAMES +#include "gamesnd.h" +#include "objectsnd.h" +#include "linklist.h" +#include "systemvars.h" +#include "multi.h" +#include "multimsgs.h" +#include "particle.h" +#include "freespace.h" +#include "multiutil.h" +#include "objcollide.h" +#include "timer.h" + +#define MAX_LIFE 10.0f +#define MIN_RADIUS_FOR_PERSISTANT_DEBRIS 50 // ship radius at which debris from it becomes persistant +#define DEBRIS_SOUND_DELAY 2000 // time to start debris sound after created + +// limit the number of hull debris chunks that can exist. +#define MAX_HULL_PIECES 10 +int Num_hull_pieces; // number of hull pieces in existance +debris Hull_debris_list; // head of linked list for hull debris chunks, for quick search + +debris Debris[MAX_DEBRIS_PIECES]; + +int Num_debris_pieces = 0; +int Debris_inited = 0; + +int Debris_model = -1; +int Debris_vaporize_model = -1; +int Debris_num_submodels = 0; +char * Debris_texture_files[MAX_SPECIES_NAMES] = { + NOX("debris01a"), // Terran + NOX("debris01b"), // Species B + NOX("debris01c"), // Shivan + }; + +int Debris_textures[MAX_SPECIES_NAMES]; + +#define MAX_DEBRIS_DIST 10000.0f // Debris goes away if it's this far away. +#define DEBRIS_DISTANCE_CHECK_TIME (10*1000) // Check every 10 seconds. +#define DEBRIS_INDEX(dp) (dp-Debris) + +#define MAX_SPEED_SMALL_DEBRIS 200 // maximum velocity of small debris piece +#define MAX_SPEED_BIG_DEBRIS 150 // maximum velocity of big debris piece +#define MAX_SPEED_CAPITAL_DEBRIS 100 // maximum velocity of capital debris piece +#define DEBRIS_SPEED_DEBUG + +// --------------------------------------------------------------------------------------- +// debris_start_death_roll() +// +// Start the sequence of a piece of debris writhing in unholy agony!!! +// +static void debris_start_death_roll(object *debris_obj, debris *debris_p) +{ + if (debris_p->is_hull) { + // tell everyone else to blow up the piece of debris + if( MULTIPLAYER_MASTER ) + send_debris_update_packet(debris_obj,DEBRIS_UPDATE_NUKE); + + int fireball_type = FIREBALL_EXPLOSION_LARGE1 + rand()%FIREBALL_NUM_LARGE_EXPLOSIONS; + fireball_create( &debris_obj->pos, fireball_type, OBJ_INDEX(debris_obj), debris_obj->radius*1.75f); + + // only play debris destroy sound if hull piece and it has been around for at least 2 seconds + if ( Missiontime > debris_p->time_started + 2*F1_0 ) { + snd_play_3d( &Snds[SND_MISSILE_IMPACT1], &debris_obj->pos, &View_position, debris_obj->radius ); + + } + } + + debris_obj->flags |= OF_SHOULD_BE_DEAD; +// demo_do_flag_dead(OBJ_INDEX(debris_obj)); +} + +// --------------------------------------------------------------------------------------- +// debris_init() +// +// This will get called at the start of each level. +// +void debris_init() +{ + int i; + + if ( !Debris_inited ) { + Debris_inited = 1; + } + + Debris_model = -1; + Debris_vaporize_model = -1; + Debris_num_submodels = 0; + + // Reset everything between levels + Num_debris_pieces = 0; + for (i=0; i-1) { + polymodel * pm; + pm = model_get(Debris_model); + Debris_num_submodels = pm->n_models; + } + + Debris_vaporize_model = model_load( NOX("debris02.pof"), 0, NULL ); + + for (i=0; iinstance; + + Assert(num >= 0 && num < MAX_DEBRIS_PIECES ); + db = &Debris[num]; + + Assert( db->flags & DEBRIS_USED ); + + // Swap in a different texture depending on the species + if ( (db->species > -1) && (db->species < MAX_SPECIES_NAMES) ) { + + pm = model_get( db->model_num ); + + if ( pm && (pm->n_textures == 1) ) { + swapped = pm->textures[0]; + pm->textures[0] = Debris_textures[db->species]; + } + } + + model_clear_instance( db->model_num ); + + // Only render electrical arcs if within 500m of the eye (for a 10m piece) + if ( vm_vec_dist_quick( &obj->pos, &Eye_position ) < obj->radius*50.0f ) { + for (i=0; iarc_timestamp[i] ) ) { + model_add_arc( db->model_num, db->submodel_num, &db->arc_pts[i][0], &db->arc_pts[i][1], MARC_TYPE_NORMAL ); + } + } + } + + if ( db->is_hull ) { + MONITOR_INC(NumHullDebrisRend,1); + submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos ); + } else { + MONITOR_INC(NumSmallDebrisRend,1); + submodel_render( db->model_num, db->submodel_num, &obj->orient, &obj->pos, MR_NO_LIGHTING ); + } + + if ((swapped!=-1) && pm) { + pm->textures[0] = swapped; + } +} + +// Removed the DEBRIS_EXPIRE flag, and remove item from Hull_debris_list +void debris_clear_expired_flag(debris *db) +{ + if ( db->flags & DEBRIS_EXPIRE ) { + db->flags &= ~DEBRIS_EXPIRE; + if ( db->is_hull ) { + Num_hull_pieces--; + list_remove(Hull_debris_list, db); + Assert( Num_hull_pieces >= 0 ); + } + } +} + +// --------------------------------------------------------------------------------------- +// debris_delete() +// +// Delete the debris object. This is only ever called via obj_delete(). Do not call directly. +// Use debris_start_death_roll() if you want to force a debris piece to die. +// +void debris_delete( object * obj ) +{ + int num; + debris *db; + + num = obj->instance; + Assert( Debris[num].objnum == OBJ_INDEX(obj)); + + db = &Debris[num]; + + Assert( Num_debris_pieces >= 0 ); + if ( db->is_hull && (db->flags & DEBRIS_EXPIRE) ) { + debris_clear_expired_flag(db); + } + + db->flags = 0; + Num_debris_pieces--; +} + +// If debris piece *db is far away from all players, make it go away very soon. +// In single player game, delete if MAX_DEBRIS_DIST from player. +// In multiplayer game, delete if MAX_DEBRIS_DIST from all players. +void maybe_delete_debris(debris *db) +{ + object *objp; + + if (timestamp_elapsed(db->next_distance_check)) { + if (!(Game_mode & GM_MULTIPLAYER)) { // In single player game, just check against player. + if (vm_vec_dist_quick(&Player_obj->pos, &Objects[db->objnum].pos) > MAX_DEBRIS_DIST) + db->lifeleft = 0.1f; + else + db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME); + } else { + for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->flags & OF_PLAYER_SHIP) { + if (vm_vec_dist_quick(&objp->pos, &Objects[db->objnum].pos) < MAX_DEBRIS_DIST) { + db->next_distance_check = timestamp(DEBRIS_DISTANCE_CHECK_TIME); + return; + } + } + } + db->lifeleft = 0.1f; + } + } +} + +// broke debris_move into debris_process_pre and debris_process_post as was done with all +// *_move functions on 8/13 by MK and MA. +void debris_process_pre( object *objp, float frame_time) +{ +} + +MONITOR(NumSmallDebris); +MONITOR(NumHullDebris); + +// --------------------------------------------------------------------------------------- +// debris_process_post() +// +// Do various updates to debris: check if time to die, start fireballs +// +// parameters: obj => pointer to debris object +// frame_time => time elapsed since last debris_move() called +// +// Maybe delete debris if it's very far away from player. +void debris_process_post(object * obj, float frame_time) +{ + int i, num; + num = obj->instance; + + int objnum = OBJ_INDEX(obj); + Assert( Debris[num].objnum == objnum ); + debris *db = &Debris[num]; + + if ( db->is_hull ) { + MONITOR_INC(NumHullDebris,1); + radar_plot_object( obj ); + + if ( timestamp_elapsed(db->sound_delay) ) { + obj_snd_assign(objnum, SND_DEBRIS, &vmd_zero_vector, 0); + db->sound_delay = 0; + } + } else { + MONITOR_INC(NumSmallDebris,1); + } + + if ( db->lifeleft >= 0.0f) { + db->lifeleft -= frame_time; + if ( db->lifeleft < 0.0f ) { + debris_start_death_roll(obj, db); + } + } + + maybe_delete_debris(db); // Make this debris go away if it's very far away. + + // ================== DO THE ELECTRIC ARCING STUFF ===================== + if ( db->arc_frequency <= 0 ) { + return; // If arc_frequency <= 0, this piece has no arcs on it + } + + if ( !timestamp_elapsed(db->fire_timeout) && timestamp_elapsed(db->next_fireball)) { + + // start the next fireball up in the next 50 - 100 ms + //db->next_fireball = timestamp_rand(60,80); + + db->next_fireball = timestamp_rand(db->arc_frequency,db->arc_frequency*2 ); + db->arc_frequency += 100; + + if (db->is_hull) { + + int n, n_arcs = ((rand()>>5) % 3)+1; // Create 1-3 sparks + + vector v1, v2, v3, v4; + submodel_get_two_random_points( db->model_num, db->submodel_num, &v1, &v2 ); + submodel_get_two_random_points( db->model_num, db->submodel_num, &v3, &v4 ); + + n = 0; + + int a = 100, b = 1000; + int lifetime = (myrand()%((b)-(a)+1))+(a); + + // Create the spark effects + for (i=0; iarc_timestamp[i] ) ) { + //db->arc_timestamp[i] = timestamp_rand(400,1000); // live up to a second + db->arc_timestamp[i] = timestamp(lifetime); // live up to a second + + switch( n ) { + case 0: + db->arc_pts[i][0] = v1; + db->arc_pts[i][1] = v2; + break; + case 1: + db->arc_pts[i][0] = v2; + db->arc_pts[i][1] = v3; + break; + + case 2: + db->arc_pts[i][0] = v2; + db->arc_pts[i][1] = v4; + break; + + default: + Int3(); + } + + n++; + if ( n == n_arcs ) + break; // Don't need to create anymore + } + } + + + // rotate v2 out of local coordinates into world. + // Use v2 since it is used in every bolt. See above switch(). + vector snd_pos; + vm_vec_unrotate(&snd_pos, &v2, &obj->orient); + vm_vec_add2(&snd_pos, &obj->pos ); + + //Play a sound effect + if ( lifetime > 750 ) { + // 1.00 second effect + snd_play_3d( &Snds[SND_DEBRIS_ARC_05], &snd_pos, &View_position, obj->radius ); + } else if ( lifetime > 500 ) { + // 0.75 second effect + snd_play_3d( &Snds[SND_DEBRIS_ARC_04], &snd_pos, &View_position, obj->radius ); + } else if ( lifetime > 250 ) { + // 0.50 second effect + snd_play_3d( &Snds[SND_DEBRIS_ARC_03], &snd_pos, &View_position, obj->radius ); + } else if ( lifetime > 100 ) { + // 0.25 second effect + snd_play_3d( &Snds[SND_DEBRIS_ARC_02], &snd_pos, &View_position, obj->radius ); + } else { + // 0.10 second effect + snd_play_3d( &Snds[SND_DEBRIS_ARC_01], &snd_pos, &View_position, obj->radius ); + } + + } + + + + } + + for (i=0; iarc_timestamp[i] ) ) { + if ( timestamp_elapsed( db->arc_timestamp[i] ) ) { + // Kill off the spark + db->arc_timestamp[i] = timestamp(-1); + } else { + // Maybe move a vertex.... 20% of the time maybe? + int mr = myrand(); + if ( mr < RAND_MAX/5 ) { + vector v1, v2; + submodel_get_two_random_points( db->model_num, db->submodel_num, &v1, &v2 ); + db->arc_pts[i][mr % 2] = v1; + } + } + } + } + +} + +// --------------------------------------------------------------------------------------- +// debris_find_oldest() +// +// Locate the oldest hull debris chunk. Search through the Hull_debris_list, which is a list +// of all the hull debris chunks. +// +int debris_find_oldest() +{ + int oldest_index; + fix oldest_time; + debris *db; + + oldest_index = -1; + oldest_time = 0x7fffffff; + + for ( db = GET_FIRST(&Hull_debris_list); db != END_OF_LIST(&Hull_debris_list); db = GET_NEXT(db) ) { + if ( (db->time_started < oldest_time) && !(Objects[db->objnum].flags & OF_SHOULD_BE_DEAD) ) { + oldest_index = DEBRIS_INDEX(db); + oldest_time = db->time_started; + } + } + + return oldest_index; +} + +#define DEBRIS_ROTVEL_SCALE 5.0f +void calc_debris_physics_properties( physics_info *pi, vector *min, vector *max ); +// --------------------------------------------------------------------------------------- +// debris_create() +// +// Create debris from an object +// +// exp_force: Explosion force, used to assign velocity to pieces. +// 1.0f assigns velocity like before. 2.0f assigns twice as much to non-inherited part of velocity +object *debris_create(object *source_obj, int model_num, int submodel_num, vector *pos, vector *exp_center, int hull_flag, float exp_force) +{ + int i, n, objnum, parent_objnum; + object *obj; + ship *shipp; + debris *db; + polymodel *pm; + int vaporize; + + parent_objnum = OBJ_INDEX(source_obj); + + Assert( (source_obj->type == OBJ_SHIP ) || (source_obj->type == OBJ_GHOST)); + Assert( source_obj->instance >= 0 && source_obj->instance < MAX_SHIPS ); + shipp = &Ships[source_obj->instance]; + vaporize = (shipp->flags &SF_VAPORIZE); + + if ( !hull_flag ) { + // Make vaporize debris seen from farther away + float dist = vm_vec_dist_quick( pos, &Eye_position ); + if (vaporize) { + dist /= 2.0f; + } + if ( dist > 200.0f ) { + //mprintf(( "Not creating debris that is %.1f m away\n", dist )); + return NULL; + } + } + + if ( hull_flag && (Num_hull_pieces >= MAX_HULL_PIECES ) ) { + // cause oldest hull debris chunk to blow up + n = debris_find_oldest(); + if ( n >= 0 ) { + debris_start_death_roll(&Objects[Debris[n].objnum], &Debris[n] ); + } + } + + for (n=0; nlifeleft = 2.0f * ((float) myrand()/(float) RAND_MAX) + 0.5f; + else + db->lifeleft = -1.0f; // large hull pieces stay around forever + } else { + db->lifeleft = (i2fl(myrand())/i2fl(RAND_MAX))*2.0f+0.1f; + } + + // increase lifetime for vaporized debris + if (vaporize) { + db->lifeleft *= 3.0f; + } + db->flags |= DEBRIS_USED; + db->is_hull = hull_flag; + db->source_objnum = parent_objnum; + db->source_sig = source_obj->signature; + db->ship_info_index = shipp->ship_info_index; + db->team = shipp->team; + db->fire_timeout = 0; // if not changed, timestamp_elapsed() will return false + db->time_started = Missiontime; + db->species = Ship_info[shipp->ship_info_index].species; + db->next_distance_check = (myrand() % 2000) + 4*DEBRIS_DISTANCE_CHECK_TIME; + + for (i=0; iarc_timestamp[i] = timestamp(-1); + // vector arc_pts[MAX_DEBRIS_ARCS][2]; // The endpoints of each arc + } + + if ( db->is_hull ) { + // Only make 1/2 of the pieces have arcs + if ( myrand() < RAND_MAX*2/3 ) { + db->arc_frequency = 1000; + } else { + db->arc_frequency = 0; + } + } else { + db->arc_frequency = 0; + } + + if ( model_num < 0 ) { + if (vaporize) { + db->model_num = Debris_vaporize_model; + } else { + db->model_num = Debris_model; + } + db->submodel_num = (myrand()>>4) % Debris_num_submodels; + } else { + db->model_num = model_num; + db->submodel_num = submodel_num; + } + float radius = submodel_get_radius( db->model_num, db->submodel_num ); + + db->next_fireball = timestamp_rand(500,2000); //start one 1/2 - 2 secs later + + if ( pos == NULL ) + pos = &source_obj->pos; + + uint flags = OF_RENDERS | OF_PHYSICS; + if ( hull_flag ) + flags |= OF_COLLIDES; + objnum = obj_create( OBJ_DEBRIS, parent_objnum, n, &source_obj->orient, pos, radius, flags ); + if ( objnum == -1 ) { + mprintf(("Couldn't create debris object -- out of object slots\n")); + return NULL; + } + + db->objnum = objnum; + + obj = &Objects[objnum]; + + // assign the network signature. The signature will be 0 for non-hull pieces, but since that + // is our invalid signature, it should be okay. + obj->net_signature = 0; + if ( (Game_mode & GM_MULTIPLAYER) && hull_flag ) { + obj->net_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS ); + } + + // -- No long need shield: bset_shield_strength(obj, 100.0f); // Hey! Set to some meaningful value! + + if (source_obj->type == OBJ_SHIP) { + obj->hull_strength = Ship_info[Ships[source_obj->instance].ship_info_index].initial_hull_strength/8.0f; + } else + obj->hull_strength = 10.0f; + + Num_debris_pieces++; + + vector rotvel, radial_vel, to_center; + + if ( exp_center ) + vm_vec_sub( &to_center,pos, exp_center ); + else + vm_vec_zero(&to_center); + + float scale; + + if ( hull_flag ) { + float t; + scale = exp_force * i2fl((myrand()%20) + 10); // for radial_vel away from location of blast center + db->sound_delay = timestamp(DEBRIS_SOUND_DELAY); + + // set up physics mass and I_inv for hull debris pieces + pm = model_get(model_num); + vector *min, *max; + min = &pm->submodel[submodel_num].min; + max = &pm->submodel[submodel_num].max; + calc_debris_physics_properties( &obj->phys_info, min, max ); + + // limit the amount of time that fireballs appear + // let fireball length be linked to radius of ship. Range is .33 radius => 3.33 radius seconds. + t = 1000*Objects[db->source_objnum].radius/3 + myrand()%(fl2i(1000*3*Objects[db->source_objnum].radius)); + db->fire_timeout = timestamp(fl2i(t)); // fireballs last from 5 - 30 seconds + + if ( Objects[db->source_objnum].radius < MIN_RADIUS_FOR_PERSISTANT_DEBRIS ) { + db->flags |= DEBRIS_EXPIRE; // debris can expire + Num_hull_pieces++; + list_append(&Hull_debris_list, db); + } else { + nprintf(("Alan","A forever chunk of debris was created from ship with radius %f\n",Objects[db->source_objnum].radius)); + } + } + else { + scale = exp_force * i2fl((myrand()%20) + 10); // for radial_vel away from blast center (non-hull) + } + + if ( vm_vec_mag_squared( &to_center ) < 0.1f ) { + vm_vec_rand_vec_quick(&radial_vel); + vm_vec_scale(&radial_vel, scale ); + } + else { + vm_vec_normalize(&to_center); + vm_vec_copy_scale(&radial_vel, &to_center, scale ); + } + + // MK: This next line causes debris pieces to get between 50% and 100% of the parent ship's + // velocity. What would be very cool is if the rotational velocity of the parent would become + // translational velocity of the debris piece. This would be based on the location of the debris + // piece in the parent object. + + // DA: here we need to vel_from_rot = w x to_center, where w is world is unrotated to world coords and offset is the + // displacement fromt the center of the parent object to the center of the debris piece + + vector world_rotvel, vel_from_rotvel; + vm_vec_unrotate ( &world_rotvel, &source_obj->phys_info.rotvel, &source_obj->orient ); + vm_vec_crossprod ( &vel_from_rotvel, &world_rotvel, &to_center ); + vm_vec_scale ( &vel_from_rotvel, DEBRIS_ROTVEL_SCALE); + + vm_vec_add (&obj->phys_info.vel, &radial_vel, &source_obj->phys_info.vel); + vm_vec_add2(&obj->phys_info.vel, &vel_from_rotvel); + +#ifdef DEBRIS_SPEED_DEBUG + // check that debris is not created with too high a velocity + if (hull_flag) { + int ship_info_flag = Ship_info[Ships[source_obj->instance].ship_info_index].flags; + if (ship_info_flag & (SIF_SMALL_SHIP | SIF_NOT_FLYABLE | SIF_HARMLESS)) { + if (vm_vec_mag_squared(&obj->phys_info.vel) > MAX_SPEED_SMALL_DEBRIS*MAX_SPEED_SMALL_DEBRIS) { + float scale = MAX_SPEED_SMALL_DEBRIS / vm_vec_mag(&obj->phys_info.vel); + vm_vec_scale(&obj->phys_info.vel, scale); + } + } else if (ship_info_flag & SIF_BIG_SHIP) { + if (vm_vec_mag_squared(&obj->phys_info.vel) > MAX_SPEED_BIG_DEBRIS*MAX_SPEED_BIG_DEBRIS) { + float scale = MAX_SPEED_BIG_DEBRIS / vm_vec_mag(&obj->phys_info.vel); + vm_vec_scale(&obj->phys_info.vel, scale); + } + } else if (ship_info_flag & SIF_HUGE_SHIP) { + if (vm_vec_mag_squared(&obj->phys_info.vel) > MAX_SPEED_CAPITAL_DEBRIS*MAX_SPEED_CAPITAL_DEBRIS) { + float scale = MAX_SPEED_CAPITAL_DEBRIS / vm_vec_mag(&obj->phys_info.vel); + vm_vec_scale(&obj->phys_info.vel, scale); + } + } else { + Warning(LOCATION, "Ship has info flag that is not among the following: SMALL, NOT_FLYABLE, HARMLESS, BIG, CAPITAL, SUPERCAP"); + } + } +#endif + +// vm_vec_scale_add(&obj->phys_info.vel, &radial_vel, &source_obj->phys_info.vel, frand()/2.0f + 0.5f); +// nprintf(("Andsager","object vel from rotvel: %0.2f, %0.2f, %0.2f\n",vel_from_rotvel.x, vel_from_rotvel.y, vel_from_rotvel.z)); + +// make sure rotational velocity does not get too high + if (radius < 1.0) { + radius = 1.0f; + } + + scale = ( 6.0f + i2fl(myrand()%4) ) / radius; + + vm_vec_rand_vec_quick(&rotvel); + vm_vec_scale(&rotvel, scale); + + obj->phys_info.flags |= PF_DEAD_DAMP; + obj->phys_info.rotvel = rotvel; + check_rotvel_limit( &obj->phys_info ); + + + // blow out his reverse thrusters. Or drag, same thing. + obj->phys_info.rotdamp = 10000.0f; + obj->phys_info.side_slip_time_const = 10000.0f; + obj->phys_info.flags |= (PF_REDUCED_DAMP | PF_DEAD_DAMP); // set damping equal for all axis and not changable + + vm_vec_zero(&obj->phys_info.max_vel); // make so he can't turn on his own VOLITION anymore. + vm_vec_zero(&obj->phys_info.max_rotvel); // make so he can't change speed on his own VOLITION anymore. + + + // ensure vel is valid + Assert( !vm_is_vec_nan(&obj->phys_info.vel) ); + +// if ( hull_flag ) { +// vm_vec_zero(&obj->phys_info.vel); +// vm_vec_zero(&obj->phys_info.rotvel); +// } + + return obj; +} + +// --------------------------------------------------------------------------------------- +// debris_hit() +// +// Alas, poor debris_obj got whacked. Fortunately, we know who did it, where and how hard, so we +// can do something about it. +// +void debris_hit(object *debris_obj, object *other_obj, vector *hitpos, float damage) +{ + debris *debris_p = &Debris[debris_obj->instance]; + + + // Do a little particle spark shower to show we hit + { + particle_emitter pe; + + pe.pos = *hitpos; // Where the particles emit from + pe.vel = debris_obj->phys_info.vel; // Initial velocity of all the particles + + vector tmp_norm; + vm_vec_sub( &tmp_norm, hitpos, &debris_obj->pos ); + vm_vec_normalize_safe(&tmp_norm); + + pe.normal = tmp_norm; // What normal the particle emit around + pe.normal_variance = 0.3f; // How close they stick to that normal 0=good, 1=360 degree + pe.min_rad = 0.20f; // Min radius + pe.max_rad = 0.40f; // Max radius + + // Sparks for first time at this spot + pe.num_low = 10; // Lowest number of particles to create + pe.num_high = 10; // Highest number of particles to create + pe.normal_variance = 0.3f; // How close they stick to that normal 0=good, 1=360 degree + pe.min_vel = 0.0f; // How fast the slowest particle can move + pe.max_vel = 10.0f; // How fast the fastest particle can move + pe.min_life = 0.25f; // How long the particles live + pe.max_life = 0.75f; // How long the particles live + particle_emit( &pe, PARTICLE_FIRE, 0 ); + } + + // multiplayer clients bail here + if(MULTIPLAYER_CLIENT){ + return; + } + + if ( damage < 0.0f ) { + damage = 0.0f; + } + + debris_obj->hull_strength -= damage; + + if (debris_obj->hull_strength < 0.0f) { + debris_start_death_roll(debris_obj, debris_p ); + } else { + // otherwise, give all the other players an update on the debris + if(MULTIPLAYER_MASTER){ + send_debris_update_packet(debris_obj,DEBRIS_UPDATE_UPDATE); + } + } +} + +// --------------------------------------------------------------------------------------- +// debris_check_collision() +// +// See if poor debris object *obj got whacked by evil *other_obj at point *hitpos. +// NOTE: debris_hit_info pointer NULL for debris:weapon collision, otherwise debris:ship collision. +// Return true if hit, else return false. +// +#pragma warning ( push ) +#pragma warning ( disable : 4701 ) +int debris_check_collision(object *pdebris, object *other_obj, vector *hitpos, collision_info_struct *debris_hit_info) +{ + mc_info mc; + int num; + + Assert( pdebris->type == OBJ_DEBRIS ); + + num = pdebris->instance; + Assert( num >= 0 ); + + Assert( Debris[num].objnum == OBJ_INDEX(pdebris)); + + // debris_hit_info NULL - so debris-weapon collision + if ( debris_hit_info == NULL ) { + // debris weapon collision + Assert( other_obj->type == OBJ_WEAPON ); + mc.model_num = Debris[num].model_num; // Fill in the model to check + mc.submodel_num = Debris[num].submodel_num; + model_clear_instance( mc.model_num ); + mc.orient = &pdebris->orient; // The object's orient + mc.pos = &pdebris->pos; // The object's position + mc.p0 = &other_obj->last_pos; // Point 1 of ray to check + mc.p1 = &other_obj->pos; // Point 2 of ray to check + mc.flags = (MC_CHECK_MODEL | MC_SUBMODEL); + + if (model_collide(&mc)) { + *hitpos = mc.hit_point_world; + } + + return mc.num_hits; + } + + // debris ship collision -- use debris_hit_info to calculate physics + object *ship_obj = other_obj; + Assert( ship_obj->type == OBJ_SHIP ); + + object *heavy = debris_hit_info->heavy; + object *light = debris_hit_info->light; + object *heavy_obj = heavy; + object *light_obj = light; + + vector zero, p0, p1; + vm_vec_zero(&zero); + vm_vec_sub(&p0, &light->last_pos, &heavy->last_pos); + vm_vec_sub(&p1, &light->pos, &heavy->pos); + + mc.pos = &zero; // The object's position + mc.p0 = &p0; // Point 1 of ray to check + mc.p1 = &p1; // Point 2 of ray to check + + // find the light object's position in the heavy object's reference frame at last frame and also in this frame. + vector p0_temp, p0_rotated; + + // Collision detection from rotation enabled if at rotaion is less than 30 degree in frame + // This should account for all ships + if ( (vm_vec_mag_squared(&heavy->phys_info.rotvel) * flFrametime*flFrametime) < (PI*PI/36) ) { + // collide_rotate calculate (1) start position and (2) relative velocity + debris_hit_info->collide_rotate = 1; + vm_vec_rotate(&p0_temp, &p0, &heavy->last_orient); + vm_vec_unrotate(&p0_rotated, &p0_temp, &heavy->orient); + mc.p0 = &p0_rotated; // Point 1 of ray to check + vm_vec_sub(&debris_hit_info->light_rel_vel, &p1, &p0_rotated); + vm_vec_scale(&debris_hit_info->light_rel_vel, 1/flFrametime); + } else { + debris_hit_info->collide_rotate = 0; + vm_vec_sub(&debris_hit_info->light_rel_vel, &light->phys_info.vel, &heavy->phys_info.vel); + } + + int mc_ret_val = 0; + + if ( debris_hit_info->heavy == ship_obj ) { // ship is heavier, so debris is sphere. Check sphere collision against ship poly model + mc.model_num = Ships[ship_obj->instance].modelnum; // Fill in the model to check + mc.orient = &ship_obj->orient; // The object's orient + mc.radius = pdebris->radius; + mc.flags = (MC_CHECK_MODEL | MC_CHECK_SPHERELINE); + + // copy important data + int copy_flags = mc.flags; // make a copy of start end positions of sphere in big ship RF + vector copy_p0, copy_p1; + copy_p0 = *mc.p0; + copy_p1 = *mc.p1; + + // first test against the sphere - if this fails then don't do any submodel tests + mc.flags = MC_ONLY_SPHERE | MC_CHECK_SPHERELINE; + + int submodel_list[MAX_ROTATING_SUBMODELS]; + int num_rotating_submodels = 0; + polymodel *pm; + + ship_model_start(ship_obj); + + if (model_collide(&mc)) { + + // Set earliest hit time + debris_hit_info->hit_time = FLT_MAX; + + // Do collision the cool new way + if ( debris_hit_info->collide_rotate ) { + // We collide with the sphere, find the list of rotating submodels and test one at a time + model_get_rotating_submodel_list(submodel_list, &num_rotating_submodels, heavy_obj); + + // Get polymodel and turn off all rotating submodels, collide against only 1 at a time. + pm = model_get(Ships[heavy_obj->instance].modelnum); + + // turn off all rotating submodels and test for collision + for (int i=0; isubmodel[submodel_list[i]].blown_off = 1; + } + + // reset flags to check MC_CHECK_MODEL | MC_CHECK_SPHERELINE and maybe MC_CHECK_INVISIBLE_FACES and MC_SUBMODEL_INSTANCE + mc.flags = copy_flags | MC_SUBMODEL_INSTANCE; + + // check each submodel in turn + for (int i=0; isubmodel[submodel_list[i]].blown_off = 0; + + // set angles for last frame (need to set to prev to get p0) + angles copy_angles = pm->submodel[submodel_list[i]].angs; + + // find the start and end positions of the sphere in submodel RF + pm->submodel[submodel_list[i]].angs = pm->submodel[submodel_list[i]].sii->prev_angs; + world_find_model_point(&p0, &light_obj->last_pos, pm, submodel_list[i], &heavy_obj->last_orient, &heavy_obj->last_pos); + + pm->submodel[submodel_list[i]].angs = copy_angles; + world_find_model_point(&p1, &light_obj->pos, pm, submodel_list[i], &heavy_obj->orient, &heavy_obj->pos); + + mc.p0 = &p0; + mc.p1 = &p1; + // mc.pos = zero // in submodel RF + + mc.orient = &vmd_identity_matrix; + mc.submodel_num = submodel_list[i]; + + if ( model_collide(&mc) ) { + if ( mc.hit_dist < debris_hit_info->hit_time ) { + mc_ret_val = 1; + + // set up debris_hit_info common + set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_ROT_HIT); + model_find_world_point(&debris_hit_info->hit_pos, &mc.hit_point, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero); + + // set up debris_hit_info for rotating submodel + if (debris_hit_info->edge_hit == 0) { + model_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel); + } + + // find position in submodel RF of light object at collison + vector int_light_pos, diff; + vm_vec_sub(&diff, mc.p1, mc.p0); + vm_vec_scale_add(&int_light_pos, mc.p0, &diff, mc.hit_dist); + model_find_world_point(&debris_hit_info->light_collision_cm_pos, &int_light_pos, mc.model_num, mc.hit_submodel, &heavy_obj->orient, &zero); + } + } + // Don't look at this submodel again + pm->submodel[submodel_list[i]].blown_off = 1; + } + + } + + // Recover and do usual ship_ship collision, but without rotating submodels + mc.flags = copy_flags; + *mc.p0 = copy_p0; + *mc.p1 = copy_p1; + mc.orient = &heavy_obj->orient; + + // usual ship_ship collision test + if ( model_collide(&mc) ) { + // check if this is the earliest hit + if (mc.hit_dist < debris_hit_info->hit_time) { + mc_ret_val = 1; + + set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_NO_ROT_HIT); + + // get collision normal if not edge hit + if (debris_hit_info->edge_hit == 0) { + model_find_obj_dir(&debris_hit_info->collision_normal, &mc.hit_normal, heavy_obj, mc.hit_submodel); + } + + // find position in submodel RF of light object at collison + vector diff; + vm_vec_sub(&diff, mc.p1, mc.p0); + vm_vec_scale_add(&debris_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist); + + } + } + + ship_model_stop( ship_obj ); + } + + } else { + // Debris is heavier obj + mc.model_num = Debris[num].model_num; // Fill in the model to check + mc.submodel_num = Debris[num].submodel_num; + model_clear_instance( mc.model_num ); + mc.orient = &pdebris->orient; // The object's orient + mc.radius = model_get_core_radius( Ships[ship_obj->instance].modelnum ); + + // check for collision between debris model and ship sphere + mc.flags = (MC_CHECK_MODEL | MC_SUBMODEL | MC_CHECK_SPHERELINE); + + mc_ret_val = model_collide(&mc); + + if (mc_ret_val) { + set_hit_struct_info(debris_hit_info, &mc, SUBMODEL_NO_ROT_HIT); + + // set normal if not edge hit + if ( !debris_hit_info->edge_hit ) { + vm_vec_unrotate(&debris_hit_info->collision_normal, &mc.hit_normal, &heavy->orient); + } + + // find position in submodel RF of light object at collison + vector diff; + vm_vec_sub(&diff, mc.p1, mc.p0); + vm_vec_scale_add(&debris_hit_info->light_collision_cm_pos, mc.p0, &diff, mc.hit_dist); + + } + } + + + if ( mc_ret_val ) { + + // SET PHYSICS PARAMETERS + // already have (hitpos - heavy) and light_cm_pos + // get heavy cm pos - already have light_cm_pos + debris_hit_info->heavy_collision_cm_pos = zero; + + // get r_heavy and r_light + debris_hit_info->r_heavy = debris_hit_info->hit_pos; + vm_vec_sub(&debris_hit_info->r_light, &debris_hit_info->hit_pos, &debris_hit_info->light_collision_cm_pos); + + // set normal for edge hit + if ( debris_hit_info->edge_hit ) { + vm_vec_copy_normalize(&debris_hit_info->collision_normal, &debris_hit_info->r_light); + vm_vec_negate(&debris_hit_info->collision_normal); + } + + // get world hitpos + vm_vec_add(hitpos, &debris_hit_info->heavy->pos, &debris_hit_info->r_heavy); + + return 1; + } else { + // no hit + return 0; + } +} +#pragma warning ( pop ) + + +// --------------------------------------------------------------------------------------- +// debris_get_team() +// +// Return the team field for a debris object +// +int debris_get_team(object *objp) +{ + Assert( objp->type == OBJ_DEBRIS ); + Assert( objp->instance >= 0 && objp->instance < MAX_DEBRIS_PIECES ); + return Debris[objp->instance].team; +} + +// fills in debris physics properties when created, specifically mass and moment of inertia +void calc_debris_physics_properties( physics_info *pi, vector *mins, vector *maxs ) +{ + float dx, dy, dz, mass; + dx = maxs->x - mins->x; + dy = maxs->y - mins->y; + dz = maxs->z - mins->z; + + // John, with new bspgen, just set pi->mass = mass + mass = 0.12f * dx * dy * dz; + pi->mass = (float) pow(mass, 0.6666667f) * 4.65f; + + pi->I_body_inv.rvec.x = 12.0f / (pi->mass * (dy*dy + dz*dz)); + pi->I_body_inv.rvec.y = 0.0f; + pi->I_body_inv.rvec.z = 0.0f; + + pi->I_body_inv.uvec.x = 0.0f; + pi->I_body_inv.uvec.y = 12.0f / (pi->mass * (dx*dx + dz*dz)); + pi->I_body_inv.uvec.z = 0.0f; + + pi->I_body_inv.fvec.x = 0.0f; + pi->I_body_inv.fvec.y = 0.0f; + pi->I_body_inv.fvec.z = 12.0f / (pi->mass * (dx*dx + dy*dy)); +} + + diff --git a/src/debugconsole/console.cpp b/src/debugconsole/console.cpp new file mode 100644 index 0000000..21b66be --- /dev/null +++ b/src/debugconsole/console.cpp @@ -0,0 +1,790 @@ +/* + * $Logfile: /Freespace2/code/DebugConsole/Console.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for managing the debug console window. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 4 6/04/99 10:35a Dave + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 17 2/05/98 11:43a Allender + * enhcanced network statistic collection. Made callback in debug console + * to do networking if player is in the console + * + * 16 1/22/98 6:42p John + * Move game_flush out of debug console into freespace. Made object + * pair code throw out some weapons that turn. Added stats for how many + * object pair are actually checked. + * + * 15 1/16/98 11:56a Allender + * ability to scroll back in debug console, and call game_flush() when + * leaving + * + * 14 1/10/98 1:14p John + * Added explanation to debug console commands + * + * 13 12/21/97 4:39p John + * fixed bug in name compare + * + * 12 12/21/97 4:33p John + * Made debug console functions a class that registers itself + * automatically, so you don't need to add the function to + * debugfunctions.cpp. + * + * 11 9/13/97 9:31a Lawrance + * if playing a demo, clear key filter then reset it once done with debug + * console + * + * 10 9/09/97 3:39p Sandeep + * warning level 4 bugs + * + * 9 6/13/97 3:50p John + * sped up debug console printf + * + * 8 6/13/97 3:27p John + * made debug console pretty + * + * 7 6/09/97 9:24a John + * Changed the way fonts are set. + * + * 6 5/29/97 3:09p John + * Took out debug menu. + * Made software scaler draw larger bitmaps. + * Optimized Direct3D some. + * + * 5 5/13/97 4:07p John + * made numbers also be marked as strings. + * + * 4 4/28/97 5:24p John + * made so : and \ don't get parsed out in debug console. + * + * 3 4/24/97 11:49a John + * added new lighting commands to console. + * + * 2 4/24/97 10:36a John + * moved the debug console stuff into it's own lib... made it compile with + * Fred. + * + * 1 4/24/97 10:16a John + * Initial rev + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include +#include + +#include "pstypes.h" +#include "freespace.h" +#include "font.h" +#include "timer.h" +#include "2d.h" +#include "key.h" +#include "alphacolors.h" +#include "osapi.h" + +#define MAX_COMMANDS 300 + +static int Num_debug_commands = 0; +static debug_command *Debug_command[MAX_COMMANDS]; + + +debug_command::debug_command(char *_name, char *_help, void (*_func)() ) +{ + int i; + + if ( Num_debug_commands >= MAX_COMMANDS ) { + Int3(); // Too many debug console commands!! Increase MAX_COMMANDS!! + return; + } + + for (i=0; iname, _name ); + + if ( ret == 0) { + Int3(); // This debug console command already exists!!!! + return; + } else if ( ret > 0 ) { + break; // Insert it here + + } else if ( ret < 0 ) { + // do nothing + } + } + + if ( i < Num_debug_commands ) { + // Insert it at element i + int j; + for (j=Num_debug_commands; j>i; j-- ) { + Debug_command[j] = Debug_command[j-1]; + } + Debug_command[i] = this; + Num_debug_commands++; + } else { + Debug_command[Num_debug_commands] = this; + Num_debug_commands++; + } + + name = _name; + help = _help; + func = _func; +} + +// some global variables +int Dc_command; // If this is set, then process the command +int Dc_help; // If this is set, then print out the help text in the form, "usage: ... \nLong description\n" ); +int Dc_status; // If this is set, then print out the current status of the command. +char *Dc_arg; // The (lowercased) string value of the argument retrieved from dc_arg +char *Dc_arg_org; // Dc_arg before it got converted to lowercase +uint Dc_arg_type; // The type of dc_arg. +char *Dc_command_line; // The rest of the command line, from the end of the last processed arg on. +int Dc_arg_int; // If Dc_arg_type & ARG_INT is set, then this is the value +float Dc_arg_float; // If Dc_arg_type & ARG_FLOAT is set, then this is the value + +int scroll_times = 0; // incremented each time display scrolls + +int debug_inited = 0; + +#define DROWS 25 +#define DCOLS 80 + +int debug_x=0, debug_y=0; +char debug_text[DROWS][DCOLS]; + + +static char command_line[1024]; +static int command_line_pos = 0; +#define DEBUG_HISTORY 16 +static char oldcommand_line[DEBUG_HISTORY][1024]; +int last_oldcommand=-1; +int command_scroll = 0; + +///=========================== SCANNER ======================= +typedef enum { + LETTER, QUOTE, SPECIAL, EOF_CODE, DIGIT, +} CHAR_CODE; + +typedef enum { + NO_TOKEN, IDENTIFIER, NUMBER, STRING, +} TOKEN_CODE; + + +#define MAX_TOKEN_STRING_LENGTH 128 + +char scanner_ch; +TOKEN_CODE scanner_token; + +char scanner_token_string[MAX_TOKEN_STRING_LENGTH]; +char scanner_word_string[MAX_TOKEN_STRING_LENGTH]; +char * scanner_bufferp = ""; +char * scanner_tokenp = scanner_token_string; + +CHAR_CODE scanner_char_table[256]; + +#define scanner_char_code(x) scanner_char_table[x] + +void scanner_get_char() +{ + if ( *scanner_bufferp == '\0' ) { + scanner_ch = 0; + return; + } + scanner_ch = *scanner_bufferp++; +} + +void scanner_init() +{ + int ch; + for (ch=0; ch<256; ++ch) scanner_char_table[ch] = SPECIAL; + for (ch='0'; ch<='9'; ++ch) scanner_char_table[ch] = DIGIT; + for (ch='A'; ch<='Z'; ++ch) scanner_char_table[ch] = LETTER; + for (ch='a'; ch<='z'; ++ch) scanner_char_table[ch] = LETTER; + + scanner_char_table['.'] = DIGIT; + scanner_char_table['-'] = DIGIT; + scanner_char_table['+'] = DIGIT; + + scanner_char_table['_'] = LETTER; + scanner_char_table[34] = QUOTE; + scanner_char_table[0] = EOF_CODE; + + + scanner_char_table[':'] = LETTER; + scanner_char_table['\\'] = LETTER; + + scanner_ch = 0; +} + + +void scanner_skip_blanks() +{ + while( (scanner_ch ==' ') || (scanner_ch =='\t') ) + scanner_get_char(); +} + + +void scanner_downshift_word() +{ + int offset = 'a' - 'A'; + char * tp; + + strcpy( scanner_word_string, scanner_token_string ); + + tp = scanner_word_string; + do { + *tp = (char)((*tp>='A') && (*tp <='Z') ? *tp + offset : *tp) ; + tp++; + } while (*tp != '\0' ); +} + + +void scanner_get_word() +{ + while( (scanner_char_code(scanner_ch)==LETTER) || (scanner_char_code(scanner_ch)==DIGIT) ) { + *scanner_tokenp++ = scanner_ch; + scanner_get_char(); + } + *scanner_tokenp = '\0'; + + scanner_token = IDENTIFIER; +} + + +void scanner_get_string() +{ + *scanner_tokenp++ = 34; + scanner_get_char(); + + while(scanner_ch != 34 ) { + *scanner_tokenp++ = scanner_ch; + scanner_get_char(); + } + scanner_get_char(); + *scanner_tokenp++ = 34; + *scanner_tokenp = '\0'; + scanner_token = STRING; +} + + + +void scanner_get_token() +{ + scanner_skip_blanks(); + scanner_tokenp = scanner_token_string; + *scanner_tokenp = 0; + + + switch( scanner_char_code(scanner_ch) ) { + case QUOTE: scanner_get_string(); break; + case EOF_CODE: scanner_token = NO_TOKEN; break; + case DIGIT: + case LETTER: scanner_get_word(); break; + default: + *scanner_tokenp++ = scanner_ch; + *scanner_tokenp = '\0'; + scanner_get_char(); + scanner_token = IDENTIFIER; + break; + } + + scanner_downshift_word(); +} + +void scanner_start_command( char * s ) +{ + scanner_bufferp = s; + scanner_get_char(); +} + + +int Dc_debug_on = 0; +jmp_buf dc_bad_arg; + +void dc_get_arg(uint type) +{ + scanner_get_token(); + + Dc_command_line = scanner_bufferp; + Dc_arg_org = scanner_token_string; + Dc_arg = scanner_word_string; + + if (Dc_debug_on) { + dc_printf( "next arg is '%s', was originally '%s'\n", Dc_arg, Dc_arg_org ); + dc_printf( "Rest of the command line is '%s'\n", Dc_command_line ); + } + + if ( scanner_token == NO_TOKEN ) { + Dc_arg_type = ARG_NONE; + } else if ( scanner_token == IDENTIFIER ) { + Dc_arg_type = ARG_STRING; + } else if ( scanner_token == STRING ) { + Dc_arg_type = ARG_QUOTE; + } else { + Dc_arg_type = ARG_STRING; + } + + if ( Dc_arg_type & ARG_STRING ) { + int i, num_digits, len; + + len = strlen(Dc_arg); + num_digits = 0; + + for (i=0; iname, Dc_arg )) { + + if (mode==0) { + if (Dc_debug_on) + dc_printf( "Calling function '%s'\n", Dc_arg ); + Dc_command = 1; + Dc_help = 0; + Dc_status = 1; + } else if (mode==1) { + if (Dc_debug_on) + dc_printf( "Checking status for '%s'\n", Dc_arg ); + Dc_command = 0; + Dc_help = 0; + Dc_status = 1; + } else { + if (Dc_debug_on) + dc_printf( "Doing help for '%s'\n", Dc_arg ); + Dc_command = 0; + Dc_help = 1; + Dc_status = 0; + } + + (*Debug_command[i]->func)(); + + if (mode==0) { + dc_get_arg(ARG_ANY); + if (!(Dc_arg_type&ARG_NONE)) { + dc_printf( "Ignoring the unused command line tail '%s %s'\n", Dc_arg_org, Dc_command_line ); + } + } + + return; + } + } + + dc_printf( "Unknown command '%s'\n", Dc_arg ); +} + +void debug_draw() +{ + int i; + + gr_clear(); + gr_set_font(FONT1); + gr_set_color_fast( &Color_bright ); + gr_string( 0x8000, 3, "Debug Console" ); + + gr_set_color_fast( &Color_normal ); + + for (i=0; i= DCOLS-1 ) { + debug_x=0; + debug_y++; + scroll_times++; + if ( debug_y >= DROWS ) { + int i; + for (i=1; i= DCOLS-1) ) { + debug_x=0; + debug_y++; + scroll_times++; + if ( debug_y >= DROWS ) { + int i; + for (i=1; i 0 ) { + command_line[--command_line_pos] = 0; + } + break; + + case KEY_F3: + if ( last_oldcommand > -1 ) { + strcpy( command_line, oldcommand_line[last_oldcommand] ); + command_line_pos = strlen(command_line); + command_line[command_line_pos] = 0; + } + break; + + case KEY_UP: + command_scroll--; + if (command_scroll<0) + command_scroll = last_oldcommand; + + if ( command_scroll > -1 ) { + strcpy( command_line, oldcommand_line[command_scroll] ); + command_line_pos = strlen(command_line); + command_line[command_line_pos] = 0; + } + break; + + case KEY_DOWN: + command_scroll++; + if (command_scroll>last_oldcommand) + command_scroll = 0; + if (command_scroll>last_oldcommand) + command_scroll = -1; + if ( command_scroll > -1 ) { + strcpy( command_line, oldcommand_line[command_scroll] ); + command_line_pos = strlen(command_line); + command_line[command_line_pos] = 0; + } + break; + + case KEY_ENTER: { + debug_output( '\n' ); + debug_draw(); + + debug_do_command(command_line); + + int i, found = 0; + for (i=0; i<=last_oldcommand; i++ ) { + if (!stricmp( oldcommand_line[i], command_line )) { + found = 1; + } + } + if ( !found ) { + if ( last_oldcommand < DEBUG_HISTORY-1 ) { + last_oldcommand++; + strcpy( oldcommand_line[last_oldcommand], command_line); + } else { + int i; + for (i=0; i" ); + strcat( debug_text[debug_y], command_line ); + debug_draw(); + + if ( _func ){ + _func(); + } + } + + while( key_inkey() ){ + os_poll(); + } +} + +void debug_help() +{ + int s, i; + + dc_printf( "Available functions:\n\n" ); + + s = scroll_times; + for (i=0; iname, Debug_command[i]->help ); + //mprintf(( "Scroll times %d\n", scroll_times - s )); + if ( scroll_times - s > DROWS - 3 ) { + int k; + dc_printf( " Press a key...B for back\n" ); + debug_draw(); + k = key_getch(); + s = scroll_times; + if ( k == KEY_B ) { + i -= ((DROWS-3)*2); + if ( i <= 0 ) + i = -1; + } + } + debug_draw(); + } + dc_printf( "\n" ); + + dc_printf( "Typing '? function_name' will give the current status.\n" ); + dc_printf( "Typing 'function_name ?' will give help on the function.\n" ); + dc_printf( "Typing ? or help will give you help.\n"); + dc_printf( "F3 selects last command line.\n" ); +} diff --git a/src/demo/demo.cpp b/src/demo/demo.cpp new file mode 100644 index 0000000..671c601 --- /dev/null +++ b/src/demo/demo.cpp @@ -0,0 +1,996 @@ +/* + * $Logfile: /Freespace2/code/Demo/Demo.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 6 8/26/99 8:51p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 5 6/16/99 10:20a Dave + * Added send-message-list sexpression. + * + * 4 4/16/99 5:54p Dave + * Support for on/off style "stream" weapons. Real early support for + * target-painting lasers. + * + * 3 3/29/99 6:17p Dave + * More work on demo system. Got just about everything in except for + * blowing ships up, secondary weapons and player death/warpout. + * + * 2 3/28/99 5:58p Dave + * Added early demo code. Make objects move. Nice and framerate + * independant, but not much else. Don't use yet unless you're me :) + * + * + * $NoKeywords: $ + */ + +#include "demo.h" +#include "missionload.h" +#include "ship.h" +#include "linklist.h" +#include "freespace.h" +#include "object.h" +#include "timer.h" +#include "gamesequence.h" +#include "systemvars.h" +#include "cfile.h" +#include "missionmessage.h" +#include "missionparse.h" +#include "shipfx.h" +#include "shiphit.h" + +// ----------------------------------------------------------------------------------------------------------------------------- +// DEMO DEFINES/VARS +// + +CFILE *Demo_file = NULL; + +// how often we dump +#define DEMO_DEFAULT_FPS 15 +int Demo_fps = 1000 / DEMO_DEFAULT_FPS; + +// timestamp for frame dumping +int Demo_stamp = -1; + +// missiontime +float Demo_missiontime = 0.0f; + +// buffer for reading and writing demo stuff +#define DEMO_BUF_SIZE 32768 +char *Demo_buf = NULL; +int Demo_buf_pos = 0; + +// # of events posted for this frame +int Demo_frame_events = 0; + +// current offset into the demo file - only used for playback +int Demo_cur_offset = -1; + +// demo version # +#define DEMO_VERSION 2 + +// an error reading or writing the demo file +int Demo_error = DEMO_ERROR_NONE; + +// all strings read out of the demo file must be no longer than this +#define DEMO_STRING_LEN 255 + +// macros +#define DEMO_DATA_FRAME() do { \ + if(Demo_file == NULL){\ + Int3();\ + DEMO_ERROR(DEMO_ERROR_GENERAL);\ + break;\ + }\ + if(Game_mode & GM_DEMO_RECORD){\ + if(Demo_buf_pos == 0) {\ + Int3();\ + break;\ + }\ + if(Demo_frame_events <= 0){\ + break;\ + }\ + if(!cfwrite_ushort((ushort)Demo_buf_pos, Demo_file)){\ + DEMO_ERROR(DEMO_ERROR_DISK_SPACE);\ + break;\ + }\ + if(!cfwrite(Demo_buf, Demo_buf_pos, 1, Demo_file)){\ + DEMO_ERROR(DEMO_ERROR_DISK_SPACE);\ + break;\ + }\ + } else if(Game_mode & GM_DEMO_PLAYBACK){\ + Demo_buf_pos = (int)cfread_ushort(Demo_file);\ + if(!cfread(Demo_buf, Demo_buf_pos, 1, Demo_file)){\ + DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);\ + break;\ + }\ + }\ + } while(0) +#define DEMO_DATA(vl, vl_size) do { if(Demo_buf == NULL){ DEMO_ERROR(DEMO_ERROR_GENERAL); break; } if((Demo_buf_pos + vl_size) >= DEMO_BUF_SIZE){ Int3(); DEMO_ERROR(DEMO_ERROR_FRAMESIZE); break; } if(Game_mode & GM_DEMO_RECORD){ memcpy(Demo_buf + Demo_buf_pos, &vl, vl_size); } else if(Game_mode & GM_DEMO_PLAYBACK){ memcpy(&vl, Demo_buf + Demo_buf_pos, vl_size); } Demo_buf_pos += vl_size; } while(0) +#define DEMO_INT(vl) do { DEMO_DATA(vl, sizeof(int)); } while(0) +#define DEMO_UINT(vl) do { DEMO_DATA(vl, sizeof(uint)); } while(0) +#define DEMO_SHORT(vl) do { DEMO_DATA(vl, sizeof(short)); } while(0) +#define DEMO_USHORT(vl) do { DEMO_DATA(vl, sizeof(ushort)); } while(0) +#define DEMO_BYTE(vl) do { DEMO_DATA(vl, sizeof(char)); } while(0) +#define DEMO_UBYTE(vl) do { DEMO_DATA(vl, sizeof(ubyte)); } while(0) +#define DEMO_FLOAT(vl) do { DEMO_DATA(vl, sizeof(float)); } while(0) +#define DEMO_VECTOR(vl) do { DEMO_DATA(vl, sizeof(vector)); } while(0) +#define DEMO_MATRIX(vl) do { DEMO_DATA(vl, sizeof(matrix)); } while(0) +#define DEMO_STRING(vl) do { int stlen; if(Game_mode & GM_DEMO_RECORD){ stlen = strlen(vl); if(stlen <= 0){ break; } DEMO_DATA(stlen, sizeof(ushort)); DEMO_DATA(*vl, strlen(vl)); } else { ushort len = 0; DEMO_USHORT(len); DEMO_DATA(*vl, len); vl[len] = '\0'; } } while(0) + +// demo events types +#define DE_DUMP 1 // standard object dump +#define DE_TRAILER 2 // end of demo trailer +#define DE_PRIMARY 3 // primary weapon fired +#define DE_UNIQUE_MESSAGE 4 // unique hud message +#define DE_BUILTIN_MESSAGE 5 // builtin hud message +#define DE_OBJ_CREATE 6 // object create message +#define DE_OBJ_WARPIN 7 // ship warpin +#define DE_OBJ_WARPOUT 8 // ship warpout +#define DE_OBJ_DEPARTED 9 // ship departed +#define DE_SHIP_KILL 10 // ship kill + +// call this when posting an error +#define DEMO_ERROR(er) do { Demo_error = er; Int3(); } while(0) + +int Demo_make = 0; +DCF(demo, "") +{ + Demo_make = !Demo_make; + if(Demo_make){ + dc_printf("Demo will be recorded\n"); + } else { + dc_printf("Demo will NOT be recorded\n"); + } +} + + +// ----------------------------------------------------------------------------------------------------------------------------- +// DEMO FORWARD DECLARATIONS +// + +// write demo header +int demo_write_header(); + +// read the demo header +int demo_read_header(); + +// write the demo trailer +void demo_write_trailer(); + +// do a recording frame +void demo_do_recording_frame_start(); + +// do a recording frame +void demo_do_recording_frame_end(); + +// do a playback frame +void demo_do_playback_frame(); + +// seek through the demo file to the proper location +// return 0 on error, 1 on success/continue, 2 if the demo is done +int demo_playback_seek(); + +// scan through a read in frame of data and apply everything necessary. returns true if the trailer (end of demo) was found +int demo_playback_seek_sub(int frame_size); + + +// ----------------------------------------------------------------------------------------------------------------------------- +// DEMO FUNCTIONS +// + +// do frame for the demo - playback and recording, returns 0 if errors were encountered during frame processing +int demo_do_frame_start() +{ +#ifndef DEMO_SYSTEM + return 1; +#else + // if we're not doing any demo stuff + if(!(Game_mode & GM_DEMO)){ + return 1; + } + + // bad + if(Demo_file == NULL){ + DEMO_ERROR(DEMO_ERROR_DISK_ACCESS); + return 0; + } + + // make sure we're not trying to record and playback at the same time + Assert( ((Game_mode & GM_DEMO_RECORD) && !(Game_mode & GM_DEMO_PLAYBACK)) || (!(Game_mode & GM_DEMO_RECORD) && (Game_mode & GM_DEMO_PLAYBACK)) ); + + // recording + if(Game_mode & GM_DEMO_RECORD){ + demo_do_recording_frame_start(); + } else { + demo_do_playback_frame(); + } + + // bad bad bad, get mwa + if(Demo_error){ + return 0; + } + + // continue + return 1; +#endif +} + +// do frame for the demo - playback and recording, returns 0 if errors were encountered during frame processing +int demo_do_frame_end() +{ +#ifndef DEMO_SYSTEM + return 1; +#else + // if we're not doing any demo stuff + if(!(Game_mode & GM_DEMO)){ + return 1; + } + + // bad + if(Demo_file == NULL){ + DEMO_ERROR(DEMO_ERROR_DISK_ACCESS); + return 0; + } + + // make sure we're not trying to record and playback at the same time + Assert( ((Game_mode & GM_DEMO_RECORD) && !(Game_mode & GM_DEMO_PLAYBACK)) || (!(Game_mode & GM_DEMO_RECORD) && (Game_mode & GM_DEMO_PLAYBACK)) ); + + // recording. there's nothing to do here for playback + if(Game_mode & GM_DEMO_RECORD){ + demo_do_recording_frame_end(); + } + + // bad bad bad, get mwa + if(Demo_error){ + return 0; + } + + // continue + return 1; +#endif +} + +// initialize a demo for recording +// NOTE : call this after loading the mission and going through the briefing, but _before_ physically moving into the mission +int demo_start_record(char *file) +{ +#ifndef DEMO_SYSTEM + return 0; +#else + char full_name[MAX_FILENAME_LEN] = ""; + + // try and allocate the buffer + Demo_buf = (char*)malloc(DEMO_BUF_SIZE); + if(Demo_buf == NULL){ + DEMO_ERROR(DEMO_ERROR_DISK_ACCESS); + return 0; + } + + // open the outfile + strcpy(full_name, file); + cf_add_ext(full_name, ".fsd"); + Demo_file = cfopen(full_name, "wb", CFILE_NORMAL, CF_TYPE_DEMOS); + if(Demo_file == NULL){ + Int3(); + Demo_error = DEMO_ERROR_DISK_ACCESS; + return 0; + } + + // no errors + Demo_error = DEMO_ERROR_NONE; + + // write the header + if(!demo_write_header()){ + return 0; + } + + // flag demo mode + Game_mode |= GM_DEMO_RECORD; + + // no events yet + Demo_frame_events = 0; + + // success + return 1; +#endif +} + +// initialize a demo for playback - calling this will load up the demo file and move the player into the playback state +int demo_start_playback(char *file) +{ +#ifndef DEMO_SYSTEM + return 0; +#else + char full_name[MAX_FILENAME_LEN] = ""; + + // try and allocate the buffer + Demo_buf = (char*)malloc(DEMO_BUF_SIZE); + if(Demo_buf == NULL){ + DEMO_ERROR(DEMO_ERROR_DISK_ACCESS); + return 0; + } + + // open the outfile + strcpy(full_name, file); + cf_add_ext(full_name, ".fsd"); + Demo_file = cfopen(full_name, "rb", CFILE_NORMAL, CF_TYPE_DEMOS); + if(Demo_file == NULL){ + DEMO_ERROR(DEMO_ERROR_DISK_ACCESS); + return 0; + } + + // no errors + Demo_error = DEMO_ERROR_NONE; + + // read the header + Demo_cur_offset = -1; + if(!demo_read_header()){ + return 0; + } + + // flag demo mode + Game_mode |= GM_DEMO_PLAYBACK; + + // everything is cool, so jump into the mission + gameseq_post_event(GS_EVENT_ENTER_GAME); + return 1; +#endif +} + +// finish the demo +void demo_close() +{ +#ifdef DEMO_SYSTEM + // if we're recording, write the trailer + if(Game_mode & GM_DEMO_RECORD){ + demo_write_trailer(); + } + + // close the demo file + if(Demo_file != NULL){ + cfclose(Demo_file); + Demo_file = NULL; + } + + // free the buffer + if(Demo_buf != NULL){ + free(Demo_buf); + Demo_buf = NULL; + } + + // if we're playing back, go back to the main hall + if(Game_mode & GM_DEMO_PLAYBACK){ + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + + // unflag demo mode + Game_mode &= ~(GM_DEMO_RECORD | GM_DEMO_PLAYBACK); +#endif +} + +// if we should run the simulation for this object, or let the demo system handle it +int demo_should_sim(object *objp) +{ +#ifndef DEMO_SYSTEM + return 1; +#else + // always sim stuff in non-demo mode + if(!(Game_mode & GM_DEMO)){ + return 1; + } + + // don't sim ships or missiles + if((objp->type == OBJ_SHIP) || ((objp->type == OBJ_WEAPON) && (objp->instance >= 0) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))){ + return 0; + } + + // sim everything else + return 1; +#endif +} + + +// ----------------------------------------------------------------------------------------------------------------------------- +// DEMO RECORDING FUNCTIONS +// + +// write demo header +int demo_write_header() +{ + uint chksum; + char *full_name; + + // write demo version # + if(!cfwrite_int(DEMO_VERSION, Demo_file)){ + DEMO_ERROR(DEMO_ERROR_DISK_ACCESS); + return 0; + } + + // write mission filename + if(!cfwrite_string_len(Game_current_mission_filename, Demo_file)){ + DEMO_ERROR(DEMO_ERROR_DISK_ACCESS); + return 0; + } + + // write mission checksum + full_name = cf_add_ext(Game_current_mission_filename, FS_MISSION_FILE_EXT); + cf_chksum_long(full_name, &chksum); + if(!cfwrite_int(chksum, Demo_file)){ + DEMO_ERROR(DEMO_ERROR_DISK_ACCESS); + return 0; + } + + // success + return 1; +} + +// write the demo trailer +void demo_write_trailer() +{ + // start a new chunk + demo_do_recording_frame_start(); + + // trailer event + ubyte frame_type = DE_TRAILER; + DEMO_UBYTE(frame_type); + + // 1 event + Demo_frame_events++; + + // write frame data to disk + DEMO_DATA_FRAME(); +} + +// start recording frame +void demo_do_recording_frame_start() +{ + // if we're not physically doing the mission + if(gameseq_get_state() != GS_STATE_GAME_PLAY){ + return; + } + + // add in frametime + Demo_missiontime += flFrametime; + + // clear the buffer, set no events, and write the header + Demo_buf_pos = 0; + Demo_frame_events = 0; + + // missiontime + float fl_time = f2fl(Missiontime); + DEMO_FLOAT(fl_time); +} + +// end recording frame +void demo_do_recording_frame_end() +{ + // if we're not physically doing the mission + if(gameseq_get_state() != GS_STATE_GAME_PLAY){ + return; + } + + // if its time to dump objects (the last thing we might dump per frame) + if((Demo_stamp == -1) || timestamp_elapsed(Demo_stamp)){ + // post an object dump event + demo_POST_object_dump(); + + // reset the stamp + Demo_stamp = timestamp(Demo_fps); + } + + // write all accumulated frame data to disk if necessary + DEMO_DATA_FRAME(); +} + +// post an object dump event +void demo_POST_object_dump() +{ + ship_obj *sobjp; + object *objp; + ushort obj_count; + ubyte team; + + // object dump event + ubyte event_type = DE_DUMP; + DEMO_UBYTE(event_type); + + // go through the ship list and count + obj_count = 0; + for ( sobjp = GET_FIRST(&Ship_obj_list); sobjp !=END_OF_LIST(&Ship_obj_list); sobjp = GET_NEXT(sobjp) ){ + // object pointer + if(sobjp->objnum < 0){ + continue; + } + + obj_count++; + } + + // write out the object count + DEMO_USHORT(obj_count); + + // go through the ship list and dump necessary stuff + for ( sobjp = GET_FIRST(&Ship_obj_list); sobjp !=END_OF_LIST(&Ship_obj_list); sobjp = GET_NEXT(sobjp) ){ + // object pointer + if(sobjp->objnum < 0){ + continue; + } + objp = &Objects[sobjp->objnum]; + + // just ships for now + DEMO_INT(objp->signature); + DEMO_VECTOR(objp->pos); + DEMO_MATRIX(objp->orient); + DEMO_FLOAT(objp->phys_info.forward_thrust); + team = (ubyte)Ships[objp->instance].team; + DEMO_UBYTE(team); + DEMO_INT(Ships[objp->instance].flags); + } + + // up the event count + Demo_frame_events++; +} + +// post a primary fired event +void demo_POST_primary_fired(object *objp, int banks, int linked) +{ + ubyte fire_info = 0; + + // object dump event + ubyte event_type = DE_PRIMARY; + DEMO_UBYTE(event_type); + + // object signature + DEMO_INT(objp->signature); + + // get fire info + fire_info = (ubyte)banks; + fire_info &= ~(1<<7); + if(linked){ + fire_info |= (1<<7); + } + DEMO_UBYTE(fire_info); + + // up the event count + Demo_frame_events++; +} + +// post a unique message +void demo_POST_unique_message(char *id, char *who_from, int m_source, int priority) +{ + // sanity + if((id == NULL) || (who_from == NULL) || (strlen(id) <= 0) || (strlen(who_from) <= 0)){ + return; + } + + // write it + ubyte event = DE_UNIQUE_MESSAGE; + DEMO_UBYTE(event); + DEMO_STRING(id); + DEMO_STRING(who_from); + DEMO_INT(m_source); + DEMO_INT(priority); + + // up the event count + Demo_frame_events++; +} + +// post a builtin message +void demo_POST_builtin_message(int type, ship *shipp, int priority, int timing) +{ + int sig = 0; + + // write it + ubyte event = DE_BUILTIN_MESSAGE; + DEMO_UBYTE(event); + DEMO_INT(type); + if(shipp == NULL){ + sig = -1; + } else if(shipp->objnum >= 0){ + sig = Objects[shipp->objnum].signature; + } + DEMO_INT(sig); + DEMO_INT(priority); + DEMO_INT(timing); + + // up the event count + Demo_frame_events++; +} + +// post an object create message +void demo_POST_obj_create(char *pobj_name, int signature) +{ + // write it + ubyte event = DE_OBJ_CREATE; + DEMO_UBYTE(event); + DEMO_STRING(pobj_name); + DEMO_INT(signature); + + // up the event count + Demo_frame_events++; +} + +// post a warpin event +void demo_POST_warpin(int signature, int ship_flags) +{ + // write it + ubyte event = DE_OBJ_WARPIN; + DEMO_UBYTE(event); + DEMO_INT(signature); + DEMO_INT(ship_flags); + + // up the event count + Demo_frame_events++; +} + +// post a warpout event +void demo_POST_warpout(int signature, int ship_flags) +{ + // write it + ubyte event = DE_OBJ_WARPOUT; + DEMO_UBYTE(event); + DEMO_INT(signature); + DEMO_INT(ship_flags); + + // up the event count + Demo_frame_events++; +} + +// post a departed event +void demo_POST_departed(int signature, int ship_flags) +{ + // write it + ubyte event = DE_OBJ_DEPARTED; + DEMO_UBYTE(event); + DEMO_INT(signature); + DEMO_INT(ship_flags); + + // up the event count + Demo_frame_events++; +} + +// post a ship kill event +void demo_POST_ship_kill(object *objp) +{ + // write it + ubyte event = DE_SHIP_KILL; + DEMO_UBYTE(event); + DEMO_INT(objp->signature); + + // up the event count + Demo_frame_events++; +} + + +// ----------------------------------------------------------------------------------------------------------------------------- +// DEMO PLAYBACK FUNCTIONS +// + +// read the demo header +int demo_read_header() +{ + int version; + uint file_checksum, my_checksum; + char *full_name; + + // read the version # + version = cfread_int(Demo_file); + if(version != DEMO_VERSION){ + DEMO_ERROR(DEMO_ERROR_VERSION); + return 0; + } + + // read mission filename + cfread_string_len(Game_current_mission_filename, MAX_FILENAME_LEN, Demo_file); + + // get mission checksum + file_checksum = cfread_int(Demo_file); + full_name = cf_add_ext(Game_current_mission_filename, FS_MISSION_FILE_EXT); + if(!cf_chksum_long(full_name, &my_checksum)){ + DEMO_ERROR(DEMO_ERROR_DISK_ACCESS); + return 0; + } + if(file_checksum != my_checksum){ + DEMO_ERROR(DEMO_ERROR_MISSION); + return 0; + } + + // get the file offset of this first frame + Demo_cur_offset = cftell(Demo_file); + + // success + return 1; +} + +// do a playback frame +void demo_do_playback_frame() +{ + // seek to the best location in the demo file + switch(demo_playback_seek()){ + // error + case 0: + return; + + // continue + case 1: + break; + + // found the trailer - this demo is done + case 2: + demo_close(); + break; + } +} + +// seek through the demo file to the proper location +// return 0 on error, 1 on success/continue, 2 if the demo is done +int demo_playback_seek() +{ + float this_time = 0.0f; + int frame_size; + int this_offset; + int ret = 0; + + // scan until we find something useful + while(1){ + // record the beginning of this chunk + this_offset = cftell(Demo_file); + + // read in the data for the next frame + DEMO_DATA_FRAME(); + frame_size = Demo_buf_pos; + Demo_buf_pos = 0; + + // check the time + DEMO_FLOAT(this_time); + + // ahead of us + if(this_time > f2fl(Missiontime)){ + // success + ret = 1; + + // seek back to the beginning of this chunk + Demo_cur_offset = this_offset; + + // bust out + break; + } + // we should scan through this chunk + else { + // returns true if it finds the demo trailer + if(demo_playback_seek_sub(frame_size)){ + return 2; + } + } + } + + // seek back to the new frame + cfseek(Demo_file, Demo_cur_offset, CF_SEEK_SET); + + // return + return ret; +} + +// scan through a read in frame of data and apply everything necessary. returns true if the trailer (end of demo) was found +int demo_playback_seek_sub(int frame_size) +{ + ubyte event = 0; + int idx; + + // apply everything + while(Demo_buf_pos < frame_size){ + // next event + DEMO_UBYTE(event); + + // process it + switch(event){ + // oops, trailer. we're done + case DE_TRAILER: + return 1; + + // object dump + case DE_DUMP: { + ushort obj_count = 0; + int obj_sig = 0; + ubyte team = 0; + vector obj_pos = vmd_zero_vector; + matrix obj_orient = vmd_identity_matrix; + float obj_fthrust = 0; + int ship_index = 0; + int ship_flags = 0; + + // get the object count + DEMO_USHORT(obj_count); + + // read in all the objects + for(idx=0; idx= 0); + if(ship_index >= 0){ + Objects[Ships[ship_index].objnum].pos = obj_pos; + Objects[Ships[ship_index].objnum].orient = obj_orient; + Objects[Ships[ship_index].objnum].phys_info.forward_thrust = obj_fthrust; + Ships[ship_index].team = team; + Ships[ship_index].flags = ship_flags; + } + } + } + break; + + // primary fired + case DE_PRIMARY: { + int obj_sig = 0 ; + ubyte fire_info = 0; + int ship_index = -1; + + // get the data and ship + DEMO_INT(obj_sig); + DEMO_UBYTE(fire_info); + ship_index = ship_get_by_signature(obj_sig); + if(ship_index >= 0){ + Ships[ship_index].weapons.current_primary_bank = (int)(fire_info & ~(1<<7)); + if(fire_info & (1<<7)){ + Ships[ship_index].flags |= SF_PRIMARY_LINKED; + } else { + Ships[ship_index].flags &= ~(SF_PRIMARY_LINKED); + } + + // fire + ship_fire_primary(&Objects[Ships[ship_index].objnum], 0, 1); + } + } + break; + + // unique message + case DE_UNIQUE_MESSAGE: { + char id[255] = ""; + char who_from[255] = ""; + int m_source = 0; + int priority = 0; + + // read the stuff in + DEMO_STRING(id); + DEMO_STRING(who_from); + DEMO_INT(m_source); + DEMO_INT(priority); + + // send the message + message_send_unique_to_player(id, who_from, m_source, priority, 0, 0); + } + break; + + // builtin message + case DE_BUILTIN_MESSAGE:{ + int type = 0; + int sig = 0; + int ship_index = 0; + int priority = 0; + int timing = 0; + + // get data and ship + DEMO_INT(type); + DEMO_INT(sig); + DEMO_INT(priority); + DEMO_INT(timing); + if(sig == -1){ + // message_send_builtin_to_player(type, NULL, priority, timing, 0, 0); + } else { + ship_index = ship_get_by_signature(sig); + if(ship_index >= 0){ + // message_send_builtin_to_player(type, &Ships[ship_index], priority, timing, 0, 0); + } + } + } + break; + + // obj create + case DE_OBJ_CREATE:{ + char pobj_name[255] = ""; + int obj_sig = 0; + int objnum = 0; + p_object *objp = NULL; + + // try and create the ship + DEMO_STRING(pobj_name); + DEMO_INT(obj_sig); + + objp = mission_parse_get_arrival_ship( pobj_name ); + if(objp != NULL){ + objnum = parse_create_object(objp); + if(objnum >= 0){ + Objects[objnum].signature = obj_sig; + } + } + } + break; + + // warpin effect + case DE_OBJ_WARPIN:{ + int obj_sig = 0; + int ship_flags = 0; + int ship_index = 0; + + // get the data and ship + DEMO_INT(obj_sig); + DEMO_INT(ship_flags); + ship_index = ship_get_by_signature(obj_sig); + if(ship_index >= 0){ + Ships[ship_index].flags = ship_flags; + shipfx_warpin_start(&Objects[Ships[ship_index].objnum]); + } + } + break; + + // warpout effect + case DE_OBJ_WARPOUT:{ + int obj_sig = 0; + int ship_flags = 0; + int ship_index = 0; + + // get the data and ship + DEMO_INT(obj_sig); + DEMO_INT(ship_flags); + ship_index = ship_get_by_signature(obj_sig); + if(ship_index >= 0){ + Ships[ship_index].flags = ship_flags; + shipfx_warpout_start(&Objects[Ships[ship_index].objnum]); + } + } + break; + + // departed + case DE_OBJ_DEPARTED:{ + int obj_sig = 0; + int ship_flags = 0; + int ship_index = 0; + + // get the data and ship + DEMO_INT(obj_sig); + DEMO_INT(ship_flags); + ship_index = ship_get_by_signature(obj_sig); + if(ship_index >= 0){ + Ships[ship_index].flags = ship_flags; + ship_departed(ship_index); + } + } + break; + + // ship kill + case DE_SHIP_KILL:{ + int obj_sig = 0; + int ship_index = 0; + + // get the data and ship + DEMO_INT(obj_sig); + ship_index = ship_get_by_signature(obj_sig); + if(ship_index >= 0){ + Objects[Ships[ship_index].objnum].hull_strength = 0.0f; + ship_generic_kill_stuff(&Objects[Ships[ship_index].objnum], 1.0f); + } + } + break; + + default: + Int3(); + return 0; + } + } + + return 0; +} diff --git a/src/directx/vd3dvec.inl b/src/directx/vd3dvec.inl new file mode 100644 index 0000000..6e711f2 --- /dev/null +++ b/src/directx/vd3dvec.inl @@ -0,0 +1,240 @@ + +/****************************************************************** + * * + * D3DVec.inl * + * * + * Float-valued 3D vector class for Direct3D. * + * * + * Copyright (c) 1996-1997 Microsoft Corp. All rights reserved. * + * * + ******************************************************************/ + +#include + +// ===================================== +// Constructors +// ===================================== + +inline +_D3DVECTOR::_D3DVECTOR(D3DVALUE f) +{ + x = y = z = f; +} + +inline +_D3DVECTOR::_D3DVECTOR(D3DVALUE _x, D3DVALUE _y, D3DVALUE _z) +{ + x = _x; y = _y; z = _z; +} + +inline +_D3DVECTOR::_D3DVECTOR(const D3DVALUE f[3]) +{ + x = f[0]; y = f[1]; z = f[2]; +} + +// ===================================== +// Access grants +// ===================================== + +inline const D3DVALUE& +_D3DVECTOR::operator[](int i) const +{ + return (&x)[i]; +} + +inline D3DVALUE& +_D3DVECTOR::operator[](int i) +{ + return (&x)[i]; +} + + +// ===================================== +// Assignment operators +// ===================================== + +inline _D3DVECTOR& +_D3DVECTOR::operator += (const _D3DVECTOR& v) +{ + x += v.x; y += v.y; z += v.z; + return *this; +} + +inline _D3DVECTOR& +_D3DVECTOR::operator -= (const _D3DVECTOR& v) +{ + x -= v.x; y -= v.y; z -= v.z; + return *this; +} + +inline _D3DVECTOR& +_D3DVECTOR::operator *= (const _D3DVECTOR& v) +{ + x *= v.x; y *= v.y; z *= v.z; + return *this; +} + +inline _D3DVECTOR& +_D3DVECTOR::operator /= (const _D3DVECTOR& v) +{ + x /= v.x; y /= v.y; z /= v.z; + return *this; +} + +inline _D3DVECTOR& +_D3DVECTOR::operator *= (D3DVALUE s) +{ + x *= s; y *= s; z *= s; + return *this; +} + +inline _D3DVECTOR& +_D3DVECTOR::operator /= (D3DVALUE s) +{ + x /= s; y /= s; z /= s; + return *this; +} + +inline _D3DVECTOR +operator + (const _D3DVECTOR& v) +{ + return v; +} + +inline _D3DVECTOR +operator - (const _D3DVECTOR& v) +{ + return _D3DVECTOR(-v.x, -v.y, -v.z); +} + +inline _D3DVECTOR +operator + (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + return _D3DVECTOR(v1.x+v2.x, v1.y+v2.y, v1.z+v2.z); +} + +inline _D3DVECTOR +operator - (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + return _D3DVECTOR(v1.x-v2.x, v1.y-v2.y, v1.z-v2.z); +} + +inline _D3DVECTOR +operator * (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + return _D3DVECTOR(v1.x*v2.x, v1.y*v2.y, v1.z*v2.z); +} + +inline _D3DVECTOR +operator / (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + return _D3DVECTOR(v1.x/v2.x, v1.y/v2.y, v1.z/v2.z); +} + +inline int +operator < (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + return v1[0] < v2[0] && v1[1] < v2[1] && v1[2] < v2[2]; +} + +inline int +operator <= (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + return v1[0] <= v2[0] && v1[1] <= v2[1] && v1[2] <= v2[2]; +} + +inline _D3DVECTOR +operator * (const _D3DVECTOR& v, D3DVALUE s) +{ + return _D3DVECTOR(s*v.x, s*v.y, s*v.z); +} + +inline _D3DVECTOR +operator * (D3DVALUE s, const _D3DVECTOR& v) +{ + return _D3DVECTOR(s*v.x, s*v.y, s*v.z); +} + +inline _D3DVECTOR +operator / (const _D3DVECTOR& v, D3DVALUE s) +{ + return _D3DVECTOR(v.x/s, v.y/s, v.z/s); +} + +inline int +operator == (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + return v1.x==v2.x && v1.y==v2.y && v1.z == v2.z; +} + +inline D3DVALUE +Magnitude (const _D3DVECTOR& v) +{ + return (D3DVALUE) sqrt(SquareMagnitude(v)); +} + +inline D3DVALUE +SquareMagnitude (const _D3DVECTOR& v) +{ + return v.x*v.x + v.y*v.y + v.z*v.z; +} + +inline _D3DVECTOR +Normalize (const _D3DVECTOR& v) +{ + return v / Magnitude(v); +} + +inline D3DVALUE +Min (const _D3DVECTOR& v) +{ + D3DVALUE ret = v.x; + if (v.y < ret) ret = v.y; + if (v.z < ret) ret = v.z; + return ret; +} + +inline D3DVALUE +Max (const _D3DVECTOR& v) +{ + D3DVALUE ret = v.x; + if (ret < v.y) ret = v.y; + if (ret < v.z) ret = v.z; + return ret; +} + +inline _D3DVECTOR +Minimize (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + return _D3DVECTOR( v1[0] < v2[0] ? v1[0] : v2[0], + v1[1] < v2[1] ? v1[1] : v2[1], + v1[2] < v2[2] ? v1[2] : v2[2]); +} + +inline _D3DVECTOR +Maximize (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + return _D3DVECTOR( v1[0] > v2[0] ? v1[0] : v2[0], + v1[1] > v2[1] ? v1[1] : v2[1], + v1[2] > v2[2] ? v1[2] : v2[2]); +} + +inline D3DVALUE +DotProduct (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + return v1.x*v2.x + v1.y * v2.y + v1.z*v2.z; +} + +inline _D3DVECTOR +CrossProduct (const _D3DVECTOR& v1, const _D3DVECTOR& v2) +{ + _D3DVECTOR result; + + result[0] = v1[1] * v2[2] - v1[2] * v2[1]; + result[1] = v1[2] * v2[0] - v1[0] * v2[2]; + result[2] = v1[0] * v2[1] - v1[1] * v2[0]; + + return result; +} + diff --git a/src/directx/vddraw.lib b/src/directx/vddraw.lib new file mode 100644 index 0000000000000000000000000000000000000000..a952c56cab3ee21cb7a5d4420ba7bdba6591e28c GIT binary patch literal 14628 zcmeHO&2JM&6#v;K5b{k3AEmT7rGQXTYCCqYDQa!SP@*~kg;07xI9B2o3&)9E)ArU1 zAx``e`~@5kr=D`?q2h!p#5sQfLRC~%JwV@^kKI{&m#|phLD#zm~^>e-lz-K`JePHk@Fx(A{B!K<%z`;Af zp+~^s7r>F@!00vL=xyNGL*T?S;N(l-)EQvxIuN@DBpv|CC&0uyu=_61H_-m;_B}4< z^A~c3@kH_4%Hni+DOas7FJ$8rXp*0=m&@6Bg2hv{M!8-qRSU}tH?wI|a-~L{Z)TGu zyS#j>ws@gjU6GwC_3}a^UoZVUSud9w>Pk(*$!cX~rCe|7aISW1sa&T%IdLzmk0rm# z@~_K{nes1<>B`DQFKS}32dFE8aM#Wl@wqo_-A%qSN47OXtwsxM84m@LXRR=|tZg#7C2TdltTMIL)2C$`2D0UAz#tna2*|}kkZ?#Hz%Xkx zJRC9w5>sYD#S}UL^Q%CI#Kzo(D>L5|^SQYk{4>{t#asu7y6)}<2Ks=<^tp2ocySmw zegt?r2;A=no+N>LG2qN8-~sX4^)X<50+`+SY*pFWHqX&K1jRAxZe&e>0X0Eaqfs@-T_-}LAHizj) zlU!o?Z4T=g4in5awpgtyGTX@eN~P1v51a2Pgu*C4rs3EuIFOgf>MjhX4{a9tO)F^#QY%CMeF7l-Rcl6;Dp-&b=tEwHbwM;xIKN;;< znniS#^X5d41D+x=r}B!s#1!zH?Sf@7(M``I-#!dIx-78Dk{eng#=+N&Xi1XWkic$} zvL!74n7>rwp1^5U;w+3A`YyJ*N*j4n9?w!st2e}9JaO8abxUq4OQt5O=C~#5%E|SbwTA>#5BgpOOT1% z-OvSRtx_?)V`FC6AuRF_UB6e}J~Y;#J%cfno%H(=eSTLm);>BE zomZA~X#WS*{b{tz4!LXp7qxlj-flvP$@dXVrHdlZ4n>yM8!B`bR z!px`xGha~26*JZ@!1F=jn`e@lR-XBr?%!!VLz^=8h#bi;&x8pxdeh8dgCO9A_14Me z7~0xeDGVlK3ooXE9KrE~ieGj~W=vmIvdcQ%xzms`LStH^kD2@;C1M~&g;xT=Ev> zHddUTXPO$cV8w`)wd+}wOq(91M9Xu!cc-C+0@Lq|mL0HT#zEi|)>$VTCm(1LH=Vsn|pdc6j^PY1pBI zi+9EjMYXIcwU4HPz&EVDPBwD9choLww$K37cYXv$9e)UYZI1Euex2%hZ>P7tUxgqY)0IV8>SL5R-faj1 zulV$Sn^hEV?s$e@{Nu%ORw#E%o0V1iVTQ4SfE9OEar!d}S`y0Vb*Se}Dt^7(WTth8 zv{|FM0~SVt6QOgD<~sLF$yStDZ+NxKLrKNsExV!3x|1G+`zvulU==4a_!N?#s^Vys ze^3hJEBaQf;;yK85%m2&hc;_ucL>JYk^zXkxJP!KC^GoPQ1124ivQ=();TNMqcLK= zI~xRMarbRK&x#aMEEV}paZgI|jh1w4Y;LnM%CJHUzfT literal 0 HcmV?d00001 diff --git a/src/directx/vdinput.lib b/src/directx/vdinput.lib new file mode 100644 index 0000000000000000000000000000000000000000..d105311a3b5824f6fb0c7a334e86026d8b534d8d GIT binary patch literal 15336 zcmeI(d7K+%83*t;yS-Q;*aLL|XC?%x}W$CsJ?G_3YHf?v?E-W-`x6pEi za^K;;A>21aMFm8SS`qJvh^UBB@kWh`h>FzbH^=jveYU0YkAEodr^)=j?@TgpX5M*c zlG*MyZOPEGmDB6?PK+#5+ora)Cp)GlM^-07H+6(5Q|Fx9+PSeZt$E(L31xXqq<5Sm zvllzJ%X;TJo^@{g^xn>aq5fsVT_KX(mnI$_jY#8O)uS#RI+&aOjNty?`j zuI*$*)tc4)?o7X7ezSTGUO0DlZ|97j8Sd?F-R9)fV7q7P zwvx^Je2sH;W1X8m-noeroGZ&?v261xJ36<17w4Ys@OFW#aWOa6jq#@A-8eVFZRKh= zv0?w$x3|9=8u|tY{Vl(x-rSmO(e2$G8{@{j^mOxUez%rq7i$>qUptHxlyNeBOdd}L zj>$AppU5-}^eq`)GPyOVmObIz26;RaRA9MOeX+51lRdsw8KIO#G$@fCBU4!^^li+)0^y*7q-LGnv%W9oGz9PB7D zmyq>EKQGo^E8A{UuR<*fmeE&vpT1UVM!u@Xjg@r;@9ceYyt+1q&mIAz8CeDq%SEfgV-V}AaXZQL=FV1c`%^WwM@G+>_o>qI{^nCPhpm|? z0Y_@Iq+ER|>S!L>@||VYW>5TeZ@2BO9Ziw8`+H=)G#sO5MLxpTvD$FRi#Cb5UZMnxp#4h+>b0JC64@0k+wA$2}&L5^hN}K+hFTDd$S4Ss9*lyfXvdwKlA2!7@_8Wm;K- z`d#r96ui>8sev%5acOz(9ep*q#jDND9Y=iwg9A%j8dfbmHoOXHZ=c%UmTV1rTsUb= zCfnND5{c%P_C~)hQ`a4fxrNJ~XdWjUZ=0Z%(Q`Da7D>;)Ac4PF6=zO`r9^rkA_cg|TiN1b6ftC7$0Cf)40?4K;wgq zXBi)C++}=-@oeL6<2lB2jprHTSd4DJLyhMfFEH*gUTA!nv0MU=Sl=S!BaDwUKFWBp z@zKV|821`yjF%WMHC|@iXWVbR+<1jCt|p?}f57-y-A? z!^UfjPclB)c&+g|<5P@JH9pPwbmKFO&os^%pJlw>_-x~IjL$Vb&-i@f3yd!`zR37u z<4cS$HNMRFa^t-56~ZW?V47-S`gU zJB{x$zT5a7<9m(oGrr&W0pkaaA2NQ}_z~krjf-OMdZI>xl*i-|_2b4*8JCPV7*8_Z z$+#T4R)Uo08HrDkA@2BezTn(2WBSFfm^6=Il_;uqqjB(u;U4D$Q zu0MD`UB4Lf>g6FXarN?amrTR*E(znjxp6Ng7IpsA>-ByZub7r%S@>#a!g*ISk3uNN!z*IO?Wsg`$6Nb=X4)RdCszh7FC z|9%-s{`+Mm`R|vLB>&JpnfUKlkmSE#QBz4$&^}2)`y>VJlN7X1CIW0fNkRK01?|(+ zBWbL+T*LB#L7cf!-rVTiidaL#;^1q=GQWBgzue(_!S2CaPOzg>G6#FrKd9al%tVdf z?e+|6F!7gZ9aVF{s2W_mg~fl>;*c`+YX(Nu;EWs=f0?sF%CyE^D$kqx^?hi?s^Ay; zrpXJMn%Y}iT<@%fU7fw@d4~mQ!Qen&|4@MWr_5M8usX!LDAq=BWF#LE$&0*P=WX=n z|NOgLV{@{@ewVveHu5&XXSCntUXV>NP3BDuj(_y0cfQN{8>>Yo(Q8rk$*AA?E;n*} z%re{OVB;?1LyTt|cN@UM#_{t z`(q|v?T?umN&c9b)s&Mo&U@rF6*Lt!JtxQ6md4iRhRu(yI8M>-kp*LGN#<~Dofs^@ zvGv1|m&l@0@S8(TuGy zht~i0$l5e|WDS2Qx4)9LbuYiJD4@mhKeJcdTkijSqyBje*}^xF7%8vQvwfksb{Z$;x6h}Y3L zI^tW?IBw!`8a*@qE*fW>_%`&m@U}EINxYuk4&IL59^RgQH~elIy*xgV#uY{UJ@gLn z4)lBB_tF?0;*;nd;T>t&tuFpP`u*_xXg5C|@js5`q z0s4dR2kFW1WExjp@!jb?;63OM!5^Zhz*A_95%CYxAAvtY?+NcoV>FHLMSm3jDBS=z z(0jvs(@8iGb~a{&WgX(KFx~^a1bz^h|gr-3fQn2f_!^2f+u?v*1~@cV<4A z?!x>o+B-8JLeIwh*>pGDP2+44pF?|R=DD+v(`(_i^g4JQeF}UEeJXq^eHwfk zeL8$PeFl66jjN#enRE`$(PzPD(d*&$^x5#)^f~Z3^ttf4^m*`k^!f1l^abz*^o8(+ z^hNMR^u_SS^d;~m^ri5n^kwj6^yTp7bRN#rSHM@$SHf4)SHV}&SHoA+*TC1%*TUD* z*TL7(*TdJ-H^4X0H^Mj4H^Dd2H^Vp6x4^g1x5Bs5x52m31-L-p4&P4S0pCI23ExTI z1>Z&A4c|@Q1K&g63*SrM2j55E58qEe06#!K2tPe*yji{YChT^q1f-(Hr0m^q1i;(@(=s(_ewVLO%mPLw^t{4D)l_`CFT@N@L{;P27T!_U*- zhrdt10KY*00R92}L->dEkKiBCFTyXe@DLpze4{W{yqH%_z(0S;Xl$< zxJv&C{uBLY_|NoT;J?tX!mrYQh5t&w2ERuC4gMSbI{Z5Qclhu0Kj44R|AhZZzX88N z{|o*X{U-b-y%FB%dy?dOdDO_m`{Ex%@XI)v_;)${l<m*3Yv)>sjR7@sj3M-p9b4cQ$kZxQ%X}> zQ$|x(Q%+M}Q$bTvQ%O@UQQ$|x(Q%+M}Q$bTvQ%O@)>sjR7@sj3OL1A_KxN@z-IN@+@K%4o`J%4y1LDrhQdDrqWfs%WZe z!i|fdeVP)QlA2PQ(wZ`wvYK+5@|p^oikeEA%9<*gs+w?%C1{_fgr=mXl%}+%jHaxn zoTj{{f~KOTlBTkzil(Y2+zblZrzxQ+sVSu?ttq1^t0|``uc@G^sHvo>tf``@stLD$ zg7#@jXi92IX-aF#Xv%8JY07IVXew$dX)0@~XsT+$E#{zoni86lno^q5nlhTQnsS=* znhKhVno63=nkt&An%uV9K1~TtNlhtDX-yeTSxq@jc})dPMNK75Wla@LRZSD?w|s}N z=`HRM;(inEF^w#8`t&Y0F+(mzq)hal{Yy_d`AcaCYA~ts{x&_hM;HCihQU3$uxjLu zI;nNuUn(^I=l9cE+QU0-yr1@pv_J+luh{!(7f9_id2H}!JA5+pmkPlg|8M!_uaML- zi2Hw)6x7H;g*(MMUpny!OQ+F0$yi8sk~(_*E}PuR2}}#t<6pm{R{jdedqz4(l3XfC z+WgGD|-ve4k)ucw$bc=!>;Q#kh-lFw9 La^Ju8AF%uzcYt+m literal 0 HcmV?d00001 diff --git a/src/directx/vdsound.lib b/src/directx/vdsound.lib new file mode 100644 index 0000000000000000000000000000000000000000..fb4d55989992e1cfda93d3a90c4afedc519186be GIT binary patch literal 5646 zcmeHKPfrs;6o0$hS|}1r;NU^y8ZjmwuzzWZglwfG5-SjBgSVy9CXf^)O);J@@pBM9 zg%>^bV2EcAoH+DDc<^9?@6GP)%(A;IYcxXYOXlBuZ)V0+@|%;jg?Y&~s{3+ZXQw~+*ZTL43c0LfE;(McS(0mhC1#!moZ2LK7w36dv{&B}JC zwb@;Jv%lM%Z|rsVJFWRntI=&4nJoAS7k2k|S{;;^jnakk_49JyRRfoE1Z*91@#V_$ zYR#-Htj(`3uGE%S&C1$xeW?Pbxm2%Ky>b+6@9ddA9!Ty5sW_W6CquO?6Fu7rhdnEV zY~YA#<@c7SmY=of^`#ePrCcjRWYlB?YK9Ru2QkD<0^k@+8!>b=25>SCa5w~TikO)k zWzd2I48btOAPzS`0o66u{2(9&*b6Slwg32WLh*0#W5H^w*=}1%3e7q*StsjM6&32Q zKjl6sm|qY}eE?-d(I^`QQwl{Y=A>W^TY`$DOBHLKCUoZCRG8XsHoA?;EZ<`SI~DZ* z6&4~5ivg*gtvAh>Slz_(R?;KB0eX+x^F+EBA{J9?#RLlh#Vi5h#H@Kdl4O7i+&%-K zN1QFrVV2OU8t3Fm2N^=AAUMRoN0m)96QE>|s8L}BYtx%_Lz8k)E)$+z4vU#=QNp6a zut+x9N1@oU$dR=~bXK#hCn`ECF>?3Zk-dw;z;`AY+4e$Svfa8z_++2Ay}&IZTbg`e zw+5s+&hlJDdRu%T>B%g&Uw2USr%t3Gy)IQH#!loXu0LXJuS|7Y@4AvHH?aQ^%V(QM zC#R?M4g2I=-hV;aS4q!E`FsBjbx!>C*c~7d`P`?DL`F*QojoK5VLgt8tg{FoL;E(; zn2)^gn)T=b$bOPfnnG*YxTM}$_Xjq>Q>6mBxVy}2c2hBeezW-bk&biY83HK#Xs%f* z#Bnb<8_{e{p$KrYbZGR5v#?d+8P=EjfC~IBi+U>x*_?DMEc{T|t{Q%Hj7%SkOi}Tp zfEPc`$3XcyU`UtT7Z`f%;x6DQ5JSRU`b`LaNg`+0LBf%G5suae3r8WpiP3SC&Uo_C zcSQ76%SRJvQy+^=QS*`J!V&vzy$0_XdCp1XAf2GGGY@>gegiQj4`S%4 zziNoNjW+eM$P^VZF&Qx;mtOT3_bLQMRbI-asnyPEOwBH3e{J&5^VPUAS$RFa1b8_|lyuzY<`m?ql06Y({ z;R}HJdVqVr1ZZdixEKG{qKx{6+roI4;`1d|M_rZez4-EpS!)xZ?`L$ty zjd)GG5#WKZ0Pe@%OD+WHz^!~Hz>B8=bm6(b84EBG@AvEwfJK)BtR4z*`UWEsGRxd7+leSUi}zj5tOJ-|hH&92V?icnrZ ze-hxeR{$=30pJ%81MK-Fz%7FTZpH1D{s1M5@p=^d%HaTa^aGeb6JX6jfW#btd5!oR zkFEPVKyo%f6XL(?D}a_-fTvL&lP(9CItAb{l+Wff0cK+TcAN$<1M9vs2jFVNw*}?* zIBr*z0c^u#PvCjm@%%^d_my~jEB>B^$EKhhB4YunvFs^4=SjSNdKJKxWI!?g9&*T2(SpGuJtdffA^6;#Z za6A>OO9#VMmBop8b2J@KCZLC7)v>hXa5%OgUKf)bD2vLAQ_*JW=~{VtA{|S$B%7k? zWJ+?&ig-h#tPZ3+%e9qJymdUyry52Wn!A(rqc3IQ1i09zD4=mYNc8ZIg;+N<6l(C7F^5oE^$? z`>CmD3#&CXws0zH;K-DeG{sw5GUB5L$`dVZ#R8l%?d{t@L_*ql5 z|82@Egz^fT^J%&H{K)w7it9YjsGQAVNF@Flz15}udYjKCLWGdN5)M?A(Tg^nf59tg-a_< z`_-jY<;9u^+37VUk@5)4BA1SBOQ$uGBReI@revxlnMkLSO(oH$rrFWDd9B8=L~A;l zNS7s38jI2M+gj7fW^+k67&a~p=x1SB>7sNjQ6H;Eqhj|9N1KU(lN*(#;&qg4$?o{_ za7k0Nm7Q9ih^5l;sD8W4lkKhPc-=gGcS0(kq|)Dam`Zc&n9Z#bf+a1Pc*P2^V_1D z&Cnz5txO7;5`s$Kt-*dF(a^-spPZPN$W#DP#*$j2v&zMEz>xbi6PW3y-!tg%a?N^? zO^jQj@kJ32b{zDO-XU9d(X_b3i~%Ku)>BIt#p>G9v9WD)<_J$2UtA%*litg6cdx9R zCOwyRot7H1VrzA(#*LNUrV3jy_q?h~;Zn1cVma4vUxb(hwo)Ax07kBs*;q-@5Xc^prg)*<@Q>k;oaCD6Y)%% zJr|uz;UTnGUb6`DT&zCO(?OeKRu9TjQRY8ciVg2cUM^xXyGxd9m`5Z4W(VXoXU>;! zH_a!LUothGurX=sZt=H5B9@*l$F7%JkuwYyaIBa=ow|6YT4)90DTdEyIn&%LbI;Rm zLpwU5I1+7&rPIjYmU~sVrRI=Ph`d~^lzu_b?oKw(Hwc}(jySM^Vo4b~SEd?~^CYL~ z3}t^ravPn!#QW0&k}FEFXRx$WQXyI%PT{B|CEgC*67-zr#IeJ*BH^DwJ#*7JS^+P3 zQo^Iuig+cE67eM6#kbs_l!_0b#WpwLi}i7W>As{S7?$*;`jLuwE4qCaVUc5Y*BFBi z>tpl-cGpczrsH#1?=+zmU32s;)9qU4_r2p@`ThbU?u9`wm^%U^C>WWjIu9cd7^xUL z3ZprrF(UM1jNY7&(Vh!%KNq7)evCq4gyYO0z~DT90r>!Z3-AUQrsy34_zt(f76JSo zqZJ=rgb^c*O6<7=qYoINc;YgQh+))YO)*A%FcPx31fx0_y_j2y8%AN~j>E_sMl@!Y zV|3+mjLJ;F4WlR1Dgh!BF^Vw>>xEI6vMca6MrSIjF@jcu$0lP`1|uC)rs9T?j;iTc zHUp1eiN~+PzgJ^~WhP!X3!^Hvcr1!hqS^SYIvVMl8^d~F#HpzP>(Gdiy*U2GC`{u# z{ELy6#%7G{B`^w@#OPiN)^9#OFNODM#d~8!rmPL`zW{$P#Ck2lXS8EI7h}Zj8r-hM z{p)c5dfaY68Ft{d1m(CC<+uzZdCPHI0Wf?e{=E_Zt^ydk8eqsx04LpyGQI`3TT#xp z;dVPfzdKOYTG(j~z(L%;TMO{_bpT)8g>t_e;LCe(yBFY#`*43f{=Of7Z@|AB@tgx|n*atqggV@e&v_WPEdZxIf@NFr?>7AVC~n(vdkpt?;PJ=t+$R8XcH;IV{(cJU z_%xP3gWI#X{RGc@4!7s=_zU>=MciJ(`o9d&|EIY947XRX&aVP|joW9t&^C5sbZZYr z0bj$2)$15>`#DD0et}WeH!$M&CPqNt!f4qqF`D)^M$vwS(aOE3pM9vWE{wu;qmF-# zGJgkU`!34$Hz?=#&`#gS?E|#c-(s}vL#*RRh~Z-r)P+TG8MMM!NW&$t04|0xa4nQT z8(ag|!KF|P7ePCOVKFR(tKdo)3hUr8coNQo+o2ljVKz*LI;epd28f@7UT_rr1bV}> z&4 z7zqJb3D-jr+z9z_6NI1xR>5dk2CHEOEP(=83KxPOM#1@T3)~EOupE92H$W~n)(hY^ z2tpGiVLYUu6t0FDPzzI`3Z}tKm=3dG4orcw;chqwa^PM#3+{m*!6V>7R(6*0%ze6Hq)@TY1ex*K9XQTHH(qLL$#Ms(DVL>gH%4BhA>kwzM*I}&L; z5@`${i8N|2WRFA|wMe<~e%j>u@WXmdR!1U@^d&wE$H~KnMrWB5+3{jf8f|_@$>vV*|eB_lNxFCuh$klkM0HyF$whJAZC&K|w)Ijz1LCkJR?7 z=+z5ut$#6Cfx)1oant^tpQ?|hqx84-|B1L=oY^J6#=w{{oq@n-uWs!2ZP*Q+Zyh~$ z=J|8t`0AGaJNO@*tJjOwtj@UFeD7XZ+NYOxck$m*dXaWcZ;Wx^tT6NJihktIg+<>3 zbDk%d_pkZCUjKOrF;D6dGuF&4<^dVZ`Vk&im8J+WO`({O* zpS{-q?D6k(`(7%7U6VfeOM@M5ziZq>DDKR(EUl~4=UowtFA$l{4t*e(w|G_boA!=g zFzCaq=&@#Qne7}adK^?(CZ$muC=j{MP73}aZ)~7r=d-8x*_@T%v1V?iKy1L`?XdTv z)CNS>V;ETz$g}{yN54t21+>il#qfq_x_q5O@nu4E>JFP0P~e{RPSK)PuZdOi1#e$8E%pHm~I8FTX3&d)Ou~>87F+Ws>`=w zVvjyefqUN5`Cgwss!tH5`py>_&mK3k1lrH3;)d6+Y#hC@%lGJB)OX{`Pi^##HFMk6 zbzjajZI-m~e39SmF&A-ujVk8igO?8cB!K+(uO9n$teIQPr$}0OkhSi7k=p{yy!F2Z zw}p7*s}!xgt z_>U|toJ6oP^U-t(h#Y4tfe_0Hr1q|=`B6h^1_p2I_O0(md+$u-+eyGZ#|<*D zGzOaBpxd{|Z1$M>7OskUm_y87GMg#;gW?Vf%$CdgY0JC?p=jUdIKz!KbIWWe z810%Qx#)-mChGw)i%+>je3nDR?wRZ)192u}h_vaTz+<_LrA0mS{>pMiJ-3WHBiZ?0 z)*cLN=2p)cf5ph|(seB`*&#<=>kzejCd;6%Zf~XOS2}8e%X&b~SdLn_s;<;W{;VTKxtc6#eBdaX7>!H zy)&faOq$zCTeHYswumj!VpYVu{&C=s@2tu?(g?U`FKe{elo>?1%r9~o>x~5)FJf|` z`d*@F<4ZpaPd`69cVo@ma@m=hzD@B`ey#-3zF*`ud;IK(JNz9E@w?}>GwkuRxRPJw zIEGTxKi_w&>VG!kziE3`KMiZ+m#?u0FODwTN(ei5tR;_k|+&$A- zV~ND_4V{aOcc_u?|KVrDrazrs=k6JgAU99iC(!jMYOct3w#Y49ud4H9$DH-#-qqc{ zoqaK?G;Hr6o2U}j%&oOsx!ybhh;Uu5$aTy+6)c`Drjj?f}*hp%n79esS^Wg*w zutZg_SG0g-4kh58^GH?UC~~uZsN5F9((8+}`cGAG2+DEjs-*xy@tEGQhdHgU;vo zSv9UrCJzve7iFl`5wv4B;6vU|y_f2P~pNTmH3*cCi1FLDNf`yL>w?NBa*A+hk+^ zX2MgHf%Q$AW(TGvPN)Pj)aO6~JOijoV9?ux{yhJkeZC#zdz65g`V=K#?e#J(fhJLC zyye&?PcIE;;lec274T_VaNkElv#m!r6wW)feS+hPOBC8EwP@(idJyEGb`|b z3$3vpjR}lp#fVQ}N_EhguqXV?TcE8#6@Ncx_&s1kG9TRJ{c8BhdQzkpFr_>=z|O|R z?y&#R5q2)+L9rLGj(j!jY<(=v@-fALCAk>rSzj+&KCk(9Maw_N8F~-+Po{a2eo8ej z;XQloJnyMu?|uBX%>xqIJRmL!LQ(TF-ZSNzl6GGx;XM|~7PZeJ0@U^wDQf>HXWI9G z_pCFbHQU!yMXCM;|1V^4yNI5R4c(#d;|#qAtaqBE?kHb9iitE zEfiT$j$RudQQOaxc!>7s`PQ$h``ewN_kaWCt)E2(bqPqkL>B)Pt)F=c)cRA3)(Abo*96hBE?=E8 z8hOuPXZ8XFf4^%+xp!b0DNJbLZ z>S=boNR+EBE3(%MOuq4o1sn#N7Z8;1=$ zgjRO^U>?T8et=$1pUp4GHI6lNZSNWE>@!;IQ(y$U=;0abUx<2VeLrP+RlOfrzUA2G zTC!PtzylAD%y4FUFJa$6(=A@Ya;!Cr$a(KBp=j}Yj(h)ae=N_6+yj;)-W@@1UIx+- z0f~TP0);gOZ19;V0e%*ksswg9lYj?IM`nQ$2~^5I|3GOEk>(0}#h-(t;NTfKFrkJjch zAMnz&Z<_FD*uFh}p7)jU_kVE0^~Y!9JZ{3DBIj9W{|ZYX6Xr7!p92{%>A16jnrnj1OX?@c4^>1&JR-c=3cn$n)EGyW(MelPJ?S6m% z=RX)z(&c-y4s)FL?6p}pDc1x1MDO%%P6j{q^d-1$+q*ITb7$`zj@T~X0dBJXp(&=J zsa=16@qKVxG8$FweTgI7c^=@Fu{`@EsK)XI-wVArEDsZp`snF+w{O#X=;yrfyO-rG zU*G}y9#}p|wI7n;XFa*l+AZ2w^>bD@(tgMT{4(vc&wmjg-$Zsaw=ER1N6%t??mV2@ z5l%cDKSy?IYuB4{dt~1mr=#0I#sHWEn3ZSKzv>vT#IehcK9iZpPPYpfk>5cJyNlAHCL4E<% zx{34-7w+2s869*G(cwtX3*3?M?!!=)O>>%6+Hc1+edUcdvk zv*vJ_Ml|{-yEVoSNchf92G%)?sxsK^ES~ED-;w^F#B+^kMkbt1_&y+EJl3ySZNM7M zRDGB6nlt^uqA*<ufHf2V7@ei%naasG%`UdiOk$?~He` zqQ&#=U3C^_A4fCsux73;UTp7d7N$N6S7f^$(6gu}wRu(aWzO{O0oz&M&<8P^5nI@j zOr`Y*rpR_ZpyzvdRrI$xn}hBF+nFNvraV0}BrXK;Ep+gF`~e}^;t9xxuUeKG$c z6>W}@mY*+jo-KOos8$vI8b|1{X0BU48Py8tt@-Ks682*~TQ&<&%|Ke`Oa@-DpCE$_ z_vcI4&mKL`eyZs2c81<7_7l)&*5$~Tuph%H>Yn+)l>1e)5bt${-UId{bMyrCMiwGv z015xuOMv-9?j*3@nFKuGKQdQCkbrfKt^x@Q+Q}ebd_BUQ3^qB-O7?&SM`?LDf()1! zz$GS=(O-dt51G$rZ4-f9-Xl=WR^RMQ8XoZB2&pu*K0zW02{+nHf^QqDlGy4@5*~1) z+%A}JERlwUE1C91yI^vntweQ2hcV7p^YDNxhfD1Ozv|nN!q1$>7sN=~CVA^EVN5$Y z_=^~ixRXQ2nggF7opnW@0uLBd94}N=n*HTM30FFO2cWv9OK(T3nqbXbdyLk1{&Y1T z^LVLH!jAU%dF`v>ul?EYURzbv?R#Jk=4_sKn$3!?SToo7W!fj}SLpNF3MGta8wWQ= z64drpSF`HnY&9zn7*ULkRaIuPwF@QuXOErl4OFo&bi{vHGuN88&T7>x-!xKU`j>E@ zy$skm!kzy6I&1wNaGw|-tg6h6ltL2bVdgteI>5%b1VP*9}RS?@;4E){*Y9W`_hn^M&-;UJ~wOtz6VUJ3?(=bp_M@&hUG{ zeX{m%j1-OaYC;kYWJpCBFzaw9gA<*}zyl5><7NKMnC|p zwFSt0VDrjlMFI!fqYoHgd!X7^Mc?ko55$_eMn6;=2U|E$HVQ5hc(BO8DzJcMMvUs0 zEuw2*A2niGRvYku2dy*ywKhP#0Q}y$G3}Mkf9wIbvC=EmzAElrO}&2Jnsq&Yt!5eZ ztIdCuwf(8_glTq!KQ}{uhYNp)!><^b$uhr9<>)@>4@^wSIy=80PS2 zXfGRn>#CKi_?J4gelzW<;XlX1{bu9Wg+M$&KPFhPg&mj_SiDzk1N=-G)$h3F?)aPk zsw=vEk8a1jpXe)VK^#_@fTL+UTDBq+e~E%8-l9$Ml=c=2gGcS?BOe zo$i@0WAi4(2d3&aFLItea(0C3Tow5ehu>&*&w19km&xRr<}ty-%66=06E)9{Q2eT2 zC|>3aKbQKU#`)Gh&xD`p-<0;j*q)&j@Q3)AuPXli4!=q5p7S!_q@Gs1C{D8-{H8>Y z!-e?w4iUR&xeVeNCd4AUvB-_6W9IKt8(*fFxBjg|#O~S6^sV)F0-1U-rlbz0ZW)-y6+UTDWM}fX5MpC)pO3$)Z~lb&HC+OteIQSt-t2K>Uk{fwC@Uv z{u+x)i+0UC8EV(d747;PXFA53xy3ucz&klHFOgiB&@qcXnmuNA#2x17s56qC?`7Aq zd-fuksYJ)s?X44H?X#27RK1Q{=~>}D$iYo@uX4CZ}aP&w?=eZE(B_k~dh`V6*-uyKhQZu>IUftNHzTlKM* zDIJ3$XJbl{;h4Wp8E%!L_LB~gyJxsergdeqtu;nrQ+_igVr0k7du^(?cl{$hs^-ov z-=ljmrrNmjQ#*~j=Qrz$agnBIyS`$aDFx$j;U07F(t)1@x_uk|g>-F+N%yiq-Isg1s|W^UP?tbQMkR7a@qRuULSMSRM)Dg=U-K}bJbm4 zzRn!&J<-%1cBtKRnsv6QX5tme*|V9Qi@uvZcGd#iVb9sKu2-MU$ZL~&*0_6Kv(Aw* zu_$~j z`*Kr~1mn0vjlMG{{_Ws*x_vJd;c7V_{H4JLJ(nb*=DiX0;YdZKYEq=Ms%H8uf_xmV zoS~1@Oeqo!J!3Rc_e?I-@^4YpJ&aj2{!|C(-E$pzUt~tFnSLx)U6(W>6Q(4I?D+YX zuZn-ypbwAPxj!q{W6j+5aJu%3%fqv(W2pu4x){DAUto$bo6;n*qPN7NRMB^?{&JBM z^zM0&jMKvr4Sl3BnP5?wLXqv*$V9Ms3+7u@Eq>1@e?Rz8R<6gIxz)G{b7itV)~vU3 zQ#u6Gvpr_MZ&$_KS-ItP2YTkxA=EgIyd$G}E}zKeewY#<7-Hs^i(2M+?KV{{@B8X& VCy3qi8i~eG#3jk*2w5NG{{hy;m!SXv literal 0 HcmV?d00001 diff --git a/src/exceptionhandler/exceptionhandler.cpp b/src/exceptionhandler/exceptionhandler.cpp new file mode 100644 index 0000000..838f496 --- /dev/null +++ b/src/exceptionhandler/exceptionhandler.cpp @@ -0,0 +1,502 @@ +/* + * $Logfile: /Freespace2/code/ExceptionHandler/ExceptionHandler.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Main file for dealing with exception handling + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 1 6/29/99 7:42p Dave + * + * 3 1/19/99 9:07a Allender + * removed compiler warning pragma's since we are already ignoring them in + * vtypes.h + * + * 2 1/18/99 4:34p Allender + * added the exception handler routines from Game Developer for structured + * exception handling in vsdk code + * + * $NoKeywords: $ + */ + +// Copyright © 1998 Bruce Dawson. +/* +This source file contains the exception handler for recording error +information after crashes. See exceptionhandler.h for information +on how to hook it in. +*/ + + +#include + +// -------------------- +// +// Defines +// +// -------------------- +#define ONEK 1024 +#define SIXTYFOURK (64*ONEK) +#define ONEM (ONEK*ONEK) +#define ONEG (ONEK*ONEK*ONEK) + +// -------------------- +// +// Enumerated Types +// +// -------------------- + + +// -------------------- +// +// Structures +// +// -------------------- + + +// -------------------- +// +// Classes +// +// -------------------- + + +// -------------------- +// +// Global Variables +// +// -------------------- + + +// -------------------- +// +// Local Variables +// +// -------------------- + +const int NumCodeBytes = 16; // Number of code bytes to record. +const int MaxStackDump = 2048; // Maximum number of DWORDS in stack dumps. +const int StackColumns = 8; // Number of columns in stack dump. + +// -------------------- +// +// Internal Functions +// +// -------------------- + +// hprintf behaves similarly to printf, with a few vital differences. +// It uses wvsprintf to do the formatting, which is a system routine, +// thus avoiding C run time interactions. For similar reasons it +// uses WriteFile rather than fwrite. +// The one limitation that this imposes is that wvsprintf, and +// therefore hprintf, cannot handle floating point numbers. +static void hprintf(HANDLE LogFile, char* Format, ...) +{ + char buffer[2000]; // wvsprintf never prints more than one K. + + va_list arglist; + va_start( arglist, Format); + wvsprintf(buffer, Format, arglist); + va_end( arglist); + + DWORD NumBytes; + WriteFile(LogFile, buffer, lstrlen(buffer), &NumBytes, 0); +} + +// Print the specified FILETIME to output in a human readable format, +// without using the C run time. +static void PrintTime(char *output, FILETIME TimeToPrint) +{ + WORD Date, Time; + if (FileTimeToLocalFileTime(&TimeToPrint, &TimeToPrint) && + FileTimeToDosDateTime(&TimeToPrint, &Date, &Time)) + { + // What a silly way to print out the file date/time. Oh well, + // it works, and I'm not aware of a cleaner way to do it. + wsprintf(output, "%d/%d/%d %02d:%02d:%02d", + (Date / 32) & 15, Date & 31, (Date / 512) + 1980, + (Time / 2048), (Time / 32) & 63, (Time & 31) * 2); + } else { + output[0] = 0; + } +} + +// Print information about a code module (DLL or EXE) such as its size, +// location, time stamp, etc. +static void ShowModuleInfo(HANDLE LogFile, HINSTANCE ModuleHandle) +{ + char ModName[MAX_PATH]; + __try { + if (GetModuleFileName(ModuleHandle, ModName, sizeof(ModName)) > 0) { + // If GetModuleFileName returns greater than zero then this must + // be a valid code module address. Therefore we can try to walk + // our way through its structures to find the link time stamp. + IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle; + if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic) { + return; + } + + IMAGE_NT_HEADERS *NTHeader = (IMAGE_NT_HEADERS*)((char *)DosHeader + DosHeader->e_lfanew); + if (IMAGE_NT_SIGNATURE != NTHeader->Signature) { + return; + } + + // Open the code module file so that we can get its file date + // and size. + HANDLE ModuleFile = CreateFile(ModName, GENERIC_READ, + FILE_SHARE_READ, 0, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0); + char TimeBuffer[100] = ""; + DWORD FileSize = 0; + if (ModuleFile != INVALID_HANDLE_VALUE) { + FileSize = GetFileSize(ModuleFile, 0); + FILETIME LastWriteTime; + if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime)) { + wsprintf(TimeBuffer, " - file date is "); + PrintTime(TimeBuffer + lstrlen(TimeBuffer), LastWriteTime); + } + CloseHandle(ModuleFile); + } + hprintf(LogFile, "%s, loaded at 0x%08x - %d bytes - %08x%s\r\n", + ModName, ModuleHandle, FileSize, + NTHeader->FileHeader.TimeDateStamp, TimeBuffer); + } + } + // Handle any exceptions by continuing from this point. + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +} + +// Scan memory looking for code modules (DLLs or EXEs). VirtualQuery is used +// to find all the blocks of address space that were reserved or committed, +// and ShowModuleInfo will display module information if they are code +// modules. + +static void RecordModuleList(HANDLE LogFile) +{ + hprintf(LogFile, "\r\n" + "\tModule list: names, addresses, sizes, time stamps " + "and file times:\r\n"); + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + const size_t PageSize = SystemInfo.dwPageSize; + // Set NumPages to the number of pages in the 4GByte address space, + // while being careful to avoid overflowing ints. + const size_t NumPages = 4 * size_t(ONEG / PageSize); + size_t pageNum = 0; + void *LastAllocationBase = 0; + while (pageNum < NumPages) { + MEMORY_BASIC_INFORMATION MemInfo; + if (VirtualQuery((void *)(pageNum * PageSize), &MemInfo, sizeof(MemInfo))) { + if (MemInfo.RegionSize > 0) { + + // Adjust the page number to skip over this block of memory. + pageNum += MemInfo.RegionSize / PageSize; + if (MemInfo.State == MEM_COMMIT && MemInfo.AllocationBase > LastAllocationBase) { + // Look for new blocks of committed memory, and try + // recording their module names - this will fail + // gracefully if they aren't code modules. + LastAllocationBase = MemInfo.AllocationBase; + ShowModuleInfo(LogFile, (HINSTANCE)LastAllocationBase); + } + } else { + pageNum += SIXTYFOURK / PageSize; + } + } else { + // If VirtualQuery fails we advance by 64K because that is the + // granularity of address space doled out by VirtualAlloc(). + pageNum += SIXTYFOURK / PageSize; + } + } +} + +// Record information about the user's system, such as processor type, amount +// of memory, etc. + +static void RecordSystemInformation(HANDLE LogFile) +{ + FILETIME CurrentTime; + GetSystemTimeAsFileTime(&CurrentTime); + char TimeBuffer[100]; + PrintTime(TimeBuffer, CurrentTime); + hprintf(LogFile, "Error occurred at %s.\r\n", TimeBuffer); + char ModuleName[MAX_PATH]; + if (GetModuleFileName(0, ModuleName, sizeof(ModuleName)) <= 0) { + lstrcpy(ModuleName, "Unknown"); + } + char UserName[200]; + DWORD UserNameSize = sizeof(UserName); + if (!GetUserName(UserName, &UserNameSize)) { + lstrcpy(UserName, "Unknown"); + } + hprintf(LogFile, "%s, run by %s.\r\n", ModuleName, UserName); + + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + hprintf(LogFile, "%d processor(s), type %d.\r\n", + SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType); + + MEMORYSTATUS MemInfo; + MemInfo.dwLength = sizeof(MemInfo); + GlobalMemoryStatus(&MemInfo); + // Print out the amount of physical memory, rounded up. + hprintf(LogFile, "%d MBytes physical memory.\r\n", (MemInfo.dwTotalPhys + + ONEM - 1) / ONEM); +} + +// Translate the exception code into something human readable. + +static const char *GetExceptionDescription(DWORD ExceptionCode) +{ + struct ExceptionNames + { + DWORD ExceptionCode; + char* ExceptionName; + }; + + ExceptionNames ExceptionMap[] = + { + {0x40010005, "a Control-C"}, + {0x40010008, "a Control-Break"}, + {0x80000002, "a Datatype Misalignment"}, + {0x80000003, "a Breakpoint"}, + {0xc0000005, "an Access Violation"}, + {0xc0000006, "an In Page Error"}, + {0xc0000017, "a No Memory"}, + {0xc000001d, "an Illegal Instruction"}, + {0xc0000025, "a Noncontinuable Exception"}, + {0xc0000026, "an Invalid Disposition"}, + {0xc000008c, "a Array Bounds Exceeded"}, + {0xc000008d, "a Float Denormal Operand"}, + {0xc000008e, "a Float Divide by Zero"}, + {0xc000008f, "a Float Inexact Result"}, + {0xc0000090, "a Float Invalid Operation"}, + {0xc0000091, "a Float Overflow"}, + {0xc0000092, "a Float Stack Check"}, + {0xc0000093, "a Float Underflow"}, + {0xc0000094, "an Integer Divide by Zero"}, + {0xc0000095, "an Integer Overflow"}, + {0xc0000096, "a Privileged Instruction"}, + {0xc00000fD, "a Stack Overflow"}, + {0xc0000142, "a DLL Initialization Failed"}, + {0xe06d7363, "a Microsoft C++ Exception"}, + }; + + for (int i = 0; i < sizeof(ExceptionMap) / sizeof(ExceptionMap[0]); i++) { + if (ExceptionCode == ExceptionMap[i].ExceptionCode) { + return ExceptionMap[i].ExceptionName; + } + } + + return "Unknown exception type"; +} + +static char* GetFilePart(char *source) +{ + char *result = strrchr(source, '\\'); + if (result) { + result++; + } else { + result = source; + } + return result; +} + +// -------------------- +// +// External Functions +// +// -------------------- + + +// Entry point into the main exception handling routine. This routine is put into an except() +// statment at the beginning of a thread and is called anytime that there is a program exception +// The data is stored in a file called ErrorLog.txt in the data directory. +// +// data: pointer to the exception data +// Message: Any message that should be printed out in the error log file +// +// returns: +// +int __cdecl RecordExceptionInfo(PEXCEPTION_POINTERS data, const char *Message) +{ + static bool BeenHere = false; + + // Going recursive! That must mean this routine crashed! + if (BeenHere) { + return EXCEPTION_CONTINUE_SEARCH; + } + + BeenHere = true; + + char ModuleName[MAX_PATH]; + char FileName[MAX_PATH] = "Unknown"; + // Create a filename to record the error information to. + // Storing it in the executable directory works well. + if (GetModuleFileName(0, ModuleName, sizeof(ModuleName)) <= 0) { + ModuleName[0] = 0; + } + + char *FilePart = GetFilePart(ModuleName); + + // Extract the file name portion and remove it's file extension. We'll + // use that name shortly. + lstrcpy(FileName, FilePart); + char *lastperiod = strrchr(FileName, '.'); + if (lastperiod) { + lastperiod[0] = 0; + } + + // Replace the executable filename with our error log file name. + lstrcpy(FilePart, "errorlog.txt"); + HANDLE LogFile = CreateFile(ModuleName, GENERIC_WRITE, 0, 0, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0); + if (LogFile == INVALID_HANDLE_VALUE) { + OutputDebugString("Error creating exception report"); + return EXCEPTION_CONTINUE_SEARCH; + } + + // Append to the error log. + SetFilePointer(LogFile, 0, 0, FILE_END); + // Print out some blank lines to separate this error log from any previous ones. + hprintf(LogFile, "\r\n\r\n\r\n\r\n"); + PEXCEPTION_RECORD Exception = data->ExceptionRecord; + PCONTEXT Context = data->ContextRecord; + + char CrashModulePathName[MAX_PATH]; + char *CrashModuleFileName = "Unknown"; + MEMORY_BASIC_INFORMATION MemInfo; + // VirtualQuery can be used to get the allocation base associated with a + // code address, which is the same as the ModuleHandle. This can be used + // to get the filename of the module that the crash happened in. + if (VirtualQuery((void*)Context->Eip, &MemInfo, sizeof(MemInfo)) && GetModuleFileName((HINSTANCE)MemInfo.AllocationBase, CrashModulePathName, sizeof(CrashModulePathName)) > 0) { + CrashModuleFileName = GetFilePart(CrashModulePathName); + } + + // Print out the beginning of the error log in a Win95 error window + // compatible format. + hprintf(LogFile, "%s caused %s in module %s at %04x:%08x.\r\n", + FileName, GetExceptionDescription(Exception->ExceptionCode), + CrashModuleFileName, Context->SegCs, Context->Eip); + hprintf(LogFile, "Exception handler called in %s.\r\n", Message); + RecordSystemInformation(LogFile); + // If the exception was an access violation, print out some additional + // information, to the error log and the debugger. + if (Exception->ExceptionCode == STATUS_ACCESS_VIOLATION && Exception->NumberParameters >= 2) { + char DebugMessage[1000]; + const char* readwrite = "Read from"; + if (Exception->ExceptionInformation[0]) { + readwrite = "Write to"; + } + + wsprintf(DebugMessage, "%s location %08x caused an access violation.\r\n", readwrite, Exception->ExceptionInformation[1]); + +#ifdef _DEBUG + // The VisualC++ debugger doesn't actually tell you whether a read + // or a write caused the access violation, nor does it tell what + // address was being read or written. So I fixed that. + OutputDebugString("Exception handler: "); + OutputDebugString(DebugMessage); +#endif + + hprintf(LogFile, "%s", DebugMessage); + } + + // Print out the register values in a Win95 error window compatible format. + hprintf(LogFile, "\r\n"); + hprintf(LogFile, "Registers:\r\n"); + hprintf(LogFile, "EAX=%08x CS=%04x EIP=%08x EFLGS=%08x\r\n", + Context->Eax, Context->SegCs, Context->Eip, Context->EFlags); + hprintf(LogFile, "EBX=%08x SS=%04x ESP=%08x EBP=%08x\r\n", + Context->Ebx, Context->SegSs, Context->Esp, Context->Ebp); + hprintf(LogFile, "ECX=%08x DS=%04x ESI=%08x FS=%04x\r\n", + Context->Ecx, Context->SegDs, Context->Esi, Context->SegFs); + hprintf(LogFile, "EDX=%08x ES=%04x EDI=%08x GS=%04x\r\n", + Context->Edx, Context->SegEs, Context->Edi, Context->SegGs); + hprintf(LogFile, "Bytes at CS:EIP:\r\n"); + + // Print out the bytes of code at the instruction pointer. Since the + // crash may have been caused by an instruction pointer that was bad, + // this code needs to be wrapped in an exception handler, in case there + // is no memory to read. If the dereferencing of code[] fails, the + // exception handler will print '??'. + unsigned char *code = (unsigned char*)Context->Eip; + for (int codebyte = 0; codebyte < NumCodeBytes; codebyte++) { + __try { + hprintf(LogFile, "%02x ", code[codebyte]); + + } + __except(EXCEPTION_EXECUTE_HANDLER) { + hprintf(LogFile, "?? "); + } + } + + // Time to print part or all of the stack to the error log. This allows + // us to figure out the call stack, parameters, local variables, etc. + hprintf(LogFile, "\r\n" + "Stack dump:\r\n"); + __try { + // Esp contains the bottom of the stack, or at least the bottom of + // the currently used area. + DWORD* pStack = (DWORD *)Context->Esp; + DWORD* pStackTop; + __asm + { + // Load the top (highest address) of the stack from the + // thread information block. It will be found there in + // Win9x and Windows NT. + mov eax, fs:[4] + mov pStackTop, eax + } + if (pStackTop > pStack + MaxStackDump) { + pStackTop = pStack + MaxStackDump; + } + + int Count = 0; + // Too many calls to WriteFile can take a long time, causing + // confusing delays when programs crash. Therefore I implemented + // simple buffering for the stack dumping code instead of calling + // hprintf directly. + char buffer[1000] = ""; + const int safetyzone = 50; + char* nearend = buffer + sizeof(buffer) - safetyzone; + char* output = buffer; + while (pStack + 1 <= pStackTop) { + if ((Count % StackColumns) == 0) { + output += wsprintf(output, "%08x: ", pStack); + } + + char *Suffix = " "; + if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop) { + Suffix = "\r\n"; + } + + output += wsprintf(output, "%08x%s", *pStack, Suffix); + pStack++; + // Check for when the buffer is almost full, and flush it to disk. + if (output > nearend) { + hprintf(LogFile, "%s", buffer); + buffer[0] = 0; + output = buffer; + } + } + // Print out any final characters from the cache. + hprintf(LogFile, "%s", buffer); + } + __except(EXCEPTION_EXECUTE_HANDLER) { + hprintf(LogFile, "Exception encountered during stack dump.\r\n"); + } + + RecordModuleList(LogFile); + + CloseHandle(LogFile); + // Return the magic value which tells Win32 that this handler didn't + // actually handle the exception - so that things will proceed as per + // normal. + return EXCEPTION_CONTINUE_SEARCH; +} diff --git a/src/fireball/fireballs.cpp b/src/fireball/fireballs.cpp new file mode 100644 index 0000000..4b98a2d --- /dev/null +++ b/src/fireball/fireballs.cpp @@ -0,0 +1,1162 @@ +/* + * $Logfile: /Freespace2/code/Fireball/FireBalls.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to move, render and otherwise deal with fireballs. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 19 9/14/99 1:27a Andsager + * Better LOD for fireballs when behind. Move point ahead to get vertex + * ahead and then find size. + * + * 18 9/12/99 11:42p Dave + * + * 17 9/13/99 10:09a Andsager + * Add debug console commands to lower model render detail and fireball + * LOD for big ship explosiosns. + * + * 16 9/09/99 8:53p Dave + * Fixed multiplayer degenerate orientation case problem. Make sure warp + * effect never goes lower than LOD 1. + * + * 15 9/07/99 12:20a Andsager + * LOD less agressive at lower hardware detail level + * + * 14 9/06/99 6:47p Jamesa + * Fixed build error. + * + * 13 9/06/99 3:23p Andsager + * Make fireball and weapon expl ani LOD choice look at resolution of the + * bitmap + * + * 12 9/01/99 10:09a Dave + * Pirate bob. + * + * 11 8/31/99 10:13p Andsager + * Add Knossos warp effect fireball + * + * 10 8/30/99 9:59a Dave + * Removed explosion LOD stuff. + * + * 9 8/28/99 4:54p Dave + * Fixed directives display for multiplayer clients for wings with + * multiple waves. Fixed hud threat indicator rendering color. + * + * 8 8/27/99 9:07p Dave + * LOD explosions. Improved beam weapon accuracy. + * + * 7 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 6 4/28/99 11:13p Dave + * Temporary checkin of artillery code. + * + * 5 4/23/99 12:01p Johnson + * Added SIF_HUGE_SHIP + * + * 4 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 3 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 75 5/18/98 2:49p Lawrance + * Allow up to double instance of sound for warp holes + * + * 74 5/15/98 3:54p John + * Added code so that only "perishable" fireballs get removed. + * + * 73 4/15/98 9:42a Adam + * added 2 more explosion types (1, actually, but placeholder for 2) + * + * 72 4/12/98 9:56a John + * Made lighting detail flags work. Made explosions cast light on + * highest. + * + * 71 4/09/98 7:58p John + * Cleaned up tmapper code a bit. Put NDEBUG around some ndebug stuff. + * Took out XPARENT flag settings in all the alpha-blended texture stuff. + * + * 70 3/31/98 5:11p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * 69 3/30/98 4:02p John + * Made machines with < 32 MB of RAM use every other frame of certain + * bitmaps. Put in code to keep track of how much RAM we've malloc'd. + * + * 68 3/29/98 12:39p John + * Made warp in glow page in + * + * 67 3/27/98 2:17p Lawrance + * Don't play warphole closing for non-captial sized warp effects + * + * 66 3/26/98 5:44p Lawrance + * take out debug comments for warphole sounds playing + * + * 65 3/26/98 5:21p John + * Added new code to preload all bitmaps at the start of a level. + * Commented it out, though. + * + * 64 3/25/98 5:30p Lawrance + * Fix bug that was not playing wormhole open sound effect at correct + * volume + * + * 63 3/25/98 10:56a Lawrance + * Make a sound for warphole close as well as warphole open, move to + * fireball lib + * + * 62 3/18/98 12:36p John + * Made hardware have nicer looking warp effect + * + * 61 3/18/98 12:01p John + * Made warp grid grow faster + * + * 60 2/26/98 10:07p Hoffoss + * Rewrote state saving and restoring to fix bugs and simplify the code. + * + * 59 2/22/98 12:19p John + * Externalized some strings + * + * 58 2/19/98 4:32p Lawrance + * Add asteroid explosion + * + * 57 2/12/98 11:53p Lawrance + * comment out use of current_frame since not used anywhere + * + * 56 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 55 1/23/98 5:06p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 54 1/19/98 9:37p Allender + * Great Compiler Warning Purge of Jan, 1998. Used pragma's in a couple + * of places since I was unsure of what to do with code. + * + * 53 1/15/98 4:58p John + * Made warp effect use a looping ani. Made the scaling up & down be in + * software. + * + * 52 1/15/98 2:32p John + * Added code to optionally take a velocity for a fireball. + * + * 51 1/11/98 2:15p John + * Changed a lot of stuff that had to do with bitmap loading. Made cfile + * not do callbacks, I put that in global code. Made only bitmaps that + * need to load for a level load. + * + * 50 1/02/98 5:04p John + * Several explosion related changes. Made fireballs not be used as + * ani's. Made ship spark system expell particles. Took away impact + * explosion for weapon hitting ship... this needs to get added to weapon + * info and makes shield hit more obvious. Only make sparks when hit + * hull, not shields. + * + * 49 12/31/97 4:48p John + * added some debug info + * + * 48 12/30/97 6:44p John + * Made g3_Draw_bitmap functions account for aspect of bitmap. + * + * 47 12/08/97 11:15a John + * added parameter to warpout for life. + * + * 46 12/02/97 3:59p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 45 11/30/97 2:03p John + * made warp effect not use an ani for height map. + * + * 44 11/16/97 12:20p John + * added code to make ships instantly warp in/out if fireball effect + * fails. But, made it so the fireball effect will kill off old sparks + * so it can go, so this should never happen. + * + * 43 11/16/97 10:48a John + * added code to remove the oldest fireball if there weren't enough slots + * for a new one. + * + * 42 11/01/97 1:49p John + * added code to page fireballs in during level load. Made player warpout + * use Adam's new camera movement pattern. Make ships docked to warping + * out ships render correctly. + * + * 41 10/29/97 5:05p John + * Changed dynamic lighting to only rotate and calculate lighting for + * point lights that are close to an object. Changed lower framerate cap + * from 4 to .5. + * + * 40 10/24/97 6:24p John + * added code to return the life left of a fireball + * + * 39 9/14/97 4:49p Lawrance + * add some demo debugging code + * + * 38 9/12/97 4:02p John + * put in ship warp out effect. + * put in dynamic lighting for warp in/out + * + * 37 9/09/97 4:49p John + * Almost done ship warp in code + * + * 36 9/08/97 8:39a John + * added in code structure for grid + * + * 35 9/03/97 5:04p Lawrance + * add error checking when restoring structs + * + * 34 9/03/97 4:32p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 33 8/29/97 2:26p John + * first rev of ship warp in effect. Nothing more than a fireball inside + * of freespace, but textest.cpp contains the correct effect code that + * needs to be transferred into the game next. + * + * 32 8/21/97 5:11p Lawrance + * frame numbering for ANI's now is from 0 -> total_frames-1. + * + * 31 8/13/97 9:50p Allender + * split *_move into *_process_pre and *_process_post functions. + * process_pre functions called before object is moved. _process_post + * functions called after object is moved. Reordered code in ship_post + * and weapon_post for multiplayer + * + * 30 8/13/97 12:06p Lawrance + * supporting multiple types of fireball explosions + * + * 29 8/05/97 10:18a Lawrance + * my_rand() being used temporarily instead of rand() + * + * 28 7/31/97 5:55p John + * made so you pass flags to obj_create. + * Added new collision code that ignores any pairs that will never + * collide. + * + * 27 7/24/97 9:54a Lawrance + * don't free fireball system if not inited + * + * 26 7/23/97 11:35a Lawrance + * ensure fireball animations are always freed properly + * + * 25 7/21/97 11:41a Lawrance + * clean up fireballs at the end of each level + * + * 24 7/20/97 6:58p Lawrance + * call anim_get_frame() to take advantage of key-framed animation + * + * 23 7/11/97 3:57p John + * made fireballs translucent. alpha=1.3x intesity. only for d3d. + * + * 22 7/11/97 12:08p John + * made fireballs randomly be rotated. + * + * 21 7/11/97 11:54a John + * added rotated 3d bitmaps. + * + * 20 7/11/97 11:19a Lawrance + * fix mem leaks, move save code from State.cpp here + * + * 19 7/10/97 1:51p Lawrance + * sorting anim fireballs + * + * 18 6/24/97 6:21p John + * added detail flags. + * sped up motion debris system a bit. + * + * 17 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 16 5/05/97 10:40p Mike + * Make missile exhaust trails look a tad nicer. + * + * 15 3/11/97 10:47p Mike + * Add a slew of secondary weapons. + * Support exhaust blobs. + * Add weapons that spawn weapons. + * Add remotely detonatable weapons. + * Add heat-seeking missiles. + * + * 14 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#include + +#include "vecmat.h" +#include "tmapper.h" +#include "2d.h" +#include "3d.h" +#include "bmpman.h" +#include "model.h" +#include "key.h" +#include "physics.h" +#include "floating.h" +#include "model.h" +#include "lighting.h" +#include "object.h" +#include "ship.h" +#include "systemvars.h" +#include "fireballs.h" +#include "linklist.h" +#include "gamesnd.h" +#include "localize.h" + +#define WARPHOLE_GROW_TIME (1.5f) // time for warphole to reach max size (also time to shrink to nothing once it begins to shrink) + +#define MAX_FIREBALL_LOD 4 + +typedef struct fireball_lod { + char filename[MAX_FILENAME_LEN]; + int bitmap_id; + int num_frames; + int fps; +} fireball_lod; + +typedef struct fireball_info { + int lod_count; + fireball_lod lod[4]; +} fireball_info; + +// flag values for fireball struct flags member +#define FBF_WARP_CLOSE_SOUND_PLAYED (1<<0) +#define FBF_WARP_CAPTIAL_SIZE (1<<1) +#define FBF_WARP_CRUISER_SIZE (1<<2) + +typedef struct fireball { + int objnum; // If -1 this object is unused + int fireball_info_index; // Index into Fireball_info array + int current_bitmap; + int orient; // For fireballs, which orientation. For warps, 0 is warpin, 1 is warpout + int flags; // see #define FBF_* + char lod; // current LOD + float time_elapsed; // in seconds + float total_time; // total lifetime of animation in seconds +} fireball; + +#define MAX_FIREBALLS 200 + +fireball Fireballs[MAX_FIREBALLS]; + +fireball_info Fireball_info[MAX_FIREBALL_TYPES]; + +int Num_fireballs = 0; + +int fireballs_inited = 0; + +int Warp_glow_bitmap = -1; + +#define FB_INDEX(fb) (fb-Fireballs) + +// play warp in sound for warp effect +void fireball_play_warphole_open_sound(int ship_class, fireball *fb) +{ + int sound_index; + float range_multiplier = 1.0f; + object *fireball_objp; + + Assert((fb != NULL) && (fb->objnum >= 0)); + if((fb == NULL) || (fb->objnum < 0)){ + return; + } + fireball_objp = &Objects[fb->objnum]; + + sound_index = SND_WARP_IN; + + if((ship_class >= 0) && (ship_class < Num_ship_types)){ + if ( Ship_info[ship_class].flags & SIF_HUGE_SHIP ) { + sound_index = SND_CAPITAL_WARP_IN; + fb->flags |= FBF_WARP_CAPTIAL_SIZE; + } else if ( Ship_info[ship_class].flags & SIF_BIG_SHIP ) { + range_multiplier = 6.0f; + fb->flags |= FBF_WARP_CRUISER_SIZE; + } + } + + snd_play_3d(&Snds[sound_index], &fireball_objp->pos, &Eye_position, fireball_objp->radius, NULL, 0, 1.0f, SND_PRIORITY_DOUBLE_INSTANCE, NULL, range_multiplier); // play warp sound effect +} + +// play warp out sound for warp effect +void fireball_play_warphole_close_sound(fireball *fb) +{ + int sound_index; + + object *fireball_objp; + + fireball_objp = &Objects[fb->objnum]; + + sound_index = SND_WARP_OUT; + + if ( fb->flags & FBF_WARP_CAPTIAL_SIZE ) { + sound_index = SND_CAPITAL_WARP_OUT; + } else { + // AL 27-3-98: Decided that warphole closing is only required for captial ship sized warp effects. + return; + } + + snd_play_3d(&Snds[sound_index], &fireball_objp->pos, &Eye_position, fireball_objp->radius); // play warp sound effect +} + +void fireball_parse_tbl() +{ + int rval, idx; + char base_filename[256] = ""; + + // open localization + lcl_ext_open(); + + if ((rval = setjmp(parse_abort)) != 0) { + Error(LOCATION, "Unable to parse fireball.tbl! Code = %i.\n", rval); + } + else { + read_file_text(NOX("fireball.tbl")); + reset_parse(); + } + + int ntypes = 0; + required_string("#Start"); + while (required_string_either("#End","$Name:")) { + Assert( ntypes < MAX_FIREBALL_TYPES); + + // base filename + required_string("$Name:"); + stuff_string(base_filename, F_NAME, NULL); + + // # of lod levels - make sure old fireball.tbl is compatible + Fireball_info[ntypes].lod_count = 1; + if(optional_string("$LOD:")){ + stuff_int(&Fireball_info[ntypes].lod_count); + } + + // stuff default filename + strcpy(Fireball_info[ntypes].lod[0].filename, base_filename); + + // stuff LOD level filenames + for(idx=1; idx= MAX_FIREBALL_LOD){ + break; + } + + sprintf(Fireball_info[ntypes].lod[idx].filename, "%s_%d", base_filename, idx); + } + + ntypes++; + } + required_string("#End"); + + // close localization + lcl_ext_close(); +} + + +void fireball_load_data() +{ + int i, idx; + fireball_info *fd; + + for ( i = 0; i < MAX_FIREBALL_TYPES; i++ ) { + fd = &Fireball_info[i]; + + for(idx=0; idxlod_count; idx++){ + fd->lod[idx].bitmap_id = bm_load_animation( fd->lod[idx].filename, &fd->lod[idx].num_frames, &fd->lod[idx].fps, 1 ); + if ( fd->lod[idx].bitmap_id < 0 ) { + Error(LOCATION, "Could not load %s anim file\n", fd->lod[idx].filename); + } + } + } + + if ( Warp_glow_bitmap == -1 ) { + Warp_glow_bitmap = bm_load( NOX("warpglow01") ); + } + +} + +void fireball_preload() +{ + // Do nothing. Called before level init, this used to page in warp effect. + // Not needed with new BmpMan system. +} + +// This will get called at the start of each level. +void fireball_init() +{ + int i; + + if ( !fireballs_inited ) { + fireballs_inited = 1; + + // Do all the processing that happens only once + fireball_parse_tbl(); + fireball_load_data(); + } + + // Reset everything between levels + Num_fireballs = 0; + for (i=0; iinstance; + fb = &Fireballs[num]; + fd = &Fireball_info[fb->fireball_info_index]; + + if ( Fireballs[num].current_bitmap < 0 ) + return; + +// gr_set_color( 0, 100, 0 ); +// g3_draw_sphere_ez( &obj->pos, obj->radius ); +// return; + + // turn off fogging + if(The_mission.flags & MISSION_FLAG_FULLNEB){ + gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0); + } + + g3_rotate_vertex(&p, &obj->pos ); + + switch( fb->fireball_info_index ) { + + case FIREBALL_EXPLOSION_MEDIUM: + gr_set_bitmap(Fireballs[num].current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f ); + g3_draw_bitmap(&p, fb->orient, obj->radius, TMAP_FLAG_TEXTURED ); + break; + + case FIREBALL_EXPLOSION_LARGE1: + case FIREBALL_EXPLOSION_LARGE2: + // case FIREBALL_EXPLOSION_LARGE3: + /* + if (!tcache_hit(Fireballs[num].current_bitmap)) { + // if we're over 200k exp vram upload this frame, change to lower lod + if (Glide_expl_textures_in_frame > 1024 * 200) { + // change fireball instance to lower LOD or don't draw + + // get current LOD and number of LODs + int cur_lod = Fireballs[num].lod; + int num_lods = Fireball_info[Fireballs[num].fireball_info_index].lod_count; + + if (num_lods > cur_lod+1) { + // bump lod + int frame = Fireballs[num].current_bitmap - Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod].bitmap_id; + Fireballs[num].lod++; + Fireballs[num].current_bitmap = Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod+1].bitmap_id + frame; + mprintf(("bumping down lod for fireball %s, frame %d\n", Fireball_info[Fireballs[num].fireball_info_index].lod[cur_lod].filename, frame)); + } + } + }*/ + + case FIREBALL_ASTEROID: + // Make the big explosions rotate with the viewer. + gr_set_bitmap(Fireballs[num].current_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.3f ); + g3_draw_rotated_bitmap(&p, (i2fl(fb->orient)*PI)/180.0f, obj->radius, TMAP_FLAG_TEXTURED ); + break; + + case FIREBALL_WARP_EFFECT: + case FIREBALL_KNOSSOS_EFFECT: { + + float percent_life = fb->time_elapsed / fb->total_time; + + float rad; + + // Code to make effect grow/shrink. + float t = fb->time_elapsed; + + if ( t < WARPHOLE_GROW_TIME ) { + rad = (float)pow(t/WARPHOLE_GROW_TIME,0.4f)*obj->radius; + //rad = t*obj->radius/WARPHOLE_GROW_TIME; + //mprintf(( "T=%.2f, Rad = %.2f\n", t, rad )); + } else if ( t < fb->total_time - WARPHOLE_GROW_TIME ) { + rad = obj->radius; + } else { + rad = (float)pow((fb->total_time - t)/WARPHOLE_GROW_TIME,0.4f)*obj->radius; + //rad = (fb->total_time - t )*obj->radius/WARPHOLE_GROW_TIME; + } + //rad = obj->radius; + + + warpin_render(&obj->orient, &obj->pos, Fireballs[num].current_bitmap, rad, percent_life, obj->radius ); + } + break; + + + default: + Int3(); + } +} + +// ----------------------------------------------------------------- +// fireball_delete() +// +// Delete a fireball. Called by object_delete() code... do not call +// directly. +// +void fireball_delete( object * obj ) +{ + int num; + fireball *fb; + + num = obj->instance; + fb = &Fireballs[num]; + + Assert( fb->objnum == OBJ_INDEX(obj)); + + Fireballs[num].objnum = -1; + Num_fireballs--; + Assert( Num_fireballs >= 0 ); +} + +// ----------------------------------------------------------------- +// fireball_delete_all() +// +// Delete all active fireballs, by calling obj_delete directly. +// +void fireball_delete_all() +{ + fireball *fb; + int i; + + for ( i = 0; i < MAX_FIREBALLS; i++ ) { + fb = &Fireballs[i]; + if ( fb->objnum != -1 ) { + obj_delete(fb->objnum); + } + } +} + +void fireball_set_framenum(int num) +{ + int framenum; + fireball *fb; + fireball_info *fd; + fireball_lod *fl; + + fb = &Fireballs[num]; + fd = &Fireball_info[Fireballs[num].fireball_info_index]; + + // valid lod? + fl = NULL; + if((fb->lod >= 0) && (fb->lod < fd->lod_count)){ + fl = &Fireball_info[Fireballs[num].fireball_info_index].lod[fb->lod]; + } + if(fl == NULL){ + // argh + return; + } + + if ( fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT ) { + float total_time = i2fl(fl->num_frames) / fl->fps; // in seconds + + framenum = fl2i(fb->time_elapsed * fl->num_frames / total_time + 0.5); + + if ( framenum < 0 ) framenum = 0; + + framenum = framenum % fl->num_frames; + + if ( fb->orient ) { + // warp out effect plays backwards + framenum = fl->num_frames-framenum-1; + fb->current_bitmap = fl->bitmap_id + framenum; + } else { + fb->current_bitmap = fl->bitmap_id + framenum; + } + } else { + + framenum = fl2i(fb->time_elapsed / fb->total_time * fl->num_frames + 0.5); + + // ensure we don't go past the number of frames of animation + if ( framenum > (fl->num_frames-1) ) { + framenum = (fl->num_frames-1); + Objects[fb->objnum].flags |= OF_SHOULD_BE_DEAD; + } + + if ( framenum < 0 ) framenum = 0; + fb->current_bitmap = fl->bitmap_id + framenum; + } +} + +int fireball_is_perishable(object * obj) +{ + // return 1; + int num, objnum; + fireball *fb; + + num = obj->instance; + objnum = OBJ_INDEX(obj); + Assert( Fireballs[num].objnum == objnum ); + + fb = &Fireballs[num]; + + if ( fb->fireball_info_index == FIREBALL_EXPLOSION_MEDIUM ) + return 1; + + if ( !((fb->fireball_info_index == FIREBALL_WARP_EFFECT) || (fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT)) ) { + if ( !(obj->flags & OF_WAS_RENDERED)) { + return 1; + } + } + + return 0; +} + + +// ----------------------------------------------------------------- +// fireball_free_one() +// +// There are too many fireballs, so delete the oldest small one +// to free up a slot. Returns the fireball slot freed. +// +int fireball_free_one() +{ + fireball *fb; + int i; + + int oldest_objnum = -1, oldest_slotnum = -1; + float lifeleft, oldest_lifeleft = 0.0f; + + for ( i = 0; i < MAX_FIREBALLS; i++ ) { + fb = &Fireballs[i]; + + // only remove the ones that aren't warp effects + if ( (fb->objnum>-1) && fireball_is_perishable(&Objects[fb->objnum]) ) { + + lifeleft = fb->total_time - fb->time_elapsed; + if ( (oldest_objnum < 0) || (lifeleft < oldest_lifeleft) ) { + oldest_slotnum = i; + oldest_lifeleft = lifeleft; + oldest_objnum = fb->objnum; + } + break; + } + } + + if ( oldest_objnum > -1 ) { + obj_delete(oldest_objnum); + } + return oldest_slotnum; +} + +// broke fireball_move into fireball_process_pre and fireball_process_post as was done +// with all *_move functions on 8/13 by Mike K. and Mark A. +void fireball_process_pre( object *objp, float frame_time) +{ +} + +int fireball_is_warp(object * obj) +{ + int num, objnum; + fireball *fb; + + num = obj->instance; + objnum = OBJ_INDEX(obj); + Assert( Fireballs[num].objnum == objnum ); + + fb = &Fireballs[num]; + + if ( fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT) + return 1; + + return 0; +} + +// mabye play sound effect for warp hole closing +void fireball_maybe_play_warp_close_sound(fireball *fb) +{ + float life_left; + + // If not a warphole fireball, do a quick out + if ( !(fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT)) { + return; + } + + // If the warhole close sound has been played, don't play it again! + if ( fb->flags & FBF_WARP_CLOSE_SOUND_PLAYED ) { + return; + } + + life_left = fb->total_time - fb->time_elapsed; + + if ( life_left < WARPHOLE_GROW_TIME ) { + fireball_play_warphole_close_sound(fb); + fb->flags |= FBF_WARP_CLOSE_SOUND_PLAYED; + } +} + +MONITOR( NumFireballs ); + +void fireball_process_post(object * obj, float frame_time) +{ + int num, objnum; + fireball *fb; + + MONITOR_INC( NumFireballs, 1 ); + + num = obj->instance; + objnum = OBJ_INDEX(obj); + Assert( Fireballs[num].objnum == objnum ); + + fb = &Fireballs[num]; + + fb->time_elapsed += frame_time; + if ( fb->time_elapsed > fb->total_time ) { + obj->flags |= OF_SHOULD_BE_DEAD; + } + + fireball_maybe_play_warp_close_sound(fb); + + fireball_set_framenum(num); +} + +// Returns life left of a fireball in seconds +float fireball_lifeleft( object *obj ) +{ + int num, objnum; + fireball *fb; + + num = obj->instance; + objnum = OBJ_INDEX(obj); + Assert( Fireballs[num].objnum == objnum ); + + fb = &Fireballs[num]; + + return fb->total_time - fb->time_elapsed; +} + +// Returns life left of a fireball in percent +float fireball_lifeleft_percent( object *obj ) +{ + int num, objnum; + fireball *fb; + + num = obj->instance; + objnum = OBJ_INDEX(obj); + Assert( Fireballs[num].objnum == objnum ); + + fb = &Fireballs[num]; + + return (fb->total_time - fb->time_elapsed) / fb->total_time; +} + +// determine LOD to use +int fireball_get_lod(vector *pos, fireball_info *fd, float size) +{ + vertex v; + int x, y, w, h, bm_size; + int must_stop = 0; + int ret_lod = 1; + int behind = 0; + + // bogus + if(fd == NULL){ + return 1; + } + + // start the frame + extern float Viewer_zoom; + extern int G3_count; + + if(!G3_count){ + g3_start_frame(1); + must_stop = 1; + } + g3_set_view_matrix(&Eye_position, &Eye_matrix, Viewer_zoom); + + // get extents of the rotated bitmap + g3_rotate_vertex(&v, pos); + + // if vertex is behind, find size if in front, then drop down 1 LOD + if (v.codes & CC_BEHIND) { + float dist = vm_vec_dist_quick(&Eye_position, pos); + vector temp; + + behind = 1; + vm_vec_scale_add(&temp, &Eye_position, &Eye_matrix.fvec, dist); + g3_rotate_vertex(&v, &temp); + + // if still behind, bail and go with default + if (v.codes & CC_BEHIND) { + behind = 0; + } + } + + if(!g3_get_bitmap_dims(fd->lod[0].bitmap_id, &v, size, &x, &y, &w, &h, &bm_size)) { + if (Detail.hardware_textures == 4) { + // straight LOD + if(w <= bm_size/8){ + ret_lod = 3; + } else if(w <= bm_size/2){ + ret_lod = 2; + } else if(w <= (1.56*bm_size)){ + ret_lod = 1; + } else { + ret_lod = 0; + } + } else { + // less aggressive LOD for lower detail settings + if(w <= bm_size/8){ + ret_lod = 3; + } else if(w <= bm_size/3){ + ret_lod = 2; + } else if(w <= (1.2*bm_size)){ + ret_lod = 1; + } else { + ret_lod = 0; + } + } + } + + // if it's behind, bump up LOD by 1 + if (behind) { + ret_lod++; + } + + // end the frame + if(must_stop){ + g3_end_frame(); + } + + // return the best lod + return min(ret_lod, fd->lod_count - 1); +} + +// Create a fireball, return object index. +int fireball_create( vector * pos, int fireball_type, int parent_obj, float size, int reverse, vector *velocity, float warp_lifetime, int ship_class, matrix *orient_override, int low_res) +{ + int n, objnum, fb_lod; + object *obj; + fireball *fb; + fireball_info *fd; + fireball_lod *fl; + + Assert( fireball_type > -1 ); + Assert( fireball_type < MAX_FIREBALL_TYPES ); + + fd = &Fireball_info[fireball_type]; + + if ( !(Game_detail_flags & DETAIL_FLAG_FIREBALLS) ) { + if ( !((fireball_type == FIREBALL_WARP_EFFECT) || (fireball_type == FIREBALL_KNOSSOS_EFFECT)) ) { + return -1; + } + } + + if ( (Num_fireballs >= MAX_FIREBALLS) || (num_objects >= MAX_OBJECTS) ) { + // who cares if we don't create a spark. + // JAS - Should this code be in? Is it better to remove an old spark + // and start a new one, or just not start the new one? + //if ( fd->type == FIREBALL_TYPE_SMALL ) { + // return -1; + //} + + //mprintf(( "Out of fireball slots, trying to free one up!\n" )); + // out of slots, so free one up. + n = fireball_free_one(); + if ( n < 0 ) { + // If there's still no free slots, then exit + //mprintf(( "ERROR: Couldn't free one up!!\n" )); + return -1; + } else { + //mprintf(( "Freed one up just fine!!\n" )); + } + } else { + for ( n = 0; n < MAX_FIREBALLS; n++ ) { + if ( Fireballs[n].objnum < 0 ) { + break; + } + } + Assert( n != MAX_FIREBALLS ); + } + + fb = &Fireballs[n]; + + // get an lod to use + fb_lod = fireball_get_lod(pos, fd, size); + + // change lod if low res is desired + if (low_res) { + fb_lod++; + fb_lod = min(fb_lod, fd->lod_count - 1); + } + + // if this is a warpout fireball, never go higher than LOD 1 + if(fireball_type == FIREBALL_WARP_EFFECT){ + /* + if(fb_lod > 1){ + fb_lod = 1; + } + */ + fb_lod = 0; + } + fl = &fd->lod[fb_lod]; + + fb->lod = (char)fb_lod; + fb->flags = 0; + matrix orient; + if(orient_override != NULL){ + orient = *orient_override; + } else { + if ( parent_obj < 0 ) { + orient = vmd_identity_matrix; + } else { + orient = Objects[parent_obj].orient; + } + } + + objnum = obj_create(OBJ_FIREBALL, parent_obj, n, &orient, pos, size, OF_RENDERS); + + if (objnum < 0) { + Int3(); // Get John, we ran out of objects for fireballs + return objnum; + } + + obj = &Objects[objnum]; + + fb->fireball_info_index = fireball_type; + fb->time_elapsed = 0.0f; + fb->objnum = objnum; + fb->current_bitmap = -1; + + switch( fb->fireball_info_index ) { + + case FIREBALL_EXPLOSION_MEDIUM: + fb->orient = (myrand()>>8) & 7; // 0 - 7 + break; + + case FIREBALL_EXPLOSION_LARGE1: + case FIREBALL_EXPLOSION_LARGE2: + // case FIREBALL_EXPLOSION_LARGE3: + case FIREBALL_ASTEROID: + fb->orient = (myrand()>>8) % 360; // 0 - 359 + break; + + case FIREBALL_WARP_EFFECT: + case FIREBALL_KNOSSOS_EFFECT: + // Play sound effect for warp hole opening up + fireball_play_warphole_open_sound(ship_class, fb); + + // warp in type + if (reverse) { + fb->orient = 1; + // if warp out, then reverse the orientation + vm_vec_scale( &obj->orient.fvec, -1.0f ); // Reverse the forward vector + vm_vec_scale( &obj->orient.rvec, -1.0f ); // Reverse the right vector + } else { + fb->orient = 0; + } + break; + + default: + Int3(); + break; + } + + if ( fb->fireball_info_index == FIREBALL_WARP_EFFECT || fb->fireball_info_index == FIREBALL_KNOSSOS_EFFECT ) { + Assert( warp_lifetime > 4.0f ); // Warp lifetime must be at least 4 seconds! + fb->total_time = warp_lifetime; // in seconds + } else { + fb->total_time = i2fl(fl->num_frames) / fl->fps; // in seconds + } + + fireball_set_framenum(n); + + if ( velocity ) { + // Make the explosion move at a constant velocity. + obj->flags |= OF_PHYSICS; + obj->phys_info.mass = 1.0f; + obj->phys_info.side_slip_time_const = 0.0f; + obj->phys_info.rotdamp = 0.0f; + obj->phys_info.vel = *velocity; + obj->phys_info.max_vel = *velocity; + obj->phys_info.desired_vel = *velocity; + obj->phys_info.speed = vm_vec_mag(velocity); + vm_vec_zero(&obj->phys_info.max_rotvel); + } + + Num_fireballs++; + return objnum; +} + +// ----------------------------------------------------------------- +// fireball_close() +// +// Called at game shutdown to clean up the fireball system +// +void fireball_close() +{ + if ( !fireballs_inited ) + return; + + fireball_delete_all(); +} + +// ----------------------------------------------------------------- +// fireball_level_close() +// +// Called when a mission ends... frees up any animations that might +// be partially played +// +void fireball_level_close() +{ + if ( !fireballs_inited ) + return; + + fireball_delete_all(); +} + +void fireballs_page_in() +{ + int i, idx; + fireball_info *fd; + + for ( i = 0; i < MAX_FIREBALL_TYPES ; i++ ) { + fd = &Fireball_info[i]; + + for(idx=0; idxlod_count; idx++){ + bm_page_in_texture( fd->lod[idx].bitmap_id, fd->lod[idx].num_frames ); + } + } + + bm_page_in_texture( Warp_glow_bitmap ); + +} \ No newline at end of file diff --git a/src/fireball/warpineffect.cpp b/src/fireball/warpineffect.cpp new file mode 100644 index 0000000..d3f46b3 --- /dev/null +++ b/src/fireball/warpineffect.cpp @@ -0,0 +1,258 @@ +/* + * $Logfile: /Freespace2/code/Fireball/WarpInEffect.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for rendering the warp in effects for ships + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 3 7/22/99 1:22p Dave + * Enable proper zbuffering for warpin glow effect. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 23 4/08/98 8:20p John + * Made "Apex" of warp effect not move. + * + * 22 3/30/98 4:02p John + * Made machines with < 32 MB of RAM use every other frame of certain + * bitmaps. Put in code to keep track of how much RAM we've malloc'd. + * + * 21 3/29/98 12:39p John + * Made warp in glow page in + * + * 20 3/26/98 5:21p John + * Added new code to preload all bitmaps at the start of a level. + * Commented it out, though. + * + * 19 3/18/98 12:36p John + * Made hardware have nicer looking warp effect + * + * 18 3/16/98 4:51p John + * Added low-level code to clip all polygons against an arbritary plane. + * Took out all old model_interp_zclip and used this new method instead. + * + * 17 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 16 3/04/98 7:07p John + * Added debug code to try to normalize a zerolength vector. + * + * 15 2/26/98 3:28p John + * fixed optimize compiler warning + * + * 14 2/24/98 6:36p John + * Made warp effect draw as a 4 poly cone. + * + * 13 2/22/98 12:19p John + * Externalized some strings + * + * 12 1/15/98 9:07p John + * Added noise to warp effect glow. + * + * 11 1/15/98 4:58p John + * Made warp effect use a looping ani. Made the scaling up & down be in + * software. + * + * 10 12/30/97 6:44p John + * Made g3_Draw_bitmap functions account for aspect of bitmap. + * + * 9 12/08/97 11:15a John + * added parameter to warpout for life. + * + * 8 12/05/97 3:46p John + * made ship thruster glow scale instead of being an animation. + * + * 7 12/02/97 3:59p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 6 10/24/97 12:18p John + * sped up warp effect by decreasing number of polys with distance. + * + * 5 9/15/97 5:45p John + * took out chunk stuff. + * made pofview display thrusters as blue polies. + * + * 4 9/12/97 4:02p John + * put in ship warp out effect. + * put in dynamic lighting for warp in/out + * + * 3 9/09/97 4:49p John + * Almost done ship warp in code + * + * 2 9/08/97 8:39a John + * added in code structure for grid + * + * 1 9/05/97 10:07a John + * + * $NoKeywords: $ + */ + + +#include "vecmat.h" +#include "tmapper.h" +#include "2d.h" +#include "3d.h" +#include "bmpman.h" +#include "model.h" +#include "key.h" +#include "physics.h" +#include "floating.h" +#include "model.h" +#include "lighting.h" +#include "object.h" +#include "ship.h" +#include "systemvars.h" +#include "animplay.h" +#include "fireballs.h" +#include "linklist.h" +#include "timer.h" + +DCF(norm,"normalize a zero length vector") +{ + if ( Dc_command ) { + vector tmp = vmd_zero_vector; + vm_vec_normalize(&tmp); + } +} + +void draw_face( vertex *v1, vertex *v2, vertex *v3 ) +{ + vector norm; + vertex *vertlist[3]; + + vm_vec_perp(&norm,(vector *)&v1->x,(vector *)&v2->x,(vector *)&v3->x); + if ( vm_vec_dot(&norm,(vector *)&v1->x ) >= 0.0 ) { + vertlist[0] = v3; + vertlist[1] = v2; + vertlist[2] = v1; + } else { + vertlist[0] = v1; + vertlist[1] = v2; + vertlist[2] = v3; + } + + g3_draw_poly( 3, vertlist, TMAP_FLAG_TEXTURED ); + +} + +void warpin_render(matrix *orient, vector *pos, int texture_bitmap_num, float radius, float life_percent, float max_radius ) +{ + int i; + + int saved_gr_zbuffering = gr_zbuffer_get(); + +// gr_zbuffering = 0; + + gr_set_bitmap( texture_bitmap_num, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f ); + + float Grid_depth = radius/2.5f; + + vector center; + + vm_vec_scale_add( ¢er, pos, &orient->fvec, -(max_radius/2.5f)/3.0f ); + + vector vecs[5]; + vertex verts[5]; + + vm_vec_scale_add( &vecs[0], ¢er, &orient->uvec, radius ); + vm_vec_scale_add2( &vecs[0], &orient->rvec, -radius ); + vm_vec_scale_add2( &vecs[0], &orient->fvec, Grid_depth ); + + vm_vec_scale_add( &vecs[1], ¢er, &orient->uvec, radius ); + vm_vec_scale_add2( &vecs[1], &orient->rvec, radius ); + vm_vec_scale_add2( &vecs[1], &orient->fvec, Grid_depth ); + + vm_vec_scale_add( &vecs[2], ¢er, &orient->uvec, -radius ); + vm_vec_scale_add2( &vecs[2], &orient->rvec, radius ); + vm_vec_scale_add2( &vecs[2], &orient->fvec, Grid_depth ); + + vm_vec_scale_add( &vecs[3], ¢er, &orient->uvec, -radius ); + vm_vec_scale_add2( &vecs[3], &orient->rvec, -radius ); + vm_vec_scale_add2( &vecs[3], &orient->fvec, Grid_depth ); + +// vm_vec_scale_add( &vecs[4], ¢er, &orient->fvec, -Grid_depth ); + vecs[4] = center; + + verts[0].u = 0.01f; verts[0].v = 0.01f; + verts[1].u = 0.99f; verts[1].v = 0.01f; + verts[2].u = 0.99f; verts[2].v = 0.99f; + verts[3].u = 0.01f; verts[3].v = 0.99f; + verts[4].u = 0.5f; verts[4].v = 0.5f; + + for (i=0; i<5; i++ ) { + g3_rotate_vertex( &verts[i], &vecs[i] ); + } + + draw_face( &verts[0], &verts[4], &verts[1] ); + draw_face( &verts[1], &verts[4], &verts[2] ); + draw_face( &verts[4], &verts[3], &verts[2] ); + draw_face( &verts[0], &verts[3], &verts[4] ); + + if ( Warp_glow_bitmap != -1 ) { + gr_set_bitmap( Warp_glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.0f ); + + float r = radius; + + int render_it; + + #define OUT_PERCENT1 0.80f + #define OUT_PERCENT2 0.90f + + #define IN_PERCENT1 0.10f + #define IN_PERCENT2 0.20f + + if ( life_percent < IN_PERCENT1 ) { + // do nothing + render_it = 0; + } else if ( life_percent < IN_PERCENT2 ) { + r *= ( life_percent-IN_PERCENT1 ) / (IN_PERCENT2-IN_PERCENT1); + render_it = 1; + } else if ( life_percent < OUT_PERCENT1 ) { + // do nothing + render_it = 1; + } else if ( life_percent < OUT_PERCENT2 ) { + r *= (OUT_PERCENT2 - life_percent) / (OUT_PERCENT2-OUT_PERCENT1); + render_it = 1; + } else { + // do nothing + render_it = 0; + } + + if (render_it) { + int saved_gr_zbuffering = gr_zbuffer_get(); + gr_zbuffer_set(GR_ZBUFF_READ); + + // Add in noise + //float Noise[NOISE_NUM_FRAMES] = { + int noise_frame = fl2i(Missiontime/15.0f) % NOISE_NUM_FRAMES; + + r *= (0.40f + Noise[noise_frame]*0.30f); + + g3_draw_bitmap( &verts[4], 0,r, TMAP_FLAG_TEXTURED ); + gr_zbuffer_set(saved_gr_zbuffering); + } + } + + gr_zbuffer_set( saved_gr_zbuffering ); +} + + + + + + + diff --git a/src/fonttool/fontcreate.cpp b/src/fonttool/fontcreate.cpp new file mode 100644 index 0000000..4b4adf8 --- /dev/null +++ b/src/fonttool/fontcreate.cpp @@ -0,0 +1,592 @@ +/* + * $Logfile: /Freespace2/code/fonttool/FontCreate.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Tool for creating new fonts + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 4 1/06/99 2:25p Dave + * Stubs and release build fixes. + * + * 3 12/02/98 9:58a Dave + * Got fonttool working under glide/direct3d. + * + * 2 10/24/98 5:15p Dave + * + * 1 10/24/98 4:58p Dave + * + * 4 10/31/97 10:30a Adam + * fixed a bug passing wrong pointer to pcx_read trashing memory. + * + * 3 10/30/97 4:56p John + * Fixed up font stuff to build. Fixed bug where it didn't show the last + * 3 characters in kerning table. + * + * 2 6/05/97 4:53p John + * First rev of new antialiased font stuff. + * + * 1 6/02/97 4:04p John + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include +#include + +#include "pstypes.h" +#include "osapi.h" +#include "cfile.h" +#include "2d.h" +#include "key.h" +#include "mouse.h" +#include "palman.h" +#include "timer.h" +#include "pcxutils.h" +#include "font.h" +#include "bmpman.h" + +#include "fonttool.h" + +static bitmap bmp; +static ubyte *data; +static int offset; +static ubyte pal[768]; + +static ubyte bkg; +static ubyte border; + +int num_bad_pixels=0; + +static void myexit(int value) +{ +// getch(); + exit(value); +} + +ubyte PIX(int x,int y) +{ + if ( x<0 || x >= bmp.w ) return bkg; + if ( y<0 || y >= bmp.h ) return bkg; + + return data[x+y*offset]; +} + +// Attempts to find a box at pixel (x,y) if it can't, then it +// returns 0, else returns 1 and w&h are filled in. +int find_box( int x, int y, int *w, int *h ) +{ + int w1, w2, h1, h2; + int y1,x1,tmp; + + if ( PIX(x,y) != border ) return 0; + + x1 = x; + y1 = y; + +// printf( "--------- Finding box at %d, %d ----------\n", x1, y1 ); + +//======================= FIND LEFT EDGE ====================== + y++; + while ( PIX(x,y)==border ) { + if ( PIX(x+1,y)==border) { + goto LeftSideFound; + } + y++; + if ( y > bmp.h ) { + printf( "Box at (%d,%d) goes off bottom of screen.\n", x1, y1 ); + myexit(1); + } + } +// printf( "Box at (%d,%d) doesn't have bottom border.\n", x1, y1 ); +// myexit(1); + return 0; + +LeftSideFound: + h1 = y - y1 - 1; +// printf( "Has a left height of %d\n", h1 ); + +//======================= FIND BOTTOM EDGE ====================== + x++; + while ( PIX(x,y)==border ) { + if ( PIX(x,y-1)==border) { + goto BottomSideFound; + } + x++; + if ( x > bmp.w ) { + printf( "Box at (%d,%d) goes off left of screen.\n", x1, y1 ); + myexit(1); + } + } + printf( "Box at (%d,%d) doesn't have right border connected to bottom.\n", x1, y1 ); + myexit(1); + +BottomSideFound: + w1 = x - x1 - 1; +// printf( "Has a bottom width of %d\n", w1 ); + +//======================= FIND RIGHT EDGE ====================== + tmp = y; + y--; + while ( PIX(x,y)==border ) { + if ( PIX(x-1,y)==border) { + goto RightSideFound; + } + y--; + if ( y < 0 ) { + printf( "Box at (%d,%d) goes off top of screen.\n", x1, y1 ); + myexit(1); + } + } + printf( "Box at (%d,%d) doesn't have top border connected to right border.\n", x1, y1 ); + myexit(1); + +RightSideFound: + h2 = tmp - y - 1; +// printf( "Has a right height of %d\n", h2 ); + + +//======================= FIND TOP EDGE ====================== + tmp = x; + x--; + while ( PIX(x,y)==border ) { + if ( PIX(x,y+1)==border) { + goto TopSideFound; + } + x--; + if ( x < 0 ) { + printf( "Box at (%d,%d) goes off left of screen.\n", x1, y1 ); + myexit(1); + } + } + printf( "Box at (%d,%d) doesn't have left border connected to top border.\n", x1, y1 ); + myexit(1); + +TopSideFound: + w2 = tmp - x - 1; +// printf( "Has a top width of %d\n", w2 ); + +//==================== + if ( h1 != h2 ) { + printf( "Box at (%d,%d) has unequal top/bottom .\n", x1, y1 ); + myexit(1); + } + if ( w1 != w2 ) { + printf( "Box at (%d,%d) has unequal left/right.\n", x1, y1 ); + myexit(1); + } + + if ( w1 < 1 ) { + printf( "Box at (%d,%d) is less than 1 pixel wide.\n", x1, y1 ); + myexit(1); + } + + if ( h1 < 1 ) { + printf( "Box at (%d,%d) is less than 1 pixel tall.\n", x1, y1 ); + myexit(1); + } + + *w = w1; + *h = h1; + + return 1; +} + + + +void found_box( font *fnt, int x, int y, int w, int h ); + +void find_all_boxes(font *fnt) +{ + int x = 0; + int y = 0; + + data = (ubyte *)bmp.data; + offset = bmp.w; + + bkg = data[0]; + printf( "Background color = %d\n", bkg ); + + for ( y=0; y= bmp.w ) { + x = 0; + if ( row_height ) { + //printf( "Moving down %d pixels\n", row_height+2); + y += row_height+2; + row_height = 0; + } else { + y++; + } + if ( y >= bmp.h ) { + break; + } + } + } + } +} + + +void fonttool_create_new( font *fnt ) +{ + strncpy( (char *)&fnt->id, "VFNT", 4 ); + fnt->version = FONT_VERSION; + fnt->num_chars = 0; + fnt->first_ascii = ' '; + fnt->w = 0; + fnt->h = 0; + fnt->num_kern_pairs = 0; + fnt->pixel_data = NULL; + fnt->pixel_data_size = 0; + fnt->kern_data = NULL; + fnt->kern_data_size = 0; + fnt->char_data = NULL; + fnt->char_data_size = 0; +} + + +void fonttool_add_char( font *fnt, int x1, int y1, int real_w, int h, ubyte *data, int rowsize ) +{ + int x, y, n, offset; + int w; + + n = fnt->num_chars; + + w = real_w; + while ( w & 1) w++; + + printf( "Adding character %d (%c) from the %dx%d pixels at (%d,%d)\n", n, n + fnt->first_ascii, real_w, h, x1, y1 ); + + // add new character data + font_char * new_char; + new_char = (font_char *)malloc( fnt->char_data_size + sizeof(font_char) ); + if ( !new_char ) { + printf( "Not enough memory to create a new character\n" ); + myexit(1); + } + if ( fnt->char_data ) { + memcpy( new_char, fnt->char_data, fnt->char_data_size ); + free(fnt->char_data); + fnt->char_data = NULL; + } + fnt->char_data = new_char; + fnt->char_data_size += sizeof(font_char); + new_char = fnt->char_data + fnt->num_chars; + fnt->num_chars++; + + // add new character pixel data + ubyte *new_pixel_data = (ubyte *)malloc( fnt->pixel_data_size+w*h ); + if ( !new_pixel_data ) { + printf( "Not enough memory to create new %dx%d character\n", w, h); + myexit(1); + } + if ( fnt->pixel_data ) { + memcpy( new_pixel_data, fnt->pixel_data, fnt->pixel_data_size ); + free(fnt->pixel_data); + fnt->pixel_data = NULL; + } + offset = fnt->pixel_data_size; + fnt->pixel_data_size += w*h; + fnt->pixel_data = new_pixel_data; + new_pixel_data = fnt->pixel_data + offset; + + new_char->byte_width = w; + new_char->spacing = real_w; + new_char->offset = offset; + new_char->kerning_entry = -1; + new_char->user_data = 0; + + for ( y=0; y= real_w) + c = 0; + else + c = data[x+y*rowsize]; + if ( c > 15 ) { + num_bad_pixels++; + c = 15; + } + *new_pixel_data++ = c; + } + } + + if ( fnt->h < 1 ) + fnt->h = h; + + int i, wtotal = 0; + + for (i=0; inum_chars; i++ ) { + wtotal += fnt->char_data[i].spacing; + } + + fnt->w = wtotal / fnt->num_chars; + if ( fnt->w < 1 ) + fnt->w = 1; +} + + +void fonttool_dump( char *filename, font *fnt ) +{ + FILE *fp; + char tmp_name[128], *p; + + strcpy( tmp_name, filename ); + p = strchr( tmp_name, '.' ); + if ( p ) *p = 0; + strcat( tmp_name, ".vf" ); + + fp = fopen( tmp_name, "wb" ); + if ( fp == NULL ) { + printf( "Couldn't open font file '%s' for writing!\n", tmp_name ); + myexit(1); + } + + fwrite( &fnt->id, 4, 1, fp ); + fwrite( &fnt->version, sizeof(int), 1, fp ); + fwrite( &fnt->num_chars, sizeof(int), 1, fp ); + fwrite( &fnt->first_ascii, sizeof(int), 1, fp ); + fwrite( &fnt->w, sizeof(int), 1, fp ); + fwrite( &fnt->h, sizeof(int), 1, fp ); + fwrite( &fnt->num_kern_pairs, sizeof(int), 1, fp ); + fwrite( &fnt->kern_data_size, sizeof(int), 1, fp ); + fwrite( &fnt->char_data_size, sizeof(int), 1, fp ); + fwrite( &fnt->pixel_data_size, sizeof(int), 1, fp ); + + if ( fnt->kern_data_size ) { + fwrite( fnt->kern_data, fnt->kern_data_size, 1, fp ); + } + if ( fnt->char_data_size ) { + fwrite( fnt->char_data, fnt->char_data_size, 1, fp ); + } + if ( fnt->pixel_data_size ) { + fwrite( fnt->pixel_data, fnt->pixel_data_size, 1, fp ); + } + + fclose(fp); +} + +void fonttool_read( char *filename, font *fnt ) +{ + FILE *fp; + char tmp_name[128], *p; + + strcpy( tmp_name, filename ); + p = strchr( tmp_name, '.' ); + if ( p ) *p = 0; + strcat( tmp_name, ".vf" ); + + fp = fopen( tmp_name, "rb" ); + if ( fp == NULL ) { + printf( "Couldn't open font file '%s' for reading!\n", tmp_name ); + myexit(1); + } + + fread( &fnt->id, 4, 1, fp ); + fread( &fnt->version, sizeof(int), 1, fp ); + fread( &fnt->num_chars, sizeof(int), 1, fp ); + fread( &fnt->first_ascii, sizeof(int), 1, fp ); + fread( &fnt->w, sizeof(int), 1, fp ); + fread( &fnt->h, sizeof(int), 1, fp ); + fread( &fnt->num_kern_pairs, sizeof(int), 1, fp ); + fread( &fnt->kern_data_size, sizeof(int), 1, fp ); + fread( &fnt->char_data_size, sizeof(int), 1, fp ); + fread( &fnt->pixel_data_size, sizeof(int), 1, fp ); + + if ( fnt->kern_data_size ) { + fnt->kern_data = (font_kernpair *)malloc( fnt->kern_data_size ); + if (!fnt->kern_data) { + printf( "Out of memory reading %d bytes of font data from %s\n", tmp_name ); + myexit(1); + } + fread( fnt->kern_data, fnt->kern_data_size, 1, fp ); + } else { + fnt->kern_data = NULL; + } + if ( fnt->char_data_size ) { + fnt->char_data = (font_char *)malloc( fnt->char_data_size ); + if (!fnt->char_data) { + printf( "Out of memory reading %d bytes of font data from %s\n", tmp_name ); + myexit(1); + } + fread( fnt->char_data, fnt->char_data_size, 1, fp ); + } else { + fnt->char_data = NULL; + } + if ( fnt->pixel_data_size ) { + fnt->pixel_data = (ubyte *)malloc( fnt->pixel_data_size ); + if (!fnt->pixel_data) { + printf( "Out of memory reading %d bytes of font data from %s\n", tmp_name ); + myexit(1); + } + fread( fnt->pixel_data, fnt->pixel_data_size, 1, fp ); + } else { + fnt->pixel_data = NULL; + } + + fclose(fp); + + // Create a bitmap for hardware cards. + // JAS: Try to squeeze this into the smallest square power of two texture. + // This should probably be done at font generation time, not here. + int w, h; + if ( fnt->pixel_data_size < 64*64 ) { + w = h = 64; + } else if ( fnt->pixel_data_size < 128*128 ) { + w = h = 128; + } else { + w = h = 256; + } + + fnt->bm_w = w; + fnt->bm_h = h; + fnt->bm_data = (ubyte *)malloc(fnt->bm_w*fnt->bm_h); + fnt->bm_u = (int *)malloc(sizeof(int)*fnt->num_chars); + fnt->bm_v = (int *)malloc(sizeof(int)*fnt->num_chars); + + int i,x,y; + x = y = 0; + for (i=0; inum_chars; i++ ) { + ubyte * fp; + int x1, y1; + fp = &fnt->pixel_data[fnt->char_data[i].offset]; + if ( x + fnt->char_data[i].byte_width >= fnt->bm_w ) { + x = 0; + y += fnt->h; + if ( y+fnt->h > fnt->bm_h ) { + Error( LOCATION, "Font too big!\n" ); + } + } + fnt->bm_u[i] = x; + fnt->bm_v[i] = y; + + for( y1=0; y1h; y1++ ) { + for (x1=0; x1char_data[i].byte_width; x1++ ) { + uint c = *fp++; + if ( c > 14 ) c = 14; + fnt->bm_data[(x+x1)+(y+y1)*fnt->bm_w] = unsigned char(c); + } + } + x += fnt->char_data[i].byte_width; + } + + fnt->bitmap_id = bm_create( 8, fnt->bm_w, fnt->bm_h, fnt->bm_data, BMP_AABITMAP ); +} + +void fonttool_copy_kern( font *src, font *dst ) +{ + if ( dst->kern_data ) { + free( dst->kern_data ); + dst->kern_data = NULL; + } + if ( (src->kern_data_size < 1) || (!src->kern_data) ) { + dst->num_kern_pairs = 0; + dst->kern_data_size = 0; + dst->kern_data = NULL; + fonttool_resync_kerning(dst); + return; + } + dst->kern_data = (font_kernpair *)malloc( src->kern_data_size ); + if (!dst->kern_data) { + printf( "Out of memory copying %d bytes of font data.\n" ); + myexit(1); + } + memcpy( dst->kern_data, src->kern_data, src->kern_data_size ); + + dst->kern_data_size = src->kern_data_size; + dst->num_kern_pairs = src->num_kern_pairs; + fonttool_resync_kerning(dst); +} + + +void found_box( font *fnt, int x, int y, int w, int h ) +{ + fonttool_add_char( fnt, x, y, w, h, &data[x+y*offset], offset ); +} + + +void fonttool_create_font(char *pcx_filename, char *font_filename) +{ + font fnt1, fnt2; + printf( "Creating font file from '%s' \n", pcx_filename ); + if ( font_filename ) { + } + + int w, h; + int pcx_error = pcx_read_header( pcx_filename, &w, &h, NULL ); + if ( pcx_error != PCX_ERROR_NONE ) { + printf( "Error reading PCX file, '%s'\n", pcx_filename ); + myexit(1); + } + + bmp.w = (short)w; + bmp.h = (short)h; + bmp.data = (uint)malloc( w*h + 768 ); + bmp.palette = (ubyte *)(bmp.data +w*h ); + if ( !bmp.data ) { + printf( "Error mallocing PCX data, '%s'\n", pcx_filename ); + myexit(1); + } + + pcx_error = pcx_read_bitmap_8bpp( pcx_filename, (ubyte *)bmp.data, pal ); + if ( pcx_error != PCX_ERROR_NONE ) { + printf( "Error reading PCX file, '%s'\n", pcx_filename ); + myexit(1); + } + + fonttool_create_new( &fnt1 ); + + find_all_boxes(&fnt1); + + if (font_filename) { + printf( "Using kern data from font '%s'\n", font_filename ); + fonttool_read( font_filename, &fnt2 ); + fonttool_copy_kern( &fnt2, &fnt1 ); + } + + printf( "\nFont is, on average, %dx%d and has %d characters, %d kerning pairs.\n", fnt1.w, fnt1.h, fnt1.num_chars, fnt1.num_kern_pairs ); + if ( num_bad_pixels > 0 ) + printf( "It had %d bad pixel(s) in it.\n(Bad means the pixel index was greater than 15).\n" , num_bad_pixels ); + + fonttool_dump( pcx_filename, &fnt1 ); + + myexit(0); +} + diff --git a/src/fonttool/fontkern.cpp b/src/fonttool/fontkern.cpp new file mode 100644 index 0000000..853ddda --- /dev/null +++ b/src/fonttool/fontkern.cpp @@ -0,0 +1,596 @@ +/* + * $Logfile: /Freespace2/code/Fonttool/FontKern.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Tool for interactively kerning fonts + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 6 5/19/99 4:07p Dave + * Moved versioning code into a nice isolated common place. Fixed up + * updating code on the pxo screen. Fixed several stub problems. + * + * 5 12/18/98 1:14a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 4 12/02/98 9:58a Dave + * Got fonttool working under glide/direct3d. + * + * 3 11/30/98 1:09p Dave + * + * 2 10/24/98 5:15p Dave + * + * 1 10/24/98 4:58p Dave + * + * 14 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 13 4/13/98 10:11a John + * Made timer functions thread safe. Made timer_init be called in all + * projects. + * + * 12 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 11 3/05/98 11:15p Hoffoss + * Changed non-game key checking to use game_check_key() instead of + * game_poll(). + * + * 10 10/30/97 4:56p John + * Fixed up font stuff to build. Fixed bug where it didn't show the last + * 3 characters in kerning table. + * + * 9 9/03/97 4:32p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 8 6/06/97 6:47p John + * Fixed bug + * + * 7 6/06/97 4:41p John + * Fixed alpha colors to be smoothly integrated into gr_set_color_fast + * code. + * + * 6 6/06/97 11:10a John + * made scrolling kern pair box. + * + * 5 6/06/97 9:21a John + * added some kerning pairs + * + * 4 6/06/97 9:18a John + * Added capital hamburger. + * + * 3 6/05/97 5:00p John + * used fonttool.pcx + * + * 2 6/05/97 4:53p John + * First rev of new antialiased font stuff. + * + * 1 6/02/97 4:04p John + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include +#include +#include + +#include "pstypes.h" +#include "osapi.h" +#include "cfile.h" +#include "2d.h" +#include "key.h" +#include "mouse.h" +#include "palman.h" +#include "timer.h" +#include "bmpman.h" +#include "osregistry.h" + +#include "fonttool.h" + +char *SampleText = "This is some sample text that is here to\n" \ +"Show you how the antialiasing will\n" \ +"look over different color backgrounds\n" \ +"KERN PAIRS: VaWaVeWeVAV-LyT.T,TyTvTcYe\n"; + +static void myexit(int value) +{ +// getch(); + exit(value); +} + +int game_check_key() +{ + return key_inkey(); +} + +int game_poll() +{ + return key_inkey(); +} + +int os_done = 0; + +void os_close() +{ + os_done = 1; +} + +int fonttool_get_kerning( font *fnt, int c1, int c2, int *pairnum ) +{ + int i; + + int l1 = c1 - fnt->first_ascii; + int l2 = c2 - fnt->first_ascii; + + for (i=0; inum_kern_pairs; i++ ) { + if ( (fnt->kern_data[i].c1 == l1) && (fnt->kern_data[i].c2 == l2) ) { + if (pairnum) *pairnum = i; + return fnt->kern_data[i].offset; + } + } + return 0; +} + +void fonttool_resync_kerning( font *fnt ) +{ + int i; + + // update all the font into + for (i=0; inum_chars; i++ ) { + fnt->char_data[i].kerning_entry = -1; + } + + for (i=0; inum_kern_pairs; i++ ) { + int c = fnt->kern_data[i].c1; + if ( fnt->char_data[c].kerning_entry == -1 ) + fnt->char_data[c].kerning_entry = (short)i; + } +} + +void fonttool_remove_kern_pair( font *fnt, int index ) +{ + // not found, add it + int i, n, new_num_pairs; + + new_num_pairs = fnt->num_kern_pairs - 1; + + if ( new_num_pairs < 1 ) { + fonttool_remove_kerning(fnt); + return; + } + + font_kernpair *new_kern_data = (font_kernpair *)malloc( new_num_pairs*sizeof(font_kernpair) ); + if (!new_kern_data) { + printf( "Out of memory!\n" ); + myexit(1); + } + + + n=0; + for (i=0; inum_kern_pairs; i++ ) { + if ( i != index ) { + new_kern_data[n] = fnt->kern_data[i]; + n++; + } + } + + if ( fnt->kern_data ) free( fnt->kern_data ); + fnt->kern_data = new_kern_data; + fnt->kern_data_size = sizeof(font_kernpair)*new_num_pairs; + fnt->num_kern_pairs = new_num_pairs; + + fonttool_resync_kerning(fnt); + + mprintf(( "Font has %d kern pairs\n", fnt->num_kern_pairs )); +} + +void fonttool_set_kerning( font *fnt, int c1, int c2, int dist ) +{ + int i; + + int l1 = c1 - fnt->first_ascii; + int l2 = c2 - fnt->first_ascii; + + for (i=0; inum_kern_pairs; i++ ) { + if ( (fnt->kern_data[i].c1 == l1) && (fnt->kern_data[i].c2 == l2) ) { + fnt->kern_data[i].offset = (signed char)dist; + if ( dist == 0 ) { + fonttool_remove_kern_pair( fnt, i ); + } + return; + } + } + if ( dist == 0 ) return; + + // not found, add it + int new_num_pairs; + + new_num_pairs = fnt->num_kern_pairs+1; + + font_kernpair *new_kern_data = (font_kernpair *)malloc( new_num_pairs*sizeof(font_kernpair) ); + if (!new_kern_data) { + printf( "Out of memory!\n" ); + myexit(1); + } + + + int n=0; + uint newcode = l1*256+l2; + + for (i=0; inum_kern_pairs; i++ ) { + uint code = fnt->kern_data[i].c1*256 + fnt->kern_data[i].c2; + if ( code < newcode ) { + new_kern_data[n] = fnt->kern_data[i]; + n++; + } else { + break; + } + } + + + new_kern_data[n].c1 = (char)l1; + new_kern_data[n].c2 = (char)l2; + new_kern_data[n].offset = (signed char)dist; + n++; + + + for (; inum_kern_pairs; i++ ) { + new_kern_data[n] = fnt->kern_data[i]; + n++; + } + + + if ( fnt->kern_data ) free( fnt->kern_data ); + fnt->kern_data = new_kern_data; + fnt->kern_data_size += sizeof(font_kernpair); + fnt->num_kern_pairs++; + + fonttool_resync_kerning(fnt); + + mprintf(( "Font has %d kern pairs\n", fnt->num_kern_pairs )); + +} + + +void fonttool_remove_kerning( font *fnt ) +{ + int i; + + for (i=0; inum_chars; i++ ) { + fnt->char_data[i].kerning_entry = -1; + } + + fnt->kern_data_size = 0; + if (fnt->kern_data) + free( fnt->kern_data ); + fnt->kern_data = NULL; + fnt->num_kern_pairs = 0; +} + + +void fonttool_edit_kerning(char *fname1) +{ + int i, k,x; + int done; + int bkg; + font KernFont, tmpfont; + int alpha = 16; + int cr=16, cb=16, cg=16; + int c1 = 'b', c2 = 'u'; + char kerntext[128]; + int current_pair = -1; + int first_item = 0; + int current_item = 0; + int num_items_displayed = 1; + int last_good_pair = -1; + char *ptr; + color ac; + + printf( "Editing kerning data for %s\n", fname1 ); + fonttool_read( fname1, &KernFont ); + + timer_init(); + + // setup the fred exe directory so CFILE can init properly + //char *c = GetCommandLine(); + //Assert(c != NULL); + //char *tok = strtok(c, " "); + //Assert(tok != NULL); + cfile_init(__argv[0]); + + os_init( "FontTool", "FontTool - Kerning Table Editor" ); + // init the registry + os_init_registry_stuff(Osreg_company_name, Osreg_app_name,NULL); + ptr = os_config_read_string(NULL, NOX("Videocard"), NULL); + if((ptr == NULL) || !stricmp(ptr, "Aucune accélération 3D") || !stricmp(ptr, "Keine 3D-Beschleunigerkarte") || !stricmp(ptr, "No 3D acceleration")){ + MessageBox((HWND)os_get_window(), "Warning, Freespace 2 requires Glide or Direct3D hardware accleration. You will not be able to run Freespace 2 without it", "Warning", MB_OK); + exit(1); + } + + if (!stricmp(ptr, NOX("3DFX Glide"))) { + // Glide + gr_init(GR_640, GR_GLIDE); + } else if (strstr(ptr, NOX("Direct 3D -"))){ + // Direct 3D + gr_init(GR_640, GR_DIRECT3D); + } else { + Int3(); + } + + gr_set_palette("none",NULL); + bkg = bm_load( "code\\fonttool\\FontTool" ); + if ( bkg < 0 ) { + printf("Error loading FontTool\n" ); + myexit(1); + } + palette_use_bm_palette(bkg); + + key_init(); + mouse_init(); + + gr_init_alphacolor( &ac, cr*16,cg*16,cb*16,alpha*16 ); + + + { + extern font *Current_font; + Current_font = &KernFont; + } + + done = 0; + while (!done) { + k = key_inkey(); + switch(k) { + case KEY_F5: + fonttool_read( fname1, &tmpfont ); + fonttool_copy_kern( &tmpfont, &KernFont ); + break; + + case KEY_F6: + fonttool_remove_kerning( &KernFont ); + break; + + case KEY_F10: + fonttool_dump( fname1, &KernFont ); + done=1; + break; + + case KEY_COMMA: + if ( alpha > 1 ) { + alpha--; + gr_init_alphacolor(&ac,cr*16,cg*16,cb*16,alpha*16); + } + break; + + case KEY_PERIOD: + if ( alpha < 17 ) { + alpha++; + gr_init_alphacolor(&ac,cr*16,cg*16,cb*16,alpha*16); + } + break; + + case KEY_R: + if ( cr == 16 ) cr = 1; else cr = 16; + gr_init_alphacolor(&ac,cr*16,cg*16,cb*16,alpha*16); + break; + + case KEY_G: + if ( cg == 16 ) cg = 1; else cg = 16; + gr_init_alphacolor(&ac,cr*16,cg*16,cb*16,alpha*16); + break; + + case KEY_B: + if ( cb == 16 ) cb = 1; else cb = 16; + gr_init_alphacolor(&ac,cr*16,cg*16,cb*16,alpha*16); + break; + + case KEY_PAD6: + x = fonttool_get_kerning( &KernFont, c1, c2, NULL ); + fonttool_set_kerning( &KernFont, c1, c2, x+1 ); + break; + + case KEY_PAD4: + x = fonttool_get_kerning( &KernFont, c1, c2, NULL ); + fonttool_set_kerning( &KernFont, c1, c2, x-1 ); + break; + + case KEY_PAD5: + fonttool_set_kerning( &KernFont, c1, c2, 0 ); + break; + + case KEY_PAD7: + if ( c1 < KernFont.first_ascii + KernFont.num_chars-1 ) c1++; + break; + + case KEY_PAD1: + if ( c1 > KernFont.first_ascii ) c1--; + break; + + case KEY_PAD9: + if ( c2 < KernFont.first_ascii + KernFont.num_chars-1 ) c2++; + mprintf(( "C2 = %d\n", c2 )); + break; + + case KEY_PAD3: + if ( c2 > KernFont.first_ascii ) c2--; + mprintf(( "C2 = %d\n", c2 )); + break; + + case KEY_PAD2: + if ( current_pair < 0 ) + current_pair = last_good_pair; + else + current_pair++; + if ( current_pair >= KernFont.num_kern_pairs ) { + current_pair = KernFont.num_kern_pairs-1; + } + if ( (current_pair < KernFont.num_kern_pairs) && (current_pair > -1) ) { + c1 = KernFont.kern_data[current_pair].c1 + KernFont.first_ascii; + c2 = KernFont.kern_data[current_pair].c2 + KernFont.first_ascii; + } + break; + + case KEY_PAD8: + if ( current_pair < 0 ) + current_pair = last_good_pair; + else + current_pair--; + if ( current_pair < 0 ) { + current_pair = 0; + } + if ( (current_pair < KernFont.num_kern_pairs) && (current_pair > -1) ) { + c1 = KernFont.kern_data[current_pair].c1 + KernFont.first_ascii; + c2 = KernFont.kern_data[current_pair].c2 + KernFont.first_ascii; + } + break; + + case KEY_ESC: + done=1; + break; + } + + if ( current_pair >= KernFont.num_kern_pairs ) { + current_pair = -1; + } + + int tmpp=-1; + fonttool_get_kerning( &KernFont, c1, c2, &tmpp ); + + if ( tmpp != -1 ) { + current_pair = tmpp; + } else { + if ( current_pair > -1 ) + last_good_pair = current_pair; + current_pair = -1; + } + + gr_reset_clip(); + gr_set_bitmap(bkg); + gr_bitmap(0,0); + gr_set_color_fast(&ac); + + sprintf( kerntext, "%c (%d)", c1, c1 ); + gr_string( 240, 210, kerntext ); + sprintf( kerntext, "%c (%d)", c2, c2 ); + gr_string( 340, 210, kerntext ); + + sprintf( kerntext, "Ham%c%crger", c1, c2 ); + gr_string( 0x8000, 240, kerntext ); + + sprintf( kerntext, "HAM%c%cRGER", c1, c2 ); + gr_string( 0x8000, 270, kerntext ); + + sprintf( kerntext, "Offset: %d pixels", fonttool_get_kerning( &KernFont, c1, c2, NULL ) ); + gr_string( 0x8000, 300, kerntext ); + + { + int tw, th; + gr_get_string_size( &tw, &th, kerntext ); + + gr_string( 0x8000, 360, SampleText ); + gr_string( 20, 360+th+20, SampleText ); + } + + int x = 5, y = 200; + int widest = 0; + + //= ( 330 - 200 ) / KernFont.h; + int num_items = KernFont.num_kern_pairs; + + if ( current_pair > -1 ) + current_item = current_pair; + + if (current_item <0 ) + current_item = 0; + + if (current_item >= num_items ) + current_item = num_items-1; + + if (current_item=(first_item+num_items_displayed)) + first_item = current_item-num_items_displayed+1; + + if (num_items <= num_items_displayed ) + first_item = 0; + + int stop = first_item+num_items_displayed; + if (stop>num_items) stop = num_items; + + int n = 0; + for (i=first_item; i widest ) + widest = tw; + y += KernFont.h; + if ( y > 330 ) { + y = 200; + x += widest + 5; + if ( x > 150 ) { + num_items_displayed=n; + break; + } + widest = 0; + } + } + if (i==stop) + num_items_displayed++; + + if (num_items_displayed < 1 ) + num_items_displayed = 1; + + if (num_items_displayed > num_items ) + num_items_displayed = num_items; + + //mprintf(( "Num items = %d\n", num_items_displayed )); + + + gr_flip(); + + } + + + exit(0); +} + diff --git a/src/fonttool/fontkerncopy.cpp b/src/fonttool/fontkerncopy.cpp new file mode 100644 index 0000000..75c8cca --- /dev/null +++ b/src/fonttool/fontkerncopy.cpp @@ -0,0 +1,57 @@ +/* + * $Logfile: /Freespace2/code/Fonttool/FontKernCopy.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Tool for copying font kerning info + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/24/98 5:15p Dave + * + * 1 10/24/98 4:58p Dave + * + * 2 6/05/97 4:53p John + * First rev of new antialiased font stuff. + * + * 1 6/02/97 4:04p John + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include +#include + +#include "pstypes.h" +#include "osapi.h" +#include "cfile.h" +#include "2d.h" +#include "key.h" +#include "mouse.h" +#include "palman.h" +#include "timer.h" + +#include "fonttool.h" + +font fnt1, fnt2; + +void fonttool_kerning_copy( char *fname1, char *fname2 ) +{ + printf( "Copying kerning data from %s to %s\n", fname1, fname2 ); + + fonttool_read( fname1, &fnt1 ); + fonttool_read( fname2, &fnt2 ); + fonttool_copy_kern( &fnt1, &fnt2 ); + + fonttool_dump( fname2, &fnt2 ); + + exit(0); +} + diff --git a/src/fonttool/fontstubs.cpp b/src/fonttool/fontstubs.cpp new file mode 100644 index 0000000..6062734 --- /dev/null +++ b/src/fonttool/fontstubs.cpp @@ -0,0 +1,99 @@ +/* + * $Logfile: /Freespace2/code/Fonttool/FontStubs.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Tool for creating/kerning fonts + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 7 5/12/99 5:34p Dave + * Fixed build error. + * + * 6 4/07/99 6:21p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 5 1/08/99 2:07p Dave + * Fixed pofview for software mode. + * + * 4 1/06/99 2:25p Dave + * Stubs and release build fixes. + * + * 3 12/02/98 9:58a Dave + * Got fonttool working under glide/direct3d. + * + * 2 10/24/98 11:43p Dave + * + * 1 10/24/98 11:43p Dave + * + * 2 10/24/98 5:15p Dave + * + * + * $NoKeywords: $ + */ + +#include "vecmat.h" + +int game_cd_changed(){return 0;} +void game_enter_state(int, int){} +void game_leave_state(int, int){} +void game_do_state(int){} +void game_process_event(int, int){} +char *Game_CDROM_dir; +void game_stop_looped_sounds(){} +int Fred_running; +int Pofview_running = 0; +int set_cdrom_path(int){return 0;} +int find_freespace_cd(char*){return 0;} +void game_flush(){} +int Game_skill_level; +void game_stop_time(){} +void game_start_time(){} +void game_do_state_common(int, int){} +void game_set_frametime(int){} +void game_increase_skill_level(){} +int Test_begin; +long Game_time_compression; +int Framerate_delay; +char *Game_current_mission_filename; +int Warpout_forced; +float Warpout_time; +int game_start_mission(void){return 0;} +void game_level_close(){} +void game_flash(float, float, float){} +void game_whack_apply(float, float){} +int game_do_cd_check(char*){return 0;} +struct fs_builtin_mission *game_find_builtin_mission(char*){return 0;} +int Game_do_state_should_skip; +int Show_target_weapons; +int Show_target_debug_info; +int Sun_drew; +int Game_subspace_effect; +void game_load_palette(){} +void game_format_time(long, char*){} +float Freespace_gamma; +void get_version_string(char*){} +int game_get_default_skill_level(){return 0;} +int Interface_framerate; +vector Camera_pos; +vector Dead_player_last_vel; +void game_set_view_clip(){} +float Viewer_zoom; +int Game_weapons_tbl_valid; +int Game_ships_tbl_valid; +void game_shudder_apply(int, float){} +int Debug_octant; +int game_hacked_data(){return 0;} +int Nebedit_running = 0; +void game_tst_mark(struct object *, struct ship*){} +int game_do_cd_mission_check(char*){return 0;} +int Player_multi_died_check; +int tst; +int game_single_step; +int last_single_step; \ No newline at end of file diff --git a/src/fonttool/fonttool.cpp b/src/fonttool/fonttool.cpp new file mode 100644 index 0000000..9a3207d --- /dev/null +++ b/src/fonttool/fonttool.cpp @@ -0,0 +1,123 @@ +/* + * $Logfile: /Freespace2/code/Fonttool/FontTool.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Tool for creating/kerning fonts + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/24/98 5:15p Dave + * + * 1 10/24/98 4:58p Dave + * + * 4 10/30/97 4:56p John + * Fixed up font stuff to build. Fixed bug where it didn't show the last + * 3 characters in kerning table. + * + * 3 6/23/97 6:05p Hoffoss + * Added stubbs to fix linking errors. + * + * 2 6/05/97 4:53p John + * First rev of new antialiased font stuff. + * + * 1 6/02/97 4:04p John + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include +#include + +#include "pstypes.h" +#include "osapi.h" +#include "cfile.h" +#include "2d.h" +#include "key.h" +#include "mouse.h" +#include "palman.h" +#include "timer.h" + +#include "fonttool.h" + +char Usage[] = "Usage:\n" \ +"\nFontTool x.pcx [y.vf]\n" \ +"\n If you specify a PCX file, then a font will be\n" \ +" created with the same base name. If you also\n" \ +" specify a font file, then it will use the kerning\n" \ +" data from that font file when it creates the new\n" \ +" font from the PCX file.\n" \ +"\nFontTool x.vf\n" \ +"\n If you specify a font file by itself then it will\n" \ +" allow you to interactively kern that font.\n" \ +"\nFontTool x.vf y.vf\n" \ +"\n If you specify two font files, then the kerning\n" \ +" data from the first font will be copied into the\n" \ +" second font.\n" \ +"\n"; + +#define NONE 0 +#define PCX 1 +#define FONT 2 + +int Font1 = -1; + +void demo_set_playback_filter() {} +float flFrametime = 0.0f; + +void freespace_menu_background() +{ + gr_reset_clip(); + gr_clear(); +} + +int main(int argc, char *argv[] ) +{ + int t1, t2; + + t1 = NONE; + t2 = NONE; + + if ( (argc < 1) || (argc>3) ) { + printf( Usage ); + return 1; + } + + if ( argc > 1 ) { + strlwr( argv[1] ); + + if ( strstr( argv[1], ".pcx" ) ) + t1 = PCX; + else if ( strstr( argv[1], ".vf" ) ) + t1 = FONT; + } + + if ( argc > 2 ) { + strlwr( argv[2] ); + + if ( strstr( argv[2], ".pcx" ) ) + t2 = PCX; + else if ( strstr( argv[2], ".vf" ) ) + t2 = FONT; + } + + if ( (t1==PCX) && (t2==NONE) ) + fonttool_create_font( argv[1], NULL ); + else if ( (t1==PCX) && (t2==FONT) ) + fonttool_create_font( argv[1], argv[2] ); + else if ( (t1==FONT) && (t2==NONE) ) + fonttool_edit_kerning( argv[1] ); + else if ( (t1==FONT) && (t2==FONT) ) + fonttool_kerning_copy( argv[1], argv[2] ); + else + printf( Usage ); + + return 0; +} diff --git a/src/fonttool/fonttool.pcx b/src/fonttool/fonttool.pcx new file mode 100644 index 0000000000000000000000000000000000000000..d28afb1b8a5eba26015ed9ec89ee2d4e11d7bc74 GIT binary patch literal 94669 zcmeFadvKlSdDscJvm1B+N;;iwW<-LdmF-ZIdQ+pcY5J0Ej4IB$6)U;P6qOvO7QM5L zy{r+^5xeBBQtpO<l&0N@Sx z+rj>R&-$Ys! zvT@_auYdjP>(;G%_~D1Y_{A@N;R|2*+~+>GX3d(9ee7fR-FM%L6)S%BXMgsmfBL6C z{NWGJ%*@n%=wJSo4>`b3_`jLDTmL(-z`2>azx=bEbu&{JrmFC~Q-fmh^KDZzd<)BckFUDd2&3v#OK6;+ueEX*i3fu-SKRY&)$Q#yK^j?Ia-(XW;5AD*EoOs z-l@x``5c-5)TC#J^Q+nQY_z>D8!cVUMo)9@?c%sIu6uT-Zmh0t>hO4WfhQ_Q>at25 z!`(PBo=s+F-l@x{vNMO~0mSr|^Ox^_g>)|@^jwX1Avca^V z^I0#@_GEoPb~Wp}1=>-jxLESqOm>A&zn^327qd}dzv5LuImZaoZGt3ndjN5i_4!=C z_h*9{IGtpW9=={K*8vp|@Zd1try1-D*ml+Z@j0EQVGt+i$;NLp5Q`g(o5bZf^A(Fc zGS0Bm5E!EWP!ly#9_ja z0CSebF%m=Y@I_9DuIYroDn9!c9wDD$9v`_fQ#TE{SjnYq66S-s&SYc9$H5xRl~q{U z*@c%Q^yji^RuQY|NQegI>BJQ#7?+EknPJYjD0xaU_NT{LNWfnmjP=Bz3p^T- zi=oRk$VaoQV0|Dv(*_ONrl7+_tK!nn+5T*BG4L--^@PG;KY1lkk`@NEqW|+Z_zb8T&n6g1 zt~zsML9PlQuOkGI}5r#QS*_zy-$7kfpQ$9*{bG0n<0bFW$XZ?z!pNe1>rc zOI*t)d2BcvoThB`eOl#pXeH&sk@$*FqZYh^e9=e-q-0W zFSDjg+2A58DA)i1f?*G$b`B)SFEf#R+RtYdmrwzaJbQ6Lyn}XmY_zy(&ZGIvLE%;} zqYQw%mpi8u-0uVnA-q#3usRBH*7Np)%u&NJ*rZK8Qz@%r@h(O!ay>q$huqw zF<1e$7!;FU{h=f$wT4Rmb#6%Tsq=xC@57~AAmy5prv%>eRPNoQjN-F}a^=3kY&gjx zfa5HH+g zID10ababvD!7LldMCE9e)0io7kt-VH{#%|GW%4{Wz*<-x z-<=s-&sfzlh0k;0bq?_&jPZOF z7jX3bI64-87552N&ImYRz#c2-b1XZ5bRKo9PI70udo&wB%uq`{aDxZ>vTKV!aBMs~ zpH(3Ib?)xv?w-y?2j|{vc(!#XXR z-8Z*3lp?zxWFzvxIfaTizynq?## zJ&<(yST@4+=d+R9J_a8gN6zw@AIeE*kKb;Jk?bn>j=@ovplk)t<;vps<2j!{UKb0i zaCgrS7af^92ePqt?nU-`5Bm3P_!|>h?`@wN$@<#s-q$1S%LW&}lW^vIabM4gMWNuX zfour5ISb(~@HvF0pLh2MFyaUO%r%tw^c}dA(CQ+nXoWWb<6<^^%OjVwf$ken??P5d zkG-89gG-oX5MFgMEMM2nlU>fny1jDorYDE8YwdM7S{LDzfez=rmJKZM^e9g&>=j3G z?yJm9)YuyUXH)Gnb!{_s2>ld8oXbYcPQJOA#j%bE+=bapnoAy;LCFu4priM<(PE@GAAX@)_T>4Gb0(&-NUxPk2I zLgS*brpnWRQ9<)f6JZi~a#Tr2J}o*<*5#E#x83j*)C0?Ww$+t1>H}E?&?_iVjY~}G zwOpx=`%w4}z;n4*D@@4KoYB~T>0^L>YCJo~B}jV#Z>$HHdrmHZiI|{2zovLIwZo~J z`qAS9oJ-%Y^L>DOh=gMS?}{K!VMuTz)zLSw6n#vtTUq6FLcm zW~@^d8e^184025`aGK|?b4bb|ImLoc(7?Y*2I^z6BRqdT!H=a(IC&4ZtOJqDd-L;o zZ<~C4IU8C8D-*6(Zkr6Ejz@xF8aP1F^=t%?h!zDU@Bz)PmaegcYZ13<2`}cs7(h10 zDsI3C*dx#_f+k}O%F~0ZA&u^htE=Tk*&f=z+YmgbIf$Ih= zCjKw;Y;~;@ZDZLKGD}p!Y6hfLZLQNlt?2Y*Q@}pV%7*vB{#jQ`do?wB)tSUQSQZf)P#=npeEfj0M?o917`5Dyf zBg!+uCOFZeygS8Z=xoaM7RYe&7Oy#&{;HXa@A9#rtV{PZ4*;Q6CQLK9m zF6;@>aovNg`xg2)@84^W@B;gv%3CHX6e;ifnO(o2Ec4IT*f?5d#XD zSnm?|ZPz*HnhWX%%`Jhgz2%ao(K)zdgSWUx-(y6V1eJshNB z)}`hl8N4FH0Acsd4~_i@lb>$aasL2|nq3R-A`vkE?sV5oonh#YuwQ^d7Uj10pmhkK znE3n$^CrG&sG+PMImUNb{H`+z-?+qJdH!u!$&nA#@@QWFa~;4p0Z)w{tHTDuZnr!^ zI=Oog!2~twDT&G?!`UU~z(e}cJb=RzkMkGg;^4t{K6N5JGa)Nfi0{vyx%Hu%SC3Aa zl2Hi7HPJA?8GyvqJw{o>WG2maU%taA>S0O)xlRXG1tMH$8Hi&V>nx-J7P7Vrjbl1d zr=4d*Do#0PXb7Z9OQU8_4vmVOOalp|o?^vDN(|qd-8iZ*I98p2rA_GB^w!lp7W|9S z2+JLp`pG3xla8aM$5h`U5(b(8e%u9}BeGPUAJ7QtM~ey_&vOcYDB-iQ*fC^|bhhf4 z2PS!3PlZ_^8mdJ1NYj5cyFm5{Lo|?p?x)l@IS1fjY{V35XeyhyI19E}u_2s)97mPv z4OGQob^{fHvlBY9Kbxp36r@Day24*^a!GxKn>>zb<}AXjtiE@J=eP)J zI0MwA_c%*=rMdzD8EPXuitl(3EPOP4)zJjWWss}6jFtE#C5&L4vs~z7?&~Og!vtay z*eCo;U7$XmxdG_YpbuArVQ+wvON%YQ&dikoWmt{rbPWrAnM%Xl)?yj7LSK?}JU=QDmK&gIF-%j)SG-=7ULD#*Qd2gHoF;hr*^Xig#r+etAr z7Sn}0DVXC)c2fEoLCSdRVF3DXNFaN{V68TaKFELn)Wai8uJ z*kTDdZDhRL;rVQWWR|l|RI`u;4h?*+#h5eYOU!%hhve$)KEvf`EfN8@jUnTJ)ZBgYbw5PeGGmN1q!>-bb ziRE)h=c-@~d5vz4h;}tdXCcg0W*rqwKDojI#(4G$X9fb$>s;w$fE)3NCd|(k@gHtH z-OJyf0KIDO{er#uI~lLO*3wgE1M^tOTEFor2Q<+&tMZ zpP0i~#%RuW;8y%6{p8=E?Ia!Q)IrXBZIJti^1=P%rHS%*D`(4blv;R>3GfJ>ibjmc zhrd`n4TdGPoAG%zG69J-%_dz^Y*>MGGDVe&f{_y9cq)$J*gbK9l+#a2-F7vnYx@d`46R?ma?50vntdQNt=uGMp>}iAB!pkVFa!YE`(XJWqnM z0nh;wK!|?LMX))>5gsN8@OTIqNniq759_AWMqNski{YkcEdon0Wty5m3~^bK6EevN zj++i#5lZQ7>M=uh${s8S;<12$mv6F#{Eph^4hh^BOGeIU<)8-z=1+HB;k?%=fl)3D zY0IWyqF^k(D?1~6B9*y!P`=>|jTACbUy*5s%PV#Hb+9)S9A(VU`ljG_93Z)I1()!M_E-(|C$79n;Q#8FK?5M&OEwSSWeH`OhfSb4yI57}NV;RA7 zANQcD6L#>$T|235)^RaBzyZAGKG%;5WVK`>nAB=2)-&8fQ{SU3j;uS^&4Hjqf&s3W zF4hSf@?umJ4Zh_Kv$64+qsGv->YB*@#}FF0YVkpT-6D?yo2w#uqE^)Eng~F3O6uBaduKMl_-y`fEDs1M03$}|R zbr2z9%r(JxMWMRjGsS|3_@w5?Ul=EZ#72ko41zU}ZS*P}LAKHC$&mc5EqjQwX8-y@ zk>SG`MwSKj-!aPKw~y87;Hzt~SJIF*V~IUw!dqAIifpLUb*aFTEf^6T7D2QMB&iTc ztFzS8s+OdM|AxQlE;J{h7a6%w6=dTyy-BaeI#{LUV(arWdFq);gx9qo zI~br(sx%<(5UY_1Z)E4Sx-(_{0)s~F$OX7@?%J@BoN?t?N;RkB_10D=+YFKXxn{mE zOpmVnx%U-f9r-!wh1T($PPURl3f@cnYfVl8_BH3Il`~~NOZ+R#l-IpGeb0Srs5D4m zL>Ba>brApSlbur!LVBl78N68v;U9&RX~YQAu;QzFz>`CYkkII6h-WN?!Ma-lw7~vb| zp}-(_)n_C4d6arx_|3Dj3gf%0r#0;(+Pnl!^)J_riE!qD&#;0lSpTi} zSezSq&pS~0-hnVeyAQ@v5~@1dOdUy?8{RSC9oV_<0smInxO(3S?;@7Hl7xDsTYX=1 zqN;cO&^dtTYPzeI3L`mEF~Waae-G@m*TMeQCzv<=*I#$a!b#;+ghz)6-w%^4JA2bO z_`q0^_fOfpi9d zdy0vuOGZhXamM&(JR9bniE)jXcu;Og>cwG7dEYZz_r~DAiVnK$az|fB|pE)RC5DG)bE--$${*Zs9>sEl5=o3mLQqpcXd|smjCi{@Ic(I&>39OZ0WRVv zh2H3iCoJZZ&1M`TySdn@4jlSM|3LmbrvvGSDIZ-{-3J}YaK0oO{V!>bklssNG$h1H zsG^BmZ-XV6fDG%Az*28$1}wl-Xg3-h-mbAP%d&6;-H|3VKowW;ZbH4?Kl^#*3Te}Z&+a3S4E(KGUHJ=>tTC!tAHj9NWu~#H_i9a3(iR@2CUo`YpmXC zjSazY*J+<3HUu-|kYg-&4Pfhzi;GK4F1Uk7X0b`Q2O53OU_qaiaYEY!2ZB|4Axc^! zbZB4}2g*sYIN+2*d;r6m=86p*PgJJa83!Ohr}!gB*HX zY)m8!d##M$&bLLRm{E9IE}0e;ffx~vP(lPcK^cNz0-PZ1WCAJ-1o$|3%sq8@7UTAo z;4Z{?>{!XJGiMdoBo{(I60ruZBkL|F+M`Kb5N0sNM57CMp$aVVqFzAqqF|Kv0gpb;J8!ZImti7^#C_I0M&c|<6h?~TyfcgeunAXs zmBZH`?O<`7KLg-82pBYus^lZR5Y0K1xS4Pm^Z~d+VM?+Ht#rgq@Q^#=wv_B|w7* z{)CfM5+~1d1M8xUpx12n41REqD=cInWQu4|^^!WmY{K^$+*wB&U{&P$WkWk6Xzxl0 zo~KE{z<4mYTZ;;wXxHfzhzJ&pQ^*@LN)KbR9IqJHQt^SJPHILv_4nzCu~;L3elie) zK?T*O`^7*qBZL*H`DnnZ<1XP$*r$pP2~zNdSTSmKFPh1G26Smgzese>(q{Ptr`&)k z&|%%cBp8D`ph-8=E0|G+jC?>OkHw4y3%i>lgD#)b5){;+wT{P(U`*Y(N`og89=Z+s zfJj7A_2$MEss34p!M4HZf$_q>;y6|QWD)mo&1V(d6S>+1G7mzL#3!O{h+C(C!!S=4 zPOOZ?_<#Yp?Oz~WEepdyyI>QO1vQJt#V8NRhi6$fa&w)<89m8Ax*76ZYUY5>^L&?a z34Ot-F2qu_~gc@Hj#p4btS6WSx#Fv>!tpW$j7Z(zt zMmTF_Pz8@mPQCG*JAu$WLKxoz)91*CcL$@dsD+P1Z z23&xDe-jswKmmBH=6oIq1FAv%^>hNz)faL)0dnG|Cu_~#b< zx`VK$jKQdr8Fi2}<;olx3vjj1Di_TVR)RZfVphYjNjK~0dJm+dZ^kZx1rla5po&_6 zrj=E=$%=#>ICRR*>o|2AAwsPu%eMbsH)F+(2mJbj+GcIb$*voX_Km< zHMIpMv;t|6#fafza20FfOmHrzIGl$~`M`}tG3I7$eUUs3ftfu@F$Xz|vkc zrX|d>4FlS;uz-N-^PvL6-98E~8O+o9a9lCR*@?6qUr)f?n{+ivl}|`QFf_btRY#WP=rJvnMi6bl0h02A8c#Jd`+9T_aHX&Md>xO{k4P7ryMkeKWHjn_Gh^wjpADkDU4Zp8 zw|3Z|q?_#06y4zvFacKq6OfV}roj;(b*{RgR13hAvFBsoTcI;2qvL1}_Q@x*3L8tq zO}5x;>d~^621J8ugc#OOd!oeCRmo{x$BTHPstrrd_%Nz@%fisu!oE!}_O_34RlJYn zmr8oG164=9it&V1^M24OMFwu85HgjvUEhRzeCl#rR%Rx7i{KOiorVa6-<}%gly`?i zFh$`JNT&EsM@mA0m+^lD6bBD{n7iUc4W+m&^HG{>y>OInMkt{QsIbl@uacVLF^1wC zoc;i&2r& zZWD^(u~tcmq%WQ{iTI?eHb(WC^vPNlf0B#?a29hf@Z1Pjkw7qv(c_qeDEQSGoa9@P z487$!!sBYi@q=qdyH;%bs7al4IkW3 zfkjiWI5n}xqT3e|UqWY$*WrDnzGvG84G8yzxKM+iwmA4Gww4S=%DMB6n;ASW^ zPl^IRY}ul>7$ZPVDI^=k3E@S(^|sJjg|VFEKoHf8#hvSGf1 zq*Fv($jt}pBXCDUGtbO&2+tZ`0EhmZVNxJp#!M;?=Etf-^f=FTGSH|(>uBt#;wnS} zE#^!`Z4H48OhqQlS`Fwphh^bx0vH#{iD*xBe0#3gj_Nv14BEa$Ai`;?nyj2SysZxR z!1EK77s5u2+Jc5UOvl!8fEv3K&)Em#>S$tZIaG2eL6LQ=!9RA5EW zu%Pw|&|nM8q-H!>$gswB&S0;TH?9wB+?5(#VQu`TD*=rm6tCd{g*r7l13DcB?N_-@ z-X$9f41oaf1iQ?#Mu8vsf;1@(jnCSxZMaE}kh8|6wl}3_g=?I_Zv_^RB#z9bSu)be zHD)GN1S?tTn@YMl8v-KJO|h`-k*UphI@UmA?f^k$+|W*wT7C zYyio0TWWNo509>%ZGV zfg@CcamN5>YN{A4!7lT$#C0-FEZYoo5gtPcEJ%WZRHnh4KGdxUqE(jj#K-u?2%>`+ z*TPpod=szGj9cXuAOI%Zew7yyG`hg8mTqnhsUmhF3=xN1p9W7#3{4jP2cG<980b{e z)^N}uIU2_nx2Se6N~Fr3nz^XX#U@Rz5>zVPDTtgZbhPh{ z)5RHMw|ywh@IApz;O~7Pg8*S4Jw+CpW$0Y0glr6rof)n|=&Yy=GA+eSZqxe_yKpaK z1P|w_DJB~BxI#BCTn8d2Sk^3AWJf|UH3odnMJvPBwCyle5+g0|Gm5lC)D2Z3FqFNp z>2BM#E$rCyliC}V9k@DHCz8|wHJ;|!XqtwD{2KSop%Uvb18(SQadk;YwB$uQW5o#MD2xepoXI! zrocke-j=otU<)}mqutBZ!p2!km0ai@tsdcjsk#r`Ocg{o-A9I>3+l>ocNDq1+0ztXB2~+Sw0VYNjjUc5F|FQ z@tVbu0`9Scf%O7?IT~k$0JGqkbIA_a-E0DhinG|(B;j;2q;G~ofc0an7>EE%pITA< zZdiobZR-(2kf|<=G{4njmAGD+se^OCOEHBpBY`qUG#QI6UyKc4&aZ)^J zw|QFuxU!>A+E|pc+^oxiLR9+BCse?=5z6Sx!F7zFhMd3p)DKome-uHei4amBc+Bg3EKvdBN=i@0yuS9dFmV^z|aOnBe9Rc^yGQR1( zaYlc}b3PY|GCxuNv0I>US`boCXolQoQEVlsr8RVfZpy}0LF;w`z^H;e``KOq{}*t_ zaJMR(m~eLV$k$%=WonUazQhr#hvoKVXL!LCaZ4!CsRJJmOM)EFs6D+wlTU>^$s3)#cb9GH_90q#JTI_s&BX;C&=fl zC&D@AneT;MrRgjA4s*;d{)x~4IY(fGaa3eNVA4rxqJ4o`k~?Gr2>HaDAc)RUo`*q+ znWlc|GpM@c7sW_=F21m@22rfg?Io%qvS95#Q^N*V?dq}NmU;=_QZ!<|%m#0h0AYaH z!x0<2z1g>CO!EFQaF$l2=b~NKjT=)DYqngL_TINySvGPvrM`-OaUX*rVPf8PU&=VF zK$9?b%ZkGmu%6^4tD8oH5|Ih0t{Y*G^6V|^whi8dV3Ojwt5)1Y$Y~m5n6zv&QU)9; z1Bbb?&EX;d>y%n>wWXejpU>o6r>BwI(PWkKS5wW%*g9!k&>6bZS&_x?8v{$M)$F{%v$bW+ zn8c+cIl$wBstodL!3=g%P$VsbRbm4tE znCOqG;Rpz>0|;p)0uYmQSNUdIaY#4I2*s~Li+2TjuJK)_4YY|ZU;bchgjVFFfe`&e z1U|UOTs3mi#lGyV)+!CRe0P9oz6~b98eQ5Fju~fD|3*Z-Kw^%tkvIn`rNs;Y3qw1x zo6}w7g3MRY5fOs&$ z0Gjzb9dB^b)XQ*~dgd)Kg^R)|s}zuq_<-r{F4IpUwZ&h1L;76`h-Tq;KoAN1RQ8{b zAj{FTdfb8_Lx5QXfeBxEp7Pl!LFiFnhbyK>qBndFg0i#R4t@eI*!S&4EHW4}{LnER zV5szF31VI2I$Jk1pE8E-*jOx?sX-80NLJI2l8CsEP>+Bbw3v6mwUaE=#2F#UhqZHC zBJO}D)S6Z7f%vR((h(WJSP+Co#>;EKN`Ikr^-v}1+ zZMoj&+jIG9Y#WBjYv3(Zf>{f`n&RR0G9Nh6oOE`KOMb`|+v^&=p{MHd2uM&z?#5C8 zBasZw2vt0Z=+ePFiUEl?;5lf2!YOE$^=u%H8(hOYlxhcRQP~EzP09Koq59V_sp( z*jG&)Axo^)ZXtUNePN=x2VRYvkn6snU}mELQ}y9r4$F=s0PXNFA(03-3BkxIWdfRO zK$y0Qa+`5gZh`W!G>qj*c~kr7NU25A_9+`HtDFlt9ohB)>smK)EJAz@T+Vek+=FIH zcEs{pEUw122sJK$Zs;+~w`ng6p#=!G{*_XP#?@|P(V*EtV-c18)e(F7-K*>{6L*Fj zMihq1e)<+KgOCBtOyVW0NCNLerbuhd%b;{zx8heYO>BgMO10i`_I3F-Y$*V^!6boi z8e@aKQzzUs&MoS+hHLJLg~QGKTH3fpY8VRI$Pce4`6KL0 zl!@A=>d3wC-T={6L=wY9k*u3?_h!5Fl4V}~)cJ(DR<=9jR-(wwN*E==d3u*cohmpH zK-J7{jDmH+@+1()N@{5`oa<>Y;f_S$9;DTWl40C=6{Tn0#n&xK1xqCZY;E8jr7(!G zBV{_y=@YXw_F979NxlRks9|yKMjB7dDLuYw)J`A^*t$>#uaHi3l3!;Yj6$#y7>MZC z*M9_&u#-AuwtB_uDb#3sZuWHz5sIq7xs~zUqovU4c&9#N-L}+7Uj)2H&NN@khHmY* zHp-&-rZ&>CwaJdwH%6%v$ahSpfAa{`CN~VkMBHiR zNMR@ksNmj9H9N-LYM`&+bf2`H-9|WWC_y*rpW2*e=8R0EzJ`iP`|0*8$^>$tG9+@Y>D9Lly6-RD7tsWWh(vOrC9YuF@br1YU&JVC#UQ9A74wnKm4s|1 z2&***+4$WclL=V0-FMM7jI0F81NGEvVnY_J1C(NfnZdXTB=gmx*>EUonV33zuH0p> zw`AuIDI&&89r@(tW<`OMEJ$gv5h#kwYVE5?VmN{_F;GpZ9)cC{UJ}Oj$LzP((Icdm zw)8yjygbi>QrS!YJ$c(bWzQ#!7_agwLQ=f(lHpmL$Oq~@&MTN#`8>~ah&B?)36bFi z-@tGqAwvYJN5ma!gI&A}LZg8Q023~H#MbR|I3g@*fRSh(aC$KwL?XplY5+%=F zzp_r-oi4!~3@Fa37FC#~J0ncJp5OyMh0?YI7l3fyv~pjoiwH8AD_3IUH8y;A;cD8O z7On|$NW@tV>;rRFKqHUE(hIW2((UqM+c{%WO^d!R68*9Gk-1%7Y5A-`j-6oj+~Uj6 zRO9L~jZBpXrd#bj!SKv-Km|Cd`j&X*HxEb#+ioBltd2eoW@ZFA7v7H79HraF!aQ(O zArt8>B@o`=k=ES+W0nbS!i5r1!vzzY?9p6=VUQ1i(6^Xe=rRuwN!62)_GDNRqnya0?@;tfnp)!RZGv2i$oGq7TJA8?C)TSx&Zc*drUIH2TV6mLuB=I$$~n4cXSmK z8kzbgGxyk0*AFaJmmftkfQOSMC<;T@I88#i0G1DEs?5MUhm#bD=^6q=EWsc)PsgK% z1Mx6ZSH8*c1G$?PeVc=B48O-x<#P0fl*b#BcxXwsOQ7H^?vTZ0A!5{+$BAWB7RpFA z>C&#t6TWlWdoW7wR5OPydSoS=so*Y)`P!T-V#U|I!xX-aD)pc6l2TcPa0M)+=S(Eq zEhN_TxO}%bnl~-2`ghI}-M(POJ-uK%*cH+S1JBf<`sv#MDoZ-NU2yJy)iJtjcS9n} zuKN3GG~fM%Zl8U|as5S(o5Lq&@m}}QYCGqU14P?JP0oM(mZxyf(+cOj)F5)J`Z%*s z+|rmeQ`dEKZixyN7>6Lid+;Kzy1{+CY$(oA%q?KB8O~z$oIH#x#Gd*FD1U=ZtFFP+ zJGYyi7f zWKDfUge!3d=dpp#0*po_I0htg(99B^Sb8^epe*!o%2+;jp<6b#1IBsHHn4A)zouAY z_SnAUgrH}pZg`=|5(2ruHkpd%N||z%WF%;>v!jeT3`W$froIeKB!QNLRo3Q-pmC9T z%=Y&Cnu}slc5fkVC}-|sy{EdR)G~`b%%`} z?)z|JC-Xh*wu>1!je9RcSYlGkLo9t>ByGVpCI4Twq)VjxueW&vq^a%+DB>HMI8?j& zGQ*5`B}3Yr>XuF&SodC2eNMhG4*G(#K`HRgKSp&iS}8qNBo68rCIs%d!aZE`GuYlz z53M!w1vX*v(UFGmYm|YTe^L|1)I`v|i;%%DqScv^K(XacMF{<6*p;GY`MEUh66?Vd zxHatVF-LU~ZB07o?r;D!)RU-P8Jg;SX#ekGMSo#jNUtMfk*B#WM-O*dAqu^-u$=ky zOx^$Y^$_~NZj-ae&+*o+8@F9^(g^IbyzO0o!5Sgam7G_+E`?MdaX8M4{x`{b8v6+) zic^z|DZEs;PZ@FFK7os#nDH1c0q(eIrwNAEOELx{bpZ3}p4nkXxWR;W0_s^^ow&^` z2%kNF1m^~9G(@LqI)$E8XCzEk*u%}(0l>*Y5NNIF+dd+4lCX1i-;+joJ zHVuPpUvq4X$M4#K5<*;7cI9?R;XYn{LX7|^=6Jm9-cR+S3E+$59?e7BKE^lk*UO=) z9^V(MvujhJC5d#_#G+|KbK2($s7wp^LQ4H~f3$^wN7(#@GRxW%cfi&qsul^hv9f7% zJx@GY9mG~}A%{knwbiG(0-n8E}({3>t;UFt~F6FT`eW&nUBG9nAlztyC!XQfj|3MEzMuK13^hq!2Wa-*2GWtr>l@@ zHI#Pa!u*qPyH&90(dVR0WL`mr!|phOZ$3yHIPDWrD?~3Vpn+im$0(HXprNwuFO@q$ zHtuAslk|Ur4GlyBTxy5#^Kv?lZRQs+6Ii5mIYE}e#8TuS<7P3LGW*0p_KKifZm0hs zzD1q^GO@n=F4edD-gk}WzoV%Gxrz55$oTARnW;N{8zzlBeAMBjrE;D1opzE-hyj%< zm5^&tYhqTqZ!^?gnC_Z0in(ICAI4vguP!z&Nv;!WwgHk(CJcv;`$=(u?`}{YyRz7m zY!L1m%BC0{1J6vP(s;%um*ItN7(X66)u>`fSy~GT$4%mDxpP2I5ZpOR)RBBEg)-(l6|#6T%4(O2yU_(jBG~LOu`nbb z$gG!6*eKzzC7X~-KR0QsB7zaChD+2K7V4^}$SGm-fNVV0>8m=3(U{&^yesl2LCq<- z7<8AVdEbpHtQLIaHk0&j_d!oFt!*Oy7-saYEDG#jiraZ7M;Eo1B%G8K8Wt6JS5Jt% zL2B1j0+Zsr(^HIX@Fhd)SpN7SD7BPiZ|-uYDJ+5>*4+oLw&+rs2RaH%cfBf`Qhoio zRolA^5DQ!+2adw0i;P1lz{l#+k zjT0sq`ftk%3cvaIC>;Ff9pbvq{~SW`zYI80<4DH|e{mzdV&fvmm>*w;m*$&qfViJG zpdY3v*I~-bJ#D=w(rZj6aPROkXX;4M#(%dw$p#605MvyBc~UF=dAM7@Yn-R?$HETa zE}_Emskc>YmG^Hm9Ol^_q!d4A2gLJ?NF#CqX<&Y!la4v0T1S)~4s;zF4+Dq4pw8qh z4%h7_Vo={xMva7{0`fRV8&I)9f~mq=_pMfGhn9WovL zQ?~F>i`9}EX=vJfH4`zuez{SQ@3y-B-ffV94vUURkWxSh3iK>N5Z40Bd`|Pz1*tq~ zppEw(j+Bou1wkoYS-Mj+KIs?JVTsk%0_%7BdPzJ?FlVXUxBc3irT~~DqdD>ckf;hUomXs?gBf#f6&yr0C1Cqu>f`zO3iAu)%*OYiy)4C0?KqNR==m1JGNA zkdPdg)kZP<3v{z^`rUnQLz6FYbD%1qW^fc~I*WWSPI97gG6R#*%Nvb!xBvvnHUK&v zuAaNm#%?D>fEK%C=88WoY9j<8IZFs|@h1JrW|>Rx-5R_THMJI%fz{ui!1*=CnYbNa1{YGe27v`qLA7GU?&d&xRn&_vQ^t(2 z4)}3z>9T-&=B8{04gsd~4YU&t2h$J>$a*;uTiTjUFNkX=sJk6hD9TxAP{2WGAmK+o z$fekJv$)!weYUD@L-F%e+iji-+K^OX&9m}}imy_NF;MsIw;#ctkvU2;c!0NpRL8iD zp}|}L0Z}S{XPJA5GwDqOtevegiuq)mb!SofF*EFJ?XXM80ouwcx=dsZ_N0n$(%tMXmSzMweix!1Rqbp7+PiNKc#*!h2=~e}(=X4hzIYRPbYhY96Huat!D0U|)jSLr=U}FzpT_ zFimXc22s_)$SVcZzD%A)-Z~ogc#O5V^Ek2=(eVVA#G6KKo%9vrw$s`0XedlpOz2RX z;{F=X_E$4YvoIph{-7;c6J}R2nbdcumU;^mGXPl82hb@{dBuIi$9%Hy;MNggByx;Y zAbiUv2K8FOL9=sm2d|Xj4qt=;{cpX)d;d$rLBiNyA|7Ur`a`Zg`Ptm!WN+^8m+<3s z&Ll^AzJby z+2Q=z=l}hm{OeDABi}wVhe~Y3yDKV@sA8vll}c{|cX^s6P=>2t3eLpacs^DdJvg!- zYu>Qs$?Wt;So~xwB!egzYD!oFY*WzC^6W$dh|!%HW<%Ck*0n4jTECo&6U!hG^x)k# zu~Q3y{^$4jSUyp|DeGFwSlr*{uKj3a5?PY!ZQ6gcV@vYpCFSPEh7Bt>?i(t3C&J6YQHRQ8_#vXfi0llj)R)9bTSFxwdGm@0_Xyms}5#)i$0HkBLl z#=K!wL)LlEL71Vljia^(jy9}qXv~h4nYh%vB!gW#ILwdcE!mOmPxzdzY{0z+PO0gr+}mP*BU_$?%Ho zRKuo^gg|i<#9ndldZ>EpiH7{tz1+Y}B_+|(jxWt7Hh~{VX|wED2?67GSClMplU0_q zL9G3Cv(e8*3!vsPnEVJWvAQT z%b$dl*@=4oOoQ&J=7!ZsRnx|mYbDg953O6-ly5k=wtYjJ)M>~!AXse`StC2v#k+8k zorLHt+bx0e*LLpRv2V|dP>4I|B8RctAc#l#R6DHu-XuR83NrI!E!&^}&YymF$L?3( z$U2_Qj|9EulISN>dP$th=&^b*vZ;PqK2~2e=9p(Mr2XO+3Bs@N=VdG z`L=v3XHMh@Z1=MKNq|R9sst|h%Zw+$Q()iFLA6>V3b!$`pc&X{}ruN6z zwl?I=Op`Y(L7)|iVjLs_<`^`%!|dFj^u z>d6&AURe@C*PWf%`qa}M&piFq)`D)w^1Avbw`tH&z@m97>ulVx8ir`vxVj86KlQ&n z{M(yLO$Q%u!?HCtH#DgkA;|Hhl*6F;s%phPhpj?xvV&Q+>t)Q^%lS*M?S3I^gCGZ> zWezdJ8H9lO@hUkj-u9p@F*?)bV@sM_e*bs>kAIhcJD%E~wZFCJl{XpiPNl-W#2|+0p3$GXcRfV3eg+}cnaW~5vMg`F z%8(e;1*ys+8T^8r*~q;*Fpix9wZcAKb@TlGf-LoNUbA^a32pkwS3mvW=GA#Y(w4^ipmsxZ$W>RVQZ6Xk8dg_|hRr+h z?YG`|HAgUWi2C|IXw&iBv*>q7CQ0%7F+_-F8SVsC;j8c_?IfVcd2{3TZ~W1Jgbiec z{cr8rS$b(t))8bq@RJZV8N&Ha3$JDBD%I~t#g^7%1J~!L*5`xELHI=W0bvHJvmR;= zV&4!X;I=Cqa&O8ym;GF#JU2_QLO`<2ADVV@MK(+%z_E%{viinP@^{)n&YZ{_?o|Po!pT82 z+02a!=KK~&_&4)WIYzXoyC1}oD z@I;W*4VX|cSI`wRy!A=2e+;HM^~6>vdaAX4Q(Jd-@{gY;ijXeLI0yNsm%zuJjjJ~! zq#N*c@`lEBzw*_;`^kTpuWmZ<_=d+@5nDvGY2}u+NltkNjf=XdkkqIksAk=&MBV<} z_jY}M-C{ir-5FkwlewNqQbIx{ zPA4K7ZAh?olr@KA*@;F+ToDmR_*KLAE_I0ZpjP{oo&}gW;Qm4F3ItoTu^~G~P`06g zuXroXs*oiN(^dcEGH8W{-SR|s^677F(^YQk!tNih&!55TQ5T#5N2f!#8hhaJWSz|$ z)_8Mey|KshDY{S~dwfV}XwakfrRTeuVS0=G4HYL*56dUbJ zp=Y;4s8)3Ax3eGYefg!GQtz!qgZxO|*^JCV?Fd9d$Cf6JM=YocMpRMMf0VWT$Lwu@ zg3{ShNO=$)+xB`#$mM~&@P<+lzYrns9K;)pF<*EhCEr`$-7$S+iUP4RV<;dkeVvmT zce|tXd$aD3Abi6_`d&}oPiW6JjA34{#>~12!UzvP-~l2IJt;y-)GA#=DAtfYr=4!+ z?UIR{5V2|H##Jml>s(8$fdCR7;5IEqM|R;tf$+|V;-7drdsk7LAo8AOx~-2r`9w8} z?|zbKBdjZ!y|3Uj<(`+?dSDybU00PVleOtj_Nr5 z{&Ro&XW!4-p39E3l#jLO?G(f#AjuBp^Ww$%!AO$Ag@`xbOuhr0bowN6O|?xQ=Lj(( z1VQ&h+!Vd7fd^*P?DiJSFYRK)Y!0bO;f{`zx_Xc{{Bb)$%Fs#OcN6cUfVvin5_JD45GTYhqkg+AgJ!G%-9yk?z~54uETnM+)p`th=eOWX#LV~uQ*A-jNX?K$k*X_mh>9h=FJIX}92!8WCk;eS zXVaUss@bWhp2^Um<_9YUOW2*l=~0GE36RXw?#LdDJZ8>ntJA=)~7Swes&s zc3{`bZ|3c#j;uZJfKo?D4K(BFaI+N6Df|D84%jO1%#M^>wm&`My3>Z8IFb>sOp15}3fl%* zEDYLhXd04A|1xr1CeV0=8-I0TBfY~dI60cGHb~5alVLAau4iHJqRK0F*9M;}8knQpPzwn6;C~dFw;jfgCzz z?WI=eXUKtaM%cLnn8kG2C2|Py$!B#*eY~Y3`Qc}u`#=BiKj{S>$U5@&x8B(I!tVSq zncG~UCJUGycyrg@ylvl}H+DR~y;M;C$MbpT2d9d(1P{%|XDbZwj_J1aY6xmG1Scr# zWjT1(F~S{2QV3Xxd8#z-R*oelG**hS$PLT*^mPJo1->q+r+e!|Gr$q0iMB{mhD$k1 z>8!H^skeN5+i&8$;E}*eA&E#TfKY+1>y(lWxa`IIqF(Y zAYq;aGqiPc#*^6*C=${);w+FvQH{>r1U!m4f0NNc?$ z=yDwCtP2UXR|b&|t_1#LOG?Mf%|FxpEMz@UXxc*@?a$g?%wKq8ch>Rky;;Zhth0sG z`_Xb41%~F|Kh=6XWhsJ$-}%%3`ww>$(ZV5b?cVvyOS|5J$VtX$?>fw=ZEx<}wd47` zrAp|9+^EHCA@hTq!=Y?+rOK6d5o3NLlsFD7$t_R$J_0qkLT)_c%Z14W;Z;HNWt=wH zQIb#PkU8ZP6boEx&Xz{WBEdK-8j>bQ5KAN-OQY!VifP+6Tn}sq*en-RC)DrEDOdd7 zmXEL4wkoTT)oyIsOp@?e*16PBL7MA<%YE-X{e&6I$-0C3*y^0_ZO=SS8W%4)TxHZa z76aJBMz^fYAKZ+$-PrKo=b!sKU;eeP=b!n~ryu@W{>g&&`Q}G+#DB{({}jSEwo3NU zJUvJRrBF4q!u{7di=}QwXMZPa-(FG-q0Eka-_Dm_e=GaJTWIxnNXJ9De_MjyJUrd% zGV!shnW_Cby82JPowaM)+ji{RS$=8PjyFgaqSy1IEyb7!{IliG((%RB`Q3V5*1huS z{UOYunDss&YQD71pZiUkTo>41T1iol=tOm+u)Cph%2cSLp*3+Zel(sH;z#BHy&u)G zAT|np)q0r1cS`sR0#KWQ$W(y71LR9fBqjjH4iEet1$@)*uYLV9UqY*YQBhy_5A#P$>((~@K@P1q??bAK;DDjd44+%)L|%`LE*e|z|=&Iy6t=4`2LO;vma!wJNCS~>&;io z#6CaRBkhltA#bUrw7sRZvs}=1ab>-@nV$8(!Ucp)J3xsp(SAiW+%1Tpvi2=x@zW6s z2m}ce%soWDj6!X!pr`&caZuON`t0<_k)95P7rnh{%X)|zyMC;;tq7&v5tJ-Z_Q2x{ z<9ZCDA-PZ!+50$=&r@4O4AqmB8(NwPhyH3%v#dW2zf3G=a!D!H^)N!G)+@p|VrXs2 z4l!i}tPAEj>Fn&-+OPlP&;9bppy)4tIsba;3;*bMKBfGwZ@ShP^KN>MAA^Sc+)R_|GOm}>cfzb0GLb@@i{akm()(`V($z2zPEN0{=U8k z`n{30zd_7SZHEy4ojdwhcTPqei8NT5d0jm5iA3|geuhDQE8MGal2Q>H1&x2)m7U&% zL{dj10ac%MFNKVl*i&Q-PGM`QVIzI_Mo`39Xk3Z5Yy}FJR6;>Dh6k-n`0$mw+AwHW zQtKj#fAGx>T0D{l7{BJFkxIpfgS&$>`nh!R% zY=?5zQd)PlzVr${?v6LU{~|sV1cY{kvSri76h+auiE{hidNbd(XD`X!pxhp`a~q^H zyHRQ^J+S@YPei?&eI#NYmYtK`L4iWAr6ygCrA+ zE^ECfhoa?GjncAVEm?egV~UPlrBJ2cLZeQscgKy75`kNNw_nv*eDaBJJwwJfQBq#i zRCF~qZ+O(iU~}V|k3N?FPX5Jm_)Oo#YubpHOoE9i@Oe!MTBijRouus8xu9lyrSXCN zjrsQQSvz*OzV!0${Ed9?z89pX315^WP61l9xkT~h8_$xxz-4}O=c_Nhvir@~q2}J# zDCF#c@>IY}$1&rj2l8(mc%TulEE&ijufmon3dsO;rtTP(+n}TbLN!88G3qz>h?&Vn zS|DOnXOPN~(3ey7NCvdb#+FK8P&rXXD1D0JDh4#XA}Xy2CJhK=mY5b|mXY83<@H8b zOyXqH0pCA9{gKkh@3ux!pLq5@@)O5UqB=8(S2tAr;nQRUZ7HxFE`Eort&U7I9#Hn) z`lLJM3}V=x#~ZD;HkDVdzOS@l?SY0yTR}_}H8P1xAjGzCc|3Ct<_;-reV{Ra zAaC6M?DLSReK$FTov*+A+U_^;M0SzgHjEC~){wROWv)REnUw@^J;Qk1Kqi;k<( zIFTE%n!`0Dsj5|DStK$D+`Wt`PTz;6ZJXIR~n`PurO0Mu}#*`VbsjWOu z+5)Oq$Y;wZCJmKnBu5MBKhGaV6bLBbU2P+}uVL*5td-`GNAz}#Wu#!_CbW*oB#S}Q zGC0SaQcvgtQ(?o;)fl9ig4WF~q?(!T9p>Bq^Aa0nCmxFKHnOL7)N^HJ?Ya{kvX$ z8F7AnH$*nviIWGvzvEego~3ywV%qwN@&l|Gk9vA>8mF*jaT-@8^&QwU642nqgf9q& z(1nzag6H&RSqW4)JaLT#OLnwc)-7e4bK++4eb9H5ZXn8_gzd74vS%Xu=wwJH1+v z=#^fwr?=_&PzdL8)$?f~{#i}9+VIf*4?p;*nc%~Z<0ChG^T9_~Z$NNVx4{HL1BaB3 zNljn_)b>hq2hzL!+2sH1LKdMYUQPb$E4$u&kpk-TFYJ8%g&m}pvW^|Eyu6=M`Rjxt zkTEbabdW&`-?lv5 z4U&SAl7FOgCFZgcq?7Fl2}NNS4~B^DN724*uh6(z{h#8V6I{lb55>tL50$1mAer?T zT2LU_eNA+rAc_zT9yJA zYzOwfy8HFEm-1JziTl2{^Oc?Z-h5;C-W_`n*ax=P9tUzu&pwxb=l5G_Bx}Gxe&Xl< z!6#T2#Zwd_px;pd7mqor0%!$STNRPxn0^la{_w2iHW=r4?c|uYByYWcGtr4F#!E zy)&zvhnJHFM)HK4(eZkm2>K<*4iWz?hdKxn<*L9Ur1Wd3EmfxK5F!QY+6w}eX=ts= z6qy#P#d91N2=v4@x*|`h2vf_x5XS4FrA$beWuQbOqaf1{@0pp$)YW49hJ0n-w6+DA zdN|rh_R|>zl{P+BWM>h1mFN-$3Lr6X=k0iv!Zcmzls1-wN^}oot=|vX>e%Ln*Xj^EYQn$7q|D-a0JE{5BVvgDAl9uNG!UGsBS@IqAL5n>M(Mf9HcBi>b9f$0f?`qhqAnT|Cwq+H z_@EH+5riQL8!)2`^^v6;QyM`e&oWVt)PRI*H196qzWT_6n{#?8+p45%gmmb}h#|-m zY>HI*Jh5{r1Xry@fO!1&cuYr^Wk=Tf=4(e@dUfZUJNCY`^Ub#~UTypGy*vKH|Mjn) z%fGR`49)aXv0a8!5$LAGUy`-O!@Y%USh#)#-Q5SUA4Ti9^xfGm5M>RWzn~RX3l1^= zId*8get(FJc5A|f=JNePDr37uDuWGvCvQc*epwK1vP6MWX8_@-lWVKQ(&4y=29PaQ z6$(=6ZA2ks3x39XwjKv@Acjs5WC`*_t}MvpemTiCNypYx^naP+mZwRPHLhF(jat@z z>Z|uZv}U!nXKWXu-1(adSA-m}0|e6ZpQVtQL(4p^m1bPU&ZVW}B+L%Jn78eH?eI&l zzwuVyMn>Bl%-%PN_rCj`{9pZU=^L#Nv^KJzz$dL4lzCcB_>*hFFu<`x!%<12-E*cR={=)tCyc|(;_3Nyx%Z5;Dy^$vIe)5l6hF{HWW#iDBT z@P*6(aPU4nA0;S+YE40*HlZ9bCxh=$s;$VS z%8vKq{+>i3J$EV8i3u>2LEJS-ODO1(Za(mWvGAKDKS+DLVX1&5#{tr0+s#vnMi3JM za3|s{UYwh6j1D72Y}!$*G%zNrG)js}l`x^?@bucTESqSP3Ch66RA@@WZK2>os2C;5 zjx96}N4F`5%Rj={h5Y1hgEpM9H5k()aS)%i^T`8)$k?fE<|Gb*tYeKfQ5Sl`6keDmPqLR3|DsbFrGd9x!*Tiv+ohiUOeUx}+YXHie}UVI zG%~mAOY48I3#T+j12OtSr%)ZGmeX^vRGR1%`^2cx6k^oESsnA}#D9WrZIndW?NbTWCk=Oj^)vg5)C^Qcs#m z8wC?#mSiZ+6U0K>ss*PQ4s1-xO_4f8*K&GVe{*u{(`Kj&*JAC;)j_sj{QF=2%$FX? zKmEl|cvlF(_HzFZCY)|OU=(`KlrWzR(?POb-bCJ z5_EDOMKm#TC;oI%v6y;yp6@q5lDb<(*qJg*DnM-!y=MGP`Jlpocz3+4Xh9^X69&g* ztx1VsxA4OeqOcfH&UTeCxA9V-!9|DPTNxCoG-O39bR>@ex7=!3be+tSr!Y|s+NVt+ zH;E|7Jv6`yFoGddkS3B$5nI7ATvp5`-6?~FpwQ^i^nGWJdlgnTMjMt<;Er4{OA)6Ff$;8HEFRp>mz$$0m7Fp%;Lq{x`~Z%S7#upZ+)r7;3>rASDsSBSH2fi6 z^c2EmhTa>Y)06aizRV3RNTe-=jEgMQ9r7JP=(C~+$X0^v)W?6XInLoXz`S6xvUJnd zr_@z&f9N+jGW@{3>o z)%^3n^0)7QbcZbqVw6Qk5aT`A$MIW9H63qHox2nzxxd_a4`0KH z!?OSdW}a>*9*ff=Ios0Q8s%Y;!%-wc=4!J^w2l-CFX0qxI5F%=E3rf zuq2#0jEXuT8IT3Zz`f=w@eE-zDg?=lG$&d>3Y-A>(1HbarAmwGO>Q|jVQZdm@l+d( zE)SDopPnhil;x@5W@<=i(^HA6$H+#xkW){odSq=J^&rTG-}#^a*)RM$y;EQO=!5J# zpt99jqK@3yu4zTT2)UKf|C!oV<}4GM8uuuVin#QEa5hg! zx$)9ey@Hj9vDz4?x#7Vt{?22c{pQA%O{*I6PBx^kCBsuCh0|q7A0F8pQe1)*7LzpV zZH-*QQjlDs8f)P+stq;C_(@vPs*+*HxY`LQTufCyLN2(+v_S-!Z4*CC8p=JdHEL)8 z?1LcN32*Z10Bw5P82hXwh~W@Xp{63lP3H>s_x%%Y{3PJss-GKErNtB*S=^Hq66 ziLyXLd2MbUhixDaZv2Y!IsMnr1Gva+uPL0Z*2-n*QQ#?F;e7j}bzy2HUO8jpc^YT9C|+{$Tx$>+ou0Zk;ADUD)Y1Iso4n(K_j1vhjhk8tNEf zKsXzv={}&Jo3EVjv);W?PCAGMSqOvI)Hu4J1)58s*|>u3o2VYZkrN&mIq^>+SHnu+JY(L;G&9TIz<{*=9^cS$so6HcpP>H$EJ@>b4B0)j_qM+ ztqj1-P`QX2njgqI@j;We5$%0=uYZ0NryU7eDN4?gPYbI91CNAG0goN9a>8-+CCjE_ zn(%4aemoqJiwY9GM0lZsTj8$+Y3c@WcP2h2{terT=OQ+=hOd4ArEvFS&+r!rB)C3~ zuZ0)0)=|J~cRk5N_VAXwa=KE`G=W*7C#q^C-bk_eYo8QBLPN==JW-z7ULcnXN2Rt*(qa}Q}Slm`I zOAUmes~o9cct^fv8xZ9Y6m>#wX<2I^SFf)NthlvznzUou`D!h3RWpFIs}R81)<1q`6IudZ1*U}Bh<~DGX1OK%_tuy2HRxirEr1=!KAkYz+_3Kchd;?i zN#4%#|F-rnz;&MIo!@aK%b=-o0Z{8QH31ThXIie;<0a!-hLa{!a_wX}E5)YUu7O2Wjl61HXt z6me4`suu9r49Du4`Nv4wva^#b9t>R&97|4t9?wKpa^c)mZZm3*7U+9?kDZA)-jWl^ z#}`Lr{Dr5jV0XjAyL+T(=7=#3+Qz7IDXm8T^QXT8oAQg5_3ZA|_jcVMw3OhpnN;Hg zIOwk-r_OgqfCZwS?XZn>AdsbTq|h$i`Q*5rFI3&@0OR>QDYIv%J4; z2Uq3yEB9qz4XWO&^G>GKIxzMr4Z3Mp{|c(gpy3rTjX+d`zFKBb6X9ie(i#b#VGZwM zgUSo%I2SJHgc6S!6GdZJv3F?di&SOAEwZc{p`ib{RKPYBL!e3gsSE31AS53mS?1Y} z%;-vNUNR9^X;8$Bg1!SxWBNHbxF9(aQ;=MQ9g@Q4k$>*l+V?0E14%ESarN|hf}AHK zr~kxjMf=uWdv-hhLw79V&e{e@znzT;m^5hr8D?_t&#AWMcXxK(zxu(h`@anBeJ|3^ z5?{4j#%&;f$wem3unaGoh;nm8l%!lL7)NuJshd$YGuLuU#9vq&`%x*tkJ%Hs5{+oa z9W?>NhBk&{`8^Pj0AcS~GJs;q+sVHG!HiPQ-lQa8^)toxJPC$Z*D?r?MP%g+HytBE z(X@DP2FEAzM=$U+TeNUS(I6}@noCqF0p`@=GH$@xdiFZU6y;Odbokn0O~ zD6Lx(fZzO;4dfudnteI{YG;1`XFvJb)eoAI3=Ht`%(CFYg8{Q%qM%YDN+BE?8>A_m zw%KJMU=zV+lU+b_GqRE8AxH6Cc zur8ATQtLRz>i$7u8MKeNE{FmK)D)2fUV*qr|6KHWBmb!c^YN_rdK?(Qi1m?3Gxxohq4mRCwEB|;G6*0CcU4Zbiv8>z1*JgG<$oe1sr@!^ZfASTpF8^oV z^)Ir|es04Tx>n!Ex|Z-dz)IT~bkD|v`@{1@3qXT;u7qxmK$uTuVg>+mR-D$uJsCny{)HUP*Pv97I#<&5l}lEWX@b&;Rx?2k zeYPvP3P|zb8X;@MjLh=r?|r;yhUl#MLXG4YLT}4H+hfBSs!g)r%SIe(UBCVrJd%vzr2KQ)C%^KAPk&|Q+D@7Zldo6Qiuou! zA3T|VR}{LqNE8Zv_Yl568=zQn%zpX3X5hDz=EdJ}LsNFoM`r3=a=$w0^gGf1RtqI? zKm22&Sp@`@wV`k{PT{LO0{#CI`w8)fOu=hnr(9jdK+>g3u)9VU7N6)|t(2g{%w>Ur zK3@>O$e;wz&(NQ#5xI#4mB1tQAxk&Gd--g(yYJCmTNy6Dp7Ez-d^Aob=ay;qfR5diQ#RVO-d3$>gF$h|tISRBw;jcENB3 zsALd{?w&w`?R$OMqr{4vh^$}}OGiP&RH%mwmiLu^Tz)z4+pQsRdN05v5Xv?KNcqfv z_p$%!cRuqEeYj)+k3TKjt+```c9{7czy{bu%SZc1ct#@fyIGO~9s?R=`ClJeo?J+GtA zQ6{l_sz9R(bELv8aM(KWM7hqsyra)C@bm2REUPFQC)W1gLsN@EAGRFTQn;;nSSQ{| zU)?OX#qB~2iy~?NXwp_WYj#cizL8W_dGghQ{CsZ22}l5%Y8D8riD?%R22azylUM=Z z#l$KY1_b%XyJ;-{__C6F>0qh=AuMwq2$Wxb90%1>fD!{2&cRHiX#@b|RxcBI~~=`v{F-*#&)pJx=slSqBTnEaY?9uZ1Za;&STD;8EvRhsdmzOKyFU^7hB~(#@ z@!;pP-}s#`eR2I~J6BrhAf?L&n1n@qPKcIhq^<{j(*y^g!rHP<_1cFr#+{;(5)*EJ zUiI*EMSGr8tFC;Y)zt>Q?1|K#SG}jkD^jnkZoj^>Mq~z)`HaZIDaxd<-Yz2S*+_V# zLb)eGT<6o)CeL4KKcO+FUdUCjDoYbjUBv!|kRL3F4lVLR8H*lrbm9aC#fx>7n1%#_ z`S^z*Q{bZBfI^`;`8k3LH_`3R`}P>sm9M^6fONTjDFYJ+T7$Ow;B=mJm}0PF$1E{q!zp_BuS{ItS9oFF&Z&Kosn|{FHq9#n^`hj zcC`XPTs;cS^K%k9#8Z?~owAvQhDGCF2b^8*Dg$j|6+DD#49wN`ZP~6p*I^SxZCg%- zN~f1R=4N)bGH-Zq<@X=FualjSzukGCElP#<;vSMJOPzbyfCeAKF#T-8VM2Hc0YIFh_L<&_$-`Zw8q!1>T9a87o z+6{Vd1F6!N?Rj(;UUR$1_!7V3mu{LMUf$KmX8(#_^L03lXI5n-yEoyuJJsKe3DCRm zt0G?yuD$R6&M!ZxAmg1d>lBtrgnY?p+p@n!+$F4j0yAkjq2rly{VA;uyTqK9dFHtT zhg_%|0M(qF!J)4HAp6n4-a(WdG3Emw$^O|rVU>(E6foZK?~%)$tMkYjh!EL^P1{!F z3}L)bXgmR54xEr8d&IFufmGDb6uYR|lcT~W>4v3n zGukAUv5>$m6CqPr6f(7ZdVv>xl{>a(yQ_VB9{)GHXhv?|#MK%MCujOH9CNY!V~iF7 zXGj%sXZb9Etxm4MI4kAOtd|{gcFtna*kg_it z$X^`;X;rvjx|M^2N?i*r9`B63~9R0KJW&P}{ zVZ%kjt@6)4!d7|!i?D@U-SuKMUr}Q|u?mwv6ZhDD#GIIbVXHf0>kqT=CR?tsBOKp; z%#>9&b*$|)&d=1K-hr>ayoq^g`(IgYf%@BIRu~)smOsLuibLxiE{jM$_p%R#;bui8 zK`7F-EQrJ?ZVx{MTC1ZoYzOH{u&DO!%Kl9s!aUW!!lWecM`s0~YdF4ZM&p zvy&+*9WP(pMPg0O&a|$cIEyX5_`ARIx%(f zxglZM5aQ*|Mz^s&`@(*v;hE5V;Rio_YTt8*j$}wxZ9fyAFJy;U1(uCG@x842aXgv%^i6+~~rm;IyM&}ojJFe(T2-XN7k)VKqK^8J0~)ja2cLcV04axzjWUN0 zd>?6Z7PR(>2P$bGG;~=5yoHM}^GRBA*r5PQA;q+$;PLsuD@WBq?@d*{?B+77{9(>M ze7*WcrK7s7){(uE^HpC_+mydj?ZD2xlF9sJfZ~g!+$K{%_#XQ0;-w5@Y3q>()nljZ zjjDrQh@CtKEExn+L4HAgkJ)3)wD4oz5$Axn4Y&^(2wQ z)ue{jv$~RmZM%Dlzd$Lx^Q#nN^g5WjPR=4?o$MaGLwYGGNSuxo?lQVNF#&Fb0r^j6NK1ti%6 z4BeDRXJV~i47wYYWaLE@<_M%o$9G*P)HZYbj*Oo#*12jMM8oWK*O2_RPZv=HHJ7PuU;C{uZulr80Qcc{eEC62 zkjOjlb9OhA9^uIcSUN!{#-9RKtd1cudQXMTG42yNgfE!wXBkX(h`35O#I-S8JUh2M zmL~fH_si&3v6?IP^e<7H8BXITV)4(6x45vToKM(pM2w23 z)d3xxaMqU(qaxqTS7c04*EX%#^bHmqeS>Hg9Jz^ub^Ko!?BGE}33)(>r5N*T_$8 z>p_8&T+&?{`T9;IF-S zc;C~{9y|ne&#>wM7xPGdI&(um3q&gS^t%D-<_a=dH|oyU9pjZS)1D;-iIj!0G(095 zv3!UmMHTC0vZBi3fQpTfEbCW;lLkK)OaKc&sS@?IGdA{6q}ZBqfKUUe_`R~iCu}8UG~hwbACmVq2(=BND5v&?1w}u z@C+N%T)~7UtQ}=551z-1FYq6EK-ZHV3(T~}a3ALsxipR8`Gbd!9L^3MK0++;%t039 z9V`zYEnjon9brfRnYHiAdA!l2$?tqF{cK{!h%}vr=3=8r&G&4@iA$_50IkZK9HNGV znFX1&ift9uxPai9(2d6+?y-oWbnYYN)P?~{K#`JTa|^!w^)Pk2_RL}u7=%N(r~I-5 zH#FN)kdBW}Yeg1d;5%r6&bg`?HzFc@? z`ZjPF-%7Ws8qjr+sWvk9*KuJA75+H;y^DQ>0nxx=4yf6uG91PwWOJ753{JRG(qe$) z;*4UmbL;4#Bl*#R{m&p(z;l36Y@XpR$<1_wY^OoN;LZk8hU+)aiO=S9_2=^*6Iu=V zGR^AF5E47chVoUX8E0RKjX1H`Q+7L5# zPNYB~(uMTheDv49^u=HO{J-pE4_?BGi{SdoV{k-)m##H|j)1H(afV6o?{pG~c~S0MgcW&vkb+u*2W%Ylj`~#$`^ka%^20tdk+aCS_$MxB=&~Sk*rn@I13?{ zp$3sLRUl~F6v3%8>~GD^BR!~G%dR11o45RrAKSR@fBEQr^f82wQSelGx~JhHl3tFk zowQ^S=HegvU~FjflaSuiw_-g=x%wioJ;%7U*6S3BeReT#&jAiIr!!ov=8epJR{iFm zqCG=pR-FTWe&A5r%*%FO-%!yV%sA16<*;#GXDV$CsY|}&txDb7JrOY^XeLjXU#Wtj z)vSbJB~GZ!>A2)E3>aUNv_(WiglBVM$5`WX(n%3wR!oy#29Py8Mg8cB1f1(t1Gvz9 zJeb01BTjkjUGvJ%iXR!>{aIZv(Mx&+h#E)}G>5t=8syKmU(Ew(ehU z$?o+q!^m&IN!?o+Rk3uHCm$XjVuY7TT}+Q2y9)z}oB5d)$}ghZ$|FR{(v^jsqd8Y$ zSEFY^US+o}Dh?5%q=iHH*uVPb|MS8CZ^4SC{ZBvtV(d>Q>T@%Cz@Aa9-^X!4d)eDF zN6O~IXJJpVp%tAWWpYA%CUS;E&9_(C-Z_NCF#s38*#TE6#;R~QUI!r}XOdgA2(rTk zF`GIWqMGJe6$jVi;*YM3V`BF7=%sZMO=PCwLd&?=&|py#q*`ZN0SI8_2?^y z53^Zh0MDabe}c93dH-C1-klmeRcv)()W;=pWI0GyxIh&}hck%HqD6)LuYTogC;qPT z+7MqDFSkwn2MR7E9?^`>V2LeI#es&=wBlkAvQwjE&81K%!iclP211Xc9YewZi&ea5 zL94pEZ_lPk=*Yg)rb!68c5kcfCPG%247PLub>;}2`yNKfws8!lofFZz$RRMqWW2!xJcmM_a z0 zm51i|8}k8oE0R@DlBINA2BOOAH9<++I`IQ5co&gyBn^TS0 zXasSJXe_WFCeLAq3-@cUGGrbPdFt6g$wXroEp~`pdvBnYoi}^2t?%LF9NI=b1|taF z!)&F-%io5X+LXZSM&%~)LXLK1k*Y%K=mlGCy@<0Y(2CpXIa=((K@F;6x?{KZlqbDc)jqs^sU z3dJ$y&(3SaNgUru9g=y}mzl2*vqlgAikLIq5u3S9F-G+YHgGf3)yniSnpNu@SPbun zek4fJOVs;d4l7ArrD#wxUCxKxhxy zNlubPSw{w#$MaY#Zbb-Jgt{km{{B<0%&ZPP&pK34(l-nWW0c1#d=w0gU(K!LlcOe0 z)#8Xzw*AR({SjWIy?>*|XNVN|Jfqu!Y=$>eJ32=#zB`BGI2Yh?N-?nq6_<|y&R<>{ zW?39+FHTqp`bURNU=e9}D8Q6RoyaQ{DnrMG+-R&IHvbipqdnoV9Th~!f9;UV1C`Y! z_EoSoW0ahf8%WA zn<5FAR989!$ zK~3gt2t=X33f}>d*h=0#N5Ad%DPYoYAlNfY7>t*uBAg6 z?awj_9?y|($ewxnIkQJYdkva+a(EoPU7W@_3k>o8RB;YqxPx()9H}F>*3$iS^oWHO z67=9QbnxltUbrbP3#TX=s%tD{gZl!H?9H)D9X0U4kl5jRd5AOrN^K_9U7Se*L(c#Z z@CrC7E!32|kvEcVacaMTML`~kA+c51c3oNYelMD>e zsB^k~NC1MtC?H5wL*;}v*Rd|L3)v2jY1f2YFB$FGrV0pTJ)x)254@E*Jy*YBCIq5* z897!xHmJ3kc?R{!6pr@}zF%wa}jA_ zG*n>Wad~1f_{IpvB6^W9&`Qc6*bo;n;2A4{QBnBGafx+Kt3xrspMik+oT}W&9r1;~ z!dXGWfNr5~3kFz*z@f18h{II(Ce8`wuRWDWZCg@+vbOCx6>dxvScD74>j`v#1FY05 zD5&l|k8cZb#&`D-l$wCx;M#!VZ;*D)HpW+n=p~M1p||MSLmW-|XWw~<9_qKiSW8eOhfv0#$@~Jt^Qcvp8JBz}?J(hDtAWDZ4yvE_yQ5wk2COMZrkAb$~iegonchC2xZkWZH{JpQP;1?|w;J<%Ei0I0G6 zK#TDg;DL)z-5{LMvL~|afBtYbaA+Vwz-Muk4Z24za8g`hur%6H{uPdxekZ`C_c=TZz`{+g^M#P}MITggr9M?12pXimJO}{8ZwxU4>JgJMsc7~r73^Zj zRuCklj#jj_`dU38GWO@WZx!d}Vh=g*?!pY^QE2VgfGUT($>*6`^xXHHL*yAzA$`Ecl-z^XR5Hb}GkAyhP*4 zzC$l&)$jgcLnKf}V}ssavJm7oH-ZP-#;>ZMtBfWKigkm^rI9z(MxLFag?4@pxoFr4rWcuEi6jjCgnZV^U5WQ{hp|4(#W2@T>WmJK}bIVguY z2>VaJ{iIWt%diSMcJQ4Ji2xN`M|rD0HLm(Fes#wQqC=q}>;I!~|G_67cxbPOIn*2R z#e5O_t=eyAC%6P_7bNbu*L#fGK?hl1VO8u(zr109~hcW~hy zbO)uQI?W?lW)NCk(|=NQDE07@wx7{|jEy=g&;fxNjbLfNH`e$lxeEeE`L zjgQ$T>KW33749&)u7X4;4>Waj!{N|9yLMYev~ZGj0(b$^=jVwUVS1i~h@ooa zMzD0>DCwc@z-p6K9|1+P<29{&rV}j#cSwFCbd>GRHb%IXq$S8a1{gM*E*R zFp$^3^%x2+>9o)c46M7%L$LK{&Tu5MC&yB0t5oT@=zrjA|N5yPy@SJht6o+*M$sU` zt^-d4`Y-J;F!i6%AmX@moQ15Skt8RymJ-7wjBA4z*i9-m+Wj0K7}+Bh@m%agdo2xR zFqE0JTZZ}XCi%}!9fdU^gSh1KyR@Z*9Xt{cC>YuqT0Y`o+&McFg&Uzioegch9{U~w?L)zbd(v~I)Yew!QvsPEM@-*4- zTepWIfRDCXcL2p5o`P0KDyC2|Il@Uy%spE8#6RQ10I7{W@N5rBXTU%L+IIjG2`~!< zD7OsXA#@wqK*|d-IU_s7wzi4zl;I6w0vj&j0#OT!4wTC1wbRN@B_sHL>8U@)yt5uR zZV|b!qW6>JWYd46o+STWSMJ~1~Ixm*992*2Y$IG=J{mI&-)pYax z(4fIAL^8_XqAci57`7ab1f6~dskzk*mq}o_3aMeWjQ7URs(q<{S-&lm7VerZnTAKy z8qyO^0FStULf2nI;#*)7!r18B2oO6hNk* zM=WV7Rc0%r?*P=oLX=#Bh7fsXXgJf7RtN$lWPy@v0T+<*H`Ev&T-81|Kr8G&K!&9c zu~H4X&WRDtN#iu}=C*kwQ6mQha}hIBhv;Z6&hAb zy(@b0{S^!pVueqa<$+x}e`Gsngl>Q64<1UXg2362(U4y~fkA2+WZCxM)L9<%C?nA& zl;lqSlK_|7&V__=YuC7a4!-6NBzCHJsdw?kaym*AGBbXhsbu`@j_a4NN{_v^HPYV~^y#evUQO;3M;)Uz2 zCy|6K1x;%o*cn-!gapr*8;OjLPyGi7IgDHlPTv%ng}v-qk*b-m5edl$HpZbyP*}o1 zhg3e#c1qkPs*oF%V)%8OLZTcIHSv6PsNOg0IM7$DN~0jAOJMlwM+8ECR0AJ&`SHiG zwmhUEG+ue4r%E5U$C3dmr7+52XuV;j2O|Xw$u^LD4=U_DL`P{RVPg7a$2TY%L@v69 zyU+%hk-{ur*c0watG=M7C{{iw;nyyLfYE6N{j--47?U)7F;-b*HDf(oBEQSuPE7rO zkbeZ#HZ+&~zgT*pLH<+8Pe}lb>uwQ-#<(h^cLzoDD;&`` zrM)uCpd*9dX_{Fe_2b+j$xI0ay+WA<@#*`rFlklqj8sLLAsv{FAQ&%Dvf@kp+S1wD zDN+^=XbuiK)Y$lNIJ85_HE2YoLcxnMHn9D8I{eIHPn0G3Kl-(7q5%xtYF%pnmBFGS z7OXp38-8V7L(%}Ee06N<5+E=n0*-U!;N1annu0@x;38<|eJ0l{%2@CyTM=Y)G*BS`FzVHjLI*`LKlR9jy?-x7 zmI4511Pq~z7_@Raw1S=Bd#q1@_$ojYdUR7zM#M_!!%hxOSK9Cd+9>td41+`a@}6y# zt($tH_Rf;l3l;MIgAt-Yay?U6od5>sTBe=g6u2>nj%;9Sw5+40T4??tJeC?)_ynXU zFyX~9`2;b*7&4rhIx&43r&qH)%*8vpW%OK7f8oiG`KS-sfU}`}^rsrsr{5~bF9(bK z)!4}>AXZBl&IyOM;O}U+)KLlwN1(#NEg>Nx35BZ1;US^Sdw8S=?*u}>MO z*(n$!5fPdo30jeJm8g`pr_i6sB)PKZIC_@y7g9n{gkExFg>Yb0g?YM^<4_r@5Jw^} zkCcz?R8WlI^8zrRHn7(YxZ3pJkJpfBE@mmrYypdFfEHu5ZP`SyflF|=oh)jZ}fUE*E z=*)m7;zV8E@P;rVIe`ab!b$}CIo9ssuem5mH0e)8H;d7Kc+Tx+ zcxva`Nt5EG@nEEAW(5nMN(Y!vSO!okKP-$;ox+M#9trn1Pf+FABLNji)sc9-u&LA2 zSuNmyq|xP@+myoavzMfN(UT041YP;D@8x?!XPTlhB4QK{mr4SztDoy7OBYi+ zn)LQ=G0@-uXp{u>e2FxTeZ#}xb@q-jUjax}Y&=4R@~e<$Dbitsgvu5C6l=MsT0N=B zb&U*BV0pL=xXggMc5|fWE14yvX7yi0sobF)59Pd9r3u#+NwKTh>0@5E76an=gck`Ok&rJM=7X9-&3X39c612on1-z ziKS(@${78?#fYRPE+{&2p<1B9LU!`$SFW`z&RR>^9o5#BY)Pe6?#V_+rpFMT@HM%c zdc!T4PlOh(%iL&~RF$xf0O(DHuyDQJ26jJUzo4GuY=KQQ$MX`L)8qwHF`IFmQiLzbV~Bk81>9!l zX(oIXKKYf?AwSvG9oZgi(Ahj$A$;v|ayQ37CcD;7q8ULU9CsE6g!zc>0D30%tPhtY z=8<~-NTotZAF$+c!rmxyxH!8bZ@r_`TH@;iB2-6O&RttnN3~^hz2o`Zw1owQ+YW2v zmTcWK>ZTG8meom7p4Mdjr zxK_ZZjD}Nq5MUn6T1kT~I)kk_(XyoFj?!T3;+DnDfYkc4mPnB;!c%B@2XAiSH0E5P z5hUGoVm`GBL{2{ei1TQ%y#T6m4p3XPJ9vIMPfV&$LsN&vBLP-;)g(K^;P4_JiF3`( z4)BU_#r-Hqkx)s<_;@&&;mo5s+~({Gl1KwWspu4-cbS+)F(KQ~{=Rd(C+b$kdVwbM zvk4XcZhi%UBb*(>^-fCc0y}*OHB+(+1r{*l4A3Y;A~Vuj&|Tk+S``)s9fns>(IszP zRB7QUi}Ux7EXnVpPIgzmm5R#Th(pWoQ27IgLfiUm!OH;Lddd&SHK{@+Q1+{VrsdM6Lm~gL-;nRZ3^I0 zrit+jlY<;F8YK&`?9AVJVgo7IT-14MRb70zAIw(;q*6Sptrn@6#9`+`VRj1V-Ltld zzVQ!#iSuN9Zz+GiZ!1%a7vi{U3)KcJtk{c&jj^qE)2xosZ2&@o5GS4!JN-~|X-$oA z>0?(gH!=TNDb-sSwJaKM;nu+|Z>cO6DI|XJqUII_$cKN@k}c-@^s#pmp@q9O=890` zJe{DCtuRLY;M8X2g{gK82$Z#kd8Gy{I{dhB8g_M^jl(=K&UN8gMSOZGR{MzK4940P ziC&bSqyGT`$^(LG^ghNcHlNM*MeRY+SEF&_lf-TWZSVG#$QIN%Y{h@iFg6ObhuZ%rclR4lNeyEaVL*Aaw_vEImS$lUIgHr&|}*Te3x2OTMVJ zWpPVuy`>o(Y9;p70K{T|$V#mv=^3qy_^kyWu(f4T-n!^yOWyhp60Od60g>j0+q}F`G)n+9BlL!5#^P!^D7VPgQ zC>h~Sgw9L9$KzEt{Kx)wEi5Q4i@if|GD;c-v%Oczdpg zH${Z-uZspwkt3TE)(M}w@azz#=}ou`y#%w%_{>7OcqXb;nd2wSlmeyPokF0<`-|a; z)*JvApKMt?)Y9C7pMX-H!R}NG0GF>V0$j{g*1G5&AY0fL&4tWkk!?3`KL5L(N(OLs0A_77t zIz`0Jd>EZ{P7)O%QAyq&E{d*P$Vz}TVM9nUlz}DbX0cA8_MrLL9b!Nc!Hwjh=o+FD zi14Wj7lL9?nv!wl%5bTQak=9->Z^v;;oFeP?2eYjt?*JyE4r_QF-pK>tyLibBc8IT zLXbL$cC2WvavrNTph_XoyGux$n@jnfj3TA|8K}Bs&M_>33rHmmhy9RkG3Oeg{L08= zoimp-USJaPfuTav*$Q028B1u`f)~`M->3mawwjIr;!2Y?ylDuB%Sn@G38oX^ujZQM zWWV=`u$VE+mqytWU3u_aW=iKm(X~No0eKkPfg~l>mE>d!k>P`Rx?^ag|1Yj_wkU6@ zF6Lj3)n8I+&KGCR+54NbC0df~{W%677_dW7n+c!A{62zT&~j`s#|bn%fcZKCZ?(jn zA{_-FS${jts@RpG>jc9-0WH63in2dlO)x+6nC?F|9HLyQEU*x>e1-o!!OE6w&->6` zFw@5RFYdXZddNetD!EK+W!Q<4g5tMJ^7ob}-^=%~MM?bNax~M8G^eJyqo_R^&_mdu zxXv;LsxTK4dn#!|5(7N??@O(@I%rAd&TPq@*$1*aD@*SDK>nZC`1-&S5}9y*_7Aca zek`?0dWBOF6h1 zBX2|(sbdtB-Q&lg6Ac*r2HVH~xx$eG`Uw10QdaQv7%Id*RmYhlP^c2eV<_lljud8? z47s`xgQNq8kVoD2+v8oY-j3tvS;tBMw!B?!6rvDxU+!TPHC$fSk%)usYp_0Vibf-j z(L95pYvZGU^;dXPD16k<1n%sj);n5DOYY2D7OQfC*Uz;q8bi6XEJ6|eCn0RAB1j{- z)R;}?eIHr|-j&HLfr91y?JP;Vb78VWu;B|SR$*6o z(&%}T_L$u|yA@H(cMM~7tLW(=eAO0yg0iZ7m>PFgtx{OsQFS>m)YA8x+1^{#EGH~v zz|7%{qA}u}6qL1TUTz>U(jlxR?p5mTHvjJHw$^1}n^@$Tyxcx&I*{9JE(h>B!`haq@`-du0$ zHNkd|X+l9&B|`&k!_7Ha_&6E#p6ldPuk&y&2A${iaBkIXCJ!eVDC#1{3XNbn(NcX7 zP9hwxXQu{$nyH$>-;TlXBP#BzL;9G|J4`J!4{a;$BvWK4qUjk$khDitBOTgfmhv>7 zilAn#U~`iZvV9wub-P13k`L!5J&ZF33IYPp&%xl~@I0@7NIeo5!1a{GQUDNMjc=xI z8Bb1shC?&liZ_zu(&N_`F6IqxKl%hat4hxno#qEbCn@dua<{h{tyx~MYh2fa9gVLI zzn;GaRiXG_1rFuO3&G=d{Zbnn!n@F4F9@kO*x+FY`5SZ;om4u2c+?fM@!N|QBKJ;( z6eMQ0$qEifRm`g6tq*U_&Q0%HX{rJX732Fpo zMq%_Ntc6v;kHM zRnBsCJ|yWT*faERQU~O8s02Y%XvjCt9Tlpr+sFV>d4Q&8hyrFAlq#y;{-_i!vOF{% z7^fvCx(XBG^erYMMLLHFjIS2TJVnZxv{TAZt_mO!aCh3Y{e}6QL}(5l2%1@-2!6zDXqrH9ba-6;(1qfN%JDCUycdPTHimL`@Fa$Z(efO$P@|B zo2r9_Nt#T|TO~;viDq^JopxXbt{uGWDGWGjQzOYWBdBM;?&~A3RsRlh<426dV3WzH z>e~nk`?e*;5G-tm#WG@dy42-V@|#gCAy^C@uVr;@;S51H5t%^I-b%MkveMI8OUX$y z6!g0T-Ha^@9T239oIatu{gfmotJhko(zP&rMbcym$&^97VqFK`>Sry((=)mJzfTx) zCQ3MXUYAcm8#b7k@i22j7c#PMdiRYX7Q|#~g4giENQ>o{PN5TIMu)=Yqn(;gvm`r0 zJCH!f>3Gpy1TV^uaFGIKf%cjo8D57kp$a)eLPoaF_tYEo+mYx;k0sn5egS7oBvy35 zoJvSNw`%o>72aCuB(^Y$R=8wD%6GK3d;rs<(cq{_&b*tZDnf&kSNaZ$^Hwt+qr9<+ zD2`}knOKedk*7pZHDu2QJQ$~eDBK9S#y_%EoCv~gTi=ZZr*7`XH3*(mtG=Blp(__G zS(8?!#Ih{t997y4N!(^Vo^JA?-T|>R``WK*w?;U+PN= z<%*;i1>4NjIU?`!GYIW<)}}{+m+0&o3370S;x(EcrWRYeJvn_!e-wLZnW%;)nj`IV z-LHkoEsI3a52ksmG>Wv_b6A~>6?`=q*UigUv&$#+aZsnHOg6bGB`EC9QU-(hy*7Ou zzD=IMjY>2R`jULpr{Kxv^VD}&^V^-D=Z!<^Th*dGL43_q0Eh#0@@-pC?d^nRR*R)i zP!onF_nmwwd*X6RM^H4cJz~GK&Ki|0g7_LW$6-FP(V0-AjnB?z-T_imCVF>$33JnQ zQqg8RO+;<7)}Bqm0Wb`=rpx z056=a^&GQQNc?3YYrR*Jn6s4I@$fd~JuxKW9%O5CG&8bjCcsgZj0)e_c|%uQ(IqAM z?DMPAa8vZ&oTjsFp5B{xqc@Y%DwdTLElC;vb}iXqdc0x>gWwc;#XiFYF(wjt=;F+_81D({$4;?Ld-D^Cz1K!22qWF^N^xSyeFH$gq4 zf0JJ`GL6MyP7sp=>EN)CYk#;2%n2w%^i*Bf4DU3|?o6q@zJ$Mpg3|x1fu6e5Tcs zn}tQ*K{YN+z9iHQ);tOM&?L*C6p1&hax&1>V-sdSlneUO5eU_>P9cAlk;YEIBS;RM zkw%Gcw1M34lL*MByS{OBv6>p zKn!6n5TmD~wmeKsd$0)Y`NMp$^#@Ze`kf?Yt1Q1}d7iuG<4Co3C~M7+KKnL&14PTz z?2KXWMm>_3%drSERE>$1j1ySkK5h`>fNm@mmIjF&&NpwG{jKBrheGy)SF?6bW`Ur=DW+Xp2S6R zGEsr0CNfGmS5iyxePRltS4jehmdB3MJ{9bvdaG1@DXFypO*iZn7*X(Jm>s@o&65= z*(IZ9^$JvoC(!BHwLb?+d;a;;3^5lLTAbb`Q8OzxiJbzB!CD&oKlNgR?;J zmR{p!ac6u_+LHg_9qVCpw5m)DLPX>|9SI0ZmkHRT=%Dy}b$mJW;al*-Dco~(Eg2w{kOhCUy>$3G7+_*`uuf0hAlf*=fJ>~8 zMg9e1M+3X@i}nq(Alq~8CZa}{Uk3mW$<;K+6!b52Oa0p4@VN%4@dX4)9+-07`_1p5 zc3Jz%RTP&i0Cva4K(8(Tn3-|f{n5;xoZ`Lk)fjVqH}Wy7Y`E`bI&a(;%oV#y63;Ty zYho(NfCyZodzJXq*>PoqSWFS3p3c;qN3K-CAY0zuRv!0+TwkdKz0Q}Xm77v+AmGWn zvw~nA76=-Lq&CP;B`8T{@V#{*G#`Y4A9RR;MXgVJ;WaW=ihxBQ{s94$6oK&13IGOK zhp37sYA9zjli=Agwp$PWXrP~2@Y6v9*hlKm4uWUX{~qe!;u!eCB5o&%EI%&Sdza80J#?x3AIq=PfnU%Z>fjcv{;(+;gBKW_+tvdF;dY?o2HEcx8?D%m;1(DL@g0VYox6iiefN*V0IQHbPa9-} z+t`=zQ+QK%nG6fZ%CVwnZU0TfjQQNWVB2t=l66QK(uyk*l9G{egKjX`x#feF&Jxe1 z=0~jphqJ4Ld@Cbcnx?>QlZQpIcYMm#SJ{=3%XE&mwJR@xLXSjtsW3oDF1J}oLZcpG z^e0i7XE7A3UQI(xqJeCE$oG5WIRW zYV)N5w93ScEm@pQOOQ4v9cEh|D^}Unn%2G~K=x18ZtNCu*glMP&SH`Ny?^-B zO?||hgOZwYQUNismo_=0w_90x2hRzhxezZ@ zpxlFXd3lxG@bYewha6gmk)TyYfK3-&(Un(7LJ!O})j%A|18m}1il@#~PM$5|NflG* zPBZYKK@u!Vi=9sy&5swqPBx?V&bY7)VTi+E9!b!ENvulR|MdPNE=np7e#=!!;Hb6K z7hpxJEh5?jahYAm!@i1&g2FPswGU)p`{S$u&BCr}@}}PDC?M-?1UfLigAE#EO!BKL z_!xK2pl%S;qDE>D#)`Xij3*}MMWA>U%^XC5h%Dfz z=Qy{sJFGmziAm$k1y*mG->^NEpj(J zs#*5{(CKA?PF$W)U@dGq$AXwS0A)hJe1c+i1E%n=@;1@5P%`0kD~3HglM@YctPRb_ zNN^bTN-Rlq9wXHjrzWs5#V)w*q0#nNp5Axx#rI^P&loX6N|co3TE2v* zska!%ZgN=3>b1+bWtBhGLsA2XZpE2APXcoP2dj|Sg5+M&9tIwjOye5!uIRGOoa(dr z-mm^Q_f}k5kW-6fHgtQwd2PO$kzzhaAZYf3t}lZG^5DWYm#Dii@wo#>-t)twKP2L? zKBJ|hI9Pby%DBQ}y72<$hQIPAe6fXe?~~vA);FK}?zf(BUE+K8XU~-GER{+g-&@W6k`!w4;gtJ61NWT-C&b9UZ0S=2BBrQ%6U0N5|4t zt2$P$>|C>^YvV>|C{O-J135*L8Jm*sx*!#*JOQy*)iW zTXyd3UALyAbJf!R-Zh;oOZ~m;wsdvw>{&n1zh&Rvo+AUjD?6I|de*F3xpdu{Rh=uF z)^#rJ=~}gZO-Jv>&YfG<^!KjcxS?}j|HdPGx6}rB{?6LIzK*3$eOo&Fd)DmjT{p0E z{l2~pNBX<^dp8`}+p}h6X;nv4=gQ`;bsZbmuiUa>Rqw_%eLd^e5vU6kChR%+rrOizn*RAYa$xBvtt>X>X_3&+F$C14|YXg1jJ3H2_ z>gZb4)U{^i8t$&6sb^Kw`Zb+h9i{bNVaEpEXlYYNGq=2|YyG;78`iI1TIy^rb@9xu z4P8r19ZjW;9Zl=`>$)`?*RSL1w(#rHrrxd%JxfblmXyQSaqir?6DLm8YPBOrj_ljFZ(v|x w@7}%r{r!D?eLLZ*#9mvrY=qSk&%u0vvt|vSvkH#nvtU}d)6Z{u>#eu`e_W=J7XSbN literal 0 HcmV?d00001 diff --git a/src/fred2/addvariabledlg.cpp b/src/fred2/addvariabledlg.cpp new file mode 100644 index 0000000..73f409a --- /dev/null +++ b/src/fred2/addvariabledlg.cpp @@ -0,0 +1,219 @@ +// AddVariableDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "addvariabledlg.h" +#include "sexp.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define NO_RESET_FOCUS 0 +#define RESET_FOCUS 1 + +///////////////////////////////////////////////////////////////////////////// +// CAddVariableDlg dialog + + +CAddVariableDlg::CAddVariableDlg(CWnd* pParent /*=NULL*/) + : CDialog(CAddVariableDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CAddVariableDlg) + m_default_value = _T(""); + m_variable_name = _T(""); + //}}AFX_DATA_INIT +} + + +void CAddVariableDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAddVariableDlg) + DDX_Text(pDX, IDC_ADD_VARIABLE_DEFAULT_VALUE, m_default_value); + DDV_MaxChars(pDX, m_default_value, 31); + DDX_Text(pDX, IDC_ADD_VARIABLE_NAME, m_variable_name); + DDV_MaxChars(pDX, m_variable_name, 31); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CAddVariableDlg, CDialog) + //{{AFX_MSG_MAP(CAddVariableDlg) + ON_BN_CLICKED(IDC_TYPE_NUMBER, OnTypeNumber) + ON_BN_CLICKED(IDC_TYPE_STRING, OnTypeString) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CAddVariableDlg message handlers + +void CAddVariableDlg::OnOK() +{ + // validation name + validate_variable_name(RESET_FOCUS); + + // validate data + if ( m_name_validated ) { + validate_data(RESET_FOCUS); + } + + // both ok, then store results + if ( m_name_validated && m_data_validated ) { +// int sexp_add_variable(char *text, char*, int type); +// char temp_name[32]; +// char temp_value[32]; +// strcpy(temp_name, m_variable_name); +// strcpy(temp_value, m_default_value); + // SEXP_VARIABLE_NUMBER SEXP_VARIABLE_STRING +// int type; +// +// if (m_type_number) { +// type = SEXP_VARIABLE_NUMBER; +// } else { +// type = SEXP_VARIABLE_STRING; +// } + +// m_sexp_var_index = sexp_add_variable(temp_value, temp_name, type); +// this get done for free CDialog::OnOk() UpdateData(TRUE); + m_create = true; + + CDialog::OnOK(); + } +} + +BOOL CAddVariableDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + // TODO: Add extra initialization here + m_variable_name = ""; + m_default_value = ""; + + // Set variable type to number + m_type_number = true; + set_variable_type(); + + m_name_validated = false; + m_data_validated = false; + m_create = false; + + // Send default name and values into dialog box + UpdateData(FALSE); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +bool is_sexp_variable_name(const char* temp_name) +{ + for (int i=0; i 0 length (3) does not already exist +void CAddVariableDlg::validate_variable_name(int set_focus) +{ + CString temp_name; + + CEdit *edit = (CEdit *) GetDlgItem(IDC_ADD_VARIABLE_NAME); + edit->GetWindowText(temp_name); + + // Check if any change and not already in list + if ( stricmp(temp_name, "") ) { + if ( (strlen(temp_name) > 0) && (get_index_sexp_variable_name(LPCTSTR(temp_name)) == -1) ) { //not already in list and length > 0 { + m_name_validated = true; + } else { + // conflicting variable name + if (strlen(temp_name) == 0) { + edit->SetWindowText(""); + } + m_name_validated = false; + if (set_focus == RESET_FOCUS) { + MessageBox("Conflicting variable name"); + edit->SetFocus(); + edit->SetSel(0, -1); + } + } + } else { + // name unchanged from default + m_name_validated = false; + if (set_focus == RESET_FOCUS) { + MessageBox("Invalid variable name"); + edit->SetFocus(); + edit->SetSel(0, -1); + } + } + +} + +void CAddVariableDlg::validate_data(int set_focus) +{ + CString temp_data; + + CEdit *edit = (CEdit *) GetDlgItem(IDC_ADD_VARIABLE_DEFAULT_VALUE); + edit->GetWindowText(temp_data); + + // check for 0 string length + if (strlen(temp_data) == 0) { + m_data_validated = false; + } else { + if (m_type_number) { + // verify valid number + int temp_num = atoi(temp_data); + char buf[TOKEN_LENGTH]; + sprintf(buf, "%d", temp_num); + + if ( stricmp(buf, temp_data) ) { + m_data_validated = false; + } else { + m_data_validated = true; + } + } else { + m_data_validated = true; + } + } + + // Display message and reset focus + if ( (!m_data_validated) && (set_focus == RESET_FOCUS) ) { + MessageBox("Invalid Default Value."); + edit->SetFocus(); + edit->SetSel(0, -1); + } +} + +// Set type to number +void CAddVariableDlg::OnTypeNumber() +{ + m_type_number = true; + set_variable_type(); +} + +// Set type to string +void CAddVariableDlg::OnTypeString() +{ + m_type_number = false; + set_variable_type(); +} + +// Set type check boxes +void CAddVariableDlg::set_variable_type() +{ + + CButton *button_string = (CButton *) GetDlgItem(IDC_TYPE_STRING); + CButton *button_number = (CButton *) GetDlgItem(IDC_TYPE_NUMBER); + + button_number->SetCheck( m_type_number); + button_string->SetCheck(!m_type_number); +} diff --git a/src/fred2/adjustgriddlg.cpp b/src/fred2/adjustgriddlg.cpp new file mode 100644 index 0000000..3d558cf --- /dev/null +++ b/src/fred2/adjustgriddlg.cpp @@ -0,0 +1,161 @@ +/* + * $Logfile: /Freespace2/code/FRED2/AdjustGridDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Editor to allow one to change Fred's grid orientation and position. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 2:59p Dave + * + * 3 8/18/97 10:01p Hoffoss + * Improved dialog by graying out fields that don't have any effect on + * current plane setting. + * + * 2 8/18/97 9:31p Hoffoss + * Added grid adjustment dialog and shield system editor dialog. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "adjustgriddlg.h" +#include "missiongrid.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// adjust_grid_dlg dialog + +adjust_grid_dlg::adjust_grid_dlg(CWnd* pParent /*=NULL*/) + : CDialog(adjust_grid_dlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(adjust_grid_dlg) + m_x = 0; + m_y = 0; + m_z = 0; + //}}AFX_DATA_INIT +} + +void adjust_grid_dlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(adjust_grid_dlg) + DDX_Control(pDX, IDC_SPIN_Z, m_spinz); + DDX_Control(pDX, IDC_SPIN_Y, m_spiny); + DDX_Control(pDX, IDC_SPIN_X, m_spinx); + DDX_Text(pDX, IDC_EDIT_X, m_x); + DDX_Text(pDX, IDC_EDIT_Y, m_y); + DDX_Text(pDX, IDC_EDIT_Z, m_z); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(adjust_grid_dlg, CDialog) + //{{AFX_MSG_MAP(adjust_grid_dlg) + ON_BN_CLICKED(IDC_XY_PLANE, OnXyPlane) + ON_BN_CLICKED(IDC_XZ_PLANE, OnXzPlane) + ON_BN_CLICKED(IDC_YZ_PLANE, OnYzPlane) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// adjust_grid_dlg message handlers + +BOOL adjust_grid_dlg::OnInitDialog() +{ + m_x = (int) The_grid->center.x; + m_y = (int) The_grid->center.y; + m_z = (int) The_grid->center.z; + CDialog::OnInitDialog(); + if (The_grid->gmatrix.uvec.y) { + ((CButton *) GetDlgItem(IDC_XZ_PLANE))->SetCheck(TRUE); + GetDlgItem(IDC_EDIT_X)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_Z)->EnableWindow(FALSE); + + } else if (The_grid->gmatrix.uvec.z) { + ((CButton *) GetDlgItem(IDC_XY_PLANE))->SetCheck(TRUE); + GetDlgItem(IDC_EDIT_X)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_Y)->EnableWindow(FALSE); + + } else { + ((CButton *) GetDlgItem(IDC_YZ_PLANE))->SetCheck(TRUE); + GetDlgItem(IDC_EDIT_Y)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_Z)->EnableWindow(FALSE); + } + + m_spinx.SetRange(99999, -99999); + m_spiny.SetRange(99999, -99999); + m_spinz.SetRange(99999, -99999); + return TRUE; +} + +void adjust_grid_dlg::OnOK() +{ + UpdateData(TRUE); + The_grid->center.x = (float) m_x; + The_grid->center.y = (float) m_y; + The_grid->center.z = (float) m_z; + + if (((CButton *) GetDlgItem(IDC_XY_PLANE)) -> GetCheck()) { + The_grid->gmatrix.fvec = vmd_x_vector; + The_grid->gmatrix.rvec = vmd_y_vector; + + } else if (((CButton *) GetDlgItem(IDC_YZ_PLANE)) -> GetCheck()) { + The_grid->gmatrix.fvec = vmd_y_vector; + The_grid->gmatrix.rvec = vmd_z_vector; + + } else { // XZ plane + The_grid->gmatrix.fvec = vmd_x_vector; + The_grid->gmatrix.rvec = vmd_z_vector; + } + + modify_grid(The_grid); + CDialog::OnOK(); +} + +void adjust_grid_dlg::OnXyPlane() +{ + GetDlgItem(IDC_EDIT_X)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN_X)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_Y)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN_Y)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_Z)->EnableWindow(TRUE); + GetDlgItem(IDC_SPIN_Z)->EnableWindow(TRUE); +} + +void adjust_grid_dlg::OnXzPlane() +{ + GetDlgItem(IDC_EDIT_X)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN_X)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_Y)->EnableWindow(TRUE); + GetDlgItem(IDC_SPIN_Y)->EnableWindow(TRUE); + GetDlgItem(IDC_EDIT_Z)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN_Z)->EnableWindow(FALSE); +} + +void adjust_grid_dlg::OnYzPlane() +{ + GetDlgItem(IDC_EDIT_X)->EnableWindow(TRUE); + GetDlgItem(IDC_SPIN_X)->EnableWindow(TRUE); + GetDlgItem(IDC_EDIT_Y)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN_Y)->EnableWindow(FALSE); + GetDlgItem(IDC_EDIT_Z)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN_Z)->EnableWindow(FALSE); +} diff --git a/src/fred2/asteroideditordlg.cpp b/src/fred2/asteroideditordlg.cpp new file mode 100644 index 0000000..de8b6e5 --- /dev/null +++ b/src/fred2/asteroideditordlg.cpp @@ -0,0 +1,670 @@ +// AsteroidEditorDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "asteroideditordlg.h" +#include "starfield.h" +#include "freddoc.h" +#include "debris.h" // Asteroid stuff. + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define ID_FIELD_MENU 9000 + +// helps in looping over combo boxes +int Dlg_id[3] = {IDC_SHIP_DEBRIS1, IDC_SHIP_DEBRIS2, IDC_SHIP_DEBRIS3}; + + +///////////////////////////////////////////////////////////////////////////// +// asteroid_editor dialog + +asteroid_editor::asteroid_editor(CWnd* pParent /*=NULL*/) + : CDialog(asteroid_editor::IDD, pParent) +{ + int i; + + //{{AFX_DATA_INIT(asteroid_editor) + m_avg_speed = 0; + m_density = 0; + m_enable_asteroids = FALSE; + m_max_x = _T(""); + m_max_y = _T(""); + m_max_z = _T(""); + m_min_x = _T(""); + m_min_y = _T(""); + m_min_z = _T(""); + m_enable_inner_bounds = FALSE; + m_field_type = FT_ACTIVE; + m_box_max_x = _T(""); + m_box_max_y = _T(""); + m_box_max_z = _T(""); + m_box_min_x = _T(""); + m_box_min_y = _T(""); + m_box_min_z = _T(""); + //}}AFX_DATA_INIT + + last_field = -1; + i=0; +// for (i=0; iGetSubMenu(0); + clear_menu(m); + i = 0; //for (i=0; iAppendMenu(MF_ENABLED | MF_STRING, ID_FIELD_MENU + i, str); + //} + + m->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); + if (cur_field != -1) + m->CheckMenuItem(ID_FIELD_MENU + cur_field, MF_BYCOMMAND | MF_CHECKED); + + CDialog::OnInitMenu(pMenu); +} + +BOOL asteroid_editor::OnCommand(WPARAM wParam, LPARAM lParam) +{ + int id; + + id = LOWORD(wParam); + if (id >= ID_FIELD_MENU && id < ID_FIELD_MENU + 1) { //MAX_ASTEROID_FIELDS) { + cur_field = id - ID_FIELD_MENU; + update_init(); + } + + return CDialog::OnCommand(wParam, lParam); +} + +int asteroid_editor::query_modified() +{ + int i; + + for (i=0; i<1 /*MAX_ASTEROID_FIELDS*/; i++) { + if (a_field[i].num_initial_asteroids != Asteroid_field.num_initial_asteroids) + return 1; + if (vm_vec_dist_quick(&a_field[i].vel, &Asteroid_field.vel) == 0.0f) + return 1; + if (a_field[i].min_bound.x != Asteroid_field.min_bound.x) + return 1; + if (a_field[i].min_bound.y != Asteroid_field.min_bound.y) + return 1; + if (a_field[i].min_bound.z != Asteroid_field.min_bound.z) + return 1; + if (a_field[i].max_bound.x != Asteroid_field.max_bound.x) + return 1; + if (a_field[i].max_bound.y != Asteroid_field.max_bound.y) + return 1; + if (a_field[i].max_bound.z != Asteroid_field.max_bound.z) + return 1; + + + if (a_field[i].has_inner_bound != Asteroid_field.has_inner_bound) + return 1; + + if (a_field[i].field_type != Asteroid_field.field_type) + return 1; + + if (a_field[i].has_inner_bound) { + if (a_field[i].inner_max_bound.x != Asteroid_field.inner_max_bound.x) + return 1; + + if (a_field[i].inner_max_bound.y != Asteroid_field.inner_max_bound.y) + return 1; + + if (a_field[i].inner_max_bound.z != Asteroid_field.inner_max_bound.z) + return 1; + + if (a_field[i].inner_min_bound.x != Asteroid_field.inner_min_bound.x) + return 1; + + if (a_field[i].inner_min_bound.y != Asteroid_field.inner_min_bound.y) + return 1; + + if (a_field[i].inner_min_bound.z != Asteroid_field.inner_min_bound.z) + return 1; + } + + } + + return 0; +} + +#define MIN_BOX_THICKNESS 400 +int asteroid_editor::validate_data() +{ + if (!m_enable_asteroids) { + return 1; + } else { + // check x + if (a_field[0].max_bound.x < a_field[0].min_bound.x) { + MessageBox("Asteroid x min is greater than max"); + return 0; + } + + // check y + if (a_field[0].max_bound.y < a_field[0].min_bound.y) { + MessageBox("Asteroid y min is greater than max"); + return 0; + } + + // check z + if (a_field[0].max_bound.z < a_field[0].min_bound.z) { + MessageBox("Asteroid z min is greater than max"); + return 0; + } + + // check if inner bounds enabled + if (a_field[0].has_inner_bound) { + if (a_field[0].inner_max_bound.x < a_field[0].inner_min_bound.x) { + MessageBox("Asteroid x inner min is greater than inner max"); + return 0; + } + + if (a_field[0].inner_max_bound.y < a_field[0].inner_min_bound.y) { + MessageBox("Asteroid y inner min is greater than inner max"); + return 0; + } + + if (a_field[0].inner_max_bound.z < a_field[0].inner_min_bound.z) { + MessageBox("Asteroid z inner min is greater than inner max"); + return 0; + } + + // check x thickness + if (a_field[0].inner_min_bound.x - MIN_BOX_THICKNESS < a_field[0].min_bound.x) { + MessageBox("Asteroid x thickness from outer box to inner box must be > 400"); + return 0; + } + + if (a_field[0].inner_max_bound.x + MIN_BOX_THICKNESS > a_field[0].max_bound.x) { + MessageBox("Asteroid x thickness from outer box to inner box must be > 400"); + return 0; + } + + // check y thickness + if (a_field[0].inner_min_bound.y - MIN_BOX_THICKNESS < a_field[0].min_bound.y) { + MessageBox("Asteroid y thickness from outer box to inner box must be > 400"); + return 0; + } + + if (a_field[0].inner_max_bound.y + MIN_BOX_THICKNESS > a_field[0].max_bound.y) { + MessageBox("Asteroid y thickness from outer box to inner box must be > 400"); + return 0; + } + + // check z thickness + if (a_field[0].inner_min_bound.z - MIN_BOX_THICKNESS < a_field[0].min_bound.z) { + MessageBox("Asteroid z thickness from outer box to inner box must be > 400"); + return 0; + } + + if (a_field[0].inner_max_bound.z + MIN_BOX_THICKNESS > a_field[0].max_bound.z) { + MessageBox("Asteroid z thickness from outer box to inner box must be > 400"); + return 0; + } + } + + // check if passive, ship debris field, at least one speceis selected + if (a_field[0].field_type == FT_PASSIVE) { + if (a_field[0].debris_genre == DG_SHIP) { + if ( (a_field[0].field_debris_type[0] == -1) && (a_field[0].field_debris_type[1] == -1) && (a_field[0].field_debris_type[2] == -1) ) { + MessageBox("You must choose one or more types of ship debris"); + return 0; + } + } + } + + // check at least one asteroid subtype is selected + if (a_field[0].debris_genre == DG_ASTEROID) { + if ( (a_field[0].field_debris_type[0] == -1) && (a_field[0].field_debris_type[1] == -1) && (a_field[0].field_debris_type[2] == -1) ) { + MessageBox("You must choose one or more asteroid subtypes"); + return 0; + } + } + + } + + return 1; +} + + + + +void asteroid_editor::OnOK() +{ + int i; + + update_init(); + if (!validate_data()) { + return; + } + for (i=0; i<1 /*MAX_ASTEROID_FIELDS*/; i++) + Asteroid_field = a_field[i]; + + update_map_window(); + theApp.record_window_data(&Asteroid_wnd_data, this); + CDialog::OnOK(); +} + +BOOL asteroid_editor::OnInitDialog() +{ + cur_field = 0; + CDialog::OnInitDialog(); + update_init(); + theApp.init_window(&Asteroid_wnd_data, this); + + m_density_spin.SetRange(1, MAX_ASTEROIDS); + return TRUE; +} + +void asteroid_editor::update_init() +{ + int num_asteroids, idx, cur_choice; + + UpdateData(TRUE); + if (last_field >= 0) { + // store into temp asteroid field + num_asteroids = a_field[last_field].num_initial_asteroids; + a_field[last_field].num_initial_asteroids = m_enable_asteroids ? m_density : 0; + if (a_field[last_field].num_initial_asteroids < 0) + a_field[last_field].num_initial_asteroids = 0; + + if (a_field[last_field].num_initial_asteroids > MAX_ASTEROIDS) + a_field[last_field].num_initial_asteroids = MAX_ASTEROIDS; + + if (num_asteroids != a_field[last_field].num_initial_asteroids) + set_modified(); + + vector vel_vec = {1.0f, 0.0f, 0.0f}; + vm_vec_scale(&vel_vec, (float) m_avg_speed); + + MODIFY(a_field[last_field].vel.x, vel_vec.x); + MODIFY(a_field[last_field].vel.y, vel_vec.y); + MODIFY(a_field[last_field].vel.z, vel_vec.z); + + MODIFY(a_field[last_field].min_bound.x, (float) atof(m_min_x)); + MODIFY(a_field[last_field].min_bound.y, (float) atof(m_min_y)); + MODIFY(a_field[last_field].min_bound.z, (float) atof(m_min_z)); + MODIFY(a_field[last_field].max_bound.x, (float) atof(m_max_x)); + MODIFY(a_field[last_field].max_bound.y, (float) atof(m_max_y)); + MODIFY(a_field[last_field].max_bound.z, (float) atof(m_max_z)); + + // type of field + MODIFY(a_field[last_field].field_type, m_field_type); + MODIFY(a_field[last_field].debris_genre, m_debris_genre); + if ( (m_field_type == FT_PASSIVE) && (m_debris_genre == DG_SHIP) ) { + // we should have ship debris + for (idx=0; idx<3; idx++) { + // loop over combo boxes, store the item data of the cur selection, -1 in no cur selection + int cur_sel = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetCurSel(); + if (cur_sel != CB_ERR) { + cur_choice = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetItemData(cur_sel); + } else { + cur_choice = -1; + } + MODIFY(a_field[cur_field].field_debris_type[idx], cur_choice); + } + } + + if ( m_debris_genre == DG_ASTEROID ) { + if ( ((CButton *)GetDlgItem(IDC_SUBTYPE1))->GetCheck() == 1) { + cur_choice = 1; + } else { + cur_choice = -1; + } + MODIFY(a_field[cur_field].field_debris_type[0], cur_choice); + + + if ( ((CButton *)GetDlgItem(IDC_SUBTYPE2))->GetCheck() == 1) { + cur_choice = 1; + } else { + cur_choice = -1; + } + MODIFY(a_field[cur_field].field_debris_type[1], cur_choice); + + + if ( ((CButton *)GetDlgItem(IDC_SUBTYPE3))->GetCheck() == 1) { + cur_choice = 1; + } else { + cur_choice = -1; + } + MODIFY(a_field[cur_field].field_debris_type[2], cur_choice); + } + + MODIFY(a_field[last_field].has_inner_bound, m_enable_inner_bounds); + + MODIFY(a_field[last_field].inner_min_bound.x, (float) atof(m_box_min_x)); + MODIFY(a_field[last_field].inner_min_bound.y, (float) atof(m_box_min_y)); + MODIFY(a_field[last_field].inner_min_bound.z, (float) atof(m_box_min_z)); + MODIFY(a_field[last_field].inner_max_bound.x, (float) atof(m_box_max_x)); + MODIFY(a_field[last_field].inner_max_bound.y, (float) atof(m_box_max_y)); + MODIFY(a_field[last_field].inner_max_bound.z, (float) atof(m_box_max_z)); + } + + Assert(cur_field >= 0); + // get from temp asteroid field into class + m_enable_asteroids = a_field[cur_field].num_initial_asteroids ? TRUE : FALSE; + m_enable_inner_bounds = a_field[cur_field].has_inner_bound ? TRUE : FALSE; + m_density = a_field[cur_field].num_initial_asteroids; + if (!m_enable_asteroids) + m_density = 10; + + // set field type + m_field_type = a_field[cur_field].field_type; + m_debris_genre = a_field[cur_field].debris_genre; +// m_debris_species = a_field[cur_field].debris_species; + + m_avg_speed = (int) vm_vec_mag(&a_field[cur_field].vel); + m_min_x.Format("%.1f", a_field[cur_field].min_bound.x); + m_min_y.Format("%.1f", a_field[cur_field].min_bound.y); + m_min_z.Format("%.1f", a_field[cur_field].min_bound.z); + m_max_x.Format("%.1f", a_field[cur_field].max_bound.x); + m_max_y.Format("%.1f", a_field[cur_field].max_bound.y); + m_max_z.Format("%.1f", a_field[cur_field].max_bound.z); + + m_box_min_x.Format("%.1f", a_field[cur_field].inner_min_bound.x); + m_box_min_y.Format("%.1f", a_field[cur_field].inner_min_bound.y); + m_box_min_z.Format("%.1f", a_field[cur_field].inner_min_bound.z); + m_box_max_x.Format("%.1f", a_field[cur_field].inner_max_bound.x); + m_box_max_y.Format("%.1f", a_field[cur_field].inner_max_bound.y); + m_box_max_z.Format("%.1f", a_field[cur_field].inner_max_bound.z); + + // set up combo boxes + int box_index; + int num_field_debris_info = MAX_DEBRIS_TYPES + 1; + + for (idx=0; idxAddString(Field_debris_info[idx].name); + ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS1))->SetItemData(box_index, Field_debris_info[idx].index); + + box_index = ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS2))->AddString(Field_debris_info[idx].name); + ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS2))->SetItemData(box_index, Field_debris_info[idx].index); + + box_index = ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS3))->AddString(Field_debris_info[idx].name); + ((CComboBox*)GetDlgItem(IDC_SHIP_DEBRIS3))->SetItemData(box_index, Field_debris_info[idx].index); + } + + // now delete asteroid data + // search by string Field_debris_info[1-3].name + + for (idx=0; idx<3; idx++) { + box_index = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->FindStringExact(0, Field_debris_info[1].name); // "Asteroid Small" + if (box_index > 0) { + ((CComboBox*)GetDlgItem(Dlg_id[idx]))->DeleteString(box_index); + } + + box_index = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->FindStringExact(0, Field_debris_info[2].name); // "Asteroid Medium" + if (box_index > 0) { + ((CComboBox*)GetDlgItem(Dlg_id[idx]))->DeleteString(box_index); + } + + box_index = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->FindStringExact(0, Field_debris_info[3].name); // "Asteroid Big" + if (box_index > 0) { + ((CComboBox*)GetDlgItem(Dlg_id[idx]))->DeleteString(box_index); + } + } + + // set active debris type for each combo box + int box_count, cur_box_data; + for (idx=0;idx<3; idx++) { + // Only set selection if not "None" + if (a_field[cur_field].field_debris_type[idx] != -1) { + box_count = ((CComboBox*)GetDlgItem(Dlg_id[idx]))->GetCount(); + for (box_index=0; box_indexGetItemData(box_index); + if (cur_box_data == a_field[cur_field].field_debris_type[idx]) { + // set cur sel + ((CComboBox*)GetDlgItem(Dlg_id[idx]))->SetCurSel(box_index); + break; + } + } + } + } + + // set up asteroid subtype checkboxes + ((CButton*)GetDlgItem(IDC_SUBTYPE1))->SetCheck(a_field[cur_field].field_debris_type[0] == 1); + ((CButton*)GetDlgItem(IDC_SUBTYPE2))->SetCheck(a_field[cur_field].field_debris_type[1] == 1); + ((CButton*)GetDlgItem(IDC_SUBTYPE3))->SetCheck(a_field[cur_field].field_debris_type[2] == 1); + + + UpdateData(FALSE); + OnEnableAsteroids(); + + last_field = cur_field; +} + +void asteroid_editor::OnEnableAsteroids() +{ + UpdateData(TRUE); + GetDlgItem(IDC_DENSITY)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_DENSITY_SPIN)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_AVG_SPEED)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_MIN_X)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_MIN_Y)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_MIN_Z)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_MAX_X)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_MAX_Y)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_MAX_Z)->EnableWindow(m_enable_asteroids); + + GetDlgItem(IDC_ENABLE_INNER_BOX)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_PASSIVE_FIELD)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_ACTIVE_FIELD)->EnableWindow(m_enable_asteroids); + + GetDlgItem(IDC_FIELD_SHIP)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_FIELD_ASTEROID)->EnableWindow(m_enable_asteroids); + GetDlgItem(IDC_SHIP_DEBRIS1)->EnableWindow(m_enable_asteroids && (Asteroid_field.debris_genre == DG_SHIP)); + GetDlgItem(IDC_SHIP_DEBRIS2)->EnableWindow(m_enable_asteroids && (Asteroid_field.debris_genre == DG_SHIP)); + GetDlgItem(IDC_SHIP_DEBRIS3)->EnableWindow(m_enable_asteroids && (Asteroid_field.debris_genre == DG_SHIP)); + GetDlgItem(IDC_SUBTYPE1)->EnableWindow(m_enable_asteroids && (Asteroid_field.debris_genre == DG_ASTEROID)); + GetDlgItem(IDC_SUBTYPE2)->EnableWindow(m_enable_asteroids && (Asteroid_field.debris_genre == DG_ASTEROID)); + GetDlgItem(IDC_SUBTYPE3)->EnableWindow(m_enable_asteroids && (Asteroid_field.debris_genre == DG_ASTEROID)); + + OnEnableInnerBox(); + OnEnableField(); +} + +void asteroid_editor::OnCancel() +{ + theApp.record_window_data(&Asteroid_wnd_data, this); + CDialog::OnCancel(); +} + +void asteroid_editor::OnClose() +{ + int z; + + update_init(); + if (query_modified()) { + z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL); + if (z == IDCANCEL) + return; + + if (z == IDYES) { + OnOK(); + return; + } + } + + CDialog::OnClose(); +} + +// enable inner box (asteroid free zone) +// only allowed for active debris field +void asteroid_editor::OnEnableInnerBox() +{ + UpdateData(TRUE); + + GetDlgItem(IDC_INNER_MIN_X)->EnableWindow(m_enable_inner_bounds && m_enable_asteroids); + GetDlgItem(IDC_INNER_MAX_X)->EnableWindow(m_enable_inner_bounds && m_enable_asteroids); + GetDlgItem(IDC_INNER_MIN_Y)->EnableWindow(m_enable_inner_bounds && m_enable_asteroids); + GetDlgItem(IDC_INNER_MAX_Y)->EnableWindow(m_enable_inner_bounds && m_enable_asteroids); + GetDlgItem(IDC_INNER_MIN_Z)->EnableWindow(m_enable_inner_bounds && m_enable_asteroids); + GetDlgItem(IDC_INNER_MAX_Z)->EnableWindow(m_enable_inner_bounds && m_enable_asteroids); +} + +// +void asteroid_editor::OnEnableField() +{ + // set check in active + if (m_enable_asteroids) { + if (m_field_type == FT_ACTIVE) { + OnActiveField(); + } else { + OnPassiveField(); + } + } + + // maybe enable species + if ( m_enable_asteroids && (m_field_type == FT_PASSIVE) && (m_debris_genre == DG_SHIP) ) { + OnFieldShip(); + } +} + + +void asteroid_editor::OnActiveField() +{ + // set field type + m_field_type = FT_ACTIVE; + m_debris_genre = DG_ASTEROID; + + // gray out ship and species + GetDlgItem(IDC_FIELD_SHIP)->EnableWindow(FALSE); + GetDlgItem(IDC_SHIP_DEBRIS1)->EnableWindow(FALSE); + GetDlgItem(IDC_SHIP_DEBRIS2)->EnableWindow(FALSE); + GetDlgItem(IDC_SHIP_DEBRIS3)->EnableWindow(FALSE); + GetDlgItem(IDC_SUBTYPE1)->EnableWindow(TRUE); + GetDlgItem(IDC_SUBTYPE2)->EnableWindow(TRUE); + GetDlgItem(IDC_SUBTYPE3)->EnableWindow(TRUE); + + // force check of asteroid + ((CButton*)GetDlgItem(IDC_FIELD_ASTEROID))->SetCheck(1); + ((CButton*)GetDlgItem(IDC_FIELD_SHIP))->SetCheck(0); + + // force check of active field + ((CButton*)GetDlgItem(IDC_ACTIVE_FIELD))->SetCheck(1); + ((CButton*)GetDlgItem(IDC_PASSIVE_FIELD))->SetCheck(0); + + // enable inner box + GetDlgItem(IDC_ENABLE_INNER_BOX)->EnableWindow(TRUE); +} + +void asteroid_editor::OnPassiveField() +{ + // set field type + m_field_type = FT_PASSIVE; + + // acivate ship + GetDlgItem(IDC_FIELD_SHIP)->EnableWindow(TRUE); + + // maybe activate species + GetDlgItem(IDC_SHIP_DEBRIS1)->EnableWindow(m_debris_genre == DG_SHIP); + GetDlgItem(IDC_SHIP_DEBRIS2)->EnableWindow(m_debris_genre == DG_SHIP); + GetDlgItem(IDC_SHIP_DEBRIS3)->EnableWindow(m_debris_genre == DG_SHIP); + + // maybe activate asteroid subtype + GetDlgItem(IDC_SUBTYPE1)->EnableWindow(m_debris_genre == DG_ASTEROID); + GetDlgItem(IDC_SUBTYPE2)->EnableWindow(m_debris_genre == DG_ASTEROID); + GetDlgItem(IDC_SUBTYPE3)->EnableWindow(m_debris_genre == DG_ASTEROID); + + + // force check of current debris type + ((CButton*)GetDlgItem(IDC_FIELD_ASTEROID))->SetCheck(m_debris_genre == DG_ASTEROID); + ((CButton*)GetDlgItem(IDC_FIELD_SHIP))->SetCheck(m_debris_genre == DG_SHIP); + + // force check of passive field + ((CButton*)GetDlgItem(IDC_ACTIVE_FIELD))->SetCheck(0); + ((CButton*)GetDlgItem(IDC_PASSIVE_FIELD))->SetCheck(1); + + // disable inner box + GetDlgItem(IDC_ENABLE_INNER_BOX)->EnableWindow(FALSE); +} + +void asteroid_editor::OnFieldShip() +{ + // set debris type + m_debris_genre = DG_SHIP; + + GetDlgItem(IDC_SHIP_DEBRIS1)->EnableWindow(TRUE); + GetDlgItem(IDC_SHIP_DEBRIS2)->EnableWindow(TRUE); + GetDlgItem(IDC_SHIP_DEBRIS3)->EnableWindow(TRUE); + + GetDlgItem(IDC_SUBTYPE1)->EnableWindow(FALSE); + GetDlgItem(IDC_SUBTYPE2)->EnableWindow(FALSE); + GetDlgItem(IDC_SUBTYPE3)->EnableWindow(FALSE); + + // force check of ship + ((CButton*)GetDlgItem(IDC_FIELD_ASTEROID))->SetCheck(0); + ((CButton*)GetDlgItem(IDC_FIELD_SHIP))->SetCheck(1); + +} + +void asteroid_editor::OnFieldAsteroid() +{ + // set debris type + m_debris_genre = DG_ASTEROID; + + GetDlgItem(IDC_SHIP_DEBRIS1)->EnableWindow(FALSE); + GetDlgItem(IDC_SHIP_DEBRIS2)->EnableWindow(FALSE); + GetDlgItem(IDC_SHIP_DEBRIS3)->EnableWindow(FALSE); + + + GetDlgItem(IDC_SUBTYPE1)->EnableWindow(TRUE); + GetDlgItem(IDC_SUBTYPE2)->EnableWindow(TRUE); + GetDlgItem(IDC_SUBTYPE3)->EnableWindow(TRUE); + + // force check of asteroid + ((CButton*)GetDlgItem(IDC_FIELD_ASTEROID))->SetCheck(1); + ((CButton*)GetDlgItem(IDC_FIELD_SHIP))->SetCheck(0); +} diff --git a/src/fred2/bgbitmapdlg.cpp b/src/fred2/bgbitmapdlg.cpp new file mode 100644 index 0000000..97add16 --- /dev/null +++ b/src/fred2/bgbitmapdlg.cpp @@ -0,0 +1,880 @@ +/* + * $Logfile: /Freespace2/code/Fred2/BgBitmapDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Background space images manager dialog + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 11 7/02/99 4:30p Dave + * Much more sophisticated lightning support. + * + * 10 6/08/99 5:42p Dave + * Fixed background bitmap division stuff. + * + * 9 6/03/99 6:42p Dave + * + * 8 6/03/99 6:37p Dave + * More TNT fun. Made perspective bitmaps more flexible. + * + * 7 5/09/99 6:00p Dave + * Lots of cool new effects. E3 build tweaks. + * + * 6 4/26/99 8:47p Dave + * Made all pof related nebula stuff customizable through Fred. + * + * 5 4/07/99 6:21p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 4 1/25/99 5:03a Dave + * First run of stealth, AWACS and TAG missile support. New mission type + * :) + * + * 3 11/14/98 5:37p Dave + * Put in support for full nebulas. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 18 4/13/98 10:25p Hoffoss + * Added a flag for subspace missions, and for aboard the Galatea or + * Bastion. + * + * 17 1/14/98 8:56a John + * Removed old nebula palette code that didn't matter what HUD colors were + * loaded. + * + * 16 12/08/97 4:48p Hoffoss + * Moved starfield editor controls to background editor. + * + * 15 11/25/97 4:48p Johnson + * Fixed bug with nebula selection. + * + * 14 11/25/97 11:40a Hoffoss + * Added support for nebula placement editing. + * + * 13 11/23/97 6:06p Hoffoss + * Renamed background bitmap editor to just background editor. + * + * 12 11/21/97 2:55p Hoffoss + * Added Nebula support to Fred. Implemented loading and saving nebula + * info to/from mission files. + * + * 11 6/11/97 3:26p Hoffoss + * Added better verification of bitmap filename to BG editor. + * + * 10 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 9 3/31/97 6:07p Hoffoss + * Fixed several errors, including BG editor not graying fields, BG editor + * not updating image when changed, Removed obsolete data from Weapon + * editor, priority not being saved when missions saved, priority not + * editable in initial orders editor. + * + * 8 3/27/97 2:24p Hoffoss + * Fixed bug in image not updating when new image selected from listbox of + * combo box. + * + * 7 3/21/97 4:24p Hoffoss + * Fixed bug in changing image to an external image file. + * + * 6 3/17/97 1:54p Hoffoss + * fixed bugs in BG editor, and added delete button functionality. + * + * 5 3/12/97 4:33p Hoffoss + * added spin controls to orient editor, light intensity level can be + * specified in BG editor. + * + * 4 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 3 2/04/97 3:09p Hoffoss + * Background bitmap editor implemented fully. + * + * 2 1/30/97 2:24p Hoffoss + * Added remaining mission file structures and implemented load/save of + * them. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "bgbitmapdlg.h" +#include "starfield.h" +#include "bmpman.h" +#include "fredview.h" +#include "freddoc.h" +#include "palman.h" +#include "nebula.h" +#include "neb.h" +#include "neblightning.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// bg_bitmap_dlg dialog + +bg_bitmap_dlg::bg_bitmap_dlg(CWnd* pParent) : CDialog(bg_bitmap_dlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(bg_bitmap_dlg) + m_neb_intensity = _T(""); + m_nebula_color = -1; + m_nebula_index = -1; + m_bank = 0; + m_heading = 0; + m_pitch = 0; + m_subspace = FALSE; + m_fullneb = FALSE; + + m_neb2_texture = 0; + + for(int idx=0; idxAddString(Nebula_colors[i]); + } + + m_slider.SetRange(100, MAX_STARS); + m_slider.SetPos(Num_stars); + sprintf(buf, "%d", Num_stars); + GetDlgItem(IDC_TOTAL)->SetWindowText(buf); + + build_nebfile_list(); + + // setup neb poof names + GetDlgItem(IDC_POOF0)->SetWindowText(Neb2_poof_filenames[0]); + GetDlgItem(IDC_POOF1)->SetWindowText(Neb2_poof_filenames[1]); + GetDlgItem(IDC_POOF2)->SetWindowText(Neb2_poof_filenames[2]); + GetDlgItem(IDC_POOF3)->SetWindowText(Neb2_poof_filenames[3]); + GetDlgItem(IDC_POOF4)->SetWindowText(Neb2_poof_filenames[4]); + GetDlgItem(IDC_POOF5)->SetWindowText(Neb2_poof_filenames[5]); + for(i=0; i 0){ + ((CComboBox*)GetDlgItem(IDC_NEB2_TEXTURE))->AddString(Neb2_bitmap_filenames[i]); + } + } + // if we have a texture selected already + if(strlen(Neb2_texture_name) > 0){ + m_neb2_texture = ((CComboBox*)GetDlgItem(IDC_NEB2_TEXTURE))->SelectString(-1, Neb2_texture_name); + if(m_neb2_texture == CB_ERR){ + ((CComboBox*)GetDlgItem(IDC_NEB2_TEXTURE))->SetCurSel(0); + m_neb2_texture = 0; + } + } else { + ((CComboBox*)GetDlgItem(IDC_NEB2_TEXTURE))->SetCurSel(0); + } + + // setup lightning storm names + ((CComboBox*)GetDlgItem(IDC_NEB2_LIGHTNING))->ResetContent(); + ((CComboBox*)GetDlgItem(IDC_NEB2_LIGHTNING))->AddString(CString("none")); + for(i=0; iAddString(CString(Storm_types[i].name)); + } + ((CComboBox*)GetDlgItem(IDC_NEB2_LIGHTNING))->SelectString(-1, Mission_parse_storm_name); + + // if the nebula intensity wasn't set before - set it now + if(Neb2_awacs < 0.0f){ + m_neb_intensity = CString("3000"); + } else { + char whee[255] = ""; + m_neb_intensity = CString(itoa((int)Neb2_awacs, whee, 10)); + } + + // determine if a full Neb2 is active - load in the full nebula filenames or the partial neb + // filenames + m_fullneb = (The_mission.flags & MISSION_FLAG_FULLNEB) ? 1 : 0; + if(m_fullneb){ + ((CButton*)GetDlgItem(IDC_FULLNEB))->SetCheck(1); + } else { + // since there is no "none" option for the full nebulas + m_nebula_index = Nebula_index + 1; + + m_nebula_color = Mission_palette; + if (Nebula_index < 0){ + GetDlgItem(IDC_NEBCOLOR)->EnableWindow(FALSE); + } + + m_pitch = Nebula_pitch; + m_bank = Nebula_bank; + m_heading = Nebula_heading; + } + + // setup sun and sunglow controls + sun_data_init(); + + // setup bitmap info + bitmap_data_init(); + + // determine if subspace is active + m_subspace = (The_mission.flags & MISSION_FLAG_SUBSPACE) ? 1 : 0; + + UpdateData(FALSE); + OnFullNeb(); + update_data(); + update_map_window(); + set_modified(); +} + +void bg_bitmap_dlg::OnClose() +{ + UpdateData(TRUE); + Mission_palette = m_nebula_color; + + if(m_fullneb){ + The_mission.flags |= MISSION_FLAG_FULLNEB; + Neb2_awacs = (float)atoi((LPCSTR)m_neb_intensity); + + // override dumb values with reasonable ones + if(Neb2_awacs <= 0.00000001f){ + Neb2_awacs = 3000.0f; + } + + // store poof flags + Neb2_poof_flags = 0; + for(int idx=0; idxGetLBText(((CComboBox*)GetDlgItem(IDC_NEB2_LIGHTNING))->GetCurSel(), Mission_parse_storm_name); + + Nebula_pitch = m_pitch; + Nebula_bank = m_bank; + Nebula_heading = m_heading; + if (Nebula_index >= 0){ + nebula_init(Nebula_filenames[Nebula_index], m_pitch, m_bank, m_heading); + } else { + nebula_close(); + } + + if (m_subspace){ + The_mission.flags |= MISSION_FLAG_SUBSPACE; + } else { + The_mission.flags &= ~MISSION_FLAG_SUBSPACE; + } + + // close sun data + sun_data_close(); + + // close bitmap data + bitmap_data_close(); + + theApp.record_window_data(&Bg_wnd_data, this); + delete Bg_bitmap_dialog; + Bg_bitmap_dialog = NULL; +} + +void bg_bitmap_dlg::update_data(int update) +{ + if (update){ + UpdateData(TRUE); + } + + UpdateData(FALSE); +} + +void bg_bitmap_dlg::OnSelchangeNebcolor() +{ + CWaitCursor wait; + + UpdateData(TRUE); + Mission_palette = m_nebula_color; + + + char palette_filename[1024]; + + Assert( Mission_palette >= 0 ); + Assert( Mission_palette <= 98 ); + + sprintf( palette_filename, "gamepalette%d-%02d", 1, Mission_palette+1 ); + + mprintf(( "Loading palette %s\n", palette_filename )); + + palette_load_table(palette_filename); + + Update_window = 1; +} + +void bg_bitmap_dlg::OnSelchangeNebpattern() +{ + CWaitCursor wait; + + UpdateData(TRUE); + + // fullneb indexes differently + Nebula_index = m_nebula_index - 1; + + GetDlgItem(IDC_NEBCOLOR)->EnableWindow(m_nebula_index ? TRUE : FALSE); + if (Nebula_index >= 0){ + nebula_init(Nebula_filenames[Nebula_index], m_pitch, m_bank, m_heading); + } else { + nebula_close(); + } + + Update_window = 1; +} + +void bg_bitmap_dlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar) +{ + char buf[40]; + + CDialog::OnHScroll(nSBCode, nPos, pScrollBar); + + MODIFY(Num_stars, m_slider.GetPos()); + sprintf(buf, "%d", Num_stars); + GetDlgItem(IDC_TOTAL)->SetWindowText(buf); +} + +// when the user toggled the "Full Nebula" button +void bg_bitmap_dlg::OnFullNeb() +{ + // determine what state we're in + UpdateData(TRUE); + if(m_fullneb){ + // enable all fullneb controls + GetDlgItem(IDC_NEB2_INTENSITY)->EnableWindow(TRUE); + GetDlgItem(IDC_NEB2_TEXTURE)->EnableWindow(TRUE); + GetDlgItem(IDC_NEB2_LIGHTNING)->EnableWindow(TRUE); + GetDlgItem(IDC_POOF0)->EnableWindow(TRUE); + GetDlgItem(IDC_POOF1)->EnableWindow(TRUE); + GetDlgItem(IDC_POOF2)->EnableWindow(TRUE); + GetDlgItem(IDC_POOF3)->EnableWindow(TRUE); + GetDlgItem(IDC_POOF4)->EnableWindow(TRUE); + GetDlgItem(IDC_POOF5)->EnableWindow(TRUE); + + // disable non-fullneb controls + GetDlgItem(IDC_NEBPATTERN)->EnableWindow(FALSE); + GetDlgItem(IDC_NEBCOLOR)->EnableWindow(FALSE); + GetDlgItem(IDC_PITCH)->EnableWindow(FALSE); + GetDlgItem(IDC_BANK)->EnableWindow(FALSE); + GetDlgItem(IDC_HEADING)->EnableWindow(FALSE); + + // check all relevant poofs + ((CButton*)GetDlgItem(IDC_POOF0))->SetCheck(FALSE); + if(m_poof[0]){ + ((CButton*)GetDlgItem(IDC_POOF0))->SetCheck(TRUE); + } + ((CButton*)GetDlgItem(IDC_POOF1))->SetCheck(FALSE); + if(m_poof[1]){ + ((CButton*)GetDlgItem(IDC_POOF1))->SetCheck(TRUE); + } + ((CButton*)GetDlgItem(IDC_POOF2))->SetCheck(FALSE); + if(m_poof[2]){ + ((CButton*)GetDlgItem(IDC_POOF2))->SetCheck(TRUE); + } + ((CButton*)GetDlgItem(IDC_POOF3))->SetCheck(FALSE); + if(m_poof[3]){ + ((CButton*)GetDlgItem(IDC_POOF3))->SetCheck(TRUE); + } + ((CButton*)GetDlgItem(IDC_POOF4))->SetCheck(FALSE); + if(m_poof[4]){ + ((CButton*)GetDlgItem(IDC_POOF4))->SetCheck(TRUE); + } + ((CButton*)GetDlgItem(IDC_POOF5))->SetCheck(FALSE); + if(m_poof[5]){ + ((CButton*)GetDlgItem(IDC_POOF5))->SetCheck(TRUE); + } + } else { + // enable all non-fullneb controls + GetDlgItem(IDC_NEBPATTERN)->EnableWindow(TRUE); + GetDlgItem(IDC_NEBCOLOR)->EnableWindow(TRUE); + GetDlgItem(IDC_PITCH)->EnableWindow(TRUE); + GetDlgItem(IDC_BANK)->EnableWindow(TRUE); + GetDlgItem(IDC_HEADING)->EnableWindow(TRUE); + + // disable all fullneb controls + GetDlgItem(IDC_NEB2_INTENSITY)->EnableWindow(FALSE); + GetDlgItem(IDC_NEB2_TEXTURE)->EnableWindow(FALSE); + GetDlgItem(IDC_NEB2_LIGHTNING)->EnableWindow(FALSE); + GetDlgItem(IDC_POOF0)->EnableWindow(FALSE); + GetDlgItem(IDC_POOF1)->EnableWindow(FALSE); + GetDlgItem(IDC_POOF2)->EnableWindow(FALSE); + GetDlgItem(IDC_POOF3)->EnableWindow(FALSE); + GetDlgItem(IDC_POOF4)->EnableWindow(FALSE); + GetDlgItem(IDC_POOF5)->EnableWindow(FALSE); + } +} + +// clear and build the nebula filename list appropriately +void bg_bitmap_dlg::build_nebfile_list() +{ + int i; + CComboBox *box = (CComboBox *) GetDlgItem(IDC_NEBPATTERN); + + // wacky + Assert(box != NULL); + if(box == NULL){ + return; + } + + // clear the box + box->ResetContent(); + + // add all necessary strings + box->AddString("None"); + for (i=0; iAddString(Nebula_filenames[i]); + } + + // select the first elementccombobox + box->SetCurSel(0); + OnSelchangeNebpattern(); +} + +void bg_bitmap_dlg::sun_data_init() +{ + int idx; + + // force the range for pitch bank and heading + ((CSpinButtonCtrl *)GetDlgItem(IDC_SUN1_P_SPIN))->SetRange(0, 359); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SUN1_B_SPIN))->SetRange(0, 359); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SUN1_H_SPIN))->SetRange(0, 359); + + // add all suns to the drop down + idx=0; + while((idx < MAX_STARFIELD_BITMAPS) && (Sun_bitmaps[idx].bitmap != -1)){ + ((CComboBox*)GetDlgItem(IDC_SUN1))->AddString(Sun_bitmaps[idx].filename); + + // next + idx++; + } + + // add all suns by bitmap filename to the list + for(idx=0; idxAddString(Suns[idx].filename); + } + + // if we have at least one item, select it + if(Num_suns > 0){ + ((CListBox*)GetDlgItem(IDC_SUN1_LIST))->SetCurSel(0); + OnSunChange(); + } +} + +void bg_bitmap_dlg::sun_data_close() +{ + // if there is an active sun, save it + sun_data_save_current(); +} + +void bg_bitmap_dlg::sun_data_save_current() +{ + // if we have an active item + if(s_index >= 0){ + // read out of the controls + UpdateData(TRUE); + + // store the data + strcpy(Suns[s_index].filename, (const char*)s_name); + Suns[s_index].scale_x = (float)s_scale; + Suns[s_index].scale_y = 1.0f; + Suns[s_index].div_x = 1; + Suns[s_index].div_y = 1; + Suns[s_index].ang.p = (float)fl_radian(s_pitch); + Suns[s_index].ang.b = (float)fl_radian(s_bank); + Suns[s_index].ang.h = (float)fl_radian(s_heading); + } +} + +void bg_bitmap_dlg::OnSunChange() +{ + int drop_index; + + // save the current sun + sun_data_save_current(); + + // select the new one + s_index = ((CListBox*)GetDlgItem(IDC_SUN1_LIST))->GetCurSel(); + + // setup data + if(s_index >= 0){ + s_pitch = (int)fl_degrees(Suns[s_index].ang.p); + s_bank = (int)fl_degrees(Suns[s_index].ang.b); + s_heading = (int)fl_degrees(Suns[s_index].ang.h); + s_scale = Suns[s_index].scale_x; + s_name = CString(Suns[s_index].filename); + + // stuff back into the controls + UpdateData(FALSE); + + // select the proper item from the dropdown + drop_index = ((CComboBox*)GetDlgItem(IDC_SUN1))->FindString( -1, Suns[s_index].filename ); + Assert(drop_index != CB_ERR); + if(drop_index != CB_ERR){ + ((CComboBox*)GetDlgItem(IDC_SUN1))->SetCurSel(drop_index); + } + } +} + +void bg_bitmap_dlg::OnAddSun() +{ + starfield_bitmap_instance *b; + + // if we've already reached max suns + if(Num_suns >= MAX_STARFIELD_BITMAPS){ + MessageBox("Max suns reached!"); + return; + } + + // save any current + sun_data_save_current(); + + // select the first sun by default + b = &Suns[Num_suns++]; + strcpy(b->filename, Sun_bitmaps[0].filename); + b->scale_x = 1.0f; + b->scale_y = 1.0f; + b->div_x = 1; + b->div_y = 1; + b->ang.p = 0; + b->ang.b = 0; + b->ang.h = 0; + + // add to the listbox and select it + int add_index = ((CListBox*)GetDlgItem(IDC_SUN1_LIST))->AddString(b->filename); + ((CListBox*)GetDlgItem(IDC_SUN1_LIST))->SetCurSel(add_index); + + // call the OnSunChange function to setup all relvant data in the class + OnSunChange(); +} + +void bg_bitmap_dlg::OnDelSun() +{ + int idx; + + // if we don't have an active item + if(s_index < 0){ + return; + } + + // remove the item from the list + ((CListBox*)GetDlgItem(IDC_SUN1_LIST))->DeleteString(s_index); + + // remove it from the list + for(idx=s_index; idxGetCurSel(); + Assert(new_index != CB_ERR); + + // get the new string + if(new_index != CB_ERR){ + ((CComboBox*)GetDlgItem(IDC_SUN1))->GetLBText(new_index, s_name); + + // change the name of the string in the listbox + ((CListBox*)GetDlgItem(IDC_SUN1_LIST))->DeleteString(s_index); + ((CListBox*)GetDlgItem(IDC_SUN1_LIST))->InsertString(s_index, (const char*)s_name); + } +} + +void bg_bitmap_dlg::bitmap_data_init() +{ + int idx; + + // force the range for pitch bank and heading + ((CSpinButtonCtrl *)GetDlgItem(IDC_SBITMAP_P_SPIN))->SetRange(0, 359); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SBITMAP_B_SPIN))->SetRange(0, 359); + ((CSpinButtonCtrl *)GetDlgItem(IDC_SBITMAP_H_SPIN))->SetRange(0, 359); + + // add all suns to the drop down + idx=0; + while((idx < MAX_STARFIELD_BITMAPS) && (Starfield_bitmaps[idx].bitmap != -1)){ + ((CComboBox*)GetDlgItem(IDC_SBITMAP))->AddString(Starfield_bitmaps[idx].filename); + + // next + idx++; + } + + // add all suns by bitmap filename to the list + for(idx=0; idxAddString(Starfield_bitmap_instance[idx].filename); + } + + // if we have at least one item, select it + if(Num_starfield_bitmaps > 0){ + ((CListBox*)GetDlgItem(IDC_SBITMAP_LIST))->SetCurSel(0); + OnBitmapChange(); + } +} + +void bg_bitmap_dlg::bitmap_data_close() +{ + // if there is an active sun, save it + bitmap_data_save_current(); +} + +void bg_bitmap_dlg::bitmap_data_save_current() +{ + // if we have an active item + if(b_index >= 0){ + // read out of the controls + UpdateData(TRUE); + + // store the data + strcpy(Starfield_bitmap_instance[b_index].filename, (const char*)b_name); + Starfield_bitmap_instance[b_index].scale_x = (float)b_scale_x; + Starfield_bitmap_instance[b_index].scale_y = (float)b_scale_y; + Starfield_bitmap_instance[b_index].div_x = b_div_x; + Starfield_bitmap_instance[b_index].div_y = b_div_y; + Starfield_bitmap_instance[b_index].ang.p = (float)fl_radian(b_pitch); + Starfield_bitmap_instance[b_index].ang.b = (float)fl_radian(b_bank); + Starfield_bitmap_instance[b_index].ang.h = (float)fl_radian(b_heading); + } +} + +void bg_bitmap_dlg::OnBitmapChange() +{ + int drop_index; + + // save the current bitmap + bitmap_data_save_current(); + + // select the new one + b_index = ((CListBox*)GetDlgItem(IDC_SBITMAP_LIST))->GetCurSel(); + + // setup data + if(b_index >= 0){ + b_pitch = (int)fl_degrees(Starfield_bitmap_instance[b_index].ang.p); + b_bank = (int)fl_degrees(Starfield_bitmap_instance[b_index].ang.b); + b_heading = (int)fl_degrees(Starfield_bitmap_instance[b_index].ang.h); + b_scale_x = Starfield_bitmap_instance[b_index].scale_x; + b_scale_y = Starfield_bitmap_instance[b_index].scale_y; + b_div_x = Starfield_bitmap_instance[b_index].div_x; + b_div_y = Starfield_bitmap_instance[b_index].div_y; + b_name = CString(Starfield_bitmap_instance[b_index].filename); + + // stuff back into the controls + UpdateData(FALSE); + + // select the proper item from the dropdown + drop_index = ((CComboBox*)GetDlgItem(IDC_SBITMAP))->FindString( -1, Starfield_bitmap_instance[b_index].filename ); + Assert(drop_index != CB_ERR); + if(drop_index != CB_ERR){ + ((CComboBox*)GetDlgItem(IDC_SBITMAP))->SetCurSel(drop_index); + } + } +} + +void bg_bitmap_dlg::OnAddBitmap() +{ + starfield_bitmap_instance *b; + + // if we've already reached max bitmaps + if(Num_starfield_bitmaps >= MAX_STARFIELD_BITMAPS){ + MessageBox("Max starfield bitmaps reached!"); + return; + } + + // save any current + bitmap_data_save_current(); + + // select the first sun by default + b = &Starfield_bitmap_instance[Num_starfield_bitmaps++]; + strcpy(b->filename, Starfield_bitmaps[0].filename); + b->scale_x = 1.0f; + b->scale_y = 1.0f; + b->div_x = 1; + b->div_y = 1; + b->ang.p = 0; + b->ang.b = 0; + b->ang.h = 0; + + // add to the listbox and select it + int add_index = ((CListBox*)GetDlgItem(IDC_SBITMAP_LIST))->AddString(b->filename); + ((CListBox*)GetDlgItem(IDC_SBITMAP_LIST))->SetCurSel(add_index); + + // call the OnBitmapChange function to setup all relvant data in the class + OnBitmapChange(); +} + +void bg_bitmap_dlg::OnDelBitmap() +{ + int idx; + + // if we don't have an active item + if(b_index < 0){ + return; + } + + // remove the item from the list + ((CListBox*)GetDlgItem(IDC_SBITMAP_LIST))->DeleteString(b_index); + + // remove it from the list + for(idx=b_index; idxGetCurSel(); + Assert(new_index != CB_ERR); + + // get the new string + if(new_index != CB_ERR){ + ((CComboBox*)GetDlgItem(IDC_SBITMAP))->GetLBText(new_index, b_name); + + // change the name of the string in the listbox + ((CListBox*)GetDlgItem(IDC_SBITMAP_LIST))->DeleteString(b_index); + ((CListBox*)GetDlgItem(IDC_SBITMAP_LIST))->InsertString(b_index, (const char*)b_name); + } +} diff --git a/src/fred2/briefingeditordlg.cpp b/src/fred2/briefingeditordlg.cpp new file mode 100644 index 0000000..87caa4d --- /dev/null +++ b/src/fred2/briefingeditordlg.cpp @@ -0,0 +1,1432 @@ +/* + * $Logfile: /Freespace2/code/Fred2/BriefingEditorDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Briefing editor dialog box class. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 9 7/19/99 3:01p Dave + * Fixed icons. Added single transport icon. + * + * 8 7/18/99 5:19p Dave + * Jump node icon. Fixed debris fogging. Framerate warning stuff. + * + * 7 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 6 5/20/99 1:46p Andsager + * Add briefing view copy and paste between stages + * + * 5 4/23/99 12:01p Johnson + * Added SIF_HUGE_SHIP + * + * 4 11/30/98 5:32p Dave + * Fixed up Fred support for software mode. + * + * 3 10/16/98 4:28p Andsager + * Fix fred dependency + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 63 5/21/98 4:20p Dave + * Fixed bug with briefing editor when no current stage selected. + * + * 62 5/21/98 12:58a Hoffoss + * Fixed warnings optimized build turned up. + * + * 61 5/14/98 4:47p Hoffoss + * Made it so when you switch to a new team's debriefing (via menu), it + * updates the camera position to what it should be for new briefing + * stage. + * + * 60 4/30/98 8:23p John + * Fixed some bugs with Fred caused by my new cfile code. + * + * 59 4/22/98 9:56a Sandeep + * + * 58 4/20/98 4:40p Hoffoss + * Added a button to 4 editors to play the chosen wave file. + * + * 57 4/16/98 2:49p Johnson + * Fixed initialization for new icons in briefing. + * + * 56 4/07/98 6:32p Dave + * Fixed function I forget to change back. + * + * 55 4/07/98 6:27p Dave + * Implemented a more boiler-plate solution to the multiple team briefing + * problem in this editor. + * + * 54 4/07/98 4:51p Dave + * (Hoffoss) Fixed a boat load of bugs caused by the new change to + * multiple briefings. Allender's code changed to support this in the + * briefing editor wasn't quite correct. + * + * 53 4/06/98 5:37p Hoffoss + * Added sexp tree support to briefings in Fred. + * + * 52 4/06/98 10:43a John + * Fixed bugs with inserting/deleting stages + * + * 51 4/03/98 12:39p Hoffoss + * Changed starting directory for browse buttons in several editors. + * + * 50 4/03/98 11:34a John + * Fixed the stuff I broke in Fred from the new breifing + * + * 49 3/26/98 6:40p Lawrance + * Don't store icon text for briefings + * + * 48 3/21/98 7:36p Lawrance + * Move jump nodes to own lib. + * + * 47 3/19/98 4:24p Hoffoss + * Added remaining support for command brief screen (ANI and WAVE file + * playing). + * + * 46 3/17/98 2:00p Hoffoss + * Added ability to make an icon from a jump node in briefing editor. + * + * 45 2/18/98 6:44p Hoffoss + * Added support for lines between icons in briefings for Fred. + * + * 44 2/09/98 9:25p Allender + * team v team support. multiple pools and breifings + * + * 43 2/04/98 4:31p Allender + * support for multiple briefings and debriefings. Changes to mission + * type (now a bitfield). Bitfield defs for multiplayer modes + * + * 42 1/28/98 7:19p Lawrance + * Get fading/highlighting animations working + * + * 41 12/28/97 5:52p Lawrance + * Add support for debriefing success/fail music. + * + * 40 11/04/97 4:33p Hoffoss + * Made saving keep the current briefing state intact. + * + * 39 11/04/97 11:31a Duncan + * Made make icon button gray if at max icons already. + * + * 38 11/03/97 4:07p Jasen + * Fixed bug in briefing editor caused by changes in TEAM_* defines in the + * past and whoever made these changes failed to update this editor along + * with it. + * + * 37 10/19/97 11:32p Hoffoss + * Added support for briefing cuts in Fred. + * + * 36 10/12/97 11:23p Mike + * About ten fixes/changes in the docking system. + * Also, renamed SIF_REARM_REPAIR to SIF_SUPPORT. + * + * 35 9/30/97 5:56p Hoffoss + * Added music selection combo boxes to Fred. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include +#include "fred.h" +#include "briefingeditordlg.h" +#include "freddoc.h" +#include "missionbriefcommon.h" +#include "missionparse.h" +#include "fredrender.h" +#include "management.h" +#include "linklist.h" +#include "mainfrm.h" +#include "bmpman.h" +#include "eventmusic.h" +#include "starfield.h" +#include "jumpnode.h" +#include "cfile.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define ID_MENU 9000 +#define NAVBUOY_NAME "Terran NavBuoy" + +static int Max_icons_for_lines; + +///////////////////////////////////////////////////////////////////////////// +// briefing_editor_dlg dialog + +briefing_editor_dlg::briefing_editor_dlg(CWnd* pParent /*=NULL*/) + : CDialog(briefing_editor_dlg::IDD, pParent) +{ + int i, z; + + // figure out max icons we can have with lines to each other less than max allowed lines. + // Basically, # lines = (# icons - 1) factorial + Max_icons_for_lines = 0; + do { + i = ++Max_icons_for_lines + 1; + z = 0; + while (--i) + z += i; + + } while (z < MAX_BRIEF_STAGE_LINES); + + //{{AFX_DATA_INIT(briefing_editor_dlg) + m_hilight = FALSE; + m_icon_image = -1; + m_icon_label = _T(""); + m_stage_title = _T(""); + m_text = _T(""); + m_time = _T(""); + m_voice = _T(""); + m_icon_text = _T(""); + m_icon_team = -1; + m_ship_type = -1; + m_change_local = FALSE; + m_id = 0; + m_briefing_music = -1; + m_cut_next = FALSE; + m_cut_prev = FALSE; + m_current_briefing = -1; + //}}AFX_DATA_INIT + m_cur_stage = 0; + m_last_stage = m_cur_icon = m_last_icon = -1; + m_tree.link_modified(&modified); // provide way to indicate trees are modified in dialog + + // copy view initialization + m_copy_view_set = 0; +} + +void briefing_editor_dlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(briefing_editor_dlg) + DDX_Control(pDX, IDC_TREE, m_tree); + DDX_Control(pDX, IDC_LINES, m_lines); + DDX_Check(pDX, IDC_HILIGHT, m_hilight); + DDX_CBIndex(pDX, IDC_ICON_IMAGE, m_icon_image); + DDX_Text(pDX, IDC_ICON_LABEL, m_icon_label); + DDX_Text(pDX, IDC_STAGE_TITLE, m_stage_title); + DDX_Text(pDX, IDC_TEXT, m_text); + DDX_Text(pDX, IDC_TIME, m_time); + DDX_Text(pDX, IDC_VOICE, m_voice); + DDX_Text(pDX, IDC_ICON_TEXT, m_icon_text); + DDX_CBIndex(pDX, IDC_TEAM, m_icon_team); + DDX_CBIndex(pDX, IDC_SHIP_TYPE, m_ship_type); + DDX_Check(pDX, IDC_LOCAL, m_change_local); + DDX_Text(pDX, IDC_ID, m_id); + DDX_CBIndex(pDX, IDC_BRIEFING_MUSIC, m_briefing_music); + DDX_Check(pDX, IDC_CUT_NEXT, m_cut_next); + DDX_Check(pDX, IDC_CUT_PREV, m_cut_prev); + //}}AFX_DATA_MAP + + DDV_MaxChars(pDX, m_text, MAX_BRIEF_LEN - 1); + DDV_MaxChars(pDX, m_voice, MAX_FILENAME_LEN - 1); + DDV_MaxChars(pDX, m_icon_label, MAX_LABEL_LEN - 1); + DDV_MaxChars(pDX, m_icon_text, MAX_ICON_TEXT_LEN - 1); +} + +BEGIN_MESSAGE_MAP(briefing_editor_dlg, CDialog) + //{{AFX_MSG_MAP(briefing_editor_dlg) + ON_WM_CLOSE() + ON_BN_CLICKED(IDC_NEXT, OnNext) + ON_BN_CLICKED(IDC_PREV, OnPrev) + ON_BN_CLICKED(IDC_BROWSE, OnBrowse) + ON_BN_CLICKED(IDC_ADD_STAGE, OnAddStage) + ON_BN_CLICKED(IDC_DELETE_STAGE, OnDeleteStage) + ON_BN_CLICKED(IDC_INSERT_STAGE, OnInsertStage) + ON_BN_CLICKED(IDC_MAKE_ICON, OnMakeIcon) + ON_BN_CLICKED(IDC_DELETE_ICON, OnDeleteIcon) + ON_BN_CLICKED(IDC_GOTO_VIEW, OnGotoView) + ON_BN_CLICKED(IDC_SAVE_VIEW, OnSaveView) + ON_CBN_SELCHANGE(IDC_ICON_IMAGE, OnSelchangeIconImage) + ON_CBN_SELCHANGE(IDC_TEAM, OnSelchangeTeam) + ON_BN_CLICKED(IDC_PROPAGATE_ICONS, OnPropagateIcons) + ON_WM_INITMENU() + ON_BN_CLICKED(IDC_LINES, OnLines) + ON_NOTIFY(NM_RCLICK, IDC_TREE, OnRclickTree) + ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE, OnBeginlabeleditTree) + ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE, OnEndlabeleditTree) + ON_BN_CLICKED(IDC_PLAY, OnPlay) + ON_BN_CLICKED(IDC_COPY_VIEW, OnCopyView) + ON_BN_CLICKED(IDC_PASTE_VIEW, OnPasteView) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// briefing_editor_dlg message handlers + +void briefing_editor_dlg::OnInitMenu(CMenu* pMenu) +{ + int i; + CMenu *m; + + // disable any items we should disable + m = pMenu->GetSubMenu(0); + + // uncheck all menu items + for (i=0; iCheckMenuItem(i, MF_BYPOSITION | MF_UNCHECKED); + + for (i=Num_teams; iEnableMenuItem(i, MF_BYPOSITION | MF_GRAYED); + + // put a check next to the currently selected item + m->CheckMenuItem(m_current_briefing, MF_BYPOSITION | MF_CHECKED ); + + CDialog::OnInitMenu(pMenu); +} + +void briefing_editor_dlg::create() +{ + int i; + CComboBox *box; + + CDialog::Create(IDD); + theApp.init_window(&Briefing_wnd_data, this); + box = (CComboBox *) GetDlgItem(IDC_ICON_IMAGE); + for (i=0; iAddString(Icon_names[i]); + + box = (CComboBox *) GetDlgItem(IDC_TEAM); + for (i=0; iAddString(Team_names[i]); + + box = (CComboBox *) GetDlgItem(IDC_SHIP_TYPE); + for (i=0; iAddString(Ship_info[i].name); + + box = (CComboBox *) GetDlgItem(IDC_BRIEFING_MUSIC); + box->AddString("None"); + for (i=0; iAddString(Spooled_music[i].name); + + m_play_bm.LoadBitmap(IDB_PLAY); + ((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm); + + m_current_briefing = 0; + Briefing = &Briefings[m_current_briefing]; + m_briefing_music = Mission_music[SCORE_BRIEFING] + 1; + UpdateData(FALSE); + update_data(); + OnGotoView(); + update_map_window(); +} + +void briefing_editor_dlg::focus_sexp(int select_sexp_node) +{ + int i, n; + + n = m_tree.select_sexp_node = select_sexp_node; + if (n != -1) { + for (i=0; inum_stages; i++) + if (query_node_in_sexp(n, Briefing->stages[i].formula)) + break; + + if (i < Briefing->num_stages) { + m_cur_stage = i; + update_data(); + GetDlgItem(IDC_TREE) -> SetFocus(); + m_tree.hilite_item(m_tree.select_sexp_node); + } + } +} + +void briefing_editor_dlg::OnOK() +{ +} + +void briefing_editor_dlg::OnCancel() +{ + OnClose(); +} + +void briefing_editor_dlg::OnClose() +{ + int bs, i, j, s, t, dup = 0; + briefing_editor_dlg *ptr; + brief_stage *sp; + + m_cur_stage = -1; + update_data(1); + + for ( bs = 0; bs < Num_teams; bs++ ) { + for (s=0; snum_icons; + for (i=0; iicons[i].id >= 0) && (sp->icons[i].id == sp->icons[j].id)) + dup = 1; + } + } + } + + if (dup) + MessageBox("You have duplicate icons names. You should resolve these.", "Warning"); + + theApp.record_window_data(&Briefing_wnd_data, this); + ptr = Briefing_dialog; + Briefing_dialog = NULL; + delete ptr; +} + +void briefing_editor_dlg::reset_editor() +{ + m_cur_stage = -1; + update_data(0); +} + +// some kind of hackish code to get around the problem if saving (which implicitly loads, +// which implicitly clears all mission info) not affecting the editor's state at save. +void briefing_editor_dlg::save_editor_state() +{ + stage_saved = m_cur_stage; + icon_saved = m_cur_icon; +} + +void briefing_editor_dlg::restore_editor_state() +{ + m_cur_stage = stage_saved; + m_cur_icon = icon_saved; +} + +void briefing_editor_dlg::update_data(int update) +{ + char buf[MAX_LABEL_LEN], buf2[MAX_ICON_TEXT_LEN], buf3[MAX_BRIEF_LEN]; + int i, j, l, lines, count, enable = TRUE, valid = 0, invalid = 0; + object *objp; + brief_stage *ptr = NULL; + + if (update) + UpdateData(TRUE); + + // save off current data before we update over it with new briefing stage/team stuff + Briefing = save_briefing; + + Mission_music[SCORE_BRIEFING] = m_briefing_music - 1; + if (m_last_stage >= 0) { + ptr = &Briefing->stages[m_last_stage]; + deconvert_multiline_string(buf3, m_text, MAX_BRIEF_LEN); + if (stricmp(ptr->new_text, buf3)) + set_modified(); + + strcpy(ptr->new_text, buf3); + MODIFY(ptr->camera_time, atoi(m_time)); + string_copy(ptr->voice, m_voice, MAX_FILENAME_LEN, 1); + i = ptr->flags; + if (m_cut_prev) + i |= BS_BACKWARD_CUT; + else + i &= ~BS_BACKWARD_CUT; + + if (m_cut_next) + i |= BS_FORWARD_CUT; + else + i &= ~BS_FORWARD_CUT; + + MODIFY(ptr->flags, i); + ptr->formula = m_tree.save_tree(); + switch (m_lines.GetCheck()) { + case 1: + // add lines between every pair of 2 marked icons if there isn't one already. + for (i=0; inum_icons - 1; i++) + for (j=i+1; jnum_icons; j++) { + if ( icon_marked[i] && icon_marked[j] ) { + for (l=0; lnum_lines; l++) + if ( ((ptr->lines[l].start_icon == i) && (ptr->lines[l].end_icon == j)) || ((ptr->lines[l].start_icon == j) && (ptr->lines[l].end_icon == i)) ) + break; + + if ((l == ptr->num_lines) && (l < MAX_BRIEF_STAGE_LINES)) { + ptr->lines[l].start_icon = i; + ptr->lines[l].end_icon = j; + ptr->num_lines++; + } + } + } + + break; + + case 0: + // remove all existing lines between any 2 marked icons + i = ptr->num_lines; + while (i--) + if ( icon_marked[ptr->lines[i].start_icon] && icon_marked[ptr->lines[i].end_icon] ) { + ptr->num_lines--; + for (l=i; lnum_lines; l++) + ptr->lines[l] = ptr->lines[l + 1]; + } + + break; + } + + if (m_last_icon >= 0) { + valid = (m_id != ptr->icons[m_last_icon].id); + if (m_id >= 0) { + if (valid && !m_change_local) { + for (i=m_last_stage+1; inum_stages; i++) { + if (find_icon(m_id, i) >= 0) { + char msg[1024]; + + valid = 0; + sprintf(msg, "Icon ID #%d is already used in a later stage. You can only\n" + "change to that ID locally. Icon ID has been reset back to %d", m_id, ptr->icons[m_last_icon].id); + + m_id = ptr->icons[m_last_icon].id; + MessageBox(msg); + break; + } + } + } + + for (i=0; inum_icons; i++) + if ((i != m_last_icon) && (ptr->icons[i].id == m_id)) { + char msg[1024]; + + sprintf(msg, "Icon ID #%d is already used in this stage. Icon ID has been reset back to %d", + m_id, ptr->icons[m_last_icon].id); + + m_id = ptr->icons[m_last_icon].id; + MessageBox(msg); + break; + } + + if (valid && !m_change_local) { + set_modified(); + reset_icon_loop(m_last_stage); + while (get_next_icon(ptr->icons[m_last_icon].id)) + iconp->id = m_id; + } + } + + ptr->icons[m_last_icon].id = m_id; + string_copy(buf, m_icon_label, MAX_LABEL_LEN); + if (stricmp(ptr->icons[m_last_icon].label, buf) && !m_change_local) { + set_modified(); + reset_icon_loop(m_last_stage); + while (get_next_icon(m_id)) + strcpy(iconp->label, buf); + } + + strcpy(ptr->icons[m_last_icon].label, buf); + if ( m_hilight ) + ptr->icons[m_last_icon].flags |= BI_HIGHLIGHT; + else + ptr->icons[m_last_icon].flags &= ~BI_HIGHLIGHT; + + if ((ptr->icons[m_last_icon].type != m_icon_image) && !m_change_local) { + set_modified(); + reset_icon_loop(m_last_stage); + while (get_next_icon(m_id)) + iconp->type = m_icon_image; + } + + ptr->icons[m_last_icon].type = m_icon_image; + if ((ptr->icons[m_last_icon].team != (1 << m_icon_team)) && !m_change_local) { + set_modified(); + reset_icon_loop(m_last_stage); + while (get_next_icon(m_id)) + iconp->team = (1 << m_icon_team); + } + + ptr->icons[m_last_icon].team = (1 << m_icon_team); + if ((ptr->icons[m_last_icon].ship_class != m_ship_type) && !m_change_local) { + set_modified(); + reset_icon_loop(m_last_stage); + while (get_next_icon(m_id)) + iconp->ship_class = m_ship_type; + } + + MODIFY(ptr->icons[m_last_icon].ship_class, m_ship_type); + deconvert_multiline_string(buf2, m_icon_text, MAX_ICON_TEXT_LEN); +/* + if (stricmp(ptr->icons[m_last_icon].text, buf2) && !m_change_local) { + set_modified(); + reset_icon_loop(m_last_stage); + while (get_next_icon(m_id)) + strcpy(iconp->text, buf2); + } + + strcpy(ptr->icons[m_last_icon].text, buf2); +*/ + } + } + + if (!::IsWindow(m_hWnd)) + return; + + // set briefing pointer to correct team + Briefing = &Briefings[m_current_briefing]; + + if ((m_cur_stage >= 0) && (m_cur_stage < Briefing->num_stages)) { + ptr = &Briefing->stages[m_cur_stage]; + m_stage_title.Format("Stage %d of %d", m_cur_stage + 1, Briefing->num_stages); + m_text = convert_multiline_string(ptr->new_text); + m_time.Format("%d", ptr->camera_time); + m_voice = ptr->voice; + m_cut_prev = (ptr->flags & BS_BACKWARD_CUT) ? 1 : 0; + m_cut_next = (ptr->flags & BS_FORWARD_CUT) ? 1 : 0; + m_tree.load_tree(ptr->formula); + + } else { + m_stage_title = _T("No stages"); + m_text = _T(""); + m_time = _T(""); + m_voice = _T(""); + m_cut_prev = m_cut_next = 0; + m_tree.clear_tree(); + enable = FALSE; + m_cur_stage = -1; + } + + if (m_cur_stage == Briefing->num_stages - 1) + GetDlgItem(IDC_NEXT) -> EnableWindow(FALSE); + else + GetDlgItem(IDC_NEXT) -> EnableWindow(enable); + + if (m_cur_stage) + GetDlgItem(IDC_PREV) -> EnableWindow(enable); + else + GetDlgItem(IDC_PREV) -> EnableWindow(FALSE); + + if (Briefing->num_stages >= MAX_BRIEF_STAGES) + GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(FALSE); + else + GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(TRUE); + + if (Briefing->num_stages) { + GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(enable); + GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(enable); + } else { + GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(FALSE); + GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(FALSE); + } + + GetDlgItem(IDC_TIME) -> EnableWindow(enable); + GetDlgItem(IDC_VOICE) -> EnableWindow(enable); + GetDlgItem(IDC_BROWSE) -> EnableWindow(enable); + GetDlgItem(IDC_TEXT) -> EnableWindow(enable); + GetDlgItem(IDC_SAVE_VIEW) -> EnableWindow(enable); + GetDlgItem(IDC_GOTO_VIEW) -> EnableWindow(enable); + GetDlgItem(IDC_CUT_PREV) -> EnableWindow(enable); + GetDlgItem(IDC_CUT_NEXT) -> EnableWindow(enable); + GetDlgItem(IDC_TREE) -> EnableWindow(enable); + GetDlgItem(IDC_PLAY) -> EnableWindow(enable); + + if ((m_cur_stage >= 0) && (m_cur_icon >= 0) && (m_cur_icon < ptr->num_icons)) { + m_hilight = (ptr->icons[m_cur_icon].flags & BI_HIGHLIGHT)?1:0; + m_icon_image = ptr->icons[m_cur_icon].type; + m_icon_team = bitmask_2_bitnum(ptr->icons[m_cur_icon].team); + m_icon_label = ptr->icons[m_cur_icon].label; + m_ship_type = ptr->icons[m_cur_icon].ship_class; +// m_icon_text = convert_multiline_string(ptr->icons[m_cur_icon].text); + m_id = ptr->icons[m_cur_icon].id; + enable = TRUE; + + } else { + m_hilight = FALSE; + m_icon_image = -1; + m_icon_team = -1; + m_ship_type = -1; + m_icon_label = _T(""); + m_cur_icon = -1; + m_id = 0; + enable = FALSE; + } + + GetDlgItem(IDC_ICON_TEXT) -> EnableWindow(enable); + GetDlgItem(IDC_ICON_LABEL) -> EnableWindow(enable); + GetDlgItem(IDC_ICON_IMAGE) -> EnableWindow(enable); + GetDlgItem(IDC_SHIP_TYPE) -> EnableWindow(enable); + GetDlgItem(IDC_HILIGHT) -> EnableWindow(enable); + GetDlgItem(IDC_TEAM) -> EnableWindow(enable); + GetDlgItem(IDC_ID) -> EnableWindow(enable); + GetDlgItem(IDC_DELETE_ICON) -> EnableWindow(enable); + + valid = invalid = 0; + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) { + if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START) || (objp->type == OBJ_WAYPOINT) || (objp->type == OBJ_JUMP_NODE)) + valid = 1; + else + invalid = 1; + } + + objp = GET_NEXT(objp); + } + + if (m_cur_stage >= 0) + ptr = &Briefing->stages[m_cur_stage]; + + if (valid && !invalid && (m_cur_stage >= 0) && (ptr->num_icons < MAX_STAGE_ICONS)) + GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(TRUE); + else + GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(FALSE); + + if (m_cur_stage >= 0) + for (i=0; inum_icons; i++) + icon_marked[i] = 0; + + valid = invalid = 0; + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) { + if (objp->type == OBJ_POINT) { + valid++; + icon_marked[objp->instance] = 1; + + } else + invalid++; + } + + objp = GET_NEXT(objp); + } + + if (valid && !invalid && (m_cur_stage >= 0)) + GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(TRUE); + else + GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(FALSE); + + count = 0; + lines = 1; // default lines checkbox to checked + + if (m_cur_stage >= 0) { + for (i=0; inum_lines; i++) + line_marked[i] = 0; + + // go through and locate all lines between marked icons + for (i=0; inum_icons - 1; i++) + for (j=i+1; jnum_icons; j++) { + if ( icon_marked[i] && icon_marked[j] ) { + for (l=0; lnum_lines; l++) + if ( ((ptr->lines[l].start_icon == i) && (ptr->lines[l].end_icon == j)) || ((ptr->lines[l].start_icon == j) && (ptr->lines[l].end_icon == i)) ) { + line_marked[l] = 1; + count++; // track number of marked lines (lines between 2 icons that are both marked) + break; + } + + // at least 1 line missing between 2 marked icons, so use mixed state + if (l == ptr->num_lines) + lines = 2; + } + } + } + + // not even 1 line between any 2 marked icons? Set checkbox to unchecked. + if (!count) + lines = 0; + + i = 0; + if (m_cur_stage >= 0){ + i = calc_num_lines_for_icons(valid) + ptr->num_lines - count; + } + + if ((valid > 1) && !invalid && (m_cur_stage >= 0) && (i <= MAX_BRIEF_STAGE_LINES)) + GetDlgItem(IDC_LINES) -> EnableWindow(TRUE); + else + GetDlgItem(IDC_LINES) -> EnableWindow(FALSE); + + m_lines.SetCheck(lines); + + UpdateData(FALSE); + if ((m_last_stage != m_cur_stage) || (Briefing != save_briefing)) { + if (m_last_stage >= 0) { + for (i=0; istages[m_last_stage].num_icons; i++) { + // save positions of all icons, in case they have moved + save_briefing->stages[m_last_stage].icons[i].pos = Objects[icon_obj[i]].pos; + // release objects being used by last stage + obj_delete(icon_obj[i]); + } + } + + if (m_cur_stage >= 0) { + for (i=0; inum_icons; i++) { + // create an object for each icon for display/manipulation purposes + icon_obj[i] = obj_create(OBJ_POINT, -1, i, NULL, &ptr->icons[i].pos, 0.0f, OF_RENDERS); + } + + obj_merge_created_list(); + } + + m_last_stage = m_cur_stage; + } + + m_last_icon = m_cur_icon; + Update_window = 1; + save_briefing = Briefing; +} + +// Given a number of icons, figure out how many lines would be required to connect each one +// with all of the others. +// +int briefing_editor_dlg::calc_num_lines_for_icons(int num) +{ + int lines = 0; + + if (num < 2) + return 0; + + // Basically, this is # lines = (# icons - 1) factorial + while (--num) + lines += num; + + return lines; +} + +void briefing_editor_dlg::OnNext() +{ + m_cur_stage++; + m_cur_icon = -1; + update_data(); + OnGotoView(); +} + +void briefing_editor_dlg::OnPrev() +{ + m_cur_stage--; + m_cur_icon = -1; + update_data(); + OnGotoView(); +} + +void briefing_editor_dlg::OnBrowse() +{ + int z; + CString name; + + UpdateData(TRUE); + + if (The_mission.game_type & MISSION_TYPE_TRAINING) + z = cfile_push_chdir(CF_TYPE_VOICE_TRAINING); + else + z = cfile_push_chdir(CF_TYPE_VOICE_BRIEFINGS); + + CFileDialog dlg(TRUE, "wav", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, + "Wave Files (*.wav)|*.wav||"); + + if (dlg.DoModal() == IDOK) { + m_voice = dlg.GetFileName(); + UpdateData(FALSE); + } + + if (!z) + cfile_pop_dir(); +} + +void briefing_editor_dlg::OnAddStage() +{ + int i; + + if (Briefing->num_stages >= MAX_BRIEF_STAGES) + return; + + m_cur_stage = i = Briefing->num_stages++; + copy_stage(i - 1, i); + update_data(1); +} + +void briefing_editor_dlg::OnDeleteStage() +{ + int i, z; + + if (m_cur_stage < 0) + return; + + Assert(Briefing->num_stages); + z = m_cur_stage; + m_cur_stage = -1; + update_data(1); + for (i=z+1; inum_stages; i++) { + copy_stage(i, i-1); + } + + Briefing->num_stages--; + m_cur_stage = z; + if (m_cur_stage >= Briefing->num_stages) + m_cur_stage = Briefing->num_stages - 1; + + update_data(0); +} + +void briefing_editor_dlg::draw_icon(object *objp) +{ + if (m_cur_stage < 0) + return; + + brief_render_icon(m_cur_stage, objp->instance, 1.0f/30.0f, objp->flags & OF_MARKED, + (float) True_rw / BRIEF_GRID_W, (float) True_rh / BRIEF_GRID_H); +} + +void briefing_editor_dlg::batch_render() +{ + int i, num_lines; + + + if (m_cur_stage < 0) + return; + + num_lines = Briefing->stages[m_cur_stage].num_lines; + for (i=0; inum_stages >= MAX_BRIEF_STAGES) + return; + + if (!Briefing->num_stages) { + OnAddStage(); + return; + } + + z = m_cur_stage; + m_cur_stage = -1; + update_data(1); + for (i=Briefing->num_stages; i>z; i--) { + copy_stage(i-1, i); + } + + Briefing->num_stages++; + copy_stage(z, z + 1); + m_cur_stage = z; + update_data(0); +} + +void briefing_editor_dlg::copy_stage(int from, int to) +{ + if ((from < 0) || (from >= Briefing->num_stages)) { + strcpy(Briefing->stages[to].new_text, ""); + strcpy(Briefing->stages[to].voice, "none.wav"); + Briefing->stages[to].camera_pos = view_pos; + Briefing->stages[to].camera_orient = view_orient; + Briefing->stages[to].camera_time = 500; + Briefing->stages[to].num_icons = 0; + Briefing->stages[to].formula = Locked_sexp_true; + return; + } + + // Copy all the data in the stage structure. + strcpy( Briefing->stages[to].new_text, Briefing->stages[from].new_text ); + strcpy( Briefing->stages[to].voice, Briefing->stages[from].voice ); + Briefing->stages[to].camera_pos = Briefing->stages[from].camera_pos; + Briefing->stages[to].camera_orient = Briefing->stages[from].camera_orient; + Briefing->stages[to].camera_time = Briefing->stages[from].camera_time; + Briefing->stages[to].flags = Briefing->stages[from].flags; + Briefing->stages[to].num_icons = Briefing->stages[from].num_icons; + Briefing->stages[to].num_lines = Briefing->stages[from].num_lines; + Briefing->stages[to].formula = Briefing->stages[from].formula; + + memmove( Briefing->stages[to].icons, Briefing->stages[from].icons, sizeof(brief_icon)*MAX_STAGE_ICONS ); + memmove( Briefing->stages[to].lines, Briefing->stages[from].lines, sizeof(brief_line)*MAX_BRIEF_STAGE_LINES ); +} + +void briefing_editor_dlg::update_positions() +{ + int i, s, z; + vector v1, v2; + + if (m_cur_stage < 0) + return; + + for (i=0; istages[m_cur_stage].num_icons; i++) { + v1 = Briefing->stages[m_cur_stage].icons[i].pos; + v2 = Objects[icon_obj[i]].pos; + if ((v1.x != v2.x) || (v1.y != v2.y) || (v1.z != v2.z)) { + Briefing->stages[m_cur_stage].icons[i].pos = Objects[icon_obj[i]].pos; + if (!m_change_local) // propagate changes through rest of stages.. + for (s=m_cur_stage+1; snum_stages; s++) { + z = find_icon(Briefing->stages[m_cur_stage].icons[i].id, s); + if (z >= 0) + Briefing->stages[s].icons[z].pos = Objects[icon_obj[i]].pos; + } + } + } +} + +void briefing_editor_dlg::OnMakeIcon() +{ + char *name; + int z, len, team, ship, waypoint, jump_node, count = -1; + int cargo = 0, cargo_count = 0, freighter_count = 0; + object *ptr; + vector min, max, pos; + brief_icon *iconp; + + if (Briefing->stages[m_cur_stage].num_icons >= MAX_STAGE_ICONS) + return; + + m_cur_icon = Briefing->stages[m_cur_stage].num_icons++; + iconp = &Briefing->stages[m_cur_stage].icons[m_cur_icon]; + ship = waypoint = jump_node = -1; + team = TEAM_FRIENDLY; + + vm_vec_make(&min, 9e19f, 9e19f, 9e19f); + vm_vec_make(&max, -9e19f, -9e19f, -9e19f); + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->flags & OF_MARKED) { + if (ptr->pos.x < min.x) + min.x = ptr->pos.x; + if (ptr->pos.x > max.x) + max.x = ptr->pos.x; + if (ptr->pos.y < min.y) + min.y = ptr->pos.y; + if (ptr->pos.y > max.y) + max.y = ptr->pos.y; + if (ptr->pos.z < min.z) + min.z = ptr->pos.z; + if (ptr->pos.z > max.z) + max.z = ptr->pos.z; + + switch (ptr->type) { + case OBJ_SHIP: + case OBJ_START: + ship = ptr->instance; + break; + + case OBJ_WAYPOINT: + waypoint = ptr->instance; + break; + + case OBJ_JUMP_NODE: + jump_node = ptr->instance; + break; + + default: + Int3(); + } + + if (ship >= 0) { + team = Ships[ship].team; + + z = ship_query_general_type(ship); + if (z == SHIP_TYPE_CARGO) + cargo_count++; + + if (z == SHIP_TYPE_FREIGHTER) + { + z = Ai_info[Ships[ship].ai_index].dock_objnum; + if (z) { // docked with anything? + if ((Objects[z].flags & OF_MARKED) && (Objects[z].type == OBJ_SHIP)) { + if (ship_query_general_type(Objects[z].instance) == SHIP_TYPE_CARGO) + freighter_count++; + } + } + } + } + + count++; + } + + ptr = GET_NEXT(ptr); + } + + if (cargo_count && cargo_count == freighter_count) + cargo = 1; + + vm_vec_avg(&pos, &min, &max); + if (ship >= 0) + name = Ships[ship].ship_name; + else if (waypoint >= 0) + name = Waypoint_lists[waypoint / 65536].name; + else if (jump_node >= 0) + name = Jump_nodes[jump_node].name; + else + return; + + len = strlen(name); + if (len >= MAX_LABEL_LEN - 1) + len = MAX_LABEL_LEN - 1; + + strncpy(iconp->label, name, len); + iconp->label[len] = 0; +// iconp->text[0] = 0; + iconp->type = 0; + iconp->team = team; + iconp->pos = pos; + iconp->flags = 0; + iconp->id = Cur_brief_id++; + if (ship >= 0) { + iconp->ship_class = Ships[ship].ship_info_index; + switch (Ship_info[Ships[ship].ship_info_index].flags & SIF_ALL_SHIP_TYPES) { + case SIF_KNOSSOS_DEVICE: + iconp->type = ICON_KNOSSOS_DEVICE; + break; + + case SIF_CORVETTE: + iconp->type = ICON_CORVETTE; + break; + + case SIF_GAS_MINER: + iconp->type = ICON_GAS_MINER; + break; + + case SIF_SUPERCAP: + iconp->type = ICON_SUPERCAP; + break; + + case SIF_SENTRYGUN: + iconp->type = ICON_SENTRYGUN; + break; + + case SIF_AWACS: + iconp->type = ICON_AWACS; + break; + + case SIF_CARGO: + if (cargo) + iconp->type = (count == 1) ? ICON_FREIGHTER_WITH_CARGO : ICON_FREIGHTER_WING_WITH_CARGO; + else + iconp->type = count ? ICON_CARGO_WING : ICON_CARGO; + + break; + + case SIF_SUPPORT: + iconp->type = ICON_SUPPORT_SHIP; + break; + + case SIF_FIGHTER: + iconp->type = count ? ICON_FIGHTER_WING : ICON_FIGHTER; + break; + + case SIF_BOMBER: + iconp->type = count ? ICON_BOMBER_WING : ICON_BOMBER; + break; + + case SIF_FREIGHTER: + if (cargo) + iconp->type = (count == 1) ? ICON_FREIGHTER_WITH_CARGO : ICON_FREIGHTER_WING_WITH_CARGO; + else + iconp->type = count ? ICON_FREIGHTER_WING_NO_CARGO : ICON_FREIGHTER_NO_CARGO; + + break; + + case SIF_CRUISER: + iconp->type = count ? ICON_CRUISER_WING : ICON_CRUISER; + break; + + case SIF_TRANSPORT: + iconp->type = count ? ICON_TRANSPORT_WING : ICON_TRANSPORT; + break; + + case SIF_CAPITAL: + case SIF_DRYDOCK: + iconp->type = ICON_CAPITAL; + break; + + default: + if (Ships[ship].ship_info_index == ship_info_lookup(NAVBUOY_NAME)) + iconp->type = ICON_WAYPOINT; + else + iconp->type = ICON_ASTEROID_FIELD; + + break; + } + } + // jumpnodes + else if(jump_node >= 0){ + iconp->ship_class = ship_info_lookup(NAVBUOY_NAME); + iconp->type = ICON_JUMP_NODE; + } + // everything else + else { + iconp->ship_class = ship_info_lookup(NAVBUOY_NAME); + iconp->type = ICON_WAYPOINT; + } + + if (!m_change_local){ + propagate_icon(m_cur_icon); + } + + icon_obj[m_cur_icon] = obj_create(OBJ_POINT, -1, m_cur_icon, NULL, &pos, 0.0f, OF_RENDERS); + Assert(icon_obj[m_cur_icon] >= 0); + obj_merge_created_list(); + unmark_all(); + set_cur_object_index(icon_obj[m_cur_icon]); + GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(FALSE); + GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(TRUE); + update_data(1); +} + +void briefing_editor_dlg::OnDeleteIcon() +{ + delete_icon(m_cur_icon); +} + +void briefing_editor_dlg::delete_icon(int num) +{ + int i, z; + + if (num < 0) + num = m_cur_icon; + + if (num < 0) + return; + + Assert(m_cur_stage >= 0); + Assert(Briefing->stages[m_cur_stage].num_icons); + z = m_cur_icon; + if (z == num) + z = -1; + if (z > num) + z--; + + m_cur_icon = -1; + update_data(1); + obj_delete(icon_obj[num]); + for (i=num+1; istages[m_cur_stage].num_icons; i++) { + Briefing->stages[m_cur_stage].icons[i-1] = Briefing->stages[m_cur_stage].icons[i]; + icon_obj[i-1] = icon_obj[i]; + Objects[icon_obj[i-1]].instance = i - 1; + } + + Briefing->stages[m_cur_stage].num_icons--; + if (z >= 0) { + m_cur_icon = z; + update_data(0); + } +} + +void briefing_editor_dlg::OnGotoView() +{ + if (m_cur_stage < 0) + return; + + view_pos = Briefing->stages[m_cur_stage].camera_pos; + view_orient = Briefing->stages[m_cur_stage].camera_orient; + Update_window = 1; +} + +void briefing_editor_dlg::OnSaveView() +{ + if (m_cur_stage < 0) + return; + + Briefing->stages[m_cur_stage].camera_pos = view_pos; + Briefing->stages[m_cur_stage].camera_orient = view_orient; +} + +void briefing_editor_dlg::OnSelchangeIconImage() +{ + update_data(1); +} + +void briefing_editor_dlg::OnSelchangeTeam() +{ + update_data(1); +} + +int briefing_editor_dlg::check_mouse_hit(int x, int y) +{ + int i; + brief_icon *ptr; + + if (m_cur_stage < 0) + return -1; + + for (i=0; istages[m_cur_stage].num_icons; i++) { + ptr = &Briefing->stages[m_cur_stage].icons[i]; + if ((x > ptr->x) && (x < ptr->x + ptr->w) && (y > ptr->y) && (y < ptr->y + ptr->h)) { + return icon_obj[i]; + } + } + + return -1; +} + +void briefing_editor_dlg::OnPropagateIcons() +{ + object *ptr; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if ((ptr->type == OBJ_POINT) && (ptr->flags & OF_MARKED)) { + propagate_icon(ptr->instance); + } + + ptr = GET_NEXT(ptr); + } +} + +void briefing_editor_dlg::propagate_icon(int num) +{ + int i, s; + + for (s=m_cur_stage+1; snum_stages; s++) { + i = Briefing->stages[s].num_icons; + if (i >= MAX_STAGE_ICONS) + continue; + + if (find_icon(Briefing->stages[m_cur_stage].icons[num].id, s) >= 0) + continue; // don't change if icon exists here already. + + Briefing->stages[s].icons[i] = Briefing->stages[m_cur_stage].icons[num]; + Briefing->stages[s].num_icons++; + } +} + +int briefing_editor_dlg::find_icon(int id, int stage) +{ + int i; + + if (id >= 0) + for (i=0; istages[stage].num_icons; i++) + if (Briefing->stages[stage].icons[i].id == id) + return i; + + return -1; +} + +void briefing_editor_dlg::reset_icon_loop(int stage) +{ + stage_loop = stage + 1; + icon_loop = -1; +} + +int briefing_editor_dlg::get_next_icon(int id) +{ + while (1) { + icon_loop++; + if (icon_loop >= Briefing->stages[stage_loop].num_icons) { + stage_loop++; + if (stage_loop > Briefing->num_stages) + return 0; + + icon_loop = -1; + continue; + } + + iconp = &Briefing->stages[stage_loop].icons[icon_loop]; + if ((id >= 0) && (iconp->id == id)) + return 1; + } +} + +BOOL briefing_editor_dlg::OnCommand(WPARAM wParam, LPARAM lParam) +{ + int id; + + // deal with figuring out menu stuff + id = LOWORD(wParam); + if ( (id >= ID_TEAM_1) && (id < ID_TEAM_3) ) { + m_current_briefing = id - ID_TEAM_1; + + // put user back at first stage for this team (or no current stage is there are none). + Briefing = &Briefings[m_current_briefing]; + if ( Briefing->num_stages > 0 ) + m_cur_stage = 0; + else + m_cur_stage = -1; + + update_data(1); + OnGotoView(); + return 1; + } + + return CDialog::OnCommand(wParam, lParam); +} + +void briefing_editor_dlg::OnLines() +{ + if (m_lines.GetCheck() == 1) + m_lines.SetCheck(0); + else + m_lines.SetCheck(1); + + update_data(1); +} + +void briefing_editor_dlg::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + m_tree.right_clicked(); + *pResult = 0; +} + +void briefing_editor_dlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1) { + *pResult = 0; + modified = 1; + + } else + *pResult = 1; +} + +void briefing_editor_dlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + *pResult = m_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText); +} + +BOOL briefing_editor_dlg::DestroyWindow() +{ + m_play_bm.DeleteObject(); + return CDialog::DestroyWindow(); +} + +void briefing_editor_dlg::OnPlay() +{ + char path[MAX_PATH_LEN + 1]; + GetDlgItem(IDC_VOICE)->GetWindowText(m_voice); + + int size, offset; + cf_find_file_location((char *) (LPCSTR) m_voice, CF_TYPE_ANY, path, &size, &offset ); + + PlaySound(path, NULL, SND_ASYNC | SND_FILENAME); +} + +void briefing_editor_dlg::OnCopyView() +{ + // TODO: Add your control notification handler code here + + m_copy_view_set = 1; + m_copy_view_pos = view_pos; + m_copy_view_orient = view_orient; +} + +void briefing_editor_dlg::OnPasteView() +{ + // TODO: Add your control notification handler code here + if (m_cur_stage < 0) + return; + + if (m_copy_view_set == 0) { + MessageBox("No view set", "Unable to copy view"); + } else { + Briefing->stages[m_cur_stage].camera_pos = m_copy_view_pos; + Briefing->stages[m_cur_stage].camera_orient = m_copy_view_orient; + + update_data(1); + OnGotoView(); + } +} diff --git a/src/fred2/campaigneditordlg.cpp b/src/fred2/campaigneditordlg.cpp new file mode 100644 index 0000000..b56172d --- /dev/null +++ b/src/fred2/campaigneditordlg.cpp @@ -0,0 +1,876 @@ +// CampaignEditorDlg.cpp : implementation file +// + +#include +#include "stdafx.h" +#include "fred.h" +#include "campaigneditordlg.h" +#include "campaigntreeview.h" +#include "campaigntreewnd.h" +#include "management.h" +#include "freddoc.h" +#include "parselo.h" +#include "missiongoals.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +int Cur_campaign_mission = -1; +int Cur_campaign_link = -1; + +// determine the node number that would be allocated without actually allocating it yet. +int campaign_sexp_tree::get_new_node_position() +{ + int i; + + for (i=0; isave_modified()){ + return; + } + } + + GetCurrentDirectory(512, buf); + strcat(buf, "\\"); + strcat(buf, Campaign.missions[Cur_campaign_mission].name); + FREDDoc_ptr->SetPathName(buf); + Campaign_wnd->DestroyWindow(); + +// if (FREDDoc_ptr->OnOpenDocument(Campaign.missions[Cur_campaign_mission].name)) { +// Bypass_clear_mission = 1; +// Campaign_wnd->DestroyWindow(); +// +// } else { +// MessageBox("Failed to load mission!", "Error"); +// } +} + +BOOL campaign_editor::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) +{ + int i; + BOOL r; + CComboBox *box; + + r = CFormView::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext); + if (r) { + box = (CComboBox *) GetDlgItem(IDC_CAMPAIGN_TYPE); + box->ResetContent(); + for (i=0; iAddString(campaign_types[i]); + } + } + + return r; +} + +void campaign_editor::load_campaign() +{ + Cur_campaign_mission = Cur_campaign_link = -1; + load_tree(0); + + if (!strlen(Campaign.filename)) + strcpy(Campaign.filename, BUILTIN_CAMPAIGN); + + if (mission_campaign_load(Campaign.filename, 0)) { + MessageBox("Couldn't open Campaign file!", "Error"); + Campaign_wnd->OnCpgnFileNew(); + return; + } + + Campaign_modified = 0; + Campaign_tree_viewp->construct_tree(); + initialize(); +} + +void campaign_editor::OnAlign() +{ + Campaign_tree_viewp->sort_elements(); + Campaign_tree_viewp->realign_tree(); + Campaign_tree_viewp->Invalidate(); +} + +void campaign_editor::initialize( int init_files ) +{ + Cur_campaign_mission = Cur_campaign_link = -1; + m_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX)); + load_tree(0); + Campaign_tree_viewp->initialize(); + + // only initialize the file dialog box when the parameter says to. This should + // only happen when a campaign type changes + if ( init_files ){ + m_filelist.initialize(); + } + + m_name = Campaign.name; + m_type = Campaign.type; + m_num_players.Format("%d", Campaign.num_players); + + if (Campaign.desc) { + m_desc = convert_multiline_string(Campaign.desc); + } else { + m_desc = _T(""); + } + + m_loop_desc = _T(""); + + m_loop_brief_anim = _T(""); + m_loop_brief_sound = _T(""); + + // single player should hide the two dialog items about number of players allowed + if ( m_type == CAMPAIGN_TYPE_SINGLE ) { + GetDlgItem(IDC_NUM_PLAYERS)->ShowWindow( SW_HIDE ); + GetDlgItem(IDC_PLAYERS_LABEL)->ShowWindow( SW_HIDE ); + } else { + GetDlgItem(IDC_NUM_PLAYERS)->ShowWindow( SW_SHOW ); + GetDlgItem(IDC_PLAYERS_LABEL)->ShowWindow( SW_SHOW ); + } + + UpdateData(FALSE); +} + +void campaign_editor::mission_selected(int num) +{ + CEdit *bc_dialog; + + bc_dialog = (CEdit *) GetDlgItem(IDC_BRIEFING_CUTSCENE); + + // clear out the text for the briefing cutscene, and put in new text if specified + bc_dialog->SetWindowText(""); + if ( strlen(Campaign.missions[num].briefing_cutscene) ) + bc_dialog->SetWindowText(Campaign.missions[num].briefing_cutscene); + + if (Campaign.missions[num].flags & CMISSION_FLAG_BASTION) { + ((CButton *) GetDlgItem(IDC_GALATEA)) -> SetCheck(0); + ((CButton *) GetDlgItem(IDC_BASTION)) -> SetCheck(1); + + } else { + ((CButton *) GetDlgItem(IDC_GALATEA)) -> SetCheck(1); + ((CButton *) GetDlgItem(IDC_BASTION)) -> SetCheck(0); + } +} + +void campaign_editor::update() +{ + char buf[MISSION_DESC_LENGTH]; + + // get data from dlog box + UpdateData(TRUE); + + // update campaign name + string_copy(Campaign.name, m_name, NAME_LENGTH); + Campaign.type = m_type; + + // update campaign desc + deconvert_multiline_string(buf, m_desc, MISSION_DESC_LENGTH); + if (Campaign.desc) { + free(Campaign.desc); + } + + Campaign.desc = NULL; + if (strlen(buf)) { + Campaign.desc = strdup(buf); + } + + // maybe update mission loop text + save_loop_desc_window(); + + // set the number of players in a multiplayer mission equal to the number of players in the first mission + if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) { + if ( Campaign.num_missions == 0 ) { + Campaign.num_players = 0; + } else { + mission a_mission; + + get_mission_info(Campaign.missions[0].name, &a_mission); + Campaign.num_players = a_mission.num_players; + } + } +} + +void campaign_editor::OnCpgnClose() +{ + Campaign_wnd->OnClose(); +} + +void campaign_editor::load_tree(int save_first) +{ + char text[80]; + int i; + HTREEITEM h; + + if (save_first) + save_tree(); + + m_tree.clear_tree(); + m_tree.DeleteAllItems(); + m_num_links = 0; + UpdateData(TRUE); + update_loop_desc_window(); + + m_last_mission = Cur_campaign_mission; + if (Cur_campaign_mission < 0) { + GetDlgItem(IDC_SEXP_TREE)->EnableWindow(FALSE); + GetDlgItem(IDC_BRIEFING_CUTSCENE)->EnableWindow(FALSE); + return; + } + + GetDlgItem(IDC_SEXP_TREE)->EnableWindow(TRUE); + GetDlgItem(IDC_BRIEFING_CUTSCENE)->EnableWindow(TRUE); + + GetDlgItem(IDC_MISSISON_LOOP_DESC)->EnableWindow(FALSE); + GetDlgItem(IDC_LOOP_BRIEF_ANIM)->EnableWindow(FALSE); + GetDlgItem(IDC_LOOP_BRIEF_SOUND)->EnableWindow(FALSE); + GetDlgItem(IDC_LOOP_BRIEF_BROWSE)->EnableWindow(FALSE); + GetDlgItem(IDC_LOOP_BRIEF_SOUND_BROWSE)->EnableWindow(FALSE); + + for (i=0; iitem.hItem) == 1) { + *pResult = 0; + Campaign_modified = 1; + } else { + *pResult = 1; + } +} + +void campaign_editor::OnEndlabeleditSexpTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + *pResult = m_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText); +} + +int campaign_editor::handler(int code, int node, char *str) +{ + int i; + + switch (code) { + case ROOT_DELETED: + for (i=0; idelete_link(i); + m_num_links--; + return node; + + default: + Int3(); + } + + return -1; +} + +void campaign_editor::save_tree(int clear) +{ + int i; + + if (m_last_mission < 0){ + return; // nothing to save + } + + for (i=0; iitemNew.hItem; + Assert(h); + + // update help on sexp + m_tree.update_help(h); + + // get handle of parent + while ((h2 = m_tree.GetParentItem(h))>0){ + h = h2; + } + + // get identifier of parent + node = m_tree.GetItemData(h); + for (i=0; iInvalidate(); + *pResult = 0; +} + +void campaign_editor::OnMoveUp() +{ + int i, last = -1; + campaign_tree_link temp; + HTREEITEM h1, h2; + + if (Cur_campaign_link >= 0) { + save_tree(); + for (i=0; iSetFocus(); +} + +void campaign_editor::OnMoveDown() +{ + int i, j; + campaign_tree_link temp; + HTREEITEM h1, h2; + + if (Cur_campaign_link >= 0) { + save_tree(); + for (i=0; iSetFocus(); +} + +void campaign_editor::swap_handler(int node1, int node2) +{ + int index1, index2; + campaign_tree_link temp; + + for (index1=0; index1 index2 + 1) { + Links[index1] = Links[index1 - 1]; + index1--; + } + + // update Cur_campaign_link + Cur_campaign_link = index1; + + Links[index1] = temp; +} + +void campaign_editor::insert_handler(int old, int node) +{ + int i; + + for (i=0; im_hWnd; + GetDlgItem(IDC_SEXP_TREE)->SetFocus(); + ::SetFocus(h); + } +} + +void campaign_editor::OnChangeBriefingCutscene() +{ + CEdit *bc_dialog; + + bc_dialog = (CEdit *) GetDlgItem(IDC_BRIEFING_CUTSCENE); + + // maybe save off the current cutscene names. + if ( Cur_campaign_mission != -1 ) { + + // see if the contents of the edit box have changed. Luckily, this returns 0 when the edit box is + // cleared. + if ( bc_dialog->GetModify() ) { + bc_dialog->GetWindowText( Campaign.missions[Cur_campaign_mission].briefing_cutscene, NAME_LENGTH ); + Campaign_modified = 1; + } + } + +} + +// what to do when changing campaign type +void campaign_editor::OnSelchangeType() +{ + // if campaign type is single player, then disable the multiplayer items + update(); + initialize(); + Campaign_modified = 1; +} + + +void campaign_editor::OnGalatea() +{ + if (Cur_campaign_mission >= 0){ + Campaign.missions[Cur_campaign_mission].flags &= ~CMISSION_FLAG_BASTION; + } +} + +void campaign_editor::OnBastion() +{ + if (Cur_campaign_mission >= 0){ + Campaign.missions[Cur_campaign_mission].flags |= CMISSION_FLAG_BASTION; + } +} + +// update the loop mission text +void campaign_editor::save_loop_desc_window() +{ + char buffer[MISSION_DESC_LENGTH]; + + // update only if active link and there is a mission has mission loop + if ( (Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_loop ) { + deconvert_multiline_string(buffer, m_loop_desc, MISSION_DESC_LENGTH); + if (Links[Cur_campaign_link].mission_loop_txt) { + free(Links[Cur_campaign_link].mission_loop_txt); + } + if (Links[Cur_campaign_link].mission_loop_brief_anim) { + free(Links[Cur_campaign_link].mission_loop_brief_anim); + } + if (Links[Cur_campaign_link].mission_loop_brief_sound) { + free(Links[Cur_campaign_link].mission_loop_brief_sound); + } + + if (strlen(buffer)) { + Links[Cur_campaign_link].mission_loop_txt = strdup(buffer); + } else { + Links[Cur_campaign_link].mission_loop_txt = NULL; + } + + deconvert_multiline_string(buffer, m_loop_brief_anim, MAX_FILENAME_LEN); + if(strlen(buffer)){ + Links[Cur_campaign_link].mission_loop_brief_anim = strdup(buffer); + } else { + Links[Cur_campaign_link].mission_loop_brief_anim = NULL; + } + + deconvert_multiline_string(buffer, m_loop_brief_sound, MAX_FILENAME_LEN); + if(strlen(buffer)){ + Links[Cur_campaign_link].mission_loop_brief_sound = strdup(buffer); + } else { + Links[Cur_campaign_link].mission_loop_brief_sound = NULL; + } + } +} + +void campaign_editor::update_loop_desc_window() +{ + bool enable_loop_desc_window; + + enable_loop_desc_window = false; + if ((Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_loop) { + enable_loop_desc_window = true; + } + + // maybe enable description window + GetDlgItem(IDC_MISSISON_LOOP_DESC)->EnableWindow(enable_loop_desc_window); + GetDlgItem(IDC_LOOP_BRIEF_ANIM)->EnableWindow(enable_loop_desc_window); + GetDlgItem(IDC_LOOP_BRIEF_SOUND)->EnableWindow(enable_loop_desc_window); + GetDlgItem(IDC_LOOP_BRIEF_BROWSE)->EnableWindow(enable_loop_desc_window); + GetDlgItem(IDC_LOOP_BRIEF_SOUND_BROWSE)->EnableWindow(enable_loop_desc_window); + + // set new text + if ((Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_loop_txt && enable_loop_desc_window) { + m_loop_desc = convert_multiline_string(Links[Cur_campaign_link].mission_loop_txt); + } else { + m_loop_desc = _T(""); + } + + // set new text + if ((Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_loop_brief_anim && enable_loop_desc_window) { + m_loop_brief_anim = convert_multiline_string(Links[Cur_campaign_link].mission_loop_brief_anim); + } else { + m_loop_brief_anim = _T(""); + } + + // set new text + if ((Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_loop_brief_sound && enable_loop_desc_window) { + m_loop_brief_sound = convert_multiline_string(Links[Cur_campaign_link].mission_loop_brief_sound); + } else { + m_loop_brief_sound = _T(""); + } + + // reset text + UpdateData(FALSE); +} + +void campaign_editor::OnToggleLoop() +{ + // check if branch selected + if (Cur_campaign_link == -1) { + return; + } + + // store mission looop text + UpdateData(TRUE); + + if ( (Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_loop ) { + if (Links[Cur_campaign_link].mission_loop_txt) { + free(Links[Cur_campaign_link].mission_loop_txt); + } + + if (Links[Cur_campaign_link].mission_loop_brief_anim) { + free(Links[Cur_campaign_link].mission_loop_brief_anim); + } + + if (Links[Cur_campaign_link].mission_loop_brief_sound) { + free(Links[Cur_campaign_link].mission_loop_brief_sound); + } + + char buffer[MISSION_DESC_LENGTH]; + + deconvert_multiline_string(buffer, m_loop_desc, MISSION_DESC_LENGTH); + if (m_loop_desc && strlen(buffer)) { + Links[Cur_campaign_link].mission_loop_txt = strdup(buffer); + } else { + Links[Cur_campaign_link].mission_loop_txt = NULL; + } + + deconvert_multiline_string(buffer, m_loop_brief_anim, MISSION_DESC_LENGTH); + if (m_loop_brief_anim && strlen(buffer)) { + Links[Cur_campaign_link].mission_loop_brief_anim = strdup(buffer); + } else { + Links[Cur_campaign_link].mission_loop_brief_anim = NULL; + } + + deconvert_multiline_string(buffer, m_loop_brief_sound, MISSION_DESC_LENGTH); + if (m_loop_brief_sound && strlen(buffer)) { + Links[Cur_campaign_link].mission_loop_brief_sound = strdup(buffer); + } else { + Links[Cur_campaign_link].mission_loop_brief_sound = NULL; + } + } + + // toggle to mission_loop setting + Links[Cur_campaign_link].mission_loop = !Links[Cur_campaign_link].mission_loop; + + // reset loop desc window (gray if inactive) + update_loop_desc_window(); + + // set root icon + int bitmap1, bitmap2; + if (Links[Cur_campaign_link].mission_loop) { + bitmap2 = BITMAP_GREEN_DOT; + bitmap1 = BITMAP_BLUE_DOT; + } else { + bitmap1 = BITMAP_BLACK_DOT; + bitmap2 = BITMAP_RED_DOT; + } + + // Search for item and update bitmap + HTREEITEM h = m_tree.GetRootItem(); + while (h) { + if ((int) m_tree.GetItemData(h) == Links[Cur_campaign_link].node) { + m_tree.SetItemImage(h, bitmap1, bitmap2); + break; + } + + h = m_tree.GetNextSiblingItem(h); + } + + // set to redraw + Campaign_tree_viewp->Invalidate(); +} + +void campaign_editor::OnBrowseLoopAni() +{ + int pushed_dir; + + UpdateData(TRUE); + + // switch directories + pushed_dir = cfile_push_chdir(CF_TYPE_INTERFACE); + CFileDialog dlg(TRUE, "ani", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, "Ani Files (*.ani)|*.ani"); + + if (dlg.DoModal() == IDOK) { + m_loop_brief_anim = dlg.GetFileName(); + UpdateData(FALSE); + } + + // move back to the proper directory + if (!pushed_dir){ + cfile_pop_dir(); + } +} + +void campaign_editor::OnBrowseLoopSound() +{ + int pushed_dir; + + UpdateData(TRUE); + + // switch directories + pushed_dir = cfile_push_chdir(CF_TYPE_VOICE_CMD_BRIEF); + CFileDialog dlg(TRUE, "wav", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, "Wave Files (*.wav)|*.wav||"); + + if (dlg.DoModal() == IDOK) { + m_loop_brief_sound = dlg.GetFileName(); + UpdateData(FALSE); + } + + // move back to the proper directory + if (!pushed_dir){ + cfile_pop_dir(); + } +} \ No newline at end of file diff --git a/src/fred2/campaignfilelistbox.cpp b/src/fred2/campaignfilelistbox.cpp new file mode 100644 index 0000000..ed236b8 --- /dev/null +++ b/src/fred2/campaignfilelistbox.cpp @@ -0,0 +1,78 @@ +// CampaignFilelistBox.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "freespace.h" +#include "campaignfilelistbox.h" +#include "campaigntreewnd.h" +#include "missioncampaign.h" +#include "missionparse.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// campaign_filelist_box + +campaign_filelist_box::campaign_filelist_box() +{ +} + +campaign_filelist_box::~campaign_filelist_box() +{ +} + + +BEGIN_MESSAGE_MAP(campaign_filelist_box, CListBox) + //{{AFX_MSG_MAP(campaign_filelist_box) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// campaign_filelist_box message handlers + +void campaign_filelist_box::initialize() +{ + int i, z; + char wild_card[256]; + WIN32_FIND_DATA file_data; + HANDLE h; + mission a_mission; + + ResetContent(); + memset(wild_card, 0, 256); + strcpy(wild_card, NOX("*")); + strcat(wild_card, FS_MISSION_FILE_EXT); + h = FindFirstFile(wild_card, &file_data); + if (h != INVALID_HANDLE_VALUE) { + do { + + // make a call to get the mission info for this mission. Passing a misison as the second + // parameter will prevent The_mission from getting overwritten. + get_mission_info( file_data.cFileName, &a_mission ); + + // only add missions of the appropriate type to the file listbox + if ( (Campaign.type == CAMPAIGN_TYPE_SINGLE) && (a_mission.game_type & (MISSION_TYPE_SINGLE|MISSION_TYPE_TRAINING)) ) + AddString(file_data.cFileName); + else if ( (Campaign.type == CAMPAIGN_TYPE_MULTI_COOP) && (a_mission.game_type & MISSION_TYPE_MULTI_COOP) ) + AddString(file_data.cFileName); + else if ( (Campaign.type == CAMPAIGN_TYPE_MULTI_TEAMS) && (a_mission.game_type & MISSION_TYPE_MULTI_TEAMS) ) + AddString(file_data.cFileName); + + } while (FindNextFile(h, &file_data)); + + FindClose(h); + } + + for (i=0; iSetTextAlign(TA_TOP | TA_CENTER); + pDC->SetTextColor(RGB(0, 0, 0)); + pDC->SetBkMode(TRANSPARENT); + + // figure out text box sizes + r = pDC->GetTextMetrics(&tm); + Assert(r); + Bx = CELL_TEXT_WIDTH + 4; + By = tm.tmHeight + 4; + + r = gray_brush.CreateSolidBrush(RGB(192, 192, 192)); + Assert(r); + pDC->FillRect(CRect(0, 0, total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT), &gray_brush); + + // create pens + r = black_pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + Assert(r); + r = white_pen.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + Assert(r); + r = red_pen.CreatePen(PS_SOLID, 1, RGB(192, 0, 0)); + Assert(r); + r = blue_pen.CreatePen(PS_SOLID, 1, RGB(0, 0, 192)); + Assert(r); + r = green_pen.CreatePen(PS_SOLID, 1, RGB(0, 192, 0)); + Assert(r); + + pDC->SelectObject(&black_pen); + // draw level seperators + for (i=1; iMoveTo(0, i * LEVEL_HEIGHT - 1); + pDC->LineTo(total_width * CELL_WIDTH, i * LEVEL_HEIGHT - 1); + } + + pDC->SelectObject(&white_pen); + for (i=1; iMoveTo(0, i * LEVEL_HEIGHT); + pDC->LineTo(total_width * CELL_WIDTH, i * LEVEL_HEIGHT); + } + + + // draw edges of the whole tree rectangle + pDC->SelectObject(&black_pen); + pDC->MoveTo(0, total_levels * LEVEL_HEIGHT); + pDC->LineTo(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT); + pDC->LineTo(total_width * CELL_WIDTH, -1); + + // draw text boxes and text + + for (i=0; im_hDC, str, strlen(str), &size); + if (size.cx > CELL_TEXT_WIDTH) { + strcpy(str + strlen(str) - 1, "..."); + GetTextExtentPoint32(pDC->m_hDC, str, strlen(str), &size); + while (size.cx > CELL_TEXT_WIDTH) { + strcpy(str + strlen(str) - 4, "..."); + GetTextExtentPoint32(pDC->m_hDC, str, strlen(str), &size); + } + } + + if (i == Cur_campaign_mission) { + pDC->SetTextColor(RGB(192, 0, 0)); + pDC->Draw3dRect(x - Bx / 2, y - By / 2, Bx, By, RGB(0, 0, 0), RGB(255, 255, 255)); + + } else { + pDC->SetTextColor(RGB(0, 0, 0)); + pDC->Draw3dRect(x - Bx / 2, y - By / 2, Bx, By, RGB(255, 255, 255), RGB(0, 0, 0)); + } + + pDC->TextOut(x, y - By / 2 + 2, str, strlen(str)); + } + + for (i=0; iSelectObject(&blue_pen); + } + + // if active link, select highlight pen (red - normal, green - mission loop) + if (i == Cur_campaign_link) { + if (Links[i].mission_loop) { + pDC->SelectObject(&green_pen); + } else { + pDC->SelectObject(&red_pen); + } + } + + // draw a line between 'from' and 'to' mission. to might be -1 in the case of end-campaign, so + // don't draw line if so. + if ( (f != t) && ( t != -1) ) { + pDC->MoveTo(Links[i].p1); + pDC->LineTo(Links[i].p2); + } + + // select (defalt) black pen + pDC->SelectObject(&black_pen); + } + + pDC->SelectObject(&black_pen); +} + +///////////////////////////////////////////////////////////////////////////// +// campaign_tree_view diagnostics + +#ifdef _DEBUG +void campaign_tree_view::AssertValid() const +{ + CScrollView::AssertValid(); +} + +void campaign_tree_view::Dump(CDumpContext& dc) const +{ + CScrollView::Dump(dc); +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// campaign_tree_view message handlers + +void campaign_tree_view::OnInitialUpdate() +{ + CScrollView::OnInitialUpdate(); + SetScrollSizes(MM_TEXT, CSize(320, 320)); +} + +void stuff_link_with_formula(int *link_idx, int formula, int mission_num) +{ + int j, node, node2, node3; + if (formula >= 0) { + if (!stricmp(CTEXT(formula), "cond")) { + // sexp is valid + + node = CDR(formula); + free_one_sexp(formula); + while (node != -1) { + node2 = CAR(node); + Links[*link_idx].from = mission_num; + Links[*link_idx].sexp = CAR(node2); + Links[*link_idx].mission_loop_txt = NULL; + Links[*link_idx].mission_loop_brief_anim = NULL; + Links[*link_idx].mission_loop_brief_sound = NULL; + sexp_mark_persistent(CAR(node2)); + free_one_sexp(node2); + node3 = CADR(node2); + if ( !stricmp( CTEXT(node3), "next-mission") ) { + node3 = CDR(node3); + for (j=0; j total_levels) + total_levels = z + 2; + + Level_counts[z]++; + z = (Campaign.missions[i].pos + 3) / 2; + if (z > total_width) + total_width = z; + } + + sort_links(); + SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT)); + Invalidate(); +} + +void campaign_tree_view::free_links() +{ + int i; + + for (i=0; i total_width) + total_width = pos; + + Level_counts[level] = pos; + if (!z) { // topmost node must always be alone on level + level++; + pos = 0; + } + } + + // now calculate the true x position of each mission + for (i=0; i 0) // sort left to right + swap = 1; + + else if (!z) { // both have same position? + z = Campaign.missions[Links[to_list[j]].from].level - + Campaign.missions[Links[to_list[k]].from].level; + + // see where from link position is relative to to link position + if (Campaign.missions[i].pos < Campaign.missions[Links[to_list[j]].from].pos) { + if (z > 0) // sort bottom to top + swap = 1; + + } else { + if (z < 0) // sort top to bottom + swap = 1; + } + } + + if (swap) { + z = to_list[j]; + to_list[j] = to_list[k]; + to_list[k] = z; + } + } + + // set all links to positions + for (j=0; j 0) + swap = 1; + + else if (!z) { + z = Campaign.missions[Links[from_list[j]].to].level - + Campaign.missions[Links[from_list[k]].to].level; + + if (Campaign.missions[i].pos < Campaign.missions[Links[from_list[j]].to].pos) { + if (z < 0) + swap = 1; + + } else { + if (z > 0) + swap = 1; + } + } + + if (swap) { + z = from_list[j]; + from_list[j] = from_list[k]; + from_list[k] = z; + } + } + + // set all links from positions + for (j=0; jm_filelist; + i = listbox->GetCurSel(); + + Mission_dropping = -1; + if (i != LB_ERR) { + Mission_dropping = i; + SetCapture(); + } + + Last_draw_size = CSize(0, 0); + Dragging_rect.SetRect(0, 0, 1, 1); + dc.DrawDragRect(Dragging_rect, Last_draw_size, NULL, CSize(0, 0)); + + } else { + if ( (Cur_campaign_link >= 0) && Links[Cur_campaign_link].mission_loop) { + // HACK!! UPDATE mission loop desc before changing selections + // save mission loop desc + char buffer[MISSION_DESC_LENGTH]; + box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_MISSISON_LOOP_DESC); + box->GetWindowText(buffer, MISSION_DESC_LENGTH); + if (strlen(buffer)) { + if (Links[Cur_campaign_link].mission_loop_txt) { + free(Links[Cur_campaign_link].mission_loop_txt); + } + Links[Cur_campaign_link].mission_loop_txt = strdup(buffer); + } else { + Links[Cur_campaign_link].mission_loop_txt = NULL; + } + + // HACK!! UPDATE mission loop desc before changing selections + // save mission loop desc + box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_LOOP_BRIEF_ANIM); + box->GetWindowText(buffer, MISSION_DESC_LENGTH); + if (strlen(buffer)) { + if (Links[Cur_campaign_link].mission_loop_brief_anim) { + free(Links[Cur_campaign_link].mission_loop_brief_anim); + } + Links[Cur_campaign_link].mission_loop_brief_anim = strdup(buffer); + } else { + Links[Cur_campaign_link].mission_loop_brief_anim = NULL; + } + + // HACK!! UPDATE mission loop desc before changing selections + // save mission loop desc + box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_LOOP_BRIEF_SOUND); + box->GetWindowText(buffer, MISSION_DESC_LENGTH); + if (strlen(buffer)) { + if (Links[Cur_campaign_link].mission_loop_brief_sound) { + free(Links[Cur_campaign_link].mission_loop_brief_sound); + } + Links[Cur_campaign_link].mission_loop_brief_sound = strdup(buffer); + } else { + Links[Cur_campaign_link].mission_loop_brief_sound = NULL; + } + } + Mission_dragging = Cur_campaign_mission = Cur_campaign_link = -1; + for (i=0; iGetDlgItem(IDC_HELP_BOX); + if (box) + box->SetWindowText(str); + } + + Campaign_tree_formp->mission_selected(Cur_campaign_mission); + break; + } + } + + Invalidate(); + UpdateWindow(); + Campaign_tree_formp->load_tree(); + if (Mission_dragging != -1) { + CRect rect = Dragging_rect; + + dc.LPtoDP(&rect); + dc.DrawDragRect(rect, Last_draw_size, NULL, CSize(0, 0)); + } + + CScrollView::OnLButtonDown(nFlags, point); +} + +void campaign_tree_view::OnMouseMove(UINT nFlags, CPoint point) +{ + int i, level, pos, x, y; + CSize draw_size; + CRect rect, r1; + CClientDC dc(this); + + OnPrepareDC(&dc); + dc.DPtoLP(&point); + if ((Mission_dragging >= 0) || (Mission_dropping >= 0)) { + if (GetCapture() != this) { + rect = Dragging_rect; + dc.LPtoDP(&rect); + dc.DrawDragRect(rect, CSize(0, 0), rect, Last_draw_size); + Mission_dragging = Mission_dropping = -1; + + } else { + for (i=0; i= 0) { // dropping a new mission into campaign? + z = Mission_dropping; + Mission_dropping = -1; + if (GetCapture() == this) { + ReleaseCapture(); + dc.LPtoDP(&Dragging_rect); + dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size); + + drop_mission(z, point); + return; + } + + } else if (Mission_dragging >= 0) { // moving position of a mission? + z = Mission_dragging; + Mission_dragging = -1; + if (GetCapture() == this) { + ReleaseCapture(); + dc.LPtoDP(&Dragging_rect); + dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size); + for (i=0; i= MAX_CAMPAIGN_TREE_LINKS) { + MessageBox("Too many links exist. Can't add any more."); + return; + } + + if (Campaign.missions[z].level >= Campaign.missions[i].level) { + MessageBox("A branch can only be set to a mission on a lower level"); + return; + } + + add_link(z, i); + return; + } + + // at this point, we are dragging a mission to a new place + level = query_level(point); + pos = query_pos(point); + if ((level < 0) || (pos < 0)) + return; + + if (!level && (get_root_mission() >= 0)) { + MessageBox("Can't move mission to this level. There is already a top level mission"); + return; + } + + for (i=0; i= MAX_CAMPAIGN_TREE_LINKS) + return -1; + + Campaign_tree_formp->load_tree(1); + Links[Total_links].from = from; + Links[Total_links].to = to; + Links[Total_links].sexp = Locked_sexp_true; + Links[Total_links].mission_loop = false; + Links[Total_links].mission_loop_txt = NULL; + Links[Total_links].mission_loop_brief_anim = NULL; + Links[Total_links].mission_loop_brief_sound = NULL; + Total_links++; + if (from != to) { + Elements[from].from_links++; + Elements[to].to_links++; + } + + sort_links(); + Campaign_tree_formp->load_tree(0); + Invalidate(); + Campaign_modified = 1; + return 0; +} + +int campaign_tree_view::query_level(const CPoint& p) +{ + int level; + + if ((p.y < 0) || (p.y >= total_levels * LEVEL_HEIGHT)) + return -1; + + level = p.y / LEVEL_HEIGHT; + Assert((level >= 0) && (level < total_levels)); + return level; +} + +int campaign_tree_view::query_pos(const CPoint& p) +{ + int pos; + + if ((p.x < 0) || (p.x >= total_width * CELL_WIDTH)) + return -1; + + pos = ((p.x * 4 / CELL_WIDTH) + 1) / 2; + Assert((pos >= 0) && (pos <= total_width * 2)); + return pos; +} + +int campaign_tree_view::query_alternate_pos(const CPoint& p) +{ + int x, pos; + + if ((p.x < 0) || (p.x >= total_width * CELL_WIDTH)) + return -1; + + x = p.x * 4 / CELL_WIDTH; + pos = (x + 1) / 2; + if (x & 1) // odd number + pos--; + else // even number + pos++; + + Assert((pos >= 0) && (pos <= total_width * 2)); + return pos; +} +/* +DROPEFFECT campaign_tree_view::OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) +{ + CClientDC dc(this); + OnPrepareDC(&dc); + + if (!pDataObject->IsDataAvailable((unsigned short)Mission_filename_cb_format)) + return DROPEFFECT_NONE; // data isn't a mission filename, the only valid data to drop here + + Mission_dropping = 1; + Last_draw_size = CSize(0, 0); + Dragging_rect.SetRect(0, 0, 1, 1); + dc.DrawDragRect(Dragging_rect, Last_draw_size, NULL, CSize(0, 0)); + return DROPEFFECT_MOVE; +} + +void campaign_tree_view::OnDragLeave() +{ + CScrollView::OnDragLeave(); + if (Mission_dropping >= 0) { + CClientDC dc(this); + OnPrepareDC(&dc); + + dc.LPtoDP(&Dragging_rect); + dc.DrawDragRect(Dragging_rect, CSize(0, 0), Dragging_rect, Last_draw_size); + Mission_dropping = -1; + } +} + +DROPEFFECT campaign_tree_view::OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) +{ + int i, level, pos, x, y; + CSize draw_size; + CRect rect, r1; + DROPEFFECT r = DROPEFFECT_MOVE; + + if (Mission_dropping < 0) + return DROPEFFECT_NONE; + + CClientDC dc(this); + OnPrepareDC(&dc); + dc.DPtoLP(&point); + + level = query_level(point); + pos = query_pos(point); + if ((level < 0) || (pos < 0)) { // off table? + draw_size = CSize(0, 0); + rect = Dragging_rect; + r = DROPEFFECT_NONE; + + } else { + draw_size = CSize(2, 2); + for (i=0; iIsDataAvailable((unsigned short)Mission_filename_cb_format)) + return FALSE; // data isn't a mission filename, the only valid data to drop here + + // Get text data from COleDataObject + hGlobal = pDataObject->GetGlobalData((unsigned short)Mission_filename_cb_format); + + // Get pointer to data + pData = (LPCSTR) GlobalLock(hGlobal); + ASSERT(pData); + + if (Campaign.num_missions >= MAX_CAMPAIGN_MISSIONS) { // Can't add any more + GlobalUnlock(hGlobal); + MessageBox("Too many missions. Can't add more to Campaign.", "Error"); + return FALSE; + } + + level = query_level(point); + pos = query_pos(point); + Assert((level >= 0) && (pos >= 0)); // this should be impossible + if (!level && (get_root_mission() >= 0)) { + GlobalUnlock(hGlobal); + MessageBox("Only 1 mission may be in the top level"); + return FALSE; + } + + // check the number of players in a multiplayer campaign against the number of players + // in the mission that was just dropped + if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) { + get_mission_info((char *)pData, &a_mission); + if ( !(a_mission.game_type & MISSION_TYPE_MULTI) ) { + char buf[256]; + + sprintf( buf, "Mission \"%s\" is not a multiplayer mission", pData ); + MessageBox(buf, "Error"); + GlobalUnlock(hGlobal); + return FALSE; + } + + if ( Campaign.num_players != 0 ) { + if ( Campaign.num_players != a_mission.num_players ) { + char buf[512]; + + sprintf(buf, "Mission \"%s\" has %d players. Campaign has %d players. Campaign will not play properly in FreeSpace", pData, a_mission.num_players, Campaign.num_players ); + MessageBox(buf, "Warning"); + } + } else { + Campaign.num_players = a_mission.num_players; + } + } + + Elements[Campaign.num_missions].from_links = Elements[Campaign.num_missions].to_links = 0; + cm = &(Campaign.missions[Campaign.num_missions++]); + cm->name = strdup(pData); + cm->formula = Locked_sexp_true; + cm->num_goals = -1; + cm->notes = NULL; + cm->briefing_cutscene[0] = 0; + for (i=0; ilevel = level; + cm->pos = pos - 1; + correct_position(Campaign.num_missions - 1); + sort_links(); + SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT)); + Invalidate(); + + // update and reinitialize dialog items + if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) { + Campaign_tree_formp->update(); + Campaign_tree_formp->initialize(0); + } + + // Unlock memory - Send dropped text into the "bit-bucket" + GlobalUnlock(hGlobal); + Campaign_modified = 1; + return TRUE; +} +*/ + +void campaign_tree_view::drop_mission(int m, CPoint point) +{ + char name[MAX_FILENAME_LEN + 1]; + int i, item, level, pos; + cmission *cm; + mission a_mission; + CListBox *listbox; + + level = query_level(point); + pos = query_pos(point); + Assert((level >= 0) && (pos >= 0)); // this should be impossible + + listbox = (CListBox *) &Campaign_tree_formp->m_filelist; + item = listbox->GetCurSel(); + if (item == LB_ERR) { + MessageBox("Select a mission from listbox to add.", "Error"); + return; + } + + if (listbox->GetTextLen(item) > MAX_FILENAME_LEN) { + char buf[256]; + + sprintf(buf, "Filename is too long. Must be %d or less characters.", MAX_FILENAME_LEN); + MessageBox(buf, "Error"); + return; // filename is too long. Would overflow our buffer + } + + // grab the filename selected from the listbox + listbox->GetText(item, name); + + if (Campaign.num_missions >= MAX_CAMPAIGN_MISSIONS) { // Can't add any more + MessageBox("Too many missions. Can't add more to Campaign.", "Error"); + return; + } + + if (!level && (get_root_mission() >= 0)) { + MessageBox("Only 1 mission may be in the top level"); + return; + } + + // check the number of players in a multiplayer campaign against the number of players + // in the mission that was just dropped + if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) { + get_mission_info(name, &a_mission); + if ( !(a_mission.game_type & MISSION_TYPE_MULTI) ) { + char buf[256]; + + sprintf(buf, "Mission \"%s\" is not a multiplayer mission", name); + MessageBox(buf, "Error"); + return; + } + + if (Campaign.num_players != 0) { + if (Campaign.num_players != a_mission.num_players) { + char buf[512]; + + sprintf(buf, "Mission \"%s\" has %d players. Campaign has %d players. Campaign will not play properly in FreeSpace", name, a_mission.num_players, Campaign.num_players ); + MessageBox(buf, "Warning"); + } + + } else { + Campaign.num_players = a_mission.num_players; + } + } + + Elements[Campaign.num_missions].from_links = Elements[Campaign.num_missions].to_links = 0; + cm = &(Campaign.missions[Campaign.num_missions++]); + cm->name = strdup(name); + cm->formula = Locked_sexp_true; + cm->num_goals = -1; + cm->notes = NULL; + cm->briefing_cutscene[0] = 0; + for (i=0; ilevel = level; + cm->pos = pos - 1; + correct_position(Campaign.num_missions - 1); + sort_links(); + SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT)); + Invalidate(); + + // update and reinitialize dialog items + if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) { + Campaign_tree_formp->update(); + Campaign_tree_formp->initialize(0); + } + + listbox->DeleteString(item); + Campaign_modified = 1; + return; +} + +void campaign_tree_view::sort_elements() +{ + int i, j, s1, s2; + + for (i=0; i=0; j--) { + s2 = Sorted[j]; + if ((Campaign.missions[s1].level > Campaign.missions[s2].level) || + ((Campaign.missions[s1].level == Campaign.missions[s2].level) && + (Campaign.missions[s1].pos > Campaign.missions[s2].pos))) + break; + + Sorted[j + 1] = s2; + } + + Sorted[j + 1] = s1; + } +} + +void campaign_tree_view::correct_position(int num) +{ + int i, z; + + // move missions down if required + if (Campaign.missions[num].level + 2 > total_levels) + total_levels = Campaign.missions[num].level + 2; + + for (i=0; i= Campaign.missions[z].level) ) { + Campaign.missions[z].level = Campaign.missions[num].level + 1; + correct_position(z); + } + } + + // space out horizontally to avoid overlap of missions + horizontally_align_mission(num, -1); + horizontally_align_mission(num, 1); +} + +void campaign_tree_view::horizontally_align_mission(int num, int dir) +{ + int i, z; + + if ((Campaign.missions[num].pos == -1) || (Campaign.missions[num].pos + 1 == total_width * 2)) { // need to expand total_width + for (i=0; i= 0) && (num < Total_links)); + if (Links[num].from != Links[num].to) { + Elements[Links[num].from].from_links--; + Elements[Links[num].to].to_links--; + } + + sexp_unmark_persistent(Links[num].sexp); + free_sexp2(Links[num].sexp); + while (num < Total_links - 1) { + Links[num] = Links[num + 1]; + num++; + } + + Total_links--; + sort_links(); + Invalidate(); + Campaign_modified = 1; + return; +} + +int campaign_tree_view::get_root_mission() +{ + int i; + + for (i=0; iTrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); + } + + } else { + Context_mission = query_level(p); + if ((Context_mission >= 0) && (Context_mission < total_levels)) + if (menu.LoadMenu(IDR_CPGN_VIEW_OFF)) { + popup = menu.GetSubMenu(0); + ASSERT(popup); + popup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); + } + } +} + +void campaign_tree_view::OnRemoveMission() +{ + remove_mission(Context_mission); + Invalidate(); + UpdateWindow(); + + // for multiplayer missions, update the data and reiniailize the dialog -- the number of player + // in the mission might have changed because of deletion of the first mission + if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) { + Campaign_tree_formp->update(); + Campaign_tree_formp->initialize(); + } +} + +void campaign_tree_view::remove_mission(int m) +{ + int i, z; + CEdit *box; + + Assert(m >= 0); + Campaign_tree_formp->m_filelist.AddString(Campaign.missions[m].name); + + z = --Campaign.num_missions; + i = Total_links; + while (i--) { + if ((Links[i].from == m) || (Links[i].to == m)) + delete_link(i); + if (Links[i].from == z) + Links[i].from = m; + if (Links[i].to == z) + Links[i].to = m; + } + + Elements[m] = Elements[z]; + Campaign.missions[m] = Campaign.missions[z]; + if (m == Cur_campaign_mission) { + Cur_campaign_mission = -1; + box = (CEdit *) Campaign_tree_formp->GetDlgItem(IDC_HELP_BOX); + if (box) + box->SetWindowText(""); + + Campaign_tree_formp->load_tree(0); + } + + Campaign_modified = 1; +} + +void campaign_tree_view::OnDeleteRow() +{ + int i, z; + + if (!Context_mission) { + MessageBox("Can't delete the top level"); + return; + } + + for (i=z=0; i Context_mission) + Campaign.missions[i].level--; + + total_levels--; + SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT)); + Invalidate(); + UpdateWindow(); + Campaign_modified = 1; +} + +void campaign_tree_view::OnInsertRow() +{ + int i; + + for (i=0; i= Context_mission) + Campaign.missions[i].level++; + + total_levels++; + SetScrollSizes(MM_TEXT, CSize(total_width * CELL_WIDTH, total_levels * LEVEL_HEIGHT)); + Invalidate(); + UpdateWindow(); + Campaign_modified = 1; +} + +void campaign_tree_view::OnAddRepeat() +{ + if (add_link(Context_mission, Context_mission)) { + MessageBox("Too many links exist. Can't add any more."); + return; + } +} + +void campaign_tree_view::OnEndOfCampaign() +{ + if ( add_link(Context_mission, -1) ) { + MessageBox("Too many links exist. Cannot add any more."); + return; + } +} diff --git a/src/fred2/campaigntreewnd.cpp b/src/fred2/campaigntreewnd.cpp new file mode 100644 index 0000000..52f16e5 --- /dev/null +++ b/src/fred2/campaigntreewnd.cpp @@ -0,0 +1,564 @@ +/* + * $Logfile: /Freespace2/code/FRED2/CampaignTreeWnd.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Campaign display tree window code. Works very closely with the Campaign editor dialog box. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 3 1/07/99 1:52p Andsager + * Initial check in of Sexp_variables + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:00p Dave + * + * 27 9/16/98 3:08p Dave + * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort + * the ship list box. Added code so that tracker stats are not stored with + * only 1 player. + * + * 26 8/19/98 9:46a Hoffoss + * Added some 'save first?' type checks to the campaign editor. + * + * 25 5/26/98 2:32p Hoffoss + * Made campaign editor come up with a new campaign instead of loading + * built-in one. + * + * 24 5/22/98 1:06a Hoffoss + * Made Fred not use OLE. + * + * 23 4/14/98 11:55a Allender + * add end-of-campaign sexpression to allow for mission replay at the end + * of campaigns + * + * 22 3/31/98 12:23a Allender + * changed macro names of campaign types to be more descriptive. Added + * "team" to objectives dialog for team v. team missions. Added two + * distinct multiplayer campaign types + * + * 21 3/18/98 10:38p Allender + * added required "num players" for multiplayer missions. Put in required + * "num players" for multiplayer campaigns. Added campaign editor support + * to determine "num players" + * + * 20 2/24/98 10:23a Johnson + * Fixed up some build bugs caused by a multiplayer struct removal. + * + * 19 12/19/97 10:29a Allender + * made initial status windows appear on top of campaign editor window + * instead of Fred main window + * + * 18 12/18/97 5:11p Allender + * initial support for ship/weapon persistence + * + * 17 12/02/97 5:29p Johnson + * Fixed bug in branch sexp error checking. Was using link number instead + * of mission number. + * + * 16 11/22/97 3:05p Allender + * support for new fields for multiplayer in campaign editor + * + * 15 9/16/97 4:19p Jasen + * Fixed a bug in Fred with Campaign mode. + * + * 14 9/01/97 6:31p Hoffoss + * Added remaining missing features to campaign editor in Fred. + * + * 13 8/14/97 11:54p Hoffoss + * Added more error checking to Campaign editor, and made exit from + * Campaign editor reload last mission in Fred (unless specifically + * loading another mission). + * + * 12 8/13/97 5:49p Hoffoss + * Fixed bugs, made additions. + * + * 11 8/13/97 12:46p Hoffoss + * Added campaign error checker, accelerator table, and mission goal data + * listings to sexp tree right click menu. + * + * 10 7/09/97 2:28p Hoffoss + * Fixed bug with adding new links, and made campaign general info update. + * + * 9 5/15/97 12:45p Hoffoss + * Extensive changes to fix many little bugs. + * + * 8 5/14/97 12:54p Hoffoss + * Added sexp tree for campaign branches, branch hilighting, and branch + * reordering. + * + * 7 5/13/97 12:46p Hoffoss + * Added close campaign editor functions, changed global pointer to have + * capped first letter. + * + * 6 5/13/97 11:13a Hoffoss + * Added remaining file menu options to campaign editor. + * + * 5 5/13/97 10:52a Hoffoss + * Added campaign saving code. + * + * 4 5/09/97 9:50a Hoffoss + * Routine code check in. + * + * 3 5/05/97 9:40a Hoffoss + * Campaign editor begun. + * + * 2 5/01/97 4:11p Hoffoss + * Started on Campaign editor stuff, being sidetracked with fixing bugs + * now, though, so checking it for now. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "campaigntreewnd.h" +#include "campaigneditordlg.h" +#include "campaigntreeview.h" +#include "management.h" +#include "mainfrm.h" +#include "fredview.h" +#include "missionsave.h" +#include "initialships.h" +#include "missionparse.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +int Mission_filename_cb_format; +int Campaign_modified = 0; +int Bypass_clear_mission; +campaign_tree_wnd *Campaign_wnd = NULL; + +IMPLEMENT_DYNCREATE(campaign_tree_wnd, CFrameWnd) + +///////////////////////////////////////////////////////////////////////////// +// campaign_tree_wnd + +campaign_tree_wnd::campaign_tree_wnd() +{ + Bypass_clear_mission = 0; +} + +campaign_tree_wnd::~campaign_tree_wnd() +{ +} + +BEGIN_MESSAGE_MAP(campaign_tree_wnd, CFrameWnd) + //{{AFX_MSG_MAP(campaign_tree_wnd) + ON_UPDATE_COMMAND_UI(ID_CPGN_FILE_OPEN, OnUpdateCpgnFileOpen) + ON_COMMAND(ID_CPGN_FILE_OPEN, OnCpgnFileOpen) + ON_WM_DESTROY() + ON_COMMAND(ID_CPGN_FILE_SAVE, OnCpgnFileSave) + ON_COMMAND(ID_CPGN_FILE_SAVE_AS, OnCpgnFileSaveAs) + ON_COMMAND(ID_CPGN_FILE_NEW, OnCpgnFileNew) + ON_COMMAND(ID_CLOSE, OnClose2) + ON_COMMAND(ID_ERROR_CHECKER, OnErrorChecker) + ON_WM_CLOSE() + ON_COMMAND(ID_INITIAL_SHIPS, OnInitialShips) + ON_COMMAND(ID_INITIAL_WEAPONS, OnInitialWeapons) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// campaign_tree_wnd message handlers + +BOOL campaign_tree_wnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext) +{ + CSize s; + + LoadAccelTable("IDR_ACC_CAMPAIGN"); + Mission_filename_cb_format = RegisterClipboardFormat("Mission Filename"); + Campaign_modified = 0; + clear_mission(); + + // create a splitter with 1 row, 2 columns + if (!m_splitter.CreateStatic(this, 1, 2)) + { + TRACE0("Failed to CreateStaticSplitter\n"); + return FALSE; + } + + // add the first splitter pane - the campaign input form in column 0 + if (!m_splitter.CreateView(0, 0, RUNTIME_CLASS(campaign_editor), CSize(0, 0), pContext)) + { + TRACE0("Failed to create first pane\n"); + return FALSE; + } + + // add the second splitter pane - the campaign tree view in column 1 + if (!m_splitter.CreateView(0, 1, RUNTIME_CLASS(campaign_tree_view), CSize(240, 100), pContext)) + { + TRACE0("Failed to create second pane\n"); + return FALSE; + } + + Campaign_tree_formp = (campaign_editor *) m_splitter.GetPane(0, 0); + Campaign_tree_viewp = (campaign_tree_view *) m_splitter.GetPane(0, 1); + s = Campaign_tree_formp->GetTotalSize(); + m_splitter.SetColumnInfo(0, s.cx, 0); + m_splitter.SetColumnInfo(1, 0, 0); + m_splitter.RecalcLayout(); + + // activate the input view + SetActiveView(Campaign_tree_formp); + OnCpgnFileNew(); +// Campaign_tree_formp->load_campaign(); + Fred_main_wnd->EnableWindow(FALSE); + return TRUE; +} + +void campaign_tree_wnd::OnUpdateCpgnFileOpen(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(); +} + +void campaign_tree_wnd::OnCpgnFileOpen() +{ + CString name; + + if (Campaign_modified) + if (save_modified()) + return; + + CFileDialog dlg(TRUE, "fc2", NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "FreeSpace Campaign files (*.fc2)|*.fc2||", this); + if (dlg.DoModal() == IDOK) + { + name = dlg.GetFileName(); + if (strlen(name) > MAX_FILENAME_LEN - 1) { + MessageBox("Filename is too long", "Error"); + return; + } + + if (!strlen(name)) + return; + + string_copy(Campaign.filename, name, MAX_FILENAME_LEN); + Campaign_tree_formp->load_campaign(); + } +} + +void campaign_tree_wnd::OnDestroy() +{ + CString str; + + OnCpgnFileNew(); + Fred_main_wnd->EnableWindow(TRUE); +// if (!Bypass_clear_mission) +// create_new_mission(); + str = FREDDoc_ptr->GetPathName(); + if (str.IsEmpty()) + create_new_mission(); + else + FREDDoc_ptr->OnOpenDocument(str); + + CFrameWnd::OnDestroy(); + Campaign_wnd = NULL; + Fred_main_wnd->SetFocus(); +} + +void campaign_tree_wnd::OnCpgnFileSave() +{ + CFred_mission_save save; + + Campaign_tree_formp->update(); + if (!Campaign.filename[0]) { + OnCpgnFileSaveAs(); + return; + } + + // sanity checking for multiplayer + /* + if ( Campaign.type == TYPE_MULTI_PLAYER ) { + + if ( (Campaign.mc_info.min_players < 0) || (Campaign.mc_info.min_players > Campaign.mc_info.max_players) ) { + MessageBox("Min players must be > 0 and <= max players", "Error", MB_OK | MB_ICONEXCLAMATION); + return; + } + if ( (Campaign.mc_info.max_players < 0) || (Campaign.mc_info.max_players > MAX_PLAYERS) ) { + char buf[256]; + + sprintf(buf, "Max players must be > 0 and <= %d", MAX_PLAYERS ); + MessageBox(buf, "Error", MB_OK | MB_ICONEXCLAMATION); + return; + } + if ( Campaign.mc_info.max_players < Campaign.mc_info.min_players ) { + MessageBox("Max Players must be greater than min players", "Error", MB_OK | MB_ICONEXCLAMATION); + return; + } + } + */ + + if (save.save_campaign_file(Campaign.filename)) + { + MessageBox("An error occured while saving!", "Error", MB_OK | MB_ICONEXCLAMATION); + return; + } + + Campaign_modified = 0; + return; +} + +void campaign_tree_wnd::OnCpgnFileSaveAs() +{ + char *old_name = NULL; + CString name; + CFred_mission_save save; + + Campaign_tree_formp->update(); + if (Campaign.filename[0]) + old_name = Campaign.filename; + + CFileDialog dlg(FALSE, "fc2", old_name, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "FreeSpace Campaign files (*.fc2)|*.fc2||", this); + if (dlg.DoModal() == IDOK) + { + name = dlg.GetFileName(); + if (strlen(name) > MAX_FILENAME_LEN - 1) { + MessageBox("Filename is too long", "Error"); + return; + } + + if (!strlen(name)){ + return; + } + + string_copy(Campaign.filename, name, MAX_FILENAME_LEN); + if (save.save_campaign_file(Campaign.filename)) + { + MessageBox("An error occured while saving!", "Error", MB_OK | MB_ICONEXCLAMATION); + return; + } + + Campaign_modified = 0; + } +} + +void campaign_tree_wnd::OnCpgnFileNew() +{ + if (Campaign_modified) + if (save_modified()) + return; + + Campaign.filename[0] = 0; + Campaign.num_missions = 0; + Campaign.num_players = 0; + strcpy(Campaign.name, "Unnamed"); + Campaign_tree_viewp->free_links(); + Campaign_tree_formp->initialize(); + Campaign_modified = 0; +} + +void campaign_tree_wnd::OnClose() +{ + if (Campaign_modified) + if (save_modified()) + return; + + CFrameWnd::OnClose(); +} + +void campaign_tree_wnd::OnClose2() +{ + if (Campaign_modified) + if (save_modified()) + return; + + DestroyWindow(); +} + +// returns 0 for success and 1 for cancel +int campaign_tree_wnd::save_modified() +{ + int r; + + r = MessageBox("This campaign has been modified. Save changes first?", "Campaign Modified", + MB_YESNOCANCEL | MB_ICONQUESTION); + + if (r == IDCANCEL) + return 1; + + if (r == IDYES) { + OnCpgnFileSave(); + if (Campaign_modified) // error occured in saving. + return 1; + } + + Campaign_modified = 0; + return 0; +} + +void campaign_tree_wnd::OnErrorChecker() +{ + Campaign_tree_formp->save_tree(0); + error_checker(); + if (!g_err) + MessageBox("No errors detected in campaign", "Woohoo!"); +} + +int campaign_tree_wnd::error_checker() +{ + int i, j, z; + int mcount[MAX_CAMPAIGN_MISSIONS], true_at[MAX_CAMPAIGN_MISSIONS]; + + for (i=0; i= Campaign.num_missions) ) + return internal_error("Branch #%d has illegal source mission", i); + if ( (Links[i].to < -1) || (Links[i].to >= Campaign.num_missions) ) + return internal_error("Branch #%d has illegal target mission", i); + Sexp_useful_number = Links[i].from; + if (fred_check_sexp(Links[i].sexp, OPR_BOOL, "formula of branch #%d", i)) + return -1; + + z = Links[i].from; + mcount[z]++; + if (Links[i].sexp == Locked_sexp_false) + if (error("Mission \"%s\" branch %d is always false", Campaign.missions[z].name, mcount[z])) + return 1; + + if (Links[i].sexp == Locked_sexp_true) { + if (true_at[z] >= 0) + if (error("Mission \"%s\" branch %d is true but is not last branch", Campaign.missions[z].name, true_at[z])) + return 1; + + true_at[z] = mcount[z]; + } + } + + // check that all missions in a multiplayer game have the same number of players + if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) { + for (i = 0; i < Campaign.num_missions; i++ ) { + mission a_mission; + + get_mission_info(Campaign.missions[i].name, &a_mission); + if ( a_mission.num_players != Campaign.num_players ) { + if ( error("Mission \"%s\" has %d players. Multiplayer campaign allows %d", Campaign.missions[i].name, a_mission.num_players, Campaign.num_players) ) + return 1; + } + } + } + + for (i=0; i 1) + return internal_error("More than one top level mission present in tree"); + + return 0; +} + +int campaign_tree_wnd::error(char *msg, ...) +{ + char buf[2048]; + va_list args; + + g_err++; + va_start(args, msg); + vsprintf(buf, msg, args); + va_end(args); + + if (MessageBox(buf, "Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDOK) + return 0; + + return 1; +} + +int campaign_tree_wnd::internal_error(char *msg, ...) +{ + char buf[2048], buf2[2048]; + va_list args; + + g_err++; + va_start(args, msg); + vsprintf(buf, msg, args); + va_end(args); + + sprintf(buf2, "%s\n\nThis is an internal error. Please let Hoffoss\n" + "know about this so he can fix it. Click cancel to debug.", buf); + + if (MessageBox(buf2, "Internal Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL) + Int3(); // drop to debugger so the problem can be analyzed. + + return -1; +} + +int campaign_tree_wnd::fred_check_sexp(int sexp, int type, char *msg, ...) +{ + char buf[512], buf2[2048], buf3[4096]; + int err = 0, z, faulty_node; + va_list args; + + va_start(args, msg); + vsprintf(buf, msg, args); + va_end(args); + + if (sexp == -1) + return 0; + + z = check_sexp_syntax(sexp, type, 1, &faulty_node, SEXP_MODE_CAMPAIGN); + if (!z) + return 0; + + convert_sexp_to_string(sexp, buf2, SEXP_ERROR_CHECK_MODE); + sprintf(buf3, "Error in %s: %s\n\nIn sexpression: %s\n\n(Error appears to be: %s)", + buf, sexp_error_message(z), buf2, Sexp_nodes[faulty_node].text); + + if (z < 0 && z > -100) + err = 1; + + if (err) + return internal_error(buf3); + + if (error(buf3)) + return 1; + + return 0; +} + +// code to deal with the initial ships that a player can choose +void campaign_tree_wnd::OnInitialShips() +{ + InitialShips isd(Campaign_tree_formp); + + isd.m_initial_items = INITIAL_SHIPS; + isd.DoModal(); +} + +void campaign_tree_wnd::OnInitialWeapons() +{ + InitialShips isd(Campaign_tree_formp); + + isd.m_initial_items = INITIAL_WEAPONS; + isd.DoModal(); +} diff --git a/src/fred2/cmdbrief.cpp b/src/fred2/cmdbrief.cpp new file mode 100644 index 0000000..de4ce4f --- /dev/null +++ b/src/fred2/cmdbrief.cpp @@ -0,0 +1,340 @@ +/* + * $Logfile: /Freespace2/code/FRED2/CmdBrief.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Command Briefing Editor + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 8 4/30/98 8:23p John + * Fixed some bugs with Fred caused by my new cfile code. + * + * 7 4/22/98 9:56a Sandeep + * + * 6 4/20/98 4:40p Hoffoss + * Added a button to 4 editors to play the chosen wave file. + * + * 5 4/03/98 12:39p Hoffoss + * Changed starting directory for browse buttons in several editors. + * + * 4 3/19/98 4:24p Hoffoss + * Added remaining support for command brief screen (ANI and WAVE file + * playing). + * + * 3 3/06/98 2:36p Hoffoss + * Placed correct text size limits on edit boxes. + * + * 2 3/05/98 3:59p Hoffoss + * Added a bunch of new command brief stuff, and asteroid initialization + * to Fred. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include +#include "fred.h" +#include "cmdbrief.h" +#include "cfile.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// cmd_brief_dlg dialog + +cmd_brief_dlg::cmd_brief_dlg(CWnd* pParent /*=NULL*/) + : CDialog(cmd_brief_dlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(cmd_brief_dlg) + m_ani_filename = _T(""); + m_text = _T(""); + m_stage_title = _T(""); + m_wave_filename = _T(""); + //}}AFX_DATA_INIT +} + +void cmd_brief_dlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(cmd_brief_dlg) + DDX_Text(pDX, IDC_ANI_FILENAME, m_ani_filename); + DDX_Text(pDX, IDC_TEXT, m_text); + DDX_Text(pDX, IDC_STAGE_TITLE, m_stage_title); + DDX_Text(pDX, IDC_WAVE_FILENAME, m_wave_filename); + //}}AFX_DATA_MAP + + DDV_MaxChars(pDX, m_text, CMD_BRIEF_TEXT_MAX - 1); + DDV_MaxChars(pDX, m_ani_filename, MAX_FILENAME_LEN - 1); + DDV_MaxChars(pDX, m_wave_filename, MAX_FILENAME_LEN - 1); +} + +BEGIN_MESSAGE_MAP(cmd_brief_dlg, CDialog) + //{{AFX_MSG_MAP(cmd_brief_dlg) + ON_BN_CLICKED(IDC_NEXT, OnNext) + ON_BN_CLICKED(IDC_PREV, OnPrev) + ON_BN_CLICKED(IDC_ADD_STAGE, OnAddStage) + ON_BN_CLICKED(IDC_INSERT_STAGE, OnInsertStage) + ON_BN_CLICKED(IDC_DELETE_STAGE, OnDeleteStage) + ON_BN_CLICKED(IDC_BROWSE_ANI, OnBrowseAni) + ON_BN_CLICKED(IDC_BROWSE_WAVE, OnBrowseWave) + ON_BN_CLICKED(IDC_PLAY, OnPlay) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// cmd_brief_dlg message handlers + +BOOL cmd_brief_dlg::OnInitDialog() +{ + Cur_cmd_brief = Cmd_briefs; // default to first cmd briefing + m_cur_stage = 0; + last_cmd_brief = NULL; + + CDialog::OnInitDialog(); + m_play_bm.LoadBitmap(IDB_PLAY); + ((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm); + + update_data(); + return TRUE; +} + +void cmd_brief_dlg::update_data(int update) +{ + int enable; + + if (update) + UpdateData(TRUE); + + // save previously editing data before we load over it. + if (last_cmd_brief && m_last_stage >= 0 && m_last_stage < last_cmd_brief->num_stages) { + char buf[CMD_BRIEF_TEXT_MAX]; + + if (last_cmd_brief->stage[m_last_stage].text) + free(last_cmd_brief->stage[m_last_stage].text); + + deconvert_multiline_string(buf, m_text, CMD_BRIEF_TEXT_MAX); + last_cmd_brief->stage[m_last_stage].text = strdup(buf); + string_copy(last_cmd_brief->stage[m_last_stage].ani_filename, m_ani_filename, MAX_FILENAME_LEN); + string_copy(last_cmd_brief->stage[m_last_stage].wave_filename, m_wave_filename, MAX_FILENAME_LEN); + } + + // load data of new stage into dialog + if (Cur_cmd_brief && Cur_cmd_brief->num_stages > 0) { + if (m_cur_stage < 0 || m_cur_stage >= Cur_cmd_brief->num_stages) + m_cur_stage = 0; + + m_stage_title.Format("Stage %d of %d", m_cur_stage + 1, Cur_cmd_brief->num_stages); + m_text = convert_multiline_string(Cur_cmd_brief->stage[m_cur_stage].text); + m_ani_filename = Cur_cmd_brief->stage[m_cur_stage].ani_filename; + m_wave_filename = Cur_cmd_brief->stage[m_cur_stage].wave_filename; + enable = TRUE; + + } else { + m_stage_title = _T("No stages"); + m_text = _T(""); + m_ani_filename = _T(""); + m_wave_filename = _T(""); + enable = FALSE; + m_cur_stage = -1; + } + + if (m_cur_stage < Cur_cmd_brief->num_stages - 1) + GetDlgItem(IDC_NEXT) -> EnableWindow(enable); + else + GetDlgItem(IDC_NEXT) -> EnableWindow(FALSE); + + if (m_cur_stage) + GetDlgItem(IDC_PREV) -> EnableWindow(enable); + else + GetDlgItem(IDC_PREV) -> EnableWindow(FALSE); + + if (Cur_cmd_brief->num_stages >= CMD_BRIEF_STAGES_MAX) + GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(FALSE); + else + GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(TRUE); + + if (Cur_cmd_brief->num_stages) { + GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(enable); + GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(enable); + + } else { + GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(FALSE); + GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(FALSE); + } + + GetDlgItem(IDC_WAVE_FILENAME) -> EnableWindow(enable); + GetDlgItem(IDC_ANI_FILENAME) -> EnableWindow(enable); + GetDlgItem(IDC_BROWSE_ANI) -> EnableWindow(enable); + GetDlgItem(IDC_BROWSE_WAVE) -> EnableWindow(enable); + GetDlgItem(IDC_TEXT) -> EnableWindow(enable); + + UpdateData(FALSE); + + last_cmd_brief = Cur_cmd_brief; + m_last_stage = m_cur_stage; +} + +void cmd_brief_dlg::OnOK() +{ + update_data(); + CDialog::OnOK(); +} + +void cmd_brief_dlg::OnNext() +{ + m_cur_stage++; + update_data(); +} + +void cmd_brief_dlg::OnPrev() +{ + m_cur_stage--; + update_data(); +} + +void cmd_brief_dlg::OnAddStage() +{ + int i; + + if (Cur_cmd_brief->num_stages >= CMD_BRIEF_STAGES_MAX) + return; + + m_cur_stage = i = Cur_cmd_brief->num_stages++; + copy_stage(i - 1, i); + update_data(1); +} + +void cmd_brief_dlg::OnInsertStage() +{ + int i, z; + + if (Cur_cmd_brief->num_stages >= CMD_BRIEF_STAGES_MAX) + return; + + if (!Cur_cmd_brief->num_stages) { + OnAddStage(); + return; + } + + z = m_cur_stage; + m_cur_stage = -1; + update_data(1); + for (i=Cur_cmd_brief->num_stages; i>z; i--) + Cur_cmd_brief->stage[i] = Cur_cmd_brief->stage[i - 1]; + + Cur_cmd_brief->num_stages++; + copy_stage(z, z + 1); + m_cur_stage = z; + update_data(0); +} + +void cmd_brief_dlg::OnDeleteStage() +{ + int i, z; + + if (m_cur_stage < 0) + return; + + Assert(Cur_cmd_brief->num_stages); + z = m_cur_stage; + m_cur_stage = -1; + update_data(1); + if (Cur_cmd_brief->stage[z].text) + free(Cur_cmd_brief->stage[z].text); + + for (i=z+1; inum_stages; i++) + Cur_cmd_brief->stage[i-1] = Cur_cmd_brief->stage[i]; + + Cur_cmd_brief->num_stages--; + m_cur_stage = z; + if (m_cur_stage >= Cur_cmd_brief->num_stages) + m_cur_stage = Cur_cmd_brief->num_stages - 1; + + update_data(0); +} + +void cmd_brief_dlg::copy_stage(int from, int to) +{ + if ((from < 0) || (from >= Cur_cmd_brief->num_stages)) { + Cur_cmd_brief->stage[to].text = strdup(""); + strcpy(Cur_cmd_brief->stage[to].ani_filename, ""); + strcpy(Cur_cmd_brief->stage[to].wave_filename, "none"); + return; + } + + Cur_cmd_brief->stage[to] = Cur_cmd_brief->stage[from]; + Cur_cmd_brief->stage[to].text = strdup(Cur_cmd_brief->stage[from].text); +} + +void cmd_brief_dlg::OnBrowseAni() +{ + int z; + CString name; + + UpdateData(TRUE); + z = cfile_push_chdir(CF_TYPE_INTERFACE); + CFileDialog dlg(TRUE, "ani", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, + "Ani Files (*.ani)|*.ani|Avi Files (*.avi)|*.avi|Both (*.ani, *.avi)|*.ani;*.avi||"); + + if (dlg.DoModal() == IDOK) { + m_ani_filename = dlg.GetFileName(); + UpdateData(FALSE); + } + + if (!z) + cfile_pop_dir(); +} + +void cmd_brief_dlg::OnBrowseWave() +{ + int z; + CString name; + + UpdateData(TRUE); + z = cfile_push_chdir(CF_TYPE_VOICE_CMD_BRIEF); + CFileDialog dlg(TRUE, "wav", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, + "Wave Files (*.wav)|*.wav||"); + + if (dlg.DoModal() == IDOK) { + m_wave_filename = dlg.GetFileName(); + UpdateData(FALSE); + } + + if (!z) + cfile_pop_dir(); +} + +BOOL cmd_brief_dlg::DestroyWindow() +{ + m_play_bm.DeleteObject(); + return CDialog::DestroyWindow(); +} + +void cmd_brief_dlg::OnPlay() +{ + char path[MAX_PATH_LEN + 1]; + GetDlgItem(IDC_WAVE_FILENAME)->GetWindowText(m_wave_filename); + + int size, offset; + cf_find_file_location((char *) (LPCSTR) m_wave_filename, CF_TYPE_ANY, path, &size, &offset ); + + PlaySound(path, NULL, SND_ASYNC | SND_FILENAME); +} diff --git a/src/fred2/createwingdlg.cpp b/src/fred2/createwingdlg.cpp new file mode 100644 index 0000000..9bf6628 --- /dev/null +++ b/src/fred2/createwingdlg.cpp @@ -0,0 +1,108 @@ +// CreateWingDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "createwingdlg.h" +#include "object.h" +#include "linklist.h" +#include "parselo.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// create_wing_dlg dialog + +create_wing_dlg::create_wing_dlg(CWnd* pParent /*=NULL*/) + : CDialog(create_wing_dlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(create_wing_dlg) + m_name = _T(""); + //}}AFX_DATA_INIT +} + +void create_wing_dlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(create_wing_dlg) + DDX_Text(pDX, IDC_NAME, m_name); + //}}AFX_DATA_MAP + + DDV_MaxChars(pDX, m_name, NAME_LENGTH - 4); +} + +BEGIN_MESSAGE_MAP(create_wing_dlg, CDialog) + //{{AFX_MSG_MAP(create_wing_dlg) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// create_wing_dlg message handlers + +void create_wing_dlg::OnOK() +{ + char msg[512]; + int i; + object *ptr; + + UpdateData(TRUE); + UpdateData(TRUE); + m_name = drop_white_space((char *)(LPCSTR) m_name); + if (m_name.IsEmpty()) { + MessageBox("You must give a name before you can continue."); + return; + } + + if (!strnicmp(m_name, "player ", 7)) { + MessageBox("Wing names can't start with the word 'player'"); + return; + } + + for (i=0; itype == OBJ_SHIP) { + i = ptr->instance; + if (!strnicmp(m_name, Ships[i].ship_name, strlen(m_name))) { + char *namep; + + namep = Ships[i].ship_name + strlen(m_name); + if (*namep == ' ') { + namep++; + while (*namep) { + if (!isdigit(*namep)) + break; + + namep++; + } + } + + if (!*namep) { + MessageBox("This wing name is already being used by a ship"); + return; + } + } + } + + ptr = GET_NEXT(ptr); + } + + for (i=0; i +#include "fred.h" +#include "debriefingeditordlg.h" +#include "freddoc.h" +#include "missionbriefcommon.h" +#include "sexp.h" +#include "cfile.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// debriefing_editor_dlg dialog + +debriefing_editor_dlg::debriefing_editor_dlg(CWnd* pParent /*=NULL*/) + : CDialog(debriefing_editor_dlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(debriefing_editor_dlg) + m_text = _T(""); + m_voice = _T(""); + m_stage_title = _T(""); + m_rec_text = _T(""); + m_current_debriefing = -1; + //}}AFX_DATA_INIT + + modified = 0; + m_cur_stage = 0; + m_last_stage = -1; + select_sexp_node = -1; +} + +void debriefing_editor_dlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(debriefing_editor_dlg) + DDX_Control(pDX, IDC_TREE, m_tree); + DDX_Text(pDX, IDC_TEXT, m_text); + DDX_Text(pDX, IDC_VOICE, m_voice); + DDX_Text(pDX, IDC_STAGE_TITLE, m_stage_title); + DDX_Text(pDX, IDC_REC_TEXT, m_rec_text); + //}}AFX_DATA_MAP + + DDV_MaxChars(pDX, m_text, MAX_BRIEF_LEN - 1); + DDV_MaxChars(pDX, m_voice, MAX_FILENAME_LEN - 1); + DDV_MaxChars(pDX, m_rec_text, MAX_RECOMMENDATION_LEN - 1); +} + +BEGIN_MESSAGE_MAP(debriefing_editor_dlg, CDialog) + //{{AFX_MSG_MAP(debriefing_editor_dlg) + ON_BN_CLICKED(IDC_NEXT, OnNext) + ON_BN_CLICKED(IDC_PREV, OnPrev) + ON_BN_CLICKED(IDC_BROWSE, OnBrowse) + ON_BN_CLICKED(IDC_ADD_STAGE, OnAddStage) + ON_BN_CLICKED(IDC_DELETE_STAGE, OnDeleteStage) + ON_BN_CLICKED(IDC_INSERT_STAGE, OnInsertStage) + ON_NOTIFY(NM_RCLICK, IDC_TREE, OnRclickTree) + ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE, OnBeginlabeleditTree) + ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE, OnEndlabeleditTree) + ON_WM_CLOSE() + ON_WM_INITMENU() + ON_BN_CLICKED(IDC_PLAY, OnPlay) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// debriefing_editor_dlg message handlers + +void debriefing_editor_dlg::OnInitMenu(CMenu* pMenu) +{ + int i; + CMenu *m; + + // disable any items we should disable + m = pMenu->GetSubMenu(0); + + // uncheck all menu items + for (i = 0; i < Num_teams; i++ ) + m->CheckMenuItem( i, MF_BYPOSITION | MF_UNCHECKED ); + + for ( i = Num_teams; i < MAX_TEAMS; i++ ) + m->EnableMenuItem(i, MF_BYPOSITION | MF_GRAYED); + + + // put a check next to the currently selected item + m->CheckMenuItem(m_current_debriefing, MF_BYPOSITION | MF_CHECKED ); + + CDialog::OnInitMenu(pMenu); +} + +BOOL debriefing_editor_dlg::OnInitDialog() +{ + int i, n; + + CDialog::OnInitDialog(); + m_play_bm.LoadBitmap(IDB_PLAY); + ((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm); + + m_current_debriefing = 0; + UpdateData(FALSE); + + Debriefing = &Debriefings[m_current_debriefing]; + + m_tree.link_modified(&modified); // provide way to indicate trees are modified in dialog + n = m_tree.select_sexp_node = select_sexp_node; + select_sexp_node = -1; + if (n != -1) { + for (i=0; inum_stages; i++) + if (query_node_in_sexp(n, Debriefing->stages[i].formula)) + break; + + if (i < Debriefing->num_stages) { + m_cur_stage = i; + update_data(); + GetDlgItem(IDC_TREE) -> SetFocus(); + m_tree.hilite_item(m_tree.select_sexp_node); + set_modified(); + return FALSE; + } + } + + update_data(); + set_modified(); + + // hard coded stuff to deal with the multiple briefings per mission. + + return TRUE; +} + +void debriefing_editor_dlg::update_data(int update) +{ + int enable, save_debriefing; + debrief_stage *ptr; + + save_debriefing = m_current_debriefing; + + if (update) + UpdateData(TRUE); + + // based on the game type, enable the multiple briefings combo box (or disable it) + + // set up the pointer to the briefing that we are editing + if ( save_debriefing != m_current_debriefing ) + Debriefing = &Debriefings[save_debriefing]; + else + Debriefing = &Debriefings[m_current_debriefing]; + + if (m_last_stage >= 0) { + ptr = &Debriefing->stages[m_last_stage]; + if (ptr->formula >= 0) + free_sexp2(ptr->formula); + + ptr->formula = m_tree.save_tree(); + deconvert_multiline_string(ptr->new_text, m_text, MAX_DEBRIEF_LEN); + deconvert_multiline_string(ptr->new_recommendation_text, m_rec_text, MAX_RECOMMENDATION_LEN); + string_copy(ptr->voice, m_voice, MAX_FILENAME_LEN); + } + + // now get new stage data + if ((m_cur_stage >= 0) && (m_cur_stage < Debriefing->num_stages)) { + ptr = &Debriefing->stages[m_cur_stage]; + m_stage_title.Format("Stage %d of %d", m_cur_stage + 1, Debriefing->num_stages); + m_tree.load_tree(ptr->formula); + m_text = convert_multiline_string(ptr->new_text); + m_rec_text = convert_multiline_string(ptr->new_recommendation_text); + m_voice = ptr->voice; + enable = TRUE; + + } else { + m_stage_title = _T("No stages"); + m_tree.clear_tree(); + m_text = _T(""); + m_rec_text = _T(""); + m_voice = _T(""); + enable = FALSE; + m_cur_stage = -1; + } + + if (m_cur_stage == Debriefing->num_stages - 1) + GetDlgItem(IDC_NEXT) -> EnableWindow(FALSE); + else + GetDlgItem(IDC_NEXT) -> EnableWindow(enable); + + if (m_cur_stage) + GetDlgItem(IDC_PREV) -> EnableWindow(enable); + else + GetDlgItem(IDC_PREV) -> EnableWindow(FALSE); + + if (Debriefing->num_stages >= MAX_DEBRIEF_STAGES) + GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(FALSE); + else + GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(TRUE); + + if (Debriefing->num_stages) { + GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(enable); + GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(enable); + + } else { + GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(FALSE); + GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(FALSE); + } + + GetDlgItem(IDC_VOICE) -> EnableWindow(enable); + GetDlgItem(IDC_BROWSE) -> EnableWindow(enable); + GetDlgItem(IDC_TEXT) -> EnableWindow(enable); + GetDlgItem(IDC_REC_TEXT) -> EnableWindow(enable); + GetDlgItem(IDC_TREE) -> EnableWindow(enable); + + m_last_stage = m_cur_stage; + UpdateData(FALSE); + + #ifndef NDEBUG + count_free_sexp_nodes(); + #endif +} + +void debriefing_editor_dlg::OnNext() +{ + m_cur_stage++; + update_data(); +} + +void debriefing_editor_dlg::OnPrev() +{ + m_cur_stage--; + update_data(); +} + +void debriefing_editor_dlg::OnBrowse() +{ + int z; + CString name; + + UpdateData(TRUE); + + if (The_mission.game_type & MISSION_TYPE_TRAINING) + z = cfile_push_chdir(CF_TYPE_VOICE_TRAINING); + else + z = cfile_push_chdir(CF_TYPE_VOICE_DEBRIEFINGS); + + CFileDialog dlg(TRUE, "wav", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, + "Wave Files (*.wav)|*.wav||"); + + if (dlg.DoModal() == IDOK) { + m_voice = dlg.GetFileName(); + UpdateData(FALSE); + } + + if (!z) + cfile_pop_dir(); +} + +void debriefing_editor_dlg::OnAddStage() +{ + int i; + + if (Debriefing->num_stages >= MAX_DEBRIEF_STAGES) + return; + + m_cur_stage = i = Debriefing->num_stages++; + copy_stage(i - 1, i, 1); + update_data(1); +} + +void debriefing_editor_dlg::OnDeleteStage() +{ + int i, z; + + if (m_cur_stage < 0) + return; + + Assert(Debriefing->num_stages); + z = m_cur_stage; + m_cur_stage = -1; + update_data(1); + for (i=z+1; inum_stages; i++) { + copy_stage(i, i - 1); + } + + Debriefing->num_stages--; + m_cur_stage = z; + if (m_cur_stage >= Debriefing->num_stages) + m_cur_stage = Debriefing->num_stages - 1; + + update_data(0); +} + +void debriefing_editor_dlg::OnInsertStage() +{ + int i, z; + + if (Debriefing->num_stages >= MAX_DEBRIEF_STAGES) + return; + + if (!Debriefing->num_stages) { + OnAddStage(); + return; + } + + z = m_cur_stage; + m_cur_stage = -1; + update_data(1); + for (i=Debriefing->num_stages; i>z; i--) { + copy_stage(i - 1, i); + } + + Debriefing->num_stages++; + copy_stage(z, z + 1); + Debriefing->stages[z].formula = -1; + m_cur_stage = z; + update_data(0); +} + +void debriefing_editor_dlg::copy_stage(int from, int to, int clear_formula) +{ + if ((from < 0) || (from >= Debriefing->num_stages)) { + strcpy(Debriefing->stages[to].new_text, ""); + strcpy(Debriefing->stages[to].voice, "none.wav"); + Debriefing->stages[to].formula = -1; + return; + } + + + if (clear_formula) + Debriefing->stages[to].formula = -1; + else + Debriefing->stages[to].formula = Debriefing->stages[from].formula; + + strcpy( Debriefing->stages[to].new_text, Debriefing->stages[from].new_text ); + strcpy( Debriefing->stages[to].voice, Debriefing->stages[from].voice ); + strcpy( Debriefing->stages[to].new_recommendation_text, Debriefing->stages[from].new_recommendation_text ); +} + +void debriefing_editor_dlg::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + m_tree.right_clicked(); + *pResult = 0; +} + +void debriefing_editor_dlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1) { + *pResult = 0; + modified = 1; + + } else + *pResult = 1; +} + +void debriefing_editor_dlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + *pResult = m_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText); +} + +void debriefing_editor_dlg::OnClose() +{ + m_cur_stage = -1; + update_data(1); + CDialog::OnClose(); +} + +void debriefing_editor_dlg::OnOK() +{ +} + +BOOL debriefing_editor_dlg::OnCommand(WPARAM wParam, LPARAM lParam) +{ + int id; + + // deal with figuring out menu stuff + id = LOWORD(wParam); + if ( (id >= ID_TEAM_1) && (id < ID_TEAM_3) ) { + update_data(1); + + // set the current debriefing + m_current_debriefing = id - ID_TEAM_1; + + // put user back at first stage for this team (or no current stage is there are none). + Debriefing = &Debriefings[m_current_debriefing]; + if ( Debriefing->num_stages > 0 ) + m_cur_stage = 0; + else + m_cur_stage = -1; + + m_last_stage = -1; + update_data(0); + } + + return CDialog::OnCommand(wParam, lParam); +} + +BOOL debriefing_editor_dlg::DestroyWindow() +{ + m_play_bm.DeleteObject(); + return CDialog::DestroyWindow(); +} + +void debriefing_editor_dlg::OnPlay() +{ + char path[MAX_PATH_LEN + 1]; + GetDlgItem(IDC_VOICE)->GetWindowText(m_voice); + + int size, offset; + cf_find_file_location((char *) (LPCSTR) m_voice, CF_TYPE_ANY, path, &size, &offset ); + + PlaySound(path, NULL, SND_ASYNC | SND_FILENAME); +} diff --git a/src/fred2/dialog1.cpp b/src/fred2/dialog1.cpp new file mode 100644 index 0000000..196dc4a --- /dev/null +++ b/src/fred2/dialog1.cpp @@ -0,0 +1,66 @@ +/* + * $Logfile: /Freespace2/code/FRED2/dialog1.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * key usage summary screen (actually a dialog box, but it's not very interactive) + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "dialog1.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// dialog1 dialog + +dialog1::dialog1(CWnd* pParent /*=NULL*/) + : CDialog(dialog1::IDD, pParent) +{ + //{{AFX_DATA_INIT(dialog1) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + +void dialog1::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(dialog1) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(dialog1, CDialog) + //{{AFX_MSG_MAP(dialog1) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// dialog1 message handlers diff --git a/src/fred2/dumpstats.cpp b/src/fred2/dumpstats.cpp new file mode 100644 index 0000000..46745bf --- /dev/null +++ b/src/fred2/dumpstats.cpp @@ -0,0 +1,908 @@ +// DumpStats.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "dumpstats.h" +#include "starfield.h" +#include "neb.h" +#include "linklist.h" +#include "object.h" +#include "jumpnode.h" +#include "missiongoals.h" +#include "eventmusic.h" +#include "asteroid.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// DumpStats dialog + + +DumpStats::DumpStats(CWnd* pParent /*=NULL*/) + : CDialog(DumpStats::IDD, pParent) +{ + //{{AFX_DATA_INIT(DumpStats) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void DumpStats::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(DumpStats) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(DumpStats, CDialog) + //{{AFX_MSG_MAP(DumpStats) + ON_BN_CLICKED(IDC_DUMP_TO_FILE, OnDumpToFile) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// DumpStats message handlers + +BOOL DumpStats::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CString buffer; + int i; + + // get author, title, etc + get_mission_stats(buffer); + + // get nebula, stars, etc. + get_background_stats(buffer); + + // get number or ships, waypoints, start points, etc. + get_object_stats(buffer); + + // get objectives / goals + get_objectives_and_goals(buffer); + + // get ship selection for player wings + get_ship_weapon_selection(buffer); + + // get messaging info + get_messaging_info(buffer); + + // get species ship breakdown + get_species_ship_breakdown(buffer); + + // get default loadouts + get_default_ship_loadouts(buffer); + + int num_tab_stops = 5; + int tab_stops[5]; + for (i=0; i<5; i++) { + tab_stops[i] = (i+1) * 16; + } + + ((CEdit*) GetDlgItem(IDC_STATS_TEXT))->SetTabStops(num_tab_stops, tab_stops); + ((CEdit*) GetDlgItem(IDC_STATS_TEXT))->SetWindowText(buffer); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void DumpStats::OnDumpToFile() +{ + // TODO: Add your control notification handler code here + + // get dump from window + CString buffer; + ((CEdit*) GetDlgItem(IDC_STATS_TEXT))->GetWindowText(buffer); + + CString dump_filename; + dump_filename.Format("%s.dmp", Mission_filename); + + CFILE *fp; + + fp = cfopen((char *)LPCTSTR(dump_filename), "wt", CFILE_NORMAL, CF_TYPE_MISSIONS); + cfputs((char *)LPCTSTR(buffer), fp); + cfclose(fp); +} + +void DumpStats::get_mission_stats(CString &buffer) +{ + CString temp; + + // Mission info + buffer += "\t MISSION INFO\r\n"; + + temp.Format("Title: %s\r\n", The_mission.name); + buffer += temp; + + temp.Format("Filename: %s\r\n", Mission_filename); + buffer += temp; + + temp.Format("Author: %s\r\n", The_mission.author); + buffer += temp; + + temp.Format("Description: %s\r\n", The_mission.mission_desc); + buffer += temp; + + temp.Format("Notes: %s\r\n", The_mission.notes); + buffer += temp; + + if (The_mission.game_type & MISSION_TYPE_SINGLE) { + temp.Format("Mission type: Single Player\r\n"); + } else if (The_mission.game_type & MISSION_TYPE_MULTI_COOP) { + temp.Format("Mission type: Multi Coop\r\n"); + } else if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) { + temp.Format("Mission type: Multi Team vs. Team\r\n"); + } else if (The_mission.game_type & MISSION_TYPE_MULTI_DOGFIGHT) { + temp.Format("Mission type: Dogfight\r\n"); + } + buffer += temp; + + if (The_mission.game_type & MISSION_TYPE_MULTI) { + temp.Format("\tNum respawns: %d\r\n", The_mission.num_respawns); + buffer += temp; + } + + if (Current_soundtrack_num >= 0) { + temp.Format("\tMusic: %s\r\n", Soundtracks[Current_soundtrack_num].name); + buffer += temp; + } + + if (The_mission.red_alert) { + buffer += "\tRed Alert\r\n"; + } + + if (The_mission.scramble) { + buffer += "\tScramble\r\n"; + } + + if (The_mission.flags & MISSION_FLAG_NO_PROMOTION) { + buffer += "\tNo Promotions\r\n"; + } + + if (The_mission.disallow_support) { + buffer += "\tNo Support ships\r\n"; + } + + temp.Format("Squadron: %s, Squadron logo: %s\r\n", The_mission.squad_name, The_mission.squad_filename); + buffer += temp; +} + +void DumpStats::get_background_stats(CString &buffer) +{ + CString temp; + int i; + + // Background + buffer += "\r\n\tBACKGROUND INFO\r\n"; + + // Num stars + temp.Format("Num_stars: %d\r\n", Num_stars); + buffer += temp; + + // Suns + temp.Format("Num_suns: %d\r\n", Num_suns); + buffer += temp; + + for (i=0; i 0) { + // active or passive + if (Asteroid_field.field_type == FT_ACTIVE) { + temp.Format("\tActive Field\r\n"); + buffer += temp; + + temp.Format("\tAsteroid Debris\r\n"); + buffer += temp; + } else { + // passive + temp.Format("\tPassive Field\r\n"); + buffer += temp; + + if (Asteroid_field.debris_genre == DG_ASTEROID) { + temp.Format("\tAsteroid Debris\r\n"); + buffer += temp; + } else { + temp.Format("\tShip Debris\r\n"); + buffer += temp; + + // species + temp.Format("\t\tSpecies: "); + for (i=0; i<3; i++) { + if (Asteroid_field.field_debris_type[i] >= 0) { + switch(Asteroid_field.field_debris_type[i]) { + case DEBRIS_TERRAN_SMALL: + case DEBRIS_TERRAN_MEDIUM: + case DEBRIS_TERRAN_LARGE: + temp += "Terran "; + break; + + case DEBRIS_VASUDAN_SMALL: + case DEBRIS_VASUDAN_MEDIUM: + case DEBRIS_VASUDAN_LARGE: + temp += "Vasudan "; + break; + + case DEBRIS_SHIVAN_SMALL: + case DEBRIS_SHIVAN_MEDIUM: + case DEBRIS_SHIVAN_LARGE: + temp += "Shivan "; + break; + } + } + } + + temp += "\r\n"; + + buffer += temp; + } + } + } + + // Nebula mission + int nebula_mission = (The_mission.flags & MISSION_FLAG_FULLNEB); + temp = "Nebula mission:"; + if (nebula_mission) { + temp += " Yes\r\n"; + } else { + temp += " No\r\n"; + } + buffer += temp; + + if (nebula_mission) { + // range + temp.Format("\tNebula awacs range: %.0f\r\n", Neb2_awacs); + buffer += temp; + + // list of poofs + for (i=0; i 0) { + temp.Format("\tNebula texture: %s\r\n", Neb2_texture_name); + buffer += temp; + } + } else { + // FS! nebula pattern + if (Nebula_index > 0) { + temp.Format("\tOld style FS1 nebula filename: %s\r\n", Nebula_filenames[Nebula_index]); + buffer += temp; + } + } + + // Subspace mission + temp = "Subspace mission:"; + if (The_mission.flags & MISSION_FLAG_SUBSPACE) { + temp += " Yes\r\n"; + } else { + temp += " No\r\n"; + } + buffer += temp; +} + +void DumpStats::get_object_stats(CString &buffer) +{ + object *objp; + int obj_type_count[MAX_OBJECT_TYPES]; + CString temp; + int num_small_ships, num_big_ships, num_huge_ships; + + memset(obj_type_count,0, sizeof(obj_type_count)); + num_small_ships = num_big_ships = num_huge_ships= 0; + + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + + // inc big ship or small ship count + if ( (objp->type == OBJ_SHIP) || (objp->type == OBJ_START) ) { + if ( Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP ) { + num_small_ships++; + } else if ( Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_BIG_SHIP ) { + num_big_ships++; + } else if ( Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP ) { + num_huge_ships++; + } + } + + obj_type_count[objp->type]++; + } + + // Statistics + buffer += "\r\n\tMISSION STATISTICS\r\n"; + + // OBJ_START is also a OBJ_SHIP + // not counting num_waves (for wings) + obj_type_count[OBJ_SHIP] += obj_type_count[OBJ_START]; + + for (int i=0; i 0) { + switch(i) { + case OBJ_SHIP: + temp.Format("Ship Count: %d\r\n", obj_type_count[i]); + buffer += temp; + break; + + case OBJ_START: + temp.Format("Start Count: %d\r\n", obj_type_count[i]); + buffer += temp; + break; + + case OBJ_WAYPOINT: + temp.Format("Waypoint Count: %d\r\n", obj_type_count[i]); + buffer += temp; + break; + + case OBJ_WING: + temp.Format("Wing Count: %d\r\n", obj_type_count[i]); + buffer += temp; + break; + + case OBJ_JUMP_NODE: + temp.Format("Jump Node Count: %d\r\n", obj_type_count[i]); + buffer += temp; + break; + + default: + Int3(); + break; + } + } + } + + buffer += "\r\nSHIPS\r\n"; + temp.Format("\tNum small ships: %d\r\n", num_small_ships); + buffer += temp; + + temp.Format("\tNum big ships: %d\r\n", num_big_ships); + buffer += temp; + + temp.Format("\tNum huge ships: %d\r\n", num_huge_ships); + buffer += temp; + + // Waypoints + int total_waypoints = 0; + buffer += "\r\nWAYPOINTS\r\n"; + for (i=0; i 0) { + temp.Format("\ttotal_waypoints: %d\r\n", total_waypoints); + buffer += temp; + } + + // Jumpnodes + int total_jumpnodes = 0; + buffer += "\r\nJUMPNODES\r\n"; + for (i=0; i 0) { + temp.Format("\ttotal_jumpnodes: %d\r\n", total_jumpnodes); + buffer += temp; + } + + + // Wings + int num_counted_wings = 0; + buffer += "\r\nWINGS\r\n"; + for (i=0; i 0) { + temp.Format("\tWing Name: %s, num_ships: %d, num_waves: %d\r\n", Wings[i].name, Wings[i].wave_count, Wings[i].num_waves); + buffer += temp; + + num_counted_wings++; + if (num_counted_wings == num_wings) { + break; + } + } + } + + // Escort + buffer += "\r\nESCORT\r\n"; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if ( (objp->type == OBJ_SHIP) || (objp->type == OBJ_START) ) { + if (Ships[objp->instance].flags & SF_ESCORT) { + temp.Format("\tShip name: %s, priority: %d\r\n", Ships[objp->instance].ship_name, Ships[objp->instance].escort_priority); + buffer += temp; + } + } + } + + // Hotkeys + buffer += "\r\nHOTKEYS\r\n"; + + // ship hotkeys + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if ( (objp->type == OBJ_SHIP) || (objp->type == OBJ_START) ) { + if (Ships[objp->instance].hotkey != -1) { + temp.Format("\tShip name: %s, hotkey: F%d\r\n", Ships[objp->instance].ship_name, (Ships[objp->instance].hotkey + 5)); + buffer += temp; + } + } + } + + // wing hotkeys + for (i=0; i 0) { + if (Wings[i].hotkey != -1) { + temp.Format("\tWing name: %s, hotkey: F%d\r\n", Wings[i].name, (Wings[i].hotkey + 5)); + buffer += temp; + } + } + } + +} + +void DumpStats::get_objectives_and_goals(CString &buffer) +{ + CString temp; + int i; + + buffer += "\r\nOBJECTIVES AND GOALS\r\n"; + + // objectives + for (i=0; i 0) { + temp.Format("\tWeapon name: %s, count %d\r\n", Weapon_info[j].name, Team_data[i].weaponry_pool[j]); + buffer += temp; + } + } + } + +} + +void DumpStats::get_messaging_info(CString &buffer) +{ + CString temp; + object *objp; + ship *shipp; + + buffer += "\r\nSHIP ACCEPTED ORDERS\r\n"; + + // go through all ships and check (.orders_accepted against default_orders) + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->type == OBJ_START || objp->type == OBJ_SHIP) { + shipp = &Ships[objp->instance]; + + if (shipp->orders_accepted != ship_get_default_orders_accepted(&Ship_info[shipp->ship_info_index])) { + temp.Format("\tShip: %s with nonstandard accepted orders\r\n", shipp->ship_name); + buffer += temp; + } + } + } +} + +void DumpStats::get_species_ship_breakdown(CString &buffer) +{ + CString temp; + int i, species; + object *objp; + ship *shipp; + + buffer += "\r\nSHIP SPECIES BREAKDOWN\r\n"; + + for (species=0; species<3; species++) { + + switch(species) { + case SPECIES_TERRAN: + buffer += "Terran\r\n"; + break; + + case SPECIES_VASUDAN: + buffer += "Vasudan\r\n"; + break; + + case SPECIES_SHIVAN: + buffer += "Shivan\r\n"; + break; + } + + + // fighter wings + buffer += "\tFighter wings:\r\n"; + for (i=0; i 0) { + int wing_leader_shipnum = Wings[i].ship_index[Wings[i].special_ship]; + if (Ship_info[Ships[wing_leader_shipnum].ship_info_index].species == species) { + if (Ship_info[Ships[wing_leader_shipnum].ship_info_index].flags & SIF_FIGHTER) { + temp.Format("\t\tWing: %s, count: %d, waves: %d, type: %s\r\n", Wings[i].name, Wings[i].wave_count, Wings[i].num_waves, Ship_info[Ships[wing_leader_shipnum].ship_info_index].name); + buffer += temp; + } + } + } + } + + // bomber wings + buffer += "\tBomber wings:\r\n"; + for (i=0; i 0) { + int wing_leader_shipnum = Wings[i].ship_index[Wings[i].special_ship]; + if (Ship_info[Ships[wing_leader_shipnum].ship_info_index].species == species) { + if (Ship_info[Ships[wing_leader_shipnum].ship_info_index].flags & SIF_BOMBER) { + temp.Format("\t\tWing: %s, count: %d, waves: %d, type: %s\r\n", Wings[i].name, Wings[i].wave_count, Wings[i].num_waves, Ship_info[Ships[wing_leader_shipnum].ship_info_index].name); + buffer += temp; + } + } + } + } + + buffer += "\tFreighters, Cargo, Transports:\r\n"; + // freighters and transports (cargo) + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->type == OBJ_START || objp->type == OBJ_SHIP) { + shipp = &Ships[objp->instance]; + + if (Ship_info[shipp->ship_info_index].species == species) { + //if (shipp->wingnum == -1) + //if (shipp->cargo1 > 0) + if (Ship_info[shipp->ship_info_index].flags & (SIF_FREIGHTER | SIF_TRANSPORT | SIF_CARGO)) { + temp.Format("\t\tName: %s Type: %s, Cargo: %s\r\n", shipp->ship_name, Ship_info[shipp->ship_info_index].name, Cargo_names[shipp->cargo1]); + buffer += temp; + } + } + } + } + + buffer += "\tNav buoy, Escape pod, Sentry gun:\r\n"; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->type == OBJ_START || objp->type == OBJ_SHIP) { + shipp = &Ships[objp->instance]; + + if (Ship_info[shipp->ship_info_index].species == species) { + //if (shipp->wingnum == -1) + //if (shipp->cargo1 > 0) + if (Ship_info[shipp->ship_info_index].flags & (SIF_NAVBUOY | SIF_ESCAPEPOD | SIF_SENTRYGUN)) { + temp.Format("\t\tName: %s, Type: %s Cargo: %s\r\n", shipp->ship_name, Ship_info[shipp->ship_info_index].name, Cargo_names[shipp->cargo1]); + buffer += temp; + } + } + } + } + + + // cruiser + buffer += "\tCruiser:\r\n"; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->type == OBJ_START || objp->type == OBJ_SHIP) { + shipp = &Ships[objp->instance]; + + if (Ship_info[shipp->ship_info_index].species == species) { + //if (shipp->wingnum == -1) + //if (shipp->cargo1 > 0) + if (Ship_info[shipp->ship_info_index].flags & (SIF_CRUISER)) { + temp.Format("\t\tName: %s, Type: %s, Cargo: %s\r\n", shipp->ship_name, Ship_info[shipp->ship_info_index].name, Cargo_names[shipp->cargo1]); + buffer += temp; + } + } + } + } + + // dry dock, cap, super cap + buffer += "\tDry dock, Capital, Supercap:\r\n"; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->type == OBJ_START || objp->type == OBJ_SHIP) { + shipp = &Ships[objp->instance]; + + if (Ship_info[shipp->ship_info_index].species == species) { + //if (shipp->wingnum == -1) + //if (shipp->cargo1 > 0) + if (Ship_info[shipp->ship_info_index].flags & (SIF_DRYDOCK|SIF_CAPITAL|SIF_SUPERCAP)) { + temp.Format("\t\tName: %s, Type: %s, Cargo: %s\r\n", shipp->ship_name, Ship_info[shipp->ship_info_index].name, Cargo_names[shipp->cargo1]); + buffer += temp; + } + } + } + } + + buffer += "\r\n"; + } +} + +void dump_loadout(ship *shipp, CString &loadout) +{ + CString temp; + char *weapon_name; + + loadout = ""; + +// PRIMARY + int pri_idx, sec_idx; + + for (pri_idx=0; pri_idx < shipp->weapons.num_primary_banks; pri_idx++) { + if (shipp->weapons.primary_bank_weapons[pri_idx] == -1) { + weapon_name = "none"; + } else { + weapon_name = Weapon_info[shipp->weapons.primary_bank_weapons[pri_idx]].name; + } + temp.Format("\t\t\tPrimary[%d]: %s\r\n", pri_idx+1, weapon_name); + loadout += temp; + } + +// SECONDARY + for (sec_idx=0; sec_idx < shipp->weapons.num_secondary_banks; sec_idx++) { + if (shipp->weapons.secondary_bank_weapons[sec_idx] == -1) { + weapon_name = "none"; + } else { + weapon_name = Weapon_info[shipp->weapons.secondary_bank_weapons[sec_idx]].name; + } + temp.Format("\t\t\tSecondary[%d]: %s\r\n", sec_idx+1, weapon_name); + loadout += temp; + } + +// TURRET + ship_subsys *ss; + for (ss = GET_FIRST(&shipp->subsys_list); ss != END_OF_LIST(&shipp->subsys_list); ss = GET_NEXT(ss) ) { + if ( (ss->system_info->type == SUBSYSTEM_TURRET) ) { +// ss->weapons.num_primary_banks, ss->weapons.num_secondary_banks, ss->weapons.primary_bank_weapons[3], ss->weapons.secondary_bank_weapons[2] +// ss->system_info->primary_banks, ss->system_info->secondary_banks + temp.Format("\t\t\tTurret: %s\r\n", ss->system_info->subobj_name); + loadout += temp; + + // PRIMARY + for (pri_idx=0; pri_idx < ss->weapons.num_primary_banks; pri_idx++) { + if (ss->weapons.primary_bank_weapons[pri_idx] == -1) { + weapon_name = "none"; + } else { + weapon_name = Weapon_info[ss->weapons.primary_bank_weapons[pri_idx]].name; + } + temp.Format("\t\t\t\tPrimary[%d]: %s\r\n", pri_idx+1, weapon_name); + loadout += temp; + } + + // SECONDARY + for (sec_idx=0; sec_idx < ss->weapons.num_secondary_banks; sec_idx++) { + if (ss->weapons.secondary_bank_weapons[sec_idx] == -1) { + weapon_name = "none"; + } else { + weapon_name = Weapon_info[ss->weapons.secondary_bank_weapons[sec_idx]].name; + } + temp.Format("\t\t\t\tSecondary[%d]: %s\r\n", sec_idx+1, weapon_name); + loadout += temp; + } + } + } + +} + +void DumpStats::get_default_ship_loadouts(CString &buffer) +{ + int i, species; + object *objp; + ship *shipp; + CString temp, loadout; + + buffer += "\r\nSHIP SPECIES BREAKDOWN\r\n"; + + for (species=0; species<3; species++) { + switch(species) { + case SPECIES_TERRAN: + buffer += "Terran\r\n"; + break; + + case SPECIES_VASUDAN: + buffer += "Vasudan\r\n"; + break; + + case SPECIES_SHIVAN: + buffer += "Shivan\r\n"; + break; + } + + + // fighter wings + buffer += "\tFighter wings:\r\n"; + for (i=0; i 0) { + int wing_leader_shipnum = Wings[i].ship_index[Wings[i].special_ship]; + if (Ship_info[Ships[wing_leader_shipnum].ship_info_index].species == species) { + if (Ship_info[Ships[wing_leader_shipnum].ship_info_index].flags & SIF_FIGHTER) { + temp.Format("\t\tWing: %s\r\n", Wings[i].name); + buffer += temp; + dump_loadout(&Ships[wing_leader_shipnum], loadout); + buffer += loadout; + } + } + } + } + + // bomber wings + buffer += "\tBomber wings:\r\n"; + for (i=0; i 0) { + int wing_leader_shipnum = Wings[i].ship_index[Wings[i].special_ship]; + if (Ship_info[Ships[wing_leader_shipnum].ship_info_index].species == species) { + if (Ship_info[Ships[wing_leader_shipnum].ship_info_index].flags & SIF_BOMBER) { + temp.Format("\t\tWing: %s\r\n", Wings[i].name); + buffer += temp; + dump_loadout(&Ships[wing_leader_shipnum], loadout); + buffer += loadout; + } + } + } + } + + buffer += "\tFreighters, Cargo, Transports:\r\n"; + // freighters and transports (cargo) + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->type == OBJ_START || objp->type == OBJ_SHIP) { + shipp = &Ships[objp->instance]; + + if (Ship_info[shipp->ship_info_index].species == species) { + //if (shipp->wingnum == -1) + //if (shipp->cargo1 > 0) + if (Ship_info[shipp->ship_info_index].flags & (SIF_FREIGHTER | SIF_TRANSPORT)) { + temp.Format("\t\tName: %s\r\n", shipp->ship_name); + buffer += temp; + dump_loadout(shipp, loadout); + buffer += loadout; + } + } + } + } + + buffer += "\tEscape pod, Sentry gun:\r\n"; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->type == OBJ_START || objp->type == OBJ_SHIP) { + shipp = &Ships[objp->instance]; + + if (Ship_info[shipp->ship_info_index].species == species) { + //if (shipp->wingnum == -1) + //if (shipp->cargo1 > 0) + if (Ship_info[shipp->ship_info_index].flags & (SIF_ESCAPEPOD | SIF_SENTRYGUN)) { + temp.Format("\t\tName: %s\r\n", shipp->ship_name); + buffer += temp; + dump_loadout(shipp, loadout); + buffer += loadout; + } + } + } + } + + + // cruiser + buffer += "\tCruiser:\r\n"; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->type == OBJ_START || objp->type == OBJ_SHIP) { + shipp = &Ships[objp->instance]; + + if (Ship_info[shipp->ship_info_index].species == species) { + //if (shipp->wingnum == -1) + //if (shipp->cargo1 > 0) + if (Ship_info[shipp->ship_info_index].flags & (SIF_CRUISER)) { + temp.Format("\t\tName: %s\r\n", shipp->ship_name); + buffer += temp; + dump_loadout(shipp, loadout); + buffer += loadout; + } + } + } + } + + // dry dock, cap, super cap + buffer += "\tCapital, Supercap:\r\n"; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->type == OBJ_START || objp->type == OBJ_SHIP) { + shipp = &Ships[objp->instance]; + + if (Ship_info[shipp->ship_info_index].species == species) { + //if (shipp->wingnum == -1) + //if (shipp->cargo1 > 0) + if (Ship_info[shipp->ship_info_index].flags & (SIF_CAPITAL|SIF_SUPERCAP)) { + temp.Format("\t\tName: %s\r\n", shipp->ship_name); + buffer += temp; + dump_loadout(shipp, loadout); + buffer += loadout; + } + } + } + } + + buffer += "\r\n"; + } + // go through all wings + + // go through all ships not in wings and FLYABLE + + // print primary, secondary, and BIG turrets +} \ No newline at end of file diff --git a/src/fred2/eventeditor.cpp b/src/fred2/eventeditor.cpp new file mode 100644 index 0000000..a79f575 --- /dev/null +++ b/src/fred2/eventeditor.cpp @@ -0,0 +1,1591 @@ +/* + * $Logfile: /Freespace2/code/fred2/EventEditor.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Event editor dialog box class and event tree class + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 12 9/13/99 8:03a Andsager + * Add command heads 3,4,5 as allowable head animations. + * + * 11 9/09/99 5:07a Andsager + * Make sure TP2 is available in FRED head ani + * + * 10 9/01/99 2:52p Andsager + * Add new heads to FRED and some debug code for playing heads + * + * 9 8/28/99 7:29p Dave + * Fixed wingmen persona messaging. Make sure locked turrets don't count + * towards the # attacking a player. + * + * 8 8/26/99 8:52p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 7 5/04/99 5:21p Andsager + * + * 6 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 5 1/21/99 9:29a Andsager + * + * 4 12/17/98 2:41p Andsager + * Changed input into sexp_tree::insert() to include bitmaps + * + * 3 11/06/98 11:21a Johnson + * Put in handling code for wacky event editor Assert(). + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 55 9/25/98 1:33p Andsager + * Add color to event editor (root and chain) indicating mission directive + * + * 54 7/09/98 10:57a Hoffoss + * Fixed bug where the 'update stuff' button was reverting changes made to + * various message fields. + * + * 53 5/15/98 5:51p Hoffoss + * Fixed escape key and cancel button bugs. + * + * 52 5/12/98 11:44a Hoffoss + * Made escape key not close dialog (and lose changes made). + * + * 51 4/30/98 9:53p Hoffoss + * Added "Head-VC" to ani list at Sandeep's request. + * + * 50 4/30/98 8:23p John + * Fixed some bugs with Fred caused by my new cfile code. + * + * 49 4/22/98 9:56a Sandeep + * + * 48 4/20/98 4:40p Hoffoss + * Added a button to 4 editors to play the chosen wave file. + * + * 47 4/03/98 5:20p Hoffoss + * Changed code so that changing a message's wave file will update the + * persona as well, if the wave file has the proper prefix. + * + * 46 4/03/98 12:39p Hoffoss + * Changed starting directory for browse buttons in several editors. + * + * 45 3/10/98 4:06p Hoffoss + * Fixed browse button blues. + * + * 44 3/06/98 2:24p Hoffoss + * Fixed bug with going to reference with deleting a entity referenced by + * an sexp tree. + * + * 43 2/16/98 6:25p Hoffoss + * Did major rework of the whole right_clicked() handler to simplify it + * all, break it down and make it more flexible. Should be a lot easier + * to work with from now on. + * + * 42 2/16/98 2:42p Hoffoss + * Added new code in preparation to simplify the sexp_tree monster. + * Checking in code now as a good foundation point that I can revert back + * to if needed. + * + * 41 1/23/98 3:06p Hoffoss + * Added an explicit item to the filename combo boxes at designers + * request. + * + * 40 1/09/98 3:41p Hoffoss + * Fixed bug with event moving not updating fields properly. + * + * 39 1/08/98 11:18a Hoffoss + * Fixed several bugs in new Event Editor. + * + * 38 1/08/98 10:24a Johnson + * Fixed bug with null strings for filenames. + * + * 37 1/07/98 5:58p Hoffoss + * Combined message editor into event editor. + * + * 36 1/06/98 8:25p Hoffoss + * Added insert event functionality to event editor. + * + * 35 1/06/98 3:31p Hoffoss + * Added image to indicate chained events, and added code to support it. + * + * 34 10/20/97 5:13p Allender + * new subsystem sabotage/repair/set sexpressions. Added new event/goal + * status checking sexpressions (not fully implemented yet). Change + * campaign save files to save all events as well as goals + * + * 33 10/10/97 6:21p Hoffoss + * Put in Fred support for training object list editing. + * + * 32 10/10/97 2:53p Johnson + * Fixed bug with new items being selected before they are fully + * registered as added. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include +#include "fred.h" +#include "freddoc.h" +#include "eventeditor.h" +#include "fredview.h" +#include "management.h" +#include "sexp_tree.h" +#include "missionmessage.h" +#include "cfile.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +event_editor *Event_editor_dlg = NULL; // global reference needed by event tree class + +// determine the node number that would be allocated without actually allocating it yet. +int sexp_event_tree::get_new_node_position() +{ + int i; + + for (i=0; iFindStringExact(-1, name) == CB_ERR) { + box->AddString(name); + } +} + +BOOL event_editor::OnInitDialog() +{ + int i, adjust = 0; + BOOL r = TRUE; + CListBox *list; + CComboBox *box; + + CDialog::OnInitDialog(); // let the base class do the default work + m_play_bm.LoadBitmap(IDB_PLAY); + ((CButton *) GetDlgItem(IDC_PLAY)) -> SetBitmap(m_play_bm); + + if (!Show_sexp_help) + adjust = -SEXP_HELP_BOX_SIZE; + + theApp.init_window(&Events_wnd_data, this, adjust); + m_event_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX)); + load_tree(); + create_tree(); + if (m_num_events >= MAX_MISSION_EVENTS){ + GetDlgItem(IDC_BUTTON_NEW_EVENT)->EnableWindow(FALSE); + } + + update_cur_event(); + i = m_event_tree.select_sexp_node; + if (i != -1) { + GetDlgItem(IDC_EVENT_TREE) -> SetFocus(); + m_event_tree.hilite_item(i); + r = FALSE; + } + + m_num_messages = Num_messages - Num_builtin_messages; + for (i=0; iLimitText(NAME_LENGTH - 1); + ((CEdit *) GetDlgItem(IDC_MESSAGE_TEXT))->LimitText(MESSAGE_LENGTH - 1); + ((CComboBox *) GetDlgItem(IDC_AVI_FILENAME))->LimitText(MAX_FILENAME_LEN - 1); + ((CComboBox *) GetDlgItem(IDC_WAVE_FILENAME))->LimitText(MAX_FILENAME_LEN - 1); + + list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST); + list->ResetContent(); + for (i=0; iAddString(m_messages[i].name); + } + + box = (CComboBox *) GetDlgItem(IDC_AVI_FILENAME); + box->ResetContent(); + box->AddString(""); + for (i=0; iAddString("Head-VC"); // force it in, since Sandeep wants it and it's not used in built-in messages + box->AddString("Head-VC2"); + + // add terran pilot heads + box->AddString("Head-TP4"); + box->AddString("Head-TP5"); + box->AddString("Head-TP6"); + box->AddString("Head-TP7"); + box->AddString("Head-TP8"); + + // add vasudan pilot heads + box->AddString("Head-VP2"); + + // BSH and CM2 + box->AddString("Head-CM2"); + box->AddString("Head-BSH"); + */ + + box = (CComboBox *) GetDlgItem(IDC_WAVE_FILENAME); + box->ResetContent(); + box->AddString(""); + for (i=0; iFindStringExact(i, Messages[i].wave_info.name) == CB_ERR){ + box->AddString(Messages[i].wave_info.name); + } + } + } + + // add the persona names into the combo box + box = (CComboBox *) GetDlgItem(IDC_PERSONA_NAME); + box->ResetContent(); + box->AddString(""); + for (i = 0; i < Num_personas; i++ ){ + box->AddString( Personas[i].name ); + } + + // set the first message to be the first non-builtin message (if it exists) + if ( Num_messages > Num_builtin_messages ){ + m_cur_msg = 0; + } else { + m_cur_msg = -1; + } + + if (Num_messages >= MAX_MISSION_MESSAGES){ + GetDlgItem(IDC_NEW_MSG)->EnableWindow(FALSE); + } + + update_cur_message(); + return r; +} + +void event_editor::load_tree() +{ + int i; + + m_event_tree.select_sexp_node = select_sexp_node; + select_sexp_node = -1; + + m_event_tree.clear_tree(); + m_num_events = Num_mission_events; + for (i=0; i"); + } + + m_events[i].formula = m_event_tree.load_sub_tree(Mission_events[i].formula); + + // we must check for the case of the repeat count being 0. This would happen if the repeat + // count is not specified in a mission + if ( m_events[i].repeat_count <= 0 ){ + m_events[i].repeat_count = 1; + } + } + + m_event_tree.post_load(); + cur_event = -1; +} + +void event_editor::create_tree() +{ + int i; + HTREEITEM h; + + m_event_tree.DeleteAllItems(); + for (i=0; i= 0) { + image = BITMAP_CHAIN; + if (m_events[i].objective_text) { + image = BITMAP_CHAIN_DIRECTIVE; + } + } else { + image = BITMAP_ROOT; + if (m_events[i].objective_text) { + image = BITMAP_ROOT_DIRECTIVE; + } + } + + h = m_event_tree.insert(m_events[i].name, image, image); + + m_event_tree.SetItemData(h, m_events[i].formula); + m_event_tree.add_sub_tree(m_events[i].formula, h); + } + + cur_event = -1; +} + +void event_editor::OnRclickEventTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + save(); + m_event_tree.right_clicked(MODE_EVENTS); + *pResult = 0; +} + +void event_editor::OnBeginlabeleditEventTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + CEdit *edit; + + if (m_event_tree.edit_label(pTVDispInfo->item.hItem) == 1) { + *pResult = 0; + modified = 1; + edit = m_event_tree.GetEditControl(); + Assert(edit); + edit->SetLimitText(NAME_LENGTH - 1); + + } else + *pResult = 1; +} + +void event_editor::OnEndlabeleditEventTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + *pResult = m_event_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText); +} + +// This is needed as a HACK around default MFC standard +// It is not required, but overrides default MFC and links no errors without. +void event_editor::OnOK() +{ + HWND h; + CWnd *w; + + save(); + w = GetFocus(); + if (w) { + h = w->m_hWnd; + GetDlgItem(IDC_EVENT_TREE)->SetFocus(); + ::SetFocus(h); + } +} + +int event_editor::query_modified() +{ + int i; + char *ptr, buf[MESSAGE_LENGTH]; + + UpdateData(TRUE); + if (modified) + return 1; + + if (Num_mission_events != m_num_events) + return 1; + + for (i=0; i= 0) { + strcpy(names[0][count], Mission_events[m_sig[i]].name); + strcpy(names[1][count], m_events[i].name); + count++; + Mission_events[m_sig[i]].result = 1; + } + + // invalidate all sexp references to deleted events. + for (i=0; i", Mission_events[i].name); + strcpy(buf + NAME_LENGTH - 2, ">"); // force it to be not too long + strcpy(names[0][count], Mission_events[i].name); + strcpy(names[1][count], buf); + count++; + } + + Num_mission_events = m_num_events; + for (i=0; i"); + } + + if (m_messages[m_cur_msg].wave_info.name){ + m_wave_filename = _T(m_messages[m_cur_msg].wave_info.name); + } else { + m_wave_filename = _T(""); + } + + // add persona id + if ( m_messages[m_cur_msg].persona_index != -1 ){ + m_persona = m_messages[m_cur_msg].persona_index + 1; // add one for the "none" at the beginning of the list + } else { + m_persona = 0; + } + + if(m_messages[m_cur_msg].multi_team >= 2){ + m_message_team = -1; + m_messages[m_cur_msg].multi_team = -1; + } else { + m_message_team = m_messages[m_cur_msg].multi_team; + } +/* + m_event_num = find_event(); + if (m_event_num < 0) { + node = -1; + m_sender = m_priority = 0; + + } else + node = CADR(Mission_events[m_event_num].formula); +*/ } + + GetDlgItem(IDC_MESSAGE_NAME)->EnableWindow(enable); + GetDlgItem(IDC_MESSAGE_TEXT)->EnableWindow(enable); + GetDlgItem(IDC_AVI_FILENAME)->EnableWindow(enable); + GetDlgItem(IDC_BROWSE_AVI)->EnableWindow(enable); + GetDlgItem(IDC_BROWSE_WAVE)->EnableWindow(enable); + GetDlgItem(IDC_WAVE_FILENAME)->EnableWindow(enable); + GetDlgItem(IDC_DELETE_MSG)->EnableWindow(enable); + GetDlgItem(IDC_PERSONA_NAME)->EnableWindow(enable); + GetDlgItem(IDC_MESSAGE_TEAM)->EnableWindow(enable); + UpdateData(FALSE); +} + +int event_editor::handler(int code, int node, char *str) +{ + int i; + + switch (code) { + case ROOT_DELETED: + for (i=0; iEnableWindow(TRUE); + return node; + + case ROOT_RENAMED: + for (i=0; icur_event; i--) { + m_events[i] = m_events[i - 1]; + m_sig[i] = m_sig[i - 1]; + } + + if (cur_event){ + reset_event(cur_event, get_event_handle(cur_event - 1)); + } else { + reset_event(cur_event, TVI_FIRST); + } + + m_num_events++; +} + +HTREEITEM event_editor::get_event_handle(int num) +{ + HTREEITEM h; + + h = m_event_tree.GetRootItem(); + while (h) { + if ((int) m_event_tree.GetItemData(h) == m_events[num].formula){ + return h; + } + + h = m_event_tree.GetNextSiblingItem(h); + } + + return 0; +} + +void event_editor::reset_event(int num, HTREEITEM after) +{ + int index; + HTREEITEM h; + + strcpy(m_events[num].name, "Event name"); + h = m_event_tree.insert(m_events[num].name, BITMAP_ROOT, BITMAP_ROOT, TVI_ROOT, after); + + m_events[num].repeat_count = 1; + m_events[num].interval = 1; + m_events[num].score = 0; + m_events[num].chain_delay = -1; + m_events[num].objective_text = NULL; + m_events[num].objective_key_text = NULL; + m_sig[num] = -1; + + m_event_tree.item_index = -1; + m_event_tree.add_operator("when", h); + index = m_events[num].formula = m_event_tree.item_index; + m_event_tree.SetItemData(h, index); + m_event_tree.add_operator("true"); + m_event_tree.item_index = index; + m_event_tree.add_operator("do-nothing"); + + m_event_tree.SelectItem(h); +// GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(FALSE); + if (num >= MAX_MISSION_EVENTS){ + GetDlgItem(IDC_BUTTON_NEW_EVENT)->EnableWindow(FALSE); + } +} + +void event_editor::OnDelete() +{ + HTREEITEM h; + + // call update_cur_event to clean up local class variables so that we can correctly + // set up the newly selected item. + cur_event = -1; + update_cur_event(); + + h = m_event_tree.GetSelectedItem(); + if (h) { + while (m_event_tree.GetParentItem(h)) + h = m_event_tree.GetParentItem(h); + + m_event_tree.setup_selected(h); + m_event_tree.OnCommand(ID_DELETE, 0); + } +} + +// this is called when you hit the escape key.. +void event_editor::OnCancel() +{ +} + +// this is called the clicking the ID_CANCEL button +void event_editor::On_Cancel() +{ + theApp.record_window_data(&Events_wnd_data, this); + delete Event_editor_dlg; + Event_editor_dlg = NULL; +} + +void event_editor::OnClose() +{ + int z; + + if (query_modified()) { + z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL); + if (z == IDCANCEL){ + return; + } + + if (z == IDYES) { + OnOk(); + return; + } + } + + theApp.record_window_data(&Events_wnd_data, this); + delete Event_editor_dlg; + Event_editor_dlg = NULL; +} + +void event_editor::insert_handler(int old, int node) +{ + int i; + + for (i=0; iitemNew.hItem; + if (!h){ + return; + } + + m_event_tree.update_help(h); + while ((h2 = m_event_tree.GetParentItem(h))>0){ + h = h2; + } + + z = m_event_tree.GetItemData(h); + for (i=0; i EnableWindow(FALSE); + GetDlgItem(IDC_REPEAT_COUNT) -> EnableWindow(FALSE); + GetDlgItem(IDC_EVENT_SCORE) -> EnableWindow(FALSE); + GetDlgItem(IDC_CHAINED) -> EnableWindow(FALSE); + GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(FALSE); + GetDlgItem(IDC_OBJ_TEXT) -> EnableWindow(FALSE); + GetDlgItem(IDC_OBJ_KEY_TEXT) -> EnableWindow(FALSE); + GetDlgItem(IDC_EVENT_TEAM)->EnableWindow(FALSE); + return; + } + + m_team = m_events[cur_event].team; + + m_repeat_count = m_events[cur_event].repeat_count; + m_interval = m_events[cur_event].interval; + m_event_score = m_events[cur_event].score; + if (m_events[cur_event].chain_delay >= 0) { + m_chained = TRUE; + m_chain_delay = m_events[cur_event].chain_delay; + GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(TRUE); + + } else { + m_chained = FALSE; + m_chain_delay = 0; + GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(FALSE); + } + + if (m_events[cur_event].objective_text){ + m_obj_text = m_events[cur_event].objective_text; + } else { + m_obj_text.Empty(); + } + + if (m_events[cur_event].objective_key_text){ + m_obj_key_text = m_events[cur_event].objective_key_text; + } else { + m_obj_key_text.Empty(); + } + + GetDlgItem(IDC_REPEAT_COUNT)->EnableWindow(TRUE); + if ( m_repeat_count <= 1 ) { + m_interval = 1; + GetDlgItem(IDC_INTERVAL_TIME) -> EnableWindow(FALSE); + } else { + GetDlgItem(IDC_INTERVAL_TIME) -> EnableWindow(TRUE); + } + + GetDlgItem(IDC_EVENT_SCORE) -> EnableWindow(TRUE); + GetDlgItem(IDC_CHAINED) -> EnableWindow(TRUE); + GetDlgItem(IDC_OBJ_TEXT) -> EnableWindow(TRUE); + GetDlgItem(IDC_OBJ_KEY_TEXT) -> EnableWindow(TRUE); + GetDlgItem(IDC_EVENT_TEAM)->EnableWindow(FALSE); + if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ){ + GetDlgItem(IDC_EVENT_TEAM)->EnableWindow(TRUE); + } + UpdateData(FALSE); +} + +void event_editor::OnUpdateRepeatCount() +{ + char buf[128]; + int count; + + count = 128; + GetDlgItem(IDC_REPEAT_COUNT)->GetWindowText(buf, count); + m_repeat_count = atoi(buf); + + if ( m_repeat_count <= 1 ){ + GetDlgItem(IDC_INTERVAL_TIME)->EnableWindow(FALSE); + } else { + GetDlgItem(IDC_INTERVAL_TIME)->EnableWindow(TRUE); + } +} + +void event_editor::swap_handler(int node1, int node2) +{ + int index1, index2; + mission_event m; + + save(); + for (index1=0; index1 index2 + 1) { + m_events[index1] = m_events[index1 - 1]; + m_sig[index1] = m_sig[index1 - 1]; + index1--; + } + + m_events[index1] = m; + cur_event = index1; + update_cur_event(); +} + +void event_editor::OnChained() +{ + int image; + HTREEITEM h; + + UpdateData(TRUE); + if (m_chained) { + GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(TRUE); + if (m_obj_text.IsEmpty()) { + image = BITMAP_CHAIN; + } else { + image = BITMAP_CHAIN_DIRECTIVE; + } + } else { + GetDlgItem(IDC_CHAIN_DELAY) -> EnableWindow(FALSE); + if (m_obj_text.IsEmpty()) { + image = BITMAP_ROOT; + } else { + image = BITMAP_ROOT_DIRECTIVE; + } + } + + h = m_event_tree.GetRootItem(); + while (h) { + if ((int) m_event_tree.GetItemData(h) == m_events[cur_event].formula) { + m_event_tree.SetItemImage(h, image, image); + return; + } + + h = m_event_tree.GetNextSiblingItem(h); + } +} + +void event_editor::OnSelchangeMessageList() +{ + static flag = 0; + + if (flag) + return; +/* + if (save_message(m_cur_msg)) { + flag = 1; + ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST)) -> SetCurSel(old); + m_cur_msg = old; + flag = 0; + return; + }*/ + + save(); + update_cur_message(); +} + +int event_editor::save_message(int num) +{ + char *ptr; + int i, conflict = 0; + CListBox *list; + + UpdateData(TRUE); + if (num >= 0) { + ptr = (char *) (LPCTSTR) m_message_name; + for (i=0; iDeleteString(num); + list->InsertString(num, m_message_name); + } + + string_copy(m_messages[num].message, m_message_text, MESSAGE_LENGTH - 1); + if (m_messages[num].avi_info.name){ + free(m_messages[num].avi_info.name); + } + + ptr = (char *) (LPCTSTR) m_avi_filename; + if (!ptr || !strlen(ptr) || !stricmp(ptr, "none") || !stricmp(ptr, "")){ + m_messages[num].avi_info.name = NULL; + } else { + m_messages[num].avi_info.name = strdup(ptr); + } + + if (m_messages[num].wave_info.name){ + free(m_messages[num].wave_info.name); + } + + ptr = (char *) (LPCTSTR) m_wave_filename; + if (!ptr || !strlen(ptr) || !stricmp(ptr, "none") || !stricmp(ptr, "")){ + m_messages[num].wave_info.name = NULL; + } else { + m_messages[num].wave_info.name = strdup(ptr); + } + + // update the persona to the message. We subtract 1 for the "None" at the beginning of the combo + // box list. + m_messages[num].persona_index = m_persona - 1; + + if(m_message_team >= 2){ + m_messages[num].multi_team = -1; + m_message_team = -1; + } else { + m_messages[num].multi_team = m_message_team; + } + + // possible TODO: auto-update event tree references to this message if we renamed it. + } + + return 0; +} + +void event_editor::OnNewMsg() +{ +// if (save_message(m_cur_msg)) +// return; + + save(); + Assert(m_num_messages + Num_builtin_messages < MAX_MISSION_MESSAGES); + strcpy(m_messages[m_num_messages].name, ""); + ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->AddString(""); + + strcpy(m_messages[m_num_messages].message, ""); + m_messages[m_num_messages].avi_info.name = NULL; + m_messages[m_num_messages].wave_info.name = NULL; + m_messages[m_num_messages].persona_index = -1; + m_messages[m_num_messages].multi_team = -1; + m_cur_msg = m_num_messages++; + if (m_num_messages + Num_builtin_messages >= MAX_MISSION_MESSAGES){ + GetDlgItem(IDC_NEW_MSG)->EnableWindow(FALSE); + } + + modified = 1; + update_cur_message(); +} + +void event_editor::OnDeleteMsg() +{ + char buf[256]; + int i; + + // handle this case somewhat gracefully + Assert((m_cur_msg >= 0) && (m_cur_msg < m_num_messages)); + if((m_cur_msg < 0) || (m_cur_msg >= m_num_messages)){ + return; + } + + if (m_messages[m_cur_msg].avi_info.name){ + free(m_messages[m_cur_msg].avi_info.name); + } + if (m_messages[m_cur_msg].wave_info.name){ + free(m_messages[m_cur_msg].wave_info.name); + } + + ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->DeleteString(m_cur_msg); + sprintf(buf, "<%s>", m_messages[m_cur_msg].name); + update_sexp_references(m_messages[m_cur_msg].name, buf, OPF_MESSAGE); + + for (i=m_cur_msg; i= m_num_messages){ + m_cur_msg = m_num_messages - 1; + } + + GetDlgItem(IDC_NEW_MSG)->EnableWindow(TRUE); + modified = 1; + update_cur_message(); +} + +void event_editor::OnBrowseAvi() +{ + int z; + CString name; + + UpdateData(TRUE); + if (!stricmp(m_avi_filename, "")) + m_avi_filename = _T(""); + + z = cfile_push_chdir(CF_TYPE_INTERFACE); + CFileDialog dlg(TRUE, "ani", m_avi_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, + "Ani Files (*.ani)|*.ani|Avi Files (*.avi)|*.avi|Both (*.ani, *.avi)|*.ani;*.avi||"); + + if (dlg.DoModal() == IDOK) { + m_avi_filename = dlg.GetFileName(); + UpdateData(FALSE); + modified = 1; + } + + if (!z) + cfile_pop_dir(); +} + +void event_editor::OnBrowseWave() +{ + int z; + CString name; + + UpdateData(TRUE); + if (!stricmp(m_wave_filename, "")) + m_wave_filename = _T(""); + + if (The_mission.game_type & MISSION_TYPE_TRAINING) + z = cfile_push_chdir(CF_TYPE_VOICE_TRAINING); + else + z = cfile_push_chdir(CF_TYPE_VOICE_SPECIAL); + + CFileDialog dlg(TRUE, "wav", m_wave_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, + "Wave Files (*.wav)|*.wav||"); + + if (dlg.DoModal() == IDOK) { + m_wave_filename = dlg.GetFileName(); + update_persona(); + } + + if (!z){ + cfile_pop_dir(); + } +} + +char *event_editor::current_message_name(int i) +{ + if ( (i < 0) || (i >= m_num_messages) ){ + return NULL; + } + + return m_messages[i].name; +} + +char *event_editor::get_message_list_item(int i) +{ + return m_messages[i].name; +} + +void event_editor::update_persona() +{ + int i, mask; + + if ((m_wave_filename[0] >= '1') && (m_wave_filename[0] <= '9') && (m_wave_filename[1] == '_') ) { + i = m_wave_filename[0] - '1'; + if ( (i < Num_personas) && (Personas[i].flags & PERSONA_FLAG_WINGMAN) ) { + m_persona = i + 1; + if ((m_persona==1) || (m_persona==2)) + m_avi_filename = "HEAD-TP1"; + else if ((m_persona==3) || (m_persona==4)) + m_avi_filename = "HEAD-TP2"; + else if ((m_persona==5)) + m_avi_filename = "HEAD-TP3"; + else if ((m_persona==6)) + m_avi_filename = "HEAD-VP1"; + } + } else { + mask = 0; + if (!strnicmp(m_wave_filename, "S_", 2)) { + mask = PERSONA_FLAG_SUPPORT; + m_avi_filename = "HEAD-CM1"; + } + else if (!strnicmp(m_wave_filename, "L_", 2)) { + mask = PERSONA_FLAG_LARGE; + m_avi_filename = "HEAD-CM1"; + } + else if (!strnicmp(m_wave_filename, "TC_", 3)) { + mask = PERSONA_FLAG_COMMAND; + m_avi_filename = "HEAD-CM1"; + } + + for (i=0; iSetWindowText(m_avi_filename); + UpdateData(FALSE); + modified = 1; +} + +void event_editor::OnSelchangeWaveFilename() +{ + int z; + CComboBox *box; + + box = (CComboBox *) GetDlgItem(IDC_WAVE_FILENAME); + z = box -> GetCurSel(); + UpdateData(TRUE); + UpdateData(TRUE); + + box -> GetLBText(z, m_wave_filename); + UpdateData(FALSE); + update_persona(); +} + +BOOL event_editor::DestroyWindow() +{ + m_play_bm.DeleteObject(); + return CDialog::DestroyWindow(); +} + +void event_editor::OnPlay() +{ + char path[MAX_PATH_LEN + 1]; + GetDlgItem(IDC_WAVE_FILENAME)->GetWindowText(m_wave_filename); + + int size, offset; + cf_find_file_location((char *)(LPCSTR)m_wave_filename, CF_TYPE_ANY, path, &size, &offset ); + + PlaySound(path, NULL, SND_ASYNC | SND_FILENAME); +} + +void event_editor::OnUpdate() +{ +// GetDlgItem(IDC_WAVE_FILENAME)->GetWindowText(m_wave_filename); + UpdateData(TRUE); + update_persona(); +} + +// code when the "team" selection in the combo box changes +void event_editor::OnSelchangeTeam() +{ + if ( cur_event < 0 ){ + return; + } + + UpdateData(TRUE); + + // team == 2, means no team + if(m_team == 2){ + m_team = -1; + } + + m_events[cur_event].team = m_team; +} + +// code when the "team" selection in the combo box changes +void event_editor::OnSelchangeMessageTeam() +{ + if ( m_cur_msg < 0 ){ + return; + } + + UpdateData(TRUE); + + // team == 2, means no team + if(m_message_team == 2){ + m_message_team = -1; + } + + m_messages[m_cur_msg].multi_team = m_message_team; +} + +// Cycles among sexp nodes with message text +void event_editor::OnDblclkMessageList() +{ + CListBox *list = (CListBox*) GetDlgItem(IDC_MESSAGE_LIST); + int num_messages; + int message_nodes[MAX_SEARCH_MESSAGE_DEPTH]; + + // get current message index and message name + int cur_index = list->GetCurSel(); + + // check if message name is in event tree + char buffer[256]; + list->GetText(cur_index, buffer); + + + num_messages = m_event_tree.find_text(buffer, message_nodes); + + if (num_messages == 0) { + char message[256]; + sprintf(message, "No events using message '%s'", buffer); + MessageBox(message); + } else { + // find last message_node + if (m_last_message_node == -1) { + m_last_message_node = message_nodes[0]; + } else { + + if (num_messages == 1) { + // only 1 message + m_last_message_node = message_nodes[0]; + } else { + // find which message and go to next message + int found_pos = -1; + for (int i=0; iOghTt8TGMO=0H(1HR*UGDq;pYzO}ndd$?O$xs5 z`~G~|+~+=X&YU^t%*>fHXXcq}j4^5a6$<)?ufcT&o?e8%ZnMfP!M#vOMeu1=(HHZ) zQ!7m8OD@Ru_q{UPF*tC+(*A6AAeZUL@>{2TEbQ+vW;!x7;cw7n&1!u1nB}I!^qLi> z6Df}NqB=)=$MDY4OLnXuUbxAa;|`m@0vN{l`R8rkwtmYc8-~-Tt{d3{VhcB&S__n~ zLyC!6sVpV6VR&Tow&Cg?@Zkbc7((mrr7)893eUA|%g^8t_^ zTGg|>v7xp%v%G6XrYoCne{s6LcHZ_K>o#s(mtH@*VR&f!x{bs0>l>OIkY{+?hRq|$ zYLy5LYJKil-*9R=ea<=QZKK=HUbcDtj?tZ?BfHWoH}AY;-IjF6S!bn(H*DTDydiBX zaHmHls)`-!K_a?9X<*m7Z5!6@*Z?lJZXew?ylq!3&-NXgx9wW!av_f-ZQi!=9QlIB z;Y&9U@AeB730RlELgPr|UMN%+Te06#<^x9fV*I6yHtxJOD5S)%yfOH1@_D`C6JO1N z0|#&?fZd6=g5L0{J&cKelB+XLHJ(tjA;hSsxA}`URyY1%;kK()>I!f=2|l|Z!Rz7YYGLx59+hv zn?oMFpztU^^a8?;zB1VD7}Es%S%_bcA>q)ug~A_a77EXRt3oCIQuxaw-CX`hV@~<0 zF*6SsQ;omMJeud&Gf4QOS#-)DP207H%%ZP6mS6YNLcZ(n8^@~ezG1BLU&h{a;=kmt zKJh?)EOo$)RpPH&$~aUg%!lbq8`}>`E#`c{>hMP#+Yb)ld-9dn8p!9RBKV1~lj;W$ zP_8FOM$E{_$SCfc7VCW^f3!T0)GWsRLe}5WqX!LI-99rDd{lL^rbk9D?CidZ_s*() zSDAzOVgLu7kXao<9;GwaAH1N>~J{rjf3^KS9F3Z1_y`Ib0nrigB5D8-O$>iX2QQ))@G$~DM!?4i_!t2nqAKt)0v<-d!w7hw z>PEoF2zVF)591sc@F^-pIr0ym9b0 z4nD?Fo>VRQfDXpN$2j?bU{c-4G!9&2#0X!7I!#H>V8TENLN9tz;000s&ue;mXL$XEc<2xyGpc?5hEgf?+T%v|#UGc+`0cJJP8uDa?f z^Uim^(|q%r-!!+~cANRZ4}M_ozyE&o;DZmEhaP&!JoeaQ=Fp)-=HMR+=GL2UMnBF& zyX*tJfu1~(E+6^yzv!AyAAN#r`p8J##3&sjBeNzTU`)qDTSg`#F@0oWgqep!sH=PE zlOtP?Eyu)`Pd@ZXUL^=H26ZE_tXpO&0!NzSHFMT103jRxwmc*Vo+75ttOFbfb;s7h zHjKqA&T0J!>}TyXpXG;ScOFzo~l&B~PEpDO2&H zgdqP(=I+p})BSc+H_`$4qqEG+i0j$2C9mA@FJNC%5|Q_CWHrE~pGilG6N-=hPp}d;VZ0{J|9b!4&+#RM8(?1TNC} zD->qnFOP5G44U3crAepjaIcbCRLZd3@rk}<1z_so&?Bmh&9kaX=!Jff!t+y{XVIo| zt_7~;KaHtWH$EqCSF5+frQW@pH~h!`M59#o!|1!_Qya&4Y%RCFs z<}dRs^q9k3kt;jvVP3OlKaA;&F%LWc`%xV>@2KYW*rQLFj>jK1Pux+Y`qV*jWyPPL z{rIC#bUgmM#~=3LPdxrOUD?NqB-*7ZT1tGXR(bvVIJY9 z&a}NC2 zs-0k-_2=>j7eQBP{OLSv4Brkn&#DUMSw{u)tfS3~pJ(Y+xOtm#E2o@ieKuvx@wi+6 z@~d-LYSNWA2LBPP(i=YU6@Q1HXJN+l|HXy*UQ7-w8vU=i@-^;X8K%a^?-Iy0LWO8&BMJj$N%2or}q7EivMY2itsc40RH8xwmNOG@VVCP~4S8)AFDsJ!I zZXr%VS&Z-?vMD!3>rnh+Wy|+E*+?R9TPT7~2O!K2gR=Nqow;J3iAI1tzksH76 z+taO8B`s$0t}aR3_hwtccm%FP_r6&cKs{dr+BM9|2YipdMMV<;-F2#Pgx5$4&;?Z< zm>hgTZRwTo1&c*+4JG1#UN!QmAIh=;2gR$~e#4jQVwbqt39)_8)zua!NlHMs@3H8V ztwyjVIeHMrAe#cBdgBA+sgXpFc`F5LORnMt(0UEM7YDD)Vt+TFzm+G^hAvNA9^N`kyFQqdFd5e}1!>AcVf76sGLybN~pITd2*X-M_x_7`lzFvF4ldVQzMcMRm?_zX;&c#wF zbKt6xPwG~_@FOakU#}Jm$8T0={6^F$`*t?p8vNm6vOLSvzQ|0_#hv+D{oBU z_o%k^?kgOsktBRXEU~C-$B!vnZxQ-p-nHJC16PfF2D15L8if-HD%DFWD0&iHRkT*8 z@I!Jndn0rd5tSvfI=gCUxs=A8iRVW*?nwo}E!;nURcyIyX5>t@1UP2K0!8x&!60Vb3WV6mAAvLr@N#Ek^e zWS7?xMjtfMm||rEC{MzuLB;AW)>bzxeUwNL=;Nt|UmBIyp=7gt-IJqq_bQTl`i5#5WV;XnQz zvPtGY>4(Z?M83FeQNcmHMAHkz8>-z3|!Q=GH!N{6!j3WBHcS12%+_%nP9 zW0Ujo%(ZI%*;ry8-;=LAr-b;+>kXgyI;B0qjCB;Rr=xGd{ptNT$uo8_;%lLBQ{m~S zvC~iQfXFlMgfpG^#GCjE_3$8s`19xi`w62)a|6}xgx8CoM@X8bN zAfX<9^Jmq=J+;bM?0%0i0)St^ZK(H~dusLm!?)@kNs?~wHr?Vy`7E9LWAx?udPtte zpW27tCF+4IpIlC~_Tj~XmGqH6V*9{8fIUFlw^wsl75}QayDGXz>9Rj)_x7;vu>S~G z(*B!2i%lzIhOl>=*VFseR2A=$>3aI6$b?0BWvGP7p&n#o+Mi7iPv4}x#qL&4Y+)A# z?1!L4EZ%9y`E2iTc$Cl5Da7c@^UF~GY5WxmkKr$mZ((~#VWsSN^L)4rx8^?p@IT`3 zgZcgW59L3czb$_|p5B#zFFrq#|0q5`m;Yycej@){eExgBbB3i|RiwS$dyCQFsxzmX zrDn6)Vutx>%mb(6{l{Um+f2hIf?Z|@AZHE`Y`GgY+ss1w=i~GVP9<;#%sO)^ zuzcu%K&u|Gur$ien4J+F0)Lh7N1oFq=MI$Yt{>%3H~r=kl$r*wJAgHeGPa|X^-@X( zJnjOQ{lzTU3;F=55xxyD=JR9(UfyIL$fC48NM~8pL)L7Fo{Tx`ffdNP!Hfn{td$au z&3A$>HMR?X(~Le5G=0p6r15s@W>DD}&~VtykvFFx&t_<1ow-P8rt#*fmaJT@1eEmw z4+%;-9dD?P;@=ivuY)d_nnLOTcv*+POF$`&cVZRB5=cc$*p6K5%wf;Nqta{IZ)XN8r1zLD3zm;6B?iJ zXlPqw+t?=kk2yO9OF!m&xThOiVzPW)lI8PW4z>uknb!$d$+@~E*T>LHN1=b)t7rjO z3+ZB+u3nhogs}|Zl=p0@(an%xC-A8a%cY;A#w_;uYJ`kwSjravKng??tb9q zWp7^LXI7{vwvlgatWSjN7Xjk z)<(Jq!IxTZO5<|c7UA1;)_vYQCU%>>lr=;B9xfm4H0>>IIBod`d1n9Os6k%I7rDP8 zzsJ-=7xj2D=1q8Nz*B=fU4^GcJgGkS;;9)=O#-_HPi=T=mZvN6RBsmHxkaAeA-H^Q z#dF@gGlDav4Jp@QBpC$<>^<80$;;J(73x^|Ldaao_HBal_&1V=Jo_-^_|1Sdl;k7- zdGi*bNPhf$l%@`&)alZ@Ro`lRPsvl8)^3Nn29V2`F95@iM)AJ9N-(~a5A9({MMw90 z^Kr}0c0oODDrxU)glam!3_4_QP)%~&_siKVbspzP`~A1Ub5`IwhJ+kth5mSc5bcHS zG>tPGM)8efHmz=mt#<03{NekW+SKnztER=E_1z(s%(l!pw`|YG{4h^{M#of+d$b$0 zIrJ1t`!jP?LcX|mv`?%r(WZJ}zebKF;D%I3@TtCq-$v9z{04aRGcKACx06hmX=9pS z`Q*5MStQ3+fxmD}t=d-TmX?X$MaY4m;UjPEi>wWOv(2z)+hVoA5r;jW_LQ|`y^P=G zT}oPdyz@P^k3A7!utzxz?Q2g47_2ZNFx0yI zKEP-VFlcX#`9pv~JLO8D{W0c`0Y-a((GK`^s3m%zIx?vyneTsse5$!;f;80?Udk`l zKsvIZX3S4heWZdk)j&m%rZQIsX)0w^kVd)k=BOZzlCU*sA6QQTOzl`NB;z=+1=9JJ zVjB84uVcpOZ)xj_V>mr3?u;?(9s0hw+PhiOu4!4=7E4PTl+tF7aW=F^bRBXth&Fza z;M9%fK!aS<#;FG$;yS)ut{TX(0lq5nZB5c@*eh?oo6mwSxud_m9yXSKEj`^GXc2y| zr^j<0G}#T<7W67!9hiyP=d{ckwu3)f7I!TMP6kq_#iYIJhsEtI&dT!U@jT11GXrX3 zJ9x2ljkyNI()cSBILG`aA??RAm~b7&2ui`xp7RA-FFS78IhUJvFvqtbIXzhFCGLr) zp?9@IeW0eZhj6UUW3+Sm?WkwWn^QiopJPEm%z@9>iTpj#7svd=&jgrfB99+b4&&LA z!r?u5VolalC+;V_dMowFiNUK5mWSTG>jQR{L+G`6-;-nieH&8B`Ho2*DRnqC4>Zq$ zah-~~T8ul}RS#NZzZrs^$RZN40^d5&YL}s9WB}`mq7$WcN!#Mj`yb4gk8S)Mu*1bR z{tGzPJN9k1dRV`J#;xZZk8Mz*ygC1};Lb zt^v@p^M96h;+Wb89bxn4`D3e4(mM1(oy`$vNrm4Z^mUNZzUz7tXG7Zm*lBXH5nvugk0ou}w<+%yf$$?`<~_icN3YJ=($xY# z6%g8%%K{iZjg&d<0eYwEmH9b2H%gf^9-sxLN8_-BBtGiUwiKqVg0m;}xQ!VzZ_H~f zh4Cd?M|yZ#k1n5+E@dvuF9-KqVQCdBWnPEYLM^Hf$JXJlI%tqHZnc&iuC$G@UvcEpxQ!dDsh2P3HSRAd-1K*rg4-HOD#Vmn^;0)LjC=!OnG5xZbmaPb=s*k{#2I#E<<~Xw9icfojn$} zBmJoD-GJrI^8gF`goXVr;A#A6IS=7mlFs~c=71L4_Eu1*pUKFcKSM3GTE~xBTd)q; z+AnBJ>7DdS555?s=1qDm8+j(9n4Q@;h?@(Mw;PH!cN%!kGYP%ay&qGF)^Y`sr{n%R z+^ccF8uv=X3f?5?4dw|+Z;*{UmGIuLlJrLNq@*{Rzrp=@q%X$35)qNTxKGFZDcq}Z zf3u`Fn`av^KH3znKbUlV|<^F zdnNk*yCl6EelgR#&FAI0$J`^&J*E}+O4La080q$6roiWA=2n3(gEz-?^fKXRxv7-r zKCEl7{62V{q}vC7j(9o5!kC^jgIakV?W@e5NKF!qOUWusB40&FKH6qem1@D;Yt7R=|I_@m5 z+N?463Lk5QuQ_HdR+D%{#9G5OrTNDCW7E(R)gQ~CE<4J4V_`p>gZg5O%6lUi~X=p!bEh=5~Za8Oq zF>qE&i7xt8kNRDNzNwcj<3w|7u?*|Ykk5_qE@)*wg1>t7w01-+Nt2_Xi_NnfTF3RE z%?l@^2=5P~R?}t%o@r_N%z0WUKDFEhW*&TOE1P;;v~RXtYK+!yr^rBCsrAua<=4@> zg{ogiEYB8oz#O>l1bH|u)g*U2{yJT`l`J5nCayC}H*dyr&o>nq!)BNf@KY#^S&-Z- zQ*!~I1NaNg!>H&&;mQQ~Jb^!w0MDn#a>JlMAM`gS&`+c7ycO{2fIo@+TcM@(s4K1x z4Z+vj4qb8d+J?I3XwSGFeLa5nFD++|l=GDE5z3L4a^^@mhn{sg^Q4@o%a$`w%K81X zD2ICgBJ|P@y>Ak|mFoSAs29RFmxa$0_-jkx7(7iX_j|PH11m&Kbc zc*jI|U8oVxmUcQse6zUo1h+QAjeAvL9ula?y#`jOCh9-596BHIi4*7a07qAl<2c6! zI2t(*aVpHwIB8h>D$809Nf?g~uZkS8W_x%Y;$boV9>0itdVuR9B|dIN?h`0`x<|7I zvud@wA>K_W8#@)y*VI~ac1|ydbC6OFDst~f`Nw+r@j2``>LQII3I&b@@0Y^L+nKxa z&lik4&9)T3EECJM6J>A2H)m~VnK?G#nxw5srEbYpLsOTVSDOEk_VFg>E?%@nrU)z!M8#kx`Wa}on_ClT5kB7>o$A7B>}_S>*8#r3yvp1k zhojrggx+e++|(3Y^~2bLUEfsuX;XDBWhK_W`&QIV19V>%$)o#MEDrA$ z??r<5N#P|AFGlB_j8=qK3by?vf}4wI`lF}f5~o*iNSz}rqh-40?IIV7TY>uDiE-cc zjd<(iY>`ps@M6A;;F-95<@$X)aOQbaWqAV{GS?UsHd6CF6>xgA(l6v{i1fE7QkEbj@Nr2r07-e}P0X8p1 znirb-L|K|dmg5s;X%bmZIAU2kQSKGyfdrmLVTmcrPZMDG07hA!On}Wxk>)znkSI&1 z$Z~R`ES)0D3y)ZqL6m!?c`$*eQLG$MmY*fS?g5OlJe2^Omm$)ImSq>p zy~6x5fhX?gp)9{hfZYQaWqCRQHZMh*yG?VVEW1ROa}s6QC9*6!Vp;a0+$+t0C-5{1 zTTNMhl>oa3Fv{}#1lYV3X^xndL|OKVEX|3s>=jvBj#!rKQSO*|IDw~8bG^v&Py*~8 zz$nWf5@7REq#a{vRp5+yzGc&xfSKU$vm3C(()f_V*GF(Y$tlUZ)i_eMIm1~0 z>k3F|eULP$>t*p;%iuM}@Vd(2X#|PBB{MLVnOv^!RB?D4%izK5kjv8BT?Vf;hIe%t zytWwLyUO5k=L;__m%jVSk&k%=W_Datq<5`;sbX5(%TqPQIKUO+O(uD-O808ew7xFu z#qEp^X;xz&FR;J&VAG22_DsyEYt5I?-}5M!alwr;>*D-C_vqWX z%e2TZu0|ZO336d#3;<>fvdy-do(r~V|1^UsqB^c}Ft_tPCMz`&Z+wos-UJp7yEJH`WDFqanKtRO}RPLv{VN)J;QwKsL9Tuw4d8K)E%&^%dxY|pARyI z#bT<_dKy!wo4@tSb+8)&n{RyIE)~^%O{#jrv8E<^lW4=+6s5Nuszdr=PDO>$CZ{* zjrrdziuwurO*+yS`v~6xvaKc&+otb804PAn?x208!IQH3~b*qkjfQ$y!q{d31c>n=ke~EQiN6 z#ah!Kc-juugR1W-#QC`$yw@X7qvT0mC9(8x2k$2Xw@GlhyV&h-bvS1TJ^zu(ae?@5#~s_gwp|x7WHYh$=Xr;*KhHx1k@mEl{b6j6^e%=4 z+y@SRho3_$jZo_NupLtHJv&}sLZeONu5J5MObXVs?OJKo(Z+nW<{SB!3PvXNHvH5Y zcQj?CCu{Um-|v{p+J(lwX?stAY$H)DTYJu>Qj&fs-l-AZt0O$S^Vvg0WNj>W4s`Gu z(yO9eV?G%5G8gdU0KYqV-BsW(0{oxNMXA=!*YGjXIu3*jB%4~=#^2v z9@q{)IvZc#QVN>)#wc%{$efq;d_S-Awq1|lXvBU|lR93?{p%<%M8IFC8aw^Z61r?CHPk*>MdCUE5mmq`Ck>~9fa@Gi=IOr@(mpS zs;o=prlnTHZX9E-jqu(3pxh_wkL{Y1?Q$V3%SNoyeJ!C~DSR*BN2hL0fV15!#IM$I zKR`yx4BL&u-!JgF32?@@SDN=@zCOd84*1kOGbcUm=4;Gn#8>Z)>Tm_dgbiXd>CLMA z#T=*_uCvWFUqUo#(0mhlroPU`{WhswojEp@LBIHDq;vXF+^b1%oo@wXQBxd`xevm> zsM~Kuy8n8DZWXS&|3Ctqy8SwIN8QgWquYa#Zcjx1$?CSzXAYMCfbA`%Ws(oogL)tIr8)EDTN%TlfFk28_@-0YlQGkLFwnJ*CDxnq3!t2n z$9)ZOZ9GTYAngQKQ8yHQUPrwm_tX^eKZEwvfx28Eb;LGq-wCqw5=*@zcY_!2A}`!k zzDwd!HU{NzZ^Vu}znwCAz`1^su4kI7it#qm9gNz4e00^e_s-({k+|wPl4qUCSPt|~ zU0*#3+By!O6OE;(i!IGZk&#djm8rS#gHA)aV`cV{Qn!d7UX8jw&nz}4AP(^+p-KBaS77@P{pdgqC9PTS zLvLU|_hTC_--`j;lej*-4>M+3zK>#_a%HT%87Y0Y#9xcGaln;`V{+Vjg%>ZeYu#6* z7D}9E@@w5;oMuLfvWyykeb|+IqLh1XLb=yOd63YGisNBh~_v$)>ojdMD{nD@3brQA$o{eEGfW~bu zfM>^@ezeV3U^PcitKrVpZLlo%9S}Agi+|FD_;%&Q;V+yJ-}y31e-rjpS-XD);-uW$ zvmTs>JMk3vKJdP<$h(UNaSoA2+@qWf_hGc1Ouo1*_aWD0^2KGk4>CM{nj6F3vcmTXR@86#Vby|Dj-c!ISnHr6oR%*CqVIXHlZ#W_RCRkEdIQZA#^t}mp7|N( zrfA&KeSDU>?rmr3L_Y^}nxiEav){b!2>ou_mHz;Z9z-MGA!XVckWyqkxffn=5Bkk} z6X3Kx_hJ^>1D-Y3Fs2!!Pr~1ezQ=u`H%R^%Jk9*)Oa7!-1m`2~g=dmQdrC=u?&5cG zNY%8?k<}M<@iohc(s(0Uajm%>4PB#FthF;hIbDoxaNalyo6gvk#yz!W2arlzw!soC z-cz}|qWYR>kK~~Uhc(QylU9+_fK}Dpg8KZBd10A(4Ew|ln3G%sZmj>yaoj)0rc^gY zX6={UM=|31erOo6Bmrh8qBF3{H(A!L0`XahG2Xj5j$yomCMEE<0RD3Ga*;EHvn}sL{`1k6?}++? zzxtVmjiv{!XAEfE-kpv_`|U2JHAiC4Ux;WWN3-L3(y+?4=8I7ttDTU##uD;qX?%a^ zOA&7AX=fqrJXV8F1OAsIe9lWKQPPfF@}Eb0V4VLzgim|Pcci(x8K0Lb{vhmTt@)P- zpXbb7j6Z!(#qnb8@lK3{^I*IFHNww;YDib@HhCo8S0X%mNL=BvYq(0;;*JUKS0h}; z_qdOkb>?3j=Wc{O&@oPJhd;_GE^$6DIOXiico2U-AlVYzJ_s=Wt~)&zq;5g{{j0#X zaS1m|nM#{inGd4PPA2|dJIVNaM#iieSgWUA!ZC$oS_bpm9&-zP3c^EM!B|=bt&C$< z()&i7o9u&~WK*>dy=!J&stIC=3R4;OU`Y`!9n%VbYoLshT>e|64 zBDwsv1OGkz(tNs(VAlq89M!o3cV^grQ<_iL6lN9m7_O0&TP0x3{~+w$RAT<>Gpsdl zHvdud87SXGF*CNOb5NhJDzi?&c6|;k7U%SHMekw%W4j)NKgo7Il{kN+QiqqqLri;Wr`XtwZhlzV1BqOFfx@o(@Od z&})Gza;Ib7=)V`#3oPzI;s_mHjGu3sV!ZuMSYj8)FCTB$K0DcX`^{72<7T}3bT~ff zQ{RkOpzl*p6bt_~aD83VSDq-oJ#z}QFPH*t*86FQXxsgqoTrqFZ__?9X1yEn2Ro;7 zGICcYXM(%&$^A9D(pTDQ*eduw+1s%i>*|&f0?rS@t0I>J-i!c`JA>jE+t$Y>5$|R` z;(opuZ=t-0i>1GJlC(L-VsB51`-G;x=9$X92YD|*-l?qVQ6e4NUxxU0t@&Cows*oF z^S!gJ(C&KFNa$VEpaZ9oU*~yul2owQ#J)oYlDHm*-^|OO)p189SL}FJ5$l{00)Iw) zxcHaGo~ltB^xMBBdwTqnkF32>yu}gTw*@aglTw_^BAmMg$6Cyg&TSFSCk1B-Y#i+& z?UrqU8t3HP^M34K(iko6C(nJMO>{e5)sN$g_d)|4|H zUuQez$vIOkC~`){6s}V8gujJ&Vo&1^aF%)aveGDpF&geO zOM^bYr}4BIb34Xwd1_T!N*qS~l-D49uQcx3PQJ#Z#CCG5K*r72fXO}6cBgLqq*XUJ zqV^6SB|SfNOSFbF2mZ-M!~)zJ4q|5V8hwX(19+TaY7^r5?SOF~#Z=c#Y`L3Y)nAQr zUk=(f!eG}CJ`k0AYeKp2iOPNdr1#Zo4|BBND>7Z4K>O9^Jt9+n(zL_1plYlkUts+Vu;67Oj2k#t2?+?;C9_n*#gKDa&Oo4 zwe0f^-x#kkFF`$Ap~Q>yH=CP)ZRv;6CAEkd!(nACg^Ds=0{w+m~8#MCa^ho z3A272#*R%V3gkPeJ6?W7m+JtW7i%rJT^&=iKDIBO02@$`K5;Ml%b1kq_RsgDb<`e# zeQ~ts3GMGC&^{lr71EyS+N-18D761Qfp$M?gtVu=zN&WMRcL!>g2InLJCBtC`|Axl zhG-wM?|*DYnAJl&tHpoG#cSE*P8zj8u8saO2^85IqVJV z>-!0EZvc#Q#Hp_DI@;$8?H?r29!9i?I-UIcwfgRvqkWOk{$V2Ri-h*%-*?4~7?-u{ zJhN<~H9$t%2|t`&$1ZsEWZRcl7pElBX{{r>0biR!of@F zUjm!%;Eq1$FVqLerEarWY>~EGzK`J6D2Q_*kkh!U&+|`8OH040Xr6dnc)9JREIhT=iHnAdugsz>XzKe zsafW26P!nMGU8Bg1CL)wkb&=O9hLfPM8wLUM|4i=Uu8wO^gJTv`BK1Xk0)~;(K)H_ zf>zRbM3&ZfF-puZlR1y*930lzfwxwY_Gq&|bf$hUx}0cD`VA4zkD~f1zb4H-dSdEB zWny}?k9WiW^5Z_-PtVz;&fd7DuIm9owM)LY2Km?rS>HRv`{kUF_KUOd_{B0m5#%km zZmj&>C)QizJKU3N`-Z+P6Z){WEXI(Y#w=FH1RGOfv{Bc7X(7mAxigEVrR+SRTwcyi zIg8c^CjHoCYLwxq)Q%%MJL#y@x?p|1+&X+8*4?IZcG8^GzXdVU!=0TpJ@vgQpPe)_ z_26OmUfSB?ICxa*pU|JDe(n%yq%oG{(RbdBeo9}*?i=MfpG)xeYYsD#49*7)uT<`m z)X$`^eLw0);a>Rx)aWIM{*OpDj&W&x)A3h#7Wn>ueEdB@a(w{1oq6#+*!V9bmHPca zOii?RR7geR=9XyOOdiXYi2uN-%y!LG{H%aqTa9=r-yKdm^NcoVOB9bfInoP%FU6kK zroWq4Bqv`uhVh)=HfTQ;jR8DY*q;8Gg&k0ThHHx9#qsP?coSTYda0B*79em94U1SmJpe5c%{LYq2l*~2W0D%Z9rk==OQj;W7QN|2&*u8k4U46Bo_Tg@ zP*zSx934Tnl~5=C^#MLdChlY1k$`&v;#~edr<};m=wx`&?(lu^cftGC@g2b~}^ZPZn<*q1xt=d!P! zDDbgjc7CA^yBNMM=lIVDJhVMLeC2oC~F#{Ie*B^YhDKb9HXc z`sKdO^8%STPF#j}3Uq$1I7xn}W~#uRgXKY=Tp_VeyAEPW(*|vTckKIZHcrX49sXXW zMjyjnCYqly{xtryJ$^?L{q|^Mbd>_R(d?CbxPDZ=Rr-5tCSFHkdm(AInN_fJ{yQGf zt*j5M#~Z+^-?(;Dl9y|*s@vpUr?-KA8hgE_9EvoLEb39-J@_ez?|G3mAV(*R2hZ{u`l?H166KzR#o}He}m^`lKH+ zioqG1dSzV2qm9>5gR2mw_IILsr~PA@r$J8IrVg)8Y5NFU4jAoU&!pFfqP3@pb~|%$cpjc?KVY-JW>^9~^| z_sHHRc{K*ix#0%EQa_Gg+c64||IP&d*W(>M>ZW_r{5J}98>4Q=I(iPhDGS%Bj;5;a0^k^PJ-()P0bvmHIz#*f2#w{eY)@HCV2_F!$&=5^Ud0UHmu)ZWca*eXYxv_G*0QF*d=@u`R zJ-b{kXP?%f72AAH2CnkC{pTVEK5SW=i}4liSg|Lf(Wb@oEX9DEMozU)KM+DmcKvw* zxfwN8DEtcFGq6ND-k1I!>C`Ng?nxne+G4H|rDV+}E4Eh8(b67k+c?h@W*dKALK|Na z?JquVif!D|#)&mT`&f^*9gqGB{IT2_iLm1ZHZu`+hQR6)VXXq2l?cP{)1bWBiLg}y zo8!T3Kdpw%nW&%A^O;OPT?K1l&(P;tOq)?7X}nQpSG;X|TjlNRBc7hel!Uh+fnMpF z%qm!9yHC*3OTz0(py$?b9Di#P=#}E}tU~u*BO~Qp1y{=wmzn2Mx;1L9rPKGdzXzb_ z{c)tR`50$Nqm64n`yrcq;0O3Is>Q}EK3*_>X|7Mn7gzvXlbB@@t+MiM@YQFTAE7Qj zhjQI0K`6#O%vH|COnM30)|m-&6^=nS0iHn*zgOUPL0*F+ma-TH!NjqIFW#l1^{Z%bS1N}?pjKF_F@a>$@;nN24RIvEPGLA{H zFAPRyaE?KrOV4!U4mC!RhhrI;?-a?`ALXNW!8iifRQzB33NtcagXCKi<%_@ZOZ+tc zbkA>R>PHwM#sqhaAn(!`lHE_jm{8J=4o3IfFJ!wBObL<3aIqbe2Iq|}X z!+P)T96RNldlfp&x8bAktV{M&zIDac$5|iSVR#Y$Qs8rMg#CRQ#<@5W+jr)|9Mt`# zIB`n-hvfRDoufwRL&eYJQ))QJ0d(@PC#~iOUR!)Ez-j!cO}kH!lYEDiKsr+=%NM&B zlYL9iVRFCzIPtvjt2LjF{IqJ*lA4+F&po&R2Ga3ze+e(XeLY~lkzXJf6{#I~7mKlp z(zX(xT?1)S_E&Au*WoMBuDnL{l}F#yU;Nazz__h{**qHa+7#uXZ^)UbJ^hn&z%ZxH zcZQUeyvCbUqUKu+9!~_1hdaCNf(Wnt*=iUpkwhB0j;SX$>-?YnJ>C*X zi)-)Iuvou?XIQ!}p|XDkww3MjqNpr-Yr2=W2R1<8yCp5XY@TN%&-y42Ee7Kxv{9VF zl;%?(qtW~Zx|?B!C7)jplzVQj$rMDlN$c$jpP4!j^V!mU0yh6CfDe{g<4M7mKaO$4 zt$TID!g1Y8M|b}EU3hU0@GkJg{Q_@G%=e@~lMe4`*Gf#Si&mea)c7fRB*%51$BqAcV7-unze4Qxi`rs| zTaoh533K$h6CtTJQ?@Kt1l`y*2-?ZNV{{TujdghH!>B*59vr<5f8Q2Ubq7Qmx@F8? z#{Q;4hh5R<+X7L@|`zm@8D|cvWWV7>N!gAr3-)+$eM4Iu zYmci~d~1SUm%Gx@zJ9y;g)_lx8d=p3`iT%p@`H{M$R22nR}XriaZHBMfQzW8j$vm@~})c&3|W&5i_Qnm|=@8od&Vtj_9#a8^| z9A3|+)!8pc^|*&%?f62}>EZUD-4)pJUkD3+-?M9VGX?TCyywM>GrQMf{GeCPwu8nJ7 zgeVT<%=>|BTbErMd zMDM#XN`>$5sI2_m1Gc<(OZ&C!zB^^D-LCuYgf`jh?7A=GW8@USXdrX)_efqn`DEgp zI-k_M{U|s5y-NE#Hnr&2()d%q_}BQB+|J=RA7_4%axLVz82j2k2i?Dk3An6`)ri$jB;(QuUQWgh zfxq^+(DuiSxW6__FusVj5%t$PqOpuKRL+G;=UzwZyQz$OQ9ml5#+Q@!itR-NPU}%| zCfX}jjg@7)j-4E1FCBwL0kcQipa=w3e-(^tGzdkNyGg(3JXGO6&h8UsrvvsmF&j-&>$l7yGcMx=dVK>R-Pc zaV6H5{zmwiR|U4d_8s<}9P77z1hfw4w{5HP?aXhbJiiax@l5wH?7@3+yfh-OmWFZ9 z(Av-kOZqkCiM=}CuKYvP?~>zkgJ3CjVe2D4$nYw z&%Fm)kI3uyEG_G`q{~FNdS!^s@{gH5wEbERl2u8x}rGC)4@D1I0zuXVY zvN%>SPUOqC36>3KM@ktSLobz>7UvLLJ=J|QO55?ZL(aR@-~H>Bc?%;*^anrB%Jjv!zYg@{d|fZ?<7=M2 zCO(};?GC9l!gp&&TfP%{$0EA4XZ$)R*ssM^$1w8jc)b}uQ?2>Bl+X6bk(qv%U5nE; zuBSB&pw%&Y&-&Br8ko5>z7`5~_)AIRA~OwEO0R1w7$;i^%?{x+iykn57TJfEF@*ba z#3cI!@rzZzd4?86tcs+-Yr$PlTEQLxfe4`Z@emMFphp{_t=PUD!Zx9C{5u6_5!&z~ z=!*A?@M*B_&H0XcBwUr?i8RYaOIkxK(FPdRwX4m#Hn1M&${4f*Xb+t0n<#3hlGc=Y zPAT=t>&zez_W`nnxs~jCP+o)@b-0v)J$;9?vMnBWOQFRqMjc*DVb4-BH{!a=7LkbW z^H3xD-jhj4OXD3kMo!!DPve9y!TzTd>O%8To1|uCP+3qo&+@8w*7rYR_z*xK@0fP}&q)5bdqy%GI*LPB^v@?qFeE?gZ~_r%u1q zsyEP10)Mi@GxIlC-uU?VFOuSgFIiTheX_((aM8yMnau zNZO$w?YojT|7f4y_atp~koJ8^+ZUw$K+--Jr2SCReifwMCut{Oa_M-!U(&jRw4X@Y zo*?Z%CGEpO+Ak#She6seC9MYS(v|URNoxzz9+$LDLE3L6?S?2Vi#7<-a&_Ue5_XT* zQK&DQUXAb8p2qQC=;4E;T*N;+`X0ysa+FT`KK{mtP8|OoW$52ohW<-De4qcXl)+zG z27gl-{Brc)T!#OD^yvHYc9)_5M3f%a&y^lNnwwm#uNQgreR-?P@bm2`Z@hl*iqhly zKeY_~cbB36^D^|$DntL@%HSU;gU|IFUUB}nYqE?iRu9zAC(H1^rwsq^i|EAZ-&ThH z$I9TBCs>9kvE{1plZ@%L^?_5JpTD=Ya^s%H1*Z5!*&Bf-qdRwP-ZG3IEv>9VklQTXv3YpghAo##0uh!EU$Se*x-G@$727V} zHoAKoPA#vjI*RboZNu^~9S;M;yVCXP`AeG@1W64@Y6+7Xk<=O{H6f`jOln3_yC!jm z1If1_seVzI*2=W{Fs+Sg4Vp&F*1dVdhGE&JU5BSM{8{arTXy1?St_eo(!lWg(QO;Tbjq@1blW96Bi#1_mn$MZ z1^jeNWfhkLLjsaaRlEH7agE9ci@ThML*yBCC$%wKk~u#GQFd_v0qcwy?k`X zR&)$LvLkeF-ndEfHKi;qM(9~F&~=?_FEb5KxK^CSxa-VYzTl(u!XB~G#CQtIG?jTc zp9e@#|K_6qu7kACV5-kMpDm1d#OL!QF5RbA=JOe(|MBud;n2BI`zeLOGx+|K-G##Y zc7pR%;Tfvog@2{bOedUp#AiO{Kg`AWY8roq!ZG;!58>F?DdI2?C2bbtGi6u<)$*x% z$N*%=<>&d6)B}Ex%b0`4)ZuRt-o;5@W6a{K@P`R${4b3bwFvcc!U<6+g>TO+6#f?X_suI5UIE>o`*i8wO}{M^u6?jjH~_q#o-!e63k>Vw$HK6$ z)6#m-HkSrwp>Q<*ILM&?dHrEKA`BW~Nn`%QRRG3u{P;yY{v56f*CLX@*bSpcjH7Kp ze8f`Zx5H86F2eb+01FYr+KIRe-AensT6AMXStjX%{-ibF}D zpQ@rXv+<3w(lpkJxl+9yD`shYQbN9oL*3QmeQd^6sR@l*GeX1Ny6zXa_{6l`63^zl zGIm!vclR>VZRwMja@RZ2q|*Q@-?M~ozb%}INEkWO{l{tCb=Nv~a5A3jzYBz&e8A+1 z?tPF)c(k^JHKr-j;9Ck ztOwn<&6dG<@^-{N4@Zr$P;#cL$CD|=@{yJnsO6j@kxJ!n@^!*UrRHH9i~ri$2qe&* z3cACct)EibJi5ZheJUmLXBV@ulYxh3UM_EBh%(N3;gkTYG`VU*u$ntf5< z)wGdjws-#>FRM$gvxHIOTC4$FgjBvmb~0vuyodm_H?eHxYn#^Y||F( zM!GBa>`9dyzR$olnw{W+R+T-5waYz(THB6xtQpGVg+W`KUD6h37wc2o0&6Rcnh$N* zU$Cq-|BEK3rU|}TnZ}=v;m4Js5sj0TOHPu(s)#U=9KVrBCI?|pwPSFllAp<{f(o6 z#e2T@onwZMPnV(2xR-#QgpX}^!JPq49aU#F-COUzH>r|MgkKL|gH~B>7`>}fu3ahH zBV5(CT6Nnh=&SKvVvZQXONqFiZn1X)lU^`SS)wPPFl~|a+ZF#5tUq$B zpua;NOUqE3uIt16RBuDyj@Ah7$+FkPouF$u;JT=eueP;Np4tGrK6)aqp28RMycsTIu?c#x|=o<8uPbpXS>v6iUYxwphPjxldsW_6c*o#lG$| zT~1DmdAi6L=85<4Ewb;3=MEE(wt{WK`4Xx%!t~-=RvLfWU&c!M3tO@EH*{oXo98YR z$2a{a?mW=*KWoK1+K67k9;Ln#p?nj52YiXc(_>xR9UJ-^}iT?i5&{ZO|b# zsB;k8_LA~C&))KTDzvU`e&P{`?ZTheh2zEP6He&AXr-RwGGK>{AoW;F{T~F;rOiA8G&A@D_UsUHCID=4jz9$hdr6r zpSoqMB+ogKGI`!-OZgtYr12N=BY{CbZlQQDi_HYo@8(RCJAG)K=<8Es+%-g~tz+&! zbLXNc@0@e#_`-3B<3|`p;*43xDp%7lM4E4X!!eih8mog4kKRiBo#W#WYIHJJl*oJ{ z@~Evm2XjShB{@Rst|pZ>@Whg6Z`vm)%d)XRJKtozX?t>GC)*EeWd%M})?|z`(JHh~SnHu5qyB}@^L$WaG%?Im zig5;X&T)qR32m7F>+p6&VQK6Q998sua`MU9rOoLu=fk(5>}oM}=1kfS!{3D&((`jV z{P`s|fGytDZ5F(<&UKvCGhg)g7wJRT7T{woNTis7FQy(C?F-AGR?AV2W1n7zruub$ z>C^@H5LzBH_|6vRtLp)lmv%Dz5 z&Z4$M%~6Z&k@Q5u(aerQ7M~?+jTtjLh)-0O*p2!!GKChA&S^Qw!bCcQXa(%U`fDyBE%q9}^<$ro$Ep!fGk4__li~lnuT>6 z#D3O(*orRLKmH1n!g~Pe0lc%w3%@)s<&S-`>!HN|ctMGz)=Qla4N%|Pv6pWF^bnWX zpB3nAwD@4G(r}_btcF6GHlWeO!RmcZTQ951W}ln<8R zW;*mU*ox>Evfi9z&66sPtw!g>oafWOv~OqfRDRC$)kkr@W+)*e@xypCqI^BYbx2ON zjg>3G+5ww$Ay%cz)7QRY_nWg$D929lRgQo5UaQl1tKtY|dyDocrRUejM6~HSQ98B& z&T-h{_{Z^ZqI@eR;K`@O`3UP=xq5co9x1OU!CJ=G2(A5QAKe;RZhqIete$eP4mwHze2iT}lJi=_#xg)1r{H+d=k}cQy z)9PjL^a-8^O&MzCEQ(UxU9fAVsdmP*CZNElQe2Mm^Ua0N2`{H6(6_v-meW~$d3Xv( z5j!t(&E974}8+;iT#D`?yNvk z*ITIxzetV*QQfpw_ z3D@;(KAT34JH&!)ftFW6*NvF(+0#kOQ@6WT)b8z$S}fyvv^lH|U2jY-rxWGy*HN4W zr#;m%SR>Z#5%Do7}BF{Vhlxt2rzSu{E9iFbDk$MjH7`Z(9Dbo_(@@DeV-7lqpCU9Rxx+aT^~{3*{f5_#4g z1dL`~EWKA}t@I9cWz@|>I2N!U+0#9uLSnaD@tnq=mRXlrCQs21`>#J&)=|jy*TeOK zkW$#!{rHLP3GM>knj~=^&_{6L-GPt~Fqp$gAJK7em?OXeW-`1?B!M@G9W3<3V zT^OHsv%rP$ZERa6HCG$lW57CMo482sZ10SS*|{})AZgRu`nJr`*eSh~k==#({h8i? z8|U}BQH#Bm)aJuKV6B8P4qASV-Ik8(&a?16B6?J>6gy?xnqOaWdrXgwnx%HwXRQD1 zEcSEB*CNak)KBHAB}Xc|ugZFk)apd`*+y3xyW+^XRlGwRQ{=4rs+8YXW+$|>R&b&7 z92@mYi|5RdDL#hAJ!@Z=){d8YZ+_3SGkxydU^}!n!P-vSuFG2}U0K_KoNOHf&>&ak zIcxXn=@?gznvP4!l(xXGk&c6VpT z&_HHocBnJEG_#_2a46TGT{?hHjdu)tIy;9lOZrv}F6mos>K83CcxAY!b7FHWNw6Z6=%G5V5!a-gkptH9N>GcM$u#w1MUtcd6s5kY^iB_oHjX03U zQS9o=^bQPwRx^G*AeP*K0yM4ws=38}%f8Fx%gc zYIKk$IE?~f!^rk$mv;!EX1vWI$wie@sjV$|okWsXWivUH%eoLiE8ewo_{%c==k+Y_ zB1dgFvQYq?J(=FVE>vf0J9>g7EsMAp9SH!9n3_{usJLwrW(!Q(>&vak0n~^9 zw*yheAq#54*i!^Sn6_pP<3%!oL_>r9S@PJ%_koKjLT(_tI_JP=DY&R=Vae3D*JIL) zjGaTPpvC&wE_YT4zWL?KHA$bNfwk`Z)Q!l z-%;iHTAH`NhZ&-(ZGc_Ck86^BFNYS`u&4pYX-TH+${~zp1%H$tcX(NtPKarCBUO9 zZ&)P9V~PGUYjS;PHbc40V0TfndLAl<{QX0#O5l-z_4WK+ykZq}^k*}JS<_J8fGM6N z_bl%jM8l3OCLm2XoJ=5@0kqOS*i*}HeY3&&AEae%A<$d!>(`Ptm|Z=nwy~kUjR*bE za(ArgALxS#Y^ZNHi~S_%yYJhd` z)?~D4RPfNi8k7#(-osS1N~;^G{z%u24e-ST+>wjMiJ`uwOOdY;hwQ}x%aPs$mr)8Y z)&j-{7Ssl>1%M?(9o;Z?Sv0W5c5Dj~+>T|PLn6CrXllSd5J{A-x3o6{lhx4Fh+QO- zn++b%O*nfH&ppxs@Z5~KgTSc+*s6XQn4Xnr7EP`2I0O=v8vxtjH~@(8n4_2sNgQ<> z5$kZt?9xna!j2V970chzi!94!=v{y$VtEYFsFS7won<`{Q2Zq3br7L<%%V>mO zsYt4|!4>`e*})+tiqT+EeIw>4S`a%VYZ^wE$p|a3?#Wz3jJZbms7f`5kvZ2l0F%`?H3Tw^q=QUY}^nqL! z9Y*jG{*dz^@Y=0Lz&$4BGLo4Vgl5{)fy$R*ASeRtI9W3lR$`N2sVx|aqSyZgFnOwF*Mih`B z&3eSAgr<6okr6=h>!VCsZM{8R-Gdle2H@*3(;F0uZrPc|K(wr9d4{cuzy|Zz3KT;W z$qMbTScShag8LP}x&h7=GhdzrY$dGGWqtjyTGVz!6Mj`mH5wx+nrp%ts!rb}y%D*ujNqXpO-lMI1$K02JI)gpn{n={ z(hKn0h~A9zodqV{r=x#G&p>tvPRX*OUYpI|L>RV8mSzWX8PR#Od9%W#o|N8OG$p${ zv!pj$9240oQP?#K?C6Iv=+Js;HgDBLI!=SqmGGhHf2)x09*_RDnnsfYpBd(#elKZX zr-07vz+ivh8Vr(`L&>KB!3pfu!vU`%tU| z2`jLnA>W4;R`kgu|5;!kiD**kqM@j)k7~M(u8yxJ^RWn!>4oE-(E>iMX~L^$xEVjW zAYGeHr~^)Srh_)AvDti5)5TCaKALePy5O_CMr5&iXf~fxz!KQuNH@*q)0!sbvBfvG znA;+{uDbgB#5)4)c1PRJ9VE|ZG+jDiGztLwvk@idCZb!zYUFJBE#@B;rK7Kp0}Ar| zQ~MY!frgj@&WxitA-G}$`D9V@cZ zgxbu%M)h!n`WLee+9B#u!?`lcpzW^?GhD=@%D0s=Iypj>}X z9|jc>4XNMK)S#Ctd-eDMXTL^reX9^JfIq%`kY&}IyA|w=0p>sgxJLoPDmy%IzoSW> z+8fp>npgJT~iv%?ADyV$rb+91Wq?h#!D2R;4Yck<2xk|A=z>4Qn-K1OPUf zA4fptd{w4D*NK^$=&{M%UrLi*64`#D5anGONR#;)(8{L<^fku zgWu?y&Ce7fnw2&yv zqmEpZ@@q|rvAY`cB9LmwFAWzPidD8$YCF$)V-s_V6XzK7mL9q@$oiNoE9NC3ME2fRw2saw_Yu$iANvC%aL47CZ!OnBuc5!lo-dWF;50#&8E_2@T!@n zuDQijCFWft&{lI)l$9#ZV6`VQWX#%Xj!vMf8QV;CBH6V|=T+-!INJL6wuU`5LSA?e~_Og}+{C>4m$C4F;gF5|0 zTAik8m`|`~X-d?VfHGSXVl}fyh&Gxz3iYhv%D!HN*I8sEUksBG!fzd#v&kHrNO!H! zZ8kVonK^ApD_YE1gk`7W6=D@1;h&IDlG181Cx)~Jdj@49jkT)hyIex2#k@dMV$!Zg zhywz(nv+}xPr+=YEp6uH#Jp<++HP5dwG zpzNR!!cvhVE)#0ELupIKCbJ;1yfu=u*_`Qe`i^&NvpFlFK+V-+UhHz| zGS?z3*`1wO&{`>|)hu*bV8>TVG($LS#gBrEj;zX~42v{1F!4$Z2nvfa zFMXt~#dL<5ijDz5*~H@3h~wRcRV4*GA4S(X0O?97Fv`|ux?MH~pX1A}YC_yV}xbn z^l3_o{u>FdH@S$Kjlo1dHz4ObQeF)R)dtfa%jUW3ST`O>EOU*-?c1@5J(fA_c|cf^ zP;`_V3s$k*K}5Q}$*f8&e61GVY*xpzV-wmV`W&3L&ORx+(Y)5ng@G0G7IU#C z1y!^faa{p!QDAX3I+_kJ-&T(lMh^I}W}7B)Cz4&uWb&vcYb2sSi}gm1&V+3DXu$Bn zbuoXXNilY$fEKeufmr#$HYghgDDu=|c6#*KW-wrxU4EWUl-=!kYc-cBRC$Xm40B!T zQ$l-%fib%^O_pq9gxbs=1;+Rmq1w!43e+_s*Xhv$&Fd7T^J-5y?dJ7K6cKN(2Q%gk zKE+}U1Mt^AK*j~&n>YHUSTT`)xsPoJgnF#fW@y)vY>p z>eMN0fuW&6o>-)q#cu#X><^92g*i^ZCl_g2xXt+O^9F{cr{r117h;E~NHmyR>|tha zu)2S00r-lUbpKkAQw>bHrx~JcCfyai`{@Ob4?(bE7)$9X{pMDv2AX}YsANS4I~+;T6> zVBFvsxEt(4yeOmk5t0c#g2jz`DHaU z);|{9mwQy|UrIl_xFWp?qw6aS@%yd5ZLmnbvY;2Ud;{a+s|t7?uSyZuapx3tD|>sw zy}BUBa^np18bkO-My!z=*wJ3=c|}n(PQRdUVBR{{z#9E^M(<%2!0^wjGv|s^=YbUSL!{1T-cc@K~^Xs3eA=4dlayFmBJ{zs4qX zgJsG`j5t`F@5=_Lczu4%MnG{Y{>2!Em4$5!+P|A@H2l&Sbe~!rj<2=n0iTa_EmYB883o6o z;S%4p_#6M)NII2x|3UFJ{!N9PJNJpp@ox-%rS}Hb8jHVkfbK(#pj7Mc@v44nn_pOZ-3Z<#pCn-{E zNhjTHv(%V6nkc`wGm_=3ugT$Mp6# zl#elzx3GB`Rmj z-8JWwm*yzf-ExUiW6=I2=abiCcl7S1MCI(bd*qy2?g_U!mnikf_Gu&M)6z`3W+_oQ zCtWM&)Lf=qJC~?+JLNh#pQi1)iBh6+cHLyoDKvEUsa&Eq1H^8|D5mu$`=j?PMH*|v z-OH18pHtOfJ&7Y}I_};E+SwdG*0B-zr$!#F^f{KXJe-#%MXUd_3Rzl^jlTO-$kJRW z`}eJorFGcc>s83o6l~u0D`cs84yz3+WT|Er3DXs_)GC{4!wOldko~Zk3R$a$Ma34Q zhaC|=p-)aaPFKqqEQw=7D(4Z z=iOzr-VP2w*hWkXE%*Kfr}t=Sp>&>TfVE8vh3(IcuriX$ZehTEKqAC+6K>6@m;{7t z%01AY`%8-_h^e84+4Di3d$IWIn?dO3EpT!WY=WM)t5o;p1zen*3Fw z@ZGi0O&*>od~z+6lSd>9Ut0?+*r|!aht|St_mPRhH`c=F^{7PY3Z>)zI#J}yYN5$I z+DNJit@+N^!ix1bIeoMh7hT(V5bQCA*``o$J^&oK$P=BgeIX^{q(w+HT9(>Dm3eI9 z_A0tiPxI8^cQ1=jq5r+nWoK2>H{CxNMUHfM%0C)KtOtIT+(IpSoKd$fE^ix34}tXf zBup9x?dS;1CrPc&3s zNRHMB=}AV)L_FCj>aVVcJmph@^C?C*$8aJ1OOhrX3q|ayMv?0Alz%mf41dP*G$UXv z(Zd70)QU%*?h}_q?=k9#=rat}qI=T6840yyjaps{wbpjeFu;4;OX_E))Y9Wo?anlc z6xCCnWt1I@vvTN5c%JQfLWx)g+KDg$pJSj+gTl`t1utQU7HmTrO5gK5UpNfWLX~^I z(R5)eLl7F;3p~l6U236Xot2ZqfPtFz!a`yMn(jpg?C|+Vq~%_0NIdAM2lUApCFyK~ zwyw-^43%9;p?2I$5>*Hj?xjWu1{vwF_=LSosB4RCVYE;@Uan8xUfV+Jc!d(!8+tWVk- zNn_gqvNk({pEG*e=V+tteBKk|7DpR}=L<$myBlqEo=c3VN;Wfc8>Q!q8Hp*Sjn?y} zjKl=lM(z2sk#yqGge`Zek#tLeULt&L_mzyeuqd>S`>LmLzD0VQ5GUN%GNLg~y3344 z{c-A8*rwdqjU>*c*bS}Ux3SfH!|35?Iy-I+_sxtZ8)KrCw$V8*_cE|+hknMqeMO=Q zTiboh2r`k{$k1kf`?e7|ogRr3?mI@LiwEHgEe0Wmgg6m2ureK!$p^lCHN zU0D+&M;9mzv))w^ivu)PQ$ri>dxl1T4>B7KaJP`OP4|66W%*+DCRL6Z;Ri+^?qpBe zTj>O7yC3F2mZal;WXRSbN77`+u+1F!W25D7Z*eIEo^(GknD1koZ0oeyhr2qa2=&y# z4_}CDHVeVV#CeUeh3VPWGi|2FpBcbu@;Rvl;qJJfd!g(g>IOoaMfrajEv!~;?a*df z{)-@b$I2X=Q*u^Rq;%ac4X~{rNKBg7dLab*VIXfaWB$r0e8())Y>$-v1AW~?E-yn*^yeH^b%51z}K@1-! zv+w=}*taOh;@UV49$>@}_(QivL6}i*X2@t|yTmJ?y5=}$C7q8+_2vQVle6fwZRS%i zD)v5RQbK=A9s1E5r?^`ffeK_Em|wJ`fNd7~Tnb9hrLhL0n6%ktJJ?ulm#rnVDR+o+ zi6uAqq?=1R%Tv0{p^8qgY@g#`GOlz&ZM(yiDvHG#Gku_Au~oVc$s(*1?g%AiT$ApW zDfO zjfLEBcTzGFIcI<8Wqugy9=U-i3(>UnIn*y}*L*&qO9LKJ6vU%q8@;`{RP{ z$0Wr%?${F34LeCS;f^a1HSw$emd}nbNHqp4NY~x9Kx9oU$P~)p-3lbR!VB7Pe^Q`! z%vzA9yL*AKb0;I5%51rNL~JeZ##5+)n-$V-oO#2GNQ@{6E6afiIYH`oFA4B-S1O~` zP@OAiKbFjMQV4bq+X{-F#sE3X&f_AJpg>L6sY1&qEGpD;6E%oH8kVl@Cad7g;>waH z?6|2a#=6%cU#;{T$}{1**Td6Po=JC4<TemF#q2=82GrXvDPdMU?3-;DFD*&mbX%1N^>Ts8cG8n=Zx5q3o9HP1!qsuJ zk8`gstUvMAEz{UEA5h7XWYf2u#n)IT9t~b9o61n-Hhm{UdRP_b_VLov75Y5%Nfediv)v*&O?0^Be^D}|eu5(VkOHf|Ge)DoEEv{#Z}gpquEUqTJBsir>u}}o zk4E&clFhd_+VNi%Kx}h#0%Kw2M*~ds92AU zWV);@ZXly$omNOmDbTb2zJRh+Y#b=0{ZaHa9I!-M+x?@`b+1cDebnd2C6dmLxQ9JH zkz~&3qS!nkk=R!e8qV^GCR0%tJ!ZF(P?kk{7YC@*13_eWQCa>u5G+Z@Juwhu+UlaX zJSh+??@9M$C1|11Qs9|#Pf?N%LNd|n$1dY{7ZcXMRIpt8=~DQoD(=-AjUgu?4q2;8 zdQ_c%t>a;y>7wpDZ44|{LP^|mPghjc0QFD}-Zok|Y-496oCqgNEcO}74J2TafwB|H zfG+|k-7^wR4&w-M%01JF=zJI$bonmyt~*od`f76KWT&vBempCX=_5jyQNN2l?b%BA zizz7DM*RDn3SFBD1?0I(_e-8Gn#c2$V9O-}$cQ`6HkI9R&#%Z3eW8n8?FCB5Gjp^! zhaa?VLkcqK&dP+XiNB;@H+9hxURcHAdaTRF;ENO$z0KZF-y0j*i!1b9QUYWv?fzU67EQm*v^>X zBn2^#Ds=teu*JPr>8U2>K{^?C(R|*NzyfKye@}>LlMBmQAhe)2E1;FXmEbbF z*gT;=(1OlWKt>f=n}(KVd+IHgLccdt;$^0fBWo8c+gp`IMs;shMI;)_+ag(Z-y|P2 zmA6Nt&e)Ps&>HN$&{W?^2eam}CW;aOW3LEFAHZ3Shkg zPPzB2!)yK#S6)!Gr1vQs;WODOmhVI=3f<`aN)8QTHZX;n?gIrxGt$5cwcG~_ zNcSAH>CuBOC?FZuEu^nx*Kr>z&=}j-60@^Xz@>YH2wzM`pS=~@K-l>EBxi;8h#M$E)`vM?imNlCi(WFCvi0bnO5$?*p59A{fc@hwY> zD}?0ZgoLDGUq`9LqLdj>9G->D){c?=&=mF~6l9C108@Nbh1u;-aYq8OVepr&V=x9VgD#==Wb{T(MlJyl(hJI3#^)=9h zt}e;?Dr_|Vv?SZ>blo*d#vxy0GRQk(dij|VeSgWrT?^iHKQC~;&=B`p;FkL@g$b}h z)c@2mP^o?q)0ts6XmF(JxL+zr!seSF96`En z?*a+ue#iwSXdlJo2+Ep%U#LF&CX#kmb|m*pB<-kZJNqjs#Ss^AZFhiD(GxYAD}e&B zWdL;C%@mN{v^bMZ17H*G=8B1g-ZQ%<#~J=~@p zqczddWlf}Zq6SxTY?_@G&n3Kz`Q4PL#UrXIa5admAQap@HOKt} zmXVxxjoZkQ-{$rl*AQgj%*=?hU0(oQFMOnb}tV)_2$%eB)dkS>(-^K8ZO?BVrSJ-_vpNSX#7IRN@;#252>4DJ7Q!eh{`;Fx8&0B2KBxk*=_{QPtl=J9!7v3H)O06RARkE8KRkNHA)J6_6GP-ylWlxL4_G#q|X##oqS1MAfy0k{j;ziEP8K z(whWumby_Py53N7%e}EA%UVN`Hldtj0zBX_WDb|{KvhuUIVTX4Fz$#6ogk`niCU^j zf;yLUf31hXQ^a?!z@k{mEmxW;hKz)JuMGuyM0$=1Z7s35ouD)RP5SQb87%4pQE#*p zHZ~6k`y7+bFRf5NOC#;`61nAFRZO;5B}=gL>sZq;oIC4S%P{;q>lmBmA`=hKI@U1^ zC(k;@!7NGFA>ec69p;9UhD{LjxrR*{Hc7DO8ph>CB2%J1*D&5%!LIwz7|_>D&wY3d z=&PphJ~9UMbu%CobUjaBInxA%u7kdIhD3+1gT8uZ2o7CXNJ+wED4UD85yNqnQ`4R~ z`>cW?GL0t21vKGa-B$oN&;szYJMW1vXhMP&R3|*Z9>Jhv<){{}()t8~j%1v|7Oivx zqCg87gCS@C<@}t|*@gdVPR9Z#yXl5Ci0GW9;mVCc53OCYVHtQ?j>(B_(-ahK5z#qI zsi-Ohib_=H3>}H-&ZKsT?JS54*Lq=}l3B7{8Og()o*AR|RZ8{S!E#ni#IvIKUZP8* zVo6hgqxK2Fokw*Z60`}2EV;@$ff*IsI2H>{(IFnPAc=x&rBcEzg9*W_QW0HRV1luV zV)=EAl64K5$Skv!Z1-w#!2~8q$&x+nUnoF)2c{WyjrWZukrsXxG5sc__vt7q5f*-3 zV!TbLa*2BIn+j@P6XGKx`AWcn-FV_ zFke5(Mi?DLW-XD8v4wcPH3qNT$mzL#YN%AFrNwP=3@RZ=R5T8ZHCXloSc5B$ALCeD z7VgS0L5N@J18QUScQp#a${_6>PWzH5r-_>y%VkvLkv(j)dV(wnY!k0rgBq zmTZn5mILA4Ws~%90lfc=U#;CXybo^a1C4=$5Ed&0c8!|OXrMb}3UKbZ05C7i%RLwh zasavjdpk3*MQGPdq!if37fahFwrd7Uv2$1gyk>ChFEec9-YrMGnNfve8~l?TTkegv z=`N;}mMQw~37khu=9(BC(`OnpZgYb0?bycs7bujAtd}L}GR%)!V`}XOrjcB> zx#s&rBs-jL`scJ^Te-OTCK|A+U3N;aae9@EHi2oGd0Q} zAi@HHuEm8|--@|mHlcVH`axwz?qaxKLU1fhNRz0ir9{DORYbxT;ZHMIt6vz@?O5KO z8Dl=@ph$i`urUjc4uMcJ8HvM6jq_Ip&U%h`e^pRcbL!iyx#I*@ftMF!>x&myLRqrN zKEL=s&T;@=GiPsTN8-?VM8b zBj!)fCA3S7(uitGj_xG5N4(M!mQRv(O2*T74^BLyi)7fqos=-mTy|%r-hIiZ31k{G zm>(|pq}BZ8Qda&UflDLD^emIC-n(Vs3?WP-Xm^L^I9p^7O}MHCX5P$X;QDh);?cEm zA)-lG)0l;7z9Pq8axArvBhbSWhmW)){;^s_w$&by(52;>LOA#3B~Uf3yky*+mr#F; zm)r{~H}%RGP2N+4G>xSTED}IgwwjHlOJLJ=S@e!FmDD4?sb`bRb}`X1!%J)EaTHi& z0snW2#oxt<4}nY#YwUSTb`qNQvE!JuZ$+ji5lv%e%vb7)AX|zmqD4s4h^Bn}EdWtS zoA9QIP5KSG2xmG7UuJSTL^(|ic6X%sJjhllT9+wA6nTPxr(^i$dMgWqtwGa;Z<6?@ zk&o2sQ@bVwBvz(nIYltk$P%pO1xJ^#sFB0JoSz2xszWXIh>;q(Ru-0q_%w)z-EOs#+a|vhE?CWHl}V;hS=kgyB`TK3gUZFSEoaIz90sZ4%W&F?YTw;l8T) z%2-A%$bGa@a6BwtoOn=Zm_2Fgkq%;V9rxJ@?e|z%_{miQI=&=9NjPel#C25J9U`b! zn~MDH>ot}M!l*_TKiouf9L1X?kZQzJE;-8-5mm>q@L}Q|X8LUdM~zpGW0Gl~R?Inj zL|PpqF0D{0RiCJ8|| z*SGQ3WXuquwaUUp-Lv?e`Oe8)`-a34z7^$}_MM!m_Kk_fXQUhw*HAOUKjlpsiwx=u zVchF+!oGr`q7vcLW}Y4=?JKZU2IpYB>>elYD-6=v;%D090Dc9R2?gzJRp&S@V2{K2 z6(;|ga>!C5f=Zw4aW=og6e=WWrHvl<_f>fOhPsVJ_T}^@iPV}ZUh0EOr_;F6n<7|i ziOV~UhC{KzQIakZTT6T^?Y=gvdT689BXVmAkHF0iMCv|4TT57+QX@PddTR;Gs1^q` zddB-I_;8U`r<7|*6xW!vx5PXf+qu7)Sgj?Nw=+kUE~vmVs4Xtw{V1aQP4}S+PVeG2 zaVpOkT7+>elB(w?XS~5_nVm1?d8aZ@`?^?t4yqn<@?qhX)gb&-i?E4^Kl5nqH z0EfQ@SSvV|Kg1VxdBi=VJBJ*B6TShL0wz9hPw|Ga83{}H>4f_2R{Fczd4RdZFTuzj zh;Mg-n}lSIWx)8u@!puUMP%0af}`VcTd~nxl^rP>2fB!!TCkW|N5&v@Sk61+^Wd9= zb5-ukCG&88`!cuB$`wjDIx~ctQ2e+`KD#8|s!k51a^o&1Beei{ca^+c7%|*}E~1VU z%7Uj?$>I;{48@m)?X{U1W+?k+Ind(1Rx)D{@Sh@sx0tLfUtGXyd}GAn5mvIL`pLU1 z2MGx7T2!ReH+ft1FMW@m`)0|qINcxU(5np+-k|R;FWKY>_1H{>lLnl#(-E8H|~4ad}SWywa);Y0z(9GmU+!^5kbFttSqWZbjk4Ye5- z?{8w*7To$moZVWGQ_?>B4qU1TWd!iHv*tAR{t7I!ymarOA3CQo`d3&`JGYjfz%-`+3KO4jUjsyF zYfWSOuP~I2KaK6b!r;|;U0#@wNn+AgUuK!0=G(<%fn~F`_Vg6NX+0Z*8FPTf&B4lW zXd;_EjoWk@M1_sax^@?ngW4u!r{|VcP+4Swju}KYdycFz(8L~X5d^j(J6Jy@kn~aF z{v{i5bNE5v1G)J$Heeb=c@^>rv%C7-hi=3cab7*qZ&kqACeW)Vvh_AQlIaU_hfuFE z)8$@Y##22auIOf>zIv3CZNn49o-{qjF}BTA@$HE@3pp(Q9ji?w_ZGTRnDdLDm zNR%@l!BMa-!9*jb3q`2tJ9ztnw%$rflIsA zVE_^aF765p+?(JxdSr!#?>;bhaRqo5LmrMXxVUOKDnfkHzz-_X1ht9(8Q)11UFkWD zD-1UeEdd!6h>#-=Ri~7Q-sz?gM&Sy?_Mjpy0(8cFMw~s}Hk5r+#9<~*2&&F-iEn|x zF0y&`h=hzdEW)?2NKmWISSC+Rh{m>G>*EX|w+>P8zpi2@Y!xFASc5p7u@sh0)F6;1 zkvb~~$9~yl0BaGcvx4nszmf~F{=N!n{Lp}Nw^bFNMg_6A#LD@P$Nx|9?;gJFhj zxF=`8tlr*4>|UDtDT(d}SjZCpGs+2p|7ql>1}bI|q{}QQi!?n--o)4Y8Dnr=H4^ET z5CzX118?_*5f0AO297>4m{*3nO+qYO#;<1wgcWeeI$T?To;BnZ&q^%Tr)AUH9Gbz~ z#MbohG_pd=oPEL~dZ`}FQXw7UY*v^~SeTX>e2ZC$hV<7Lk6{VA*pNUt^mg_-BQU+OW1&~)l~-EYY8gCYw5}hM?S)WR%G;- z7r2m2SDAQu5gxRTW3`fN2sM)yu|n${@s0U3UpEHd%0UYK$W2Ix5TX?!{(Q%Dk~Bd? z(F#*V%p{>j*W*ZXB-Ci+!ce?|iebf&V51S~6MVqPJz|YUvfOTLHqc?CPqfhp2!9R^ zh&&oWn`crPrinfpL0(nPhsylLer>P|4I4uuk*>pJ!-O`#43SCK;R>UH@UeOFj>P8? z6hUuJAEM~Kv&2z?*YYue17+{3a|A=;5Ow$bI&9m-T90VF@2)e1ZD?cZ5Z^Suj1*D| z;hn^I2QIKmu#HOFx68=^uqoo4#-tHj%RCo`Xu9uDJW|nXF?o-8r!jee%G79 zJ6f_T{5Z+F%o_z@z4*<~cMX@+Za-wJ;*y*N6?1XMzScC^u~qTKF-**L z8Bd3>saal_6=W$UYQ^G`kB_2do*RpYk$Y#Y?bDn7yu!G_3Vl*ub*{}Fo&XGyM^JK&UXf14?n5!SQ1v1*42 zrQQ2og2=AVD4yJ_aTjOU?-5!yCu^5MlMuIcRzE&lmMsF_W-Q(+mpa^_P4wH0K}Pl9{mBLh+e9X(LoD2k zZN$Nc!B$aT%EC_&88>6|u{y-)$gzCZG3wvWrq3kda^(OgM>zTS+tECt!Z~wifIj(u4+wPV)dUrq0F%I0a8$p?YGUR_FupZQ5 zq)qed(VP_f8}~@}xBNPZ-*|NqI8q?!o!C zhq%+cyqhlNu`1U}6{H+Ud3N*8Mh$N?O<7N&1dr763eV|dc&5QmDmeG3NLz;Ek-W!) zd4os8`C#`5_b8PqoZrf1KA8L-1NtEFMve2^8RyCHJcj?O(!P`NJX-U8OpWI!8P7?y z#)Gs*{(@SrO8a@nX{AQ}(f`#;_sfho0{E@9<8-^lJ+_2Yr=WiI-G=0rTrwObGz~vo?lAyI;pBYJJ^fM8%ehk z(h(?2(qR?H>^QxVq&q@Vyt^f-ED1M~?u3}mUQvqb&rK3+YaC9K`GLGMjx-V#&h585{!YQv&Z!W66t!0ixe|KookC-S2L;JO$-Sl|thyJLgkA2n zC4s1F=av-Wh`qI>5J2p0LhQbj2?$nAwv9G-Gl1&Vj_5i{Q=AzI`ljrsHq z5pT~H?CG145JF(F=d)K}im|0Gi)0Q^7zeWxtwu!hf) zr5%1sZDG)tVG$}j8Y6efx@p92GIEnKvpj0!b0W18>+#tUsB|>VRby@+8(BgOy7vfW zdTh|ZHwe_rL&+Q|4N`r{5Lx-YQ8sPkBbj`@Bp1GJ9AMD3D zH46;(lboJKf7)NVRf-jKyU@!do?QqD_f<*jBWL|Q{%*SeioaX#7x8!7{WAXUxNGC@ z3HPh`d(!KfnHtRhJjj_;H-)jIyOP7~GR zyGov0#F69py`|mjJ<$8O`C;`|r^6LUA$HkOr+d|jP?53R+VbfNz74D-9w4pnRhFAc z!Bm9q1g+|03YYk!2y<7HyVDhcSr6vkF+Qr>kNczYnR{`D1HAU!9}8Wh!}u7lPY|hD z$nL)n&)$Cc5As|~P$Ps$tllX5kCH0Tyrjp4Zw0>+sWF+L>~%M)az1?%QcVlC?^|L~ z`1kl=l-^M3%Al3GQR&awfOOf!m$KW;cjHR`Vv+B|+}-;0@Vn`56MwhdZGEq5vh}KU z)LXS%BgFW7ii2M(T{7TrwLnV(a-r+6fxf(I>4w`ZIrY&SYBe`n8cdtw4 z>PFK4l*4P7%Hfp|IJ{n{9A58K4zHTP;q}Ed4cVMh>d+dB-BeXg?zI_`dnHpd@CvMQ zcpV20uYB5Tc(qnpyy~MsP;nLTdXP`y)m=r#XhpF%${XPBqI#kI9<7xMU|fO80*ej# zD|btSk&GFN*gA1ktZ8|gaQup=vasQ?vKS9VSx@U6rz&R}b#~Me$vxbDB#Pm4 zuDkB~9*DrYspOUyCTC*eO3qOs5_8rDa-ZHqbCZRLIyOE8V-`h`XFFZ@gFPcbTl0td z%po3b=lDxw<`(RDs%Iw=mL-8OtP)UJ6Xy)8#8VCoyNIG(W7tIyWhF`(X3-)Znu-*z z)N`jKnwjt1!xF`M5|MyKj10-zNK7#|S4uF31nL2nQjb|vmzwwg!h>%&u1b)0^HC#1 znKsaJ57(9p>D2Q4JAD&v$GfoR8APeN(zs4hE}hmjmU3ODBy&B|>!?rFWO4&_VtQN7 zq?c1aQn}cH87(gKtlylYOme6gQ_)-Xft_c@{pD5nb$!G)V(5OaKP8sfUMj<{iT)k3 zT#LS4h<073zrFN-p&>Td;Gw-_S>wMmo$hZ$lRaOXtUugJBP=d)xrWw;*?k+n*0?Cp zT#yOQrZ8u2wl&;6tjc|rHe63Jxt^JT%=$ZsZTk8)v4qGo&LEc1=}~OcZ7#$%30SSQ zf!L-yxDeZN-_&Ol+j8G9&LEbM>q?N)wp^nS+ajWM5ZfYnwfqjHZM!S<*~GTp<;EGr z60ltfBDU?Ch1j;ctua&Dw!5AF4q~~FS)Wa8$9>BggV@B+RyNmt~Vkg~|`fOsk8NfJ$Sc1(f zLBvkFP9b*E-NTp>JLxv-?;sW@9(^{kQ|>C`3}T5^uLKc0`{dWFDpp z(Ga#6KeBn)%0rke|34j3HaPGUY`4Xkdq2YM3@}*u54wZ5{^u_t3-3$F`CD{eyHo}JY z)!1pzFu15W`801_XTer#f&O1*HMQXkM|vK zps`nJY*{Fj*BIY&x3r6h(y&Z+f6y~2o!JlK4bC#Ic#{HW&vAISC6Ej;MrLuQcQ!X7 zMOG2^^^ROKQiHyg@098mVmq?1INqTLjYnbJ5e+1SH?fvP|SgkWg9K z4DD6H4OG_O>6am^Cif8SA$pwj&-JIO!`G)CN4@m*ypmU+^&@yidM5bGmc@quEI9SQ zZMf?G%*1o(eCY0@zrD#(a&#|ON-#-#J@aObd!|0)OgW3!r>@^L2H+=DQXsQSi?5_J zB`^?!g~jy>5fqo!I=W7?ulLRh_%rril$$Ga6HW3{N@8dT*%AL@yL-K+w#&JQ5SGe& zk+LVim)O3_M7F}+sUk(GR=iuOoEQRtP+!XuiR{Em3RQM54n<7zwUBR6X5IgiHR9>| zIR5+B-ZXY(xDg2L$Zc72Ec?v=>8y&k(DmYRf0!(1=6YL3?#^1H`Z@a5hu5KFzF`cQ z)B)4_{f*GjFVjgI-S!MCiSVB30zpG}_~KU-e0u6+7B)rcR9>BqhKdA@o7nTulL zm37b7^h&j`agDZB6>LHI_G~S$77H8KXh*i7OnbJVOAA}ue|54#Fy%Omo#9fI%1m=3 z`ctgz_0xoAkXzky?8T7n%Lr)mHZ2m)!ElldVr{z9peJ_ki zzOso-6r0)GQa4c5_tlrtk6Ays*XgtF7wYq$YF^l5aq+PTk|Xy%J7#uG*$v0T~c}XxYtcnrtGszWrL|%*p=dmA5FsC@{pH zEX8wgQVGQljww3&!G^K4s?rprSWqIwK0QXiF}2_Zbe15)v;~RnZUcIS-rMO%@Ib#Y z%99R0rurnMA{M%;vJ|cGf2F7tg}5mXSvSqpP^hVDGtE!v zgbABxx%Y{^{SZo6VHpF0TRU3qmK8SWmvsW!(46^Ez5;(E`^bG-9&8}|jQo`EWaR!s zeiAy0bASuZK9Tsgp&T}E`en$*N2AXn+An;v!Q8&^1N4i{ZIxax%ZB;wl**l>jPlcz z?N^0Qodv@`rQZhbHmSTA&dIz6HU(LKRL9u$b(9*Sl|-$v^`RrvJuQbY+12=C-PI72 zbi(!pFX>*TB&%z=N<-UR@BY0JN~&1{LmV~sVZLCMpXrwoTy>X3FpPu|+~vL)!Jtc; zBZ@?mm761FeexCMiF8}wyRRzM9xR-oxB4#<6Ig;v$5H8P${8Lh?w4Yw!E77BRrlox z2IFf4cezU=XeQMEMq{tvc;k`KX0~0UoBubW$!ry5sdO~GM&BgUGZk|`RkHcNxKl!x zXQuO?t6kaSd(~-*YgBAmo9oF*n{q58QzM_QT;UALE7Fs=-SA<(QYoh4ag*Dt<0A$N zi-%?l4pD~ZM_b%`l!3G|{|e#FTIMkuQGuk2$a^(eApP%bNCn-PZD2ws&bhB>8U9D7 zu<@OzHbC4IHnnou3fSxq?n{35Xc8esMv~|zvJq48nBFD6587DvDf{3xx7Av+KpnOK z(cf#mc^xJF*p>}GBjVT(+1}b)6D|z<{;aZJJVUU^6>ry$gKd%cqSnW7!P(}urmY+a z7v=6e6=G+IZ>GO*O5Odp(^zQGeXRiONN9YUFUI<}*}KqnZ^Y>EEES7Kn!AnuvX9Gu z@;XVHF@mGqBX=uhk)JjX9u<X;1YExZF(kVB)txskZzdWE3o#eEdUb7~3?d`WbGZ zWqzrzV{uZW*X+n!UkCy*lJ+{x-r1h)rH@cjZ-=h6sn)~wnUx?)&w=*4x0Bzui1bx= zP^7QAgCl*HJ0#L~xkLH=e_ffjsS^FEHc*R4>>~{>bBc}y=j&)6Kdt1OrAr+51%2P( z_bD!KqXD7j-5ZsjyzN)4UI_0(_+Ym)JrRs5ENb*bi(3Xm3njA1lKfIXFT1~LRa5H)TZ8gVB=AH~Byb-d=iOZmuMq(vZhHePW(zbVwGt8!x0J1!M{TP=iNj$@lmH|hW= z?vxuDF#l9rLl$&mkM^Gk@t#y~U3xXaxwxuK;OqZZX4;LO7e=L zkxW$_Z?jS-P<@+6XhKzIO)c26$a+Gdhby#WksvkRz+EUQvHO?}>Fe6%=t}C!=+4!| z@~9=Sc}g`=)0*dd+kBj<2j@lC$WtdNMSl7w{fk7H)iKaEzhupA9r`lOTz3I&;N%)O zy*b`=sM%>})4nWk=G?!jIEfF^+aivV=FEFW%0$a~iau*y`px^317*zS@&DF%Tg%7J zq+Xy!)78%JwJs=kDy7$ELr1jPy4;!Ez!>v%?S{HkZTRt7AGkC}bJqv1QnQXAD_U+% zOAlO-mL9kcEj@7iS$g2Ev-H5#W;~1=j;$D*-Dt1yR<>A79^^MdCp`BL{(-Ig)nrYS zeGSBKvgO}vZn%pPv(h4$-B5QdTb&qZGW_JH1vk*e`F|H6TY(}!d8-di9W4Y z=<3^slG_Xvm0sO)*+AR*T^Mx>7&sJnsf_*mvK$xY__!ylqeJ;Rq|= z4{L1%kJyD-_bFdlm`1!g{|`Cm$=tdAr=zoGM=NQ}<`CPXay!6F70+z#`A$*Mr0lt^%;C(YSBk!0ld=7S=FT4A;yi~7 z7r2gLYW}pAVnTVUXVSiB)antruzYzu8bc1{p(YVR({BD0~4y+bU zZo-ppW_N}(HquoNj%f+-n$i-Q|As?49inL`Tvz%Oi8nXmXL=g2jLjZ!o;H$HVNJNJ&MG`yZfXn?v;Np<^Pk(I?5 zW#J(Uw*3XF{Kc2B>Z4qhhzMm=99_PeU9>M5>vJAYK?!!l{IoFCkBkY2n;BD-Sh83q zizpWAsuHe8t0&OdWs3C>Xf1b0jn;OD)@U7fSdBK}4zJNB-4Qj~l)Gh()^$hLXc!)E zRVOywQFUU|-MUU}x!crp8p^+u{a)M6tvTssE7+ z+mcvGuvN`*X03C5?N0J2&BDf!T(xL(ZMlQISbD~%d(Iu5XyrM>;)ov~8)4s%iM-(u zq7^tuwS6vkY-ICM2HbIx5>By_^Iak_ofOo!4aJSj5Rp7F`M9mwy+iNI}39fqT#93ELFM+Dm@yZpE zrINGW(Rn7^6~u6G$DD+_pM>mlcM8O{=@mvSx!27d<4a>@iX}r~9};2-)yLoHbXQ3$ z)-;Q0&b^33cej+*_s0n?w~;@18I&XkA_(wxId^42qLH!wmo$vIZjX}0VU{_!XGvS! zvE=qDNf=h#-X%$t9=A_PBOo&ei%Z&)8W8s@iSa1P{#WYT6#vAYY!kR)yqt8$(ela+ zhd_u#q%GQ`xg}Oyb2nVDXjfb}B0!WhmdzwhilniBu~>1cB15qz2Zvp?{vI!_C{%7; zBh#gV}Z+OJ1ym?C5>_JqI4@i^2e~8J_@J# z!L*uPu9tX`a3n&@y;ulx@ZX&+zf5`=Su?RuxR(kO&Y+{kIrfOe6U)6U4ONxNf?`LJ z8w)ckpU65PMc0iWT8WNOc&fgBw=aVP1Ow5qp40A)`l>n2hx+7JS+OMG?-M*E{#{r_ ztBT3po8t>l#2GBs5us(zlO&1Iac>bqdbRZfozBb=&z&808)rxE?OIN2a0w_sA4%L)f>@B{ZuRkUdd=b=?t)Un#5RDJ*a=_^bqJVk8L8Ii+M4-e1MTL}9zRUSQ zgjOU=t=37o^6l``g->jis1HYk+{&<{F=75gGL-+EJMTcrK95IlQRwq{^nVOKUGQx3 z!uH?G|8O_s$IJO|nTYZ``FGYW5K(^45p)-dL@Va~%ayB0%xJ0bqE!o%d z->tahZ&j9;Px622p+WxMwe*tzXAcWF1+B=J{Li{=$p7>jemBxyrMSh(*Yw0@S4RH1 zivQSppllW$U-7*DAl#eb!0>MiT)rRA-23YB?~KLg_|`1FEuixEe=aGO>H3EMd~Evt z&|D4wO2EawHSsb=Uor|902#X}*ESD85(3ACKUzn|f=d=E=*jT;w+d?73R8gPTdyx!w5>$X6%zr23>bjjy#3-aGr zlP~Xg?R>yLIu>6>u$BLVHGCP$c@g*V_p|qxG99xR(tn|rUh;o##HBZt{e!A;S4aGN zHT)S+c5V;yzhA@8;{EqKUhw_-Z@1!)%KGoIP%a-C9_m+a#TcwxyJh*FMEe{|C0uvi z-u!oRE&r{2DHpE<{6#hVV(|8^fWNASAI0E!hqZveriQizGhVqzy4)|MZ_{9u)XFf6DZ>!;#QNG9hOTgbz z!!M?Mr#&s;7u4|mNPqJ40{-zDzD)VgctOBFQNteq?#>SQg*E(vjNQ9m9`H}q@M|Rf zxdH!=8h$F}JM2vX|7;EaB$RV#7VAHstKm|kXVNT&f4+tvN&h>UW-zcQ!%{ z|56Q~p?&Vnuw?j`Yxvtqf6jXXerXMV7?jf(7A*Z&Yxs%#Ik(pZ0sndpzYF!Vn`*c8 z->BgyQof6+B*VX1!!IQL*3Sg|yES|Ud^6Q*>94HeABJ)k!;;}YuHh$>|Kq4u!>_5~ zr%L-%Er$QBhMz(HXI>`wLHtK!@hAF`{@|YrshBj_abG;^VpMY&x^SK#je;x2c zYq*T6Xa6qXr`Pansh=nPKHxvC;ZKqDRHBvdni_s8`1GcLZ@LAKMf=F8c1gmu0rUDh zg0G1i7`^;GYWN}4pW8R!d)9ENpZ5L%|La=$4&(hk8u7jA>B;}v148>UDra1DP7^?N3YuJw;YYWS9yQNCj~W%r(4NRH(g fBX^^n`;nX{-HoQq{3G{8o&WKihlz#1e60L`2(kc- literal 0 HcmV?d00001 diff --git a/src/fred2/fred.cpp b/src/fred2/fred.cpp new file mode 100644 index 0000000..8f9d29f --- /dev/null +++ b/src/fred2/fred.cpp @@ -0,0 +1,822 @@ +/* + * $Logfile: /Freespace2/code/Fred2/FRED.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * FRED.cpp : Defines the class behaviors for the application. + * Global editor dialog box classes are instantiated here, initializes the + * application (MFC level at least), processes the INI file. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 6 5/19/99 4:07p Dave + * Moved versioning code into a nice isolated common place. Fixed up + * updating code on the pxo screen. Fixed several stub problems. + * + * 5 3/26/99 4:49p Dave + * Made cruisers able to dock with stuff. Made docking points and paths + * visible in fred. + * + * 4 3/20/99 5:09p Dave + * Fixed release build fred warnings and unhandled exception. + * + * 3 10/29/98 10:41a Dave + * Change the way cfile initializes exe directory. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 71 7/02/98 12:17p Hoffoss + * Load and save options 'anti-aliased gridlines' and 'double fine + * gridlines'. + * + * 70 5/22/98 11:02a Allender + * Added error conditions on compile and new MAX_SHIPS limits for when + * FRED is defined when building + * + * 69 5/22/98 1:06a Hoffoss + * Made Fred not use OLE. + * + * 68 4/17/98 1:41p Allender + * took out function calls in NDEBUG mode + * + * 67 3/25/98 4:14p Hoffoss + * Split ship editor up into ship editor and a misc dialog, which tracks + * flags and such. + * + * 66 3/23/98 4:04p Hoffoss + * Fixed dialog window initialization so it looks better at startup (they + * don't flash on for a second). + * + * 65 3/19/98 11:41a Hoffoss + * Fixed problems with rendering and reading of flying controls in Fred. + * + * 64 3/19/98 11:11a Allender + * made Fred actually be able to move camera again -- Hoffos is going to + * fix the code right. + * + * 63 3/18/98 3:10p Allender + * fixed update problems in OnIdle routine + * + * 62 2/06/98 4:39p Hoffoss + * Added better checking for whether Fred is in the foreground or + * background. + * + * 61 8/19/97 5:46p Hoffoss + * Changed font used in Fred, and added display to show current eye + * position. + * + * 60 8/17/97 10:22p Hoffoss + * Fixed several bugs in Fred with Undo feature. In the process, recoded + * a lot of CFile.cpp. + * + * 59 8/14/97 2:32p Hoffoss + * fixed bug where controlling an object doesn't cause screen updates, and + * added a number of cool features to viewpoint/control object code. + * + * 58 8/13/97 1:38p Hoffoss + * Added ability to update multiple times, which in needed in one case to + * brute force redraw so deleted ships actually do get removed from the + * display. + * + * 57 8/12/97 6:32p Hoffoss + * Added code to allow hiding of arrival and departure cues in editors. + * + * 56 8/10/97 4:52p Hoffoss + * Made Fred startup with main window having focus. + * + * 55 8/06/97 7:55p Hoffoss + * Fixed bug with objects not seeming to be where they are drawn (due to + * new briefing clip render stuff). This caused rendering problems, + * though. So I fixed those next. + * + * 54 8/05/97 1:29p Hoffoss + * show sexp help saved between sessions. + * + * 53 8/01/97 3:10p Hoffoss + * Made Sexp help hidable. + * + * 52 8/01/97 12:49p Hoffoss + * Changed recently used files total to 9 instead of 4. + * + * 51 6/18/97 11:46a Hoffoss + * Fixed initial order object reference updating and added briefing dialog + * window tracking data. + * + * 50 6/03/97 10:18a Allender + * make showCmd default to maximized since 0 will make the window hidden + * by default + * + * 49 6/02/97 5:18p Hoffoss + * Fixed bug in error checker and improved startup code. + * + * 48 6/02/97 11:52a Hoffoss + * Custom cursors displayed when over objects in different modes. + * + * 47 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 46 5/09/97 9:45a Hoffoss + * Added OLE initialization. (needed for drag and drop functionality) + * + * 45 5/06/97 10:34a Hoffoss + * Fixed bug where dialog boxes that change size (in the code) still + * retain their old size from the INI stored values. They are resized now + * automatically. + * + * 44 5/05/97 9:41a Hoffoss + * Campaign editor begun. + * + * 43 5/02/97 8:39a Allender + * turn of heap allocation debuging + * + * 42 4/29/97 1:58p Hoffoss + * Added some debugging to Fred to try and track down sexp corruption + * causes. + * + * 41 4/28/97 3:43p Hoffoss + * Show_waypoints defaults to on for Fred, and is saved as a preference + * now. + * + * 40 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 39 4/03/97 11:35a Hoffoss + * Fixed bugs: viewpoint didn't reset, initial orders not updated when + * referenced ship is renamed or deleted. + * + * 38 4/01/97 11:43p Mike + * Resolve link errors due to addition of SystemVars.cpp. + * + * 37 3/26/97 12:44p Hoffoss + * Took out the debugging 3d compass on/off toggling I put in to test + * rerendering with. + * + * 36 3/20/97 3:55p Hoffoss + * Major changes to how dialog boxes initialize (load) and update (save) + * their internal data. This should simplify things and create less + * problems. + * + * 35 3/13/97 12:08p Hoffoss + * Waypoint path editor finished (apparently I didn't get around to + * completing it before). + * + * 34 3/12/97 12:39p Hoffoss + * Fixed bugs in wing object management functions, several small additions + * and rearrangements. + * + * 33 3/06/97 3:35p Hoffoss + * Added Show_outline stuff, moved show options to the view menu, fixed a + * bug in message dialog editor. + * + * 32 2/28/97 11:31a Hoffoss + * Implemented modeless dialog saving and restoring, and changed some + * variables names. + * + * 31 2/27/97 5:54p Hoffoss + * Implemented support for saving and restoring window positions. + * + * 30 2/25/97 6:10p Hoffoss + * Fixed bug with modeless dialog box errors on update. + * + * 29 2/24/97 12:50p Hoffoss + * First attempt at non-continuous redrawing. + * + * 28 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 27 2/12/97 12:25p Hoffoss + * Expanded on global error checker, added initial orders conflict + * checking and warning, added waypoint editor dialog and code. + * + * 26 1/30/97 2:24p Hoffoss + * Added remaining mission file structures and implemented load/save of + * them. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" + +#include "mainfrm.h" +#include "freddoc.h" +#include "fredview.h" +#include "fredrender.h" +#include "management.h" + +#include "2d.h" +#include "key.h" +#include "object.h" +#include "editor.h" +#include "campaigntreewnd.h" +#include "campaigntreeview.h" +#include "campaigneditordlg.h" + +#ifdef NDEBUG +#ifndef FRED +#error macro FRED is not defined when trying to build release Fred. Please define FRED macro in build settings in all Fred projects +#endif +#endif + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +int Fred_running = 1; +int User_interface = HOFFOSS_INTERFACE; +int FrameCount = 0; +int Fred_active = 1; +int Update_window = 1; +HCURSOR h_cursor_move, h_cursor_rotate; + +CWnd *Prev_window; +CShipEditorDlg Ship_editor_dialog; +wing_editor Wing_editor_dialog; +waypoint_path_dlg Waypoint_editor_dialog; +bg_bitmap_dlg *Bg_bitmap_dialog = NULL; +briefing_editor_dlg *Briefing_dialog = NULL; + +window_data Main_wnd_data; +window_data Ship_wnd_data; +window_data Wing_wnd_data; +window_data Object_wnd_data; +window_data Mission_goals_wnd_data; +window_data Messages_wnd_data; +window_data Player_wnd_data; +window_data Events_wnd_data; +window_data Bg_wnd_data; +window_data Briefing_wnd_data; +window_data Reinforcement_wnd_data; +window_data Waypoint_wnd_data; +window_data Starfield_wnd_data; +window_data Asteroid_wnd_data; +window_data Mission_notes_wnd_data; + +///////////////////////////////////////////////////////////////////////////// +// CFREDApp + +BEGIN_MESSAGE_MAP(CFREDApp, CWinApp) + //{{AFX_MSG_MAP(CFREDApp) + ON_COMMAND(ID_APP_ABOUT, OnAppAbout) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG_MAP + // Standard file based document commands + ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) + ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) + // Standard print setup command + ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CFREDApp construction + +CFREDApp::CFREDApp() +{ + app_init = 0; + + #ifndef NDEBUG + outwnd_init(); + #endif + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only CFREDApp object + +CFREDApp theApp; + +///////////////////////////////////////////////////////////////////////////// +// CFREDApp initialization +char *c; +char *tok = "whee"; +BOOL CFREDApp::InitInstance() +{ + // disable the debug memory stuff + _CrtSetDbgFlag(~(_CRTDBG_ALLOC_MEM_DF) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)); + + // Standard initialization + // If you are not using these features and wish to reduce the size + // of your final executable, you should remove from the following + // the specific initialization routines you do not need. + +#ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL +#else + Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif + + LoadStdProfileSettings(9); // Load standard INI file options (including MRU) + User_interface = GetProfileInt("Preferences", "User interface", User_interface); + Show_stars = GetProfileInt("Preferences", "Show stars", Show_stars); + Show_grid_positions = GetProfileInt("Preferences", "Show grid positions", Show_grid_positions); + Show_coordinates = GetProfileInt("Preferences", "Show coordinates", Show_coordinates); + Show_compass = GetProfileInt("Preferences", "Show compass", Show_compass); + Show_ship_models = GetProfileInt("Preferences", "Show ship models", Show_ship_models); + Show_ship_info = GetProfileInt("Preferences", "Show ship info", Show_ship_info); + Show_outlines = GetProfileInt("Preferences", "Show outlines", Show_outlines); + Show_waypoints = GetProfileInt("Preferences", "Show waypoints", Show_waypoints); + Show_sexp_help = GetProfileInt("Preferences", "Show sexp help", Show_sexp_help); + physics_speed = GetProfileInt("Preferences", "Physics speed", physics_speed); + physics_rot = GetProfileInt("Preferences", "Physics rotation", physics_rot); + Hide_ship_cues = GetProfileInt("Preferences", "Hide ship cues", Hide_ship_cues); + Hide_wing_cues = GetProfileInt("Preferences", "Hide wing cues", Hide_wing_cues); + Autosave_disabled = GetProfileInt("Preferences", "Autosave disabled", Autosave_disabled); + double_fine_gridlines = GetProfileInt("Preferences", "Double fine gridlines", double_fine_gridlines); + Aa_gridlines = GetProfileInt("Preferences", "Anti aliased gridlines", Aa_gridlines); + Show_dock_points = GetProfileInt("Preferences", "Show dock points", Show_dock_points); + Show_paths_fred = GetProfileInt("Preferences", "Show paths", Show_paths_fred); + read_window("Main window", &Main_wnd_data); + read_window("Ship window", &Ship_wnd_data); + read_window("Wing window", &Wing_wnd_data); + read_window("Waypoint window", &Waypoint_wnd_data); + read_window("Object window", &Object_wnd_data); + read_window("Mission goals window", &Mission_goals_wnd_data); + read_window("Messages window", &Messages_wnd_data); + read_window("Player window", &Player_wnd_data); + read_window("Events window", &Events_wnd_data); + read_window("Bg window", &Bg_wnd_data); + read_window("Briefing window", &Briefing_wnd_data); + read_window("Reinforcement window", &Reinforcement_wnd_data); + read_window("Starfield window", &Starfield_wnd_data); + read_window("Asteroid window", &Asteroid_wnd_data); + read_window("Mission notes window", &Mission_notes_wnd_data); + write_ini_file(1); + + // Register the application's document templates. Document templates + // serve as the connection between documents, frame windows and views. + + CSingleDocTemplate* pDocTemplate; + pDocTemplate = new CSingleDocTemplate( + IDR_MAINFRAME, + RUNTIME_CLASS(CFREDDoc), + RUNTIME_CLASS(CMainFrame), // main SDI frame window + RUNTIME_CLASS(CFREDView)); + AddDocTemplate(pDocTemplate); + + // Enable DDE Execute open + EnableShellOpen(); + RegisterShellFileTypes(TRUE); + + // setup the fred exe directory so CFILE can init properly + /* + c = GetCommandLine(); + Assert(c != NULL); + if(c == NULL){ + return FALSE; + } + tok = strtok(c, " \n"); + Assert(tok != NULL); + if(tok == NULL){ + return FALSE; + } + // Fred_exe_dir = strdup(c); + strcpy(Fred_exe_dir, tok); + */ + strcpy(Fred_exe_dir, __argv[0]); + + // Parse command line for standard shell commands, DDE, file open + CCommandLineInfo cmdInfo; + ParseCommandLine(cmdInfo); + + m_nCmdShow = Main_wnd_data.p.showCmd; + OnFileNew(); + + // Enable drag/drop open + m_pMainWnd->DragAcceptFiles(); + + h_cursor_move = LoadCursor(IDC_CURSOR1); + h_cursor_rotate = LoadCursor(IDC_CURSOR2); + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CAboutDlg dialog used for App About + +class CAboutDlg : public CDialog +{ +public: + CAboutDlg(); + +// Dialog Data + //{{AFX_DATA(CAboutDlg) + enum { IDD = IDD_ABOUTBOX }; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAboutDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + //{{AFX_MSG(CAboutDlg) + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) +{ + //{{AFX_DATA_INIT(CAboutDlg) + //}}AFX_DATA_INIT +} + +void CAboutDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAboutDlg) + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) + //{{AFX_MSG_MAP(CAboutDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +// App command to run the dialog +void CFREDApp::OnAppAbout() +{ + CAboutDlg aboutDlg; + aboutDlg.DoModal(); +} + +///////////////////////////////////////////////////////////////////////////// +// CFREDApp commands + +char *edit_mode_text[] = { + "Ships", + "Waypoints", +}; +/* "Grid", + "Wing", + "Object Relative" +}; +*/ + +char *control_mode_text[] = { + "Camera", + "Object" +}; + +void show_control_mode(void) +{ + CString str; + + CMainFrame* pFrame = (CMainFrame*) AfxGetApp()->m_pMainWnd; + CStatusBar* pStatus = &pFrame->m_wndStatusBar; + //CStatusBarCtrl pStatusBarCtrl; + + if (pStatus) { +// pStatus->GetStatusBarCtrl().SetParts(NUM_STATUS_PARTS, parts); + + if (Marked) + str.Format("Marked: %d", Marked); + else + str = _T(""); + pStatus->SetPaneText(1, str); + + if (viewpoint) + str.Format("Viewpoint: %s", object_name(view_obj)); + else + str.Format("Viewpoint: Camera"); + + pStatus->SetPaneText(2, str); + + if (FREDDoc_ptr->IsModified()) + pStatus->SetPaneText(3, "MODIFIED"); + else + pStatus->SetPaneText(3, ""); + + str.Format("Units = %.1f Meters", The_grid->square_size); + pStatus->SetPaneText(4, str); + +// pStatus->SetPaneText(4, "abcdefg"); +// pStatus->SetPaneText(4, "1234567890!"); + } + +} + +#define MAX_PENDING_MESSAGES 16 + +typedef struct { + int frame_to_process, hwnd, id, wparam, lparam; +} pending_message; + +pending_message Pending_messages[MAX_PENDING_MESSAGES]; + +// Process messages that needed to wait until a frame had gone by. +void process_pending_messages(void) +{ + int i; + + for (i=0; ihwnd, pmp->id, pmp->wparam, pmp->lparam); + Pending_messages[i].frame_to_process = -1; + } +} + +// Add a message to be processed to a buffer. +// Wait skip_count frames before processing. +void add_pending_message(HWND hwnd, int id, int wparam, int lparam, int skip_count) +{ + int i; + + for (i=0; im_hDC, 0, 0, gr_screen.max_w, gr_screen.max_h); + gr_screen.clip_width = w; + gr_screen.clip_height = h; +} + +BOOL CFREDApp::OnIdle(LONG lCount) +{ + int adjust = 0; + CWnd *top, *wnd; + + if (!Show_sexp_help) + adjust = -SEXP_HELP_BOX_SIZE; + + if (!app_init) { + app_init = 1; + theApp.init_window(&Ship_wnd_data, &Ship_editor_dialog, adjust, 1); + theApp.init_window(&Wing_wnd_data, &Wing_editor_dialog, adjust, 1); + theApp.init_window(&Waypoint_wnd_data, &Waypoint_editor_dialog, 0, 1); + init_window(&Main_wnd_data, Fred_main_wnd); + Fred_main_wnd->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + + Ship_editor_dialog.calc_cue_height(); + Wing_editor_dialog.calc_cue_height(); + } + + CWinApp::OnIdle(lCount); + internal_integrity_check(); + if (Update_ship) { + Ship_editor_dialog.initialize_data(1); + Update_ship = 0; + } + + if (Update_wing) { + Wing_editor_dialog.initialize_data(1); + Update_wing = 0; + } + + Prev_window = CFREDView::GetActiveWindow(); + + // Find the root window of the active window + wnd = top = Prev_window; + while (wnd) { + top = wnd; + wnd = wnd->GetParent(); + } + + // See if the active window is a child of Fred + if (Prev_window) + Fred_active = ( (top == Fred_main_wnd) || (top == Campaign_wnd) ); + else + Fred_active = 0; + + if (!Fred_active) + return FALSE; // if fred isn't active, don't waste any time with it. + + game_do_frame(); // do stuff that needs doing, whether we render or not. + show_control_mode(); + + if (!Update_window) + return FALSE; + + render_frame(); // "do the rendering!" Renders image to offscreen buffer + + CFREDView* pFV = CFREDView::GetView(); + CDC* pDC = pFV->GetDC(); + + // gr_surface_flip(); + + // if you hit the next Assert, find Hoffoss or Allender. If neither here, then comment it out. + Assert( Update_window >= 0 ); + if (Update_window) { + draw_render_window(pDC); // this actually copies the offscreen buffer to the screen + Update_window--; + } + + process_pending_messages(); + pFV->ReleaseDC(pDC); + + FrameCount++; + return TRUE; +} + +void update_map_window() +{ + if (Fred_active) { + Update_window++; // on idle will handle the drawing already. + return; + } + + if (!Fred_main_wnd) + return; + + CFREDView* pFV = CFREDView::GetView(); + if (!pFV) + return; + + render_frame(); // "do the rendering!" + + CDC* pDC = pFV->GetDC(); + Assert(pDC); + + draw_render_window(pDC); + if ( Update_window > 0 ) + Update_window--; + + show_control_mode(); + process_pending_messages(); + pFV->ReleaseDC(pDC); + + FrameCount++; +} + +void CFREDApp::write_ini_file(int degree) +{ + WriteProfileInt("Preferences", "User interface", User_interface); + WriteProfileInt("Preferences", "Show stars", Show_stars); + WriteProfileInt("Preferences", "Show grid positions", Show_grid_positions); + WriteProfileInt("Preferences", "Show coordinates", Show_coordinates); + WriteProfileInt("Preferences", "Show compass", Show_compass); + WriteProfileInt("Preferences", "Show ship models", Show_ship_models); + WriteProfileInt("Preferences", "Show ship info", Show_ship_info); + WriteProfileInt("Preferences", "Show outlines", Show_outlines); + WriteProfileInt("Preferences", "Physics speed", physics_speed); + WriteProfileInt("Preferences", "Physics rotation", physics_rot); + WriteProfileInt("Preferences", "Show waypoints", Show_waypoints); + WriteProfileInt("Preferences", "Show sexp help", Show_sexp_help); + WriteProfileInt("Preferences", "Hide ship cues", Hide_ship_cues); + WriteProfileInt("Preferences", "Hide wing cues", Hide_wing_cues); + WriteProfileInt("Preferences", "Autosave disabled", Autosave_disabled); + WriteProfileInt("Preferences", "Double fine gridlines", double_fine_gridlines); + WriteProfileInt("Preferences", "Anti aliased gridlines", Aa_gridlines); + WriteProfileInt("Preferences", "Show dock points", Show_dock_points); + WriteProfileInt("Preferences", "Show paths", Show_paths_fred); + + if (!degree) { + record_window_data(&Waypoint_wnd_data, &Waypoint_editor_dialog); + record_window_data(&Wing_wnd_data, &Wing_editor_dialog); + record_window_data(&Ship_wnd_data, &Ship_editor_dialog); + record_window_data(&Main_wnd_data, Fred_main_wnd); + + write_window("Main window", &Main_wnd_data); + write_window("Ship window", &Ship_wnd_data); + write_window("Wing window", &Wing_wnd_data); + write_window("Waypoint window", &Waypoint_wnd_data); + write_window("Object window", &Object_wnd_data); + write_window("Mission goals window", &Mission_goals_wnd_data); + write_window("Messages window", &Messages_wnd_data); + write_window("Player window", &Player_wnd_data); + write_window("Events window", &Events_wnd_data); + write_window("Bg window", &Bg_wnd_data); + write_window("Briefing window", &Briefing_wnd_data); + write_window("Reinforcement window", &Reinforcement_wnd_data); + write_window("Starfield window", &Starfield_wnd_data); + write_window("Asteroid window", &Asteroid_wnd_data); + write_window("Mission notes window", &Mission_notes_wnd_data); + } +} + +void CFREDApp::write_window(char *name, window_data *wndd) +{ + WriteProfileInt(name, "valid", wndd->valid); + WriteProfileInt(name, "length", wndd->p.length); + WriteProfileInt(name, "flags", wndd->p.flags); + WriteProfileInt(name, "showCmd", wndd->p.showCmd); + WriteProfileInt(name, "ptMinPosition.x", wndd->p.ptMinPosition.x); + WriteProfileInt(name, "ptMinPosition.y", wndd->p.ptMinPosition.y); + WriteProfileInt(name, "ptMaxPosition.x", wndd->p.ptMaxPosition.x); + WriteProfileInt(name, "ptMaxPosition.y", wndd->p.ptMaxPosition.y); + WriteProfileInt(name, "rcNormalPosition.left", wndd->p.rcNormalPosition.left); + WriteProfileInt(name, "rcNormalPosition.top", wndd->p.rcNormalPosition.top); + WriteProfileInt(name, "rcNormalPosition.right", wndd->p.rcNormalPosition.right); + WriteProfileInt(name, "rcNormalPosition.bottom", wndd->p.rcNormalPosition.bottom); + WriteProfileInt(name, "Visible", wndd->visible); +} + +void CFREDApp::read_window(char *name, window_data *wndd) +{ + wndd->processed = 0; + wndd->valid = GetProfileInt(name, "valid", FALSE); + wndd->p.length = GetProfileInt(name, "length", 0); + wndd->p.flags = GetProfileInt(name, "flags", 0); + wndd->p.showCmd = GetProfileInt(name, "showCmd", SW_SHOWMAXIMIZED); + wndd->p.ptMinPosition.x = GetProfileInt(name, "ptMinPosition.x", 0); + wndd->p.ptMinPosition.y = GetProfileInt(name, "ptMinPosition.y", 0); + wndd->p.ptMaxPosition.x = GetProfileInt(name, "ptMaxPosition.x", 0); + wndd->p.ptMaxPosition.y = GetProfileInt(name, "ptMaxPosition.y", 0); + wndd->p.rcNormalPosition.left = GetProfileInt(name, "rcNormalPosition.left", 0); + wndd->p.rcNormalPosition.top = GetProfileInt(name, "rcNormalPosition.top", 0); + wndd->p.rcNormalPosition.right = GetProfileInt(name, "rcNormalPosition.right", 0); + wndd->p.rcNormalPosition.bottom = GetProfileInt(name, "rcNormalPosition.bottom", 0); + wndd->visible = GetProfileInt(name, "Visible", 1); +} + +int CFREDApp::init_window(window_data *wndd, CWnd *wnd, int adjust, int pre) +{ + int width, height; + WINDOWPLACEMENT p; + + if (pre && !wndd->visible) + return -1; + + if (wndd->processed) + return -2; + + Assert(wnd->GetSafeHwnd()); + wnd->GetWindowPlacement(&p); + width = p.rcNormalPosition.right - p.rcNormalPosition.left; + height = p.rcNormalPosition.bottom - p.rcNormalPosition.top + adjust; + wndd->p.rcNormalPosition.right = wndd->p.rcNormalPosition.left + width; + wndd->p.rcNormalPosition.bottom = wndd->p.rcNormalPosition.top + height; + + if (wndd->valid) { + wnd->SetWindowPlacement(&wndd->p); +// if (!wndd->visible) +// wnd->ShowWindow(SW_SHOW); +// else +// wnd->ShowWindow(SW_HIDE); + } + + record_window_data(wndd, wnd); + wndd->processed = 1; + return 0; +} + +void CFREDApp::record_window_data(window_data *wndd, CWnd *wnd) +{ + wnd->GetWindowPlacement(&wndd->p); + wndd->visible = wnd->IsWindowVisible(); + wndd->valid = TRUE; +} diff --git a/src/fred2/fred.rc b/src/fred2/fred.rc new file mode 100644 index 0000000..50ca48d --- /dev/null +++ b/src/fred2/fred.rc @@ -0,0 +1,2934 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +// Generated Help ID header file +#define APSTUDIO_HIDDEN_SYMBOLS +#include "resource.hm" +#undef APSTUDIO_HIDDEN_SYMBOLS + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif\r\n" + "#include ""res\\FRED.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#include ""afxprint.rc"" // printing/print preview resources\r\n" + "#endif\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "res\\FRED.ico" +IDR_FREDTYPE ICON DISCARDABLE "res\\FREDDoc.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp" +IDB_OPERATOR BITMAP DISCARDABLE "res\\bitmap1.bmp" +IDB_DATA BITMAP DISCARDABLE "res\\data.bmp" +IDB_ROOT BITMAP DISCARDABLE "res\\root.bmp" +IDB_CHAINED BITMAP DISCARDABLE "res\\chained.bmp" +IDB_PLAY BITMAP DISCARDABLE "res\\bmp00001.bmp" +IDB_ROOT_DIRECTIVE BITMAP DISCARDABLE "res\\root_directive.bmp" +IDB_CHAINED_DIRECTIVE BITMAP DISCARDABLE "res\\chained_directive.bmp" +IDB_GREEN_DOT BITMAP DISCARDABLE "res\\green_do.bmp" +IDB_BLACK_DOT BITMAP DISCARDABLE "res\\black_do.bmp" +IDB_VARIABLE BITMAP DISCARDABLE "res\\variable.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_MAINFRAME TOOLBAR DISCARDABLE 23, 23 +BEGIN + BUTTON ID_SELECT + BUTTON ID_SELECT_AND_MOVE + BUTTON ID_SELECT_AND_ROTATE + SEPARATOR + BUTTON ID_ROTATE_LOCALLY + SEPARATOR + BUTTON ID_CONSTRAIN_X + BUTTON ID_CONSTRAIN_Y + BUTTON ID_CONSTRAIN_Z + BUTTON ID_CONSTRAIN_XZ + BUTTON ID_CONSTRAIN_YZ + BUTTON ID_CONSTRAIN_XY + SEPARATOR + BUTTON ID_SELECT_LIST + BUTTON ID_SELECTION_LOCK + SEPARATOR + BUTTON ID_FORM_WING + BUTTON ID_DISBAND_WING + SEPARATOR + BUTTON ID_ZOOM_SELECTED + BUTTON ID_ZOOM_EXTENTS + SEPARATOR + BUTTON ID_SHOW_DISTANCES + BUTTON ID_LOOKAT_OBJ + SEPARATOR + BUTTON ID_NEW_SHIP_TYPE +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINMENU MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVE_AS + MENUITEM "Re&vert", ID_REVERT + MENUITEM SEPARATOR + MENUITEM "&Run FreeSpace\tAlt+R", ID_RUN_FREESPACE + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "Undo\tCtrl+Z", ID_EDIT_UNDO + MENUITEM "Delete\tDel", ID_EDIT_DELETE + MENUITEM "Delete Wing\tCtrl+Del", ID_EDIT_DELETE_WING + MENUITEM SEPARATOR + MENUITEM "Disable Undo", ID_DISABLE_UNDO + END + POPUP "&View" + BEGIN + MENUITEM "&Toolbar", ID_VIEW_TOOLBAR, CHECKED + MENUITEM "&Status Bar", ID_VIEW_STATUS_BAR, CHECKED + MENUITEM SEPARATOR + POPUP "Display Filter" + BEGIN + MENUITEM "Show Ships", ID_SHOW_SHIPS + MENUITEM "Show Player Starts", ID_SHOW_STARTS + MENUITEM "Show Waypoints", ID_SHOW_WAYPOINTS + MENUITEM SEPARATOR + MENUITEM "Show Friendly", ID_SHOW_FRIENDLY + MENUITEM "Show Hostile", ID_SHOW_HOSTILE + END + MENUITEM SEPARATOR + MENUITEM "Hide Marked Objects", ID_HIDE_OBJECTS + MENUITEM "Show Hidden Objects", ID_SHOW_HIDDEN_OBJECTS + MENUITEM SEPARATOR + MENUITEM "Show Ship Models\tShift+Alt+M", + ID_EDIT_POPUP_SHOW_SHIP_MODELS + , CHECKED + MENUITEM "Show Outlines\tShift+Alt+O", ID_VIEW_OUTLINES + MENUITEM "Show Ship Info\tShift+Alt+I", ID_EDIT_POPUP_SHOW_SHIP_ICONS + , CHECKED, HELP + MENUITEM "Show Coordinates\tShift+Alt+C", ID_SHOW_COORDINATES + MENUITEM "Show Grid Positions\tShift+Alt+P", ID_SHOW_GRID_POSITIONS + MENUITEM "Show Distances\tD", ID_SHOW_DISTANCES + MENUITEM "Show Model Paths", ID_SHOW_PATHS + MENUITEM "Show Model Dock Points", ID_SHOW_DOCK_POINTS + MENUITEM SEPARATOR + MENUITEM "Show &Grid\tShift+Alt+G", ID_VIEW_GRID + MENUITEM "Show Horizon\tShift+Alt+H", ID_SHOW_HORIZON + MENUITEM "Double Fine Gridlines", ID_DOUBLE_FINE_GRIDLINES + MENUITEM "Anti-Aliased Gridlines", ID_AA_GRIDLINES + MENUITEM "Show 3D Compass\tShift+Alt+3", ID_EDIT_POPUP_SHOW_COMPASS + , CHECKED + MENUITEM "Show Background\tShift+Alt+B", ID_SHOW_STARFIELD + MENUITEM SEPARATOR + POPUP "Viewpoint\tShift+V" + BEGIN + MENUITEM "Camera", ID_CHANGE_VIEWPOINT_EXTERNAL + , CHECKED + MENUITEM "Current Ship", ID_CHANGE_VIEWPOINT_FOLLOW + + END + MENUITEM "Save Camera Pos\tCtrl+P", ID_SAVE_CAMERA + MENUITEM "Restore Camera Pos\tCtrl+R", ID_RESTORE_CAMERA + END + POPUP "&Speed" + BEGIN + POPUP "Movement" + BEGIN + MENUITEM "x1\t1", ID_SPEED1, CHECKED + MENUITEM "x2\t2", ID_SPEED2 + MENUITEM "x3\t3", ID_SPEED3 + MENUITEM "x5\t4", ID_SPEED5 + MENUITEM "x8\t5", ID_SPEED8 + MENUITEM "x10\t6", ID_SPEED10 + MENUITEM "x50\t7", ID_SPEED50 + MENUITEM "x100\t8", ID_SPEED100 + END + POPUP "Rotation" + BEGIN + MENUITEM "x1\tShift+1", ID_ROT1, CHECKED + MENUITEM "x5\tShift+2", ID_ROT2 + MENUITEM "x12\tShift+3", ID_ROT3 + MENUITEM "x25\tShift+4", ID_ROT4 + MENUITEM "x50\tShift+5", ID_ROT5 + END + END + POPUP "E&ditors" + BEGIN + MENUITEM "&Ships\tShift+S", ID_EDITORS_SHIPS + MENUITEM "&Wings\tShift+W", ID_EDITORS_WING + MENUITEM "Objects\tShift+O", ID_EDITORS_ORIENT + MENUITEM "Waypoint Paths\tShift+Y", ID_EDITORS_WAYPOINT + MENUITEM "Mission &Objectives\tShift+G", ID_EDITORS_GOALS + MENUITEM "&Events\tShift+E", ID_EDITORS_EVENTS + MENUITEM "Team Loadout\tShift+P", ID_EDITORS_PLAYER + MENUITEM "Background\tShift+I", ID_EDITORS_BG_BITMAPS + MENUITEM "Reinforcements\tShift+R", ID_EDITORS_REINFORCEMENT + MENUITEM "Asteroid Field\tShift+A", ID_ASTEROID_EDITOR + MENUITEM "&Mission Specs\tShift+N", ID_FILE_MISSIONNOTES + MENUITEM "&Briefing\tShift+B", ID_EDITORS_BRIEFING + MENUITEM "&Debriefing\tShift+D", ID_EDITORS_DEBRIEFING + MENUITEM "Shield System", ID_EDITORS_SHIELD_SYS + MENUITEM "Command Briefing", ID_CMD_BRIEF + MENUITEM SEPARATOR + MENUITEM "Campaign\tShift+C", ID_EDITOR_CAMPAIGN + END + POPUP "&Groups" + BEGIN + MENUITEM "Group 1\tCtrl+1", ID_GROUP1 + MENUITEM "Group 2\tCtrl+2", ID_GROUP2 + MENUITEM "Group 3\tCtrl+3", ID_GROUP3 + MENUITEM "Group 4\tCtrl+4", ID_GROUP4 + MENUITEM "Group 5\tCtrl+5", ID_GROUP5 + MENUITEM "Group 6\tCtrl+6", ID_GROUP6 + MENUITEM "Group 7\tCtrl+7", ID_GROUP7 + MENUITEM "Group 8\tCtrl+8", ID_GROUP8 + MENUITEM "Group 9\tCtrl+9", ID_GROUP9 + POPUP "Set Group" + BEGIN + MENUITEM "Group 1", ID_SET_GROUP1 + MENUITEM "Group 2", ID_SET_GROUP2 + MENUITEM "Group 3", ID_SET_GROUP3 + MENUITEM "Group 4", ID_SET_GROUP4 + MENUITEM "Group 5", ID_SET_GROUP5 + MENUITEM "Group 6", ID_SET_GROUP6 + MENUITEM "Group 7", ID_SET_GROUP7 + MENUITEM "Group 8", ID_SET_GROUP8 + MENUITEM "Group 9", ID_SET_GROUP9 + END + END + POPUP "&Misc" + BEGIN + MENUITEM "Level Object\tL", ID_LEVEL_OBJ + MENUITEM "Align Object\tCtrl+L", ID_ALIGN_OBJ + MENUITEM "Mark Wing\tW", ID_MARK_WING + MENUITEM "Control Object\tT", ID_CONTROL_OBJ + MENUITEM "Next Object\tTab", ID_NEXT_OBJ + MENUITEM "Previous Object\tCtrl+Tab", ID_PREV_OBJ + MENUITEM "Adjust Grid", ID_EDITORS_ADJUST_GRID + MENUITEM "Next Subsystem\tK", ID_NEXT_SUBSYS + MENUITEM "Prev Subsystem\tShift+K", ID_PREV_SUBSYS + MENUITEM "Cancel Subsystem\tAlt+K", ID_CANCEL_SUBSYS + MENUITEM "Mission Statistics\tCtrl+Shift+D", ID_DUMP_STATS + MENUITEM SEPARATOR + MENUITEM "Error checker\tShift+H", ID_ERROR_CHECKER + END + POPUP "&Help" + BEGIN + MENUITEM "&Help Topics", ID_HELP_FINDER + MENUITEM SEPARATOR + MENUITEM "&About FRED2", ID_APP_ABOUT + MENUITEM "Show Sexp Help", ID_SHOW_SEXP_HELP + END +END + +IDR_MENU_SHIP_POPUP MENU DISCARDABLE +BEGIN + POPUP "Properties" + BEGIN + MENUITEM "Edit Ship", ID_EDITORS_SHIPS + MENUITEM "Edit Position and Orientation", ID_EDITORS_ORIENT + MENUITEM "Edit Wing", ID_EDITORS_WING + END +END + +IDR_MENU_EDIT_POPUP MENU DISCARDABLE +BEGIN + POPUP "EDIT" + BEGIN + MENUITEM "Show Ship Models", ID_EDIT_POPUP_SHOW_SHIP_MODELS + , CHECKED + MENUITEM "Show Outlines", ID_VIEW_OUTLINES + MENUITEM "Show Ship Info", ID_EDIT_POPUP_SHOW_SHIP_ICONS + , CHECKED, HELP + MENUITEM "Show Coordinates", ID_SHOW_COORDINATES + MENUITEM "Show Grid Positions", ID_SHOW_GRID_POSITIONS + MENUITEM "Show Distances", ID_SHOW_DISTANCES + MENUITEM SEPARATOR + POPUP "Control Mode" + BEGIN + MENUITEM "Camera", ID_CONTROL_MODE_CAMERA + MENUITEM "Current Ship", ID_CONTROL_MODE_SHIP + END + POPUP "Viewpoint" + BEGIN + MENUITEM "Camera", ID_CHANGE_VIEWPOINT_EXTERNAL + , CHECKED + MENUITEM "Current Ship", ID_CHANGE_VIEWPOINT_FOLLOW + + END + MENUITEM SEPARATOR + END +END + +IDR_MENU_CAMPAIGN MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_CPGN_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_CPGN_FILE_OPEN + MENUITEM "&Save\tCtrl+S", ID_CPGN_FILE_SAVE + MENUITEM "Save &As...", ID_CPGN_FILE_SAVE_AS + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_CLOSE + END + POPUP "Other" + BEGIN + MENUITEM "Error Checker\tAlt+H", ID_ERROR_CHECKER + END + POPUP "&Initial Status" + BEGIN + MENUITEM "Ships", ID_INITIAL_SHIPS + MENUITEM "Weapons", ID_INITIAL_WEAPONS + END +END + +IDR_MENU_EDIT_SEXP_TREE MENU DISCARDABLE +BEGIN + POPUP "Edit sexp tree" + BEGIN + MENUITEM "&Delete Item\tDelete", ID_DELETE + MENUITEM "&Edit Data", ID_EDIT_TEXT + MENUITEM "Expand All", ID_EXPAND_ALL + MENUITEM SEPARATOR + MENUITEM "Cut\tCtrl+X", ID_EDIT_CUT, GRAYED + MENUITEM "Copy\tCtrl+C", ID_EDIT_COPY + MENUITEM "Paste\tCtrl+V", ID_EDIT_PASTE, GRAYED + MENUITEM "Add Paste", ID_EDIT_PASTE_SPECIAL + , GRAYED + MENUITEM SEPARATOR + POPUP "Add Operator" + BEGIN + MENUITEM "placeholder", ID_PLACEHOLDER, GRAYED + END + POPUP "Add Data" + BEGIN + MENUITEM "Number", ID_ADD_NUMBER, GRAYED + MENUITEM "String", ID_ADD_STRING, GRAYED + MENUITEM SEPARATOR + END + MENUITEM SEPARATOR + POPUP "Insert Operator" + BEGIN + MENUITEM "placeholder", ID_PLACEHOLDER, GRAYED + END + MENUITEM SEPARATOR + POPUP "Replace Operator" + BEGIN + MENUITEM "placeholder", ID_PLACEHOLDER, GRAYED + END + POPUP "Replace Data" + BEGIN + MENUITEM "Number", ID_REPLACE_NUMBER + , GRAYED + MENUITEM "String", ID_REPLACE_STRING + , GRAYED + MENUITEM SEPARATOR + END + MENUITEM SEPARATOR + MENUITEM "Add Variable", ID_SEXP_TREE_ADD_VARIABLE + MENUITEM "Modify Variable", ID_SEXP_TREE_MODIFY_VARIABLE + POPUP "Replace Variable" + BEGIN + MENUITEM "Placeholder", ID_PLACEHOLDER, GRAYED + END + END +END + +IDR_WING_EDIT_MENU MENU DISCARDABLE +BEGIN + POPUP "&Select Wing" + BEGIN + MENUITEM "PlaceHolder", ID_SELECTSHIP_PLACEHOLDER + END +END + +IDR_SHIP_EDIT_MENU MENU DISCARDABLE +BEGIN + POPUP "&Select Ship" + BEGIN + MENUITEM "PlaceHolder", ID_SELECTSHIP_PLACEHOLDER + END +END + +IDR_PLAYER_EDIT_MENU MENU DISCARDABLE +BEGIN + POPUP "Select Team" + BEGIN + MENUITEM "Team 1", ID_TEAM_1 + MENUITEM "Team 2", ID_TEAM_2 + END +END + +IDR_WAYPOINT_PATH_EDIT_MENU MENU DISCARDABLE +BEGIN + POPUP "&Select Waypoint Path" + BEGIN + MENUITEM "PlaceHolder", ID_SELECTSHIP_PLACEHOLDER + END +END + +IDR_ASTEROID_FIELD_MENU MENU DISCARDABLE +BEGIN + POPUP "Select Field" + BEGIN + MENUITEM "PlaceHolder", ID_SELECTSHIP_PLACEHOLDER + END +END + +IDR_CPGN_VIEW_OFF MENU DISCARDABLE +BEGIN + POPUP "Blah" + BEGIN + MENUITEM "&Delete Row", ID_DELETE_ROW + MENUITEM "&Insert Row", ID_INSERT_ROW + END +END + +IDR_CPGN_VIEW_ON MENU DISCARDABLE +BEGIN + POPUP "Blah" + BEGIN + MENUITEM "&Remove Mission\tDelete", ID_REMOVE_MISSION + MENUITEM "Add a Repeat Link", ID_ADD_REPEAT + MENUITEM "End of Campaign", ID_END_OF_CAMPAIGN + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "1", ID_SPEED1, VIRTKEY, NOINVERT + "1", ID_GROUP1, VIRTKEY, CONTROL, NOINVERT + "1", ID_ROT1, VIRTKEY, SHIFT, NOINVERT + "2", ID_SPEED2, VIRTKEY, NOINVERT + "2", ID_GROUP2, VIRTKEY, CONTROL, NOINVERT + "2", ID_ROT2, VIRTKEY, SHIFT, NOINVERT + "3", ID_SPEED3, VIRTKEY, NOINVERT + "3", ID_GROUP3, VIRTKEY, CONTROL, NOINVERT + "3", ID_ROT3, VIRTKEY, SHIFT, NOINVERT + "3", ID_EDIT_POPUP_SHOW_COMPASS, VIRTKEY, SHIFT, ALT, + NOINVERT + "4", ID_SPEED5, VIRTKEY, NOINVERT + "4", ID_GROUP4, VIRTKEY, CONTROL, NOINVERT + "4", ID_ROT4, VIRTKEY, SHIFT, NOINVERT + "5", ID_SPEED8, VIRTKEY, NOINVERT + "5", ID_GROUP5, VIRTKEY, CONTROL, NOINVERT + "5", ID_ROT5, VIRTKEY, SHIFT, NOINVERT + "6", ID_SPEED10, VIRTKEY, NOINVERT + "6", ID_GROUP6, VIRTKEY, CONTROL, NOINVERT + "7", ID_SPEED50, VIRTKEY, NOINVERT + "7", ID_GROUP7, VIRTKEY, CONTROL, NOINVERT + "8", ID_SPEED100, VIRTKEY, NOINVERT + "8", ID_GROUP8, VIRTKEY, CONTROL, NOINVERT + "9", ID_GROUP9, VIRTKEY, CONTROL, NOINVERT + "A", ID_ASTEROID_EDITOR, VIRTKEY, SHIFT, NOINVERT + "B", ID_EDITORS_BRIEFING, VIRTKEY, SHIFT, NOINVERT + "B", ID_SHOW_STARFRIELD, VIRTKEY, SHIFT, ALT, NOINVERT + "C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + "C", ID_EDITOR_CAMPAIGN, VIRTKEY, SHIFT, NOINVERT + "C", ID_SHOW_COORDINATES, VIRTKEY, SHIFT, ALT, NOINVERT + "D", ID_SHOW_DISTANCES, VIRTKEY, NOINVERT + "D", ID_DISBAND_WING, VIRTKEY, CONTROL, NOINVERT + "D", ID_EDITORS_DEBRIEFING, VIRTKEY, SHIFT, NOINVERT + "D", ID_DUMP_STATS, VIRTKEY, SHIFT, CONTROL, + NOINVERT + "E", ID_EDITORS_EVENTS, VIRTKEY, SHIFT, NOINVERT + "G", ID_EDITORS_GOALS, VIRTKEY, SHIFT, NOINVERT + "G", ID_VIEW_GRID, VIRTKEY, SHIFT, ALT, NOINVERT + "H", ID_SELECT_LIST, VIRTKEY, NOINVERT + "H", ID_ERROR_CHECKER, VIRTKEY, SHIFT, NOINVERT + "H", ID_SHOW_HORIZON, VIRTKEY, SHIFT, ALT, NOINVERT + "I", ID_EDITORS_BG_BITMAPS, VIRTKEY, SHIFT, NOINVERT + "I", ID_EDIT_POPUP_SHOW_SHIP_ICONS, VIRTKEY, SHIFT, ALT, + NOINVERT + "K", ID_NEXT_SUBSYS, VIRTKEY, NOINVERT + "K", ID_CANCEL_SUBSYS, VIRTKEY, ALT, NOINVERT + "K", ID_PREV_SUBSYS, VIRTKEY, SHIFT, NOINVERT + "L", ID_LEVEL_OBJ, VIRTKEY, NOINVERT + "L", ID_ALIGN_OBJ, VIRTKEY, CONTROL, NOINVERT + "M", ID_SELECT_AND_MOVE, VIRTKEY, NOINVERT + "M", ID_EDIT_POPUP_SHOW_SHIP_MODELS, VIRTKEY, SHIFT, ALT, + NOINVERT + "N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT + "N", ID_FILE_MISSIONNOTES, VIRTKEY, SHIFT, NOINVERT + "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "O", ID_EDITORS_ORIENT, VIRTKEY, SHIFT, NOINVERT + "O", ID_VIEW_OUTLINES, VIRTKEY, SHIFT, ALT, NOINVERT + "P", ID_SAVE_CAMERA, VIRTKEY, CONTROL, NOINVERT + "P", ID_EDITORS_PLAYER, VIRTKEY, SHIFT, NOINVERT + "P", ID_SHOW_GRID_POSITIONS, VIRTKEY, SHIFT, ALT, NOINVERT + "R", ID_RESTORE_CAMERA, VIRTKEY, CONTROL, NOINVERT + "R", ID_RUN_FREESPACE, VIRTKEY, ALT, NOINVERT + "R", ID_EDITORS_REINFORCEMENT, VIRTKEY, SHIFT, NOINVERT + "S", ID_SELECT, VIRTKEY, NOINVERT + "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + "S", ID_EDITORS_SHIPS, VIRTKEY, SHIFT, NOINVERT + "T", ID_CONTROL_OBJ, VIRTKEY, NOINVERT + "V", ID_LOOKAT_OBJ, VIRTKEY, CONTROL, NOINVERT + "V", ID_TOGGLE_VIEWPOINT, VIRTKEY, SHIFT, NOINVERT + VK_BACK, ID_EDIT_UNDO, VIRTKEY, ALT, NOINVERT + VK_DELETE, ID_EDIT_DELETE, VIRTKEY, NOINVERT + VK_DELETE, ID_EDIT_DELETE_WING, VIRTKEY, CONTROL, NOINVERT + VK_DELETE, ID_EDIT_CUT, VIRTKEY, SHIFT, NOINVERT + VK_F1, ID_CONTEXT_HELP, VIRTKEY, SHIFT, NOINVERT + VK_F6, ID_NEXT_PANE, VIRTKEY, NOINVERT + VK_F6, ID_PREV_PANE, VIRTKEY, SHIFT, NOINVERT + VK_INSERT, ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + VK_INSERT, ID_EDIT_PASTE, VIRTKEY, SHIFT, NOINVERT + VK_SPACE, ID_MISC_STATISTICS, VIRTKEY, ALT, NOINVERT + VK_TAB, ID_NEXT_OBJ, VIRTKEY, NOINVERT + VK_TAB, ID_PREV_OBJ, VIRTKEY, CONTROL, NOINVERT + "W", ID_MARK_WING, VIRTKEY, NOINVERT + "W", ID_FORM_WING, VIRTKEY, CONTROL, NOINVERT + "W", ID_EDITORS_WING, VIRTKEY, SHIFT, NOINVERT + "X", ID_ROTATE_LOCALLY, VIRTKEY, NOINVERT + "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT + "Y", ID_EDITORS_WAYPOINT, VIRTKEY, SHIFT, NOINVERT + "Z", ID_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT + "Z", ID_ZOOM_SELECTED, VIRTKEY, ALT, NOINVERT + "Z", ID_ZOOM_EXTENTS, VIRTKEY, SHIFT, NOINVERT +END + +IDR_ACC_CAMPAIGN ACCELERATORS DISCARDABLE +BEGIN + "H", ID_ERROR_CHECKER, VIRTKEY, ALT, NOINVERT + "N", ID_CPGN_FILE_NEW, VIRTKEY, CONTROL, NOINVERT + "O", ID_CPGN_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "S", ID_CPGN_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + VK_RETURN, ID_END_EDIT, VIRTKEY, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 266, 70 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About FRED2" +FONT 8, "MS Sans Serif" +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20 + LTEXT "FRED2 - FreeSpace Editor, Version 1.00",IDC_STATIC,40, + 10,167,8,SS_NOPREFIX + LTEXT "Copyright © 1999 Volition, Inc.",IDC_STATIC,40,25,164,8 + DEFPUSHBUTTON "OK",IDOK,227,7,32,14,WS_GROUP + LTEXT "All Rights Reserved",IDC_STATIC,41,35,64,8 +END + +IDD_SHIP_EDITBAR DIALOGEX 0, 0, 293, 52 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Ship Editbar" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + EDITTEXT IDC_SHIP_NAME,26,2,60,14,ES_AUTOHSCROLL + COMBOBOX IDC_SHIP_CLASS,26,19,60,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SHIP_WING,26,35,60,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_POS_X,101,2,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN_POSITIONX,"msctls_updown32", + UDS_ARROWKEYS,141,2,11,14 + EDITTEXT IDC_POS_Y,101,19,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN_POSITIONY,"msctls_updown32", + UDS_ARROWKEYS,141,19,11,14 + EDITTEXT IDC_POS_Z,101,35,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN_POSITIONZ,"msctls_updown32", + UDS_ARROWKEYS,141,35,11,14 + COMBOBOX IDC_SHIP_IFF,189,2,57,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SHIP_AI_CLASS,189,19,57,30,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SHIP_STATUS,189,35,57,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Edit",IDC_SHIP_EDITOR,250,1,41,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Goals",IDC_GOAL_EDITOR,250,18,41,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Waypoints",IDC_WAYPOINT_EDITOR,250,35,41,14,0, + WS_EX_STATICEDGE + LTEXT "X",IDC_STATIC,94,5,8,8 + LTEXT "Z",IDC_STATIC,94,38,8,8 + LTEXT "Y",IDC_STATIC,94,22,8,8 + LTEXT "Name",IDC_STATIC,2,6,22,8 + LTEXT "Class",IDC_STATIC,6,22,18,8 + LTEXT "Wing",IDC_STATIC,6,38,18,8 + LTEXT "IFF",IDC_STATIC,173,6,11,8 + LTEXT "AI Class",IDC_STATIC,159,22,26,8 + LTEXT "Status",IDC_STATIC,163,38,21,8 +END + +IDD_MESSAGE_EDITOR DIALOGEX 0, 0, 198, 366 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Message Editor" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LISTBOX IDC_MESSAGE_LIST,7,18,127,74,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_NAME,31,96,127,14,ES_AUTOHSCROLL + EDITTEXT IDC_MESSAGE_TEXT,7,126,184,54,ES_MULTILINE | + ES_AUTOVSCROLL | ES_WANTRETURN + COMBOBOX IDC_AVI_FILENAME,49,196,71,111,CBS_DROPDOWN | + CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Browse",IDC_BROWSE_AVI,126,196,50,12 + COMBOBOX IDC_WAVE_FILENAME,49,211,71,110,CBS_DROPDOWN | + CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Browse",IDC_BROWSE_WAVE,126,211,50,12 + COMBOBOX IDC_PERSONA_NAME,49,226,71,57,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SENDER,40,262,71,122,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_PRIORITY,144,262,41,91,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Tree1",IDC_TREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | + WS_BORDER | WS_TABSTOP,13,289,171,63,WS_EX_CLIENTEDGE + PUSHBUTTON "New",IDC_NEW,141,18,50,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Delete",IDC_DELETE,141,36,50,14,0,WS_EX_STATICEDGE + PUSHBUTTON "OK",ID_OK,141,58,50,14 + PUSHBUTTON "Cancel",IDCANCEL,141,76,50,14 + LTEXT "Message Text",IDC_STATIC,7,115,48,8 + LTEXT "Name",IDC_STATIC,7,98,20,8 + LTEXT "Messages",IDC_STATIC,57,8,35,8 + LTEXT "ANI file",IDC_STATIC,21,198,23,8 + LTEXT "Wave file",IDC_STATIC,13,213,31,8 + LTEXT "Formula",IDC_STATIC,13,279,66,8 + GROUPBOX "Default Send",IDC_STATIC,7,251,184,107 + LTEXT "Sender",IDC_STATIC,13,264,24,8 + LTEXT "Priority",IDC_STATIC,119,264,22,8 + LTEXT "Persona",IDC_STATIC,17,228,27,8 + GROUPBOX "Message Properties",IDC_STATIC,7,184,184,60 +END + +IDD_SHIP_GOALS_EDITOR DIALOG DISCARDABLE 0, 0, 412, 205 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Initial Orders" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_BEHAVIOR1,37,20,71,132,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_OBJECT1,111,20,94,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SUBSYSTEM1,208,20,79,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCK1,291,20,71,93,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PRIORITY1,365,20,40,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,365,20,11, + 12 + COMBOBOX IDC_BEHAVIOR2,37,36,71,128,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_OBJECT2,111,36,94,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SUBSYSTEM2,208,36,79,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCK2,291,36,71,102,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PRIORITY2,365,36,40,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN2,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,365,36,11, + 12 + COMBOBOX IDC_BEHAVIOR3,37,52,71,132,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_OBJECT3,111,52,94,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SUBSYSTEM3,208,52,79,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCK3,291,52,71,96,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PRIORITY3,365,52,40,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN3,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,365,52,11, + 12 + COMBOBOX IDC_BEHAVIOR4,37,68,71,130,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_OBJECT4,111,68,94,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SUBSYSTEM4,208,68,79,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCK4,291,68,71,97,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PRIORITY4,365,68,40,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN4,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,365,68,11, + 12 + COMBOBOX IDC_BEHAVIOR5,37,84,71,133,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_OBJECT5,111,84,94,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SUBSYSTEM5,208,84,79,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCK5,291,84,71,108,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PRIORITY5,365,84,40,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN5,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,365,84,11, + 12 + COMBOBOX IDC_BEHAVIOR6,37,100,71,126,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_OBJECT6,111,100,94,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SUBSYSTEM6,208,100,79,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCK6,291,100,71,98,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PRIORITY6,365,100,40,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN6,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,365,100, + 11,12 + COMBOBOX IDC_BEHAVIOR7,37,116,71,130,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_OBJECT7,111,116,94,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SUBSYSTEM7,208,116,79,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCK7,291,116,71,119,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PRIORITY7,365,116,40,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN7,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,365,116, + 11,12 + COMBOBOX IDC_BEHAVIOR8,37,132,71,133,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_OBJECT8,111,132,94,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SUBSYSTEM8,208,132,79,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCK8,291,132,71,102,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PRIORITY8,365,132,40,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN8,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,365,132, + 11,12 + COMBOBOX IDC_BEHAVIOR9,37,148,71,129,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_OBJECT9,111,148,94,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SUBSYSTEM9,208,148,79,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCK9,291,148,71,118,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PRIORITY9,365,148,40,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN9,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,365,148, + 11,12 + COMBOBOX IDC_BEHAVIOR10,37,164,71,131,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_OBJECT10,111,164,94,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SUBSYSTEM10,208,164,79,109,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCK10,291,164,71,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PRIORITY10,365,164,40,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN10,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,365,164, + 11,12 + PUSHBUTTON "OK",IDOK,299,184,50,14 + PUSHBUTTON "Cancel",IDCANCEL,355,184,50,14 + LTEXT "Order 2",IDC_STATIC,7,39,24,8 + LTEXT "Order 3",IDC_STATIC,7,55,24,8 + LTEXT "Order 4",IDC_STATIC,7,71,24,8 + LTEXT "Order 5",IDC_STATIC,7,87,24,8 + LTEXT "Order 6",IDC_STATIC,7,103,24,8 + LTEXT "Order 7",IDC_STATIC,7,119,24,8 + LTEXT "Order 8",IDC_STATIC,7,135,24,8 + LTEXT "Order 9",IDC_STATIC,7,151,24,8 + LTEXT "Behavior",IDC_STATIC,57,8,29,8 + LTEXT "Priority",IDC_STATIC,383,8,22,8 + LTEXT "Object",IDC_STATIC,135,8,22,8 + LTEXT "Order 1",IDC_STATIC,7,23,24,8 + LTEXT "Order 10",IDC_STATIC,7,167,28,8 + LTEXT "Subsys/Docker's Bay",IDC_STATIC,209,8,69,8 + LTEXT "Dockee's Bay",IDC_STATIC,303,8,45,8 +END + +IDD_MISSION_NOTES DIALOG DISCARDABLE 0, 0, 322, 266 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Mission Specs" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_MISSION_TITLE,41,7,109,14,ES_AUTOHSCROLL + EDITTEXT IDC_DESIGNER_NAME,41,23,109,14,ES_AUTOHSCROLL + CONTROL "Single Player",IDC_SINGLE,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,7,42,57,10 + CONTROL "Multi Player",IDC_MULTI,"Button",BS_AUTORADIOBUTTON,7, + 51,52,10 + CONTROL "Training",IDC_TRAINING,"Button",BS_AUTORADIOBUTTON,7,60, + 41,10 + CONTROL "Cooperative",IDC_COOP,"Button",BS_AUTORADIOBUTTON | + WS_GROUP,76,42,54,10 + CONTROL "Team Vs. Team",IDC_TEAMVTEAM,"Button", + BS_AUTORADIOBUTTON,76,51,66,10 + CONTROL "Dogfight",IDC_DOGFIGHT,"Button",BS_AUTORADIOBUTTON,76, + 60,43,10 + CONTROL "All Teams at War",IDC_FULL_WAR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,163,9,70,10 + CONTROL "Red Alert Mission",IDC_RED_ALERT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,163,19,78,10 + CONTROL "Scramble Mission",IDC_SCRAMBLE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,163,29,72,10 + CONTROL "Disallow Support Ships",IDC_SUPPORT_ALLOWED,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,163,39,95,10 + CONTROL "Disallow Promotions/Badges",IDC_NO_PROMOTION,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,163,49,123,10 + EDITTEXT IDC_RESPAWNS,215,81,50,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_RESPAWN_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,265,81,9,14 + COMBOBOX IDC_MUSIC,187,99,78,140,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_MISSION_DESC,7,166,307,38,ES_MULTILINE | + ES_AUTOVSCROLL | ES_WANTRETURN + EDITTEXT IDC_MISSION_NOTES,7,219,307,40,ES_MULTILINE | + ES_AUTOVSCROLL | ES_WANTRETURN + DEFPUSHBUTTON "OK",IDOK,264,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,264,25,50,14 + RTEXT "Title",IDC_STATIC,7,10,29,8 + RTEXT "Designer Notes",IDC_STATIC,7,208,50,8 + LTEXT "xx/xx/xx",IDC_MODIFIED,57,83,101,8 + RTEXT "Designer",IDC_STATIC,7,25,29,8 + RTEXT "Created:",IDC_STATIC,23,74,28,8 + LTEXT "xx/xx/xx",IDC_CREATED,57,74,103,8 + RTEXT "Last Modified:",IDC_STATIC,7,83,45,8 + LTEXT "Mission Description",IDC_STATIC,7,157,62,8 + LTEXT "Music",IDC_STATIC,163,102,20,8 + LTEXT "Max Respawns",IDC_STATIC,163,84,52,8 + EDITTEXT IDC_SQUAD_NAME,209,127,98,14,ES_AUTOHSCROLL + GROUPBOX "Squadron Reassign",IDC_STATIC,163,117,151,45 + LTEXT "Name",IDC_STATIC,175,128,28,8 + PUSHBUTTON "Logo",IDC_SQUAD_LOGO_BUTTON,169,142,31,14 + EDITTEXT IDC_SQUAD_LOGO,209,142,98,14,ES_AUTOHSCROLL | + ES_READONLY + CONTROL "Disable Built-in Messages",IDC_DISABLE_BUILTIN_MSGS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,163,59,123,10 + CONTROL "No Traitor",IDC_NO_TRAITOR,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,163,69,123,10 +END + +IDD_PREFERENCES DIALOG DISCARDABLE 0, 0, 333, 111 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Preferences" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Confirm deleting objects",ID_CONFIRM_DELETING,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,18,91,10 + CONTROL "Star Field",IDC_PREF_STARFIELD,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,13,38,45,10 + CONTROL "Fighters",ID_SHOW_FIGHTERS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,119,18,41,10 + CONTROL "Capital Ships",ID_SHOW_CAPITALSHIPS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,119,28,57,10 + CONTROL "Planets",ID_SHOW_PLANETS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,119,39,39,10 + CONTROL "Misc Objects",ID_SHOW_MISCOBJECTS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,119,49,57,10 + CONTROL "Waypoints",ID_SHOW_WAYPOINTS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,179,18,49,10 + CONTROL "Grid",ID_SHOW_GRID,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,179,28,29,10 + CONTROL "Elevations",ID_SHOW_ELEVATIONS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,179,39,49,10 + PUSHBUTTON "Save as Default",IDC_SAVE_DEFAULT_PREFS,270,47,56,14 + DEFPUSHBUTTON "OK",IDOK,276,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,276,24,50,14 + GROUPBOX "Default Display",IDC_DISPLAY,107,7,136,56 +END + +IDD_SHIP_EDITOR DIALOGEX 0, 0, 321, 418 +STYLE DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | + WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE | WS_EX_CONTEXTHELP +CAPTION "Edit Ship" +MENU IDR_SHIP_EDIT_MENU +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + PUSHBUTTON "Prev",IDC_PREV,264,7,24,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Next",IDC_NEXT,290,7,24,14,0,WS_EX_STATICEDGE + EDITTEXT IDC_SHIP_NAME,47,7,94,14,ES_AUTOHSCROLL + COMBOBOX IDC_SHIP_CLASS,47,22,94,207,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_AI_CLASS,47,36,94,185,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SHIP_TEAM,47,50,94,196,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_SHIP_CARGO1,47,64,94,258,CBS_DROPDOWN | + CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_HOTKEY,180,21,68,122,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_SCORE,180,52,68,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Player Ship",IDC_PLAYER_SHIP,"Button",BS_3STATE | + WS_TABSTOP,179,70,51,10 + PUSHBUTTON "Initial Status",IDC_INITIAL_STATUS,101,102,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "Initial Orders",IDC_GOALS,156,102,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "Delete",IDC_DELETE_SHIP,264,23,50,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Reset",IDC_SHIP_RESET,264,39,50,14,BS_CENTER | + BS_MULTILINE,WS_EX_STATICEDGE + PUSHBUTTON "Weapons",IDC_WEAPONS,264,55,50,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Player Orders",IDC_IGNORE_ORDERS,264,71,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "TBL Info",IDC_SHIP_TBL,210,102,50,14,0,WS_EX_STATICEDGE + CONTROL "Hide Cues",IDC_HIDE_CUES,"Button",BS_AUTOCHECKBOX | + BS_PUSHLIKE | WS_TABSTOP,265,103,49,12 + COMBOBOX IDC_ARRIVAL_LOCATION,46,134,103,127,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_ARRIVAL_TARGET,46,148,103,262,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_ARRIVAL_DISTANCE,46,162,40,14,ES_AUTOHSCROLL | + ES_NUMBER + EDITTEXT IDC_ARRIVAL_DELAY,46,178,40,14,ES_AUTOHSCROLL | + ES_NUMBER + CONTROL "Spin1",IDC_ARRIVAL_DELAY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,86,178,11,14 + CONTROL "Update Cue",IDC_UPDATE_ARRIVAL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,94,196,54,10 + CONTROL "Tree1",IDC_ARRIVAL_TREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | + WS_BORDER | WS_TABSTOP,15,206,133,84,WS_EX_CLIENTEDGE + CONTROL "No Warp Effect",IDC_NO_ARRIVAL_WARP,"Button",BS_3STATE | + WS_TABSTOP,16,292,65,10 + COMBOBOX IDC_DEPARTURE_LOCATION,202,134,103,127,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DEPARTURE_TARGET,202,148,103,262,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_DEPARTURE_DELAY,201,178,40,14,ES_AUTOHSCROLL | + ES_NUMBER + CONTROL "Spin3",IDC_DEPARTURE_DELAY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,240,178,11,14 + CONTROL "Update Cue",IDC_UPDATE_DEPARTURE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,251,196,54,10 + CONTROL "Tree1",IDC_DEPARTURE_TREE,"SysTreeView32", + TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | + TVS_EDITLABELS | WS_BORDER | WS_TABSTOP,172,206,133,84, + WS_EX_CLIENTEDGE + CONTROL "No Warp Effect",IDC_NO_DEPARTURE_WARP,"Button", + BS_3STATE | WS_TABSTOP,172,292,65,10 + EDITTEXT IDC_HELP_BOX,7,316,307,95,ES_MULTILINE | ES_READONLY, + WS_EX_TRANSPARENT + LTEXT "Ship Name",IDC_STATIC,7,10,36,8 + LTEXT "Ship Class",IDC_STATIC,9,24,34,8 + LTEXT "Cargo",IDC_STATIC,23,66,20,8 + RTEXT "Wing:",IDC_STATIC,156,10,20,8 + LTEXT "Team",IDC_STATIC,24,52,19,8 + LTEXT "Location",IDC_STATIC,15,134,28,8 + LTEXT "Cue:",IDC_STATIC,15,196,16,8 + GROUPBOX "Arrival",IDC_CUE_FRAME,7,121,150,185 + LTEXT "Location",IDC_STATIC,172,134,28,8 + LTEXT "Cue:",IDC_STATIC,172,196,16,8 + GROUPBOX "Departure",IDC_STATIC,164,121,150,185 + LTEXT "AI Class",IDC_STATIC,17,38,26,8 + LTEXT "Delay",IDC_STATIC,15,182,19,8 + LTEXT "Delay",IDC_STATIC,172,182,19,8 + LTEXT "Seconds",IDC_STATIC,94,182,45,8 + LTEXT "Seconds",IDC_STATIC,247,182,45,8 + LTEXT "Static",IDC_WING,180,10,67,8 + LTEXT "Hotkey",IDC_STATIC,152,23,24,8 + LTEXT "Score",IDC_STATIC,156,55,20,8 + LTEXT "Target",IDC_STATIC,15,150,22,8 + LTEXT "Distance",IDC_STATIC,15,166,29,8 + LTEXT "Target",IDC_STATIC,172,150,22,8 + PUSHBUTTON "Misc",IDC_FLAGS,47,102,50,14,0,WS_EX_STATICEDGE + COMBOBOX IDC_SHIP_PERSONA,180,37,68,129,CBS_DROPDOWNLIST | + CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Persona",IDC_STATIC,149,39,27,8 + PUSHBUTTON "Special Exp",IDC_SPECIAL_EXP,264,87,50,14,0, + WS_EX_STATICEDGE + LTEXT "Alt Name",IDC_STATIC,14,80,30,8 + COMBOBOX IDC_SHIP_ALT,47,79,94,125,CBS_DROPDOWN | WS_VSCROLL | + WS_TABSTOP +END + +IDD_WEAPON_EDITOR DIALOG DISCARDABLE 0, 0, 445, 79 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Weapon Editor" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST,7,8,75,64,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_GUN1,136,9,67,101,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_GUN2,136,26,67,117,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_GUN3,136,44,67,120,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_AI_CLASS,136,62,67,108,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_MISSILE1,270,9,67,116,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_AMMO1,342,8,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,386,46,9, + 14 + COMBOBOX IDC_MISSILE2,270,26,67,111,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_AMMO2,342,25,40,14,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN2,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,396,46,9, + 14 + COMBOBOX IDC_MISSILE3,270,44,67,109,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_AMMO3,342,43,40,14,ES_AUTOHSCROLL + CONTROL "Spin3",IDC_SPIN3,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,408,46,9, + 14 + COMBOBOX IDC_MISSILE4,270,62,67,114,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_AMMO4,342,60,40,14,ES_AUTOHSCROLL + CONTROL "Spin4",IDC_SPIN4,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,418,46,9, + 14 + DEFPUSHBUTTON "Close",IDOK,388,9,50,14 + LTEXT "Gun bank 1",IDC_STATIC,92,11,38,8 + LTEXT "Gun bank 2",IDC_STATIC,92,28,38,8 + LTEXT "Gun bank 3",IDC_STATIC,92,46,38,8 + LTEXT "AI class",IDC_STATIC,104,63,26,8 + LTEXT "Missile bank 1",IDC_STATIC,218,11,46,8 + LTEXT "Missile bank 2",IDC_STATIC,218,28,46,8 + LTEXT "Missile bank 3",IDC_STATIC,218,46,46,8 + LTEXT "Missile bank 4",IDC_STATIC,218,63,46,8 +END + +IDD_SHIP_MARKINGS DIALOG DISCARDABLE 0, 0, 252, 140 +STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | + WS_CAPTION +CAPTION "Ship Markings" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Markings",-1,19,10,29,8 + COMBOBOX IDC_MODEL_NAME,52,8,88,30,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + CONTROL "",IDC_MODEL_WINDOW,"Static",SS_BLACKFRAME,11,33,164,97 + DEFPUSHBUTTON "OK",IDOK,187,8,50,14 + PUSHBUTTON "Cancel",IDCANCEL,187,25,50,14 +END + +IDD_MISSION_GOALS DIALOGEX 0, 0, 299, 270 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Mission Objectives" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + PUSHBUTTON "New Obj.",IDC_BUTTON_NEW_GOAL,152,146,44,14,0, + WS_EX_STATICEDGE,HIDC_BUTTON_NEW_GOAL + CONTROL "Tree1",IDC_GOALS_TREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | + TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,6,7,136,153, + WS_EX_CLIENTEDGE + COMBOBOX IDC_DISPLAY_GOAL_TYPES_DROP,219,7,73,119, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_GOAL_TYPE_DROP,211,34,73,108,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_GOAL_NAME,211,49,73,14,ES_AUTOHSCROLL + EDITTEXT IDC_GOAL_DESC,157,65,127,14,ES_AUTOHSCROLL + EDITTEXT IDC_GOAL_SCORE,211,81,73,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Objective Invalid",IDC_GOAL_INVALID,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,157,116,69,10 + CONTROL "Don't Play Completion Sound",IDC_NO_MUSIC,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,157,126,107,10 + PUSHBUTTON "OK",ID_OK,200,146,44,14 + PUSHBUTTON "Cancel",IDCANCEL,248,146,44,14 + EDITTEXT IDC_HELP_BOX,6,167,286,95,ES_MULTILINE | ES_READONLY | + WS_VSCROLL,WS_EX_TRANSPARENT + GROUPBOX "Current Objective",IDC_STATIC,152,24,140,117 + LTEXT "Display Types",IDC_STATIC,170,9,45,8,NOT WS_GROUP + LTEXT "Type",IDC_STATIC,188,36,17,8,NOT WS_GROUP + LTEXT "Name",IDC_STATIC,185,52,20,8 + LTEXT "Score",IDC_STATIC,185,84,20,8 + COMBOBOX IDC_OBJ_TEAM,211,97,73,71,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + LTEXT "Team",IDC_STATIC,186,99,19,8 +END + +IDD_SHIP_SELECT DIALOGEX 0, 0, 214, 207 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Ship Selection" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LISTBOX IDC_SHIP_LIST,7,7,98,131,LBS_NOINTEGRALHEIGHT | + LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP + LISTBOX IDC_WING_LIST,7,139,98,61,LBS_NOINTEGRALHEIGHT | + LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP + CONTROL "Player starting points",IDC_FILTER_STARTS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,115,7,81,10 + CONTROL "Waypoints",IDC_FILTER_WAYPOINTS,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,115,19,49,10 + CONTROL "Ships",IDC_FILTER_SHIPS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,115,32,33,10 + CONTROL "Friendly",IDC_FILTER_SHIPS_FRIENDLY,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,128,45,40,10 + CONTROL "Hostile",IDC_FILTER_SHIPS_HOSTILE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,128,58,37,10 + CONTROL "Neutral",IDC_FILTER_SHIPS_NEUTRAL,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,128,71,39,10 + CONTROL "Unknown",IDC_FILTER_SHIPS_UNKNOWN,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,128,84,47,10 + PUSHBUTTON "All",IDC_ALL,115,104,35,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Clear",IDC_CLEAR,115,116,35,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Invert",IDC_INVERT,115,129,35,14,0,WS_EX_STATICEDGE + DEFPUSHBUTTON "OK",IDOK,156,167,50,14 + PUSHBUTTON "Cancel",IDCANCEL,156,186,50,14 +END + +IDD_WING_EDITOR DIALOGEX 0, 0, 297, 417 +STYLE DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | + WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CLIENTEDGE +CAPTION "Wing Edit" +MENU IDR_WING_EDIT_MENU +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + PUSHBUTTON "Prev",IDC_PREV,240,7,24,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Next",IDC_NEXT,266,7,24,14,0,WS_EX_STATICEDGE + EDITTEXT IDC_WING_NAME,65,7,73,14,ES_AUTOHSCROLL + COMBOBOX IDC_WING_SPECIAL_SHIP,65,23,73,161,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_WING_WAVES,65,37,73,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SPIN_WAVES,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,136,36,9, + 14 + EDITTEXT IDC_WING_WAVE_THRESHOLD,65,53,73,15,ES_AUTOHSCROLL | + ES_NUMBER + CONTROL "Spin2",IDC_SPIN_WAVE_THRESHOLD,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,135,52,9,14 + COMBOBOX IDC_HOTKEY,65,70,73,115,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Reinforcement Unit",IDC_REINFORCEMENT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,143,16,77,10 + CONTROL "Ignore for Counting Goals",IDC_IGNORE_COUNT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,143,27,96,10 + CONTROL "No Arrival Music",IDC_NO_ARRIVAL_MUSIC,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,143,38,67,10 + CONTROL "No Arrival Message",IDC_NO_ARRIVAL_MESSAGE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,143,49,77,10 + PUSHBUTTON "Delete Wing",IDC_DELETE_WING,240,23,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "Disband Wing",IDC_DISBAND_WING,240,39,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "Initial Orders",IDC_GOALS2,240,55,50,14,0, + WS_EX_STATICEDGE + CONTROL "Hide Cues",IDC_HIDE_CUES,"Button",BS_AUTOCHECKBOX | + BS_PUSHLIKE | WS_TABSTOP,240,71,49,12 + GROUPBOX "Departure",IDC_STATIC,152,90,138,219 + COMBOBOX IDC_ARRIVAL_LOCATION,47,102,91,127,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_ARRIVAL_DELAY,47,118,40,14,ES_AUTOHSCROLL | + ES_NUMBER + CONTROL "Spin3",IDC_ARRIVAL_DELAY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,84,119,9,14 + EDITTEXT IDC_ARRIVAL_DELAY_MIN,39,144,28,14,ES_AUTOHSCROLL | + ES_NUMBER + EDITTEXT IDC_ARRIVAL_DELAY_MAX,99,144,28,14,ES_AUTOHSCROLL | + ES_NUMBER + COMBOBOX IDC_ARRIVAL_TARGET,46,169,91,188,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_ARRIVAL_DISTANCE,46,183,40,14,ES_AUTOHSCROLL | + ES_NUMBER + CONTROL "Tree1",IDC_ARRIVAL_TREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | + WS_BORDER | WS_TABSTOP,15,209,121,84,WS_EX_CLIENTEDGE + COMBOBOX IDC_DEPARTURE_LOCATION,192,102,91,127,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_DEPARTURE_DELAY,192,118,40,14,ES_AUTOHSCROLL | + ES_NUMBER + CONTROL "Spin3",IDC_DEPARTURE_DELAY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,230,117,9,14 + COMBOBOX IDC_DEPARTURE_TARGET,188,169,91,188,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Tree1",IDC_DEPARTURE_TREE,"SysTreeView32", + TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | + TVS_EDITLABELS | WS_BORDER | WS_TABSTOP,160,209,121,84, + WS_EX_CLIENTEDGE + EDITTEXT IDC_HELP_BOX,7,315,283,95,ES_MULTILINE | ES_READONLY, + WS_EX_TRANSPARENT + RTEXT "Wing Name",IDC_STATIC,19,10,42,8 + RTEXT "Wave Threshold",IDC_STATIC,7,56,54,8 + RTEXT "# of Waves",IDC_STATIC,16,40,45,8 + RTEXT "Leader",IDC_STATIC,38,25,23,8,NOT WS_GROUP + LTEXT "Location",IDC_STATIC,15,104,28,8 + LTEXT "Cue:",IDC_STATIC,15,198,16,8 + GROUPBOX "Arrival",IDC_CUE_FRAME,7,90,138,219 + LTEXT "Location",IDC_STATIC,160,104,28,8 + LTEXT "Cue:",IDC_STATIC,160,198,16,8 + LTEXT "Delay",IDC_STATIC,160,120,19,8 + LTEXT "Seconds",IDC_STATIC,238,120,45,8 + LTEXT "Delay",IDC_STATIC,15,120,19,8 + LTEXT "Seconds",IDC_STATIC,92,120,45,8 + RTEXT "Hotkey",IDC_STATIC,37,72,24,8 + GROUPBOX "Delay Between Waves",IDC_STATIC,15,134,119,30 + LTEXT "Min",IDC_STATIC,21,147,12,8 + LTEXT "Max",IDC_STATIC,80,147,14,8 + LTEXT "Target",IDC_STATIC,15,171,22,8 + LTEXT "Distance",IDC_STATIC,15,186,29,8 + LTEXT "Target",IDC_STATIC,157,171,22,8 + CONTROL "No Warp Effect",IDC_NO_ARRIVAL_WARP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,13,294,91,10 + CONTROL "No Warp Effect",IDC_NO_DEPARTURE_WARP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,155,294,92,10 + CONTROL "No Dynamic Goals",IDC_NO_DYNAMIC,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,143,60,75,10 +END + +IDD_OPERATOR_ARGUMENT_TYPES DIALOG DISCARDABLE 0, 0, 235, 50 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Select Argument Types" +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Numbers",ID_NUMBERS,7,29,50,14 + PUSHBUTTON "Ships",ID_SHIPS,64,29,50,14 + PUSHBUTTON "Wings",ID_WINGS,121,29,50,14 + PUSHBUTTON "Boolean",ID_BOOLEAN,178,29,50,14 + LTEXT "This operator can accept any type of arguments, however they must\nall match. What type of arguments would you like to use?", + IDC_STATIC,7,7,221,18 +END + +IDD_LOADOUT_EDITOR DIALOG DISCARDABLE 0, 0, 204, 199 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Team Loadout Editor" +MENU IDR_PLAYER_EDIT_MENU +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_SHIP_LIST,7,19,91,103,LBS_OWNERDRAWFIXED | + LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_SHIP_POOL,67,124,32,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,57,124,11, + 14 + LISTBOX IDC_WEAPON_LIST,104,19,93,103,LBS_OWNERDRAWFIXED | + LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_WEAPON_POOL,165,124,32,14,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_POOL_SPIN,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,155,124, + 11,14 + EDITTEXT IDC_DELAY,119,158,32,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_DELAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,47,156,9, + 14 + DEFPUSHBUTTON "OK",IDOK,48,178,41,14 + PUSHBUTTON "Cancel",IDCANCEL,99,178,41,14 + LTEXT "Available starting ships",IDC_STATIC,7,7,74,8 + LTEXT "Extra available",IDC_STATIC,7,127,52,8 + LTEXT "Amount used in wings:",IDC_STATIC,7,141,72,8 + LTEXT "",IDC_WINGS_SHP_COUNT,81,141,18,8 + LTEXT "Player entry delay",IDC_STATIC,59,160,58,8 + LTEXT "Available weapons pool",IDC_STATIC,104,7,76,8 + LTEXT "Amount used in wings:",IDC_STATIC,104,141,72,8 + LTEXT "",IDC_WINGS_WPN_COUNT,179,141,18,8 + LTEXT "Extra available",IDC_STATIC,104,127,52,8 +END + +IDD_ORIENT_EDITOR DIALOG DISCARDABLE 0, 0, 209, 119 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Object Orientation Editor" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_POSITION_X,24,19,49,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,82,18,10, + 14 + EDITTEXT IDC_POSITION_Y,24,35,49,14,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN2,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,81,36,10, + 14 + EDITTEXT IDC_POSITION_Z,24,51,49,14,ES_AUTOHSCROLL + CONTROL "Spin3",IDC_SPIN3,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,82,52,10, + 14 + CONTROL "Point to:",IDC_POINT_TO_CHECKBOX,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,91,7,42,10 + CONTROL "Object:",IDC_POINT_TO_OBJECT,"Button", + BS_AUTORADIOBUTTON,105,20,39,10 + COMBOBOX IDC_OBJECT_LIST,117,34,85,85,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Location:",IDC_POINT_TO_LOCATION,"Button", + BS_AUTORADIOBUTTON,105,51,45,10 + EDITTEXT IDC_LOCATION_X,127,65,49,14,ES_AUTOHSCROLL + CONTROL "Spin4",IDC_SPIN4,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,100,65,9, + 14 + EDITTEXT IDC_LOCATION_Y,127,81,49,14,ES_AUTOHSCROLL + CONTROL "Spin5",IDC_SPIN5,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,101,82,10, + 14 + EDITTEXT IDC_LOCATION_Z,127,98,49,14,ES_AUTOHSCROLL + CONTROL "Spin6",IDC_SPIN6,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,101,98,10, + 14 + DEFPUSHBUTTON "OK",IDOK,7,79,50,14 + PUSHBUTTON "Cancel",IDCANCEL,7,98,50,14 + LTEXT "X:",IDC_STATIC,13,23,8,8 + LTEXT "Y:",IDC_STATIC,13,39,8,8 + LTEXT "Z:",IDC_STATIC,13,55,8,8 + LTEXT "X:",IDC_STATIC,117,68,8,8 + LTEXT "Y:",IDC_STATIC,117,84,8,8 + LTEXT "Z:",IDC_STATIC,117,100,8,8 + GROUPBOX "Position",IDC_STATIC,7,7,72,64 +END + +IDD_EVENT_EDITOR DIALOGEX 0, 0, 437, 383 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Mission Event Edit" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + CONTROL "Tree1",IDC_EVENT_TREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | + TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,7,196,236, + WS_EX_CLIENTEDGE + PUSHBUTTON "New Event",IDC_BUTTON_NEW_EVENT,207,7,50,14,0, + WS_EX_STATICEDGE,HIDC_BUTTON_NEW_EVENT + PUSHBUTTON "Insert Event",IDC_INSERT,207,24,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "Delete Event",IDC_DELETE,207,41,50,14,0, + WS_EX_STATICEDGE + EDITTEXT IDC_REPEAT_COUNT,210,108,45,14,ES_AUTOHSCROLL | + ES_NUMBER + EDITTEXT IDC_INTERVAL_TIME,210,138,45,14,ES_AUTOHSCROLL | + ES_NUMBER + EDITTEXT IDC_EVENT_SCORE,210,168,45,14,ES_AUTOHSCROLL + CONTROL "Chained",IDC_CHAINED,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,211,204,42,10 + EDITTEXT IDC_CHAIN_DELAY,211,228,45,14,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_OBJ_TEXT,86,246,117,14,ES_AUTOHSCROLL + EDITTEXT IDC_OBJ_KEY_TEXT,86,262,117,14,ES_AUTOHSCROLL + PUSHBUTTON "New Msg",IDC_NEW_MSG,229,59,50,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Delete Msg",IDC_DELETE_MSG,229,77,50,14,0, + WS_EX_STATICEDGE + LISTBOX IDC_MESSAGE_LIST,285,17,145,74,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_MESSAGE_NAME,285,95,145,14,ES_AUTOHSCROLL + EDITTEXT IDC_MESSAGE_TEXT,261,125,169,54,ES_MULTILINE | + ES_AUTOVSCROLL | ES_WANTRETURN + COMBOBOX IDC_AVI_FILENAME,303,194,67,111,CBS_DROPDOWN | + CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Browse",IDC_BROWSE_AVI,374,194,49,14,0,WS_EX_STATICEDGE + COMBOBOX IDC_WAVE_FILENAME,303,210,67,110,CBS_DROPDOWN | + CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Browse",IDC_BROWSE_WAVE,374,210,34,14,0, + WS_EX_STATICEDGE + COMBOBOX IDC_PERSONA_NAME,303,225,67,57,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "OK",ID_OK,324,266,50,14 + PUSHBUTTON "Cancel",ID_CANCEL,380,266,50,14 + EDITTEXT IDC_HELP_BOX,7,281,423,95,ES_MULTILINE | ES_READONLY, + WS_EX_TRANSPARENT + LTEXT "Repeat Count",IDC_STATIC,210,98,45,8 + LTEXT "Interval time",IDC_STATIC,210,127,45,8 + LTEXT "Score",IDC_STATIC,210,159,45,8 + LTEXT "Chain Delay",IDC_STATIC,211,217,39,8 + LTEXT "Directive text",IDC_STATIC,37,249,44,8 + LTEXT "Directive keypress text",IDC_STATIC,7,265,74,8 + LTEXT "Message Text",IDC_STATIC,261,114,48,8 + LTEXT "Name",IDC_STATIC,261,97,20,8 + LTEXT "Messages",IDC_STATIC,335,7,35,8 + LTEXT "ANI file",IDC_STATIC,275,197,23,8 + LTEXT "Wave file",IDC_STATIC,267,212,31,8 + LTEXT "Persona",IDC_STATIC,271,227,27,8 + GROUPBOX "Message Properties",IDC_STATIC,261,183,169,80 + PUSHBUTTON "IDB_PLAY",IDC_PLAY,410,210,13,14,BS_BITMAP, + WS_EX_TRANSPARENT | WS_EX_STATICEDGE + PUSHBUTTON "Update Stuff",IDC_UPDATE,373,226,50,14 + COMBOBOX IDC_EVENT_TEAM,209,187,46,76,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_MESSAGE_TEAM,304,243,46,76,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Team",IDC_STATIC,279,245,20,8 + LTEXT "(multiplayer TvT only)",IDC_STATIC,353,246,68,8 +END + +IDD_BG_BITMAP DIALOG DISCARDABLE 0, 0, 422, 298 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Background Editor" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_SUN1_P,363,42,23,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin4",IDC_SUN1_H_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,387,42,9, + 14 + EDITTEXT IDC_SUN1_B,363,57,23,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin3",IDC_SUN1_B_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,387,57,9, + 14 + EDITTEXT IDC_SUN1_H,363,73,23,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SUN1_P_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,387,73,9, + 14 + EDITTEXT IDC_SUN1_SCALE,363,90,23,14,ES_AUTOHSCROLL + CONTROL "Slider1",IDC_SLIDER1,"msctls_trackbar32",TBS_BOTH | + TBS_NOTICKS | WS_TABSTOP,223,156,100,15 + COMBOBOX IDC_NEBPATTERN,13,237,61,108,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_NEBCOLOR,13,266,61,108,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + EDITTEXT IDC_PITCH,156,228,40,14,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_BANK,156,246,40,14,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_HEADING,156,265,40,14,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Pattern:",IDC_STATIC,13,226,52,8 + LTEXT "Color:",IDC_STATIC,13,255,52,8 + LTEXT "Pitch",IDC_STATIC,135,231,17,8 + GROUPBOX "Nebula",IDC_STATIC,7,133,208,158 + LTEXT "Bank",IDC_STATIC,134,250,18,8 + LTEXT "Heading",IDC_STATIC,124,269,28,8 + LTEXT "Number of stars:",IDC_STATIC,228,145,52,8 + LTEXT "Static",IDC_TOTAL,284,145,43,8 + CONTROL "Takes place inside Subspace",IDC_SUBSPACE,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,229,179,109,10 + CONTROL "Full Nebula",IDC_FULLNEB,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,14,142,62,10 + EDITTEXT IDC_NEB2_INTENSITY,15,163,40,14,ES_AUTOHSCROLL + COMBOBOX IDC_SUN1,353,26,49,265,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + GROUPBOX "Misc",IDC_STATIC,222,132,193,159 + GROUPBOX "Suns",IDC_STATIC,222,7,193,125 + GROUPBOX "Bitmaps",IDC_STATIC,7,7,207,125 + LTEXT "Scale",IDC_STATIC,337,93,19,8 + LTEXT "P",IDC_STATIC,353,45,8,8 + LTEXT "B",IDC_STATIC,353,60,8,8 + LTEXT "H",IDC_STATIC,353,76,8,8 + LISTBOX IDC_SUN1_LIST,233,20,102,87,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add",IDC_ADD_SUN,231,113,41,14 + PUSHBUTTON "Delete",IDC_DEL_SUN,293,113,40,14 + EDITTEXT IDC_SBITMAP_P,160,28,23,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin4",IDC_SBITMAP_H_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,184,28,9, + 14 + EDITTEXT IDC_SBITMAP_B,160,44,23,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin3",IDC_SBITMAP_B_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,184,44,9, + 14 + EDITTEXT IDC_SBITMAP_H,160,60,23,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_SBITMAP_P_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_AUTOBUDDY | UDS_ARROWKEYS,184,60,9, + 14 + EDITTEXT IDC_SBITMAP_SCALE_X,133,84,36,14,ES_AUTOHSCROLL + COMBOBOX IDC_SBITMAP,149,14,49,265,CBS_DROPDOWNLIST | CBS_SORT | + WS_VSCROLL | WS_TABSTOP + LTEXT "Scale (x/y)",IDC_STATIC,156,75,35,8 + LTEXT "P",IDC_STATIC,150,31,8,8 + LTEXT "B",IDC_STATIC,150,47,8,8 + LTEXT "H",IDC_STATIC,150,63,8,8 + LISTBOX IDC_SBITMAP_LIST,27,22,102,87,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Add",IDC_ADD_SBITMAP,27,113,41,14 + PUSHBUTTON "Delete",IDC_DEL_SBITMAP,89,113,40,14 + CONTROL "",IDC_STATIC,"Static",SS_BLACKFRAME,7,214,204,2 + COMBOBOX IDC_NEB2_TEXTURE,65,164,63,88,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Range:",IDC_STATIC,25,154,24,8 + LTEXT "Pattern:",IDC_STATIC,85,154,26,8 + LTEXT "Poofs",IDC_STATIC,164,140,19,8 + CONTROL "Check1",IDC_POOF0,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 137,149,41,10 + CONTROL "Check2",IDC_POOF1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 137,159,41,10 + CONTROL "Check3",IDC_POOF2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 137,168,41,10 + CONTROL "Check4",IDC_POOF3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 137,177,41,10 + CONTROL "Check5",IDC_POOF4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 137,186,41,10 + CONTROL "Check6",IDC_POOF5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, + 137,195,41,10 + EDITTEXT IDC_SBITMAP_SCALE_Y,171,84,40,14,ES_AUTOHSCROLL + LTEXT "# divisions (x/y)",IDC_STATIC,151,103,50,8 + EDITTEXT IDC_SBITMAP_DIV_X,133,113,36,14,ES_AUTOHSCROLL | + ES_NUMBER + EDITTEXT IDC_SBITMAP_DIV_Y,171,113,41,14,ES_AUTOHSCROLL | + ES_NUMBER + LTEXT "Lightning Storm",IDC_STATIC,72,183,50,8 + COMBOBOX IDC_NEB2_LIGHTNING,65,192,63,91,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP +END + +IDD_REINFORCEMENT_EDITOR DIALOGEX 0, 0, 178, 119 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Reinforcements Editor" +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + PUSHBUTTON "Add",IDC_ADD,121,7,50,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Delete",IDC_DELETE,121,23,50,14,WS_DISABLED, + WS_EX_STATICEDGE + LISTBOX IDC_LIST,7,7,105,66,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | + WS_TABSTOP,WS_EX_CLIENTEDGE | WS_EX_STATICEDGE + EDITTEXT IDC_USES,67,77,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_USES_SPIN,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,107,74,9, + 14 + EDITTEXT IDC_DELAY,67,97,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_DELAY_SPIN,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,107,96,9, + 14 + DEFPUSHBUTTON "OK",IDOK,121,45,50,14 + PUSHBUTTON "Cancel",IDCANCEL,121,62,50,14 + LTEXT "Uses",IDC_STATIC,47,78,17,8 + LTEXT "Delay After Arrival",IDC_STATIC,7,100,57,8 +END + +IDD_REINFORCEMENT_SELECT DIALOG DISCARDABLE 0, 0, 156, 132 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Select Reinforcement Unit" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST,7,7,87,118,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | + WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,99,94,50,14,WS_DISABLED + PUSHBUTTON "Cancel",IDCANCEL,99,111,50,14 +END + +IDD_WAYPOINT_PATH_EDITOR DIALOG DISCARDABLE 0, 0, 145, 29 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Waypoint Path/Jump Node Editor" +MENU IDR_WAYPOINT_PATH_EDIT_MENU +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Name",IDC_STATIC,7,10,20,8 + EDITTEXT IDC_NAME,32,7,106,14,ES_AUTOHSCROLL +END + +IDD_WING_CREATE DIALOG DISCARDABLE 0, 0, 119, 68 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Create Wing" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_NAME,7,27,105,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,7,47,50,14 + PUSHBUTTON "Cancel",IDCANCEL,62,47,50,14 + LTEXT "Please specify a name for this\nnew wing",IDC_STATIC,7, + 7,99,18 +END + +IDD_INITIAL_STATUS DIALOG DISCARDABLE 0, 0, 167, 226 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Initial Status Editor" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_VELOCITY,37,7,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_VELOCITY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,78,7,9,14 + EDITTEXT IDC_HULL,57,24,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_HULL_SPIN,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,98,24,11, + 14 + CONTROL "Has Shield System",IDC_HAS_SHIELDS,"Button",BS_3STATE | + WS_TABSTOP,7,42,75,10 + CONTROL "Locked",IDC_LOCKED,"Button",BS_3STATE | WS_TABSTOP,95, + 42,40,10 + EDITTEXT IDC_SHIELDS,57,54,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin3",IDC_SHIELDS_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,99,55,9,14 + COMBOBOX IDC_DOCKED,57,71,80,106,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + COMBOBOX IDC_DOCKER_POINT,57,87,80,118,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_DOCKEE_POINT,57,103,80,102,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LISTBOX IDC_SUBSYS,16,135,68,60,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_DAMAGE,90,145,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_DAMAGE_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,118,167,9,14 + DEFPUSHBUTTON "OK",IDOK,110,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,110,24,50,14 + LTEXT "Velocity",IDC_STATIC,7,10,26,8 + LTEXT "Shield Integrity",IDC_STATIC,7,57,47,8 + LTEXT "Docked with",IDC_STATIC,7,73,41,8 + GROUPBOX "Subsystems",IDC_STATIC,7,121,132,99 + LTEXT "Integrity",IDC_STATIC,90,135,28,8 + LTEXT "Hull Integrity",IDC_STATIC,7,27,40,8 + LTEXT "Docker point",IDC_STATIC,7,89,42,8 + LTEXT "Dockee point",IDC_STATIC,7,105,44,8 + EDITTEXT IDC_CARGO_NAME,39,201,86,12,ES_AUTOHSCROLL + LTEXT "Cargo:",IDC_STATIC,15,203,22,8 +END + +IDD_ASTEROID_EDITOR DIALOG DISCARDABLE 0, 0, 376, 210 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Debris Field Editor" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Enabled",IDC_ENABLE_ASTEROIDS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,7,42,10 + EDITTEXT IDC_DENSITY,60,162,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin1",IDC_DENSITY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,79,162,11,14 + EDITTEXT IDC_AVG_SPEED,60,178,40,14,ES_AUTOHSCROLL | ES_NUMBER + EDITTEXT IDC_MIN_X,230,50,40,14,ES_AUTOHSCROLL + EDITTEXT IDC_MAX_X,230,66,40,14,ES_AUTOHSCROLL + EDITTEXT IDC_MIN_Y,230,82,40,14,ES_AUTOHSCROLL + EDITTEXT IDC_MAX_Y,230,98,40,14,ES_AUTOHSCROLL + EDITTEXT IDC_MIN_Z,230,114,40,14,ES_AUTOHSCROLL + EDITTEXT IDC_MAX_Z,230,129,40,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,319,172,50,14 + PUSHBUTTON "Cancel",IDCANCEL,319,189,50,14 + LTEXT "Min X:",IDC_STATIC,204,52,21,8 + LTEXT "Max X:",IDC_STATIC,204,68,23,8 + LTEXT "Min Y:",IDC_STATIC,204,84,21,8 + LTEXT "Max Y:",IDC_STATIC,204,100,23,8 + LTEXT "Min Z:",IDC_STATIC,204,116,21,8 + LTEXT "Max Z:",IDC_STATIC,204,132,23,8 + LTEXT "Number:",IDC_STATIC,13,166,28,8 + LTEXT "Avg. Speed:",IDC_STATIC,13,182,40,8 + CONTROL "Active Field",IDC_ACTIVE_FIELD,"Button", + BS_AUTORADIOBUTTON,33,35,53,10 + CONTROL "Passive Field",IDC_PASSIVE_FIELD,"Button", + BS_AUTORADIOBUTTON,33,47,57,10 + EDITTEXT IDC_INNER_MIN_X,320,50,40,14,ES_AUTOHSCROLL + EDITTEXT IDC_INNER_MAX_X,320,66,40,14,ES_AUTOHSCROLL + EDITTEXT IDC_INNER_MIN_Y,320,82,40,14,ES_AUTOHSCROLL + EDITTEXT IDC_INNER_MAX_Y,320,98,40,14,ES_AUTOHSCROLL + EDITTEXT IDC_INNER_MIN_Z,320,114,40,14,ES_AUTOHSCROLL + EDITTEXT IDC_INNER_MAX_Z,320,129,40,14,ES_AUTOHSCROLL + LTEXT "Min X:",IDC_STATIC,294,52,21,8 + LTEXT "Max X:",IDC_STATIC,294,68,23,8 + LTEXT "Min Y:",IDC_STATIC,294,84,21,8 + LTEXT "Max Y:",IDC_STATIC,294,100,23,8 + LTEXT "Min Z:",IDC_STATIC,294,116,21,8 + LTEXT "Max Z:",IDC_STATIC,294,132,23,8 + CONTROL "Enable",IDC_ENABLE_INNER_BOX,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,292,36,38,10 + GROUPBOX "Outer Box",IDC_STATIC,192,20,86,148 + GROUPBOX "Inner Box",IDC_STATIC,288,20,79,148 + CONTROL "Ship",IDC_FIELD_SHIP,"Button",BS_AUTORADIOBUTTON,55,85, + 30,10 + CONTROL "Asteroid",IDC_FIELD_ASTEROID,"Button", + BS_AUTORADIOBUTTON,55,74,41,10 + GROUPBOX "Field Properties",IDC_STATIC,7,20,175,176 + GROUPBOX "",IDC_STATIC,28,28,71,30 + COMBOBOX IDC_SHIP_DEBRIS1,92,98,74,95,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SHIP_DEBRIS2,92,113,74,95,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SHIP_DEBRIS3,92,128,74,95,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Brown",IDC_SUBTYPE1,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,23,100,36,10 + CONTROL "Blue",IDC_SUBTYPE2,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,23,115,30,10 + CONTROL "Orange",IDC_SUBTYPE3,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,23,130,39,10 + GROUPBOX "",IDC_STATIC,16,61,159,94 +END + +IDD_CAMPAIGN DIALOGEX 0, 0, 231, 528 +STYLE WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + LISTBOX IDC_FILELIST,7,17,217,77,LBS_SORT | LBS_NOINTEGRALHEIGHT | + LBS_MULTICOLUMN | WS_HSCROLL | WS_TABSTOP + EDITTEXT IDC_NAME,63,98,66,14,ES_AUTOHSCROLL + COMBOBOX IDC_CAMPAIGN_TYPE,159,98,65,116,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_BRIEFING_CUTSCENE,67,187,66,14,ES_AUTOHSCROLL + CONTROL "Tree1",IDC_SEXP_TREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | + TVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,7,218,158,103, + WS_EX_CLIENTEDGE + PUSHBUTTON "Move Up",IDC_MOVE_UP,172,211,50,14,BS_CENTER, + WS_EX_STATICEDGE + PUSHBUTTON "Move Down",IDC_MOVE_DOWN,172,228,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "Realign Tree",IDC_ALIGN,174,268,50,14 + PUSHBUTTON "Load Mission",ID_LOAD,174,287,50,14 + PUSHBUTTON "Close",ID_CPGN_CLOSE,174,306,50,14 + EDITTEXT IDC_HELP_BOX,7,327,217,73,ES_MULTILINE | ES_READONLY, + WS_EX_TRANSPARENT + LTEXT "Available missions",IDC_STATIC,7,7,58,8 + LTEXT "Campaign name",IDC_STATIC,7,101,52,8 + LTEXT "Type",IDC_STATIC,139,100,17,8 + LTEXT "Branches",IDC_STATIC,7,208,31,8 + LTEXT "Briefing Cutscene",IDC_STATIC,7,190,56,8 + LTEXT "Number of Players:",IDC_PLAYERS_LABEL,139,190,60,8 + LTEXT "Static",IDC_NUM_PLAYERS,201,189,23,8 + CONTROL "Galatea",IDC_GALATEA,"Button",BS_AUTORADIOBUTTON,67,206, + 41,10 + CONTROL "Bastion",IDC_BASTION,"Button",BS_AUTORADIOBUTTON,118, + 206,39,10 + LTEXT "Campaign Description",IDC_STATIC,7,118,70,8 + EDITTEXT IDC_MISSISON_LOOP_DESC,7,422,217,55,ES_MULTILINE | + ES_AUTOVSCROLL + PUSHBUTTON "Toggle Loop",IDC_TOGGLE_LOOP,172,245,50,14,0, + WS_EX_STATICEDGE + GROUPBOX "Branch Options",IDC_STATIC,168,200,58,63 + EDITTEXT IDC_DESC2,7,128,217,55,ES_MULTILINE | ES_AUTOVSCROLL + LTEXT "Mission Loop Description",IDC_STATIC,7,409,80,8 + EDITTEXT IDC_LOOP_BRIEF_ANIM,69,481,100,14,ES_AUTOHSCROLL + LTEXT "Loop brief anim",IDC_STATIC,15,483,49,8 + PUSHBUTTON "Browse",IDC_LOOP_BRIEF_BROWSE,174,481,50,14,0, + WS_EX_STATICEDGE + EDITTEXT IDC_LOOP_BRIEF_SOUND,69,500,100,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse",IDC_LOOP_BRIEF_SOUND_BROWSE,174,501,50,14,0, + WS_EX_STATICEDGE + LTEXT "Loop brief voice",IDC_STATIC,13,501,52,8 +END + +IDD_TEXT_VIEW DIALOG DISCARDABLE 0, 0, 324, 285 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ship TBL Data" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_EDIT1,7,7,310,271,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + +IDD_BRIEFING_EDITOR DIALOGEX 0, 0, 258, 393 +STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Briefing Editor" +MENU IDR_PLAYER_EDIT_MENU +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + PUSHBUTTON "Add Stage",IDC_ADD_STAGE,201,7,50,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Insert Stage",IDC_INSERT_STAGE,201,23,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "Delete Stage",IDC_DELETE_STAGE,201,39,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "Prev",IDC_PREV,64,7,25,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Next",IDC_NEXT,92,7,25,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Save View",IDC_SAVE_VIEW,144,7,50,14,0,WS_EX_STATICEDGE + EDITTEXT IDC_TIME,101,23,31,14,ES_AUTOHSCROLL | ES_NUMBER + PUSHBUTTON "Goto View",IDC_GOTO_VIEW,144,23,50,14,0, + WS_EX_STATICEDGE + EDITTEXT IDC_VOICE,65,39,68,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse",IDC_BROWSE,136,39,42,14,0,WS_EX_STATICEDGE + COMBOBOX IDC_BRIEFING_MUSIC,66,55,75,135,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + CONTROL "Cut to next stage",IDC_CUT_NEXT,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,168,78,69,10 + CONTROL "Cut to previous stage",IDC_CUT_PREV,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,168,87,83,10 + EDITTEXT IDC_TEXT,7,100,244,40,ES_MULTILINE | ES_AUTOVSCROLL | + ES_WANTRETURN + PUSHBUTTON "Make Icon",IDC_MAKE_ICON,195,256,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "Delete Icon",IDC_DELETE_ICON,195,276,50,14,0, + WS_EX_STATICEDGE + EDITTEXT IDC_ICON_LABEL,51,261,75,14,ES_AUTOHSCROLL + COMBOBOX IDC_ICON_IMAGE,52,277,75,135,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_SHIP_TYPE,52,293,75,145,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_TEAM,52,309,75,84,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Highlight",IDC_HILIGHT,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,139,264,43,10 + EDITTEXT IDC_ID,151,277,34,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Change locally",IDC_LOCAL,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,139,312,62,10 + PUSHBUTTON "Propagate",IDC_PROPAGATE_ICONS,195,293,50,14,0, + WS_EX_STATICEDGE + EDITTEXT IDC_ICON_TEXT,14,336,230,40,ES_MULTILINE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "Stage 1 of 9",IDC_STAGE_TITLE,7,10,53,8 + GROUPBOX "Current Icon Info",IDC_STATIC,7,248,244,136 + LTEXT "Label",IDC_STATIC,13,264,18,8 + LTEXT "Icon image",IDC_STATIC,13,280,36,8 + LTEXT "Text",IDC_STATIC,7,91,15,8 + LTEXT "Camera Transition Time (ms)",IDC_STATIC,7,28,90,8 + LTEXT "Voice Wave File",IDC_STATIC,7,41,53,8 + LTEXT "Text",IDC_STATIC,14,325,15,8 + LTEXT "Team",IDC_STATIC,13,312,19,8 + LTEXT "Ship Type",IDC_STATIC,13,296,33,8 + LTEXT "ID",IDC_STATIC,139,280,8,8 + LTEXT "Briefing Music",IDC_STATIC,7,58,45,8 + CONTROL "Draw lines between marked icons",IDC_LINES,"Button", + BS_3STATE | WS_TABSTOP,129,239,122,10 + CONTROL "Tree1",IDC_TREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | + WS_BORDER | WS_TABSTOP,7,153,244,84,WS_EX_CLIENTEDGE + LTEXT "Usage Formula",IDC_STATIC,7,143,48,8 + PUSHBUTTON "IDB_PLAY",IDC_PLAY,181,39,13,14,BS_BITMAP, + WS_EX_TRANSPARENT | WS_EX_STATICEDGE + PUSHBUTTON "Copy View",IDC_COPY_VIEW,144,60,50,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "Paste View",IDC_PASTE_VIEW,201,60,50,14,0, + WS_EX_STATICEDGE +END + +IDD_IGNORE_ORDERS DIALOG DISCARDABLE 0, 0, 120, 217 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ignore Orders" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "order 1",IDC_CHECK1,"Button",BS_3STATE | WS_TABSTOP,7, + 47,105,10 + CONTROL "order 2",IDC_CHECK2,"Button",BS_3STATE | WS_TABSTOP,7, + 62,105,10 + CONTROL "order 3",IDC_CHECK3,"Button",BS_3STATE | WS_TABSTOP,7, + 77,105,10 + CONTROL "order 4",IDC_CHECK4,"Button",BS_3STATE | WS_TABSTOP,7, + 92,105,10 + CONTROL "order 5",IDC_CHECK5,"Button",BS_3STATE | WS_TABSTOP,7, + 107,105,10 + CONTROL "order 6",IDC_CHECK6,"Button",BS_3STATE | WS_TABSTOP,7, + 122,105,10 + CONTROL "order 7",IDC_CHECK7,"Button",BS_3STATE | WS_TABSTOP,7, + 137,105,10 + CONTROL "order 8",IDC_CHECK8,"Button",BS_3STATE | WS_TABSTOP,7, + 152,105,10 + CONTROL "order 9",IDC_CHECK9,"Button",BS_3STATE | WS_TABSTOP,7, + 167,105,10 + DEFPUSHBUTTON "OK",IDOK,62,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,62,24,50,14 + CONTROL "order 10",IDC_CHECK10,"Button",BS_3STATE | WS_TABSTOP,7, + 183,105,10 +END + +IDD_DEBRIEFING_EDITOR DIALOGEX 0, 0, 322, 172 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Debriefing Editor" +MENU IDR_PLAYER_EDIT_MENU +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + PUSHBUTTON "Add",IDC_ADD_STAGE,7,24,38,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Insert",IDC_INSERT_STAGE,48,24,38,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Delete",IDC_DELETE_STAGE,89,24,38,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Prev",IDC_PREV,73,7,25,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Next",IDC_NEXT,101,7,25,14,0,WS_EX_STATICEDGE + EDITTEXT IDC_VOICE,193,7,68,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse",IDC_BROWSE,265,7,34,14,0,WS_EX_STATICEDGE + CONTROL "Tree1",IDC_TREE,"SysTreeView32",TVS_HASBUTTONS | + TVS_HASLINES | TVS_LINESATROOT | TVS_EDITLABELS | + WS_BORDER | WS_TABSTOP,7,55,122,110,WS_EX_CLIENTEDGE + EDITTEXT IDC_TEXT,135,34,179,57,ES_MULTILINE | ES_AUTOVSCROLL | + ES_WANTRETURN | WS_VSCROLL + EDITTEXT IDC_REC_TEXT,136,108,179,57,ES_MULTILINE | + ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "Text",IDC_STATIC,135,24,15,8 + LTEXT "Voice Wave File",IDC_STATIC,135,10,53,8 + LTEXT "Stage 1 of 9",IDC_STAGE_TITLE,7,10,53,8 + LTEXT "Usage Formula",IDC_STATIC,7,43,48,8 + LTEXT "Recommendation Text",IDC_STATIC,135,96,72,8 + PUSHBUTTON "IDB_PLAY",IDC_PLAY,302,7,13,14,BS_BITMAP, + WS_EX_TRANSPARENT | WS_EX_STATICEDGE +END + +IDD_ADJUST_GRID DIALOG DISCARDABLE 0, 0, 145, 93 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Adjust Grid" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "X-Z Plane",IDC_XZ_PLANE,"Button",BS_AUTORADIOBUTTON | + BS_PUSHLIKE,7,18,47,13 + CONTROL "X-Y Plane",IDC_XY_PLANE,"Button",BS_AUTORADIOBUTTON | + BS_PUSHLIKE,7,33,47,13 + CONTROL "Y-Z Plane",IDC_YZ_PLANE,"Button",BS_AUTORADIOBUTTON | + BS_PUSHLIKE,7,48,47,13 + EDITTEXT IDC_EDIT_Y,98,17,40,14,ES_AUTOHSCROLL + CONTROL "Spin1",IDC_SPIN_Y,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,58,72,10, + 14 + EDITTEXT IDC_EDIT_Z,98,32,40,14,ES_AUTOHSCROLL + CONTROL "Spin3",IDC_SPIN_Z,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,76,72,10, + 14 + EDITTEXT IDC_EDIT_X,98,47,40,14,ES_AUTOHSCROLL + CONTROL "Spin2",IDC_SPIN_X,"msctls_updown32",UDS_SETBUDDYINT | + UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,67,72,10, + 14 + DEFPUSHBUTTON "OK",IDOK,7,72,50,14 + PUSHBUTTON "Cancel",IDCANCEL,88,72,50,14 + LTEXT "Type:",IDC_STATIC,7,7,19,8 + LTEXT "Y Level",IDC_STATIC,69,20,25,8 + LTEXT "Z Level",IDC_STATIC,69,35,25,8 + LTEXT "X Level",IDC_STATIC,69,50,25,8 +END + +IDD_SHIELD_SYS DIALOG DISCARDABLE 0, 0, 155, 130 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Shield System Editor" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_TYPE,13,21,72,102,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Has shield system",IDC_TYPE_YES,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,13,38,72,10 + CONTROL "No shield system",IDC_TYPE_NO,"Button", + BS_AUTORADIOBUTTON,13,48,69,10 + COMBOBOX IDC_TEAM,13,82,72,132,CBS_DROPDOWNLIST | WS_VSCROLL | + WS_TABSTOP + CONTROL "Has shield system",IDC_TEAM_YES,"Button", + BS_AUTORADIOBUTTON | WS_GROUP,13,100,72,10 + CONTROL "No shield system",IDC_TEAM_NO,"Button", + BS_AUTORADIOBUTTON,13,110,69,10 + GROUPBOX "All ships of type",IDC_STATIC,7,7,85,55 + GROUPBOX "All ships on team",IDC_STATIC,7,68,85,55 + DEFPUSHBUTTON "OK",IDOK,98,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,98,24,50,14 +END + +IDD_INITIAL_SHIPS DIALOG DISCARDABLE 0, 0, 127, 210 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Initial Ships Allowed" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_INITIAL_LIST,10,7,107,173,LBS_OWNERDRAWFIXED | + LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | + WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",IDOK,7,189,50,14 + PUSHBUTTON "Cancel",IDCANCEL,70,189,50,14 +END + +IDD_CMD_BRIEF DIALOGEX 0, 0, 189, 158 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Command Briefing Editor" +MENU IDR_PLAYER_EDIT_MENU +FONT 8, "MS Sans Serif", 0, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,132,14,50,14 + PUSHBUTTON "Cancel",IDCANCEL,132,31,50,14 + LTEXT "Briefing Text",IDC_STATIC,7,44,40,8 + LTEXT "Ani Filename",IDC_STATIC,7,121,41,8 + EDITTEXT IDC_ANI_FILENAME,61,118,68,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse",IDC_BROWSE_ANI,132,118,50,14,0,WS_EX_STATICEDGE + EDITTEXT IDC_TEXT,7,55,175,59,ES_MULTILINE | ES_AUTOVSCROLL | + ES_WANTRETURN | WS_VSCROLL + PUSHBUTTON "Add",IDC_ADD_STAGE,7,24,38,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Insert",IDC_INSERT_STAGE,48,24,38,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Delete",IDC_DELETE_STAGE,89,24,38,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Prev",IDC_PREV,73,7,25,14,0,WS_EX_STATICEDGE + PUSHBUTTON "Next",IDC_NEXT,101,7,25,14,0,WS_EX_STATICEDGE + LTEXT "Stage 1 of 9",IDC_STAGE_TITLE,7,10,53,8 + LTEXT "Wave Filename",IDC_STATIC,7,139,50,8 + EDITTEXT IDC_WAVE_FILENAME,61,136,68,14,ES_AUTOHSCROLL + PUSHBUTTON "Browse",IDC_BROWSE_WAVE,132,136,34,14,0, + WS_EX_STATICEDGE + PUSHBUTTON "IDB_PLAY",IDC_PLAY,169,136,13,14,BS_BITMAP, + WS_EX_TRANSPARENT | WS_EX_STATICEDGE +END + +IDD_SHIP_FLAGS DIALOG DISCARDABLE 0, 0, 120, 250 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Additional Ship Properties" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,7,229,50,14 + PUSHBUTTON "Cancel",IDCANCEL,63,229,50,14 + CONTROL "Destroy before Mission",IDC_DESTROY_CHECK,"Button", + BS_3STATE | WS_TABSTOP,7,7,87,10 + EDITTEXT IDC_DESTROY_VALUE,19,17,40,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Spin2",IDC_DESTROY_SPIN,"msctls_updown32", + UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | + UDS_ARROWKEYS,7,17,11,14 + CONTROL "Cargo Known",IDC_CARGO_KNOWN,"Button",BS_3STATE | + WS_TABSTOP,7,44,59,10 + CONTROL "Reinforcement Unit",IDC_REINFORCEMENT,"Button", + BS_3STATE | WS_TABSTOP,7,54,75,10 + CONTROL "Protect Ship",IDC_PROTECT_SHIP,"Button",BS_3STATE | + WS_TABSTOP,7,64,59,10 + CONTROL "Ignore for Counting Goals",IDC_IGNORE_COUNT,"Button", + BS_3STATE | WS_TABSTOP,7,82,93,10 + CONTROL "Escort Ship",IDC_ESCORT,"Button",BS_3STATE | WS_TABSTOP, + 7,92,52,10 + CONTROL "No Arrival Music",IDC_NO_ARRIVAL_MUSIC,"Button", + BS_3STATE | WS_TABSTOP,7,121,67,10 + LTEXT "Seconds",IDC_STATIC,63,20,29,8 + CONTROL "Invulnerable",IDC_INVULNERABLE,"Button",BS_3STATE | + WS_TABSTOP,7,130,55,10 + CONTROL "Hidden from Sensors",IDC_HIDDEN_FROM_SENSORS,"Button", + BS_3STATE | WS_TABSTOP,7,140,81,10 + CONTROL "Scannable",IDC_SCANNABLE,"Button",BS_3STATE | + WS_TABSTOP,7,34,50,10 + CONTROL "Kamikaze",IDC_KAMIKAZE,"Button",BS_3STATE | WS_TABSTOP, + 7,150,47,10 + CONTROL "No Dynamic Goals",IDC_NO_DYNAMIC,"Button",BS_3STATE | + WS_TABSTOP,7,175,75,10 + EDITTEXT IDC_KDAMAGE,51,159,57,14,ES_AUTOHSCROLL + LTEXT "Damage",IDC_STATIC,19,161,28,8 + CONTROL "Red Alert Carry Status",IDC_REDALERTCARRY,"Button", + BS_3STATE | WS_TABSTOP,7,186,85,10 + LTEXT "Priority",IDC_STATIC,19,108,22,8 + EDITTEXT IDC_ESCORT_PRIORITY,43,105,26,12,ES_AUTOHSCROLL + EDITTEXT IDC_RESPAWN_PRIORITY,64,211,49,14,ES_AUTOHSCROLL + LTEXT "Respawn Priority",IDC_STATIC,7,214,54,8 + CONTROL "Beam Protect Ship",IDC_BEAM_PROTECT_SHIP,"Button", + BS_3STATE | WS_TABSTOP,7,74,80,8 + CONTROL "Special Warp",IDC_SPECIAL_WARP,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,7,197,59,10 +END + +IDD_ADD_VARIABLE DIALOG DISCARDABLE 0, 0, 374, 49 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Add Variable" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_ADD_VARIABLE_NAME,83,21,98,12,ES_AUTOHSCROLL + EDITTEXT IDC_ADD_VARIABLE_DEFAULT_VALUE,199,21,98,12, + ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,317,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,317,24,50,14 + LTEXT "Variable Name",IDC_STATIC,83,7,47,8 + LTEXT "Default Value",IDC_STATIC,199,7,44,8 + CONTROL "String",IDC_TYPE_STRING,"Button",BS_AUTORADIOBUTTON,15, + 15,34,10 + CONTROL "Number",IDC_TYPE_NUMBER,"Button",BS_AUTORADIOBUTTON,15, + 25,41,10 + GROUPBOX "Variable Type",IDC_STATIC,7,7,58,34 +END + +IDD_MODIFY_VARIABLE DIALOG DISCARDABLE 0, 0, 422, 103 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Modify Variable" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_MODIFY_DEFAULT_VALUE,251,24,98,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,365,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,365,24,50,14 + COMBOBOX IDC_MODIFY_VARIABLE_NAME,137,24,98,66,CBS_DROPDOWN | + WS_VSCROLL | WS_TABSTOP + CONTROL "String",IDC_TYPE_STRING,"Button",BS_AUTORADIOBUTTON,21, + 19,34,10 + CONTROL "Number",IDC_TYPE_NUMBER,"Button",BS_AUTORADIOBUTTON,21, + 32,41,10 + GROUPBOX "Modify Variable Type",IDC_STATIC,7,7,114,38 + PUSHBUTTON "Delete",ID_DELETE_VARIABLE,37,72,50,14 + LTEXT "Variable Name",IDC_STATIC,137,15,47,8 + LTEXT "Default Value",IDC_STATIC,251,15,44,8 + GROUPBOX "Delete Variable and References",IDC_STATIC,7,56,114,38 +END + +IDD_SPECIAL_DAMAGE DIALOG DISCARDABLE 0, 0, 234, 103 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Special Explosion" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,177,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,177,24,50,14 + CONTROL "Enable Special Explosion",IDC_ENABLE_SPECIAL_EXP,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,7,95,10 + EDITTEXT IDC_SPECIAL_DAMAGE,91,19,62,12,ES_AUTOHSCROLL + EDITTEXT IDC_SPECIAL_BLAST,91,33,62,12,ES_AUTOHSCROLL + EDITTEXT IDC_SPECIAL_INNER_RAD,91,47,62,12,ES_AUTOHSCROLL + EDITTEXT IDC_SPECIAL_OUTER_RAD,91,61,62,12,ES_AUTOHSCROLL + CONTROL "",IDC_ENABLE_SHOCKWAVE,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,91,75,16,8 + EDITTEXT IDC_SPECIAL_SHOCK_SPEED,91,85,62,12,ES_AUTOHSCROLL + LTEXT "Inner Damage Radius:",IDC_STATIC,15,49,72,8 + LTEXT "Outer Damage Radius:",IDC_STATIC,14,62,73,8 + LTEXT "Damage:",IDC_STATIC,57,23,30,8 + LTEXT "Create Shockwave:",IDC_STATIC,23,75,64,8 + LTEXT "Shockwave Speed:",IDC_STATIC,23,88,64,8 + LTEXT "Blast:",IDC_STATIC,69,36,18,8 +END + +IDD_DUMP_STATS DIALOG DISCARDABLE 0, 0, 577, 375 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Mission Stats" +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Dump to File",IDC_DUMP_TO_FILE,209,349,76,19 + PUSHBUTTON "Cancel",IDCANCEL,292,348,76,19 + EDITTEXT IDC_STATS_TEXT,7,7,563,333,ES_MULTILINE | ES_AUTOHSCROLL | + ES_READONLY | ES_WANTRETURN | ES_NUMBER | WS_VSCROLL +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Volition Inc.\0" + VALUE "FileDescription", "Fred\0" + VALUE "FileVersion", "1.03\0" + VALUE "InternalName", "FRED\0" + VALUE "LegalCopyright", "Copyright © 1998\0" + VALUE "OriginalFilename", "FRED.EXE\0" + VALUE "ProductName", "FreeSpace\0" + VALUE "ProductVersion", "1.03\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 259 + TOPMARGIN, 7 + BOTTOMMARGIN, 63 + END + + IDD_SHIP_EDITBAR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 286 + TOPMARGIN, 8 + BOTTOMMARGIN, 45 + END + + IDD_MESSAGE_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 191 + TOPMARGIN, 8 + BOTTOMMARGIN, 359 + END + + IDD_SHIP_GOALS_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 405 + TOPMARGIN, 8 + BOTTOMMARGIN, 198 + END + + IDD_MISSION_NOTES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 314 + TOPMARGIN, 7 + BOTTOMMARGIN, 259 + END + + IDD_PREFERENCES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 326 + TOPMARGIN, 7 + BOTTOMMARGIN, 104 + END + + IDD_SHIP_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 314 + TOPMARGIN, 7 + BOTTOMMARGIN, 411 + END + + IDD_WEAPON_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 438 + TOPMARGIN, 8 + BOTTOMMARGIN, 72 + END + + IDD_SHIP_MARKINGS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 245 + TOPMARGIN, 8 + BOTTOMMARGIN, 133 + END + + IDD_MISSION_GOALS, DIALOG + BEGIN + LEFTMARGIN, 6 + RIGHTMARGIN, 292 + TOPMARGIN, 7 + BOTTOMMARGIN, 263 + END + + IDD_SHIP_SELECT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 206 + TOPMARGIN, 7 + BOTTOMMARGIN, 200 + END + + IDD_WING_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 290 + TOPMARGIN, 7 + BOTTOMMARGIN, 410 + END + + IDD_OPERATOR_ARGUMENT_TYPES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 43 + END + + IDD_LOADOUT_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 197 + TOPMARGIN, 7 + BOTTOMMARGIN, 192 + END + + IDD_ORIENT_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 202 + TOPMARGIN, 7 + BOTTOMMARGIN, 112 + END + + IDD_EVENT_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 430 + TOPMARGIN, 7 + BOTTOMMARGIN, 376 + END + + IDD_BG_BITMAP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 415 + TOPMARGIN, 7 + BOTTOMMARGIN, 291 + END + + IDD_REINFORCEMENT_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 171 + TOPMARGIN, 7 + BOTTOMMARGIN, 112 + END + + IDD_REINFORCEMENT_SELECT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 149 + TOPMARGIN, 7 + BOTTOMMARGIN, 125 + END + + IDD_WAYPOINT_PATH_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 138 + TOPMARGIN, 7 + BOTTOMMARGIN, 22 + END + + IDD_WING_CREATE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 112 + TOPMARGIN, 7 + BOTTOMMARGIN, 61 + END + + IDD_INITIAL_STATUS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 160 + TOPMARGIN, 7 + BOTTOMMARGIN, 219 + END + + IDD_ASTEROID_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 369 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END + + IDD_CAMPAIGN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 224 + TOPMARGIN, 7 + BOTTOMMARGIN, 521 + END + + IDD_TEXT_VIEW, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 317 + TOPMARGIN, 7 + BOTTOMMARGIN, 278 + END + + IDD_BRIEFING_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 251 + TOPMARGIN, 7 + BOTTOMMARGIN, 386 + END + + IDD_IGNORE_ORDERS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 112 + TOPMARGIN, 7 + BOTTOMMARGIN, 210 + END + + IDD_DEBRIEFING_EDITOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 315 + TOPMARGIN, 7 + BOTTOMMARGIN, 165 + END + + IDD_ADJUST_GRID, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 138 + TOPMARGIN, 7 + BOTTOMMARGIN, 86 + END + + IDD_SHIELD_SYS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 148 + TOPMARGIN, 7 + BOTTOMMARGIN, 123 + END + + IDD_INITIAL_SHIPS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 120 + TOPMARGIN, 7 + BOTTOMMARGIN, 203 + END + + IDD_CMD_BRIEF, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 182 + TOPMARGIN, 7 + BOTTOMMARGIN, 151 + END + + IDD_SHIP_FLAGS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 113 + TOPMARGIN, 7 + BOTTOMMARGIN, 243 + END + + IDD_ADD_VARIABLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 367 + TOPMARGIN, 7 + BOTTOMMARGIN, 42 + END + + IDD_MODIFY_VARIABLE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 415 + TOPMARGIN, 7 + BOTTOMMARGIN, 96 + END + + IDD_SPECIAL_DAMAGE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 227 + TOPMARGIN, 7 + BOTTOMMARGIN, 96 + END + + IDD_DUMP_STATS, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 570 + TOPMARGIN, 7 + BOTTOMMARGIN, 368 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog Info +// + +IDD_SHIP_EDITBAR DLGINIT +BEGIN + IDC_SHIP_CLASS, 0x403, 6, 0 +0x6977, 0x676e, 0x0031, + IDC_SHIP_WING, 0x403, 6, 0 +0x6977, 0x676e, 0x0031, + IDC_SHIP_IFF, 0x403, 6, 0 +0x6977, 0x676e, 0x0031, + IDC_SHIP_AI_CLASS, 0x403, 6, 0 +0x6977, 0x676e, 0x0031, + IDC_SHIP_STATUS, 0x403, 6, 0 +0x6977, 0x676e, 0x0031, + 0 +END + +IDD_SHIP_EDITOR DLGINIT +BEGIN + IDC_SHIP_CLASS, 0x403, 1, 0 +"\000" + IDC_SHIP_TEAM, 0x403, 8, 0 +0x6f48, 0x7473, 0x6c69, 0x0065, + IDC_SHIP_TEAM, 0x403, 9, 0 +0x7246, 0x6569, 0x646e, 0x796c, "\000" + IDC_SHIP_TEAM, 0x403, 8, 0 +0x654e, 0x7475, 0x6172, 0x006c, + IDC_SHIP_TEAM, 0x403, 8, 0 +0x6e55, 0x6e6b, 0x776f, 0x006e, + IDC_HOTKEY, 0x403, 5, 0 +0x6f4e, 0x656e, "\000" + IDC_HOTKEY, 0x403, 11, 0 +0x6553, 0x2074, 0x2031, 0x4628, 0x2935, "\000" + IDC_HOTKEY, 0x403, 11, 0 +0x6553, 0x2074, 0x2032, 0x4628, 0x2936, "\000" + IDC_HOTKEY, 0x403, 11, 0 +0x6553, 0x2074, 0x2033, 0x4628, 0x2937, "\000" + IDC_HOTKEY, 0x403, 11, 0 +0x6553, 0x2074, 0x2034, 0x4628, 0x2938, "\000" + IDC_HOTKEY, 0x403, 11, 0 +0x6553, 0x2074, 0x2035, 0x4628, 0x2939, "\000" + IDC_HOTKEY, 0x403, 12, 0 +0x6553, 0x2074, 0x2036, 0x4628, 0x3031, 0x0029, + IDC_HOTKEY, 0x403, 12, 0 +0x6553, 0x2074, 0x2037, 0x4628, 0x3131, 0x0029, + IDC_HOTKEY, 0x403, 12, 0 +0x6553, 0x2074, 0x2038, 0x4628, 0x3231, 0x0029, + IDC_HOTKEY, 0x403, 7, 0 +0x6948, 0x6464, 0x6e65, "\000" + 0 +END + +IDD_SHIP_MARKINGS DLGINIT +BEGIN + IDC_MODEL_NAME, 0x403, 6, 0 +0x6977, 0x676e, 0x0031, + 0 +END + +IDD_MISSION_GOALS DLGINIT +BEGIN + IDC_DISPLAY_GOAL_TYPES_DROP, 0x403, 14, 0 +0x7250, 0x6d69, 0x7261, 0x2079, 0x6f47, 0x6c61, 0x0073, + IDC_DISPLAY_GOAL_TYPES_DROP, 0x403, 16, 0 +0x6553, 0x6f63, 0x646e, 0x7261, 0x2079, 0x6f47, 0x6c61, 0x0073, + IDC_DISPLAY_GOAL_TYPES_DROP, 0x403, 12, 0 +0x6f42, 0x756e, 0x2073, 0x6f47, 0x6c61, 0x0073, + IDC_GOAL_TYPE_DROP, 0x403, 13, 0 +0x7250, 0x6d69, 0x7261, 0x2079, 0x6f47, 0x6c61, "\000" + IDC_GOAL_TYPE_DROP, 0x403, 15, 0 +0x6553, 0x6f63, 0x646e, 0x7261, 0x2079, 0x6f47, 0x6c61, "\000" + IDC_GOAL_TYPE_DROP, 0x403, 11, 0 +0x6f42, 0x756e, 0x2073, 0x6f47, 0x6c61, "\000" + IDC_OBJ_TEAM, 0x403, 7, 0 +0x6554, 0x6d61, 0x3120, "\000" + IDC_OBJ_TEAM, 0x403, 7, 0 +0x6554, 0x6d61, 0x3220, "\000" + 0 +END + +IDD_WING_EDITOR DLGINIT +BEGIN + IDC_WING_SPECIAL_SHIP, 0x403, 6, 0 +0x6977, 0x676e, 0x0031, + IDC_HOTKEY, 0x403, 5, 0 +0x6f4e, 0x656e, "\000" + IDC_HOTKEY, 0x403, 11, 0 +0x6553, 0x2074, 0x2031, 0x4628, 0x2935, "\000" + IDC_HOTKEY, 0x403, 11, 0 +0x6553, 0x2074, 0x2032, 0x4628, 0x2936, "\000" + IDC_HOTKEY, 0x403, 11, 0 +0x6553, 0x2074, 0x2033, 0x4628, 0x2937, "\000" + IDC_HOTKEY, 0x403, 11, 0 +0x6553, 0x2074, 0x2034, 0x4628, 0x2938, "\000" + IDC_HOTKEY, 0x403, 11, 0 +0x6553, 0x2074, 0x2035, 0x4628, 0x2939, "\000" + IDC_HOTKEY, 0x403, 12, 0 +0x6553, 0x2074, 0x2036, 0x4628, 0x3031, 0x0029, + IDC_HOTKEY, 0x403, 12, 0 +0x6553, 0x2074, 0x2037, 0x4628, 0x3131, 0x0029, + IDC_HOTKEY, 0x403, 12, 0 +0x6553, 0x2074, 0x2038, 0x4628, 0x3231, 0x0029, + IDC_HOTKEY, 0x403, 7, 0 +0x6948, 0x6464, 0x6e65, "\000" + 0 +END + +IDD_ORIENT_EDITOR DLGINIT +BEGIN + IDC_OBJECT_LIST, 0x403, 12, 0 +0x6c70, 0x6361, 0x6865, 0x6c6f, 0x6564, 0x0072, + 0 +END + +IDD_MESSAGE_EDITOR DLGINIT +BEGIN + IDC_PRIORITY, 0x403, 4, 0 +0x6f4c, 0x0077, + IDC_PRIORITY, 0x403, 7, 0 +0x6f4e, 0x6d72, 0x6c61, "\000" + IDC_PRIORITY, 0x403, 5, 0 +0x6948, 0x6867, "\000" + 0 +END + +IDD_MODIFY_VARIABLE DLGINIT +BEGIN + IDC_MODIFY_VARIABLE_NAME, 0x403, 1, 0 +"\000" + 0 +END + +IDD_EVENT_EDITOR DLGINIT +BEGIN + IDC_EVENT_TEAM, 0x403, 8, 0 +0x6554, 0x6d61, 0x3120, 0x0020, + IDC_EVENT_TEAM, 0x403, 7, 0 +0x6554, 0x6d61, 0x3220, "\000" + IDC_EVENT_TEAM, 0x403, 5, 0 +0x6f6e, 0x656e, "\000" + IDC_MESSAGE_TEAM, 0x403, 8, 0 +0x6554, 0x6d61, 0x3120, 0x0020, + IDC_MESSAGE_TEAM, 0x403, 7, 0 +0x6554, 0x6d61, 0x3220, "\000" + IDC_MESSAGE_TEAM, 0x403, 5, 0 +0x6f6e, 0x656e, "\000" + 0 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Cursor +// + +IDC_CURSOR1 CURSOR DISCARDABLE "res\\cursor1.cur" +IDC_CURSOR2 CURSOR DISCARDABLE "res\\cursor2.cur" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDR_MAINFRAME "FRED2 - FreeSpace2 Mission Editor\nUntitled\nFRED2\nFreeSpace2 Missions (*.fs2)\n.fs2\nFreeSpace2Mission\nFreeSpace2 Mission" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + AFX_IDS_APP_TITLE "FRED2" + AFX_IDS_IDLEMESSAGE "For Help, press F1" + AFX_IDS_HELPMODEMESSAGE "Select an object on which to get Help" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_NEW "Create a new campaign\nNew" + ID_FILE_OPEN "Open an existing campaign\nOpen" + ID_FILE_CLOSE "Close the active document\nClose" + ID_FILE_SAVE "Save the active campaign\nSave" + ID_FILE_SAVE_AS "Save the active campaign with a new name\nSave As" + ID_FILE_PAGE_SETUP "Change the printing options\nPage Setup" + ID_FILE_PRINT_SETUP "Change the printer and printing options\nPrint Setup" + ID_FILE_PRINT "Print the active document\nPrint" + ID_FILE_PRINT_PREVIEW "Display full pages\nPrint Preview" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_APP_ABOUT "Display program information, version number and copyright\nAbout" + ID_APP_EXIT "Exit the campaign editor\nExit" + ID_HELP_INDEX "Opens Help\nHelp Topics" + ID_HELP_FINDER "List Help topics\nHelp Topics" + ID_HELP_USING "Display instructions about how to use help\nHelp" + ID_CONTEXT_HELP "Display help for clicked on buttons, menus and windows\nHelp" + ID_HELP "Display help for current task or command\nHelp" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_MRU_FILE1 "Open this document" + ID_FILE_MRU_FILE2 "Open this document" + ID_FILE_MRU_FILE3 "Open this document" + ID_FILE_MRU_FILE4 "Open this document" + ID_FILE_MRU_FILE5 "Open this document" + ID_FILE_MRU_FILE6 "Open this document" + ID_FILE_MRU_FILE7 "Open this document" + ID_FILE_MRU_FILE8 "Open this document" + ID_FILE_MRU_FILE9 "Open this document" + ID_FILE_MRU_FILE10 "Open this document" + ID_FILE_MRU_FILE11 "Open this document" + ID_FILE_MRU_FILE12 "Open this document" + ID_FILE_MRU_FILE13 "Open this document" + ID_FILE_MRU_FILE14 "Open this document" + ID_FILE_MRU_FILE15 "Open this document" + ID_FILE_MRU_FILE16 "Open this document" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_NEXT_PANE "Switch to the next window pane\nNext Pane" + ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_WINDOW_SPLIT "Split the active window into panes\nSplit" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDIT_CLEAR "Erase the selection\nErase" + ID_EDIT_CLEAR_ALL "Erase everything\nErase All" + ID_EDIT_COPY "Select and Move objects\nSelect and Move" + ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" + ID_EDIT_FIND "Find the specified text\nFind" + ID_EDIT_PASTE "Insert Clipboard contents\nPaste" + ID_EDIT_REPEAT "Repeat the last action\nRepeat" + ID_EDIT_REPLACE "Replace specific text with different text\nReplace" + ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" + ID_EDIT_UNDO "Undo the last action\nUndo" + ID_EDIT_REDO "Redo the previously undone action\nRedo" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" + ID_VIEW_STATUS_BAR "Show or hide the status bar\nToggle StatusBar" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCSIZE "Change the window size" + AFX_IDS_SCMOVE "Change the window position" + AFX_IDS_SCMINIMIZE "Reduce the window to an icon" + AFX_IDS_SCMAXIMIZE "Enlarge the window to full size" + AFX_IDS_SCNEXTWINDOW "Switch to the next document window" + AFX_IDS_SCPREVWINDOW "Switch to the previous document window" + AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCRESTORE "Restore the window to normal size" + AFX_IDS_SCTASKLIST "Activate Task List" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_PREVIEW_CLOSE "Close print preview mode\nCancel Preview" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_MISSIONNOTES "Displays designer notes and mission properties\nMission Specs" + ID_SELECT "Select objects mode\nSelect (S)" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_VIEW_FIGHTERS "Show/Hide Fighter objects" + ID_VIEW_CAPITALSHIPS "Show/Hide Capital Ship objects" + ID_VIEW_PLANETS "Show/Hide Planet objects" + ID_VIEW_MISCOBJECTS "Show/Hide misc objects" + ID_FILE_PREFERENCES "Set FRED2 user preferences\nUser preferences" + ID_EDIT_DELETE "Delete selected object(s)\nDelete object(s)" + ID_EDIT_HOLD "Hold current state in buffer\nHold state" + ID_EDIT_FETCH "Fetch held state from buffer\nFetch state" + ID_EDITORS_OBJECTS "Activates Ship Editor screen\nShip Editor" + ID_EDITORS_AI_CLASSES "Activates AI Class editor screen\nAI Class" + ID_EDITORS_ART "Activates Art Editor screen\nArt Editor" + ID_EDITORS_MESSAGE "Activates Message Editor\nMessage Editor" + ID_EDITORS_MUSIC "Activates Music Editor screen\nMusic Editor" + ID_EDITORS_SHIPS "Open ship editor dialog window to edit ship" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDITORS_GOALS "Activates Mission Goal Editor\nMission Goal Editor" + ID_EDITORS_WAYPOINTS "Activates Waypoint Editor\nWaypoint Editor" + ID_EDITORS_SOUND "Activates Sound Editor\nSound Editor" + ID_EDITORS_TERRAIN "Activates Terrain Editor Screen\nTerrain Editor" + ID_EDIT_DUPLICATE "Duplicate selected object(s)\nDuplicate object(s)" + ID_MIKE_GRIDCONTROL "Gahooga!" + ID_PROPERTIES_ONE "This is Mike's prompt!" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_INDICATOR_EXT "EXT" + ID_INDICATOR_CAPS "CAP" + ID_INDICATOR_NUM "NUM" + ID_INDICATOR_SCRL "SCRL" + ID_INDICATOR_OVR "OVR" + ID_INDICATOR_REC "REC" + ID_INDICATOR_MODE "MODE" + ID_INDICATOR_LEFT "LEFT" + ID_INDICATOR_RIGHT "RIGHT" + ID_INDICATOR_MODIFIED "MODIFIED" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_MISCSTUFF_SHOWSHIPSASICONS "This is the (help?) prompt!" + ID_EDIT_POPUP_SHOW_SHIP_MODELS + "Show actual polygon models for visible ships" + ID_EDIT_POPUP_SHOW_SHIP_ICONS + "Display ship names and ship types for visible ships" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_ADD_MISSION_TIME "Evaluates to elapsed time into mission in seconds" + ID_ADD_EQUALS "Evaluates to true if numbers or objects are the same" + ID_ADD_GREATER_THAN "Evaluates to true if first value is larger than second" + ID_ADD_AND "Evaluates to true only if all subexpressions are true" + ID_ADD_OR "Evaluates to true if any subexpression is true." + ID_ADD_MULTIPLY "Evaluates to product of subexpressions" + ID_ADD_DIVIDE "Evaluates to division of first subexpressions by remaining subexpressions" + ID_ADD_MOD "Evaluates to remainder of division of first subexpression by remaining subexpressions" + ID_EDIT_TEXT "Change this item's text data" + ID_ADD_TRUE "Evaluates to a boolean value of true" + ID_ADD_FALSE "Evaluates to a boolean value of false" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDIT_POPUP_SHOW_COMPASS + "Display a 3 dimentional compass in the upper right corner of window" + ID_CHANGE_VIEWPOINT_EXTERNAL + "See things from an external camera's viewpoint" + ID_CHANGE_VIEWPOINT_START + "See things from the player's starting position viewpoint" + ID_CHANGE_VIEWPOINT_FOLLOW + "See things from the current object's viewpoint" + ID_ADD_PLUS "Evaluates to sum of subexpressions" + ID_ADD_MINUS "Evaluates to difference between first subexpression and remaining subexpressions" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_SHOW_GRID_POSITIONS "Show position on grid for all visible objects" + ID_SHOW_COORDINATES "Display the coordinates of objects/waypoints." + ID_SELECT_LIST "Select from List\nSelect List (H)" + ID_CONTRAIN_X "Constrain to X axis\nX Constraint" + ID_CONSTRAIN_Y "Constrain to Y axis\nY Constraint (`)" + ID_BUTTON32922 "Constrain to X axis\nX Constraint" + ID_BUTTON32923 "Constrain to XZ axis\nXZ Constraint" + ID_ZOOM "Zoom view in/out\nZoom Mode" + ID_SELECTION_LOCK "Selection Lock\nSelection Lock (Spacebar)" + ID_BUTTON32926 "Fo" + ID_DISSOLVE_WING "Remove selected ships from Wing(s)\nRemove from Wing" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_SELECT_AND_MOVE "Select and Move objects\nSelect and Move (M)" + ID_SELECT_AND_ROTATE "Select and Rotate objects\nSelect and Rotate (R)" + ID_CONSTRAIN_X "Constrain to global X axis\nX Constraint (`)" + ID_CONSTRAIN_Z "Constrain to global Z axis\nZ Constraint (`)" + ID_CONSTRAIN_XZ "Constrain to global XZ plane\nXZ Constraint (`)" + ID_FORM_WING "Form marked ships into Wing\nForm Wing (Ctrl+W)" + ID_DISBAND_WING "Take marked ships out of wing\nDisband Wing (Ctrl+D)" + ID_SHOW_DISTANCED "Show distances between marked objects" + ID_SHOW_DISTANCES "Show distances between marked objects\nShow distances (D)" + ID_UNIVERSAL_HEADING "Heading changes are universal, instead of local" + ID_FIND_DISTANCE "Find Distance between two objects\nFind Distance" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_BUTTON32944 "Constrain to YZ axis\nYZ Constraint" + ID_BUTTON32945 "Constrain to XY axis\nXY Constraint" + ID_CONSTRAIN_YZ "Constrain to global YZ plane\nYZ Constraint (`)" + ID_CONSTRAIN_XY "Constrain to global XY plane\nXY Constraint (`)" + ID_FLYING_CONTROLS "Use real time flying controls instead of stepped controls" + ID_SELECT_AND_ROTATE_LOCALLY "Rotate objects locally\nRotate Locally" + ID_ROTATE_LOCALLY "Rotate around local object's axis system\nRotate locally (X)" + ID_ZOOM_TO_SELECTED "Zoom Selected\nZoom Selected" + ID_ZOOM_SELECTED "Zoom in on selected object\nZoom Selected (Alt Z)" + ID_ZOOM_EXTENTS "Zoom out to view all objects in universe\nZoom Extents (Shift Z)" + ID_SHOW_HORIZON "Show the horizon line" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDITORS_PLAYER "Edit loadout information for all teams" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_REVERT "Revert changes by reloading mission from last save\nRevert" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_ERROR_CHECKER "Checks campaign for errors" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_LOOKAT_OBJ "Rotate camera around selected object\nRotate around object (Ctrl+V)" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_EDITORS_ADJUST_GRID "Adjust the orientation and level of the grid." + ID_EDITORS_SHIELD_SYS "Editor for availability of shield system for ships." + ID_LEVEL_CAMERA "Level object to grid (use camera if no objects are marked)" + ID_ALIGN_OBJ "Align object's axis with global axis (use camera if no objects are marked)" + ID_MARK_WING "Mark entire wing selected object is a member of." +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_CONTROL_OBJ "Movement keys control marked objects instead of camera." + ID_CHECKOUT "Checks out the mission from Source Safe" + ID_CHECKIN "Checks in the mission to Source Safe" + ID_AA_GRIDLINES "Toggle Gridlines being drawn anti-aliased or not." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif +#include "res\FRED.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#include "afxprint.rc" // printing/print preview resources +#endif +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/fred2/freddoc.cpp b/src/fred2/freddoc.cpp new file mode 100644 index 0000000..6974b1e --- /dev/null +++ b/src/fred2/freddoc.cpp @@ -0,0 +1,1045 @@ +/* + * $Logfile: /Freespace2/code/Fred2/FREDDoc.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * FREDDoc.cpp : implementation of the CFREDDoc class + * Document class for document/view architechure, which we don't really use in + * Fred, but MFC forces you do use like it or not. Handles loading/saving + * mainly. Most of the MFC related stuff is handled in FredView. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 7 7/23/99 2:12p Jamesa + * fix gamepalette request + * + * 6 5/20/99 6:59p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 5 10/29/98 6:49p Dave + * Finished up Fred support for externalizing mission and campaign files. + * + * 4 10/29/98 12:50p Dave + * Intermediate checkin for fred hash table stuff. + * + * 3 10/28/98 11:30a Dave + * Temporary checkin. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:00p Dave + * + * 94 9/21/98 8:46p Dave + * Put in special check in fred for identifying unknown ships. + * + * 93 9/16/98 6:54p Dave + * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort + * the ship list box. Added code so that tracker stats are not stored with + * only 1 player. + * + * 92 8/13/98 9:53a Hoffoss + * Fixed bug where loading mission doesn't update palette used for that + * mission. + * + * 91 4/30/98 8:23p John + * Fixed some bugs with Fred caused by my new cfile code. + * + * 90 4/28/98 2:13p Hoffoss + * Added code to help keep invalid player ship types from existing in + * mission. + * + * 89 4/10/98 10:22p Mike + * Fix bug in parsing medals.tbl. + * + * 88 4/10/98 4:51p Hoffoss + * Made several changes related to tooltips. + * + * 87 3/25/98 4:14p Hoffoss + * Split ship editor up into ship editor and a misc dialog, which tracks + * flags and such. + * + * 86 3/10/98 4:26p Hoffoss + * Changed jump node structure to include a name. Position is now taken + * from the object (each jump node has an associated object now). + * + * 85 2/26/98 4:59p Allender + * groundwork for team vs team briefings. Moved weaponry pool into the + * Team_data structure. Added team field into the p_info structure. + * Allow for mutliple structures in the briefing code. + * + * 84 1/28/98 11:00a Johnson + * Fixed parse code for medals table (new field) + * + * 83 11/06/97 4:37p Allender + * a ton of medal work. Removed an uneeded element in the scoring + * structure. Fix up medals screen to apprioriate display medals (after + * mask was changed). Fix Fred to only display medals which may actually + * be granted. Added image_filename to player struct for Jason Hoffoss + * + * 82 11/05/97 10:19p Mike + * Comment out call to parse_medal_tbl() to allow Fred to run. + * + * 81 11/05/97 4:43p Allender + * reworked medal/rank system to read all data from tables. Made Fred + * read medals.tbl. Changed ai-warp to ai-warp-out which doesn't require + * waypoint for activation + * + * 80 11/04/97 4:50p Hoffoss + * Made mission loading failure display an error message stating so. + * + * 79 11/04/97 4:33p Hoffoss + * Made saving keep the current briefing state intact. + * + * 78 10/20/97 1:20p Hoffoss + * Fixed bug loading mission file after a save. + * + * 77 10/19/97 11:36p Hoffoss + * Made mission saving call reload the mission after saving it to verify + * it was written error free. + * + * 76 10/08/97 11:47a Hoffoss + * Added better fred handling of Weaponry_pool. + * + * 75 9/20/97 8:16a John + * Made .clr files go into the Cache directory. Replaced cfopen(name,NULL) + * to delete a file with cf_delete. + * + * 74 9/18/97 11:52a Johnson + * Fixed bug with player starts count being incorrect. + * + * 73 9/17/97 11:58a Hoffoss + * Fixed mission load code to properly set arrival cues to player ships. + * + * 72 9/17/97 10:32a Allender + * fixup references to old "Player" ship to new real shipname + * + * 71 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 70 9/09/97 6:50p Hoffoss + * Fixed bug with mission saving. + * + * 69 9/09/97 1:55p Hoffoss + * Fixed bug with saving missions that are readonly. + * + * 68 8/21/97 1:49p Hoffoss + * Fixed bugs with loading files. Wasn't clearing the last mission first, + * and multiplayer starts in a mission caused it to think it was modified + * right away. + * + * 67 8/20/97 5:45p Hoffoss + * Fixed bugs with mission name in sexp in non campaign mode in Fred. + * + * 66 8/20/97 4:03p Hoffoss + * Fixed bug in reload current mission (revert). + * + * 65 8/17/97 10:22p Hoffoss + * Fixed several bugs in Fred with Undo feature. In the process, recoded + * a lot of CFile.cpp. + * + * 64 8/16/97 9:24p Hoffoss + * Added support for team of players in multiplayer. + * + * 63 8/16/97 6:44p Hoffoss + * Changes to allow any player to be in a wing. + * + * 62 8/16/97 3:53p Hoffoss + * Added OF_NO_SHIELDS define and support in Fred and mission load/save. + * + * 61 8/13/97 5:49p Hoffoss + * Fixed bugs, made additions. + * + * 60 8/13/97 12:46p Hoffoss + * Added campaign error checker, accelerator table, and mission goal data + * listings to sexp tree right click menu. + * + * 59 8/12/97 1:55a Hoffoss + * Made extensive changes to object reference checking and handling for + * object deletion call. + * + * 58 7/31/97 5:55p John + * made so you pass flags to obj_create. + * Added new collision code that ignores any pairs that will never + * collide. + * + * 57 7/30/97 2:10p Hoffoss + * Made new missions that have never been saved yet save backups. + * + * 56 7/28/97 3:50p Hoffoss + * Added global error checker call to mission load and save. + * + * 55 7/10/97 2:32p Hoffoss + * Made message editor dialog box modeless. + * + * 54 7/08/97 11:35a Hoffoss + * Fixed bug in initial orders, and also redid how shields are handled by + * Fred. + * + * 53 6/26/97 5:18p Hoffoss + * Major rework of briefing editor functionality. + * + * 52 6/24/97 10:22a Hoffoss + * Added a forced update for briefing info prior to a mission save. + * + * 51 6/19/97 11:34a Hoffoss + * Fixed bug in auto mission convertion. + * + * 50 6/18/97 3:25p Hoffoss + * Added autoconverting of old ship names (for ships in wings) at mission + * load time, and added an hourglass cursor change when autosaving. + * + * 49 6/17/97 11:07a Hoffoss + * Fixed a few bugs: revert doesn't reset mission first, and sexp tree + * copy doesn't act act as a single chain. + * + * 48 6/11/97 10:02a Comet + * fixed a bug. + * + * 47 6/09/97 4:57p Hoffoss + * Added autosave and undo to Fred. + * + * 46 6/05/97 6:10p Hoffoss + * Added features: Autosaving, object hiding. Also fixed some minor bugs. + * + * 45 5/23/97 3:18p Hoffoss + * Event editor made modeless. + * + * 44 5/21/97 5:42p Hoffoss + * Added features requested on Todo list. + * + * 43 5/01/97 4:14p Hoffoss + * Viewer position saved to missions now for Fred restoration of state. + * + * 42 4/25/97 3:53p Hoffoss + * Fixed bug in weapons save/load and moved weapon look up function to + * weapons.cpp. + * + * 41 4/25/97 3:28p Mike + * Making shield multi-part. + * + * 40 4/21/97 5:02p Hoffoss + * Player/player status editing supported, and both saved and loaded from + * Mission files. + * + * 39 3/20/97 3:55p Hoffoss + * Major changes to how dialog boxes initialize (load) and update (save) + * their internal data. This should simplify things and create less + * problems. + * + * 38 3/04/97 6:27p Hoffoss + * Changes to Fred to handle new wing structure. + * + * 37 2/28/97 11:31a Hoffoss + * Implemented modeless dialog saving and restoring, and changed some + * variables names. + * + * 36 2/27/97 3:09p Allender + * major wing structure enhancement. simplified wing code. All around + * better wing support + * + * 35 2/20/97 4:03p Hoffoss + * Several ToDo items: new reinforcement clears arrival cue, reinforcement + * control from ship and wing dialogs, show grid toggle. + * + * 34 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 14 2/12/97 12:25p Hoffoss + * Expanded on global error checker, added initial orders conflict + * checking and warning, added waypoint editor dialog and code. + * + * 13 1/30/97 2:24p Hoffoss + * Added remaining mission file structures and implemented load/save of + * them. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include + +#include "freddoc.h" +#include "fredview.h" +#include "prefsdlg.h" + +#include "3d.h" +#include "object.h" +#include "editor.h" +#include "ai.h" +#include "ailocal.h" +#include "cfile.h" +#include "ship.h" +#include "missionparse.h" +#include "missiongoals.h" +#include "missionsave.h" +#include "weapon.h" +#include "management.h" +#include "linklist.h" +#include "fredrender.h" +#include "mainfrm.h" +#include "eventeditor.h" +#include "aigoals.h" +#include "messageeditordlg.h" +#include "palman.h" +#include "fhash.h" + +extern int num_objects; + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +// In editor mode, use class CFile, in game, use CFILE (our file) +#define XFILE CFile + +///////////////////////////////////////////////////////////////////////////// +// CFREDDoc + +IMPLEMENT_DYNCREATE(CFREDDoc, CDocument) + +BEGIN_MESSAGE_MAP(CFREDDoc, CDocument) + //{{AFX_MSG_MAP(CFREDDoc) + ON_COMMAND(ID_EDIT_DELETE, OnEditDelete) + ON_COMMAND(ID_DUPLICATE, OnDuplicate) + ON_COMMAND(ID_EDIT_COPY, OnEditCopy) + ON_COMMAND(ID_EDIT_CUT, OnEditCut) + ON_COMMAND(ID_EDIT_HOLD, OnEditHold) + ON_COMMAND(ID_EDIT_FETCH, OnEditFetch) + ON_COMMAND(ID_EDIT_PASTE, OnEditPaste) + ON_COMMAND(ID_EDIT_UNDO, OnEditUndo) + ON_COMMAND(ID_FILE_PREFERENCES, OnFilePreferences) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CFREDDoc construction/destruction + +// Global pointer to the FREDDoc class. +// Used by MK to, among other things, I hope, update the modified flag from +// outside the FREDDoc class. +CFREDDoc *FREDDoc_ptr = NULL; +int Local_modified = 0; +int Undo_available = 0; +int Undo_count = 0; + +extern int Fred_found_unknown_ship_during_parsing; + +CFREDDoc::CFREDDoc() +{ + int i; + + FREDDoc_ptr = this; + confirm_deleting = TRUE; + show_capital_ships = TRUE; + show_elevations = TRUE; + show_fighters = TRUE; + show_grid = TRUE; + show_misc_objects = TRUE; + show_planets = TRUE; + show_waypoints = TRUE; + show_starfield = TRUE; + + for (i=0; iicon_select(-1); // clean things up first + + len = strlen(mission_pathname); + strcpy(name, mission_pathname); + if (name[len - 4] == '.') + len -= 4; + + name[len] = 0; // drop extension + i = len; + while (i--) + if ((name[i] == '\\') || (name[i] == ':')) + break; + + strcpy(Mission_filename, name + i + 1); +// for (i=1; i<=BACKUP_DEPTH; i++) { +// sprintf(name + len, ".%.3d", i); +// unlink(name); +// } + + if (load_mission(mission_pathname)) { + *Mission_filename = 0; + return FALSE; + } + + Fred_view_wnd->global_error_check(); + autosave("nothing"); + Undo_count = 0; + return TRUE; +} + +// save mission to a file +BOOL CFREDDoc::OnSaveDocument(LPCTSTR pathname) +{ + CFred_mission_save save; + char name[1024]; + int len; + DWORD attrib; + FILE *fp; + + len = strlen(pathname); + strcpy(name, pathname); + if (name[len - 4] == '.') + len -= 4; + + name[len] = 0; // drop extension + while (len--) + if ((name[len] == '\\') || (name[len] == ':')) + break; + + strcpy(Mission_filename, name + len + 1); + Fred_view_wnd->global_error_check(); + if (Briefing_dialog) { + Briefing_dialog->update_data(1); + Briefing_dialog->save_editor_state(); + } + + if (Event_editor_dlg) + Fred_main_wnd->MessageBox("Event editor dialog is still open, so changes there won't be saved"); + + if (Message_editor_dlg) + Fred_main_wnd->MessageBox("Message editor dialog is still open, so changes there won't be saved"); + + fp = fopen(pathname, "r"); + if (fp) { + fclose(fp); + attrib = GetFileAttributes(pathname); + if (attrib & FILE_ATTRIBUTE_READONLY) { + Fred_main_wnd->MessageBox("File is read-only. You need to check it out before saving to it"); + return FALSE; + } + } + + if (save.save_mission_file((char *) pathname)) { + Fred_main_wnd->MessageBox("An error occured while saving!", NULL, MB_OK | MB_ICONEXCLAMATION); + return FALSE; + } + + SetModifiedFlag(FALSE); + if (load_mission((char *) pathname)) + Error(LOCATION, "Failed attempting to reload mission after saving. Report this bug now!"); + + if (Briefing_dialog) { + Briefing_dialog->restore_editor_state(); + Briefing_dialog->update_data(1); + } + + return TRUE; +// return CDocument::OnSaveDocument(pathname); +} + +int CFREDDoc::check_undo() +{ + char name[256]; + FILE *fp; + + Undo_available = 0; + if (!Undo_count) + return 0; + + strcpy(name, MISSION_BACKUP_NAME); + strcat(name, ".002"); + fp = fopen(name, "r"); + if (!fp) + return 0; + + fclose(fp); + Undo_available = 1; + return 1; +} + +int CFREDDoc::autosave(char *desc) +{ + int i; + CFred_mission_save save; + CWaitCursor wait; + + if (Autosave_disabled) + return 0; + + if (Briefing_dialog) + Briefing_dialog->update_data(1); + + if (save.autosave_mission_file(MISSION_BACKUP_NAME)) { + Undo_count = Undo_available = 0; + return -1; + } + + for (i=BACKUP_DEPTH; i>1; i--) + undo_desc[i] = undo_desc[i - 1]; + + if (desc) + undo_desc[1] = desc; + else + undo_desc[1].Empty(); + + Undo_count++; + check_undo(); + return 0; +} + +int CFREDDoc::autoload() +{ + char name[256], backup_name[256]; + int i, r, len; + FILE *fp; + + strcpy(name, MISSION_BACKUP_NAME); + strcat(name, ".002"); + fp = fopen(name, "r"); + if (!fp) + return 0; + + fclose(fp); + if (Briefing_dialog) + Briefing_dialog->icon_select(-1); // clean things up first + +// editor_init_mission(); + r = load_mission(name); + Update_window = 1; + + strcpy(backup_name, MISSION_BACKUP_NAME); + len = strlen(backup_name); + strcat(backup_name, ".001"); + cf_delete(backup_name, CF_TYPE_MISSIONS); + + for (i=1; iMessageBox(name); + create_new_mission(); + return -1; + } + + if(Fred_found_unknown_ship_during_parsing){ + Fred_view_wnd->MessageBox("Fred encountered unknown ship/weapon classes when parsing the mission file. This may be due to mission disk data you do not have"); + } + Fred_found_unknown_ship_during_parsing = 0; + + for (i=0; icount; j++){ + ob = obj_create(OBJ_WAYPOINT, -1, i * 65536 + j, NULL, &wptr->waypoints[j], 0.0f, OF_RENDERS); + } + } + + obj_merge_created_list(); + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_PLAYER_SHIP) { + Assert(objp->type == OBJ_SHIP); + objp->type = OBJ_START; +// Player_starts++; + } + + objp = GET_NEXT(objp); + } + + for (i=0; i= 0); + Assert(Mission_palette <= 98); + + if (The_mission.flags & MISSION_FLAG_SUBSPACE) { + strcpy(name, NOX("gamepalette-subspace")); + } else { + strcpy(name, "gamepalette1-01"); + // sprintf(name, NOX("gamepalette1-%02d"), Mission_palette + 1); + } + + palette_load_table(name); + + // go through all ships and translate their alternate name indices + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + // if this is a ship, check it, and mark its possible alternate name down in the auxiliary array + if(((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->instance >= 0) && (Ships[objp->instance].alt_type_index >= 0)){ + mission_parse_lookup_alt_index(Ships[objp->instance].alt_type_index, Fred_alt_names[objp->instance]); + + // also zero it + Ships[objp->instance].alt_type_index = -1; + } + + objp = GET_NEXT(objp); + } + + + view_pos = Parse_viewer_pos; + view_orient = Parse_viewer_orient; + set_modified(0); + + return 0; +} + +// Editor-level interface to mission load/save. + +// Does nothing now.. Handled by OnOpenDocument and OnSaveDocument. This is because we +// want to avoid using the CArchive for file I/O -JH +void CFREDDoc::Serialize(CArchive& ar) +{ + return; +/* The original Serialize code + int rw_flag; + XFILE *fp; +// CString CSfilename; +// char filename[128], *tfilename; + + fp = ar.GetFile(); + rw_flag = ar.IsStoring(); + +// CSfilename = fp->GetFileName(); +// tfilename = CSfilename.GetBuffer(16); +// strcpy(filename, tfilename); +// CSfilename.ReleaseBuffer(); +// -- Don't close this, it gets closed by MFC -- ar.Close(); + + cfile_serialize(fp, rw_flag); + cfile_serialize_editor(fp, rw_flag); +*/ +} + +///////////////////////////////////////////////////////////////////////////// +// CFREDDoc diagnostics + +#ifdef _DEBUG +void CFREDDoc::AssertValid() const +{ + CDocument::AssertValid(); +} + +void CFREDDoc::Dump(CDumpContext& dc) const +{ + CDocument::Dump(dc); +} +#endif //_DEBUG + + +///////////////////////////////////////////////////////////////////////////// +// CFREDDoc commands + +void CFREDDoc::OnEditDelete() +{ + // TODO: Add your command handler code here + +} + +void CFREDDoc::OnDuplicate() +{ + // TODO: Add your command handler code here + +} + +void CFREDDoc::OnEditCopy() +{ + // TODO: Add your command handler code here + +} + +void CFREDDoc::OnEditCut() +{ + // TODO: Add your command handler code here + +} + +void CFREDDoc::OnEditPaste() +{ + // TODO: Add your command handler code here + +} + +void CFREDDoc::OnEditHold() +{ + // TODO: Add your command handler code here + +} + +void CFREDDoc::OnEditFetch() +{ + // TODO: Add your command handler code here + +} + +void CFREDDoc::OnEditUndo() +{ + // TODO: Add your command handler code here + +} + +void CFREDDoc::OnFilePreferences() +{ + CPrefsDlg dlg; + // Initialize dialog data + dlg.m_ConfirmDeleting = confirm_deleting; + dlg.m_ShowCapitalShips = show_capital_ships; + dlg.m_ShowElevations = show_elevations; + dlg.m_ShowFighters = show_fighters; + dlg.m_ShowGrid = show_grid; + dlg.m_ShowMiscObjects = show_misc_objects; + dlg.m_ShowPlanets = show_planets; + dlg.m_ShowWaypoints = show_waypoints; + dlg.m_ShowStarfield = show_starfield; + + // Invoke the dialog box + if (dlg.DoModal() == IDOK) + { + // retrieve the dialog data + confirm_deleting = dlg.m_ConfirmDeleting; + show_capital_ships = dlg.m_ShowCapitalShips; + show_elevations = dlg.m_ShowElevations; + show_fighters = dlg.m_ShowFighters; + show_grid = dlg.m_ShowGrid; + show_misc_objects = dlg.m_ShowMiscObjects; + show_planets = dlg.m_ShowPlanets; + show_waypoints = dlg.m_ShowWaypoints; + show_starfield = dlg.m_ShowStarfield; + } + +} + +// initialize (clear out) the mission, so it's empty and ready to use. +void CFREDDoc::editor_init_mission() +{ + reset_mission(); + SetModifiedFlag(FALSE); +} +/* +void CFREDDoc::OnFileNew() +{ + // If mission has been modified, offer to save before continuing. + while (IsModified()) { + int rval; + + rval = MessageBox(NULL, + "You have not saved your work.\n(Which isn't surprising...)\nSave it now?", + "Creating New Mission", + MB_YESNOCANCEL + MB_ICONEXCLAMATION); + + if (rval == IDYES) { + OnFileSave(); + } else if (rval == IDCANCEL) + return; + else if (rval == IDNO) + break; + } + + editor_init_mission(); + +} +*/ +void CFREDDoc::UpdateStatus(int flags) +{ + if (FREDDoc_ptr) + if (flags & US_WORLD_CHANGED) + FREDDoc_ptr->SetModifiedFlag(); +} + +void CFREDDoc::OnEditClearAll() +{ + DeleteContents(); +} + + +void CFREDDoc::DeleteContents() +{ + editor_init_mission(); +} + +void set_modified(BOOL arg) +{ + Local_modified = arg; + FREDDoc_ptr->SetModifiedFlag(arg); +} + +// call this if an unknown ship class was discovered during parsing. Sets up a warning message for players +void fred_notify_unknown_ship_during_parse() +{ + Fred_found_unknown_ship_during_parsing = 1; +} + + ////////////////////////////////////////////////////////////////////////// +// +// Below is old, obsolete code, kept around just in case it might be found +// useful some time in the future for something. +// + ////////////////////////////////////////////////////////////////////////// + +#if 0 +#define SerializeFloat(fp, mode, f) if (mode == 1) fp->Write(&f, sizeof(float)); else fp->Read(&f, sizeof(float)) +#define SerializeInt(fp, mode, f) if (mode == 1) fp->Write(&f, sizeof(int)); else fp->Read(&f, sizeof(int)) + +void SerializeVector(XFILE *fp, int mode, vector *v) +{ + SerializeFloat(fp, mode, v->x); + SerializeFloat(fp, mode, v->y); + SerializeFloat(fp, mode, v->z); +} + +void SerializeMatrix(XFILE *fp, int mode, matrix *m) +{ + SerializeVector(fp, mode, &m->rvec); + SerializeVector(fp, mode, &m->uvec); + SerializeVector(fp, mode, &m->fvec); +} + +void SerializePhysicsInfo(XFILE *fp, int mode, physics_info *pi) +{ + SerializeFloat(fp, mode, pi->mass); + SerializeFloat(fp, mode, pi->drag); + SerializeVector(fp, mode, &pi->max_thrust); + SerializeVector(fp, mode, &pi->max_rotthrust); + SerializeFloat(fp, mode, pi->turnroll); + SerializeInt(fp, mode, pi->flags); + SerializeVector(fp, mode, &pi->velocity); + SerializeVector(fp, mode, &pi->rotvel); + SerializeVector(fp, mode, &pi->thrust); + SerializeVector(fp, mode, &pi->rotthrust); +} + +///////////////////////////////////////////////////////////////////////////// +// CFREDDoc serialization +void SerializeObject(XFILE *fp, int mode, object *objp) +{ + SerializeInt(fp, mode, objp->signature); + SerializeInt(fp, mode, objp->type); + SerializeInt(fp, mode, objp->parent); + SerializeInt(fp, mode, objp->parent_sig); + SerializeInt(fp, mode, objp->parent_type); + SerializeInt(fp, mode, objp->instance); + SerializeInt(fp, mode, objp->flags); + SerializeFloat(fp, mode, objp->radius); +// SerializeInt(fp, mode, objp->wing); + SerializePhysicsInfo(fp, mode, &objp->phys_info); + SerializeVector(fp, mode, &objp->pos); + SerializeMatrix(fp, mode, &objp->orient); +} + +void SerializeAI(XFILE *fp, int mode, ai_info *aip) +{ + SerializeInt(fp, mode, aip->shipnum); + SerializeInt(fp, mode, aip->type); + SerializeInt(fp, mode, aip->wing); +//MWA -- SerializeInt(fp, mode, aip->current_waypoint); +} + +void SerializeShip(XFILE *fp, int mode, ship *shipp) +{ + SerializeInt(fp, mode, shipp->objnum); + SerializeInt(fp, mode, shipp->ai_index); + SerializeInt(fp, mode, shipp->subtype); + SerializeInt(fp, mode, shipp->modelnum); + SerializeInt(fp, mode, shipp->hits); + SerializeInt(fp, mode, shipp->dying); +} + +void SerializeGrid(XFILE *fp, int mode, grid *gridp) +{ + int i; + + SerializeInt(fp, mode, gridp->nrows); + SerializeInt(fp, mode, gridp->ncols); + SerializeMatrix(fp, mode, &gridp->gmatrix); + SerializePhysicsInfo(fp, mode, &gridp->physics); + SerializeFloat(fp, mode, gridp->square_size); + SerializeFloat(fp, mode, gridp->planeD); + + for (i=0; igpoints[i]); + +} + +void cfile_serialize(XFILE *fp, int flag) +{ + int i; + int highest_object_index = 0, highest_ship_index = 0, highest_ai_index = 0; + + Assert((flag == 0) || (flag == 1)); + +// fp = cfopen(filename, flag ? "wb" : "rb"); +// if (!fp) +// MessageBox(NULL, strerror(errno), "File Open Error!", MB_ICONSTOP); + + // Find highest used object if writing. + if (flag == 1) { + for (i=MAX_OBJECTS-1; i>0; i--) + if (Objects[i].type != OBJ_NONE) { + highest_object_index = i; + break; + } + } + + if (flag == 0) { + num_ships = 0; + num_objects = 0; + } + + SerializeInt(fp, flag, highest_object_index); + + for (i=1; i<=highest_object_index; i++) { + SerializeObject(fp, flag, &Objects[i]); + if (flag == 0) + if (Objects[i].type != OBJ_NONE) + num_objects++; + } + + // Read/write ships + if (flag == 1) { + for (i=MAX_SHIPS-1; i>0; i--) + if (Ships[i].objnum) { + highest_ship_index = i; + break; + } + } + + SerializeInt(fp, flag, highest_ship_index); + + for (i=1; i<=highest_ship_index; i++) { + SerializeShip(fp, flag, &Ships[i]); + if (flag == 0) + if (Ships[i].objnum) + num_ships++; + } + + // Read/write AI info + if (flag == 1) { + for (i=MAX_AI_INFO-1; i>0; i--) + if (Ai_info[i].shipnum) { + highest_ai_index = i; + break; + } + } + + SerializeInt(fp, flag, highest_ai_index); + + for (i=1; i<=highest_ai_index; i++) + SerializeAI(fp, flag, &Ai_info[i]); +} + +void cfile_serialize_editor(XFILE *fp, int flag) +{ + // Editor only stuff + SerializeMatrix(fp, flag, &view_orient); + SerializeVector(fp, flag, &view_pos); + + SerializeInt(fp, flag, Control_mode); + SerializeInt(fp, flag, cur_object_index); + SerializeInt(fp, flag, cur_wing); + + SerializeGrid(fp, flag, The_grid); + +} +#endif diff --git a/src/fred2/fredrender.cpp b/src/fred2/fredrender.cpp new file mode 100644 index 0000000..02a1514 --- /dev/null +++ b/src/fred2/fredrender.cpp @@ -0,0 +1,1841 @@ +/* + * $Logfile: /Freespace2/code/fred2/FredRender.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Handles rendering the scene in the window for Fred. Also handles several other + * miscellaneous tasks. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 6 4/07/99 6:21p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 5 3/26/99 4:49p Dave + * Made cruisers able to dock with stuff. Made docking points and paths + * visible in fred. + * + * 4 2/07/99 8:51p Andsager + * Add inner bound to asteroid field. Inner bound tries to stay astroid + * free. Wrap when within and don't throw at ships inside. + * + * 3 1/27/99 4:09p Andsager + * Added highlight to ship subsystems + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 162 5/21/98 12:58a Hoffoss + * Fixed warnings optimized build turned up. + * + * 161 4/11/98 6:53p John + * Added first rev of subspace effect. + * + * 160 4/07/98 4:17p John + * Made Fred be able to move suns. Made suns actually affect the lighting + * in the game. + * + * 159 3/21/98 7:36p Lawrance + * Move jump nodes to own lib. + * + * 158 3/19/98 11:41a Hoffoss + * Fixed problems with rendering and reading of flying controls in Fred. + * + * 157 3/14/98 5:11p Hoffoss + * Changed the starting camera position and orientation for Fred. + * + * 156 3/10/98 4:24p Hoffoss + * Made object detection under cursor use the object's radius if it has + * one. + * + * 155 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 154 3/10/98 1:41p Sandeep + * + * 153 3/09/98 10:56a Hoffoss + * Added jump node objects to Fred. + * + * 152 3/03/98 11:00a Andsager + * Fixed fred bug with control object initialization. + * + * 151 2/18/98 6:45p Hoffoss + * Added support for lines between icons in briefings for Fred. + * + * 150 2/12/98 3:48p Hoffoss + * Reduced scaler for extra precision. + * + * 149 2/10/98 6:43p Lawrance + * Moved asteroid code to a separate lib. + * + * 148 2/06/98 3:08p Mike + * More asteroid stuff, including resolving conflicts between the two + * asteroid_field structs! + * + * 147 1/27/98 11:02a John + * Added first rev of sparks. Made all code that calls model_render call + * clear_instance first. Made debris pieces not render by default when + * clear_instance is called. + * + * 146 1/16/98 2:22p Hoffoss + * Changed rendering colors of objects. + * + * 145 12/29/97 5:09p Allender + * fixed problems with speed not being reported properly in multiplayer + * games. Made read_flying_controls take frametime as a parameter. More + * ship/weapon select stuff + * + * 144 10/30/97 3:30p Hoffoss + * Made anti-aliased gridlines an option in Fred. + * + * 143 10/29/97 5:13p John + * Trying to fix my lighting bugs in Fred. + * + * 142 10/03/97 9:48a John + * took out alphacolors some grid lines don't draw antialiased. + * + * 141 10/03/97 8:55a John + * moved Fred's grid_render code out of MissionGrid and into Fred. Added + * code to turn background under overlays grey. + * + * 140 9/18/97 9:59a John + * fixed bug I caused in model_collide + * + * 139 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 138 9/09/97 2:12p Hoffoss + * Added code to allow briefing editor view to be a 1:1 pixel size fixed + * as the FreeSpace view will have. + * + * 137 9/06/97 2:13p Mike + * Replace support for TEAM_NEUTRAL + * + * 136 8/29/97 5:41p Johnson + * Fixed bug with controlling marked objects without having done any + * rotations yet of any other type. + * + * 135 8/25/97 5:58p Hoffoss + * Created menu items for keypress functions in Fred, and fixed bug this + * uncovered with wing_delete function. + * + * 134 8/19/97 5:46p Hoffoss + * Changed font used in Fred, and added display to show current eye + * position. + * + * 133 8/19/97 11:23a Hoffoss + * Fixes to briefing editor icon select code. + * + * 132 8/17/97 10:22p Hoffoss + * Fixed several bugs in Fred with Undo feature. In the process, recoded + * a lot of CFile.cpp. + * + * 131 8/16/97 2:02a Hoffoss + * Made docked objects move together in Fred. + * + * 130 8/14/97 7:01p Hoffoss + * Fixed bug with outline rendering while in background bitmap editing + * mode. + * + * 129 8/14/97 2:32p Hoffoss + * fixed bug where controlling an object doesn't cause screen updates, and + * added a number of cool features to viewpoint/control object code. + * + * 128 8/13/97 1:38p Hoffoss + * Added ability to update multiple times, which in needed in one case to + * brute force redraw so deleted ships actually do get removed from the + * display. + * + * 127 8/12/97 1:55a Hoffoss + * Made extensive changes to object reference checking and handling for + * object deletion call. + * + * 126 8/07/97 6:01p Hoffoss + * Added a rotate about selected object button to toolbar and + * functionality, as requested by Comet. + * + * 125 8/06/97 7:55p Hoffoss + * Fixed bug with objects not seeming to be where they are drawn (due to + * new briefing clip render stuff). This caused rendering problems, + * though. So I fixed those next. + * + * 124 8/06/97 6:10p Hoffoss + * Changed Fred to display a forced aspect ratio while briefing editor is + * open. This aspect ratio is the same as the briefing view in FreeSpace, + * so icons appear on and off screen the same as would be in FreeSpace. + * + * 123 7/24/97 10:24a Mike + * Restore support for Unknown team + * + * 122 7/17/97 1:34p Hoffoss + * Fixed Fred to work with physics changes. + * + * 121 6/26/97 6:03p Hoffoss + * briefing icon selection now has higher priority of selection than other + * objects. + * + * 120 6/26/97 5:18p Hoffoss + * Major rework of briefing editor functionality. + * + * 119 6/26/97 4:20p Hoffoss + * Changed the behavior of Ctrl-L + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "freddoc.h" +#include "fredview.h" +#include "mainfrm.h" + +#include +#include +#include +#include + +#include "management.h" +#include "vecmat.h" +#include "tmapper.h" +#include "2d.h" +#include "3d.h" +#include "model.h" +#include "bmpman.h" +#include "key.h" +#include "physics.h" +#include "floating.h" +#include "object.h" +#include "model.h" +#include "palman.h" +#include "editor.h" +#include "ailocal.h" +#include "ship.h" +#include "cfile.h" +#include "missionparse.h" +#include "linklist.h" +#include "fvi.h" +#include "3dinternal.h" +#include "weapon.h" +#include "wing.h" +#include "fredrender.h" +#include "windows.h" +#include "starfield.h" +#include "timer.h" +#include "lighting.h" +#include "asteroid.h" +#include "jumpnode.h" + +extern float flFrametime; +extern subsys_to_render Render_subsys; + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define MAX_FRAMETIME (F1_0/4) // Frametime gets saturated at this. +#define MIN_FRAMETIME (F1_0/120) +#define MAX_STARS 500 +#define LOLLIPOP_SIZE 2.5f + +int Aa_gridlines = 0; +int Fred_outline = 0; +int inited = -1; +int player_start1; +int Editing_mode = 1; +int Control_mode = 0; +int last_x=0, last_y=0; +int Show_grid = 1; +int Show_outlines = 0; +int Show_stars = 0; +int Show_grid_positions = 1; +int Show_coordinates = 0; +int Show_distances = 0; +int Show_horizon = 0; +int Show_asteroid_field = 1; +int Lookat_mode = 0; +int Single_axis_constraint = 0; +int Universal_heading = 0; +int Flying_controls_mode = 1; +int Group_rotate = 1; +int info_popup_active = 0; +int rendering_order[MAX_SHIPS]; +int render_count = 0; +int Last_cursor_over = -1; +int True_rw, True_rh; +int Fixed_briefing_size = 1; + +fix lasttime = 0; +vector my_pos = {0.0f, 0.0f, -5.0f}; +vector view_pos, eye_pos, Viewer_pos, Last_eye_pos = { 0.0f }; +vector Last_control_pos = { 0.0f }; +vector Grid_center; +vector Constraint = { 1.0f, 0.0f, 1.0f }; +vector Anticonstraint = { 0.0f, 1.0f, 0.0f }; +vector Tp1, Tp2; // test points +matrix Grid_gmatrix; +matrix my_orient = IDENTITY_MATRIX; +matrix trackball_orient = IDENTITY_MATRIX; +matrix view_orient = IDENTITY_MATRIX, eye_orient, Last_eye_orient = IDENTITY_MATRIX; +matrix Last_control_orient = IDENTITY_MATRIX; +physics_info view_physics; +control_info view_controls; +CWnd info_popup; + +static vector Global_light_world = { 0.208758f, -0.688253f, -0.694782f }; + +void display_distances(); +void render_model_x(vector *pos, grid *gridp, int col_scheme = 0); +void draw_orient_sphere(object *obj, int r, int g, int b); +void draw_orient_sphere2(int col, object *obj, int r, int g, int b); +void render_compass(void); +void draw_compass_arrow(vector *v0); +void process_controls(vector *pos, matrix *orient, float frametime, int key, int mode = 0); +void render_one_model(object *objp); +void inc_mission_time(); +void draw_asteroid_field(); +void hilight_bitmap(); + +// Called every time a new mission is created (and erasing old mission from memory). +// New mission should be blank at this point. +void fred_render_init() +{ + vector f, u, r; + + physics_init(&view_physics); + view_physics.max_vel.z = 5.0f; //forward/backward + view_physics.max_rotvel.x = 1.5f; //pitch + memset(&view_controls, 0, sizeof(control_info)); + + vm_vec_make(&view_pos, 0.0f, 150.0f, -200.0f); + vm_vec_make(&f, 0.0f, -0.5f, 0.866025404f); // 30 degree angle + vm_vec_make(&u, 0.0f, 0.866025404f, 0.5f); + vm_vec_make(&r, 1.0f, 0.0f, 0.0f); + vm_vector_2_matrix(&view_orient, &f, &u, &r); + + The_grid = create_default_grid(); + maybe_create_new_grid(The_grid, &view_pos, &view_orient, 1); +// vm_set_identity(&view_orient); +} + +void level_object(matrix *orient) +{ + vector u; + + u = orient->uvec = The_grid->gmatrix.uvec; + if (u.x) // y-z plane + { + orient->fvec.x = orient->rvec.x = 0.0f; + + } else if (u.y) { // x-z plane + orient->fvec.y = orient->rvec.y = 0.0f; + + } else if (u.z) { // x-y plane + orient->fvec.z = orient->rvec.z = 0.0f; + } + + vm_fix_matrix(orient); +} + +void level_controlled() +{ + int cmode, count = 0; + object *objp; + + cmode = Control_mode; + if ((viewpoint == 1) && !cmode) + cmode = 2; + + switch (cmode) { + case 0: // Control the viewer's location and orientation + level_object(&view_orient); + break; + + case 2: // Control viewpoint object + level_object(&Objects[view_obj].orient); + object_moved(&Objects[view_obj]); + set_modified(); + FREDDoc_ptr->autosave("level object"); + break; + + case 1: // Control the current object's location and orientation + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) + level_object(&objp->orient); + + objp = GET_NEXT(objp); + } + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) { + object_moved(objp); + count++; + } + + objp = GET_NEXT(objp); + } + + if (count) { + if (count > 1) + FREDDoc_ptr->autosave("level objects"); + else + FREDDoc_ptr->autosave("level object"); + + set_modified(); + } + + break; + } + + return; +} + +void align_vector_to_axis(vector *v) +{ + float x, y, z; + + x = v->x; + if (x < 0) + x = -x; + + y = v->y; + if (y < 0) + y = -y; + + z = v->z; + if (z < 0) + z = -z; + + if ((x > y) && (x > z)) { // x axis + if (v->x < 0) // negative x + vm_vec_make(v, -1.0f, 0.0f, 0.0f); + else // positive x + vm_vec_make(v, 1.0f, 0.0f, 0.0f); + + } else if (y > z) { // y axis + if (v->y < 0) // negative y + vm_vec_make(v, 0.0f, -1.0f, 0.0f); + else // positive y + vm_vec_make(v, 0.0f, 1.0f, 0.0f); + + } else { // z axis + if (v->z < 0) // negative z + vm_vec_make(v, 0.0f, 0.0f, -1.0f); + else // positive z + vm_vec_make(v, 0.0f, 0.0f, 1.0f); + } +} + +void verticalize_object(matrix *orient) +{ + align_vector_to_axis(&orient->fvec); + align_vector_to_axis(&orient->uvec); + align_vector_to_axis(&orient->rvec); + vm_fix_matrix(orient); // just in case something odd occurs. +} + +void verticalize_controlled() +{ + int cmode, count = 0; + object *objp; + + cmode = Control_mode; + if ((viewpoint == 1) && !cmode) + cmode = 2; + + switch (cmode) { + case 0: // Control the viewer's location and orientation + verticalize_object(&view_orient); + break; + + case 2: // Control viewpoint object + verticalize_object(&Objects[view_obj].orient); + object_moved(&Objects[view_obj]); + FREDDoc_ptr->autosave("align object"); + set_modified(); + break; + + case 1: // Control the current object's location and orientation + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) + verticalize_object(&objp->orient); + + objp = GET_NEXT(objp); + } + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) { + object_moved(objp); + count++; + } + + objp = GET_NEXT(objp); + } + + if (count) { + if (count > 1) + FREDDoc_ptr->autosave("align objects"); + else + FREDDoc_ptr->autosave("align object"); + + set_modified(); + } + + break; + } + + return; +} + +void move_mouse( int btn, int mdx, int mdy ) +{ + int dx, dy; + + dx = mdx - last_x; + dy = mdy - last_y; + last_x = mdx; + last_y = mdy; + + if ( btn & 1 ) { + matrix tempm, mousem; + + if ( dx || dy ) { + vm_trackball( dx, dy, &mousem ); + vm_matrix_x_matrix(&tempm, &trackball_orient, &mousem); + trackball_orient = tempm; + view_orient = trackball_orient; + } + } + + if ( btn & 2 ) { + my_pos.z += (float)dy; + } +} + +/////////////////////////////////////////////////// +void process_system_keys(int key) +{ +// mprintf(("Key = %d\n", key)); + switch (key) { + + case KEY_LAPOSTRO: + CFREDView::GetView()->cycle_constraint(); + break; + + case KEY_R: // for some stupid reason, an accelerator for 'R' doesn't work. + Editing_mode = 2; + break; + + case KEY_SPACEBAR: + Selection_lock = !Selection_lock; + break; + + case KEY_ESC: + if (button_down) + cancel_drag(); + + break; + } +} + +void render_waypoints(void) +{ + int i, j; + vertex v; + waypoint_list *ptr; + + for (i=0; icount; j++) + { + g3_rotate_vertex(&v, &ptr->waypoints[j]); + if (!(v.codes & CC_BEHIND)) + if (!(g3_project_vertex(&v) & PF_OVERFLOW)) + { + if (cur_waypoint_list == i && cur_waypoint == j) + gr_set_color(255, 255, 255); + else if (Waypoint_lists[i].flags[j] & WL_MARKED) + gr_set_color(160, 255, 0); + else + gr_set_color(160, 96, 0); + + g3_draw_sphere(&v, LOLLIPOP_SIZE); + if (j) + gr_set_color(0, 0, 0); + else + gr_set_color(160, 96, 0); + + g3_draw_sphere(&v, LOLLIPOP_SIZE * 0.66667f); + gr_set_color(160, 96, 0); + g3_draw_sphere(&v, LOLLIPOP_SIZE * 0.33333f); + } + } + + for (j=0; jcount; j++) + render_model_x(&ptr->waypoints[j], The_grid, 1); + + gr_set_color(160, 96, 0); + for (j=1; jcount; j++) + rpd_line(&ptr->waypoints[j-1], &ptr->waypoints[j]); + } +} + +// -------------------------------------------------------------------------------- +// get_subsystem_world_pos2() returns the world position for a given subobject on a ship +// + +vector* get_subsystem_world_pos2(object* parent_obj, ship_subsys* subsys, vector* world_pos) +{ + if (subsys == NULL) { + *world_pos = parent_obj->pos; + return world_pos; + } + + vm_vec_unrotate(world_pos, &subsys->system_info->pnt, &parent_obj->orient); + vm_vec_add2(world_pos, &parent_obj->pos); + + return world_pos; +} + +// returns 1 for valid bounding rect, 0 otherwise +int get_subsys_bounding_rect(object *ship_obj, ship_subsys *subsys, int *x1, int *x2, int *y1, int *y2) +{ + if (subsys != NULL) { + vertex subobj_vertex; + vector subobj_pos; + + get_subsystem_world_pos2(ship_obj, subsys, &subobj_pos); + + g3_rotate_vertex(&subobj_vertex, &subobj_pos); + + g3_project_vertex(&subobj_vertex); + if (subobj_vertex.flags & PF_OVERFLOW) // if overflow, no point in drawing brackets + return 0; + + int bound_rc; + + bound_rc = subobj_find_2d_bound(subsys->system_info->radius, &ship_obj->orient, &subobj_pos, x1, y1, x2, y2); + if ( bound_rc != 0 ) + return 0; + + return 1; + } + + return 0; +} + +void cancel_display_active_ship_subsystem() +{ + Render_subsys.do_render = false; + Render_subsys.ship_obj = NULL; + Render_subsys.cur_subsys = NULL; +} + +void display_active_ship_subsystem() +{ + if (cur_object_index != -1) { + if (Objects[cur_object_index].type == OBJ_SHIP) { + + object *objp = &Objects[cur_object_index]; + int x1, y1, x2, y2; + char buf[256]; + + // switching to a new ship, so reset + if (objp != Render_subsys.ship_obj) { + cancel_display_active_ship_subsystem(); + return; + } + + if (Render_subsys.do_render) { + + // get subsys name + strcpy(buf, Render_subsys.cur_subsys->system_info->subobj_name); + + // get bounding box + if ( get_subsys_bounding_rect(objp, Render_subsys.cur_subsys, &x1, &x2, &y1, &y2) ) { + + // set color + gr_set_color(255, 32, 32); + + // draw box + gr_line(x1, y1, x1, y2); gr_line(x1-1, y1, x1-1, y2); + gr_line(x1, y2, x2, y2); gr_line(x1, y2+1, x2, y2+1); + gr_line(x2, y2, x2, y1); gr_line(x2+1, y2, x2+1, y1); + gr_line(x2, y1, x1, y1); gr_line(x2, y1-1, x1, y1-1); + + // draw text + gr_string_win( (x1+x2)/2, y2 + 10, buf); + } + } + } + } else { + cancel_display_active_ship_subsystem(); + } +} + +void render_models(void) +{ + render_count = 0; + obj_render_all(render_one_model); + if (Briefing_dialog) + Briefing_dialog->batch_render(); +} + +void render_one_model(object *objp) +{ + int j, z; + object *o2; + + Assert(objp->type != OBJ_NONE); + + if ( objp->type == OBJ_JUMP_NODE ) { + return; + } + + if ((objp->type == OBJ_WAYPOINT) && !Show_waypoints) + return; + + if ((objp->type == OBJ_START) && !Show_starts) + return; + + if (objp->type == OBJ_SHIP) { + if (!Show_ships) + return; + + if ((Ships[objp->instance].team == TEAM_FRIENDLY) && !Show_friendly) + return; + + if ((Ships[objp->instance].team == TEAM_HOSTILE) && !Show_hostile) + return; + + if ((Ships[objp->instance].team == TEAM_NEUTRAL) && !Show_neutral) + return; + } + + if (objp->flags & OF_HIDDEN) + return; + + rendering_order[render_count] = OBJ_INDEX(objp); + Fred_outline = 0; + if ((OBJ_INDEX(objp) == cur_object_index) && !Bg_bitmap_dialog) + Fred_outline = 0xffffff; + + else if ((objp->flags & OF_MARKED) && !Bg_bitmap_dialog) // is it a marked object? + Fred_outline = 0x9fff00; + + else if ((objp->type == OBJ_SHIP) && Show_outlines) { + switch (Ships[objp->instance].team) { + case TEAM_FRIENDLY: Fred_outline = 0x0000c0; break; + case TEAM_NEUTRAL: Fred_outline = 0x007f7f; break; + case TEAM_UNKNOWN: Fred_outline = 0x684010; break; + case TEAM_HOSTILE: Fred_outline = 0x7f0000; break; + default: + Fred_outline = 0x7f007f; break; + } + + } else if ((objp->type == OBJ_START) && Show_outlines) { + Fred_outline = 0x007f00; + + } else + Fred_outline = 0; + + // build flags + if ((Show_ship_models || Show_outlines) && ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START))){ + if (Show_ship_models){ + j = MR_NORMAL; + } else { + j = MR_NO_POLYS; + } + + if(Show_dock_points){ + j |= MR_BAY_PATHS; + } + + if(Show_paths_fred){ + j |= MR_SHOW_PATHS; + } + + z = objp->instance; + + model_clear_instance( Ships[z].modelnum ); + +// if (!viewpoint || OBJ_INDEX(objp) != cur_object_index) + { + if (Fred_outline) { + model_set_outline_color(Fred_outline >> 16, (Fred_outline >> 8) & 0xff, Fred_outline & 0xff); + model_render(Ships[z].modelnum, &objp->orient, &objp->pos, j | MR_SHOW_OUTLINE); + } else { + model_render(Ships[z].modelnum, &objp->orient, &objp->pos, j); + } + } + + } else { + int r = 0, g = 0, b = 0; + + if (objp->type == OBJ_SHIP) + { + if (!Show_ships) + return; + + switch (Ships[objp->instance].team) { + case TEAM_FRIENDLY: r = 0; g = 0; b = 192; break; + case TEAM_NEUTRAL: r = 0; g = 127; b = 127; break; + case TEAM_UNKNOWN: r = 104; g = 64; b = 16; break; + case TEAM_HOSTILE: r = 127; g = 0; b = 0; break; + default: r = 127; g = 0; b = 0; break; + } + + } else if (objp->type == OBJ_START) { + r = 0; g = 127; b = 0; + + } else if (objp->type == OBJ_WAYPOINT) { + r = 96; g = 0; b = 112; + + } else if (objp->type == OBJ_POINT) { + if (objp->instance != BRIEFING_LOOKAT_POINT_ID) { + Assert(Briefing_dialog); + Briefing_dialog->draw_icon(objp); + goto skip; + } + + r = 196; g = 32; b = 196; + + } else + Assert(0); + + if (Fred_outline) + draw_orient_sphere2(Fred_outline, objp, r, g, b); + else + draw_orient_sphere(objp, r, g, b); + } + +skip: + if (objp->type == OBJ_WAYPOINT) + for (j=0; jtype == OBJ_WAYPOINT) + if ((o2->instance == objp->instance - 1) || (o2->instance == objp->instance + 1)) + rpd_line(&o2->pos, &objp->pos); + } + + render_model_x(&objp->pos, The_grid); + render_count++; +} + +void display_distances() +{ + char buf[20]; + object *objp, *o2; + vector pos; + vertex v; + + gr_set_color(255, 0, 0); + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) + { + if (objp->flags & OF_MARKED) + { + o2 = GET_NEXT(objp); + while (o2 != END_OF_LIST(&obj_used_list)) + { + if (o2->flags & OF_MARKED) + { + rpd_line(&objp->pos, &o2->pos); + vm_vec_avg(&pos, &objp->pos, &o2->pos); + g3_rotate_vertex(&v, &pos); + if (!(v.codes & CC_BEHIND)) + if (!(g3_project_vertex(&v) & PF_OVERFLOW)) { + sprintf(buf, "%.1f", vm_vec_dist(&objp->pos, &o2->pos)); + gr_string_win((int) v.sx, (int) v.sy, buf); + } + } + + o2 = GET_NEXT(o2); + } + } + + objp = GET_NEXT(objp); + } +} + +void display_ship_info() +{ + char buf[512], pos[80]; + int render = 1; + object *objp; + vertex v; + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + Assert(objp->type != OBJ_NONE); + Fred_outline = 0; + render = 1; + if (OBJ_INDEX(objp) == cur_object_index) + Fred_outline = 0xffffff; + else if (objp->flags & OF_MARKED) // is it a marked object? + Fred_outline = 0x9fff00; + else + Fred_outline = 0; + + if ((objp->type == OBJ_WAYPOINT) && !Show_waypoints) + render = 0; + + if ((objp->type == OBJ_START) && !Show_starts) + render = 0; + + if (objp->type == OBJ_SHIP) { + if (!Show_ships) + render = 0; + + if ((Ships[objp->instance].team == TEAM_FRIENDLY) && !Show_friendly) + render = 0; + + if ((Ships[objp->instance].team == TEAM_HOSTILE) && !Show_hostile) + render = 0; + + if ((Ships[objp->instance].team == TEAM_NEUTRAL) && !Show_neutral) + render = 0; + } + + if (objp->flags & OF_HIDDEN) + render = 0; + + g3_rotate_vertex(&v, &objp->pos); + if (!(v.codes & CC_BEHIND) && render) + if (!(g3_project_vertex(&v) & PF_OVERFLOW)) { + *buf = 0; + if (Show_ship_info) + { + if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) { + ship *shipp; + int ship_type; + + shipp = &Ships[objp->instance]; + ship_type = shipp->ship_info_index; + ASSERT(ship_type >= 0); + sprintf(buf, "%s\n%s", shipp->ship_name, Ship_info[ship_type].short_name); + + } else if (objp->type == OBJ_WAYPOINT) { + sprintf(buf, "%s\nWaypoint %d", + Waypoint_lists[objp->instance / 65536].name, + (objp->instance & 0xffff) + 1); + + } else if (objp->type == OBJ_POINT) { + if (objp->instance == BRIEFING_LOOKAT_POINT_ID) + strcpy(buf, "Camera lookat point"); + else + strcpy(buf, "Briefing icon"); + + } else if (objp->type == OBJ_JUMP_NODE) { + strcpy(buf, "Jump Node"); + } else + Assert(0); + } + + if (Show_coordinates) + { + sprintf(pos, "( %.0f , %.0f , %.0f )", objp->pos.x, objp->pos.y, objp->pos.z); + if (*buf) + strcat(buf, "\n"); + + strcat(buf, pos); + } + + if (*buf) + { + if (Fred_outline) + gr_set_color(Fred_outline >> 16, (Fred_outline >> 8) & 0xff, Fred_outline & 0xff); + else + gr_set_color(160, 160, 160); + + gr_string_win((int) v.sx, (int) v.sy, buf); + } + } + + objp = GET_NEXT(objp); + } +} + +void draw_orient_sphere(object *obj, int r, int g, int b) +{ + int flag = 0; + vertex v; + vector v1, v2; + float size; + + size = fl_sqrt(vm_vec_dist(&eye_pos, &obj->pos) / 20.0f); + if (size < LOLLIPOP_SIZE) + size = LOLLIPOP_SIZE; + + if ((obj->type != OBJ_WAYPOINT) && (obj->type != OBJ_POINT)) + { + flag = (vm_vec_dotprod(&eye_orient.fvec, &obj->orient.fvec) < 0.0f); + v1 = v2 = obj->pos; + vm_vec_scale_add2(&v1, &obj->orient.fvec, size); + vm_vec_scale_add2(&v2, &obj->orient.fvec, size * 1.5f); + + if (!flag) { + gr_set_color(192, 192, 192); + rpd_line(&v1, &v2); + } + } + + gr_set_color(r, g, b); + g3_rotate_vertex(&v, &obj->pos); + if (!(v.codes & CC_BEHIND)) + if (!(g3_project_vertex(&v) & PF_OVERFLOW)) + g3_draw_sphere(&v, size); + + if (flag) { + gr_set_color(192, 192, 192); + rpd_line(&v1, &v2); + } +} + +void draw_orient_sphere2(int col, object *obj, int r, int g, int b) +{ + int flag = 0; + vertex v; + vector v1, v2; + float size; + + size = fl_sqrt(vm_vec_dist(&eye_pos, &obj->pos) / 20.0f); + if (size < LOLLIPOP_SIZE) + size = LOLLIPOP_SIZE; + + if ((obj->type != OBJ_WAYPOINT) && (obj->type != OBJ_POINT)) + { + flag = (vm_vec_dotprod(&eye_orient.fvec, &obj->orient.fvec) < 0.0f); + + v1 = v2 = obj->pos; + vm_vec_scale_add2(&v1, &obj->orient.fvec, size); + vm_vec_scale_add2(&v2, &obj->orient.fvec, size * 1.5f); + + if (!flag) { + gr_set_color(192, 192, 192); + rpd_line(&v1, &v2); + } + } + + g3_rotate_vertex(&v, &obj->pos); + if (!(v.codes & CC_BEHIND)) + if (!(g3_project_vertex(&v) & PF_OVERFLOW)) + { + gr_set_color((col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff); + g3_draw_sphere(&v, size); + gr_set_color(r, g, b); + g3_draw_sphere(&v, size * 0.75f); + } + + if (flag) { + gr_set_color(192, 192, 192); + rpd_line(&v1, &v2); + } +} + +void render_model_x(vector *pos, grid *gridp, int col_scheme) +{ + vector gpos; // Location of point on grid. + vector tpos; + float dxz; + plane tplane; + vector *gv; + + if (!Show_grid_positions) + return; + + tplane.A = gridp->gmatrix.uvec.x; + tplane.B = gridp->gmatrix.uvec.y; + tplane.C = gridp->gmatrix.uvec.z; + tplane.D = gridp->planeD; + + compute_point_on_plane(&gpos, &tplane, pos); + dxz = vm_vec_dist(pos, &gpos)/8.0f; + gv = &gridp->gmatrix.uvec; + if (gv->x * pos->x + gv->y * pos->y + gv->z * pos->z < -gridp->planeD) + gr_set_color(0, 127, 0); + else + gr_set_color(192, 192, 192); + + + rpd_line(&gpos, pos); // Line from grid to object center. + + tpos = gpos; + + vm_vec_scale_add2(&gpos, &gridp->gmatrix.rvec, -dxz/2); + vm_vec_scale_add2(&gpos, &gridp->gmatrix.fvec, -dxz/2); + + vm_vec_scale_add2(&tpos, &gridp->gmatrix.rvec, dxz/2); + vm_vec_scale_add2(&tpos, &gridp->gmatrix.fvec, dxz/2); + + rpd_line(&gpos, &tpos); + + vm_vec_scale_add2(&gpos, &gridp->gmatrix.rvec, dxz); + vm_vec_scale_add2(&tpos, &gridp->gmatrix.rvec, -dxz); + + rpd_line(&gpos, &tpos); +} + +void render_active_rect(void) +{ + if (box_marking) { + gr_set_color(255, 255, 255); + gr_line(marking_box.x1, marking_box.y1, marking_box.x1, marking_box.y2); + gr_line(marking_box.x1, marking_box.y2, marking_box.x2, marking_box.y2); + gr_line(marking_box.x2, marking_box.y2, marking_box.x2, marking_box.y1); + gr_line(marking_box.x2, marking_box.y1, marking_box.x1, marking_box.y1); + } +} + + +void process_movement_keys(int key, vector *mvec, angles *angs) +{ + int raw_key; + + mvec->x = 0.0f; + mvec->y = 0.0f; + mvec->z = 0.0f; + angs->p = 0.0f; + angs->b = 0.0f; + angs->h = 0.0f; + + raw_key = key & 0xff; + + switch (raw_key) { + case KEY_PAD1: mvec->x += -1.0f; break; + case KEY_PAD3: mvec->x += +1.0f; break; + case KEY_PADPLUS: mvec->y += -1.0f; break; + case KEY_PADMINUS: mvec->y += +1.0f; break; + case KEY_A: mvec->z += +1.0f; break; + case KEY_Z: mvec->z += -1.0f; break; + case KEY_PAD4: angs->h += -0.1f; break; + case KEY_PAD6: angs->h += +0.1f; break; + case KEY_PAD8: angs->p += -0.1f; break; + case KEY_PAD2: angs->p += +0.1f; break; + case KEY_PAD7: angs->b += -0.1f; break; + case KEY_PAD9: angs->b += +0.1f; break; + + } + + if (key & KEY_SHIFTED) { + vm_vec_scale(mvec, 5.0f); + angs->p *= 5.0f; + angs->b *= 5.0f; + angs->h *= 5.0f; + } +} + +void process_controls(vector *pos, matrix *orient, float frametime, int key, int mode) +{ + if (Flying_controls_mode) { + grid_read_camera_controls(&view_controls, frametime); + + if (key_get_shift_status()) + memset(&view_controls, 0, sizeof(control_info)); + + if ((fabs(view_controls.pitch) > (frametime / 100)) && + (fabs(view_controls.vertical) > (frametime / 100)) && + (fabs(view_controls.heading) > (frametime / 100)) && + (fabs(view_controls.sideways) > (frametime / 100)) && + (fabs(view_controls.bank) > (frametime / 100)) && + (fabs(view_controls.forward) > (frametime / 100))) + Update_window = 1; + + flFrametime = frametime; + physics_read_flying_controls(orient, &view_physics, &view_controls, flFrametime); + if (mode) + physics_sim_editor(pos, orient, &view_physics, frametime); + else + physics_sim(pos, orient, &view_physics, frametime); + + } else { + vector movement_vec, rel_movement_vec; + angles rotangs; + matrix newmat, rotmat; + + process_movement_keys(key, &movement_vec, &rotangs); + vm_vec_rotate(&rel_movement_vec, &movement_vec, &The_grid->gmatrix); + vm_vec_add2(pos, &rel_movement_vec); + + vm_angles_2_matrix(&rotmat, &rotangs); + if (rotangs.h && Universal_heading) + vm_transpose_matrix(orient); + vm_matrix_x_matrix(&newmat, orient, &rotmat); + *orient = newmat; + if (rotangs.h && Universal_heading) + vm_transpose_matrix(orient); + } +} + +int Fred_grid_colors_inited = 0; +color Fred_grid_bright, Fred_grid_dark, Fred_grid_bright_aa, Fred_grid_dark_aa; + +// Renders a grid defined in a grid struct +void fred_render_grid(grid *gridp) +{ + int i, ncols, nrows; + + if ( !Fred_grid_colors_inited ) { + Fred_grid_colors_inited = 1; + + gr_init_alphacolor( &Fred_grid_dark_aa, 64, 64, 64, 255 ); + gr_init_alphacolor( &Fred_grid_bright_aa, 128, 128, 128, 255 ); + gr_init_color( &Fred_grid_dark, 64, 64, 64 ); + gr_init_color( &Fred_grid_bright, 128, 128, 128 ); + } + + ncols = gridp->ncols; + nrows = gridp->nrows; + if (double_fine_gridlines) { + ncols *= 2; + nrows *= 2; + } + + if (Aa_gridlines) + gr_set_color_fast(&Fred_grid_dark_aa); + else + gr_set_color_fast(&Fred_grid_dark); + + // Draw the column lines. + for (i=0; i<=ncols; i++) + rpd_line(&gridp->gpoints1[i], &gridp->gpoints2[i]); + + // Draw the row lines. + for (i=0; i<=nrows; i++) + rpd_line(&gridp->gpoints3[i], &gridp->gpoints4[i]); + + ncols = gridp->ncols / 2; + nrows = gridp->nrows / 2; + + // now draw the larger, brighter gridlines that is x10 the scale of smaller one. + if (Aa_gridlines) + gr_set_color_fast(&Fred_grid_bright_aa); + else + gr_set_color_fast(&Fred_grid_bright); + + for (i=0; i<=ncols; i++) + rpd_line(&gridp->gpoints5[i], &gridp->gpoints6[i]); + + for (i=0; i<=nrows; i++) + rpd_line(&gridp->gpoints7[i], &gridp->gpoints8[i]); +} + +void render_frame() +{ + char buf[256]; + int x, y, w, h, inst; + vector pos; + vertex v; + angles a; + + g3_end_frame(); + + gr_reset_clip(); + gr_clear(); + + if (Briefing_dialog) { + CRect rect; + + Fred_main_wnd->GetClientRect(rect); + True_rw = rect.Width(); + True_rh = rect.Height(); + if (Fixed_briefing_size) { + True_rw = BRIEF_GRID_W; + True_rh = BRIEF_GRID_H; + + } else { + if ((float) True_rh / (float) True_rw > (float) BRIEF_GRID_H / (float) BRIEF_GRID_W) { + True_rh = (int) ((float) BRIEF_GRID_H * (float) True_rw / (float) BRIEF_GRID_W); + + } else { // Fred is wider than briefing window + True_rw = (int) ((float) BRIEF_GRID_W * (float) True_rh / (float) BRIEF_GRID_H); + } + } + + g3_start_frame(0); + gr_set_color(255, 255, 255); + gr_line(0, True_rh, True_rw, True_rh); + gr_line(True_rw, 0, True_rw, True_rh); + g3_end_frame(); + gr_set_clip(0, 0, True_rw, True_rh); + } + + g3_start_frame(1); // 1 means use zbuffering + gr_set_font(Fred_font); + light_reset(); + + g3_set_view_matrix(&eye_pos, &eye_orient, 0.5f); + Viewer_pos = eye_pos; // for starfield code + + if ( Bg_bitmap_dialog ) { + stars_draw( Show_stars, 1, Show_stars, 0 ); + } else { + stars_draw( Show_stars, Show_stars, Show_stars, 0 ); + } + + if (Show_horizon) { + gr_set_color(128, 128, 64); + g3_draw_horizon_line(); + } + + if (Show_asteroid_field) { + gr_set_color(192, 96, 16); + draw_asteroid_field(); + } + + if (Show_grid) + fred_render_grid(The_grid); + if (Bg_bitmap_dialog) + hilight_bitmap(); + + gr_set_color(0, 0, 64); + render_models(); + if (Show_distances) + display_distances(); + + display_ship_info(); + display_active_ship_subsystem(); + render_active_rect(); + + if (query_valid_object(Cursor_over)) { // display a tool-tip like infobox + pos = Objects[Cursor_over].pos; + inst = Objects[Cursor_over].instance; + if ((Objects[Cursor_over].type == OBJ_SHIP) || (Objects[Cursor_over].type == OBJ_START)) { + vm_extract_angles_matrix(&a, &Objects[Cursor_over].orient); + sprintf(buf, "%s\n%s\n( %.1f , %.1f , %.1f )\nHeading: %.2f\nPitch: %.2f\nBank: %.2f", + Ships[inst].ship_name, Ship_info[Ships[inst].ship_info_index].short_name, + pos.x, pos.y, pos.z, a.h, a.p, a.b); + + } else if (Objects[Cursor_over].type == OBJ_WAYPOINT) { + sprintf(buf, "%s\nWaypoint %d\n( %.1f , %.1f , %.1f )", + Waypoint_lists[inst / 65536].name, (inst & 0xffff) + 1, pos.x, pos.y, pos.z); + + } else if (Objects[Cursor_over].type == OBJ_POINT) { + sprintf(buf, "Briefing icon\n( %.1f , %.1f , %.1f )", pos.x, pos.y, pos.z); + + } else + sprintf(buf, "( %.1f , %.1f , %.1f )", pos.x, pos.y, pos.z); + + g3_rotate_vertex(&v, &pos); + if (!(v.codes & CC_BEHIND)) + if (!(g3_project_vertex(&v) & PF_OVERFLOW)) { + x = (int) v.sx; + y = (int) v.sy + 20; + gr_get_string_size_win(&w, &h, buf); + gr_set_color(192, 192, 192); + gr_rect(x-1, y-1, w+2, h+2); + gr_set_color(255, 255, 255); + gr_line(x-2, y-2, x+w+1, y-2); + gr_line(x-2, y-2, x-2, y+h+1); + gr_line(x+w+1, y-2, x+w+1, y+h+1); + gr_line(x-2, y+h+1, x+w+1, y+h+1); + + gr_set_color(0, 0, 0); + gr_string_win(x, y, buf); + } + } + + gr_set_color(0, 160, 0); + jumpnode_render_all(); + + sprintf(buf, "( %.1f , %.1f , %.1f )", eye_pos.x, eye_pos.y, eye_pos.z); + gr_get_string_size_win(&w, &h, buf); + gr_set_color(192, 192, 192); + gr_string_win(gr_screen.max_w - w - 2, 2, buf); + + g3_end_frame(); + render_compass(); + gr_reset_clip(); + if (Briefing_dialog) + gr_set_clip(0, 0, True_rw, True_rh); + + g3_start_frame(0); + g3_set_view_matrix(&eye_pos, &eye_orient, 0.5f); +} + +void game_do_frame() +{ + int key, cmode; + vector viewer_position, control_pos; + object *objp; + matrix control_orient; + + inc_mission_time(); + + viewer_position = my_orient.fvec; + vm_vec_scale(&viewer_position,my_pos.z); + + if ((viewpoint == 1) && !query_valid_object(view_obj)) + viewpoint = 0; + + key = key_inkey(); + process_system_keys(key); + cmode = Control_mode; + if ((viewpoint == 1) && !cmode) + cmode = 2; + + control_pos = Last_control_pos; + control_orient = Last_control_orient; + +// if ((key & KEY_MASK) == key) // unmodified + switch (cmode) { + case 0: // Control the viewer's location and orientation + process_controls(&view_pos, &view_orient, f2fl(Frametime), key, 1); + control_pos = view_pos; + control_orient = view_orient; + break; + + case 2: // Control viewpoint object + process_controls(&Objects[view_obj].pos, &Objects[view_obj].orient, f2fl(Frametime), key); + object_moved(&Objects[view_obj]); + control_pos = Objects[view_obj].pos; + control_orient = Objects[view_obj].orient; + break; + + case 1: // Control the current object's location and orientation + if (query_valid_object()) { + vector delta_pos, leader_old_pos; + matrix leader_orient, leader_transpose, tmp; + object *leader; + + leader = &Objects[cur_object_index]; + leader_old_pos = leader->pos; // save original position + leader_orient = leader->orient; // save original orientation + vm_copy_transpose_matrix(&leader_transpose, &leader_orient); + + process_controls(&leader->pos, &leader->orient, f2fl(Frametime), key); + vm_vec_sub(&delta_pos, &leader->pos, &leader_old_pos); // get position change + control_pos = leader->pos; + control_orient = leader->orient; + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + Assert(objp->type != OBJ_NONE); + if ((objp->flags & OF_MARKED) && (cur_object_index != OBJ_INDEX(objp))) { + if (Group_rotate) { + matrix rot_trans; + vector tmpv1, tmpv2; + + // change rotation matrix to rotate in opposite direction. This rotation + // matrix is what the leader ship has rotated by. + vm_copy_transpose_matrix(&rot_trans, &view_physics.last_rotmat); + + // get point relative to our point of rotation (make POR the origin). Since + // only the leader has been moved yet, and not the objects, we have to use + // the old leader's position. + vm_vec_sub(&tmpv1, &objp->pos, &leader_old_pos); + + // convert point from real-world coordinates to leader's relative coordinate + // system (z=forward vec, y=up vec, x=right vec + vm_vec_rotate(&tmpv2, &tmpv1, &leader_orient); + + // now rotate the point by the transpose from above. + vm_vec_rotate(&tmpv1, &tmpv2, &rot_trans); + + // convert point back into real-world coordinates + vm_vec_rotate(&tmpv2, &tmpv1, &leader_transpose); + + // and move origin back to real-world origin. Object is now at it's correct + // position. Note we used the leader's new position, instead of old position. + vm_vec_add(&objp->pos, &leader->pos, &tmpv2); + + // Now fix the object's orientation to what it should be. + vm_matrix_x_matrix(&tmp, &objp->orient, &view_physics.last_rotmat); + vm_orthogonalize_matrix(&tmp); // safety check + objp->orient = tmp; + + } else { + vm_vec_add2(&objp->pos, &delta_pos); + vm_matrix_x_matrix(&tmp, &objp->orient, &view_physics.last_rotmat); + objp->orient = tmp; + } + } + + objp = GET_NEXT(objp); + } + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) + object_moved(objp); + + objp = GET_NEXT(objp); + } + + set_modified(); + } + + break; + + default: + Assert(0); + } + + if (Lookat_mode && query_valid_object()) { + float dist; + + dist = vm_vec_dist(&view_pos, &Objects[cur_object_index].pos); + vm_vec_scale_add(&view_pos, &Objects[cur_object_index].pos, &view_orient.fvec, -dist); + } + + switch (viewpoint) + { + case 0: + eye_pos = view_pos; + eye_orient = view_orient; + break; + + case 1: + eye_pos = Objects[view_obj].pos; + eye_orient = Objects[view_obj].orient; + break; + + default: + Assert(0); + } + + maybe_create_new_grid(The_grid, &eye_pos, &eye_orient); + + if (Cursor_over != Last_cursor_over) { + Last_cursor_over = Cursor_over; + Update_window = 1; + } + + // redraw screen if controlled object moved or rotated + if (vm_vec_cmp(&control_pos, &Last_control_pos) || vm_matrix_cmp(&control_orient, &Last_control_orient)) { + Update_window = 1; + Last_control_pos = control_pos; + Last_control_orient = control_orient; + } + + // redraw screen if current viewpoint moved or rotated + if (vm_vec_cmp(&eye_pos, &Last_eye_pos) || vm_matrix_cmp(&eye_orient, &Last_eye_orient)) { + Update_window = 1; + Last_eye_pos = eye_pos; + Last_eye_orient = eye_orient; + } +} + +void hilight_bitmap() +{ + /* + int i; + vertex p[4]; + + if (Starfield_bitmaps[Cur_bitmap].bitmap_index == -1) // can't draw if no bitmap + return; + + for (i=0; i<4; i++) + { + g3_rotate_faraway_vertex(&p[i], &Starfield_bitmaps[Cur_bitmap].points[i]); + if (p[i].codes & CC_BEHIND) + return; + + g3_project_vertex(&p[i]); + if (p[i].flags & PF_OVERFLOW) + return; + } + + gr_set_color(255, 255, 255); + g3_draw_line(&p[0], &p[1]); + g3_draw_line(&p[1], &p[2]); + g3_draw_line(&p[2], &p[3]); + g3_draw_line(&p[3], &p[0]); + */ +} + +void draw_asteroid_field() +{ + int i, j; + vector p[8], ip[8]; + vertex v[8], iv[8]; + + for (i=0; i<1 /*MAX_ASTEROID_FIELDS*/; i++) + if (Asteroid_field.num_initial_asteroids) { + p[0].x = p[2].x = p[4].x = p[6].x = Asteroid_field.min_bound.x; + p[1].x = p[3].x = p[5].x = p[7].x = Asteroid_field.max_bound.x; + p[0].y = p[1].y = p[4].y = p[5].y = Asteroid_field.min_bound.y; + p[2].y = p[3].y = p[6].y = p[7].y = Asteroid_field.max_bound.y; + p[0].z = p[1].z = p[2].z = p[3].z = Asteroid_field.min_bound.z; + p[4].z = p[5].z = p[6].z = p[7].z = Asteroid_field.max_bound.z; + + for (j=0; j<8; j++) + g3_rotate_vertex(&v[j], &p[j]); + + g3_draw_line(&v[0], &v[1]); + g3_draw_line(&v[2], &v[3]); + g3_draw_line(&v[4], &v[5]); + g3_draw_line(&v[6], &v[7]); + g3_draw_line(&v[0], &v[2]); + g3_draw_line(&v[1], &v[3]); + g3_draw_line(&v[4], &v[6]); + g3_draw_line(&v[5], &v[7]); + g3_draw_line(&v[0], &v[4]); + g3_draw_line(&v[1], &v[5]); + g3_draw_line(&v[2], &v[6]); + g3_draw_line(&v[3], &v[7]); + + + // maybe draw inner box + if (Asteroid_field.has_inner_bound) { + + gr_set_color(16, 192, 92); + + ip[0].x = ip[2].x = ip[4].x = ip[6].x = Asteroid_field.inner_min_bound.x; + ip[1].x = ip[3].x = ip[5].x = ip[7].x = Asteroid_field.inner_max_bound.x; + ip[0].y = ip[1].y = ip[4].y = ip[5].y = Asteroid_field.inner_min_bound.y; + ip[2].y = ip[3].y = ip[6].y = ip[7].y = Asteroid_field.inner_max_bound.y; + ip[0].z = ip[1].z = ip[2].z = ip[3].z = Asteroid_field.inner_min_bound.z; + ip[4].z = ip[5].z = ip[6].z = ip[7].z = Asteroid_field.inner_max_bound.z; + + for (j=0; j<8; j++) + g3_rotate_vertex(&iv[j], &ip[j]); + + g3_draw_line(&iv[0], &iv[1]); + g3_draw_line(&iv[2], &iv[3]); + g3_draw_line(&iv[4], &iv[5]); + g3_draw_line(&iv[6], &iv[7]); + g3_draw_line(&iv[0], &iv[2]); + g3_draw_line(&iv[1], &iv[3]); + g3_draw_line(&iv[4], &iv[6]); + g3_draw_line(&iv[5], &iv[7]); + g3_draw_line(&iv[0], &iv[4]); + g3_draw_line(&iv[1], &iv[5]); + g3_draw_line(&iv[2], &iv[6]); + g3_draw_line(&iv[3], &iv[7]); + } + + } +} + +// See if object "objnum" obstructs the vector from point p0 to point p1. +// If so, return true and stuff hit point in *hitpos. +// If not, return false. +int object_check_collision(object *objp, vector *p0, vector *p1, vector *hitpos) +{ + mc_info mc; + + if ((objp->type == OBJ_NONE) || (objp->type == OBJ_POINT)) + return 0; + + if ((objp->type == OBJ_WAYPOINT) && !Show_waypoints) + return 0; + + if ((objp->type == OBJ_START) && !Show_starts) + return 0; + + if (objp->type == OBJ_SHIP) { + if (!Show_ships) + return 0; + + if ((Ships[objp->instance].team == TEAM_FRIENDLY) && !Show_friendly) + return 0; + + if ((Ships[objp->instance].team == TEAM_HOSTILE) && !Show_hostile) + return 0; + + if ((Ships[objp->instance].team == TEAM_NEUTRAL) && !Show_neutral) + return 0; + } + + if (objp->flags & OF_HIDDEN) + return 0; + + if ((Show_ship_models || Show_outlines) && (objp->type == OBJ_SHIP)) { + mc.model_num = Ships[objp->instance].modelnum; // Fill in the model to check + + } else if ((Show_ship_models || Show_outlines) && (objp->type == OBJ_START)) { + mc.model_num = Ships[objp->instance].modelnum; // Fill in the model to check + + } else + return fvi_ray_sphere(hitpos, p0, p1, &objp->pos, (objp->radius > 0.1f) ? objp->radius : LOLLIPOP_SIZE); + + mc.orient = &objp->orient; // The object's orient + mc.pos = &objp->pos; // The object's position + mc.p0 = p0; // Point 1 of ray to check + mc.p1 = p1; // Point 2 of ray to check + mc.flags = MC_CHECK_MODEL | MC_CHECK_RAY; // flags + model_collide(&mc); + *hitpos = mc.hit_point_world; + if ( mc.num_hits < 1 ) { + // check shield + mc.orient = &objp->orient; // The object's orient + mc.pos = &objp->pos; // The object's position + mc.p0 = p0; // Point 1 of ray to check + mc.p1 = p1; // Point 2 of ray to check + mc.flags = MC_CHECK_SHIELD; // flags + model_collide(&mc); + *hitpos = mc.hit_point_world; + } + + return mc.num_hits; +} + +// Finds the closest object or waypoint under the mouse cursor and returns +// it's index, or -1 if nothing there. +int select_object(int cx, int cy) +{ + int best = -1; + double dist, best_dist = 9e99; + vector p0, p1, v, hitpos; + vertex vt; + object *ptr; + + if (Briefing_dialog) { + best = Briefing_dialog->check_mouse_hit(cx, cy); + if (best >= 0) + return best; + } + +/* gr_reset_clip(); + g3_start_frame(0); + g3_set_view_matrix(&eye_pos, &eye_orient, 0.5f);*/ + + // Get 3d vector specified by mouse cursor location. + g3_point_to_vec(&v, cx, cy); + +// g3_end_frame(); + if (!v.x && !v.y && !v.z) // zero vector + return -1; + + p0 = view_pos; + vm_vec_scale_add(&p1, &p0, &v, 100.0f); + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) + { + if (object_check_collision(ptr, &p0, &p1, &hitpos)) { + hitpos.x = ptr->pos.x - view_pos.x; + hitpos.y = ptr->pos.y - view_pos.y; + hitpos.z = ptr->pos.z - view_pos.z; + dist = hitpos.x * hitpos.x + hitpos.y * hitpos.y + hitpos.z * hitpos.z; + if (dist < best_dist) { + best = OBJ_INDEX(ptr); + best_dist = dist; + } + } + + ptr = GET_NEXT(ptr); + } + + if (best >= 0) + return best; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) + { + g3_rotate_vertex(&vt, &ptr->pos); + if (!(vt.codes & CC_BEHIND)) + if (!(g3_project_vertex(&vt) & PF_OVERFLOW)) { + hitpos.x = vt.sx - cx; + hitpos.y = vt.sy - cy; + dist = hitpos.x * hitpos.x + hitpos.y * hitpos.y; + if ((dist < 8) && (dist < best_dist)) { + best = OBJ_INDEX(ptr); + best_dist = dist; + } + } + + ptr = GET_NEXT(ptr); + } + + return best; +} + +void render_compass(void) +{ + vector v, eye = { 0.0f }; + + if (!Show_compass) + return; + + gr_set_clip(gr_screen.max_w - 100, 0, 100, 100); + g3_start_frame(0); // required !!! + vm_vec_scale_add2(&eye, &eye_orient.fvec, -1.5f); + g3_set_view_matrix(&eye, &eye_orient, 1.0f); + + v.x = 1.0f; + v.y = v.z = 0.0f; + if (vm_vec_dotprod(&eye, &v) < 0.0f) + gr_set_color(159, 20, 20); + else + gr_set_color(255, 32, 32); + draw_compass_arrow(&v); + + v.y = 1.0f; + v.x = v.z = 0.0f; + if (vm_vec_dotprod(&eye, &v) < 0.0f) + gr_set_color(20, 159, 20); + else + gr_set_color(32, 255, 32); + draw_compass_arrow(&v); + + v.z = 1.0f; + v.x = v.y = 0.0f; + if (vm_vec_dotprod(&eye, &v) < 0.0f) + gr_set_color(20, 20, 159); + else + gr_set_color(32, 32, 255); + draw_compass_arrow(&v); + + g3_end_frame(); +} + +void draw_compass_arrow(vector *v0) +{ + vector v1 = { 0.0f }; + vertex tv0, tv1; + + g3_rotate_vertex(&tv0, v0); + g3_rotate_vertex(&tv1, &v1); + g3_project_vertex(&tv0); + g3_project_vertex(&tv1); +// tv0.sx = (tv0.sx - tv1.sx) * 1 + tv1.sx; +// tv0.sy = (tv0.sy - tv1.sy) * 1 + tv1.sy; + g3_draw_line(&tv0, &tv1); +} + + +void inc_mission_time() +{ + fix thistime; + + thistime = timer_get_fixed_seconds(); + if (!lasttime) + Frametime = F1_0 / 30; + else + Frametime = thistime - lasttime; + + if (Frametime > MAX_FRAMETIME) + Frametime = MAX_FRAMETIME; + + if (Frametime < MIN_FRAMETIME) + Frametime = MIN_FRAMETIME; + + Missiontime += Frametime; + lasttime = thistime; +} diff --git a/src/fred2/fredstubs.cpp b/src/fred2/fredstubs.cpp new file mode 100644 index 0000000..7b5a36b --- /dev/null +++ b/src/fred2/fredstubs.cpp @@ -0,0 +1,633 @@ +/* + * $Logfile: /Freespace2/code/Fred2/FredStubs.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Bogus C file for functions and variable stubs that Fred needs because it + * includes some libraries that makes functions calls to other libraries that FRED + * doesn't include. In a perfect world, programmers would work harder to keep + * the code they write more self-contained and not tie all the code everywhere + * to each other. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 26 9/27/99 10:13a Jefff + * another stub for not_in_demo popup + * + * 25 9/13/99 5:16p Dave + * New stubs + * + * 24 9/06/99 8:43p Dave + * Checked in some stubs. + * + * 23 9/01/99 10:14a Dave + * Pirate bob. + * + * 22 8/26/99 10:15a Dave + * Don't apply beam whacks to docked ships. + * + * 21 8/04/99 2:24a Dave + * Fixed escort gauge ordering for dogfight. + * + * 20 7/08/99 10:53a Dave + * New multiplayer interpolation scheme. Not 100% done yet, but still + * better than the old way. + * + * 19 6/10/99 11:11a Jamesa + * More stubs. + * + * 18 4/12/99 10:36a Johnson + * Stub for game_hacked_data + * + * 17 3/19/99 6:15p Dave + * Stubs + * + * 16 2/23/99 7:03p Dave + * Rewrote a horribly mangled and evil team loadout dialog. Bugs gone. + * + * 15 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 14 1/14/99 12:49a Dave + * Made an attempt to put briefing icons back into FRED. + * + * 13 1/12/99 5:45p Dave + * Whole slew of new stubs. + * + * 12 1/08/99 2:07p Dave + * Temporary checkin. Super early support for AWACS and beam weapons. + * + * 11 12/06/98 2:36p Dave + * Stub. + * + * 10 12/03/98 10:14a Dave + * + * 9 11/30/98 5:32p Dave + * Fixed up Fred support for software mode. + * + * 8 11/19/98 8:05a Dave + * Psnet stub + * + * 7 11/12/98 12:12a Dave + * Stub for new turret fired packet. + * + * 6 10/21/98 9:56a Dave + * Fixed stupid linker thing. + * + * 5 10/13/98 9:57a Dave + * + * 4 10/13/98 9:27a Dave + * Started neatening up freespace.h + * + * 3 10/12/98 1:01p Dave + * Fixed object rotation bug (uninitialized data). Changed a few stubs to + * correspond to new var names. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 238 9/20/98 9:46p Dave + * send_change_iff_packet() stub. + * + * 237 9/15/98 11:44a Dave + * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring + * scale factors. Fixed standalone filtering of MD missions to non-MD + * hosts. + * + * 236 9/12/98 2:17p Dave + * Multiplayer reinforcement fix stub. + * + * 235 8/28/98 3:28p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 234 8/26/98 2:14p Dave + * + * 233 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 232 8/11/98 9:21a Dave + * new TvT stub. + * + * 231 7/14/98 4:57p Allender + * + * 230 7/06/98 5:08p Hoffoss + * + * 229 6/22/98 11:02a Hoffoss + * + * 228 6/09/98 12:12p Hoffoss + * Added XSTR localization code. + * + * 227 6/07/98 3:25p Lawrance + * + * 226 5/25/98 2:20p Allender + * scoring_level_close() + * + * 225 5/23/98 6:53p Allender + * AL: add Skill_level to stubs + * + * 224 5/21/98 4:14a Allender + * + * 223 5/18/98 1:56a Allender + * respawn limit to 999 max + * + * 222 5/18/98 12:41a Allender + * fixed subsystem problems on clients (i.e. not reporting properly on + * damage indicator). Fixed ingame join problem with respawns. minor + * comm menu stuff + * + * 221 5/15/98 11:04p Sandeep + * fixed a bug caused by Alan. :) + * + * 220 5/14/98 2:24p Sandeep + * + * 219 5/13/98 11:34p Mike + * Model caching system. + * + * 218 5/13/98 11:16p Allender + * + * 217 5/09/98 10:35p Allender + * + * 216 5/09/98 9:49p Allender + * + * 215 5/09/98 3:38p Sandeep + * + * 214 5/08/98 11:20a Allender + * fix ingame join trouble. Small messaging fix. Enable collisions for + * friendlies again + * + * 213 5/05/98 5:12p Sandeep + * + * 212 5/05/98 11:10a Johnson + * Fix help overlay link errors + * + * 211 5/04/98 6:06p Lawrance + * Make red alert mode work! + * + * 210 4/30/98 9:15a Allender + * + * 209 4/28/98 8:07a Jasen + * JAS: Stubbed in cdrom_path + * + * 208 4/27/98 10:17p Allender + * + * 207 4/27/98 8:56p Jim + * stub for modifcation of scoring_eval_hit + * + * 206 4/26/98 12:42p Sandeep + * stubbed send_turret_fired_packet. Stop breaking fred! :) + * + * 205 4/25/98 1:57p Mike + * Oops, someone else had just stubbed out big_explosion_flash. + * + * 204 4/25/98 1:56p Mike + * Stub out big_explosion_flash(). + * + * 203 4/25/98 1:20p Jim + * stub for big_explosion_flash + * + * 202 4/20/98 8:54a Mike + * Stub opposing_team_mask(). + * + * 201 4/13/98 9:53p Hoffoss + * + * 200 4/13/98 2:48p Allender + * countermeasure sucess packet + * + * 199 4/12/98 12:15p Jim + * Stub out Subspace_effect + * + * 198 4/10/98 12:16p Allender + * fix ship hit kill and debris packets + * + * 197 4/10/98 9:15a Allender + * + * 196 4/09/98 8:42p Sandeep + * + * 195 4/09/98 12:46a Sandeep + * + * 194 4/08/98 8:37a Mike + * Stub out hud_find_target_distance(). + * + * 193 4/07/98 10:11p Lawrance + * stub out hud_communications_state + * + * 192 4/07/98 2:52p Andsager + * stub out Energy_levels + * + * 191 4/05/98 10:33a John + * Stubbed in a variable + * + * 190 3/31/98 5:11p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * 189 3/30/98 10:26a Hoffoss + * + * 188 3/26/98 6:41p Lawrance + * stub out bmap paging function + * + * 187 3/19/98 10:26a Dave + * Put in several HUD_offset problems. + * + * 186 3/17/98 10:27a Johnson + * AL: stub out some asteroid network calls + * + * 185 3/14/98 4:57p Lawrance + * stub out some wingman status functions + * + * 184 3/12/98 5:36p John + * Took out any unused shaders. Made shader code take rgbc instead of + * matrix and vector since noone used it like a matrix and it would have + * been impossible to do in hardware. Made Glide implement a basic + * shader for online help. + * + * 183 3/12/98 2:21p Johnson + * Fixed some Fred bugs related to jump nodes. + * + * 182 3/11/98 10:22p Dave + * Laid groundwork for new observer HUD. Split up multi respawning into + * its own module. + * + * 181 3/11/98 9:36p Allender + * + * 180 3/11/98 12:25a Lawrance + * stub yet another HUD function. + * + * 179 3/10/98 5:08p Allender + * fixed up multiplayer death messages (I hope). changes in object update + * packets + * + * 178 3/09/98 9:54p Hoffoss + * + * 177 3/09/98 5:03p Lawrance + * stub hud function + * + * 176 3/09/98 4:30p Allender + * multiplayer secondary weapon changes. red-alert and cargo-known-delay + * sexpressions. Add time cargo revealed to ship structure + * + * 175 3/09/98 10:50a Hoffoss + * + * 174 3/09/98 12:12a Lawrance + * Add support for Red Alert missions + * + * 173 3/08/98 12:03p Allender + * changed how ship network signatures are handed out. Done at mission + * load time. Space reserved in wings for all waves/counts for their + * signatures. Fixed some secondary firing issues + * + * 172 3/06/98 5:10p Allender + * made time to: field in extended targetbox use support time to dock code + * for all docking shpis. Only display for waypoints and docking (not + * undocking). Small fixups to message menu -- not allowing depart when + * disabled. Depart is now by default ignored for all non-small ships + * + * 171 3/05/98 11:15p Hoffoss + * Changed non-game key checking to use game_check_key() instead of + * game_poll(). + * + * 170 3/04/98 5:04p Hoffoss + * stub out hud function + * + * 169 3/03/98 1:21a Lawrance + * stub out some hud escort functions + * + * 168 2/27/98 10:34a Johnson + * + * 167 2/24/98 3:08p Allender + * + * 166 2/23/98 5:07p Allender + * made net_signature in the object structure an unsigned short. Created + * permanent and non-permanent network object "pools". + * + * 165 2/19/98 7:06p Sandeep + * + * 164 2/18/98 10:34p Allender + * repair/rearm system (for single and multi) about finished. + * dock/undock and ai goals packets implemented for multiplayer + * + * 163 2/17/98 5:03p Allender + * major cdhanges to rearm repair code. All flag and variable setting + * done in one function. A little more work to do. Fix bug in squad + * messaging when hotkey was used on invalid target + * + * 162 2/17/98 8:58a Mike + * Resolve link errors with stubs in FredStubs. + * + * 161 2/12/98 5:12p Lawrance + * stub out hud function + * + * 160 2/11/98 9:44p Allender + * rearm repair code fixes. hud support view shows abort status. New + * support ship killed message. More network stats + * + * 159 2/11/98 5:47p Dave + * multiplayer packet function stub + * + * 158 2/10/98 9:55a Lawrance + * stub out cmeasure function + * + * 157 1/29/98 5:22p Dave + * Made ingame join ignore bad packets more gracefully. + * + * 156 1/29/98 9:00a Allender + * yet more stubs + * + * 155 1/28/98 7:31p Lawrance + * stub out some more hud stuff + * + * 154 1/24/98 4:00p Lawrance + * stub out some new hud functions + * + * 153 1/22/98 11:46p Hoffoss + * Fixed linking problem with Fred. + * + * 152 1/20/98 4:45p Allender + * more, uh..., umm...., more stubs -- yeah, that's it + * + * 151 1/20/98 11:43a Sandeep + * fixed unresolved external + * + * 150 1/17/98 12:35a Sandeep + * fixed fred stub build error + * + * 149 1/16/98 10:40a Lawrance + * stub out hud function + * + * 148 1/14/98 5:22p Allender + * save/restore hotkey selections when replaying the same mission + * + * 147 1/13/98 5:37p Dave + * Reworked a lot of standalone interface code. Put in single and + * multiplayer popups for death sequence. Solidified multiplayer kick + * code. + * + * 146 1/12/98 9:29p Mike + * Stub out send_mission_goal_info_packet(). + * + * 145 1/10/98 1:22a Lawrance + * fix link error in FRED + * + * 144 1/07/98 4:40p Allender + * minor modification to special messages. Fixed cargo_revealed problem + * for multiplayer and problem with is-cargo-known sexpression + * + * 143 1/05/98 10:06p Lawrance + * stub out some HUD functions + * + * 142 1/02/98 10:20p Lawrance + * stub out hud_set_default_color() + * + * 141 1/02/98 9:12p Lawrance + * remove some obsolete stubs + * + * 140 12/24/97 9:57p Lawrance + * remove stub + * + * 139 12/24/97 3:37p Hoffoss + * Moved control config stuff to seperate library to Fred can access it as + * well. + * + * 138 12/19/97 2:01p Johnson + * added stubs for game_flash + * + * 137 12/19/97 11:56a John + * Added texturing to missilie trails. Took out Alan's old sphere + * debugging code. Added palette flash effect code. + * + * 136 12/19/97 11:21a Hoffoss + * + * 135 12/18/97 8:46p Lawrance + * Move IFF_color definitions from HUD->ship, so FRED can use them. + * + * 134 12/16/97 9:32p Lawrance + * stub out demo_query_debug() + * + * 133 12/16/97 6:20p Hoffoss + * Added more debugging code for demos, and fixed a bug in demo + * recording/playback. + * + * $NoKeywords: $ + */ + +#include "pstypes.h" +#include "object.h" +#include "key.h" +#include "ship.h" +#include "2d.h" +#include "missionparse.h" +#include "psnet.h" +#include "scoring.h" + +float flFrametime; +int game_zbuffer = 1; +int Current_mission = 0xdeadbeef; +char **Builtin_mission_names; +char *Game_current_mission_filename; +CFILE *Working_demo; +struct beam_info; + +int Sun_drew = 0; + +void init_ets(struct object*){} + +control_info PlayerControls; + +char * Game_CDROM_dir = NULL; + +void game_flash(float r, float g, float b ) +{ +} + +void freespace_menu_background() +{ + gr_reset_clip(); + gr_clear(); +} + +int My_observer_num; + +void std_update_goals() +{ +} + +void os_close() +{ +} + +int gameseq_get_state(void) +{ + return 0; +} + +int game_check_key() +{ + return key_inkey(); +} + +int game_poll() +{ + return key_inkey(); +} + +void multi_delete_ship(object *obj) +{ +} + +void send_homing_fired_packet() +{ +} + +void game_flush() +{ +} + +typedef struct config_struct +{ + int boob; +} config_struct; + +config_struct default_config; + +typedef struct netgame_info +{ + int bubba; +} netgame_info; + +void send_netgame_state_packet() +{ +} + +void send_update_state_packet() +{ +} + +void send_goal_status_packet() +{ +} + +int Show_area_effect; + +void state_set_mem(unsigned char *c, int i) {} +int state_check_mem(unsigned char *c, int i) { return 0; } + +void demo_do_flag_dead(int i) {} +void demo_checkpoint() {} +void demo_set_playback_filter() {} + +void multi_end_sequence() +{ +} +void multi_server_respawn() {} + +void multi_build_respawn_points() {} + +void store_p_object( p_object *pbojp, CFILE *fp ) {} +void restore_p_object( p_object *pobjp, CFILE *dp) {} + +int Multi_squad_msg_targ; +int Multi_squad_msg_local; +void send_support_warpin_packet(int,int,int){} +void send_support_warpin_packet( int net_sig, int how ) {} + +int demo_query_debug(int id) { return 0; }; + +void send_support_warpin_packet(int){} +void game_whack_apply(float x, float y) {} + +void save_restore_vector(vector *vec, CFILE *fp, int version, vector *deflt) {} +void save_restore_matrix(matrix *mat, CFILE *fp, int version, matrix *deflt) {} +void save_restore_float(float *fl, CFILE *fp, int version, float deflt) {} +void save_restore_angles(angles *ang, CFILE *fp, int version, angles *deflt) {} +void save_restore_int(int *n, CFILE *fp, int version, int deflt) {} +void save_restore_uint(uint *n, CFILE *fp, int version, uint deflt) {} +void save_restore_short(short *n, CFILE *fp, int version, short deflt) {} +void save_restore_ushort(ushort *n, CFILE *fp, int version, ushort deflt) {} +void save_restore_ubyte(ubyte *n, CFILE *fp, int version, ubyte deflt) {} +void save_restore_fix(fix *n, CFILE *fp, int version, fix deflt) {} +void save_restore_string(char *str, CFILE *fp, int len, int version, char *deflt) {} +char *restore_string_alloc(CFILE *fp, int version, char *deflt) { return NULL; } + +void save_restore_p_object(p_object *pobj, CFILE *fp) {} + +void demo_write_char(char x) {} +char demo_read_char() { return 0; } + +int red_alert_default_status() {return 0;} + +void send_ship_kill_packet(struct object *,struct object *,float,unsigned char) {} +void send_debris_create_packet( object *objp, ushort net_sig, int model_num, vector pos) {} + +int Game_subspace_effect; +void big_explosion_flash(float x) {}; + +int game_do_cd_check(char *) {return 0;} + +void game_stop_looped_sounds() {} + +int Game_skill_level; +int game_cd_changed(void) {return 0;} + +int Interface_framerate; +void game_set_view_clip(){} +float Viewer_zoom; +int Pofview_running = 0; + +int Warpout_forced = 0; +float Warpout_time; +vector Camera_pos; +vector Dead_player_last_vel; +int game_start_mission(){return 0;} +int Game_weapons_tbl_valid; +int Game_ships_tbl_valid; +void game_level_close(){} +void game_enter_state(int, int){} +void game_leave_state(int, int){} +int Test_begin; +int Debug_octant; +int Framerate_delay; +void game_start_time(){} +void game_stop_time(){} +int game_get_default_skill_level(){return 0;} +void game_load_palette(){} +float Freespace_gamma; +int set_cdrom_path(int){return 0;} +int find_freespace_cd(char*){return 0;} +void get_version_string(){} +void game_do_state_common(int, int){} +void game_set_frametime(int){} +void game_increase_skill_level(){} +void get_version_string(char*){} +int Show_target_weapons; +int Show_target_debug_info; +int Game_do_state_should_skip; +long Game_time_compression; +struct fs_builtin_mission *game_find_builtin_mission(char*){return NULL;} +void game_format_time(long, char*){} +void game_do_state(int){} +void game_process_event(int, int){} +void game_shudder_apply(int, float){} +int game_hacked_data(){return 0;} +int game_single_step; +int last_single_step; +void get_version_string_short(char *){} +void game_tst_mark(struct object *, struct ship *){} +int tst; +int game_do_cd_mission_check(char *){return 1;} +int Player_multi_died_check; + +void game_feature_not_in_demo_popup() {} +int Nebedit_running = 0; \ No newline at end of file diff --git a/src/fred2/fredview.cpp b/src/fred2/fredview.cpp new file mode 100644 index 0000000..fa0c0d5 --- /dev/null +++ b/src/fred2/fredview.cpp @@ -0,0 +1,4819 @@ +/* + * $Logfile: /Freespace2/code/Fred2/FREDView.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * View class for a document/view architechure design program, which we don't + * want or need, but MFC forces us to use. This is the main place we handle + * MFC messages, events, etc. Sort of the interface between our code and MFC. + * There is also a lot of our code in here related to these things. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 17 7/15/99 3:07p Dave + * 32 bit detection support. Mouse coord commandline. + * + * 16 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 15 6/04/99 2:20p Andsager + * Add dump stats basic functionality + * + * 14 4/07/99 6:21p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 13 3/26/99 4:49p Dave + * Made cruisers able to dock with stuff. Made docking points and paths + * visible in fred. + * + * 12 3/20/99 5:09p Dave + * Fixed release build fred warnings and unhandled exception. + * + * 11 3/04/99 11:56a Johnson + * Fixed an undo-rotation bug. + * + * 10 3/02/99 9:25p Dave + * Added a bunch of model rendering debug code. Started work on fixing + * beam weapon wacky firing. + * + * 9 3/01/99 10:00a Dave + * Fxied several dogfight related stats bugs. + * + * 8 2/23/99 2:32p Dave + * First run of oldschool dogfight mode. + * + * 7 1/27/99 4:09p Andsager + * Added highlight to ship subsystems + * + * 6 1/07/99 1:52p Andsager + * Initial check in of Sexp_variables + * + * 5 12/18/98 1:49a Dave + * Fixed Fred initialization problem resulting from hi-res mode changes. + * + * 4 10/13/98 9:27a Dave + * Started neatening up freespace.h + * + * 3 10/12/98 1:01p Dave + * Fixed object rotation bug (uninitialized data). Changed a few stubs to + * correspond to new var names. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:00p Dave + * + * 248 9/16/98 6:54p Dave + * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort + * the ship list box. Added code so that tracker stats are not stored with + * only 1 player. + * + * 247 9/14/98 3:40p Allender + * better error checking for invalid number of waves for player wings in a + * multiplayer game. Better popup message in FreeSpace side. + * + * 246 6/18/98 4:28p Hoffoss + * Made invalid dock points in an initial order flag an error in Fred. + * + * 245 6/17/98 4:50p Hoffoss + * Added error checking for arrival delays used on wing player is in. + * + * 244 6/16/98 10:24a Hoffoss + * Switched internal errors over to normal looking errors for release + * build. + * + * 243 5/21/98 11:48a Hoffoss + * Removed check in and check out options. + * + * 242 5/21/98 12:58a Hoffoss + * Fixed warnings optimized build turned up. + * + * 241 5/19/98 1:19p Allender + * new low level reliable socket reading code. Make all missions/campaign + * load/save to data missions folder (i.e. we are rid of the player + * missions folder) + * + * 240 5/14/98 5:31p Hoffoss + * Added some more error checking. + * + * 239 5/10/98 10:05p Allender + * only show cutscenes which have been seen before. Made Fred able to + * write missions anywhere, defaulting to player misison folder, not data + * mission folder. Fix FreeSpace code to properly read missions from + * correct locations + * + * 238 4/28/98 2:13p Hoffoss + * Added code to help keep invalid player ship types from existing in + * mission. + * + * 237 4/26/98 6:05p Hoffoss + * Added multiplayer error checks requested by Dave B. + * + * 236 4/17/98 2:37p Duncan + * Added check to error checker for arrival/depature targets valid. + * + * 235 4/14/98 4:35p Hoffoss + * Fixed bug with launching FreeSpace from Fred. Current working + * directory wasn't being set properly. + * + * 234 4/14/98 3:38p Hoffoss + * Fixed the FreeSpace launch code in Fred. + * + * 233 4/07/98 9:42a Allender + * put in persona combo box into ship editor. Removed code to assign + * personas based on message + * + * 232 4/01/98 10:48a Hoffoss + * Changed Fred to not allow command briefings in multiplayer missions. + * + * 231 3/26/98 3:01p Hoffoss + * Put in check for alpha wing num ships < 4 for furball multiplayer + * missions. + * + * 230 3/25/98 4:14p Hoffoss + * Split ship editor up into ship editor and a misc dialog, which tracks + * flags and such. + * + * 229 3/23/98 4:04p Hoffoss + * Fixed dialog window initialization so it looks better at startup (they + * don't flash on for a second). + * + * 228 3/21/98 7:36p Lawrance + * Move jump nodes to own lib. + * + * 227 3/19/98 3:37p Adam + * Fixed Int3() when you try to right click on a briefing icon. + * + * 226 3/16/98 5:05p Hoffoss + * Fixed viewpoint bug. + * + * 225 3/12/98 2:21p Johnson + * Fixed some Fred bugs related to jump nodes. + * + * 224 3/10/98 6:11p Hoffoss + * Added jump node renaming abilities to Fred. + * + * 223 3/10/98 4:26p Hoffoss + * Changed jump node structure to include a name. Position is now taken + * from the object (each jump node has an associated object now). + * + * 222 3/09/98 10:03p Hoffoss + * Added support for loading/saving jump nodes to mission files. + * + * 221 3/09/98 10:56a Hoffoss + * Added jump node objects to Fred. + * + * 220 3/05/98 3:59p Hoffoss + * Added a bunch of new command brief stuff, and asteroid initialization + * to Fred. + * + * 219 2/26/98 4:59p Allender + * groundwork for team vs team briefings. Moved weaponry pool into the + * Team_data structure. Added team field into the p_info structure. + * Allow for mutliple structures in the briefing code. + * + * 218 2/09/98 9:25p Allender + * team v team support. multiple pools and breifings + * + * 217 2/07/98 9:13p Hoffoss + * Added some more error checking to global error checker. + * + * 216 2/06/98 4:39p Hoffoss + * Added better checking for whether Fred is in the foreground or + * background. + * + * 215 2/04/98 4:32p Allender + * support for multiple briefings and debriefings. Changes to mission + * type (now a bitfield). Bitfield defs for multiplayer modes + * + * 214 2/02/98 4:36p Hoffoss + * Made no mission title given flag an error. + * + * 213 1/29/98 5:14p Hoffoss + * Added error checking for more than 4 ships in a player starting wing. + * + * 212 1/21/98 5:22p Hoffoss + * Added check to global error checker to make sure Alpha wing exists for + * multiplayer missions. + * + * 211 1/14/98 8:40p Allender + * don't check builtin messages when checking persona stuff + * + * 210 1/07/98 11:24p Allender + * add latency of 0 to key_mark function calls + * + * 209 12/31/97 3:56p Hoffoss + * Forced alpha wing to always have a true arrival cue. + * + * 208 12/09/97 8:11a Allender + * have Fred warn on certain starting wing conditions + * + * 207 11/24/97 10:14p Allender + * removed num_ships from use!!!! it was creating problems + * + * 206 11/24/97 11:07a Allender + * error checking on wings ignoring player orders + * + * 205 11/17/97 4:57p Allender + * added persona support in FreeSpace. A couple of new messages for + * message.tbl which Dan didn't have + * + * 204 11/14/97 5:21p Johnson + * Added debriefing formulas to error checker. + * + * 203 11/05/97 4:43p Allender + * reworked medal/rank system to read all data from tables. Made Fred + * read medals.tbl. Changed ai-warp to ai-warp-out which doesn't require + * waypoint for activation + * + * 202 10/30/97 3:30p Hoffoss + * Made anti-aliased gridlines an option in Fred. + * + * 201 10/28/97 9:54a Jasen + * don't check for personas when wing is sending message + * + * 200 10/22/97 3:15p Hoffoss + * Fixed ai-stay-still initial order to use waypoints instead of waypoint + * paths. + * + * 199 10/22/97 1:58p Hoffoss + * Added support for AI_GOALS_PLAY_DEAD and changed AI_GOALS_STAY_STILL + * for Fred. + * + * 198 10/14/97 10:59a Allender + * more persona work. Made global error checker call funciton to assign + * and check personas + * + * 197 10/10/97 5:03p Allender + * started work on ai-stay-still + * + * 196 10/01/97 12:37p Hoffoss + * Changed Fred (and FreeSpace) to utilize alpha, beta and gamma as player + * starting wing names. + * + * 195 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 194 9/10/97 3:48p Duncan + * Added a case that was never added in the past (probably overlooked). + * + * 193 9/09/97 3:39p Sandeep + * warning level 4 bugs + * + * 192 9/09/97 2:12p Hoffoss + * Added code to allow briefing editor view to be a 1:1 pixel size fixed + * as the FreeSpace view will have. + * + * 191 9/06/97 2:13p Mike + * Replace support for TEAM_NEUTRAL + * + * 190 9/03/97 4:32p Hoffoss + * Added error number range error checking to Fred, and defaulted numbers + * for # of times docked to 1 in sexp trees. + * + * 189 9/02/97 4:32p Hoffoss + * Made minimized child windows restore if the window is activated. + * + * 188 9/02/97 1:34p Johnson + * Fixed bug I swear wasn't there when I checked this in originally. + * Strange. + * + * 187 9/01/97 6:59p Hoffoss + * Added source safe checkin and checkout capabilities to Fred. + * + * 186 8/28/97 8:56a Hoffoss + * Added more checking to sexp error checker, fixed some bugs. + * + * 185 8/26/97 4:18p Hoffoss + * Added error checking to initial orders dialog when ok is clicked. + * + * 184 8/25/97 5:58p Hoffoss + * Created menu items for keypress functions in Fred, and fixed bug this + * uncovered with wing_delete function. + * + * 183 8/22/97 4:16p Hoffoss + * added support for arrival and departure info in ship editor using + * wing's info if editing marked ships in a wing instead of using ship's. + * + * 182 8/18/97 9:31p Hoffoss + * Added grid adjustment dialog and shield system editor dialog. + * + * 181 8/17/97 10:22p Hoffoss + * Fixed several bugs in Fred with Undo feature. In the process, recoded + * a lot of CFile.cpp. + * + * 180 8/16/97 5:23p Hoffoss + * Added restrictions for single player mission type to allow only 1 + * player start. + * + * 179 8/16/97 2:02a Hoffoss + * Made docked objects move together in Fred. + * + * 178 8/15/97 11:08a Hoffoss + * Created a list of order types that can be used for several things, and + * yet easily changable. Added order error checking against ship types. + * + * 177 8/15/97 1:07a Hoffoss + * Changed code to disallow some ship types from being in a wing, and + * disallowing some ship types from being able to have initial orders. + * + * 176 8/14/97 6:37p Hoffoss + * Added briefing icon id support to Fred. + * + * 175 8/14/97 2:32p Hoffoss + * fixed bug where controlling an object doesn't cause screen updates, and + * added a number of cool features to viewpoint/control object code. + * + * 174 8/14/97 9:30a Hoffoss + * Added support for new ai goals "stay near ship" and "keep safe + * distance". + * + * 173 8/13/97 6:23p Hoffoss + * Fixed initially docked code in Fred. + * + * 172 8/13/97 5:40p Hoffoss + * Fixed typo. + * + * 171 8/12/97 10:42p Hoffoss + * Changed code to campaign editor load an accelerator table when created. + * + * 170 8/12/97 6:32p Hoffoss + * Added code to allow hiding of arrival and departure cues in editors. + * + * 169 8/12/97 5:56p Johnson + * Fixed bug with global error checker. Initially docked ships flagged as + * error when they aren't really. + * + * 168 8/12/97 1:29p Duncan + * Fixed bug with ai_undock initial order. + * + * 167 8/11/97 7:11p Hoffoss + * Somehow SS didn't merge the files, so I've reconstructed the changes + * Allender made. + * + * 166 8/11/97 6:54p Hoffoss + * Groups now supported in Fred. + * + * 165 8/11/97 11:51a Allender + * added stamp stuff to Fred + * + * 164 8/07/97 6:01p Hoffoss + * Added a rotate about selected object button to toolbar and + * functionality, as requested by Comet. + * + * 163 8/07/97 2:07p Hoffoss + * Added duplicate icon label error checking to global error checker, and + * made briefign mode boxmark only icons if at least one icon is in the + * selection box. + * + * 162 8/06/97 10:20a Hoffoss + * Changed rotation speeds to be less linear and hopefully more useful. + * + * 161 8/05/97 2:27p Jasen + * Fixed bug in global error checker that caused crashes when in briefing + * mode. + * + * 160 8/05/97 1:31p Hoffoss + * Removed excessive source control info from header. + * + * 159 8/01/97 5:52p Hoffoss + * Changed initially docked to disallow illegal docking combinations, and + * changed error checker to check for this as well. + * + * 158 8/01/97 3:10p Hoffoss + * Made Sexp help hidable. + * + * 157 8/01/97 12:52p Hoffoss + * Added variable, fixed bug with global error check. + * + * 156 7/31/97 6:41p Hoffoss + * Added checks to global error checker. + * + * 155 7/30/97 4:30p Hoffoss + * Made Fred sexp error checker more explicit. + * + * 154 7/29/97 5:16p Hoffoss + * Fixed bug in ship_query_general_type() and added docking checking to + * Fred's error checking. + * + * 153 7/29/97 1:44p Hoffoss + * Make player ships exception to ship target within same wing error + * traping. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" + +#include "freddoc.h" +#include "fredview.h" +#include "fredrender.h" +#include "cfile.h" +#include "grid.h" +#include "mainfrm.h" +#include "editor.h" +#include "management.h" +#include "2d.h" +#include "3d.h" +#include "object.h" +#include "linklist.h" +#include "fvi.h" // For find_plane_line_intersection +#include "vecmat.h" +#include "key.h" +#include "ailocal.h" +#include "ai.h" +#include "aigoals.h" +#include "ship.h" // for ship names +#include "missiongoalsdlg.h" +#include "wing.h" +#include "ship_select.h" +#include "playerstarteditor.h" +#include "orienteditor.h" +#include "eventeditor.h" +#include "messageeditordlg.h" +#include "starfield.h" +#include "starfieldeditor.h" +#include "floating.h" +#include "reinforcementeditordlg.h" +#include "asteroideditordlg.h" +#include "campaigntreewnd.h" +#include "debriefingeditordlg.h" +#include "adjustgriddlg.h" +#include "shieldsysdlg.h" +#include "cmdbrief.h" +#include "jumpnode.h" +#include "dumpstats.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +subsys_to_render Render_subsys; + +// the next variable is used for executable stamping -- please leave it alone!!! +#define FRED_EXPIRE_TIME (7 * 1000) +char stamp[STAMP_STRING_LENGTH] = { STAMP_STRING }; +int expire_game; + +#define EXPIRE_BAD_CHECKSUM 1 +#define EXPIRE_BAD_TIME 2 + +#define SHIP_TYPES 8000 +#define REDUCER 100.0f +#define DUP_DRAG_OF_WING 2 + +LOCAL int Duped_wing; + +int Autosave_disabled = 0; +int Show_sexp_help = 1; +int Show_ships = 1; +int Show_starts = 1; +int Show_friendly = 1; +int Show_hostile = 1; +int Show_neutral = 1; +int Show_ship_info = 1; +int Show_ship_models = 0; +int Show_compass = 1; +int Show_dock_points = 0; +int Show_paths_fred = 0; +int Selection_lock = 0; +int viewpoint = 0; +int view_obj; +int button_down = 0; +int Marked = 0, moved = 0; +int on_object = -1; +int Cursor_over = -1; +int Dup_drag = 0; +int physics_speed = 1; +int physics_rot = 20; +int box_marking = 0; +int last_mouse_x, last_mouse_y, mouse_dx, mouse_dy; +int Cur_bitmap = -1; +int Id_select_type_jump_node; +int Id_select_type_start = 0; +int Id_select_type_waypoint = 0; +int Hide_ship_cues = 0, Hide_wing_cues = 0; +vector original_pos, saved_cam_pos; +matrix bitmap_matrix_backup, saved_cam_orient = { 0.0f }; +Marking_box marking_box; +object_orient_pos rotation_backup[MAX_OBJECTS]; + +// used by error checker, but needed in more than just one function. +char *names[MAX_OBJECTS], flags[MAX_OBJECTS]; +int obj_count = 0; +int g_err = 0; + +void view_universe(int just_marked = 0); +void select_objects(); +void drag_rotate_save_backup(); + +///////////////////////////////////////////////////////////////////////////// +// CFREDView + +CFREDView *Fred_view_wnd = NULL; + +IMPLEMENT_DYNCREATE(CFREDView, CView) + +BEGIN_MESSAGE_MAP(CFREDView, CView) + ON_MESSAGE(WM_GOODBYE, OnGoodbye) + ON_MESSAGE(WM_MENU_POPUP_SHIPS, OnMenuPopupShips) + ON_MESSAGE(WM_MENU_POPUP_EDIT, OnMenuPopupEdit) + + //{{AFX_MSG_MAP(CFREDView) + ON_COMMAND(ID_VIEW_GRID, OnViewGrid) + ON_UPDATE_COMMAND_UI(ID_VIEW_GRID, OnUpdateViewGrid) + ON_COMMAND(ID_SHOW_WAYPOINTS, OnViewWaypoints) + ON_UPDATE_COMMAND_UI(ID_SHOW_WAYPOINTS, OnUpdateViewWaypoints) + ON_WM_LBUTTONDOWN() + ON_COMMAND(ID_EDITORS_SHIPS, OnEditorsShips) + ON_WM_KEYDOWN() + ON_WM_KEYUP() + ON_WM_SETFOCUS() + ON_WM_KILLFOCUS() + ON_WM_SIZE() + ON_WM_MOUSEMOVE() + ON_WM_LBUTTONUP() + ON_COMMAND(ID_MISCSTUFF_SHOWSHIPSASICONS, OnMiscstuffShowshipsasicons) + ON_WM_CONTEXTMENU() + ON_COMMAND(ID_EDIT_POPUP_SHOW_SHIP_ICONS, OnEditPopupShowShipIcons) + ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_SHIP_ICONS, OnUpdateEditPopupShowShipIcons) + ON_COMMAND(ID_EDIT_POPUP_SHOW_SHIP_MODELS, OnEditPopupShowShipModels) + ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_SHIP_MODELS, OnUpdateEditPopupShowShipModels) + + ON_COMMAND(ID_SHOW_PATHS, OnShowPaths) + ON_UPDATE_COMMAND_UI(ID_SHOW_PATHS, OnUpdateShowPaths) + ON_COMMAND(ID_SHOW_DOCK_POINTS, OnShowDockPoints) + ON_UPDATE_COMMAND_UI(ID_SHOW_DOCK_POINTS, OnUpdateShowDockPoints) + + ON_COMMAND(ID_MISC_STATISTICS, OnMiscStatistics) + ON_COMMAND(ID_EDIT_POPUP_SHOW_COMPASS, OnEditPopupShowCompass) + ON_UPDATE_COMMAND_UI(ID_EDIT_POPUP_SHOW_COMPASS, OnUpdateEditPopupShowCompass) + ON_UPDATE_COMMAND_UI(ID_CHANGE_VIEWPOINT_EXTERNAL, OnUpdateChangeViewpointExternal) + ON_COMMAND(ID_CHANGE_VIEWPOINT_EXTERNAL, OnChangeViewpointExternal) + ON_UPDATE_COMMAND_UI(ID_CHANGE_VIEWPOINT_FOLLOW, OnUpdateChangeViewpointFollow) + ON_COMMAND(ID_CHANGE_VIEWPOINT_FOLLOW, OnChangeViewpointFollow) + ON_COMMAND(ID_EDITORS_GOALS, OnEditorsGoals) + ON_COMMAND(ID_SPEED1, OnSpeed1) + ON_COMMAND(ID_SPEED2, OnSpeed2) + ON_COMMAND(ID_SPEED5, OnSpeed5) + ON_COMMAND(ID_SPEED10, OnSpeed10) + ON_UPDATE_COMMAND_UI(ID_SPEED1, OnUpdateSpeed1) + ON_COMMAND(ID_SPEED3, OnSpeed3) + ON_COMMAND(ID_SPEED8, OnSpeed8) + ON_COMMAND(ID_ROT1, OnRot1) + ON_COMMAND(ID_ROT2, OnRot2) + ON_COMMAND(ID_ROT3, OnRot3) + ON_COMMAND(ID_ROT4, OnRot4) + ON_COMMAND(ID_ROT5, OnRot5) + ON_UPDATE_COMMAND_UI(ID_SPEED2, OnUpdateSpeed2) + ON_UPDATE_COMMAND_UI(ID_SPEED3, OnUpdateSpeed3) + ON_UPDATE_COMMAND_UI(ID_SPEED5, OnUpdateSpeed5) + ON_UPDATE_COMMAND_UI(ID_SPEED8, OnUpdateSpeed8) + ON_UPDATE_COMMAND_UI(ID_SPEED10, OnUpdateSpeed10) + ON_UPDATE_COMMAND_UI(ID_ROT1, OnUpdateRot1) + ON_UPDATE_COMMAND_UI(ID_ROT2, OnUpdateRot2) + ON_UPDATE_COMMAND_UI(ID_ROT3, OnUpdateRot3) + ON_UPDATE_COMMAND_UI(ID_ROT4, OnUpdateRot4) + ON_UPDATE_COMMAND_UI(ID_ROT5, OnUpdateRot5) + ON_COMMAND(ID_CONTROL_MODE_CAMERA, OnControlModeCamera) + ON_UPDATE_COMMAND_UI(ID_CONTROL_MODE_CAMERA, OnUpdateControlModeCamera) + ON_COMMAND(ID_CONTROL_MODE_SHIP, OnControlModeShip) + ON_UPDATE_COMMAND_UI(ID_CONTROL_MODE_SHIP, OnUpdateControlModeShip) + ON_COMMAND(ID_SHOW_GRID_POSITIONS, OnShowGridPositions) + ON_UPDATE_COMMAND_UI(ID_SHOW_GRID_POSITIONS, OnUpdateShowGridPositions) + ON_COMMAND(ID_SHOW_COORDINATES, OnShowCoordinates) + ON_UPDATE_COMMAND_UI(ID_SHOW_COORDINATES, OnUpdateShowCoordinates) + ON_COMMAND(ID_SPEED50, OnSpeed50) + ON_UPDATE_COMMAND_UI(ID_SPEED50, OnUpdateSpeed50) + ON_COMMAND(ID_SPEED100, OnSpeed100) + ON_UPDATE_COMMAND_UI(ID_SPEED100, OnUpdateSpeed100) + ON_COMMAND(ID_SELECT, OnSelect) + ON_UPDATE_COMMAND_UI(ID_SELECT, OnUpdateSelect) + ON_COMMAND(ID_SELECT_AND_MOVE, OnSelectAndMove) + ON_UPDATE_COMMAND_UI(ID_SELECT_AND_MOVE, OnUpdateSelectAndMove) + ON_COMMAND(ID_SELECT_AND_ROTATE, OnSelectAndRotate) + ON_UPDATE_COMMAND_UI(ID_SELECT_AND_ROTATE, OnUpdateSelectAndRotate) + ON_COMMAND(ID_CONSTRAIN_X, OnConstrainX) + ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_X, OnUpdateConstrainX) + ON_COMMAND(ID_CONSTRAIN_Y, OnConstrainY) + ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_Y, OnUpdateConstrainY) + ON_COMMAND(ID_CONSTRAIN_Z, OnConstrainZ) + ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_Z, OnUpdateConstrainZ) + ON_COMMAND(ID_CONSTRAIN_XZ, OnConstrainXz) + ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_XZ, OnUpdateConstrainXz) + ON_COMMAND(ID_SELECTION_LOCK, OnSelectionLock) + ON_UPDATE_COMMAND_UI(ID_SELECTION_LOCK, OnUpdateSelectionLock) + ON_WM_LBUTTONDBLCLK() + ON_COMMAND(ID_DOUBLE_FINE_GRIDLINES, OnDoubleFineGridlines) + ON_UPDATE_COMMAND_UI(ID_DOUBLE_FINE_GRIDLINES, OnUpdateDoubleFineGridlines) + ON_COMMAND(ID_SHOW_DISTANCES, OnShowDistances) + ON_UPDATE_COMMAND_UI(ID_SHOW_DISTANCES, OnUpdateShowDistances) + ON_COMMAND(ID_UNIVERSAL_HEADING, OnUniversalHeading) + ON_UPDATE_COMMAND_UI(ID_UNIVERSAL_HEADING, OnUpdateUniversalHeading) + ON_COMMAND(ID_FLYING_CONTROLS, OnFlyingControls) + ON_UPDATE_COMMAND_UI(ID_FLYING_CONTROLS, OnUpdateFlyingControls) + ON_COMMAND(ID_ROTATE_LOCALLY, OnRotateLocally) + ON_UPDATE_COMMAND_UI(ID_ROTATE_LOCALLY, OnUpdateRotateLocally) + ON_COMMAND(ID_CONSTRAIN_XY, OnConstrainXy) + ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_XY, OnUpdateConstrainXy) + ON_UPDATE_COMMAND_UI(ID_CONSTRAIN_YZ, OnUpdateConstrainYz) + ON_COMMAND(ID_CONSTRAIN_YZ, OnConstrainYz) + ON_COMMAND(ID_SELECT_LIST, OnSelectList) + ON_COMMAND(ID_ZOOM_EXTENTS, OnZoomExtents) + ON_COMMAND(ID_ZOOM_SELECTED, OnZoomSelected) + ON_UPDATE_COMMAND_UI(ID_ZOOM_SELECTED, OnUpdateZoomSelected) + ON_COMMAND(ID_FORM_WING, OnFormWing) + ON_UPDATE_COMMAND_UI(ID_FORM_WING, OnUpdateFormWing) + ON_COMMAND(ID_DISBAND_WING, OnDisbandWing) + ON_UPDATE_COMMAND_UI(ID_DISBAND_WING, OnUpdateDisbandWing) + ON_COMMAND(ID_SHOW_HORIZON, OnShowHorizon) + ON_UPDATE_COMMAND_UI(ID_SHOW_HORIZON, OnUpdateShowHorizon) + ON_COMMAND(ID_EDITORS_WING, OnEditorsWing) + ON_COMMAND(ID_EDITORS_PLAYER, OnEditorsPlayer) + ON_COMMAND(ID_EDITORS_ORIENT, OnEditorsOrient) + ON_COMMAND(ID_EDITORS_EVENTS, OnEditorsEvents) + ON_UPDATE_COMMAND_UI(ID_EDITORS_ORIENT, OnUpdateEditorsOrient) + ON_COMMAND(ID_EDITORS_MESSAGE, OnEditorsMessage) + ON_COMMAND(ID_EDITORS_STARFIELD, OnEditorsStarfield) + ON_COMMAND(ID_EDITORS_BG_BITMAPS, OnEditorsBgBitmaps) + ON_COMMAND(ID_EDITORS_REINFORCEMENT, OnEditorsReinforcement) + ON_COMMAND(ID_ERROR_CHECKER, OnErrorChecker) + ON_COMMAND(ID_EDITORS_WAYPOINT, OnEditorsWaypoint) + ON_COMMAND(ID_VIEW_OUTLINES, OnViewOutlines) + ON_UPDATE_COMMAND_UI(ID_VIEW_OUTLINES, OnUpdateViewOutlines) + ON_UPDATE_COMMAND_UI(ID_NEW_SHIP_TYPE, OnUpdateNewShipType) + ON_COMMAND(ID_SHOW_STARFIELD, OnShowStarfield) + ON_UPDATE_COMMAND_UI(ID_SHOW_STARFIELD, OnUpdateShowStarfield) + ON_COMMAND(ID_ASTEROID_EDITOR, OnAsteroidEditor) + ON_COMMAND(ID_RUN_FREESPACE, OnRunFreespace) + ON_COMMAND(ID_EDITOR_CAMPAIGN, OnEditorCampaign) + ON_COMMAND(ID_SHOW_SHIPS, OnShowShips) + ON_UPDATE_COMMAND_UI(ID_SHOW_SHIPS, OnUpdateShowShips) + ON_COMMAND(ID_SHOW_STARTS, OnShowStarts) + ON_UPDATE_COMMAND_UI(ID_SHOW_STARTS, OnUpdateShowStarts) + ON_COMMAND(ID_SHOW_FRIENDLY, OnShowFriendly) + ON_UPDATE_COMMAND_UI(ID_SHOW_FRIENDLY, OnUpdateShowFriendly) + ON_COMMAND(ID_SHOW_HOSTILE, OnShowHostile) + ON_UPDATE_COMMAND_UI(ID_SHOW_HOSTILE, OnUpdateShowHostile) + ON_COMMAND(ID_TOGGLE_VIEWPOINT, OnToggleViewpoint) + ON_COMMAND(ID_REVERT, OnRevert) + ON_UPDATE_COMMAND_UI(ID_REVERT, OnUpdateRevert) + ON_WM_SETCURSOR() + ON_COMMAND(ID_HIDE_OBJECTS, OnHideObjects) + ON_COMMAND(ID_SHOW_HIDDEN_OBJECTS, OnShowHiddenObjects) + ON_COMMAND(ID_EDIT_UNDO, OnEditUndo) + ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo) + ON_COMMAND(ID_EDITORS_BRIEFING, OnEditorsBriefing) + ON_COMMAND(ID_EDITORS_DEBRIEFING, OnEditorsDebriefing) + ON_COMMAND(ID_SAVE_CAMERA, OnSaveCamera) + ON_COMMAND(ID_RESTORE_CAMERA, OnRestoreCamera) + ON_UPDATE_COMMAND_UI(ID_RESTORE_CAMERA, OnUpdateRestoreCamera) + ON_COMMAND(ID_SHOW_SEXP_HELP, OnShowSexpHelp) + ON_UPDATE_COMMAND_UI(ID_SHOW_SEXP_HELP, OnUpdateShowSexpHelp) + ON_COMMAND(ID_LOOKAT_OBJ, OnLookatObj) + ON_UPDATE_COMMAND_UI(ID_LOOKAT_OBJ, OnUpdateLookatObj) + ON_COMMAND(ID_EDITORS_ADJUST_GRID, OnEditorsAdjustGrid) + ON_COMMAND(ID_EDITORS_SHIELD_SYS, OnEditorsShieldSys) + ON_COMMAND(ID_LEVEL_OBJ, OnLevelObj) + ON_COMMAND(ID_ALIGN_OBJ, OnAlignObj) + ON_COMMAND(ID_CONTROL_OBJ, OnControlObj) + ON_COMMAND(ID_NEXT_OBJ, OnNextObj) + ON_COMMAND(ID_PREV_OBJ, OnPrevObj) + ON_COMMAND(ID_EDIT_DELETE_WING, OnEditDeleteWing) + ON_COMMAND(ID_MARK_WING, OnMarkWing) + ON_UPDATE_COMMAND_UI(ID_CONTROL_OBJ, OnUpdateControlObj) + ON_COMMAND(ID_EDIT_DELETE, OnEditDelete) + ON_COMMAND(ID_AA_GRIDLINES, OnAaGridlines) + ON_UPDATE_COMMAND_UI(ID_AA_GRIDLINES, OnUpdateAaGridlines) + ON_COMMAND(ID_CMD_BRIEF, OnCmdBrief) + ON_COMMAND(ID_DISABLE_UNDO, OnDisableUndo) + ON_UPDATE_COMMAND_UI(ID_DISABLE_UNDO, OnUpdateDisableUndo) + ON_UPDATE_COMMAND_UI(ID_CMD_BRIEF, OnUpdateCmdBrief) + ON_COMMAND(ID_NEXT_SUBSYS, OnNextSubsys) + ON_COMMAND(ID_PREV_SUBSYS, OnPrevSubsys) + ON_COMMAND(ID_CANCEL_SUBSYS, OnCancelSubsys) + ON_COMMAND(ID_DUMP_STATS, OnDumpStats) + //}}AFX_MSG_MAP + ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) + ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) + ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) + ON_COMMAND_RANGE(ID_GROUP1, ID_GROUP9, OnGroup) + ON_COMMAND_RANGE(ID_SET_GROUP1, ID_SET_GROUP9, OnSetGroup) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CFREDView construction/destruction + +CFREDView::CFREDView() +{ + //m_ConfirmDeleting = TRUE; + //m_ShowCapitalShips = TRUE; + //m_ShowElevations = TRUE; + //m_ShowFighters = TRUE; + //m_ShowGrid = TRUE; + //m_ShowMiscObjects = TRUE; + //m_ShowPlanets = TRUE; + //m_ShowWaypoints = TRUE; + + m_pGDlg = new CGrid(this); + + fred_init(); + + //if (!(int errno = gr_init(640, 480, 32))) + // Error(LOCATION, "Hey, gr_init failed! Error code = %i", errno); + Fred_view_wnd = this; +} + +CFREDView::~CFREDView() +{ + delete m_pGDlg; + + gr_close(); +} + +void CALLBACK expire_game_proc( HWND wnd, UINT uMsg, UINT idEvent, DWORD dwTime) +{ + KillTimer(wnd, 1); + if ( expire_game == EXPIRE_BAD_CHECKSUM ) + MessageBox (wnd, "Fred can no longer run due to internal overlay error", NULL, MB_OK | MB_ICONERROR |MB_TASKMODAL|MB_SETFOREGROUND); + else + MessageBox (wnd, "Error: cannot enter DOS mode for 80x40 color text mode display.", NULL, MB_OK | MB_ICONERROR|MB_TASKMODAL|MB_SETFOREGROUND); + exit(1); +} + +BOOL CFREDView::PreCreateWindow(CREATESTRUCT& cs) +{ + BOOL casperl; + + casperl = CView::PreCreateWindow(cs); + cs.y = 0; // doesn't seem to do anything. :( + +// other miscellaneous initializations + cfile_chdir("data\\missions"); + set_physics_controls(); + return casperl; +} + +///////////////////////////////////////////////////////////////////////////// +// CFREDView drawing + +void CFREDView::OnDraw(CDC* pDC) +{ + CRect clip; + + CFREDDoc* pDoc = GetDocument(); + ASSERT_VALID(pDoc); + + Update_window = 1; + if (Fred_active) + return; + + pDC->GetClipBox(&clip); + gr_set_clip(clip.left, clip.top, clip.right - clip.left + 1, clip.bottom - clip.top + 1); + Assert(clip.left <= clip.right); + Assert(clip.top <= clip.bottom); + gr_flip_window((uint) pDC->m_hDC, clip.left, clip.top, + clip.right - clip.left + 1, clip.bottom - clip.top + 1); +} + +///////////////////////////////////////////////////////////////////////////// +// CFREDView printing + +BOOL CFREDView::OnPreparePrinting(CPrintInfo* pInfo) +{ + // default preparation + return DoPreparePrinting(pInfo); +} + +void CFREDView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) +{ + // TODO: add extra initialization before printing +} + +void CFREDView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) +{ + // TODO: add cleanup after printing +} + +///////////////////////////////////////////////////////////////////////////// +// CFREDView diagnostics + +#ifdef _DEBUG +void CFREDView::AssertValid() const +{ + CView::AssertValid(); +} + +void CFREDView::Dump(CDumpContext& dc) const +{ + CView::Dump(dc); +} + +CFREDDoc* CFREDView::GetDocument() // non-debug version is inline +{ + ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CFREDDoc))); + return (CFREDDoc*)m_pDocument; +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CFREDView message handlers + +void CFREDView::OnViewGrid() +{ + Show_grid = !Show_grid; + Update_window = 1; +} + +void CFREDView::OnUpdateViewGrid(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_grid); +} + +void CFREDView::OnViewWaypoints() +{ + Show_waypoints = !Show_waypoints; + Update_window = 1; +} + +void CFREDView::OnUpdateViewWaypoints(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_waypoints); +} + +#define MAX_MOVE_DISTANCE 25.0f + +// If cur_object_index references a valid object, drag it from its current +// location to the new cursor location specified by "point". +// It is dragged relative to the main grid. It's y coordinate is not changed. +// Return value: 0/1 = didn't/did move object all the way to goal. +int drag_objects() +{ + int z, cobj, flag, rval = 1; + float r; + float distance_moved = 0.0f; + vector cursor_dir, int_pnt; + vector movement_vector; + vector obj; + vector vec1, vec2; + object *objp, *ptr; + // starfield_bitmaps *bmp; + + /* + if (Bg_bitmap_dialog) { + if (Cur_bitmap < 0) + return -1; + + bmp = &Starfield_bitmaps[Cur_bitmap]; + if (Single_axis_constraint && Constraint.z) { + bmp->dist *= 1.0f + mouse_dx / -800.0f; + calculate_bitmap_points(bmp, 0.0f); + + } else { + g3_point_to_vec_delayed(&bmp->m.fvec, marking_box.x2, marking_box.y2); + vm_orthogonalize_matrix(&bmp->m); + calculate_bitmap_points(bmp, 0.0f); + } + return rval; + } + */ + + if (!query_valid_object()) + return -1; + + if ((Dup_drag == 1) && (Briefing_dialog)) + Dup_drag = 0; + + if (Dup_drag == 1) { + dup_object(NULL); // reset waypoint list + cobj = Duped_wing = -1; + flag = 0; + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + Assert(objp->type != OBJ_NONE); + if (objp->flags & OF_MARKED) { + if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) { + z = Ships[objp->instance].wingnum; + if (!flag) + Duped_wing = z; + else if (Duped_wing != z) + Duped_wing = -1; + + } else + Duped_wing = -1; + + flag = 1; + z = dup_object(objp); + if (z == -1) { + cobj = -1; + break; + } + + if (cur_object_index == OBJ_INDEX(objp) ) + cobj = z; + } + + objp = GET_NEXT(objp); + } + + obj_merge_created_list(); + if (cobj == -1) { + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + ptr = GET_NEXT(objp); + if (objp->flags & OF_TEMP_MARKED) + delete_object(objp); + + objp = ptr; + } + + button_down = 0; + return -1; + } + + unmark_all(); + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_TEMP_MARKED) { + objp->flags &= ~OF_TEMP_MARKED; + mark_object(OBJ_INDEX(objp)); + } + + objp = GET_NEXT(objp); + } + + set_cur_object_index(cobj); + if (Duped_wing != -1) + Dup_drag = DUP_DRAG_OF_WING; // indication for later that we duped objects in a wing + else + Dup_drag = 0; + + drag_rotate_save_backup(); + set_modified(); + Update_window = 1; + } + + objp = &Objects[cur_object_index]; + Assert(objp->type != OBJ_NONE); + obj = int_pnt = objp->pos; + + // Get 3d vector specified by mouse cursor location. + g3_point_to_vec_delayed(&cursor_dir, marking_box.x2, marking_box.y2); + if (Single_axis_constraint) { +// if (fvi_ray_plane(&int_pnt, &obj, &view_orient.fvec, &view_pos, &cursor_dir, 0.0f) >= 0.0f ) { +// vm_vec_add(&p1, &obj, &Constraint); +// find_nearest_point_on_line(&nearest_point, &obj, &p1, &int_pnt); +// int_pnt = nearest_point; +// distance_moved = vm_vec_dist(&obj, &int_pnt); +// } + + vector tmpAnticonstraint = Anticonstraint; + vector tmpObject = obj; + + tmpAnticonstraint.x = 0.0f; + r = fvi_ray_plane(&int_pnt, &tmpObject, &tmpAnticonstraint, &view_pos, &cursor_dir, 0.0f); + + // If intersected behind viewer, don't move. Too confusing, not what user wants. + vm_vec_sub(&vec1, &int_pnt, &view_pos); + vm_vec_sub(&vec2, &obj, &view_pos); + if ((r>=0.0f) && (vm_vec_dot(&vec1, &vec2) >= 0.0f)) { + vector tmp1; + vm_vec_sub( &tmp1, &int_pnt, &obj ); + tmp1.x *= Constraint.x; + tmp1.y *= Constraint.y; + tmp1.z *= Constraint.z; + vm_vec_add( &int_pnt, &obj, &tmp1 ); + + distance_moved = vm_vec_dist(&obj, &int_pnt); + } + + + } else { // Move in x-z plane, defined by grid. Preserve height. + r = fvi_ray_plane(&int_pnt, &obj, &Anticonstraint, &view_pos, &cursor_dir, 0.0f); + + // If intersected behind viewer, don't move. Too confusing, not what user wants. + vm_vec_sub(&vec1, &int_pnt, &view_pos); + vm_vec_sub(&vec2, &obj, &view_pos); + if ((r>=0.0f) && (vm_vec_dot(&vec1, &vec2) >= 0.0f)) + distance_moved = vm_vec_dist(&obj, &int_pnt); + } + + // If moved too far, then move max distance along vector. + vm_vec_sub(&movement_vector, &int_pnt, &obj); +/* if (distance_moved > MAX_MOVE_DISTANCE) { + vm_vec_normalize(&movement_vector); + vm_vec_scale(&movement_vector, MAX_MOVE_DISTANCE); + } */ + + if (distance_moved) { + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + Assert(objp->type != OBJ_NONE); + if (objp->flags & OF_MARKED) { + vm_vec_add(&objp->pos, &objp->pos, &movement_vector); + if (objp->type == OBJ_WAYPOINT) { + Waypoint_lists[objp->instance / 65536].waypoints[objp->instance & 0xffff] = + objp->pos; + } + } + + objp = GET_NEXT(objp); + } + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) + object_moved(objp); + + objp = GET_NEXT(objp); + } + } + + if (Briefing_dialog) + Briefing_dialog->update_positions(); + + set_modified(); + return rval; +} + +void drag_rotate_save_backup() +{ + object *objp; + + /* + if (Cur_bitmap != -1) + bitmap_matrix_backup = Starfield_bitmaps[Cur_bitmap].m; + */ + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + Assert(objp->type != OBJ_NONE); + if (objp->flags & OF_MARKED) { + rotation_backup[OBJ_INDEX(objp)].pos = objp->pos; + rotation_backup[OBJ_INDEX(objp)].orient = objp->orient; + } + + objp = GET_NEXT(objp); + } +} + +int drag_rotate_objects() +{ + int rval = 1; + vector int_pnt, obj; + angles a; + matrix leader_orient, leader_transpose, tmp, newmat, rotmat; + object *leader, *objp; + // starfield_bitmaps *bmp; + + Update_window = 1; + /* + if (Bg_bitmap_dialog) { + if (Cur_bitmap < 0) + return -1; + + bmp = &Starfield_bitmaps[Cur_bitmap]; + calculate_bitmap_points(bmp, mouse_dx / -300.0f); + return rval; + } + */ + + if (!query_valid_object()){ + return -1; + } + + objp = &Objects[cur_object_index]; + Assert(objp->type != OBJ_NONE); + obj = int_pnt = objp->pos; + + memset(&a, 0, sizeof(angles)); + if (Single_axis_constraint) { + if (Constraint.x) + a.p = mouse_dy / REDUCER; + else if (Constraint.y) + a.h = mouse_dx / REDUCER; + else if (Constraint.z) + a.b = -mouse_dx / REDUCER; + + } else { + if (!Constraint.x) { // yz + a.b = -mouse_dx / REDUCER; + a.h = mouse_dy / REDUCER; + } else if (!Constraint.y) { // xz + a.p = mouse_dy / REDUCER; + a.b = -mouse_dx / REDUCER; + } else if (!Constraint.z) { // xy + a.p = mouse_dy / REDUCER; + a.h = mouse_dx / REDUCER; + } + } + + leader = &Objects[cur_object_index]; + leader_orient = leader->orient; // save original orientation + vm_copy_transpose_matrix(&leader_transpose, &leader_orient); + + vm_angles_2_matrix(&rotmat, &a); + vm_matrix_x_matrix(&newmat, &leader->orient, &rotmat); + leader->orient = newmat; + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + Assert(objp->type != OBJ_NONE); + if ((objp->flags & OF_MARKED) && (cur_object_index != OBJ_INDEX(objp) )) { + if (Group_rotate) { + matrix rot_trans; + vector tmpv1, tmpv2; + + // change rotation matrix to rotate in opposite direction. This rotation + // matrix is what the leader ship has rotated by. + vm_copy_transpose_matrix(&rot_trans, &rotmat); + + // get point relative to our point of rotation (make POR the origin). + vm_vec_sub(&tmpv1, &objp->pos, &leader->pos); + + // convert point from real-world coordinates to leader's relative coordinate + // system (z=forward vec, y=up vec, x=right vec + vm_vec_rotate(&tmpv2, &tmpv1, &leader_orient); + + // now rotate the point by the transpose from above. + vm_vec_rotate(&tmpv1, &tmpv2, &rot_trans); + + // convert point back into real-world coordinates + vm_vec_rotate(&tmpv2, &tmpv1, &leader_transpose); + + // and move origin back to real-world origin. Object is now at it's correct + // position. + vm_vec_add(&objp->pos, &leader->pos, &tmpv2); + + // Now fix the object's orientation to what it should be. + vm_matrix_x_matrix(&tmp, &objp->orient, &rotmat); + vm_orthogonalize_matrix(&tmp); // safety check + objp->orient = tmp; + + } else { + vm_matrix_x_matrix(&tmp, &objp->orient, &rotmat); + objp->orient = tmp; + } + } + + objp = GET_NEXT(objp); + } + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) + object_moved(objp); + + objp = GET_NEXT(objp); + } + + set_modified(); + return rval; +} + +void cancel_drag() +{ + Update_window = 1; + + /* + if (Bg_bitmap_dialog) { + Assert(!vm_check_matrix_for_zeros(&bitmap_matrix_backup)); + Starfield_bitmaps[Cur_bitmap].m = bitmap_matrix_backup; + calculate_bitmap_points(&Starfield_bitmaps[Cur_bitmap], 0.0f); + button_down = box_marking = 0; + Update_window = 1; + return; + } + */ + + if (button_down) { + if (Editing_mode == 1) { + vector movement_vector; + object *objp; + + if (query_valid_object()) { + objp = &Objects[cur_object_index]; + Assert(objp->type != OBJ_NONE); + vm_vec_sub(&movement_vector, &original_pos, &objp->pos); + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + Assert(objp->type != OBJ_NONE); + if (objp->flags & OF_MARKED) + vm_vec_add(&objp->pos, &objp->pos, &movement_vector); + + objp = GET_NEXT(objp); + } + } + + } else if (Editing_mode == 2) { + object *objp; + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + Assert(objp->type != OBJ_NONE); + if (objp->flags & OF_MARKED) { + int obj_index = OBJ_INDEX(objp); + + if(!IS_VEC_NULL(&rotation_backup[obj_index].orient.rvec) && + !IS_VEC_NULL(&rotation_backup[obj_index].orient.uvec) && + !IS_VEC_NULL(&rotation_backup[obj_index].orient.fvec)){ + + objp->pos = rotation_backup[obj_index].pos; + objp->orient = rotation_backup[obj_index].orient; + } + } + + objp = GET_NEXT(objp); + } + } + } + + button_down = box_marking = 0; + if (Briefing_dialog) + Briefing_dialog->update_positions(); +} + +void CFREDView::OnLButtonDown(UINT nFlags, CPoint point) +{ + int list = -1; + + if (!Fred_active) { + CView::OnLButtonDown(nFlags, point); + return; + } + + if (cur_waypoint != -1) + list = cur_waypoint_list * 65536 + cur_waypoint; + + marking_box.x1 = point.x; + marking_box.y1 = point.y; + Dup_drag = 0; + + on_object = select_object(point.x, point.y); + button_down = 1; + SetCapture(); + drag_rotate_save_backup(); + + if (nFlags & MK_CONTROL) { // add a new object + if (!Bg_bitmap_dialog) { + if (on_object == -1) { + Selection_lock = 0; // force off selection lock + on_object = create_object_on_grid(list); + + } else + Dup_drag = 1; + + } else { + /* + Selection_lock = 0; // force off selection lock + on_object = Cur_bitmap = create_bg_bitmap(); + Bg_bitmap_dialog->update_data(); + Update_window = 1; + if (Cur_bitmap == -1) + MessageBox("Background bitmap limit reached.\nCan't add more."); + */ + } + + } else if (!Selection_lock) { + if (Bg_bitmap_dialog) { + Cur_bitmap = on_object; + Bg_bitmap_dialog -> update_data(); + + } else if ((nFlags & MK_SHIFT) || (on_object == -1) || !(Objects[on_object].flags & OF_MARKED)) { + if (!(nFlags & MK_SHIFT)) + unmark_all(); + + if (on_object != -1) { + if (Objects[on_object].flags & OF_MARKED) + unmark_object(on_object); + else + mark_object(on_object); + } + } + } + + if (query_valid_object()) + original_pos = Objects[cur_object_index].pos; + + moved = 0; + if (Selection_lock) { + if (Editing_mode == 1) + drag_objects(); + else if (Editing_mode == 2) + drag_rotate_objects(); + + Update_window = 1; + } + + if (query_valid_object() && (Marked == 1) && (Objects[cur_object_index].type == OBJ_POINT)) { + Assert(Briefing_dialog); + Briefing_dialog->icon_select(Objects[cur_object_index].instance); + + } else { + if (Briefing_dialog) + Briefing_dialog->icon_select(-1); + } + + CView::OnLButtonDown(nFlags, point); +} + +void CFREDView::OnMouseMove(UINT nFlags, CPoint point) +{ + mouse_dx = point.x - last_mouse_x; + mouse_dy = point.y - last_mouse_y; + last_mouse_x = marking_box.x2 = point.x; + last_mouse_y = marking_box.y2 = point.y; + Cursor_over = select_object(point.x, point.y); + + if (!(nFlags & MK_LBUTTON)) + button_down = 0; + + // The following will cancel a drag operation if another program running in memory + // happens to jump in and take over (such as new email has arrived popup boxes). + if (button_down && GetCapture() != this) + cancel_drag(); + + if (!button_down && GetCapture() == this) + ReleaseCapture(); + + if (button_down) { + if (abs(marking_box.x1 - marking_box.x2) > 1 || abs(marking_box.y1 - marking_box.y2) > 1) + moved = 1; + + if (moved) { + if (on_object != -1 || Selection_lock) { + if (Editing_mode == 1) + drag_objects(); + else if (Editing_mode == 2) + drag_rotate_objects(); + + } else if (!Bg_bitmap_dialog) + box_marking = 1; + + if (mouse_dx || mouse_dy) + Update_window = 1; + } + } + + CView::OnMouseMove(nFlags, point); +} + +void CFREDView::OnLButtonUp(UINT nFlags, CPoint point) +{ + marking_box.x2 = point.x; + marking_box.y2 = point.y; + + if (button_down && GetCapture() != this) + cancel_drag(); + + if (GetCapture() == this) + ReleaseCapture(); + + if (button_down) { + if ((abs(marking_box.x1 - marking_box.x2) > 1) || (abs(marking_box.y1 - marking_box.y2) > 1)) + moved = 1; + + if (moved) { + if ((on_object != -1) || Selection_lock) { + if (Editing_mode == 1) + drag_objects(); + else if ((Editing_mode == 2) || (Editing_mode == 3)) + drag_rotate_objects(); + + set_modified(); + FREDDoc_ptr->autosave("object move"); + + } else + box_marking = 1; + } + + if (Bg_bitmap_dialog) { + box_marking = 0; + + } else { + if (box_marking) { + select_objects(); + box_marking = 0; + + } else if ((!moved && on_object != -1) && !Selection_lock && !(nFlags & MK_SHIFT)) { + unmark_all(); + mark_object(on_object); + } + } + + button_down = 0; + Update_window = 1; + if (Dup_drag == DUP_DRAG_OF_WING) { + char msg[256]; + int ship; + object *objp; + + sprintf(msg, "Add cloned ships to wing %s?", Wings[Duped_wing].name); + if (MessageBox(msg, "Query", MB_YESNO) == IDYES) { + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) { + if (Wings[Duped_wing].wave_count >= MAX_SHIPS_PER_WING) { + MessageBox("Max ships per wing limit reached"); + break; + } + +// Can't do player starts, since only player 1 is currently allowed to be in a wing + + Assert(objp->type == OBJ_SHIP); + ship = objp->instance; + Assert(Ships[ship].wingnum == -1); + sprintf(Ships[ship].ship_name, "%s %d", Wings[Duped_wing].name, + Wings[Duped_wing].wave_count + 1); + + Wings[Duped_wing].ship_index[Wings[Duped_wing].wave_count] = ship; + Ships[ship].wingnum = Duped_wing; + + wing_objects[Duped_wing][Wings[Duped_wing].wave_count] = OBJ_INDEX(objp); + Wings[Duped_wing].wave_count++; + } + + objp = GET_NEXT(objp); + } + } + } + } + + if (query_valid_object() && (Marked == 1) && (Objects[cur_object_index].type == OBJ_POINT)) { + Assert(Briefing_dialog); + Briefing_dialog->icon_select(Objects[cur_object_index].instance); + + } else { + if (Briefing_dialog) + Briefing_dialog->icon_select(-1); + } + + CView::OnLButtonUp(nFlags, point); +} + +// This function never gets called because nothing causes +// the WM_GOODBYE event to occur. +// False! When you close the Ship Dialog, this function is called! --MK, 8/30/96 +LONG CFREDView::OnGoodbye(UINT wParam, LONG lParam) +{ + Ship_editor_dialog.DestroyWindow(); + Wing_editor_dialog.DestroyWindow(); + + return 0L; +} + +void CFREDView::OnEditorsShips() +{ + int adjust = 0; + + Assert(Ship_editor_dialog.GetSafeHwnd()); + if (!Show_sexp_help) + adjust = -SEXP_HELP_BOX_SIZE; + + if (!theApp.init_window(&Ship_wnd_data, &Ship_editor_dialog, adjust)) + return; + + Ship_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Ship_editor_dialog.ShowWindow(SW_RESTORE); +} + +void CFREDView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT lParam) +{ + uint lKeyData; + + lKeyData = lParam & 255; // key data + if (lParam & 256) lKeyData += 0x80; + key_mark(lKeyData, 1, 0); + + CView::OnKeyDown(nChar, nRepCnt, lParam); +} + +void CFREDView::OnKeyUp(UINT nChar, UINT nRepCnt, UINT lParam) +{ + uint lKeyData; + + lKeyData = lParam & 255; // key data + if (lParam & 256) lKeyData += 0x80; + key_mark(lKeyData, 0, 0); + + CView::OnKeyUp(nChar, nRepCnt, lParam); +} + +void CFREDView::OnSetFocus(CWnd* pOldWnd) +{ + static int flag = 0; + + if (flag) + return; + + flag = 1; + nprintf(("Fred routing", "OnSetFocus() called\n")); + if (Update_ship) { + Ship_editor_dialog.initialize_data(1); + Update_ship = 0; + } + + if (Update_wing) { + Wing_editor_dialog.initialize_data(1); + Update_wing = 0; + } + +/* if (Wing_editor_dialog.verify() == -1) + return; // abort + + if (Ship_editor_dialog.verify() == -1) + return; // abort*/ + + if (update_dialog_boxes()) { + nprintf(("Fred routing", "OnSetFocus() returned (error occured)\n")); + flag = 0; + Ship_editor_dialog.bypass_errors = 0; + Wing_editor_dialog.bypass_errors = 0; + return; // abort + } + + if (Local_modified) { + FREDDoc_ptr->autosave("Editing"); + Local_modified = 0; + } + + Fred_active = 1; + CView::OnSetFocus(pOldWnd); + nprintf(("Fred routing", "Main window focus accepted\n")); + key_got_focus(); + + Cursor_over = -1; + if (GetActiveWindow() != Fred_main_wnd) { + Fred_main_wnd->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + nprintf(("Fred routing", "OnSetFocus() had to put main window back on top\n")); + } + + flag = 0; +} + +void CFREDView::OnKillFocus(CWnd* pNewWnd) +{ + nprintf(("Fred routing", "OnKillFocus() called\n")); + Fred_active = 0; + Local_modified = 0; + CView::OnKillFocus(pNewWnd); + key_lost_focus(); + Cursor_over = -1; +} + +void CFREDView::OnSize(UINT nType, int cx, int cy) +{ + CView::OnSize(nType, cx, cy); + + if ((cx > 0) && (cy > 0)) { + gr_init(GR_640, GR_SOFTWARE, 8, cx, cy); + } +} + +void do_trackball_stuff(int nFlags, CPoint point) +{ + int btn = 0; + + if (nFlags & MK_LBUTTON){ + btn |= 1; + } + if (nFlags & MK_RBUTTON){ + btn |= 2; + } + + move_mouse(btn, point.x, point.y); +} + +// If add_flag != 0, then add found objects to current wing, else create new wing. +void select_objects() +{ + int x, y, valid, icon_mode = 0; + vertex v; + object *ptr; + + if (marking_box.x1 > marking_box.x2) { + x = marking_box.x1; + marking_box.x1 = marking_box.x2; + marking_box.x2 = x; + } + + if (marking_box.y1 > marking_box.y2) { + y = marking_box.y1; + marking_box.y1 = marking_box.y2; + marking_box.y2 = y; + } + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + valid = 1; + if (ptr->flags & OF_HIDDEN) + valid = 0; + + Assert(ptr->type != OBJ_NONE); + switch (ptr->type) { + case OBJ_WAYPOINT: + if (!Show_waypoints) + valid = 0; + break; + + case OBJ_START: + if (!Show_starts) + valid = 0; + break; + + case OBJ_SHIP: + if (!Show_ships) + valid = 0; + switch (Ships[ptr->instance].team) { + case TEAM_FRIENDLY: + if (!Show_friendly) + valid = 0; + break; + + case TEAM_HOSTILE: + if (!Show_hostile) + valid = 0; + break; + + case TEAM_NEUTRAL: + if (!Show_neutral) + valid = 0; + break; + } + + break; + } + + g3_rotate_vertex(&v, &ptr->pos); + if (!(v.codes & CC_BEHIND) && valid) + if (!(g3_project_vertex(&v) & PF_OVERFLOW)) { + x = (int) v.sx; + y = (int) v.sy; + + if (x >= marking_box.x1 && x <= marking_box.x2 && y >= marking_box.y1 && y <= marking_box.y2) { + if (ptr->flags & OF_MARKED) + unmark_object(OBJ_INDEX(ptr)); + else + mark_object(OBJ_INDEX(ptr)); + + if (ptr->type == OBJ_POINT) + icon_mode = 1; + } + } + + ptr = GET_NEXT(ptr); + } + + if (icon_mode) { + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if ((ptr->flags & OF_MARKED) && (ptr->type != OBJ_POINT)) + unmark_object(OBJ_INDEX(ptr)); + + ptr = GET_NEXT(ptr); + } + } + + Update_ship = Update_wing = 1; +} + +LONG CFREDView::OnMenuPopupShips(UINT wParam, LONG lParam) +{ + CMenu menu; + CPoint point; + + point = * ((CPoint*) lParam); + + ClientToScreen(&point); + + menu.LoadMenu(IDR_MENU_SHIP_POPUP); + menu.GetSubMenu(0)->TrackPopupMenu( + TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); + + return 0L; +} + +LONG CFREDView::OnMenuPopupEdit(UINT wParam, LONG lParam) +{ + CMenu menu; + CPoint point; + + point = * ((CPoint*) lParam); + + ClientToScreen(&point); + + menu.LoadMenu(IDR_MENU_EDIT_POPUP); + menu.GetSubMenu(0)->TrackPopupMenu( + TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); + + return 0L; +} + +int g_Ships_as_icons = 0; + +void CFREDView::OnMiscstuffShowshipsasicons() +{ + Update_window = 1; + if (g_Ships_as_icons == 0) + g_Ships_as_icons = 1; + else + g_Ships_as_icons = 0; +} + +// right mouse button popup menu stuff +void CFREDView::OnContextMenu(CWnd* /*pWnd*/, CPoint point) +{ + // make sure window is active +// GetParentFrame()->ActivateFrame(); + + CMenu menu; + int objnum; + CPoint local = point; + + if (button_down) { + cancel_drag(); + return; + } + + ScreenToClient(&local); + objnum = select_object(local.x, local.y); + + if (objnum != -1) { + set_cur_object_index(objnum); + if (menu.LoadMenu(IDR_MENU_SHIP_POPUP)) { + int id = ID_EDITORS_SHIPS; + CMenu* pPopup = menu.GetSubMenu(0); + + ASSERT(pPopup != NULL); + if (Marked > 1) + pPopup->ModifyMenu(ID_EDITORS_SHIPS, MF_BYCOMMAND | MF_STRING, ID_EDITORS_SHIPS, "Edit Marked Ships"); + else { + CString str; + + if ((Objects[objnum].type == OBJ_START) || (Objects[objnum].type == OBJ_SHIP)) + str.Format("Edit %s", Ships[Objects[objnum].instance].ship_name); + + else if (Objects[objnum].type == OBJ_JUMP_NODE) { + id = ID_EDITORS_WAYPOINT; + str.Format("Edit %s", Jump_nodes[Objects[objnum].instance].name); + + } else if (Objects[objnum].type == OBJ_WAYPOINT) { + id = ID_EDITORS_WAYPOINT; + str.Format("Edit %s:%d", Waypoint_lists[Objects[objnum].instance / 65536].name, + (Objects[objnum].instance & 0xffff) + 1); + + } else if (Objects[objnum].type == OBJ_POINT) { + return; + + } else { + Int3(); + str = _T("Unknown"); + } + + pPopup->ModifyMenu(ID_EDITORS_SHIPS, MF_BYCOMMAND | MF_STRING, id, str); + } + + pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd()); // use main window for cmds + } + + } else { + if (menu.LoadMenu(IDR_MENU_EDIT_POPUP)) { + int i; + CMenu* pPopup = menu.GetSubMenu(0); + CMenu shipPopup, player_submenu, species_submenu[MAX_SPECIES_NAMES]; + ASSERT(pPopup != NULL); + + // create a popup menu based on the ship models read in ship.cpp. + shipPopup.CreatePopupMenu(); + shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_waypoint, "Waypoint"); + shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_start, "Player Start"); + shipPopup.AppendMenu(MF_STRING | MF_ENABLED, SHIP_TYPES + Id_select_type_jump_node, "Jump Node"); + for (i=0; iAppendMenu(MF_STRING | MF_POPUP | MF_ENABLED, + (UINT) shipPopup.m_hMenu, "New Object Type"); + + CWnd::DrawMenuBar(); // AppendMenu documentation says to do this. + pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd()); + } + } +} + +void CFREDView::OnEditPopupShowShipIcons() +{ + Show_ship_info = !Show_ship_info; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateEditPopupShowShipIcons(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_ship_info); +} + +void CFREDView::OnEditPopupShowShipModels() +{ + Show_ship_models = !Show_ship_models; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateEditPopupShowShipModels(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_ship_models); +} + +void CFREDView::OnEditPopupShowCompass() +{ + Show_compass = !Show_compass; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateEditPopupShowCompass(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_compass); +} + +// View implementation file +CFREDView *CFREDView::GetView() +{ + CFrameWnd *pFrame = (CFrameWnd *) (AfxGetApp()->m_pMainWnd); + + CView *pView = pFrame->GetActiveView(); + + if (!pView) + return NULL; + + // Fail if view is of wrong kind + // (this could occur with splitter windows, or additional + // views on a single document + if (! pView->IsKindOf(RUNTIME_CLASS(CFREDView))) + return NULL; + + return (CFREDView *) pView; +} + +/*void CFREDView::OnShipType0() +{ + cur_model_index = 1; +} + +void CFREDView::OnShipType1() +{ + cur_model_index = 2; +} + +void CFREDView::OnShipType2() +{ + cur_model_index = 3; +} + +void CFREDView::OnShipType3() +{ + cur_model_index = 4; +} + +void CFREDView::OnShipType4() +{ + cur_model_index = 5; +} + +void CFREDView::OnShipType5() +{ + cur_model_index = 6; +} + +void CFREDView::OnUpdateShipType1(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(cur_model_index == 2); +} + +void CFREDView::OnUpdateShipType2(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(cur_model_index == 3); +} + +void CFREDView::OnUpdateShipType3(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(cur_model_index == 4); +} + +void CFREDView::OnUpdateShipType4(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(cur_model_index == 5); +} + +void CFREDView::OnUpdateShipType5(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(cur_model_index == 6); +} + + +void CFREDView::OnUpdateShipType0(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(cur_model_index == 1); + +} + +void CFREDView::OnEditShipType6() +{ + cur_model_index = 7; + +} + +void CFREDView::OnUpdateEditShipType6(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(cur_model_index == 7); +} */ + + +// following code added by MWA 09/04/96 +// Implements messages for popup menu built on the fly. +// not sure how stable the code is, but appears to work for now. +// id's for the menu items are simply the model numbers for +// the ships. Shouldn't conflict with any other ID_* thingys. +BOOL CFREDView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) +{ + int id = (int) nID; + + if (!pHandlerInfo) { + if ((id >= SHIP_TYPES) && (id < SHIP_TYPES + Num_ship_types + 3)) { + if (nCode == CN_COMMAND) { + cur_model_index = id - SHIP_TYPES; + m_new_ship_type_combo_box.SetCurSelNEW(cur_model_index); + + } else if (nCode == CN_UPDATE_COMMAND_UI) { + // Update UI element state + ((CCmdUI*) pExtra)->SetCheck(cur_model_index + SHIP_TYPES == id); + ((CCmdUI*) pExtra)->Enable(TRUE); + } + + return TRUE; + } + } + + return CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); +} + +void CFREDView::OnMiscStatistics() +{ + char buf[2048]; + + sprintf(buf, + "Number of Objects: %d\n" + "Number of Ships: %d\n" + "Number of Wings: %d\n", + num_objects, ship_get_num_ships(), num_wings); + + MessageBox(buf, "FRED Statistics"); +} + +void CFREDView::OnUpdateChangeViewpointExternal(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(!viewpoint); +} + +void CFREDView::OnChangeViewpointExternal() +{ + viewpoint = 0; + Update_window = 1; +} + +void CFREDView::OnUpdateChangeViewpointFollow(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(viewpoint == 1); +} + +void CFREDView::OnChangeViewpointFollow() +{ + viewpoint = 1; + view_obj = cur_object_index; + Update_window = 1; +} + +void CFREDView::OnEditorsGoals() +{ + CMissionGoalsDlg dlg; + + dlg.DoModal(); +} + +void CFREDView::OnSpeed1() +{ + physics_speed = 1; + set_physics_controls(); +} + +void CFREDView::OnSpeed2() +{ + physics_speed = 2; + set_physics_controls(); +} + +void CFREDView::OnSpeed3() +{ + physics_speed = 3; + set_physics_controls(); +} + +void CFREDView::OnSpeed5() +{ + physics_speed = 5; + set_physics_controls(); +} + +void CFREDView::OnSpeed8() +{ + physics_speed = 8; + set_physics_controls(); +} + +void CFREDView::OnSpeed10() +{ + physics_speed = 10; + set_physics_controls(); +} + +void CFREDView::OnSpeed50() +{ + physics_speed = 50; + set_physics_controls(); +} + +void CFREDView::OnSpeed100() +{ + physics_speed = 100; + set_physics_controls(); +} + +void CFREDView::OnRot1() +{ + physics_rot = 2; + set_physics_controls(); +} + +void CFREDView::OnRot2() +{ + physics_rot = 10; + set_physics_controls(); +} + +void CFREDView::OnRot3() +{ + physics_rot = 25; + set_physics_controls(); +} + +void CFREDView::OnRot4() +{ + physics_rot = 50; + set_physics_controls(); +} + +void CFREDView::OnRot5() +{ + physics_rot = 100; + set_physics_controls(); +} + +void CFREDView::OnUpdateSpeed1(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_speed == 1); +} + +void CFREDView::OnUpdateSpeed2(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_speed == 2); +} + +void CFREDView::OnUpdateSpeed3(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_speed == 3); +} + +void CFREDView::OnUpdateSpeed5(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_speed == 5); +} + +void CFREDView::OnUpdateSpeed8(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_speed == 8); +} + +void CFREDView::OnUpdateSpeed10(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_speed == 10); +} + +void CFREDView::OnUpdateSpeed50(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_speed == 50); +} + +void CFREDView::OnUpdateSpeed100(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_speed == 100); +} + +void CFREDView::OnUpdateRot1(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_rot == 2); +} + +void CFREDView::OnUpdateRot2(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_rot == 10); +} + +void CFREDView::OnUpdateRot3(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_rot == 25); +} + +void CFREDView::OnUpdateRot4(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_rot == 50); +} + +void CFREDView::OnUpdateRot5(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(physics_rot == 100); +} + +void CFREDView::OnControlModeCamera() +{ + Control_mode = 0; +} + +void CFREDView::OnUpdateControlModeCamera(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(!Control_mode); +} + +void CFREDView::OnControlModeShip() +{ + Control_mode = 1; +} + +void CFREDView::OnUpdateControlModeShip(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Control_mode == 1); +} + +void CFREDView::OnShowGridPositions() +{ + Show_grid_positions = !Show_grid_positions; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowGridPositions(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_grid_positions); +} + +void CFREDView::OnShowCoordinates() +{ + Show_coordinates = !Show_coordinates; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowCoordinates(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_coordinates); +} + +void CFREDView::OnSelect() +{ + Editing_mode = 0; +} + +void CFREDView::OnUpdateSelect(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(!Editing_mode); +} + +void CFREDView::OnSelectAndMove() +{ + Editing_mode = 1; +} + +void CFREDView::OnUpdateSelectAndMove(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Editing_mode == 1); +} + +void CFREDView::OnSelectAndRotate() +{ + Editing_mode = 2; +} + +void CFREDView::OnUpdateSelectAndRotate(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Editing_mode == 2); +} + +void CFREDView::OnConstrainX() +{ + vm_vec_make(&Constraint, 1.0f, 0.0f, 0.0f); + vm_vec_make(&Anticonstraint, 0.0f, 1.0f, 1.0f); + Single_axis_constraint = 1; +} + +void CFREDView::OnUpdateConstrainX(CCmdUI* pCmdUI) +{ + pCmdUI->SetRadio(Constraint.x && !Constraint.y && !Constraint.z); +} + +void CFREDView::OnConstrainY() +{ + vm_vec_make(&Constraint, 0.0f, 1.0f, 0.0f); + vm_vec_make(&Anticonstraint, 1.0f, 0.0f, 1.0f); + Single_axis_constraint = 1; +} + +void CFREDView::OnUpdateConstrainY(CCmdUI* pCmdUI) +{ + pCmdUI->SetRadio(!Constraint.x && Constraint.y && !Constraint.z); +} + +void CFREDView::OnConstrainZ() +{ + vm_vec_make(&Constraint, 0.0f, 0.0f, 1.0f); + vm_vec_make(&Anticonstraint, 1.0f, 1.0f, 0.0f); + Single_axis_constraint = 1; +} + +void CFREDView::OnUpdateConstrainZ(CCmdUI* pCmdUI) +{ + pCmdUI->SetRadio(!Constraint.x && !Constraint.y && Constraint.z); +} + +void CFREDView::OnConstrainXz() +{ + vm_vec_make(&Constraint, 1.0f, 0.0f, 1.0f); + vm_vec_make(&Anticonstraint, 0.0f, 1.0f, 0.0f); + Single_axis_constraint = 0; +} + +void CFREDView::OnUpdateConstrainXz(CCmdUI* pCmdUI) +{ + pCmdUI->SetRadio(Constraint.x && !Constraint.y && Constraint.z); +} + +void CFREDView::OnConstrainXy() +{ + vm_vec_make(&Constraint, 1.0f, 1.0f, 0.0f); + vm_vec_make(&Anticonstraint, 0.0f, 0.0f, 1.0f); + Single_axis_constraint = 0; +} + +void CFREDView::OnUpdateConstrainXy(CCmdUI* pCmdUI) +{ + pCmdUI->SetRadio(Constraint.x && Constraint.y && !Constraint.z); +} + +void CFREDView::OnConstrainYz() +{ + vm_vec_make(&Constraint, 0.0f, 1.0f, 1.0f); + vm_vec_make(&Anticonstraint, 1.0f, 0.0f, 0.0f); + Single_axis_constraint = 0; +} + +void CFREDView::OnUpdateConstrainYz(CCmdUI* pCmdUI) +{ + pCmdUI->SetRadio(!Constraint.x && Constraint.y && Constraint.z); +} + +void CFREDView::OnSelectionLock() +{ + Selection_lock = !Selection_lock; +} + +void CFREDView::OnUpdateSelectionLock(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Selection_lock); +} + +void CFREDView::OnLButtonDblClk(UINT nFlags, CPoint point) +{ + CView::OnLButtonDblClk(nFlags, point); + if (Cursor_over != -1) { + switch (Objects[Cursor_over].type) { + case OBJ_SHIP: + case OBJ_START: + OnEditorsShips(); + break; + + case OBJ_WAYPOINT: + case OBJ_JUMP_NODE: + OnEditorsWaypoint(); + break; + } + + } else if (Briefing_dialog) + Fixed_briefing_size = !Fixed_briefing_size; +} + +void CFREDView::OnDoubleFineGridlines() +{ + double_fine_gridlines = !double_fine_gridlines; + maybe_create_new_grid(The_grid, &eye_pos, &eye_orient, 1); + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateDoubleFineGridlines(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(double_fine_gridlines); +} + +void CFREDView::OnShowDistances() +{ + Show_distances = !Show_distances; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowDistances(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_distances); +} + +void CFREDView::OnUniversalHeading() +{ + Universal_heading = !Universal_heading; +} + +void CFREDView::OnUpdateUniversalHeading(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Universal_heading); +} + +void CFREDView::OnFlyingControls() +{ + Flying_controls_mode = !Flying_controls_mode; +} + +void CFREDView::OnUpdateFlyingControls(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Flying_controls_mode); +} + +void CFREDView::OnRotateLocally() +{ + Group_rotate = !Group_rotate; +} + +void CFREDView::OnUpdateRotateLocally(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(!Group_rotate); +} + +void CFREDView::OnSelectList() +{ + ship_select dlg; + + dlg.DoModal(); +} + +// position camera to view all objects on the screen at once. Doesn't change orientation. +void view_universe(int just_marked) +{ + int i, max = 0, flags[MAX_OBJECTS]; + float dist, largest = 20.0f; + vector center, p1, p2; // center of all the objects collectively + vertex v; + object *ptr; + + for (i=0; ipos.x; + p1.y = p2.y = ptr->pos.y; + p1.z = p2.z = ptr->pos.z; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (!just_marked || (ptr->flags & OF_MARKED)) { + center = ptr->pos; + if (center.x < p1.x) + p1.x = center.x; + if (center.x > p2.x) + p2.x = center.x; + if (center.y < p1.y) + p1.y = center.y; + if (center.y > p2.y) + p2.y = center.y; + if (center.z < p1.z) + p1.z = center.z; + if (center.z > p2.z) + p2.z = center.z; + } + + ptr = GET_NEXT(ptr); + } + + vm_vec_avg(¢er, &p1, &p2); + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (!just_marked || (ptr->flags & OF_MARKED)) { + dist = vm_vec_dist_squared(¢er, &ptr->pos); + if (dist > largest) + largest = dist; + + flags[OBJ_INDEX(ptr)] = 1; // flag object as needing on-screen check + if (OBJ_INDEX(ptr) > max) + max = OBJ_INDEX(ptr); + } + + ptr = GET_NEXT(ptr); + } + + dist = fl_sqrt(largest) + 1.0f; + vm_vec_scale_add(&view_pos, ¢er, &view_orient.fvec, -dist); + g3_set_view_matrix(&view_pos, &view_orient, 0.5f); + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (!just_marked || (ptr->flags & OF_MARKED)) { + g3_rotate_vertex(&v, &ptr->pos); + Assert(!(v.codes & CC_BEHIND)); + if (g3_project_vertex(&v) & PF_OVERFLOW) + Int3(); + + while (v.codes & CC_OFF) { // is point off screen? + dist += 5.0f; // zoom out a little and check again. + vm_vec_scale_add(&view_pos, ¢er, &view_orient.fvec, -dist); + g3_set_view_matrix(&view_pos, &view_orient, 0.5f); + g3_rotate_vertex(&v, &ptr->pos); + if (g3_project_vertex(&v) & PF_OVERFLOW) + Int3(); + } + } + + ptr = GET_NEXT(ptr); + } + + dist *= 1.1f; + vm_vec_scale_add(&view_pos, ¢er, &view_orient.fvec, -dist); + g3_set_view_matrix(&view_pos, &view_orient, 0.5f); + Update_window = 1; +} + +void CFREDView::cycle_constraint() +{ + if (Single_axis_constraint) { + if (Constraint.x) + OnConstrainY(); + else if (Constraint.y) + OnConstrainZ(); + else if (Constraint.z) + OnConstrainXz(); + + } else { + if (!Constraint.x) + OnConstrainXy(); + else if (!Constraint.y) + OnConstrainYz(); + else if (!Constraint.z) + OnConstrainX(); + } +} + +void CFREDView::OnZoomExtents() +{ + view_universe(); +} + +void CFREDView::OnZoomSelected() +{ + if (query_valid_object()) { + if (Marked > 1) + view_universe(1); + else + vm_vec_scale_add(&view_pos, &Objects[cur_object_index].pos, &view_orient.fvec, Objects[cur_object_index].radius * -3.0f); + } + + Update_window = 1; +} + +void CFREDView::OnUpdateZoomSelected(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(query_valid_object()); +} + +void CFREDView::OnFormWing() +{ + if (!create_wing()) + FREDDoc_ptr->autosave("form wing"); +} + +void CFREDView::OnUpdateFormWing(CCmdUI* pCmdUI) +{ + int count = 0; + object *ptr; + + if (query_valid_object()) { + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->flags & OF_MARKED) { + if (ptr->type == OBJ_SHIP) + switch (ship_query_general_type(ptr->instance)) + { + case SHIP_TYPE_FIGHTER_BOMBER: + case SHIP_TYPE_CRUISER: + case SHIP_TYPE_AWACS: + case SHIP_TYPE_GAS_MINER: + case SHIP_TYPE_CORVETTE: + case SHIP_TYPE_FREIGHTER: + case SHIP_TYPE_CAPITAL: + case SHIP_TYPE_TRANSPORT: + count++; + } + + if (ptr->type == OBJ_START) + count++; + } + + ptr = GET_NEXT(ptr); + } + } + + pCmdUI->Enable(count > 0); +} + +int query_single_wing_marked() +{ + int i, obj; + + if (!query_valid_object()) + return 0; + + if (cur_wing == -1) + return 0; + + i = Wings[cur_wing].wave_count; + if (Marked != i) // does marked object count match number of ships in wing? + return 0; + + while (i--) { + obj = wing_objects[cur_wing][i]; + if ((Objects[obj].type != OBJ_SHIP) && (Objects[obj].type != OBJ_START)) + Error(LOCATION, "Invalid objects detected in wing \"%s\"", Wings[cur_wing].name); + +// if (Ships[Objects[obj].instance].wingnum != cur_wing) +// return 0; + Assert(Ships[Objects[obj].instance].wingnum == cur_wing); + if (!(Objects[obj].flags & OF_MARKED)) // ensure all ships in wing.are marked + return 0; + } + + return 1; +} + +void CFREDView::OnDisbandWing() +{ + if (query_single_wing_marked()) { + remove_wing(cur_wing); + FREDDoc_ptr->autosave("wing disband"); + + } else + MessageBox("One and only one wing must be selected for this operation"); +} + +void CFREDView::OnUpdateDisbandWing(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(query_single_wing_marked()); +} + +void CFREDView::OnShowHorizon() +{ + Show_horizon = !Show_horizon; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowHorizon(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_horizon); +} + +void CFREDView::OnEditorsWing() +{ + int adjust = 0; + + Assert(Wing_editor_dialog.GetSafeHwnd()); + if (!Show_sexp_help) + adjust = -SEXP_HELP_BOX_SIZE; + + if (!theApp.init_window(&Wing_wnd_data, &Wing_editor_dialog, adjust)) + return; + + Wing_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Wing_editor_dialog.ShowWindow(SW_RESTORE); +} + +void CFREDView::OnEditorsPlayer() +{ + player_start_editor dlg; + + dlg.DoModal(); +} + +void CFREDView::OnEditorsOrient() +{ + orient_editor dlg; + + dlg.DoModal(); +} + +void CFREDView::OnEditorsEvents() +{ + if (Message_editor_dlg) { + MessageBox("You must close the message editor before opening the event editor"); + return; + } + + if (!Event_editor_dlg) { + Event_editor_dlg = new event_editor; + Event_editor_dlg->Create(event_editor::IDD); + } + + Event_editor_dlg->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Event_editor_dlg->ShowWindow(SW_RESTORE); +} + +void CFREDView::OnUpdateEditorsOrient(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(query_valid_object()); +} + +void CFREDView::OnEditorsMessage() +{ + if (Event_editor_dlg) { + MessageBox("You must close the event editor before opening the message editor"); + return; + } + + if (!Message_editor_dlg) { + Message_editor_dlg = new CMessageEditorDlg; + Message_editor_dlg->Create(CMessageEditorDlg::IDD); + } + + Message_editor_dlg->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Message_editor_dlg->ShowWindow(SW_RESTORE); +} + +void CFREDView::OnEditorsStarfield() +{ + starfield_editor dlg; + + dlg.DoModal(); +} + +void CFREDView::place_background_bitmap(vector v) +{ +} + +void CFREDView::OnEditorsBgBitmaps() +{ + if (!Bg_bitmap_dialog) { + Bg_bitmap_dialog = new bg_bitmap_dlg; + Bg_bitmap_dialog->create(); + } + + Bg_bitmap_dialog->SetWindowPos(&wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Bg_bitmap_dialog->ShowWindow(SW_RESTORE); +} + +void CFREDView::OnEditorsReinforcement() +{ + reinforcement_editor_dlg dlg; + + dlg.DoModal(); +} + +void CFREDView::OnErrorChecker() +{ + int z; + + z = global_error_check(); + if (!z) + MessageBox("No errors were detected in this mission", "Woohoo!"); + + for (z=0; zinstance; + if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { + if (i < 0 || i >= MAX_SHIPS){ + return internal_error("An object has an illegal ship index"); + } + + z = Ships[i].ship_info_index; + if ((z < 0) || (z >= Num_ship_types)){ + return internal_error("A ship has an illegal class"); + } + + if (ptr->type == OBJ_START) { + t++; + if (!(Ship_info[z].flags & SIF_PLAYER_SHIP)) { + ptr->type = OBJ_SHIP; + Player_starts--; + t--; + if (error("Invalid ship type for a player. Ship has been reset to non-player ship.")){ + return 1; + } + } + + for (n=count=0; n= 0){ + count++; + } + } + + if (!count){ + if (error("Player \"%s\" has no primary weapons. Should have at least 1", Ships[i].ship_name)){ + return 1; + } + } + + for (n=count=0; n= 0){ + count++; + } + } + + if (!count){ + if (error("Player \"%s\" has no secondary weapons. Should have at least 1", Ships[i].ship_name)){ + return 1; + } + } + } + + if (Ships[i].objnum != OBJ_INDEX(ptr)){ + return internal_error("Object/ship references are corrupt"); + } + + names[obj_count] = Ships[i].ship_name; + wing = Ships[i].wingnum; + if (wing >= 0) { // ship is part of a wing, so check this + if (wing < 0 || wing >= MAX_WINGS){ // completely out of range? + return internal_error("A ship has an illegal wing index"); + } + + j = Wings[wing].wave_count; + if (!j){ + return internal_error("A ship is in a non-existant wing"); + } + + if (j < 0 || j > MAX_SHIPS_PER_WING){ + return internal_error("Invalid number of ships in wing \"%s\"", Wings[z].name); + } + + while (j--){ + if (wing_objects[wing][j] == OBJ_INDEX(ptr)){ // look for object in wing's table + break; + } + } + + if (j < 0){ + return internal_error("Ship/wing references are corrupt"); + } + } + + if ( (Ships[i].flags & SF_KILL_BEFORE_MISSION) && (Ships[i].hotkey >= 0) ){ + if (error("Ship flagged as \"destroy before mission start\" has a hotkey assignment")){ + return 1; + } + } + + if ( (Ships[i].flags & SF_KILL_BEFORE_MISSION) && (ptr->type == OBJ_START) ){ + if (error("Player start flagged as \"destroy before mission start\"")){ + return 1; + } + } + } else if (ptr->type == OBJ_WAYPOINT) { + j = i / 65536; // waypoint path number + z = i & 0xffff; // waypoint number in path + if (j < 0 || j >= Num_waypoint_lists){ + return internal_error("Object references an illegal waypoint path number"); + } + + if (z >= Waypoint_lists[j].count){ + return internal_error("Object references an illegal waypoint number in path"); + } + + sprintf(buf, "%s:%d", Waypoint_lists[j].name, z + 1); + names[obj_count] = new char[strlen(buf) + 1]; + strcpy(names[obj_count], buf); + flags[obj_count] = 1; + } else if (ptr->type == OBJ_POINT) { + if (!Briefing_dialog){ + return internal_error("Briefing icon detected when not in briefing edit mode"); + } + } else if (ptr->type == OBJ_JUMP_NODE) { + if (i < 0 || i >= Num_jump_nodes){ + return internal_error("Object has illegal jump node index"); + } + } else { + return internal_error("An unknown object type (%d) was detected", ptr->type); + } + + for (i=0; i= 0) { // is ship being used? + count++; + if (!query_valid_object(Ships[i].objnum)){ + return internal_error("Ship uses an unused object"); + } + + z = Objects[Ships[i].objnum].type; + if ((z != OBJ_SHIP) && (z != OBJ_START)){ + return internal_error("Object should be a ship, but isn't"); + } + + if (fred_check_sexp(Ships[i].arrival_cue, OPR_BOOL, "arrival cue of ship \"%s\"", Ships[i].ship_name)){ + return -1; + } + + if (fred_check_sexp(Ships[i].departure_cue, OPR_BOOL, "departure cue of ship \"%s\"", Ships[i].ship_name)){ + return -1; + } + + if (Ships[i].arrival_location != ARRIVE_AT_LOCATION) { + if (Ships[i].arrival_anchor < 0){ + if (error("Ship \"%s\" requires a valid arrival target", Ships[i].ship_name)){ + return 1; + } + } + } + + if (Ships[i].departure_location != DEPART_AT_LOCATION) { + if (Ships[i].departure_anchor < 0){ + if (error("Ship \"%s\" requires a valid departure target", Ships[i].ship_name)){ + return 1; + } + } + } + + ai = Ships[i].ai_index; + if (ai < 0 || ai >= MAX_AI_INFO){ + return internal_error("AI index out of range for ship \"%s\"", Ships[i].ship_name); + } + + if (Ai_info[ai].shipnum != i){ + return internal_error("AI/ship references are corrupt"); + } + + if ((str = error_check_initial_orders(Ai_info[ai].goals, i, -1))>0) { + if (*str == '*') + return internal_error("Initial orders error for ship \"%s\"\n\n%s", Ships[i].ship_name, str + 1); + else if (*str == '!') + return 1; + else if (error("Initial orders error for ship \"%s\"\n\n%s", Ships[i].ship_name, str)) + return 1; + } + + obj = Ai_info[ai].dock_objnum; + if (obj >= 0) { + if (!query_valid_object(obj)){ + return internal_error("Ship \"%s\" initially docked with non-existant ship", Ships[i].ship_name); + } + + if (Objects[obj].type != OBJ_SHIP && Objects[obj].type != OBJ_START){ + return internal_error("Ship \"%s\" initially docked with non-ship object", Ships[i].ship_name); + } + + ship = get_ship_from_obj(obj); + if (!ship_docking_valid(i, ship) && !ship_docking_valid(ship, i)){ + return internal_error("Docking illegal between \"%s\" and \"%s\" (initially docked)", Ships[i].ship_name, Ships[ship].ship_name); + } + + z = get_docking_list(Ships[i].modelnum); + point = Ai_info[ai].dock_index; + if (point < 0 || point >= z){ + internal_error("Invalid docker point (\"%s\" initially docked with \"%s\")", Ships[i].ship_name, Ships[ship].ship_name); + } + + z = get_docking_list(Ships[ship].modelnum); + point = Ai_info[ai].dockee_index; + if (point < 0 || point >= z){ + internal_error("Invalid dockee point (\"%s\" initially docked with \"%s\")", Ships[i].ship_name, Ships[ship].ship_name); + } + } + } + } + + if (count != ship_get_num_ships()){ + return internal_error("num_ships is incorrect"); + } + + count = 0; + for (i=0; i MAX_SHIPS_PER_WING){ + return internal_error("Invalid number of ships in wing \"%s\"", Wings[i].name); + } + + while (j--) { + obj = wing_objects[i][j]; + if (obj < 0 || obj >= MAX_OBJECTS){ + return internal_error("Wing_objects has an illegal object index"); + } + + if (!query_valid_object(obj)){ + return internal_error("Wing_objects references an unused object"); + } + +// Now, at this point, we can assume several things. We have a valid object because +// we passed query_valid_object(), and all valid objects were already checked above, +// so this object has valid information, such as the instance. + + if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) { + ship = Objects[obj].instance; + sprintf(buf, "%s %d", Wings[i].name, j + 1); + if (stricmp(buf, Ships[ship].ship_name)){ + return internal_error("Ship \"%s\" in wing should be called \"%s\"", Ships[ship].ship_name, buf); + } + + switch (ship_query_general_type(ship)) + { + case SHIP_TYPE_FIGHTER_BOMBER: + case SHIP_TYPE_CRUISER: + case SHIP_TYPE_AWACS: + case SHIP_TYPE_GAS_MINER: + case SHIP_TYPE_CORVETTE: + case SHIP_TYPE_FREIGHTER: + case SHIP_TYPE_CAPITAL: + case SHIP_TYPE_TRANSPORT: + case SHIP_TYPE_SUPERCAP: + break; + + default: + if (error("Ship \"%s\" is an illegal type to be in a wing", Ships[ship].ship_name)){ + return 1; + } + } + } else { + return internal_error("Wing_objects of \"%s\" references an illegal object type", Wings[i].name); + } + + if (Ships[ship].wingnum != i){ + return internal_error("Wing/ship references are corrupt"); + } + + if (ship != Wings[i].ship_index[j]){ + return internal_error("Ship/wing references are corrupt"); + } + + if (team < 0){ + team = Ships[ship].team; + } else if (team != Ships[ship].team && team < 999){ + if (error("ship teams mixed within same wing (\"%s\")", Wings[i].name)){ + return 1; + } + } + } + + if ((Wings[i].special_ship < 0) || (Wings[i].special_ship >= Wings[i].wave_count)){ + return internal_error("Special ship out of range for \"%s\"", Wings[i].name); + } + + if (Wings[i].num_waves < 0){ + return internal_error("Number of waves for \"%s\" is negative", Wings[i].name); + } + + if ((Wings[i].threshold < 0) || (Wings[i].threshold >= Wings[i].wave_count)){ + return internal_error("Threshold for \"%s\" is invalid", Wings[i].name); + } + + if (Wings[i].threshold + Wings[i].wave_count > MAX_SHIPS_PER_WING) { + Wings[i].threshold = MAX_SHIPS_PER_WING - Wings[i].wave_count; + if(error("Threshold for wing \"%s\" is higher than allowed. Reset to %d", Wings[i].name, Wings[i].threshold)){ + return 1; + } + } + + for (j=0; j0) { + if (*str == '*') + return internal_error("Initial orders error for wing \"%s\"\n\n%s", Wings[i].name, str + 1); + else if (*str == '!') + return 1; + else if (error("Initial orders error for wing \"%s\"\n\n%s", Wings[i].name, str)) + return 1; + } + + } + } + + if (count != num_wings){ + return internal_error("num_wings is incorrect"); + } + + for (i=0; i MAX_PLAYERS){ + return internal_error("Number of player starts exceeds max limit"); + } + + if (!multi && (Player_starts > 1)){ + if (error("Multiple player starts exist, but this is a single player mission")){ + return 1; + } + } + + if (Num_reinforcements > MAX_REINFORCEMENTS){ + return internal_error("Number of reinforcements exceeds max limit"); + } + + for (i=0; i= 0) && !stricmp(Ships[ship].ship_name, Reinforcements[i].name)) { + z = 1; + break; + } + } + + for (wing=0; wing= MAX_SHIPS) // hacked! -1 should be illegal.. + return internal_error("Message originator index is out of range"); + + if (Ships[z].objnum == -1) + return internal_error("Message originator points to nonexistant ship"); + + if (fred_check_sexp(Messages[i].sexp, OPR_BOOL, + "Message formula from \"%s\"", Ships[Messages[i].who_from].ship_name)) + return -1; + }*/ + + Assert((Player_start_shipnum >= 0) && (Player_start_shipnum < MAX_SHIPS) && (Ships[Player_start_shipnum].objnum >= 0)); + i = global_error_check_player_wings(multi); + if (i){ + return i; + } + + for (i=0; inum_icons; + for (i=0; iicons[i].id > 0) && (sp->icons[i].id == sp->icons[j].id)){ + if (error("Duplicate icon IDs %d in briefing stage %d", sp->icons[i].id, s + 1)){ + return 1; + } + } + } + } + } + } + + for ( j = 0; j < Num_teams; j++ ) { + for (i=0; i MAX_JUMP_NODES){ + return internal_error("Jump node count is illegal"); + } + + fred_check_message_personas(); + + return g_err; +} + +int CFREDView::global_error_check_mixed_player_wing(int w) +{ + int i, s, species = -1, mixed = 0; + + for (i=0; i= 0) { + free_sexp2(Wings[alpha].arrival_cue); + Wings[alpha].arrival_cue = Locked_sexp_true; + } + + if (multi && (alpha < 0)){ + if (error("Alpha wing is required for multiplayer missions")){ + return 1; + } + } + + // Check to be sure that any player starting wing doesn't have > 1 wave for multiplayer + if ( multi ) { + if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ) { + if ( alpha && (Wings[alpha].num_waves > 1) ) { + error("Alpha wing must contain only 1 wave.\nThis change has been made for you."); + Wings[alpha].num_waves = 1; + return 1; + } + if ( zeta && (Wings[zeta].num_waves > 1) ) { + error("Zeta wing must contain only 1 wave.\nThis change has been made for you."); + Wings[zeta].num_waves = 1; + return 1; + } + + } else { + if ( alpha && (Wings[alpha].num_waves > 1) ) { + error("Alpha wing must contain only 1 wave.\nThis change has been made for you."); + Wings[alpha].num_waves = 1; + return 1; + } + if ( beta && (Wings[beta].num_waves > 1) ) { + error("Beta wing must contain only 1 wave.\nThis change has been made for you."); + Wings[beta].num_waves = 1; + return 1; + } + if ( gamma && (Wings[gamma].num_waves > 1) ) { + error("Gamma wing must contain only 1 wave.\nThis change has been made for you."); + Wings[gamma].num_waves = 1; + return 1; + } + } + } + + // if not a multiplayer misison, or a coop multiplayer mission, then do "normal" + // wing name checking. + if ( !multi || (The_mission.game_type & MISSION_TYPE_MULTI_COOP) ) { + if (((alpha >= 0) || (z >= 0)) && (alpha != z)){ + if (error("Player start is not in Alpha wing")){ + return 1; + } + } + + if ((beta >= 0) && (alpha < 0)){ + if (error("Alpha wing required, but not present")){ + return 1; + } + } + + if ((gamma >= 0) && (beta < 0)){ + if (error("Beta wing required, but not present")){ + return 1; + } + } + + if ((alpha >= 0) && (Wings[alpha].wave_count > 4)){ + if (error("Alpha wing has too many ships. Should only have 4 max.")){ + return 1; + } + } + + if ((beta >= 0) && (Wings[beta].wave_count > 4)){ + if (error("Beta wing has too many ships. Should only have 4 max.")){ + return 1; + } + } + + if ((gamma >= 0) && (Wings[gamma].wave_count > 4)){ + if (error("Gamma wing has too many ships. Should only have 4 max.")){ + return 1; + } + } + + if ((alpha >= 0) && Wings[alpha].arrival_delay){ + if (error("Alpha wing shouldn't have a non-zero arrival delay")){ + return 1; + } + } + } else if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ) { + if ( zeta == -1 ){ + if (error("Zeta wing is required for multiplayer team vs. team missions")){ + return 1; + } + } + + if ( Wings[alpha].wave_count > 4 ){ + if (error("Alpha wing has too many ships. Should only have 4 max.")){ + return 1; + } + } + + if ( Wings[zeta].wave_count > 4 ) { + if (error("Zeta wing has too many ships. Should only have 4 max.")){ + return 1; + } + } + + if ((alpha >= 0) && Wings[alpha].arrival_delay){ + if (error("Alpha wing shouldn't have a non-zero arrival delay")){ + return 1; + } + } + + if ((zeta >= 0) && Wings[zeta].arrival_delay){ + if (error("Zeta wing shouldn't have a non-zero arrival delay")){ + return 1; + } + } + } else if ( The_mission.game_type & MISSION_TYPE_MULTI_DOGFIGHT ) { + if ( Wings[alpha].wave_count > 4 ) { + if (error("Alpha wing has too many ships. Should only have 4 max.")){ + return 1; + } + } + + if ((alpha >= 0) && Wings[alpha].arrival_delay){ + if (error("Alpha wing shouldn't have a non-zero arrival delay")){ + return 1; + } + } + } else { + error("Unknown game type: %d", The_mission.game_type); + return 1; + } + + if (multi) { + if (alpha >= 0){ + if (global_error_check_mixed_player_wing(alpha)){ + return 1; + } + } + + if (beta >= 0){ + if (global_error_check_mixed_player_wing(beta)){ + return 1; + } + } + + if (gamma >= 0){ + if (global_error_check_mixed_player_wing(gamma)){ + return 1; + } + } + + if (zeta >= 0){ + if (global_error_check_mixed_player_wing(zeta)){ + return 1; + } + } + } + + alpha_count = zeta_count = 0; + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + i = ptr->instance; + err = 0; + if (ptr->type == OBJ_START) { + z = Ships[i].wingnum; + if (z < 0) { + err = 1; + } else { + if (z == alpha){ + alpha_count++; + } else if (z == zeta){ + zeta_count++; + } + + if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) { + if ((z != alpha) && (z != zeta)){ + err = 1; + } + } else { + if ((z != alpha) && (z != beta) && (z != gamma)){ + err = 1; + } + } + } + + if (err) { + if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) { + if (error("Player \"%s\" should be part Alpha or Zeta wing", Ships[i].ship_name)){ + return 1; + } + } else { + if (error("Player \"%s\" should be part Alpha, Beta or Gamma wing", Ships[i].ship_name)){ + return 1; + } + } + } + } + + ptr = GET_NEXT(ptr); + } + + if ((alpha >= 0) && !alpha_count){ + if (error("Alpha wing doesn't contain any players, which it should.")){ + return 1; + } + } + + if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS){ + if ((zeta >= 0) && !zeta_count){ + if (error("Zeta wing doesn't contain any players, which it should.")){ + return 1; + } + } + } + + return 0; +} + +int CFREDView::error(char *msg, ...) +{ + char buf[2048]; + va_list args; + + va_start(args, msg); + vsprintf(buf, msg, args); + va_end(args); + + g_err = 1; + if (MessageBox(buf, "Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDOK) + return 0; + + return 1; +} + +int CFREDView::internal_error(char *msg, ...) +{ + char buf[2048]; + va_list args; + + va_start(args, msg); + vsprintf(buf, msg, args); + va_end(args); + + g_err = 1; + +#ifndef NDEBUG + char buf2[2048]; + + sprintf(buf2, "%s\n\nThis is an internal error. Please let Jason\n" + "know about this so he can fix it. Click cancel to debug.", buf); + + if (MessageBox(buf2, "Internal Error", MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL) + Int3(); // drop to debugger so the problem can be analyzed. + +#else + MessageBox(buf, "Error", MB_OK | MB_ICONEXCLAMATION); +#endif + + return -1; +} + +int CFREDView::fred_check_sexp(int sexp, int type, char *msg, ...) +{ + char buf[512], buf2[2048], buf3[4096]; + int err = 0, z, faulty_node; + va_list args; + + va_start(args, msg); + vsprintf(buf, msg, args); + va_end(args); + + if (sexp == -1) + return 0; + + z = check_sexp_syntax(sexp, type, 1, &faulty_node); + if (!z) + return 0; + + convert_sexp_to_string(sexp, buf2, SEXP_ERROR_CHECK_MODE); + sprintf(buf3, "Error in %s: %s\n\nIn sexpression: %s\n(Error appears to be: %s)", + buf, sexp_error_message(z), buf2, Sexp_nodes[faulty_node].text); + + if (z < 0 && z > -100) + err = 1; + + if (err) + return internal_error(buf3); + + if (error(buf3)) + return 1; + + return 0; +} + +void CFREDView::OnEditorsWaypoint() +{ + int adjust = 0; + + Assert(Waypoint_editor_dialog.GetSafeHwnd()); + if (!Show_sexp_help) + adjust = -SEXP_HELP_BOX_SIZE; + + if (!theApp.init_window(&Waypoint_wnd_data, &Waypoint_editor_dialog)) + return; + + Waypoint_editor_dialog.SetWindowPos(&wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Waypoint_editor_dialog.ShowWindow(SW_RESTORE); +} + +char *error_check_initial_orders(ai_goal *goals, int ship, int wing) +{ + char *source; + int i, j, num, flag, found, inst, team, team2; + object *ptr; + + if (ship >= 0) { + source = Ships[ship].ship_name; + team = Ships[ship].team; + for (i=0; ierror("Order \"%s\" isn't allowed for ship \"%s\"", get_order_name(goals[i].ai_mode), source)) + return "!"; + } + + } else { + Assert(wing >= 0); + Assert(Wings[wing].wave_count > 0); + source = Wings[wing].name; + team = Ships[Objects[wing_objects[wing][0]].instance].team; + for (j=0; jerror("Order \"%s\" isn't allowed for ship \"%s\"", get_order_name(goals[i].ai_mode), + Ships[Wings[wing].ship_index[j]].ship_name)) + return "!"; + } + } + + for (i=0; i 0) { + if (*goals[i].ship_name == '<') + return "Invalid target"; + + if (!stricmp(goals[i].ship_name, source)) + if (ship >= 0) + return "Target of ship's goal is itself"; + else + return "Target of wing's goal is itself"; + } + + inst = team2 = -1; + if (flag == 1) { // target waypoint required + for (j=0; jtype == OBJ_SHIP || ptr->type == OBJ_START) { + inst = ptr->instance; + if (!stricmp(goals[i].ship_name, Ships[inst].ship_name)) { + found = 1; + break; + } + } + + ptr = GET_NEXT(ptr); + } + + if (!found) + return "*Invalid target ship name"; + + if (wing >= 0) { // check if target ship is in wing + if (Ships[inst].wingnum == wing && Objects[Ships[inst].objnum].type != OBJ_START) + return "Target ship of wing's goal is within said wing"; + } + + team2 = Ships[inst].team; + + } else if (flag == 3) { // target wing required + for (j=0; j= MAX_WINGS) + return "*Invalid target wing name"; + + if (ship >= 0) { // check if ship is in target wing + if (Ships[ship].wingnum == j) + return "Target wing of ship's goal is same wing said ship is part of"; + } + + team2 = Ships[Objects[wing_objects[j][0]].instance].team; + + } else if (flag == 4) { + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START) { + inst = ptr->instance; + if (!stricmp(goals[i].ship_name, Ships[inst].ship_name)) { + found = 2; + break; + } + + } else if (ptr->type == OBJ_WAYPOINT) { + if (!stricmp(goals[i].ship_name, object_name(OBJ_INDEX(ptr)))) { + found = 1; + break; + } + } + + ptr = GET_NEXT(ptr); + } + + if (!found) + return "*Invalid target ship or waypoint name"; + + if (found == 2) { + if (wing >= 0) { // check if target ship is in wing + if (Ships[inst].wingnum == wing && Objects[Ships[inst].objnum].type != OBJ_START) + return "Target ship of wing's goal is within said wing"; + } + + team2 = Ships[inst].team; + } + } + + switch (goals[i].ai_mode) { + case AI_GOAL_DESTROY_SUBSYSTEM: + Assert(flag == 2 && inst >= 0); + if (ship_get_subsys_index(&Ships[inst], goals[i].docker.name, 1) < 0) + return "Unknown subsystem type"; + + break; + + case AI_GOAL_DOCK: { + int dock1 = -1, dock2 = -1, model1, model2; + + Assert(flag == 2 && inst >= 0); + if (!ship_docking_valid(ship, inst)) + return "Docking illegal between given ship types"; + + model1 = Ships[ship].modelnum; + num = get_docking_list(model1); + for (j=0; j= 0) && (dock2 >= 0)) { + if ( !(model_get_dock_index_type(model1, dock1) & model_get_dock_index_type(model2, dock2)) ) + return "Dock points are incompatible"; + } + + break; + } + } + + switch (goals[i].ai_mode) { + case AI_GOAL_GUARD: + case AI_GOAL_GUARD_WING: + // if ((team == TEAM_HOSTILE && team2 == TEAM_FRIENDLY) || (team == TEAM_FRIENDLY && team2 == TEAM_HOSTILE)) { + if (team != team2) { // MK, added support for TEAM_NEUTRAL. Won't this work? + if (ship >= 0) + return "Ship assigned to guard opposite team"; + else + return "Wing assigned to guard opposite team"; + } + + break; + + case AI_GOAL_CHASE: + case AI_GOAL_CHASE_WING: + case AI_GOAL_DESTROY_SUBSYSTEM: + case AI_GOAL_DISARM_SHIP: + case AI_GOAL_DISABLE_SHIP: + if (team == team2) { + if (ship >= 0) + return "Ship assigned to attack same team"; + else + return "Wings assigned to attack same team"; + } + + break; + } + } + + return NULL; +} + +// function (which is called externally from message editor and event editor) so skip through +// the sexpression nodes to look for send-message commands to try to associate message personas +// to ships +void fred_check_message_personas() +{ +/* + int i, op, j, ship_index; + char *mname, *who_from; + object *objp; + + // this function is responsible for assigning personas to ships as well as error checking them. + // clear out the persona index on all ship objects + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if ( objp->type == OBJ_SHIP ) { + Ships[objp->instance].persona_index = -1; + } + } + + + for (i = 0; i < MAX_SEXP_NODES; i++ ) { + if ( Sexp_nodes[i].type == SEXP_NOT_USED ) + continue; + + // look for only operator nodes + if ( Sexp_nodes[i].subtype != SEXP_ATOM_OPERATOR ) + continue; + + // now look for the send-message opeator + op = find_operator( Sexp_nodes[i].text ); + if ( op != OP_SEND_MESSAGE ) + continue; + + // have the message. parse through the message to determine who is sending the message. + who_from = CTEXT(CDR(i)); + + // we can ignore messages from any wingman, and allied, or from God. + if ( !stricmp(who_from, "") || !stricmp(who_from, "") || (who_from[0] == '#') ) + continue; + + mname = CTEXT(CDR(CDR(CDR(i)))); + + // check to see if who_from is a wing. Don't do processing if so. + if ( wing_name_lookup(who_from, 1) != -1 ) + continue; + + ship_index = ship_name_lookup( who_from ); + if ( ship_index == -1 ) { + Int3(); // get allender. something funny is up with shipnames in send-message + continue; + } + + for ( j = Num_builtin_messages; j < Num_messages; j++ ) { + if ( !stricmp(mname, Messages[j].name) ) { + + // check to see if there is a persona for this message -- if not, bail + if ( Messages[j].persona_index == -1 ) + break; + + // if a ship isn't assigned a persona, and this message says that he is, assign it, and move on + if ( Ships[ship_index].persona_index == -1 ) { + Ships[ship_index].persona_index = Messages[j].persona_index; + continue; + } + + // we must be sure of the following conditions: + // 1) a ship isn't assigned > 1 persona + + if ( Ships[ship_index].persona_index != Messages[j].persona_index ) + Fred_view_wnd->error("Ship %s has at least two personas attached to it:\n%s and %s", Ships[ship_index].ship_name, Personas[Ships[ship_index].persona_index].name, Personas[Messages[j].persona_index].name ); + } + } + } + + // check that two or more ships are not using the same persona + for (i = 0; i < Num_personas; i++ ) { + int persona_count; + object *objp; + + // move through object list looking for number of shis using this persona + persona_count = 0; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if ( objp->type != OBJ_SHIP ) + continue; + if (Ships[objp->instance].persona_index == i ) + persona_count++; + } + + if ( persona_count > 1 ) + Fred_view_wnd->error("Persona %s used by more than 1 ship", Personas[Messages[j].persona_index].name ); + } +*/ + +} + +void CFREDView::OnViewOutlines() +{ + Show_outlines = !Show_outlines; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateViewOutlines(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_outlines); +} + +void CFREDView::OnUpdateNewShipType(CCmdUI* pCmdUI) +{ + int z; + CWnd *bar; + + z = m_new_ship_type_combo_box.GetCurSelNEW(); + if (z == CB_ERR) + m_new_ship_type_combo_box.SetCurSelNEW(cur_model_index); + else + cur_model_index = z; + + bar = GetDlgItem(pCmdUI->m_nID); + if (!bar) { + pCmdUI -> ContinueRouting(); + return; // not for us + } + + pCmdUI -> SetCheck((bar->GetStyle() & WS_VISIBLE) != 0); +} + +void CFREDView::OnShowStarfield() +{ + Show_stars = !Show_stars; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowStarfield(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_stars); +} + +void CFREDView::OnAsteroidEditor() +{ + asteroid_editor dlg; + + dlg.DoModal(); +} + +void CFREDView::OnRunFreespace() +{ + BOOL r; + STARTUPINFO si; + PROCESS_INFORMATION pi; + char *lpMsgBuf; + + if (!FREDDoc_ptr->SaveModified()) + return; + + si.cb = sizeof(si); + si.lpReserved = NULL; + si.lpDesktop = NULL; + si.lpTitle = NULL; + si.dwFlags = 0; + si.cbReserved2 = 0; + si.lpReserved2 = NULL; + + r = CreateProcess("..\\..\\Fs.exe", NULL, NULL, NULL, FALSE, 0, NULL, "..\\..", &si, &pi); + if (!r) { + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + + // Display the string. + MessageBox(lpMsgBuf); + + // Free the buffer. + LocalFree( lpMsgBuf ); + } +} + +void CFREDView::OnEditorCampaign() +{ + if (!FREDDoc_ptr->SaveModified()) + return; + + Assert(!Campaign_wnd); + Campaign_wnd = new campaign_tree_wnd; + if (Campaign_wnd->Create(NULL, "Campaign Editor", WS_OVERLAPPEDWINDOW | WS_MAXIMIZE, + CFrameWnd::rectDefault, NULL, "IDR_MENU_CAMPAIGN")) { + Campaign_wnd->ShowWindow(SW_SHOW); + Campaign_wnd->UpdateWindow(); + } +} + +void CFREDView::OnShowShips() +{ + Show_ships = !Show_ships; + correct_marking(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowShips(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_ships); +} + +void CFREDView::OnShowStarts() +{ + Show_starts = !Show_starts; + correct_marking(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowStarts(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_starts); +} + +void CFREDView::OnShowFriendly() +{ + Show_friendly = !Show_friendly; + correct_marking(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowFriendly(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_friendly); +} + +void CFREDView::OnShowHostile() +{ + Show_hostile = !Show_hostile; + correct_marking(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowHostile(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_hostile); +} + +void CFREDView::OnToggleViewpoint() +{ + if (viewpoint || !query_valid_object()) + viewpoint = 0; + + else { + viewpoint = 1; + view_obj = cur_object_index; + } + + Update_window = 1; +} + +void CFREDView::OnRevert() +{ + if (!FREDDoc_ptr->SaveModified()) + return; + + FREDDoc_ptr->DeleteContents(); + FREDDoc_ptr->OnOpenDocument(NULL); +} + +void CFREDView::OnUpdateRevert(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(*Mission_filename); +} + +BOOL CFREDView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) +{ + if ((Cursor_over >= 0) || Selection_lock) { + if (Editing_mode == 1) { + SetCursor(h_cursor_move); + return TRUE; + + } else if (Editing_mode == 2) { + SetCursor(h_cursor_rotate); + return TRUE; + } + } + + return CView::OnSetCursor(pWnd, nHitTest, message); +} + +void CFREDView::OnHideObjects() +{ + object *ptr; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->flags & OF_MARKED) { + ptr->flags |= OF_HIDDEN; + unmark_object(OBJ_INDEX(ptr)); + } + + ptr = GET_NEXT(ptr); + } +} + +void CFREDView::OnShowHiddenObjects() +{ + object *ptr; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + ptr->flags &= ~OF_HIDDEN; + ptr = GET_NEXT(ptr); + } + + Update_window = 1; +} + +void CFREDView::OnEditUndo() +{ + vector viewer_pos; + matrix viewer_orient; + + if (Undo_available) { + viewer_pos = view_pos; + viewer_orient = view_orient; + FREDDoc_ptr->autoload(); + view_pos = viewer_pos; + view_orient = viewer_orient; + } +} + +void CFREDView::OnUpdateEditUndo(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(Undo_available); +} + +void CFREDView::OnEditorsBriefing() +{ + if (!Briefing_dialog) { + Briefing_dialog = new briefing_editor_dlg; + Briefing_dialog->create(); + } + + Briefing_dialog->SetWindowPos(&wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Briefing_dialog->ShowWindow(SW_RESTORE); +} + +void CFREDView::OnEditorsDebriefing() +{ + debriefing_editor_dlg dlg; + + dlg.DoModal(); +} + +void CFREDView::OnSaveCamera() +{ + saved_cam_pos = view_pos; + saved_cam_orient = view_orient; +} + +void CFREDView::OnRestoreCamera() +{ + view_pos = saved_cam_pos; + view_orient = saved_cam_orient; + Update_window = 1; +} + +void CFREDView::OnUpdateRestoreCamera(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!IS_VEC_NULL(&saved_cam_orient.fvec)); +} + +void CFREDView::OnShowSexpHelp() +{ + CRect rect; + + Show_sexp_help = !Show_sexp_help; + Ship_editor_dialog.show_hide_sexp_help(); + Wing_editor_dialog.show_hide_sexp_help(); + + if (Event_editor_dlg) { + Event_editor_dlg->GetWindowRect(rect); + if (Show_sexp_help) + rect.bottom += SEXP_HELP_BOX_SIZE; + else + rect.bottom -= SEXP_HELP_BOX_SIZE; + + Event_editor_dlg->MoveWindow(rect); + } +} + +void CFREDView::OnUpdateShowSexpHelp(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_sexp_help); +} + +void CFREDView::OnLookatObj() +{ + Lookat_mode = !Lookat_mode; + if (Lookat_mode && query_valid_object()) { + vector v, loc; + matrix m; + + loc = Objects[cur_object_index].pos; + vm_vec_sub(&v, &loc, &view_pos); + + if (v.x || v.y || v.z) { + vm_vector_2_matrix(&m, &v, NULL, NULL); + view_orient = m; + } + } +} + +void CFREDView::OnUpdateLookatObj(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Lookat_mode); +} + +void CFREDView::OnGroup(UINT nID) +{ + int n = 1 << (nID - ID_GROUP1); + object *objp; + + unmark_all(); + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->type == OBJ_SHIP) { + if (Ships[objp->instance].group & n) + mark_object(OBJ_INDEX(objp)); + } + + objp = GET_NEXT(objp); + } + + Update_window = 1; +} + +void CFREDView::OnSetGroup(UINT nID) +{ + int i, err = 0, n = 1 << (nID - ID_SET_GROUP1); + object *objp; + + for (i=0; iflags & OF_MARKED) { + if (objp->type == OBJ_SHIP) { + Ships[objp->instance].group |= n; + + } else + err = 1; + } + + objp = GET_NEXT(objp); + } + + if (err) + Fred_main_wnd->MessageBox("Only ships can be in groups, and not players or waypoints, etc.\n" + "These illegal objects you marked were not placed in the group"); + + Update_window = 1; +} + +void CFREDView::OnInitialUpdate() +{ + char *ptr, text[512]; + + CView::OnInitialUpdate(); + + // check the time/checksum strings. + expire_game = 0; + ptr = &stamp[0]; + if ( memcmp(ptr, DEFAULT_CHECKSUM_STRING, strlen(DEFAULT_CHECKSUM_STRING)) ) { + int stamped_checksum, checksum; + + // the checksum is not the default checksum. Calculate the checksum of the string + // and compare it. + memcpy(&stamped_checksum, ptr, sizeof(stamped_checksum) ); + ptr = &stamp[0]; + ptr += 8; // get us to the actual string to calculate the checksum + CALCULATE_STAMP_CHECKSUM(); + + if ( checksum != stamped_checksum ){ + expire_game = EXPIRE_BAD_CHECKSUM; + } + + // now check the time + ptr = &stamp[0]; + ptr += 4; + if ( memcmp( ptr, DEFAULT_TIME_STRING, strlen(DEFAULT_TIME_STRING)) ) { + int expire_time, current_time; + + // not the default time -- check against the current time + memcpy( &expire_time, ptr, sizeof(expire_time) ); + time( (long *)¤t_time ); + if ( current_time > expire_time ) + expire_game = EXPIRE_BAD_TIME; + } + + // since the default checksum has changed -- put up a message which shows who the program + // is stamped for + ptr = &stamp[0]; + ptr += 8; + sprintf(text, "This version of Fred has been compiled for %s", ptr); + MessageBox(text, NULL, MB_OK); + + if ( expire_game ) + SetTimer(1, FRED_EXPIRE_TIME, expire_game_proc); + } +} + +void CFREDView::OnEditorsAdjustGrid() +{ + adjust_grid_dlg dlg; + + dlg.DoModal(); + Update_window = 1; +} + +void CFREDView::OnEditorsShieldSys() +{ + shield_sys_dlg dlg; + + dlg.DoModal(); +} + +void CFREDView::OnLevelObj() +{ + level_controlled(); + Update_window = 1; +} + +void CFREDView::OnAlignObj() +{ + verticalize_controlled(); + Update_window = 1; +} + +void CFREDView::OnControlObj() +{ + Control_mode = (Control_mode + 1) % 2; +} + +void CFREDView::OnNextObj() +{ + object *ptr; + + if (Bg_bitmap_dialog) { + if (Cur_bitmap == -1) + { + if (Num_starfield_bitmaps) + { + Cur_bitmap = 0; + Bg_bitmap_dialog -> update_data(); + } + + return; + } + + Cur_bitmap++; + if (Cur_bitmap >= Num_starfield_bitmaps) + Cur_bitmap = 0; + + Bg_bitmap_dialog -> update_data(); + return; + } + + if (EMPTY(&obj_used_list)) + return; + + if (query_valid_object()) { + ptr = Objects[cur_object_index].next; + if (ptr == END_OF_LIST(&obj_used_list)) + ptr = GET_NEXT(ptr); + + } else + ptr = GET_FIRST(&obj_used_list); + + if (Marked > 1) { // cycle through marked list + while (!(ptr->flags & OF_MARKED)) + { + ptr = GET_NEXT(ptr); + if (ptr == END_OF_LIST(&obj_used_list)) + ptr = GET_NEXT(ptr); + } + + set_cur_object_index(OBJ_INDEX(ptr)); + + } else { + if (Marked) + unmark_object(cur_object_index); + + mark_object(OBJ_INDEX(ptr)); + } +} + +void CFREDView::OnPrevObj() +{ + int arr[MAX_OBJECTS], i = 0, n = 0; + object *ptr; + + if (Bg_bitmap_dialog) { + if (Cur_bitmap == -1) + { + if (Num_starfield_bitmaps) + { + Cur_bitmap = Num_starfield_bitmaps - 1; + Bg_bitmap_dialog -> update_data(); + } + + return; + } + + Cur_bitmap--; + if (Cur_bitmap < 0) + Cur_bitmap = Num_starfield_bitmaps - 1; + + Bg_bitmap_dialog -> update_data(); + return; + } + + if (EMPTY(&obj_used_list)) + return; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (cur_object_index == OBJ_INDEX(ptr)) + i = n; + + arr[n++] = OBJ_INDEX(ptr); + ptr = GET_NEXT(ptr); + } + + Assert(n); + if (query_valid_object()) { + i--; + if (i < 0) + i = n - 1; + + } else + i = n - 1; + + if (Marked > 1) { // cycle through marked list + while (!(Objects[i].flags & OF_MARKED)) + { + i--; + if (i < 0) + i = n - 1; + } + + set_cur_object_index(i); + + } else { + if (Marked) + unmark_object(cur_object_index); + + mark_object(i); + } +} + +void CFREDView::OnEditDelete() +{ + if (!button_down && Marked) { + delete_marked(); + FREDDoc_ptr->autosave("object delete"); + } + + Update_window = 2; // For some strange reason, need to redraw twice for it to take. +} + +void CFREDView::OnEditDeleteWing() +{ + if (!button_down && (cur_wing >= 0)) { + delete_wing(); + FREDDoc_ptr->autosave("wing delete"); + cur_wing = -1; + if (!Marked) + Ship_editor_dialog.initialize_data(1); + + Wing_editor_dialog.initialize_data(1); + } + + Update_window = 2; // For some strange reason, need to redraw twice for it to take. +} + +void CFREDView::OnMarkWing() +{ + int i, wing = cur_wing; + + if (wing != -1) + { + unmark_all(); + for (i=0; i= 0 && Wings[wing].special_ship < Wings[wing].wave_count); + set_cur_object_index(wing_objects[wing][Wings[wing].special_ship]); + } +} + +void CFREDView::OnUpdateControlObj(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Control_mode != 0); +} + +void CFREDView::OnAaGridlines() +{ + Aa_gridlines = !Aa_gridlines; +} + +void CFREDView::OnUpdateAaGridlines(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Aa_gridlines); + Update_window = 1; +} + +void CFREDView::OnCmdBrief() +{ + cmd_brief_dlg dlg; + + dlg.DoModal(); + Update_window = 1; +} + +void CFREDView::OnDisableUndo() +{ + Autosave_disabled = !Autosave_disabled; +} + +void CFREDView::OnUpdateDisableUndo(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Autosave_disabled); +} + +void CFREDView::OnUpdateCmdBrief(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(!(The_mission.game_type & MISSION_TYPE_MULTI)); +} + +int get_visible_sub_system_count(ship *shipp) +{ + int count = 0; + ship_subsys *cur_subsys; + + for (cur_subsys = GET_FIRST(&shipp->subsys_list); cur_subsys != END_OF_LIST(&shipp->subsys_list); cur_subsys = GET_NEXT(cur_subsys)) { + if (cur_subsys->system_info->subobj_num != -1) { + count++; + } + } + + return count; +} + +int get_next_visible_subsys(ship *shipp, ship_subsys **next_subsys) +{ + int count = get_visible_sub_system_count(shipp); + + // return don't try to display + if (count == 0) { + return 0; + } + + // first timer + if (*next_subsys == NULL) { + *next_subsys = &shipp->subsys_list; + } + + // look before wrap + for (*next_subsys = GET_NEXT(*next_subsys); *next_subsys != END_OF_LIST(&shipp->subsys_list); *next_subsys = GET_NEXT(*next_subsys)) { + if ((*next_subsys)->system_info->subobj_num != -1) { + Update_window = 1; + return 1; + } + } + + // look for first after wrap + for (*next_subsys = GET_FIRST(&shipp->subsys_list); *next_subsys != END_OF_LIST(&shipp->subsys_list); *next_subsys = GET_NEXT(*next_subsys)) { + if ((*next_subsys)->system_info->subobj_num != -1) { + Update_window = 1; + return 1; + } + } + + Int3(); // should be impossible to miss + return 0; +} + +int get_prev_visible_subsys(ship *shipp, ship_subsys **prev_subsys) +{ + int count = get_visible_sub_system_count(shipp); + + // return don't try to display + if (count == 0) { + return 0; + } + + // first timer + Assert(*prev_subsys != NULL); + + // look before wrap + for (*prev_subsys = GET_PREV(*prev_subsys); *prev_subsys != END_OF_LIST(&shipp->subsys_list); *prev_subsys = GET_PREV(*prev_subsys)) { + if ((*prev_subsys)->system_info->subobj_num != -1) { + Update_window = 1; + return 1; + } + } + + // look for first after wrap + for (*prev_subsys = GET_LAST(&shipp->subsys_list); *prev_subsys != END_OF_LIST(&shipp->subsys_list); *prev_subsys = GET_PREV(*prev_subsys)) { + if ((*prev_subsys)->system_info->subobj_num != -1) { + Update_window = 1; + return 1; + } + } + + Int3(); // should be impossible to miss + return 0; +} + +// update next subsystem to view +void CFREDView::OnNextSubsys() +{ + object *objp; + + if (cur_object_index < 0) { + OnCancelSubsys(); + } + + objp = &Objects[cur_object_index]; + + // check if cur object is ship type + if (objp->type == OBJ_SHIP) { + + // check if same ship + if (Render_subsys.ship_obj == objp) { + + // if already on, advance to next + if (Render_subsys.do_render) { + if ( !get_next_visible_subsys(&Ships[objp->instance], &Render_subsys.cur_subsys) ) { + OnCancelSubsys(); + } + } else { + Int3(); + } + } else { + // clean up + OnCancelSubsys(); + + // set up new and advance to first + Render_subsys.do_render = true; + Render_subsys.ship_obj = objp; + if ( !get_next_visible_subsys(&Ships[objp->instance], &Render_subsys.cur_subsys) ) { + OnCancelSubsys(); + } + } + } else { + // not ship type + OnCancelSubsys(); + } +} + +void CFREDView::OnPrevSubsys() +{ + if (!Render_subsys.do_render) { + return; + } + + if ( (cur_object_index < 0) || (Objects[cur_object_index].type != OBJ_SHIP) || (&Objects[cur_object_index] != Render_subsys.ship_obj) ) { + OnCancelSubsys(); + return; + } + + if ( !get_prev_visible_subsys(&Ships[Objects[cur_object_index].instance], &Render_subsys.cur_subsys) ) { + OnCancelSubsys(); + } + +} + +void CFREDView::OnCancelSubsys() +{ + Render_subsys.do_render = false; + Render_subsys.ship_obj = NULL; + Render_subsys.cur_subsys = NULL; + Update_window = 1; +} + +void CFREDView::OnShowPaths() +{ + Show_paths_fred = !Show_paths_fred; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowPaths(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_paths_fred); +} + +void CFREDView::OnShowDockPoints() +{ + Show_dock_points = !Show_dock_points; + theApp.write_ini_file(); + Update_window = 1; +} + +void CFREDView::OnUpdateShowDockPoints(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Show_dock_points); +} + +void CFREDView::OnDumpStats() +{ + DumpStats dlg; + + dlg.DoModal(); +} \ No newline at end of file diff --git a/src/fred2/grid.cpp b/src/fred2/grid.cpp new file mode 100644 index 0000000..9a6e90c --- /dev/null +++ b/src/fred2/grid.cpp @@ -0,0 +1,224 @@ +/* + * $Logfile: /Freespace2/code/FRED2/Grid.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Grid dialog box created by Mike. Probably will never be used again. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 5 6/18/97 11:36p Lawrance + * move grid rendering code to MissionGrid.cpp + * + * 4 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "grid.h" + +#include "3d.h" +#include "object.h" +#include "editor.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +extern int double_fine_gridlines; + +///////////////////////////////////////////////////////////////////////////// +// CGrid dialog + +// Modeless constructor, MK +CGrid::CGrid(CView* pView) +{ + m_pGView = pView; +} + + +CGrid::CGrid(CWnd* pParent /*=NULL*/) + : CDialog(CGrid::IDD, pParent) +{ + //{{AFX_DATA_INIT(CGrid) + m_GridSize = 0; + //}}AFX_DATA_INIT + + m_pGView = NULL; + +} + + +void CGrid::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CGrid) + DDX_Text(pDX, IDC_GRID_SIZE, m_GridSize); + DDV_MinMaxUInt(pDX, m_GridSize, 2, 20); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CGrid, CDialog) + //{{AFX_MSG_MAP(CGrid) + ON_BN_CLICKED(IDC_GRID_XY_PLANE, OnGridXyPlane) + ON_BN_CLICKED(IDC_GRID_XZ_PLANE, OnGridXzPlane) + ON_BN_CLICKED(IDC_GRID_YZ_PLANE, OnGridYzPlane) + ON_WM_CLOSE() + ON_WM_DESTROY() + ON_WM_KILLFOCUS() + ON_WM_VSCROLL() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CGrid message handlers + +extern grid *The_grid; + +void GridOrient(vector *forward, vector *right) +{ + vector center; + int nrows, ncols; + float square_size; + + if (The_grid != NULL){ + center = The_grid->center; + nrows = The_grid->nrows; + ncols = The_grid->ncols; + square_size = The_grid->square_size; + } else { + vm_vec_make(¢er, 0.0f, 0.0f, 0.0f); + nrows = 20; + ncols = 20; + square_size = 2.0f; + } + + The_grid = create_grid(The_grid, forward, + right, + ¢er, + nrows, ncols, + square_size); + + physics_init(&The_grid->physics); +} + +void CGrid::OnGridXyPlane() +{ + vector forward, right; + + vm_vec_make(&forward, 0.0f, 1.0f, 0.0f); + vm_vec_make(&right, 1.0f, 0.0f, 0.0f); + + GridOrient(&forward, &right); +} + +void CGrid::OnGridXzPlane() +{ + vector forward, right; + + vm_vec_make(&forward, 0.0f, 0.0f, 1.0f); + vm_vec_make(&right, 1.0f, 0.0f, 0.0f); + + GridOrient(&forward, &right); +} + +void CGrid::OnGridYzPlane() +{ + vector forward, right; + + vm_vec_make(&forward, 0.0f, 1.0f, 0.0f); + vm_vec_make(&right, 0.0f, 0.0f, 1.0f); + + GridOrient(&forward, &right); +} + +BOOL CGrid::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) +{ + return CDialog::Create(IDD, pParentWnd); +} + +BOOL CGrid::Create() +{ + return CDialog::Create(CGrid::IDD); +} + + +void CGrid::OnClose() +{ + DestroyWindow(); +} + +void CGrid::OnDestroy() +{ + UpdateData(TRUE); + + CDialog::OnDestroy(); + +} + +void CGrid::OnKillFocus(CWnd* pNewWnd) +{ + CDialog::OnKillFocus(pNewWnd); + + DestroyWindow(); + +} + +BOOL CGrid::DestroyWindow() +{ + // TODO: Add your specialized code here and/or call the base class + + UpdateData(TRUE); + + return CDialog::DestroyWindow(); +} + +BOOL CGrid::OnInitDialog() +{ + CDialog::OnInitDialog(); + + CSpinButtonCtrl* pSpin = (CSpinButtonCtrl*) GetDlgItem(IDC_SPIN_GRID_SIZE); + pSpin->SetRange(2, 20); + if ((m_GridSize < 2) || (m_GridSize > 20)) + m_GridSize = The_grid->ncols/5; + pSpin->SetPos(m_GridSize); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void CGrid::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + CString strValue; + + CSpinButtonCtrl* pSpin = (CSpinButtonCtrl*) GetDlgItem(IDC_SPIN_GRID_SIZE); + strValue.Format("%i", pSpin->GetPos()); + pSpin->GetBuddy()->SetWindowText(strValue); + + The_grid->nrows = pSpin->GetPos()*5; + The_grid->ncols = The_grid->nrows; + + modify_grid(The_grid); + + CDialog::OnVScroll(nSBCode, nPos, pScrollBar); +} diff --git a/src/fred2/hlp/afxcore.rtf b/src/fred2/hlp/afxcore.rtf new file mode 100644 index 0000000..4b2be0a --- /dev/null +++ b/src/fred2/hlp/afxcore.rtf @@ -0,0 +1,23 @@ +{\rtf1\ansi \deff0\deflang1033{\fonttbl{\f0\froman\fcharset0\fprq2 Tms Rmn{\*\falt Times New Roman};}{\f2\fswiss\fcharset0\fprq2 Helv{\*\falt Arial};}{\f7\fswiss\fcharset0\fprq2 MS Sans Serif;} +{\f9\fswiss\fcharset0\fprq2 Helvetica{\*\falt Arial};}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255; +\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\stylesheet{\nowidctlpar \fs20 \snext0 Normal;}{\s1\sb240\nowidctlpar +\b\f2\ul \sbasedon0\snext0 heading 1;}{\s2\sb120\nowidctlpar \b\f2 \sbasedon0\snext0 heading 2;}{\s3\li360\nowidctlpar \b \sbasedon0\snext17 heading 3;}{\s4\li360\nowidctlpar \ul \sbasedon0\snext17 heading 4;}{\s5\li720\nowidctlpar \b\fs20 +\sbasedon0\snext17 heading 5;}{\s6\li720\nowidctlpar \fs20\ul \sbasedon0\snext17 heading 6;}{\s7\li720\nowidctlpar \i\fs20 \sbasedon0\snext17 heading 7;}{\s8\li720\nowidctlpar \i\fs20 \sbasedon0\snext17 heading 8;}{\s9\li720\nowidctlpar \i\fs20 +\sbasedon0\snext17 heading 9;}{\*\cs10 \additive Default Paragraph Font;}{\*\cs15 \additive\f0\fs16\up6\lang1033 \sbasedon10 footnote reference;}{\s16\nowidctlpar \fs20 \sbasedon0\snext16 footnote text;}{\s17\li720\nowidctlpar \fs20 \sbasedon0\snext17 +Normal Indent;}{\s18\fi-240\li480\sb80\nowidctlpar\tx480 \f9 \sbasedon0\snext18 nscba;}{\s19\fi-240\li240\sa20\nowidctlpar \f9 \sbasedon0\snext19 j;}{\s20\li480\sa20\nowidctlpar \f9 \sbasedon0\snext20 ij;}{\s21\sb80\sa20\nowidctlpar \f9 +\sbasedon0\snext21 btb;}{\s22\fi-240\li2400\sb20\sa20\nowidctlpar \f9\fs20 \sbasedon0\snext22 ctcb;}{\s23\fi-240\li480\sa40\nowidctlpar\tx480 \f9 \sbasedon0\snext23 ns;}{\s24\sa120\nowidctlpar \f9\fs28 \sbasedon0\snext24 TT;}{ +\s25\fi-240\li2400\sa20\nowidctlpar \f9 \sbasedon0\snext25 crtj;}{\s26\fi-240\li480\nowidctlpar\tx480 \f9 \sbasedon0\snext26 nsca;}{\s27\sa20\nowidctlpar \f9 \sbasedon0\snext27 bt;}{\s28\li240\sb120\sa40\nowidctlpar \f9 \sbasedon0\snext28 Hf;}{ +\s29\li240\sb120\sa40\nowidctlpar \f9 \sbasedon0\snext29 Hs;}{\s30\li480\sb120\sa40\nowidctlpar \f9 \sbasedon0\snext30 RT;}{\s31\fi-2160\li2160\sb240\sa80\nowidctlpar\tx2160 \f9 \sbasedon0\snext31 c;}{\s32\li2160\sa20\nowidctlpar \f9 \sbasedon0\snext32 +ct;}{\s33\li240\sa20\nowidctlpar \f9 \sbasedon0\snext33 it;}{\s34\li480\nowidctlpar \f9\fs20 \sbasedon0\snext34 nsct;}{\s35\fi-160\li400\sb80\sa40\nowidctlpar \f9 \sbasedon0\snext35 nscb;}{\s36\fi-2640\li2880\sb120\sa40\nowidctlpar\brdrb\brdrs\brdrw15 +\brdrbtw\brdrs\brdrw15 \tx2880 \f9 \sbasedon0\snext36 HC2;}{\s37\fi-2640\li2880\sb120\sa20\nowidctlpar\tx2880 \f9 \sbasedon0\snext37 C2;}{\s38\fi-240\li2400\sa20\nowidctlpar \f9\fs20 \sbasedon0\snext38 ctc;}{\s39\li2160\sb160\nowidctlpar \f9 +\sbasedon0\snext39 crt;}{\s40\li480\sb20\sa40\nowidctlpar \f9 \sbasedon0\snext40 or;}{\s41\fi-259\li360\sb40\sa40\nowidctlpar\tx360 \f7\fs20 \sbasedon0\snext41 Ln1;}{\s42\li115\sb80\sa80\nowidctlpar \f7\fs20 \sbasedon0\snext0 *Intro;}{ +\s43\li115\sb80\sa80\keepn\nowidctlpar \b\f7 \sbasedon3\snext42 *Title;}{\s44\fi-245\li360\sb80\nowidctlpar \f7\fs20 \snext44 *Jl;}{\s45\li360\sb40\sa40\nowidctlpar \f7\fs20 \snext0 Lp1;}{\s46\fi-1800\li1915\sb60\sl-240\slmult0\nowidctlpar\tx1915 +\f7\fs20 \sbasedon0\snext46 Tph;}{\s47\li115\sb120\sa80\nowidctlpar \b\f7\fs20 \snext41 Proch;}{\*\cs48 \additive\super \sbasedon10 endnote reference;}}{\info{\title AFXCORE}{\subject MFC Core RTF Help}{\author Jason Hoffoss}{\operator Adam Pletcher} +{\creatim\yr1992\mo10\dy7\hr11\min4}{\revtim\yr1998\mo5\dy26\hr19\min26}{\version2}{\edmins1}{\nofpages1}{\nofwords34}{\nofchars195}{\*\company Parallax Software}{\vern57431}}\widowctrl\ftnbj\aenddoc\hyphcaps0 \fet0\sectd \linex576\endnhere {\*\pnseclvl1 +\pnucrm\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang{\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang{\pntxta )}}{\*\pnseclvl5 +\pndec\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang +{\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang{\pntxtb (}{\pntxta )}}\pard\plain \sl240\slmult0\widctlpar \fs20 {\cs15\fs16\up6 #{\footnote \pard\plain \s16\widctlpar \fs20 {\cs15\fs16\up6 #} main_index}}{\fs16\up6 }{ +\b\f2\fs24\up6 FRED Help}{\b\f2 +\par }{\f9 +\par }{\f2 For detailed information on how to use FRED, please refer to the FRED documentation (FRED.rtf file). It should be used as the definitive source of information for the operation of FRED rather than this online help system. +\par }} \ No newline at end of file diff --git a/src/fred2/hlp/afxhelp.hm b/src/fred2/hlp/afxhelp.hm new file mode 100644 index 0000000..b511e5e --- /dev/null +++ b/src/fred2/hlp/afxhelp.hm @@ -0,0 +1,272 @@ +// This is a part of the Microsoft Foundation Classes C++ library. +// Copyright (C) 1992-1995 Microsoft Corporation +// All rights reserved. +// +// This source code is only intended as a supplement to the +// Microsoft Foundation Classes Reference and related +// electronic documentation provided with the library. +// See these sources for detailed information regarding the +// Microsoft Foundation Classes product. + +// Non-Client HitTest help IDs +HID_HT_NOWHERE 0x40000 +HID_HT_CAPTION 0x40002 +HID_HT_SIZE 0x40004 +HID_HT_HSCROLL 0x40006 +HID_HT_VSCROLL 0x40007 +HID_HT_MINBUTTON 0x40008 +HID_HT_MAXBUTTON 0x40009 +HID_HT_SIZE 0x4000A // alias: ID_HT_LEFT +HID_HT_SIZE 0x4000B // alias: ID_HT_RIGHT +HID_HT_SIZE 0x4000C // alias: ID_HT_TOP +HID_HT_SIZE 0x4000D // alias: ID_HT_TOPLEFT +HID_HT_SIZE 0x4000E // alias: ID_HT_TOPRIGHT +HID_HT_SIZE 0x4000F // alias: ID_HT_BOTTOM +HID_HT_SIZE 0x40010 // alias: ID_HT_BOTTOMLEFT +HID_HT_SIZE 0x40011 // alias: ID_HT_BOTTOMRIGHT +HID_HT_SIZE 0x40012 // alias: ID_HT_BORDER + +// WM_SYSCOMMAND help IDs +HID_SC_SIZE 0x1EF00 +HID_SC_MOVE 0x1EF01 +HID_SC_MINIMIZE 0x1EF02 +HID_SC_MAXIMIZE 0x1EF03 +HID_SC_NEXTWINDOW 0x1EF04 +HID_SC_PREVWINDOW 0x1EF05 +HID_SC_CLOSE 0x1EF06 +HID_SC_RESTORE 0x1EF12 +HID_SC_TASKLIST 0x1EF13 + +// File MRU and aliases +HID_FILE_MRU_FILE1 0x1E110 +HID_FILE_MRU_FILE1 0x1E111 // aliases: MRU_2 - MRU_16 +HID_FILE_MRU_FILE1 0x1E112 +HID_FILE_MRU_FILE1 0x1E113 +HID_FILE_MRU_FILE1 0x1E114 +HID_FILE_MRU_FILE1 0x1E115 +HID_FILE_MRU_FILE1 0x1E116 +HID_FILE_MRU_FILE1 0x1E117 +HID_FILE_MRU_FILE1 0x1E118 +HID_FILE_MRU_FILE1 0x1E119 +HID_FILE_MRU_FILE1 0x1E11A +HID_FILE_MRU_FILE1 0x1E11B +HID_FILE_MRU_FILE1 0x1E11C +HID_FILE_MRU_FILE1 0x1E11D +HID_FILE_MRU_FILE1 0x1E11E +HID_FILE_MRU_FILE1 0x1E11F + +// Window menu list +HID_WINDOW_ALL 0x1EF1F + +// OLE menu and aliases +HID_OLE_VERB_1 0x1E210 +HID_OLE_VERB_1 0x1E211 // aliases: VERB_2 -> VERB_16 +HID_OLE_VERB_1 0x1E212 +HID_OLE_VERB_1 0x1E213 +HID_OLE_VERB_1 0x1E214 +HID_OLE_VERB_1 0x1E215 +HID_OLE_VERB_1 0x1E216 +HID_OLE_VERB_1 0x1E217 +HID_OLE_VERB_1 0x1E218 +HID_OLE_VERB_1 0x1E219 +HID_OLE_VERB_1 0x1E21A +HID_OLE_VERB_1 0x1E21B +HID_OLE_VERB_1 0x1E21C +HID_OLE_VERB_1 0x1E21D +HID_OLE_VERB_1 0x1E21E +HID_OLE_VERB_1 0x1E21F + +// Commands (HID_*) +HID_FILE_NEW 0x1E100 +HID_FILE_OPEN 0x1E101 +HID_FILE_CLOSE 0x1E102 +HID_FILE_SAVE 0x1E103 +HID_FILE_SAVE_AS 0x1E104 +HID_FILE_PAGE_SETUP 0x1E105 +HID_FILE_PRINT_SETUP 0x1E106 +HID_FILE_PRINT 0x1E107 +HID_FILE_PRINT_DIRECT 0x1E108 +HID_FILE_PRINT_PREVIEW 0x1E109 +HID_FILE_UPDATE 0x1E10A +HID_FILE_SAVE_COPY_AS 0x1E10B +HID_FILE_SEND_MAIL 0x1E10C +HID_EDIT_CLEAR 0x1E120 +HID_EDIT_CLEAR_ALL 0x1E121 +HID_EDIT_COPY 0x1E122 +HID_EDIT_CUT 0x1E123 +HID_EDIT_FIND 0x1E124 +HID_EDIT_PASTE 0x1E125 +HID_EDIT_PASTE_LINK 0x1E126 +HID_EDIT_PASTE_SPECIAL 0x1E127 +HID_EDIT_REPEAT 0x1E128 +HID_EDIT_REPLACE 0x1E129 +HID_EDIT_SELECT_ALL 0x1E12A +HID_EDIT_UNDO 0x1E12B +HID_EDIT_REDO 0x1E12C +HID_WINDOW_NEW 0x1E130 +HID_WINDOW_ARRANGE 0x1E131 +HID_WINDOW_CASCADE 0x1E132 +HID_WINDOW_TILE_HORZ 0x1E133 +HID_WINDOW_TILE_VERT 0x1E134 +HID_WINDOW_SPLIT 0x1E135 +HID_APP_ABOUT 0x1E140 +HID_APP_EXIT 0x1E141 +HID_HELP_INDEX 0x1E142 +HID_HELP_FINDER 0x1E143 +HID_HELP_USING 0x1E144 +HID_CONTEXT_HELP 0x1E145 +HID_NEXT_PANE 0x1E150 +HID_PREV_PANE 0x1E151 +HID_FORMAT_FONT 0x1E160 +HID_OLE_INSERT_NEW 0x1E200 +HID_OLE_EDIT_LINKS 0x1E201 +HID_OLE_EDIT_CONVERT 0x1E202 +HID_OLE_EDIT_CHANGE_ICON 0x1E203 +HID_OLE_EDIT_PROPERTIES 0x1E204 +HID_VIEW_TOOLBAR 0x1E800 +HID_VIEW_STATUS_BAR 0x1E801 +HID_RECORD_FIRST 0x1E900 +HID_RECORD_LAST 0x1E901 +HID_RECORD_NEXT 0x1E902 +HID_RECORD_PREV 0x1E903 +HID_WIZBACK 0x13023 +HID_WIZNEXT 0x13024 +HID_WIZFINISH 0x13025 + +// Dialogs (AFX_HIDD_*) +AFX_HIDD_FILEOPEN 0x27004 +AFX_HIDD_FILESAVE 0x27005 +AFX_HIDD_FONT 0x27006 +AFX_HIDD_COLOR 0x27007 +AFX_HIDD_PRINT 0x27008 +AFX_HIDD_PRINTSETUP 0x27009 +AFX_HIDD_FIND 0x2700A +AFX_HIDD_REPLACE 0x2700B +AFX_HIDD_NEWTYPEDLG 0x27801 +AFX_HIDD_PRINTDLG 0x27802 +AFX_HIDD_PREVIEW_TOOLBAR 0x27803 +AFX_HIDD_PREVIEW_SHORTTOOLBAR 0x2780B +AFX_HIDD_INSERTOBJECT 0x27804 +AFX_HIDD_CHANGEICON 0x27805 +AFX_HIDD_CONVERT 0x27806 +AFX_HIDD_PASTESPECIAL 0x27807 +AFX_HIDD_EDITLINKS 0x27808 +AFX_HIDD_FILEBROWSE 0x27809 +AFX_HIDD_BUSY 0x2780A +AFX_HIDD_OBJECTPROPERTIES 0x2780C +AFX_HIDD_CHANGESOURCE 0x2780D + +// Prompts/Errors (AFX_HIDP_*) +AFX_HIDP_NO_ERROR_AVAILABLE 0x3F020 +AFX_HIDP_INVALID_FILENAME 0x3F100 +AFX_HIDP_FAILED_TO_OPEN_DOC 0x3F101 +AFX_HIDP_FAILED_TO_SAVE_DOC 0x3F102 +AFX_HIDP_ASK_TO_SAVE 0x3F103 +AFX_HIDP_FAILED_TO_CREATE_DOC 0x3F104 +AFX_HIDP_FILE_TOO_LARGE 0x3F105 +AFX_HIDP_FAILED_TO_START_PRINT 0x3F106 +AFX_HIDP_FAILED_TO_LAUNCH_HELP 0x3F107 +AFX_HIDP_INTERNAL_FAILURE 0x3F108 +AFX_HIDP_COMMAND_FAILURE 0x3F109 +AFX_HIDP_FAILED_MEMORY_ALLOC 0x3F10A +AFX_HIDP_PARSE_INT 0x3F110 +AFX_HIDP_PARSE_REAL 0x3F111 +AFX_HIDP_PARSE_INT_RANGE 0x3F112 +AFX_HIDP_PARSE_REAL_RANGE 0x3F113 +AFX_HIDP_PARSE_STRING_SIZE 0x3F114 +AFX_HIDP_PARSE_RADIO_BUTTON 0x3F115 +AFX_HIDP_PARSE_BYTE 0x3F116 +AFX_HIDP_PARSE_UINT 0x3F117 +AFX_HIDP_PARSE_DATETIME 0x3F118 +AFX_HIDP_PARSE_CURRENCY 0x3F119 +AFX_HIDP_FAILED_INVALID_FORMAT 0x3F120 +AFX_HIDP_FAILED_INVALID_PATH 0x3F121 +AFX_HIDP_FAILED_DISK_FULL 0x3F122 +AFX_HIDP_FAILED_ACCESS_READ 0x3F123 +AFX_HIDP_FAILED_ACCESS_WRITE 0x3F124 +AFX_HIDP_FAILED_IO_ERROR_READ 0x3F125 +AFX_HIDP_FAILED_IO_ERROR_WRITE 0x3F126 +AFX_HIDP_STATIC_OBJECT 0x3F180 +AFX_HIDP_FAILED_TO_CONNECT 0x3F181 +AFX_HIDP_SERVER_BUSY 0x3F182 +AFX_HIDP_BAD_VERB 0x3F183 +AFX_HIDP_FAILED_TO_NOTIFY 0x3F185 +AFX_HIDP_FAILED_TO_LAUNCH 0x3F186 +AFX_HIDP_ASK_TO_UPDATE 0x3F187 +AFX_HIDP_FAILED_TO_UPDATE 0x3F188 +AFX_HIDP_FAILED_TO_REGISTER 0x3F189 +AFX_HIDP_FAILED_TO_AUTO_REGISTER 0x3F18A +AFX_HIDP_FAILED_TO_CONVERT 0x3F18B +AFX_HIDP_GET_NOT_SUPPORTED 0x3F18C +AFX_HIDP_SET_NOT_SUPPORTED 0x3F18D +AFX_HIDP_ASK_TO_DISCARD 0x3F18E +AFX_HIDP_FAILED_TO_CREATE 0x3F18F +AFX_HIDP_FAILED_MAPI_LOAD 0x3F190 +AFX_HIDP_INVALID_MAPI_DLL 0x3F191 +AFX_HIDP_FAILED_MAPI_SEND 0x3F192 +AFX_HIDP_FILE_NONE 0x3F1A0 +AFX_HIDP_FILE_GENERIC 0x3F1A1 +AFX_HIDP_FILE_NOT_FOUND 0x3F1A2 +AFX_HIDP_FILE_BAD_PATH 0x3F1A3 +AFX_HIDP_FILE_TOO_MANY_OPEN 0x3F1A4 +AFX_HIDP_FILE_ACCESS_DENIED 0x3F1A5 +AFX_HIDP_FILE_INVALID_FILE 0x3F1A6 +AFX_HIDP_FILE_REMOVE_CURRENT 0x3F1A7 +AFX_HIDP_FILE_DIR_FULL 0x3F1A8 +AFX_HIDP_FILE_BAD_SEEK 0x3F1A9 +AFX_HIDP_FILE_HARD_IO 0x3F1AA +AFX_HIDP_FILE_SHARING 0x3F1AB +AFX_HIDP_FILE_LOCKING 0x3F1AC +AFX_HIDP_FILE_DISKFULL 0x3F1AD +AFX_HIDP_FILE_EOF 0x3F1AE +AFX_HIDP_ARCH_NONE 0x3F1B0 +AFX_HIDP_ARCH_GENERIC 0x3F1B1 +AFX_HIDP_ARCH_READONLY 0x3F1B2 +AFX_HIDP_ARCH_ENDOFFILE 0x3F1B3 +AFX_HIDP_ARCH_WRITEONLY 0x3F1B4 +AFX_HIDP_ARCH_BADINDEX 0x3F1B5 +AFX_HIDP_ARCH_BADCLASS 0x3F1B6 +AFX_HIDP_ARCH_BADSCHEMA 0x3F1B7 +AFX_HIDP_SQL_CONNECT_FAIL 0x3F281 +AFX_HIDP_SQL_RECORDSET_FORWARD_ONLY 0x3F282 +AFX_HIDP_SQL_EMPTY_COLUMN_LIST 0x3F283 +AFX_HIDP_SQL_FIELD_SCHEMA_MISMATCH 0x3F284 +AFX_HIDP_SQL_ILLEGAL_MODE 0x3F285 +AFX_HIDP_SQL_MULTIPLE_ROWS_AFFECTED 0x3F286 +AFX_HIDP_SQL_NO_CURRENT_RECORD 0x3F287 +AFX_HIDP_SQL_NO_ROWS_AFFECTED 0x3F288 +AFX_HIDP_SQL_RECORDSET_READONLY 0x3F289 +AFX_HIDP_SQL_SQL_NO_TOTAL 0x3F28A +AFX_HIDP_SQL_ODBC_LOAD_FAILED 0x3F28B +AFX_HIDP_SQL_DYNASET_NOT_SUPPORTED 0x3F28C +AFX_HIDP_SQL_SNAPSHOT_NOT_SUPPORTED 0x3F28D +AFX_HIDP_SQL_API_CONFORMANCE 0x3F28E +AFX_HIDP_SQL_SQL_CONFORMANCE 0x3F28F +AFX_HIDP_SQL_NO_DATA_FOUND 0x3F290 +AFX_HIDP_SQL_ROW_UPDATE_NOT_SUPPORTED 0x3F291 +AFX_HIDP_SQL_ODBC_V2_REQUIRED 0x3F292 +AFX_HIDP_SQL_NO_POSITIONED_UPDATES 0x3F293 +AFX_HIDP_SQL_LOCK_MODE_NOT_SUPPORTED 0x3F294 +AFX_HIDP_SQL_DATA_TRUNCATED 0x3F295 +AFX_HIDP_SQL_ROW_FETCH 0x3F296 +AFX_HIDP_SQL_INCORRECT_ODBC 0x3F297 +AFX_HIDP_SQL_UPDATE_DELETE_FAILED 0x3F298 +AFX_HIDP_SQL_DYNAMIC_CURSOR_NOT_SUPPORTED 0x3F299 +AFX_HIDP_DAO_ENGINE_INITIALIZATION 0x3F2A0 +AFX_HIDP_DAO_DFX_BIND 0x3F2A1 +AFX_HIDP_DAO_OBJECT_NOT_OPEN 0x3F2A2 +AFX_HIDP_DAO_ROWTOOSHORT 0x3F2A3 +AFX_HIDP_DAO_BADBINDINFO 0x3F2A4 +AFX_HIDP_DAO_COLUMNUNAVAILABLE 0x3F2A5 + +// Frame Controls (AFX_HIDW_*) +AFX_HIDW_TOOLBAR 0x5E800 +AFX_HIDW_STATUS_BAR 0x5E801 +AFX_HIDW_PREVIEW_BAR 0x5E802 +AFX_HIDW_RESIZE_BAR 0x5E803 +AFX_HIDW_DOCKBAR_TOP 0x5E81B +AFX_HIDW_DOCKBAR_LEFT 0x5E81C +AFX_HIDW_DOCKBAR_RIGHT 0x5E81D +AFX_HIDW_DOCKBAR_BOTTOM 0x5E81E +AFX_HIDW_DOCKBAR_FLOAT 0x5E81F + diff --git a/src/fred2/hlp/afxprint.rtf b/src/fred2/hlp/afxprint.rtf new file mode 100644 index 0000000..14725d7 --- /dev/null +++ b/src/fred2/hlp/afxprint.rtf @@ -0,0 +1,98 @@ +{\rtf1\ansi \deff0\deflang1024{\fonttbl{\f0\froman Times New Roman;}{\f1\froman Symbol;}{\f2\fswiss Arial;}{\f3\froman Tms Rmn;}{\f4\fswiss Helv;}{\f5\fswiss Helvetica;}{\f6\fswiss MS Sans Serif;}} +{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue127;\red0\green127\blue127;\red0\green127\blue0; +\red127\green0\blue127;\red127\green0\blue0;\red127\green127\blue0;\red127\green127\blue127;\red192\green192\blue192;}{\stylesheet{\s244 \f3\fs16\up6\lang1033 \sbasedon0\snext0 footnote reference;}{\s245 \f3\fs20\lang1033 +\sbasedon0\snext245 footnote text;}{\s246\li720 \i\f3\fs20\lang1033 \sbasedon0\snext255 heading 9;}{\s247\li720 \i\f3\fs20\lang1033 \sbasedon0\snext255 heading 8;}{\s248\li720 \i\f3\fs20\lang1033 \sbasedon0\snext255 heading 7;}{\s249\li720 +\f3\fs20\ul\lang1033 \sbasedon0\snext255 heading 6;}{\s250\li720 \b\f3\fs20\lang1033 \sbasedon0\snext255 heading 5;}{\s251\li360 \f3\ul\lang1033 \sbasedon0\snext255 heading 4;}{\s252\li360 \b\f3\lang1033 \sbasedon0\snext255 heading 3;}{\s253\sb120 +\b\f4\lang1033 \sbasedon0\snext0 heading 2;}{\s254\sb240 \b\f4\ul\lang1033 \sbasedon0\snext0 heading 1;}{\s255\li720 \f3\fs20\lang1033 \sbasedon0\snext255 Normal Indent;}{\f3\fs20\lang1033 \snext0 Normal;}{\s2\fi-240\li480\sb80\tx480 \f5\lang1033 +\sbasedon0\snext2 nscba;}{\s3\fi-240\li240\sa20 \f5\lang1033 \sbasedon0\snext3 j;}{\s4\li480\sa20 \f5\lang1033 \sbasedon0\snext4 ij;}{\s5\sb80\sa20 \f5\lang1033 \sbasedon0\snext5 btb;}{\s6\fi-240\li2400\sb20\sa20 \f5\fs20\lang1033 \sbasedon0\snext6 ctcb;} +{\s7\fi-240\li480\sa40\tx480 \f5\lang1033 \sbasedon0\snext7 ns;}{\s8\sa120 \f5\fs28\lang1033 \sbasedon0\snext8 TT;}{\s9\fi-240\li2400\sa20 \f5\lang1033 \sbasedon0\snext9 crtj;}{\s10\fi-240\li480\tx480 \f5\lang1033 \sbasedon0\snext10 nsca;}{\s11\sa20 +\f5\lang1033 \sbasedon0\snext11 bt;}{\s12\li240\sb120\sa40 \f5\lang1033 \sbasedon0\snext12 Hf;}{\s13\li240\sb120\sa40 \f5\lang1033 \sbasedon0\snext13 Hs;}{\s14\li480\sb120\sa40 \f5\lang1033 \sbasedon0\snext14 RT;}{\s15\fi-2160\li2160\sb240\sa80\tx2160 +\f5\lang1033 \sbasedon0\snext15 c;}{\s16\li2160\sa20 \f5\lang1033 \sbasedon0\snext16 ct;}{\s17\li240\sa20 \f5\lang1033 \sbasedon0\snext17 it;}{\s18\li480 \f5\fs20\lang1033 \sbasedon0\snext18 nsct;}{\s19\fi-160\li400\sb80\sa40 \f5\lang1033 +\sbasedon0\snext19 nscb;}{\s20\fi-2640\li2880\sb120\sa40\brdrb\brdrs\brdrw15 \brdrbtw\brdrs\brdrw15 \tx2880 \f5\lang1033 \sbasedon0\snext20 HC2;}{\s21\fi-2640\li2880\sb120\sa20\tx2880 \f5\lang1033 \sbasedon0\snext21 C2;}{\s22\fi-240\li2400\sa20 +\f5\fs20\lang1033 \sbasedon0\snext22 ctc;}{\s23\li2160\sb160 \f5\lang1033 \sbasedon0\snext23 crt;}{\s24\li480\sb20\sa40 \f5\lang1033 \sbasedon0\snext24 or;}{\s25\fi-259\li360\sb40\sa40\tx360 \f6\fs20\lang1033 \sbasedon0\snext25 Ln1;}{\s26\li115\sb80\sa80 +\f6\fs20\lang1033 \sbasedon0\snext0 *Intro;}{\s27\li115\sb80\sa80\keepn \b\f6\lang1033 \sbasedon252\snext26 *Title;}{\s28\fi-245\li360\sb80 \f6\fs20\lang1033 \snext28 *Jl;}{\s29\li360\sb40\sa40 \f6\fs20\lang1033 \snext0 Lp1;}{ +\s30\fi-1800\li1915\sb60\sl-240\tx1915 \f6\fs20\lang1033 \sbasedon0\snext30 Tph;}{\s31\li115\sb120\sa80 \b\f6\fs20\lang1033 \snext25 Proch;}}{\info{\title AFXPRINT}{\subject MFC Print RTF Help}{\author AFX}{\creatim\yr1992\mo10\dy7\hr12\min33}{\version1} +{\edmins46}{\nofpages1}{\nofwords65536}{\nofchars65536}{\vern16433}}\paperw12240\paperh15840\margl1800\margr1800\margt1440\margb1440\gutter0 \widowctrl\ftnbj \sectd \linex0\endnhere \pard\plain \sl240 \f3\fs20\lang1033 {\f4 \page }{\f4 #{\footnote +\pard\plain \sl240 \f3\fs20\lang1033 # HID_FILE_PRINT}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} printing and print preview}}{\f4 }{\fs16\up6 ${\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 $} File Print command} +}{\f4 }{\b\f4 Print command (File menu)}{\b\f4 +\par }{\f4 +\par }{\f4 Use this command to print a document. This command presents a }{\f4\uldb Print dialog box}{\v AFX_HIDD_PRINT}{\f4 +, where you may specify the range of pages to be printed, the number of copies, the destination printer, and other printer setup options.}{\b\f4 +\par }{\b\f4 +\par }{\b\f4 Shortcuts}{\b\f4 +\par }\pard \fi-1080\li1440\sl240\tx1440 {\f4 Toolbar:}{\f4 \tab }{\f4 \{}{\f4 bmc }{\f4 fileprnt.bmp}{\f4 \}}{\f4 +\par }{\f4 Keys:}{\f4 \tab }{\f4 CTRL+P}{\f4 +\par }\pard \sl240 {\f4 +\par }{\f4 \page }{\f4 #{\footnote \pard\plain \sl240 \f3\fs20\lang1033 # AFX_HIDD_PRINT}}{\b\f4 Print }{\b\f4 dialog box}{\b\f4 +\par }{\f4 +\par }{\f4 The following options allow you to specify how the document should be printed:}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Printer}{\b\f4 +\par }\pard \li360\sl240 {\f4 This is the active printer and printer connection. Choose the Setup option to change the printer and printer connection.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Setup}{\b\f4 +\par }\pard \li360\sl240 {\f4 Displays a }{\f4\uldb Print Setup dialog box}{\v AFX_HIDD_PRINTSETUP}{\f4 , so you can select a printer and printer connection.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Print Range}{\b\f4 +\par }\pard \li360\sl240 {\f4 Specify the pages you want to print:}{\f4 +\par }\trowd \trgaph108\trleft402 \cellx1647\cellx8622\pard \sl240\intbl {\b\f4 All}{\b\f4 \cell }{\f4 Prints the entire document.}{\f4 \cell }\pard \intbl {\f4 \row }\trowd \trgaph108\trleft402 \cellx1647\cellx8622\pard \sl240\intbl {\b\f4 Selection}{\b\f4 +\cell }{\f4 Prints the currently selected text.}{\f4 \cell }\pard \intbl {\f4 \row }\trowd \trgaph108\trleft402 \cellx1647\cellx8622\pard \sl240\intbl {\b\f4 Pages}{\b\f4 \cell }{\f4 Prints the range of pages you specify in }{\f4 the From and To boxes.}{ +\f4 \cell }\pard \intbl {\f4 \row }\pard \sb60\sl240 {\b\f4 Copies}{\b\f4 +\par }\pard \li360\sl240 {\f4 Specify the number of copies you want to print for the above page range.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Collate Copies}{\b\f4 +\par }\pard \li360\sl240 {\f4 Prints copies in page number order, instead of separated multiple copies of each page.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Print Quality}{\b\f4 +\par }\pard \li360\sl240 {\f4 Select the quality of the printing. Generally, lower quality printing takes less time to produce.}{\f4 +\par }\pard \sl240 {\f4 \page }{\f4 #{\footnote \pard\plain \sl240 \f3\fs20\lang1033 # AFX_HIDD_PRINTDLG}}{\b\f4 P}{\b\f4 rint Progress Dialog}{\b\f4 +\par }{\f4 +\par }{\f4 The Printing dialog box is shown during the time that <> is sending output to the printer. The page number indicates the progre}{\f4 ss of the printing.}{\f4 +\par }{\f4 +\par }{\f4 To abort printing, choose Cancel.}{\f4 +\par }{\f4 +\par }{\f4 \page }{\f4 #{\footnote \pard\plain \sl240 \f3\fs20\lang1033 # HID_FILE_PRINT_PREVIEW}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} printing and print preview}}{\f4 }{\fs16\up6 ${\footnote \pard\plain \s245 +\f3\fs20\lang1033 {\fs16\up6 $} File Print Preview command}}{\f4 }{\b\f4 Print Preview command (File menu)}{\f4 +\par }{\f4 +\par }{\f4 Use this command to display the active document as it would appear when printed. When you choose this command, the main window will be replaced with a print preview window in which one or two pages will be displayed in their printed format. The }{ +\f4\uldb print preview toolbar}{\v\f4 AFX_HIDW_PREVIEW_BAR}{\f4 offers you options to view either one or two pages at a time; move back and forth through the document; zoom in and out of pages; and initiate a print job.}{\f4 +\par }{\f4 \page }{\f4 #{\footnote \pard\plain \sl240 \f3\fs20\lang1033 # AFX_HIDW_PREVIEW_BAR}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} printing and print preview}}{\f4 }{\fs16\up6 ${\footnote \pard\plain \s245 +\f3\fs20\lang1033 {\fs16\up6 $} print preview toolbar}}{\f4 }{\b\f4 Print Preview toolbar}{\b\f4 +\par }{\f4 +\par }{\f4 The print preview toolbar offers you the following options:}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Print}{\b\f4 +\par }\pard \li360\sl240 {\f4 Bring up the print dialog box, to start a print job.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Next Page}{\b\f4 +\par }\pard \li360\sl240 {\f4 Preview the next printed page.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Prev Page}{\b\f4 +\par }\pard \li360\sl240 {\f4 Preview the previous printed page.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 One Page / Two Page}{\b\f4 +\par }\pard \li360\sl240 {\f4 Preview one or two printed pages at a time.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Zoom In}{\b\f4 +\par }\pard \li360\sl240 {\f4 Take a closer look at the printed page.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Zoom Out}{\b\f4 +\par }\pard \li360\sl240 {\f4 Take a larger look at the printed page.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Close}{\b\f4 +\par }\pard \li360\sl240 {\f4 Return from print preview to the editing window.}{\f4 +\par }\pard \sl240 {\f4 +\par }{\f4 \page }{\f4 #{\footnote \pard\plain \sl240 \f3\fs20\lang1033 # HID_FILE_PRINT_SETUP}}{\fs16\up6 K{\footnote \pard\plain \s245 \f3\fs20\lang1033 {\fs16\up6 K} printing and print preview}}{\f4 }{\fs16\up6 ${\footnote \pard\plain \s245 +\f3\fs20\lang1033 {\fs16\up6 $} File Print Setup command}}{\f4 }{\b\f4 Print Setup command (File menu)}{\b\f4 +\par }{\f4 +\par }{\f4 Use this command to select a printer and a printer connection. This command presents a }{\f4\uldb Print Setup dialog box}{\v AFX_HIDD_PRINTSETUP}{\f4 , where you specify the printer and its connection.}{\b\f4 +\par }{\f4 \page }{\f4 #{\footnote \pard\plain \sl240 \f3\fs20\lang1033 # AFX_HIDD_PRINTSETUP}}{\b\f4 Print}{\b\f4 Setup dialog box}{\b\f4 +\par }{\f4 +\par }{\f4 The following options allow you to select the destination printer and its connection.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Printer}{\b\f4 +\par }\pard \li360\sl240 {\f4 +Select the printer you want to use. Choose the Default Printer; or choose the Specific Printer option and select one of the current installed printers shown in the box. You install printers and configure ports using the Windows Control Panel. }{\f4 + +\par }\pard \sb60\sl240 {\b\f4 Orientation}{\b\f4 +\par }\pard \li360\sl240 {\f4 Choose Portrait or Landscape.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Paper Size}{\b\f4 +\par }\pard \li360\sl240 {\f4 Select the size of paper that the document is to be printed on.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Paper Source}{\b\f4 +\par }\pard \li360\sl240 {\f4 Some printers offer multiple trays for different paper sources. Specify the tray here.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Options}{\b\f4 +\par }\pard \li360\sl240 {\f4 Displays a dialog box where you can make additional choices about printing, specific to the type of printer you have selected.}{\f4 +\par }\pard \sb60\sl240 {\b\f4 Network...}{\b\f4 +\par }\pard \li360\sl240 {\f4 Choose this button to connect to a network location, assigning it a new drive letter.}{\f4 +\par }\pard \sl240 {\f4 \page }{\f4 #{\footnote \pard\plain \sl240 \f3\fs20\lang1033 # HID_FILE_PAGE_SETUP}}{\f4 ${\footnote \pard\plain \sl240 \f3\fs20\lang1033 $ File Page Setup Command}}{\b\f4 Page Setup command (File menu)}{\b\f4 +\par }{\b\f4 +\par }{\f4 << Write application-specific help here. >>}{\f4 +\par }\pard +\par } \ No newline at end of file diff --git a/src/fred2/hlp/appexit.bmp b/src/fred2/hlp/appexit.bmp new file mode 100644 index 0000000000000000000000000000000000000000..21eee3c6b0d54e53a4b178fe361a15e57738f50b GIT binary patch literal 2262 zcmbtVJx;?g7&VB2&Ef_K77!yd5&}jT*%1RrV9v+^Sh})u0>sioAU5t0g%A-+W#0E= zJ5FPFz^z@|FYkSSj(c|XzHPYOV?01VK|e>|Fk2W)tuh8*Q&sYV5mWTa%;$3ej=#n^ znxW%ETeL2=`k#0|E6Tfd`pb_pfALzzyF2GT3Yag;&ym{Eud!-O1pZfqp-OYXHqkebs>Szpk@<&OmdN!$qLm%i-d{G8Z3C zGkMa#}{cTj_2eFU-cYX%YaS9-|R-5W(dT>K2ey|o(^!`77!!WKTi>7Lje?<-A%;D zbmA}HwEk>5JqV=d^N%@-k~DsU{QFzO%n0WL?CVR#OL1JP(AWI)eUajN{`r0*yCXic zwZ2Ro;hGHgpXSKvP+#d%-wD3b9sZyEbjbJg93D8$;QfvE13kkJ^pGDaYv4=jXp7H( dQWaWA>KI4JQ409#+FmeJHZ~!~+Fz2+Mln;WFbm#&SZtS{s8uh%6hQv+X$&FmehK2eC z1z1q6jY>-@dhZN83+~}auDFWxJF>lEGk1~l5}qW?4G_;@q#4Y9?#XxKtvCixWuV3I*=7(972XxKZ6GxgXkDff4pDhW4?L6X+q)r vJkCFxXBeU}Yq%`OrVA!7zc=`MA1$uwS^2!FUNg|LYjFQBJ->N-|Bm1f#UyYh literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/curhelp.bmp b/src/fred2/hlp/curhelp.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a21ba12a1554f288afc563219713327b226a9e72 GIT binary patch literal 502 zcmZuuF%H5o40I)iY^;bE@D3EIEc^yhMh}tjv+^V+%2v)!gVTs>?dE(Jw{g$c_dtd_ z@(FQ791sKTkcoKJjcgJ_M1i?Hkg-HC<(wfbcGI%x3V};Y%3BmCa)EkZlVh)Z?)XP4 zCe#0MGK=dS=OFPSvg#}y55@k|2|PaBkFN?Hm(=uwY#VBSqGy!b=>0+(B}M15<+{7| bi@=Q+`gu?R3io-=IuD&;h0We|{RQ*|m)*7u literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/editcopy.bmp b/src/fred2/hlp/editcopy.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5e1e65ec6f6d3d052f7bf47e15ced59f3dae3903 GIT binary patch literal 502 zcma)(F%H5o3`JcD3AU_=6L1d1&;|7>Omxjgj+G;Mh*-_Ho5qP)_^qA1{Iqf1+v78l z>4khnTo7l(Lp<_{XgHKx!b>>ih+qjW1KqpL1gKDl=7x~1PvLojNkez3{Qw}a$dZ_kcg zcjOUqL7WjC^~gm0@f|{9nAl)0A7m^MOerN8*S^;EVGwv{wz9SXUC!_`N=XxUqwQ{N^d{d`c;R>Ob6!3#MaRI2S4C{w$o~Vo9Alt Nuo+hQ?3-^_KyQxgzrg?i literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/editpast.bmp b/src/fred2/hlp/editpast.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0774f8f31fa0f71eca681f5b79708eaa241dc627 GIT binary patch literal 502 zcmZ{gJr2S!424}4DM$=m5hq~m))k=)EGSp0REe(M$Z^v%%L_-^7BF=tU`_)vrU<5GSzv_tQV23b;E|H>ib7V0F_>93 z3U`WgVqbLs&lb8ApNSl{Qf{ZoS?iR|F#P(i_9wrtm;Ml}FW-lc?M}+@p;6iNIEU$# u+Yu)~s{K294AsZ~K8d#Kd(R;M!RZEaKaQ3a?_6bwMY6x6VbE1V8@R>>HQk%DQcQURSf*>Ri{aIzGibr*k* z=T}1s7t}p+huk6?TBH6$Ai|&!@&^@DWT3w9(U3GqL}y5ZE-7CCYXbxYsxiL8g4;@c z;ymF8j~qJ<9{X4EtWkI;pCl-pi(u5pPH67;?8MCd(G3oQ$?w@36-@ptC|vipt*QIN t>p0B)o?FHr^8M1`ASgVazE%HoBX+vJ@`Kp=pl|y)E#Gr>t6iPZ{R>c)#X|r9 literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/filenew.bmp b/src/fred2/hlp/filenew.bmp new file mode 100644 index 0000000000000000000000000000000000000000..ac4c76cc38024e1cd37bb3ab0445534842f0462b GIT binary patch literal 566 zcmZ?rHDh7`gEAng0mKSGECa-h3@ktrA824;03wEl1_%WtflMH7U^sB#08rvT7%}|+ z4`Kl^5Q2n2e3HQjpb1dG57NwD4hIDwK0lmW4&iseKs&K~u<;xnaP?q&2A2PU0oDEw kA39Ln_kjb64{|@y({T4g0|3eg1rh{+!vj4$VUonr0OROM+yDRo literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/fileopen.bmp b/src/fred2/hlp/fileopen.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a1ada60c7446e2b9a62661b59dbe91df9342653b GIT binary patch literal 566 zcma)&F%AMD5JhLzL=p-vpuMGui3ix;yI8sHbk42dNZW6Le?VABOxC~5z=O{$nelPo zQPKWW7 zQ`uFVGE33>Pfpd1Nb%DRx~}Z@x?$?MowK3%ozI5Cc1dqGg`J0*`gVaDb{;Qim`Nb2 e_mxDrVNbgitxlg2q(eFUZLA6J5{UX(;tzx(+l3|0@l;MiH)BUo}$SdVdPe gV8>pdpZ6PD|9-uN(vKa^=JWWSXBB<-nGYF}FOD+A+5i9m literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/filesave.bmp b/src/fred2/hlp/filesave.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3e604b3b056df46f715bb5be58d5f9992ca05d28 GIT binary patch literal 502 zcmbVII}U>|6f-JP*;qSp0LDt(fStQ^D(l)@BzP9pN=<=w#W zWF5(YypauBRzPBI;#7no4UsUha!CrDa}2zF@LpFWhgzz48WFvE*EQpzy3cYAdP{hH z)8V=<VBrsX6t$303W@C Bwjuxk literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/fred.hm b/src/fred2/hlp/fred.hm new file mode 100644 index 0000000..105a62f --- /dev/null +++ b/src/fred2/hlp/fred.hm @@ -0,0 +1,351 @@ +// MAKEHELP.BAT generated Help Map file. Used by FRED.HPJ. + +// Commands (ID_* and IDM_*) +HID_MY_STATUS_BAR 0x10065 +HID_SHOW_FIGHTERS 0x1042F +HID_SHOW_CAPITALSHIPS 0x10430 +HID_SHOW_PLANETS 0x10431 +HID_SHOW_MISCOBJECTS 0x10432 +HID_SHOW_WAYPOINTS 0x10433 +HID_SHOW_GRID 0x10434 +HID_SHOW_ELEVATIONS 0x10435 +HID_BOOLEAN 0x104AE +HID_NUMBERS 0x104CD +HID_SHIPS 0x104CE +HID_WINGS 0x104CF +HID_OK 0x10550 +HID_CLOSE 0x10551 +HID_LOAD 0x10552 +HID_CPGN_CLOSE 0x10559 +HID_UPDATE 0x105B4 +HID_CANCEL 0x105D9 +HID_FILE_MISSIONNOTES 0x18003 +HID_DUPLICATE 0x18006 +HID_VIEW_ROTATE 0x18007 +HID_SELECT 0x18008 +HID_ROTATE 0x18009 +HID_VIEW_ZOOM 0x1800A +HID_DELETE 0x1800B +HID_MOVE 0x1800C +HID_VIEW_PAN 0x1800D +HID_VIEW_FIGHTERS 0x18010 +HID_VIEW_CAPITALSHIPS 0x18011 +HID_VIEW_PLANETS 0x18012 +HID_VIEW_MISCOBJECTS 0x18013 +HID_FILE_PREFERENCES 0x18014 +HID_EDIT_DELETE 0x18015 +HID_EDIT_HOLD 0x18016 +HID_EDIT_FETCH 0x18017 +HID_CONFIRM_DELETING 0x18017 +HID_EDITORS_OBJECTS 0x18018 +HID_EDITORS_AI_CLASSES 0x18019 +HID_EDITORS_ART 0x1801A +HID_EDITORS_MESSAGE 0x1801B +HID_EDITORS_MUSIC 0x1801C +HID_EDITORS_SHIP_CLASSES 0x1801E +HID_EDITORS_SHIPS 0x1801F +HID_EDITORS_GOALS 0x18020 +HID_EDITORS_WAYPOINTS 0x18021 +HID_EDITORS_SOUND 0x18022 +HID_EDITORS_TERRAIN 0x18023 +HID_EDIT_DUPLICATE 0x18024 +HID_VIEW_ELEVATIONS 0x18026 +HID_VIEW_WAYPOINTS 0x18027 +HID_VIEW_GRID 0x18028 +HID_MIKE_GRIDCONTROL 0x1802B +HID_PROPERTIES_ONE 0x1802C +HID_PROPERTIES_TWO 0x1802D +HID_PROPERTIES_THREE 0x1802E +HID_PROPERTIES_FOUR 0x1802F +HID_PROPERTIES_SUBMENU_SUB1 0x18030 +HID_PROPERTIES_SUBMENU_SUB2 0x18031 +HID_SHIP_TYPE_2 0x18031 +HID_MISCSTUFF_SHOWSHIPSASICONS 0x18032 +HID_EDIT_POPUP_SHOW_SHIP_MODELS 0x18034 +HID_EDIT_POPUP_SHOW_SHIP_ICONS 0x18037 +HID_SHIP_TYPE_3 0x18038 +HID_SHIP_TYPE_4 0x18039 +HID_SHIP_TYPE_5 0x1803A +HID_SHIP_TYPE_1 0x1803C +HID_SHIP_TYPE_0 0x1803D +HID_EDIT_SHIP_TYPE_6 0x1803E +HID_MISC_STATISTICS 0x1803F +HID_EDIT_POPUP_SHOW_COMPASS 0x18040 +HID_CHANGE_VIEWPOINT_EXTERNAL 0x18043 +HID_CHANGE_VIEWPOINT_START 0x18044 +HID_CHANGE_VIEWPOINT_FOLLOW 0x18045 +HID_ADD 0x18047 +HID_ADD_PLUS 0x18048 +HID_ADD_MINUS 0x18049 +HID_ADD_DESTROY 0x1804E +HID_ADD_DESTROY_WING 0x1804F +HID_ADD_DISABLE 0x18050 +HID_ADD_MISSION_TIME 0x18051 +HID_ADD_EQUALS 0x18052 +HID_ADD_GREATER_THAN 0x18053 +HID_ADD_AND 0x18054 +HID_ADD_OR 0x18055 +HID_ADD_MULTIPLY 0x18056 +HID_ADD_DIVIDE 0x18057 +HID_ADD_MOD 0x18058 +HID_EDIT_TEXT 0x18059 +HID_ADD_TRUE 0x1805A +HID_ADD_FALSE 0x1805B +HID_HELP_INPUT_INTERFACE 0x1805C +HID_ADD_LESS_THAN 0x1805D +HID_ADD_ELAPSED_TIME 0x1805E +HID_ADD_TIME_SHIP_DESTROYED 0x1805F +HID_ADD_TIME_SHIP_ARRIVED 0x18060 +HID_ADD_TIME_SHIP_DEPARTED 0x18061 +HID_INFO 0x18062 +HID_REPLACE_DESTROY 0x18066 +HID_REPLACE_DESTROY_WING 0x18067 +HID_REPLACE_DISABLE 0x18068 +HID_REPLACE_ELAPSED_TIME 0x18069 +HID_REPLACE_TIME_SHIP_DESTROYED 0x1806A +HID_REPLACE_TIME_SHIP_ARRIVED 0x1806B +HID_REPLACE_TIME_SHIP_DEPARTED 0x1806C +HID_REPLACE_TRUE 0x1806D +HID_REPLACE_FALSE 0x1806E +HID_REPLACE_AND 0x1806F +HID_REPLACE_OR 0x18070 +HID_REPLACE_EQUALS 0x18071 +HID_REPLACE_GREATER_THAN 0x18072 +HID_REPLACE_LESS_THAN 0x18073 +HID_REPLACE_PLUS 0x18074 +HID_REPLACE_MINUS 0x18075 +HID_REPLACE_MULTIPLY 0x18076 +HID_REPLACE_DIVIDE 0x18077 +HID_REPLACE_MOD 0x18078 +HID_ADD_NUMBER 0x18079 +HID_ADD_STRING 0x1807A +HID_REPLACE_NUMBER 0x1807B +HID_REPLACE_STRING 0x1807C +HID_PLACEHOLDER 0x1807E +HID_SPEED1 0x18082 +HID_SPEED2 0x18083 +HID_SPEED5 0x18084 +HID_SPEED10 0x18085 +HID_SPEED3 0x18086 +HID_ROT1 0x18087 +HID_ROT2 0x18088 +HID_ROT3 0x18089 +HID_ROT4 0x1808A +HID_ROT5 0x1808B +HID_SPEED8 0x1808C +HID_EDIT_MODE_SHIPS 0x1808D +HID_EDIT_MODE_WAYPOINTS 0x1808E +HID_CONTROL_MODE_CAMERA 0x1808F +HID_CONTROL_MODE_START 0x18090 +HID_CONTROL_MODE_SHIP 0x18091 +HID_SHOW_GRID_POSITIONS 0x18092 +HID_SHOW_COORDINATES 0x18093 +HID_SPEED50 0x18095 +HID_SPEED100 0x18096 +HID_SELECT_LIST 0x18097 +HID_CONTRAIN_X 0x18098 +HID_CONSTRAIN_Y 0x18099 +HID_BUTTON32922 0x1809A +HID_BUTTON32923 0x1809B +HID_ZOOM 0x1809C +HID_SELECTION_LOCK 0x1809D +HID_BUTTON32926 0x1809E +HID_DISSOLVE_WING 0x1809F +HID_SELECT_AND_MOVE 0x180A4 +HID_SELECT_AND_ROTATE 0x180A5 +HID_CONSTRAIN_X 0x180A6 +HID_CONSTRAIN_Z 0x180A7 +HID_CONSTRAIN_XZ 0x180A8 +HID_FORM_WING 0x180A9 +HID_REMOVE_WING 0x180AA +HID_DISBAND_WING 0x180AA +HID_DOUBLE_FINE_GRIDLINES 0x180AB +HID_SHOW_DISTANCED 0x180AC +HID_SHOW_DISTANCES 0x180AD +HID_UNIVERSAL_HEADING 0x180AE +HID_FIND_DISTANCE 0x180AF +HID_BUTTON32944 0x180B0 +HID_BUTTON32945 0x180B1 +HID_CONSTRAIN_YZ 0x180B2 +HID_CONSTRAIN_XY 0x180B3 +HID_FLYING_CONTROLS 0x180B4 +HID_SELECT_AND_ROTATE_LOCALLY 0x180B5 +HID_ROTATE_LOCALLY 0x180B6 +HID_ZOOM_TO_SELECTED 0x180B7 +HID_ZOOM_SELECTED 0x180B8 +HID_ZOOM_EXTENTS 0x180B9 +HID_SHOW_HORIZON 0x180BA +HID_EDITORS_WING 0x180BB +HID_SELECTSHIP_PLACEHOLDER 0x180BF +HID_ADD_ESCORT 0x180C1 +HID_ADD_DOCK 0x180C2 +HID_ADD_TIME_WING_DESTROYED 0x180C3 +HID_ADD_TIME_WING_ARRIVED 0x180C4 +HID_ADD_TIME_WING_DEPARTED 0x180C5 +HID_REPLACE_ESCORT 0x180C6 +HID_REPLACE_DOCK 0x180C7 +HID_REPLACE_TIME_WING_DESTROYED 0x180C8 +HID_REPLACE_TIME_WING_ARRIVED 0x180C9 +HID_REPLACE_TIME_WING_DEPARTED 0x180CA +HID_SPLIT_LINE 0x180CB +HID_EDITORS_PLAYER 0x180CC +HID_EDITORS_ORIENT 0x180CD +HID_EDITORS_EVENTS 0x180CE +HID_EDITORS_STARFIELD 0x180CF +HID_EDITORS_BG_BITMAPS 0x180D0 +HID_EDITORS_REINFORCEMENT 0x180D1 +HID_ERROR_CHECKER 0x180D2 +HID_EDITORS_WAYPOINT 0x180D3 +HID_VIEW_OUTLINES 0x180D4 +HID_NEW_SHIP_TYPE 0x180D5 +HID_SHOW_STARFIELD 0x180D7 +HID_ASTEROID_EDITOR 0x180D8 +HID_RUN_FREESPACE 0x180D9 +HID_EDITOR_CAMPAIGN 0x180DA +HID_SHOW_FRIENDLY 0x180DC +HID_SHOW_HOSTILE 0x180DD +HID_SHOW_SHIPS 0x180DE +HID_SHOW_STARTS 0x180DF +HID_TOGGLE_VIEWPOINT 0x180E0 +HID_CPGN_FILE_NEW 0x180E3 +HID_CPGN_FILE_OPEN 0x180E4 +HID_CPGN_FILE_SAVE 0x180E5 +HID_CPGN_FILE_SAVE_AS 0x180E6 +HID_SHOW_STARFRIELD 0x180E7 +HID_REVERT 0x180E8 +HID_HIDE_OBJECTS 0x180EA +HID_SHOW_HIDDEN_OBJECTS 0x180EB +HID_GROUP_SET 0x180EC +HID_EXPAND_ALL 0x180ED +HID_EDITORS_BRIEFING 0x180EE +HID_EDITORS_DEBRIEFING 0x180EF +HID_SAVE_CAMERA 0x180F0 +HID_RESTORE_CAMERA 0x180F1 +HID_SHOW_SEXP_HELP 0x180F2 +HID_LOOKAT_OBJ 0x180F3 +HID_GROUP1 0x180F4 +HID_GROUP2 0x180F5 +HID_GROUP3 0x180F6 +HID_GROUP4 0x180F7 +HID_GROUP5 0x180F8 +HID_GROUP6 0x180F9 +HID_GROUP7 0x180FA +HID_GROUP8 0x180FB +HID_GROUP9 0x180FC +HID_SET_GROUP1 0x180FD +HID_SET_GROUP2 0x180FE +HID_SET_GROUP3 0x180FF +HID_SET_GROUP4 0x18100 +HID_SET_GROUP5 0x18101 +HID_SET_GROUP6 0x18102 +HID_SET_GROUP7 0x18103 +HID_SET_GROUP8 0x18104 +HID_SET_GROUP9 0x18105 +HID_END_EDIT 0x18107 +HID_EDITORS_ADJUST_GRID 0x18108 +HID_EDITORS_SHIELD_SYS 0x18109 +HID_LEVEL_CAMERA 0x1810A +HID_EDIT_DELETE_WING 0x1810B +HID_ALIGN_OBJ 0x1810C +HID_LEVEL_OBJ 0x1810D +HID_MARK_WING 0x1810E +HID_NEXT_OBJ 0x1810F +HID_PREV_OBJ 0x18110 +HID_CONTROL_OBJ 0x18111 +HID_DELETE_ROW 0x18112 +HID_REMOVE_MISSION 0x18113 +HID_INSERT_ROW 0x18114 +HID_CHECKOUT 0x18115 +HID_CHECKIN 0x18116 +HID_ADD_REPEAT 0x18117 +HID_AA_GRIDLINES 0x18118 +HID_INITIAL_SHIPS 0x18119 +HID_INITIAL_WEAPONS 0x1811A +HID_TEAM_1 0x1811B +HID_TEAM_2 0x1811C +HID_TEAM_3 0x1811D +HID_CMD_BRIEF 0x1811E +HID_DISABLE_UNDO 0x1811F +HID_END_OF_CAMPAIGN 0x18120 +HID_INDICATOR_MODE 0x1E706 +HID_INDICATOR_LEFT 0x1E707 +HID_INDICATOR_RIGHT 0x1E708 +HID_INDICATOR_MODIFIED 0x1E709 + +// Prompts (IDP_*) + +// Resources (IDR_*) +HIDR_MAINFRAME 0x20080 +HIDR_MAINMENU 0x20080 +HIDR_FREDTYPE 0x20081 +HIDR_CAMPAIGN_VIEW 0x20082 +HIDR_CAMPAIGN_DLG 0x20083 +HIDR_TOOLBAR1 0x20096 +HIDR_MENU_SHIP_POPUP 0x200B7 +HIDR_MENU_EDIT_POPUP 0x200B8 +HIDR_MENU1 0x200B9 +HIDR_MENU_MISSION_GOALS_TREE 0x200BC +HIDR_MENU_EDIT_SEXP_TREE 0x200BC +HIDR_SHIP_EDIT_MENU 0x200BF +HIDR_WING_EDIT_MENU 0x200C4 +HIDR_PLAYER_EDIT_MENU 0x200CA +HIDR_WAYPOINT_PATH_EDIT_MENU 0x200D3 +HIDR_ASTEROID_FIELD_MENU 0x200E8 +HIDR_CPGN_VIEW_OFF 0x200E9 +HIDR_CPGN_VIEW_ON 0x200EA +HIDR_MENU_POPUP_TOGGLE1 0x28033 +HIDR_SHIP_POPUP_CHECK1 0x28036 + +// Dialogs (IDD_*) +HIDD_ABOUTBOX 0x20064 +HIDD_SHIP_EDITBAR 0x2009A +HIDD_SHIP_CLASS_EDITOR 0x200A1 +HIDD_AI_CLASS_EDITOR 0x200A2 +HIDD_MESSAGE_EDITOR 0x200A3 +HIDD_SHIP_GOALSS 0x200A5 +HIDD_SHIP_GOALS2 0x200A6 +HIDD_SHIP_GOALS 0x200A6 +HIDD_SHIP_GOALS_EDITOR 0x200A6 +HIDD_MODEL_EDITOR 0x200A8 +HIDD_ART_EDITOR 0x200AA +HIDD_SOUND_EDITOR 0x200AB +HIDD_MISSION_NOTES 0x200AC +HIDD_MUSIC_EDITOR 0x200AD +HIDD_TERRAIN_EDITOR 0x200AE +HIDD_PREFERENCES 0x200AF +HIDD_SHIP_EDITOR 0x200B0 +HIDD_WEAPON_EDITOR 0x200B1 +HIDD_SHIP_MARKINGS 0x200B2 +HIDD_DIALOG1 0x200B3 +HIDD_MISSION_GOALS 0x200B3 +HIDD_DIALOG2 0x200B4 +HIDD_HELP_INPUT_INTERFACE 0x200B4 +HIDD_DIALOG3 0x200B5 +HIDD_SHIP_SELECT 0x200B5 +HIDD_GRID 0x200B6 +HIDD_WING_EDITOR 0x200C0 +HIDD_OPERATOR_ARGUMENT_TYPES 0x200C6 +HIDD_PLAYER_EDITOR 0x200C7 +HIDD_ORIENT_EDITOR 0x200C8 +HIDD_EVENT_EDITOR 0x200CB +HIDD_EDITORS_MESSAGES 0x200CD +HIDD_STARFIELD 0x200CE +HIDD_BG_BITMAP 0x200CF +HIDD_REINFORCEMENT_EDITOR 0x200D0 +HIDD_REINFORCEMENT_SELECT 0x200D1 +HIDD_WAYPOINT_PATH_EDITOR 0x200D2 +HIDD_WING_CREATE 0x200D4 +HIDD_INITIAL_STATUS 0x200D6 +HIDD_ASTEROID_EDITOR 0x200D7 +HIDD_CAMPAIGN 0x200D8 +HIDD_TEXT_VIEW 0x200DA +HIDD_BRIEFING_EDITOR 0x200DD +HIDD_IGNORE_ORDERS 0x200DE +HIDD_DEBRIEFING_EDITOR 0x200DF +HIDD_ADJUST_GRID 0x200E6 +HIDD_SHIELD_SYS 0x200E7 +HIDD_INITIAL_SHIPS 0x200EE +HIDD_CMD_BRIEF 0x200F2 +HIDD_SHIP_FLAGS 0x200F3 + +// Frame Controls (IDW_*) diff --git a/src/fred2/hlp/fred.hpj b/src/fred2/hlp/fred.hpj new file mode 100644 index 0000000..5091990 --- /dev/null +++ b/src/fred2/hlp/fred.hpj @@ -0,0 +1,65 @@ +; This file is maintained by HCW. Do not modify this file directly. + +[OPTIONS] +HCW=0 +COMPRESS=12 Hall Zeck +ERRORLOG=help.log +LCID=0x409 0x0 0x0 ;English (United States) +REPORT=Yes +FTS=1 +CONTENTS=main_index +TITLE=Fred Help +BMROOT=.. +BMROOT=. +ROOT=.. +ROOT=. +HLP=..\..\Fred.hlp + +[FILES] +.\AFXCore.rtf + +[ALIAS] +HIDR_MAINFRAME = main_index +HIDD_ABOUTBOX = HID_APP_ABOUT +HID_HT_SIZE = HID_SC_SIZE +HID_HT_HSCROLL = scrollbars +HID_HT_VSCROLL = scrollbars +HID_HT_MINBUTTON = HID_SC_MINIMIZE +HID_HT_MAXBUTTON = HID_SC_MAXIMIZE +AFX_HIDP_INVALID_FILENAME = AFX_HIDP_default +AFX_HIDP_FAILED_TO_OPEN_DOC = AFX_HIDP_default +AFX_HIDP_FAILED_TO_SAVE_DOC = AFX_HIDP_default +AFX_HIDP_ASK_TO_SAVE = AFX_HIDP_default +AFX_HIDP_FAILED_TO_CREATE_DOC = AFX_HIDP_default +AFX_HIDP_FILE_TOO_LARGE = AFX_HIDP_default +AFX_HIDP_FAILED_TO_START_PRINT = AFX_HIDP_default +AFX_HIDP_FAILED_TO_LAUNCH_HELP = AFX_HIDP_default +AFX_HIDP_INTERNAL_FAILURE = AFX_HIDP_default +AFX_HIDP_COMMAND_FAILURE = AFX_HIDP_default +AFX_HIDP_PARSE_INT = AFX_HIDP_default +AFX_HIDP_PARSE_REAL = AFX_HIDP_default +AFX_HIDP_PARSE_INT_RANGE = AFX_HIDP_default +AFX_HIDP_PARSE_REAL_RANGE = AFX_HIDP_default +AFX_HIDP_PARSE_STRING_SIZE = AFX_HIDP_default +AFX_HIDP_FAILED_INVALID_FORMAT = AFX_HIDP_default +AFX_HIDP_FAILED_INVALID_PATH = AFX_HIDP_default +AFX_HIDP_FAILED_DISK_FULL = AFX_HIDP_default +AFX_HIDP_FAILED_ACCESS_READ = AFX_HIDP_default +AFX_HIDP_FAILED_ACCESS_WRITE = AFX_HIDP_default +AFX_HIDP_FAILED_IO_ERROR_READ = AFX_HIDP_default +AFX_HIDP_FAILED_IO_ERROR_WRITE = AFX_HIDP_default +AFX_HIDP_STATIC_OBJECT = AFX_HIDP_default +AFX_HIDP_FAILED_TO_CONNECT = AFX_HIDP_default +AFX_HIDP_SERVER_BUSY = AFX_HIDP_default +AFX_HIDP_BAD_VERB = AFX_HIDP_default +AFX_HIDP_FAILED_MEMORY_ALLOC = AFX_HIDP_default +AFX_HIDP_FAILED_TO_NOTIFY = AFX_HIDP_default +AFX_HIDP_FAILED_TO_LAUNCH = AFX_HIDP_default +AFX_HIDP_ASK_TO_UPDATE = AFX_HIDP_default +AFX_HIDP_FAILED_TO_UPDATE = AFX_HIDP_default +AFX_HIDP_FAILED_TO_REGISTER = AFX_HIDP_default +AFX_HIDP_FAILED_TO_AUTO_REGISTER = AFX_HIDP_default + +[MAP] +#include afxhelp.hm +#include diff --git a/src/fred2/hlp/help.log b/src/fred2/hlp/help.log new file mode 100644 index 0000000..e69de29 diff --git a/src/fred2/hlp/hlpsbar.bmp b/src/fred2/hlp/hlpsbar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4c06d09e3ddf72f961c63880192096e732ac73ea GIT binary patch literal 7158 zcmeI0u};G<5Qbfe!CM}H1&M_biGiJ+=im(p8{XoL2VtV!+9eNw`Ubp05Q>x_q2~DS zLJJNOQj7#wD;L|buU6&n&UbNgaoHRik!!Rks1Gw^j&Pru1GGPhGX}*t*Wb{hi|R~S zmiU3B7$WYW;$jt#Cz&iTS>O*XfB;NRvJd-NpPo!?|K-ynj7na{ZK$GG=myhx22ahX zZLS(3%L8>%1S`7Z(M_Q8Uf2dY?`3M*Z1v)7k*wd^yiJv@o*&QkvN>ZNqRDYHKHjK% z?)0+R9xIkjlB)fdYBVScc9+H4#CZ{Z)6V8`7>s0lTi!3#?n(BWcYJ{_!f%@6?UN0N zpfcR$fee&>F&#ybE0?e@IAZ$ literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/hlptbar.bmp b/src/fred2/hlp/hlptbar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4d454c392c9098f68a628bc05d59b4607008ecc1 GIT binary patch literal 2354 zcmeHHF>Zt~5Oj2r;#7PiQkp3DgY@}CUO*Mv?Xq3Ik_SYBC(&^d1vk64Sq|_k7zgU7QJ)dUWYRsIfu&;g#kSv8o8!vurTQ5hCvV- zeljKZxgq@A_~(e->OF?*k^IHZ!SlBGHO{0QGCV!1{27x=^-yJTXRtI_`30x5)xuyo znO<@Zqjyy?!jEz?oxuskc|McdfkWxcRgn-whxQ=x?h;3AX_d3vBRtPIJogPc#%YD~ zl*g%~P|D3|ZtFUI6jF|8BGJmPbLv{%@}tfMPLF^a>H*5);9EL8c%J?j=){8>@s-Kx zTX`uPbz~>bj`JXp+m>5_&Et8SaPScL77zBU9!V&0h>1+cD<_&gckqJ-897>5U=%1zP$dVZo0n* zGCkoJ)Dd++4YY$3amx*yF+wabF9RG?6jLb$LA8@=)fozhE-8OYvl-SjVyyeA>!Cb$ zoEDZ8oD48@$M^BiD6=bId{kyBA8-~Fp@S(Nk#~KG9~;e@m8c=E&6nwi=M)5 z!8vm{^!k?<>=;8R>lbghGwgi5-gSnZug?4O&3=2nz2CeKC@SyG_>0eiYCYDYnnYU9 I)0SUAZ;joz_y7O^ literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/recnext.bmp b/src/fred2/hlp/recnext.bmp new file mode 100644 index 0000000000000000000000000000000000000000..400839b86301556c6fae783093552f983a48f1b1 GIT binary patch literal 502 zcma)(y$-@K41})|Qpc`{vFgBcFtPA1Omy{72_7qt)QL6Rxj!+P>cmby-(_)bk8cBy z7weUIVV;={PON~fy2D>FifouI2P>~kLrRH4_Klo%V{)k_jv;MkS)=2b+i{PlxZ81D z__M|PFi-xZ_@}!XfrpBF8G_!+5Lt%EwO+lK9Q~3b3j;eIJr08~426wUsUs`uSTXb*QP4`rtYF@(4jDy*>?@ z-lz}w26u2n7gQo%a|2fxAr_dI0Tol2sg#1C+D*0U492BP%4cadqpWH%*6X-yP;Pe| z7nV)aADpr2{QT2%?mZ{Z`R+Ldj=6;W65lW3e98WNkpJEvd^FVhF>l=@vVI=1{sQ^} Dt@pa* literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/scmax.bmp b/src/fred2/hlp/scmax.bmp new file mode 100644 index 0000000000000000000000000000000000000000..f135732c5ad10215c8f38c3640a03e3c2ed0e02b GIT binary patch literal 502 zcmb`DF%H5o3`KuXCD=+f#Kg!s*ttt5SiO;fL*-Pg<~vR$2O%bYVka-(O(S2fuY{%> z^OvH9D3bh%)0Dm?1e!ROB|A>w=$0X?28jip zacNZa(AJnKG6Yz7rcNV*PI7RMMb0=14KkyP?dBkGfe!*#conGl9*Ii@6<=ag1j5DI z5vEw@EJK&2Nc!LYN!7aabLD!p(0gu+O;Bf7f^7 NnWkn~#h&<2mmeBMO1uC7 literal 0 HcmV?d00001 diff --git a/src/fred2/hlp/scmin.bmp b/src/fred2/hlp/scmin.bmp new file mode 100644 index 0000000000000000000000000000000000000000..8668f5ab99493018065fa5c328b996ae2024d21f GIT binary patch literal 502 zcmb`Du?_+u5JU%aiPxQU#>&#~u=iiAwB1e%KZ>ui#Tnp|T?;Gcn1#nKaM|W6d$b$cJWc3!^u+4WF9 z=ejsf$4y|orpUbC*Xup8(e1v$m;)OSn8)T&uV3)z|H^B_MnCxYQ18#0*35)Q@dq%+ B+SUL7 literal 0 HcmV?d00001 diff --git a/src/fred2/ignoreordersdlg.cpp b/src/fred2/ignoreordersdlg.cpp new file mode 100644 index 0000000..ede4682 --- /dev/null +++ b/src/fred2/ignoreordersdlg.cpp @@ -0,0 +1,358 @@ +/* + * $Logfile: /Freespace2/code/FRED2/IgnoreOrdersDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C code for dialog to set which orders from the player that a particular ship should ignore + * + */ + +#include "stdafx.h" +#include "fred.h" +#include "ignoreordersdlg.h" +#include "hudsquadmsg.h" // need this for the menu stuff +#include "linklist.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +// super cool macro to make IDC_* names + +// note: If you change this table at all, keep it in sync with version in HUDsquadmsg.cpp +fred_comm_order Fred_comm_orders[] = { + ATTACK_TARGET_ITEM, "Attack my target", + DISABLE_TARGET_ITEM, "Disable my target", + DISARM_TARGET_ITEM, "Disarm my target", + DISABLE_SUBSYSTEM_ITEM, "Destroy my subsystem", + PROTECT_TARGET_ITEM, "Protect my target", + IGNORE_TARGET_ITEM, "Ignore my target", + FORMATION_ITEM, "Form on my wing", + COVER_ME_ITEM, "Cover me", + ENGAGE_ENEMY_ITEM, "Engage enemy", + CAPTURE_TARGET_ITEM, "Capture my target", + REARM_REPAIR_ME_ITEM, "Rearm/repair me", + ABORT_REARM_REPAIR_ITEM, "Abort rearm repair", + DEPART_ITEM, "Depart", +}; + +int Fred_comm_orders_max = sizeof(Fred_comm_orders)/sizeof(fred_comm_order); + +///////////////////////////////////////////////////////////////////////////// +// ignore_orders_dlg dialog + +ignore_orders_dlg::ignore_orders_dlg(CWnd* pParent /*=NULL*/) + : CDialog(ignore_orders_dlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(ignore_orders_dlg) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + +void ignore_orders_dlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(ignore_orders_dlg) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(ignore_orders_dlg, CDialog) + //{{AFX_MSG_MAP(ignore_orders_dlg) + ON_BN_CLICKED(IDC_CHECK1, OnCheck1) + ON_BN_CLICKED(IDC_CHECK2, OnCheck2) + ON_BN_CLICKED(IDC_CHECK3, OnCheck3) + ON_BN_CLICKED(IDC_CHECK4, OnCheck4) + ON_BN_CLICKED(IDC_CHECK5, OnCheck5) + ON_BN_CLICKED(IDC_CHECK6, OnCheck6) + ON_BN_CLICKED(IDC_CHECK7, OnCheck7) + ON_BN_CLICKED(IDC_CHECK8, OnCheck8) + ON_BN_CLICKED(IDC_CHECK9, OnCheck9) + ON_BN_CLICKED(IDC_CHECK10, OnCheck10) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// ignore_orders_dlg message handlers + +BOOL ignore_orders_dlg::OnInitDialog() +{ + int i, default_orders, last_bottom, orders_accepted; + RECT window_size; + char buf[128]; + object *objp; + + CDialog::OnInitDialog(); + + check_boxes[0].button = (CButton *)GetDlgItem( IDC_CHECK1 ); + check_boxes[1].button = (CButton *)GetDlgItem( IDC_CHECK2 ); + check_boxes[2].button = (CButton *)GetDlgItem( IDC_CHECK3 ); + check_boxes[3].button = (CButton *)GetDlgItem( IDC_CHECK4 ); + check_boxes[4].button = (CButton *)GetDlgItem( IDC_CHECK5 ); + check_boxes[5].button = (CButton *)GetDlgItem( IDC_CHECK6 ); + check_boxes[6].button = (CButton *)GetDlgItem( IDC_CHECK7 ); + check_boxes[7].button = (CButton *)GetDlgItem( IDC_CHECK8 ); + check_boxes[8].button = (CButton *)GetDlgItem( IDC_CHECK9 ); + check_boxes[9].button = (CButton *)GetDlgItem( IDC_CHECK10 ); + + // change the labels on the check boxes to reflect the set of default + // orders for this ship + if ( m_ship >= 0 ) { + default_orders = ship_get_default_orders_accepted( &Ship_info[Ships[m_ship].ship_info_index] ); + } else { + // we are doing multiple edit on ships. We just need to get default orders for + // the first marked ship since they'd better all be the same anyway!!! + default_orders = 0; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->flags & OF_MARKED)) { + int these_orders; + + these_orders = ship_get_default_orders_accepted( &Ship_info[Ships[objp->instance].ship_info_index] ); + if ( default_orders == 0 ) + default_orders = these_orders; + else if ( default_orders != these_orders ) + Int3(); + } + } + + } + + // set the checkboxes for the orders accepted + m_num_checks_active = 0; + for (i = 0; i < MAX_SHIP_ORDERS; i++ ) { + if ( default_orders & Fred_comm_orders[i].value ) { + Assert( m_num_checks_active < MAX_CHECKBOXES ); + check_boxes[m_num_checks_active].button->SetWindowText( Fred_comm_orders[i].menu_text ); + check_boxes[m_num_checks_active].id = Fred_comm_orders[i].value; + m_num_checks_active++; + } + } + + // resize the dialog by shrinking the height by the number of checkboxes that + // we removed + GetWindowRect( &window_size ); + + // hide the rest of the dialog items + last_bottom = 0; + for ( i = MAX_CHECKBOXES - 1; i >= m_num_checks_active; i-- ) { + RECT check_size; + + // get the size of the checkbox, then hide it. + check_boxes[i].button->GetWindowRect( &check_size ); + check_boxes[i].button->ShowWindow(SW_HIDE); + + // shrink the size of the parent window by the size of the checkbox + if ( last_bottom != 0 ) + window_size.bottom -= (last_bottom - check_size.bottom ); + + last_bottom = check_size.bottom; + + } + + // call MoveWindow to resize the window + MoveWindow( &window_size, TRUE ); + + // set the check marks in the box based on orders_accepted valud in the ship structure(s) + if ( m_ship >= 0 ) { + orders_accepted = Ships[m_ship].orders_accepted; + for ( i = 0; i < m_num_checks_active; i++ ) { + if ( check_boxes[i].id & orders_accepted ) + check_boxes[i].button->SetCheck(1); + } + } else { + int first_time; + + first_time = 1; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) && (objp->flags & OF_MARKED)) { + + // get the orders for this ship. If a state is not set + orders_accepted = Ships[objp->instance].orders_accepted; + if ( first_time ) { + for ( i = 0; i < m_num_checks_active; i++ ) { + if ( check_boxes[i].id & orders_accepted ) + check_boxes[i].button->SetCheck(1); + } + first_time = 0; + } else { + for ( i = 0; i < m_num_checks_active; i++ ) { + // see if the order matches the check box order + if ( check_boxes[i].id & orders_accepted ) { + // if it matches, if it is not already set, then it is indeterminate. + if ( !check_boxes[i].button->GetCheck() ) + check_boxes[i].button->SetCheck(2); + } else { + // if the order isn't active, and already set, mark as indeterminite. + if ( check_boxes[i].button->GetCheck() ) + check_boxes[i].button->SetCheck(2); + } + } + } + } + } + } + + // finally, set the title of the window to be something else. We really aren't checking orders + // which will be ignore, but rather orders which will be accepted + sprintf(buf, "Player orders accepted" ); + SetWindowText(buf); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +// for the OnOK function, we scan through the list of checkboxes, and reset the ship's orders accepted +// variable based on the state of the checkboxes +void ignore_orders_dlg::OnOK() +{ + int orders_accepted, i; + object *objp; + + // clear out the orders, then set the bits according to which check boxes are set + if ( m_ship >= 0 ) { + orders_accepted = 0; + for ( i = 0; i < m_num_checks_active; i++ ) { + if ( check_boxes[i].button->GetCheck() ) + orders_accepted |= check_boxes[i].id; + } + Ships[m_ship].orders_accepted = orders_accepted; + } else { + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->flags & OF_MARKED)) { + Ships[objp->instance].orders_accepted = 0; + for ( i = 0; i < m_num_checks_active; i++ ) { + int box_value; + + box_value = check_boxes[i].button->GetCheck(); + // get the status of the checkbox -- if in the indeterminite state, then + // skip it + if ( box_value == 2 ) + continue; + + // if the button is set, then set the bit, otherwise, clear the bit + if ( box_value == 1 ) + Ships[objp->instance].orders_accepted |= check_boxes[i].id; + else + Ships[objp->instance].orders_accepted &= ~(check_boxes[i].id); + } + } + } + } + + CDialog::OnOK(); +} + +// stupid routines to deal with the tri-state values of the checkboxes +void ignore_orders_dlg::OnCheck1() +{ + CButton *button; + + button = (CButton *)GetDlgItem(IDC_CHECK1); + if (button->GetCheck() == 1) + button->SetCheck(0); + else + button->SetCheck(1); +} + +void ignore_orders_dlg::OnCheck2() +{ + CButton *button; + + button = (CButton *)GetDlgItem(IDC_CHECK2); + if (button->GetCheck() == 1) + button->SetCheck(0); + else + button->SetCheck(1); +} + +void ignore_orders_dlg::OnCheck3() +{ + CButton *button; + + button = (CButton *)GetDlgItem(IDC_CHECK3); + if (button->GetCheck() == 1) + button->SetCheck(0); + else + button->SetCheck(1); +} + +void ignore_orders_dlg::OnCheck4() +{ + CButton *button; + + button = (CButton *)GetDlgItem(IDC_CHECK4); + if (button->GetCheck() == 1) + button->SetCheck(0); + else + button->SetCheck(1); +} + +void ignore_orders_dlg::OnCheck5() +{ + CButton *button; + + button = (CButton *)GetDlgItem(IDC_CHECK5); + if (button->GetCheck() == 1) + button->SetCheck(0); + else + button->SetCheck(1); +} + +void ignore_orders_dlg::OnCheck6() +{ + CButton *button; + + button = (CButton *)GetDlgItem(IDC_CHECK6); + if (button->GetCheck() == 1) + button->SetCheck(0); + else + button->SetCheck(1); +} + +void ignore_orders_dlg::OnCheck7() +{ + CButton *button; + + button = (CButton *)GetDlgItem(IDC_CHECK7); + if (button->GetCheck() == 1) + button->SetCheck(0); + else + button->SetCheck(1); +} + +void ignore_orders_dlg::OnCheck8() +{ + CButton *button; + + button = (CButton *)GetDlgItem(IDC_CHECK8); + if (button->GetCheck() == 1) + button->SetCheck(0); + else + button->SetCheck(1); +} + +void ignore_orders_dlg::OnCheck9() +{ + CButton *button; + + button = (CButton *)GetDlgItem(IDC_CHECK9); + if (button->GetCheck() == 1) + button->SetCheck(0); + else + button->SetCheck(1); +} + +void ignore_orders_dlg::OnCheck10() +{ + CButton *button; + + button = (CButton *)GetDlgItem(IDC_CHECK10); + if (button->GetCheck() == 1) + button->SetCheck(0); + else + button->SetCheck(1); +} diff --git a/src/fred2/initialships.cpp b/src/fred2/initialships.cpp new file mode 100644 index 0000000..82d8e16 --- /dev/null +++ b/src/fred2/initialships.cpp @@ -0,0 +1,159 @@ +// InitialShips.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "initialships.h" +#include "campaigntreeview.h" +#include "campaigneditordlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define IDCAT(x,y) x ## y + +///////////////////////////////////////////////////////////////////////////// +// InitialShips dialog + + +InitialShips::InitialShips(CWnd* pParent /*=NULL*/) + : CDialog(InitialShips::IDD, pParent) +{ + //{{AFX_DATA_INIT(InitialShips) + //}}AFX_DATA_INIT +} + + +void InitialShips::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(InitialShips) + DDX_Control(pDX, IDC_INITIAL_LIST, m_initial_list); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(InitialShips, CDialog) + //{{AFX_MSG_MAP(InitialShips) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// InitialShips message handlers + +BOOL InitialShips::OnInitDialog() +{ + int i, j; + + CDialog::OnInitDialog(); + + m_list_count = 0; + // change the window text, get the index into the array, and check the box for either the ships + // or weapons + if ( m_initial_items == INITIAL_SHIPS ) { + for ( i = 0; i < Num_ship_types; i++ ) { + if ( Ship_info[i].flags & SIF_PLAYER_SHIP ) { + m_initial_list.AddString( Ship_info[i].name ); + if ( Campaign.ships_allowed[i] ) { + m_initial_list.SetCheck(m_list_count, 1); + } else if ( (strlen(Campaign.filename) == 0) && strstr(Ship_info[i].name, "Myrmidon") ) { + m_initial_list.SetCheck(m_list_count, 1); + } else { + m_initial_list.SetCheck(m_list_count, 0); + } + m_initial_list.SetItemData(m_list_count, i); + m_list_count++; + } + } + + // set the titale of the window + SetWindowText("Initial Ships Allowed"); + } else if ( m_initial_items == INITIAL_WEAPONS ) { + // get the list of initial weapons available by looking at all possible player ships, getting + // the weapon information for those ships, then putting those weapons onto the list + int allowed_weapons[MAX_WEAPON_TYPES]; + + memset( allowed_weapons, 0, sizeof(allowed_weapons) ); + for (i = 0; i < Num_ship_types; i++ ) { + if ( Ship_info[i].flags & SIF_PLAYER_SHIP ) { + for ( j = 0; j < MAX_WEAPON_TYPES; j++ ) { + if ( Ship_info[i].allowed_weapons[j] ) + allowed_weapons[j] = 1; + } + } + } + + // now add the weapons to the list + for (i = 0; i < MAX_WEAPON_TYPES; i++ ) { + if ( allowed_weapons[i] ) { + m_initial_list.AddString( Weapon_info[i].name ); + int add_weapon = 0; + if ( Campaign.weapons_allowed[i] ) { + add_weapon = 1; + } else if ( strlen(Campaign.filename) == 0 ) { + if ( strstr(Weapon_info[i].name, "Subach")) { + add_weapon = 1; + } else if ( strstr(Weapon_info[i].name, "Akheton")) { + add_weapon = 1; + } else if ( strstr(Weapon_info[i].name, "Rockeye")) { + add_weapon = 1; + } else if ( strstr(Weapon_info[i].name, "Tempest")) { + add_weapon = 1; + } + } + + if (add_weapon) { + m_initial_list.SetCheck( m_list_count, 1 ); + } else { + m_initial_list.SetCheck( m_list_count, 0 ); + } + + m_initial_list.SetItemData(m_list_count, i ); + m_list_count++; + } + } + SetWindowText("Initial Weapons Allowed"); + } else + Int3(); + + + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void InitialShips::OnOK() +{ + int i, index; + + // zero out whichever array we are setting + if ( m_initial_items == INITIAL_SHIPS ) { + for ( i = 0; i < MAX_SHIP_TYPES; i++ ){ + Campaign.ships_allowed[i] = 0; + } + } else if ( m_initial_items == INITIAL_WEAPONS ) { + for (i = 0; i < MAX_WEAPON_TYPES; i++ ) + Campaign.weapons_allowed[i] = 0; + } + + for ( i = 0; i < m_list_count; i++ ) { + if ( m_initial_list.GetCheck(i) ) { + // this item is checked. Get the index into either the ship info array or the weapons + // array + index = m_initial_list.GetItemData(i); + if ( m_initial_items == INITIAL_SHIPS ) { + Campaign.ships_allowed[index] = 1; + } else if ( m_initial_items == INITIAL_WEAPONS ) { + Campaign.weapons_allowed[index] = 1; + } else + Int3(); + + } + } + + CDialog::OnOK(); +} + diff --git a/src/fred2/initialstatus.cpp b/src/fred2/initialstatus.cpp new file mode 100644 index 0000000..1e3de19 --- /dev/null +++ b/src/fred2/initialstatus.cpp @@ -0,0 +1,599 @@ +// InitialStatus.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "freddoc.h" +#include "initialstatus.h" +#include "management.h" +#include "linklist.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// initial_status dialog + +initial_status::initial_status(CWnd* pParent /*=NULL*/) + : CDialog(initial_status::IDD, pParent) +{ + //{{AFX_DATA_INIT(initial_status) + m_damage = 0; + m_docked = -1; + m_shields = 0; + m_velocity = 0; + m_hull = 0; + m_dockee_point = -1; + m_docker_point = -1; + m_has_shields = FALSE; + m_locked = FALSE; + m_cargo_name = _T(""); + //}}AFX_DATA_INIT + inited = 0; + cur_subsys = LB_ERR; + m_multi_edit = 0; +} + +void initial_status::DoDataExchange(CDataExchange* pDX) +{ + CString str; + + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(initial_status) + DDX_Control(pDX, IDC_HULL_SPIN, m_hull_spin); + DDX_Control(pDX, IDC_VELOCITY_SPIN, m_velocity_spin); + DDX_Control(pDX, IDC_SHIELDS_SPIN, m_shields_spin); + DDX_Control(pDX, IDC_DAMAGE_SPIN, m_damage_spin); + DDX_Text(pDX, IDC_DAMAGE, m_damage); + DDV_MinMaxInt(pDX, m_damage, 0, 100); + DDX_CBIndex(pDX, IDC_DOCKED, m_docked); + DDX_CBIndex(pDX, IDC_DOCKEE_POINT, m_dockee_point); + DDX_CBIndex(pDX, IDC_DOCKER_POINT, m_docker_point); + DDX_Check(pDX, IDC_HAS_SHIELDS, m_has_shields); + DDX_Check(pDX, IDC_LOCKED, m_locked); + DDX_Text(pDX, IDC_CARGO_NAME, m_cargo_name); + DDV_MaxChars(pDX, m_cargo_name, 20); + //}}AFX_DATA_MAP + + if (pDX->m_bSaveAndValidate) { + GetDlgItem(IDC_VELOCITY)->GetWindowText(str); + m_velocity = atoi(str); + if (m_velocity < 0) + m_velocity = 0; + if (m_velocity > 100) + m_velocity = 100; + + GetDlgItem(IDC_SHIELDS)->GetWindowText(str); + m_shields = atoi(str); + if (m_shields < 0) + m_shields = 0; + if (m_shields > 100) + m_shields = 100; + + GetDlgItem(IDC_HULL)->GetWindowText(str); + m_hull = atoi(str); + if (m_hull < 0) + m_hull = 0; + if (m_hull > 100) + m_hull = 100; + } +} + +BEGIN_MESSAGE_MAP(initial_status, CDialog) + //{{AFX_MSG_MAP(initial_status) + ON_LBN_SELCHANGE(IDC_SUBSYS, OnSelchangeSubsys) + ON_CBN_SELCHANGE(IDC_DOCKED, OnSelchangeDocked) + ON_CBN_SELCHANGE(IDC_DOCKER_POINT, OnSelchangeDockerPoint) + ON_BN_CLICKED(IDC_HAS_SHIELDS, OnHasShields) + ON_BN_CLICKED(IDC_LOCKED, OnLocked) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// initial_status message handlers + +BOOL initial_status::OnInitDialog() +{ + int z, vflag, sflag, hflag, ship, type; + ship_subsys *ptr; + CComboBox *box; + CString str; + object *objp; + + m_ship = cur_ship; + if (m_ship == -1) { + Assert((Objects[cur_object_index].type == OBJ_SHIP) || (Objects[cur_object_index].type == OBJ_START)); + m_ship = get_ship_from_obj(cur_object_index); + Assert(m_ship >= 0); + } + + vflag = sflag = hflag = 0; + m_velocity = (int) Objects[cur_object_index].phys_info.speed; + m_shields = (int) Objects[cur_object_index].shields[0]; + m_hull = (int) Objects[cur_object_index].hull_strength; + if (Objects[cur_object_index].flags & OF_NO_SHIELDS) + m_has_shields = 0; + else + m_has_shields = 1; + + if (Ships[m_ship].flags & SF_LOCKED) + m_locked = 1; + else + m_locked = 0; + + if (m_multi_edit) { + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->flags & OF_MARKED)) { + if (objp->phys_info.speed != m_velocity) + vflag = 1; + if ((int) objp->shields[0] != m_shields) + sflag = 1; + if ((int) objp->hull_strength != m_hull) + hflag = 1; + if (objp->flags & OF_NO_SHIELDS) { + if (m_has_shields) + m_has_shields = 2; + + } else { + if (!m_has_shields) + m_has_shields = 2; + } + + Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)); + if (Ships[get_ship_from_obj(objp)].flags & SF_LOCKED) { + if (!m_locked) + m_locked = 2; + + } else { + if (m_locked) + m_locked = 2; + } + } + + objp = GET_NEXT(objp); + } + } + + CDialog::OnInitDialog(); + str.Format("%d", m_velocity); + GetDlgItem(IDC_VELOCITY)->SetWindowText(str); + str.Format("%d", m_shields); + GetDlgItem(IDC_SHIELDS)->SetWindowText(str); + str.Format("%d", m_hull); + GetDlgItem(IDC_HULL)->SetWindowText(str); + + inited = 1; + box = (CComboBox *) GetDlgItem(IDC_DOCKED); + box->ResetContent(); + if (!m_multi_edit) { + z = box->AddString("Nothing"); + m_docked = 0; + + type = model_get_dock_types(Ships[m_ship].modelnum); + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) { + ship = get_ship_from_obj(objp); + if ((ship != m_ship) && (ship_docking_valid(m_ship, ship) || ship_docking_valid(ship, m_ship))) { + if (model_get_dock_types(Ships[ship].modelnum) & type) { + z = box->AddString(Ships[ship].ship_name); + box->SetItemData(z, ship); + } + } + } + + objp = GET_NEXT(objp); + } + + m_docked_with = -1; + z = Ai_info[Ships[m_ship].ai_index].dock_objnum; + if (z >= 0) { + Assert(Objects[z].type == OBJ_SHIP); + z = m_docked_with = get_ship_from_obj(z); + m_docked = box->FindStringExact(-1, Ships[z].ship_name); + + m_docker_index = Ai_info[Ships[m_ship].ai_index].dock_index; + m_dockee_index = Ai_info[Ships[m_ship].ai_index].dockee_index; + initialize_docker_points(); + initialize_dockee_points(); + + } else { + m_docker_index = m_dockee_index = m_docker_point = m_dockee_point = -1; + GetDlgItem(IDC_DOCKER_POINT)->EnableWindow(FALSE); + GetDlgItem(IDC_DOCKEE_POINT)->EnableWindow(FALSE); + } + + ptr = GET_FIRST(&Ships[m_ship].subsys_list); + while (ptr != END_OF_LIST(&Ships[m_ship].subsys_list)) { + ((CListBox *) GetDlgItem(IDC_SUBSYS)) -> AddString(ptr->system_info->subobj_name); + ptr = GET_NEXT(ptr); + } + + } else { + GetDlgItem(IDC_DOCKED)->EnableWindow(FALSE); + GetDlgItem(IDC_DOCKER_POINT)->EnableWindow(FALSE); + GetDlgItem(IDC_DOCKEE_POINT)->EnableWindow(FALSE); + GetDlgItem(IDC_SUBSYS)->EnableWindow(FALSE); + GetDlgItem(IDC_DAMAGE)->EnableWindow(FALSE); + } + + GetDlgItem(IDC_SHIELDS)->EnableWindow(m_has_shields ? TRUE : FALSE); + GetDlgItem(IDC_SHIELDS_SPIN)->EnableWindow(m_has_shields ? TRUE : FALSE); + + m_velocity_spin.SetRange(0, 100); + m_hull_spin.SetRange(0, 100); + m_shields_spin.SetRange(0, 100); + m_damage_spin.SetRange(0, 100); + change_subsys(); + UpdateData(FALSE); + if (vflag) + GetDlgItem(IDC_VELOCITY)->SetWindowText(""); + if (sflag) + GetDlgItem(IDC_SHIELDS)->SetWindowText(""); + if (hflag) + GetDlgItem(IDC_HULL)->SetWindowText(""); + + return TRUE; +} + +void initial_status::initialize_docker_points() +{ + int i, type; + CComboBox *box; + + box = (CComboBox *) GetDlgItem(IDC_DOCKER_POINT); + box->ResetContent(); + if (m_docked_with < 0) { + m_docker_index = m_docker_point = -1; + return; + } + + type = model_get_dock_types(Ships[m_docked_with].modelnum); + set_valid_dock_points(m_ship, type, box); + i = box->GetCount(); + Assert(i); // this shouldn't happen. + while (i--) + if ((int) box->GetItemData(i) == m_docker_index) + break; + + m_docker_point = i; + if (i < 0) { + m_docker_point = 0; + m_docker_index = box->GetItemData(0); + } +} + +void initial_status::initialize_dockee_points() +{ + int i, type; + CComboBox *box; + + box = (CComboBox *) GetDlgItem(IDC_DOCKEE_POINT); + box->ResetContent(); + if ((m_docked_with < 0) || (m_docker_index < 0)) { + m_dockee_index = m_dockee_point = -1; + return; + } + + type = model_get_dock_index_type(Ships[m_ship].modelnum, m_docker_index); + set_valid_dock_points(m_docked_with, type, box); + i = box->GetCount(); + Assert(i); // this shouldn't happen. + while (i--) + if ((int) box->GetItemData(i) == m_dockee_index) + break; + + m_dockee_point = i; + if (i < 0) { + m_dockee_point = 0; + m_dockee_index = box->GetItemData(0); + } +} + +void initial_status::change_subsys() +{ + int z, cargo_index, enable = FALSE, enable_cargo_name = FALSE; + ship_subsys *ptr; + + if (cur_subsys != LB_ERR) { + ptr = GET_FIRST(&Ships[m_ship].subsys_list); + while (cur_subsys--) { + Assert(ptr != END_OF_LIST(&Ships[m_ship].subsys_list)); + ptr = GET_NEXT(ptr); + } + + MODIFY(ptr -> current_hits, 100.0f - (float) m_damage); + + // update cargo name + if (strlen(m_cargo_name) > 0) { + cargo_index = string_lookup(m_cargo_name, Cargo_names, Num_cargo); + if (cargo_index == -1) { + if (Num_cargo < MAX_CARGO); + cargo_index = Num_cargo++; + strcpy(Cargo_names[cargo_index], m_cargo_name); + ptr->subsys_cargo_name = cargo_index; + } else { + ptr->subsys_cargo_name = cargo_index; + } + } else { + ptr->subsys_cargo_name = -1; + } + set_modified(); + } + + cur_subsys = z = ((CListBox *) GetDlgItem(IDC_SUBSYS)) -> GetCurSel(); + if (z == LB_ERR) { + m_damage = 100; + + } else { + ptr = GET_FIRST(&Ships[m_ship].subsys_list); + while (z--) { + Assert(ptr != END_OF_LIST(&Ships[m_ship].subsys_list)); + ptr = GET_NEXT(ptr); + } + + m_damage = 100 - (int) ptr -> current_hits; + if ( (Ship_info[Ships[m_ship].ship_info_index].flags & SIF_HUGE_SHIP) && valid_cap_subsys_cargo_list(ptr->system_info->subobj_name) ) { + enable_cargo_name = TRUE; + if (ptr->subsys_cargo_name != -1) { + m_cargo_name = Cargo_names[ptr->subsys_cargo_name]; + } else { + m_cargo_name = _T(""); + } + } else { + m_cargo_name = _T(""); + } + enable = TRUE; + } + + GetDlgItem(IDC_DAMAGE) -> EnableWindow(enable); + GetDlgItem(IDC_DAMAGE_SPIN) -> EnableWindow(enable); + GetDlgItem(IDC_CARGO_NAME)->EnableWindow(enable && enable_cargo_name); + UpdateData(FALSE); +} + +void initial_status::OnOK() +{ + char buf[256]; + int z, obj, vflag = 0, sflag = 0, hflag = 0; + object *o1, *o2, *objp; + + if (GetDlgItem(IDC_VELOCITY)->GetWindowText(buf, 255)) + vflag = 1; + if (GetDlgItem(IDC_SHIELDS)->GetWindowText(buf, 255)) + sflag = 1; + if (GetDlgItem(IDC_HULL)->GetWindowText(buf, 255)) + hflag = 1; + + UpdateData(TRUE); + UpdateData(TRUE); + change_subsys(); + if (m_multi_edit) { + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->flags & OF_MARKED)) { + if (vflag) + MODIFY(objp->phys_info.speed, (float) m_velocity); + + if (sflag) + MODIFY(objp->shields[0], (float) m_shields); + + if (hflag) + MODIFY(objp->hull_strength, (float) m_hull); + + if (m_has_shields == 1) + objp->flags &= ~OF_NO_SHIELDS; + else if (!m_has_shields) + objp->flags |= OF_NO_SHIELDS; + + if (m_locked == 1) + Ships[get_ship_from_obj(objp)].flags |= SF_LOCKED; + else if (!m_has_shields) + Ships[get_ship_from_obj(objp)].flags &= ~SF_LOCKED; + } + + objp = GET_NEXT(objp); + } + + } else { + MODIFY(Objects[cur_object_index].phys_info.speed, (float) m_velocity); + MODIFY(Objects[cur_object_index].shields[0], (float) m_shields); + MODIFY(Objects[cur_object_index].hull_strength, (float) m_hull); + if (m_has_shields) + Objects[cur_object_index].flags &= ~OF_NO_SHIELDS; + else + Objects[cur_object_index].flags |= OF_NO_SHIELDS; + + if (m_locked == 1) + Ships[m_ship].flags |= SF_LOCKED; + else if (!m_locked) + Ships[m_ship].flags &= ~SF_LOCKED; + + obj = -1; + if (m_docked_with >= 0) + obj = Ships[m_docked_with].objnum; + + z = Ai_info[Ships[m_ship].ai_index].dock_objnum; + if (z >= 0) { + int o; + + if (obj >= 0) { + o = Ai_info[Ships[get_ship_from_obj(obj)].ai_index].dock_objnum; + if (o >= 0) + undock(o); + } + + undock(Ships[m_ship].objnum); + Update_window = 1; + } + + MODIFY(Ai_info[Ships[m_ship].ai_index].dock_objnum, obj); + if (obj >= 0) { + int s1type, s2type; + + // sets up the actual docking on the Fred screen. After docking is done, use a loose + // set of rules to possibly set an arrival cue to false + o1 = &Objects[Ships[m_ship].objnum]; + o2 = &Objects[Ships[m_docked_with].objnum]; + m_docker_index = ((CComboBox *) GetDlgItem(IDC_DOCKER_POINT)) -> GetItemData(m_docker_point); + MODIFY(Ai_info[Ships[m_ship].ai_index].dock_index, m_docker_index); + m_dockee_index = ((CComboBox *) GetDlgItem(IDC_DOCKEE_POINT)) -> GetItemData(m_dockee_point); + MODIFY(Ai_info[Ships[m_ship].ai_index].dockee_index, m_dockee_index); + + // based on the types of the two ships docked, set the arrival cue of the "smaller" ship + // to false. + s1type = Ship_info[Ships[m_ship].ship_info_index].flags; + s2type = Ship_info[Ships[m_docked_with].ship_info_index].flags; + + if (ship_docking_valid(m_ship, m_docked_with)) { + ai_dock_with_object(o1, o2, 89, AIDO_DOCK_NOW, m_docker_index, m_dockee_index); + } else { + ai_dock_with_object(o2, o1, 89, AIDO_DOCK_NOW, m_dockee_index, m_docker_index); + } + + // based on the rules already defined for docking (see ship_docking_valid()), we can make + // assumptions about what are "small" and "large" ships in this process. Set the arrival + // cue of the small ships to false so that they can properly arrive in the mission when + // their parent does + if ( (s2type & SIF_CARGO) || (s1type & SIF_BIG_SHIP) ) { + reset_arrival_to_false( m_docked_with ); + } else if ( (s1type & SIF_CARGO) || (s2type & SIF_BIG_SHIP) ) { + reset_arrival_to_false( m_ship ); + } else { + // default rule -- pick one! + reset_arrival_to_false( m_ship ); + } + + Assert ( (Ships[m_ship].flags & SF_INITIALLY_DOCKED) || (Ships[m_docked_with].flags & SF_INITIALLY_DOCKED) ); + + Update_window = 1; + } + + if (Update_window) + update_map_window(); + } + + CDialog::OnOK(); +} + +void initial_status::undock(int obj) +{ + int o2; + vector v; + int ship_num, other_ship_num; + + o2 = Ai_info[Ships[get_ship_from_obj(obj)].ai_index].dock_objnum; + if (o2 < 0) + return; + + vm_vec_sub(&v, &Objects[obj].pos, &Objects[o2].pos); + vm_vec_normalize(&v); + ship_num = get_ship_from_obj(obj); + other_ship_num = get_ship_from_obj(o2); + + if (ship_docking_valid(ship_num, other_ship_num) ) + vm_vec_scale_add2(&Objects[obj].pos, &v, Objects[obj].radius * 2.0f); + else + vm_vec_scale_add2(&Objects[o2].pos, &v, Objects[o2].radius * -2.0f); + + Ai_info[Ships[ship_num].ai_index].dock_objnum = -1; + Ai_info[Ships[other_ship_num].ai_index].dock_objnum = -1; + + // check to see if one of these ships has an arrival cue of false. If so, then + // reset it back to default value of true. be sure to correctly update before + // and after setting data. + Ship_editor_dialog.update_data(1); + if ( Ships[ship_num].arrival_cue == Locked_sexp_false ) { + Ships[ship_num].arrival_cue = Locked_sexp_true; + } else if ( Ships[other_ship_num].arrival_cue == Locked_sexp_false ) { + Ships[other_ship_num].arrival_cue = Locked_sexp_true; + } + + // reset the initially docked flags on both ships (only one will be set, but this s + // just for safety!) + Ships[ship_num].flags &= ~(SF_INITIALLY_DOCKED); + Ships[other_ship_num].flags &= ~(SF_INITIALLY_DOCKED); + Ship_editor_dialog.initialize_data(1); + +} + +void initial_status::OnSelchangeSubsys() +{ + UpdateData(TRUE); + UpdateData(TRUE); + change_subsys(); +} + +void initial_status::OnSelchangeDocked() +{ + UpdateData(TRUE); + UpdateData(TRUE); + if (m_docked) { + GetDlgItem(IDC_DOCKER_POINT)->EnableWindow(TRUE); + GetDlgItem(IDC_DOCKEE_POINT)->EnableWindow(TRUE); + + m_docked_with = ((CComboBox *) GetDlgItem(IDC_DOCKED)) -> GetItemData(m_docked); + initialize_docker_points(); + initialize_dockee_points(); + + } else { // selected 'nothing' as being docked with + m_docked_with = m_docker_index = m_dockee_index = m_docker_point = m_dockee_point = -1; + GetDlgItem(IDC_DOCKER_POINT)->EnableWindow(FALSE); + GetDlgItem(IDC_DOCKEE_POINT)->EnableWindow(FALSE); + } + + UpdateData(FALSE); +} + +void initial_status::OnSelchangeDockerPoint() +{ + UpdateData(TRUE); + UpdateData(TRUE); + initialize_dockee_points(); + UpdateData(FALSE); +} + +void initial_status::OnHasShields() +{ + if (m_has_shields == 1) + m_has_shields = 0; + else + m_has_shields = 1; + + ((CButton *) GetDlgItem(IDC_HAS_SHIELDS))->SetCheck(m_has_shields); + GetDlgItem(IDC_SHIELDS)->EnableWindow(m_has_shields); + GetDlgItem(IDC_SHIELDS_SPIN)->EnableWindow(m_has_shields); +} + +// function to set the arrival cue of a ship to false +void initial_status::reset_arrival_to_false( int shipnum ) +{ + char buf[256]; + + // if the cue is not false, make it false. Be sure to all ship editor dialog functions + // to update date before and after we modify the cue. + if ( Ships[shipnum].arrival_cue != Locked_sexp_false ) { + Ship_editor_dialog.update_data(1); + free_sexp2(Ships[shipnum].arrival_cue); + Ships[shipnum].arrival_cue = Locked_sexp_false; + Ship_editor_dialog.initialize_data(1); + sprintf(buf, "Setting arrival cue of ship %s\nto false for initial docking purposes.", Ships[shipnum].ship_name); + MessageBox(buf, "", MB_OK | MB_ICONEXCLAMATION); + } + + Ships[shipnum].flags |= SF_INITIALLY_DOCKED; +} + +void initial_status::OnLocked() +{ + if (m_locked == 1) + m_locked = 0; + else + m_locked = 1; + + ((CButton *) GetDlgItem(IDC_LOCKED))->SetCheck(m_locked); +} diff --git a/src/fred2/mainfrm.cpp b/src/fred2/mainfrm.cpp new file mode 100644 index 0000000..b559404 --- /dev/null +++ b/src/fred2/mainfrm.cpp @@ -0,0 +1,710 @@ +/* + * $Logfile: /Freespace2/code/Fred2/MainFrm.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * MainFrm.cpp : implementation of the CMainFrame class + * The main frame class of a document/view architechure, which we hate but must + * deal with, due to Microsoft limiting our freedom and forcing us to use whether + * we want to or not. The main frame is basically the container window that other + * view windows are within. In Fred, our view window is always maximized inside + * the main frame window, so you can't tell the difference between the two. A few + * old MFC events are handled here because the people working on the code before + * me (Hoffoss) decided to put it here. I've been putting it all in FredView. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 8 10/13/99 2:55p Jefff + * fixed unnumbered XSTRs + * + * 7 10/13/99 9:22a Daveb + * Fixed Fred jumpnode placing bug. Fixed 1024 glide tiled texture problem + * related to movies. Fixed launcher spawning from PXO screen. + * + * 6 9/14/99 11:07p Andsager + * Improve freddoc error message. Fix Frerd2 about box for "2" part of + * Fred2 + * + * 5 9/08/99 12:07a Andsager + * Add browser based help to Fred + * + * 4 9/01/99 10:15a Dave + * + * 3 11/19/98 8:36a Dave + * Removed ST reordering of ships in Fred. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 31 9/16/98 6:54p Dave + * Upped max sexpression nodes to 1800 (from 1600). Changed FRED to sort + * the ship list box. Added code so that tracker stats are not stored with + * only 1 player. + * + * 30 3/09/98 10:56a Hoffoss + * Added jump node objects to Fred. + * + * 29 2/20/98 12:56p Adam + * Made ship class box even wider. + * + * 28 2/20/98 12:07p Hoffoss + * Made ship type combo box on toolbar wider. + * + * 27 9/30/97 2:11p Hoffoss + * Removed player start item from toolbar combo box. + * + * 26 8/21/97 5:39p Hoffoss + * Fixed warning when building optimized. Was MFC code I copied, which + * says something about MFC I guess. + * + * 25 8/17/97 10:22p Hoffoss + * Fixed several bugs in Fred with Undo feature. In the process, recoded + * a lot of CFile.cpp. + * + * 24 8/14/97 2:32p Hoffoss + * fixed bug where controlling an object doesn't cause screen updates, and + * added a number of cool features to viewpoint/control object code. + * + * 23 7/21/97 3:57p Hoffoss + * Removed group combo box from toolbar, since I don't think I'll ever get + * it working right. + * + * 22 6/26/97 12:39p Mike + * Add ship_type to ship_info and ships.tbl. + * + * 21 6/09/97 4:57p Hoffoss + * Added autosave and undo to Fred. + * + * 20 5/05/97 1:35p Hoffoss + * View window is now refocused when a new ship type selection is made. + * + * 19 5/01/97 10:54a Hoffoss + * Removed obsolete files/classes from Fred project, and any reference to + * them. + * + * 18 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 17 4/03/97 11:35a Hoffoss + * Fixed bugs: viewpoint didn't reset, initial orders not updated when + * referenced ship is renamed or deleted. + * + * 16 3/10/97 4:58p Hoffoss + * Added waypoint and start types to drop down toolbar combo box and fixed + * context menu new ship type selection to also work. + * + * 15 3/10/97 12:54p Hoffoss + * Added drop down combo box to toolbar and fixed compiling errors Mark + * (maybe Mike?) introduced to code. + * + * 14 2/28/97 11:31a Hoffoss + * Implemented modeless dialog saving and restoring, and changed some + * variables names. + * + * 13 2/27/97 5:54p Hoffoss + * Implemented support for saving and restoring window positions. + * + * 12 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" + +#include "mainfrm.h" + +#include "freddoc.h" +#include "fredview.h" + +#include "messageeditordlg.h" +#include "shipclasseditordlg.h" +#include "missionnotesdlg.h" +#include "grid.h" +#include "dialog1.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame + +IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd) + +BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) + ON_MESSAGE(WM_MENU_POPUP_EDIT, OnMenuPopupTest) + ON_CBN_SELCHANGE(ID_NEW_SHIP_TYPE, OnNewShipTypeChange) + + //{{AFX_MSG_MAP(CMainFrame) + ON_WM_CREATE() + ON_COMMAND(ID_EDITORS_AI_CLASSES, OnEditorsAiClasses) + ON_COMMAND(ID_EDITORS_GOALS, OnEditorsGoals) + ON_COMMAND(ID_EDITORS_ART, OnEditorsArt) + ON_COMMAND(ID_EDITORS_MUSIC, OnEditorsMusic) + ON_COMMAND(ID_EDITORS_SHIP_CLASSES, OnEditorsShipClasses) + ON_COMMAND(ID_EDITORS_SOUND, OnEditorsSound) + ON_COMMAND(ID_EDITORS_TERRAIN, OnEditorsTerrain) + ON_COMMAND(ID_FILE_MISSIONNOTES, OnFileMissionnotes) + ON_WM_LBUTTONUP() + ON_WM_DESTROY() + ON_COMMAND(ID_VIEW_STATUS_BAR, OnViewStatusBar) + ON_UPDATE_COMMAND_UI(ID_VIEW_STATUS_BAR, OnUpdateViewStatusBar) + ON_UPDATE_COMMAND_UI(ID_INDICATOR_LEFT, OnUpdateLeft) + ON_UPDATE_COMMAND_UI(ID_INDICATOR_RIGHT, OnUpdateRight) + ON_COMMAND(ID_MIKE_GRIDCONTROL, OnMikeGridcontrol) + ON_COMMAND(IDR_MENU_POPUP_TOGGLE1, OnMenuPopupToggle1) + ON_UPDATE_COMMAND_UI(IDR_MENU_POPUP_TOGGLE1, OnUpdateMenuPopupToggle1) + ON_WM_RBUTTONDOWN() + ON_COMMAND(ID_HELP_INPUT_INTERFACE, OnHelpInputInterface) + ON_WM_CLOSE() + ON_WM_INITMENU() + ON_COMMAND(ID_HELP_FINDER, OnFredHelp) + ON_COMMAND(ID_HELP, OnFredHelp) + //ON_COMMAND(ID_CONTEXT_HELP, OnFredHelp) + //ON_COMMAND(ID_DEFAULT_HELP, OnFredHelp) + //}}AFX_MSG_MAP + // Global help commands +END_MESSAGE_MAP() + +#define FRED_HELP_URL "\\data\\freddocs\\index.html" + +static UINT indicators[] = +{ + ID_SEPARATOR, // status line indicator + ID_SEPARATOR, + ID_SEPARATOR, + ID_INDICATOR_MODIFIED, + ID_SEPARATOR, +// ID_INDICATOR_LEFT, +// ID_INDICATOR_RIGHT, +// ID_INDICATOR_CAPS, +// ID_INDICATOR_NUM, +// ID_INDICATOR_SCRL, +}; + +CMainFrame *Fred_main_wnd; +color_combo_box m_new_ship_type_combo_box; + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame construction/destruction + +CMainFrame::CMainFrame() +{ +} + +CMainFrame::~CMainFrame() +{ +} + +int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + int z; + CRect rect; + + if (CFrameWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + if (!m_wndToolBar.Create(this) || + !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) + { + TRACE0("Failed to create toolbar\n"); + return -1; // fail to create + } + + // Create the combo box + z = m_wndToolBar.CommandToIndex(ID_NEW_SHIP_TYPE); + Assert(z != -1); + m_wndToolBar.SetButtonInfo(z, ID_NEW_SHIP_TYPE, TBBS_SEPARATOR, 230); + + // Design guide advises 12 pixel gap between combos and buttons +// m_wndToolBar.SetButtonInfo(1, ID_SEPARATOR, TBBS_SEPARATOR, 12); + m_wndToolBar.GetItemRect(z, &rect); + rect.top = 3; + rect.bottom = rect.top + 550; + if (!m_new_ship_type_combo_box.Create(CBS_DROPDOWNLIST | WS_VISIBLE | WS_VSCROLL | CBS_HASSTRINGS | LBS_OWNERDRAWFIXED, + rect, &m_wndToolBar, ID_NEW_SHIP_TYPE)) + { + TRACE0("Failed to create new ship type combo-box\n"); + return FALSE; + } + +/* if (!m_wndStatusBar.Create(this) || + !m_wndStatusBar.SetIndicators(indicators, + sizeof(indicators)/sizeof(UINT))) + { + TRACE0("Failed to create status bar\n"); + return -1; // fail to create + } +*/ + +/* if (!m_wndStatusBar.Create(this, + WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, ID_MY_STATUS_BAR) || + !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT)))*/ + + if (!m_wndStatusBar.Create(this) || + !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) + { + TRACE0("Failed to create status bar\n"); + return -1; + + } else { + m_wndStatusBar.SetPaneInfo(0, 0, SBPS_STRETCH, 0); + m_wndStatusBar.SetPaneInfo(1, 0, SBPS_NORMAL, 80); + m_wndStatusBar.SetPaneInfo(2, 0, SBPS_NORMAL, 180); +// m_wndStatusBar.SetPaneInfo(3, 0, SBPS_NORMAL, 100); + m_wndStatusBar.SetPaneInfo(4, 0, SBPS_NORMAL, 130); + } + + // TODO: Remove this if you don't want tool tips or a resizeable toolbar + m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | + CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); + + // TODO: Delete these three lines if you don't want the toolbar to + // be dockable + m_wndToolBar.EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM); + EnableDocking(CBRS_ALIGN_ANY); + DockControlBar(&m_wndToolBar); + + Fred_main_wnd = this; + Ship_editor_dialog.Create(); + Wing_editor_dialog.Create(); + Waypoint_editor_dialog.Create(); + init_tools(); + LoadBarState("Tools state"); + return 0; +} + +BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + return CFrameWnd::PreCreateWindow(cs); +} + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame diagnostics + +#ifdef _DEBUG +void CMainFrame::AssertValid() const +{ + CFrameWnd::AssertValid(); +} + +void CMainFrame::Dump(CDumpContext& dc) const +{ + CFrameWnd::Dump(dc); +} + +#endif //_DEBUG + +//void CMainFrame::OnEditorsShips() +//{ +// CShipEditorDlg dlg; +// +// dlg.DoModal(); +// +//} + +void CMainFrame::OnEditorsAiClasses() +{ +} + +void CMainFrame::OnEditorsGoals() +{ +} + +void CMainFrame::OnEditorsArt() +{ +} + +void CMainFrame::OnEditorsMusic() +{ +} + +void CMainFrame::OnEditorsShipClasses() +{ +} + +void CMainFrame::OnEditorsSound() +{ +} + +void CMainFrame::OnEditorsTerrain() +{ +} + +void CMainFrame::OnFileMissionnotes() +{ + CMissionNotesDlg dlg; + + dlg.DoModal(); +} + +// I have been unable to get this message event to occur. +void CMainFrame::OnLButtonUp(UINT nFlags, CPoint point) +{ + CFrameWnd::OnLButtonUp(nFlags, point); +} + +// This event is invoked when you click on the black X in the upper right corner +// or when you do File/Exit. +void CMainFrame::OnDestroy() +{ + Fred_main_wnd = NULL; + CFrameWnd::OnDestroy(); +} + +void CMainFrame::OnViewStatusBar() +{ + m_wndStatusBar.ShowWindow((m_wndStatusBar.GetStyle() & WS_VISIBLE) == 0); + RecalcLayout(); +} + +void CMainFrame::OnUpdateViewStatusBar(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck((m_wndStatusBar.GetStyle() & WS_VISIBLE) != 0); +} + +void CMainFrame::OnUpdateLeft(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(::GetKeyState(VK_LBUTTON) < 0); +} + +void CMainFrame::OnUpdateRight(CCmdUI* pCmdUI) +{ + pCmdUI->Enable(::GetKeyState(VK_RBUTTON) < 0); +} + +void CMainFrame::OnMikeGridcontrol() +{ + CGrid dlg; + + dlg.DoModal(); +} + +int Toggle1_var = 0; + +void CMainFrame::OnMenuPopupToggle1() +{ + if (Toggle1_var == 0) + Toggle1_var = 1; + else + Toggle1_var = 0; + +} + +void CMainFrame::OnUpdateMenuPopupToggle1(CCmdUI* pCmdUI) +{ + pCmdUI->SetCheck(Toggle1_var); +} + +LONG CMainFrame::OnMenuPopupTest(UINT wParam, LONG lParam) +{ + CMenu menu; + CPoint point; + + point = * ((CPoint*) lParam); + + ClientToScreen(&point); + + menu.LoadMenu(IDR_MENU1); + menu.GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); + + return 0L; +} + +CPoint Global_point2; + +void CMainFrame::OnRButtonDown(UINT nFlags, CPoint point) +{ + Global_point2 = point; + + PostMessage(WM_MENU_POPUP_TEST, nFlags, (int) &Global_point2); + CFrameWnd::OnRButtonDown(nFlags, point); +} + +void CMainFrame::OnHelpInputInterface() +{ + dialog1 dlg; + + dlg.DoModal(); +} + +void CMainFrame::OnClose() +{ + theApp.write_ini_file(); + SaveBarState("Tools state"); + CFrameWnd::OnClose(); +} + +void CMainFrame::init_tools() +{ + static int count = 0; + int i; + //int highest_terran_index; + //char ship_name[256]; + //int ship_index; + + Assert(count < 2); + + // add + if (count++) { + for (i=0; i= Num_ship_types) || (Ship_info[ship_index].species != SPECIES_TERRAN)){ + break; + } + highest_terran_index++; + } + */ + } +} + +void CMainFrame::OnNewShipTypeChange() +{ + if (Fred_view_wnd) + Fred_view_wnd->SetFocus(); +} + +void color_combo_box::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) +{ + int m_cyText = 24, z; + CString strText; + char ship_name[256]; + + // You must override DrawItem and MeasureItem for LBS_OWNERDRAWVARIABLE + ASSERT((GetStyle() & (LBS_OWNERDRAWFIXED | CBS_HASSTRINGS)) == + (LBS_OWNERDRAWFIXED | CBS_HASSTRINGS)); + + CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); + + // I think we need to do a lookup by ship name here + if(lpDrawItemStruct->itemID >= (uint)Num_ship_types){ + z = lpDrawItemStruct->itemID; + } else { + memset(ship_name, 0, 256); + GetLBText(lpDrawItemStruct->itemID, ship_name); + z = ship_info_lookup(ship_name); + } + + if ((z >= 0) && (lpDrawItemStruct->itemAction & (ODA_DRAWENTIRE | ODA_SELECT))) + { + int cyItem = GetItemHeight(z); + BOOL fDisabled = !IsWindowEnabled(); + + COLORREF newTextColor = RGB(0x80, 0x80, 0x80); // light gray + if (!fDisabled) { + if (z >= Num_ship_types) + newTextColor = RGB(0, 0, 0); + else + switch (Ship_info[z].species) { + case SPECIES_TERRAN: newTextColor = RGB(0, 0, 192); break; + case SPECIES_VASUDAN: newTextColor = RGB(0, 128, 0); break; + case SPECIES_SHIVAN: newTextColor = RGB(192, 0, 0); break; + case SPECIES_NONE: newTextColor = RGB(224, 128, 0); break; + } + } + + COLORREF oldTextColor = pDC->SetTextColor(newTextColor); + COLORREF newBkColor = GetSysColor(COLOR_WINDOW); + COLORREF oldBkColor = pDC->SetBkColor(newBkColor); + + if (newTextColor == newBkColor) + newTextColor = RGB(0xC0, 0xC0, 0xC0); // dark gray + + if (!fDisabled && ((lpDrawItemStruct->itemState & ODS_SELECTED) != 0)) + { + pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT)); + pDC->SetBkColor(GetSysColor(COLOR_HIGHLIGHT)); + } + + if (m_cyText == 0) + VERIFY(cyItem >= CalcMinimumItemHeight()); + + if (z == Id_select_type_jump_node) + strText = _T("Jump Node"); + else if (z == Id_select_type_start) + strText = _T("Player Start"); + else if (z == Id_select_type_waypoint) + strText = _T("Waypoint"); + else + strText = _T(Ship_info[z].name); +// GetLBText(lpDrawItemStruct->itemID, strText); + + pDC->ExtTextOut(lpDrawItemStruct->rcItem.left, + lpDrawItemStruct->rcItem.top + max(0, (cyItem - m_cyText) / 2), + ETO_OPAQUE, &(lpDrawItemStruct->rcItem), strText, strText.GetLength(), NULL); + + pDC->SetTextColor(oldTextColor); + pDC->SetBkColor(oldBkColor); + } + + if ((lpDrawItemStruct->itemAction & ODA_FOCUS) != 0) + pDC->DrawFocusRect(&(lpDrawItemStruct->rcItem)); +} + +int color_combo_box::CalcMinimumItemHeight() +{ + int nResult = 1; + + if ((GetStyle() & (LBS_HASSTRINGS | LBS_OWNERDRAWFIXED)) == + (LBS_HASSTRINGS | LBS_OWNERDRAWFIXED)) + { + CClientDC dc(this); + CFont* pOldFont = dc.SelectObject(GetFont()); + TEXTMETRIC tm; + VERIFY (dc.GetTextMetrics ( &tm )); + dc.SelectObject(pOldFont); + + nResult = tm.tmHeight; + } + + return nResult; +} + +void color_combo_box::MeasureItem(LPMEASUREITEMSTRUCT) +{ + // You must override DrawItem and MeasureItem for LBS_OWNERDRAWVARIABLE + ASSERT((GetStyle() & (LBS_OWNERDRAWFIXED | CBS_HASSTRINGS)) == + (LBS_OWNERDRAWFIXED | CBS_HASSTRINGS)); +} + +int color_combo_box::SetCurSelNEW(int model_index) +{ + if((model_index < 0) || (model_index >= Num_ship_types)){ + return SetCurSel(model_index); + } + + // lookup the ship name + return FindString(0, Ship_info[model_index].name); +} + +int color_combo_box::GetCurSelNEW() +{ + int cur_sel; + int ship_info; + char ship_name[256]; + char *hmmm = ship_name; + + // see if we have a special item (>= Num_ship_types) + cur_sel = GetCurSel(); + if(cur_sel >= Num_ship_types){ + return cur_sel; + } + + // otherwise lookup the ship by name + memset(ship_name, 0, 256); + if(GetLBText(cur_sel, hmmm) == CB_ERR){ + return CB_ERR; + } + ship_info = ship_info_lookup(ship_name); + if((ship_info < 0) || (ship_info >= Num_ship_types)){ + return CB_ERR; + } + return ship_info; +} + +void CMainFrame::OnInitMenu(CMenu* pMenu) +{ + CString str; + + if (Undo_available && !FREDDoc_ptr->undo_desc[1].IsEmpty()) + str = "Undo " + FREDDoc_ptr->undo_desc[1] + "\tCtrl+Z"; + else + str = "Undo\tCtrl+Z"; + + if (pMenu->GetMenuState(ID_EDIT_UNDO, MF_BYCOMMAND) != -1) + pMenu->ModifyMenu(ID_EDIT_UNDO, MF_BYCOMMAND, ID_EDIT_UNDO, str); + + CFrameWnd::OnInitMenu(pMenu); +} + + +void url_launch(char *url) +{ + int r; + + r = (int) ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOW); + if (r < 32) { + char *txt = NULL; + + switch (r) { + case 0: txt = XSTR("The operating system is out of memory or resources.", 1107); break; + case ERROR_BAD_FORMAT: txt = XSTR("The .EXE file is invalid (non-Win32 .EXE or error in .EXE image).", 1108); break; + case SE_ERR_ACCESSDENIED: txt = XSTR("The operating system denied access to the specified file. ", 1109); break; + case SE_ERR_ASSOCINCOMPLETE: txt = XSTR("The filename association is incomplete or invalid.\r\n(You need to have a default Internet browser installed)", 1110); break; + case SE_ERR_DDEBUSY: txt = XSTR("The DDE transaction could not be completed because other DDE transactions were being processed.", 1111); break; + case SE_ERR_DDEFAIL: txt = XSTR("The DDE transaction failed.", 1112); break; + case SE_ERR_DDETIMEOUT: txt = XSTR("The DDE transaction could not be completed because the request timed out.", 1113); break; + case SE_ERR_DLLNOTFOUND: txt = XSTR("The specified dynamic-link library was not found.", 1114); break; + case SE_ERR_OOM: txt = XSTR("There was not enough memory to complete the operation.", 1115); break; + case SE_ERR_SHARE: txt = XSTR("A sharing violation occurred.", 1116); break; + + // No browser installed message + case SE_ERR_NOASSOC: + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: txt = XSTR("\r\nUnable to locate Fred Help file: \\data\\freddocs\\index.html\r\n", 1479); break; + + default: txt = XSTR("Unknown error occurred.", 1118); break; + } + AfxMessageBox(txt, MB_OK | MB_ICONERROR); + } +} + + +void CMainFrame::OnFredHelp() +{ + char buffer[_MAX_PATH]; + + // get exe path + strcpy(buffer, Fred_exe_dir); + + // strip exe name + char *last_slash = strrchr(buffer, '\\'); + if ( last_slash == NULL) { + return; + } else { + *last_slash = 0; + } + + // add rest of path + strcat(buffer, FRED_HELP_URL); + + // shell_open url + url_launch(buffer); +} + diff --git a/src/fred2/makehelp.bat b/src/fred2/makehelp.bat new file mode 100644 index 0000000..d2f41ce --- /dev/null +++ b/src/fred2/makehelp.bat @@ -0,0 +1,31 @@ +@echo off +REM -- First make map file from Microsoft Visual C++ generated resource.h +echo // MAKEHELP.BAT generated Help Map file. Used by FRED.HPJ. >"hlp\fred.hm" +echo. >>"hlp\fred.hm" +echo // Commands (ID_* and IDM_*) >>"hlp\fred.hm" +makehm ID_,HID_,0x10000 IDM_,HIDM_,0x10000 resource.h >>"hlp\fred.hm" +echo. >>"hlp\fred.hm" +echo // Prompts (IDP_*) >>"hlp\fred.hm" +makehm IDP_,HIDP_,0x30000 resource.h >>"hlp\fred.hm" +echo. >>"hlp\fred.hm" +echo // Resources (IDR_*) >>"hlp\fred.hm" +makehm IDR_,HIDR_,0x20000 resource.h >>"hlp\fred.hm" +echo. >>"hlp\fred.hm" +echo // Dialogs (IDD_*) >>"hlp\fred.hm" +makehm IDD_,HIDD_,0x20000 resource.h >>"hlp\fred.hm" +echo. >>"hlp\fred.hm" +echo // Frame Controls (IDW_*) >>"hlp\fred.hm" +makehm IDW_,HIDW_,0x50000 resource.h >>"hlp\fred.hm" +REM -- Make help for Project FRED + + +echo Building Win32 Help files +start /wait hcrtf -x "hlp\fred.hpj" +echo. +if exist Debug\nul copy "hlp\fred.hlp" Debug +if exist Debug\nul copy "hlp\fred.cnt" Debug +if exist Release\nul copy "hlp\fred.hlp" Release +if exist Release\nul copy "hlp\fred.cnt" Release +echo. + + diff --git a/src/fred2/management.cpp b/src/fred2/management.cpp new file mode 100644 index 0000000..fb4cc83 --- /dev/null +++ b/src/fred2/management.cpp @@ -0,0 +1,2655 @@ +/* + * $Logfile: /Freespace2/code/Fred2/Management.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * This file handles the management of Objects, Ships, Wings, etc. Basically + * all the little structures we have that usually inter-relate that need to + * be handled in a standard way, and thus should be handled by a single + * function. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 29 10/13/99 9:22a Daveb + * Fixed Fred jumpnode placing bug. Fixed 1024 glide tiled texture problem + * related to movies. Fixed launcher spawning from PXO screen. + * + * 28 9/08/99 10:01p Dave + * Make sure game won't run in a drive's root directory. Make sure + * standalone routes suqad war messages properly to the host. + * + * 27 7/23/99 2:02p Jamesa + * Don't require gamepalettes + * + * 26 7/15/99 3:07p Dave + * 32 bit detection support. Mouse coord commandline. + * + * 25 7/02/99 4:30p Dave + * Much more sophisticated lightning support. + * + * 24 5/20/99 6:59p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 23 4/26/99 8:47p Dave + * Made all pof related nebula stuff customizable through Fred. + * + * 22 4/16/99 2:34p Andsager + * Second pass on debris fields + * + * 21 4/15/99 5:00p Andsager + * Frist pass on Debris field + * + * 20 4/07/99 6:21p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 19 3/31/99 9:50a Andsager + * Interface for generalization of asteroid field (debris field) + * + * 18 3/30/99 5:40p Dave + * Fixed reinforcements for TvT in multiplayer. + * + * 17 3/24/99 4:05p Dave + * Put in support for assigning the player to a specific squadron with a + * specific logo. Preliminary work for doing pos/orient checksumming in + * multiplayer to reduce bandwidth. + * + * 16 3/20/99 5:09p Dave + * Fixed release build fred warnings and unhandled exception. + * + * 15 2/23/99 7:03p Dave + * Rewrote a horribly mangled and evil team loadout dialog. Bugs gone. + * + * 14 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 13 2/10/99 11:11a Johnson + * Think I fixed the problem where the Neb2_awacs value was improperly + * being checked for in Fred. + * + * 12 2/07/99 8:51p Andsager + * Add inner bound to asteroid field. Inner bound tries to stay astroid + * free. Wrap when within and don't throw at ships inside. + * + * 11 1/25/99 5:03a Dave + * First run of stealth, AWACS and TAG missile support. New mission type + * :) + * + * 10 1/19/99 3:57p Andsager + * Round 2 of variables + * + * 9 12/18/98 1:14a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 8 11/06/98 10:15a Dave + * + * 7 10/29/98 9:22p Dave + * Removed minor bug concering externalization of campaign files. + * + * 6 10/29/98 6:49p Dave + * Finished up Fred support for externalizing mission and campaign files. + * + * 5 10/29/98 10:41a Dave + * Change the way cfile initializes exe directory. + * + * 4 10/23/98 5:07p Dave + * Put in beginnings of localization/externalization functionality. + * + * 3 10/22/98 6:13p Dave + * Added registry and localization support. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 227 6/17/98 2:05p Hoffoss + * Fixed bug: Shield system for team or ship type flags new reset when + * mission gets cleared out (new or load mission). + * + * 226 5/20/98 10:19p Allender + * make Fred work with NDEBUG build + * + * 225 5/17/98 1:48p Allender + * made number of respawns for new missions 3 + * + * 224 5/01/98 12:34p John + * Added code to force FreeSpace to run in the same dir as exe and made + * all the parse error messages a little nicer. + * + * 223 4/30/98 8:23p John + * Fixed some bugs with Fred caused by my new cfile code. + * + * 222 4/27/98 4:07p Allender + * make orders_accepted not be assigned to -1 when creating new ships. + * Don't set the use_default_orders flag when orders are -1 + * + * 221 4/25/98 6:43p Allender + * reset model data when initializeing new mission + * + * 220 4/17/98 1:41p Allender + * took out function calls in NDEBUG mode + * + * 219 4/13/98 10:59a Hoffoss + * Made "new mission" clear the cmd brief structure. + * + * 218 4/13/98 10:11a John + * Made timer functions thread safe. Made timer_init be called in all + * projects. + * + * 217 4/06/98 5:37p Hoffoss + * Added sexp tree support to briefings in Fred. + * + * 216 4/06/98 12:55p John + * Upped the gamma for Fred. + * + * 215 4/03/98 11:34a John + * Fixed the stuff I broke in Fred from the new breifing + * + * 214 4/02/98 3:00p Johnson + * Fixed a bug a release build turned up. + * + * 213 3/24/98 1:36p Hoffoss + * Moved call to load_filter_info() to after cfile_init(), as it is + * dependent on that being set up. + * + * 212 3/24/98 12:42p Allender + * fixed a couple of minor problems with arrival targets + * + * 211 3/21/98 7:36p Lawrance + * Move jump nodes to own lib. + * + * 210 3/17/98 4:16p Allender + * minor changes to the kamikaze flag + * + * 209 3/17/98 11:55a Johnson + * Fixed bug where jump nodes wheren't being cleared on a new mission. + * + * 208 3/10/98 4:26p Hoffoss + * Changed jump node structure to include a name. Position is now taken + * from the object (each jump node has an associated object now). + * + * 207 3/09/98 4:30p Allender + * multiplayer secondary weapon changes. red-alert and cargo-known-delay + * sexpressions. Add time cargo revealed to ship structure + * + * 206 3/09/98 10:56a Hoffoss + * Added jump node objects to Fred. + * + * 205 3/06/98 5:10p Allender + * made time to: field in extended targetbox use support time to dock code + * for all docking shpis. Only display for waypoints and docking (not + * undocking). Small fixups to message menu -- not allowing depart when + * disabled. Depart is now by default ignored for all non-small ships + * + * 204 3/05/98 3:59p Hoffoss + * Added a bunch of new command brief stuff, and asteroid initialization + * to Fred. + * + * 203 2/26/98 4:59p Allender + * groundwork for team vs team briefings. Moved weaponry pool into the + * Team_data structure. Added team field into the p_info structure. + * Allow for mutliple structures in the briefing code. + * + * 202 2/17/98 12:07p Hoffoss + * Changed over to using SF_CARGO_REVEALED in fred. + * + * 201 2/17/98 10:12a Hoffoss + * Fixed bug with sprintf() in reference_handler(). Forgot the first + * argument! :) Amazing it never crashed before. + * + * 200 2/13/98 11:45a Hoffoss + * Made all new ships created in Fred default to 33% initial speed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "mainfrm.h" +#include "freddoc.h" +#include "fredview.h" +#include "fredrender.h" +#include "ailocal.h" +#include "aigoals.h" +#include "ship.h" +#include "linklist.h" +#include "missionparse.h" +#include "missionmessage.h" +#include "missiongoals.h" +#include "missionbriefcommon.h" +#include "management.h" +#include "cfile.h" +#include "palman.h" +#include "2d.h" +#include "3d.h" +#include "weapon.h" +#include "key.h" +#include "parselo.h" +#include "fvi.h" +#include "starfield.h" +#include "sexp.h" +#include "mouse.h" +#include "missioncampaign.h" +#include "wing.h" +#include "messageeditordlg.h" +#include "eventeditor.h" +#include "missiongoalsdlg.h" +#include "shieldsysdlg.h" +#include "eventmusic.h" +#include "debriefingeditordlg.h" +#include "nebula.h" +#include "asteroid.h" +#include "hudsquadmsg.h" +#include "jumpnode.h" +#include "medals.h" +#include "localize.h" +#include "osregistry.h" +#include "fhash.h" +#include "timer.h" +#include "neb.h" +#include "neblightning.h" + +#define MAX_DOCKS 50 + +#define UNKNOWN_USER "Unknown" + +int cur_wing = -1; +int cur_wing_index; +int cur_object_index = -1; +int cur_ship = -1; +int cur_model_index = 0; +int cur_waypoint = -1; +int cur_waypoint_list = -1; +int delete_flag; +int bypass_update = 0; +int Default_player_model = 0; +int Update_ship = 0; +int Update_wing = 0; +int Fred_font; + +char Fred_exe_dir[512] = ""; + +char Fred_alt_names[MAX_SHIPS][NAME_LENGTH+1]; + +// object numbers for ships in a wing. +int wing_objects[MAX_WINGS][MAX_SHIPS_PER_WING]; + +char *Docking_bay_list[MAX_DOCKS]; + +CCriticalSection CS_cur_object_index; + +ai_goal_list Ai_goal_list[] = { + "Waypoints", AI_GOAL_WAYPOINTS, + "Waypoints once", AI_GOAL_WAYPOINTS_ONCE, + "Warp", AI_GOAL_WARP, + "Destroy subsystem", AI_GOAL_DESTROY_SUBSYSTEM, + "Attack", AI_GOAL_CHASE | AI_GOAL_CHASE_WING, + "Dock", AI_GOAL_DOCK, + "Undock", AI_GOAL_UNDOCK, + "Guard", AI_GOAL_GUARD | AI_GOAL_GUARD_WING, + "Attack any ship", AI_GOAL_CHASE_ANY, + "Disable ship", AI_GOAL_DISABLE_SHIP, + "Disarm ship", AI_GOAL_DISARM_SHIP, + "Evade ship", AI_GOAL_EVADE_SHIP, + "Ignore ship", AI_GOAL_IGNORE, + "Stay near ship", AI_GOAL_STAY_NEAR_SHIP, + "Keep safe distance", AI_GOAL_KEEP_SAFE_DISTANCE, + "Stay still", AI_GOAL_STAY_STILL, + "Play dead", AI_GOAL_PLAY_DEAD, +}; + +int Ai_goal_list_size = sizeof(Ai_goal_list) / sizeof(ai_goal_list); + +// internal function prototypes +void set_cur_indices(int obj); +int common_object_delete(int obj); +int create_waypoint(vector *pos, int list); +int create_ship(matrix *orient, vector *pos, int ship_type); +int query_ship_name_duplicate(int ship); +char *reg_read_string( char *section, char *name, char *default_value ); + +extern int Nmodel_num; +extern int Nmodel_bitmap; + +void string_copy(char *dest, CString &src, int max_len, int modify) +{ + int len; + + if (modify) + if (strcmp(src, dest)) + set_modified(); + + len = strlen(src); + if (len >= max_len) + len = max_len - 1; + + strncpy(dest, src, len); + dest[len] = 0; +} + +// converts a multiline string (one with newlines in it) into a windows format multiline +// string (newlines changed to '\r\n'). +CString convert_multiline_string(char *src) +{ + char *ptr, buf[256]; + int i; + static CString str; + + str = _T(""); + while ((ptr = strchr(src, '\n'))!=NULL) { + i = ptr - src; + while (i > 250) { + strncpy(buf, src, 250); + buf[250] = 0; + str += buf; + src += 250; + i -= 250; + } + + if (i) + strncpy(buf, src, i); + + buf[i] = 0; + str += buf; + str += "\r\n"; + src = ptr + 1; + } + + i = strlen(src); + if (i) + str += src; + + return str; +} + +// Converts a windows format multiline CString back into a normal multiline string. +void deconvert_multiline_string(char *buf, CString &str, int max_len) +{ + char *ptr = buf; + int i, j; + CString str2; + + Assert(max_len > 1); + Assert(buf); + max_len -= 2; + while ((i = str.Find("\r\n")) >= 0) { + for (j=0; j 0); + + // doh + if(cfile_init(Fred_exe_dir)){ + exit(1); + } + + // initialize localization module. Make sure this is done AFTER initialzing OS. + // NOTE : Fred should ALWAYS run in Enlish. Otherwise it might swap in another language + // when saving - which would cause inconsistencies when externalizing to tstrings.tbl via Exstr + // trust me on this :) + lcl_init(LCL_ENGLISH); + + #ifndef NDEBUG + load_filter_info(); + #endif + + gr_init(GR_640, GR_SOFTWARE, 8); + gr_set_gamma(3.0f); + + sprintf(palette_filename, "gamepalette%d-%02d", 1, 1); + mprintf(("Loading palette %s\n", palette_filename)); + palette_load_table(palette_filename); + + Fred_font = gr_init_font("font01.vf"); + key_init(); + mouse_init(); + + mission_brief_common_init(); + obj_init(); + model_free_all(); // Free all existing models + ai_init(); + weapon_init(); + parse_medal_tbl(); // get medal names for sexpression usage + ship_init(); + init_parse(); + + // initialize and activate external string hash table + // make sure to do here so that we don't parse the table files into the hash table - waste of space + fhash_init(); + fhash_activate(); + + create_new_mission(); + neb2_init(); // fullneb stuff + stars_init(); + brief_init_icons(); + event_music_parse_musictbl(); + cmd_brief_reset(); + Show_waypoints = TRUE; + Campaign.filename[0] = 0; // indicate initialized state + + stars_level_init(); + + // neb lightning + nebl_init(); + + gr_reset_clip(); + g3_start_frame(0); + g3_set_view_matrix(&eye_pos, &eye_orient, 0.5f); + + for (i=0; i init_tools(); +} + +void set_physics_controls() +{ + physics_init(&view_physics); + view_physics.max_vel.x *= physics_speed / 3.0f; + view_physics.max_vel.y *= physics_speed / 3.0f; + view_physics.max_vel.z *= physics_speed / 3.0f; + view_physics.max_rear_vel *= physics_speed / 3.0f; + + view_physics.max_rotvel.x *= physics_rot / 30.0f; + view_physics.max_rotvel.y *= physics_rot / 30.0f; + view_physics.max_rotvel.z *= physics_rot / 30.0f; + view_physics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED; + theApp.write_ini_file(1); +} + +int create_object_on_grid(int list) +{ + int obj = -1; + float rval; + vector dir,pos; + + g3_point_to_vec_delayed(&dir, marking_box.x2, marking_box.y2); + + rval = fvi_ray_plane(&pos, &The_grid->center, &The_grid->gmatrix.uvec, &view_pos, &dir, 0.0f); + + if (rval>=0.0f) { + unmark_all(); + obj = create_object(&pos, list); + if (obj >= 0) { + mark_object(obj); + FREDDoc_ptr->autosave("object create"); + + } else if (obj == -1) + Fred_main_wnd->MessageBox("Maximum ship limit reached. Can't add any more ships."); + } + + return obj; +} + +void fix_ship_name(int ship) +{ + int i = 1; + + do { + sprintf(Ships[ship].ship_name, "U.R.A. Moron %d", i++); + } while (query_ship_name_duplicate(ship)); +} + +int create_ship(matrix *orient, vector *pos, int ship_type) +{ + int obj, ship, z1, z2; + ship_info *sip; + + obj = ship_create(orient, pos, ship_type); + if (obj == -1) + return -1; + + Objects[obj].phys_info.speed = 33.0f; + + ship = Objects[obj].instance; + sip = &Ship_info[Ships[ship].ship_info_index]; + + if (query_ship_name_duplicate(ship)) + fix_ship_name(ship); + + z1 = Shield_sys_teams[Ships[ship].team]; + z2 = Shield_sys_types[ship_type]; + if (((z1 == 1) && z2) || (z2 == 1)) + Objects[obj].flags |= OF_NO_SHIELDS; + + z1 = Ship_info[Ships[ship].ship_info_index].species; + if (z1 == SPECIES_SHIVAN) { + Ships[ship].team = TEAM_HOSTILE; + Ships[ship].flags &= ~SF_CARGO_REVEALED; + + } else { + Ships[ship].team = TEAM_FRIENDLY; + Ships[ship].flags |= SF_CARGO_REVEALED; + } + + if ( Ships[ship].team == TEAM_FRIENDLY ) { + + // if this ship is not a small ship, then make the orders be the default orders without + // the depart item + if ( !(sip->flags & SIF_SMALL_SHIP) ) { + Ships[ship].orders_accepted = ship_get_default_orders_accepted( sip ); + Ships[ship].orders_accepted &= ~DEPART_ITEM; + } + + } else { + Ships[ship].orders_accepted = 0; + } + + Ai_info[Ships[ship].ai_index].kamikaze_damage = min(1000.0f, 200.0f + (sip->initial_hull_strength / 4.0f)); + + return obj; +} + +int query_ship_name_duplicate(int ship) +{ + int i; + + for (i=0; iinstance; + if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) { + obj = create_ship(&objp->orient, &objp->pos, Ships[inst].ship_info_index); + if (obj == -1) + return -1; + + n = Objects[obj].instance; + Ships[n].team = Ships[inst].team; + Ships[n].arrival_cue = dup_sexp_chain(Ships[inst].arrival_cue); + Ships[n].departure_cue = dup_sexp_chain(Ships[inst].departure_cue); + Ships[n].cargo1 = Ships[inst].cargo1; + Ships[n].arrival_location = Ships[inst].arrival_location; + Ships[n].departure_location = Ships[inst].departure_location; + Ships[n].arrival_delay = Ships[inst].arrival_delay; + Ships[n].departure_delay = Ships[inst].departure_delay; + Ships[n].weapons = Ships[inst].weapons; + Ships[n].hotkey = Ships[inst].hotkey; + + aip1 = &Ai_info[Ships[n].ai_index]; + aip2 = &Ai_info[Ships[inst].ai_index]; + aip1->behavior = aip2->behavior; + aip1->ai_class = aip2->ai_class; + for (i=0; igoals[i] = aip2->goals[i]; + + if ( aip2->ai_flags & AIF_KAMIKAZE ) + aip1->ai_flags |= AIF_KAMIKAZE; + if ( aip2->ai_flags & AIF_NO_DYNAMIC ) + aip2->ai_flags |= AIF_NO_DYNAMIC; + + aip1->kamikaze_damage = aip2->kamikaze_damage; + + objp1 = &Objects[obj]; + objp2 = &Objects[Ships[inst].objnum]; + objp1->phys_info.speed = objp2->phys_info.speed; + objp1->phys_info.fspeed = objp2->phys_info.fspeed; + objp1->hull_strength = objp2->hull_strength; + objp1->shields[0] = objp2->shields[0]; + + subp1 = GET_FIRST(&Ships[n].subsys_list); + subp2 = GET_FIRST(&Ships[inst].subsys_list); + while (subp1 != END_OF_LIST(&Ships[n].subsys_list)) { + Assert(subp2 != END_OF_LIST(&Ships[inst].subsys_list)); + subp1 -> current_hits = subp2 -> current_hits; + subp1 = GET_NEXT(subp1); + subp2 = GET_NEXT(subp2); + } + + for (i=0; itype == OBJ_WAYPOINT) { + obj = create_waypoint(&objp->pos, list); + list = Objects[obj].instance; + } + + if (obj == -1) + return -1; + + Objects[obj].pos = objp->pos; + Objects[obj].orient = objp->orient; + Objects[obj].flags |= OF_TEMP_MARKED; + return obj; +} + +int create_object(vector *pos, int list) +{ + int obj, n; + + if (cur_model_index == Id_select_type_waypoint) + obj = create_waypoint(pos, list); + + else if (cur_model_index == Id_select_type_start) { + if (Player_starts >= MAX_PLAYERS) { + Fred_main_wnd->MessageBox("Unable to create new player start point.\n" + "You have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION); + obj = -2; + + } else if (The_mission.game_type & MISSION_TYPE_SINGLE) { + Fred_main_wnd->MessageBox("You can't have more than one player start in\n" + "single player missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION); + obj = -2; + + } else if (The_mission.game_type & MISSION_TYPE_TRAINING) { + Fred_main_wnd->MessageBox("You can't have more than one player start in\n" + "a training missions.\n", NULL, MB_OK | MB_ICONEXCLAMATION); + obj = -2; + + } else + obj = create_player(Player_starts, pos, NULL, Default_player_model); + + } else if (cur_model_index == Id_select_type_jump_node) { + if (Num_jump_nodes >= MAX_JUMP_NODES) { + Fred_main_wnd->MessageBox("Unable to create more jump nodes. You have reached the limit.", NULL, MB_OK | MB_ICONEXCLAMATION); + obj = -2; + + } else { + obj = jumpnode_create(pos); + } + + } else if(Ship_info[cur_model_index].flags & SIF_NO_FRED){ + obj = -1; + } else { // creating a ship + obj = create_ship(NULL, pos, cur_model_index); + if (obj == -1) + return -1; + + n = Objects[obj].instance; + Ships[n].arrival_cue = alloc_sexp("true", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1); + Ships[n].departure_cue = alloc_sexp("false", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, -1); + Ships[n].cargo1 = 0; + } + + if (obj < 0) + return obj; + + obj_merge_created_list(); + set_modified(); + Update_window = 1; + return obj; +} + +int create_player(int num, vector *pos, matrix *orient, int type, int init) +{ + int obj; + + if (type == -1){ + type = Default_player_model; + } + + Assert(type >= 0); + Assert(Player_starts < MAX_PLAYERS); + Player_starts++; + obj = create_ship(orient, pos, type); + Objects[obj].type = OBJ_START; + + // be sure arrival/departure cues are set + Ships[Objects[obj].instance].arrival_cue = Locked_sexp_true; + Ships[Objects[obj].instance].departure_cue = Locked_sexp_false; + obj_merge_created_list(); + set_modified(); + return obj; +} + +int query_waypoint_path_name_duplicate(int list) +{ + int i; + + for (i=0; iMessageBox("Unable to create new waypoint path. You\n" + "have reached the maximum limit.", NULL, MB_OK | MB_ICONEXCLAMATION); + return -1; + } + + Assert((list >= 0) && (list < MAX_WAYPOINT_LISTS)); // illegal index or out of lists. + if (Waypoint_lists[list].count >= MAX_WAYPOINTS_PER_LIST) { + Fred_main_wnd->MessageBox("Unable to create new waypoint. You have\n" + "reached the maximum limit on waypoints per list.", NULL, MB_OK | MB_ICONEXCLAMATION); + return -1; + } + + if (Waypoint_lists[list].count > index) { + i = Waypoint_lists[list].count; + while (i > index) { + Waypoint_lists[list].waypoints[i] = Waypoint_lists[list].waypoints[i - 1]; + Waypoint_lists[list].flags[i] = Waypoint_lists[list].flags[i - 1]; + i--; + } + } + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + Assert(ptr->type != OBJ_NONE); + if (ptr->type == OBJ_WAYPOINT) { + i = ptr->instance; + if ((i / 65536 == list) && ((i & 0xffff) >= index)){ + ptr->instance++; + } + } + + ptr = GET_NEXT(ptr); + } + + Waypoint_lists[list].count++; + Waypoint_lists[list].flags[index] = 0; + Waypoint_lists[list].waypoints[index] = *pos; + if (list >= Num_waypoint_lists){ + Num_waypoint_lists = list + 1; + } + + obj = obj_create(OBJ_WAYPOINT, -1, list * 65536 + index, NULL, pos, 0.0f, OF_RENDERS); + set_modified(); + return obj; +} + +void create_new_mission() +{ + reset_mission(); + *Mission_filename = 0; + FREDDoc_ptr->autosave("nothing"); + Undo_count = 0; +} + +void reset_mission() +{ + clear_mission(); + player_start1 = create_player(0, &vmd_zero_vector, &vmd_identity_matrix); +} + +void clear_mission() +{ + char *str; + int i, j, count; + CTime t; + + // clean up everything we need to before we reset back to defaults. + if (Briefing_dialog){ + Briefing_dialog->reset_editor(); + } + + cmd_brief_reset(); + mission_event_shutdown(); + + Asteroid_field.num_initial_asteroids = 0; // disable asteroid field by default. + Asteroid_field.speed = 0.0f; + vm_vec_make(&Asteroid_field.min_bound, -1000.0f, -1000.0f, -1000.0f); + vm_vec_make(&Asteroid_field.max_bound, 1000.0f, 1000.0f, 1000.0f); + vm_vec_make(&Asteroid_field.inner_min_bound, -500.0f, -500.0f, -500.0f); + vm_vec_make(&Asteroid_field.inner_max_bound, 500.0f, 500.0f, 500.0f); + Asteroid_field.has_inner_bound = 0; + Asteroid_field.field_type = FT_ACTIVE; + Asteroid_field.debris_genre = DG_ASTEROID; + Asteroid_field.field_debris_type[0] = -1; + Asteroid_field.field_debris_type[1] = -1; + Asteroid_field.field_debris_type[2] = -1; + + strcpy(Mission_parse_storm_name, "none"); + + obj_init(); + model_free_all(); // Free all existing models + ai_init(); + ship_init(); + Num_ai_dock_names = 0; + Num_jump_nodes = 0; + num_wings = 0; + for (i=0; i= MAX_OBJECTS || Objects[index].type == OBJ_NONE) + return FALSE; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + Assert(ptr->type != OBJ_NONE); + if (OBJ_INDEX(ptr) == index) + obj_found = TRUE; + + ptr = GET_NEXT(ptr); + } + + Assert(obj_found); // just to make sure it's in the list like it should be. + return TRUE; +} + +int query_valid_ship(int index) +{ + int obj_found = FALSE; + object *ptr; + + if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_SHIP) + return FALSE; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + Assert(ptr->type != OBJ_NONE); + if (OBJ_INDEX(ptr) == index) + obj_found = TRUE; + + ptr = GET_NEXT(ptr); + } + + Assert(obj_found); // just to make sure it's in the list like it should be. + return TRUE; +} + +int query_valid_waypoint(int index) +{ + int obj_found = FALSE; + object *ptr; + + if (index < 0 || index >= MAX_OBJECTS || Objects[index].type != OBJ_WAYPOINT) + return FALSE; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + Assert(ptr->type != OBJ_NONE); + if (OBJ_INDEX(ptr) == index) + obj_found = TRUE; + + ptr = GET_NEXT(ptr); + } + + Assert(obj_found); // just to make sure it's in the list like it should be. + return TRUE; +} + +// Sets the current object to whatever is specified or advances to the next object +// in the list if nothing is passed. +void set_cur_object_index(int obj) +{ + if (obj < 0) + unmark_all(); + else + mark_object(obj); + + set_cur_indices(obj); // select the new object + Update_ship = Update_wing = 1; + Waypoint_editor_dialog.initialize_data(1); + Update_window = 1; +} + +// changes the currently selected wing. It is assumed that cur_wing == cur_ship's wing +// number. Don't call this if this won't be true, or else you'll screw things up. +void set_cur_wing(int wing) +{ + cur_wing = wing; +/* if (cur_ship != -1) + Assert(cur_wing == Ships[cur_ship].wingnum); + if ((cur_object_index != -1) && (Objects[cur_object_index].type == OBJ_SHIP)) + Assert(cur_wing == Ships[Objects[cur_object_index].instance].wingnum);*/ + Update_wing = 1; + Update_window = 1; +} + +// sets up the various cur_* global variables related to the selecting of an object. This +// is an internal function that shouldn't typically get called directly. Use set_cur_object_index() instead. +void set_cur_indices(int obj) +{ + int i; + object *ptr; + CSingleLock sync(&CS_cur_object_index); + + sync.Lock(); // Don't modify until it's unlocked (if it's locked elsewhere). + if (query_valid_object(obj)) { + cur_object_index = obj; + cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1; + if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) { + cur_ship = Objects[obj].instance; + cur_wing = Ships[cur_ship].wingnum; + if (cur_wing >= 0) + for (i=0; inext; + + Assert(ptr != END_OF_LIST(&obj_used_list)); + cur_object_index = OBJ_INDEX(ptr); + Assert(ptr->type != OBJ_NONE); + cur_ship = cur_wing = cur_waypoint_list = cur_waypoint = -1; + if (ptr->type == OBJ_SHIP) { + cur_ship = ptr->instance; + cur_wing = Ships[cur_ship].wingnum; + for (i=0; itype == OBJ_WAYPOINT) { + cur_waypoint_list = ptr->instance / 65536; + cur_waypoint = ptr->instance & 0xffff; + } +} + +int update_dialog_boxes() +{ + int z; + + nprintf(("Fred routing", "updating dialog boxes\n")); + + // check wing first, since ships are dependent on wings, but not the reverse + z = Wing_editor_dialog.update_data(0); + if (z) { + nprintf(("Fred routing", "wing dialog save failed\n")); + Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + + return z; + } + + z = Ship_editor_dialog.update_data(0); + if (z) { + nprintf(("Fred routing", "ship dialog save failed\n")); + Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + + return z; + } + + z = Waypoint_editor_dialog.update_data(0); + if (z) { + nprintf(("Fred routing", "waypoint dialog save failed\n")); + Waypoint_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + + return z; + } + + update_map_window(); + return 0; +} + +int delete_object(int obj) +{ + int r; + + Ship_editor_dialog.bypass_all++; + r = common_object_delete(obj); + Ship_editor_dialog.bypass_all--; + return r; +} + +int delete_object(object *ptr) +{ + int r; + + Ship_editor_dialog.bypass_all++; + r = common_object_delete(OBJ_INDEX(ptr)); + Ship_editor_dialog.bypass_all--; + return r; +} + +int delete_ship(int ship) +{ + int r; + + Ship_editor_dialog.bypass_all++; + r = common_object_delete(Ships[ship].objnum); + Ship_editor_dialog.bypass_all--; + return r; +} + +int common_object_delete(int obj) +{ + char msg[255], *name; + int i, z, r, type, num; + object *objp; + + type = Objects[obj].type; + if (type == OBJ_START) { + i = Objects[obj].instance; + if (Player_starts < 2) { // player 1 start + Fred_main_wnd->MessageBox("Must have at least 1 player starting point!", + NULL, MB_OK | MB_ICONEXCLAMATION); + + unmark_object(obj); + return 1; + } + + Assert((i >= 0) && (i < MAX_SHIPS)); + sprintf(msg, "Player %d", i + 1); + name = msg; + r = reference_handler(name, REF_TYPE_PLAYER, obj); + if (r) + return r; + + if (Ships[i].wingnum >= 0) { + r = delete_ship_from_wing(i); + if (r) + return r; + } + + Objects[obj].type = OBJ_SHIP; // was allocated as a ship originally, so remove as such. + invalidate_references(name, REF_TYPE_PLAYER); + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + // check if any ship is docked with this ship and break dock if so. + if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) { + num = get_ship_from_obj(objp); + if (Ai_info[Ships[num].ai_index].dock_objnum == obj) + Ai_info[Ships[num].ai_index].dock_objnum = -1; + } + + objp = GET_NEXT(objp); + } + + if (Player_start_shipnum == i) { // need a new single player start. + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->type == OBJ_START) { + Player_start_shipnum = objp->instance; + break; + } + + objp = GET_NEXT(objp); + } + } + + Player_starts--; + + } else if (type == OBJ_WAYPOINT) { + int list, count; + + list = Objects[obj].instance / 65536; + i = Objects[obj].instance & 0xffff; + Assert(list >= 0 && list < MAX_WAYPOINT_LISTS); + count = Waypoint_lists[list].count; + Assert(i >= 0 && i < count); + + if (Waypoint_lists[list].count == 1) { + name = Waypoint_lists[list].name; + r = reference_handler(name, REF_TYPE_PATH, obj); + if (r) + return r; + } + + sprintf(msg, "%s:%d", Waypoint_lists[list].name, i + 1); + name = msg; + r = reference_handler(name, REF_TYPE_WAYPOINT, obj); + if (r) + return r; + + invalidate_references(name, REF_TYPE_WAYPOINT); + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if ((objp->type == OBJ_WAYPOINT) && ((objp->instance / 65536) == list)) + if ((objp->instance & 0xffff) > i) + objp->instance--; + + objp = GET_NEXT(objp); + } + + while (i < count - 1) { + Waypoint_lists[list].waypoints[i] = Waypoint_lists[list].waypoints[i + 1]; + i++; + } + + Waypoint_lists[list].count--; + if (!Waypoint_lists[list].count) { + invalidate_references(Waypoint_lists[list].name, REF_TYPE_PATH); + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if ((objp->type == OBJ_WAYPOINT) && ((objp->instance / 65536) > list)) + objp->instance -= 65536; + + objp = GET_NEXT(objp); + } + + while (list < Num_waypoint_lists - 1) { + Waypoint_lists[list] = Waypoint_lists[list + 1]; + list++; + } + + Num_waypoint_lists--; + Waypoint_lists[list].count = 0; + } + + } else if (type == OBJ_SHIP) { + name = Ships[Objects[obj].instance].ship_name; + r = reference_handler(name, REF_TYPE_SHIP, obj); + if (r) + return r; + + z = Objects[obj].instance; + if (Ships[z].wingnum >= 1) { + invalidate_references(name, REF_TYPE_SHIP); + r = delete_ship_from_wing(z); + if (r) + return r; + + } else if (Ships[z].wingnum >= 0) { + r = delete_ship_from_wing(z); + if (r) + return r; + + invalidate_references(name, REF_TYPE_SHIP); + } + + for (i=0; itype == OBJ_START) || (objp->type == OBJ_SHIP)) { + num = get_ship_from_obj(objp); + if (Ai_info[Ships[num].ai_index].dock_objnum == obj) + Ai_info[Ships[num].ai_index].dock_objnum = -1; + } + + objp = GET_NEXT(objp); + } + + } else if (type == OBJ_POINT) { + Assert(Briefing_dialog); + Briefing_dialog->delete_icon(Objects[obj].instance); + Update_window = 1; + return 0; + + } else if (type == OBJ_JUMP_NODE) { + i = Objects[obj].instance; + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if ((objp->type == OBJ_JUMP_NODE) && (objp->instance > i)) + objp->instance--; + + objp = GET_NEXT(objp); + } + + while (i < Num_jump_nodes - 1) { + Jump_nodes[i] = Jump_nodes[i + 1]; + i++; + } + + Num_jump_nodes--; + } + + unmark_object(obj); + obj_delete(obj); + set_modified(); + Update_window = 1; + return 0; +} + +void delete_marked() +{ + object *ptr, *next; + + delete_flag = 0; + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + next = GET_NEXT(ptr); + if (ptr->flags & OF_MARKED) + if (delete_object(ptr) == 2) // user went to a reference, so don't get in the way. + break; + + ptr = next; + } + + if (!delete_flag) + set_cur_object_index(-1); + + Update_window = 1; +} + +void delete_reinforcement(int num) +{ + int i; + + for (i=num; i= 0) { + if (Wings[wing].wave_count == 1) { + cur_wing = -1; + Update_wing = 1; + r = delete_wing(wing, 1); + if (r) { + if (r == 2){ + delete_flag = 1; + } + + return r; + } + + } else { + i = Wings[wing].wave_count; + end = i - 1; + while (i--){ + if (wing_objects[wing][i] == Ships[ship].objnum){ + break; + } + } + + Assert(i != -1); // Error, object should be in wing. + if (Wings[wing].special_ship == i){ + Wings[wing].special_ship = 0; + } else if (Wings[wing].special_ship > i) { + Wings[wing].special_ship--; + } + + if (i != end) { + wing_objects[wing][i] = wing_objects[wing][end]; + Wings[wing].ship_index[i] = Wings[wing].ship_index[end]; + if (Objects[wing_objects[wing][i]].type == OBJ_SHIP) { + sprintf(name, "%s %d", Wings[wing].name, i + 1); + rename_ship(Wings[wing].ship_index[i], name); + } + } + + if (Wings[wing].threshold >= Wings[wing].wave_count){ + Wings[wing].threshold = Wings[wing].wave_count - 1; + } + + Wings[wing].wave_count--; + if (Wings[wing].wave_count && (Wings[wing].threshold >= Wings[wing].wave_count)){ + Wings[wing].threshold = Wings[wing].wave_count - 1; + } + } + } + + set_modified(); + return 0; +} + +// What does this do? +void add_ship_to_wing() +{ + int org_object = cur_object_index; + vector tvec; + + set_cur_object_index(); + if (Objects[org_object].type == OBJ_NONE) { + create_object(vm_vec_make(&tvec, 10.0f, 10.0f, 10.0f)); + + } else { + Objects[cur_object_index] = Objects[org_object]; + Objects[cur_object_index].pos.x += 3.0f; + Objects[cur_object_index].pos.y += 3.0f; + physics_init(&Objects[cur_object_index].phys_info); + Objects[cur_object_index].orient = Objects[org_object].orient; + } + + set_modified(); +} + +// Return true if current object is valid and is in a wing. +// Else return false. +int query_object_in_wing(int obj) +{ + if (query_valid_object(obj)){ + if (Ships[Objects[obj].instance].wingnum != -1){ + return TRUE; + } + } + + return FALSE; +} + +void mark_object(int obj) +{ + Assert(query_valid_object(obj)); + if (!(Objects[obj].flags & OF_MARKED)) { + Objects[obj].flags |= OF_MARKED; // set as marked + Marked++; + Update_window = 1; + if (cur_object_index == -1){ + set_cur_object_index(obj); + } + Update_ship = Update_wing = 1; + Waypoint_editor_dialog.initialize_data(1); + } +} + +void unmark_object(int obj) +{ + Assert(query_valid_object(obj)); + if (Objects[obj].flags & OF_MARKED) { + Objects[obj].flags &= ~OF_MARKED; + Marked--; + Update_window = 1; + if (obj == cur_object_index) { // need to find a new index + object *ptr; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->flags & OF_MARKED) { + set_cur_object_index(OBJ_INDEX(ptr)); // found one + return; + } + + ptr = GET_NEXT(ptr); + } + + set_cur_object_index(-1); // can't find one; nothing is marked. + } + Update_ship = Update_wing = 1; + Waypoint_editor_dialog.initialize_data(1); + } +} + +// clears the marked flag of all objects (so nothing is marked) +void unmark_all() +{ + int i; + + if (Marked) { + for (i=0; iGetMenuItemCount(); + while (count--){ + ptr->DeleteMenu(count, MF_BYPOSITION); + } +} + +void generate_wing_popup_menu(CMenu *mptr, int first_id, int state) +{ + int i, z, columns, rows, count; + + columns = 1; + rows = num_wings; + while (rows > 25) { + columns++; + rows = num_wings / columns; + } + + count = rows + 1; + for (i=0; iAppendMenu(z, first_id + i, Wings[i].name); + } + } + + mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); +} + +void generate_ship_popup_menu(CMenu *mptr, int first_id, int state, int filter) +{ + int z, ship, columns, rows, count, num_ships; + object *ptr; + + columns = 1; + num_ships = ship_get_num_ships(); + rows = num_ships; + while (rows > 25) { + columns++; + rows = num_ships / columns; + } + + count = rows + 1; + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if ((ptr->type == OBJ_SHIP) || ((ptr->type == OBJ_START) && (filter & SHIP_FILTER_PLAYERS))) { + z = 1; + if (filter & SHIP_FILTER_FLYABLE) { + if (Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE){ + z = 0; + } + } + + if (z) { + z = state | MF_STRING; + if (!count--) { + count = rows; + z |= MF_MENUBARBREAK; + } + + ship = ptr->instance; + mptr->AppendMenu(z, first_id + ship, Ships[ship].ship_name); + } + } + + ptr = GET_NEXT(ptr); + } + + mptr->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); +} + +// Alternate string lookup function, taking a CString instead. The reason that it's here, +// instead of parselo.cpp, is because the class CString require an include of windows.h, +// which everyone wants to avoid including in any freespace header files. So.. +int string_lookup(CString str1, char *strlist[], int max) +{ + int i; + + for (i=0; iGetMenuItemCount(); + while (i--) { + if ((submenu = base->GetSubMenu(i))>0) { + if (gray_menu_tree(submenu)) { + count++; + } else { + base->EnableMenuItem(i, MF_GRAYED | MF_BYPOSITION); + } + + } else { + z = base->GetMenuState(i, MF_BYPOSITION); + if (z == MF_ENABLED){ + count++; + } + } + } + + return count; +} + +int query_initial_orders_conflict(int wing) +{ + int i, z; + + Assert(wing != -1); + if (wing == -1){ + return 0; + } + + if (query_initial_orders_empty(Wings[wing].ai_goals)){ + return 0; + } + + i = Wings[wing].wave_count; // wing has orders, now check ships. + while (i--) { + z = Ships[Objects[wing_objects[wing][i]].instance].ai_index; + if (!query_initial_orders_empty(Ai_info[z].goals)){ // ship also has orders + return 1; + } + } + + return 0; +} + +int query_initial_orders_empty(ai_goal *ai_goals) +{ + int i; + + for (i=0; in_docks <= MAX_DOCKS); + for (i=0; in_docks; i++) + Docking_bay_list[i] = pm->docking_bays[i].name; + + return pm->n_docks; +} + +// DA 1/7/99 These ship names are not variables +int rename_ship(int ship, char *name) +{ + int i; + + Assert(ship >= 0); + Assert(strlen(name) < NAME_LENGTH); + + update_sexp_references(Ships[ship].ship_name, name); + ai_update_goal_references(REF_TYPE_SHIP, Ships[ship].ship_name, name); + for (i=0; i", name); + update_sexp_references(name, new_name); + ai_update_goal_references(type, name, new_name); + for (i=0; i= 0) { + verify_sexp_tree(Ships[i].arrival_cue); + verify_sexp_tree(Ships[i].departure_cue); + if (Ships[i].ai_index < 0) + Assert(0); + if (Ai_info[Ships[i].ai_index].shipnum != i) + Int3(); + } + + return 0; +} + +void correct_marking() +{ + object *ptr; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->flags & OF_MARKED) { + if (ptr->flags & OF_HIDDEN) + unmark_object(OBJ_INDEX(ptr)); + + else switch (ptr->type) { + case OBJ_WAYPOINT: + if (!Show_waypoints) + unmark_object(OBJ_INDEX(ptr)); + break; + + case OBJ_START: + if (!Show_starts) + unmark_object(OBJ_INDEX(ptr)); + break; + + case OBJ_SHIP: + if (!Show_ships) + unmark_object(OBJ_INDEX(ptr)); + + switch (Ships[ptr->instance].team) { + case TEAM_FRIENDLY: + if (!Show_friendly) + unmark_object(OBJ_INDEX(ptr)); + break; + + case TEAM_HOSTILE: + if (!Show_hostile) + unmark_object(OBJ_INDEX(ptr)); + break; + + case TEAM_NEUTRAL: + if (!Show_neutral) + unmark_object(OBJ_INDEX(ptr)); + break; + } + + break; + } + } + + ptr = GET_NEXT(ptr); + } +} + +// Fills a combo box with a list of all docking points of type 'type' on ship 'ship'. +// Item data is the actual docking point index. +void set_valid_dock_points(int ship, int type, CComboBox *box) +{ + int i, z, num, model; + + model = Ships[ship].modelnum; + num = model_get_num_dock_points(model); + for (i=0; iAddString(model_get_dock_name(model, i)); + box->SetItemData(z, i); + } + + Assert(box->GetCount()); +} + +// Given an object index, find the ship index for that object. +int get_ship_from_obj(int obj) +{ + if ((Objects[obj].type == OBJ_SHIP) || (Objects[obj].type == OBJ_START)) + return Objects[obj].instance; + + Int3(); + return 0; +} + +int get_ship_from_obj(object *objp) +{ + if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) + return objp->instance; + + Int3(); + return 0; +} + +void ai_update_goal_references(int type, char *old_name, char *new_name) +{ + int i; + + for (i=0; i= 0) // skip if unused + if (query_referenced_in_ai_goals(Ai_info[i].goals, type, name)) + return Ai_info[i].shipnum | SRC_SHIP_ORDER; + + for (i=0; i= 0) + unmark_object(obj); + + return 1; + } + + if (r == 2) { + delete_flag = 1; + return 2; + } + } + + r = query_referenced_in_ai_goals(type, name); + if (r) { + n = r & SRC_DATA_MASK; + switch (r & SRC_MASK) { + case SRC_SHIP_ORDER: + sprintf(text, "ship \"%s\"", Ships[n].ship_name); + break; + + case SRC_WING_ORDER: + sprintf(text, "wing \"%s\"", Wings[n].name); + break; + + default: // very bad. Someone added an sexp somewhere and didn't change this. + Error(LOCATION, "\"%s\" referenced by an unknown initial orders source! " + "Run for the hills and let Hoffoss know right now!", name); + } + + sprintf(msg, "%s is referenced by the initial orders of %s (possibly \n" + "more initial orders). Do you want to delete it anyway?\n\n" + "(click Cancel to go to the reference)", type_name, text); + + r = orders_reference_handler(r, msg); + if (r == 1) { + if (obj >= 0) + unmark_object(obj); + + return 1; + } + + if (r == 2) { + delete_flag = 1; + return 2; + } + } + + if ((type != REF_TYPE_SHIP) && (type != REF_TYPE_WING)) + return 0; + + for (n=0; nMessageBox(msg, NULL, MB_YESNO | MB_ICONEXCLAMATION); + if (r == IDNO) { + if (obj >= 0) + unmark_object(obj); + + return 1; + } + } + + return 0; +} + +int orders_reference_handler(int code, char *msg) +{ + int r, n; + + r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION); + if (r == IDNO) + return 1; + + if (r == IDYES) + return 0; + + ShipGoalsDlg dlg_goals; + + n = code & SRC_DATA_MASK; + switch (code & SRC_MASK) { + case SRC_SHIP_ORDER: + unmark_all(); + mark_object(Ships[n].objnum); + + dlg_goals.self_ship = n; + dlg_goals.DoModal(); + if (!query_initial_orders_empty(Ai_info[Ships[n].ai_index].goals)) + if ((Ships[n].wingnum >= 0) && (query_initial_orders_conflict(Ships[n].wingnum))) + Fred_main_wnd->MessageBox("This ship's wing also has initial orders", "Possible conflict"); + + break; + + case SRC_WING_ORDER: + unmark_all(); + mark_wing(n); + + dlg_goals.self_wing = n; + dlg_goals.DoModal(); + if (query_initial_orders_conflict(n)) + Fred_main_wnd->MessageBox("One or more ships of this wing also has initial orders", "Possible conflict"); + + break; + + default: // very bad. Someone added an sexp somewhere and didn't change this. + Error(LOCATION, "Unknown initial order reference source"); + } + + delete_flag = 1; + return 2; +} + +int sexp_reference_handler(int node, int code, char *msg) +{ + int r; + + r = Fred_main_wnd->MessageBox(msg, "Warning", MB_YESNOCANCEL | MB_ICONEXCLAMATION); + if (r == IDNO) + return 1; + + if (r == IDYES) + return 0; + + switch (code & SRC_MASK) { + case SRC_SHIP_ARRIVAL: + case SRC_SHIP_DEPARTURE: + if (!Ship_editor_dialog.GetSafeHwnd()) + Ship_editor_dialog.Create(); + + Ship_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Ship_editor_dialog.ShowWindow(SW_RESTORE); + + Ship_editor_dialog.select_sexp_node = node; + unmark_all(); + mark_object(Ships[code & SRC_DATA_MASK].objnum); + break; + + case SRC_WING_ARRIVAL: + case SRC_WING_DEPARTURE: + if (!Wing_editor_dialog.GetSafeHwnd()) + Wing_editor_dialog.Create(); + + Wing_editor_dialog.SetWindowPos(&Fred_main_wnd->wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Wing_editor_dialog.ShowWindow(SW_RESTORE); + + Wing_editor_dialog.select_sexp_node = node; + unmark_all(); + mark_wing(code & SRC_DATA_MASK); + break; + + case SRC_EVENT: + if (Message_editor_dlg) { + Fred_main_wnd->MessageBox("You must close the message editor before the event editor can be opened"); + break; + } + + if (!Event_editor_dlg) { + Event_editor_dlg = new event_editor; + Event_editor_dlg->select_sexp_node = node; + Event_editor_dlg->Create(event_editor::IDD); + } + + Event_editor_dlg->SetWindowPos(&CWnd::wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Event_editor_dlg->ShowWindow(SW_RESTORE); + break; + + case SRC_MISSION_GOAL: { + CMissionGoalsDlg dlg; + + dlg.select_sexp_node = node; + dlg.DoModal(); + break; + } + + case SRC_DEBRIEFING: { + debriefing_editor_dlg dlg; + + dlg.select_sexp_node = node; + dlg.DoModal(); + break; + } + + case SRC_BRIEFING: { + if (!Briefing_dialog) { + Briefing_dialog = new briefing_editor_dlg; + Briefing_dialog->create(); + } + + Briefing_dialog->SetWindowPos(&Briefing_dialog->wndTop, 0, 0, 0, 0, + SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + Briefing_dialog->ShowWindow(SW_RESTORE); + Briefing_dialog->focus_sexp(node); + break; + } + + default: // very bad. Someone added an sexp somewhere and didn't change this. + Error(LOCATION, "Unknown sexp reference source"); + } + + delete_flag = 1; + return 2; +} + +char *object_name(int obj) +{ + static char text[80]; + int i; + + if (!query_valid_object(obj)) + return "*none*"; + + i = Objects[obj].instance; + switch (Objects[obj].type) { + case OBJ_SHIP: + case OBJ_START: + return Ships[i].ship_name; + + case OBJ_WAYPOINT: + sprintf(text, "%s:%d", Waypoint_lists[i / 65536].name, (i & 0xffff) + 1); + return text; + + case OBJ_POINT: + return "Briefing icon"; + } + + return "*unknown*"; +} + +char *get_order_name(int order) +{ + int i; + + if (order == AI_GOAL_NONE) // special case + return "None"; + + for (i=0; itype == OBJ_WAYPOINT) + Waypoint_lists[ptr->instance / 65536].waypoints[ptr->instance & 0xffff] = ptr->pos; + + if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { // do we have a ship? + sh1 = get_ship_from_obj(ptr); + o2 = Ai_info[Ships[sh1].ai_index].dock_objnum; + if (o2 >= 0) { // is it docked with something? + sh2 = get_ship_from_obj(o2); + if (mark || !(Objects[o2].flags & OF_MARKED) || (sh1 < sh2)) + dock_orient_and_approach(&Objects[o2], ptr, DOA_DOCK_STAY); + } + } +} + +// determine if all the ships in a given wing are all marked or not. +int query_whole_wing_marked(int wing) +{ + int count = 0; + object *ptr; + + if (!Wings[wing].wave_count) + return 0; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->flags & OF_MARKED) + if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) + if (Ships[get_ship_from_obj(ptr)].wingnum == wing) + count++; + + ptr = GET_NEXT(ptr); + } + + if (count == Wings[wing].wave_count) + return 1; + + return 0; +} + +void generate_weaponry_usage_list(int *arr, int wing) +{ + int i, j; + ship_weapon *swp; + + if (wing < 0) + return; + + i = Wings[wing].wave_count; + while (i--) { + swp = &Ships[Wings[wing].ship_index[i]].weapons; + j = swp->num_primary_banks; + while (j--) + arr[swp->primary_bank_weapons[j]]++; + + j = swp->num_secondary_banks; + while (j--) + arr[swp->secondary_bank_weapons[j]] += int(ceil(swp->secondary_bank_ammo[j] * swp->secondary_bank_capacity[j] / 100 / Weapon_info[swp->secondary_bank_weapons[j]].cargo_size)); + } +} + +void generate_weaponry_usage_list(int *arr) +{ + int i; + + for (i=0; iResetContent(); + + // add the "special" targets, i.e. any friendly, any hostile, etc. + if ( flags & SHIPS_2_COMBO_SPECIAL ) { + for (i=0; iAddString(Special_arrival_anchor_names[i]); + box->SetItemData(id, SPECIAL_ARRIVAL_ANCHORS_OFFSET + i); + } + } + + // either add all ships to the list, or only add ships with docking bays. + if ( flags & SHIPS_2_COMBO_ALL_SHIPS ) { + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags & OF_MARKED) ) { + id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name); + box->SetItemData(id, get_ship_from_obj(objp)); + } + } + } else if ( flags & SHIPS_2_COMBO_DOCKING_BAY_ONLY ) { + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if ( ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && !(objp->flags & OF_MARKED) ) { + polymodel *pm; + + // determine if this ship has a docking bay + pm = model_get( Ships[objp->instance].modelnum ); + Assert( pm ); + if ( pm->ship_bay && (pm->ship_bay->num_paths > 0) ) { + id = box->AddString(Ships[get_ship_from_obj(objp)].ship_name); + box->SetItemData(id, get_ship_from_obj(objp)); + } + } + } + } +} + +char *reg_read_string( char *section, char *name, char *default_value ) +{ + HKEY hKey = NULL; + DWORD dwType, dwLen; + char keyname[1024]; + static char tmp_string_data[1024]; + LONG lResult; + + strcpy( keyname, section ); + + lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE, // Where it is + keyname, // name of key + NULL, // DWORD reserved + KEY_QUERY_VALUE, // Allows all changes + &hKey ); // Location to store key + + if ( lResult != ERROR_SUCCESS ) { + mprintf(( "Error opening registry key '%s'\n", keyname )); + goto Cleanup; + } + + if ( !name ) { + mprintf(( "No variable name passed\n" )); + goto Cleanup; + } + + dwLen = 1024; + lResult = RegQueryValueEx( hKey, // Handle to key + name, // The values name + NULL, // DWORD reserved + &dwType, // What kind it is + (ubyte *)&tmp_string_data, // value to set + &dwLen ); // How many bytes to set + + if ( lResult != ERROR_SUCCESS ) { + mprintf(( "Error reading registry key '%s'\n", name )); + goto Cleanup; + } + + default_value = tmp_string_data; + +Cleanup: + if ( hKey ) + RegCloseKey(hKey); + + return default_value; +} diff --git a/src/fred2/messageeditordlg.cpp b/src/fred2/messageeditordlg.cpp new file mode 100644 index 0000000..2d09f40 --- /dev/null +++ b/src/fred2/messageeditordlg.cpp @@ -0,0 +1,687 @@ +/* + * $Logfile: /Freespace2/code/FRED2/MessageEditorDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Old message editor dialog box handling code. This was designed a LONG time ago + * and because so much changed, I created a new one from scratch instead. This is + * only around just in case it might be useful. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 26 4/07/98 10:51a Allender + * remove any allied from message senders. Make heads for mission + * specific messages play appropriately + * + * 25 1/07/98 5:58p Hoffoss + * Combined message editor into event editor. + * + * 24 1/06/98 4:19p Hoffoss + * Made message editor accept returns instead of closing dialog. + * + * 23 12/30/97 9:39a Allender + * minor fixups with message editing + * + * 22 11/17/97 4:57p Allender + * added persona support in FreeSpace. A couple of new messages for + * message.tbl which Dan didn't have + * + * 21 10/16/97 8:54p Duncan + * Fixed bug in adding new message. + * + * 20 10/14/97 10:31a Allender + * disable persona field for builtin messages + * + * 19 10/13/97 11:37a Allender + * added personas to message editor in Fred + * + * 18 10/10/97 6:21p Hoffoss + * Put in Fred support for training object list editing. + * + * 17 10/08/97 7:52p Hoffoss + * Made renaming events update all sexp references to it (added a + * generalized function to replace all references to a name by argument + * type). + * + * 16 10/08/97 4:41p Hoffoss + * Changed the way message editor works. Each message is updated + * perminently when you switch messages (as if ok button was pressed). + * + * 15 9/09/97 9:19a Hoffoss + * Fixed bug in Fred: messages shouldn't be send from the player to the + * player. + * + * 14 8/08/97 3:29p Duncan + * Fixed bug with strings exceeding their limits. + * + * 13 8/05/97 5:12p Jasen + * Added advanced_stricmp() function to handle NULL pointers gracefully, + * and utilized it in message editor query update function. + * + * 12 8/01/97 3:47p Hoffoss + * Fixed bug where message editor believes modifications occured when + * nothing actually changed. + * + * 11 7/24/97 4:34p Hoffoss + * Fixed omission bug. + * + * 10 7/16/97 6:30p Hoffoss + * Added icons to sexp trees, mainly because I think they will be required + * for drag n drop. + * + * 9 7/14/97 9:55p Hoffoss + * Making changes to message editor system. + * + * 8 7/10/97 2:32p Hoffoss + * Made message editor dialog box modeless. + * + * 7 7/02/97 5:09p Hoffoss + * Added browse buttons to message editor. + * + * 6 5/20/97 2:28p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 5 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 4 4/01/97 5:15p Hoffoss + * Fixed errors in max length checks, renaming a wing now renames the + * ships in the wing as well, as it should. + * + * 3 3/11/97 2:19p Hoffoss + * New message structure support for Fred. + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "messageeditordlg.h" +#include "freddoc.h" +#include "management.h" +#include "sexp_tree.h" +#include "eventeditor.h" +#include "missionmessage.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +CMessageEditorDlg *Message_editor_dlg = NULL; + +///////////////////////////////////////////////////////////////////////////// +// CMessageEditorDlg dialog + +CMessageEditorDlg::CMessageEditorDlg(CWnd* pParent /*=NULL*/) + : CDialog(CMessageEditorDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CMessageEditorDlg) + m_avi_filename = _T(""); + m_wave_filename = _T(""); + m_message_text = _T(""); + m_message_name = _T(""); + m_cur_msg = -1; + m_priority = -1; + m_sender = -1; + m_persona = -1; + //}}AFX_DATA_INIT + + m_tree.link_modified(&modified); + modified = 0; + m_event_num = -1; +} + +void CMessageEditorDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CMessageEditorDlg) + DDX_Control(pDX, IDC_TREE, m_tree); + DDX_CBString(pDX, IDC_AVI_FILENAME, m_avi_filename); + DDX_CBString(pDX, IDC_WAVE_FILENAME, m_wave_filename); + DDX_Text(pDX, IDC_MESSAGE_TEXT, m_message_text); + DDX_Text(pDX, IDC_NAME, m_message_name); + DDX_LBIndex(pDX, IDC_MESSAGE_LIST, m_cur_msg); + DDX_CBIndex(pDX, IDC_PRIORITY, m_priority); + DDX_CBIndex(pDX, IDC_SENDER, m_sender); + DDX_CBIndex(pDX, IDC_PERSONA_NAME, m_persona); + //}}AFX_DATA_MAP + DDV_MaxChars(pDX, m_message_name, NAME_LENGTH - 1); + DDV_MaxChars(pDX, m_message_text, MESSAGE_LENGTH - 1); + DDV_MaxChars(pDX, m_avi_filename, MAX_FILENAME_LEN - 1); + DDV_MaxChars(pDX, m_wave_filename, MAX_FILENAME_LEN - 1); +} + +BEGIN_MESSAGE_MAP(CMessageEditorDlg, CDialog) + //{{AFX_MSG_MAP(CMessageEditorDlg) + ON_LBN_SELCHANGE(IDC_MESSAGE_LIST, OnSelchangeMessageList) + ON_EN_UPDATE(IDC_NAME, OnUpdateName) + ON_BN_CLICKED(IDC_DELETE, OnDelete) + ON_BN_CLICKED(IDC_NEW, OnNew) + ON_WM_CLOSE() + ON_BN_CLICKED(IDC_BROWSE_AVI, OnBrowseAvi) + ON_BN_CLICKED(IDC_BROWSE_WAVE, OnBrowseWave) + ON_NOTIFY(NM_RCLICK, IDC_TREE, OnRclickTree) + ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE, OnBeginlabeleditTree) + ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE, OnEndlabeleditTree) + ON_BN_CLICKED(ID_OK, OnOk) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMessageEditorDlg message handlers + +BOOL CMessageEditorDlg::OnInitDialog() +{ + int i; + CListBox *list; + CComboBox *box; + + CDialog::OnInitDialog(); + theApp.init_window(&Messages_wnd_data, this); + m_tree.setup(); + + ((CEdit *) GetDlgItem(IDC_NAME))->LimitText(NAME_LENGTH - 1); + ((CEdit *) GetDlgItem(IDC_MESSAGE_TEXT))->LimitText(MESSAGE_LENGTH - 1); + ((CComboBox *) GetDlgItem(IDC_AVI_FILENAME))->LimitText(MAX_FILENAME_LEN - 1); + ((CComboBox *) GetDlgItem(IDC_WAVE_FILENAME))->LimitText(MAX_FILENAME_LEN - 1); + + list = (CListBox *) GetDlgItem(IDC_MESSAGE_LIST); + list->ResetContent(); + for (i=0; iFindStringExact(-1, Messages[i].name) == CB_ERR); + // mwa we should probably not include builtin messages into this list! + list->AddString(Messages[i].name); + } + + box = (CComboBox *) GetDlgItem(IDC_AVI_FILENAME); + for (i=0; iFindStringExact(-1, Messages[i].avi_info.name) == CB_ERR) + box->AddString(Messages[i].avi_info.name); + + box = (CComboBox *) GetDlgItem(IDC_WAVE_FILENAME); + for (i=0; iFindStringExact(i, Messages[i].wave_info.name) == CB_ERR) + box->AddString(Messages[i].wave_info.name); + + // add the persona names into the combo box + box = (CComboBox *)GetDlgItem(IDC_PERSONA_NAME); + box->ResetContent(); + box->AddString(""); + for (i = 0; i < Num_personas; i++ ) + box->AddString( Personas[i].name ); + + box = (CComboBox *) GetDlgItem(IDC_SENDER); + for (i=0; i= 0) && (Objects[Ships[i].objnum].type == OBJ_SHIP)) + box->AddString(Ships[i].ship_name); + + for (i=0; iAddString(Wings[i].name); + + box->AddString(""); + + // MWA 4/7/98 -- removed any allied + //box->AddString(""); + + // set the first message to be the first non-builtin message (if it exists) + if ( Num_messages > Num_builtin_messages ) + m_cur_msg = Num_builtin_messages; + else if (Num_messages) + m_cur_msg = 0; + else + m_cur_msg = -1; + + if (Num_messages >= MAX_MISSION_MESSAGES) + GetDlgItem(IDC_NEW)->EnableWindow(FALSE); + + update_cur_message(); + return TRUE; +} + +int CMessageEditorDlg::query_modified() +{ + char *ptr, buf[MESSAGE_LENGTH]; + int i; + + UpdateData(TRUE); + if (modified) + return 1; + + if (m_cur_msg < 0) + return 0; + + ptr = (char *) (LPCTSTR) m_message_name; + for (i=0; i= 0) + return 1; + + } else { + if (m_event_num < 0) + return 1; + } + + return 0; +} + +void CMessageEditorDlg::OnOK() +{ +} + +void CMessageEditorDlg::OnOk() +{ + update(m_cur_msg); + theApp.record_window_data(&Messages_wnd_data, this); + delete Message_editor_dlg; + Message_editor_dlg = NULL; +} + +void CMessageEditorDlg::OnCancel() +{ + theApp.record_window_data(&Messages_wnd_data, this); + delete Message_editor_dlg; + Message_editor_dlg = NULL; +} + +// load controls with structure data +void CMessageEditorDlg::update_cur_message() +{ + int node, enable = TRUE, enable2 = TRUE; + + if (m_cur_msg < 0) + { + enable = enable2 = FALSE; + m_message_name = _T(""); + m_message_text = _T(""); + m_avi_filename = _T(""); + m_wave_filename = _T(""); + m_tree.clear_tree(); + m_persona = 0; + m_sender = m_priority = -1; + + } else { + m_message_name = Messages[m_cur_msg].name; + m_message_text = Messages[m_cur_msg].message; + if (Messages[m_cur_msg].avi_info.name) + m_avi_filename = _T(Messages[m_cur_msg].avi_info.name); + else + m_avi_filename = _T(""); + + if (Messages[m_cur_msg].wave_info.name) + m_wave_filename = _T(Messages[m_cur_msg].wave_info.name); + else + m_wave_filename = _T(""); + + // add persona id + if ( Messages[m_cur_msg].persona_index != -1 ) + m_persona = Messages[m_cur_msg].persona_index + 1; // add one for the "none" at the beginning of the list + else + m_persona = 0; + + m_event_num = find_event(); + if (m_event_num < 0) { + node = -1; + m_sender = m_priority = 0; + + } else + node = CADR(Mission_events[m_event_num].formula); + + m_tree.load_tree(node, "false"); + } + + if (m_cur_msg < Num_builtin_messages) + enable = FALSE; + + GetDlgItem(IDC_NAME)->EnableWindow(enable); + GetDlgItem(IDC_MESSAGE_TEXT)->EnableWindow(enable); + GetDlgItem(IDC_AVI_FILENAME)->EnableWindow(enable); + GetDlgItem(IDC_WAVE_FILENAME)->EnableWindow(enable); + GetDlgItem(IDC_DELETE)->EnableWindow(enable); + GetDlgItem(IDC_TREE)->EnableWindow(enable2); + GetDlgItem(IDC_SENDER)->EnableWindow(enable2); + GetDlgItem(IDC_PRIORITY)->EnableWindow(enable2); + GetDlgItem(IDC_PERSONA_NAME)->EnableWindow(enable); + UpdateData(FALSE); +} + +int CMessageEditorDlg::find_event() +{ + char *str; + int i, formula, node; + CComboBox *box; + + for (i=0; iFindStringExact(-1, str); + if (m_sender == CB_ERR) + m_sender = 0; + + box = (CComboBox *) GetDlgItem(IDC_PRIORITY); + str = CTEXT(CDR(CDR(node))); + m_priority = box->FindStringExact(-1, str); + if (m_priority == CB_ERR) + m_priority = 0; + + return i; + } + } + } + } + + m_sender = m_priority = 0; + return -1; +} + +void CMessageEditorDlg::OnSelchangeMessageList() +{ + int old = m_cur_msg; + static flag = 0; + + if (flag) + return; + + if (update(m_cur_msg)) { + flag = 1; + ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST)) -> SetCurSel(old); + m_cur_msg = old; + flag = 0; + return; + } + + update_cur_message(); +} + +void CMessageEditorDlg::OnUpdateName() +{ +} + +int CMessageEditorDlg::update(int num) +{ + char *ptr, buf[4096]; + int i, node, fnode; + CListBox *list; + + UpdateData(TRUE); + if (num >= 0) + { + ptr = (char *) (LPCTSTR) m_message_name; + for (i=0; iDeleteString(num); + list->InsertString(num, m_message_name); + } + + string_copy(Messages[num].message, m_message_text, MESSAGE_LENGTH - 1); + if (Messages[num].avi_info.name) + free(Messages[num].avi_info.name); + + ptr = (char *) (LPCTSTR) m_avi_filename; + if (!ptr || !strlen(ptr)) + Messages[num].avi_info.name = NULL; + else + Messages[num].avi_info.name = strdup(ptr); + + if (Messages[num].wave_info.name) + free(Messages[num].wave_info.name); + + ptr = (char *) (LPCTSTR) m_wave_filename; + if (!ptr || !strlen(ptr)) + Messages[num].wave_info.name = NULL; + else + Messages[num].wave_info.name = strdup(ptr); + + // update the persona to the message. We subtract 1 for the "None" at the beginning of the combo + // box list. + Messages[num].persona_index = m_persona - 1; + + if (m_tree.query_false()) { + if (m_event_num >= 0) { // need to delete event + i = m_event_num; + free_sexp2(Mission_events[i].formula); + Assert(i < Num_mission_events); + while (i < Num_mission_events - 1) { + Mission_events[i] = Mission_events[i + 1]; + i++; + } + + Num_mission_events--; + m_event_num = -1; + } + + } else { + if (m_event_num >= 0) + free_sexp2(Mission_events[m_event_num].formula); + + else { + if (Num_mission_events == MAX_MISSION_EVENTS) { + MessageBox("You have reached the limit on mission events.\n" + "Can't add an event to send this message."); + + goto exit; + } + + Assert(Num_mission_events < MAX_MISSION_EVENTS); + m_event_num = Num_mission_events++; + string_copy(Mission_events[m_event_num].name, m_message_name, NAME_LENGTH - 1); + Mission_events[m_event_num].repeat_count = 1; + Mission_events[m_event_num].interval = 1; + Mission_events[m_event_num].score = 0; + Mission_events[m_event_num].chain_delay = -1; + Mission_events[m_event_num].objective_text = NULL; + Mission_events[m_event_num].objective_key_text = NULL; + } + + fnode = m_tree.save_tree(); + ptr = (char *) (LPCTSTR) m_message_name; + node = alloc_sexp(ptr, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1); + ((CComboBox *) GetDlgItem(IDC_PRIORITY))->GetLBText(m_priority, buf); + node = alloc_sexp(buf, SEXP_ATOM, SEXP_ATOM_STRING, -1, node); + ((CComboBox *) GetDlgItem(IDC_SENDER))->GetLBText(m_sender, buf); + node = alloc_sexp(buf, SEXP_ATOM, SEXP_ATOM_STRING, -1, node); + node = alloc_sexp("send-message", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, node); + node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1); + node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, fnode, node); + node = alloc_sexp("when", SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, node); + Mission_events[m_event_num].formula = node; + } + } + +exit: + if (query_modified()) + set_modified(); + + modified = 0; + return 0; +} + +void CMessageEditorDlg::OnDelete() +{ + char buf[256]; + int i; + + Assert((m_cur_msg >= 0) && (m_cur_msg < Num_messages)); + if (Messages[m_cur_msg].avi_info.name) + free(Messages[m_cur_msg].avi_info.name); + if (Messages[m_cur_msg].wave_info.name) + free(Messages[m_cur_msg].wave_info.name); + + ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->DeleteString(m_cur_msg); + sprintf(buf, "<%s>", Messages[m_cur_msg].name); + update_sexp_references(Messages[m_cur_msg].name, buf, OPF_MESSAGE); + + for (i=m_cur_msg; i= Num_messages) + m_cur_msg = Num_messages - 1; + + GetDlgItem(IDC_NEW)->EnableWindow(TRUE); + modified = 1; + update_cur_message(); +} + +void CMessageEditorDlg::OnNew() +{ + if (update(m_cur_msg)) + return; + + Assert(Num_messages < MAX_MISSION_MESSAGES); + strcpy(Messages[Num_messages].name, ""); + ((CListBox *) GetDlgItem(IDC_MESSAGE_LIST))->AddString(""); + + strcpy(Messages[Num_messages].message, ""); + Messages[Num_messages].avi_info.name = NULL; + Messages[Num_messages].wave_info.name = NULL; + Messages[Num_messages].persona_index = -1; + m_cur_msg = Num_messages++; + if (Num_messages >= MAX_MISSION_MESSAGES) + GetDlgItem(IDC_NEW)->EnableWindow(FALSE); + + modified = 1; + update_cur_message(); +} + +void CMessageEditorDlg::OnClose() +{ + int z; + + modified = query_modified(); + if (modified) { + z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL); + if (z == IDCANCEL) + return; + + if (z == IDYES) { + OnOK(); + return; + } + } + + theApp.record_window_data(&Messages_wnd_data, this); + delete Message_editor_dlg; + Message_editor_dlg = NULL; +} + +void CMessageEditorDlg::OnBrowseAvi() +{ + CString name; + + UpdateData(TRUE); + CFileDialog dlg(TRUE, "ani", m_avi_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, + "Ani Files (*.ani)|*.ani|Avi Files (*.avi)|*.avi|Both (*.ani, *.avi)|*.ani;*.avi||"); + + if (dlg.DoModal() == IDOK) + { + m_avi_filename = dlg.GetFileName(); + UpdateData(FALSE); + modified = 1; + } +} + +void CMessageEditorDlg::OnBrowseWave() +{ + CString name; + + UpdateData(TRUE); + CFileDialog dlg(TRUE, "wav", m_wave_filename, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, + "Wave Files (*.wav)|*.wav||"); + + if (dlg.DoModal() == IDOK) + { + m_wave_filename = dlg.GetFileName(); + UpdateData(FALSE); + modified = 1; + } +} + +void CMessageEditorDlg::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + m_tree.right_clicked(); + *pResult = 0; +} + +void CMessageEditorDlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + CEdit *edit; + + if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1) { + *pResult = 0; + modified = 1; + edit = m_tree.GetEditControl(); + Assert(edit); + edit->SetLimitText(NAME_LENGTH - 1); + + } else + *pResult = 1; +} + +void CMessageEditorDlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + *pResult = m_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText); +} diff --git a/src/fred2/missiongoalsdlg.cpp b/src/fred2/missiongoalsdlg.cpp new file mode 100644 index 0000000..3d8734f --- /dev/null +++ b/src/fred2/missiongoalsdlg.cpp @@ -0,0 +1,790 @@ +/* + * $Logfile: /Freespace2/code/fred2/MissionGoalsDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Mission goals editor dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 4 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 3 1/19/99 3:57p Andsager + * Round 2 of variables + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 47 5/23/98 12:20p Sandeep + * + * 46 5/22/98 1:06a Hoffoss + * Made Fred not use OLE. + * + * 45 5/20/98 1:04p Hoffoss + * Made credits screen use new artwork and removed rating field usage from + * Fred (a goal struct member). + * + * 44 3/31/98 12:23a Allender + * changed macro names of campaign types to be more descriptive. Added + * "team" to objectives dialog for team v. team missions. Added two + * distinct multiplayer campaign types + * + * 43 12/08/97 2:03p Hoffoss + * Added Fred support for MGF_NO_MUSIC flag in objectives. + * + * 42 11/11/97 4:13p Duncan + * changed assert to abort operation instead. + * + * 41 10/10/97 6:21p Hoffoss + * Put in Fred support for training object list editing. + * + * 40 10/10/97 2:53p Johnson + * Fixed bug with new items being selected before they are fully + * registered as added. + * + * 39 10/09/97 1:03p Hoffoss + * Renaming events or goals now updates sexp references as well. + * + * 38 9/30/97 12:30p Hoffoss + * Drag and drop reordering of sexp tree roots now does an insert after + * rather than a swap operation. + * + * 37 9/09/97 3:39p Sandeep + * warning level 4 bugs + * + * 36 8/12/97 3:33p Hoffoss + * Fixed the "press cancel to go to reference" code to work properly. + * + * 35 8/12/97 2:39p Johnson + * fixed bug with sexp tree goal name problem. + * + * 34 8/01/97 3:10p Hoffoss + * Made Sexp help hidable. + * + * 33 7/30/97 5:23p Hoffoss + * Removed Sexp tree verification code, since it duplicates normal sexp + * verification, and is just another set of code to keep maintained. + * + * 32 7/25/97 3:05p Allender + * added score field to goals and events editor + * + * 31 7/25/97 2:40p Hoffoss + * Fixed bug in sexp tree selection updating handling. + * + * 30 7/24/97 12:45p Hoffoss + * Added sexp help system to sexp trees and some dialog boxes. + * + * 29 7/17/97 4:10p Hoffoss + * Added drag and drop to sexp trees for reordering root items. + * + * 28 7/16/97 6:30p Hoffoss + * Added icons to sexp trees, mainly because I think they will be required + * for drag n drop. + * + * 27 7/07/97 12:04p Allender + * mission goal validation. + * + * 26 6/02/97 8:47p Hoffoss + * Fixed bug with inserting an operator at root position, but under a + * label. + * + * 25 5/20/97 2:28p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 24 5/01/97 4:12p Hoffoss + * Added return handling to dialogs. + * + * 23 4/25/97 12:50p Allender + * change globals to new naming conventions + * + * 22 4/23/97 11:55a Hoffoss + * Fixed many bugs uncovered while trying to create Mission 6. + * + * 21 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 20 4/11/97 4:22p Hoffoss + * Fixed bug in Sexp trees, moved Show starfield option to view menu and + * removed preferences dialog box. + * + * 19 4/11/97 10:11a Hoffoss + * Name fields supported by Fred for Events and Mission Goals. + * + * 18 4/10/97 3:20p Mike + * Change hull damage to be like shields. + * + * 17 4/01/97 5:15p Hoffoss + * Fixed errors in max length checks, renaming a wing now renames the + * ships in the wing as well, as it should. + * + * 16 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 15 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "freddoc.h" +#include "fredview.h" +#include "linklist.h" +#include "sexp.h" +#include "missiongoalsdlg.h" +#include "management.h" +#include "operatorargtypeselect.h" + +#define ID_ADD_SHIPS 9000 +#define ID_REPLACE_SHIPS 11000 +#define ID_ADD_WINGS 13000 +#define ID_REPLACE_WINGS 15000 + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +CMissionGoalsDlg *Goal_editor_dlg; // global reference needed by sexp_tree class + +///////////////////////////////////////////////////////////////////////////// +// sexp_goal_tree class member functions + +// determine the node number that would be allocated without actually allocating it yet. +int sexp_goal_tree::get_new_node_position() +{ + int i; + + for (i=0; i= MAX_GOALS) + GetDlgItem(IDC_BUTTON_NEW_GOAL)->EnableWindow(FALSE); + + Goal_editor_dlg = this; + i = m_goals_tree.select_sexp_node; + if (i != -1) { + GetDlgItem(IDC_GOALS_TREE) -> SetFocus(); + m_goals_tree.hilite_item(i); + return FALSE; + } + + return TRUE; +} + +void CMissionGoalsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CMissionGoalsDlg) + DDX_Control(pDX, IDC_GOALS_TREE, m_goals_tree); + DDX_Text(pDX, IDC_GOAL_DESC, m_goal_desc); + DDX_CBIndex(pDX, IDC_GOAL_TYPE_DROP, m_goal_type); + DDX_CBIndex(pDX, IDC_DISPLAY_GOAL_TYPES_DROP, m_display_goal_types); + DDX_Text(pDX, IDC_GOAL_NAME, m_name); + DDX_Check(pDX, IDC_GOAL_INVALID, m_goal_invalid); + DDX_Text(pDX, IDC_GOAL_SCORE, m_goal_score); + DDX_Check(pDX, IDC_NO_MUSIC, m_no_music); + DDX_CBIndex(pDX, IDC_OBJ_TEAM, m_team); + //}}AFX_DATA_MAP + DDV_MaxChars(pDX, m_goal_desc, MAX_GOAL_TEXT - 1); + DDV_MaxChars(pDX, m_name, NAME_LENGTH - 1); +} + +BEGIN_MESSAGE_MAP(CMissionGoalsDlg, CDialog) + //{{AFX_MSG_MAP(CMissionGoalsDlg) + ON_CBN_SELCHANGE(IDC_DISPLAY_GOAL_TYPES_DROP, OnSelchangeDisplayGoalTypesDrop) + ON_NOTIFY(TVN_SELCHANGED, IDC_GOALS_TREE, OnSelchangedGoalsTree) + ON_NOTIFY(NM_RCLICK, IDC_GOALS_TREE, OnRclickGoalsTree) + ON_NOTIFY(TVN_ENDLABELEDIT, IDC_GOALS_TREE, OnEndlabeleditGoalsTree) + ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_GOALS_TREE, OnBeginlabeleditGoalsTree) + ON_BN_CLICKED(IDC_BUTTON_NEW_GOAL, OnButtonNewGoal) + ON_EN_CHANGE(IDC_GOAL_DESC, OnChangeGoalDesc) + ON_EN_CHANGE(IDC_GOAL_RATING, OnChangeGoalRating) + ON_CBN_SELCHANGE(IDC_GOAL_TYPE_DROP, OnSelchangeGoalTypeDrop) + ON_EN_CHANGE(IDC_GOAL_NAME, OnChangeGoalName) + ON_BN_CLICKED(ID_OK, OnOk) + ON_WM_CLOSE() + ON_BN_CLICKED(IDC_GOAL_INVALID, OnGoalInvalid) + ON_EN_CHANGE(IDC_GOAL_SCORE, OnChangeGoalScore) + ON_BN_CLICKED(IDC_NO_MUSIC, OnNoMusic) + ON_CBN_SELCHANGE(IDC_OBJ_TEAM, OnSelchangeTeam) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMissionGoalsDlg message handlers + +// Initialization: sets up internal working copy of mission goals and goal trees. +void CMissionGoalsDlg::load_tree() +{ + int i; + + m_goals_tree.select_sexp_node = select_sexp_node; + select_sexp_node = -1; + + m_goals_tree.clear_tree(); + m_num_goals = Num_goals; + for (i=0; i"); + + m_goals[i].formula = m_goals_tree.load_sub_tree(Mission_goals[i].formula); + } + + m_goals_tree.post_load(); + cur_goal = -1; + update_cur_goal(); +} + +// create the CTreeCtrl tree from the goal tree, filtering based on m_display_goal_types +void CMissionGoalsDlg::create_tree() +{ + int i; + HTREEITEM h; + + m_goals_tree.DeleteAllItems(); + m_goals_tree.reset_handles(); + for (i=0; iitemNew.hItem; + if (!h) + return; + + m_goals_tree.update_help(h); + while ((h2 = m_goals_tree.GetParentItem(h)) != 0) + h = h2; + + z = m_goals_tree.GetItemData(h); + for (i=0; i EnableWindow(FALSE); + GetDlgItem(IDC_GOAL_NAME) -> EnableWindow(FALSE); + GetDlgItem(IDC_GOAL_DESC) -> EnableWindow(FALSE); + GetDlgItem(IDC_GOAL_INVALID)->EnableWindow(FALSE); + GetDlgItem(IDC_GOAL_SCORE)->EnableWindow(FALSE); + GetDlgItem(IDC_NO_MUSIC)->EnableWindow(FALSE); + GetDlgItem(IDC_OBJ_TEAM)->EnableWindow(FALSE); + return; + } + + m_name = _T(m_goals[cur_goal].name); + m_goal_desc = _T(m_goals[cur_goal].message); + m_goal_type = m_goals[cur_goal].type & GOAL_TYPE_MASK; + if ( m_goals[cur_goal].type & INVALID_GOAL ){ + m_goal_invalid = 1; + } else { + m_goal_invalid = 0; + } + + if ( m_goals[cur_goal].flags & MGF_NO_MUSIC ){ + m_no_music = 1; + } else { + m_no_music = 0; + } + + m_goal_score = m_goals[cur_goal].score; + + m_team = m_goals[cur_goal].team; + + UpdateData(FALSE); + GetDlgItem(IDC_GOAL_TYPE_DROP) -> EnableWindow(TRUE); + GetDlgItem(IDC_GOAL_NAME) -> EnableWindow(TRUE); + GetDlgItem(IDC_GOAL_DESC) -> EnableWindow(TRUE); +// GetDlgItem(IDC_GOAL_RATING) -> EnableWindow(TRUE); + GetDlgItem(IDC_GOAL_INVALID)->EnableWindow(TRUE); + GetDlgItem(IDC_GOAL_SCORE)->EnableWindow(TRUE); + GetDlgItem(IDC_NO_MUSIC)->EnableWindow(TRUE); + GetDlgItem(IDC_OBJ_TEAM)->EnableWindow(FALSE); + if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ){ + GetDlgItem(IDC_OBJ_TEAM)->EnableWindow(TRUE); + } +} + +// handler for context menu (i.e. a right mouse button click). +void CMissionGoalsDlg::OnRclickGoalsTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + m_goals_tree.right_clicked(MODE_GOALS); + *pResult = 0; +} + +// goal tree item label editing is requested. Determine if it should be allowed. +void CMissionGoalsDlg::OnBeginlabeleditGoalsTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + if (m_goals_tree.edit_label(pTVDispInfo->item.hItem) == 1) { + *pResult = 0; + modified = 1; + } else { + *pResult = 1; + } +} + +// Once we finish editing, we need to clean up, which we do here. +void CMissionGoalsDlg::OnEndlabeleditGoalsTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + *pResult = m_goals_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText); +} + +void CMissionGoalsDlg::OnOK() +{ + HWND h; + CWnd *w; + + w = GetFocus(); + if (w) { + h = w->m_hWnd; + GetDlgItem(IDC_GOALS_TREE)->SetFocus(); + ::SetFocus(h); + } +} + +int CMissionGoalsDlg::query_modified() +{ + int i; + + if (modified) + return 1; + + if (Num_goals != m_num_goals) + return 1; + + for (i=0; i= 0) { + strcpy(names[0][count], Mission_goals[m_sig[i]].name); + strcpy(names[1][count], m_goals[i].name); + count++; + Mission_goals[m_sig[i]].satisfied = 1; + } + + // invalidate all sexp references to deleted events. + for (i=0; i", Mission_goals[i].name); + strcpy(buf + NAME_LENGTH - 2, ">"); // force it to be not too long + strcpy(names[0][count], Mission_goals[i].name); + strcpy(names[1][count], buf); + count++; + } + + Num_goals = m_num_goals; + for (i=0; i= MAX_GOALS){ + GetDlgItem(IDC_BUTTON_NEW_GOAL)->EnableWindow(FALSE); + } + + m_goals_tree.SelectItem(h); +} + +int CMissionGoalsDlg::handler(int code, int node) +{ + int goal; + + switch (code) { + case ROOT_DELETED: + for (goal=0; goalEnableWindow(TRUE); + return node; + + default: + Int3(); + } + + return -1; +} + +void CMissionGoalsDlg::OnChangeGoalDesc() +{ + if (cur_goal < 0){ + return; + } + + UpdateData(TRUE); + string_copy(m_goals[cur_goal].message, m_goal_desc, MAX_GOAL_TEXT); +} + +void CMissionGoalsDlg::OnChangeGoalRating() +{ + if (cur_goal < 0){ + return; + } + + UpdateData(TRUE); +} + +void CMissionGoalsDlg::OnSelchangeGoalTypeDrop() +{ + HTREEITEM h, h2; + int otype; + + if (cur_goal < 0){ + return; + } + + UpdateData(TRUE); + UpdateData(TRUE); // doesn't seem to update unless we do it twice.. + + // change the type being sure to keep the invalid bit if set + otype = m_goals[cur_goal].type; + m_goals[cur_goal].type = m_goal_type; + if ( otype & INVALID_GOAL ){ + m_goals[cur_goal].type |= INVALID_GOAL; + } + + h = m_goals_tree.GetSelectedItem(); + Assert(h); + while ((h2 = m_goals_tree.GetParentItem(h)) != 0){ + h = h2; + } + + m_goals_tree.DeleteItem(h); + cur_goal = -1; + update_cur_goal(); +} + +void CMissionGoalsDlg::OnChangeGoalName() +{ + HTREEITEM h, h2; + + if (cur_goal < 0){ + return; + } + + UpdateData(TRUE); + h = m_goals_tree.GetSelectedItem(); + if (!h){ + return; + } + + while ((h2 = m_goals_tree.GetParentItem(h)) != 0){ + h = h2; + } + + m_goals_tree.SetItemText(h, m_name); + string_copy(m_goals[cur_goal].name, m_name, NAME_LENGTH); +} + +void CMissionGoalsDlg::OnCancel() +{ + theApp.record_window_data(&Messages_wnd_data, this); + CDialog::OnCancel(); +} + +void CMissionGoalsDlg::OnClose() +{ + int z; + + if (query_modified()) { + z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL); + if (z == IDCANCEL) + return; + + if (z == IDYES) { + OnOk(); + return; + } + } + + CDialog::OnClose(); +} + +void CMissionGoalsDlg::insert_handler(int old, int node) +{ + int i; + + for (i=0; i index2 + 1) { + m_goals[index1] = m_goals[index1 - 1]; + m_sig[index1] = m_sig[index1 - 1]; + index1--; + } + + m_goals[index1] = m; +} + +void CMissionGoalsDlg::OnChangeGoalScore() +{ + if (cur_goal < 0){ + return; + } + + UpdateData(TRUE); + m_goals[cur_goal].score = m_goal_score; +} + + +// code when the "team" selection in the combo box changes +void CMissionGoalsDlg::OnSelchangeTeam() +{ + if ( cur_goal < 0 ){ + return; + } + + UpdateData(TRUE); + m_goals[cur_goal].team = m_team; +} diff --git a/src/fred2/missionnotesdlg.cpp b/src/fred2/missionnotesdlg.cpp new file mode 100644 index 0000000..8e5803e --- /dev/null +++ b/src/fred2/missionnotesdlg.cpp @@ -0,0 +1,486 @@ +/* + * $Logfile: /Freespace2/code/Fred2/MissionNotesDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Mission notes editor dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 6 8/23/99 6:21p Jefff + * added "no traitor" option to missions (and fred) + * + * 5 8/23/99 5:04p Jefff + * Added new mission flag to disable built-in messages from playing. + * Added fred support as well. + * + * 4 3/24/99 4:05p Dave + * Put in support for assigning the player to a specific squadron with a + * specific logo. Preliminary work for doing pos/orient checksumming in + * multiplayer to reduce bandwidth. + * + * 3 2/23/99 2:32p Dave + * First run of oldschool dogfight mode. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 23 6/17/98 2:47p Hoffoss + * Changed so missions are single, multi or training. Not supporting + * combos of the above anymore (in Fred). + * + * 22 5/25/98 12:16p Hoffoss + * Removed dogfight option from dialog. No longer supported. + * + * 21 5/18/98 1:56a Allender + * respawn limit to 999 max + * + * 20 5/05/98 11:05p Allender + * ability to flag mission as "no promotion" where promotions and badges + * are *not* granted even if they should be. Slight fix to multiplayer + * problem where locking_subsys is wrong for players current target + * + * 19 4/03/98 12:17a Allender + * new sexpression to detect departed or destroyed. optionally disallow + * support ships. Allow docking with escape pods + * + * 18 3/26/98 5:24p Allender + * put in respawn edit box into mission notes dialog. Made loading of + * missions/campaign happen when first entering the game setup screen. + * + * 17 3/18/98 3:17p Allender + * fix scramble checkbox + * + * 16 3/18/98 10:27a Allender + * fixed bug with missions always being tagged a single player regardless + * of checkbox setting + * + * 15 3/16/98 8:27p Allender + * Fred support for two new AI flags -- kamikaze and no dynamic goals. + * + * 14 3/09/98 4:30p Allender + * multiplayer secondary weapon changes. red-alert and cargo-known-delay + * sexpressions. Add time cargo revealed to ship structure + * + * 13 2/09/98 9:25p Allender + * team v team support. multiple pools and breifings + * + * 12 2/04/98 4:32p Allender + * support for multiple briefings and debriefings. Changes to mission + * type (now a bitfield). Bitfield defs for multiplayer modes + * + * 11 1/02/98 4:55p Hoffoss + * Added support for Mission_all_attack flag to Fred and loading/saving + * code. + * + * 10 9/30/97 5:56p Hoffoss + * Added music selection combo boxes to Fred. + * + * 9 8/11/97 3:19p Hoffoss + * Implemented mission description. + * + * 8 6/11/97 2:14p Hoffoss + * Added game type (mission type) selection to Fred. + * + * 7 5/20/97 2:28p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 6 5/06/97 2:43p Hoffoss + * Fixed bug in Mission notes dialog, where window wasn't being destroyed. + * + * 5 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 4 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 3 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "freddoc.h" +#include "missionnotesdlg.h" +#include "management.h" +#include "eventmusic.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define NO_SQUAD "" + +// module globals to hold button information +CButton *coop, *team, *dogfight; + +///////////////////////////////////////////////////////////////////////////// +// CMissionNotesDlg dialog + +CMissionNotesDlg::CMissionNotesDlg(CWnd* pParent /*=NULL*/) : CDialog(CMissionNotesDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CMissionNotesDlg) + m_created = _T(""); + m_modified = _T(""); + m_mission_notes = _T(""); + m_designer_name = _T(""); + m_mission_title = _T(""); + m_mission_desc = _T(""); + m_squad_filename = _T(""); + m_squad_name = _T(NO_SQUAD); + m_music = -1; + m_full_war = FALSE; + m_red_alert = FALSE; + m_scramble = FALSE; + m_num_respawns = 0; + m_disallow_support = FALSE; + m_no_promotion = FALSE; + m_no_builtin_msgs = FALSE; + m_no_traitor = FALSE; + //}}AFX_DATA_INIT +} + +void CMissionNotesDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CMissionNotesDlg) + DDX_Control(pDX, IDC_RESPAWN_SPIN, m_respawn_spin); + DDX_Text(pDX, IDC_CREATED, m_created); + DDX_Text(pDX, IDC_MODIFIED, m_modified); + DDX_Text(pDX, IDC_MISSION_NOTES, m_mission_notes); + DDX_Text(pDX, IDC_DESIGNER_NAME, m_designer_name); + DDX_Text(pDX, IDC_MISSION_TITLE, m_mission_title); + DDX_Text(pDX, IDC_MISSION_DESC, m_mission_desc); + DDX_Text(pDX, IDC_SQUAD_LOGO, m_squad_filename); + DDX_Text(pDX, IDC_SQUAD_NAME, m_squad_name); + DDX_CBIndex(pDX, IDC_MUSIC, m_music); + DDX_Check(pDX, IDC_FULL_WAR, m_full_war); + DDX_Check(pDX, IDC_RED_ALERT, m_red_alert); + DDX_Check(pDX, IDC_SCRAMBLE, m_scramble); + DDX_Text(pDX, IDC_RESPAWNS, m_num_respawns); + DDV_MinMaxUInt(pDX, m_num_respawns, 0, 999); + DDX_Check(pDX, IDC_SUPPORT_ALLOWED, m_disallow_support); + DDX_Check(pDX, IDC_NO_PROMOTION, m_no_promotion); + DDX_Check(pDX, IDC_DISABLE_BUILTIN_MSGS, m_no_builtin_msgs); + DDX_Check(pDX, IDC_NO_TRAITOR, m_no_traitor); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CMissionNotesDlg, CDialog) + //{{AFX_MSG_MAP(CMissionNotesDlg) + ON_WM_CLOSE() + ON_BN_CLICKED(IDC_TRAINING, OnTraining) + ON_BN_CLICKED(IDC_MULTI, OnMulti) + ON_BN_CLICKED(IDC_SINGLE, OnSingle) + ON_BN_CLICKED(IDC_SQUAD_LOGO_BUTTON, OnSquadLogo) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMissionNotesDlg message handlers + +int CMissionNotesDlg::query_modified() +{ + if (m_mission_title != m_mission_title_orig){ + return 1; + } + if (m_designer_name != m_designer_name_orig){ + return 1; + } + if (m_mission_notes != m_mission_notes_orig){ + return 1; + } + if (m_mission_desc != m_mission_desc_orig){ + return 1; + } + if (Current_soundtrack_num != m_music - 1){ + return 1; + } + if (Mission_all_attack != m_full_war){ + return 1; + } + + return 0; +} + +void CMissionNotesDlg::OnOK() +{ + int new_m_type, flags, is_multi = 0, is_training = 0, is_single = 0; + + UpdateData(); + is_single = (((CButton *) GetDlgItem(IDC_SINGLE))->GetCheck() == 1); + is_multi = (((CButton *) GetDlgItem(IDC_MULTI))->GetCheck() == 1); + is_training = (((CButton *) GetDlgItem(IDC_TRAINING))->GetCheck() == 1); + + // deal with changing the mission type. Code is done this way since training missions + // just override anything else. + new_m_type = 0; + if (is_training) { + new_m_type = MISSION_TYPE_TRAINING; + } else { + if (is_single){ + new_m_type = MISSION_TYPE_SINGLE; + } + + if (is_multi) { + new_m_type |= MISSION_TYPE_MULTI; + if (coop->GetCheck()){ + new_m_type |= MISSION_TYPE_MULTI_COOP; + } else if (team->GetCheck()){ + new_m_type |= MISSION_TYPE_MULTI_TEAMS; + } else if(dogfight->GetCheck()){ + new_m_type |= MISSION_TYPE_MULTI_DOGFIGHT; + } else { + Int3(); // get allender -- multiplayer mode not set!!! + } + } + } + + if (!new_m_type) { + MessageBox("You must select the game type: training, single, or multiplayer", "Error"); + return; + } + + MODIFY(The_mission.game_type, new_m_type ); + MODIFY(The_mission.red_alert, m_red_alert ); + MODIFY(The_mission.scramble, m_scramble ); + MODIFY(The_mission.num_respawns, (int)m_num_respawns ); + MODIFY(The_mission.disallow_support, (int)m_disallow_support); + + // set the flags for no promotion + flags = The_mission.flags; + if ( m_no_promotion ) { + The_mission.flags |= MISSION_FLAG_NO_PROMOTION; + } else { + The_mission.flags &= ~MISSION_FLAG_NO_PROMOTION; + } + + // set flags for no builtin messages + if ( m_no_builtin_msgs ) { + The_mission.flags |= MISSION_FLAG_NO_BUILTIN_MSGS; + } else { + The_mission.flags &= ~MISSION_FLAG_NO_BUILTIN_MSGS; + } + + // set no traitor flags + if ( m_no_traitor ) { + The_mission.flags |= MISSION_FLAG_NO_TRAITOR; + } else { + The_mission.flags &= ~MISSION_FLAG_NO_TRAITOR; + } + + if ( flags != The_mission.flags ){ + set_modified(); + } + + string_copy(The_mission.name, m_mission_title, NAME_LENGTH, 1); + string_copy(The_mission.author, m_designer_name, NAME_LENGTH, 1); + deconvert_multiline_string(The_mission.notes, m_mission_notes, NOTES_LENGTH); + deconvert_multiline_string(The_mission.mission_desc, m_mission_desc, MISSION_DESC_LENGTH); + + // copy squad stuff + if(m_squad_name == CString(NO_SQUAD)){ + strcpy(The_mission.squad_name, ""); + strcpy(The_mission.squad_filename, ""); + } else { + string_copy(The_mission.squad_name, m_squad_name, NAME_LENGTH); + string_copy(The_mission.squad_filename, m_squad_filename, MAX_FILENAME_LEN); + } + + MODIFY(Current_soundtrack_num, m_music - 1); + MODIFY(Mission_all_attack, m_full_war); + if (query_modified()){ + set_modified(); + } + + theApp.record_window_data(&Mission_notes_wnd_data, this); + + // update the Num_teams variable accoriding to mission types + Num_teams = 1; + if ( (The_mission.game_type & MISSION_TYPE_MULTI) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) ){ + Num_teams = 2; + } + + CDialog::OnOK(); +} + +void CMissionNotesDlg::OnCancel() +{ + theApp.record_window_data(&Mission_notes_wnd_data, this); + CDialog::OnCancel(); +} + +BOOL CMissionNotesDlg::OnInitDialog() +{ + int i; + CComboBox *box; + + // set up the radio box states + coop = (CButton *)GetDlgItem(IDC_COOP); + team = (CButton *)GetDlgItem(IDC_TEAMVTEAM); + dogfight = (CButton *)GetDlgItem(IDC_DOGFIGHT); + + m_mission_title_orig = m_mission_title = _T(The_mission.name); + m_designer_name_orig = m_designer_name = _T(The_mission.author); + m_created = _T(The_mission.created); + m_modified = _T(The_mission.modified); + m_mission_notes_orig = m_mission_notes = convert_multiline_string(The_mission.notes); + m_mission_desc_orig = m_mission_desc = convert_multiline_string(The_mission.mission_desc); + m_red_alert = The_mission.red_alert; + m_scramble = The_mission.scramble; + m_disallow_support = The_mission.disallow_support; + m_no_promotion = (The_mission.flags & MISSION_FLAG_NO_PROMOTION) ? 1 : 0; + m_no_builtin_msgs = (The_mission.flags & MISSION_FLAG_NO_BUILTIN_MSGS) ? 1 : 0; + m_no_traitor = (The_mission.flags & MISSION_FLAG_NO_TRAITOR) ? 1 : 0; + CDialog::OnInitDialog(); + + box = (CComboBox *) GetDlgItem(IDC_MUSIC); + box->AddString("None"); + for (i=0; iAddString(Soundtracks[i].name); + } + + // squad info + if(strlen(The_mission.squad_name) > 0){ + m_squad_name = _T(The_mission.squad_name); + m_squad_filename = _T(The_mission.squad_filename); + } else { + m_squad_name = _T(NO_SQUAD); + m_squad_filename = _T(""); + } + + m_type = The_mission.game_type; + m_music = Current_soundtrack_num + 1; + m_full_war = Mission_all_attack; + + // set up the game type checkboxes accoring to m_type + if ( m_type & MISSION_TYPE_SINGLE ){ + ((CButton *) GetDlgItem(IDC_SINGLE))->SetCheck(1); + } + + // for multiplayer -- be sure to assign a default type if not already assigned. + if ( m_type & MISSION_TYPE_MULTI ){ + ((CButton *) GetDlgItem(IDC_MULTI))->SetCheck(1); + } + + if ( m_type & MISSION_TYPE_TRAINING ){ + ((CButton *) GetDlgItem(IDC_TRAINING))->SetCheck(1); + } + + // we need to set one of these three multiplayer modes so interface looks correct + if ( !(m_type & (MISSION_TYPE_MULTI_COOP | MISSION_TYPE_MULTI_DOGFIGHT | MISSION_TYPE_MULTI_TEAMS)) ){ + m_type |= MISSION_TYPE_MULTI_COOP; + } + + if ( m_type & MISSION_TYPE_MULTI_COOP ){ + coop->SetCheck(1); + } else if ( m_type & MISSION_TYPE_MULTI_TEAMS ){ + team->SetCheck(1); + } else if ( m_type & MISSION_TYPE_MULTI_DOGFIGHT ){ + dogfight->SetCheck(1); + } else { + Int3(); // get allender -- multiplayer mode not set!!! + } + + m_respawn_spin.SetRange(0, 99); + m_num_respawns = The_mission.num_respawns; + + set_types(); + UpdateData(FALSE); + theApp.init_window(&Mission_notes_wnd_data, this); + return TRUE; +} + +void CMissionNotesDlg::OnClose() +{ + int z; + + if (query_modified()) { + z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL); + if (z == IDCANCEL){ + return; + } + + if (z == IDYES) { + OnOK(); + return; + } + } + + CDialog::OnClose(); +} + +// when training button is set, we need to disable all other buttons +void CMissionNotesDlg::OnTraining() +{ + UpdateData(TRUE); + set_types(); +} + +void CMissionNotesDlg::OnMulti() +{ + UpdateData(TRUE); + set_types(); +} + +void CMissionNotesDlg::OnSingle() +{ + UpdateData(TRUE); + set_types(); +} + +void CMissionNotesDlg::set_types() +{ + int enable = 0; + + // when training is checked, no other type is active + if (((CButton *) GetDlgItem(IDC_MULTI))->GetCheck() == 1){ + enable = 1; + } + + coop->EnableWindow(enable); + team->EnableWindow(enable); + dogfight->EnableWindow(enable); + GetDlgItem(IDC_RESPAWNS)->EnableWindow(enable); + GetDlgItem(IDC_RESPAWN_SPIN)->EnableWindow(enable); +} + +void CMissionNotesDlg::OnSquadLogo() +{ + CString pcx_filename; + int z; + + // get list of squad images + z = cfile_push_chdir(CF_TYPE_SQUAD_IMAGES_MAIN); + CFileDialog dlg(TRUE, "pcx", pcx_filename, OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, "Pcx Files (*.pcx)|*.pcx"); + + // if we have a result + if (dlg.DoModal() == IDOK) { + m_squad_filename = dlg.GetFileName(); + } else { + m_squad_filename = _T(""); + } + UpdateData(FALSE); + + // restore directory + if (!z){ + cfile_pop_dir(); + } +} \ No newline at end of file diff --git a/src/fred2/missionsave.cpp b/src/fred2/missionsave.cpp new file mode 100644 index 0000000..32f365d --- /dev/null +++ b/src/fred2/missionsave.cpp @@ -0,0 +1,2742 @@ +/* + * $Logfile: /Freespace2/code/Fred2/MissionSave.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Mission saving in Fred. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 33 8/28/99 7:29p Dave + * Fixed wingmen persona messaging. Make sure locked turrets don't count + * towards the # attacking a player. + * + * 32 8/27/99 12:04a Dave + * Campaign loop screen. + * + * 31 8/26/99 8:52p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 30 8/17/99 5:20p Andsager + * Control campaign editor bug. + * + * 29 8/16/99 3:53p Andsager + * Add special warp in interface in Fred and saving / reading. + * + * 28 7/02/99 4:30p Dave + * Much more sophisticated lightning support. + * + * 27 6/10/99 11:06a Andsager + * Mission designed selection of asteroid types. + * + * 26 6/03/99 6:37p Dave + * More TNT fun. Made perspective bitmaps more flexible. + * + * 25 5/20/99 6:59p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 24 5/09/99 6:00p Dave + * Lots of cool new effects. E3 build tweaks. + * + * 23 4/26/99 8:47p Dave + * Made all pof related nebula stuff customizable through Fred. + * + * 22 4/26/99 12:49p Andsager + * Add protect object from beam support to Fred + * + * 21 4/16/99 2:34p Andsager + * Second pass on debris fields + * + * 20 4/15/99 5:00p Andsager + * Frist pass on Debris field + * + * 19 4/07/99 6:21p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 18 3/31/99 9:50a Andsager + * Interface for generalization of asteroid field (debris field) + * + * 17 3/24/99 4:05p Dave + * Put in support for assigning the player to a specific squadron with a + * specific logo. Preliminary work for doing pos/orient checksumming in + * multiplayer to reduce bandwidth. + * + * 16 3/01/99 7:39p Dave + * Added prioritizing ship respawns. Also fixed respawns in TvT so teams + * don't mix respawn points. + * + * 15 2/26/99 6:01p Andsager + * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay + * + * 14 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 13 2/11/99 2:15p Andsager + * Add ship explosion modification to FRED + * + * 12 2/07/99 8:51p Andsager + * Add inner bound to asteroid field. Inner bound tries to stay astroid + * free. Wrap when within and don't throw at ships inside. + * + * 11 2/03/99 12:42p Andsager + * Add escort priority. Modify ship_flags_dlg to include field. Save and + * Load. Add escort priority field to ship. + * + * 10 1/25/99 5:03a Dave + * First run of stealth, AWACS and TAG missile support. New mission type + * :) + * + * 9 1/19/99 3:57p Andsager + * Round 2 of variables + * + * 8 1/07/99 1:52p Andsager + * Initial check in of Sexp_variables + * + * 7 12/17/98 2:43p Andsager + * Modify fred campaign save file to include optional mission loops + * + * 6 11/14/98 5:37p Dave + * Put in support for full nebulas. + * + * 5 11/05/98 4:18p Dave + * Modified mission file format. + * + * 4 10/29/98 9:22p Dave + * Removed minor bug concering externalization of campaign files. + * + * 3 10/29/98 6:49p Dave + * Finished up Fred support for externalizing mission and campaign files. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:00p Dave + * + * 207 9/16/98 10:42a Hoffoss + * Added sexp node counting to fsm files for end user designers. + * + * 206 9/15/98 7:24p Dave + * Minor UI changes. Localized bunch of new text. + * + * 205 9/15/98 11:44a Dave + * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring + * scale factors. Fixed standalone filtering of MD missions to non-MD + * hosts. + * + * 204 9/10/98 1:17p Dave + * Put in code to flag missions and campaigns as being MD or not in Fred + * and Freespace. Put in multiplayer support for filtering out MD + * missions. Put in multiplayer popups for warning of non-valid missions. + * + * 203 5/21/98 12:58a Hoffoss + * Fixed warnings optimized build turned up. + * + * 202 5/20/98 1:04p Hoffoss + * Made credits screen use new artwork and removed rating field usage from + * Fred (a goal struct member). + * + * 201 5/19/98 1:19p Allender + * new low level reliable socket reading code. Make all missions/campaign + * load/save to data missions folder (i.e. we are rid of the player + * missions folder) + * + * 200 5/13/98 5:13p Allender + * red alert support to go back to previous mission + * + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "freespace.h" +#include "missionsave.h" +#include "missiongoals.h" +#include "missionmessage.h" +#include "missionparse.h" +#include "fredrender.h" +#include "aigoals.h" +#include "starfield.h" +#include "lighting.h" +#include "linklist.h" +#include "weapon.h" +#include "missioncampaign.h" +#include "campaigntreewnd.h" +#include "campaigntreeview.h" +#include "campaigneditordlg.h" +#include "sexp.h" +#include "missionbriefcommon.h" +#include "management.h" +#include "eventmusic.h" +#include "nebula.h" +#include "asteroid.h" +#include "missioncmdbrief.h" +#include "jumpnode.h" +#include "mainfrm.h" +#include "fhash.h" +#include "neb.h" +#include "osapi.h" + +int CFred_mission_save::save_mission_file(char *pathname) +{ + char backup_name[256], savepath[MAX_PATH_LEN], *p; + CTime t; + + t = CTime::GetCurrentTime(); + strcpy(The_mission.modified, t.Format("%x at %X")); + + strcpy(savepath, ""); + p = strrchr(pathname, '\\'); + if ( p ) { + *p = '\0'; + strcpy(savepath, pathname); + *p = '\\'; + strcat(savepath, "\\"); + } + strcat(savepath, "saving.xxx"); + + reset_parse(); + fred_parse_flag = 0; + fp = cfopen(savepath, "wt", CFILE_NORMAL); + if (!fp) { + nprintf(("Error", "Can't open mission file to save.\n")); + return -1; + } + + if (save_mission_info()) + err = -2; + else if (save_plot_info()) + err = -3; + else if (save_variables()) + err = -3; +// else if (save_briefing_info()) +// err = -4; + else if (save_cmd_briefs()) + err = -4; + else if (save_briefing()) + err = -4; + else if (save_debriefing()) + err = -5; + else if (save_players()) + err = -6; + else if (save_objects()) + err = -7; + else if (save_wings()) + err = -8; + else if (save_events()) + err = -9; + else if (save_goals()) + err = -10; + else if (save_waypoints()) + err = -11; + else if (save_messages()) + err = -12; + else if (save_reinforcements()) + err = -13; + else if (save_bitmaps()) + err = -14; + else if (save_asteroid_fields()) + err = -15; + else if (save_music()) + err = -16; + else { + required_string_fred("#End"); + parse_comments(2); + token_found = NULL; + parse_comments(); + fout("\n"); + } + + cfclose(fp); + if (err) { + mprintf(("Mission saving error code #%d", err)); + + } else { + strcpy(backup_name, pathname); + if (backup_name[strlen(backup_name) - 4] == '.') + backup_name[strlen(backup_name) - 4] = 0; + + strcat(backup_name, ".bak"); + cf_attrib(pathname, 0, FILE_ATTRIBUTE_READONLY, CF_TYPE_MISSIONS); + cf_delete(backup_name, CF_TYPE_MISSIONS); + cf_rename(pathname, backup_name, CF_TYPE_MISSIONS); + cf_rename(savepath, pathname, CF_TYPE_MISSIONS); + } + + return err; +} + +int CFred_mission_save::autosave_mission_file(char *pathname) +{ + char backup_name[256], name2[256]; + int i, len; + CTime t; + + t = CTime::GetCurrentTime(); + strcpy(The_mission.modified, t.Format("%x at %X")); + + len = strlen(pathname); + strcpy(backup_name, pathname); + strcpy(name2, pathname); + sprintf(backup_name + len, ".%.3d", BACKUP_DEPTH); + cf_delete(backup_name, CF_TYPE_MISSIONS); + for (i=BACKUP_DEPTH; i>1; i--) { + sprintf(backup_name + len, ".%.3d", i - 1); + sprintf(name2 + len, ".%.3d", i); + cf_rename(backup_name, name2, CF_TYPE_MISSIONS); + } + + strcpy(backup_name + len, ".001"); + reset_parse(); + fred_parse_flag = 0; + fp = cfopen(backup_name, "wt", CFILE_NORMAL, CF_TYPE_MISSIONS); + if (!fp) { + nprintf(("Error", "Can't open mission file to save.\n")); + return -1; + } + + if (save_mission_info()) + err = -2; + else if (save_plot_info()) + err = -3; + else if (save_variables()) + err = -3; +// else if (save_briefing_info()) +// err = -4; + else if (save_cmd_briefs()) + err = -4; + else if (save_briefing()) + err = -4; + else if (save_debriefing()) + err = -5; + else if (save_players()) + err = -6; + else if (save_objects()) + err = -7; + else if (save_wings()) + err = -8; + else if (save_events()) + err = -9; + else if (save_goals()) + err = -10; + else if (save_waypoints()) + err = -11; + else if (save_messages()) + err = -12; + else if (save_reinforcements()) + err = -13; + else if (save_bitmaps()) + err = -14; + else if (save_asteroid_fields()) + err = -15; + else if (save_music()) + err = -16; + else { + required_string_fred("#End"); + parse_comments(2); + token_found = NULL; + parse_comments(); + fout("\n"); + } + + cfclose(fp); + if (err) + mprintf(("Mission saving error code #%d", err)); + + return err; +} + +int CFred_mission_save::save_mission_info() +{ + // int f = count_free_sexp_nodes(); + // fout("// Of %d total sexp nodes, %d free, %d used\n\n",MAX_SEXP_NODES, f, MAX_SEXP_NODES - f); + + required_string_fred("#Mission Info"); + parse_comments(0); + + required_string_fred("$Version:"); + parse_comments(2); + fout(" %.2f", FRED_MISSION_VERSION); + + // XSTR + required_string_fred("$Name:"); + parse_comments(); + fout(" "); + fout_ext("%s", The_mission.name); + + required_string_fred("$Author:"); + parse_comments(); + fout(" %s", The_mission.author); + + required_string_fred("$Created:"); + parse_comments(); + fout(" %s", The_mission.created); + + required_string_fred("$Modified:"); + parse_comments(); + fout(" %s", The_mission.modified); + + required_string_fred("$Notes:"); + parse_comments(); + fout("\n%s", The_mission.notes); + + required_string_fred("$End Notes:"); + parse_comments(0); + + // XSTR + required_string_fred("$Mission Desc:"); + parse_comments(2); + fout("\n"); + fout_ext("%s", The_mission.mission_desc); + fout("\n"); + + required_string_fred("$end_multi_text"); + parse_comments(0); + +#if 0 + if (optional_string_fred("+Game Type:")) + parse_comments(2); + else + fout("\n\n+Game Type:"); + fout("\n%s", Game_types[The_mission.game_type]); +#endif + + if ( optional_string_fred("+Game Type Flags:")){ + parse_comments(2); + } else { + fout("\n+Game Type Flags:"); + } + + fout(" %d", The_mission.game_type); + + if (optional_string_fred("+Flags:")){ + parse_comments(2); + } else { + fout("\n+Flags:"); + } + + fout(" %d", The_mission.flags); + + // maybe write out Nebula intensity + if(The_mission.flags & MISSION_FLAG_FULLNEB){ + Assert(Neb2_awacs > 0.0f); + fout("\n+NebAwacs: %f\n", Neb2_awacs); + + // storm name + fout("\n+Storm: %s\n", Mission_parse_storm_name); + } + + // For multiplayer missions -- write out the number of player starts and number of respawns + if (The_mission.game_type & MISSION_TYPE_MULTI) { + if (optional_string_fred("+Num Players:")) + parse_comments(2); + else + fout("\n+Num Players:"); + + fout(" %d", Player_starts); + + if (optional_string_fred("+Num Respawns:")) + parse_comments(2); + else + fout("\n+Num Respawns:"); + + fout(" %d", The_mission.num_respawns); + } + + if ( optional_string_fred("+Red Alert:")){ + parse_comments(2); + } else { + fout("\n+Red Alert:"); + } + fout(" %d", The_mission.red_alert ); + + if ( optional_string_fred("+Scramble:")){ + parse_comments(2); + } else { + fout("\n+Scramble:"); + } + fout(" %d", The_mission.scramble); + + if ( optional_string_fred("+Disallow Support:")){ + parse_comments(2); + } else { + fout("\n+Disallow Support:"); + } + + fout(" %d", The_mission.disallow_support); + + if (Mission_all_attack) { + if (optional_string_fred("+All Teams Attack")){ + parse_comments(); + } else { + fout("\n+All Teams Attack"); + } + } + + if (Entry_delay_time) { + if (optional_string_fred("+Player Entry Delay:")) + parse_comments(2); + else + fout("\n\n+Player Entry Delay:"); + + fout("\n%f", f2fl(Entry_delay_time)); + } + + if (optional_string_fred("+Viewer pos:")){ + parse_comments(2); + } else { + fout("\n\n+Viewer pos:"); + } + + save_vector(view_pos); + + if (optional_string_fred("+Viewer orient:")){ + parse_comments(); + } else { + fout("\n+Viewer orient:"); + } + + save_matrix(view_orient); + + // squadron info + if(!(The_mission.game_type & MISSION_TYPE_MULTI) && (strlen(The_mission.squad_name) > 0)){ + // squad name + fout("\n+SquadReassignName: %s", The_mission.squad_name); + + // maybe squad logo + if(strlen(The_mission.squad_filename) > 0){ + fout("\n+SquadReassignLogo: %s", The_mission.squad_filename); + } + } + + return err; +} + +int CFred_mission_save::save_plot_info() +{ + required_string_fred("#Plot Info"); + parse_comments(2); + + // XSTR + required_string_fred("$Tour:"); + parse_comments(2); + fout(" "); + fout_ext("%s", The_mission.tour_name); + + required_string_fred("$Pre-Briefing Cutscene:"); + parse_comments(); + fout(" %s", The_mission.pre_briefing_cutscene); + + required_string_fred("$Pre-Mission Cutscene:"); + parse_comments(); + fout(" %s", The_mission.pre_mission_cutscene); + + required_string_fred("$Next Mission Success:"); + parse_comments(); + fout(" %s", The_mission.next_mission_success); + + required_string_fred("$Next Mission Partial:"); + parse_comments(); + fout(" %s", The_mission.next_mission_partial); + + required_string_fred("$Next Mission Failure:"); + parse_comments(); + fout(" %s", The_mission.next_mission_failure); + + return err; +} + +int CFred_mission_save::save_cmd_brief() +{ + int stage; + + stage = 0; + required_string_fred("#Command Briefing"); + parse_comments(2); + + if (The_mission.game_type & MISSION_TYPE_MULTI) + return err; // no command briefings allowed in multiplayer missions. + + for (stage=0; stagenum_stages; stage++) { + required_string_fred("$Stage Text:"); + parse_comments(2); + + // XSTR + fout("\n"); + fout_ext("%s", Cur_cmd_brief->stage[stage].text); + + required_string_fred("$end_multi_text", "$Stage Text:"); + parse_comments(); + + required_string_fred("$Ani Filename:"); + parse_comments(); + fout(" %s", Cur_cmd_brief->stage[stage].ani_filename); + + required_string_fred("+Wave Filename:", "$Ani Filename:"); + parse_comments(); + fout(" %s", Cur_cmd_brief->stage[stage].wave_filename); + } + + return err; +} + +int CFred_mission_save::save_cmd_briefs() +{ + int i; + + for (i=0; inew_text); + fout_ext(out); + + required_string_fred("$end_multi_text", "$start_stage"); + parse_comments(); + + if (!drop_white_space(bs->voice)[0]){ + strcpy(bs->voice, "None"); + } + + required_string_fred("$voice:"); + parse_comments(); + fout(" %s", bs->voice); + + required_string_fred("$camera_pos:"); + parse_comments(); + save_vector(bs->camera_pos); + + required_string_fred("$camera_orient:"); + parse_comments(); + save_matrix(bs->camera_orient); + + required_string_fred("$camera_time:"); + parse_comments(); + fout(" %d", bs->camera_time); + + required_string_fred("$num_lines:"); + parse_comments(); + fout(" %d", bs->num_lines); + + for (k=0; knum_lines; k++) { + required_string_fred("$line_start:"); + parse_comments(); + fout(" %d", bs->lines[k].start_icon); + + required_string_fred("$line_end:"); + parse_comments(); + fout(" %d", bs->lines[k].end_icon); + } + + required_string_fred("$num_icons:"); + parse_comments(); + Assert(bs->num_icons <= MAX_STAGE_ICONS ); + fout(" %d", bs->num_icons); + + required_string_fred("$Flags:"); + parse_comments(); + fout(" %d", bs->flags); + + required_string_fred("$Formula:"); + parse_comments(); + convert_sexp_to_string(bs->formula, out, SEXP_SAVE_MODE); + fout(" %s", out); + + for ( j = 0; j < bs->num_icons; j++ ) { + bi = &bs->icons[j]; + + required_string_fred("$start_icon"); + parse_comments(); + + required_string_fred("$type:"); + parse_comments(); + fout(" %d", bi->type); + + required_string_fred("$team:"); + parse_comments(); + fout(" %s", Team_names[bitmask_2_bitnum(bi->team)]); + + required_string_fred("$class:"); + parse_comments(); + if (bi->ship_class < 0) + bi->ship_class = 0; + + fout(" %s", Ship_info[bi->ship_class].name); + + required_string_fred("$pos:"); + parse_comments(); + save_vector(bi->pos); + + if (drop_white_space(bi->label)[0]) { + if (optional_string_fred("$label:")) + parse_comments(); + else + fout("\n$label:"); + + fout(" %s", bi->label); + } + + if (optional_string_fred("+id:")) + parse_comments(); + else + fout("\n+id:"); + + fout(" %d", bi->id); + + required_string_fred("$hlight:"); + parse_comments(); + fout(" %d", (bi->flags & BI_HIGHLIGHT)?1:0 ); + + required_string_fred("$multi_text"); + parse_comments(); + +// sprintf(out,"\n%s", bi->text); +// fout(out); + + required_string_fred("$end_multi_text"); + parse_comments(); + + required_string_fred("$end_icon"); + parse_comments(); + } + + required_string_fred("$end_stage"); + parse_comments(); + } + required_string_fred("$end_briefing"); + parse_comments(); + } + + return err; +} + +int CFred_mission_save::save_debriefing() +{ + int j, i; + char out[8192]; + + for ( j = 0; j < Num_teams; j++ ) { + + Debriefing = &Debriefings[j]; + + required_string_fred("#Debriefing_info"); + parse_comments(2); + + required_string_fred("$Num stages:"); + parse_comments(2); + fout(" %d", Debriefing->num_stages); + + for (i=0; inum_stages; i++) { + required_string_fred("$Formula:"); + parse_comments(2); + convert_sexp_to_string(Debriefing->stages[i].formula, out, SEXP_SAVE_MODE); + fout(" %s", out); + + // XSTR + required_string_fred("$Multi text"); + parse_comments(); + fout("\n "); + fout_ext("%s", Debriefing->stages[i].new_text); + + required_string_fred("$end_multi_text"); + parse_comments(); + + if (!drop_white_space(Debriefing->stages[i].voice)[0]){ + strcpy(Debriefing->stages[i].voice, "None"); + } + + required_string_fred("$Voice:"); + parse_comments(); + fout(" %s", Debriefing->stages[i].voice); + + // XSTR + required_string_fred("$Recommendation text:"); + parse_comments(); + fout("\n "); + fout_ext("%s", Debriefing->stages[i].new_recommendation_text); + + required_string_fred("$end_multi_text"); + parse_comments(); + } + } + + return err; +} + +int sexp_variable_block_count(); +// save variables +int CFred_mission_save::save_variables() +{ + char *type; + char number[] = "number"; + char string[] = "string"; + char block[] = "block"; + int i; + + // sort sexp_variables + sexp_variable_sort(); + + // get count + int num_variables = sexp_variable_count(); + int num_block_vars = sexp_variable_block_count(); + int total_variables = num_variables + num_block_vars; + + if (total_variables > 0) { + + // write 'em out + required_string_fred("#Sexp_variables"); + parse_comments(2); + + required_string_fred("$Variables:"); + parse_comments(2); + + fout ("\n("); +// parse_comments(); + + for (i=0; i 0){ + fout("\n\n#Alternate Types:\n"); + + // write them all out + for(i=0; i= 0); + fout(" %s", Ships[Player_start_shipnum].ship_name); + + required_string_fred("$Ship Choices:"); + parse_comments(); + fout(" (\n"); + + for (j=0; j 0){ + fout("\t\"%s\"\t%d\n", Weapon_info[j].name, Team_data[i].weaponry_pool[j] + used_pool[j]); + } + } + + fout(")"); + } + + return err; +} + +int CFred_mission_save::save_objects() +{ + char out[4096]; + int i, j, z; + ai_info *aip; + object *objp; + ship_info *sip; + + required_string_fred("#Objects"); + parse_comments(2); + fout("\t\t;! %d total\n", ship_get_num_ships() ); + + for (i=z=0; i= 0); + aip = &Ai_info[Ships[i].ai_index]; + + required_string_fred("$AI Behavior:"); + parse_comments(); + fout(" %s", Ai_behavior_names[aip->behavior]); + + if (Ships[i].weapons.ai_class != Ship_info[Ships[i].ship_info_index].ai_class) { + if (optional_string_fred("+AI Class:", "$Name:")) + parse_comments(); + else + fout("\n+AI Class:"); + + fout(" %s", Ai_class_names[Ships[i].weapons.ai_class]); + } + + save_ai_goals(Ai_info[Ships[i].ai_index].goals, i); + + // XSTR + required_string_fred("$Cargo 1:"); + parse_comments(); + fout(" "); + fout_ext("%s", Cargo_names[Ships[i].cargo1]); + + save_common_object_data(&Objects[Ships[i].objnum], &Ships[i]); + + if (Ships[i].wingnum >= 0){ + Ships[i].arrival_location = ARRIVE_AT_LOCATION; + } + + required_string_fred("$Arrival Location:"); + parse_comments(); + fout(" %s", Arrival_location_names[Ships[i].arrival_location]); + + if (Ships[i].arrival_location != ARRIVE_AT_LOCATION) { + if (optional_string_fred("+Arrival Distance:", "$Name:")){ + parse_comments(); + } else { + fout("\n+Arrival Distance:"); + } + + fout(" %d", Ships[i].arrival_distance); + if (optional_string_fred("$Arrival Anchor:", "$Name:")){ + parse_comments(); + } else { + fout("\n$Arrival Anchor:"); + } + + z = Ships[i].arrival_anchor; + if (z >= SPECIAL_ARRIVAL_ANCHORS_OFFSET){ + fout(" %s", Special_arrival_anchor_names[z - SPECIAL_ARRIVAL_ANCHORS_OFFSET]); + } else if (z >= 0) { + fout(" %s", Ships[z].ship_name); + } else { + fout(" "); + } + } + + if (Ships[i].arrival_delay) { + if (optional_string_fred("+Arrival Delay:", "$Name:")){ + parse_comments(); + } else { + fout("\n+Arrival Delay:"); + } + + fout(" %d", Ships[i].arrival_delay); + } + + required_string_fred("$Arrival Cue:"); + parse_comments(); + convert_sexp_to_string(Ships[i].arrival_cue, out, SEXP_SAVE_MODE); + fout(" %s", out); + + required_string_fred("$Departure Location:"); + parse_comments(); + fout(" %s", Departure_location_names[Ships[i].departure_location]); + + + if ( Ships[i].departure_location == DEPART_AT_DOCK_BAY ) { + required_string_fred("$Departure Anchor:"); + parse_comments(); + + if ( Ships[i].departure_anchor >= 0 ){ + fout(" %s", Ships[Ships[i].departure_anchor].ship_name ); + } else { + fout(" "); + } + } + + if (Ships[i].departure_delay) { + if (optional_string_fred("+Departure delay:", "$Name:")){ + parse_comments(); + } else { + fout("\n+Departure delay:"); + } + + fout(" %d", Ships[i].departure_delay); + } + + required_string_fred("$Departure Cue:"); + parse_comments(); + convert_sexp_to_string(Ships[i].departure_cue, out, SEXP_SAVE_MODE); + fout(" %s", out); + + required_string_fred("$Determination:"); + parse_comments(); + fout(" %d", Ships[i].determination); + + if (optional_string_fred("+Flags:", "$Name:")) { + parse_comments(); + fout (" ("); + } else + fout("\n+Flags: ("); + + if (Ships[i].flags & SF_CARGO_REVEALED) + fout(" \"cargo-known\""); + if (Ships[i].flags & SF_IGNORE_COUNT) + fout(" \"ignore-count\""); + if (objp->flags & OF_PROTECTED) + fout(" \"protect-ship\""); + if (objp->flags & OF_BEAM_PROTECTED) + fout(" \"beam-protect-ship\""); + if (Ships[i].flags & SF_REINFORCEMENT) + fout(" \"reinforcement\""); + if (objp->flags & OF_NO_SHIELDS) + fout(" \"no-shields\""); + if (Ships[i].flags & SF_ESCORT) + fout(" \"escort\""); + if (objp->type == OBJ_START) + fout(" \"player-start\""); + if (Ships[i].flags & SF_NO_ARRIVAL_MUSIC) + fout(" \"no-arrival-music\""); + if (Ships[i].flags & SF_NO_ARRIVAL_WARP) + fout(" \"no-arrival-warp\""); + if (Ships[i].flags & SF_NO_DEPARTURE_WARP) + fout(" \"no-departure-warp\""); + if (Ships[i].flags & SF_LOCKED) + fout(" \"locked\""); + if (Ships[i].flags & SF_INVULNERABLE) + fout(" \"invulnerable\""); + if (Ships[i].flags & SF_HIDDEN_FROM_SENSORS) + fout(" \"hidden-from-sensors\""); + if ( Ships[i].flags & SF_SCANNABLE ) + fout(" \"scannable\""); + if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_KAMIKAZE ) + fout(" \"kamikaze\""); + if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_NO_DYNAMIC ) + fout(" \"no-dynamic\""); + if ( Ships[i].flags & SF_RED_ALERT_STORE_STATUS ) + fout(" \"red-alert-carry\""); + if ( objp->flags & OF_SPECIAL_WARP ) + fout(" \"special-warp\""); + fout(" )"); + + fout("+Respawn priority: %d\n", Ships[i].respawn_priority); + + if (Ships[i].flags & SF_ESCORT) { + if (optional_string_fred("+Escort priority:", "$Name:")) { + parse_comments(); + } else { + fout("\n+Escort priority:"); + } + + fout(" %d", Ships[i].escort_priority); + } + + if (Ships[i].special_exp_index != -1) { + if (optional_string_fred("+Special Exp index:", "$Name:")) { + parse_comments(); + } else { + fout("+Special Exp index:"); + } + + fout(" %d", Ships[i].special_exp_index); + } + + + if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_KAMIKAZE ) { + if ( optional_string_fred("+Kamikaze Damage:", "$Name:")){ + parse_comments(); + } else { + fout("\n+Kamikaze Damage:"); + } + + fout(" %d", (int)(Ai_info[Ships[i].ai_index].kamikaze_damage) ); + } + + if (Ships[i].hotkey != -1) { + if (optional_string_fred("+Hotkey:", "$Name:")){ + parse_comments(); + } else { + fout("\n+Hotkey:"); + } + + fout(" %d", Ships[i].hotkey); + } + + // mwa -- new code to save off information about initially docked ships. + if ( Ships[i].flags & SF_INITIALLY_DOCKED ) { + Assert ( aip->dock_objnum != -1 ); // ge allender if you hit this + + if (optional_string_fred("+Docked With:", "$Name:")) + parse_comments(); + else + fout("\n+Docked With:"); + + switch (Objects[aip->dock_objnum].type) { + case OBJ_SHIP: + case OBJ_START: + j = Objects[aip->dock_objnum].instance; + break; + + default: + Int3(); + } + + fout(" %s", Ships[j].ship_name); + + required_string_fred("$Docker Point:", "$Name:"); + parse_comments(); + fout(" %s", model_get_dock_name(Ships[j].modelnum, aip->dockee_index)); + + required_string_fred("$Dockee Point:", "$Name:"); + parse_comments(); + fout(" %s", model_get_dock_name(Ships[i].modelnum, aip->dock_index)); + + } + + // check the ship flag about killing off the ship before a missino starts. Write out the appropriate + // variable if necessary + if ( Ships[i].flags & SF_KILL_BEFORE_MISSION ) { + if ( optional_string_fred("+Destroy At:", "$Name:")) + parse_comments(); + else + fout ("\n+Destroy At: "); + + fout(" %d", Ships[i].final_death_time ); + } + + // possibly write out the orders that this ship will accept. We'll only do it if the orders + // are not the default set of orders + if ( Ships[i].orders_accepted != ship_get_default_orders_accepted( &Ship_info[Ships[i].ship_info_index]) ) { + if ( optional_string_fred("+Orders Accepted:", "$Name:") ) + parse_comments(); + else + fout("\n+Orders Accepted:"); + + fout(" %d\t\t;! note that this is a bitfield!!!", Ships[i].orders_accepted); + } + + if (Ships[i].group >= 0) { + if (optional_string_fred("+Group:", "$Name:")) + parse_comments(); + else + fout("\n+Group:"); + + fout(" %d", Ships[i].group); + } + + if (Ships[i].score) { + if (optional_string_fred("+Score:", "$Name:")) + parse_comments(); + else + fout("\n+Score:"); + + fout(" %d", Ships[i].score); + } + + // deal with the persona for this ship as well. + if ( Ships[i].persona_index != -1 ) { + if (optional_string_fred("+Persona Index:", "$Name:")) + parse_comments(); + else + fout("\n+Persona Index:"); + + fout(" %d", Ships[i].persona_index); + } + + z++; + } + + return err; +} + +int CFred_mission_save::save_common_object_data(object *objp, ship *shipp) +{ + int j, z; + ship_subsys *ptr; + ship_info *sip; + ship_weapon *wp; + + sip = &Ship_info[shipp->ship_info_index]; + if ((int) objp->phys_info.speed) { + if (optional_string_fred("+Initial Velocity:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+Initial Velocity:"); + + fout(" %d", (int) objp->phys_info.speed); + } + + if ((int) objp->hull_strength != (int) sip->initial_hull_strength) { + if (optional_string_fred("+Initial Hull:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+Initial Hull:"); + + fout(" %d", (int) objp->hull_strength); + } + + if ((int) get_shield_strength(objp) != 100) { + if (optional_string_fred("+Initial Shields:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+Initial Shields:"); + + fout(" %d", (int) objp->shields[0]); + } + + // save normal ship weapons info + required_string_fred("+Subsystem:", "$Name:"); + parse_comments(); + fout(" Pilot"); + + wp = &shipp->weapons; + z = 0; + j = wp->num_primary_banks; + while (j--) + if (wp->primary_bank_weapons[j] != sip->primary_bank_weapons[j]) + z = 1; + + if (z) { + if (optional_string_fred("+Primary Banks:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+Primary Banks:"); + + fout(" ( "); + for (j=0; jnum_primary_banks; j++) + fout("\"%s\" ", Weapon_info[wp->primary_bank_weapons[j]].name); + + fout(")"); + } + + z = 0; + j = wp->num_secondary_banks; + while (j--) + if (wp->secondary_bank_weapons[j] != sip->secondary_bank_weapons[j]) + z = 1; + + if (z) { + if (optional_string_fred("+Secondary Banks:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+Secondary Banks:"); + + fout(" ( "); + for (j=0; jnum_secondary_banks; j++) + fout("\"%s\" ", Weapon_info[wp->secondary_bank_weapons[j]].name); + + fout(")"); + } + + z = 0; + j = wp->num_secondary_banks; + while (j--) + if (wp->secondary_bank_ammo[j] != 100) + z = 1; + + if (z) { + if (optional_string_fred("+Sbank Ammo:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+Sbank Ammo:"); + + fout(" ( "); + for (j=0; jnum_secondary_banks; j++) + fout("%d ", wp->secondary_bank_ammo[j]); + + fout(")"); + } + + ptr = GET_FIRST(&shipp->subsys_list); + while (ptr != END_OF_LIST(&shipp->subsys_list)) { + if ( (ptr->current_hits) || (ptr->system_info->type == SUBSYSTEM_TURRET) || (ptr->subsys_cargo_name != -1)) { + if (optional_string_fred("+Subsystem:", "$Name:")) + parse_comments(); + else + fout("\n+Subsystem:"); + + fout(" %s", ptr->system_info->subobj_name); + } + + if (ptr->current_hits) { + if (optional_string_fred("$Damage:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n$Damage:"); + + fout(" %d", (int) ptr->current_hits); + } + + if (ptr->subsys_cargo_name != -1) { + if (optional_string_fred("+Cargo Name:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+Cargo Name:"); + + fout_ext("%s", Cargo_names[ptr->subsys_cargo_name]); + } + + if (ptr->system_info->type == SUBSYSTEM_TURRET) + save_turret_info(ptr, shipp - Ships); + + ptr = GET_NEXT(ptr); + } + +/* for (j=0; jstatus_count; j++) { + required_string_fred("$Status Description:"); + parse_comments(-1); + fout(" %s", Status_desc_names[shipp->status_type[j]]); + + required_string_fred("$Status:"); + parse_comments(-1); + fout(" %s", Status_type_names[shipp->status[j]]); + + required_string_fred("$Target:"); + parse_comments(-1); + fout(" %s", Status_target_names[shipp->target[j]]); + }*/ + + return err; +} + +int CFred_mission_save::save_wings() +{ + char out[4096]; + int i, j, z, ship, count = 0; + + fred_parse_flag = 0; + required_string_fred("#Wings"); + parse_comments(2); + fout("\t\t;! %d total", num_wings); + + for (i=0; i= SPECIAL_ARRIVAL_ANCHORS_OFFSET) + fout(" %s", Special_arrival_anchor_names[z - SPECIAL_ARRIVAL_ANCHORS_OFFSET]); + else if (z >= 0) + fout(" %s", Ships[z].ship_name); + else + fout(" "); + } + + if (Wings[i].arrival_delay) { + if (optional_string_fred("+Arrival delay:", "$Name:")) + parse_comments(); + else + fout("\n+Arrival delay:"); + + fout(" %d", Wings[i].arrival_delay); + } + + required_string_fred("$Arrival Cue:"); + parse_comments(); + convert_sexp_to_string(Wings[i].arrival_cue, out, SEXP_SAVE_MODE); + fout(" %s", out); + + required_string_fred("$Departure Location:"); + parse_comments(); + fout(" %s", Departure_location_names[Wings[i].departure_location]); + + if ( Wings[i].departure_location == DEPART_AT_DOCK_BAY ) { + required_string_fred("$Departure Anchor:"); + parse_comments(); + + if ( Wings[i].departure_anchor >= 0 ) + fout(" %s", Ships[Wings[i].departure_anchor].ship_name ); + else + fout(" "); + } + + if (Wings[i].departure_delay) { + if (optional_string_fred("+Departure delay:", "$Name:")) + parse_comments(); + else + fout("\n+Departure delay:"); + + fout(" %d", Wings[i].departure_delay); + } + + required_string_fred("$Departure Cue:"); + parse_comments(); + convert_sexp_to_string(Wings[i].departure_cue, out, SEXP_SAVE_MODE); + fout(" %s", out); + + required_string_fred("$Ships:"); + parse_comments(); + fout(" (\t\t;! %d total\n", Wings[i].wave_count); + + for (j=0; jtype == OBJ_JUMP_NODE) && (ptr->instance == i)) + break; + + ptr = GET_NEXT(ptr); + } + + Assert(ptr != END_OF_LIST(&obj_used_list)); + + required_string_fred("$Jump Node:", "$Jump Node Name:"); + parse_comments(2); + save_vector(ptr->pos); + + required_string_fred("$Jump Node Name:", "$Jump Node:"); + parse_comments(); + fout(" %s", Jump_nodes[i].name); + } + + for (i=0; i= 2)){ + fout(" %d", -1); + } else { + fout(" %d", Messages[i].multi_team); + } + + // XSTR + required_string_fred("$MessageNew:"); + parse_comments(); + fout(" "); + fout_ext("%s", Messages[i].message); + fout("\n"); + required_string_fred("$end_multi_text"); + parse_comments(0); + + if ( Messages[i].persona_index != -1 ) { + if ( optional_string_fred("+Persona:", "$Name:")) + parse_comments(); + else + fout("\n+Persona:"); + + fout(" %s", Personas[Messages[i].persona_index].name ); + } + + if (Messages[i].avi_info.name) { + if (optional_string_fred("+AVI Name:", "$Name:")) + parse_comments(); + else + fout("\n+AVI Name:"); + + fout(" %s", Messages[i].avi_info.name); + } + + if (Messages[i].wave_info.name) { + if (optional_string_fred("+Wave Name:", "$Name:")) + parse_comments(); + else + fout("\n+Wave Name:"); + + fout(" %s", Messages[i].wave_info.name); + } + } + + return err; +} + +int CFred_mission_save::save_vector(vector &v) +{ + fout(" %f, %f, %f", v.x, v.y, v.z); + return 0; +} + +int CFred_mission_save::save_matrix(matrix &m) +{ + fout("\n\t%f, %f, %f,\n", m.rvec.x, m.rvec.y, m.rvec.z); + fout("\t%f, %f, %f,\n", m.uvec.x, m.uvec.y, m.uvec.z); + fout("\t%f, %f, %f", m.fvec.x, m.fvec.y, m.fvec.z); + return 0; +} + +// saves comments from previous campaign/mission file +void CFred_mission_save::parse_comments(int newlines) +{ + char *comment_start = NULL; + int state = 0, same_line = 0, first_comment = 1, tab = 0, flag = 0; + + if (newlines < 0) { + newlines = -newlines; + tab = 1; + } + + if (newlines) + same_line = 1; + + if (fred_parse_flag || !Token_found_flag || !token_found || (token_found && (*Mission_text_raw == EOF_CHAR))) { + while (newlines-- > 0) + fout("\n"); + + if (tab) + fout("\t"); + + if (token_found) + fout("%s", token_found); + + return; + } + + while (*raw_ptr != EOF_CHAR) { + if (!state) { + if (token_found && (*raw_ptr == *token_found)) + if (!strnicmp(raw_ptr, token_found, strlen(token_found))) { + same_line = newlines - 1 + same_line; + while (same_line-- > 0) + fout("\n"); + + if (tab) + fout("\t"); + + fout("%s", token_found); + return; + } + + if ((*raw_ptr == '/') && (raw_ptr[1] == '*')) { + comment_start = raw_ptr; + state = 1; + } + + if ((*raw_ptr == ';') && (raw_ptr[1] != '!')) { + comment_start = raw_ptr; + state = 2; + } + + if ((*raw_ptr == '/') && (raw_ptr[1] == '/')) { + comment_start = raw_ptr; + state = 2; + } + + if (*raw_ptr == '\n') + flag = 1; + + if (state && flag) + fout("\n"); + + } else { + if ((*raw_ptr == '\n') && (state == 2)) { + if (first_comment && !flag) + fout("\t\t"); + + *raw_ptr = 0; + fout("%s\n", comment_start); + *raw_ptr = '\n'; + state = first_comment = same_line = flag = 0; + } + + if ((*raw_ptr == '*') && (raw_ptr[1] == '/') && (state == 1)) { + if (first_comment && !flag) + fout("\t\t"); + + state = raw_ptr[2]; + raw_ptr[2] = 0; + fout("%s", comment_start); + raw_ptr[2] = (char)state; + state = first_comment = flag = 0; + } + } + + raw_ptr++; + } + + return; +} + +int CFred_mission_save::fout(char *format, ...) +{ + char str[16384]; + va_list args; + + if (err){ + return err; + } + + va_start(args, format); + vsprintf(str, format, args); + va_end(args); + Assert(strlen(str) < 16384); + + cfputs(str, fp); + return 0; +} + +int CFred_mission_save::fout_ext(char *format, ...) +{ + char str[16384]; + char str_out[16384] = ""; + va_list args; + int str_id; + + if (err){ + return err; + } + + va_start(args, format); + vsprintf(str, format, args); + va_end(args); + Assert(strlen(str) < 16384); + + // lookup the string in the hash table + str_id = fhash_string_exists(str); + // doesn't exist, so assign it an ID of -1 and stick it in the table + if(str_id <= -2){ + sprintf(str_out, " XSTR(\"%s\", -1)", str); + + // add the string to the table + fhash_add_str(str, -1); + } + // _does_ exist, so just write it out as it is + else { + sprintf(str_out, " XSTR(\"%s\", %d)", str, str_id); + } + + cfputs(str_out, fp); + return 0; +} + +void CFred_mission_save::save_ai_goals(ai_goals *goalp, int ship) +{ + char *str = NULL, buf[80]; + int i, valid, flag = 1; + + for (i=0; i= 0 ) { + if ( optional_string_fred("+Chained:", "$Formula:")){ + parse_comments(); + } else { + fout("\n+Chained:"); + } + + fout(" %d", Mission_events[i].chain_delay); + } + + //XSTR + if (Mission_events[i].objective_text) { + if (optional_string_fred("+Objective:", "$Formula:")){ + parse_comments(); + } else { + fout("\n+Objective:"); + } + + fout(" "); + fout_ext("%s", Mission_events[i].objective_text); + } + + //XSTR + if (Mission_events[i].objective_key_text) { + if (optional_string_fred("+Objective key:", "$Formula:")){ + parse_comments(); + } else { + fout("\n+Objective key:"); + } + + fout(" "); + fout_ext("%s", Mission_events[i].objective_key_text); + } + + // save team + if (Mission_events[i].team >= 0){ + if (optional_string_fred("+Team:")){ + parse_comments(); + } else { + fout("\n+Team:"); + } + fout(" "); + fout("%d", Mission_events[i].team); + } + } + + return err; +} + +int CFred_mission_save::save_reinforcements() +{ + int i, j, type; + + fred_parse_flag = 0; + required_string_fred("#Reinforcements"); + parse_comments(2); + fout("\t\t;! %d total\n", Num_reinforcements); + + for (i=0; i= 0) { + if (optional_string_fred("+Nebula:")){ + parse_comments(); + } else { + fout("\n+Nebula:"); + } + fout(" %s", Nebula_filenames[Nebula_index]); + + required_string_fred("+Color:"); + parse_comments(); + fout(" %s", Nebula_colors[Mission_palette]); + + required_string_fred("+Pitch:"); + parse_comments(); + fout(" %d", Nebula_pitch); + + required_string_fred("+Bank:"); + parse_comments(); + fout(" %d", Nebula_bank); + + required_string_fred("+Heading:"); + parse_comments(); + fout(" %d", Nebula_heading); + } + } + + // save suns by sun bitmap filename + for(idx=0; idxweapons; + + if (wp->ai_class != Ship_info[Ships[ship].ship_info_index].ai_class) { + if (optional_string_fred("+AI Class:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+AI Class:"); + + fout(" %s", Ai_class_names[wp->ai_class]); + } + + z = 0; + i = wp->num_primary_banks; + while (i--) + if (wp->primary_bank_weapons[i] != ptr->system_info->primary_banks[i]) + z = 1; + + if (z) { + if (optional_string_fred("+Primary Banks:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+Primary Banks:"); + + fout(" ( "); + for (i=0; inum_primary_banks; i++) + fout("\"%s\" ", Weapon_info[wp->primary_bank_weapons[i]].name); + + fout(")"); + } + + z = 0; + i = wp->num_secondary_banks; + while (i--) + if (wp->secondary_bank_weapons[i] != ptr->system_info->secondary_banks[i]) + z = 1; + + if (z) { + if (optional_string_fred("+Secondary Banks:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+Secondary Banks:"); + + fout(" ( "); + for (i=0; inum_secondary_banks; i++) + fout("\"%s\" ", Weapon_info[wp->secondary_bank_weapons[i]].name); + + fout(")"); + } + + z = 0; + i = wp->num_secondary_banks; + while (i--) + if (wp->secondary_bank_ammo[i] != 100) + z = 1; + + if (z) { + if (optional_string_fred("+Sbank Ammo:", "$Name:", "+Subsystem:")) + parse_comments(); + else + fout("\n+Sbank Ammo:"); + + fout(" ( "); + for (i=0; inum_secondary_banks; i++) + fout("%d ", wp->secondary_bank_ammo[i]); + + fout(")"); + } +} + +int CFred_mission_save::save_campaign_file(char *pathname) +{ + int i, j, m, flag; + + Campaign_tree_formp->save_tree(); // flush all changes so they get saved. + Campaign_tree_viewp->sort_elements(); + reset_parse(); + fred_parse_flag = 0; + + pathname = cf_add_ext(pathname, FS_CAMPAIGN_FILE_EXT); + fp = cfopen(pathname, "wt", CFILE_NORMAL, CF_TYPE_MISSIONS); + if (!fp) { + nprintf(("Error", "Can't open campaign file to save.\n")); + return -1; + } + + required_string_fred("$Name:"); + parse_comments(0); + fout(" %s", Campaign.name); + + Assert((Campaign.type >= 0) && (Campaign.type < MAX_CAMPAIGN_TYPES)); + required_string_fred("$Type:"); + parse_comments(); + fout(" %s", campaign_types[Campaign.type]); + + // XSTR + if (Campaign.desc) { + required_string_fred("+Description:"); + parse_comments(); + fout("\n"); + fout_ext("%s", Campaign.desc); + fout("\n$end_multi_text"); + } + + if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) { + required_string_fred("+Num Players:"); + parse_comments(); + fout(" %d", Campaign.num_players); + } + + // write out the ships and weapons which the player can start the campaign with + optional_string_fred("+Starting Ships: ("); + parse_comments(2); + for (i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Campaign.ships_allowed[i] ) + fout(" \"%s\" ", Ship_info[i].name ); + } + fout( ")\n" ); + + optional_string_fred("+Starting Weapons: ("); + parse_comments(); + for (i = 0; i < MAX_WEAPON_TYPES; i++ ) { + if ( Campaign.weapons_allowed[i] ) + fout(" \"%s\" ", Weapon_info[i].name ); + } + fout( ")\n" ); + + fred_parse_flag = 0; + for (i=0; i 1) { + char buffer[1024]; + sprintf(buffer, "Multiple branching loop error from mission %s\nEdit campaign for *at most* 1 loop from each mission.", Campaign.missions[m].name); + MessageBox((HWND)os_get_window(), buffer, "Error", MB_OK); + } + } + + if (optional_string_fred("+Level:", "$Mission:")){ + parse_comments(); + } else { + fout("\n\n+Level:"); + } + + fout(" %d", Campaign.missions[m].level); + + if (optional_string_fred("+Position:", "$Mission:")){ + parse_comments(); + } else { + fout("\n+Position:"); + } + + fout(" %d", Campaign.missions[m].pos); + } + + required_string_fred("#End"); + parse_comments(2); + token_found = NULL; + parse_comments(); + fout("\n"); + + cfclose(fp); + if (err) + mprintf(("Campaign saving error code #%d", err)); + else + Campaign_wnd->error_checker(); + + return err; +} + +void CFred_mission_save::save_campaign_sexp(int node, int link_num) +{ + char out[4096]; + + Sexp_string = out; + *out = 0; + Assert(node >= 0); + + // if the link num is -1, then this is a end-of-campaign location + if ( link_num != -1 ) { + if (build_sexp_string(node, 2, SEXP_SAVE_MODE)) + fout(" (\n %s\n ( next-mission \"%s\" )\n )\n", out, Campaign.missions[link_num].name); + else + fout(" ( %s( next-mission \"%s\" ) )\n", out, Campaign.missions[link_num].name); + } else { + if (build_sexp_string(node, 2, SEXP_SAVE_MODE)){ + fout(" (\n %s\n ( end-of-campaign )\n )\n", out); + } else { + fout(" ( %s( end-of-campaign ) )\n", out ); + } + } +} + + diff --git a/src/fred2/modifyvariabledlg.cpp b/src/fred2/modifyvariabledlg.cpp new file mode 100644 index 0000000..b67adc5 --- /dev/null +++ b/src/fred2/modifyvariabledlg.cpp @@ -0,0 +1,485 @@ +// ModifyVariableDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "modifyvariabledlg.h" +#include "sexp.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define NO_RESET_FOCUS 0 +#define RESET_FOCUS 1 + + +///////////////////////////////////////////////////////////////////////////// +// CModifyVariableDlg dialog + +CModifyVariableDlg::CModifyVariableDlg(CWnd* pParent /*=NULL*/) + : CDialog(CModifyVariableDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CModifyVariableDlg) + m_default_value = _T(""); + m_cur_variable_name = _T(""); + //}}AFX_DATA_INIT +} + + +void CModifyVariableDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CModifyVariableDlg) + DDX_Text(pDX, IDC_MODIFY_DEFAULT_VALUE, m_default_value); + DDV_MaxChars(pDX, m_default_value, 31); + DDX_CBString(pDX, IDC_MODIFY_VARIABLE_NAME, m_cur_variable_name); + DDV_MaxChars(pDX, m_cur_variable_name, 31); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CModifyVariableDlg, CDialog) + //{{AFX_MSG_MAP(CModifyVariableDlg) + ON_BN_CLICKED(ID_DELETE_VARIABLE, OnDeleteVariable) + ON_BN_CLICKED(IDC_TYPE_STRING, OnTypeString) + ON_BN_CLICKED(IDC_TYPE_NUMBER, OnTypeNumber) + ON_CBN_SELCHANGE(IDC_MODIFY_VARIABLE_NAME, OnSelchangeModifyVariableName) + ON_CBN_EDITCHANGE(IDC_MODIFY_VARIABLE_NAME, OnEditchangeModifyVariableName) + ON_EN_KILLFOCUS(IDC_MODIFY_DEFAULT_VALUE, OnKillfocusModifyDefaultValue) + ON_CBN_DROPDOWN(IDC_MODIFY_VARIABLE_NAME, OnDropdownModifyVariableName) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CModifyVariableDlg message handlers + +// Maybe delete variable +void CModifyVariableDlg::OnDeleteVariable() +{ + CString temp_name; + int rval; + + // Check for name change + CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME); + cbox->GetWindowText(temp_name); + + // Can't delete. Name has been changed + if ( stricmp(Sexp_variables[get_sexp_var_index()].variable_name, temp_name) ) { + MessageBox("Can not delete variable. Name has been changed."); + return; + } + + int num_counts = m_p_sexp_tree->get_variable_count(Sexp_variables[get_sexp_var_index()].variable_name); + if (num_counts > 0) { + char buffer[256]; + sprintf(buffer, "Can not delete variable. Used in %d location(s).", num_counts); + MessageBox(buffer); + return; + } + + // maybe delete variable + rval = MessageBox("This will permanantly delete the variable. Do you want to continue?", NULL, MB_OKCANCEL); + + if (rval == IDOK) { + // delete variable and exit + m_deleted = true; + // next statement does UpdataData(TRUE); + CDialog::OnOK(); + } +} + +// Set type to string +void CModifyVariableDlg::OnTypeString() +{ + // check if type actually modified + if (m_type_number == true) { + + // Don't allow type change if in use. + int num_counts = m_p_sexp_tree->get_variable_count(Sexp_variables[get_sexp_var_index()].variable_name); + if (num_counts > 0) { + char buffer[256]; + sprintf(buffer, "Can not modify variable type. Used in %d location(s).", num_counts); + MessageBox(buffer); + + m_type_number = true; + m_modified_type = false; + set_variable_type(); + return; + } + + // keep track if type is really changed + if (m_modified_type == true) { + m_modified_type = false; + } else { + m_modified_type = true; + } + } + m_type_number = false; + set_variable_type(); +} + +// Set type to number +void CModifyVariableDlg::OnTypeNumber() +{ + // check if type actually modified + if (m_type_number == false) { + + // Don't allow type change if in use. + int num_counts = m_p_sexp_tree->get_variable_count(Sexp_variables[get_sexp_var_index()].variable_name); + if (num_counts > 0) { + char buffer[256]; + sprintf(buffer, "Can not modify variable type. Used in %d location(s).", num_counts); + MessageBox(buffer); + + m_type_number = false; + m_modified_type = false; + set_variable_type(); + return; + } + + // keep track if type is really changed + if (m_modified_type == true) { + m_modified_type = false; + } else { + m_modified_type = true; + } + } + m_type_number = true; + set_variable_type(); +} + +#pragma warning (push) +#pragma warning( disable : 4800 ) // Disable "forcing value to bool 'true' or 'false'" + +void CModifyVariableDlg::OnSelchangeModifyVariableName() +{ + CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME); + + // get index of current selection + int index = cbox->GetCurSel(); + + // check an item was actually selected, and not outside the box + if (index == CB_ERR) { + return; + } + + // check if another has been modified + if (m_modified_type || m_modified_name || m_modified_value) { + + // Don't send message if changing to self + if (index != m_combo_last_modified_index) { + MessageBox("Can only modify one variable."); + } + + //reset focus to current + cbox->SetCurSel(m_combo_last_modified_index); + return; + } + + m_combo_last_modified_index = index; + + // Get index into sexp_variables + int sexp_variable_index = get_sexp_var_index(); + + // Set new type for selection + m_type_number = (bool) (Sexp_variables[sexp_variable_index].type & SEXP_VARIABLE_NUMBER); + set_variable_type(); + + // Set new default value for selection + if (sexp_variable_index > -1) { + CEdit *edit = (CEdit *) GetDlgItem(IDC_MODIFY_DEFAULT_VALUE); + edit->SetWindowText(Sexp_variables[sexp_variable_index].text); + } +} +#pragma warning (pop) + +// Check if variable name has changed from Sexp_variables[].varaible name +void CModifyVariableDlg::OnEditchangeModifyVariableName() +{ + // Do string compare to check for change + CString temp_name; + + // Get current variable name + CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME); + cbox->GetWindowText(temp_name); + + // Check if variable name is modified + if ( strcmp(Sexp_variables[get_sexp_var_index()].variable_name, temp_name) ) { + m_modified_name = true; + } else { + m_modified_name = false; + } +} + + +#pragma warning (push) +#pragma warning( disable : 4800 ) // Disable "forcing value to bool 'true' or 'false'" + +BOOL CModifyVariableDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + int i, box_index; + // Init combo box and translation table from combo box to sexp_varaibles + CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME); + cbox->ResetContent(); + + for (i=0; iAddString(Sexp_variables[i].variable_name); + + // check no error + if ( !((box_index == CB_ERR) || (box_index == CB_ERRSPACE)) ) { + m_traslate_combo_to_sexp[box_index] = i; + } + } + } + + // Exit gracefully if nothing added to combo box + if (cbox->GetCount() == 0) { + Int3(); // this should not happen + OnCancel(); + } + + int last_modified = 0; + // Set current variable + if (m_start_index > -1) { + for (i=0; iSetCurSel(last_modified); + + // Set the default value + if (m_traslate_combo_to_sexp[last_modified] > -1) { + CEdit *edit = (CEdit *) GetDlgItem(IDC_MODIFY_DEFAULT_VALUE); + edit->SetWindowText(Sexp_variables[m_traslate_combo_to_sexp[last_modified]].text); + } + + // Set old variable name + m_old_var_name = Sexp_variables[m_traslate_combo_to_sexp[last_modified]].variable_name; + + // Set type + m_type_number = (Sexp_variables[last_modified].type & SEXP_VARIABLE_NUMBER); + set_variable_type(); + + // keep track of changes + m_modified_name = false; + m_modified_value = false; + m_modified_type = false; + m_deleted = false; + m_do_modify = false; + + m_data_validated = false; + m_var_name_validated = false; + + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} +#pragma warning (pop) + + +void CModifyVariableDlg::set_variable_type() +{ + // get buttons + CButton *button_string = (CButton *) GetDlgItem(IDC_TYPE_STRING); + CButton *button_number = (CButton *) GetDlgItem(IDC_TYPE_NUMBER); + + // assign state + button_number->SetCheck( m_type_number); + button_string->SetCheck(!m_type_number); +} + +void CModifyVariableDlg::OnOK() +{ + CString temp_data; + + // Validate data + CEdit *edit = (CEdit *) GetDlgItem(IDC_MODIFY_DEFAULT_VALUE); + edit->GetWindowText(temp_data); + + // validate data + validate_data(temp_data, RESET_FOCUS); + if (m_data_validated) { + // Dont get OnKillfocusModifyDefaultValue when ok + if (!m_modified_value) { + if ( strcmp(Sexp_variables[get_sexp_var_index()].text, temp_data) ) { + m_modified_value = true; + } + } + + // validate variable name + validate_var_name(RESET_FOCUS); + if (m_var_name_validated) { + + // maybe set m_do_modify -- this is needed. compare with OnCancel() + if (m_modified_name || m_modified_value || m_modified_type) { + m_do_modify = true; + } + CDialog::OnOK(); + } + } +} + +void CModifyVariableDlg::OnKillfocusModifyDefaultValue() +{ + // Do string compare to check for change + CString temp_data; + + CEdit *edit = (CEdit *) GetDlgItem(IDC_MODIFY_DEFAULT_VALUE); + edit->GetWindowText(temp_data); + + if ( strcmp(Sexp_variables[get_sexp_var_index()].text, temp_data) ) { + m_modified_value = true; + } else { + m_modified_value = false; + } +} + +// validate data +// check (1) zero length (2) invalid chars (3) value if numberf +void CModifyVariableDlg::validate_data(CString &temp_data, int set_focus) +{ + // display invalid data message + bool message = false; + char message_text[256]; + + // check length > 0 + int length = strlen(temp_data); + if (length == 0) { + strcpy(message_text, "Invalid Default Value"); + message = true; + + } else if (m_type_number) { + // check if string and str(atoi(stri)) are same + int temp_num = atoi(temp_data); + char buf[TOKEN_LENGTH]; + sprintf(buf, "%d", temp_num); + + if ( stricmp(buf, temp_data) ) { + message = true; + strcpy(message_text, "Invalid Default Value"); + } else { + message = false; + } + } + + // check for invalid characters + int rval = strcspn(temp_data, "@()"); + if (rval != length) { + message = true; + sprintf(message_text, "Invalid char '%c' in Default Value", temp_data[rval]); + } + + // display message + if ( message ) { + m_data_validated = false; + MessageBox(message_text); + + // reset focus + if (set_focus == RESET_FOCUS) { + CEdit *edit = (CEdit *) GetDlgItem(IDC_MODIFY_DEFAULT_VALUE); + edit->SetFocus(); + edit->SetSel(0, -1); + } + } + + // string always ok, numbers if no message + m_data_validated = !message; +} + +// validate variable name +// check (1) zero length (2) invalid chars (3) already in use +void CModifyVariableDlg::validate_var_name(int set_focus) +{ + CString temp_name; + CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME); + cbox->GetWindowText(temp_name); + + int cur_sel = cbox->GetCurSel(); + m_old_var_name = Sexp_variables[m_traslate_combo_to_sexp[cur_sel]].variable_name; + + // display invalid data message + bool message = false; + char message_text[256]; + + // check length > 0 + int length = strlen(temp_name); + if (length == 0) { + strcpy(message_text, "Invalid Variable Name"); + message = true; + } else { + + // check for invalid characters + int rval = strcspn(temp_name, "@()"); + if (rval != length) { + message = true; + sprintf(message_text, "Invalid char '%c' in Variable Name", temp_name[rval]); + } else { + int index = get_index_sexp_variable_name(temp_name); + + // if not a new name and not start name + if ( (index != -1) && (index != m_traslate_combo_to_sexp[m_combo_last_modified_index]) ) { + message = true; + strcpy(message_text, "Variable Name already in use"); + } + } + } + + // display message + if ( message ) { + MessageBox(message_text); + + // reset focus + if (set_focus == RESET_FOCUS) { + cbox->SetFocus(); + } + } + + // set var_name_validated + m_var_name_validated = !message; +} + + +int CModifyVariableDlg::get_sexp_var_index() +{ + int index = m_traslate_combo_to_sexp[m_combo_last_modified_index]; + Assert( (index >= 0) && (index < MAX_SEXP_VARIABLES) ); + + return index; +} + +// Reset text in drop down list +void CModifyVariableDlg::OnDropdownModifyVariableName() +{ + CString temp_name; + + // Get current variable name + CComboBox *cbox = (CComboBox *) GetDlgItem(IDC_MODIFY_VARIABLE_NAME); + cbox->GetWindowText(temp_name); + + // Reset combo box text + int rval; + rval = cbox->InsertString(m_combo_last_modified_index, temp_name); + if ( (rval == CB_ERR) || (rval == CB_ERRSPACE) ) { + AfxMessageBox("An internal error has occured."); + OnCancel(); + } + cbox->DeleteString(m_combo_last_modified_index+1); + + cbox->SetCurSel(m_combo_last_modified_index); +} diff --git a/src/fred2/operatorargtypeselect.cpp b/src/fred2/operatorargtypeselect.cpp new file mode 100644 index 0000000..b69a965 --- /dev/null +++ b/src/fred2/operatorargtypeselect.cpp @@ -0,0 +1,94 @@ +/* + * $Logfile: /Freespace2/code/FRED2/OperatorArgTypeSelect.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Dialog box handling code for selecting an argument return type of an SEXP. + * Changes to SEXPs made this no longer needed, but just in case more changes + * cause it to be needed again, it's still around. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 3 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "operatorargtypeselect.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// OperatorArgTypeSelect dialog + + +OperatorArgTypeSelect::OperatorArgTypeSelect(CWnd* pParent /*=NULL*/) + : CDialog(OperatorArgTypeSelect::IDD, pParent) +{ + //{{AFX_DATA_INIT(OperatorArgTypeSelect) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void OperatorArgTypeSelect::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(OperatorArgTypeSelect) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(OperatorArgTypeSelect, CDialog) + //{{AFX_MSG_MAP(OperatorArgTypeSelect) + ON_BN_CLICKED(ID_BOOLEAN, OnBoolean) + ON_BN_CLICKED(ID_NUMBERS, OnNumbers) + ON_BN_CLICKED(ID_SHIPS, OnShips) + ON_BN_CLICKED(ID_WINGS, OnWings) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// OperatorArgTypeSelect message handlers + +void OperatorArgTypeSelect::OnBoolean() +{ + EndDialog(ID_BOOLEAN); +} + +void OperatorArgTypeSelect::OnNumbers() +{ + EndDialog(ID_NUMBERS); +} + +void OperatorArgTypeSelect::OnShips() +{ + EndDialog(ID_SHIPS); +} + +void OperatorArgTypeSelect::OnWings() +{ + EndDialog(ID_WINGS); +} diff --git a/src/fred2/orienteditor.cpp b/src/fred2/orienteditor.cpp new file mode 100644 index 0000000..f22f00a --- /dev/null +++ b/src/fred2/orienteditor.cpp @@ -0,0 +1,338 @@ +/* + * $Logfile: /Freespace2/code/FRED2/OrientEditor.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Object orientation editor (or just object editor) dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 18 7/13/98 10:04a Hoffoss + * Fixed bug where jump nodes in mission would screw up indexing. + * + * 17 3/12/98 2:21p Johnson + * Fixed some Fred bugs related to jump nodes. + * + * 16 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 15 8/16/97 2:02a Hoffoss + * Made docked objects move together in Fred. + * + * 14 8/15/97 8:24p Hoffoss + * Fixed bug with orient editor. + * + * 13 8/14/97 3:14p Hoffoss + * Fixed name displayed for players. + * + * 12 7/29/97 2:20p Hoffoss + * Fixed some minor bugs. + * + * 11 6/26/97 11:18a Hoffoss + * Fixed bug in orient editor. OBJ_POINT type wasn't accounted for. + * + * 10 5/20/97 2:28p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 9 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 8 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 7 3/12/97 4:33p Hoffoss + * added spin controls to orient editor, light intensity level can be + * specified in BG editor. + * + * 6 2/28/97 11:31a Hoffoss + * Implemented modeless dialog saving and restoring, and changed some + * variables names. + * + * 5 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 4 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "orienteditor.h" +#include "management.h" +#include "linklist.h" +#include "fredview.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +#define PREC 0.0001f + +///////////////////////////////////////////////////////////////////////////// +// orient_editor dialog + +orient_editor::orient_editor(CWnd* pParent /*=NULL*/) + : CDialog(orient_editor::IDD, pParent) +{ + vector pos; + + //{{AFX_DATA_INIT(orient_editor) + m_object_index = 0; + m_point_to = FALSE; + m_position_z = _T(""); + m_position_y = _T(""); + m_position_x = _T(""); + m_location_x = _T("0.0"); + m_location_y = _T("0.0"); + m_location_z = _T("0.0"); + //}}AFX_DATA_INIT + Assert(query_valid_object()); + pos = Objects[cur_object_index].pos; + m_position_x.Format("%.1f", pos.x); + m_position_y.Format("%.1f", pos.y); + m_position_z.Format("%.1f", pos.z); +} + +void orient_editor::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(orient_editor) + DDX_Control(pDX, IDC_SPIN6, m_spin6); + DDX_Control(pDX, IDC_SPIN5, m_spin5); + DDX_Control(pDX, IDC_SPIN4, m_spin4); + DDX_Control(pDX, IDC_SPIN3, m_spin3); + DDX_Control(pDX, IDC_SPIN2, m_spin2); + DDX_Control(pDX, IDC_SPIN1, m_spin1); + DDX_CBIndex(pDX, IDC_OBJECT_LIST, m_object_index); + DDX_Check(pDX, IDC_POINT_TO_CHECKBOX, m_point_to); + DDX_Text(pDX, IDC_POSITION_Z, m_position_z); + DDX_Text(pDX, IDC_POSITION_Y, m_position_y); + DDX_Text(pDX, IDC_POSITION_X, m_position_x); + DDX_Text(pDX, IDC_LOCATION_X, m_location_x); + DDX_Text(pDX, IDC_LOCATION_Y, m_location_y); + DDX_Text(pDX, IDC_LOCATION_Z, m_location_z); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(orient_editor, CDialog) + //{{AFX_MSG_MAP(orient_editor) + ON_WM_CLOSE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// orient_editor message handlers + +BOOL orient_editor::OnInitDialog() +{ + char text[80]; + int type; + CComboBox *box; + object *ptr; + + CDialog::OnInitDialog(); + theApp.init_window(&Object_wnd_data, this); + ((CButton *) GetDlgItem(IDC_POINT_TO_OBJECT))->SetCheck(1); + box = (CComboBox *) GetDlgItem(IDC_OBJECT_LIST); + box->ResetContent(); + + total = 0; + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (Marked != 1 || OBJ_INDEX(ptr) != cur_object_index) { + if ((ptr->type == OBJ_START) || (ptr->type == OBJ_SHIP)) { + box->AddString(Ships[ptr->instance].ship_name); + index[total++] = OBJ_INDEX(ptr); + + } else if (ptr->type == OBJ_WAYPOINT) { + sprintf(text, "%s:%d", Waypoint_lists[ptr->instance / 65536].name, + (ptr->instance & 0xffff) + 1); + + box->AddString(text); + index[total++] = OBJ_INDEX(ptr); + + } else if ((ptr->type == OBJ_POINT) || (ptr->type == OBJ_JUMP_NODE)) { + } else + Assert(0); // unknown object type. + } + + ptr = GET_NEXT(ptr); + } + + type = Objects[cur_object_index].type; + if (Marked == 1 && type == OBJ_WAYPOINT) { + GetDlgItem(IDC_POINT_TO_CHECKBOX)->EnableWindow(0); + GetDlgItem(IDC_POINT_TO_OBJECT)->EnableWindow(0); + GetDlgItem(IDC_POINT_TO_LOCATION)->EnableWindow(0); + GetDlgItem(IDC_OBJECT_LIST)->EnableWindow(0); + GetDlgItem(IDC_LOCATION_X)->EnableWindow(0); + GetDlgItem(IDC_LOCATION_Y)->EnableWindow(0); + GetDlgItem(IDC_LOCATION_Z)->EnableWindow(0); + m_object_index = -1; + + } else { + m_object_index = 0; + } + + m_spin1.SetRange(99999, -99999); + m_spin1.SetPos((int) convert(m_position_x)); + m_spin2.SetRange(99999, -99999); + m_spin2.SetPos((int) convert(m_position_y)); + m_spin3.SetRange(99999, -99999); + m_spin3.SetPos((int) convert(m_position_z)); + m_spin4.SetRange(99999, -99999); + m_spin5.SetRange(99999, -99999); + m_spin6.SetRange(99999, -99999); + UpdateData(FALSE); + return TRUE; +} + +int orient_editor::query_modified() +{ + float dif; + + dif = Objects[cur_object_index].pos.x - convert(m_position_x); + if ((dif > PREC) || (dif < -PREC)) + return 1; + dif = Objects[cur_object_index].pos.y - convert(m_position_y); + if ((dif > PREC) || (dif < -PREC)) + return 1; + dif = Objects[cur_object_index].pos.z - convert(m_position_z); + if ((dif > PREC) || (dif < -PREC)) + return 1; + + if (((CButton *) GetDlgItem(IDC_POINT_TO_CHECKBOX))->GetCheck() == 1) + return 1; + + return 0; +} + +void orient_editor::OnOK() +{ + vector delta, pos; + object *ptr; + + UpdateData(TRUE); + pos.x = convert(m_position_x); + pos.y = convert(m_position_y); + pos.z = convert(m_position_z); + + if ((((CButton *) GetDlgItem(IDC_POINT_TO_OBJECT))->GetCheck() == 1) || + (((CButton *) GetDlgItem(IDC_POINT_TO_LOCATION))->GetCheck() == 1)) + set_modified(); + + vm_vec_sub(&delta, &pos, &Objects[cur_object_index].pos); + if (delta.x || delta.y || delta.z) + set_modified(); + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->flags & OF_MARKED) { + vm_vec_add2(&ptr->pos, &delta); + update_object(ptr); + } + + ptr = GET_NEXT(ptr); + } + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->flags & OF_MARKED) + object_moved(ptr); + + ptr = GET_NEXT(ptr); + } + + theApp.record_window_data(&Object_wnd_data, this); + CDialog::OnOK(); +} + +void orient_editor::update_object(object *ptr) +{ + if (ptr->type != OBJ_WAYPOINT && m_point_to) { + vector v, loc; + matrix m; + + memset(&v, 0, sizeof(vector)); + loc.x = convert(m_location_x); + loc.y = convert(m_location_y); + loc.z = convert(m_location_z); + if (((CButton *) GetDlgItem(IDC_POINT_TO_OBJECT))->GetCheck() == 1) { + v = Objects[index[m_object_index]].pos; + vm_vec_sub2(&v, &ptr->pos); + + } else if (((CButton *) GetDlgItem(IDC_POINT_TO_LOCATION))->GetCheck() == 1) { + vm_vec_sub(&v, &loc, &ptr->pos); + + } else { + Assert(0); // neither radio button is checked. + } + + if (!v.x && !v.y && !v.z){ + return; // can't point to itself. + } + + vm_vector_2_matrix(&m, &v, NULL, NULL); + ptr->orient = m; + } +} + +float orient_editor::convert(CString &str) +{ + char buf[256]; + int i, j, len; + + string_copy(buf, str, 255); + len = strlen(buf); + for (i=j=0; i 0){ + m_ship_list.SetCheck(ct, TRUE); + } else { + m_ship_list.SetCheck(ct, FALSE); + } + + // next + ct++; + } + } + + // create a checklistbox for each weapon ship type + m_weapon_list.ResetContent(); + ct = 0; + for (i=0; i 0){ + m_weapon_list.SetCheck(ct, TRUE); + } else { + m_weapon_list.SetCheck(ct, FALSE); + } + + ct++; + } + } + + // be sure that nothing is selected + m_ship_list.SetCurSel(-1); + m_weapon_list.SetCurSel(-1); + UpdateData(FALSE); +} + +void player_start_editor::OnInitMenu(CMenu* pMenu) +{ + int i; + CMenu *m; + + // disable any items we should disable + m = pMenu->GetSubMenu(0); + + // uncheck all menu items + for (i = 0; i < Num_teams; i++ ){ + m->CheckMenuItem(i, MF_BYPOSITION | MF_UNCHECKED); + } + + for ( i = Num_teams; i < MAX_TEAMS; i++ ){ + m->EnableMenuItem(i, MF_BYPOSITION | MF_GRAYED); + } + + // put a check next to the currently selected item + m->CheckMenuItem(selected_team, MF_BYPOSITION | MF_CHECKED); + + CDialog::OnInitMenu(pMenu); +} + +// switch between active teams +BOOL player_start_editor::OnCommand(WPARAM wParam, LPARAM lParam) +{ + int id; + + // select a team + id = LOWORD(wParam); + switch(id){ + case ID_TEAM_1: + selected_team = 0; + reset_controls(); + break; + + case ID_TEAM_2: + selected_team = 1; + reset_controls(); + break; + } + + // low level stuff + return CDialog::OnCommand(wParam, lParam); +} + +// ship list changed +void player_start_editor::OnSelchangeShipList() +{ + int selected; + int si_index; + char ship_name[255] = ""; + + // determine if we've selected something + selected = m_ship_list.GetCurSel(); + if (selected != -1) { + // lookup the ship + m_ship_list.GetText(m_ship_list.GetCurSel(), ship_name); + si_index = ship_info_lookup(ship_name); + + // if we have a valid ship type + if(si_index >= 0){ + // if this item is checked + if(m_ship_list.GetCheck(selected)) { + if(ship_pool[selected_team][si_index] <= 0){ + ship_pool[selected_team][si_index] = 5; + m_ship_pool = 5; + } else { + m_ship_pool = ship_pool[selected_team][si_index]; + } + } + // otherwise zero the count + else { + ship_pool[selected_team][si_index] = 0; + m_ship_pool = 0; + } + } else { + Int3(); + } + } + + // update shtuff + UpdateData(FALSE); +} + +// weapon list changed +void player_start_editor::OnSelchangeWeaponList() +{ + int selected; + int wi_index; + char weapon_name[255] = ""; + + // determine if we've selected something + selected = m_weapon_list.GetCurSel(); + if (selected != -1) { + // lookup the weapon + m_weapon_list.GetText(m_weapon_list.GetCurSel(), weapon_name); + wi_index = weapon_name_lookup(weapon_name); + + // if we have a valid weapon type + if(wi_index >= 0){ + // if this item is checked + if(m_weapon_list.GetCheck(selected)) { + if(weapon_pool[selected_team][wi_index] <= 0){ + weapon_pool[selected_team][wi_index] = 100; + m_weapon_pool = 100; + } else { + m_weapon_pool = weapon_pool[selected_team][wi_index]; + } + } + // otherwise zero the count + else { + weapon_pool[selected_team][wi_index] = 0; + m_weapon_pool = 0; + } + } else { + Int3(); + } + } + + // update shtuff + UpdateData(FALSE); +} + +// cancel +void player_start_editor::OnCancel() +{ + theApp.record_window_data(&Player_wnd_data, this); + CDialog::OnCancel(); +} + +// ok +void player_start_editor::OnOK() +{ + int i, idx; + + // store player entry time delay + Entry_delay_time = i2f(m_delay); + + // store ship pools + for(i=0; i 0){ + Team_data[i].ship_count[Team_data[i].number_choices] = ship_pool[i][idx]; + Team_data[i].ship_list[Team_data[i].number_choices++] = idx; + } + } + } + + // store weapon pools + for(i=0; i= 0){ + ship_pool[selected_team][si_index] = m_ship_pool; + } + }; +} + +// weapon pool count change +void player_start_editor::OnUpdateWeaponPool() +{ + int selected, wi_index; + char weapon_name[255] = ""; + + if (!dlg_inited){ + return; + } + + UpdateData(TRUE); + + // if we have a ship selected and checked, update the pool + selected = m_weapon_list.GetCurSel(); + if((selected != -1) && m_weapon_list.GetCheck(selected)){ + // lookup the ship + m_weapon_list.GetText(m_weapon_list.GetCurSel(), weapon_name); + wi_index = weapon_info_lookup(weapon_name); + + // if we have a valid ship type + if(wi_index >= 0){ + weapon_pool[selected_team][wi_index] = m_weapon_pool; + } + }; +} \ No newline at end of file diff --git a/src/fred2/prefsdlg.cpp b/src/fred2/prefsdlg.cpp new file mode 100644 index 0000000..e8e276a --- /dev/null +++ b/src/fred2/prefsdlg.cpp @@ -0,0 +1,114 @@ +/* + * $Logfile: /Freespace2/code/FRED2/PrefsDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Fred Preferences dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 3 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "prefsdlg.h" +#include "freddoc.h" +#include "fredview.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CPrefsDlg dialog + + + +CPrefsDlg::CPrefsDlg(CWnd* pParent /*=NULL*/) + : CDialog(CPrefsDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CPrefsDlg) + m_ConfirmDeleting = TRUE; + m_ShowCapitalShips = TRUE; + m_ShowElevations = TRUE; + m_ShowFighters = TRUE; + m_ShowGrid = TRUE; + m_ShowMiscObjects = TRUE; + m_ShowPlanets = TRUE; + m_ShowWaypoints = TRUE; + m_ShowStarfield = FALSE; + //}}AFX_DATA_INIT +} + +extern int Show_stars; + +void CPrefsDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CPrefsDlg) + DDX_Check(pDX, ID_CONFIRM_DELETING, m_ConfirmDeleting); + DDX_Check(pDX, ID_SHOW_CAPITALSHIPS, m_ShowCapitalShips); + DDX_Check(pDX, ID_SHOW_ELEVATIONS, m_ShowElevations); + DDX_Check(pDX, ID_SHOW_FIGHTERS, m_ShowFighters); + DDX_Check(pDX, ID_SHOW_GRID, m_ShowGrid); + DDX_Check(pDX, ID_SHOW_MISCOBJECTS, m_ShowMiscObjects); + DDX_Check(pDX, ID_SHOW_PLANETS, m_ShowPlanets); + DDX_Check(pDX, ID_SHOW_WAYPOINTS, m_ShowWaypoints); + DDX_Check(pDX, IDC_PREF_STARFIELD, m_ShowStarfield); + //}}AFX_DATA_MAP + + Show_stars = m_ShowStarfield; + // CFREDView::SetViewParms(m_ConfirmDeleting); +} + + +BEGIN_MESSAGE_MAP(CPrefsDlg, CDialog) + //{{AFX_MSG_MAP(CPrefsDlg) + ON_BN_CLICKED(IDC_SAVE_DEFAULT_PREFS, OnSaveDefaultPrefs) + ON_WM_CLOSE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CPrefsDlg message handlers + +void CPrefsDlg::OnSaveDefaultPrefs() +{ + // Put code to save user prefs here. + + m_ConfirmDeleting = 1; +} + +void CPrefsDlg::OnClose() +{ + // MessageBeep((WORD) -1); + + CDialog::OnClose(); +} + +BOOL CPrefsDlg::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) +{ + // MessageBeep((WORD) -1); + + return CDialog::Create(IDD, pParentWnd); +} diff --git a/src/fred2/reinforcementeditordlg.cpp b/src/fred2/reinforcementeditordlg.cpp new file mode 100644 index 0000000..3642a5d --- /dev/null +++ b/src/fred2/reinforcementeditordlg.cpp @@ -0,0 +1,462 @@ +/* + * $Logfile: /Freespace2/code/fred2/ReinforcementEditorDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Reinforcements editor dialog handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 3 3/30/99 5:40p Dave + * Fixed reinforcements for TvT in multiplayer. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 17 5/23/98 3:33p Hoffoss + * Removed unused code in reinforcements editor and make ships.tbl button + * in ship editor disappear in release build. + * + * 16 1/02/98 12:45p Allender + * remove bogus assert + * + * 15 7/17/97 10:17a Allender + * more reinforcement messaging stuff -- now working within Freespace. + * Fixed a couple of Fred bugs relating to not setting reinforcement flag + * properly + * + * 14 7/16/97 11:02p Allender + * added messaging for reinforcements. One (or one of several) can now + * play if reinforcement are not yet available, or when they are arriving + * + * 13 7/08/97 10:15a Allender + * making ships/wings reinforcements now do not set the arrival cue to + * false. A reinforcement may only be available after it's arrival cue is + * true + * + * 12 5/20/97 2:29p Hoffoss + * Added message box queries for close window operation on all modal + * dialog boxes. + * + * 11 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 10 4/29/97 3:02p Hoffoss + * Reinforcement type is now automatically handled by Fred. + * + * 9 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 8 3/20/97 3:55p Hoffoss + * Major changes to how dialog boxes initialize (load) and update (save) + * their internal data. This should simplify things and create less + * problems. + * + * 7 2/27/97 3:09p Allender + * major wing structure enhancement. simplified wing code. All around + * better wing support + * + * 6 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 5 2/20/97 4:03p Hoffoss + * Several ToDo items: new reinforcement clears arrival cue, reinforcement + * control from ship and wing dialogs, show grid toggle. + * + * 4 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 3 2/04/97 3:10p Hoffoss + * Reinforcements editor fully implemented. + * + * 2 2/03/97 1:32p Hoffoss + * Reinforcement editor functional, but still missing a few options. + * Checking in good code now prior to experimenting, so I can revert if + * needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "reinforcementeditordlg.h" +#include "missionparse.h" +#include "linklist.h" +#include "ship.h" +#include "freddoc.h" +#include "management.h" +#include "missionmessage.h" + +#define ID_WING_DATA 9000 + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// reinforcement_editor_dlg dialog + +reinforcement_editor_dlg::reinforcement_editor_dlg(CWnd* pParent /*=NULL*/) + : CDialog(reinforcement_editor_dlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(reinforcement_editor_dlg) + m_uses = 0; + m_delay = 0; + //}}AFX_DATA_INIT + cur = -1; +} + +void reinforcement_editor_dlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(reinforcement_editor_dlg) + DDX_Control(pDX, IDC_DELAY_SPIN, m_delay_spin); + DDX_Control(pDX, IDC_USES_SPIN, m_uses_spin); + DDX_Text(pDX, IDC_USES, m_uses); + DDX_Text(pDX, IDC_DELAY, m_delay); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(reinforcement_editor_dlg, CDialog) + //{{AFX_MSG_MAP(reinforcement_editor_dlg) + ON_LBN_SELCHANGE(IDC_LIST, OnSelchangeList) + ON_BN_CLICKED(IDC_ADD, OnAdd) + ON_BN_CLICKED(IDC_DELETE, OnDelete) + ON_WM_CLOSE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// reinforcement_editor_dlg message handlers + +BOOL reinforcement_editor_dlg::OnInitDialog() +{ + int i; + CListBox *box; + + CDialog::OnInitDialog(); + theApp.init_window(&Reinforcement_wnd_data, this); + box = (CListBox *) GetDlgItem(IDC_LIST); + for (i=0; iAddString(Reinforcements[i].name); + } + + m_num_reinforcements = Num_reinforcements; + m_uses_spin.SetRange(1, 99); + m_delay_spin.SetRange(0, 1000); + update_data(); + if (Num_reinforcements == MAX_REINFORCEMENTS) + GetDlgItem(IDC_ADD) -> EnableWindow(FALSE); + + return TRUE; +} + +void reinforcement_editor_dlg::update_data() +{ + object *objp; + int enable = TRUE; + + if (cur < 0) { + m_uses = 0; + m_delay = 0; + enable = FALSE; + + } else { + m_uses = m_reinforcements[cur].uses; + m_delay = m_reinforcements[cur].arrival_delay; + } + + UpdateData(FALSE); + + GetDlgItem(IDC_USES)->EnableWindow(enable); + GetDlgItem(IDC_USES_SPIN)->EnableWindow(enable); + GetDlgItem(IDC_DELAY)->EnableWindow(enable); + GetDlgItem(IDC_DELAY_SPIN)->EnableWindow(enable); + + if (cur < 0) + return; + + // disable the uses entries if the reinforcement is a ship + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->type == OBJ_SHIP) { + if ( !stricmp( Ships[objp->instance].ship_name, m_reinforcements[cur].name) ) { + GetDlgItem(IDC_USES)->EnableWindow(FALSE); + GetDlgItem(IDC_USES_SPIN)->EnableWindow(FALSE); + break; + } + } + objp = GET_NEXT(objp); + } +} + +void reinforcement_editor_dlg::OnSelchangeList() +{ + save_data(); + cur = ((CListBox *) GetDlgItem(IDC_LIST))->GetCurSel(); + GetDlgItem(IDC_DELETE) -> EnableWindow(cur != -1); + update_data(); +} + +void reinforcement_editor_dlg::save_data() +{ + UpdateData(TRUE); + UpdateData(TRUE); + if (cur >= 0) { + Assert(cur < m_num_reinforcements); + m_reinforcements[cur].uses = m_uses; + m_reinforcements[cur].arrival_delay = m_delay; + + // save the message information to the reinforcement structure. First clear out the string + // entires in the Reinforcement structure + memset( m_reinforcements[cur].no_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH ); + memset( m_reinforcements[cur].yes_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH ); + } +} + +int reinforcement_editor_dlg::query_modified() +{ + int i, j; + + save_data(); + if (Num_reinforcements != m_num_reinforcements) + return 1; + + for (i=0; itype == OBJ_SHIP) { + z = box->AddString(Ships[ptr->instance].ship_name); + box->SetItemData(z, OBJ_INDEX(ptr)); + } + + ptr = GET_NEXT(ptr); + } + + for (i=0; iAddString(Wings[i].name); + box->SetItemData(z, ID_WING_DATA + i); + } + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void reinforcement_select::OnSelchangeList() +{ + cur = ((CListBox *) GetDlgItem(IDC_LIST))->GetCurSel(); + GetDlgItem(IDOK)->EnableWindow(cur != -1); +} + +void reinforcement_select::OnOK() +{ + cur = ((CListBox *) GetDlgItem(IDC_LIST))->GetCurSel(); + Assert(cur != -1); + ((CListBox *) GetDlgItem(IDC_LIST)) -> GetText(cur, name); + CDialog::OnOK(); +} + +void reinforcement_select::OnCancel() +{ + cur = -1; + CDialog::OnCancel(); +} + +void reinforcement_editor_dlg::OnAdd() +{ + int i, wing_index; + reinforcement_select dlg; + CString name_check; + + dlg.DoModal(); + if (dlg.cur != -1) { + // if we've run out of reinforcement slots + if (m_num_reinforcements == MAX_REINFORCEMENTS) { + MessageBox("Reached limit on reinforcements. Can't add more!"); + return; + } + + // if this is a wing, make sure its a valid wing (no mixed ship teams) + wing_index = wing_lookup((char*)dlg.name); + if(wing_index >= 0){ + if(wing_has_conflicting_teams(wing_index)){ + MessageBox("Cannot have a reinforcement wing with mixed teams, sucka!"); + return; + } + } + + i = m_num_reinforcements++; + strcpy(m_reinforcements[i].name, dlg.name); + ((CListBox *) GetDlgItem(IDC_LIST)) -> AddString(dlg.name); + m_reinforcements[i].type = 0; + m_reinforcements[i].uses = 1; + m_reinforcements[i].arrival_delay = 0; + memset( m_reinforcements[i].no_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH ); + memset( m_reinforcements[i].yes_messages, 0, MAX_REINFORCEMENT_MESSAGES * NAME_LENGTH ); + if (m_num_reinforcements == MAX_REINFORCEMENTS){ + GetDlgItem(IDC_ADD) -> EnableWindow(FALSE); + } + } +} + +void reinforcement_editor_dlg::OnDelete() +{ + int i; + + if (cur != -1) { + ((CListBox *) GetDlgItem(IDC_LIST)) -> DeleteString(cur); + for (i=cur; i EnableWindow(FALSE); +} + +void reinforcement_editor_dlg::OnClose() +{ + int z; + + if (query_modified()) { + z = MessageBox("Do you want to keep your changes?", "Close", MB_ICONQUESTION | MB_YESNOCANCEL); + if (z == IDCANCEL) + return; + + if (z == IDYES) { + OnOK(); + return; + } + } + + CDialog::OnClose(); +} diff --git a/src/fred2/res/bitmap1.bmp b/src/fred2/res/bitmap1.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a9df873d56394b0bc09cc49e210410cf1d8154dc GIT binary patch literal 246 zcmaKi!3}^g3JQ3H literal 0 HcmV?d00001 diff --git a/src/fred2/res/chained.bmp b/src/fred2/res/chained.bmp new file mode 100644 index 0000000000000000000000000000000000000000..0c90517d448818f0fb77377aae55c0e10afec452 GIT binary patch literal 246 zcmZ?r{l)+RWk5;;hy|dSk%0v)(Eui~5kMJ`WJ3dl0+K)`!+`?_fHX+rKNvCm{|{mT zF%W`;fW%!OxWEM>G3W~n3}6X{1`eN zBQha1nVXvuYrh{8`H*>PD#>RjL{8NNyIDoPZ-{(6DRP$e9Q^`)LQv2$_8#6cj99WP zO^ht1-|sV zO;ajTJmfn`u`ibn4dcN~OdH1y_efo8((ct#8W*I#T$`_#B6VwK^2Z_vK#@eE++17C zY>5S3Q+0iVf=4;|7j`_!t_AixR5^9)Ep$ z;is&=@c91eg?=_s(>J$BtZMgsiatl*yXSq@hqsJ7j)glNCq|b4>7F;W*<2QT3QpI? z{kc|~PYP~S)*RX4aE!(Jn(3RPMwFYQc(Jex{^!>>%8S@6%zqs})0=;ayFc@Yc9!u) zTmDGogLg%i=~Dn)=!hKqROH$zuE}wc&o79aw|D#<*645EOdZWzp9c+7Nj4d$Lxv0^ zYKjvlMwYQo{a^bs`sF*BoSc+Kqai0woRI11X*qfFq;xtRIdkTWoI7_;u3Whymo8nB zix)4-!oq@NU#MKZtn!sS5gi8k`N@tvdHGVl{NYCuIl8Ck^pHe{&Y^SYoarcmxp?%R zg-7qvd-S#~N1vn55pwi7dL{|z1NwkIpa|#{S&=ow%#P6!+!4sJ$?^FX(=#|291IQy z2SY@H!NK5Qa4b$) zO%6j2Lrxv+*r-Z;dNj<8c8t^@kT1X%;0WZ4M{69^Aixofg9nSp&87fLfF%GCNEojG zEP;&yr+^~B!atojoJoKsSUn<(-3B`abTB9k3WLI+FenW1I>8WNP#6@300Uv@_`{$u zC=3b%q3P&hP#6>j1whFk2&QaQoIEi+vWG)sZ1eH*o_s4;uU?ha)m6ED`?lP>cTXNY zdL$1YK9r|VpUSgm&*b^@=Q0=!AyGp zW8*92@TS`76oq{Y9nsi+w9)pg{ENfNKR9%V-Rtp%$H(MhtYIWO#ZNjwEo~4x)F+Q=m((cYidn?+^ZoS%O zqJ8E_yOjC&_P)KJSUbZ(iqh*XZMWLh=;QTZuv7GU3AVjtHd zc#QW;vd(H8tHTjoq2D{5wMmJUh!jt(%J3?Y0iY zuD>2`Ew$RSGt=F6dr;i9pB#T{e4E8*u({I88yhF>^~yJ3{32BBO8qKg`qf+B`Lx0A qZ;E8^!FaW|#-3h~v8l|Irt%IK_U<0`s=UQsd5j~LG0y+^d+smXHOe;t literal 0 HcmV?d00001 diff --git a/src/fred2/res/fred.rc2 b/src/fred2/res/fred.rc2 new file mode 100644 index 0000000..9d84609 --- /dev/null +++ b/src/fred2/res/fred.rc2 @@ -0,0 +1,13 @@ +// +// FRED.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/src/fred2/res/freddoc.ico b/src/fred2/res/freddoc.ico new file mode 100644 index 0000000000000000000000000000000000000000..6c05566a293efa323f2affa228bca30f8c428d67 GIT binary patch literal 3310 zcmcJR&1+m$6u{5)g=VP1Nj6!9HfABs?!g7=tQxX#QHXRC5Eo5Ax!p-1V2VilKnSx_ z5P}c7=t4yoZt@ol2rkTp?2?UvLLikOIKj<;!Q=1TJ2QFHOqxdYzPTUgl5f{UZf1hrtRmmnMcy3~xy^c(zCf=D3R=P5(XGIUr6|(G z$dX=2yWPg1@h7UbqH~$b1~=41a+l}1aPf11ne3vH*<_l_TxN4_a)zqskoEkl`-j3i zveW8Cme-58{8z*HT9%Kmoo)Rv&$up@&04jed5&Ja?0=d3GSBF!7i+8a7{_f*#m%N*nL{dcE7&W{HYjk?moTMY!|gm|9-;pQ?+wG zNuQA#KbBn8o6}pMJM|+&l|`c z?KE^yr$RE0IDSN>Rpz8m>ht=7dgjSL&aGDK^E(XY|KMYh_dXGM|AX{iFU*KsdmyrU zQ{>Tgkxv&y?$|s2CTsMMAE$16hrb7PQ%Nxvrx8Pe5j8c56C=x5r}nRH>HPAYjE#*+ zyh5rT3V9D#YJg0n^JtHa{s={SF$Jidyt>^ z{*b*FFXYP~ek75-*PdR|LlPZ2ht8pMrsG>r#_5|Srf$g#Xu9jM4%;uT9x?h>Yw zq$LSTm`ZY&7)yeb3`n-Yc+hLSqiW1}kZ?$Iza+GC^! zfqVhB07oESe6+?v4FVj&IJmL+xY-n739tkp0tw?0fF-ao;1o~<+m2ZAXZ z6(>&&kL=;l7~6cjyd~et%F2ptY;4Hp=B8|IZOP8gj%;sl%l`hp92^|T;o+eiA0Nxf z$%%BkUFrO#^3AhnyvGIAwRgZK;zh~gnrPC!S2WCW&pd35>nQiMQFUfFubyZ0Pg~;7 zz8qZ!n*M&9lLzA{FyD4+d<>VQ5b;R?o=PJG<2!wr$X^e74(H{Xh3M|L5rc)&IGF E0QgtBa{vGU literal 0 HcmV?d00001 diff --git a/src/fred2/res/green_do.bmp b/src/fred2/res/green_do.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e236161a41c30acca956a6e60a7cc9c761d103b8 GIT binary patch literal 246 zcmZ?r{l)+RWk5;;hy|dSk%0v)(Eui~5kMJ`WJ3dl0+K)`!+`?_fHX+rKNvCm{|{mT jF%W`;KztH_k`h?)CnY6)5Ccwsg7Wnt^na*2qD%$=eP)S@ literal 0 HcmV?d00001 diff --git a/src/fred2/res/root.bmp b/src/fred2/res/root.bmp new file mode 100644 index 0000000000000000000000000000000000000000..78b193ad9893cf42254b17fc1fba71a2e2fb3767 GIT binary patch literal 246 zcmZ?r{l)+RWk5;;hy|dSk%0v)(Eui~5kMJ`WJ3dl0+K)`!+`?_fHX+rKNvCm{|{mT kF%W`;fW%!Az?m~(#dpq}ISXRI=|52Zc?f+Ms*Wg=0W!CQpa1{> literal 0 HcmV?d00001 diff --git a/src/fred2/res/root_directive.bmp b/src/fred2/res/root_directive.bmp new file mode 100644 index 0000000000000000000000000000000000000000..bcc280d332624b66724fbe7ebaa1c177e4b25c00 GIT binary patch literal 246 zcmZ?r{l)+RWk5;;hy|dSk%0v)(Eui~5kMJ`WJ3dl0+K)`!+`?_fHX+rKNvCm{|{mT lF%W`;KztIw%$Z=tKWEOI4`RUSpHTjM2>l3E0RT6ukb?jK literal 0 HcmV?d00001 diff --git a/src/fred2/res/toolbar.bmp b/src/fred2/res/toolbar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..386d62712201c805b8304eb1c7d36fce32d45b0d GIT binary patch literal 5178 zcmds5F^Xh25Y;x>WU4D%Dd->ScfW`N!{y+Aokl;QQ%KDc_@iQ#Qgsf2;0- zsHNbq)ao51RD5fBczD3TS%2kxriL#*gdp1gq?KZinAER`Teb0dlxCWz(^2Z}>C~Df zik>_M7nBu)!(KQ(QSYB1uK^5Z)fC?))UF(1c)MMvZbDtw@(s?TtsQ*U1^Mv;_DWD4 zar?e^r5*d|np>Pc?MdZT<7LZfu~VEn_4%#sb{kpu3+yFS92#tF+dYj%I9le`lWd)v zwc8dKOtgZV3hdO*e_772J>E!b5rP+>$uQjP7VNHg@A`yU*ESKeMY`W!_B+bGW&=F=!P61EDQ4$&~1%w)sPz zN}9sJ&Kop?Mt=CMAZAS+)wZ9!R5~%+5AOuFk6B=t@Ez_8EmJ`BYKU!|N&|Xmu`~y; zcRMG?aT5Y}xym5CQcx{#^*vJT!^Zsi$7Yf)JmrE6dYe*%^0r@z-v^27{o!r;Tw;mQ z>wUVDo5@>n7)lJ>d?4yZ1AI*2BKc&&YN>SvmX4#L$0>vf4XwIV4vT<$yd~Efnz1=k zJ4`aL=+!BHUHDcz2$>uZu!99?TI|DzaK|w<&m?o_T)9fk$pA-Fwcu4YznY_-J0O^M zSEoPGQOyOTZip_hZQ0d{a;E6d=Ah5b^&!ejYGtZ1z%(OO!LEZ2xiLU%%lqa?Ofa`X zdbJMLu6^&vV1;`3u;6AI>)hO&x3~hktrq1BG?V1sN}-6QsUGygQOiA}M%S2r&EsqM zq7jxYtHm=ZBks7-kU;Ob5uqN{n%nt{34(}}xYXV0>R;;??pyX=H_kva$rZOH&En&9 z!j^hdfx?onZ?uV}QTy+9J42fTR9qcO7gpcEtlIN`IMAvUjb0m>gZ!Es>Y*$qJQ%5I2QSZc(ix?}#qT zZLBW(AmWV*CZl1ZbEDDQ)U|oL8OBT3ZCN;9(US@tEYUzzc2?`iU&V<~a&s3Wgl3Ww z7rBk$mG+|CR*^3mK`OarwT|6)%`G;{cL!1>yHK}2eXYDcH~wDa2M5!zessNB$DUK? z<}P%OvN3}(LA(wlWw|+Iu3d-R+h{G}1HwAHL zGJht*Vy=Act~*M2l;g6ruqar_XkIHK!2BHV!%uUFP{c%eFbPrVZSuj?>iMjU_x{KTR8A%>PKVLqN#^?jz|LZXeVj;a9$aKG zWm(sTH;7}g&Ycm=+!v1=;X7?#o9A9MC&8{U?3xvzRds=SURz%g0TW);0vq*|9%L(K z&;_;;w^Wp+F%^{Z$wE z0=xEgNrsK1If(fIX2bwt`&cF|3=ATLn00OwHVTOpP4q!d(A|^e1WiDaJpMBtFHXrk z%aZAkSZ_t&^F0ZhwkfM}^9pwTF*|QzQ%!77G57;`$EF$sep{bZ`@|bbN$cNyM+9@X zKB);UCdWUqk6u`jGaF$`i|qs#xFITK<|eJ_Nr0NU08 J1VAnn005}QdoTb1 literal 0 HcmV?d00001 diff --git a/src/fred2/resource.hm b/src/fred2/resource.hm new file mode 100644 index 0000000..11f2593 --- /dev/null +++ b/src/fred2/resource.hm @@ -0,0 +1,5 @@ +// Microsoft Developer Studio generated Help ID include file. +// Used by FRED.rc +// +#define HIDC_BUTTON_NEW_EVENT 0x80cb04f1 +#define HIDC_BUTTON_NEW_GOAL 0x80b3049f diff --git a/src/fred2/sexp_tree.cpp b/src/fred2/sexp_tree.cpp new file mode 100644 index 0000000..a3b55ff --- /dev/null +++ b/src/fred2/sexp_tree.cpp @@ -0,0 +1,5531 @@ +/* + * $Logfile: /Freespace2/code/Fred2/Sexp_tree.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Sexp tree handler class. Almost everything is handled by this class. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 43 9/07/99 9:22p Jefff + * added 2 more assignable medals + * + * 42 9/07/99 1:05a Andsager + * Added team-score sexp for multi team vs team missions + * + * 41 8/27/99 4:07p Andsager + * Add is-ship-visible sexp. Make ship-vanish sexp SINGLE player only + * + * 40 8/24/99 4:25p Andsager + * Add ship-vanish sexp + * + * 39 8/16/99 10:04p Andsager + * Add special-warp-dist and special-warpout-name sexp for Knossos device + * warpout. + * + * 38 8/09/99 2:00p Dave + * 2 new sexpressions. + * + * 37 8/02/99 4:26p Dave + * Added 2 new sexpressions. + * + * 36 8/02/99 1:43p Andsager + * format fix + * + * 35 7/28/99 1:36p Andsager + * Modify cargo1 to include flag CARGO_NO_DEPLETE. Add sexp + * cargo-no-deplete (only for BIG / HUGE). Modify ship struct to pack + * better. + * + * 34 7/24/99 4:56p Dave + * Added 3 new sexpressions. + * + * 33 7/21/99 8:10p Dave + * First run of supernova effect. + * + * 32 7/20/99 9:19p Andsager + * Added facing waypoint sexp + * + * 31 7/20/99 9:54a Andsager + * Add subsys-set-random sexp + * + * 30 7/19/99 12:02p Andsager + * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to + * only blow up subsystem if its strength is > 0 + * + * 29 7/13/99 3:37p Andsager + * Add secondaries-depleted sexp + * + * 28 7/12/99 12:01p Andsager + * Make message by default come from command. + * + * 27 7/08/99 12:06p Andsager + * Add turret-tagged-only and turret-tagged-clear sexp. + * + * 26 6/29/99 10:08a Andsager + * Add guardian sexp + * + * 25 6/23/99 5:51p Andsager + * Add waypoint-cap-speed. Checkin stealth ai - inactive. + * + * 24 6/16/99 10:21a Dave + * Added send-message-list sexpression. + * + * 23 6/01/99 8:35p Dave + * Finished lockarm weapons. Added proper supercap weapons/damage. Added + * awacs-set-radius sexpression. + * + * 22 5/24/99 11:28a Dave + * Sexpression for adding/removing ships from the hud escort list. + * + * 21 5/20/99 1:40p Andsager + * Fix find_text() to only look at nodes that are used. + * + * 20 5/04/99 5:21p Andsager + * + * 19 4/28/99 9:33a Andsager + * Add turret-free and turret-lock (and -all) sexp. Stargger start time + * of beam weapons beam-free and beam-free-all. + * + * 18 4/26/99 2:14p Andsager + * Add beam-protect-ship and beam-unprotect-ship sexp. + * + * 17 4/23/99 12:01p Johnson + * Added SIF_HUGE_SHIP + * + * 16 4/02/99 9:54a Dave + * Added a few more options in the weapons.tbl for beam weapons. Attempt + * at putting "pain" packets into multiplayer. + * + * 15 3/20/99 3:46p Dave + * Added support for model-based background nebulae. Added 3 new + * sexpressions. + * + * 14 3/04/99 6:09p Dave + * Added in sexpressions for firing beams and checking for if a ship is + * tagged. + * + * 13 3/01/99 10:00a Dave + * Fxied several dogfight related stats bugs. + * + * 12 2/26/99 6:01p Andsager + * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay + * + * 11 1/26/99 10:09a Andsager + * Better checking for modifying/deleting variables + * + * 10 1/25/99 5:16p Andsager + * Handle change of variable type on modify-variable + * + * 9 1/25/99 8:10a Andsager + * Add sexp_modify_variable(). Changed syntax checking to allow, adding + * operator return type ambiguous + * + * 8 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 7 1/20/99 9:02a Andsager + * Fix bug in verify_and_fix_arguments, where list of strings can have + * NULL strings. + * + * 6 1/19/99 3:57p Andsager + * Round 2 of variables + * + * 5 12/17/98 2:39p Andsager + * Added bitmaps for campaign editor. Changed input into insert() to + * include bitmaps + * + * 4 12/17/98 2:34p Andsager + * new bitmap and dialog for campaign editor + * + * 3 10/13/98 9:27a Dave + * Started neatening up freespace.h + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 179 9/25/98 1:33p Andsager + * Add color to event editor (root and chain) indicating mission directive + * + * 178 9/15/98 4:26p Allender + * added sexpression help for some sexpressions + * + * 177 6/09/98 5:15p Lawrance + * French/German localization + * + * 176 5/21/98 12:58a Hoffoss + * Fixed warnings optimized build turned up. + * + * 175 5/15/98 6:45p Hoffoss + * Made some things not appear in the release version of Fred. + * + * 174 5/14/98 10:15a Allender + * add optional argument to prevous-goal/event operators to specify what + * sexpression should return when being played as a single mission + * + * 173 5/04/98 10:57a Johnson + * Fixed bug with labeled roots allowing insert. + * + * 172 4/25/98 7:39p Allender + * fixd some small hotkey stuff. Worked on turret orientation being + * correct for multiplayer. new sexpression called end-campaign will will + * end the main campaign + * + * 171 4/23/98 5:49p Hoffoss + * Added tracking of techroom database list info in pilot files, added + * sexp to add more to list, made mouse usable on ship listing in tech + * room. + * + * 170 4/15/98 3:46p Hoffoss + * Fixed bug with getting a default argument value from an opf listing was + * utilizing temporary memory that was being destroyed before we were + * finished with it. + * + * 169 4/14/98 5:46p Hoffoss + * Added special-check operator. + * + * 168 4/14/98 5:24p Hoffoss + * Added a custom operator for training handling for Mike K. + * + * 167 4/14/98 4:19p Jim + * Fixed bug with deleting an argument to an operator that you shouldn't + * be allowed to. + * + * 166 4/07/98 10:51a Allender + * remove any allied from message senders. Make heads for mission + * specific messages play appropriately + * + * 165 4/03/98 12:17a Allender + * new sexpression to detect departed or destroyed. optionally disallow + * support ships. Allow docking with escape pods + * + * 164 3/30/98 2:57p Hoffoss + * Fixed event listing in campaign editor mode. + * + * 163 3/26/98 3:13p Duncan + * Fixed bug in goal name listing generation function. Allender forgot + * about an assumption being made with them when he used it for + * invalidate-goal. + * + * 162 3/23/98 2:46p Hoffoss + * Fixed bug with default argument available for OPF_MESSAGE even when + * there were no messages, and added "#Command" as a message source to + * listing. + * + * 161 3/21/98 7:36p Lawrance + * Move jump nodes to own lib. + * + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "sexp_tree.h" +#include "fred.h" +#include "freddoc.h" +#include "management.h" +#include "sexp.h" +#include "operatorargtypeselect.h" +#include "linklist.h" +#include "eventeditor.h" +#include "missiongoalsdlg.h" +#include "aigoals.h" +#include "missionmessage.h" +#include "missioncampaign.h" +#include "campaigneditordlg.h" +#include "hudsquadmsg.h" +#include "ignoreordersdlg.h" +#include "medals.h" +#include "controlsconfig.h" +#include "hudgauges.h" +#include "starfield.h" +#include "jumpnode.h" +#include "addvariabledlg.h" +#include "modifyvariabledlg.h" + +#define MAX_OP_MENUS 30 + +#define ID_VARIABLE_MENU 0xda00 +#define ID_ADD_MENU 0xdc00 +#define ID_REPLACE_MENU 0xde00 +// note: stay below 0xe000 so we don't collide with MFC defines.. + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +BEGIN_MESSAGE_MAP(sexp_tree, CTreeCtrl) + //{{AFX_MSG_MAP(sexp_tree) + ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag) + ON_WM_MOUSEMOVE() + ON_WM_LBUTTONUP() + ON_WM_DESTROY() + ON_WM_LBUTTONDOWN() + ON_NOTIFY_REFLECT(TVN_KEYDOWN, OnKeydown) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +static int Add_count, Replace_count; +static int Modify_variable; + +struct sexp_help_struct { + int id; + char *help; + +} Sexp_help[] = { + { OP_PLUS, "Plus (Arithmetic operator)\r\n" + "\tAdds numbers and returns results.\r\n\r\n" + "Returns a number; Takes 2 or more numeric arguments." }, + + { OP_MINUS, "Minus (Arithmetic operator)\r\n" + "\tSubtracts numbers and returns results.\r\n\r\n" + "Returns a number; Takes 2 or more numeric arguments." }, + + { OP_MOD, "Mod (Arithmetic operator)\r\n" + "\tDivides numbers and returns the remainer.\r\n\r\n" + "Returns a number; Takes 2 or more numeric arguments." }, + + { OP_MUL, "Multiply (Arithmetic operator)\r\n" + "\tMultiplies numbers and returns results.\r\n\r\n" + "Returns a number; Takes 2 or more numeric arguments." }, + + { OP_DIV, "Divide (Arithmetic operator)\r\n" + "\tDivides numbers and returns results.\r\n\r\n" + "Returns a number; Takes 2 or more numeric arguments." }, + + { OP_RAND, "Random number (Arithmetic operator)\r\n" + "\tGets a random number and returns result.\r\n\r\n" + "Returns a number; Takes 2 numeric arguments.\r\n" + "\t1:\tLow range of random number.\r\n" + "\t2:\tHigh range of random number." }, + + { OP_TRUE, "True (Boolean operator)\r\n" + "\tA true boolean state\r\n\r\n" + "Returns a boolean value." }, + + { OP_FALSE, "False (Boolean operator)\r\n" + "\tA false boolean state\r\n\r\n" + "Returns a boolean value." }, + + { OP_AND, "And (Boolean operator)\r\n" + "\tAnd is true if all of it's arguments are true.\r\n\r\n" + "Returns a boolean value; Takes 2 or more boolean arguments." }, + + { OP_OR, "Or (Boolean operator)\r\n" + "\tOr is true if any of it's arguments are true.\r\n\r\n" + "Returns a boolean value; Takes 2 or more boolean arguments." }, + + { OP_EQUALS, "Equals (Boolean operator)\r\n" + "\tIs true if all of it's arguments are equal.\r\n\r\n" + "Returns a boolean value; Takes 2 or more numeric arguments." }, + + { OP_GREATER_THAN, "Greater than (Boolean operator)\r\n" + "\tTrue if first argument is greater than the second argument.\r\n\r\n" + "Returns a boolean value; Takes 2 numeric arguments." }, + + { OP_LESS_THAN, "Less than (Boolean operator)\r\n" + "\tTrue if first argument is less than the second argument.\r\n\r\n" + "Returns a boolean value; Takes 2 numeric arguments." }, + + { OP_IS_IFF, "Is IFF (Boolean operator)\r\n" + "\tTrue if ship{s} are all of the specified team.\r\n\r\n" + "Returns a boolean value; Takes 2 or more arguments:\r\n" + "/t1:\tTeam (\"friendly\", \"hostile\" or \"unknown\").\r\n" + "\tRest:\tName of ship to check." }, + + { OP_HAS_TIME_ELAPSED, "Has time elapsed (Boolean operator)\r\n" + "\tBecomes true when the specified amount of time has elapsed (Mission time " + "becomes greater than the specified time).\r\n" + "Returns a boolean value; Takes 1 numeric argument:\r\n" + "\t1:\tThe amount of time in seconds." }, + + { OP_NOT, "Not (Boolean operator)\r\n" + "\tReturns opposite boolean value of argument (True becomes false, and " + "false becomes true).\r\n\r\n" + "Returns a boolean value; Takes 1 boolean argument." }, + + { OP_PREVIOUS_GOAL_TRUE, "Previous Mission Goal True (Boolean operator)\r\n" + "\tReturns true if the specified goal in the specified mission is true " + "(or succeeded). It returns false otherwise.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n" + "\t1:\tName of the mission.\r\n" + "\t2:\tName of the goal in the mission.\r\n" + "\t3:\t(Optional) True/False which signifies what this sexpession should return when " + "this mission is played as a single mission." }, + + { OP_PREVIOUS_GOAL_FALSE, "Previous Mission Goal False (Boolean operator)\r\n" + "\tReturns true if the specified goal in the specified mission " + "is false (or failed). It returns false otherwise.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n" + "\t1:\tName of the mission.\r\n" + "\t2:\tName of the goal in the mission.\r\n" + "\t3:\t(Optional) True/False which signifies what this sexpession should return when " + "this mission is played as a single mission." }, + + { OP_PREVIOUS_GOAL_INCOMPLETE, "Previous Mission Goal Incomplete (Boolean operator)\r\n" + "\tReturns true if the specified goal in the specified mission " + "is incomplete (not true or false). It returns false otherwise.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n" + "\t1:\tName of the mission.\r\n" + "\t2:\tName of the goal in the mission.\r\n" + "\t3:\t(Optional) True/False which signifies what this sexpession should return when " + "this mission is played as a single mission." }, + + { OP_PREVIOUS_EVENT_TRUE, "Previous Mission Event True (Boolean operator)\r\n" + "\tReturns true if the specified event in the specified mission is true " + "(or succeeded). It returns false otherwise.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n" + "\t1:\tName of the mission.\r\n" + "\t2:\tName of the event in the mission.\r\n" + "\t3:\t(Optional) True/False which signifies what this sexpession should return when " + "this mission is played as a single mission." }, + + { OP_PREVIOUS_EVENT_FALSE, "Previous Mission Event False (Boolean operator)\r\n" + "\tReturns true if the specified event in the specified mission " + "is false (or failed). It returns false otherwise.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n" + "\t1:\tName of the mission.\r\n" + "\t2:\tName of the event in the mission.\r\n" + "\t3:\t(Optional) True/False which signifies what this sexpession should return when " + "this mission is played as a single mission." }, + + { OP_PREVIOUS_EVENT_INCOMPLETE, "Previous Mission Event Incomplete (Boolean operator)\r\n" + "\tReturns true if the specified event in the specified mission " + "is incomplete (not true or false). It returns false otherwise.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments + 1 optional:\r\n" + "\t1:\tName of the mission.\r\n" + "\t2:\tName of the event in the mission.\r\n" + "\t3:\t(Optional) True/False which signifies what this sexpession should return when " + "this mission is played as a single mission." }, + + { OP_GOAL_TRUE_DELAY, "Mission Goal True (Boolean operator)\r\n" + "\tReturns true N seconds after the specified goal in the this mission is true " + "(or succeeded). It returns false otherwise.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments:\r\n" + "\t1:\tName of the event in the mission.\r\n" + "\t2:\tNumber of seconds to delay before returning true."}, + + { OP_GOAL_FALSE_DELAY, "Mission Goal False (Boolean operator)\r\n" + "\tReturns true N seconds after the specified goal in the this mission is false " + "(or failed). It returns false otherwise.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments:\r\n" + "\t1:\tName of the event in the mission.\r\n" + "\t2:\tNumber of seconds to delay before returning true."}, + + { OP_GOAL_INCOMPLETE, "Mission Goal Incomplete (Boolean operator)\r\n" + "\tReturns true if the specified goal in the this mission is incomplete. This " + "sexpression will only be useful in conjunction with another sexpression like" + "has-time-elapsed. Used alone, it will return true upon misison startup." + "Returns a boolean value; Takes 1 argument:\r\n" + "\t1:\tName of the event in the mission."}, + + { OP_EVENT_TRUE_DELAY, "Mission Event True (Boolean operator)\r\n" + "\tReturns true N seconds after the specified event in the this mission is true " + "(or succeeded). It returns false otherwise.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments:\r\n" + "\t1:\tName of the event in the mission.\r\n" + "\t2:\tNumber of seconds to delay before returning true."}, + + { OP_EVENT_FALSE_DELAY, "Mission Event False (Boolean operator)\r\n" + "\tReturns true N seconds after the specified event in the this mission is false " + "(or failed). It returns false otherwise.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments:\r\n" + "\t1:\tName of the event in the mission.\r\n" + "\t2:\tNumber of seconds to delay before returning true."}, + + { OP_EVENT_INCOMPLETE, "Mission Event Incomplete (Boolean operator)\r\n" + "\tReturns true if the specified event in the this mission is incomplete. This " + "sexpression will only be useful in conjunction with another sexpression like" + "has-time-elapsed. Used alone, it will return true upon misison startup." + "Returns a boolean value; Takes 1 argument:\r\n" + "\t1:\tName of the event in the mission."}, + + { OP_IS_DESTROYED_DELAY, "Is destroyed delay (Boolean operator)\r\n" + "\tBecomes true seconds after all specified ships have been destroyed.\r\n\r\n" + "Returns a boolean value; Takes 2 or more arguments:\r\n" + "\t1:\tTime delay in seconds (see above).\r\n" + "\tRest:\tName of ship (or wing) to check status of." }, + + { OP_IS_SUBSYSTEM_DESTROYED_DELAY, "Is subsystem destroyed delay (Boolean operator)\r\n" + "\tBecomes true seconds after the specified subsystem of the specified " + "ship is destroyed.\r\n\r\n" + "Returns a boolean value; Takes 3 arguments:\r\n" + "\t1:\tName of ship the subsystem we are checking is on.\r\n" + "\t2:\tThe name of the subsystem we are checking status of.\r\n" + "\t3:\tTime delay in seconds (see above)." }, + + { OP_IS_DISABLED_DELAY, "Is disabled delay (Boolean operator)\r\n" + "\tBecomes true seconds after the specified ship(s) are disabled. A " + "ship is disabled when all of it's engine subsystems are destroyed. All " + "ships must be diabled for this function to return true.\r\n\r\n" + "Returns a boolean value; Takes 2 or more arguments:\r\n" + "\t1:\tTime delay is seconds (see above).\r\n" + "\tRest:\tNames of ships to check disabled status of." }, + + { OP_IS_DISARMED_DELAY, "Is disarmed delay (Boolean operator)\r\n" + "\tBecomes true seconds after the specified ship(s) are disarmed. A " + "ship is disarmed when all of it's turret subsystems are destroyed. All " + "ships must be disarmed for this function to return true.\r\n\r\n" + "Returns a boolean value; Takes 2 or more arguments:\r\n" + "\t1:\tTime delay is seconds (see above).\r\n" + "\tRest:\tNames of ships to check disarmed status of." }, + + { OP_HAS_DOCKED_DELAY, "Has docked delay (Boolean operator)\r\n" + "\tBecomes true seconds after the specified ships have docked the " + "specified number of times.\r\n\r\n" + "Returns a boolean value; Takes 4 arguments:\r\n" + "\t1:\tThe name of the docker ship\r\n" + "\t2:\tThe name of the dockee ship\r\n" + "\t3:\tThe number of times they have to have docked\r\n" + "\t4:\tTime delay in seconds (see above)." }, + + { OP_HAS_UNDOCKED_DELAY, "Has undocked delay (Boolean operator)\r\n" + "\tBecomes true seconds after the specified ships have undocked the " + "specified number of times.\r\n\r\n" + "Returns a boolean value; Takes 4 arguments:\r\n" + "\t1:\tThe name of the docker ship\r\n" + "\t2:\tThe name of the dockee ship\r\n" + "\t3:\tThe number of times they have to have undocked\r\n" + "\t4:\tTime delay in seconds (see above)." }, + + { OP_HAS_ARRIVED_DELAY, "Has arrived delay (Boolean operator)\r\n" + "\tBecomes true seconds after the specified ship(s) have arrived into the mission\r\n\r\n" + "Returns a boolean value; Takes 2 or more arguments:\r\n" + "\t1:\tTime delay in seconds (see above).\r\n" + "\tRest:\tName of ship (or wing) we want to check has arrived." }, + + { OP_HAS_DEPARTED_DELAY, "Has departed delay (Boolean operator)\r\n" + "\tBecomes true seconds after the specified ship(s) or wing(s) have departed " + "from the mission by warping out. If any ship was destroyed, this operator will " + "never be true.\r\n\r\n" + "Returns a boolean value; Takes 2 or more arguments:\r\n" + "\t1:\tTime delay in seconds (see above).\r\n" + "\tRest:\tName of ship (or wing) we want to check has departed." }, + + { OP_WAYPOINTS_DONE_DELAY, "Waypoints done delay (Boolean operator)\r\n" + "\tBecomes true seconds after the specified ship has completed flying the " + "specified waypoint path.\r\n\r\n" + "Returns a boolean value; Takes 3 arguments:\r\n" + "\t1:\tName of ship we are checking.\r\n" + "\t2:\tWaypoint path we want to check if ship has flown.\r\n" + "\t3:\tTime delay in seconds (see above)." }, + + { OP_SHIP_TYPE_DESTROYED, "Ship Type Destroyed (Boolean operator)\r\n" + "\tBecomes true when the specified percentage of ship types in this mission " + "have been destroyed. The ship type is a generic type such as fighter/bomber, " + "transport, etc. Fighters and bombers count as the same type.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments:\r\n" + "\t1:\tPercentage of ships that must be destroyed.\r\n" + "\t2:\tShip type to check for." }, + + { OP_TIME_SHIP_DESTROYED, "Time ship destroyed (Time operator)\r\n" + "\tReturns the time the specified ship was destroy.\r\n\r\n" + "Returns a numeric value; Takes 1 argument:\r\n" + "\t1:\tName of ship we want to check." }, + + { OP_TIME_SHIP_ARRIVED, "Time ship arrived (Time operator)\r\n" + "\tReturns the time the specified ship arrived into the mission.\r\n\r\n" + "Returns a numeric value; Takes 1 argument:\r\n" + "\t1:\tName of ship we want to check." }, + + { OP_TIME_SHIP_DEPARTED, "Time ship departed (Time operator)\r\n" + "\tReturns the time the specified ship departed the mission by warping out. Being " + "destroyed doesn't count departed.\r\n\r\n" + "Returns a numeric value; Takes 1 argument:\r\n" + "\t1:\tName of ship we want to check." }, + + { OP_TIME_WING_DESTROYED, "Time wing destroyed (Time operator)\r\n" + "\tReturns the time the specified wing was destroy.\r\n\r\n" + "Returns a numeric value; Takes 1 argument:\r\n" + "\t1:\tName of wing we want to check." }, + + { OP_TIME_WING_ARRIVED, "Time wing arrived (Time operator)\r\n" + "\tReturns the time the specified wing arrived into the mission.\r\n\r\n" + "Returns a numeric value; Takes 1 argument:\r\n" + "\t1:\tName of wing we want to check." }, + + { OP_TIME_WING_DEPARTED, "Time wing departed (Time operator)\r\n" + "\tReturns the time the specified wing departed the mission by warping out. All " + "ships in the wing have to have warped out. If any are destroyed, the wing can " + "never be considered departed.\r\n\r\n" + "Returns a numeric value; Takes 1 argument:\r\n" + "\t1:\tName of ship we want to check." }, + + { OP_MISSION_TIME, "Mission time (Time operator)\r\n" + "\tReturns the current time into the mission.\r\n\r\n" + "Returns a numeric value." }, + + { OP_TIME_DOCKED, "Time docked (Time operator)\r\n" + "\tReturns the time the specified ships docked.\r\n\r\n" + "Returns a numeric value; Takes 3 arguments:\r\n" + "\t1:\tThe name of the docker ship.\r\n" + "\t2:\tThe name of the dockee ship.\r\n" + "\t3:\tThe number of times they must have docked to be true." }, + + { OP_TIME_UNDOCKED, "Time undocked (Time operator)\r\n" + "\tReturns the time the specified ships undocked.\r\n\r\n" + "Returns a numeric value; Takes 3 arguments:\r\n" + "\t1:\tThe name of the docker ship.\r\n" + "\t2:\tThe name of the dockee ship.\r\n" + "\t3:\tThe number of times they must have undocked to be true." }, + + { OP_SHIELDS_LEFT, "Sheilds left (Status operator)\r\n" + "\tReturns the current level of the specified ship's shields as a percentage.\r\n\r\n" + "Returns a numeric value; Takes 1 argument:\r\n" + "\t1:\tName of ship to check." }, + + { OP_HITS_LEFT, "Hits left (Status operator)\r\n" + "\tReturns the current level of the specified ship's hull as a percentage.\r\n\r\n" + "Returns a numeric value; Takes 1 argument:\r\n" + "\t1:\tName of ship to check." }, + + { OP_HITS_LEFT_SUBSYSTEM, "Hits left subsystem (Status operator)\r\n" + "\tReturns the current level of the specified ship's subsystem integrity as a percentage.\r\n\r\n" + "Returns a numeric value; Takes 1 argument:\r\n" + "\t1:\tName of ship to check.\r\n" + "\t2:\tName of subsystem on ship to check." }, + + { OP_DISTANCE, "Distance (Misc. Operator)\r\n" + "\tReturns the distance between 2 objects. These objects can be either a ship, " + "a wing, or a waypoint.\r\n\r\n" + "Returns a numeric value; Takes 2 arguments:\r\n" + "\t1:\tThe name of one of the objects.\r\n" + "\t2:\tThe name of the other object." }, + + { OP_LAST_ORDER_TIME, "Last order time (Status operator)\r\n" + "\tReturns true if seconds have elapsed since one or more ships have received " + "a meaningful order from the player. A meaningful order is currently any order that " + "is not the warp out order.\r\n\r\n" + "Returns a boolean value; Takes 2 or more arguments:\r\n" + "\t1:\tTime in seconds that must elapse.\r\n" + "\tRest:\tName of ship or wing to check for having received orders." }, + + { OP_WHEN, "When (Conditional operator)\r\n" + "\tPerforms specified actions when a condition becomes true\r\n\r\n" + "Takes 2 or more arguments:\r\n" + "\t1:\tBoolean expression that must be true for actions to take place.\r\n" + "\tRest:\tActions to take when boolean expression becomes true." }, + + { OP_COND, "Blah" }, + + { OP_CHANGE_IFF, "Change IFF (Action operator)\r\n" + "\tSets the specified ship(s) to the specified team.\r\n" + "Takes 2 or more arguments:\r\n" + "\t1:\tTeam to change to (\"friendly\", \"hostile\" or \"unknown\").\r\n" + "\tRest:\tName of ship to change team status of." }, + + { OP_MODIFY_VARIABLE, "Modify-variable (Misc. operator)\r\n" + "\tModifies variable to specified value\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of Variable.\r\n" + "\t2:\tValue to be set." }, + + { OP_PROTECT_SHIP, "Protect ship (Action operator)\r\n" + "\tProtects a ship from being attacked by any enemy ship. Any ship" + "that is protected will not come under enemy fire.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\tAll:\tName of ship(s) to protect." }, + + { OP_UNPROTECT_SHIP, "Unprotect ship (Action operator)\r\n" + "\tUnprotects a ship from being attacked by any enemy ship. Any ship" + "that is not protected can come under enemy fire. This function is the opposite" + "of protect-ship.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\tAll:\tName of ship(s) to protect." }, + + { OP_BEAM_PROTECT_SHIP, "Beam Protect ship (Action operator)\r\n" + "\tProtects a ship from being attacked with beam weapon. Any ship" + "that is beam protected will not come under enemy beam fire.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\tAll:\tName of ship(s) to protect." }, + + { OP_BEAM_UNPROTECT_SHIP, "Beam Unprotect ship (Action operator)\r\n" + "\tUnprotects a ship from being attacked with beam weapon. Any ship" + "that is not beam protected can come under enemy beam fire. This function is the opposite" + "of beam-protect-ship.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\tAll:\tName of ship(s) to protect." }, + + { OP_SEND_MESSAGE, "Send message (Action operator)\r\n" + "\tSends a message to the player. Can be send by a ship, wing, or special " + "source. To send it from a special source, make the first character of the first " + "argument a \"#\".\r\n\r\n" + "Takes 3 arguments:\r\n" + "\t1:\tName of who the message is from.\r\n" + "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n" + "\t3:\tName of message (from message editor)." }, + + { OP_SELF_DESTRUCT, "Self destruct (Action operator)\r\n" + "\tCauses the specified ship(s) to self destruct.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\tAll:\tName of ship to self destruct." }, + + { OP_NEXT_MISSION, "Next Mission (Action operator)\r\n" + "\tThe next mission operator is used for campaign branching in the campaign editor. " + "It specifies which mission should played be next in the campaign. This operator " + "generally follows a 'when' or 'cond' statment in the campaign file.\r\n\r\n" + "Takes 1 argument:\r\n" + "\t1:\tName of mission (filename) to proceed to." }, + + { OP_CLEAR_GOALS, "Clear goals (Action operator)\r\n" + "\tClears the goals for the specified ships and/or wings.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\tAll:\tName of ship or wing." }, + + { OP_ADD_GOAL, "Add goal (Action operator)\r\n" + "\tAdds a goal to a ship or wing.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of ship or wing to all goal to.\r\n" + "\t2:\tGoal to add." }, + + { OP_SABOTAGE_SUBSYSTEM, "Sabotage subystem (Action operator)\r\n" + "\tReduces the specified subsystem integrity by the specified percentage." + "If the percntage strength of the subsystem (after completion) is less than 0%," + "subsystem strength is set to 0%.\r\n\r\n" + "Takes 3 arguments:\r\n" + "\t1:\tName of ship subsystem is on.\r\n" + "\t2:\tName of subsystem to sabotage.\r\n" + "\t3:\tPercentage to reduce subsystem integrity by." }, + + { OP_REPAIR_SUBSYSTEM, "Repair Subystem (Action operator)\r\n" + "\tIncreases the specified subsystem integrity by the specified percentage." + "If the percntage strength of the subsystem (after completion) is greater than 100%," + "subsystem strength is set to 100%.\r\n\r\n" + "Takes 3 arguments:\r\n" + "\t1:\tName of ship subsystem is on.\r\n" + "\t2:\tName of subsystem to repair.\r\n" + "\t3:\tPercentage to increase subsystem integrity by." }, + + { OP_SET_SUBSYSTEM_STRNGTH, "Set Subsystem Strength (Action operator)\r\n" + "\tSets the specified subsystem to the the specified percentage." + "If the percentage specified is < 0, strength is set to 0. If the percentage is " + "> 100 % the subsystem strength is set to 100%.\r\n\r\n" + "Takes 3 arguments:\r\n" + "\t1:\tName of ship subsystem is on.\r\n" + "\t2:\tName of subsystem to set strength.\r\n" + "\t3:\tPercentage to set subsystem integrity to." }, + + { OP_INVALIDATE_GOAL, "Invalidate goal (Action operator)\r\n" + "\tMakes a mission goal invalid, which causes it to now show up on mission goals " + "screen, or be evaluated.\r\n" + "Takes 1 or more arguments:\r\n" + "\tAll:\tName of mission goal to invalidate." }, + + { OP_VALIDATE_GOAL, "Validate goal (Action operator)\r\n" + "\tMakes a mission goal valid again, so it shows up on mission goals screen.\r\n" + "Takes 1 or more arguments:\r\n" + "\tAll:\tName of mission goal to validate." }, + + { OP_SEND_RANDOM_MESSAGE, "Send random message (Action operator)\r\n" + "\tSends a random message to the player from those supplied. Can be send by a " + "ship, wing, or special source. To send it from a special source, make the first " + "character of the first argument a \"#\".\r\n\r\n" + "Takes 3 or more arguments:\r\n" + "\t1:\tName of who the message is from.\r\n" + "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\")." + "\tRest:\tName of message (from message editor)." }, + + { OP_TRANSFER_CARGO, "Transfer Cargo (Action operator)\r\n" + "\tTransfers the cargo from one ship to another ship.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of ship that cargo is being transferred from.\r\n" + "\t2:\tName of ship that cargo is being transferred to." }, + + { OP_EXCHANGE_CARGO, "Exchange Cargo (Action operator)" + "\tExchanges the cargos of two ships. If one of the two ships contains no cargo, " + "the cargo is transferred instead.\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of one of the ships.\r\n" + "\t2:\tName of the other ship." }, + + { OP_INT3, "Error (Debug directive)\r\n" + "Causes the game to halt with an error." }, + + { OP_AI_CHASE, "Ai-chase (Ship goal)\r\n" + "\tCauses the specified ship to chase and attack the specified target.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of ship to chase.\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_DOCK, "Ai-dock (Ship goal)\r\n" + "\tCauses one ship to dock with another ship.\r\n\r\n" + "Takes 4 arguments:\r\n" + "\t1:\tName of dockee ship (The ship that \"docker\" will dock with).\r\n" + "\t2:\tDocker's docking point - Which dock point docker uses to dock.\r\n" + "\t3:\tDockee's docking point - Which dock point on dockee docker will move to.\r\n" + "\t4:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_UNDOCK, "Ai-undock (Ship goal)\r\n" + "\tCauses the specified ship to undock from who it is currently docked with.\r\n\r\n" + "Takes 1 arguments:\r\n" + "\t1:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_WARP_OUT, "Ai-warp-out (Ship/Wing Goal)\r\n" + "\tCauses the specified ship/wing to warp out of the mission. Currently, the ship will " + "warp out at it's current location. This behavior will change. Currently, the first " + "argument means nothing.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of waypoint path to follow to warp out (not used).\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_WAYPOINTS, "Ai-waypoints (Ship goal)\r\n" + "\tCauses the specified ship to fly a waypoint path continuously.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of waypoint path to fly.\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_WAYPOINTS_ONCE, "Ai-waypoints once (Ship goal)\r\n" + "\tCauses the specified ship to fly a waypoint path.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of waypoint path to fly.\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_DESTROY_SUBSYS, "Ai-destroy subsys (Ship goal)\r\n" + "\tCauses the specified ship to attack and try and destroy the specified subsystem " + "on the specified ship.\r\n\r\n" + "Takes 3 arguments:\r\n" + "\t1:\tName of ship subsystem is on.\r\n" + "\t2:\tName of subsystem on the ship to attack and destroy.\r\n" + "\t3:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_CHASE_WING, "Ai-chase wing (Ship goal)\r\n" + "\tCauses the specified ship to chase and attack the specified target.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of wing to chase.\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_DISABLE_SHIP, "Ai-disable-ship (Ship/wing goal)\r\n" + "\tThis AI goal causes a ship/wing to destroy all of the engine subsystems on " + "the specified ship. This goal is different than ai-destroy-subsystem since a ship " + "may have multiple engine subsystems requiring the use of > 1 ai-destroy-subsystem " + "goals.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of ship whose engine subsystems should be destroyed\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_DISARM_SHIP, "Ai-disarm-ship (Ship/wing goal)\r\n" + "\tThis AI goal causes a ship/wing to destroy all of the turret subsystems on " + "the specified ship. This goal is different than ai-destroy-subsystem since a ship " + "may have multiple turret subsystems requiring the use of > 1 ai-destroy-subsystem " + "goals.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of ship whose turret subsystems should be destroyed\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_GUARD, "Ai-guard (Ship goal)\r\n" + "\tCauses the specified ship to guard a ship from other ships not on the same team.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of ship to guard.\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_CHASE_ANY, "Ai-chase any (Ship goal)\r\n" + "\tCauses the specified ship to chase and attack any ship on the opposite team.\r\n\r\n" + "Takes 1 arguments:\r\n" + "\t1:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_GUARD_WING, "Ai-guard wing (Ship goal)\r\n" + "\tCauses the specified ship to guard a wing of ships from other ships not on the " + "same team.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of wing to guard.\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_NOP, "Do-nothing (Action operator)\r\n" + "\tDoes nothing. This is used as the default for any required action arguments " + "of an operator." }, + + { OP_KEY_PRESSED, "Key-pressed (Boolean training operator)\r\n" + "\tBecomes true when the specified default key has been pressed. Default key " + "refers to the what the key normally is when not remapped. FreeSpace will " + "automatically account for any keys that have been remapped. If the optional " + "delay is specified, becomes true that many seconds after the key has been pressed.\r\n\r\n" + "Returns a boolean value; Takes 1 or 2 arguments:\r\n" + "\t1:\tDefault key to check for.\r\n" + "\t2:\tDelay before operator registers as true (optional).\r\n" }, + + { OP_KEY_RESET, "Key-reset (Training operator)\r\n" + "\tMarks the specified default key as having not been pressed, so key-pressed will be false " + "until the player presses it again. See key-pressed help for more information about " + "what a default key is.\r\n\r\n" + "Returns a boolean value; Takes 1 argument:\r\n" + "\t1:\tDefault key to reset." }, + + { OP_TARGETED, "Targeted (Boolean training operator)\r\n" + "\tIs true as long as the player has the specified ship (or ship's subsystem) targeted, " + "or has been targeted for the specified amount of time.\r\n\r\n" + "Returns a boolean value; Takes 1 to 3 arguments (first required, rest optional):\r\n" + "\t1:\tName of ship to check if targeted by player.\r\n" + "\t2:\tLength of time target should have been kept for (optional).\r\n" + "\t3:\tName of subsystem on ship to check if targeted (optional)." }, + + { OP_SPEED, "Speed (Boolean training operator)\r\n" + "\tBecomes true when the player has been within the specified speed range set by " + "set-training-context-speed for the specified amount of time.\r\n\r\n" + "Returns a boolean value; Takes 1 argument:\r\n" + "\t1:\tTime in seconds." }, + + { OP_FACING, "Facing (Boolean training operator)\r\n" + "\tIs true as long as the specified ship is within the player's specified " + "forward cone. A forward cone is defined as any point that the angle between the " + "vector of the point and the player, and the forward facing vector is within the " + "given angle.\r\n\r\n" + "Returns a boolean value; Takes 2 argument:\r\n" + "\t1:\tShip to check is withing forward cone.\r\n" + "\t2:\tAngle in degrees of the forward cone." }, + + { OP_FACING2, "Facing Waypoint(Boolean training operator)\r\n" + "\tIs true as long as the specified first waypoint is within the player's specified " + "forward cone. A forward cone is defined as any point that the angle between the " + "vector of the point and the player, and the forward facing vector is within the " + "given angle.\r\n\r\n" + "Returns a boolean value; Takes 2 argument:\r\n" + "\t1:\tName of waypoint path whose first point is withing forward cone.\r\n" + "\t2:\tAngle in degrees of the forward cone." }, + + { OP_ORDER, "Order (Boolean training operator)\r\n" + "\tBecomes true when the player had given the specified ship or wing the specified order.\r\n\r\n" + "Returns a boolean value; Takes 2 arguments:\r\n" + "\t1:\tName of ship or wing to check if given order to.\r\n" + "\t2:\tName of order to check if player has given." }, + + { OP_WAYPOINT_MISSED, "Waypoint-missed (Boolean training operator)\r\n" + "\tBecomes true when a waypoint is flown, but the waypoint is ahead of the one " + "they are supposed to be flying. The one they are supposed to be flying is the " + "next one in sequence in the path after the last one they have hit.\r\n\r\n" + "Returns a boolean value; Takes no arguments." }, + + { OP_PATH_FLOWN, "Path-flown (Boolean training operator)\r\n" + "\tBecomes true when all the waypoints in the path have been flown, in sequence.\r\n\r\n" + "Returns a boolean value; Takes no arguments." }, + + { OP_WAYPOINT_TWICE, "Waypoint-twice (Boolean training operator)\r\n" + "\tBecomes true when a waypoint is hit that is before the last one hit, which " + "indicates they have flown a waypoint twice.\r\n\r\n" + "Returns a boolean value; Takes no arguments." }, + + { OP_TRAINING_MSG, "Training-msg (Action training operator)\r\n" + "\tSends the player a training message. Uses the same messages as normal messages, " + "only they get displayed differently using this operator. If a secondary message " + "is specified, it is sent the last time, while the primary message is sent all other " + "times (event should have a repeat count greater than 1).\r\n\r\n" + "Takes 1-3 arguments:\r\n" + "\t1:\tName of primary message to send.\r\n" + "\t2:\tName of secondary message to send (optional).\r\n" + "\t3:\tDelay (in seconds) to wait before sending message. (optional)\r\n" + "\t4:\tAmount of Time (in seconds) to display message (optional)." }, + + { OP_SET_TRAINING_CONTEXT_FLY_PATH, "Set-training-context-fly-path (Training context operator)\r\n" + "\tTells FreeSpace that the player is expected to fly a waypoint path. This must be " + "executed before waypoint-missed, waypoint-twice and path-flown operators become valid.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of waypoint path player should fly.\r\n" + "\t2:\tDistance away a player needs to be from a waypoint for it to be registered as flown." }, + + { OP_SET_TRAINING_CONTEXT_SPEED, "Set-training-context-speed (Training context operator)\r\n" + "\tTells FreeSpace that the player is expected to fly within a certain speed range. Once " + "this operator has been executed, you can measure how long they have been within this " + "speed range with the speed operator.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tMinimum speed of range player is to fly between.\r\n" + "\t2:\tMaximum speed of range player is to fly between." }, + + { OP_GRANT_PROMOTION, "Grant promotion (Action operator)\r\n" + "\tIn a single player game, this function grants a player an automatic promotion to the " + "next rank which the player can obtain. If he is already at the highest rank, this " + "operator has no effect. It takes no arguments." }, + + { OP_GRANT_MEDAL, "Grant medal (Action operator)\r\n" + "\tIn single player missions, this function grants the given medal to the player. " + "Currently, only 1 medal will be allowed to be given per mission.\r\n\r\n" + "Takes 1 argument:\r\n" + "\t1:\tName of medal to grant to player." }, + + { OP_GOOD_SECONDARY_TIME, "Set prefered secondary weapons\r\n" + "\tThis sexpression is used to inform the AI about prefered secondary weapons to " + "fire during combat. When this expression is evaulated, any AI ships of the given " + "team prefer to fire the given weapon at the given ship. (Prefered over other " + "secondary weapons)\r\n\r\n" + "Takes 4 argument:\r\n" + "\t1:\tTeam name which will prefer firing given weapon\r\n" + "\t2:\tMaximum number of this type of weapon above team can fire.\r\n" + "\t3:\tWeapon name (list includes only the valid weapons for this expression\r\n" + "\t4:\tShip name at which the above named team should fire the above named weapon." }, + + { OP_AND_IN_SEQUENCE, "And in sequence (Boolean operator)\r\n" + "\tReturns true if all of it's arguments have become true in the order they are " + "listed in.\r\n\r\n" + "Returns a boolean value; Takes 2 or more boolean arguments." }, + + { OP_SKILL_LEVEL_AT_LEAST, "Skill level at least (Boolean operator)\r\n" + "\tReturns true if the player has selected the given skill level or higher.\r\n\r\n" + "Returns a boolean value; Takes 1 arguments:\r\n" + "\t1:\tName of the skill level to check." }, + + { OP_NUM_PLAYERS, "Num players (Status operator)\r\n" + "\tReturns the current number of players (multiplayer) playing in the current mission.\r\n\r\n" + "Returns a numeric value; Takes no arguments." }, + + { OP_IS_CARGO_KNOWN, "Is cargo known (Boolean operator)\r\n" + "\tReturns true if all of the specified objects' cargo is known by the player (i.e. they " + "have scanned each one.\r\n\r\n" + "Returns a boolean value; Takes 1 or more arguments:\r\n" + "\tAll:\tName of ship to check if it's cargo is known." }, + + { OP_HAS_BEEN_TAGGED_DELAY, "Has ship been tagged (delay) (Boolean operator)\r\n" + "\tReturns true if all of the specified ships have been tagged.\r\n\r\n" + "Returns a boolean value after seconds when all ships have been tagged; Takes 2 or more arguments:\r\n" + "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned." + "\tRest:\tNames of ships to check if tagged.." }, + + { OP_CAP_SUBSYS_CARGO_KNOWN_DELAY, "Is capital ship subsystem cargo known (delay) (Boolean operator)\r\n" + "\tReturns true if all of the specified subsystem cargo is known by the player.\r\n" + "\tNote: Cargo must be explicitly named.\r\n\r\n" + "Returns a boolean value after seconds when all cargo is known; Takes 3 or more arguments:\r\n" + "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned.\r\n" + "\t2:\tName of Captial ship\r\n" + "\tRest:\tNames of subsystems to check for cargo known.." }, + + { OP_CARGO_KNOWN_DELAY, "Is cargo known (delay) (Boolean operator)\r\n" + "\tReturns true if all of the specified objects' cargo is known by the player (i.e. they " + "have scanned each one.\r\n\r\n" + "Returns a boolean value after seconds when all cargo is known; Takes 2 or more arguments:\r\n" + "\t1:\tDelay in seconds after which sexpression will return true when all cargo scanned." + "\tRest:\tNames of ships/cargo to check for cargo known.." }, + + { OP_WAS_PROMOTION_GRANTED, "Was promotion granted (Boolean operator)\r\n" + "\tReturns true if a promotion was granted via the 'Grant promotion' operator in the mission.\r\n\r\n" + "Returns a boolean value; Takes no arguments." }, + + { OP_WAS_MEDAL_GRANTED, "Was medal granted (Boolean operator)\r\n" + "\tReturns true if a medal was granted via via the 'Grant medal' operator in the mission. " + "If you provide the optional argument to this operator, then true is only returned if the " + "specified medal was granted.\r\n\r\n" + "Returns a boolean value; Takes 0 or 1 arguments:\r\n" + "\t1:\tName of medal to specifically check for (optional)." }, + + { OP_GOOD_REARM_TIME, "Good rearm time (Action operator)\r\n" + "\tInforms the game logic that right now is a good time for a given team to attempt to " + "rearm their ships. The time parameter specified how long the \"good time\" will last.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tTeam Name\r\n" + "\t2:\tTime in seconds rearm window should last" }, + + { OP_ALLOW_SHIP, "Allow ship (Action operator)\r\n" + "\tThis operator makes the given ship type available to the Terran team. Players will be " + "able to have ships of this type in their starting wings in all future missions of this " + "campaign.\r\n\r\n" + "Takes 1 arguments:\r\n" + "\t1:\tName of ship type (or ship class) to allow." }, + + { OP_ALLOW_WEAPON, "Allow weapon (Action operator)\r\n" + "\tThis operator makes the given weapon available to the Terran team. Players will be " + "able to equip ships with in all future missions of this campaign.\r\n\r\n" + "Takes 1 arguments:\r\n" + "\t1:\tName of weapon (primary or secondary) to allow." }, + + { OP_TECH_ADD_SHIP, "Tech add ship (Action operator)\r\n" + "\tThis operator makes the given ship type available in the techroom database. Players will " + "then be able to view this ship's specs there.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\tAll:\tName of ship type (or ship class) to add." }, + + { OP_TECH_ADD_WEAPON, "Tech add weapon (Action operator)\r\n" + "\tThis operator makes the given weapon available in the techroom database. Players will " + "then be able to view this weapon's specs there.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\tAll:\tName of weapon (primary or secondary) to add." }, + + { OP_AI_EVADE_SHIP, "Ai-evade ship (Ship goal)\r\n" + "\tCauses the specified ship to go into evade mode and run away like the weak " + "sally-boy it is.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of ship to evade from.\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_STAY_NEAR_SHIP, "Ai-stay near ship (Ship goal)\r\n" + "\tCauses the specified ship to keep itself near the given ship and not stray too far " + "away from it.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of ship to stay near.\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_KEEP_SAFE_DISTANCE, "Ai-keep safe distance (Ship goal)\r\n" + "\tTells the specified ship to stay a safe distance away from any ship that isn't on the " + "same team as it.\r\n\r\n" + "Takes 1 argument:\r\n" + "\t1:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_IGNORE, "Ai-ignore (Ship goal)\r\n" + "\tTells the specified ship to ignore the given ship and not consider it as a valid " + "target to attack.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tName of ship to ignore.\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_STAY_STILL, "Ai-stay still (Ship goal)\r\n" + "\tCauses the specified ship to stay still. The ship will do nothing until attacked at " + "which time the ship will come to life and defend itself.\r\n\r\n" + "Takes 2 arguments:\r\n" + "\t1:\tShip or waypoint the ship staying still will directly face (currently not implemented)\r\n" + "\t2:\tGoal priority (number between 0 and 89)." }, + + { OP_AI_PLAY_DEAD, "Ai-play dead (Ship goal)\r\n" + "\tCauses the specified ship to pretend that it is dead and not do anything. This " + "expression should be used to indicate that a ship has no pilot and cannot respond " + "to any enemy threats. A ship playing dead will not respond to any attack. This " + "should really be named ai-is-dead\r\n\r\n" + "Takes 1 argument:\r\n" + "\t1:\tGoal priority (number between 0 and 89)." }, + + { OP_FLASH_HUD_GAUGE, "Ai-flash hud gauge (Training goal)\r\n" + "\tCauses the specified hud gauge to flash to draw the player's attention to it.\r\n\r\n" + "Takes 1 argument:\r\n" + "\t1:\tName of hud gauge to flash." }, + + { OP_SHIP_VISIBLE, "ship-visible\r\n" + "\tCauses the ships listed in this sexpression to be visible with player sensors.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\t1+:\tName of ships to make visible to sensors." }, + + { OP_SHIP_INVISIBLE, "ship-invisible\r\n" + "\tCauses the ships listed in this sexpression to be invisible to player sensors.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\t1+:\tName of ships to make invisible to sensors." }, + + { OP_SHIP_VULNERABLE, "ship-vulnerable\r\n" + "\tCauses the ship listed in this sexpression to be vulnerable to weapons.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\t1+:\tName of ships to make vulnerable to weapons." }, + + { OP_SHIP_INVULNERABLE, "ship-invulnerable\r\n" + "\tCauses the ships listed in this sexpression to be invulnerable to weapons. Use with caution!!!!\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\t1+:\tName of ships to make invulnerable to weapons." }, + + { OP_SHIP_GUARDIAN, "ship-guardian\r\n" + "\tCauses the ships listed in this sexpression to not be killable by weapons. Use with caution!!!!\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\t1+:\tName of ships to make invulnerable to weapons." }, + + { OP_SHIP_NO_GUARDIAN, "ship-no-guardian\r\n" + "\tCauses the ships listed in this sexpression to be killable by weapons, if not invulnerable.\r\n\r\n" + "Takes 1 or more arguments:\r\n" + "\t1+:\tName of ships to make vulnerable to weapons." }, + + { OP_PERCENT_SHIPS_DEPARTED, "percent-ships-departed\r\n" + "\tBoolean function which returns true if the percentage of ships in the listed ships and wings " + "which have departed is greater or equal to the given percentage. For wings, all ships of all waves " + "are used for calculation for the total possible ships to depart.\r\n\r\n" + "Takes 2 or more arguments:\r\n" + "\t1:\tPercentge of departed ships at which this function will return true.\r\n" + "\t2+:\tList of ships/wing whose departure status should be determined." }, + + { OP_PERCENT_SHIPS_DESTROYED, "percent-ships-destroyed\r\n" + "\tBoolean function which returns true if the percentage of ships in the listed ships and wings " + "which have been destroyed is greater or equal to the given percentage. For wings, all ships of all waves " + "are used for calculation for the total possible ships to be destroyed.\r\n\r\n" + "Takes 2 or more arguments:\r\n" + "\t1:\tPercentge of destroyed ships at which this function will return true.\r\n" + "\t2+:\tList of ships/wing whose destroyed status should be determined." }, + + { OP_RED_ALERT, "red-alert\r\n" + "\tCauses Red Alert status in a mission. This function ends the current mission, and moves to " + "the next mission in the campaign under red alert status. There should only be one branch from " + "a mission that uses this expression\r\n\r\n" + "Takes no arguments."}, + + { OP_DEPART_NODE_DELAY, "depart-node-delay\r\n" + "\tReturns true N seconds after the listed ships depart, if those ships depart within the " + "radius of the given jump node. The delay value is given in seconds.\r\n\r\n" + "Takes 3 or more arguments:r\n" + "\t1:\tDelay in seconds after the last ship listed departe before this expression can return true.\r\n" + "\t2:\tName of a jump node\r\n" + "\t3+:\tList of ships to check for departure within radius of the jump node." }, + + { OP_DESTROYED_DEPARTED_DELAY, "destroyed-or-departed-delay\r\n" + "\tReturns true N seconds after all the listed ships or wings have been destroyed or have " + "departed.\r\n\r\n" + "Takes 2 or more arguments:\r\n" + "\t1:\tDelay in seconda after the last ship/wing is destroyed or departerd this expression can return true.\r\n" + "\t2+:\tName of a ship or wing" }, + + { OP_SPECIAL_CHECK, "Special-check\r\n" + "\tDo some special check in training. Ask Mike K. about how it works.\r\n\r\n" + "Returns a boolean value; Takes 1 argument:\r\n" + "\t1:\tExtra special number (tm)" }, + + { OP_END_CAMPAIGN, "end-campaign\r\n" + "\tEnds the builtin campaign. Should only be used by the main FreeSpace campaign\r\n" }, + + { OP_WARP_BROKEN, "break-warp\r\n" + "\tBreak the warp drive on the specified ship. A broken warp drive can be repaired by " + "a repair ship. Takes 1 or more arguments:\r\n" + "\t1:\tList of ships to break the warp drive on" }, + { OP_WARP_NOT_BROKEN, "fix-warp\r\n" + "\tFixes a broken warp drive instantaneously. This option applies to warp drives broken with " + "the break-warp sepxression. Takes 1 or more arguments:\r\n" + "\t1:\tList of ships whose warp drive should be fixed"}, + { OP_WARP_NEVER, "never-warp\r\n" + "\tNever allows a ship to warp out. When this sexpression is used, the given ships will " + "never be able to warp out. The warp drive cannot be repaired. Takes 1 or more arguments:\r\n" + "\t1:\tList of ships whose are not allowed to warp out under any condition"}, + { OP_WARP_ALLOWED, "allow-warp\r\n" + "\tAllows a ship which was previously not allowed to warp out to do so. When this sexpression is " + "used, the given ships will be able to warp out again. Takes 1 or more arguments:\r\n" + "\t1:\tList of ships whose are allowed to warp out"}, + { OP_JETTISON_CARGO, "jettison-cargo-delay\r\n" + "\tCauses a cargo carrying ship to jettison its cargo without the undocking procedure. Takes 2 arguments"}, + { OP_BEAM_FIRE, "beam-fire\r\n" + "\tFire a beam weapon from a specified subsystem\r\n" + "\t1:\tShip which will be firing\r\n" + "\t2:\tTurret which will fire the beam (note, this turret must have at least 1 beam weapon on it)\r\n" + "\t3:\tShip which will be targeted\r\n" + "Use add-data to add a specific subsystem to target on the specified target ship"}, + { OP_IS_TAGGED, "is-tagged\r\n" + "\tReturns whether a given ship is tagged or not\r\n"}, + { OP_NUM_KILLS, "num-kills\r\n" + "\tReturns the # of kills a player has. The ship specified in the first field should be the ship the player is in.\r\n" + "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n" + "\ttime there is no player in a given ship, this sexpression will return 0"}, + { OP_NUM_TYPE_KILLS, "num-type-kills\r\n" + "\tReturns the # of kills a player has on a given ship type (fighter, bomber, cruiser, etc).\r\n" + "The ship specified in the first field should be the ship the player is in.\r\n" + "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n" + "\ttime there is no player in a given ship, this sexpression will return 0"}, + { OP_NUM_CLASS_KILLS, "num-class-kills\r\n" + "\tReturns the # of kills a player has on a specific ship class (Ulysses, Hercules, etc).\r\n" + "The ship specified in the first field should be the ship the player is in.\r\n" + "\tSo, for single player, this would be alpha 1. For multiplayer, it can be any ship with a player in it. If, at any\r\n" + "\ttime there is no player in a given ship, this sexpression will return 0"}, + { OP_BEAM_FREE, "beam-free\r\n" + "\tSets one or more beam weapons to allow firing for a given ship\r\n" + "\t1: Ship to be operated on\r\n" + "\t2, 3, etc : List of turrets to activate\r\n"}, + { OP_BEAM_FREE_ALL, "beam-free-all\r\n" + "\tSets all beam weapons on the specified ship to be active\r\n"}, + { OP_BEAM_LOCK, "beam-lock\r\n" + "\tSets one or more beam weapons to NOT allow firing for a given ship\r\n" + "\t1: Ship to be operated on\r\n" + "\t2, 3, etc : List of turrets to deactivate\r\n"}, + { OP_BEAM_LOCK_ALL, "beam-lock-all\r\n" + "\tSets all beam weapons on the specified ship to be deactivated\r\n"}, + { OP_TURRET_FREE, "turret-free\r\n" + "\tSets one or more turret weapons to allow firing for a given ship\r\n" + "\t1: Ship to be operated on\r\n" + "\t2, 3, etc : List of turrets to activate\r\n"}, + { OP_TURRET_FREE_ALL, "turret-free-all\r\n" + "\tSets all turret weapons on the specified ship to be active\r\n"}, + { OP_TURRET_LOCK, "turret-lock\r\n" + "\tSets one or more turret weapons to NOT allow firing for a given ship\r\n" + "\t1: Ship to be operated on\r\n" + "\t2, 3, etc : List of turrets to deactivate\r\n"}, + { OP_TURRET_LOCK_ALL, "turret-lock-all\r\n" + "\tSets all turret weapons on the specified ship to be deactivated\r\n"}, + { OP_ADD_REMOVE_ESCORT, "add-remove-escort\r\n" + "\tAdds or removes a ship from an escort list.\r\n" + "\t1: Ship to be added or removed\r\n" + "\t2: 0 to remove from the list, any positive value to add to the list\r\n" + "NOTE : it _IS_ safe to add a ship which may already be on the list or remove\r\n" + "a ship which is not on the list\r\n"}, + { OP_AWACS_SET_RADIUS, "awacs-set-radius\r\n" + "\tSets the awacs radius for a given ship subsystem. NOTE : does not work properly in multiplayer\r\n" + "\t1: Ship which has the awacs subsystem\r\n" + "\t2: Awacs subsystem\r\n" + "\t3: New radius\r\n"}, + { OP_SEND_MESSAGE_LIST, "send-message-list\r\n" + "\tSends a series of delayed messages. All times are accumulated" + "\t1:\tName of who the message is from.\r\n" + "\t2:\tPriority of message (\"Low\", \"Normal\" or \"High\").\r\n" + "\t3:\tName of message (from message editor).\r\n" + "\t4:\tDelay in ms\r\n" + "Use Add-Data for multiple messages" + "IMPORTANT : each additional message in the list MUST HAVE 4 entries\r\n" + "any message without the 4 proper fields will be ignore, as will any\r\n" + "successive messages"}, + { OP_CAP_WAYPOINT_SPEED, "cap-waypoint-speed\r\n" + "\tSets the maximum speed of a ship while flying waypoints.\r\n" + "\t1: Ship name\r\n" + "\t2: Maximum speed while flying waypoints\r\n" + "\tNOTE: This will only work if the ship is already in the game\r\n" + "\tNOTE: Set to -1 to reset\r\n"}, + { OP_TURRET_TAGGED_ONLY_ALL, "turret-tagged-only\r\n" + "\tMakes turrets target and hence fire strictly at tagged objects\r\n" + "\t1: Ship name\r\n" + "\tNOTE: Will not stop a turret already firing at an untagged ship\r\n"}, + { OP_TURRET_TAGGED_CLEAR_ALL, "turret-tagged-clear\r\n" + "\tRelaxes restriction on turrets targeting only tagged ships\r\n" + "\t1: Ship name\r\n"}, + { OP_SECONDARIES_DEPLETED, "secondaries-depleted\r\n" + "\tReturns true if ship is out secondary weapons\r\n" + "\t1: Ship name\r\n"}, + { OP_SUBSYS_SET_RANDOM, "subsys-set-random\r\n" + "\tSets ship subsystem strength in a given range\r\n" + "\t1: Ship name\r\n" + "\t2: Low range\r\n" + "\t3: High range\r\n" + "\t4: List of subsys names not to be randomized\r\n"}, + { OP_SUPERNOVA_START, "supernova-start\r\n" + "\t1: Time in seconds until the supernova shockwave hits the player\r\n"}, + { OP_SHIELD_RECHARGE_PCT, "shield-recharge-pct\r\n" + "\tReturns a percentage from 0 to 100\r\n" + "\t1: Ship name\r\n" }, + { OP_ENGINE_RECHARGE_PCT, "engine-recharge-pct\r\n" + "\tReturns a percentage from 0 to 100\r\n" + "\t1: Ship name\r\n" }, + { OP_WEAPON_RECHARGE_PCT, "weapon-recharge-pct\r\n" + "\tReturns a percentage from 0 to 100\r\n" + "\t1: Ship name\r\n" }, + { OP_CARGO_NO_DEPLETE, "cargo-no-deplete\r\n" + "\tCauses the named ship to have unlimited cargo.\r\n" + "\tNote: only applies to BIG or HUGE ships\r\n" + "Takes 1 or more arguments:\r\n" + "\t1:\tName of one of the ships.\r\n" + "\t2:\toptional: 1 disallow depletion, 0 allow depletion." }, + { OP_SHIELD_QUAD_LOW, "shield-quad-low\r\n" + "\tReturns true if the specified ship has a shield quadrant below\r\n" + "\tthe specified threshold percentage\r\n" + "\t1: Ship name\r\n" + "\t2: Percentage\r\n" }, + { OP_SECONDARY_AMMO_PCT, "secondary-ammo-pct\r\n" + "\tReturns the percentage of ammo remaining in the specified bank (0 to 100)\r\n" + "\t1: Ship name\r\n" + "\t2: Bank to check (0, 1, 2 are legal banks. 3 will return the cumulative average for all banks" }, + { OP_IS_SECONDARY_SELECTED, "is-secondary-selected\r\n" + "\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n" + "\t1: Ship name\r\n" + "\t2: Bank to check (0 .. num_banks - 1)\r\n"}, + { OP_IS_PRIMARY_SELECTED, "is-primary-selected\r\n" + "\tReturns true if the specified bank is selected (0 .. num_banks - 1)\r\n" + "\t1: Ship name\r\n" + "\t2: Bank to check (0 .. num_banks - 1)\r\n"}, + { OP_SPECIAL_WARP_DISTANCE, "special-warp-dist\r\n" + "\tReturns distance to the plane of the knossos device in percent length of ship\r\n" + "\t(ie, 100 means front of ship is 1 ship length from plane of knossos device)\r\n" + "\t1: Ship name\r\n"}, + { OP_SET_SPECIAL_WARPOUT_NAME, "special-warpout-name\r\n" + "\tSets the name of the knossos device to be used for warpout\r\n" + "\t1: Ship name to exit\r\n" + "\t2: Name of knossos device\r\n"}, + { OP_SHIP_VANISH, "ship-vanish\r\n" + "\tMakes the named ship vanish (no log and vanish)\r\n" + "\tSingle Player Only! Warning: This will cause ship exit not to be logged, so 'has-departed', etc. will not work\r\n" + "\t1: List of ship names to vanish\r\n"}, + { OP_IS_SHIP_VISIBLE, "is-ship-visible\r\n" + "\tCheck whether ship is visible on Player's radar\r\n" + "\tSingle Player Only! Returns 0 - not visible, 1 - partially visible, 2 - fully visible.\r\n" + "\t1: Name of ship to check\r\n"}, + { OP_TEAM_SCORE, "team-score\r\n" + "\tGet the score of a multi team vs team game.\r\n" + "\t1: Team index (1 for team 1 and 2 for team 2)\r\n"}, + +}; + +struct op_menu_struct { + char *name; + int id; + +} op_menu[] = { + { "Objectives", OP_CATAGORY_OBJECTIVE }, + { "Time", OP_CATAGORY_TIME }, + { "Logical", OP_CATAGORY_LOGICAL }, + { "Arithmetic", OP_CATAGORY_ARITHMETIC }, + { "Status", OP_CATAGORY_STATUS }, + { "Change", OP_CATAGORY_CHANGE }, + { "Conditionals", OP_CATAGORY_CONDITIONAL }, + { "Debugging", OP_CATAGORY_DEBUG }, + { "Ai goals", OP_CATAGORY_AI }, + { "Event/Goals", OP_CATAGORY_GOAL_EVENT }, + { "Training", OP_CATAGORY_TRAINING }, +}; + +int Num_op_menus = sizeof(op_menu) / sizeof(op_menu_struct); + +// constructor +sexp_tree::sexp_tree() +{ + select_sexp_node = -1; + root_item = -1; + m_mode = 0; + m_dragging = FALSE; + m_p_image_list = NULL; + help_box = NULL; + clear_tree(); +} + +// clears out the tree, so all the nodes are unused. +void sexp_tree::clear_tree(char *op) +{ + int i; + + total = flag = 0; + for (i=0; i -1); + + sprintf(combined_name, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text); +} + + + +// creates a tree from a given Sexp_nodes[] point under a given parent. Recursive. +void sexp_tree::load_branch(int index, int parent) +{ + int cur = -1; + char combined_var_name[2*TOKEN_LENGTH + 2]; + + while (index != -1) { + Assert(Sexp_nodes[index].type != SEXP_NOT_USED); + if (Sexp_nodes[index].subtype == SEXP_ATOM_LIST) { + load_branch(Sexp_nodes[index].first, parent); // do the sublist and continue + + } else if (Sexp_nodes[index].subtype == SEXP_ATOM_OPERATOR) { + cur = allocate_node(parent); + if ((index == select_sexp_node) && !flag) { // translate sexp node to our node + select_sexp_node = cur; + flag = 1; + } + + set_node(cur, (SEXPT_OPERATOR | SEXPT_VALID), Sexp_nodes[index].text); + load_branch(Sexp_nodes[index].rest, cur); // operator is new parent now + return; // 'rest' was just used, so nothing left to use. + + } else if (Sexp_nodes[index].subtype == SEXP_ATOM_NUMBER) { + cur = allocate_node(parent); + if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) { + get_combined_variable_name(combined_var_name, Sexp_nodes[index].text); + set_node(cur, (SEXPT_VARIABLE | SEXPT_NUMBER | SEXPT_VALID), combined_var_name); + } else { + set_node(cur, (SEXPT_NUMBER | SEXPT_VALID), Sexp_nodes[index].text); + } + + } else if (Sexp_nodes[index].subtype == SEXP_ATOM_STRING) { + cur = allocate_node(parent); + if (Sexp_nodes[index].type & SEXP_FLAG_VARIABLE) { + get_combined_variable_name(combined_var_name, Sexp_nodes[index].text); + set_node(cur, (SEXPT_VARIABLE | SEXPT_STRING | SEXPT_VALID), combined_var_name); + } else { + set_node(cur, (SEXPT_STRING | SEXPT_VALID), Sexp_nodes[index].text); + } + + } else + Assert(0); // unknown and/or invalid sexp type + + if ((index == select_sexp_node) && !flag) { // translate sexp node to our node + select_sexp_node = cur; + flag = 1; + } + + index = Sexp_nodes[index].rest; + if (index == -1) + return; + } +} + +int sexp_tree::query_false(int node) +{ + if (node < 0) + node = root_item; + + Assert(node >= 0); + Assert(nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID)); + Assert(nodes[node].next == -1); // must make this assumption or else it will confuse code! + if (find_operator(nodes[node].text) == OP_FALSE){ + return TRUE; + } + + return FALSE; +} + +// builds an sexp of the tree and returns the index of it. This allocates sexp nodes. +int sexp_tree::save_tree(int node) +{ + if (node < 0) + node = root_item; + + Assert(node >= 0); + Assert(nodes[node].type == (SEXPT_OPERATOR | SEXPT_VALID)); + Assert(nodes[node].next == -1); // must make this assumption or else it will confuse code! + return save_branch(node); +} + +// get variable name from sexp_tree node .text +void var_name_from_sexp_tree_text(char *var_name, const char *text) +{ + int var_name_length = strcspn(text, "("); + Assert(var_name_length < TOKEN_LENGTH - 1); + + strncpy(var_name, text, var_name_length); + var_name[var_name_length] = '\0'; +} + +#define NO_PREVIOUS_NODE -9 +// called recursively to save a tree branch and everything under it +int sexp_tree::save_branch(int cur, int at_root) +{ + int start, node = -1, last = NO_PREVIOUS_NODE; + char var_name_text[TOKEN_LENGTH]; + + start = -1; + while (cur != -1) { + if (nodes[cur].type & SEXPT_OPERATOR) { + node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_OPERATOR, -1, save_branch(nodes[cur].child)); + + if ((nodes[cur].parent >= 0) && !at_root) { + node = alloc_sexp("", SEXP_LIST, SEXP_ATOM_LIST, node, -1); + } + } else if (nodes[cur].type & SEXPT_NUMBER) { + // allocate number, maybe variable + if (nodes[cur].type & SEXPT_VARIABLE) { + var_name_from_sexp_tree_text(var_name_text, nodes[cur].text); + node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_NUMBER, -1, -1); + } else { + node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_NUMBER, -1, -1); + } + } else if (nodes[cur].type & SEXPT_STRING) { + // allocate string, maybe variable + if (nodes[cur].type & SEXPT_VARIABLE) { + var_name_from_sexp_tree_text(var_name_text, nodes[cur].text); + node = alloc_sexp(var_name_text, (SEXP_ATOM | SEXP_FLAG_VARIABLE), SEXP_ATOM_STRING, -1, -1); + } else { + node = alloc_sexp(nodes[cur].text, SEXP_ATOM, SEXP_ATOM_STRING, -1, -1); + } + } else if (nodes[cur].type & SEXPT_STRING) { + Assert( !(nodes[cur].type & SEXPT_VARIABLE) ); + Int3(); + } else { + Assert(0); // unknown and/or invalid type + } + + if (last == NO_PREVIOUS_NODE){ + start = node; + } else if (last >= 0){ + Sexp_nodes[last].rest = node; + } + + last = node; + Assert(last != NO_PREVIOUS_NODE); // should be impossible + cur = nodes[cur].next; + if (at_root){ + return start; + } + } + + return start; +} + +// allocate a node. Remains used until freed. +int sexp_tree::allocate_node() +{ + int i; + + for (i=0; i 0); + *modified = 1; + nodes[node].type = SEXPT_UNUSED; + total--; + if (nodes[node].child != -1) + free_node2(nodes[node].child); + + if (nodes[node].next != -1) + free_node2(nodes[node].next); +} + +// initialize the data for a node. Should be called right after a new node is allocated. +void sexp_tree::set_node(int node, int type, char *text) +{ + Assert(type != SEXPT_UNUSED); + Assert(nodes[node].type != SEXPT_UNUSED); + nodes[node].type = type; + size_t max_length; + if (type & SEXPT_VARIABLE) { + max_length = 2 * TOKEN_LENGTH + 2; + } else { + max_length = TOKEN_LENGTH; + } + Assert(strlen(text) < max_length); + strcpy(nodes[node].text, text); +} + +void sexp_tree::post_load() +{ + if (!flag) + select_sexp_node = -1; +} + +// build or rebuild a CTreeCtrl object with the current tree data +void sexp_tree::build_tree() +{ + if (!flag) + select_sexp_node = -1; + + DeleteAllItems(); + add_sub_tree(0, TVI_ROOT); +} + +// Create the CTreeCtrl tree from the tree data. The tree data should already be setup by +// this point. +void sexp_tree::add_sub_tree(int node, HTREEITEM root) +{ +// char str[80]; + int node2; + + Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE); + node2 = nodes[node].child; + + // check for single argument operator case (prints as one line) +/* if (node2 != -1 && nodes[node2].child == -1 && nodes[node2].next == -1) { + sprintf(str, "%s %s", nodes[node].text, nodes[node2].text); + nodes[node].handle = insert(str, root); + nodes[node].flags = OPERAND | EDITABLE; + nodes[node2].flags = COMBINED; + return; + }*/ + + // bitmap to draw in tree + int bitmap; + + if (nodes[node].type & SEXPT_OPERATOR) { + nodes[node].flags = OPERAND; + bitmap = BITMAP_OPERATOR; + } else { + if (nodes[node].type & SEXPT_VARIABLE) { + nodes[node].flags = NOT_EDITABLE; + bitmap = BITMAP_VARIABLE; + } else { + nodes[node].flags = EDITABLE; + bitmap = BITMAP_DATA; + } + } + + root = nodes[node].handle = insert(nodes[node].text, bitmap, bitmap, root); + + node = node2; + while (node != -1) { + Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE); + Assert(nodes[node].type & SEXPT_VALID); + if (nodes[node].type & SEXPT_OPERATOR) { + add_sub_tree(node, root); + + } else { + Assert(nodes[node].child == -1); + if (nodes[node].type & SEXPT_VARIABLE) { + nodes[node].handle = insert(nodes[node].text, BITMAP_VARIABLE, BITMAP_VARIABLE, root); + nodes[node].flags = NOT_EDITABLE; + } else { + nodes[node].handle = insert(nodes[node].text, BITMAP_DATA, BITMAP_DATA, root); + nodes[node].flags = EDITABLE; + } + } + + node = nodes[node].next; + } +} + +void sexp_tree::setup_selected(HTREEITEM h) +{ + int i; + + item_index = -1; + item_handle = h; + if (!h) + item_handle = GetSelectedItem(); + + for (i=0; i= 0); + + // get parent and check "modify-variable" + index = nodes[item_index].parent; + Assert( index != -1 ); + Assert( !(stricmp(nodes[index].text, "modify-variable")) ); + + // get first child and verify type variable + index = nodes[index].child; + Assert( index != -1 ); + Assert( nodes[index].type & SEXPT_VARIABLE); + + return index; +} + +// handler for right mouse button clicks. +void sexp_tree::right_clicked(int mode) +{ + char buf[256]; + int i, j, z, count, op, add_type, replace_type, type; + sexp_list_item *list; + UINT _flags; + HTREEITEM h; + POINT click_point, mouse; + CMenu menu, *mptr, *popup_menu, *add_data_menu = NULL, *replace_data_menu = NULL; + CMenu *add_op_menu, add_op_submenu[MAX_OP_MENUS]; + CMenu *replace_op_menu, replace_op_submenu[MAX_OP_MENUS]; + CMenu *insert_op_menu, insert_op_submenu[MAX_OP_MENUS]; + CMenu *replace_variable_menu = NULL; + + m_mode = mode; + add_instance = replace_instance = -1; + Assert(Num_operators <= MAX_OPERATORS); + Assert(Num_op_menus < MAX_OP_MENUS); + GetCursorPos(&mouse); + click_point = mouse; + ScreenToClient(&click_point); + h = HitTest(CPoint(click_point), &_flags); // find out what they clicked on + if (h && menu.LoadMenu(IDR_MENU_EDIT_SEXP_TREE)) { + update_help(h); + popup_menu = menu.GetSubMenu(0); + ASSERT(popup_menu != NULL); + SelectDropTarget(h); + + add_op_menu = replace_op_menu = insert_op_menu = NULL; + + // get pointers to several key popup menus we'll need to modify + i = popup_menu->GetMenuItemCount(); + while (i--) { + if ( (mptr = popup_menu->GetSubMenu(i)) > 0 ) { + popup_menu->GetMenuString(i, buf, sizeof(buf), MF_BYPOSITION); + + if (!stricmp(buf, "add operator")) { + add_op_menu = mptr; + + } else if (!stricmp(buf, "replace operator")) { + replace_op_menu = mptr; + + } else if (!stricmp(buf, "add data")) { + add_data_menu = mptr; + + } else if (!stricmp(buf, "replace data")) { + replace_data_menu = mptr; + + } else if (!stricmp(buf, "insert operator")) { + insert_op_menu = mptr; + + } else if (!stricmp(buf, "replace variable")) { + replace_variable_menu = mptr; + } + } + } + + // add popup menus for all the operator catagories + for (i=0; iAppendMenu(MF_POPUP, (UINT) add_op_submenu[i].m_hMenu, op_menu[i].name); + replace_op_menu->AppendMenu(MF_POPUP, (UINT) replace_op_submenu[i].m_hMenu, op_menu[i].name); + insert_op_menu->AppendMenu(MF_POPUP, (UINT) insert_op_submenu[i].m_hMenu, op_menu[i].name); + } + + // get rid of the placeholders we needed to ensure popup menus stayed popup menus, + // i.e. MSDEV will convert empty popup menus into normal menu items. + add_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); + replace_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); + insert_op_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); + replace_variable_menu->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); + + + // get item_index + item_index = -1; + for (i=0; i= 0) { + // get type of sexp_tree item clicked on + int type = get_type(h); + + int parent = nodes[item_index].parent; + if (parent >= 0) { + op = identify_operator(nodes[parent].text); + Assert(op >= 0); + int first_arg = nodes[parent].child; + + // get arg count of item to replace + Replace_count = 0; + int temp = first_arg; + while (temp != item_index) { + Replace_count++; + temp = nodes[temp].next; + + // DB - added 3/4/99 + if(temp == -1){ + break; + } + } + + int op_type = query_operator_argument_type(op, Replace_count); // check argument type at this position + + // special case dont allow replace data for variable names + if (op_type != OPF_AMBIGUOUS) { + + if ( (type & SEXPT_STRING) || (type & SEXPT_NUMBER) ) { + + int max_sexp_vars = MAX_SEXP_VARIABLES; + // prevent collisions in id numbers: ID_VARIABLE_MENU + 512 = ID_ADD_MENU + Assert(max_sexp_vars < 512); + + for (int idx=0; idxAppendMenu(flag, (ID_VARIABLE_MENU + idx), buf); + } + } + } + } + } + } + + // cant modify if no variables + if (sexp_variable_count() == 0) { + menu.EnableMenuItem(ID_SEXP_TREE_MODIFY_VARIABLE, MF_GRAYED); + } + } + + // add operator menu items to the various catagory submenus they belong in + for (i=0; iTrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this); + return; + } + + Assert(item_index != -1); // handle not found, which should be impossible. + if (!(nodes[item_index].flags & EDITABLE)) { + menu.EnableMenuItem(ID_EDIT_TEXT, MF_GRAYED); + } + + if (nodes[item_index].parent == -1) { // root node + menu.EnableMenuItem(ID_DELETE, MF_GRAYED); // can't delete the root item. + } + +/* if ((nodes[item_index].flags & OPERAND) && (nodes[item_index].flags & EDITABLE)) // expandable? + menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED); + + z = nodes[item_index].child; + if (z != -1 && nodes[z].next == -1 && nodes[z].child == -1) + menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED); + + z = nodes[nodes[item_index].parent].child; + if (z != -1 && nodes[z].next == -1 && nodes[z].child == -1) + menu.EnableMenuItem(ID_SPLIT_LINE, MF_ENABLED);*/ + + // change enabled status of 'add' type menu options. + add_type = 0; + if (nodes[item_index].flags & OPERAND) { + add_type = OPR_STRING; + int child = nodes[item_index].child; + Add_count = count_args(child); + op = identify_operator(nodes[item_index].text); + Assert(op >= 0); + + // get listing of valid argument values and add to menus + type = query_operator_argument_type(op, Add_count); + list = get_listing_opf(type, item_index, Add_count); + if (list) { + sexp_list_item *ptr; + + int data_idx = 0; + ptr = list; + while (ptr) { + if (ptr->op >= 0) { + // enable operators with correct return type + menu.EnableMenuItem(Operators[ptr->op].value, MF_ENABLED); + + } else { + // add data + if ( (data_idx + 3) % 30) { + add_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text); + } else { + add_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_ADD_MENU + data_idx, ptr->text); + } + } + + data_idx++; + ptr = ptr->next; + } + } + + // special handling for the non-string formats + if (type == OPF_NONE) { // an argument can't be added + add_type = 0; + + } else if (type == OPF_NULL) { // arguments with no return values + add_type = OPR_NULL; + + } else if (type == OPF_NUMBER) { // takes numbers + add_type = OPR_NUMBER; + menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED); + + } else if (type == OPF_POSITIVE) { // takes non-negative numbers + add_type = OPR_POSITIVE; + menu.EnableMenuItem(ID_ADD_NUMBER, MF_ENABLED); + + } else if (type == OPF_BOOL) { // takes true/false bool values + add_type = OPR_BOOL; + + } else if (type == OPF_AI_GOAL) { + add_type = OPR_AI_GOAL; + } + + // add_type unchanged from above + if (add_type == OPR_STRING) { + menu.EnableMenuItem(ID_ADD_STRING, MF_ENABLED); + } + + list->destroy(); + } + + // disable operators that do not have arguments available + for (j=0; j= 0) { + replace_type = OPR_STRING; + op = identify_operator(nodes[parent].text); + Assert(op >= 0); + int first_arg = nodes[parent].child; + count = count_args(nodes[parent].child); + + // already at minimum number of arguments? + if (count <= Operators[op].min) { + menu.EnableMenuItem(ID_DELETE, MF_GRAYED); + } + + // get arg count of item to replace + Replace_count = 0; + int temp = first_arg; + while (temp != item_index) { + Replace_count++; + temp = nodes[temp].next; + + // DB - added 3/4/99 + if(temp == -1){ + break; + } + } + + // maybe gray delete + for (i=Replace_count+1; iop >= 0) { + menu.EnableMenuItem(Operators[ptr->op].value | OP_REPLACE_FLAG, MF_ENABLED); + + } else { + if ( (data_idx + 3) % 30) + replace_data_menu->AppendMenu(MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text); + else + replace_data_menu->AppendMenu(MF_MENUBARBREAK | MF_STRING | MF_ENABLED, ID_REPLACE_MENU + data_idx, ptr->text); + } + + data_idx++; + ptr = ptr->next; + } + } + + if (type == OPF_NONE) { // takes no arguments + replace_type = 0; + + } else if (type == OPF_NUMBER) { // takes numbers + replace_type = OPR_NUMBER; + menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED); + + } else if (type == OPF_POSITIVE) { // takes non-negative numbers + replace_type = OPR_POSITIVE; + menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED); + + } else if (type == OPF_BOOL) { // takes true/false bool values + replace_type = OPR_BOOL; + + } else if (type == OPF_NULL) { // takes operator that doesn't return a value + replace_type = OPR_NULL; + + } else if (type == OPF_AI_GOAL) { + replace_type = OPR_AI_GOAL; + } + + // default to string + if (replace_type == OPR_STRING) { + menu.EnableMenuItem(ID_REPLACE_STRING, MF_ENABLED); + } + + // modify string or number if (modify_variable) + if ( !stricmp(Operators[op].text, "modify-variable") ) { + int modify_type = get_modify_variable_type(); + + if (modify_type == OPF_NUMBER) { + menu.EnableMenuItem(ID_REPLACE_NUMBER, MF_ENABLED); + menu.EnableMenuItem(ID_REPLACE_STRING, MF_GRAYED); + } + // no change for string type + } + + list->destroy(); + + } else { // top node, so should be a Boolean type. + if (m_mode == MODE_EVENTS) { // return type should be null + replace_type = OPR_NULL; + for (j=0; j= -1); + if (z != -1) { + op = identify_operator(nodes[z].text); + Assert(op != -1); + j = nodes[z].child; + count = 0; + while (j != item_index) { + count++; + j = nodes[j].next; + } + + type = query_operator_argument_type(op, count); // check argument type at this position + + } else { + if (m_mode == MODE_EVENTS) + type = OPF_NULL; + else + type = OPF_BOOL; + } + + for (j=0; j -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)) { + Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST); + + if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) { + j = find_operator(CTEXT(Sexp_clipboard)); + Assert(j); + z = query_operator_return_type(j); + + if ((z == OPR_POSITIVE) && (replace_type == OPR_NUMBER)) + z = OPR_NUMBER; + + if (replace_type == z) + menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED); + + z = query_operator_return_type(j); + if ((z == OPR_POSITIVE) && (add_type == OPR_NUMBER)) + z = OPR_NUMBER; + + if (add_type == z) + menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED); + + } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) { + if ((replace_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1)) + menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED); + + else if (replace_type == OPR_NUMBER) + menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED); + + if ((add_type == OPR_POSITIVE) && (atoi(CTEXT(Sexp_clipboard)) > -1)) + menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED); + + else if (add_type == OPR_NUMBER) + menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED); + + } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) { + if (replace_type == OPR_STRING) + menu.EnableMenuItem(ID_EDIT_PASTE, MF_ENABLED); + + if (add_type == OPR_STRING) + menu.EnableMenuItem(ID_EDIT_PASTE_SPECIAL, MF_ENABLED); + + } else + Int3(); // unknown and/or invalid sexp type + } + + if (!(menu.GetMenuState(ID_DELETE, MF_BYCOMMAND) & MF_GRAYED)) + menu.EnableMenuItem(ID_EDIT_CUT, MF_ENABLED); + + gray_menu_tree(popup_menu); + popup_menu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, mouse.x, mouse.y, this); + } +} + +// counts the number of arguments an operator has. Call this with the node of the first +// argument of the operator +int sexp_tree::count_args(int node) +{ + int count = 0; + + while (node != -1) { + count++; + node = nodes[node].next; + } + + return count; +} + +// identify what type of argument this is. You call it with the node of the first argument +// of an operator. It will search through enough of the arguments to determine what type of +// data they are. +int sexp_tree::identify_arg_type(int node) +{ + int type = -1; + + while (node != -1) { + Assert(nodes[node].type & SEXPT_VALID); + switch (SEXPT_TYPE(nodes[node].type)) { + case SEXPT_OPERATOR: + type = find_operator(nodes[node].text); + Assert(type); + return query_operator_return_type(type); + + case SEXPT_NUMBER: + return OPR_NUMBER; + + case SEXPT_STRING: // either a ship or a wing + type = SEXP_ATOM_STRING; + break; // don't return, because maybe we can narrow selection down more. + } + + node = nodes[node].next; + } + + return type; +} + +// determine if an item should be editable. This doesn't actually edit the label. +int sexp_tree::edit_label(HTREEITEM h) +{ + int i; + + for (i=0; ihandler(ROOT_RENAMED, item_index, str); + return 1; + + } else + Int3(); // root labels shouldn't have been editable! + } + + Assert(node < MAX_SEXP_TREE_SIZE); + if (nodes[node].type & SEXPT_OPERATOR) { + str = match_closest_operator(str, node); + SetItemText(h, str); + item_index = node; + add_or_replace_operator(identify_operator(str), 1); + r = 0; + } + + // Error checking would not hurt here + len = strlen(str); + if (len >= TOKEN_LENGTH) + len = TOKEN_LENGTH - 1; + + strncpy(nodes[node].text, str, len); + nodes[node].text[len] = 0; + +/* node = nodes[node].parent; + if (node != -1) { + child = nodes[node].child; + if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) { + merge_operator(child); + return 0; + } + }*/ + + return r; +} + +// Check if 'op' is a valid operator match for operator format (OPF) 'type'. Returns true if it +// is, false if not. +// +int sexp_tree::check_operator_validity(int op, int type) +{ + int rtype; + + rtype = query_operator_return_type(op); + switch (type) { + case OPF_NUMBER: + return ( (rtype == OPR_NUMBER) || (rtype == OPR_POSITIVE) ); + + case OPF_POSITIVE: + return (rtype == OPR_POSITIVE); + + case OPF_BOOL: + return (rtype == OPR_BOOL); + + case OPF_NULL: + return (rtype == OPR_NULL); + + case OPF_AI_GOAL: + return (rtype == OPR_AI_GOAL); + } + + return 0; +} + +// Look for the valid operator that is the closest match for 'str' and return the operator +// number of it. What operators are valid is determined by 'node', and an operator is valid +// if it is allowed to fit at position 'node' +// +char *sexp_tree::match_closest_operator(char *str, int node) +{ + int z, n, i, op, arg_num, type; + char *sub_best = NULL, *best = NULL; + + z = nodes[node].parent; + if (z < 0) { + return str; + } + + op = identify_operator(nodes[z].text); + if (op < 0) + return str; + + // determine which argument we are of the parent + arg_num = 0; + n = nodes[z].child; + while (n != node) { + Assert(n >= 0); + arg_num++; + n = nodes[n].next; + } + + type = query_operator_argument_type(op, arg_num); // check argument type at this position + for (i=0; i 0) ) + sub_best = Operators[i].text; + } + } + + if (!best) + best = sub_best; // no best found, use our plan #2 best found. + + Assert(best); // we better have some valid operator at this point. + return best; + +/* char buf[256]; + int child; + + if (nodes[node].flags == EDITABLE) // data + node = nodes[node].parent; + + if (node != -1) { + child = nodes[node].child; + if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) { + sprintf(buf, "%s %s", nodes[node].text, nodes[child].text); + SetItemText(nodes[node].handle, buf); + nodes[node].flags = OPERAND | EDITABLE; + nodes[child].flags = COMBINED; + DeleteItem(nodes[child].handle); + nodes[child].handle = NULL; + return; + } + }*/ +} + +// this really only handles messages generated by the right click popup menu +BOOL sexp_tree::OnCommand(WPARAM wParam, LPARAM lParam) +{ + int i, z, id, node, op, type; + sexp_list_item *list, *ptr; + HTREEITEM h; + + if ((item_index >= 0) && (item_index < total)) + item_handle = nodes[item_index].handle; + + id = LOWORD(wParam); + + + // Add variable + if (id == ID_SEXP_TREE_ADD_VARIABLE) { + CAddVariableDlg dlg; + dlg.DoModal(); + + if ( dlg.m_create ) { + + // set type + int type; + if ( dlg.m_type_number ) { + type = SEXP_VARIABLE_NUMBER; + } else { + type = SEXP_VARIABLE_STRING; + } + + // add variable + sexp_add_variable(dlg.m_default_value, dlg.m_variable_name, type); + + // sort variable + sexp_variable_sort(); + } + return 1; + } + + // Modify variable + if (id == ID_SEXP_TREE_MODIFY_VARIABLE) { + CModifyVariableDlg dlg; + + // get sexp_variable index for item index + dlg.m_start_index = get_item_index_to_var_index(); + + // get pointer to tree + dlg.m_p_sexp_tree = this; + + dlg.DoModal(); + + Assert( !(dlg.m_deleted && dlg.m_do_modify) ); + + if (dlg.m_deleted) { + // find index in sexp_variable list + int sexp_var_index = get_index_sexp_variable_name(dlg.m_cur_variable_name); + Assert(sexp_var_index != -1); + + // delete from list + sexp_variable_delete(sexp_var_index); + + // sort list + sexp_variable_sort(); + + // delete from sexp_tree, replacing with "number" or "string" as needed + // further error checking from add_data() + delete_sexp_tree_variable(dlg.m_cur_variable_name); + + return 1; + } + + if (dlg.m_do_modify) { + // check sexp_tree -- warn on type + // find index and change either (1) name, (2) type, (3) value + int sexp_var_index = get_index_sexp_variable_name(dlg.m_old_var_name); + Assert(sexp_var_index != -1); + + // save old name, since name may be modified + char old_name[TOKEN_LENGTH]; + strcpy(old_name, Sexp_variables[sexp_var_index].variable_name); + + // set type + int type; + if (dlg.m_type_number) { + type = SEXP_VARIABLE_NUMBER; + } else { + type = SEXP_VARIABLE_STRING; + } + + // update sexp_variable + sexp_fred_modify_variable(dlg.m_default_value, dlg.m_cur_variable_name, sexp_var_index, type); + + // modify sexp_tree + modify_sexp_tree_variable(old_name, sexp_var_index); + + // Don't sort until after modify, since modify uses index + if (dlg.m_modified_name) { + sexp_variable_sort(); + } + + return 1; + } + + // no change + return 1; + } + + + // check if REPLACE_VARIABLE_MENU + if ( (id >= ID_VARIABLE_MENU) && (id < ID_VARIABLE_MENU + 511)) { + + Assert(item_index >= 0); + + // get index into list of type valid variables + int var_idx = id - ID_VARIABLE_MENU; + Assert( (var_idx >= 0) && (var_idx < MAX_SEXP_VARIABLES) ); + + int type = get_type(item_handle); + Assert( (type & SEXPT_NUMBER) || (type & SEXPT_STRING) ); + + // dont do type check for modify-variable + if (Modify_variable) { + if (Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER) { + type = SEXPT_NUMBER; + } else if (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) { + type = SEXPT_STRING; + } else { + Int3(); // unknown type + } + + } else { + + // verify type in tree is same as type in Sexp_variables array + if (type & SEXPT_NUMBER) { + Assert(Sexp_variables[var_idx].type & SEXP_VARIABLE_NUMBER); + } + + if (type & SEXPT_STRING) { + Assert( (Sexp_variables[var_idx].type & SEXP_VARIABLE_STRING) ); + } + } + + // Replace data + replace_variable_data(var_idx, (type | SEXPT_VARIABLE)); + + return 1; + } + + + if ((id >= ID_ADD_MENU) && (id < ID_ADD_MENU + 511)) { + Assert(item_index >= 0); + op = identify_operator(nodes[item_index].text); + Assert(op >= 0); + + type = query_operator_argument_type(op, Add_count); + list = get_listing_opf(type, item_index, Add_count); + Assert(list); + + id -= ID_ADD_MENU; + ptr = list; + while (id) { + id--; + ptr = ptr->next; + Assert(ptr); + } + + Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0)); + expand_operator(item_index); + add_data(ptr->text, ptr->type); + list->destroy(); + return 1; + } + + if ((id >= ID_REPLACE_MENU) && (id < ID_REPLACE_MENU + 511)) { + Assert(item_index >= 0); + Assert(nodes[item_index].parent >= 0); + op = identify_operator(nodes[nodes[item_index].parent].text); + Assert(op >= 0); + + type = query_operator_argument_type(op, Replace_count); // check argument type at this position + list = get_listing_opf(type, nodes[item_index].parent, Replace_count); + Assert(list); + + id -= ID_REPLACE_MENU; + ptr = list; + while (id) { + id--; + ptr = ptr->next; + Assert(ptr); + } + + Assert((SEXPT_TYPE(ptr->type) != SEXPT_OPERATOR) && (ptr->op < 0)); + expand_operator(item_index); + replace_data(ptr->text, ptr->type); + list->destroy(); + return 1; + } + + for (op=0; op= 0) + h = nodes[z].handle; + + else { + h = GetParentItem(nodes[item_index].handle); + if (m_mode == MODE_GOALS) { + Assert(Goal_editor_dlg); + Goal_editor_dlg->insert_handler(item_index, node); + SetItemData(h, node); + + } else if (m_mode == MODE_EVENTS) { + Assert(Event_editor_dlg); + Event_editor_dlg->insert_handler(item_index, node); + SetItemData(h, node); + + } else if (m_mode == MODE_CAMPAIGN) { + Campaign_tree_formp->insert_handler(item_index, node); + SetItemData(h, node); + + } else { + h = TVI_ROOT; + root_item = node; + } + } + + item_handle = nodes[node].handle = insert(Operators[op].text, BITMAP_OPERATOR, BITMAP_OPERATOR, h, nodes[item_index].handle); + move_branch(item_index, node); + + item_index = node; + for (i=1; i -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)); + Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST); + + if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) { + expand_operator(item_index); + replace_operator(CTEXT(Sexp_clipboard)); + if (Sexp_nodes[Sexp_clipboard].rest != -1) { + load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index); + i = nodes[item_index].child; + while (i != -1) { + add_sub_tree(i, nodes[item_index].handle); + i = nodes[i].next; + } + } + + } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) { + Assert(Sexp_nodes[Sexp_clipboard].rest == -1); + expand_operator(item_index); + replace_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID)); + + } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) { + Assert(Sexp_nodes[Sexp_clipboard].rest == -1); + expand_operator(item_index); + replace_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID)); + + } else + Assert(0); // unknown and/or invalid sexp type + + return 1; + + case ID_EDIT_PASTE_SPECIAL: // add paste, instead of replace. + // the following assumptions are made.. + Assert((Sexp_clipboard > -1) && (Sexp_nodes[Sexp_clipboard].type != SEXP_NOT_USED)); + Assert(Sexp_nodes[Sexp_clipboard].subtype != SEXP_ATOM_LIST); + + if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_OPERATOR) { + expand_operator(item_index); + add_operator(CTEXT(Sexp_clipboard)); + if (Sexp_nodes[Sexp_clipboard].rest != -1) { + load_branch(Sexp_nodes[Sexp_clipboard].rest, item_index); + i = nodes[item_index].child; + while (i != -1) { + add_sub_tree(i, nodes[item_index].handle); + i = nodes[i].next; + } + } + + } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_NUMBER) { + Assert(Sexp_nodes[Sexp_clipboard].rest == -1); + expand_operator(item_index); + add_data(CTEXT(Sexp_clipboard), (SEXPT_NUMBER | SEXPT_VALID)); + + } else if (Sexp_nodes[Sexp_clipboard].subtype == SEXP_ATOM_STRING) { + Assert(Sexp_nodes[Sexp_clipboard].rest == -1); + expand_operator(item_index); + add_data(CTEXT(Sexp_clipboard), (SEXPT_STRING | SEXPT_VALID)); + + } else + Assert(0); // unknown and/or invalid sexp type + + return 1; + +/* case ID_SPLIT_LINE: + if ((nodes[item_index].flags & OPERAND) && (nodes[item_index].flags & EDITABLE)) // expandable? + expand_operator(item_index); + else + merge_operator(item_index); + + return 1;*/ + + case ID_EXPAND_ALL: + expand_branch(item_handle); + return 1; + + case ID_EDIT_TEXT: + if (edit_label(item_handle)) { + *modified = 1; + EditLabel(item_handle); + } + + return 1; + + case ID_ADD_STRING: { + int node; + + node = add_data("string", (SEXPT_STRING | SEXPT_VALID)); + EditLabel(nodes[node].handle); + return 1; + } + + case ID_ADD_NUMBER: { + int node; + + node = add_data("number", (SEXPT_NUMBER | SEXPT_VALID)); + EditLabel(nodes[node].handle); + return 1; + } + + case ID_EDIT_CUT: + if (Sexp_clipboard != -1) { + sexp_unmark_persistent(Sexp_clipboard); + free_sexp2(Sexp_clipboard); + } + + Sexp_clipboard = save_branch(item_index, 1); + sexp_mark_persistent(Sexp_clipboard); + // fall through to ID_DELETE case. + + case ID_DELETE: { + int parent, node; + HTREEITEM h; + + if ((m_mode & ST_ROOT_DELETABLE) && (item_index == -1)) { + item_index = GetItemData(item_handle); + if (m_mode == MODE_GOALS) { + Assert(Goal_editor_dlg); + node = Goal_editor_dlg->handler(ROOT_DELETED, item_index); + + } else if (m_mode == MODE_EVENTS) { + Assert(Event_editor_dlg); + node = Event_editor_dlg->handler(ROOT_DELETED, item_index); + + } else { + Assert(m_mode == MODE_CAMPAIGN); + node = Campaign_tree_formp->handler(ROOT_DELETED, item_index); + } + + Assert(node >= 0); + free_node2(node); + DeleteItem(item_handle); + *modified = 1; + return 1; + } + + Assert(item_index >= 0); + h = GetParentItem(item_handle); + parent = nodes[item_index].parent; + if ((parent == -1) && (m_mode == MODE_EVENTS)) + Int3(); // no longer used, temporary to check if called still. + + Assert(parent != -1 && nodes[parent].handle == h); + free_node(item_index); + DeleteItem(item_handle); + + node = nodes[parent].child; +/* if (node != -1 && nodes[node].next == -1 && nodes[node].child == -1) { + sprintf(buf, "%s %s", nodes[parent].text, nodes[node].text); + SetItem(h, TVIF_TEXT, buf, 0, 0, 0, 0, 0); + nodes[parent].flags = OPERAND | EDITABLE; + nodes[node].flags = COMBINED; + DeleteItem(nodes[node].handle); + }*/ + + *modified = 1; + return 1; + } + } + + return CTreeCtrl::OnCommand(wParam, lParam); +} + +// adds to or replaces (based on passed in flag) the current operator +void sexp_tree::add_or_replace_operator(int op, int replace_flag) +{ + int i, op_index, op2; + + op_index = item_index; + if (replace_flag) { + if (nodes[item_index].flags & OPERAND) { // are both operators? + op2 = identify_operator(nodes[item_index].text); + Assert(op2 >= 0); + i = count_args(nodes[item_index].child); + if ((i >= Operators[op].min) && (i <= Operators[op].max)) { // are old num args valid? + while (i--) + if (query_operator_argument_type(op2, i) != query_operator_argument_type(op, i)) // does each arg match expected type? + break; + + if (i < 0) { // everything is ok, so we can keep old arguments with new operator + set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), Operators[op].text); + SetItemText(nodes[item_index].handle, Operators[op].text); + nodes[item_index].flags = OPERAND; + return; + } + } + } + + replace_operator(Operators[op].text); + + } else + add_operator(Operators[op].text); + + // fill in all the required (minimum) arguments with default values + for (i=0; i= FIRST_OP) { // do we have an op value instead of an op number (index)? + for (i=0; inext) + ptr = ptr->next; + + ptr->next = item; + item->set_op(op_num); +} + +// add a node to end of list +// Defaults: t = SEXPT_STRING +// +void sexp_list_item::add_data(char *str, int t) +{ + sexp_list_item *item, *ptr; + + item = new sexp_list_item; + ptr = this; + while (ptr->next) + ptr = ptr->next; + + ptr->next = item; + item->set_data(str, t); +} + +// add a node to end of list, allocating memory for the text +// Defaults: t = SEXPT_STRING +// +void sexp_list_item::add_data_dup(char *str, int t) +{ + sexp_list_item *item, *ptr; + + item = new sexp_list_item; + ptr = this; + while (ptr->next) + ptr = ptr->next; + + ptr->next = item; + item->set_data(strdup(str), t); + item->flags |= SEXP_ITEM_F_DUP; +} + +// add an sexp list to end of another list (join lists) +// +void sexp_list_item::add_list(sexp_list_item *list) +{ + sexp_list_item *ptr; + + ptr = this; + while (ptr->next) + ptr = ptr->next; + + ptr->next = list; +} + +// free all nodes of list +// +void sexp_list_item::destroy() +{ + sexp_list_item *ptr, *ptr2; + + ptr = this; + while (ptr) { + ptr2 = ptr->next; + if (ptr->flags & SEXP_ITEM_F_DUP) + free(ptr->text); + + delete ptr; + ptr = ptr2; + } +} + +int sexp_tree::add_default_operator(int op, int argnum) +{ + char buf[256]; + int index; + sexp_list_item item; + HTREEITEM h; + + h = item_handle; + index = item_index; + item.text = buf; + if (get_default_value(&item, op, argnum)) + return -1; + + if (item.type & SEXPT_OPERATOR) { + Assert((item.op >= 0) && (item.op < Num_operators)); + add_or_replace_operator(item.op); + item_index = index; + item_handle = h; + + } else { + // special case for modify-variable (data added 1st arg is variable) + if ( !stricmp(Operators[op].text, "modify-variable") ) { + if (argnum == 0) { + + int sexp_var_index = get_index_sexp_variable_name(item.text); + Assert(sexp_var_index != -1); + int type = SEXPT_VALID | SEXPT_VARIABLE; + if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) { + type |= SEXPT_STRING; + } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) { + type |= SEXPT_NUMBER; + } else { + Int3(); + } + + char node_text[2*TOKEN_LENGTH + 2]; + sprintf(node_text, "%s(%s)", item.text, Sexp_variables[sexp_var_index].text); + add_variable_data(node_text, type); + } else { + // the the variable name + char buf2[256]; + Assert(argnum == 1); + sexp_list_item temp_item; + temp_item.text = buf2; + get_default_value(&temp_item, op, 0); + int sexp_var_index = get_index_sexp_variable_name(temp_item.text); + Assert(sexp_var_index != -1); + + // from name get type + int temp_type = Sexp_variables[sexp_var_index].type; + int type; + if (temp_type & SEXP_VARIABLE_NUMBER) { + type = SEXPT_VALID | SEXPT_NUMBER; + } else if (temp_type & SEXP_VARIABLE_STRING) { + type = SEXPT_VALID | SEXPT_STRING; + } else { + Int3(); + } + add_data(item.text, type); + } + } else { + add_data(item.text, item.type); + } + } + + return 0; +} + +int sexp_tree::get_default_value(sexp_list_item *item, int op, int i) +{ + char *str = NULL; + int type, index; + sexp_list_item *list; + HTREEITEM h; + + h = item_handle; + index = item_index; + type = query_operator_argument_type(op, i); + switch (type) { + case OPF_NULL: + item->set_op(OP_NOP); + return 0; + + case OPF_BOOL: + item->set_op(OP_TRUE); + return 0; + + case OPF_NUMBER: + case OPF_POSITIVE: + case OPF_AMBIGUOUS: + // if the top level operators is an AI goal, and we are adding the last number required, + // assume that this number is a priority and make it 89 instead of 1. + if ((query_operator_return_type(op) == OPR_AI_GOAL) && (i == (Operators[op].min - 1))) + item->set_data("89", (SEXPT_NUMBER | SEXPT_VALID)); + else if (((Operators[op].value == OP_HAS_DOCKED_DELAY) || (Operators[op].value == OP_HAS_UNDOCKED_DELAY)) && (i == 2)) + item->set_data("1", (SEXPT_NUMBER | SEXPT_VALID)); + else if ( (Operators[op].value == OP_SHIP_TYPE_DESTROYED) || (Operators[op].value == OP_GOOD_SECONDARY_TIME) ) + item->set_data("100", (SEXPT_NUMBER | SEXPT_VALID)); + else + item->set_data("0", (SEXPT_NUMBER | SEXPT_VALID)); + + return 0; + } + + list = get_listing_opf(type, index, i); + if (list) { + char *ptr; + + ptr = item->text; + *item = *list; + item->text = ptr; + strcpy(item->text, list->text); + + list->destroy(); + return 0; + } + + // catch anything that doesn't have a default value. Just describe what should be here instead + switch (type) { + case OPF_SHIP: + case OPF_SHIP_NOT_PLAYER: + case OPF_SHIP_WING: + case OPF_SHIP_POINT: + case OPF_SHIP_WING_POINT: + str = ""; + break; + + case OPF_WING: + str = ""; + break; + + case OPF_DOCKER_POINT: + str = ""; + break; + + case OPF_DOCKEE_POINT: + str = ""; + break; + + case OPF_SUBSYSTEM: + case OPF_AWACS_SUBSYSTEM: + str = ""; + break; + + case OPF_POINT: + str = ""; + break; + + case OPF_MESSAGE: + str = ""; + break; + + case OPF_WHO_FROM: + //str = ""; + str = ""; + break; + + case OPF_WAYPOINT_PATH: + str = ""; + break; + + case OPF_MISSION_NAME: + str = ""; + break; + + case OPF_GOAL_NAME: + str = ""; + break; + + case OPF_SHIP_TYPE: + str = ""; + break; + + case OPF_EVENT_NAME: + str = ""; + break; + + case OPF_HUGE_WEAPON: + str = ""; + break; + + case OPF_JUMP_NODE_NAME: + str = ""; + break; + + default: + str = ""; + break; + } + + item->set_data(str, (SEXPT_STRING | SEXPT_VALID)); + return 0; +} + +int sexp_tree::query_default_argument_available(int op) +{ + int i; + + Assert(op >= 0); + for (i=0; itype == OBJ_SHIP) + return 1; + + ptr = GET_NEXT(ptr); + } + + return 0; + + case OPF_WING: + for (j=0; j 0) + return 1; + + return 0; + + case OPF_GOAL_NAME: { + int value; + + value = Operators[op].value; + + if (m_mode == MODE_CAMPAIGN) + return 1; + + // need to be sure that previous-goal functions are available. (i.e. we are providing a default argument for them) + else if ((value == OP_PREVIOUS_GOAL_TRUE) || (value == OP_PREVIOUS_GOAL_FALSE) || (value == OP_PREVIOUS_GOAL_INCOMPLETE) || Num_goals) + return 1; + + return 0; + } + + case OPF_EVENT_NAME: { + int value; + + value = Operators[op].value; + if (m_mode == MODE_CAMPAIGN) + return 1; + + // need to be sure that previous-event functions are available. (i.e. we are providing a default argument for them) + else if ((value == OP_PREVIOUS_EVENT_TRUE) || (value == OP_PREVIOUS_EVENT_FALSE) || (value == OP_PREVIOUS_EVENT_INCOMPLETE) || Num_mission_events) + return 1; + + return 0; + } + + case OPF_MESSAGE: + if (m_mode == MODE_EVENTS) { + Assert(Event_editor_dlg); + if (Event_editor_dlg->current_message_name(0)) + return 1; + + } else { + if (Num_messages > Num_builtin_messages) + return 1; + } + + return 0; + + case OPF_VARIABLE_NAME: + if (sexp_variable_count() > 0) { + return 1; + } else { + return 0; + } + + default: + Int3(); + + } + + return 0; +} + +// expand a combined line (one with an operator and it's one argument on the same line) into +// 2 lines. +void sexp_tree::expand_operator(int node) +{ + int data; + HTREEITEM h; + + if (nodes[node].flags & COMBINED) { + node = nodes[node].parent; + Assert((nodes[node].flags & OPERAND) && (nodes[node].flags & EDITABLE)); + } + + if ((nodes[node].flags & OPERAND) && (nodes[node].flags & EDITABLE)) { // expandable? + Assert(nodes[node].type & SEXPT_OPERATOR); + h = nodes[node].handle; + data = nodes[node].child; + Assert(data != -1 && nodes[data].next == -1 && nodes[data].child == -1); + + SetItem(h, TVIF_TEXT, nodes[node].text, 0, 0, 0, 0, 0); + nodes[node].flags = OPERAND; + nodes[data].handle = insert(nodes[data].text, BITMAP_DATA, BITMAP_DATA, h); + nodes[data].flags = EDITABLE; + Expand(h, TVE_EXPAND); + } +} + +// expand a CTreeCtrl branch and all of it's children +void sexp_tree::expand_branch(HTREEITEM h) +{ + Expand(h, TVE_EXPAND); + h = GetChildItem(h); + while (h) { + expand_branch(h); + h = GetNextSiblingItem(h); + } +} + +void sexp_tree::merge_operator(int node) +{ +/* char buf[256]; + int child; + + if (nodes[node].flags == EDITABLE) // data + node = nodes[node].parent; + + if (node != -1) { + child = nodes[node].child; + if (child != -1 && nodes[child].next == -1 && nodes[child].child == -1) { + sprintf(buf, "%s %s", nodes[node].text, nodes[child].text); + SetItemText(nodes[node].handle, buf); + nodes[node].flags = OPERAND | EDITABLE; + nodes[child].flags = COMBINED; + DeleteItem(nodes[child].handle); + nodes[child].handle = NULL; + return; + } + }*/ +} + +// add a data node under operator pointed to by item_index +int sexp_tree::add_data(char *data, int type) +{ + int node; + + expand_operator(item_index); + node = allocate_node(item_index); + set_node(node, type, data); + nodes[node].handle = insert(data, BITMAP_DATA, BITMAP_DATA, nodes[item_index].handle); + nodes[node].flags = EDITABLE; + *modified = 1; + return node; +} + +// add a (variable) data node under operator pointed to by item_index +int sexp_tree::add_variable_data(char *data, int type) +{ + int node; + + Assert(type & SEXPT_VARIABLE); + + expand_operator(item_index); + node = allocate_node(item_index); + set_node(node, type, data); + nodes[node].handle = insert(data, BITMAP_VARIABLE, BITMAP_VARIABLE, nodes[item_index].handle); + nodes[node].flags = NOT_EDITABLE; + *modified = 1; + return node; +} + +// add an operator under operator pointed to by item_index. Updates item_index to point +// to this new operator. +void sexp_tree::add_operator(char *op, HTREEITEM h) +{ + int node; + + if (item_index == -1) { + node = allocate_node(-1); + set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op); + item_handle = nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, h); + + } else { + expand_operator(item_index); + node = allocate_node(item_index); + set_node(node, (SEXPT_OPERATOR | SEXPT_VALID), op); + item_handle = nodes[node].handle = insert(op, BITMAP_OPERATOR, BITMAP_OPERATOR, nodes[item_index].handle); + } + + nodes[node].flags = OPERAND; + item_index = node; + *modified = 1; +} + +// add an operator with one argument under operator pointed to by item_index. This function +// exists because the one arg case is a special case. The operator and argument is +// displayed on the same line. +/*void sexp_tree::add_one_arg_operator(char *op, char *data, int type) +{ + char str[80]; + int node1, node2; + + expand_operator(item_index); + node1 = allocate_node(item_index); + node2 = allocate_node(node1); + set_node(node1, SEXPT_OPERATOR, op); + set_node(node2, type, data); + sprintf(str, "%s %s", op, data); + nodes[node1].handle = insert(str, nodes[item_index].handle); + nodes[node1].flags = OPERAND | EDITABLE; + nodes[node2].flags = COMBINED; + *modified = 1; +}*/ + +/* +int sexp_tree::verify_tree(int *bypass) +{ + return verify_tree(0, bypass); +} + +// check the sexp tree for errors. Return -1 if error, or 0 if no errors. If an error +// is found, item_index = node of error. +int sexp_tree::verify_tree(int node, int *bypass) +{ + int i, type, count, op, type2, op2, argnum = 0; + + if (!total) + return 0; // nothing to check + + Assert(node >= 0 && node < MAX_SEXP_TREE_SIZE); + Assert(nodes[node].type == SEXPT_OPERATOR); + + op = identify_operator(nodes[node].text); + if (op == -1) + return node_error(node, "Unknown operator", bypass); + + count = count_args(nodes[node].child); + if (count < Operators[op].min) + return node_error(node, "Too few arguments for operator", bypass); + if (count > Operators[op].max) + return node_error(node, "Too many arguments for operator", bypass); + + node = nodes[node].child; // get first argument + while (node != -1) { + type = query_operator_argument_type(op, argnum); + Assert(nodes[node].type & SEXPT_VALID); + if (nodes[node].type == SEXPT_OPERATOR) { + if (verify_tree(node) == -1) + return -1; + + op2 = identify_operator(nodes[node].text); // no error checking, because it was done in the call above. + type2 = query_operator_return_type(op2); + + } else if (nodes[node].type == SEXPT_NUMBER) { + char *ptr; + + type2 = OPR_NUMBER; + ptr = nodes[node].text; + while (*ptr) + if (!isdigit(*ptr++)) + return node_error(node, "Number is invalid", bypass); + + } else if (nodes[node].type == SEXPT_STRING) { + type2 = SEXP_ATOM_STRING; + + } else + Assert(0); // unknown and invalid sexp node type. + + switch (type) { + case OPF_NUMBER: + if (type2 != OPR_NUMBER) + return node_error(node, "Number or number return type expected here", bypass); + + break; + + case OPF_SHIP: + if (type2 == SEXP_ATOM_STRING) + if (ship_name_lookup(nodes[node].text) == -1) + type2 = 0; + + if (type2 != SEXP_ATOM_STRING) + return node_error(node, "Ship name expected here", bypass); + + break; + + case OPF_WING: + if (type2 == SEXP_ATOM_STRING) + if (wing_name_lookup(nodes[node].text) == -1) + type2 = 0; + + if (type2 != SEXP_ATOM_STRING) + return node_error(node, "Wing name expected here", bypass); + + break; + + case OPF_SHIP_WING: + if (type2 == SEXP_ATOM_STRING) + if (ship_name_lookup(nodes[node].text) == -1) + if (wing_name_lookup(nodes[node].text) == -1) + type2 = 0; + + if (type2 != SEXP_ATOM_STRING) + return node_error(node, "Ship or wing name expected here", bypass); + + break; + + case OPF_BOOL: + if (type2 != OPR_BOOL) + return node_error(node, "Boolean return type expected here", bypass); + + break; + + case OPF_NULL: + if (type2 != OPR_NULL) + return node_error(node, "No return type operator expected here", bypass); + + break; + + case OPF_POINT: + if (type2 != SEXP_ATOM_STRING || verify_vector(nodes[node].text)) + return node_error(node, "3d coordinate expected here", bypass); + + break; + + case OPF_SUBSYSTEM: + if (type2 == SEXP_ATOM_STRING) + if (ai_get_subsystem_type(nodes[node].text) == SUBSYSTEM_UNKNOWN) + type2 = 0; + + if (type2 != SEXP_ATOM_STRING) + return node_error(node, "Subsystem name expected here", bypass); + + break; + + case OPF_IFF: + if (type2 == SEXP_ATOM_STRING) { + for (i=0; i -1); + int sexp_var_index; + + // get arg + int parent = nodes[item_index].parent; + Assert(parent != -1); + + if ( !stricmp(nodes[parent].text, "modify-variable") ) { + Assert(nodes[parent].child != -1); + sexp_var_index = get_tree_name_to_sexp_variable_index(nodes[nodes[parent].child].text); + Assert(sexp_var_index != -1); + } else { + Int3(); // should not be called otherwise + } + + if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_NUMBER) { + return OPF_NUMBER; + } else if (Sexp_variables[sexp_var_index].type & SEXP_VARIABLE_STRING) { + return OPF_AMBIGUOUS; + } else { + Int3(); + return 0; + } +} + + +void sexp_tree::verify_and_fix_arguments(int node) +{ + int op, arg_num, type, tmp; + static int flag = 0; + sexp_list_item *list, *ptr; + + if (flag) + return; + + flag++; + op = identify_operator(nodes[node].text); + if (op < 0) + return; + + tmp = item_index; + item_index = node; + + arg_num = 0; + item_index = nodes[node].child; + while (item_index >= 0) { + // get listing of valid argument values for node item_index + type = query_operator_argument_type(op, arg_num); + // special case for modify-variable + if (type == OPF_AMBIGUOUS) { + // check if parent variable type is number, returns OPF_NUMBER or OPF_AMBIGUOUS + type = get_modify_variable_type(); + } + if (query_restricted_opf_range(type)) { + list = get_listing_opf(type, node, arg_num); + if (!list && (arg_num >= Operators[op].min)) { + free_node(item_index, 1); + item_index = tmp; + flag--; + return; + } + + if (list) { + // get a pointer to nodes[item_index].text for normal value + // or default variable value if variable + char *text_ptr; + char default_variable_text[TOKEN_LENGTH]; + if (nodes[item_index].type & SEXPT_VARIABLE) { + // special case for modify-variable + if ( !stricmp(Operators[op].text, "modify-variable") ) { + // make text_ptr to start - before '(' + get_variable_name_from_sexp_tree_node_text(nodes[item_index].text, default_variable_text); + text_ptr = default_variable_text; + } else { + get_variable_default_text_from_variable_text(nodes[item_index].text, default_variable_text); + text_ptr = default_variable_text; + } + } else { + text_ptr = nodes[item_index].text; + } + + ptr = list; + while (ptr) { + + if (ptr->text != NULL) { + // make sure text is not NULL + // check that proposed text is valid for operator + if ( !stricmp(ptr->text, text_ptr) ) + break; + + ptr = ptr->next; + } else { + // text is NULL, so set ptr to NULL to end loop + ptr = NULL; + } + } + + if (!ptr) { // argument isn't in list of valid choices, + if (list->op >= 0) { + replace_operator(list->text); + } else { + replace_data(list->text, list->type); + } + } + + } else { + bool invalid = false; + if (type == OPF_AMBIGUOUS) { + if (SEXPT_TYPE(nodes[item_index].type) == SEXPT_OPERATOR) { + invalid = true; + } + } else { + if (SEXPT_TYPE(nodes[item_index].type) != SEXPT_OPERATOR) { + invalid = true; + } + } + + if (invalid) { + replace_data("", (SEXPT_STRING | SEXPT_VALID)); + } + } + + if (nodes[item_index].type & SEXPT_OPERATOR) + verify_and_fix_arguments(item_index); + } + + item_index = nodes[item_index].next; + arg_num++; + } + + item_index = tmp; + flag--; +} + +void sexp_tree::replace_data(char *data, int type) +{ + int node; + HTREEITEM h; + + node = nodes[item_index].child; + if (node != -1) + free_node2(node); + + nodes[item_index].child = -1; + h = nodes[item_index].handle; + while (ItemHasChildren(h)) + DeleteItem(GetChildItem(h)); + + set_node(item_index, type, data); + SetItemText(h, data); + SetItemImage(h, BITMAP_DATA, BITMAP_DATA); + nodes[item_index].flags = EDITABLE; + + // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced) + verify_and_fix_arguments(nodes[item_index].parent); + + *modified = 1; + update_help(GetSelectedItem()); +} + + +// Replaces data with sexp_variable type data +void sexp_tree::replace_variable_data(int var_idx, int type) +{ + int node; + HTREEITEM h; + char buf[128]; + + Assert(type & SEXPT_VARIABLE); + + node = nodes[item_index].child; + if (node != -1) + free_node2(node); + + nodes[item_index].child = -1; + h = nodes[item_index].handle; + while (ItemHasChildren(h)) { + DeleteItem(GetChildItem(h)); + } + + // Assemble name + sprintf(buf, "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text); + + set_node(item_index, type, buf); + SetItemText(h, buf); + SetItemImage(h, BITMAP_VARIABLE, BITMAP_VARIABLE); + nodes[item_index].flags = NOT_EDITABLE; + + // check remaining data beyond replaced data for validity (in case any of it is dependent on data just replaced) + verify_and_fix_arguments(nodes[item_index].parent); + + *modified = 1; + update_help(GetSelectedItem()); +} + + + +void sexp_tree::replace_operator(char *op) +{ + int node; + HTREEITEM h; + + node = nodes[item_index].child; + if (node != -1) + free_node2(node); + + nodes[item_index].child = -1; + h = nodes[item_index].handle; + while (ItemHasChildren(h)) + DeleteItem(GetChildItem(h)); + + set_node(item_index, (SEXPT_OPERATOR | SEXPT_VALID), op); + SetItemText(h, op); + nodes[item_index].flags = OPERAND; + *modified = 1; + update_help(GetSelectedItem()); + + // hack added at Allender's request. If changing ship in an ai-dock operator, re-default + // docking point. +} + +/*void sexp_tree::replace_one_arg_operator(char *op, char *data, int type) +{ + char str[80]; + int node; + HTREEITEM h; + + node = nodes[item_index].child; + if (node != -1) + free_node2(node); + + nodes[item_index].child = -1; + h = nodes[item_index].handle; + while (ItemHasChildren(h)) + DeleteItem(GetChildItem(h)); + + node = allocate_node(item_index); + set_node(item_index, SEXPT_OPERATOR, op); + set_node(node, type, data); + sprintf(str, "%s %s", op, data); + SetItemText(h, str); + nodes[item_index].flags = OPERAND | EDITABLE; + nodes[node].flags = COMBINED; + *modified = 1; + update_help(GetSelectedItem()); +}*/ + +// moves a whole sexp tree branch to a new position under 'parent' and after 'after'. +// The expansion state is preserved, and node handles are updated. +void sexp_tree::move_branch(int source, int parent) +{ + int node; + + // if no source, skip everything + if (source != -1) { + node = nodes[source].parent; + if (node != -1) { + if (nodes[node].child == source) + nodes[node].child = nodes[source].next; + else { + node = nodes[node].child; + while (nodes[node].next != source) { + node = nodes[node].next; + Assert(node != -1); + } + + nodes[node].next = nodes[source].next; + } + } + + nodes[source].parent = parent; + nodes[source].next = -1; + if (parent) { + if (nodes[parent].child == -1) + nodes[parent].child = source; + else { + node = nodes[parent].child; + while (nodes[node].next != -1) + node = nodes[node].next; + + nodes[node].next = source; + } + + move_branch(nodes[source].handle, nodes[parent].handle); + + } else + move_branch(nodes[source].handle); + } +} + +HTREEITEM sexp_tree::move_branch(HTREEITEM source, HTREEITEM parent, HTREEITEM after) +{ + int i, image1, image2; + HTREEITEM h = 0, child, next; + + if (source) { + for (i=0; iDragShowNolock(TRUE); + m_p_image_list->SetDragCursorImage(0, CPoint(0, 0)); + m_p_image_list->BeginDrag(0, CPoint(0,0)); + m_p_image_list->DragMove(m_pt); + m_p_image_list->DragEnter(this, m_pt); + SetCapture(); + m_dragging = TRUE; +} + +void sexp_tree::OnLButtonDown(UINT nFlags, CPoint point) +{ + m_pt = point; + CTreeCtrl::OnLButtonDown(nFlags, point); +} + +void sexp_tree::OnMouseMove(UINT nFlags, CPoint point) +{ + HTREEITEM hitem; + UINT flags; + + if (m_dragging) { + ASSERT(m_p_image_list != NULL); + m_p_image_list->DragMove(point); + if ((hitem = HitTest(point, &flags)) != NULL) + if (!GetParentItem(hitem)) { + m_p_image_list->DragLeave(this); + SelectDropTarget(hitem); + m_h_drop = hitem; + m_p_image_list->DragEnter(this, point); + } + } + + CTreeCtrl::OnMouseMove(nFlags, point); +} + +void sexp_tree::OnLButtonUp(UINT nFlags, CPoint point) +{ + int index1, index2; + + if (m_dragging) { + ASSERT(m_p_image_list != NULL); + m_p_image_list->DragLeave(this); + m_p_image_list->EndDrag(); + delete m_p_image_list; + m_p_image_list = NULL; + + if (m_h_drop && m_h_drag != m_h_drop) { + Assert(m_h_drag); + index1 = GetItemData(m_h_drag); + index2 = GetItemData(m_h_drop); + swap_roots(m_h_drag, m_h_drop); + if (m_mode == MODE_GOALS) { + Assert(Goal_editor_dlg); + Goal_editor_dlg->swap_handler(index1, index2); + + } else if (m_mode == MODE_EVENTS) { + Assert(Event_editor_dlg); + Event_editor_dlg->swap_handler(index1, index2); + + } else if (m_mode == MODE_CAMPAIGN) { + Campaign_tree_formp->swap_handler(index1, index2); + + } else + Assert(0); + + } else + MessageBeep(0); + + ReleaseCapture(); + m_dragging = FALSE; + SelectDropTarget(NULL); + } + + CTreeCtrl::OnLButtonUp(nFlags, point); +} + +void sexp_tree::setup(CEdit *ptr) +{ + CImageList *pimagelist; + CBitmap bitmap; + + help_box = ptr; + if (help_box) { + int stops[2] = { 10, 30 }; + + help_box -> SetTabStops(2, (LPINT) stops); + } + + pimagelist = GetImageList(TVSIL_NORMAL); + if (!pimagelist) { + pimagelist = new CImageList(); + pimagelist->Create(16, 16, TRUE/*bMask*/, 2, 9); + + bitmap.LoadBitmap(IDB_OPERATOR); + pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF); + bitmap.DeleteObject(); + + bitmap.LoadBitmap(IDB_DATA); + pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF); + bitmap.DeleteObject(); + + bitmap.LoadBitmap(IDB_VARIABLE); + pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF); + bitmap.DeleteObject(); + + bitmap.LoadBitmap(IDB_ROOT); + pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF); + bitmap.DeleteObject(); + + bitmap.LoadBitmap(IDB_ROOT_DIRECTIVE); + pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF); + bitmap.DeleteObject(); + + bitmap.LoadBitmap(IDB_CHAINED); + pimagelist->Add(&bitmap, (COLORREF) 0xFF00FF); + bitmap.DeleteObject(); + + bitmap.LoadBitmap(IDB_CHAINED_DIRECTIVE); + pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF); + bitmap.DeleteObject(); + + bitmap.LoadBitmap(IDB_GREEN_DOT); + pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF); + bitmap.DeleteObject(); + + bitmap.LoadBitmap(IDB_BLACK_DOT); + pimagelist->Add(&bitmap, (COLORREF) 0xFFFFFF); + bitmap.DeleteObject(); + + SetImageList(pimagelist, TVSIL_NORMAL); + } +} +//#define BITMAP_OPERATOR 0 +//#define BITMAP_DATA 1 +//#define BITMAP_VARIABLE 2 +//#define BITMAP_ROOT 3 +//#define BITMAP_ROOT_DIRECTIVE 4 +//#define BITMAP_CHAIN 5 +//#define BITMAP_CHAIN_DIRECTIVE 6 +//#define BITMAP_GREEN_DOT 7 +//#define BITMAP_BLACK_DOT 8 + + +HTREEITEM sexp_tree::insert(LPCTSTR lpszItem, int image, int sel_image, HTREEITEM hParent, HTREEITEM hInsertAfter) +{ + return InsertItem(lpszItem, image, sel_image, hParent, hInsertAfter); + +} + +void sexp_tree::OnDestroy() +{ + CImageList *pimagelist; + + pimagelist = GetImageList(TVSIL_NORMAL); + if (pimagelist) { + pimagelist->DeleteImageList(); + delete pimagelist; + } + + CTreeCtrl::OnDestroy(); +} + +HTREEITEM sexp_tree::handle(int node) +{ + return nodes[node].handle; +} + +char *sexp_tree::help(int code) +{ + int i; + + i = sizeof(Sexp_help) / sizeof(sexp_help_struct); + while (i--) { + if (Sexp_help[i].id == code) + break; + } + + if (i >= 0) + return Sexp_help[i].help; + + return NULL; +} + +// get type of item clicked on +int sexp_tree::get_type(HTREEITEM h) +{ + int i; + + // get index into sexp_tree + for (i=0; i= MAX_SEXP_TREE_SIZE) ) { + // Int3(); // This would be the root of the tree -- ie, event name + return -1; + } + + return nodes[i].type; +} + + +void sexp_tree::update_help(HTREEITEM h) +{ + char *str; + int i, j, z, c, code; + CString text; + + for (i=0; iGetDlgItem(IDC_HELP_BOX); + if (!help_box || !::IsWindow(help_box->m_hWnd)) + return; + + for (i=0; i= MAX_SEXP_TREE_SIZE) || !nodes[i].type) { + help_box->SetWindowText(""); + return; + } + + if (SEXPT_TYPE(nodes[i].type) != SEXPT_OPERATOR) { + z = nodes[i].parent; + if (z < 0) { + Warning(LOCATION, "Sexp data \"%s\" has no parent!", nodes[i].text); + return; + } + + code = identify_operator(nodes[z].text); + if (code >= 0) { + c = 0; + j = nodes[z].child; + while ((j >= 0) && (j != i)) { + j = nodes[j].next; + c++; + } + + Assert(j >= 0); + if (query_operator_argument_type(code, c) == OPF_MESSAGE) { + for (j=0; jSetWindowText(text); + return; + } + } + } + + i = z; + } + + code = find_operator(nodes[i].text); + str = help(code); + if (!str) + str = "No help available"; + + help_box->SetWindowText(str); +} + +// find list of sexp_tree nodes with text +// stuff node indices into find[] +int sexp_tree::find_text(char *text, int *find) +{ + int i, find_count; + + // initialize find + for (i=0; iwVKey; + if (key == VK_SPACE) + EditLabel(GetSelectedItem()); + + *pResult = 0; +} + +// Determine if a given opf code has a restricted argument range (i.e. has a specific, limited +// set of argument values, or has virtually unlimited possibilities. For example, boolean values +// only have true or false, so it is restricted, but a number could be anything, so it's not. +// +int sexp_tree::query_restricted_opf_range(int opf) +{ + switch (opf) { + case OPF_NUMBER: + case OPF_POSITIVE: + case OPF_WHO_FROM: + return 0; + } + + return 1; +} + +// generate listing of valid argument values. +// opf = operator format to generate list for +// parent_node = the parent node we are generating list for +// arg_index = argument number of parent this argument will go at +// +sexp_list_item *sexp_tree::get_listing_opf(int opf, int parent_node, int arg_index) +{ + switch (opf) { + case OPF_NONE: + return NULL; + + case OPF_NULL: + return get_listing_opf_null(); + + case OPF_BOOL: + return get_listing_opf_bool(parent_node); + + case OPF_NUMBER: + return get_listing_opf_number(); + + case OPF_SHIP: + return get_listing_opf_ship(parent_node); + + case OPF_WING: + return get_listing_opf_wing(); + + case OPF_AWACS_SUBSYSTEM: + case OPF_SUBSYSTEM: + return get_listing_opf_subsystem(parent_node, arg_index); + + case OPF_POINT: + return get_listing_opf_point(); + + case OPF_IFF: + return get_listing_opf_iff(); + + case OPF_AI_GOAL: + return get_listing_opf_ai_goal(parent_node); + + case OPF_DOCKER_POINT: + return get_listing_opf_docker_point(parent_node); + + case OPF_DOCKEE_POINT: + return get_listing_opf_dockee_point(parent_node); + + case OPF_MESSAGE: + return get_listing_opf_message(); + + case OPF_WHO_FROM: + return get_listing_opf_who_from(); + + case OPF_PRIORITY: + return get_listing_opf_priority(); + + case OPF_WAYPOINT_PATH: + return get_listing_opf_waypoint_path(); + + case OPF_POSITIVE: + return get_listing_opf_positive(); + + case OPF_MISSION_NAME: + return get_listing_opf_mission_name(); + + case OPF_SHIP_POINT: + return get_listing_opf_ship_point(); + + case OPF_GOAL_NAME: + return get_listing_opf_goal_name(parent_node); + + case OPF_SHIP_WING: + return get_listing_opf_ship_wing(); + + case OPF_SHIP_WING_POINT: + return get_listing_opf_ship_wing_point(); + + case OPF_SHIP_TYPE: + return get_listing_opf_ship_type(); + + case OPF_KEYPRESS: + return get_listing_opf_keypress(); + + case OPF_EVENT_NAME: + return get_listing_opf_event_name(parent_node); + + case OPF_AI_ORDER: + return get_listing_opf_ai_order(); + + case OPF_SKILL_LEVEL: + return get_listing_opf_skill_level(); + + case OPF_MEDAL_NAME: + return get_listing_opf_medal_name(); + + case OPF_WEAPON_NAME: + return get_listing_opf_weapon_name(); + + case OPF_SHIP_CLASS_NAME: + return get_listing_opf_ship_class_name(); + + case OPF_HUD_GAUGE_NAME: + return get_listing_opf_hud_gauge_name(); + + case OPF_HUGE_WEAPON: + return get_listing_opf_huge_weapon(); + + case OPF_SHIP_NOT_PLAYER: + return get_listing_opf_ship_not_player(); + + case OPF_JUMP_NODE_NAME: + return get_listing_opf_jump_nodes(); + + case OPF_VARIABLE_NAME: + return get_listing_opf_variable_names(); + + case OPF_AMBIGUOUS: + return NULL(); + + default: + Int3(); // unknown OPF code + } + + return NULL; +} + +sexp_list_item *sexp_tree::get_listing_opf_null() +{ + int i; + sexp_list_item head; + + for (i=0; i= 0 ) { + op = find_operator(nodes[parent_node].text); + + // prune out to only capital ships + if (!stricmp(nodes[parent_node].text, "cap-subsys-cargo-known-delay")) { + require_cap_ship = 1; + } + + // get the dock_ship number of if this goal is an ai dock goal. used to prune out unwanted ships out + // of the generated ship list + dock_ship = -1; + if ( op == OP_AI_DOCK ) { + int z; + + z = nodes[parent_node].parent; + Assert(z >= 0); + Assert(!stricmp(nodes[z].text, "add-ship-goal") || !stricmp(nodes[z].text, "add-wing-goal") || !stricmp(nodes[z].text, "add-goal")); + + z = nodes[z].child; + Assert(z >= 0); + + dock_ship = ship_name_lookup(nodes[z].text, 1); + Assert( dock_ship != -1 ); + } + } + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) { + if ( op == OP_AI_DOCK ) { + // only include those ships in the list which the given ship can dock with. + if ( (dock_ship != ptr->instance) && ship_docking_valid(dock_ship , ptr->instance) ) + head.add_data(Ships[ptr->instance].ship_name ); + + } else { + if ( !require_cap_ship || (Ship_info[Ships[ptr->instance].ship_info_index].flags & SIF_HUGE_SHIP) ) { + head.add_data(Ships[ptr->instance].ship_name); + } + } + } + + ptr = GET_NEXT(ptr); + } + + return head.next; +} + +sexp_list_item *sexp_tree::get_listing_opf_wing() +{ + int i; + sexp_list_item head; + + for (i=0; i= 0); + + // get the operator type of the node + op = find_operator(nodes[parent_node].text); + + // first child node + child = nodes[parent_node].child; + Assert(child >= 0); + + switch(op){ + // where we care about hull strength + case OP_REPAIR_SUBSYSTEM: + case OP_SABOTAGE_SUBSYSTEM: + case OP_SET_SUBSYSTEM_STRNGTH: + special_subsys = OPS_STRENGTH; + break; + + // awacs subsystems + case OP_AWACS_SET_RADIUS: + special_subsys = OPS_AWACS; + break; + + // where we care about capital ship subsystem cargo + case OP_CAP_SUBSYS_CARGO_KNOWN_DELAY: + special_subsys = OPS_CAP_CARGO; + + // get the next sibling + child = nodes[child].next; + break; + + // where we care about turrets carrying beam weapons + case OP_BEAM_FIRE: + special_subsys = OPS_BEAM_TURRET; + + // if this is arg index 3 (targeted ship) + if(arg_index == 3){ + Assert(arg_index == 3); + child = nodes[child].next; + Assert(child >= 0); + child = nodes[child].next; + } else { + Assert(arg_index == 1); + } + break; + } + + // now find the ship and add all relevant subsystems + Assert(child >= 0); + sh = ship_name_lookup(nodes[child].text, 1); + if (sh >= 0) { + subsys = GET_FIRST(&Ships[sh].subsys_list); + while (subsys != END_OF_LIST(&Ships[sh].subsys_list)) { + // add stuff + switch(special_subsys){ + // subsystem cargo + case OPS_CAP_CARGO: + if (valid_cap_subsys_cargo_list(subsys->system_info->subobj_name) ) { + head.add_data(subsys->system_info->subobj_name); + } + break; + + // beam fire + case OPS_BEAM_TURRET: + head.add_data(subsys->system_info->subobj_name); + break; + + // awacs level + case OPS_AWACS: + if (subsys->system_info->flags & MSS_FLAG_AWACS) { + head.add_data(subsys->system_info->subobj_name); + } + break; + + // everything else + default: + head.add_data(subsys->system_info->subobj_name); + break; + } + + // next subsystem + subsys = GET_NEXT(subsys); + } + } + + // if one of the subsystem strength operators, append the Hull string + if(special_subsys == OPS_STRENGTH){ + head.add_data(SEXP_HULL_STRING); + } + + return head.next; +} + +sexp_list_item *sexp_tree::get_listing_opf_point() +{ + char buf[NAME_LENGTH+8]; + int i, j; + sexp_list_item head; + + for (i=0; i= 0); + child = nodes[parent_node].child; + Assert(child >= 0); + n = ship_name_lookup(nodes[child].text, 1); + if (n >= 0) { + // add operators if it's an ai-goal and ai-goal is allowed for that ship + for (i=0; i= 0) { + for (w=0; w= 0); + Assert(!stricmp(nodes[parent_node].text, "ai-dock")); + + z = nodes[parent_node].parent; + Assert(z >= 0); + Assert(!stricmp(nodes[z].text, "add-ship-goal") || !stricmp(nodes[z].text, "add-wing-goal") || !stricmp(nodes[z].text, "add-goal")); + + z = nodes[z].child; + Assert(z >= 0); + + sh = ship_name_lookup(nodes[z].text, 1); + if (sh >= 0) { + z = get_docking_list(Ships[sh].modelnum); + for (i=0; i= 0); + Assert(!stricmp(nodes[parent_node].text, "ai-dock")); + + z = nodes[parent_node].child; + Assert(z >= 0); + + sh = ship_name_lookup(nodes[z].text, 1); + if (sh >= 0) { + z = get_docking_list(Ships[sh].modelnum); + for (i=0; icurrent_message_name(i), str; i++) + head.add_data(str); + + } else { + for (i=Num_builtin_messages; i"); + head.add_data("#Command"); + head.add_data(""); + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) + if (!(Ship_info[Ships[get_ship_from_obj(ptr)].ship_info_index].flags & SIF_NOT_FLYABLE)) + head.add_data(Ships[ptr->instance].ship_name); + + ptr = GET_NEXT(ptr); + } + + return head.next; +} + +sexp_list_item *sexp_tree::get_listing_opf_priority() +{ + sexp_list_item head; + + head.add_data("High"); + head.add_data("Normal"); + head.add_data("Low"); + return head.next; +} + +sexp_list_item *sexp_tree::get_listing_opf_waypoint_path() +{ + int i; + sexp_list_item head; + + for (i=0; i"); + head.add_data(""); + head.add_data(""); + head.add_data(""); + head.add_list(get_listing_opf_ship()); + head.add_list(get_listing_opf_wing()); + head.add_list(get_listing_opf_point()); + return head.next; +} + +sexp_list_item *sexp_tree::get_listing_opf_mission_name() +{ + int i; + sexp_list_item head; + + if ((m_mode == MODE_CAMPAIGN) && (Cur_campaign_mission >= 0)) { + for (i=0; i= 0); + child = nodes[parent_node].child; + Assert(child >= 0); + + for (m=0; m 0) { + head.add_data_dup(textify_scancode(Control_config[i].key_default)); + } + } + + return head.next; +} + +sexp_list_item *sexp_tree::get_listing_opf_event_name(int parent_node) +{ + int i, m; + sexp_list_item head; + + + if (m_mode == MODE_CAMPAIGN) { + int child; + + Assert(parent_node >= 0); + child = nodes[parent_node].child; + Assert(child >= 0); + + for (m=0; mtype == OBJ_SHIP) + head.add_data(Ships[ptr->instance].ship_name); + + ptr = GET_NEXT(ptr); + } + + return head.next; +} + +sexp_list_item *sexp_tree::get_listing_opf_jump_nodes() +{ + int i; + sexp_list_item head; + + for (i = 0; i < Num_jump_nodes; i++ ) + head.add_data( Jump_nodes[i].name ); + + return head.next; +} + +// creates list of Sexp_variables +sexp_list_item *sexp_tree::get_listing_opf_variable_names() +{ + int i; + sexp_list_item head; + + for (i=0; i 0) && (nodes[item_index].type & SEXPT_VARIABLE) ) { + + return get_tree_name_to_sexp_variable_index(nodes[item_index].text); + } else { + return -1; + } +} + +int sexp_tree::get_tree_name_to_sexp_variable_index(const char *tree_name) +{ + char var_name[TOKEN_LENGTH]; + + int chars_to_copy = strcspn(tree_name, "("); + Assert(chars_to_copy < TOKEN_LENGTH - 1); + + // Copy up to '(' and add null termination + strncpy(var_name, tree_name, chars_to_copy); + var_name[chars_to_copy] = '\0'; + + // Look up index + return get_index_sexp_variable_name(var_name); +} + +int sexp_tree::get_variable_count(const char *var_name) +{ + int idx; + int count = 0; + char compare_name[64]; + + // get name to compare + strcpy(compare_name, var_name); + strcat(compare_name, "("); + + // look for compare name + for (idx=0; idx= 0) { + z = (Objects[Ships[i].objnum].flags & OF_NO_SHIELDS) ? 1 : 0; + if (!teams[Ships[i].team]) + Shield_sys_teams[Ships[i].team] = z; + else if (Shield_sys_teams[Ships[i].team] != z) + Shield_sys_teams[Ships[i].team] = 2; + + if (!types[Ships[i].ship_info_index]) + Shield_sys_types[Ships[i].ship_info_index] = z; + else if (Shield_sys_types[Ships[i].ship_info_index] != z) + Shield_sys_types[Ships[i].ship_info_index] = 2; + + teams[Ships[i].team]++; + types[Ships[i].ship_info_index]++; + } + + box = (CComboBox *) GetDlgItem(IDC_TYPE); + box->ResetContent(); + for (i=0; iAddString(Ship_info[i].name); + + box = (CComboBox *) GetDlgItem(IDC_TEAM); + box->ResetContent(); + for (i=0; iAddString(Team_names[i]); + + CDialog::OnInitDialog(); + set_team(); + set_type(); + return TRUE; +} + +void shield_sys_dlg::OnOK() +{ + int i, z; + + OnSelchangeTeam(); + OnSelchangeType(); + for (i=0; i= 0) { + z = Shield_sys_teams[Ships[i].team]; + if (!Shield_sys_types[Ships[i].ship_info_index]) + z = 0; + else if (Shield_sys_types[Ships[i].ship_info_index] == 1) + z = 1; + + if (!z) + Objects[Ships[i].objnum].flags &= ~OF_NO_SHIELDS; + else if (z == 1) + Objects[Ships[i].objnum].flags |= OF_NO_SHIELDS; + } + + CDialog::OnOK(); +} + +void shield_sys_dlg::OnSelchangeTeam() +{ + Assert(m_team >= 0); + if (((CButton *) GetDlgItem(IDC_TEAM_YES))->GetCheck()) + Shield_sys_teams[m_team] = 0; + else if (((CButton *) GetDlgItem(IDC_TEAM_NO))->GetCheck()) + Shield_sys_teams[m_team] = 1; + + UpdateData(TRUE); + set_team(); +} + +void shield_sys_dlg::set_team() +{ + if (!Shield_sys_teams[m_team]) + ((CButton *) GetDlgItem(IDC_TEAM_YES))->SetCheck(TRUE); + else + ((CButton *) GetDlgItem(IDC_TEAM_YES))->SetCheck(FALSE); + + if (Shield_sys_teams[m_team] == 1) + ((CButton *) GetDlgItem(IDC_TEAM_NO))->SetCheck(TRUE); + else + ((CButton *) GetDlgItem(IDC_TEAM_NO))->SetCheck(FALSE); +} + +void shield_sys_dlg::OnSelchangeType() +{ + Assert(m_type >= 0); + if (((CButton *) GetDlgItem(IDC_TYPE_YES))->GetCheck()) + Shield_sys_types[m_type] = 0; + else if (((CButton *) GetDlgItem(IDC_TYPE_NO))->GetCheck()) + Shield_sys_types[m_type] = 1; + + UpdateData(TRUE); + set_type(); +} + +void shield_sys_dlg::set_type() +{ + if (!Shield_sys_types[m_type]) + ((CButton *) GetDlgItem(IDC_TYPE_YES))->SetCheck(TRUE); + else + ((CButton *) GetDlgItem(IDC_TYPE_YES))->SetCheck(FALSE); + + if (Shield_sys_types[m_type] == 1) + ((CButton *) GetDlgItem(IDC_TYPE_NO))->SetCheck(TRUE); + else + ((CButton *) GetDlgItem(IDC_TYPE_NO))->SetCheck(FALSE); +} diff --git a/src/fred2/ship_select.cpp b/src/fred2/ship_select.cpp new file mode 100644 index 0000000..ceb0fa8 --- /dev/null +++ b/src/fred2/ship_select.cpp @@ -0,0 +1,592 @@ +/* + * $Logfile: /Freespace2/code/FRED2/ship_select.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Object selection (marking) dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 21 2/13/98 11:48a Hoffoss + * Fixed up some bugs with object selection when filters are turned off. + * + * 20 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 19 9/09/97 10:29a Hoffoss + * Added support for neutral team, and fixed changes made to how team is + * used in ship structure. + * + * 18 9/06/97 2:13p Mike + * Replace support for TEAM_NEUTRAL + * + * 17 8/26/97 11:08a Hoffoss + * Added waypoint paths to object selection dialog. + * + * 16 7/28/97 5:10p Hoffoss + * Removed all occurances of neutral team from Fred. + * + * 15 7/24/97 2:43p Hoffoss + * Made changes whiteside requested. Double clicking acts as clicking ok + * button. + * + * 14 7/24/97 10:24a Mike + * Restore support for Unknown team + * + * 13 7/10/97 11:24a Hoffoss + * Fixed bugs in briefing editor system. Make icon button not updating + * when selecting ships via list select, and after making icon, it wasn't + * being displayed, though it was selected. + * + * 12 5/26/97 10:30a Hoffoss + * Added select wing to object select dialog. + * + * 11 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 10 3/07/97 4:37p Mike + * Make rockeye missile home. + * Remove UNKNOWN and NEUTRAL teams. + * + * 9 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "ship_select.h" +#include "linklist.h" +#include "object.h" +#include "ship.h" +#include "management.h" +#include "fredview.h" + +#define ACTIVITY_SHIP 1 +#define ACTIVITY_WING 2 + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +int filter_ships = TRUE; +int filter_starts = TRUE; +int filter_waypoints = TRUE; +int filter_friendly = TRUE; +int filter_hostile = TRUE; +int filter_neutral = TRUE; +int filter_unknown = TRUE; + +///////////////////////////////////////////////////////////////////////////// +// ship_select dialog + +ship_select::ship_select(CWnd* pParent /*=NULL*/) + : CDialog(ship_select::IDD, pParent) +{ + //{{AFX_DATA_INIT(ship_select) + m_filter_ships = TRUE; + m_filter_starts = TRUE; + m_filter_waypoints = TRUE; + m_filter_friendly = TRUE; + m_filter_hostile = TRUE; + m_filter_neutral = TRUE; + m_filter_unknown = TRUE; + //}}AFX_DATA_INIT + + m_filter_ships = filter_ships; + m_filter_starts = filter_starts; + m_filter_waypoints = filter_waypoints; + m_filter_friendly = filter_friendly; + m_filter_hostile = filter_hostile; + m_filter_neutral = filter_neutral; + m_filter_unknown = filter_unknown; + activity = 0; +} + +void ship_select::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(ship_select) + DDX_Control(pDX, IDC_WING_LIST, m_wing_list); + DDX_Control(pDX, IDC_SHIP_LIST, m_ship_list); + DDX_Check(pDX, IDC_FILTER_SHIPS, m_filter_ships); + DDX_Check(pDX, IDC_FILTER_STARTS, m_filter_starts); + DDX_Check(pDX, IDC_FILTER_WAYPOINTS, m_filter_waypoints); + DDX_Check(pDX, IDC_FILTER_SHIPS_FRIENDLY, m_filter_friendly); + DDX_Check(pDX, IDC_FILTER_SHIPS_HOSTILE, m_filter_hostile); + DDX_Check(pDX, IDC_FILTER_SHIPS_NEUTRAL, m_filter_neutral); + DDX_Check(pDX, IDC_FILTER_SHIPS_UNKNOWN, m_filter_unknown); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(ship_select, CDialog) + //{{AFX_MSG_MAP(ship_select) + ON_CBN_SELCHANGE(IDC_WING_DISPLAY_FILTER, OnSelchangeWingDisplayFilter) + ON_BN_CLICKED(IDC_FILTER_SHIPS, OnFilterShips) + ON_BN_CLICKED(IDC_FILTER_STARTS, OnFilterStarts) + ON_BN_CLICKED(IDC_FILTER_WAYPOINTS, OnFilterWaypoints) + ON_BN_CLICKED(IDC_FILTER_SHIPS_FRIENDLY, OnFilterShipsFriendly) + ON_BN_CLICKED(IDC_FILTER_SHIPS_HOSTILE, OnFilterShipsHostile) + ON_BN_CLICKED(IDC_FILTER_SHIPS_NEUTRAL, OnFilterShipsNeutral) + ON_BN_CLICKED(IDC_FILTER_SHIPS_UNKNOWN, OnFilterShipsUnknown) + ON_BN_CLICKED(IDC_CLEAR, OnClear) + ON_BN_CLICKED(IDC_ALL, OnAll) + ON_BN_CLICKED(IDC_INVERT, OnInvert) + ON_LBN_DBLCLK(IDC_SHIP_LIST, OnDblclkShipList) + ON_LBN_SELCHANGE(IDC_WING_LIST, OnSelchangeWingList) + ON_LBN_SELCHANGE(IDC_SHIP_LIST, OnSelchangeShipList) + ON_LBN_DBLCLK(IDC_WING_LIST, OnDblclkWingList) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// ship_select message handlers + +void ship_select::OnSelchangeWingDisplayFilter() +{ + UpdateData(TRUE); + create_list(); +} + +BOOL ship_select::OnInitDialog() +{ + int i, flags; + object *ptr; + + wlist_size = wplist_size = 0; + CDialog::OnInitDialog(); + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) + { + flags = ptr->flags & ~OF_TEMP_MARKED; + if (flags & OF_MARKED) + flags |= OF_TEMP_MARKED; + else + flags &= ~OF_TEMP_MARKED; + + ptr->flags = flags; + ptr = GET_NEXT(ptr); + } + + list_size = 0; + create_list(); + GetDlgItem(IDC_FILTER_SHIPS_FRIENDLY)->EnableWindow(m_filter_ships); + GetDlgItem(IDC_FILTER_SHIPS_HOSTILE)->EnableWindow(m_filter_ships); + GetDlgItem(IDC_FILTER_SHIPS_NEUTRAL)->EnableWindow(m_filter_ships); + GetDlgItem(IDC_FILTER_SHIPS_UNKNOWN)->EnableWindow(m_filter_ships); + + // Elements 0 - wlist_size are wings, and elements wlist_size - wplist_size are waypoint paths + m_wing_list.ResetContent(); + wlist_size = 0; + for (i=0; itype == OBJ_START) + { + m_ship_list.AddString(Ships[ptr->instance].ship_name); + obj_index[list_size++] = ptr; + if (ptr->flags & OF_TEMP_MARKED) + m_ship_list.SetSel(list_size - 1); + } + + ptr = GET_NEXT(ptr); + } + } + + if (m_filter_ships) { + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) + { + if (ptr->type == OBJ_SHIP) + { + flag = 0; + switch (Ships[ptr->instance].team) + { + case TEAM_FRIENDLY: + flag = m_filter_friendly; + break; + + case TEAM_HOSTILE: + flag = m_filter_hostile; + break; + + case TEAM_NEUTRAL: + flag = m_filter_neutral; + break; + + case TEAM_UNKNOWN: + flag = m_filter_unknown; + break; + + default: + flag = m_filter_hostile; + break; + } + + if (flag) + { + m_ship_list.AddString(Ships[ptr->instance].ship_name); + obj_index[list_size++] = ptr; + if (ptr->flags & OF_TEMP_MARKED) + m_ship_list.SetSel(list_size - 1); + } + } + + ptr = GET_NEXT(ptr); + } + } + + if (m_filter_waypoints) + { + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) + { + if (ptr->type == OBJ_WAYPOINT) + { + sprintf(text, "%s:%d", Waypoint_lists[ptr->instance / 65536].name, + (ptr->instance & 0xffff) + 1); + m_ship_list.AddString(text); + obj_index[list_size++] = ptr; + if (ptr->flags & OF_TEMP_MARKED) + m_ship_list.SetSel(list_size - 1); + } + + ptr = GET_NEXT(ptr); + } + } +} + +void ship_select::OnOK() +{ + object *ptr; + + unmark_all(); + update_status(); + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) + { + if (ptr->flags & OF_TEMP_MARKED) + mark_object(OBJ_INDEX(ptr)); + + ptr = GET_NEXT(ptr); + } + + if (query_valid_object() && (Marked == 1) && (Objects[cur_object_index].type == OBJ_POINT)) { + Assert(Briefing_dialog); + Briefing_dialog->icon_select(Objects[cur_object_index].instance); + + } else { + if (Briefing_dialog) + Briefing_dialog->icon_select(-1); + } + + filter_ships = m_filter_ships; + filter_starts = m_filter_starts; + filter_waypoints = m_filter_waypoints; + filter_friendly = m_filter_friendly; + filter_hostile = m_filter_hostile; + filter_neutral = m_filter_neutral; + filter_unknown = m_filter_unknown; + CDialog::OnOK(); +} + +void ship_select::update_status() +{ + int i, z; + object *ptr; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + ptr->flags &= ~OF_TEMP_MARKED; + ptr = GET_NEXT(ptr); + } + + for (i=0; iflags &= ~OF_TEMP_MARKED; + else + obj_index[i]->flags |= OF_TEMP_MARKED; + } + + OnSelchangeShipList(); +} + +void ship_select::OnFilterShips() +{ + UpdateData(TRUE); + create_list(); + GetDlgItem(IDC_FILTER_SHIPS_FRIENDLY)->EnableWindow(m_filter_ships); + GetDlgItem(IDC_FILTER_SHIPS_HOSTILE)->EnableWindow(m_filter_ships); + GetDlgItem(IDC_FILTER_SHIPS_NEUTRAL)->EnableWindow(m_filter_ships); + GetDlgItem(IDC_FILTER_SHIPS_UNKNOWN)->EnableWindow(m_filter_ships); +} + +void ship_select::OnFilterStarts() +{ + UpdateData(TRUE); + create_list(); +} + +void ship_select::OnFilterWaypoints() +{ + UpdateData(TRUE); + create_list(); +} + +void ship_select::OnFilterShipsFriendly() +{ + UpdateData(TRUE); + create_list(); +} + +void ship_select::OnFilterShipsHostile() +{ + UpdateData(TRUE); + create_list(); +} + +void ship_select::OnFilterShipsNeutral() +{ + UpdateData(TRUE); + create_list(); +} + +void ship_select::OnFilterShipsUnknown() +{ + UpdateData(TRUE); + create_list(); +} + +void ship_select::OnClear() +{ + int i; + object *ptr; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) + { + ptr->flags &= ~OF_TEMP_MARKED; + ptr = GET_NEXT(ptr); + } + + for (i=0; iflags |= OF_TEMP_MARKED; + m_ship_list.SetSel(i); + } + + for (i=0; i 0) + { + obj_index[i]->flags &= ~OF_TEMP_MARKED; + m_ship_list.SetSel(i, FALSE); + + } else { + obj_index[i]->flags |= OF_TEMP_MARKED; + m_ship_list.SetSel(i); + } + } + + OnSelchangeShipList(); +} + +void ship_select::OnDblclkShipList() +{ + OnOK(); + +/* int i, j, z, wing; + + z = m_ship_list.GetCaretIndex(); + switch (obj_index[z]->type) { + case OBJ_SHIP: + wing = Ships[obj_index[z]->instance].wingnum; + if (wing >= 0) { + for (i=0; i 0) ? 1 : 0; + if (z != wing_sel_last[i]) { + for (j=0; j 0) ? 1 : 0; + if (z != wing_sel_last[i]) { + for (j=0; jtype == OBJ_WAYPOINT) && (obj_index[k]->instance == wing_index[i] * 65536 + j)) { + m_ship_list.SetSel(k, z ? TRUE : FALSE); + break; + } + + wing_sel_last[i] = z; + } + } + + activity = 0; +} + +void ship_select::OnSelchangeShipList() +{ + int i, j, k, count; + + if (activity) + return; + + activity = ACTIVITY_SHIP; + for (i=0; itype == OBJ_WAYPOINT) && (obj_index[k]->instance == wing_index[i] * 65536 + j)) { + if (m_ship_list.GetSel(k)) + count++; + + break; + } + + if (count == Waypoint_lists[wing_index[i]].count) + wing_sel_last[i] = 1; + else + wing_sel_last[i] = 0; + + m_wing_list.SetSel(i, wing_sel_last[i] ? TRUE : FALSE); + } + + activity = 0; +} + +void ship_select::OnDblclkWingList() +{ + OnOK(); +} diff --git a/src/fred2/shipchecklistbox.cpp b/src/fred2/shipchecklistbox.cpp new file mode 100644 index 0000000..8a6fcff --- /dev/null +++ b/src/fred2/shipchecklistbox.cpp @@ -0,0 +1,67 @@ +/* + * $Logfile: /Freespace2/code/FRED2/ShipCheckListBox.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * A custom check list box class that allows space bar to toggle the state of all + * the selected checkboxes. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 4 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "shipchecklistbox.h" + +BEGIN_MESSAGE_MAP(ShipCheckListBox, CCheckListBox) + //{{AFX_MSG_MAP(CCheckListBox) + ON_WM_KEYDOWN() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +BOOL ShipCheckListBox::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) +{ + BOOL b; + + b = CCheckListBox::Create(LBS_OWNERDRAWFIXED | dwStyle, rect, pParentWnd, nID); + SetCheckStyle(BS_AUTOCHECKBOX); + return b; +} + +void ShipCheckListBox::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + if (nChar == VK_SPACE) + { + int i, list_size; + + list_size = GetCount(); + for (i=0; i 0) + { + if (GetCheck(i)) + SetCheck(i, 0); + else + SetCheck(i, 1); + } + + } else + CCheckListBox::OnKeyDown(nChar, nRepCnt, nFlags); +} diff --git a/src/fred2/shipclasseditordlg.cpp b/src/fred2/shipclasseditordlg.cpp new file mode 100644 index 0000000..c55d207 --- /dev/null +++ b/src/fred2/shipclasseditordlg.cpp @@ -0,0 +1,123 @@ +/* + * $Logfile: /Freespace2/code/FRED2/ShipClassEditorDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Ship class editor dialog handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "shipclasseditordlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CShipClassEditorDlg dialog + + +CShipClassEditorDlg::CShipClassEditorDlg(CWnd* pParent /*=NULL*/) + : CDialog(CShipClassEditorDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CShipClassEditorDlg) + m_ShipClassAfterburner = FALSE; + m_ShipClassAIClass = -1; + m_ShipClassArmor = _T(""); + m_ShipClassCloak = FALSE; + m_ShipClassDebrisModel = -1; + m_ShipClassModel = -1; + m_ShipClassEngine = _T(""); + m_ShipClassExplosion1 = _T(""); + m_ShipClassExplosion2 = _T(""); + m_ShipClassIFF = _T(""); + m_ShipClassManufacturer = _T(""); + m_ShipClassMaxBank = 0; + m_ShipClassMaxPitch = 0; + m_ShipClassMaxRoll = 0; + m_ShipClassMaxSpeed = 0; + m_ShipClassName = _T(""); + m_ShipClassPowerPlant = _T(""); + m_ShipClassScore = 0; + m_ShipClassShields = 0; + m_ShipClassWarpdrive = FALSE; + m_ShipClassTurretWeapon1 = _T(""); + m_ShipClassTurretWeapon2 = _T(""); + m_ShipClassWeaponSpecial = _T(""); + m_ShipClassWeapon1 = _T(""); + m_ShipClassWeapon2 = _T(""); + //}}AFX_DATA_INIT +} + + +void CShipClassEditorDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CShipClassEditorDlg) + DDX_Control(pDX, IDC_SOUNDS, m_SoundsEditor); + DDX_Control(pDX, IDC_SCLASS_WINDOW, m_ShipClassWindow); + DDX_Control(pDX, IDC_SCLASS_NEW, m_ShipClassNew); + DDX_Control(pDX, IDC_SCLASS_DELETE, m_ShipClassDelete); + DDX_Control(pDX, IDC_MODELS, m_ModelsEditor); + DDX_Control(pDX, IDC_GOALS, m_GoalsEditor); + DDX_Check(pDX, IDC_SCLASS_AFTERBURNER, m_ShipClassAfterburner); + DDX_CBIndex(pDX, IDC_SCLASS_AI_CLASS, m_ShipClassAIClass); + DDX_CBString(pDX, IDC_SCLASS_ARMOR, m_ShipClassArmor); + DDX_Check(pDX, IDC_SCLASS_CLOAK, m_ShipClassCloak); + DDX_CBIndex(pDX, IDC_SCLASS_DEBRIS_MODEL, m_ShipClassDebrisModel); + DDX_CBIndex(pDX, IDC_SCLASS_3D_OBJECT, m_ShipClassModel); + DDX_Text(pDX, IDC_SCLASS_ENGINES, m_ShipClassEngine); + DDX_CBString(pDX, IDC_SCLASS_EXPLOSION1, m_ShipClassExplosion1); + DDX_CBString(pDX, IDC_SCLASS_EXPLOSION2, m_ShipClassExplosion2); + DDX_CBString(pDX, IDC_SCLASS_IFF, m_ShipClassIFF); + DDX_Text(pDX, IDC_SCLASS_MANUFACTURER, m_ShipClassManufacturer); + DDX_Text(pDX, IDC_SCLASS_MAX_BANK, m_ShipClassMaxBank); + DDX_Text(pDX, IDC_SCLASS_MAX_PITCH, m_ShipClassMaxPitch); + DDX_Text(pDX, IDC_SCLASS_MAX_ROLL, m_ShipClassMaxRoll); + DDX_Text(pDX, IDC_SCLASS_MAX_SPEED, m_ShipClassMaxSpeed); + DDX_CBString(pDX, IDC_SCLASS_NAME, m_ShipClassName); + DDX_Text(pDX, IDC_SCLASS_POWER_PLANT, m_ShipClassPowerPlant); + DDX_Text(pDX, IDC_SCLASS_SCORE, m_ShipClassScore); + DDX_Text(pDX, IDC_SCLASS_SHIELDS, m_ShipClassShields); + DDX_Check(pDX, IDC_SCLASS_WARPDRIVE, m_ShipClassWarpdrive); + DDX_CBString(pDX, IDC_SCLASS_TURRET_WEAPON1, m_ShipClassTurretWeapon1); + DDX_CBString(pDX, IDC_SCLASS_TURRET_WEAPON2, m_ShipClassTurretWeapon2); + DDX_CBString(pDX, IDC_SCLASS_WEAPON_SPECIAL, m_ShipClassWeaponSpecial); + DDX_CBString(pDX, IDC_SCLASS_WEAPON1, m_ShipClassWeapon1); + DDX_CBString(pDX, IDC_SCLASS_WEAPON2, m_ShipClassWeapon2); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(CShipClassEditorDlg, CDialog) + //{{AFX_MSG_MAP(CShipClassEditorDlg) + // NOTE: the ClassWizard will add message map macros here + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CShipClassEditorDlg message handlers diff --git a/src/fred2/shipeditordlg.cpp b/src/fred2/shipeditordlg.cpp new file mode 100644 index 0000000..0c922ed --- /dev/null +++ b/src/fred2/shipeditordlg.cpp @@ -0,0 +1,2164 @@ +/* + * $Logfile: /Freespace2/code/fred2/ShipEditorDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Single ship editing dialog + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 8 8/18/99 1:32p Andsager + * Mark Vasudan wingmen as such. + * + * 7 8/18/99 1:12p Andsager + * Allow ships to be assigned Vasudan persona (wingman 6) + * + * 6 8/16/99 10:52p Andsager + * Allow closer ship positioning for NEAR_SHIP ship and wing arrivals. + * + * 5 8/11/99 9:27a Andsager + * Fix compile warning. + * + * 4 5/20/99 6:59p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 3 2/11/99 2:15p Andsager + * Add ship explosion modification to FRED + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 157 6/16/98 10:17a Hoffoss + * Fixed define that was backwards and no one caught before release (ya, + * great testing..) + * + * 156 5/23/98 3:33p Hoffoss + * Removed unused code in reinforcements editor and make ships.tbl button + * in ship editor disappear in release build. + * + * 155 5/21/98 12:58a Hoffoss + * Fixed warnings optimized build turned up. + * + * 154 4/28/98 2:13p Hoffoss + * Added code to help keep invalid player ship types from existing in + * mission. + * + * 153 4/24/98 2:57p Jim + * Fixed typo in code. + * + * 152 4/22/98 11:55a Hoffoss + * Fixed bug with ship editor's hide cue button when sexp help is on at + * startup. + * + * 151 4/16/98 5:59p Duncan + * Fixed bad assumption, apparently. + * + * 150 4/07/98 9:42a Allender + * put in persona combo box into ship editor. Removed code to assign + * personas based on message + * + * 149 3/30/98 1:15p Hoffoss + * Fixed bug with arrival/departure cue window size calculation. Wasn't + * working anymore after my changes to dialog initing at startup. + * + * 148 3/27/98 12:02p Sandeep + * + * 147 3/25/98 4:14p Hoffoss + * Split ship editor up into ship editor and a misc dialog, which tracks + * flags and such. + * + * 146 3/21/98 7:36p Lawrance + * Move jump nodes to own lib. + * + * 145 3/16/98 8:27p Allender + * Fred support for two new AI flags -- kamikaze and no dynamic goals. + * + * 144 3/10/98 6:11p Hoffoss + * Added jump node renaming abilities to Fred. + * + * 143 3/09/98 4:30p Allender + * multiplayer secondary weapon changes. red-alert and cargo-known-delay + * sexpressions. Add time cargo revealed to ship structure + * + * 142 2/22/98 1:21a Hoffoss + * Disabled weapon editor if mutliple ship types marked. + * + * 141 2/17/98 12:07p Hoffoss + * Changed over to using SF_CARGO_REVEALED in fred. + * + * 140 2/17/98 11:42a Hoffoss + * Added support for hidden from sensors condition. + * + * 139 2/13/98 2:42p Duncan + * Fixed bug, moved line down where it needs to be because + * pre-initializations need to be done first. + * + * 138 2/10/98 1:42p Allender + * allow > MAX_ESCORT_SHIPS to be marked as escort ships + * + * 137 2/06/98 2:54p Hoffoss + * Fixed some bugs in dialog init, and cleared up some of the confusion + * about how it works by renaming some variables and adding comments. + * + * 136 2/06/98 10:48a Hoffoss + * Fixed bug with not being able to make ships players in multi mode + * missions. + * + * 135 2/04/98 4:32p Allender + * support for multiple briefings and debriefings. Changes to mission + * type (now a bitfield). Bitfield defs for multiplayer modes + * + * 134 1/29/98 5:14p Hoffoss + * Added support for a SF_INVULNERABLE ship flag in Fred. + * + * 133 1/12/98 10:41a Allender + * fixed minor bug with ship editor and ignore orders dialog + * + * 132 12/05/97 4:07p Hoffoss + * Changed code to allow WHO_FROM type ship sources to only show flyable + * ships in list. + * + * 131 11/24/97 2:31p Allender + * allow ignore orders dialog to be active for players + * + * 130 11/24/97 9:07a Allender + * ignore orders dialog should function as multi-edit now + * + * 129 11/22/97 4:17p Allender + * first pass of making ignore orders dialog multi edit + * + * 128 11/13/97 4:14p Allender + * automatic assignment of hotkeys for starting wings. Appripriate + * warnings when they are incorrectly used. hotkeys correctly assigned to + * ships/wing arriving after mission start + * + * 127 11/11/97 3:32p Johnson + * allender: Combo boxes need to have lists initialized before setting + * internal class variable for arrival/depature targets + * + * 126 11/11/97 2:13p Allender + * docking bay support for Fred and Freespace. Added hook to ai code for + * arrival/departure from dock bays. Fred support now sufficient. + * + * 125 11/10/97 10:13p Allender + * added departure anchor to Fred and Freespace in preparation for using + * docking bays. Functional in Fred, not in FreeSpace. + * + * 124 10/21/97 4:49p Allender + * added flags to Fred and FreeSpace to forgo warp effect (toggle in ship + * editor in Fred) + * + * 123 10/14/97 5:33p Hoffoss + * Added Fred support (and fsm support) for the no_arrival_music flags in + * ships and wings. + * + * 122 9/18/97 10:49a Allender + * increment/decrement Player_start variable when making a ship a player + * start + * + * 121 9/17/97 5:43p Hoffoss + * Added Fred support for new player start information. + * + * 120 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 119 9/15/97 2:35p Hoffoss + * Fixed bug in Fred where a player ship and normal ship both marked + * clobbered each other's ship type in ship editor dialog. + * + * 118 9/09/97 10:29a Hoffoss + * Added support for neutral team, and fixed changes made to how team is + * used in ship structure. + * + * 117 9/09/97 9:27a Hoffoss + * Removed #Jason Hoffoss# comments from code. Code is already set up to + * handle the situation properly. + * + * 116 9/06/97 2:13p Mike + * Replace support for TEAM_NEUTRAL + * + * 115 9/04/97 5:35p Hoffoss + * Fixed arrival distance stuff. + * + * 114 9/04/97 5:04p Johnson + * Fixed bug with arrival target distance checking. + * + * 113 9/04/97 4:31p Hoffoss + * Fixed bug: Changed ship editor to not touch wing info (arrival or + * departure cues) to avoid conflicts with wing editor's changes. + * + * 112 8/30/97 9:52p Hoffoss + * Implemented arrival location, distance, and anchor in Fred. + * + * 111 8/28/97 8:56a Hoffoss + * Added more checking to sexp error checker, fixed some bugs. + * + * 110 8/25/97 5:56p Hoffoss + * Added multiple asteroid field support, loading and saving of asteroid + * fields, and ship score field to Fred. + * + * 109 8/22/97 4:16p Hoffoss + * added support for arrival and departure info in ship editor using + * wing's info if editing marked ships in a wing instead of using ship's. + * + * 108 8/21/97 11:37p Hoffoss + * Fixed bug: when renaming a ship that is a reinforcement, an extra + * instance of it is added to the reinforcement list. + * + * 107 8/20/97 6:53p Hoffoss + * Implemented escort flag support in Fred. + * + * 106 8/19/97 2:53p Hoffoss + * Fixed bug where multiple ships editing doesn't change players. + * + * 105 8/19/97 1:44p Hoffoss + * Fixed bug with updating too quickly (i.e. via prev and next buttons). + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "freddoc.h" +#include "fredview.h" +#include "mainfrm.h" +#include "3d.h" +#include "physics.h" +#include "editor.h" +#include "ailocal.h" +#include "aigoals.h" +#include "parselo.h" +#include "management.h" +#include "linklist.h" +#include "initialstatus.h" +#include "weaponeditordlg.h" +#include "ship.h" +#include "textviewdlg.h" +#include "player.h" // used for the max_keyed_target stuff +#include "ignoreordersdlg.h" +#include "missionparse.h" +#include "model.h" +#include "starfield.h" +#include "jumpnode.h" +#include "shipflagsdlg.h" +#include "missionmessage.h" +#include "shipspecialdamage.h" + +#define ID_SHIP_MENU 9000 + +#define NO_PERSONA_INDEX 999 + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +void numeric_edit_control::setup(int id, CWnd *wnd) +{ + control_id = id; + dlg = wnd; +} + +void numeric_edit_control::init(int n) +{ + value = n; + unique = 1; +} + +void numeric_edit_control::set(int n) +{ + if (n != value){ + unique = 0; + } +} + +void numeric_edit_control::display() +{ + CString str; + + if (unique){ + str.Format("%d", value); + } + + dlg->GetDlgItem(control_id)->SetWindowText(str); +} + +void numeric_edit_control::save(int *n) +{ + CString str; + + if (control_id) { + dlg->GetDlgItem(control_id)->GetWindowText(str); + if (!str.IsEmpty()){ + MODIFY(*n, atoi(str)); + } + } +} + +void numeric_edit_control::fix(int n) +{ + if (unique) { + CString str; + CWnd *w; + + value = n; + str.Format("%d", n); + w = dlg->GetDlgItem(control_id); + dlg->GetDlgItem(control_id)->SetWindowText(str); + } +} + +///////////////////////////////////////////////////////////////////////////// +// CShipEditorDlg dialog + +CShipEditorDlg::CShipEditorDlg(CWnd* pParent /*=NULL*/) + : CDialog(CShipEditorDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CShipEditorDlg) + m_ship_name = _T(""); + m_cargo1 = _T(""); + m_ship_class = -1; + m_team = -1; + m_arrival_location = -1; + m_departure_location = -1; + m_ai_class = -1; + m_hotkey = -1; + m_update_arrival = FALSE; + m_update_departure = FALSE; + m_arrival_target = -1; + m_departure_target = -1; + m_persona = -1; + //}}AFX_DATA_INIT + + m_pSEView = NULL; + initialized = editing = multi_edit = 0; + select_sexp_node = -1; + bypass_errors = 0; +} + +// Modeless constructor, MK +CShipEditorDlg::CShipEditorDlg(CView* pView) +{ + m_pSEView = pView; + initialized = editing = 0; + select_sexp_node = -1; +} + +void CShipEditorDlg::DoDataExchange(CDataExchange* pDX) +{ + int n; + CString str; + + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CShipEditorDlg) + DDX_Control(pDX, IDC_NO_DEPARTURE_WARP, m_no_departure_warp); + DDX_Control(pDX, IDC_NO_ARRIVAL_WARP, m_no_arrival_warp); + DDX_Control(pDX, IDC_PLAYER_SHIP, m_player_ship); + DDX_Control(pDX, IDC_DEPARTURE_DELAY_SPIN, m_departure_delay_spin); + DDX_Control(pDX, IDC_ARRIVAL_DELAY_SPIN, m_arrival_delay_spin); + DDX_Control(pDX, IDC_DEPARTURE_TREE, m_departure_tree); + DDX_Control(pDX, IDC_ARRIVAL_TREE, m_arrival_tree); + DDX_Text(pDX, IDC_SHIP_NAME, m_ship_name); + DDX_CBString(pDX, IDC_SHIP_CARGO1, m_cargo1); + DDX_CBIndex(pDX, IDC_SHIP_CLASS, m_ship_class); + DDX_CBIndex(pDX, IDC_SHIP_TEAM, m_team); + DDX_CBIndex(pDX, IDC_ARRIVAL_LOCATION, m_arrival_location); + DDX_CBIndex(pDX, IDC_DEPARTURE_LOCATION, m_departure_location); + DDX_CBIndex(pDX, IDC_AI_CLASS, m_ai_class); + DDX_CBIndex(pDX, IDC_HOTKEY, m_hotkey); + DDX_Check(pDX, IDC_UPDATE_ARRIVAL, m_update_arrival); + DDX_Check(pDX, IDC_UPDATE_DEPARTURE, m_update_departure); + DDX_CBIndex(pDX, IDC_ARRIVAL_TARGET, m_arrival_target); + DDX_CBIndex(pDX, IDC_DEPARTURE_TARGET, m_departure_target); + DDX_CBIndex(pDX, IDC_SHIP_PERSONA, m_persona); + //}}AFX_DATA_MAP + DDV_MaxChars(pDX, m_ship_name, NAME_LENGTH - 1); + DDV_MaxChars(pDX, m_cargo1, NAME_LENGTH - 1); + + if (pDX->m_bSaveAndValidate) { // get dialog control values + GetDlgItem(IDC_ARRIVAL_DELAY)->GetWindowText(str); + n = atoi(str); + if (n < 0){ + n = 0; + } + + m_arrival_delay.init(n); + + GetDlgItem(IDC_ARRIVAL_DISTANCE)->GetWindowText(str); + m_arrival_dist.init(atoi(str)); + + GetDlgItem(IDC_DEPARTURE_DELAY)->GetWindowText(str); + n = atoi(str); + if (n < 0) + n = 0; + m_departure_delay.init(n); + + GetDlgItem(IDC_SCORE)->GetWindowText(str); + m_score.init(atoi(str)); + } +} + +BEGIN_MESSAGE_MAP(CShipEditorDlg, CDialog) + //{{AFX_MSG_MAP(CShipEditorDlg) + ON_WM_CLOSE() + ON_NOTIFY(NM_RCLICK, IDC_ARRIVAL_TREE, OnRclickArrivalTree) + ON_NOTIFY(NM_RCLICK, IDC_DEPARTURE_TREE, OnRclickDepartureTree) + ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_ARRIVAL_TREE, OnBeginlabeleditArrivalTree) + ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_DEPARTURE_TREE, OnBeginlabeleditDepartureTree) + ON_NOTIFY(TVN_ENDLABELEDIT, IDC_ARRIVAL_TREE, OnEndlabeleditArrivalTree) + ON_NOTIFY(TVN_ENDLABELEDIT, IDC_DEPARTURE_TREE, OnEndlabeleditDepartureTree) + ON_BN_CLICKED(IDC_GOALS, OnGoals) + ON_CBN_SELCHANGE(IDC_SHIP_CLASS, OnSelchangeShipClass) + ON_BN_CLICKED(IDC_INITIAL_STATUS, OnInitialStatus) + ON_BN_CLICKED(IDC_WEAPONS, OnWeapons) + ON_BN_CLICKED(IDC_SHIP_RESET, OnShipReset) + ON_BN_CLICKED(IDC_DELETE_SHIP, OnDeleteShip) + ON_BN_CLICKED(IDC_SHIP_TBL, OnShipTbl) + ON_BN_CLICKED(IDC_NEXT, OnNext) + ON_NOTIFY(TVN_SELCHANGED, IDC_ARRIVAL_TREE, OnSelchangedArrivalTree) + ON_NOTIFY(TVN_SELCHANGED, IDC_DEPARTURE_TREE, OnSelchangedDepartureTree) + ON_BN_CLICKED(IDC_HIDE_CUES, OnHideCues) + ON_BN_CLICKED(IDC_PREV, OnPrev) + ON_CBN_SELCHANGE(IDC_ARRIVAL_LOCATION, OnSelchangeArrivalLocation) + ON_BN_CLICKED(IDC_PLAYER_SHIP, OnPlayerShip) + ON_BN_CLICKED(IDC_NO_ARRIVAL_WARP, OnNoArrivalWarp) + ON_BN_CLICKED(IDC_NO_DEPARTURE_WARP, OnNoDepartureWarp) + ON_CBN_SELCHANGE(IDC_DEPARTURE_LOCATION, OnSelchangeDepartureLocation) + ON_CBN_SELCHANGE(IDC_HOTKEY, OnSelchangeHotkey) + ON_BN_CLICKED(IDC_FLAGS, OnFlags) + ON_BN_CLICKED(IDC_IGNORE_ORDERS, OnIgnoreOrders) + ON_WM_INITMENU() + ON_BN_CLICKED(IDC_SPECIAL_EXP, OnSpecialExp) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CShipEditorDlg message handlers + +BOOL CShipEditorDlg::Create() +{ + int i, index; + BOOL r; + CComboBox *ptr; + + r = CDialog::Create(IDD, Fred_main_wnd); + + ptr = (CComboBox *) GetDlgItem(IDC_ARRIVAL_LOCATION); + ptr->ResetContent(); + for (i=0; iAddString(Arrival_location_names[i]); + } + + ptr = (CComboBox *) GetDlgItem(IDC_DEPARTURE_LOCATION); + ptr->ResetContent(); + for (i=0; iAddString(Departure_location_names[i]); + } + + ptr = (CComboBox *) GetDlgItem(IDC_SHIP_CLASS); + ptr->ResetContent(); + for (i=0; iAddString(Ship_info[i].name); + } + + ptr = (CComboBox *) GetDlgItem(IDC_AI_CLASS); + ptr->ResetContent(); + for (i=0; iAddString(Ai_class_names[i]); + } + + // alternate ship name combobox + ptr = (CComboBox *)GetDlgItem(IDC_SHIP_ALT); + ptr->ResetContent(); + ptr->AddString(""); + ptr->SetCurSel(0); + + // deal with the persona dialog + ptr = (CComboBox *)GetDlgItem(IDC_SHIP_PERSONA); + ptr->ResetContent(); + index = ptr->AddString(""); + if ( index >= 0 ){ + ptr->SetItemData(index, NO_PERSONA_INDEX); + } + + for ( i = 0; i < Num_personas; i++ ) { + if ( Personas[i].flags & PERSONA_FLAG_WINGMAN ) { + int index; + + // don't bother putting any vasudan personas on the list -- done automatically by code +// if ( Personas[i].flags & PERSONA_FLAG_VASUDAN ){ +// continue; +// } + + CString persona_name = Personas[i].name; + if ( Personas[i].flags & PERSONA_FLAG_VASUDAN ){ + persona_name += " -Vas"; + } + + index = ptr->AddString(persona_name); + if ( index >= 0 ){ + ptr->SetItemData(index, i); + } + } + } + + m_score.setup(IDC_SCORE, this); + m_arrival_dist.setup(IDC_ARRIVAL_DISTANCE, this); + m_arrival_delay.setup(IDC_ARRIVAL_DELAY, this); + m_departure_delay.setup(IDC_DEPARTURE_DELAY, this); + + m_hotkey = 0; + m_arrival_tree.link_modified(&modified); // provide way to indicate trees are modified in dialog + m_arrival_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX)); + m_departure_tree.link_modified(&modified); + m_departure_tree.setup(); + m_arrival_delay_spin.SetRange(0, 999); + m_departure_delay_spin.SetRange(0, 999); + initialize_data(1); + + return r; +} + +// This gets called when you click on the "X" button. Note that OnClose +// does not destroy the window. It only hides it. +void CShipEditorDlg::OnClose() +{ + if (verify() && (!bypass_errors)) { + SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + bypass_errors = 0; + return; + } + + if (update_data()) { + SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + bypass_errors = 0; + return; + } + + SetWindowPos(Fred_main_wnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); + Fred_main_wnd->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); +} + +BOOL CShipEditorDlg::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) +{ + BOOL r; + + r = CDialog::Create(IDD, pParentWnd); + return r; +} + +int CShipEditorDlg::tristate_set(int val, int cur_state) +{ + if (val) { + if (!cur_state){ + return 2; + } + + } else { + if (cur_state){ + return 2; + } + } + + return cur_state; +} + +// called to initialize the dialog box to reflect what ships we currently have marked. Any +// time what we have marked changes, this should get called again. +// +// Notes: player_count is the number of player starts marked, when we are in a non-multiplayer +// mission (NMM). In a multiplayer mission (MM), player_count will always be zero. +// ship_count in NMM is the number of ships (i.e. not player starts) that are marked. In MM, +// ship_count is the number of ships and player starts. Total_count is the sum of ship_count +// and player_count in all cases. The reason player_count isn't used in MM, and ship_count +// is used instead to track player starts is because in MM, player starts can be edited as +// freely as AI ships, and are very likely to be AI ships sometimes. Thus, treating them like +// AI ships instead of player starts simplifies processing. +// +void CShipEditorDlg::initialize_data(int full_update) +{ + int i, type, ship_count, player_count, total_count, wing = -1, pvalid_count; + int a_cue, d_cue, cue_init = 0, cargo = 0, base_ship, base_player, pship = -1; + int no_arrival_warp = 0, no_departure_warp = 0, escort_count, ship_orders, current_orders; + int pship_count; // a total count of the player ships not marked + object *objp; + CWnd *w = NULL; + CString str; + CComboBox *box, *departure_box; + CSingleLock sync(&CS_update); + + nprintf(("Fred routing", "Ship dialog load\n")); + if (!GetSafeHwnd() || bypass_all) + return; + + sync.Lock(); // don't initialize if we are still updating. Wait until update is done. + + box = (CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET); + management_add_ships_to_combo( box, SHIPS_2_COMBO_SPECIAL | SHIPS_2_COMBO_ALL_SHIPS ); + + departure_box = (CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET); + management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY ); + + if (The_mission.game_type & MISSION_TYPE_MULTI){ + mission_type = 0; // multi player mission + } else { + mission_type = 1; // non-multiplayer mission (implies single player mission I guess) + } + + // figure out what all we are editing. + ship_count = player_count = escort_count = pship_count = pvalid_count = 0; + base_ship = base_player = -1; + enable = p_enable = 1; + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if ((objp->type == OBJ_SHIP) && (Ships[objp->instance].flags & SF_ESCORT)){ + escort_count++; // get a total count of escort ships + } + + if (objp->type == OBJ_START){ + pship_count++; // count all player starts in mission + } + + if (objp->flags & OF_MARKED) { + type = objp->type; + if ((type == OBJ_START) && !mission_type){ // in multiplayer missions, starts act like ships + type = OBJ_SHIP; + } + + i = -1; + if (type == OBJ_START) { + player_count++; + // if player_count is 1, base_player will be the one and only player + i = base_player = objp->instance; + + } else if (type == OBJ_SHIP) { + ship_count++; + // if ship_count is 1, base_ship will be the one and only ship + i = base_ship = objp->instance; + } + + if (i >= 0){ + if (Ship_info[Ships[i].ship_info_index].flags & SIF_PLAYER_SHIP){ + pvalid_count++; + } + } + } + + objp = GET_NEXT(objp); + } + + total_count = ship_count + player_count; // get total number of objects being edited. + if (total_count > 1){ + multi_edit = 1; + } else { + multi_edit = 0; + } + + a_cue = d_cue = -1; + m_arrival_location = -1; + m_arrival_dist.blank(); + m_arrival_target = -1; + m_arrival_delay.blank(); + m_departure_location = -1; + m_departure_target = -1; + m_departure_delay.blank(); + + player_ship = single_ship = -1; + m_arrival_tree.select_sexp_node = m_departure_tree.select_sexp_node = select_sexp_node; + select_sexp_node = -1; + ship_orders = 0; // assume they are all the same type + if (ship_count) { + box = (CComboBox *) GetDlgItem(IDC_SHIP_CARGO1); + box->ResetContent(); + for (i=0; iAddString(Cargo_names[i]); + } + + if (!multi_edit) { + Assert((ship_count == 1) && (base_ship >= 0)); + m_ship_name = Ships[base_ship].ship_name; + } else { + m_ship_name = _T(""); + } + + m_update_arrival = m_update_departure = 1; + base_player = 0; + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) { + if (objp->flags & OF_MARKED) { + // do processing for both ships and players + i = get_ship_from_obj(objp); + if (base_player >= 0) { + m_ship_class = Ships[i].ship_info_index; + m_team = bitmask_2_bitnum(Ships[i].team); + pship = (objp->type == OBJ_START) ? 1 : 0; + base_player = -1; + + } else { + if (Ships[i].ship_info_index != m_ship_class) + m_ship_class = -1; + if (bitmask_2_bitnum(Ships[i].team) != m_team) + m_team = -1; + + pship = tristate_set(Objects[Ships[i].objnum].type == OBJ_START, pship); + } + + // 'and' in the ship type of this ship to our running bitfield + current_orders = ship_get_default_orders_accepted( &Ship_info[Ships[i].ship_info_index] ); + if (!ship_orders){ + ship_orders = current_orders; + } else if (ship_orders != current_orders){ + ship_orders = -1; + } + + if (Ships[i].flags & SF_ESCORT){ + escort_count--; // remove marked escorts from count + } + + if (Objects[Ships[i].objnum].type == OBJ_START){ + pship_count--; // removed marked starts from count + } + + // do processing only for ships (plus players if in a multiplayer mission + if ((objp->type == OBJ_SHIP) || ((objp->type == OBJ_START) && !mission_type)) { + // process this if ship not in a wing + if (Ships[i].wingnum < 0) { + if (!cue_init) { + cue_init = 1; + a_cue = Ships[i].arrival_cue; + d_cue = Ships[i].departure_cue; + m_arrival_location = Ships[i].arrival_location; + m_arrival_dist.init(Ships[i].arrival_distance); + m_arrival_target = Ships[i].arrival_anchor; + m_arrival_delay.init(Ships[i].arrival_delay); + m_departure_location = Ships[i].departure_location; + m_departure_delay.init(Ships[i].departure_delay); + m_departure_target = Ships[i].departure_anchor; + + } else { + cue_init++; + if (Ships[i].arrival_location != m_arrival_location){ + m_arrival_location = -1; + } + + if (Ships[i].departure_location != m_departure_location){ + m_departure_location = -1; + } + + m_arrival_dist.set(Ships[i].arrival_distance); + m_arrival_delay.set(Ships[i].arrival_delay); + m_departure_delay.set(Ships[i].departure_delay); + + if (Ships[i].arrival_anchor != m_arrival_target){ + m_arrival_target = -1; + } + + if (!cmp_sexp_chains(a_cue, Ships[i].arrival_cue)) { + a_cue = -1; + m_update_arrival = 0; + } + + if (!cmp_sexp_chains(d_cue, Ships[i].departure_cue)) { + d_cue = -1; + m_update_departure = 0; + } + + if ( Ships[i].departure_anchor != m_departure_target ){ + m_departure_target = -1; + } + } + } + + // process the first ship in group, else process the rest + if (base_ship >= 0) { + m_ai_class = Ships[i].weapons.ai_class; + cargo = Ships[i].cargo1; + m_cargo1 = Cargo_names[cargo]; + m_hotkey = Ships[i].hotkey + 1; + m_score.init(Ships[i].score); + + m_persona = Ships[i].persona_index + 1; + + // we use final_death_time member of ship structure for holding the amount of time before a mission + // to destroy this ship + wing = Ships[i].wingnum; + if (wing < 0) { + GetDlgItem(IDC_WING) -> SetWindowText("None"); + + } else { + GetDlgItem(IDC_WING) -> SetWindowText(Wings[wing].name); + if (!query_whole_wing_marked(wing)) + m_update_arrival = m_update_departure = 0; + } + + // set routine local varaiables for ship/object flags + no_arrival_warp = (Ships[i].flags & SF_NO_ARRIVAL_WARP) ? 1 : 0; + no_departure_warp = (Ships[i].flags & SF_NO_DEPARTURE_WARP) ? 1 : 0; + + base_ship = -1; + if (!multi_edit) + single_ship = i; + + } else { + if (Ships[i].weapons.ai_class != m_ai_class){ + m_ai_class = -1; + } + + if (Ships[i].cargo1 != cargo){ + m_cargo1 = _T(""); + } + + m_score.set(Ships[i].score); + + if (Ships[i].hotkey != m_hotkey - 1){ + m_hotkey = -1; + } + + if ( Ships[i].persona_index != (m_persona-1) ){ + m_persona = -1; + } + + if (Ships[i].wingnum != wing){ + GetDlgItem(IDC_WING) -> SetWindowText(""); + } + + no_arrival_warp = tristate_set(Ships[i].flags & SF_NO_ARRIVAL_WARP, no_arrival_warp); + no_departure_warp = tristate_set(Ships[i].flags & SF_NO_DEPARTURE_WARP, no_departure_warp); + } + } + } + } + + objp = GET_NEXT(objp); + } + + if (multi_edit) { + m_arrival_tree.clear_tree(""); + m_departure_tree.clear_tree(""); + } + + if (cue_init) { + m_arrival_tree.load_tree(a_cue); + m_departure_tree.load_tree(d_cue, "false"); + + } else { + m_arrival_tree.clear_tree(); + m_arrival_tree.DeleteAllItems(); + m_departure_tree.clear_tree(); + m_departure_tree.DeleteAllItems(); + } + + m_player_ship.SetCheck(pship); + m_no_arrival_warp.SetCheck(no_arrival_warp); + m_no_departure_warp.SetCheck(no_departure_warp); + + if (!multi_edit) { + i = m_arrival_tree.select_sexp_node; + if (i != -1) { + w = GetDlgItem(IDC_ARRIVAL_TREE); + m_arrival_tree.hilite_item(i); + + } else { + i = m_departure_tree.select_sexp_node; + if (i != -1) { + w = GetDlgItem(IDC_DEPARTURE_TREE); + m_departure_tree.hilite_item(i); + } + } + } + + } else { // no ships selected, 0 or more player ships selected + if (player_count > 1) { // multiple player ships selected + Assert(base_player >= 0); + m_ship_name = _T(""); + m_player_ship.SetCheck(TRUE); + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if ((objp->type == OBJ_START) && (objp->flags & OF_MARKED)) { + i = objp->instance; + if (base_player >= 0) { + m_ship_class = Ships[i].ship_info_index; + m_team = bitmask_2_bitnum(Ships[i].team); + base_player = -1; + + } else { + if (Ships[i].ship_info_index != m_ship_class) + m_ship_class = -1; + if (bitmask_2_bitnum(Ships[i].team) != m_team) + m_team = -1; + } + } + + objp = GET_NEXT(objp); + } + + // only 1 player selected.. + } else if (query_valid_object() && (Objects[cur_object_index].type == OBJ_START)) { + Assert((player_count == 1) && !multi_edit); + player_ship = Objects[cur_object_index].instance; + m_ship_name = Ships[player_ship].ship_name; + m_ship_class = Ships[player_ship].ship_info_index; + m_team = bitmask_2_bitnum(Ships[player_ship].team); + m_player_ship.SetCheck(TRUE); + + } else { // no ships or players selected.. + m_ship_name = _T(""); + m_ship_class = -1; + m_team = -1; + m_persona = -1; + m_player_ship.SetCheck(FALSE); + } + + m_ai_class = -1; + m_cargo1 = _T(""); + m_hotkey = 0; + m_score.blank(); // cause control to be blank + m_arrival_location = -1; + m_departure_location = -1; + m_arrival_delay.blank(); + m_departure_delay.blank(); + m_arrival_dist.blank(); + m_arrival_target = -1; + m_departure_target = -1; + m_arrival_tree.clear_tree(); + m_arrival_tree.DeleteAllItems(); + m_departure_tree.clear_tree(); + m_departure_tree.DeleteAllItems(); + m_no_arrival_warp.SetCheck(0); + m_no_departure_warp.SetCheck(0); + enable = p_enable = 0; + GetDlgItem(IDC_WING)->SetWindowText(_T("None")); + } + + box = (CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET); + // must put the appropriate ships into the list depending on arrival location + if ( m_arrival_location != ARRIVE_FROM_DOCK_BAY ){ + management_add_ships_to_combo( box, SHIPS_2_COMBO_SPECIAL | SHIPS_2_COMBO_ALL_SHIPS ); + } else { + management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY ); + } + + // set the internal variable appropriatly + if (m_arrival_target >= SPECIAL_ARRIVAL_ANCHORS_OFFSET){ + m_arrival_target -= SPECIAL_ARRIVAL_ANCHORS_OFFSET; + } else if (m_arrival_target >= 0) { + m_arrival_target = box->FindStringExact(-1, Ships[m_arrival_target].ship_name); + } + + box = (CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET); + // must put the appropriate ships into the list depending on departure location + if ( m_departure_location == DEPART_AT_DOCK_BAY ){ + management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY ); + } else { + box->ResetContent(); + } + + if ( m_departure_target >= 0 ){ + m_departure_target = box->FindStringExact( -1, Ships[m_departure_target].ship_name ); + } + + initialized = 1; + if (player_count) { + box = (CComboBox *) GetDlgItem(IDC_SHIP_TEAM); + if (!mission_type){ // multiplayer mission + box->EnableWindow(TRUE); + } + + else { + box->EnableWindow(FALSE); + m_team = -1; + } + + box->ResetContent(); + for (i=0; i<2; i++) // hard coded: only allow friendly and hostile + box->AddString(Team_names[i]); + } else { + box = (CComboBox *) GetDlgItem(IDC_SHIP_TEAM); + box->EnableWindow(enable); + box->ResetContent(); + for (i=0; iAddString(Team_names[i]); + } + } + + m_score.display(); + m_arrival_dist.display(); + m_arrival_delay.display(); + m_departure_delay.display(); + + if (full_update) + UpdateData(FALSE); + + if (!cue_init) { + GetDlgItem(IDC_ARRIVAL_LOCATION)->EnableWindow(FALSE); + GetDlgItem(IDC_ARRIVAL_DELAY)->EnableWindow(FALSE); + GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(FALSE); + GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(FALSE); + GetDlgItem(IDC_ARRIVAL_DELAY_SPIN)->EnableWindow(FALSE); + GetDlgItem(IDC_ARRIVAL_TREE)->EnableWindow(FALSE); + GetDlgItem(IDC_DEPARTURE_LOCATION)->EnableWindow(FALSE); + GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(FALSE); + GetDlgItem(IDC_DEPARTURE_DELAY)->EnableWindow(FALSE); + GetDlgItem(IDC_DEPARTURE_DELAY_SPIN)->EnableWindow(FALSE); + GetDlgItem(IDC_DEPARTURE_TREE)->EnableWindow(FALSE); + GetDlgItem(IDC_NO_ARRIVAL_WARP)->EnableWindow(FALSE); + GetDlgItem(IDC_NO_DEPARTURE_WARP)->EnableWindow(FALSE); + + } else { + GetDlgItem(IDC_ARRIVAL_LOCATION)->EnableWindow(enable); + if (m_arrival_location) { + GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(enable); + GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(enable); + } else { + GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(FALSE); + GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(FALSE); + } + + GetDlgItem(IDC_DEPARTURE_LOCATION)->EnableWindow(enable); + if ( m_departure_location ) { + GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(enable); + } else { + GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(FALSE); + } + + GetDlgItem(IDC_ARRIVAL_DELAY)->EnableWindow(enable); + GetDlgItem(IDC_ARRIVAL_DELAY_SPIN)->EnableWindow(enable); + GetDlgItem(IDC_ARRIVAL_TREE)->EnableWindow(enable); + GetDlgItem(IDC_DEPARTURE_LOCATION)->EnableWindow(enable); + GetDlgItem(IDC_DEPARTURE_DELAY)->EnableWindow(enable); + GetDlgItem(IDC_DEPARTURE_DELAY_SPIN)->EnableWindow(enable); + GetDlgItem(IDC_DEPARTURE_TREE)->EnableWindow(enable); + GetDlgItem(IDC_NO_ARRIVAL_WARP)->EnableWindow(enable); + GetDlgItem(IDC_NO_DEPARTURE_WARP)->EnableWindow(enable); + } + + if (total_count) { + GetDlgItem(IDC_SHIP_NAME)->EnableWindow(!multi_edit); + GetDlgItem(IDC_SHIP_CLASS)->EnableWindow(TRUE); + GetDlgItem(IDC_SHIP_ALT)->EnableWindow(TRUE); + GetDlgItem(IDC_INITIAL_STATUS)->EnableWindow(TRUE); + GetDlgItem(IDC_WEAPONS)->EnableWindow(m_ship_class >= 0); + GetDlgItem(IDC_FLAGS)->EnableWindow(TRUE); + + } else { + GetDlgItem(IDC_SHIP_NAME)->EnableWindow(FALSE); + GetDlgItem(IDC_SHIP_CLASS)->EnableWindow(FALSE); + GetDlgItem(IDC_SHIP_ALT)->EnableWindow(FALSE); + GetDlgItem(IDC_INITIAL_STATUS)->EnableWindow(FALSE); + GetDlgItem(IDC_WEAPONS)->EnableWindow(FALSE); + GetDlgItem(IDC_FLAGS)->EnableWindow(FALSE); + } + + GetDlgItem(IDC_AI_CLASS)->EnableWindow(enable); + GetDlgItem(IDC_SHIP_CARGO1)->EnableWindow(enable); + GetDlgItem(IDC_HOTKEY)->EnableWindow(enable); + if ((m_ship_class >= 0) && !(Ship_info[m_ship_class].flags & SIF_CARGO) && !(Ship_info[m_ship_class].flags & SIF_NO_SHIP_TYPE)) + GetDlgItem(IDC_GOALS)->EnableWindow(enable); + else if (multi_edit) + GetDlgItem(IDC_GOALS)->EnableWindow(enable); + else + GetDlgItem(IDC_GOALS)->EnableWindow(FALSE); + + // !pship_count used because if allowed to clear, we would have no player starts + if (mission_type || !pship_count || (pship_count + total_count > MAX_PLAYERS) || (pvalid_count < total_count)) + m_player_ship.EnableWindow(FALSE); + else + m_player_ship.EnableWindow(TRUE); + + GetDlgItem(IDC_DELETE_SHIP)->EnableWindow(enable); + GetDlgItem(IDC_SHIP_RESET)->EnableWindow(enable); + GetDlgItem(IDC_SCORE)->EnableWindow(enable); + +#ifndef NDEBUG + GetDlgItem(IDC_SHIP_TBL)->EnableWindow(m_ship_class >= 0); +#else + GetDlgItem(IDC_SHIP_TBL)->EnableWindow(0); + GetDlgItem(IDC_SHIP_TBL)->ShowWindow(SW_HIDE); +#endif + + if (cue_init > 1) { // more than one ship (players don't have cues to edit) + GetDlgItem(IDC_UPDATE_ARRIVAL)->ShowWindow(SW_SHOW); + GetDlgItem(IDC_UPDATE_DEPARTURE)->ShowWindow(SW_SHOW); + + } else { + GetDlgItem(IDC_UPDATE_ARRIVAL)->ShowWindow(SW_HIDE); + GetDlgItem(IDC_UPDATE_DEPARTURE)->ShowWindow(SW_HIDE); + } + + if (multi_edit || (total_count > 1)) { + // we will allow the ignore orders dialog to be multi edit if all selected + // ships are the same type. the ship_type (local) variable holds the ship types + // for all ships. Determine how may bits set and enable/diable window + // as appropriate + if ( /*(m_team == -1) ||*/ (ship_orders == -1) ){ + GetDlgItem(IDC_IGNORE_ORDERS)->EnableWindow(FALSE); + } else { + GetDlgItem(IDC_IGNORE_ORDERS)->EnableWindow(TRUE); + } + } else + // always enabled when one ship is selected + GetDlgItem(IDC_IGNORE_ORDERS)->EnableWindow(enable); + + // always enabled if >= 1 ship selected + GetDlgItem(IDC_SHIP_PERSONA)->EnableWindow(enable); + + if (multi_edit){ + SetWindowText("Edit Marked Ships"); + } else if (player_count) { + SetWindowText("Edit Player Ship"); + } else { + SetWindowText("Edit Ship"); + } + + // setup alternate name stuff + if(player_ship >= 0){ + ship_alt_name_init(player_ship); + } else { + ship_alt_name_init(single_ship); + } + + modified = 0; + if (w){ + w->SetFocus(); + } +} + +// update ship structure(s) with dialog data. The data is first checked for errors. If +// no errors occur, returns 0. If an error occurs, returns -1. If the update is bypassed, +// returns 1. Bypass is necessary to avoid an infinite loop, and it doesn't actually +// update the data. Bypass only occurs if bypass mode is active and we still get an error. +// Once the error no longer occurs, bypass mode is cleared and data is updated. +int CShipEditorDlg::update_data(int redraw) +{ + char *str, old_name[255]; + object *ptr; + int i, z, wing; + CSingleLock sync(&CS_cur_object_index), sync2(&CS_update); + + nprintf(("Fred routing", "Ship dialog save\n")); + if (!GetSafeHwnd() || !initialized || bypass_all) + return 0; + + sync.Lock(); // don't allow cur_object_index to change while we are using it + sync2.Lock(); // don't allow reinitialization until we are done updating + UpdateData(TRUE); + UpdateData(TRUE); + Wing_editor_dialog.update_data_safe(); + if (multi_edit) { // editing multiple ships (ships and/or players) + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (((ptr->type == OBJ_START) || (ptr->type == OBJ_SHIP)) && (ptr->flags & OF_MARKED)) + update_ship(get_ship_from_obj(ptr)); + + ptr = GET_NEXT(ptr); + } + + } else if (player_ship >= 0) { // editing a single player + update_ship(player_ship); + + } else if (single_ship >= 0) { // editing a single ship + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (cur_object_index != OBJ_INDEX(ptr))) { + str = Ships[ptr->instance].ship_name; + if (!stricmp(m_ship_name, str)) { + if (bypass_errors) + return 1; + + bypass_errors = 1; + z = MessageBox("This ship name is already being used by another ship\n" + "Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL); + + if (z == IDCANCEL) + return -1; + + m_ship_name = _T(Ships[single_ship].ship_name); + UpdateData(FALSE); + } + } + + ptr = GET_NEXT(ptr); + } + + for (i=0; i= 0) { + Assert((wing < MAX_WINGS) && Wings[wing].wave_count); + for (i=0; itype == OBJ_START) { + Player_start_shipnum = ptr->instance; + break; + } + + ptr = GET_NEXT(ptr); + } + } + + if (modified) + set_modified(); + + Wing_editor_dialog.initialize_data_safe(1); + bypass_errors = 0; + modified = 0; + + if (redraw) + update_map_window(); + + return 0; +} + +int CShipEditorDlg::update_ship(int ship) +{ + int z, d; + CString str; + CComboBox *box; + int persona; + + // THIS DIALOG IS THE SOME OF THE WORST CODE I HAVE EVER SEEN IN MY ENTIRE LIFE. + // IT TOOK A RIDICULOUSLY LONG AMOUNT OF TIME TO ADD 2 FUNCTIONS. OMG + ship_alt_name_close(ship); + + if ((Ships[ship].ship_info_index != m_ship_class) && (m_ship_class != -1)) { + change_ship_type(ship, m_ship_class); + set_modified(); + } + + if (m_team != -1) + MODIFY(Ships[ship].team, 1 << m_team); + + if (Objects[Ships[ship].objnum].type != OBJ_SHIP){ + if (mission_type || (Objects[Ships[ship].objnum].type != OBJ_START)){ + return 0; + } + } + + if (m_ai_class != -1){ + MODIFY(Ships[ship].weapons.ai_class, m_ai_class); + } + if (strlen(m_cargo1)) { + z = string_lookup(m_cargo1, Cargo_names, Num_cargo); + if (z == -1) { + Assert(Num_cargo < MAX_CARGO); + z = Num_cargo++; + strcpy(Cargo_names[z], m_cargo1); + } + + MODIFY(Ships[ship].cargo1, (char)z); + } + + m_score.save(&Ships[ship].score); + if (m_arrival_location != -1) + MODIFY(Ships[ship].arrival_location, m_arrival_location); + if (m_departure_location != -1) + MODIFY(Ships[ship].departure_location, m_departure_location); + + // do the persona update + // m_persona holds the index into the list. Get the item data associated with this index and then + // assign to the ship taking care that we check for the NO_PERSONA_INDEX id + box = (CComboBox *)GetDlgItem(IDC_SHIP_PERSONA); + persona = box->GetItemData(m_persona); + if ( persona == NO_PERSONA_INDEX ) + persona = -1; + + MODIFY(Ships[ship].persona_index, persona); + + if (Ships[ship].wingnum < 0) { + if (!multi_edit || m_update_arrival) { // should we update the arrival cue? + if (Ships[ship].arrival_cue >= 0) + free_sexp2(Ships[ship].arrival_cue); + + Ships[ship].arrival_cue = m_arrival_tree.save_tree(); + } + + if (!multi_edit || m_update_departure) { + if (Ships[ship].departure_cue >= 0) + free_sexp2(Ships[ship].departure_cue); + + Ships[ship].departure_cue = m_departure_tree.save_tree(); + } + + m_arrival_dist.save(&Ships[ship].arrival_distance); + m_arrival_delay.save(&Ships[ship].arrival_delay); + m_departure_delay.save(&Ships[ship].departure_delay); + if (m_arrival_target >= 0) { + z = ((CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET)) -> GetItemData(m_arrival_target); + MODIFY(Ships[ship].arrival_anchor, z); + + // if the arrival is not hyperspace or docking bay -- force arrival distance to be + // greater than 2*radius of target. + if (((m_arrival_location != ARRIVE_FROM_DOCK_BAY) && (m_arrival_location != ARRIVE_AT_LOCATION)) && (z >= 0) && (z < SPECIAL_ARRIVAL_ANCHORS_OFFSET)) { + d = int(min(500, 2.0f * Objects[Ships[ship].objnum].radius)); + if ((Ships[ship].arrival_distance < d) && (Ships[ship].arrival_distance > -d)) { + str.Format("Ship must arrive at least %d meters away from target.\n" + "Value has been reset to this. Use with caution!\r\n" + "Reccomended distance is %d meters.\r\n", d, (int)(2.0f * Objects[Ships[ship].objnum].radius) ); + + MessageBox(str); + if (Ships[ship].arrival_distance < 0) + Ships[ship].arrival_distance = -d; + else + Ships[ship].arrival_distance = d; + + m_arrival_dist.fix(Ships[ship].arrival_distance); + } + } + } + z = ((CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET))->GetItemData(m_departure_target); + MODIFY(Ships[ship].departure_anchor, z ); + } + + if (m_hotkey != -1) + MODIFY(Ships[ship].hotkey, m_hotkey - 1); + + switch( m_no_arrival_warp.GetCheck() ) { + case 0: + if (Ships[ship].flags & SF_NO_ARRIVAL_WARP) + set_modified(); + + Ships[ship].flags &= ~SF_NO_ARRIVAL_WARP; + break; + + case 1: + if (!(Ships[ship].flags & SF_NO_ARRIVAL_WARP)) + set_modified(); + + Ships[ship].flags |= SF_NO_ARRIVAL_WARP; + break; + } + + switch( m_no_departure_warp.GetCheck() ) { + case 0: + if (Ships[ship].flags & SF_NO_DEPARTURE_WARP) + set_modified(); + + Ships[ship].flags &= ~SF_NO_DEPARTURE_WARP; + break; + + case 1: + if (!(Ships[ship].flags & SF_NO_DEPARTURE_WARP)) + set_modified(); + + Ships[ship].flags |= SF_NO_DEPARTURE_WARP; + break; + } + + switch (m_player_ship.GetCheck()) { + case 1: + if (Objects[Ships[ship].objnum].type != OBJ_START) { + Player_starts++; + set_modified(); + } + + Objects[Ships[ship].objnum].type = OBJ_START; + break; + + case 0: + if (Objects[Ships[ship].objnum].type == OBJ_START) { + Player_starts--; + set_modified(); + } + + Objects[Ships[ship].objnum].type = OBJ_SHIP; + break; + } + + Update_ship = 1; + return 0; +} + +void CShipEditorDlg::OnOK() +{ + HWND h; + CWnd *w; + + w = GetFocus(); + if (w) { + h = w->m_hWnd; + GetDlgItem(IDC_ARRIVAL_TREE)->SetFocus(); + ::SetFocus(h); + } +} + +void CShipEditorDlg::OnInitMenu(CMenu *pMenu) +{ + CMenu *m; + + m = pMenu->GetSubMenu(0); + clear_menu(m); + generate_ship_popup_menu(m, ID_SHIP_MENU, MF_ENABLED, SHIP_FILTER_PLAYERS); + if (cur_ship != -1) + m->CheckMenuItem(ID_SHIP_MENU + cur_ship, MF_BYCOMMAND | MF_CHECKED); + + CWnd::OnInitMenu(pMenu); +} + +BOOL CShipEditorDlg::OnCommand(WPARAM wParam, LPARAM lParam) +{ + int id, ship; + + id = LOWORD(wParam); + if (id >= ID_SHIP_MENU && id < ID_SHIP_MENU + MAX_SHIPS) { + if (!update_data()) { + ship = id - ID_SHIP_MENU; + unmark_all(); + set_cur_object_index(Ships[ship].objnum); + return 1; + } + } + + return CDialog::OnCommand(wParam, lParam); +} + +void CShipEditorDlg::OnRclickArrivalTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + m_arrival_tree.right_clicked(); + *pResult = 0; +} + +void CShipEditorDlg::OnRclickDepartureTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + m_departure_tree.right_clicked(); + *pResult = 0; +} + +void CShipEditorDlg::OnBeginlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + if (m_arrival_tree.edit_label(pTVDispInfo->item.hItem) == 1) { + *pResult = 0; + modified = editing = 1; + + } else + *pResult = 1; +} + +void CShipEditorDlg::OnBeginlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + if (m_departure_tree.edit_label(pTVDispInfo->item.hItem) == 1) { + *pResult = 0; + modified = editing = 1; + + } else + *pResult = 1; +} + +void CShipEditorDlg::OnEndlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + *pResult = m_arrival_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText); + editing = 0; +} + +void CShipEditorDlg::OnEndlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + *pResult = m_departure_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText); + editing = 0; +} + +int CShipEditorDlg::verify() +{ + nprintf(("Fred routing", "Ship dialog verify\n")); + if (!GetSafeHwnd() || !modified) + return 0; + + if (bypass_errors) + return 1; + + return 0; +} + +void CShipEditorDlg::OnGoals() +{ + ShipGoalsDlg dlg_goals; + + Assert(query_valid_object()); +// if (multi_edit) +// dlg_goals.initialize_multi(); +// +// else { +// Assert(single_ship != -1); +// dlg_goals.self_ship = single_ship; +// dlg_goals.initialize(Ai_info[Ships[single_ship].ai_index].goals); +// } + + if (!multi_edit) { + Assert(single_ship != -1); + dlg_goals.self_ship = single_ship; + } + + dlg_goals.DoModal(); + if (!multi_edit && !query_initial_orders_empty(Ai_info[Ships[single_ship].ai_index].goals)) + if ((Ships[single_ship].wingnum >= 0) && (query_initial_orders_conflict(Ships[single_ship].wingnum))) + MessageBox("This ship's wing also has initial orders", "Possible conflict"); +} + +void CShipEditorDlg::OnSelchangeShipClass() +{ + object *ptr; + + UpdateData(TRUE); + UpdateData(TRUE); + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags & OF_MARKED)) + if (Ships[ptr->instance].ship_info_index != m_ship_class) { + change_ship_type(ptr->instance, m_ship_class); + set_modified(); + } + + ptr = GET_NEXT(ptr); + } + + update_map_window(); +} + +void CShipEditorDlg::OnInitialStatus() +{ + initial_status dlg; + + dlg.m_multi_edit = multi_edit; + dlg.DoModal(); +} + +void CShipEditorDlg::OnWeapons() +{ + int i, ship = -1; + WeaponEditorDlg dlg; + object *objp; + CComboBox *box; + + dlg.m_multi_edit = multi_edit; + dlg.DoModal(); + + if (multi_edit) { + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (objp->flags & OF_MARKED) + if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) { + i = objp->instance; + if (ship) { + if (Ships[i].weapons.ai_class != Ships[ship].weapons.ai_class) + m_ai_class = -1; + + } else { + ship = i; + m_ai_class = Ships[i].weapons.ai_class; + } + } + + objp = GET_NEXT(objp); + } + + } else { + ship = single_ship; + if (ship < 0) + ship = player_ship; + + Assert(ship >= 0); + m_ai_class = Ships[ship].weapons.ai_class; + } + + box = (CComboBox *) GetDlgItem(IDC_AI_CLASS); + box->SetCurSel(m_ai_class); +} + +void CShipEditorDlg::OnShipReset() +{ + int i, j, index, ship; + object *objp; + ship_info *sip; + ship_subsys *ptr; + ship_weapon *wp; + model_subsystem *sp; + + m_cargo1 = "Nothing"; + m_ai_class = AI_DEFAULT_CLASS; + if (m_ship_class) { + if (Ship_info[m_ship_class].species == SPECIES_SHIVAN) + m_team = TEAM_HOSTILE; + else + m_team = TEAM_FRIENDLY; + } + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if (((objp->type == OBJ_SHIP) || ((objp->type == OBJ_START) && !mission_type)) && (objp->flags & OF_MARKED)) { + ship = objp->instance; + + // reset ship goals + for (i=0; iphys_info.speed = 0.0f; + objp->shields[0] = 100.0f; + objp->hull_strength = 100.0f; + + sip = &Ship_info[Ships[ship].ship_info_index]; + for (i=0; inum_primary_banks; i++) + Ships[ship].weapons.primary_bank_weapons[i] = sip->primary_bank_weapons[i]; + + for (i=0; inum_secondary_banks; i++) { + Ships[ship].weapons.secondary_bank_weapons[i] = sip->secondary_bank_weapons[i]; + Ships[ship].weapons.secondary_bank_capacity[i] = sip->secondary_bank_ammo_capacity[i]; + } + + index = 0; + ptr = GET_FIRST(&Ships[ship].subsys_list); + while (ptr != END_OF_LIST(&Ships[ship].subsys_list)) { + ptr->current_hits = 0.0f; + if (ptr->system_info->type == SUBSYSTEM_TURRET) { + wp = &ptr->weapons; + sp = &Ship_info[Ships[ship].ship_info_index].subsystems[index]; + + j = 0; + for (i=0; iprimary_banks[i] != -1){ + wp->primary_bank_weapons[j++] = sp->primary_banks[i]; + } + } + + wp->num_primary_banks = j; + j = 0; + for (i=0; isecondary_banks[i] != -1) { + wp->secondary_bank_weapons[j] = sp->secondary_banks[i]; + wp->secondary_bank_capacity[j++] = sp->secondary_bank_capacity[i]; + } + } + + wp->num_secondary_banks = j; + for (i=0; isecondary_bank_ammo[i] = 100; + } + } + + index++; + ptr = GET_NEXT(ptr); + } + } + + objp = GET_NEXT(objp); + } + + UpdateData(FALSE); + if (multi_edit){ + MessageBox("Ships reset to ship class defaults"); + } else { + MessageBox("Ship reset to ship class defaults"); + } +} + +void CShipEditorDlg::OnDeleteShip() +{ + delete_marked(); + unmark_all(); +} + +void CShipEditorDlg::OnShipTbl() +{ + text_view_dlg dlg; + + dlg.set(m_ship_class); + dlg.DoModal(); +} + +int CShipEditorDlg::make_ship_list(int *arr) +{ + int n = 0; + object *ptr; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if ((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)){ + arr[n++] = OBJ_INDEX(ptr); + } + + ptr = GET_NEXT(ptr); + } + + return n; +} + +void CShipEditorDlg::OnPrev() +{ + int i, n, arr[MAX_SHIPS]; + + if (!update_data()) { + n = make_ship_list(arr); + if (!n){ + return; + } + + if (cur_ship < 0){ + i = n - 1; + } + + else { + for (i=0; iitemNew.hItem; + if (h){ + m_arrival_tree.update_help(h); + } + + *pResult = 0; +} + +void CShipEditorDlg::OnSelchangedDepartureTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + HTREEITEM h; + + NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; + h = pNMTreeView->itemNew.hItem; + if (h){ + m_departure_tree.update_help(h); + } + + *pResult = 0; +} + +void CShipEditorDlg::calc_cue_height() +{ + CRect cue; + + GetDlgItem(IDC_CUE_FRAME)->GetWindowRect(cue); + cue_height = cue.bottom - cue.top + 10; + if (Show_sexp_help){ + cue_height += SEXP_HELP_BOX_SIZE; + } + + if (Hide_ship_cues) { + ((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> SetCheck(1); + OnHideCues(); + } +} + +void CShipEditorDlg::show_hide_sexp_help() +{ + CRect rect; + + if (Show_sexp_help){ + cue_height += SEXP_HELP_BOX_SIZE; + } else { + cue_height -= SEXP_HELP_BOX_SIZE; + } + + if (((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> GetCheck()){ + return; + } + + GetWindowRect(rect); + + if (Show_sexp_help){ + rect.bottom += SEXP_HELP_BOX_SIZE; + } else { + rect.bottom -= SEXP_HELP_BOX_SIZE; + } + + MoveWindow(rect); +} + +void CShipEditorDlg::OnHideCues() +{ + CRect rect; + + GetWindowRect(rect); + if (((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> GetCheck()) { + rect.bottom -= cue_height; + Hide_ship_cues = 1; + + } else { + rect.bottom += cue_height; + Hide_ship_cues = 0; + } + + MoveWindow(rect); +} + +void CShipEditorDlg::OnSelchangeArrivalLocation() +{ + CComboBox *box; + + UpdateData(); + box = (CComboBox *)GetDlgItem( IDC_ARRIVAL_TARGET ); + if (m_arrival_location) { + GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(TRUE); + GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(TRUE); + if (m_arrival_target < 0) { + m_arrival_target = 0; + } + + // determine which items we should put into the arrival target combo box + if ( m_arrival_location == ARRIVE_FROM_DOCK_BAY ) { + management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY ); + } else { + management_add_ships_to_combo( box, SHIPS_2_COMBO_SPECIAL | SHIPS_2_COMBO_ALL_SHIPS ); + } + + } else { + m_arrival_target = -1; + GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(FALSE); + GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(FALSE); + } + UpdateData(FALSE); +} + +void CShipEditorDlg::OnSelchangeDepartureLocation() +{ + CComboBox *box; + + UpdateData(); + box = (CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET); + if ( m_departure_location ) { + box->EnableWindow(TRUE); + if ( m_departure_target < 0 ) { + m_departure_target = 0; + } + + // we need to build up the list box content based on the departure type. When + // from a docking bay, only show ships in the list which have them. Show all ships otherwise + if ( m_departure_location == DEPART_AT_DOCK_BAY ) { + management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY ); + } else { + // I think that this section is currently illegal + Int3(); + } + + } else { + m_departure_target = -1; + box->EnableWindow(FALSE); + } + UpdateData(FALSE); +} + + +void CShipEditorDlg::OnPlayerShip() +{ + if (m_player_ship.GetCheck() == 1) + m_player_ship.SetCheck(0); + else + m_player_ship.SetCheck(1); + + update_map_window(); +} + +void CShipEditorDlg::OnNoArrivalWarp() +{ + if (m_no_arrival_warp.GetCheck() == 1) + m_no_arrival_warp.SetCheck(0); + else + m_no_arrival_warp.SetCheck(1); +} + +void CShipEditorDlg::OnNoDepartureWarp() +{ + if (m_no_departure_warp.GetCheck() == 1) + m_no_departure_warp.SetCheck(0); + else + m_no_departure_warp.SetCheck(1); +} + + +// function to possibly warn user when he selects a hotkey which might be used for +// a player wing. +void CShipEditorDlg::OnSelchangeHotkey() +{ + int set_num; + char buf[256]; + + UpdateData(TRUE); + set_num = m_hotkey-1; // use -1 since values associated with hotkey sets are 1 index based + + // the first three sets are generally reserved for player starting wings. + if ( set_num < MAX_STARTING_WINGS ) { + sprintf( buf, "This hotkey set should probably be reserved\nfor wing %s", Starting_wing_names[set_num] ); + MessageBox(buf, NULL, MB_OK); + } +} + +void CShipEditorDlg::OnFlags() +{ + ship_flags_dlg dlg; + + dlg.setup(p_enable); + dlg.DoModal(); +} + +void CShipEditorDlg::OnIgnoreOrders() +{ + // TODO: Add your control notification handler code here + ignore_orders_dlg player_order_dlg; + + Assert(query_valid_object()); + + if (!multi_edit) { + if ( single_ship != -1 ){ + player_order_dlg.m_ship = single_ship; + } else { + player_order_dlg.m_ship = player_ship; + } + } else { + player_order_dlg.m_ship = -1; + } + + player_order_dlg.DoModal(); +} + +void CShipEditorDlg::OnSpecialExp() +{ + // TODO: Add your control notification handler code here + ShipSpecialDamage dlg; + dlg.DoModal(); +} + +// alternate ship name stuff +void CShipEditorDlg::ship_alt_name_init(int base_ship) +{ + int idx; + CComboBox *ptr = (CComboBox*)GetDlgItem(IDC_SHIP_ALT); + if(ptr == NULL){ + Int3(); + return; + } + + // multi-edit. bah + if(multi_edit){ + GetDlgItem(IDC_SHIP_ALT)->EnableWindow(FALSE); + return; + } + GetDlgItem(IDC_SHIP_ALT)->EnableWindow(TRUE); + + // reset the combobox and add all relevant strings + ptr->ResetContent(); + ptr->AddString(""); + for(idx=0; idxAddString(Mission_alt_types[idx]); + } + + // "none" + if(base_ship < 0){ + ptr->SetCurSel(0); + } + + // otherwise look his stuff up + if(strlen(Fred_alt_names[base_ship])){ + ptr->SelectString(0, Fred_alt_names[base_ship]); + } else { + ptr->SetCurSel(0); + } +} + +void CShipEditorDlg::ship_alt_name_close(int base_ship) +{ + CString cstr; + char str[NAME_LENGTH+2] = ""; + char *p; + CComboBox *ptr = (CComboBox*)GetDlgItem(IDC_SHIP_ALT); + + if(multi_edit){ + return; + } + + if(ptr == NULL){ + Int3(); + return; + } + + // see if we have something besides "none" selected + ptr->GetWindowText(cstr); + if(cstr == CString("")){ + // zero the entry + strcpy(Fred_alt_names[base_ship], ""); + + return; + } + p = cstr.GetBuffer(0); + if(p == NULL){ + return; + } + strcpy(str, p); + + // otherwise see if it already exists + if(mission_parse_lookup_alt(str) >= 0){ + strcpy(Fred_alt_names[base_ship], str); + + return; + } + + // otherwise try and add it + if(mission_parse_add_alt(str) >= 0){ + strcpy(Fred_alt_names[base_ship], str); + + return; + } + + // bad - couldn't add + strcpy(Fred_alt_names[base_ship], ""); + MessageBox("Couldn't add new alternate type name. Already using too many!"); +} \ No newline at end of file diff --git a/src/fred2/shipflagsdlg.cpp b/src/fred2/shipflagsdlg.cpp new file mode 100644 index 0000000..54acda2 --- /dev/null +++ b/src/fred2/shipflagsdlg.cpp @@ -0,0 +1,691 @@ +// ShipFlagsDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "shipflagsdlg.h" +#include "linklist.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// ship_flags_dlg dialog + +ship_flags_dlg::ship_flags_dlg(CWnd* pParent /*=NULL*/) + : CDialog(ship_flags_dlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(ship_flags_dlg) + //}}AFX_DATA_INIT +} + +void ship_flags_dlg::DoDataExchange(CDataExchange* pDX) +{ + int n; + CString str; + + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(ship_flags_dlg) + DDX_Control(pDX, IDC_REDALERTCARRY, m_red_alert_carry); + DDX_Control(pDX, IDC_SCANNABLE, m_scannable); + DDX_Control(pDX, IDC_REINFORCEMENT, m_reinforcement); + DDX_Control(pDX, IDC_PROTECT_SHIP, m_protect_ship); + DDX_Control(pDX, IDC_BEAM_PROTECT_SHIP, m_beam_protect_ship); + DDX_Control(pDX, IDC_NO_DYNAMIC, m_no_dynamic); + DDX_Control(pDX, IDC_NO_ARRIVAL_MUSIC, m_no_arrival_music); + DDX_Control(pDX, IDC_KAMIKAZE, m_kamikaze); + DDX_Control(pDX, IDC_INVULNERABLE, m_invulnerable); + DDX_Control(pDX, IDC_IGNORE_COUNT, m_ignore_count); + DDX_Control(pDX, IDC_HIDDEN_FROM_SENSORS, m_hidden); + DDX_Control(pDX, IDC_ESCORT, m_escort); + DDX_Control(pDX, IDC_DESTROY_CHECK, m_destroy); + DDX_Control(pDX, IDC_CARGO_KNOWN, m_cargo_known); + DDX_Control(pDX, IDC_DESTROY_SPIN, m_destroy_spin); + DDX_Control(pDX, IDC_SPECIAL_WARP, m_special_warp); + //}}AFX_DATA_MAP + + if (pDX->m_bSaveAndValidate) { // get dialog control values + GetDlgItem(IDC_DESTROY_VALUE)->GetWindowText(str); + n = atoi(str); + if (n < 0) + n = 0; + + m_destroy_value.init(n); + + GetDlgItem(IDC_KDAMAGE)->GetWindowText(str); + m_kdamage.init(atoi(str)); + + // get escort priority + GetDlgItem(IDC_ESCORT_PRIORITY)->GetWindowText(str); + m_escort_value.init(atoi(str)); + + // get respawn priority + if(The_mission.game_type & MISSION_TYPE_MULTI){ + GetDlgItem(IDC_RESPAWN_PRIORITY)->GetWindowText(str); + m_respawn_priority.init(atoi(str)); + } + } +} + +BEGIN_MESSAGE_MAP(ship_flags_dlg, CDialog) + //{{AFX_MSG_MAP(ship_flags_dlg) + ON_BN_CLICKED(IDC_CARGO_KNOWN, OnCargoKnown) + ON_BN_CLICKED(IDC_DESTROY_CHECK, OnDestroyCheck) + ON_BN_CLICKED(IDC_ESCORT, OnEscort) + ON_BN_CLICKED(IDC_HIDDEN_FROM_SENSORS, OnHiddenFromSensors) + ON_BN_CLICKED(IDC_IGNORE_COUNT, OnIgnoreCount) + ON_BN_CLICKED(IDC_INVULNERABLE, OnInvulnerable) + ON_BN_CLICKED(IDC_KAMIKAZE, OnKamikaze) + ON_BN_CLICKED(IDC_NO_ARRIVAL_MUSIC, OnNoArrivalMusic) + ON_BN_CLICKED(IDC_NO_DYNAMIC, OnNoDynamic) + ON_BN_CLICKED(IDC_PROTECT_SHIP, OnProtectShip) + ON_BN_CLICKED(IDC_BEAM_PROTECT_SHIP, OnBeamProtectShip) + ON_BN_CLICKED(IDC_REINFORCEMENT, OnReinforcement) + ON_BN_CLICKED(IDC_SCANNABLE, OnScannable) + ON_BN_CLICKED(IDC_REDALERTCARRY, OnRedalertcarry) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// ship_flags_dlg message handlers + +void ship_flags_dlg::setup(int n) +{ + p_enable = n; +} + +BOOL ship_flags_dlg::OnInitDialog() +{ + int i, j, first; + int protect_ship = 0, beam_protect_ship = 0, ignore_count = 0, reinforcement = 0, cargo_known = 0, destroy_before_mission = 0; + int no_arrival_music = 0, escort = 0, invulnerable = 0, hidden_from_sensors = 0; + int scannable = 0, kamikaze = 0, no_dynamic = 0, red_alert_carry = 0, special_warp = 0; + object *objp; + bool ship_in_wing = false; + + first = 1; + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) { + if (objp->flags & OF_MARKED) { + i = objp->instance; + if (first) { + first = 0; + scannable = (Ships[i].flags & SF_SCANNABLE) ? 1 : 0; + red_alert_carry = (Ships[i].flags & SF_RED_ALERT_STORE_STATUS) ? 1 : 0; + special_warp = (Objects[Ships[i].objnum].flags & OF_SPECIAL_WARP) ? 1 : 0; + protect_ship = (Objects[Ships[i].objnum].flags & OF_PROTECTED) ? 1 : 0; + beam_protect_ship = (Objects[Ships[i].objnum].flags & OF_BEAM_PROTECTED) ? 1 : 0; + invulnerable = (Ships[i].flags & SF_INVULNERABLE) ? 1 : 0; + hidden_from_sensors = (Ships[i].flags & SF_HIDDEN_FROM_SENSORS) ? 1 : 0; + ignore_count = (Ships[i].flags & SF_IGNORE_COUNT) ? 1 : 0; + no_arrival_music = (Ships[i].flags & SF_NO_ARRIVAL_MUSIC) ? 1 : 0; + cargo_known = (Ships[i].flags & SF_CARGO_REVEALED) ? 1 : 0; + no_dynamic = (Ai_info[Ships[i].ai_index].ai_flags & AIF_NO_DYNAMIC) ? 1 : 0; + + destroy_before_mission = (Ships[i].flags & SF_KILL_BEFORE_MISSION) ? 1 : 0; + m_destroy_value.init(Ships[i].final_death_time); + + kamikaze = (Ai_info[Ships[i].ai_index].ai_flags & AIF_KAMIKAZE) ? 1 : 0; + m_kdamage.init( (int) Ai_info[Ships[i].ai_index].kamikaze_damage ); + + escort = (Ships[i].flags & SF_ESCORT) ? 1 : 0; + m_escort_value.init(Ships[i].escort_priority); + + if(The_mission.game_type & MISSION_TYPE_MULTI){ + m_respawn_priority.init(Ships[i].respawn_priority); + } + + for (j=0; jEnableWindow(FALSE); + } + + GetDlgItem(IDC_REINFORCEMENT)->EnableWindow(p_enable); + GetDlgItem(IDC_CARGO_KNOWN)->EnableWindow(p_enable); + GetDlgItem(IDC_PROTECT_SHIP)->EnableWindow(p_enable); + GetDlgItem(IDC_DESTROY_CHECK)->EnableWindow(p_enable); + GetDlgItem(IDC_DESTROY_VALUE)->EnableWindow(p_enable); + GetDlgItem(IDC_DESTROY_SPIN)->EnableWindow(p_enable); + GetDlgItem(IDC_SCANNABLE)->EnableWindow(p_enable); + + // disable the spinner and edit window if the corrsponding check box + // is not checked! + if (m_destroy.GetCheck() != 1) { + GetDlgItem(IDC_DESTROY_VALUE)->EnableWindow(FALSE); + GetDlgItem(IDC_DESTROY_SPIN)->EnableWindow(FALSE); + } + + // disable destroy option for ship in wing + if (ship_in_wing) { + GetDlgItem(IDC_DESTROY_CHECK)->EnableWindow(FALSE); + GetDlgItem(IDC_DESTROY_VALUE)->EnableWindow(FALSE); + GetDlgItem(IDC_DESTROY_SPIN)->EnableWindow(FALSE); + } + + // maybe disable escort priority window + if (m_escort.GetCheck() == 1) + GetDlgItem(IDC_ESCORT_PRIORITY)->EnableWindow(TRUE); + else + GetDlgItem(IDC_ESCORT_PRIORITY)->EnableWindow(FALSE); + + // maybe disable kamikaze damage window + if (m_kamikaze.GetCheck() == 1) + GetDlgItem(IDC_KDAMAGE)->EnableWindow(TRUE); + else + GetDlgItem(IDC_KDAMAGE)->EnableWindow(FALSE); + + return TRUE; +} + +void ship_flags_dlg::OnOK() +{ + object *objp; + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) { + if (objp->flags & OF_MARKED) + update_ship(objp->instance); + } + + objp = GET_NEXT(objp); + } + + CDialog::OnOK(); +} + +void ship_flags_dlg::update_ship(int ship) +{ + if (m_reinforcement.GetCheck() != 2) + set_reinforcement(Ships[ship].ship_name, m_reinforcement.GetCheck()); + + switch (m_cargo_known.GetCheck()) { + case 1: + if ( !(Ships[ship].flags & SF_CARGO_REVEALED) ) + set_modified(); + + Ships[ship].flags |= SF_CARGO_REVEALED; + break; + + case 0: + if ( Ships[ship].flags & SF_CARGO_REVEALED ) + set_modified(); + + Ships[ship].flags &= ~SF_CARGO_REVEALED; + break; + } + + // update the flags for IGNORE_COUNT and PROTECT_SHIP + switch (m_protect_ship.GetCheck()) { + case 1: + if (!(Objects[Ships[ship].objnum].flags & OF_PROTECTED) ) + set_modified(); + + Objects[Ships[ship].objnum].flags |= OF_PROTECTED; + break; + + case 0: + if ( Objects[Ships[ship].objnum].flags & OF_PROTECTED ) + set_modified(); + + Objects[Ships[ship].objnum].flags &= ~OF_PROTECTED; + break; + } + + switch (m_beam_protect_ship.GetCheck()) { + case 1: + if (!(Objects[Ships[ship].objnum].flags & OF_BEAM_PROTECTED) ) + set_modified(); + + Objects[Ships[ship].objnum].flags |= OF_BEAM_PROTECTED; + break; + + case 0: + if ( Objects[Ships[ship].objnum].flags & OF_BEAM_PROTECTED ) + set_modified(); + + Objects[Ships[ship].objnum].flags &= ~OF_BEAM_PROTECTED; + break; + } + + switch (m_invulnerable.GetCheck()) { + case 1: + if ( !(Ships[ship].flags & SF_INVULNERABLE) ) + set_modified(); + + Ships[ship].flags |= SF_INVULNERABLE; + break; + + case 0: + if ( Ships[ship].flags & SF_INVULNERABLE ) + set_modified(); + + Ships[ship].flags &= ~SF_INVULNERABLE; + break; + } + + switch (m_hidden.GetCheck()) { + case 1: + if ( !(Ships[ship].flags & SF_HIDDEN_FROM_SENSORS) ) + set_modified(); + + Ships[ship].flags |= SF_HIDDEN_FROM_SENSORS; + break; + + case 0: + if ( Ships[ship].flags & SF_HIDDEN_FROM_SENSORS ) + set_modified(); + + Ships[ship].flags &= ~SF_HIDDEN_FROM_SENSORS; + break; + } + + switch (m_ignore_count.GetCheck()) { + case 1: + if ( !(Ships[ship].flags & SF_IGNORE_COUNT) ) + set_modified(); + + Ships[ship].flags |= SF_IGNORE_COUNT; + break; + + case 0: + if (Ships[ship].flags & SF_IGNORE_COUNT) + set_modified(); + + Ships[ship].flags &= ~SF_IGNORE_COUNT; + break; + } + + switch (m_escort.GetCheck()) { + case 1: + if (!(Ships[ship].flags & SF_ESCORT)) + set_modified(); + + Ships[ship].flags |= SF_ESCORT; + m_escort_value.save(&Ships[ship].escort_priority); + break; + + case 0: + if (Ships[ship].flags & SF_ESCORT) + set_modified(); + + Ships[ship].flags &= ~SF_ESCORT; + break; + } + + // deal with updating the "destroy before the mission" stuff + switch (m_destroy.GetCheck()) { + case 0: // this means no check in checkbox + if ( Ships[ship].flags & SF_KILL_BEFORE_MISSION ) + set_modified(); + + Ships[ship].flags &= ~SF_KILL_BEFORE_MISSION; + break; + + case 1: // this means checkbox is checked + if ( !(Ships[ship].flags & SF_KILL_BEFORE_MISSION) ) + set_modified(); + + Ships[ship].flags |= SF_KILL_BEFORE_MISSION; + m_destroy_value.save(&Ships[ship].final_death_time); + break; + } // a mixed state is 2, and since it's not handled, it doesn't change + + switch (m_no_arrival_music.GetCheck()) { + case 0: + if (Ships[ship].flags & SF_NO_ARRIVAL_MUSIC) + set_modified(); + + Ships[ship].flags &= ~SF_NO_ARRIVAL_MUSIC; + break; + + case 1: + if (!(Ships[ship].flags & SF_NO_ARRIVAL_MUSIC)) + set_modified(); + + Ships[ship].flags |= SF_NO_ARRIVAL_MUSIC; + break; + } + + switch (m_scannable.GetCheck()) { + case 1: + if ( !(Ships[ship].flags & SF_SCANNABLE) ) + set_modified(); + + Ships[ship].flags |= SF_SCANNABLE; + break; + + case 0: + if ( Ships[ship].flags & SF_SCANNABLE ) + set_modified(); + + Ships[ship].flags &= ~SF_SCANNABLE; + break; + } + + switch (m_red_alert_carry.GetCheck()) { + case 1: + if ( !(Ships[ship].flags & SF_RED_ALERT_STORE_STATUS) ) + set_modified(); + + Ships[ship].flags |= SF_RED_ALERT_STORE_STATUS; + break; + + case 0: + if ( Ships[ship].flags & SF_RED_ALERT_STORE_STATUS ) + set_modified(); + + Ships[ship].flags &= ~SF_RED_ALERT_STORE_STATUS; + break; + } + + switch (m_special_warp.GetCheck()) { + case 1: + if ( !(Objects[Ships[ship].objnum].flags & OF_SPECIAL_WARP) ) + set_modified(); + + Objects[Ships[ship].objnum].flags |= OF_SPECIAL_WARP; + break; + + case 0: + if ( (Objects[Ships[ship].objnum].flags & OF_SPECIAL_WARP) ) + set_modified(); + + Objects[Ships[ship].objnum].flags &= (~OF_SPECIAL_WARP); + break; + } + + switch (m_no_dynamic.GetCheck()) { + case 1: + if ( !(Ai_info[Ships[ship].ai_index].ai_flags & AIF_NO_DYNAMIC) ) + set_modified(); + + Ai_info[Ships[ship].ai_index].ai_flags |= AIF_NO_DYNAMIC; + break; + + case 0: + if ( Ai_info[Ships[ship].ai_index].ai_flags & AIF_NO_DYNAMIC ) + set_modified(); + + Ai_info[Ships[ship].ai_index].ai_flags &= ~AIF_NO_DYNAMIC; + break; + } + + switch (m_kamikaze.GetCheck()) { + case 1: { + int damage; + + if ( !(Ai_info[Ships[ship].ai_index].ai_flags & AIF_KAMIKAZE) ) + set_modified(); + + Ai_info[Ships[ship].ai_index].ai_flags |= AIF_KAMIKAZE; + m_kdamage.save(&damage); + Ai_info[Ships[ship].ai_index].kamikaze_damage = i2fl(damage); + break; + } + + case 0: + if ( Ai_info[Ships[ship].ai_index].ai_flags & AIF_KAMIKAZE ) + set_modified(); + + Ai_info[Ships[ship].ai_index].ai_flags &= ~AIF_KAMIKAZE; + Ai_info[Ships[ship].ai_index].kamikaze_damage = 0.0f; + break; + } + + Ships[ship].respawn_priority = 0; + if(The_mission.game_type & MISSION_TYPE_MULTI){ + m_respawn_priority.save(&Ships[ship].respawn_priority); + } +} + +int ship_flags_dlg::tristate_set(int val, int cur_state) +{ + if (val) { + if (!cur_state){ + return 2; + } + } else { + if (cur_state){ + return 2; + } + } + + return cur_state; +} + +// a stub for now, but might be useful later. Easier than ripping out the calls to this +// everywhere at least. +void ship_flags_dlg::set_modified() +{ +} + +void ship_flags_dlg::OnCargoKnown() +{ + if (m_cargo_known.GetCheck() == 1){ + m_cargo_known.SetCheck(0); + } else { + m_cargo_known.SetCheck(1); + } +} + +void ship_flags_dlg::OnDestroyCheck() +{ + if (m_destroy.GetCheck() == 1) { + m_destroy.SetCheck(0); + GetDlgItem(IDC_DESTROY_VALUE)->EnableWindow(FALSE); + GetDlgItem(IDC_DESTROY_SPIN)->EnableWindow(FALSE); + + } else { + m_destroy.SetCheck(1); + GetDlgItem(IDC_DESTROY_VALUE)->EnableWindow(TRUE); + GetDlgItem(IDC_DESTROY_SPIN)->EnableWindow(TRUE); + } +} + +void ship_flags_dlg::OnEscort() +{ + if (m_escort.GetCheck() == 1) { + m_escort.SetCheck(0); + GetDlgItem(IDC_ESCORT_PRIORITY)->EnableWindow(FALSE); + } else { + m_escort.SetCheck(1); + GetDlgItem(IDC_ESCORT_PRIORITY)->EnableWindow(TRUE); + } +} + +void ship_flags_dlg::OnHiddenFromSensors() +{ + if (m_hidden.GetCheck() == 1){ + m_hidden.SetCheck(0); + } else { + m_hidden.SetCheck(1); + } +} + +void ship_flags_dlg::OnIgnoreCount() +{ + if (m_ignore_count.GetCheck() == 1){ + m_ignore_count.SetCheck(0); + } else { + m_ignore_count.SetCheck(1); + } +} + +void ship_flags_dlg::OnInvulnerable() +{ + if (m_invulnerable.GetCheck() == 1){ + m_invulnerable.SetCheck(0); + } else { + m_invulnerable.SetCheck(1); + } +} + +void ship_flags_dlg::OnKamikaze() +{ + if (m_kamikaze.GetCheck() == 1) { + GetDlgItem(IDC_KDAMAGE)->EnableWindow(FALSE); + m_kamikaze.SetCheck(0); + + } else { + GetDlgItem(IDC_KDAMAGE)->EnableWindow(TRUE); + m_kamikaze.SetCheck(1); + } +} + +void ship_flags_dlg::OnNoArrivalMusic() +{ + if (m_no_arrival_music.GetCheck() == 1){ + m_no_arrival_music.SetCheck(0); + } else { + m_no_arrival_music.SetCheck(1); + } +} + +void ship_flags_dlg::OnNoDynamic() +{ + if (m_no_dynamic.GetCheck() == 1){ + m_no_dynamic.SetCheck(0); + } else { + m_no_dynamic.SetCheck(1); + } +} + +void ship_flags_dlg::OnProtectShip() +{ + if (m_protect_ship.GetCheck() == 1){ + m_protect_ship.SetCheck(0); + } else { + m_protect_ship.SetCheck(1); + } +} + +void ship_flags_dlg::OnBeamProtectShip() +{ + if (m_beam_protect_ship.GetCheck() == 1){ + m_beam_protect_ship.SetCheck(0); + } else { + m_beam_protect_ship.SetCheck(1); + } +} + +void ship_flags_dlg::OnReinforcement() +{ + if (m_reinforcement.GetCheck() == 1){ + m_reinforcement.SetCheck(0); + } else { + m_reinforcement.SetCheck(1); + } +} + +void ship_flags_dlg::OnScannable() +{ + if (m_scannable.GetCheck() == 1){ + m_scannable.SetCheck(0); + } else { + m_scannable.SetCheck(1); + } +} + +void ship_flags_dlg::OnRedalertcarry() +{ + if (m_red_alert_carry.GetCheck() == 1){ + m_red_alert_carry.SetCheck(0); + } else { + m_red_alert_carry.SetCheck(1); + } +} + diff --git a/src/fred2/shipgoalsdlg.cpp b/src/fred2/shipgoalsdlg.cpp new file mode 100644 index 0000000..4dc77b8 --- /dev/null +++ b/src/fred2/shipgoalsdlg.cpp @@ -0,0 +1,1378 @@ +/* + * $Logfile: /Freespace2/code/fred2/ShipGoalsDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Initial orders editor dialog box handling code. This dialog is used for both + * ship and wing initial orders, and can support more if need be without modification. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 4 3/26/99 4:49p Dave + * Made cruisers able to dock with stuff. Made docking points and paths + * visible in fred. + * + * 3 3/25/99 3:32p Johnson + * HACK: Allow Mycerinus to dock + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 57 7/06/98 5:12p Hoffoss + * Fixed bug where 3-5th orders don't update data listing when object + * changes. + * + * 56 5/21/98 12:58a Hoffoss + * Fixed warnings optimized build turned up. + * + * 55 4/06/98 11:34a Hoffoss + * Fixed bug caused by change in max ai goals. Should be able to handle + * anything from 0 to 10 ai goals now (i.e. it's more flexible now). + * + * 54 11/05/97 4:43p Allender + * reworked medal/rank system to read all data from tables. Made Fred + * read medals.tbl. Changed ai-warp to ai-warp-out which doesn't require + * waypoint for activation + * + * 53 10/22/97 3:15p Hoffoss + * Fixed ai-stay-still initial order to use waypoints instead of waypoint + * paths. + * + * 52 10/22/97 1:58p Hoffoss + * Added support for AI_GOALS_PLAY_DEAD and changed AI_GOALS_STAY_STILL + * for Fred. + * + * 51 10/10/97 5:03p Allender + * started work on ai-stay-still + * + * 50 9/18/97 11:28a Duncan + * Fixed bug with player objects having wrong name. + * + * 49 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 48 9/09/97 3:39p Sandeep + * warning level 4 bugs + * + * 47 9/02/97 5:10p Johnson + * Fixed bug with wing initial orders available to select from. + * + * 46 8/26/97 4:18p Hoffoss + * Added error checking to initial orders dialog when ok is clicked. + * + * 45 8/19/97 11:59a Duncan + * Fixed bug in initial orders set_item(). + * + * 44 8/16/97 4:51p Hoffoss + * Fixed bugs with wing deletion and removing ships from a wing. + * + * 43 8/15/97 5:14p Hoffoss + * Completely changed around how initial orders dialog worked. It's + * pretty awesome now. + * + * 42 8/14/97 7:07p Hoffoss + * Changed "guard ship" to "guard", since it can be used with wings now as + * well as ships. + * + * 41 8/10/97 4:41p Hoffoss + * Fixed bugs with docking initial order. + * + * 40 8/05/97 10:10p Hoffoss + * Made attack and guard orders handle both ships and wings as targets. + * + * 39 8/01/97 12:21p Jasen + * Fixed bug with docking order (in iinitial orders) where original dockee + * was deleted, and so not found when looked up. + * + * 38 7/30/97 12:31p Hoffoss + * Made improvements to ship goals editor (initial orders) to disallow + * illegal orders. + * + * 37 7/25/97 4:30p Hoffoss + * Changes to ship lookup to include player ships. + * + * 36 7/24/97 4:55p Allender + * added ai-evade-ship to fred and to Freespace + * + * 35 7/22/97 3:40p Allender + * Changed the way that attacking subsystem works (for initial orders). + * The code was behaving two different ways. Orders are now model + * specific as per which subsystem to attack. + * + * 34 7/17/97 4:23p Allender + * ai-guard-wing now in both Fred and Freespace + * + * 33 7/09/97 1:56p Jasen + * Added new case to data mode checkunder save item. + * + * 32 6/11/97 2:13p Hoffoss + * Fixed bug in initial order initialization, and made subsystem combo box + * use ship specific subsystems. + * + * 31 6/04/97 12:00a Allender + * added disarm and disable as initial order options + * + * 30 5/30/97 4:50p Hoffoss + * Added code to allow marked ship editing of data in child dialogs of + * ship editor dialog. + * + * 29 5/07/97 1:32p Allender + * fix bug from previous checkin where undock orders were not persistent + * + * 28 5/07/97 1:08p Allender + * make undock initial order only accept a priority + * + * 27 4/03/97 3:18p Allender + * + * 26 3/31/97 6:07p Hoffoss + * Fixed several errors, including BG editor not graying fields, BG editor + * not updating image when changed, Removed obsolete data from Weapon + * editor, priority not being saved when missions saved, priority not + * editable in initial orders editor. + * + * 25 3/28/97 3:19p Hoffoss + * Added chase-any goal to initial orders. + * + * 24 3/21/97 4:04p Bernal + * fixed crash when initial orders references illegal objects. (Jason) + * + * 23 3/21/97 10:29a Hoffoss + * forced negative priorities to 50 + * + * 22 3/19/97 4:53p Hoffoss + * fixed bug with unused fields not being cleared. + * + * 21 3/13/97 12:20p Hoffoss + * fixed bug in docking point specification of initial orders dialog. + * + * 20 3/10/97 6:43p Hoffoss + * Standardized docking goal usage by fred to use names instead of + * indexes. + * + * 19 3/10/97 5:37p Hoffoss + * fixed bug in dock goal selection. + * + * 18 3/10/97 12:54p Hoffoss + * Added drop down combo box to toolbar and fixed compiling errors Mark + * (maybe Mike?) introduced to code. + * + * 17 3/04/97 12:52p Allender + * docking styff. Added dock type to docking structure in model code. + * Renamed structure member in ai_goals. Temporary checkin. (i.e. + * rearming will use rearm dock points) + * + * 16 3/03/97 4:32p Hoffoss + * Initial orders supports new docking stuff Allender added. + * + * 15 2/27/97 3:15p Allender + * major wing structure enhancement. simplified wing code. All around + * better wing support + * + * 14 2/26/97 2:45p Hoffoss + * Initial orders dialog now only allows 89 as a max on priority. + * + * 13 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 12 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "shipgoalsdlg.h" +#include "object.h" +#include "linklist.h" +#include "management.h" +#include "aigoals.h" +#include "freddoc.h" +#include "fredview.h" + +#define TYPE_PATH 0x1000 +#define TYPE_SHIP 0x2000 +#define TYPE_PLAYER 0x3000 +#define TYPE_WING 0x4000 +#define TYPE_WAYPOINT 0x5000 +#define TYPE_MASK 0xf000 +#define DATA_MASK 0x0fff + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// ShipGoalsDlg dialog + +ShipGoalsDlg::ShipGoalsDlg(CWnd* pParent /*=NULL*/) + : CDialog(ShipGoalsDlg::IDD, pParent) +{ + int i; + + for (i=0; i= 0) { // editing orders for just one ship + for (i=0; i= 0) { // editing orders for just one wing + for (i=0; itype == OBJ_SHIP) && (ptr->flags & OF_MARKED)){ + for (i=0; iinstance, Ai_goal_list[i].def)){ + valid[i] = 0; + } + } + } + + ptr = GET_NEXT(ptr); + } + } + + for (i=0; itype == OBJ_SHIP) || (ptr->type == OBJ_START)) { + i = ptr->instance; + + if ((self_ship > 0) && (self_ship != i) && ship_docking_valid(self_ship, i)){ + z = 1; + } + } + ptr = GET_NEXT(ptr); + } + + if (!z){ + for (i=0; i ResetContent(); + z = m_behavior_box[i] -> AddString("None"); + m_behavior_box[i] -> SetItemData(z, (DWORD) AI_GOAL_NONE); + for (j=0; j AddString(Ai_goal_list[j].name); + m_behavior_box[i] -> SetItemData(z, (DWORD) Ai_goal_list[j].def); + } + } + } + + if (self_ship >= 0){ + initialize(Ai_info[Ships[self_ship].ai_index].goals, self_ship); + } else if (self_wing >= 0){ + initialize(Wings[self_wing].ai_goals); + } else { + initialize_multi(); + } + + for (i=0; i SetRange(0, 89); + ((CSpinButtonCtrl *) GetDlgItem(IDC_SPIN2)) -> SetRange(0, 89); + ((CSpinButtonCtrl *) GetDlgItem(IDC_SPIN3)) -> SetRange(0, 89); + ((CSpinButtonCtrl *) GetDlgItem(IDC_SPIN4)) -> SetRange(0, 89); + ((CSpinButtonCtrl *) GetDlgItem(IDC_SPIN5)) -> SetRange(0, 89); + ((CSpinButtonCtrl *) GetDlgItem(IDC_SPIN6)) -> SetRange(0, 89); + ((CSpinButtonCtrl *) GetDlgItem(IDC_SPIN7)) -> SetRange(0, 89); + ((CSpinButtonCtrl *) GetDlgItem(IDC_SPIN8)) -> SetRange(0, 89); + ((CSpinButtonCtrl *) GetDlgItem(IDC_SPIN9)) -> SetRange(0, 89); + ((CSpinButtonCtrl *) GetDlgItem(IDC_SPIN10)) -> SetRange(0, 89); + + return TRUE; +} + +void ShipGoalsDlg::initialize_multi() +{ + int i, flag = 0; + object *ptr; + int behavior[ED_MAX_GOALS]; + int priority[ED_MAX_GOALS]; + int subsys[ED_MAX_GOALS]; + int dock2[ED_MAX_GOALS]; + int data[ED_MAX_GOALS]; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if ((ptr->type == OBJ_SHIP) && (ptr->flags & OF_MARKED)) { + initialize(Ai_info[Ships[ptr->instance].ai_index].goals, ptr->instance); + if (!flag) { + flag = 1; + for (i=0; i 89){ + m_priority[item] = 50; + } + + m_behavior[item] = 0; + if (mode != AI_GOAL_NONE) { + i = m_behavior_box[item] -> GetCount(); + while (i-- > 0){ + if (mode & (m_behavior_box[item]->GetItemData(i))) { + m_behavior[item] = i; + break; + } + } + } + + switch (mode) { + case AI_GOAL_NONE: + case AI_GOAL_CHASE_ANY: + case AI_GOAL_UNDOCK: + case AI_GOAL_KEEP_SAFE_DISTANCE: + case AI_GOAL_PLAY_DEAD: + case AI_GOAL_WARP: + continue; + + case AI_GOAL_STAY_STILL: + flag = 9; // target is a ship or a waypoint + break; + + case AI_GOAL_CHASE: + case AI_GOAL_GUARD: + case AI_GOAL_DISABLE_SHIP: + case AI_GOAL_DISARM_SHIP: + case AI_GOAL_IGNORE: + case AI_GOAL_EVADE_SHIP: + case AI_GOAL_STAY_NEAR_SHIP: + break; + + case AI_GOAL_WAYPOINTS: + case AI_GOAL_WAYPOINTS_ONCE: + flag = 4; // target is a waypoint + break; + + case AI_GOAL_DESTROY_SUBSYSTEM: + num = ship_name_lookup(goalp[item].ship_name, 1); + if (num != -1) + m_subsys[item] = ship_get_subsys_index(&Ships[num], goalp[item].docker.name, 1); + + break; + + case AI_GOAL_DOCK: + m_subsys[item] = -1; + num = get_docking_list(Ships[ship].modelnum); + for (i=0; itype == OBJ_SHIP) || (ptr->type == OBJ_START)) { + inst = ptr->instance; + if (ptr->type == OBJ_SHIP) { + Assert(inst >= 0 && inst < MAX_SHIPS); + if (!stricmp(goalp[item].ship_name, Ships[inst].ship_name)) { + m_data[item] = inst | TYPE_SHIP; + break; + } + + } else { + Assert(inst >= 0 && inst < MAX_SHIPS); + if (!stricmp(goalp[item].ship_name, Ships[inst].ship_name)) { + m_data[item] = inst | TYPE_PLAYER; + break; + } + } + } + + ptr = GET_NEXT(ptr); + } + } + + if (flag & 0x2) { + for (i=0; i= 0) + m_data[item] = i | TYPE_WAYPOINT; + } + + switch (mode) { + case AI_GOAL_DOCK: + m_dock2[item] = -1; + if (m_data[item]) { + num = get_docking_list(Ships[m_data[item] & DATA_MASK].modelnum); + for (i=0; i= MAX_AI_GOALS) + m_behavior_box[item] -> EnableWindow(FALSE); + + Assert(item >= 0 && item < ED_MAX_GOALS); + m_object_box[item] -> ResetContent(); + if (m_behavior[item] < 1) { + m_object_box[item] -> EnableWindow(FALSE); + m_subsys_box[item] -> EnableWindow(FALSE); + m_dock2_box[item] -> EnableWindow(FALSE); + m_priority_box[item] -> EnableWindow(FALSE); + m_subsys[item] = -1; + m_dock2[item] = -1; + return; + } + + mode = m_behavior_box[item] -> GetItemData(m_behavior[item]); + m_priority_box[item] -> EnableWindow(TRUE); + if ((mode == AI_GOAL_CHASE_ANY) || (mode == AI_GOAL_UNDOCK) || (mode == AI_GOAL_KEEP_SAFE_DISTANCE) || (mode == AI_GOAL_PLAY_DEAD) || (mode == AI_GOAL_WARP) ) { + m_object_box[item] -> EnableWindow(FALSE); + m_subsys_box[item] -> EnableWindow(FALSE); + m_dock2_box[item] -> EnableWindow(FALSE); + m_subsys[item] = -1; + m_dock2[item] = -1; + return; + } + + m_object_box[item] -> EnableWindow(TRUE); + + switch (mode) { + case AI_GOAL_WAYPOINTS: + case AI_GOAL_WAYPOINTS_ONCE: + //case AI_GOAL_WARP: + for (i=0; i AddString(Waypoint_lists[i].name); + m_object_box[item] -> SetItemData(z, i | TYPE_PATH); + if (init && (m_data[item] == (i | TYPE_PATH))) + m_object[item] = z; + } + + break; + + case AI_GOAL_STAY_STILL: + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->type == OBJ_WAYPOINT) { + t = TYPE_SHIP; + z = m_object_box[item] -> AddString(object_name(OBJ_INDEX(ptr))); + m_object_box[item] -> SetItemData(z, OBJ_INDEX(ptr) | TYPE_WAYPOINT); + if (init && (m_data[item] == (OBJ_INDEX(ptr) | TYPE_WAYPOINT))) + m_object[item] = z; + } + + ptr = GET_NEXT(ptr); + } + + break; + } + + switch (mode) { + case AI_GOAL_DESTROY_SUBSYSTEM: + case AI_GOAL_CHASE | AI_GOAL_CHASE_WING: + case AI_GOAL_DOCK: + case AI_GOAL_GUARD | AI_GOAL_GUARD_WING: + case AI_GOAL_DISABLE_SHIP: + case AI_GOAL_DISARM_SHIP: + case AI_GOAL_EVADE_SHIP: + case AI_GOAL_IGNORE: + case AI_GOAL_STAY_NEAR_SHIP: + case AI_GOAL_STAY_STILL: + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->type == OBJ_SHIP || ptr->type == OBJ_START) { + inst = ptr->instance; + if (ptr->type == OBJ_SHIP) + t = TYPE_SHIP; + else + t = TYPE_PLAYER; + + Assert(inst >= 0 && inst < MAX_SHIPS); + // remove all marked ships from list + if (!goalp && (ptr->flags & OF_MARKED)) + inst = -1; + + // when docking, remove invalid dock targets + else if (mode == AI_GOAL_DOCK) { + if (self_ship < 0 || !ship_docking_valid(self_ship, inst)) + inst = -1; + } + + // disallow ship being it's own target + if (inst >= 0 && inst != self_ship) { + z = m_object_box[item] -> AddString(Ships[inst].ship_name); + m_object_box[item] -> SetItemData(z, inst | t); + if (init && (m_data[item] == (inst | t))) + m_object[item] = z; + } + } + + ptr = GET_NEXT(ptr); + } + + break; + } + + switch (mode) { + case AI_GOAL_CHASE | AI_GOAL_CHASE_WING: + case AI_GOAL_GUARD | AI_GOAL_GUARD_WING: + for (i=0; i AddString(Wings[i].name); + m_object_box[item] -> SetItemData(z, i | TYPE_WING); + if (init && (m_data[item] == (i | TYPE_WING))) + m_object[item] = z; + } + + break; + } + + if (mode == AI_GOAL_DESTROY_SUBSYSTEM) { + m_subsys_box[item] -> EnableWindow(TRUE); + m_dock2_box[item] -> EnableWindow(FALSE); + m_dock2[item] = -1; + set_object(item); + if (!m_behavior[item]) + set_item(item, init); + + } else if (mode == AI_GOAL_DOCK) { + num = get_docking_list(Ships[cur_ship].modelnum); + m_subsys_box[item] -> EnableWindow(TRUE); + m_subsys_box[item] -> ResetContent(); + for (i=0; i AddString(Docking_bay_list[i]); + m_subsys_box[item] -> SetItemDataPtr(z, Docking_bay_list[i]); + } + + set_object(item); + if (!m_behavior[item]) + set_item(item, init); + + } else { + m_subsys_box[item] -> EnableWindow(FALSE); + m_dock2_box[item] -> EnableWindow(FALSE); + m_subsys[item] = -1; + m_dock2[item] = -1; + } +} + +void ShipGoalsDlg::OnSelchangeBehavior1() +{ + UpdateData(TRUE); + m_object[0] = 0; + m_subsys[0] = m_dock2[0] = 0; + set_item(0); + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeBehavior2() +{ + UpdateData(TRUE); + m_object[1] = 0; + set_item(1); + m_subsys[1] = m_dock2[1] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeBehavior3() +{ + UpdateData(TRUE); + m_object[2] = 0; + set_item(2); + m_subsys[2] = m_dock2[2] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeBehavior4() +{ + UpdateData(TRUE); + m_object[3] = 0; + set_item(3); + m_subsys[3] = m_dock2[3] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeBehavior5() +{ + UpdateData(TRUE); + m_object[4] = 0; + set_item(4); + m_subsys[4] = m_dock2[4] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeBehavior6() +{ + UpdateData(TRUE); + m_object[5] = 0; + set_item(5); + m_subsys[5] = m_dock2[5] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeBehavior7() +{ + UpdateData(TRUE); + m_object[6] = 0; + set_item(6); + m_subsys[6] = m_dock2[6] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeBehavior8() +{ + UpdateData(TRUE); + m_object[7] = 0; + set_item(7); + m_subsys[7] = m_dock2[7] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeBehavior9() +{ + UpdateData(TRUE); + m_object[8] = 0; + set_item(8); + m_subsys[8] = m_dock2[8] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeBehavior10() +{ + UpdateData(TRUE); + m_object[9] = 0; + set_item(9); + m_subsys[9] = m_dock2[9] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::update() +{ + int i; + + if (goalp) { + for (i=0; itype == OBJ_SHIP) && (ptr->flags & OF_MARKED)) { + goalp = Ai_info[Ships[ptr->instance].ai_index].goals; + for (i=0; itype == OBJ_SHIP) && (ptr->flags & OF_MARKED)) { + self_ship = ptr->instance; + goalp = Ai_info[Ships[self_ship].ai_index].goals; + verify_orders(self_ship); + } + + ptr = GET_NEXT(ptr); + } + } +} + +int ShipGoalsDlg::verify_orders(int ship) +{ + char *str, buf[2048]; + + if ((str = error_check_initial_orders(goalp, self_ship, self_wing))!=NULL) { + if (*str == '!') + return 1; + else if (*str == '*') + str++; + + if (ship >= 0) + sprintf(buf, "Initial orders error for ship \"%s\"\n\n%s", Ships[ship].ship_name, str); + else + strcpy(buf, str); + + if (MessageBox(buf, "Error", MB_OKCANCEL | MB_ICONEXCLAMATION) != IDOK) + return 1; + } + + return 0; +} + +void ShipGoalsDlg::update_item(int item, int multi) +{ + char *docker, *dockee, *subsys; + int mode; + char buf[512], save[80]; + + if (item >= MAX_AI_GOALS) + return; + + if (!multi || m_priority[item] >= 0) + goalp[item].priority = m_priority[item]; + + if (m_behavior[item] < 0) { + if (multi) + return; + else + m_behavior[item] = 0; + } + + mode = m_behavior_box[item] -> GetItemData(m_behavior[item]); + switch (mode) { + case AI_GOAL_NONE: + case AI_GOAL_CHASE_ANY: + case AI_GOAL_UNDOCK: + case AI_GOAL_KEEP_SAFE_DISTANCE: + case AI_GOAL_PLAY_DEAD: + case AI_GOAL_WARP: + MODIFY(goalp[item].ai_mode, mode); + return; + + case AI_GOAL_WAYPOINTS: + case AI_GOAL_WAYPOINTS_ONCE: + case AI_GOAL_DISABLE_SHIP: + case AI_GOAL_DISARM_SHIP: + case AI_GOAL_IGNORE: + case AI_GOAL_EVADE_SHIP: + case AI_GOAL_STAY_NEAR_SHIP: + case AI_GOAL_STAY_STILL: + break; + + case AI_GOAL_DESTROY_SUBSYSTEM: + subsys = NULL; + if (!multi || (m_data[item] && (m_subsys[item] >= 0))) + subsys = (char *) m_subsys_box[item]->GetItemDataPtr(m_subsys[item]); + //MODIFY(goalp[item].ai_submode, m_subsys[item] + 1); + + if (!subsys) { + sprintf(buf, "Order #%d doesn't have valid subsystem name. Order will be removed", item + 1); + MessageBox(buf, "Notice"); + MODIFY(goalp[item].ai_mode, AI_GOAL_NONE); + return; + + } else { + if (!goalp[item].docker.name || (goalp[item].docker.name && !stricmp(goalp[item].docker.name, subsys))) + set_modified(); + + goalp[item].docker.name = subsys; + } + + break; + + case AI_GOAL_CHASE | AI_GOAL_CHASE_WING: + switch (m_data[item] & TYPE_MASK) { + case TYPE_SHIP: + case TYPE_PLAYER: + mode = AI_GOAL_CHASE; + break; + + case TYPE_WING: + mode = AI_GOAL_CHASE_WING; + break; + } + + break; + + case AI_GOAL_DOCK: + docker = NULL; + if (!multi || (m_data[item] && (m_subsys[item] >= 0))) + docker = (char *) m_subsys_box[item] -> GetItemDataPtr(m_subsys[item]); + + dockee = NULL; + if (!multi || (m_data[item] && (m_dock2[item] >= 0))) + dockee = (char *) m_dock2_box[item] -> GetItemDataPtr(m_dock2[item]); + + if (docker == (char *) 0xffffffff) + docker = NULL; + if (dockee == (char *) 0xffffffff) + dockee = NULL; + + if (!docker || !dockee) { + sprintf(buf, "Order #%d doesn't have valid docking points. Order will be removed", item + 1); + MessageBox(buf, "Notice"); + MODIFY(goalp[item].ai_mode, AI_GOAL_NONE); + return; + + } else { + if (!goalp[item].docker.name) + set_modified(); + else if (!stricmp(goalp[item].docker.name, docker)) + set_modified(); + + if (!goalp[item].dockee.name) + set_modified(); + else if (!stricmp(goalp[item].dockee.name, dockee)) + set_modified(); + + goalp[item].docker.name = docker; + goalp[item].dockee.name = dockee; + } + + break; + + case AI_GOAL_GUARD | AI_GOAL_GUARD_WING: + switch (m_data[item] & TYPE_MASK) { + case TYPE_SHIP: + case TYPE_PLAYER: + mode = AI_GOAL_GUARD; + break; + + case TYPE_WING: + mode = AI_GOAL_GUARD_WING; + break; + } + + break; + + default: + Warning(LOCATION, "Unknown AI_GOAL type 0x%x", mode); + MODIFY(goalp[item].ai_mode, AI_GOAL_NONE); + return; + } + + MODIFY(goalp[item].ai_mode, mode); + + *save = 0; + if (goalp[item].ship_name) + strcpy(save, goalp[item].ship_name); + + switch (m_data[item] & TYPE_MASK) { + int not_used; + + case TYPE_SHIP: + case TYPE_PLAYER: + goalp[item].ship_name = ai_get_goal_ship_name(Ships[m_data[item] & DATA_MASK].ship_name, ¬_used); + break; + + case TYPE_WING: + goalp[item].ship_name = ai_get_goal_ship_name(Wings[m_data[item] & DATA_MASK].name, ¬_used); + break; + + case TYPE_PATH: + goalp[item].ship_name = ai_get_goal_ship_name(Waypoint_lists[m_data[item] & DATA_MASK].name, ¬_used); + break; + + case TYPE_WAYPOINT: + goalp[item].ship_name = ai_get_goal_ship_name(object_name(m_data[item] & DATA_MASK), ¬_used); + break; + + case 0: + case -1: + case (-1 & TYPE_MASK): + if (multi) + return; + + sprintf(buf, "Order #%d doesn't have a valid target. Order will be removed", item + 1); + MessageBox(buf, "Notice"); + MODIFY(goalp[item].ai_mode, AI_GOAL_NONE); + return; + + default: + Assert(0); + } + + if (stricmp(save, goalp[item].ship_name)) + set_modified(); +} + +void ShipGoalsDlg::OnOK() +{ + int i, mode; + + UpdateData(TRUE); + + for (i=0; i GetItemData(m_behavior[i]); + if ((mode != AI_GOAL_NONE) && (mode != AI_GOAL_CHASE_ANY) && (mode != AI_GOAL_UNDOCK) && (mode != AI_GOAL_KEEP_SAFE_DISTANCE) && (mode != AI_GOAL_PLAY_DEAD) && (mode != AI_GOAL_WARP) ) { + if (!m_object_box[i] -> GetCount()) // no valid objects? + m_behavior[i] = 0; + else + m_data[i] = m_object_box[i] -> GetItemData(m_object[i]); + } + } + + update(); + CDialog::OnOK(); +} + +void ShipGoalsDlg::OnSelchangeObject1() +{ + UpdateData(TRUE); + set_object(0); + m_subsys[0] = m_dock2[0] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeObject2() +{ + UpdateData(TRUE); + set_object(1); + m_subsys[1] = m_dock2[1] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeObject3() +{ + UpdateData(TRUE); + set_object(2); + m_subsys[2] = m_dock2[2] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeObject4() +{ + UpdateData(TRUE); + set_object(3); + m_subsys[3] = m_dock2[3] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeObject5() +{ + UpdateData(TRUE); + set_object(4); + m_subsys[4] = m_dock2[4] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeObject6() +{ + UpdateData(TRUE); + set_object(5); + m_subsys[5] = m_dock2[5] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeObject7() +{ + UpdateData(TRUE); + set_object(6); + m_subsys[6] = m_dock2[6] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeObject8() +{ + UpdateData(TRUE); + set_object(7); + m_subsys[7] = m_dock2[7] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeObject9() +{ + UpdateData(TRUE); + set_object(8); + m_subsys[8] = m_dock2[8] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::OnSelchangeObject10() +{ + UpdateData(TRUE); + set_object(9); + m_subsys[9] = m_dock2[9] = 0; + UpdateData(FALSE); +} + +void ShipGoalsDlg::set_object(int item) +{ + char *str; + int i = 0, z, num, not_used, mode; + ship_subsys *subsys; + + if (m_behavior[item] > 0) { + mode = m_behavior_box[item] -> GetItemData(m_behavior[item]); + if (!m_object_box[item] -> GetCount()) + m_behavior[item] = m_data[item] = 0; + else + m_data[item] = m_object_box[item] -> GetItemData(m_object[item]); + + if ((mode == AI_GOAL_DOCK) && (m_data[item] >= 0)) { + num = get_docking_list(Ships[m_data[item] & DATA_MASK].modelnum); + m_dock2_box[item] -> EnableWindow(TRUE); + m_dock2_box[item] -> ResetContent(); + for (i=0; i AddString(Docking_bay_list[i]); + str = ai_get_goal_ship_name(Docking_bay_list[i], ¬_used); + m_dock2_box[item] -> SetItemDataPtr(z, str); + } + + } else if ((mode == AI_GOAL_DESTROY_SUBSYSTEM) && (m_data[item] >= 0)) { + if (((m_data[item] & TYPE_MASK) == TYPE_SHIP) || ((m_data[item] & TYPE_MASK) == TYPE_PLAYER)) { + i = m_data[item] & DATA_MASK; + + } else + Int3(); + + m_subsys_box[item] -> ResetContent(); + subsys = GET_FIRST(&Ships[i].subsys_list); + z = 0; + while (subsys != END_OF_LIST(&Ships[i].subsys_list)) { + m_subsys_box[item]->AddString(subsys->system_info->subobj_name); + m_subsys_box[item]->SetItemDataPtr(z, subsys->system_info->subobj_name); + z++; + subsys = GET_NEXT(subsys); + } + } + } +} diff --git a/src/fred2/shipspecialdamage.cpp b/src/fred2/shipspecialdamage.cpp new file mode 100644 index 0000000..317c456 --- /dev/null +++ b/src/fred2/shipspecialdamage.cpp @@ -0,0 +1,204 @@ +// ShipSpecialDamage.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "shipspecialdamage.h" +#include "linklist.h" +#include "sexp.h" +#include "freddoc.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// ShipSpecialDamage dialog + + +ShipSpecialDamage::ShipSpecialDamage(CWnd* pParent /*=NULL*/) + : CDialog(ShipSpecialDamage::IDD, pParent) +{ + //{{AFX_DATA_INIT(ShipSpecialDamage) + //}}AFX_DATA_INIT +} + + +void ShipSpecialDamage::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(ShipSpecialDamage) + DDX_Check(pDX, IDC_ENABLE_SHOCKWAVE, m_shock_enabled); + DDX_Check(pDX, IDC_ENABLE_SPECIAL_EXP, m_special_exp_enabled); + DDX_Text(pDX, IDC_SPECIAL_INNER_RAD, m_inner_rad); + DDV_MinMaxInt(pDX, m_inner_rad, 10, 10000); + DDX_Text(pDX, IDC_SPECIAL_OUTER_RAD, m_outer_rad); + DDV_MinMaxInt(pDX, m_outer_rad, 11, 10000); + DDX_Text(pDX, IDC_SPECIAL_DAMAGE, m_damage); + DDV_MinMaxInt(pDX, m_damage, 0, 100000); + DDX_Text(pDX, IDC_SPECIAL_SHOCK_SPEED, m_shock_speed); + DDV_MinMaxInt(pDX, m_shock_speed, 10, 1000); + DDX_Text(pDX, IDC_SPECIAL_BLAST, m_blast); + DDV_MinMaxInt(pDX, m_blast, 0, 100000); + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(ShipSpecialDamage, CDialog) + //{{AFX_MSG_MAP(ShipSpecialDamage) + ON_BN_CLICKED(IDC_ENABLE_SHOCKWAVE, OnEnableShockwave) + ON_BN_CLICKED(IDC_ENABLE_SPECIAL_EXP, OnEnableSpecialExp) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// ShipSpecialDamage message handlers + +void ShipSpecialDamage::OnEnableShockwave() +{ + // TODO: Add your control notification handler code here + UpdateData(TRUE); + + // enable/disable shock speed + DoGray();} + +void ShipSpecialDamage::OnEnableSpecialExp() +{ + // TODO: Add your control notification handler code here + UpdateData(TRUE); + + DoGray(); +} + +BOOL ShipSpecialDamage::OnInitDialog() +{ + + // TODO: Add extra initialization here + + // get ship num + object *objp; + + m_ship_num = -1; + + objp = GET_FIRST(&obj_used_list); + while (objp != END_OF_LIST(&obj_used_list)) { + if ((objp->type == OBJ_START) || (objp->type == OBJ_SHIP)) { + if (objp->flags & OF_MARKED) { + + m_ship_num = objp->instance; + break; + } + } + objp = GET_NEXT(objp); + } + + if (Ships[m_ship_num].special_exp_index == -1) { + // get default_table_values + ship_info *sip; + sip = &Ship_info[Ships[m_ship_num].ship_info_index]; + + m_inner_rad = (int)sip->inner_rad; + m_outer_rad = (int)sip->outer_rad; + m_damage = (int) sip->damage; + m_blast = (int) sip->blast; + m_shock_enabled = (int) sip->explosion_propagates; + m_shock_speed = (int) sip->shockwave_speed; + m_special_exp_enabled = FALSE; + + if (m_inner_rad < 10) m_inner_rad = 10; + if (m_outer_rad < 11) m_outer_rad = 11; + if (m_shock_speed < 10) m_shock_speed = 10; + } else { + int index = Ships[m_ship_num].special_exp_index; + Assert( (index > 0) && (index < MAX_SEXP_VARIABLES-5) ); + + m_inner_rad = atoi(Sexp_variables[index++].text); + m_outer_rad = atoi(Sexp_variables[index++].text); + m_damage = atoi(Sexp_variables[index++].text); + m_blast = atoi(Sexp_variables[index++].text); + m_shock_enabled = atoi(Sexp_variables[index++].text); + m_shock_speed = atoi(Sexp_variables[index++].text); + m_special_exp_enabled = TRUE; + } + + CDialog::OnInitDialog(); + + // maybe gray out lots of stuff + DoGray(); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void ShipSpecialDamage::DoGray() +{ + GetDlgItem(IDC_SPECIAL_DAMAGE)->EnableWindow(m_special_exp_enabled); + GetDlgItem(IDC_SPECIAL_BLAST)->EnableWindow(m_special_exp_enabled); + GetDlgItem(IDC_SPECIAL_INNER_RAD)->EnableWindow(m_special_exp_enabled); + GetDlgItem(IDC_SPECIAL_OUTER_RAD)->EnableWindow(m_special_exp_enabled); + GetDlgItem(IDC_ENABLE_SHOCKWAVE)->EnableWindow(m_special_exp_enabled); + GetDlgItem(IDC_SPECIAL_SHOCK_SPEED)->EnableWindow(m_special_exp_enabled && m_shock_enabled); +} + + +void ShipSpecialDamage::OnCancel() +{ + // TODO: Add extra cleanup here + + CDialog::OnCancel(); +} + +void ShipSpecialDamage::OnOK() +{ + UpdateData(TRUE); + + // TODO: Add extra validation here + if (m_special_exp_enabled) { + + int start; + + if (m_inner_rad > m_outer_rad) { + MessageBox("Inner radius must be less than outer radius"); + return; + } + + if (Ships[m_ship_num].special_exp_index == -1) { + // get free sexp_variables + start = sexp_variable_allocate_block(Ships[m_ship_num].ship_name, SEXP_VARIABLE_BLOCK | SEXP_VARIABLE_BLOCK_EXP); + if (start == -1) { + MessageBox("Unable to allocate storage, try deleting Sexp variables"); + return; + } else { + Ships[m_ship_num].special_exp_index = start; + } + } else { + start = Ships[m_ship_num].special_exp_index; + } + // set to update + set_modified(); + + // set em + sprintf(Sexp_variables[start+INNER_RAD].text, "%d", m_inner_rad); + sprintf(Sexp_variables[start+OUTER_RAD].text, "%d", m_outer_rad); + sprintf(Sexp_variables[start+DAMAGE].text, "%d", m_damage); + sprintf(Sexp_variables[start+BLAST].text, "%d", m_blast); + sprintf(Sexp_variables[start+PROPAGATE].text, "%d", m_shock_enabled); + sprintf(Sexp_variables[start+SHOCK_SPEED].text, "%d", m_shock_speed); + + } else { + if (Ships[m_ship_num].special_exp_index != -1) { + // set to update + set_modified(); + + // free block + sexp_variable_block_free(Ships[m_ship_num].ship_name, Ships[m_ship_num].special_exp_index, SEXP_VARIABLE_BLOCK |SEXP_VARIABLE_BLOCK_EXP); + + // set index to no exp block + Ships[m_ship_num].special_exp_index = -1; + } + } + + CDialog::OnOK(); +} diff --git a/src/fred2/starfieldeditor.cpp b/src/fred2/starfieldeditor.cpp new file mode 100644 index 0000000..7f2e815 --- /dev/null +++ b/src/fred2/starfieldeditor.cpp @@ -0,0 +1,131 @@ +/* + * $Logfile: /Freespace2/code/FRED2/StarfieldEditor.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Starfield editor dialog handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 11 12/08/97 4:48p Hoffoss + * Moved starfield editor controls to background editor. + * + * 10 4/17/97 2:01p Hoffoss + * All dialog box window states are saved between sessions now. + * + * 9 4/17/97 9:33a Hoffoss + * Squished a warning. + * + * 8 4/16/97 5:18p Hoffoss + * Moved Asteroid field editor stuff to a seperate dialog box. + * + * 7 3/17/97 3:00p Hoffoss + * slider updates on the fly now. + * + * 6 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 5 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * 4 1/31/97 3:16p Hoffoss + * Asteroid field management implemented. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "starfieldeditor.h" +#include "starfield.h" +#include "freddoc.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// starfield_editor dialog + +starfield_editor::starfield_editor(CWnd* pParent /*=NULL*/) + : CDialog(starfield_editor::IDD, pParent) +{ + //{{AFX_DATA_INIT(starfield_editor) + //}}AFX_DATA_INIT +} + +void starfield_editor::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(starfield_editor) + DDX_Control(pDX, IDC_SLIDER1, m_slider); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(starfield_editor, CDialog) + //{{AFX_MSG_MAP(starfield_editor) + ON_WM_HSCROLL() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// starfield_editor message handlers + +void starfield_editor::OnOK() +{ + char buf[40]; + + UpdateData(TRUE); + theApp.record_window_data(&Starfield_wnd_data, this); + MODIFY(Num_stars, m_slider.GetPos()); + sprintf(buf, "%d", Num_stars); + GetDlgItem(IDC_TOTAL)->SetWindowText(buf); + update_map_window(); +} + +void starfield_editor::OnCancel() +{ + theApp.record_window_data(&Starfield_wnd_data, this); + CDialog::OnCancel(); +} + +BOOL starfield_editor::OnInitDialog() +{ + char buf[40]; + CDialog::OnInitDialog(); + theApp.init_window(&Starfield_wnd_data, this); + + m_slider.SetRange(100, MAX_STARS); + m_slider.SetPos(Num_stars); + sprintf(buf, "%d", Num_stars); + GetDlgItem(IDC_TOTAL)->SetWindowText(buf); + return TRUE; +} + +void starfield_editor::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) +{ + char buf[40]; + + CDialog::OnHScroll(nSBCode, nPos, pScrollBar); + + MODIFY(Num_stars, m_slider.GetPos()); + sprintf(buf, "%d", Num_stars); + GetDlgItem(IDC_TOTAL)->SetWindowText(buf); +} diff --git a/src/fred2/stdafx.cpp b/src/fred2/stdafx.cpp new file mode 100644 index 0000000..b674882 --- /dev/null +++ b/src/fred2/stdafx.cpp @@ -0,0 +1,36 @@ +/* + * $Logfile: /Freespace2/code/FRED2/StdAfx.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * +// stdafx.cpp : source file that includes just the standard includes +// FRED.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + * + * Above is the MFC generated notes on this file. Basically, you don't need + * do touch anything here. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:08 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:01p Dave + * + * 1 10/07/98 3:00p Dave + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" + diff --git a/src/fred2/textviewdlg.cpp b/src/fred2/textviewdlg.cpp new file mode 100644 index 0000000..bdc4f88 --- /dev/null +++ b/src/fred2/textviewdlg.cpp @@ -0,0 +1,102 @@ +// TextViewDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "fred.h" +#include "textviewdlg.h" +#include "cfile.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// text_view_dlg dialog + +text_view_dlg::text_view_dlg(CWnd* pParent /*=NULL*/) + : CDialog(text_view_dlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(text_view_dlg) + m_edit = _T(""); + //}}AFX_DATA_INIT +} + +void text_view_dlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(text_view_dlg) + DDX_Text(pDX, IDC_EDIT1, m_edit); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(text_view_dlg, CDialog) + //{{AFX_MSG_MAP(text_view_dlg) + ON_EN_SETFOCUS(IDC_EDIT1, OnSetfocusEdit1) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// text_view_dlg message handlers + +void text_view_dlg::set(int ship_class) +{ + char line[256], line2[256]; + int i, j, found = 0, comment = 0; + CFILE *fp; + + if (ship_class < 0) + return; + + fp = cfopen("ships.tbl", "r"); + Assert(fp); + + while (cfgets(line, 255, fp)) { + while (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = 0; + + for (i=j=0; line[i]; i++) { + if (line[i] == '/' && line[i+1] == '/') + break; + + if (line[i] == '/' && line[i+1] == '*') { + comment = 1; + i++; + continue; + } + + if (line[i] == '*' && line[i+1] == '/') { + comment = 0; + i++; + continue; + } + + if (!comment) + line2[j++] = line[i]; + } + + line2[j] = 0; + if (!strnicmp(line2, "$Name:", 6)) { + found = 0; + i = 6; + while (line2[i] == ' ' || line2[i] == '\t') + i++; + + if (!stricmp(line2 + i, Ship_info[ship_class].name)) + found = 1; + } + + if (found) { + m_edit += line; + m_edit += "\r\n"; + } + } + + cfclose(fp); +} + +void text_view_dlg::OnSetfocusEdit1() +{ + ((CEdit *) GetDlgItem(IDC_EDIT1)) -> SetSel(-1, -1); +} diff --git a/src/fred2/waypointpathdlg.cpp b/src/fred2/waypointpathdlg.cpp new file mode 100644 index 0000000..9592fe9 --- /dev/null +++ b/src/fred2/waypointpathdlg.cpp @@ -0,0 +1,447 @@ +/* + * $Logfile: /Freespace2/code/FRED2/WaypointPathDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Waypoint editor + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 12 3/21/98 7:36p Lawrance + * Move jump nodes to own lib. + * + * 11 3/10/98 6:11p Hoffoss + * Added jump node renaming abilities to Fred. + * + * 10 8/12/97 1:55a Hoffoss + * Made extensive changes to object reference checking and handling for + * object deletion call. + * + * 9 5/23/97 1:53p Hoffoss + * Fixed problems with modeless dialog updating. It won't get caught in + * an infinate loop anymore, but still gives an error warning 3 times when + * using cancel and trying to switch window focus to main window. Don't + * know if I can fix that, but it's not too critical right now. + * + * 8 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 7 4/28/97 4:14p Hoffoss + * Fixed several bugs involving waypoints. + * + * 6 3/13/97 12:09p Hoffoss + * Waypoint path editor finished (apparently I didn't get around to + * completing it before). + * + * 5 2/28/97 11:31a Hoffoss + * Implemented modeless dialog saving and restoring, and changed some + * variables names. + * + * 4 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 3 2/12/97 5:50p Hoffoss + * Expanded on error checking. + * + * 2 2/12/97 12:26p Hoffoss + * Expanded on global error checker, added initial orders conflict + * checking and warning, added waypoint editor dialog and code. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "waypointpathdlg.h" +#include "management.h" +#include "mainfrm.h" +#include "object.h" +#include "linklist.h" +#include "ship.h" +#include "aigoals.h" +#include "starfield.h" +#include "jumpnode.h" + +#define ID_JUMP_NODE_MENU 8000 +#define ID_WAYPOINT_MENU 9000 + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// waypoint_path_dlg dialog + +waypoint_path_dlg::waypoint_path_dlg(CWnd* pParent /*=NULL*/) + : CDialog(waypoint_path_dlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(waypoint_path_dlg) + m_name = _T(""); + //}}AFX_DATA_INIT + bypass_errors = 0; +} + +void waypoint_path_dlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(waypoint_path_dlg) + DDX_Text(pDX, IDC_NAME, m_name); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(waypoint_path_dlg, CDialog) + //{{AFX_MSG_MAP(waypoint_path_dlg) + ON_WM_CLOSE() + ON_WM_INITMENU() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// waypoint_path_dlg message handlers + +BOOL waypoint_path_dlg::Create() +{ + BOOL r; + r = CDialog::Create(IDD, Fred_main_wnd); + initialize_data(1); + return r; +} + +void waypoint_path_dlg::OnInitMenu(CMenu* pMenu) +{ + int i; + CMenu *m; + + m = pMenu->GetSubMenu(0); + clear_menu(m); + for (i=0; iAppendMenu(MF_ENABLED | MF_STRING, ID_WAYPOINT_MENU + i, Waypoint_lists[i].name); + + for (i=0; iAppendMenu(MF_ENABLED | MF_STRING, ID_JUMP_NODE_MENU + i, Jump_nodes[i].name); + + m->DeleteMenu(ID_PLACEHOLDER, MF_BYCOMMAND); + if (cur_waypoint_list != -1) + m->CheckMenuItem(ID_WAYPOINT_MENU + cur_waypoint_list, MF_BYCOMMAND | MF_CHECKED); + + if (cur_object_index >= 0) + if (Objects[cur_object_index].type == OBJ_JUMP_NODE) + m->CheckMenuItem(ID_JUMP_NODE_MENU + Objects[cur_object_index].instance, MF_BYCOMMAND | MF_CHECKED); + + CDialog::OnInitMenu(pMenu); +} + +void waypoint_path_dlg::OnOK() +{ +} + +void waypoint_path_dlg::OnClose() +{ + if (update_data()) { + SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + bypass_errors = 0; + return; + } + + SetWindowPos(Fred_main_wnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); + Fred_main_wnd->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); +} + +void waypoint_path_dlg::initialize_data(int full_update) +{ + int enable = TRUE; + + if (!GetSafeHwnd()) + return; + + if (query_valid_object() && Objects[cur_object_index].type == OBJ_WAYPOINT) + Assert(cur_waypoint_list == (Objects[cur_object_index].instance / 65536)); + + if (cur_waypoint_list >= 0) { + m_name = _T(Waypoint_lists[cur_waypoint_list].name); + + } else if (Objects[cur_object_index].type == OBJ_JUMP_NODE) { + m_name = _T(Jump_nodes[Objects[cur_object_index].instance].name); + + } else { + m_name = _T(""); + enable = FALSE; + } + + if (full_update) + UpdateData(FALSE); + + GetDlgItem(IDC_NAME)->EnableWindow(enable); +} + +int waypoint_path_dlg::update_data(int redraw) +{ + char *str, old_name[255]; + int i, z, inst; + object *ptr; + + if (!GetSafeHwnd()) + return 0; + + UpdateData(TRUE); + UpdateData(TRUE); + + if (query_valid_object() && Objects[cur_object_index].type == OBJ_WAYPOINT) + Assert(cur_waypoint_list == (Objects[cur_object_index].instance / 65536)); + + if (cur_waypoint_list >= 0) { + if (!strnicmp(m_name, "player ", 7)) { + if (bypass_errors) + return 1; + + bypass_errors = 1; + z = MessageBox("Waypoint path names can't start with the word 'player'\n" + "Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL); + + if (z == IDCANCEL) + return -1; + + m_name = _T(Waypoint_lists[cur_waypoint_list].name); + UpdateData(FALSE); + } + + for (i=0; itype == OBJ_SHIP) { + if (!stricmp(m_name, Ships[ptr->instance].ship_name)) { + if (bypass_errors) + return 1; + + bypass_errors = 1; + z = MessageBox("This waypoint path name is already being used by a ship\n" + "Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL); + + if (z == IDCANCEL) + return -1; + + m_name = _T(Waypoint_lists[cur_waypoint_list].name); + UpdateData(FALSE); + } + } + + ptr = GET_NEXT(ptr); + } + + for (i=0; itype == OBJ_SHIP) { + if (!stricmp(m_name, Ships[ptr->instance].ship_name)) { + if (bypass_errors) + return 1; + + bypass_errors = 1; + z = MessageBox("This jump node name is already being used by a ship\n" + "Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL); + + if (z == IDCANCEL) + return -1; + + m_name = _T(Jump_nodes[inst].name); + UpdateData(FALSE); + } + } + + ptr = GET_NEXT(ptr); + } + + for (i=0; i= ID_WAYPOINT_MENU) && (id < ID_WAYPOINT_MENU + MAX_WAYPOINT_LISTS)) { + if (!update_data()) { + point = id - ID_WAYPOINT_MENU; + unmark_all(); + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->type == OBJ_WAYPOINT) + if ((ptr->instance / 65536) == point) + mark_object(OBJ_INDEX(ptr)); + + ptr = GET_NEXT(ptr); + } + + return 1; + } + } + + if ((id >= ID_JUMP_NODE_MENU) && (id < ID_JUMP_NODE_MENU + Num_jump_nodes)) { + if (!update_data()) { + point = id - ID_JUMP_NODE_MENU; + unmark_all(); + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->type == OBJ_JUMP_NODE) + if (ptr->instance == point) + mark_object(OBJ_INDEX(ptr)); + + ptr = GET_NEXT(ptr); + } + + return 1; + } + } + + return CDialog::OnCommand(wParam, lParam); +} diff --git a/src/fred2/weaponeditordlg.cpp b/src/fred2/weaponeditordlg.cpp new file mode 100644 index 0000000..283eff3 --- /dev/null +++ b/src/fred2/weaponeditordlg.cpp @@ -0,0 +1,667 @@ +/* + * $Logfile: /Freespace2/code/Fred2/WeaponEditorDlg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Weapon editor dialog box handling code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 4/28/99 11:13p Dave + * Temporary checkin of artillery code. + * + * 3 4/23/99 12:01p Johnson + * Added SIF_HUGE_SHIP + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 17 5/14/98 5:31p Hoffoss + * Fixed some stupid behaviour in this editor related to ammo counts being + * -99. + * + * 16 4/22/98 11:27a Hoffoss + * Make big weapons available for capital ships as well as big ships (why + * the hell a capital ship isn't a big ship is beyond me, though). + * + * 15 4/14/98 11:46a Johnson + * Added window enables for spinners, but it seems to be ignored. + * + * 14 2/26/98 10:44a Johnson + * Fixed bug with SetRange() not being called on editor first open. + * + * 13 2/22/98 1:32a Hoffoss + * Changed editor to use raw ammo counts isntead of percentages, and make + * it select first item in list by default. + * + * 12 2/13/98 3:38p Johnson + * Fixed bug where multi ship editing won't update player ships. + * + * 11 12/11/97 5:46p Hoffoss + * Changed Fred to not display weapons that are not available to various + * ships. + * + * 10 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 9 5/30/97 4:50p Hoffoss + * Added code to allow marked ship editing of data in child dialogs of + * ship editor dialog. + * + * 8 4/21/97 5:02p Hoffoss + * Player/player status editing supported, and both saved and loaded from + * Mission files. + * + * 7 4/16/97 2:02p Hoffoss + * Fixed bug Mike created by changing code while I had file checked out + * still. + * + * 6 4/16/97 1:59p Hoffoss + * Weapon editor now fully functional. + * + * 5 4/10/97 3:20p Mike + * Change hull damage to be like shields. + * + * 4 4/09/97 11:48a Hoffoss + * Initial work to weapon editor. + * + * 3 3/31/97 6:07p Hoffoss + * Fixed several errors, including BG editor not graying fields, BG editor + * not updating image when changed, Removed obsolete data from Weapon + * editor, priority not being saved when missions saved, priority not + * editable in initial orders editor. + * + * 2 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "weaponeditordlg.h" +#include "linklist.h" +#include "management.h" +#include "weapon.h" +#include "ship.h" + +#define BLANK_FIELD -99 + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// WeaponEditorDlg dialog + +WeaponEditorDlg::WeaponEditorDlg(CWnd* pParent /*=NULL*/) + : CDialog(WeaponEditorDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(WeaponEditorDlg) + m_ai_class = -1; + m_ammo1 = 0; + m_ammo2 = 0; + m_ammo3 = 0; + m_ammo4 = 0; + m_gun1 = -1; + m_gun2 = -1; + m_gun3 = -1; + m_missile1 = -1; + m_missile2 = -1; + m_missile3 = -1; + m_missile4 = -1; + m_cur_item = -1; + //}}AFX_DATA_INIT + m_last_item = -1; + m_multi_edit = 0; +} + +int save_number(char *str, int *val) +{ + char buf[40]; + int num; + + num = atoi(str); + sprintf(buf, "%d", num); + if (strncmp(str, buf, strlen(buf))) + return 0; + + *val = num; + return 1; +} + +void WeaponEditorDlg::DoDataExchange(CDataExchange* pDX) +{ + CString str; + + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(WeaponEditorDlg) + DDX_Control(pDX, IDC_SPIN4, m_spin4); + DDX_Control(pDX, IDC_SPIN3, m_spin3); + DDX_Control(pDX, IDC_SPIN2, m_spin2); + DDX_Control(pDX, IDC_SPIN1, m_spin1); + DDX_CBIndex(pDX, IDC_AI_CLASS, m_ai_class); + DDX_CBIndex(pDX, IDC_GUN1, m_gun1); + DDX_CBIndex(pDX, IDC_GUN2, m_gun2); + DDX_CBIndex(pDX, IDC_GUN3, m_gun3); + DDX_CBIndex(pDX, IDC_MISSILE1, m_missile1); + DDX_CBIndex(pDX, IDC_MISSILE2, m_missile2); + DDX_CBIndex(pDX, IDC_MISSILE3, m_missile3); + DDX_CBIndex(pDX, IDC_MISSILE4, m_missile4); + DDX_LBIndex(pDX, IDC_LIST, m_cur_item); + //}}AFX_DATA_MAP + + if (pDX->m_bSaveAndValidate) { + GetDlgItem(IDC_AMMO1)->GetWindowText(str); + if (save_number((char *) (LPCSTR) str, &m_ammo1)) { + m_ammo_max1 = (m_missile1 <= 0) ? 0 : get_max_ammo_count_for_bank(m_ship_class, 0, m_missile1 + First_secondary_index - 1); + if (m_ammo1 < 0) + m_ammo1 = 0; + if (m_ammo1 > m_ammo_max1) + m_ammo1 = m_ammo_max1; + } + + GetDlgItem(IDC_AMMO2)->GetWindowText(str); + if (save_number((char *) (LPCSTR) str, &m_ammo2)) { + m_ammo_max2 = (m_missile2 <= 0) ? 0 : get_max_ammo_count_for_bank(m_ship_class, 1, m_missile2 + First_secondary_index - 1); + if (m_ammo2 < 0) + m_ammo2 = 0; + if (m_ammo2 > m_ammo_max2) + m_ammo2 = m_ammo_max2; + } + + GetDlgItem(IDC_AMMO3)->GetWindowText(str); + if (save_number((char *) (LPCSTR) str, &m_ammo3)) { + m_ammo_max3 = (m_missile3 <= 0) ? 0 : get_max_ammo_count_for_bank(m_ship_class, 2, m_missile3 + First_secondary_index - 1); + if (m_ammo3 < 0) + m_ammo3 = 0; + if (m_ammo3 > m_ammo_max3) + m_ammo3 = m_ammo_max3; + } + + GetDlgItem(IDC_AMMO4)->GetWindowText(str); + if (save_number((char *) (LPCSTR) str, &m_ammo4)) { + m_ammo_max4 = (m_missile4 <= 0) ? 0 : get_max_ammo_count_for_bank(m_ship_class, 3, m_missile4 + First_secondary_index - 1); + if (m_ammo4 < 0) + m_ammo4 = 0; + if (m_ammo4 > m_ammo_max4) + m_ammo4 = m_ammo_max4; + } + + m_spin1.SetRange(0, m_ammo_max1); + m_spin2.SetRange(0, m_ammo_max2); + m_spin3.SetRange(0, m_ammo_max3); + m_spin4.SetRange(0, m_ammo_max4); + + } else { + if (m_ammo1 != BLANK_FIELD) + DDX_Text(pDX, IDC_AMMO1, m_ammo1); + else + GetDlgItem(IDC_AMMO1)->SetWindowText(""); + + if (m_ammo2 != BLANK_FIELD) + DDX_Text(pDX, IDC_AMMO2, m_ammo2); + else + GetDlgItem(IDC_AMMO2)->SetWindowText(""); + + if (m_ammo3 != BLANK_FIELD) + DDX_Text(pDX, IDC_AMMO3, m_ammo3); + else + GetDlgItem(IDC_AMMO3)->SetWindowText(""); + + if (m_ammo4 != BLANK_FIELD) + DDX_Text(pDX, IDC_AMMO4, m_ammo4); + else + GetDlgItem(IDC_AMMO4)->SetWindowText(""); + } +} + +BEGIN_MESSAGE_MAP(WeaponEditorDlg, CDialog) + //{{AFX_MSG_MAP(WeaponEditorDlg) + ON_LBN_SELCHANGE(IDC_LIST, OnSelchangeList) + ON_WM_CLOSE() + ON_CBN_SELCHANGE(IDC_MISSILE1, OnSelchangeMissile1) + ON_CBN_SELCHANGE(IDC_MISSILE2, OnSelchangeMissile2) + ON_CBN_SELCHANGE(IDC_MISSILE3, OnSelchangeMissile3) + ON_CBN_SELCHANGE(IDC_MISSILE4, OnSelchangeMissile4) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// WeaponEditorDlg message handlers + +BOOL WeaponEditorDlg::OnInitDialog() +{ + int i, z, big = 1, end1, end2, inst, flag = 0; + object *ptr; + model_subsystem *psub; + ship_subsys *ssl, *pss; + CComboBox *box; + CListBox *list; + + CDialog::OnInitDialog(); + m_ship = cur_ship; + if (m_ship == -1) + m_ship = Objects[cur_object_index].instance; + + end1 = First_secondary_index; + end2 = Num_weapon_types; + + list = (CListBox *) GetDlgItem(IDC_LIST); + + z = list->AddString("Pilot"); + if (m_multi_edit) { + list->SetItemDataPtr(z, &pilot); + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags & OF_MARKED)) { + inst = ptr->instance; + if (!(ship_get_SIF(inst) & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) + big = 0; + + if (!flag) { + pilot = Ships[inst].weapons; + m_ship_class = Ships[inst].ship_info_index; + flag = 1; + + } else { + Assert(Ships[inst].ship_info_index == m_ship_class); + if (pilot.ai_class != Ships[inst].weapons.ai_class) + pilot.ai_class = BLANK_FIELD; + + for (i=0; iSetItemDataPtr(z, &Ships[m_ship].weapons); + ssl = &Ships[m_ship].subsys_list; + for (pss = GET_FIRST(ssl); pss != END_OF_LIST(ssl); pss = GET_NEXT(pss)) { + psub = pss->system_info; + if (psub->type == SUBSYSTEM_TURRET) { + z = list->AddString(psub->subobj_name); + list->SetItemDataPtr(z, &pss->weapons); + } + } + } + + box = (CComboBox *) GetDlgItem(IDC_AI_CLASS); + for (i=0; iAddString(Ai_class_names[i]); + } + + for (i=0; iAddString("None"); + for (i=0; iAddString(Weapon_info[i].name); + } + + box = (CComboBox *) GetDlgItem(IDC_GUN2); + box->AddString("None"); + for (i=0; iAddString(Weapon_info[i].name); + } + + box = (CComboBox *) GetDlgItem(IDC_GUN3); + box->AddString("None"); + for (i=0; iAddString(Weapon_info[i].name); + } + + for (i=First_secondary_index; iAddString("None"); + for (i=First_secondary_index; iAddString(Weapon_info[i].name); + } + + box = (CComboBox *) GetDlgItem(IDC_MISSILE2); + box->AddString("None"); + for (i=First_secondary_index; iAddString(Weapon_info[i].name); + } + + box = (CComboBox *) GetDlgItem(IDC_MISSILE3); + box->AddString("None"); + for (i=First_secondary_index; iAddString(Weapon_info[i].name); + } + + box = (CComboBox *) GetDlgItem(IDC_MISSILE4); + box->AddString("None"); + for (i=First_secondary_index; iAddString(Weapon_info[i].name); + } + + m_cur_item = 0; + UpdateData(FALSE); + change_selection(); + UpdateData(TRUE); + return TRUE; +} + +void WeaponEditorDlg::OnSelchangeList() +{ + UpdateData(TRUE); + UpdateData(TRUE); + change_selection(); +} + +void WeaponEditorDlg::change_selection() +{ + CString a1, a2, a3, a4; + + GetDlgItem(IDC_AMMO1)->GetWindowText(a1); + GetDlgItem(IDC_AMMO2)->GetWindowText(a2); + GetDlgItem(IDC_AMMO3)->GetWindowText(a3); + GetDlgItem(IDC_AMMO4)->GetWindowText(a4); + + if (m_last_item >= 0) { + cur_weapon->ai_class = m_ai_class; + cur_weapon->primary_bank_weapons[0] = m_gun1 - 1; + cur_weapon->primary_bank_weapons[1] = m_gun2 - 1; + cur_weapon->primary_bank_weapons[2] = m_gun3 - 1; + if (m_missile1 > 0) + m_missile1 += First_secondary_index; + + cur_weapon->secondary_bank_weapons[0] = m_missile1 - 1; + if (m_missile2 > 0) + m_missile2 += First_secondary_index; + + cur_weapon->secondary_bank_weapons[1] = m_missile2 - 1; + if (m_missile3 > 0) + m_missile3 += First_secondary_index; + + cur_weapon->secondary_bank_weapons[2] = m_missile3 - 1; + if (m_missile4 > 0) + m_missile4 += First_secondary_index; + + cur_weapon->secondary_bank_weapons[3] = m_missile4 - 1; + cur_weapon->secondary_bank_ammo[0] = m_ammo_max1 ? (m_ammo1 * 100 / m_ammo_max1) : 0; + cur_weapon->secondary_bank_ammo[1] = m_ammo_max2 ? (m_ammo2 * 100 / m_ammo_max2) : 0; + cur_weapon->secondary_bank_ammo[2] = m_ammo_max3 ? (m_ammo3 * 100 / m_ammo_max3) : 0; + cur_weapon->secondary_bank_ammo[3] = m_ammo_max4 ? (m_ammo4 * 100 / m_ammo_max4) : 0; + if (m_multi_edit) { + if (!strlen(a1)) + cur_weapon->secondary_bank_ammo[0] = BLANK_FIELD; + if (!strlen(a2)) + cur_weapon->secondary_bank_ammo[1] = BLANK_FIELD; + if (!strlen(a3)) + cur_weapon->secondary_bank_ammo[2] = BLANK_FIELD; + if (!strlen(a4)) + cur_weapon->secondary_bank_ammo[3] = BLANK_FIELD; + } + } + + m_gun1 = m_gun2 = m_gun3 = m_missile1 = m_missile2 = m_missile3 = m_missile4 = -1; + m_ammo1 = m_ammo2 = m_ammo3 = m_ammo4 = BLANK_FIELD; + m_ammo_max1 = m_ammo_max2 = m_ammo_max3 = m_ammo_max4 = 0; + if (m_cur_item < 0) { + m_last_item = m_cur_item; + GetDlgItem(IDC_GUN1)->EnableWindow(FALSE); + GetDlgItem(IDC_GUN2)->EnableWindow(FALSE); + GetDlgItem(IDC_GUN3)->EnableWindow(FALSE); + GetDlgItem(IDC_MISSILE1)->EnableWindow(FALSE); + GetDlgItem(IDC_MISSILE2)->EnableWindow(FALSE); + GetDlgItem(IDC_MISSILE3)->EnableWindow(FALSE); + GetDlgItem(IDC_MISSILE4)->EnableWindow(FALSE); + GetDlgItem(IDC_AMMO1)->EnableWindow(FALSE); + GetDlgItem(IDC_AMMO2)->EnableWindow(FALSE); + GetDlgItem(IDC_AMMO3)->EnableWindow(FALSE); + GetDlgItem(IDC_AMMO4)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN1)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN2)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN3)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN4)->EnableWindow(FALSE); + GetDlgItem(IDC_AI_CLASS)->EnableWindow(FALSE); + UpdateData(FALSE); + return; + } + + cur_weapon = (ship_weapon *) ((CListBox *) GetDlgItem(IDC_LIST))->GetItemDataPtr(m_cur_item); + GetDlgItem(IDC_AI_CLASS)->EnableWindow(TRUE); + m_ai_class = cur_weapon->ai_class; + + if (cur_weapon->num_primary_banks > 0) { + m_gun1 = cur_weapon->primary_bank_weapons[0] + 1; + GetDlgItem(IDC_GUN1)->EnableWindow(TRUE); + } else + GetDlgItem(IDC_GUN1)->EnableWindow(FALSE); + + if (cur_weapon->num_primary_banks > 1) { + m_gun2 = cur_weapon->primary_bank_weapons[1] + 1; + GetDlgItem(IDC_GUN2)->EnableWindow(TRUE); + } else + GetDlgItem(IDC_GUN2)->EnableWindow(FALSE); + + if (cur_weapon->num_primary_banks > 2) { + m_gun3 = cur_weapon->primary_bank_weapons[2] + 1; + GetDlgItem(IDC_GUN3)->EnableWindow(TRUE); + } else + GetDlgItem(IDC_GUN3)->EnableWindow(FALSE); + + if (cur_weapon->num_secondary_banks > 0) { + m_missile1 = cur_weapon->secondary_bank_weapons[0] + 1; + if (m_missile1 > 0) { + m_ammo_max1 = get_max_ammo_count_for_bank(m_ship_class, 0, m_missile1 - 1); + if (cur_weapon->secondary_bank_ammo[0] != BLANK_FIELD) + m_ammo1 = cur_weapon->secondary_bank_ammo[0] * m_ammo_max1 / 100; + m_missile1 -= First_secondary_index; + } + + GetDlgItem(IDC_MISSILE1)->EnableWindow(TRUE); + GetDlgItem(IDC_AMMO1)->EnableWindow(TRUE); + GetDlgItem(IDC_SPIN1)->EnableWindow(TRUE); + + } else { + GetDlgItem(IDC_MISSILE1)->EnableWindow(FALSE); + GetDlgItem(IDC_AMMO1)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN1)->EnableWindow(FALSE); + } + + if (cur_weapon->num_secondary_banks > 1) { + m_missile2 = cur_weapon->secondary_bank_weapons[1] + 1; + if (m_missile2 > 0) { + m_ammo_max2 = get_max_ammo_count_for_bank(m_ship_class, 1, m_missile2 - 1); + if (cur_weapon->secondary_bank_ammo[1] != BLANK_FIELD) + m_ammo2 = cur_weapon->secondary_bank_ammo[1] * m_ammo_max2 / 100; + m_missile2 -= First_secondary_index; + } + + GetDlgItem(IDC_MISSILE2)->EnableWindow(TRUE); + GetDlgItem(IDC_AMMO2)->EnableWindow(TRUE); + GetDlgItem(IDC_SPIN2)->EnableWindow(TRUE); + + } else { + GetDlgItem(IDC_MISSILE2)->EnableWindow(FALSE); + GetDlgItem(IDC_AMMO2)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN2)->EnableWindow(FALSE); + } + + if (cur_weapon->num_secondary_banks > 2) { + m_missile3 = cur_weapon->secondary_bank_weapons[2] + 1; + if (m_missile3 > 0) { + m_ammo_max3 = get_max_ammo_count_for_bank(m_ship_class, 2, m_missile3 - 1); + if (cur_weapon->secondary_bank_ammo[2] != BLANK_FIELD) + m_ammo3 = cur_weapon->secondary_bank_ammo[2] * m_ammo_max3 / 100; + m_missile3 -= First_secondary_index; + } + + GetDlgItem(IDC_MISSILE3)->EnableWindow(TRUE); + GetDlgItem(IDC_AMMO3)->EnableWindow(TRUE); + GetDlgItem(IDC_SPIN3)->EnableWindow(TRUE); + + } else { + GetDlgItem(IDC_MISSILE3)->EnableWindow(FALSE); + GetDlgItem(IDC_AMMO3)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN3)->EnableWindow(FALSE); + } + + if (cur_weapon->num_secondary_banks > 3) { + m_missile4 = cur_weapon->secondary_bank_weapons[3] + 1; + if (m_missile4 > 0) { + m_ammo_max4 = get_max_ammo_count_for_bank(m_ship_class, 3, m_missile4 - 1); + if (cur_weapon->secondary_bank_ammo[3] != BLANK_FIELD) + m_ammo4 = cur_weapon->secondary_bank_ammo[3] * m_ammo_max4 / 100; + m_missile4 -= First_secondary_index; + } + + GetDlgItem(IDC_MISSILE4)->EnableWindow(TRUE); + GetDlgItem(IDC_AMMO4)->EnableWindow(TRUE); + GetDlgItem(IDC_SPIN4)->EnableWindow(TRUE); + + } else { + GetDlgItem(IDC_MISSILE4)->EnableWindow(FALSE); + GetDlgItem(IDC_AMMO4)->EnableWindow(FALSE); + GetDlgItem(IDC_SPIN4)->EnableWindow(FALSE); + } + + m_last_item = m_cur_item; + UpdateData(FALSE); + if (m_multi_edit) { + if (m_ammo1 == BLANK_FIELD) + GetDlgItem(IDC_AMMO1)->SetWindowText(""); + if (m_ammo2 == BLANK_FIELD) + GetDlgItem(IDC_AMMO2)->SetWindowText(""); + if (m_ammo3 == BLANK_FIELD) + GetDlgItem(IDC_AMMO3)->SetWindowText(""); + if (m_ammo4 == BLANK_FIELD) + GetDlgItem(IDC_AMMO4)->SetWindowText(""); + } +} + +void WeaponEditorDlg::OnOK() +{ + UpdateData(TRUE); + UpdateData(TRUE); + change_selection(); + update_pilot(); + CDialog::OnOK(); +} + +void WeaponEditorDlg::OnCancel() +{ + UpdateData(TRUE); + UpdateData(TRUE); + change_selection(); + update_pilot(); + CDialog::OnCancel(); +} + +void WeaponEditorDlg::OnClose() +{ + UpdateData(TRUE); + UpdateData(TRUE); + change_selection(); + update_pilot(); + CDialog::OnClose(); +} + +void WeaponEditorDlg::update_pilot() +{ + int i; + object *ptr; + ship_weapon *weapon; + + if (m_multi_edit) { + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags & OF_MARKED)) { + weapon = &Ships[ptr->instance].weapons; + + if (pilot.ai_class >= 0) + weapon->ai_class = pilot.ai_class; + + for (i=0; iprimary_bank_weapons[i] = pilot.primary_bank_weapons[i]; + + for (i=0; isecondary_bank_weapons[i] = pilot.secondary_bank_weapons[i]; + if (pilot.secondary_bank_ammo[i] >= 0) + weapon->secondary_bank_ammo[i] = pilot.secondary_bank_ammo[i]; + } + } + + ptr = GET_NEXT(ptr); + } + } +} + +void WeaponEditorDlg::OnSelchangeMissile1() +{ + UpdateData(TRUE); + UpdateData(TRUE); + m_ammo_max1 = get_max_ammo_count_for_bank(m_ship_class, 0, m_missile1 + First_secondary_index - 1); + m_ammo1 = m_ammo_max1 ? (m_ammo_max1) : 0; + change_selection(); +} + +void WeaponEditorDlg::OnSelchangeMissile2() +{ + UpdateData(TRUE); + UpdateData(TRUE); + m_ammo_max2 = get_max_ammo_count_for_bank(m_ship_class, 0, m_missile2 + First_secondary_index - 1); + m_ammo2 = m_ammo_max2 ? (m_ammo_max2) : 0; + change_selection(); +} + +void WeaponEditorDlg::OnSelchangeMissile3() +{ + UpdateData(TRUE); + UpdateData(TRUE); + m_ammo_max3 = get_max_ammo_count_for_bank(m_ship_class, 0, m_missile3 + First_secondary_index - 1); + m_ammo3 = m_ammo_max3 ? (m_ammo_max3) : 0; + change_selection(); +} + +void WeaponEditorDlg::OnSelchangeMissile4() +{ + UpdateData(TRUE); + UpdateData(TRUE); + m_ammo_max4 = get_max_ammo_count_for_bank(m_ship_class, 0, m_missile4 + First_secondary_index - 1); + m_ammo4 = m_ammo_max4 ? (m_ammo_max4) : 0; + change_selection(); +} diff --git a/src/fred2/wing.cpp b/src/fred2/wing.cpp new file mode 100644 index 0000000..ffbb259 --- /dev/null +++ b/src/fred2/wing.cpp @@ -0,0 +1,758 @@ +/* + * $Logfile: /Freespace2/code/Fred2/wing.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Wing management functions for dealing with wing related operations + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 3 3/01/99 10:00a Dave + * Fxied several dogfight related stats bugs. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 59 2/26/98 4:59p Allender + * groundwork for team vs team briefings. Moved weaponry pool into the + * Team_data structure. Added team field into the p_info structure. + * Allow for mutliple structures in the briefing code. + * + * 58 1/06/98 2:27p Hoffoss + * Made wing deleting free up arrival and departure cue sexp trees, which + * wasn't happening before for some reason. + * + * 57 12/29/97 4:55p Johnson + * Added some fixes. + * + * 56 11/21/97 11:46a Johnson + * Changed code to delete reinforcement wings when the wing is deleted. + * + * 55 11/13/97 4:14p Allender + * automatic assignment of hotkeys for starting wings. Appripriate + * warnings when they are incorrectly used. hotkeys correctly assigned to + * ships/wing arriving after mission start + * + * 54 10/01/97 12:37p Hoffoss + * Changed Fred (and FreeSpace) to utilize alpha, beta and gamma as player + * starting wing names. + * + * 53 9/16/97 9:41p Hoffoss + * Changed Fred code around to stop using Parse_player structure for + * player information, and use actual ships instead. + * + * 52 9/11/97 4:04p Hoffoss + * Fixed bug in Fred: disband wing wasn't making sure the ship editor's + * data was synced. + * + * 51 9/06/97 2:13p Mike + * Replace support for TEAM_NEUTRAL + * + * 50 9/02/97 3:44p Johnson + * Fixed bugs with thresholds not being lowered. + * + * 49 9/02/97 3:24p Johnson + * Fixed bug: ships removed from wings will lower threshold if required. + * + * 48 8/30/97 9:52p Hoffoss + * Implemented arrival location, distance, and anchor in Fred. + * + * 47 8/25/97 5:58p Hoffoss + * Created menu items for keypress functions in Fred, and fixed bug this + * uncovered with wing_delete function. + * + * 46 8/16/97 6:44p Hoffoss + * Changes to allow any player to be in a wing. + * + * 45 8/16/97 4:51p Hoffoss + * Fixed bugs with wing deletion and removing ships from a wing. + * + * 44 8/15/97 1:07a Hoffoss + * Changed code to disallow some ship types from being in a wing, and + * disallowing some ship types from being able to have initial orders. + * + * 43 8/12/97 1:55a Hoffoss + * Made extensive changes to object reference checking and handling for + * object deletion call. + * + * 42 8/10/97 4:24p Hoffoss + * Added warning when creating a wing if wing is mixed. + * + * 41 8/08/97 1:31p Hoffoss + * Added syncronization protection to cur_object_index changes. + * + * 40 8/08/97 10:07a Hoffoss + * Fixed bug where ship references aren't updated when wing is created + * (i.e. ships being renamed, but not in the references too). + * + * 39 8/01/97 3:24p Hoffoss + * Fixed bug where when player is removed from a wing, the ship associated + * with the player doesn't get it's wingnum variable updated. + * + * 38 7/09/97 2:38p Allender + * organized ship/wing editor dialogs. Added protect ship and ignore + * count checkboxes to those dialogs. Changed flag code for + * parse_objects. Added unprotect sexpressions + * + * 37 6/30/97 11:36a Hoffoss + * Fixed bug with wing reforming. + * + * 36 6/18/97 2:36p Hoffoss + * Wing ship numbering starts at 1 instead of 0, and changed form wing to + * allow reforming a wing. + * + * 35 6/04/97 12:00a Allender + * make wing goal priorities default to -1 so that their priorities get + * set more appropriatly in the initial order dialog + * + * 34 5/14/97 4:08p Lawrance + * removing my_index from game arrays + * + * 33 5/08/97 10:54a Hoffoss + * Fixed bug in reforming wings screwing up ship names. + * + * 32 4/30/97 9:17a Hoffoss + * Hotkey for wing set to none when new wing created. + * + * 31 3/28/97 3:19p Hoffoss + * Fixed bug. + * + * 30 3/20/97 3:55p Hoffoss + * Major changes to how dialog boxes initialize (load) and update (save) + * their internal data. This should simplify things and create less + * problems. + * + * 29 3/17/97 4:29p Hoffoss + * Automated player's wing flaging as a starting player wing. + * + * 28 3/12/97 12:40p Hoffoss + * Fixed bugs in wing object management functions, several small additions + * and rearrangements. + * + * 27 3/04/97 6:27p Hoffoss + * Changes to Fred to handle new wing structure. + * + * 26 2/28/97 11:31a Hoffoss + * Implemented modeless dialog saving and restoring, and changed some + * variables names. + * + * 25 2/27/97 3:16p Allender + * major wing structure enhancement. simplified wing code. All around + * better wing support + * + * 24 2/27/97 2:31p Hoffoss + * Changed wing create code to bash ship's cues to false. + * + * 23 2/24/97 5:38p Hoffoss + * Added dialog box to name a wing at creation, code to change ship names + * to match wing name, and code to maintain these ship names. + * + * 22 2/20/97 4:03p Hoffoss + * Several ToDo items: new reinforcement clears arrival cue, reinforcement + * control from ship and wing dialogs, show grid toggle. + * + * 21 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "fred.h" +#include "freddoc.h" +#include "fredview.h" +#include "3d.h" +#include "physics.h" +#include "object.h" +#include "editor.h" +#include "ailocal.h" +#include "ship.h" +#include "vecmat.h" +#include "management.h" +#include "linklist.h" +#include "mainfrm.h" +#include "wing.h" +#include "createwingdlg.h" +#include "management.h" + +#define MULTI_WING 999999 + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +int already_deleting_wing = 0; + +void remove_player_from_wing(int player, int min = 1); + +// Finds a free wing slot (i.e. unused) +int find_free_wing() +{ + int i; + + for (i=0; i= 0 && Wings[wing].special_ship < Wings[wing].wave_count); + set_cur_object_index(wing_objects[wing][Wings[wing].special_ship]); + for (i=0; i= Wings[wing].wave_count)) + Wings[wing].threshold = Wings[wing].wave_count - 1; + } + + Ships[ship].wingnum = -1; + } + + set_modified(); + // reset ship name to non-wing default ship name + sprintf(buf, "%s %d", Ship_info[Ships[ship].ship_info_index].name, ship); + rename_ship(ship, buf); +} + +// Takes a player out of a wing, deleting wing if that was the only ship in it. +void remove_player_from_wing(int player, int min) +{ + remove_ship_from_wing(player, min); +} + +// Forms a wing from marked objects +int create_wing() +{ + char msg[1024]; + int i, ship, wing = -1, waypoints = 0, count = 0, illegal_ships = 0; + int friendly, hostile, leader; + object *ptr; + create_wing_dlg dlg; + + if (!query_valid_object()) + return -1; + + leader = cur_object_index; + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if ((ptr->type == OBJ_SHIP) && (ptr->flags & OF_MARKED)) { + count++; + i = -1; + switch (ptr->type) { + case OBJ_SHIP: + case OBJ_START: + i = Ships[ptr->instance].wingnum; + break; + } + + if (i >= 0) { + if (wing < 0) + wing = i; + else if (wing != i) + wing = MULTI_WING; + } + } + + ptr = GET_NEXT(ptr); + } + + if (count > MAX_SHIPS_PER_WING) { + sprintf(msg, "You have too many ships marked!\n" + "A wing is limited to %d ships total", MAX_SHIPS_PER_WING); + + Fred_main_wnd->MessageBox(msg, "Error", MB_ICONEXCLAMATION); + return -1; + } + + if ((wing >= 0) && (wing != MULTI_WING)) { + sprintf(msg, "Do you want to reform wing \"%s\"?", Wings[wing].name); + i = Fred_main_wnd->MessageBox(msg, "Query", MB_YESNOCANCEL); + if (i == IDCANCEL) + return -1; + + else if (i == IDNO) + wing = -1; + + else { // must be IDYES + for (i=Wings[wing].wave_count-1; i>=0; i--) { + ptr = &Objects[wing_objects[wing][i]]; + switch (ptr->type) { + case OBJ_SHIP: + remove_ship_from_wing(ptr->instance, 0); + break; + + case OBJ_START: + remove_player_from_wing(ptr->instance, 0); + break; + + default: + Int3(); // shouldn't be in a wing! + } + } + + Assert(!Wings[wing].wave_count); + num_wings--; + } + + } else + wing = -1; + + if (wing < 0) { + wing = find_free_wing(); + Wings[wing].num_waves = 1; + Wings[wing].threshold = 0; + Wings[wing].arrival_location = Wings[wing].departure_location = 0; + Wings[wing].arrival_distance = 0; + Wings[wing].arrival_anchor = -1; + Wings[wing].arrival_cue = Locked_sexp_true; + Wings[wing].departure_cue = Locked_sexp_false; + Wings[wing].hotkey = -1; + Wings[wing].flags = 0; + + for (i=0; iMessageBox("Too many wings, can't create more!", + "Error", MB_ICONEXCLAMATION); + + return -1; + } + + if (dlg.DoModal() == IDCANCEL) + return -1; + + string_copy(Wings[wing].name, dlg.m_name, NAME_LENGTH - 1); + } + + set_cur_indices(-1); + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->flags & OF_MARKED) { +// if ((ptr->type == OBJ_START) && (ptr->instance)) { +// starts++; +// unmark_object(OBJ_INDEX(ptr)); + +// } else if (ptr->type == OBJ_WAYPOINT) { + if (ptr->type == OBJ_WAYPOINT) { + waypoints++; + unmark_object(OBJ_INDEX(ptr)); + + } else if (ptr->type == OBJ_SHIP) { + switch (ship_query_general_type(ptr->instance)) + { + case SHIP_TYPE_FIGHTER_BOMBER: + case SHIP_TYPE_CRUISER: + case SHIP_TYPE_AWACS: + case SHIP_TYPE_GAS_MINER: + case SHIP_TYPE_CORVETTE: + case SHIP_TYPE_FREIGHTER: + case SHIP_TYPE_CAPITAL: + case SHIP_TYPE_TRANSPORT: + case SHIP_TYPE_SUPERCAP: + break; + + default: + illegal_ships++; + unmark_object(OBJ_INDEX(ptr)); + } + } + } + + ptr = GET_NEXT(ptr); + } + + // if this wing is a player starting wing, automatically set the hotkey for this wing + for (i = 0; i < MAX_STARTING_WINGS; i++ ) { + if ( !stricmp(Wings[wing].name, Starting_wing_names[i]) ) { + Wings[wing].hotkey = i; + break; + } + } + + count = friendly = hostile = 0; + if (Objects[Ships[Player_start_shipnum].objnum].flags & OF_MARKED) + count = 1; + + ptr = GET_FIRST(&obj_used_list); + while (ptr != END_OF_LIST(&obj_used_list)) { + if (ptr->flags & OF_MARKED) { + if ((ptr->type == OBJ_START) && (ptr->instance == Player_start_shipnum)) + i = 0; // player 1 start always goes to front of the wing + else + i = count++; + + Assert((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)); + ship = ptr->instance; + if (Ships[ship].wingnum != -1) { + if (ptr->type == OBJ_SHIP) + remove_ship_from_wing(ship); + else + remove_player_from_wing(ptr->instance); + } + + sprintf(msg, "%s %d", Wings[wing].name, i + 1); + rename_ship(ship, msg); + if (Ships[ship].team == TEAM_FRIENDLY) + friendly = 1; + else if ((Ships[ship].team == TEAM_HOSTILE) || (Ships[ship].team == TEAM_NEUTRAL)) + hostile = 1; + + Wings[wing].ship_index[i] = ship; + Ships[ship].wingnum = wing; + if (Ships[ship].arrival_cue >= 0) + free_sexp2(Ships[ship].arrival_cue); + + Ships[ship].arrival_cue = Locked_sexp_false; + if (Ships[ship].departure_cue >= 0) + free_sexp2(Ships[ship].departure_cue); + + Ships[ship].departure_cue = Locked_sexp_false; + + wing_objects[wing][i] = OBJ_INDEX(ptr); + if (OBJ_INDEX(ptr) == leader) + Wings[wing].special_ship = i; + } + + ptr = GET_NEXT(ptr); + } + + if (!count) // this should never happen, so if it does, needs to be fixed now. + Error(LOCATION, "No valid ships were selected to form wing from"); + + Wings[wing].wave_count = count; + num_wings++; + +// if (starts) +// Fred_main_wnd->MessageBox("Multi-player starting points can't be part of a wing!\n" +// "All marked multi-player starting points were ignored", +// "Error", MB_ICONEXCLAMATION); + + if (waypoints) + Fred_main_wnd->MessageBox("Waypoints can't be part of a wing!\n" + "All marked waypoints were ignored", + "Error", MB_ICONEXCLAMATION); + + if (illegal_ships) + Fred_main_wnd->MessageBox("Some ship types aren't allowed to be in a wing.\n" + "All marked ships of these types were ignored", + "Error", MB_ICONEXCLAMATION); + + if (friendly && hostile) + Fred_main_wnd->MessageBox("Both hostile and friendly ships in same wing", "Warning"); + + mark_wing(wing); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////// +// Old stuff down there.. + +#define MAX_WING_VECTORS 8 // 8 vectors per wing formation. Possible to have more + // than 8 ships. A vector is where a ship is located relative + // to the leader. Other ships can be located at the same + // vector relative to another member. So wing formation + // size is not limited by this constant. +#define MAX_WING_FORMATIONS 8 // 8 different kinds of wing formations + +typedef struct formation { + int num_vectors; + vector offsets[MAX_WING_VECTORS]; +} formation; + +formation Wing_formations[MAX_WING_FORMATIONS]; + +//wing Wings[MAX_WINGS]; + +int Wings_initialized = 0; + +void initialize_wings(void) +{ + if (Wings_initialized) + return; + + Wings_initialized = 1; + + Wing_formations[0].num_vectors = 2; + + Wing_formations[0].offsets[0].x = -5.0f; + Wing_formations[0].offsets[0].y = +1.0f; + Wing_formations[0].offsets[0].z = -5.0f; + + Wing_formations[0].offsets[1].x = +5.0f; + Wing_formations[0].offsets[1].y = +1.0f; + Wing_formations[0].offsets[1].z = -5.0f; +} + +void create_wings_from_objects(void) +{ + int i; + + for (i=0; i= 0) && (wingnum < MAX_WINGS)); + Assert(Wings[wingnum].wave_count < MAX_SHIPS_PER_WING); +// JEH strcpy(Wings[wingnum].ship_names[Wings[wingnum].count++], i; + } + +} + +int get_free_objnum(void) +{ + int i; + + for (i=1; i= 0) && (wing_type < MAX_WING_FORMATIONS)); + Assert(Wing_formations[wing_type].num_vectors > 0); + Assert(Wing_formations[wing_type].num_vectors < MAX_WING_VECTORS); + + Assert(Objects[leader_index].type != OBJ_NONE); + Assert(max_size < MAX_SHIPS_PER_WING); + + num_placed = 0; + wingp = &Wing_formations[wing_type]; + num_vectors = wingp->num_vectors; + cur_vec_index = 0; + parent = lobjp; + vm_copy_transpose_matrix(&rotmat, &lobjp->orient); + + while (num_placed < max_size) { + vector wvec; + int curobj; + + if (*wingmen == -1) { + if (!fill_flag) + break; + else { + curobj = get_free_objnum(); + Assert(curobj != -1); + Objects[curobj].type = lobjp->type; + Assert(Wings[cur_wing].wave_count < MAX_SHIPS_PER_WING); +// JEH Wings[cur_wing].ship_list[Wings[cur_wing].count] = curobj; + Wings[cur_wing].wave_count++; + } + } else + curobj = *wingmen++; + + Objects[curobj] = *lobjp; + vm_vec_rotate(&wvec, &wingp->offsets[cur_vec_index], &rotmat); + cur_vec_index = (cur_vec_index + 1) % num_vectors; + + if (num_placed < num_vectors) + parent = lobjp; + else + parent = &Objects[wing_list[num_placed - num_vectors]]; + + wing_list[num_placed] = curobj; + + vm_vec_add(&Objects[curobj].pos, &parent->pos, &wvec); + + num_placed++; + } + +} + +// Create a wing. +// cur_object_index becomes the leader. +// If count == -1, then all objects of wing cur_wing get added to the wing. +// If count == +n, then n objects are added to the wing. +void test_form_wing(int count) +{ + int i, wingmen[MAX_OBJECTS]; + int j, fill_flag; + + j = 0; + + Assert(cur_object_index != -1); + Assert(Objects[cur_object_index].type != OBJ_NONE); + Assert(get_wingnum(cur_object_index) != -1); + get_wingnum(cur_object_index); + + wingmen[0] = -1; + + for (i=1; i 0) { + fill_flag = 1; + j += count; + } + + set_wingnum(cur_object_index, cur_wing); + create_wing(0, cur_object_index, wingmen, j, fill_flag); +} \ No newline at end of file diff --git a/src/fred2/wing_editor.cpp b/src/fred2/wing_editor.cpp new file mode 100644 index 0000000..3b80448 --- /dev/null +++ b/src/fred2/wing_editor.cpp @@ -0,0 +1,1374 @@ +/* + * $Logfile: /Freespace2/code/fred2/wing_editor.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Wing editor dialog box handler code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 8/16/99 10:52p Andsager + * Allow closer ship positioning for NEAR_SHIP ship and wing arrivals. + * + * 2 10/07/98 6:28p Dave + * Initial checkin. Renamed all relevant stuff to be Fred2 instead of + * Fred. Globalized mission and campaign file extensions. Removed Silent + * Threat specific code. + * + * 1 10/07/98 3:02p Dave + * + * 1 10/07/98 3:00p Dave + * + * 83 9/14/98 3:31p Allender + * don't allow alpha, beta, and gamma to get > 1 wave when editing a + * multiplayer missions + * + * 82 6/17/98 4:50p Hoffoss + * Added error checking for arrival delays used on wing player is in. + * + * 81 5/22/98 10:10a Hoffoss + * Fixed bug with hide cue button not working correctly. + * + * 80 3/24/98 12:30p Allender + * arrival/departure target boxes were getting initialized incorrectly + * + * 79 3/21/98 7:36p Lawrance + * Move jump nodes to own lib. + * + * 78 3/16/98 8:27p Allender + * Fred support for two new AI flags -- kamikaze and no dynamic goals. + * + * 77 3/10/98 6:11p Hoffoss + * Added jump node renaming abilities to Fred. + * + * 76 2/23/98 9:48p Allender + * added no arrival/departure warps to wings + * + * 75 12/31/97 3:56p Hoffoss + * Forced alpha wing to always have a true arrival cue. + * + * 74 12/29/97 4:55p Johnson + * Added some fixes. + * + * 73 12/18/97 10:40a Hoffoss + * Fixed bug with a few of the checkboxes not initializing properly. + * + * 72 11/25/97 10:03a Allender + * added no arrival message checkbox to wing editor + * + * 71 11/25/97 9:42a Hoffoss + * Removed starting wing checkbox from wing editor. + * + * 70 11/13/97 4:14p Allender + * automatic assignment of hotkeys for starting wings. Appripriate + * warnings when they are incorrectly used. hotkeys correctly assigned to + * ships/wing arriving after mission start + * + * 69 11/11/97 2:13p Allender + * docking bay support for Fred and Freespace. Added hook to ai code for + * arrival/departure from dock bays. Fred support now sufficient. + * + * 68 11/10/97 10:13p Allender + * added departure anchor to Fred and Freespace in preparation for using + * docking bays. Functional in Fred, not in FreeSpace. + * + * 67 10/28/97 3:33p Hoffoss + * Fixed bug where <1 num_waves was being allowed to be entered into Fred. + * + * 66 10/14/97 5:33p Hoffoss + * Added Fred support (and fsm support) for the no_arrival_music flags in + * ships and wings. + * + * 65 10/01/97 12:37p Hoffoss + * Changed Fred (and FreeSpace) to utilize alpha, beta and gamma as player + * starting wing names. + * + * 64 9/04/97 5:35p Hoffoss + * Fixed arrival distance stuff. + * + * 63 9/04/97 5:04p Johnson + * Fixed bug with arrival target distance checking. + * + * 62 9/04/97 4:30p Hoffoss + * Removed sexp tree info from grayed trees. + * + * 61 8/30/97 9:52p Hoffoss + * Implemented arrival location, distance, and anchor in Fred. + * + * 60 8/22/97 4:16p Hoffoss + * added support for arrival and departure info in ship editor using + * wing's info if editing marked ships in a wing instead of using ship's. + * + * 59 8/21/97 3:20p Duncan + * Fixed bug in wing renaming when a player is in the wing. + * + * 58 8/19/97 1:44p Hoffoss + * Fixed bug with updating too quickly (i.e. via prev and next buttons). + * + * 57 8/15/97 5:14p Hoffoss + * Completely changed around how initial orders dialog worked. It's + * pretty awesome now. + * + * 56 8/15/97 11:24a Hoffoss + * Changed order of events. + * + * 55 8/13/97 11:31p Hoffoss + * Added bound checking on min/max wave delay. + * + * 54 8/13/97 11:22p Hoffoss + * Implemented wave delay min and max in Fred. + * + * 53 8/12/97 7:17p Hoffoss + * Added previous button to ship and wing editors. + * + * 52 8/12/97 6:32p Hoffoss + * Added code to allow hiding of arrival and departure cues in editors. + * + * 51 8/12/97 3:33p Hoffoss + * Fixed the "press cancel to go to reference" code to work properly. + * + * 50 8/12/97 1:55a Hoffoss + * Made extensive changes to object reference checking and handling for + * object deletion call. + * + * 49 8/10/97 4:22p Hoffoss + * Made main display update when ships or wings are renamed. + * + * 48 8/08/97 10:00a Hoffoss + * Added protection from threshold being equal or higher than the number + * of ships in a wing. + * + * 47 8/01/97 2:45p Hoffoss + * Fixed bug with no new item in MFC CTreeCtrl selection changed message. + * Throught it shouldn't happen, but Whiteside made it happen. + * + * 46 7/30/97 5:23p Hoffoss + * Removed Sexp tree verification code, since it duplicates normal sexp + * verification, and is just another set of code to keep maintained. + * + * 45 7/30/97 12:31p Hoffoss + * Made improvements to ship goals editor (initial orders) to disallow + * illegal orders. + * + * 44 7/28/97 2:28p Hoffoss + * Added bypasses to MFC integer validation routines. + * + * 43 7/25/97 2:40p Hoffoss + * Fixed bug in sexp tree selection updating handling. + * + * 42 7/24/97 4:44p Hoffoss + * Added sexp help to more dialogs, and changed made some changes to make + * it work correctly for ship and wing editors. + * + * 41 7/18/97 2:05p Hoffoss + * Fixed some bugs BoundsChecker turned up. + * + * 40 7/16/97 6:30p Hoffoss + * Added icons to sexp trees, mainly because I think they will be required + * for drag n drop. + * + * 39 7/09/97 2:38p Allender + * organized ship/wing editor dialogs. Added protect ship and ignore + * count checkboxes to those dialogs. Changed flag code for + * parse_objects. Added unprotect sexpressions + * + * 38 7/08/97 10:15a Allender + * making ships/wings reinforcements now do not set the arrival cue to + * false. A reinforcement may only be available after it's arrival cue is + * true + * + * 37 7/02/97 3:30p Hoffoss + * Put in code to validate and bash if necessary the wing wave threshold. + * + * 36 6/18/97 3:07p Hoffoss + * Wing ship names are 1 indexes instead of 0 indexed now. + * + * 35 6/05/97 6:10p Hoffoss + * Added features: Autosaving, object hiding. Also fixed some minor bugs. + * + * 34 5/30/97 11:33a Allender + * more hotkey combo box stuff + * + * 33 5/23/97 1:53p Hoffoss + * Fixed problems with modeless dialog updating. It won't get caught in + * an infinate loop anymore, but still gives an error warning 3 times when + * using cancel and trying to switch window focus to main window. Don't + * know if I can fix that, but it's not too critical right now. + * + * 32 5/01/97 4:15p Hoffoss + * Fixed bugs. + * + * 31 4/28/97 2:37p Hoffoss + * Added hotkey editing to Fred for ships and wings. + * + * 30 4/23/97 11:55a Hoffoss + * Fixed many bugs uncovered while trying to create Mission 6. + * + * 29 4/07/97 1:53p Hoffoss + * Fixed a few bugs, and added sexp chain duplicating for object + * duplicating. + * + * 28 4/03/97 11:35a Hoffoss + * Fixed bugs: viewpoint didn't reset, initial orders not updated when + * referenced ship is renamed or deleted. + * + * 27 4/01/97 5:15p Hoffoss + * Fixed errors in max length checks, renaming a wing now renames the + * ships in the wing as well, as it should. + * + * 26 3/20/97 3:55p Hoffoss + * Major changes to how dialog boxes initialize (load) and update (save) + * their internal data. This should simplify things and create less + * problems. + * + * 25 3/17/97 4:29p Hoffoss + * Automated player's wing flaging as a starting player wing. + * + * 24 3/05/97 10:54a Hoffoss + * removed special arrival/departure cue token usage in wing editor. + * + * 23 2/28/97 11:31a Hoffoss + * Implemented modeless dialog saving and restoring, and changed some + * variables names. + * + * 22 2/27/97 3:16p Allender + * major wing structure enhancement. simplified wing code. All around + * better wing support + * + * 21 2/24/97 5:38p Hoffoss + * Added dialog box to name a wing at creation, code to change ship names + * to match wing name, and code to maintain these ship names. + * + * 20 2/21/97 5:34p Hoffoss + * Added extensive modification detection and fixed a bug in initial + * orders editor. + * + * 19 2/20/97 4:03p Hoffoss + * Several ToDo items: new reinforcement clears arrival cue, reinforcement + * control from ship and wing dialogs, show grid toggle. + * + * 18 2/17/97 5:28p Hoffoss + * Checked RCS headers, added them were missing, changing description to + * something better, etc where needed. + * + * $NoKeywords: $ + */ + +#include "stdafx.h" +#include "mainfrm.h" +#include "fred.h" +#include "freddoc.h" +#include "management.h" +#include "wing.h" +#include "linklist.h" +#include "aigoals.h" +#include "fredview.h" +#include "starfield.h" +#include "jumpnode.h" + +#define ID_WING_MENU 9000 + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// wing_editor dialog + +wing_editor::wing_editor(CWnd* pParent /*=NULL*/) + : CDialog(wing_editor::IDD, pParent) +{ + //{{AFX_DATA_INIT(wing_editor) + m_wing_name = _T(""); + m_special_ship = -1; + m_waves = 0; + m_threshold = 0; + m_arrival_location = -1; + m_departure_location = -1; + m_arrival_delay = 0; + m_departure_delay = 0; + m_reinforcement = FALSE; + m_hotkey = -1; + m_ignore_count = FALSE; + m_arrival_delay_max = 0; + m_arrival_delay_min = 0; + m_arrival_dist = 0; + m_arrival_target = -1; + m_no_arrival_music = FALSE; + m_departure_target = -1; + m_no_arrival_message = FALSE; + m_no_arrival_warp = FALSE; + m_no_departure_warp = FALSE; + m_no_dynamic = FALSE; + //}}AFX_DATA_INIT + modified = 0; + select_sexp_node = -1; + bypass_errors = 0; +} + +void wing_editor::DoDataExchange(CDataExchange* pDX) +{ + CString str; + + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(wing_editor) + DDX_Control(pDX, IDC_DEPARTURE_DELAY_SPIN, m_departure_delay_spin); + DDX_Control(pDX, IDC_ARRIVAL_DELAY_SPIN, m_arrival_delay_spin); + DDX_Control(pDX, IDC_DEPARTURE_TREE, m_departure_tree); + DDX_Control(pDX, IDC_ARRIVAL_TREE, m_arrival_tree); + DDX_Control(pDX, IDC_SPIN_WAVE_THRESHOLD, m_threshold_spin); + DDX_Control(pDX, IDC_SPIN_WAVES, m_waves_spin); + DDX_Text(pDX, IDC_WING_NAME, m_wing_name); + DDX_CBIndex(pDX, IDC_WING_SPECIAL_SHIP, m_special_ship); + DDX_CBIndex(pDX, IDC_ARRIVAL_LOCATION, m_arrival_location); + DDX_CBIndex(pDX, IDC_DEPARTURE_LOCATION, m_departure_location); + DDX_Check(pDX, IDC_REINFORCEMENT, m_reinforcement); + DDX_CBIndex(pDX, IDC_HOTKEY, m_hotkey); + DDX_Check(pDX, IDC_IGNORE_COUNT, m_ignore_count); + DDX_Text(pDX, IDC_ARRIVAL_DISTANCE, m_arrival_dist); + DDX_CBIndex(pDX, IDC_ARRIVAL_TARGET, m_arrival_target); + DDX_Check(pDX, IDC_NO_ARRIVAL_MUSIC, m_no_arrival_music); + DDX_CBIndex(pDX, IDC_DEPARTURE_TARGET, m_departure_target); + DDX_Check(pDX, IDC_NO_ARRIVAL_MESSAGE, m_no_arrival_message); + DDX_Check(pDX, IDC_NO_ARRIVAL_WARP, m_no_arrival_warp); + DDX_Check(pDX, IDC_NO_DEPARTURE_WARP, m_no_departure_warp); + DDX_Check(pDX, IDC_NO_DYNAMIC, m_no_dynamic); + //}}AFX_DATA_MAP + + if (pDX->m_bSaveAndValidate) { // get dialog control values + GetDlgItem(IDC_ARRIVAL_DELAY)->GetWindowText(str); + m_arrival_delay = atoi(str); + if (m_arrival_delay < 0) + m_arrival_delay = 0; + + GetDlgItem(IDC_DEPARTURE_DELAY)->GetWindowText(str); + m_departure_delay = atoi(str); + if (m_departure_delay < 0) + m_departure_delay = 0; + + GetDlgItem(IDC_WING_WAVES)->GetWindowText(str); + m_waves = atoi(str); + if (m_waves < 0) + m_waves = 0; + + GetDlgItem(IDC_WING_WAVE_THRESHOLD)->GetWindowText(str); + m_threshold = atoi(str); + if (m_threshold < 0) + m_threshold = 0; + + GetDlgItem(IDC_ARRIVAL_DELAY_MIN)->GetWindowText(str); + m_arrival_delay_min = atoi(str); + if (m_arrival_delay_min < 0) + m_arrival_delay_min = 0; + + GetDlgItem(IDC_ARRIVAL_DELAY_MAX)->GetWindowText(str); + m_arrival_delay_max = atoi(str); + if (m_arrival_delay_max < 0) + m_arrival_delay_max = 0; + + } else { + DDX_Text(pDX, IDC_ARRIVAL_DELAY, m_arrival_delay); + DDX_Text(pDX, IDC_DEPARTURE_DELAY, m_departure_delay); + DDX_Text(pDX, IDC_WING_WAVES, m_waves); + DDX_Text(pDX, IDC_WING_WAVE_THRESHOLD, m_threshold); + DDX_Text(pDX, IDC_ARRIVAL_DELAY_MIN, m_arrival_delay_min); + DDX_Text(pDX, IDC_ARRIVAL_DELAY_MAX, m_arrival_delay_max); + } +} + +BEGIN_MESSAGE_MAP(wing_editor, CDialog) + //{{AFX_MSG_MAP(wing_editor) + ON_WM_INITMENU() + ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_WAVES, OnDeltaposSpinWaves) + ON_NOTIFY(NM_RCLICK, IDC_ARRIVAL_TREE, OnRclickArrivalTree) + ON_NOTIFY(NM_RCLICK, IDC_DEPARTURE_TREE, OnRclickDepartureTree) + ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_ARRIVAL_TREE, OnBeginlabeleditArrivalTree) + ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_DEPARTURE_TREE, OnBeginlabeleditDepartureTree) + ON_NOTIFY(TVN_ENDLABELEDIT, IDC_ARRIVAL_TREE, OnEndlabeleditArrivalTree) + ON_NOTIFY(TVN_ENDLABELEDIT, IDC_DEPARTURE_TREE, OnEndlabeleditDepartureTree) + ON_BN_CLICKED(IDC_DELETE_WING, OnDeleteWing) + ON_BN_CLICKED(IDC_DISBAND_WING, OnDisbandWing) + ON_WM_CLOSE() + ON_BN_CLICKED(IDC_GOALS2, OnGoals2) + ON_BN_CLICKED(IDC_REINFORCEMENT, OnReinforcement) + ON_BN_CLICKED(IDC_NEXT, OnNext) + ON_NOTIFY(TVN_SELCHANGED, IDC_ARRIVAL_TREE, OnSelchangedArrivalTree) + ON_NOTIFY(TVN_SELCHANGED, IDC_DEPARTURE_TREE, OnSelchangedDepartureTree) + ON_BN_CLICKED(IDC_HIDE_CUES, OnHideCues) + ON_BN_CLICKED(IDC_PREV, OnPrev) + ON_CBN_SELCHANGE(IDC_ARRIVAL_LOCATION, OnSelchangeArrivalLocation) + ON_CBN_SELCHANGE(IDC_DEPARTURE_LOCATION, OnSelchangeDepartureLocation) + ON_CBN_SELCHANGE(IDC_HOTKEY, OnSelchangeHotkey) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// wing_editor message handlers + +BOOL wing_editor::Create() +{ + BOOL r; + int i; + CComboBox *box; + + r = CDialog::Create(IDD, Fred_main_wnd); + box = (CComboBox *) GetDlgItem(IDC_ARRIVAL_LOCATION); + box->ResetContent(); + for (i=0; iAddString(Arrival_location_names[i]); + + box = (CComboBox *) GetDlgItem(IDC_DEPARTURE_LOCATION); + box->ResetContent(); + for (i=0; iAddString(Departure_location_names[i]); + + m_hotkey = 0; + m_waves_spin.SetRange(1, 99); + m_arrival_tree.link_modified(&modified); // provide way to indicate trees are modified in dialog + m_arrival_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX)); + m_departure_tree.link_modified(&modified); + m_departure_tree.setup(); + m_arrival_delay_spin.SetRange(0, 999); + m_departure_delay_spin.SetRange(0, 999); + + initialize_data(1); + return r; +} + +void wing_editor::OnInitMenu(CMenu* pMenu) +{ + CMenu *m; + + m = pMenu->GetSubMenu(0); + clear_menu(m); + generate_wing_popup_menu(m, ID_WING_MENU, MF_ENABLED); + if (cur_wing != -1) + m->CheckMenuItem(ID_WING_MENU + cur_wing, MF_BYCOMMAND | MF_CHECKED); + + CDialog::OnInitMenu(pMenu); +} + +void wing_editor::OnOK() +{ + HWND h; + CWnd *w; + + w = GetFocus(); + if (w) { + h = w->m_hWnd; + GetDlgItem(IDC_ARRIVAL_TREE)->SetFocus(); + ::SetFocus(h); + } +} + +void wing_editor::OnClose() +{ + if (verify() && (!bypass_errors)) { + SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + bypass_errors = 0; + return; + } + + if (update_data()) { + SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE); + bypass_errors = 0; + return; + } + + SetWindowPos(Fred_main_wnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); + Fred_main_wnd->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); +} + +// initialize everything that update_data_safe() saves. +void wing_editor::initialize_data_safe(int full_update) +{ + int i, enable = TRUE, player_wing = 0, player_enabled = 1; + CComboBox *arrival_box, *departure_box; + + nprintf(("Fred routing", "Wing dialog load safe\n")); + if (!GetSafeHwnd()) + return; + + arrival_box = (CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET); + departure_box = (CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET); + + m_ignore_count = 0; + if (cur_wing < 0) { + m_special_ship = -1; + m_arrival_location = -1; + m_departure_location = -1; + m_arrival_delay = 0; + m_arrival_delay_min = 0; + m_arrival_delay_max = 0; + m_arrival_dist = 0; + m_arrival_target = -1; + m_departure_target = -1; + m_departure_delay = 0; + m_arrival_tree.clear_tree(); + m_departure_tree.clear_tree(); + m_arrival_tree.DeleteAllItems(); + m_departure_tree.DeleteAllItems(); + m_reinforcement = FALSE; + m_hotkey = 0; + m_ignore_count = 0; + m_no_arrival_music = 0; + m_no_arrival_message = 0; + m_no_arrival_warp = 0; + m_no_departure_warp = 0; + m_no_dynamic = 0; + player_enabled = enable = FALSE; + + } else { + CComboBox *ptr; + + if (cur_wing == wing_name_lookup("alpha", 1)) + player_enabled = 0; + + if ((The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) && (cur_wing == wing_name_lookup("zeta", 1))) + player_enabled = 0; + + // in multiplayer coop missions, alpha, beta, and gamma are both off limits. + if ( The_mission.game_type & MISSION_TYPE_MULTI_COOP ) { + if ( (cur_wing == wing_name_lookup("alpha", 1)) || (cur_wing == wing_name_lookup("beta", 1)) || (cur_wing == wing_name_lookup("gamma", 1)) ) { + player_enabled = 0; + } + } + + if ((Player_start_shipnum >= 0) && (Player_start_shipnum < MAX_SHIPS) && (Ships[Player_start_shipnum].objnum >= 0)) + if (Ships[Player_start_shipnum].wingnum == cur_wing) + player_wing = 1; + + m_special_ship = Wings[cur_wing].special_ship; + m_waves = Wings[cur_wing].num_waves; + m_threshold = Wings[cur_wing].threshold; + m_arrival_location = Wings[cur_wing].arrival_location; + m_departure_location = Wings[cur_wing].departure_location; + m_arrival_delay = Wings[cur_wing].arrival_delay; + m_arrival_delay_min = Wings[cur_wing].wave_delay_min; + m_arrival_delay_max = Wings[cur_wing].wave_delay_max; + m_arrival_dist = Wings[cur_wing].arrival_distance; + m_arrival_target = Wings[cur_wing].arrival_anchor; + m_departure_target = Wings[cur_wing].departure_anchor; + m_no_dynamic = (Wings[cur_wing].flags & WF_NO_DYNAMIC)?1:0; + + // Add the ships/special items to the combo box here before data is updated + if ( m_arrival_location == ARRIVE_FROM_DOCK_BAY ) { + management_add_ships_to_combo( arrival_box, SHIPS_2_COMBO_DOCKING_BAY_ONLY ); + } else { + management_add_ships_to_combo( arrival_box, SHIPS_2_COMBO_SPECIAL | SHIPS_2_COMBO_ALL_SHIPS ); + } + + if (m_arrival_target >= SPECIAL_ARRIVAL_ANCHORS_OFFSET) + m_arrival_target -= SPECIAL_ARRIVAL_ANCHORS_OFFSET; + else if (m_arrival_target >= 0) + m_arrival_target = arrival_box->FindStringExact(-1, Ships[m_arrival_target].ship_name); + + if ( m_departure_target >= 0 ) + m_departure_target = departure_box->FindStringExact(-1, Ships[m_departure_target].ship_name); + + // add the ships to the departure target combo box + if ( m_departure_location == DEPART_AT_DOCK_BAY ) { + management_add_ships_to_combo( departure_box, SHIPS_2_COMBO_DOCKING_BAY_ONLY ); + } else { + departure_box->ResetContent(); + } + + m_departure_delay = Wings[cur_wing].departure_delay; + if (player_wing) + m_arrival_tree.load_tree(Locked_sexp_true); + else + m_arrival_tree.load_tree(Wings[cur_wing].arrival_cue); + + m_departure_tree.load_tree(Wings[cur_wing].departure_cue, "false"); + m_hotkey = Wings[cur_wing].hotkey+1; + if (Wings[cur_wing].flags & WF_IGNORE_COUNT) + m_ignore_count = 1; + else + m_ignore_count = 0; + + if (Wings[cur_wing].flags & WF_NO_ARRIVAL_MUSIC) + m_no_arrival_music = 1; + else + m_no_arrival_music = 0; + + if ( Wings[cur_wing].flags & WF_NO_ARRIVAL_MESSAGE ) + m_no_arrival_message = 1; + else + m_no_arrival_message = 0; + + if ( Wings[cur_wing].flags & WF_NO_ARRIVAL_WARP ) + m_no_arrival_warp = 1; + else + m_no_arrival_warp = 0; + + if ( Wings[cur_wing].flags & WF_NO_DEPARTURE_WARP ) + m_no_departure_warp = 1; + else + m_no_departure_warp = 0; + + ptr = (CComboBox *) GetDlgItem(IDC_WING_SPECIAL_SHIP); + ptr->ResetContent(); + for (i=0; iAddString(Ships[Wings[cur_wing].ship_index[i]].ship_name); + + m_threshold_spin.SetRange(0, Wings[cur_wing].wave_count - 1); + for (i=0; iEnableWindow(enable); + GetDlgItem(IDC_WING_SPECIAL_SHIP)->EnableWindow(enable); + GetDlgItem(IDC_WING_WAVES)->EnableWindow(player_enabled); + GetDlgItem(IDC_WING_WAVE_THRESHOLD)->EnableWindow(player_enabled); + GetDlgItem(IDC_DISBAND_WING)->EnableWindow(enable); + GetDlgItem(IDC_SPIN_WAVES)->EnableWindow(player_enabled); + GetDlgItem(IDC_SPIN_WAVE_THRESHOLD)->EnableWindow(player_enabled); + GetDlgItem(IDC_ARRIVAL_LOCATION)->EnableWindow(enable); + + GetDlgItem(IDC_ARRIVAL_DELAY)->EnableWindow(player_enabled); + GetDlgItem(IDC_ARRIVAL_DELAY_MIN)->EnableWindow(player_enabled); + GetDlgItem(IDC_ARRIVAL_DELAY_MAX)->EnableWindow(player_enabled); + GetDlgItem(IDC_ARRIVAL_DELAY_SPIN)->EnableWindow(player_enabled); + if (m_arrival_location) { + GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(enable); + GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(enable); + } else { + GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(FALSE); + GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(FALSE); + } + GetDlgItem(IDC_NO_DYNAMIC)->EnableWindow(enable); + + if ( m_departure_location ) { + GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(enable); + } else { + GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(FALSE); + } + + if (player_wing) + GetDlgItem(IDC_ARRIVAL_TREE)->EnableWindow(0); + else + GetDlgItem(IDC_ARRIVAL_TREE)->EnableWindow(enable); + + GetDlgItem(IDC_DEPARTURE_LOCATION)->EnableWindow(enable); + GetDlgItem(IDC_DEPARTURE_DELAY)->EnableWindow(enable); + GetDlgItem(IDC_DEPARTURE_DELAY_SPIN)->EnableWindow(enable); + GetDlgItem(IDC_DEPARTURE_TREE)->EnableWindow(enable); + GetDlgItem(IDC_GOALS2)->EnableWindow(enable); + GetDlgItem(IDC_DELETE_WING)->EnableWindow(enable); + GetDlgItem(IDC_REINFORCEMENT)->EnableWindow(enable); + GetDlgItem(IDC_HOTKEY)->EnableWindow(enable); + GetDlgItem(IDC_IGNORE_COUNT)->EnableWindow(enable); + GetDlgItem(IDC_NO_ARRIVAL_MUSIC)->EnableWindow(enable); + GetDlgItem(IDC_NO_ARRIVAL_MESSAGE)->EnableWindow(enable); + GetDlgItem(IDC_NO_ARRIVAL_WARP)->EnableWindow(enable); + GetDlgItem(IDC_NO_DEPARTURE_WARP)->EnableWindow(enable); + + if (cur_wing >= 0) { + enable = TRUE; + + // check to see if the wing has a ship which is not a fighter/bomber type. If so, then disable + // the wing_waves and wing_threshold stuff + for (i = 0; i < Wings[cur_wing].wave_count; i++ ) { + int sflag; + + sflag = Ship_info[Ships[Wings[cur_wing].ship_index[i]].ship_info_index].flags; + if ( !(sflag & SIF_FIGHTER) && !(sflag & SIF_BOMBER) ) + enable = FALSE; + } + + } else + enable = FALSE; +} + +void wing_editor::initialize_data(int full_update) +{ + int i; + CWnd *w = NULL; + + nprintf(("Fred routing", "Wing dialog load\n")); + if (!GetSafeHwnd()) + return; + + m_arrival_tree.select_sexp_node = m_departure_tree.select_sexp_node = select_sexp_node; + select_sexp_node = -1; + if (cur_wing == -1) + m_wing_name = _T(""); + else + m_wing_name = _T(Wings[cur_wing].name); + + initialize_data_safe(full_update); + modified = 0; + if (w) + w -> SetFocus(); + + i = m_arrival_tree.select_sexp_node; + if (i != -1) { + w = GetDlgItem(IDC_ARRIVAL_TREE); + m_arrival_tree.hilite_item(i); + + } else { + i = m_departure_tree.select_sexp_node; + if (i != -1) { + w = GetDlgItem(IDC_DEPARTURE_TREE); + m_departure_tree.hilite_item(i); + } + } +} + +// update wing structure(s) with dialog data. The data is first checked for errors. If +// no errors occur, returns 0. If an error occurs, returns -1. If the update is bypassed, +// returns 1. Bypass is necessary to avoid an infinite loop, and it doesn't actually +// update the data. Bypass only occurs if bypass mode is active and we still get an error. +// Once the error no longer occurs, bypass mode is cleared and data is updated. +int wing_editor::update_data(int redraw) +{ + char *str, old_name[255], buf[512]; + int i, z; + object *ptr; + + nprintf(("Fred routing", "Wing dialog save\n")); + if (!GetSafeHwnd()) + return 0; + + UpdateData(TRUE); + UpdateData(TRUE); + + if (cur_wing >= 0) { + if (!strnicmp(m_wing_name, "player ", 7)) { + if (bypass_errors) + return 1; + + bypass_errors = 1; + z = MessageBox("Wing names can't start with the word 'player'\n" + "Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL); + + if (z == IDCANCEL) + return -1; + + m_wing_name = _T(Wings[cur_wing].name); + UpdateData(FALSE); + } + + for (i=0; itype == OBJ_SHIP) { + if (!stricmp(m_wing_name, Ships[ptr->instance].ship_name)) { + if (bypass_errors) + return 1; + + bypass_errors = 1; + z = MessageBox("This wing name is already being used by a ship\n" + "Press OK to restore old name", "Error", MB_ICONEXCLAMATION | MB_OKCANCEL); + + if (z == IDCANCEL) + return -1; + + m_wing_name = _T(Wings[cur_wing].name); + UpdateData(FALSE); + } + } + + ptr = GET_NEXT(ptr); + } + + for (i=0; i= Wings[cur_wing].wave_count) { + m_threshold = Wings[cur_wing].wave_count - 1; + if (!bypass_errors) + sprintf(buf, "Wave threshold is set too high. Value has been lowered to %d", (int) m_threshold); + + MessageBox(buf); + } + + if (m_threshold + Wings[cur_wing].wave_count > MAX_SHIPS_PER_WING) { + m_threshold = MAX_SHIPS_PER_WING - Wings[cur_wing].wave_count; + if (!bypass_errors) + sprintf(buf, "Wave threshold is set too high. Value has been lowered to %d", (int) m_threshold); + + MessageBox(buf); + } + + if (m_waves < 1) { + m_waves = 1; + if (!bypass_errors) + sprintf(buf, "Number of waves illegal. Has been set to 1.", (int) m_waves); + + MessageBox(buf); + } + + MODIFY(Wings[cur_wing].special_ship, m_special_ship); + MODIFY(Wings[cur_wing].num_waves, m_waves); + MODIFY(Wings[cur_wing].threshold, m_threshold); + MODIFY(Wings[cur_wing].arrival_location, m_arrival_location); + MODIFY(Wings[cur_wing].departure_location, m_departure_location); + MODIFY(Wings[cur_wing].arrival_delay, m_arrival_delay); + if (m_arrival_delay_min > m_arrival_delay_max) { + if (!bypass_errors) + sprintf(buf, "Arrival delay minimum greater than maximum. Value lowered to %d", m_arrival_delay_max); + + MessageBox(buf); + m_arrival_delay_min = m_arrival_delay_max; + } + + MODIFY(Wings[cur_wing].wave_delay_min, m_arrival_delay_min); + MODIFY(Wings[cur_wing].wave_delay_max, m_arrival_delay_max); + MODIFY(Wings[cur_wing].arrival_distance, m_arrival_dist); + if (m_arrival_target >= 0) { + i = ((CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET)) -> GetItemData(m_arrival_target); + MODIFY(Wings[cur_wing].arrival_anchor, i); + + // when arriving near or in front of a ship, be sure that we are far enough away from it!!! + if (((m_arrival_location != ARRIVE_AT_LOCATION) && (m_arrival_location != ARRIVE_FROM_DOCK_BAY)) && (i >= 0) && (i < SPECIAL_ARRIVAL_ANCHORS_OFFSET)) { + d = int(min(500, 2.0f * Objects[Ships[i].objnum].radius)); + if ((Wings[cur_wing].arrival_distance < d) && (Wings[cur_wing].arrival_distance > -d)) { + if (!bypass_errors) + sprintf(buf, "Ship must arrive at least %d meters away from target.\n" + "Value has been reset to this. Use with caution!\r\n" + "Reccomended distance is %d meters.\r\n", d, (int)(2.0f * Objects[Ships[i].objnum].radius) ); + + MessageBox(buf); + if (Wings[cur_wing].arrival_distance < 0) + Wings[cur_wing].arrival_distance = -d; + else + Wings[cur_wing].arrival_distance = d; + + m_arrival_dist = Wings[cur_wing].arrival_distance; + } + } + } + + i = ((CComboBox*)GetDlgItem(IDC_DEPARTURE_TARGET))->GetItemData(m_departure_target); + MODIFY(Wings[cur_wing].departure_anchor, i); + + MODIFY(Wings[cur_wing].departure_delay, m_departure_delay); + hotkey = m_hotkey - 1; + MODIFY(Wings[cur_wing].hotkey, hotkey); + if ( m_ignore_count ) { + if ( !(Wings[cur_wing].flags & WF_IGNORE_COUNT) ) + set_modified(); + Wings[cur_wing].flags |= WF_IGNORE_COUNT; + + } else { + if ( Wings[cur_wing].flags & WF_IGNORE_COUNT ) + set_modified(); + Wings[cur_wing].flags &= ~WF_IGNORE_COUNT; + } + + if ( m_no_arrival_music ) { + if ( !(Wings[cur_wing].flags & WF_NO_ARRIVAL_MUSIC) ) + set_modified(); + Wings[cur_wing].flags |= WF_NO_ARRIVAL_MUSIC; + + } else { + if ( Wings[cur_wing].flags & WF_NO_ARRIVAL_MUSIC ) + set_modified(); + Wings[cur_wing].flags &= ~WF_NO_ARRIVAL_MUSIC; + } + + // check the no message flag + if ( m_no_arrival_message ) { + if ( !(Wings[cur_wing].flags & WF_NO_ARRIVAL_MESSAGE) ) + set_modified(); + Wings[cur_wing].flags |= WF_NO_ARRIVAL_MESSAGE; + + } else { + if ( Wings[cur_wing].flags & WF_NO_ARRIVAL_MESSAGE ) + set_modified(); + Wings[cur_wing].flags &= ~WF_NO_ARRIVAL_MESSAGE; + } + + // set the no warp effect for wings flag + if ( m_no_arrival_warp ) { + if ( !(Wings[cur_wing].flags & WF_NO_ARRIVAL_WARP) ) + set_modified(); + Wings[cur_wing].flags |= WF_NO_ARRIVAL_WARP; + } else { + if ( Wings[cur_wing].flags & WF_NO_ARRIVAL_WARP ) + set_modified(); + Wings[cur_wing].flags &= ~WF_NO_ARRIVAL_WARP; + } + // set the no warp effect for wings flag + if ( m_no_departure_warp ) { + if ( !(Wings[cur_wing].flags & WF_NO_DEPARTURE_WARP) ) + set_modified(); + Wings[cur_wing].flags |= WF_NO_DEPARTURE_WARP; + } else { + if ( Wings[cur_wing].flags & WF_NO_DEPARTURE_WARP ) + set_modified(); + Wings[cur_wing].flags &= ~WF_NO_DEPARTURE_WARP; + } + + if ( m_no_dynamic ) { + if ( !(Wings[cur_wing].flags & WF_NO_DYNAMIC) ) + set_modified(); + Wings[cur_wing].flags |= WF_NO_DYNAMIC; + } else { + if ( Wings[cur_wing].flags & WF_NO_DYNAMIC ) + set_modified(); + Wings[cur_wing].flags &= ~WF_NO_DYNAMIC; + } + + if (Wings[cur_wing].arrival_cue >= 0) + free_sexp2(Wings[cur_wing].arrival_cue); + Wings[cur_wing].arrival_cue = m_arrival_tree.save_tree(); + + if (Wings[cur_wing].departure_cue >= 0) + free_sexp2(Wings[cur_wing].departure_cue); + Wings[cur_wing].departure_cue = m_departure_tree.save_tree(); + + if (modified) + set_modified(); +} + +BOOL wing_editor::OnCommand(WPARAM wParam, LPARAM lParam) +{ + int id, wing; + + id = LOWORD(wParam); + if (id >= ID_WING_MENU && id < ID_WING_MENU + MAX_WINGS) { + if (!update_data()) { + wing = id - ID_WING_MENU; + mark_wing(wing); + return 1; + } + } + + return CDialog::OnCommand(wParam, lParam); +} + +void wing_editor::OnDeltaposSpinWaves(NMHDR* pNMHDR, LRESULT* pResult) +{ + int new_pos; + NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR; + + new_pos = pNMUpDown->iPos + pNMUpDown->iDelta; + if (new_pos > 0 && new_pos < 100) { + *pResult = 0; + modified = 1; + + } else + *pResult = 1; +} + +void wing_editor::OnRclickArrivalTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + m_arrival_tree.right_clicked(); + *pResult = 0; +} + +void wing_editor::OnRclickDepartureTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + m_departure_tree.right_clicked(); + *pResult = 0; +} + +void wing_editor::OnBeginlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + if (m_arrival_tree.edit_label(pTVDispInfo->item.hItem) == 1) { + *pResult = 0; + modified = 1; + + } else + *pResult = 1; +} + +void wing_editor::OnBeginlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + if (m_departure_tree.edit_label(pTVDispInfo->item.hItem) == 1) { + *pResult = 0; + modified = 1; + + } else + *pResult = 1; +} + +void wing_editor::OnEndlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + *pResult = m_arrival_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText); +} + +void wing_editor::OnEndlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; + + *pResult = m_departure_tree.end_label_edit(pTVDispInfo->item.hItem, pTVDispInfo->item.pszText); +} + +int wing_editor::verify() +{ + nprintf(("Fred routing", "Wing dialog verify\n")); + if (!GetSafeHwnd() || !modified) + return 0; + + if (bypass_errors) + return 1; + + return 0; +} + +// delete wing and all ships that are part of the wing +void wing_editor::OnDeleteWing() +{ + modified = 0; // no need to run update checks, since wing will be gone shortly anyway. + delete_wing(cur_wing); +} + +// delete wing, but leave ships intact and wingless +void wing_editor::OnDisbandWing() +{ + modified = 0; // no need to run update checks, since wing will be gone shortly anyway. + remove_wing(cur_wing); +} + +void wing_editor::OnGoals2() +{ + ShipGoalsDlg dlg_goals; + + Assert(cur_wing != -1); + dlg_goals.self_wing = cur_wing; + dlg_goals.DoModal(); + if (query_initial_orders_conflict(cur_wing)) + MessageBox("One or more ships of this wing also has initial orders", + "Possible conflict"); +} + +void wing_editor::OnReinforcement() +{ + UpdateData(TRUE); + UpdateData(TRUE); + //if (m_reinforcement) + // m_arrival_tree.clear_tree("false"); +} + +void wing_editor::OnPrev() +{ + int wing, count = 0; + + if (!update_data() && num_wings) { + wing = cur_wing - 1; + if (wing < 0) + wing = MAX_WINGS - 1; + + while (!Wings[wing].wave_count) { + wing--; + if (count++ > MAX_WINGS) + return; + + if (wing < 0) + wing = MAX_WINGS - 1; + } + + mark_wing(wing); + Wing_editor_dialog.initialize_data(1); + Update_wing = 0; + } + + return; +} + +void wing_editor::OnNext() +{ + int wing, count = 0; + + if (!update_data() && num_wings) { + wing = cur_wing + 1; + if (wing >= MAX_WINGS) + wing = 0; + + while (!Wings[wing].wave_count) { + wing++; + if (count++ > MAX_WINGS) + return; + + if (wing >= MAX_WINGS) + wing = 0; + } + + mark_wing(wing); + Wing_editor_dialog.initialize_data(1); + Update_wing = 0; + } + + return; +} + +void wing_editor::OnSelchangedArrivalTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + HTREEITEM h; + + NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; + h = pNMTreeView->itemNew.hItem; + if (h) + m_arrival_tree.update_help(h); + + *pResult = 0; +} + +void wing_editor::OnSelchangedDepartureTree(NMHDR* pNMHDR, LRESULT* pResult) +{ + HTREEITEM h; + + NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; + h = pNMTreeView->itemNew.hItem; + if (h) + m_departure_tree.update_help(h); + + *pResult = 0; +} + +void wing_editor::calc_cue_height() +{ + CRect cue; + + GetDlgItem(IDC_CUE_FRAME)->GetWindowRect(cue); + cue_height = cue.bottom - cue.top + 10; + if (Show_sexp_help) + cue_height += SEXP_HELP_BOX_SIZE; + + if (Hide_wing_cues) { + ((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> SetCheck(1); + OnHideCues(); + } +} + +void wing_editor::show_hide_sexp_help() +{ + CRect rect; + + if (Show_sexp_help) + cue_height += SEXP_HELP_BOX_SIZE; + else + cue_height -= SEXP_HELP_BOX_SIZE; + + if (((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> GetCheck()) + return; + + GetWindowRect(rect); + if (Show_sexp_help) + rect.bottom += SEXP_HELP_BOX_SIZE; + else + rect.bottom -= SEXP_HELP_BOX_SIZE; + + MoveWindow(rect); +} + +void wing_editor::OnHideCues() +{ + CRect rect; + + GetWindowRect(rect); + if (((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> GetCheck()) { + rect.bottom -= cue_height; + Hide_wing_cues = 1; + + } else { + rect.bottom += cue_height; + Hide_wing_cues = 0; + } + + MoveWindow(rect); +} + +void wing_editor::OnSelchangeArrivalLocation() +{ + CComboBox *box; + + box = (CComboBox *)GetDlgItem(IDC_ARRIVAL_TARGET); + UpdateData(); + if (m_arrival_location) { + GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(TRUE); + GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(TRUE); + if (m_arrival_target < 0) { + m_arrival_target = 0; + } + + // determine which items we should put into the arrival target combo box + if ( m_arrival_location == ARRIVE_FROM_DOCK_BAY ) { + management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY ); + } else { + management_add_ships_to_combo( box, SHIPS_2_COMBO_SPECIAL | SHIPS_2_COMBO_ALL_SHIPS ); + } + } else { + m_arrival_target = -1; + GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(FALSE); + GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(FALSE); + } + UpdateData(FALSE); +} + +void wing_editor::OnSelchangeDepartureLocation() +{ + CComboBox *box; + + box = (CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET); + UpdateData(); + if (m_departure_location) { + GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(TRUE); + if (m_departure_target < 0) { + m_departure_target = 0; + } + // we need to build up the list box content based on the departure type. When + // from a docking bay, only show ships in the list which have them. Show all ships otherwise + if ( m_departure_location == DEPART_AT_DOCK_BAY ) { + management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY ); + } else { + // I think that this section is currently illegal + Int3(); + } + } else { + m_departure_target = -1; + GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(FALSE); + } + UpdateData(FALSE); +} + +// see if hotkey should possibly be reserved for player starting wing +void wing_editor::OnSelchangeHotkey() +{ + char buf[256]; + int set_num, i; + + UpdateData(TRUE); + set_num = m_hotkey - 1; // hotkey sets are 1 index based + + // first, determine if we are currently working with a starting wing + for ( i = 0; i < MAX_STARTING_WINGS; i++ ) { + if ( !stricmp( Wings[cur_wing].name, Starting_wing_names[i]) ) + break; + } + if ( i == MAX_STARTING_WINGS ) + return; + + // we have a player starting wing. See if we assigned a non-standard hotkey + if ( (set_num >= MAX_STARTING_WINGS) || (set_num != i) ) { + sprintf(buf, "Assigning nonstandard hotkey to wing %s (default is F%d)", Wings[cur_wing].name, 5+i); + MessageBox(buf, NULL, MB_OK | MB_ICONEXCLAMATION ); + } + +} diff --git a/src/freespace2/app_icon.ico b/src/freespace2/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3c8ce7eaee0319b6d4b3b7bc4b87bc53656678b1 GIT binary patch literal 3310 zcmcJRy=z=Y5Wq({k-I{0_jLDEi9}Hmq|UGqP-UYBE(`)Us^ucADuLk&H37dQAXEkd zg6&)+CMnYBAIRY%MYOflL~tOGgY1w~Cmi9;Z}z?W)K`L&y_@}*nVtFV?0d(FOi4u+ z78;`M_YXuqV{9~v_0p8cM-{=YR*@fSA`hoUmY6Rf50EKALCe{DeakUo$@5~MXDXde zhfxh7ekBB*A$(M&UD2vf`?F%K<^Scn`m~mhW~k+rd!re4$yUqznw+_=At+_AmR*!; z8}3(^(Y4wu_w=JPTCUy5x{MLkYSKp02R)O){#?}~>%woCZ*ooD)xXcH<|t^s3VTd+VqXM%%w#4{js8dls&h)6Ec6I`ZsNq}n4}_ruN5`E)aMksk9) zsfN&%`{DFSHJpB4Ddb5N*Lqjv{-sok7^lR!A;t?Q>NB z@Htm5ZRIue$7-!G>`E^Gcz-iB#F$;&B^@yyeuv*%JTBEtgo-j+S;0|uCB_;%8KM)1bO^8$hUGR z;f;ouns6qeq?Vqvkc#3RFENp4AG32I@9Wx!~e zSXeA9u`F;{I4m3%4hu&t6&w}@3xkD0OK0J*FjyEoxdMGGA$f`q>sVf`4piiJc*Rna z+ri{W>PX;Va^!Xx9YGwq9l7Im#N6n~@5t}SuUF*A@2HJ~!NK6DgI*g|iKj=yNUzsO z4LtcgY#t6zzIe6DK@B_{UO6~eylysmSUfBq2v5TJ2*Bdm=yCEWJS=<>;&3M(7O(Y) zEc!I)Eue!Tzz|>vFa#I^4DoS-!NU+>2rzgU2!r4cLx3T`5MUrQf*ytdLx3Rw5XkQd z7T6eYvc#~gEgT`nRv#;$$oI0bu_4>r+p@E>BYS&$a&T}U`}_NHbaW)g$H#JVaw2DE zXL5diE*BRU()%mOch8^m9_Lh7-vJ$n7grirm%18jywSI|yX@`ut=r{&?XO?o9ADWU zJraJFuJ^ga-fFj#q|r!{b~|e|&&FcRrdZl-{)EwrG0D=c(x<8WRL@w~{b`zJX_jTJ z**|d6y$uY?u6z?`coTQm8_}kAmC=?K4pHA~UA4=sI@B9UN-kt_Ke4u&83+kTSvs3$ zqtS=+G;0E#k}lzJvqM8U)3`V%$aTC7&LcDB})lFQu?&+HwlGW=O^(8dg{_bXhGjc!JTYYO}+&XKvGVU#*N{nN+!6EK=KH_tw{5L&c8ffBf h2eJE;_kI5F+J76HGF-9W^gk>9m(%}Y|MUO3zX5=2+FJks literal 0 HcmV?d00001 diff --git a/src/freespace2/freespace.cpp b/src/freespace2/freespace.cpp new file mode 100644 index 0000000..ea86215 --- /dev/null +++ b/src/freespace2/freespace.cpp @@ -0,0 +1,8560 @@ +/* + * $Logfile: /Freespace2/code/Freespace2/FreeSpace.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Freespace main body + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 201 6/16/00 3:15p Jefff + * sim of the year dvd version changes, a few german soty localization + * fixes + * + * 200 11/03/99 11:06a Jefff + * 1.2 checksums + * + * 199 10/26/99 5:07p Jamest + * fixed jeffs dumb debug code + * + * 198 10/25/99 5:53p Jefff + * call control_config_common_init() on startup + * + * 197 10/14/99 10:18a Daveb + * Fixed incorrect CD checking problem on standalone server. + * + * 196 10/13/99 9:22a Daveb + * Fixed Fred jumpnode placing bug. Fixed 1024 glide tiled texture problem + * related to movies. Fixed launcher spawning from PXO screen. + * + * 195 10/06/99 11:05a Jefff + * new oem upsell 3 hotspot coords + * + * 194 10/06/99 10:31a Jefff + * OEM updates + * + * 193 10/01/99 9:10a Daveb + * V 1.1 PATCH + * + * 192 9/15/99 4:57a Dave + * Updated ships.tbl checksum + * + * 191 9/15/99 3:58a Dave + * Removed framerate warning at all times. + * + * 190 9/15/99 3:16a Dave + * Remove mt-011.fs2 from the builtin mission list. + * + * 189 9/15/99 1:45a Dave + * Don't init joystick on standalone. Fixed campaign mode on standalone. + * Fixed no-score-report problem in TvT + * + * 188 9/14/99 6:08a Dave + * Updated (final) single, multi, and campaign list. + * + * 187 9/14/99 3:26a Dave + * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer + * respawn-too-early problem. Made a few crash points safe. + * + * 186 9/13/99 4:52p Dave + * RESPAWN FIX + * + * 185 9/12/99 8:09p Dave + * Fixed problem where skip-training button would cause mission messages + * not to get paged out for the current mission. + * + * 184 9/10/99 11:53a Dave + * Shutdown graphics before sound to eliminate apparent lockups when + * Directsound decides to be lame. Fix TOPMOST problem with D3D windows. + * + * 183 9/09/99 11:40p Dave + * Handle an Assert() in beam code. Added supernova sounds. Play the right + * 2 end movies properly, based upon what the player did in the mission. + * + * 182 9/08/99 10:29p Dave + * Make beam sound pausing and unpausing much safer. + * + * 181 9/08/99 10:01p Dave + * Make sure game won't run in a drive's root directory. Make sure + * standalone routes suqad war messages properly to the host. + * + * 180 9/08/99 3:22p Dave + * Updated builtin mission list. + * + * 179 9/08/99 12:01p Jefff + * fixed Game_builtin_mission_list typo on Training-2.fs2 + * + * 178 9/08/99 9:48a Andsager + * Add force feedback for engine wash. + * + * 177 9/07/99 4:01p Dave + * Fixed up a string.tbl paroblem (self destruct message). Make sure IPX + * does everything properly (setting up address when binding). Remove + * black rectangle background from UI_INPUTBOX. + * + * 176 9/13/99 2:40a Dave + * Comment in full 80 minute CD check for RELEASE_REAL builds. + * + * 175 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 174 9/06/99 1:30a Dave + * Intermediate checkin. Started on enforcing CD-in-drive to play the + * game. + * + * 173 9/06/99 1:16a Dave + * Make sure the user sees the intro movie. + * + * 172 9/04/99 8:00p Dave + * Fixed up 1024 and 32 bit movie support. + * + * 171 9/03/99 1:32a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 170 9/01/99 10:49p Dave + * Added nice SquadWar checkbox to the client join wait screen. + * + * 169 9/01/99 10:14a Dave + * Pirate bob. + * + * 168 8/29/99 4:51p Dave + * Fixed damaged checkin. + * + * 167 8/29/99 4:18p Andsager + * New "burst" limit for friendly damage. Also credit more damage done + * against large friendly ships. + * + * 166 8/27/99 6:38p Alanl + * crush the blasted repeating messages bug + * + * 164 8/26/99 9:09p Dave + * Force framerate check in everything but a RELEASE_REAL build. + * + * 163 8/26/99 9:45a Dave + * First pass at easter eggs and cheats. + * + * 162 8/24/99 8:55p Dave + * Make sure nondimming pixels work properly in tech menu. + * + * 161 8/24/99 1:49a Dave + * Fixed client-side afterburner stuttering. Added checkbox for no version + * checking on PXO join. Made button info passing more friendly between + * client and server. + * + * 160 8/22/99 5:53p Dave + * Scoring fixes. Added self destruct key. Put callsigns in the logfile + * instead of ship designations for multiplayer players. + * + * 159 8/22/99 1:19p Dave + * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in + * which d3d cards are detected. + * + * 158 8/20/99 2:09p Dave + * PXO banner cycling. + * + * 157 8/19/99 10:59a Dave + * Packet loss detection. + * + * 156 8/19/99 10:12a Alanl + * preload mission-specific messages on machines greater than 48MB + * + * 155 8/16/99 4:04p Dave + * Big honking checkin. + * + * 154 8/11/99 5:54p Dave + * Fixed collision problem. Fixed standalone ghost problem. + * + * 153 8/10/99 7:59p Jefff + * XSTR'ed some stuff + * + * 152 8/10/99 6:54p Dave + * Mad optimizations. Added paging to the nebula effect. + * + * 151 8/10/99 3:44p Jefff + * loads Intelligence information on startup + * + * 150 8/09/99 3:47p Dave + * Fixed incorrect nebula regeneration. Default HUD to low-contrast in + * non-nebula missions. + * + * 149 8/09/99 2:21p Andsager + * Fix patching from multiplayer direct to launcher update tab. + * + * 148 8/09/99 10:36a Dave + * Version info for game. + * + * 147 8/06/99 9:46p Dave + * Hopefully final changes for the demo. + * + * 146 8/06/99 3:34p Andsager + * Make title version info "(D)" -> "D" show up nicely + * + * 145 8/06/99 2:59p Adamp + * Fixed NT launcher/update problem. + * + * 144 8/06/99 1:52p Dave + * Bumped up MAX_BITMAPS for the demo. + * + * 143 8/06/99 12:17p Andsager + * Demo: down to just 1 demo dog + * + * 142 8/05/99 9:39p Dave + * Yet another new checksum. + * + * 141 8/05/99 6:19p Dave + * New demo checksums. + * + * 140 8/05/99 5:31p Andsager + * Up demo version 1.01 + * + * 139 8/05/99 4:22p Andsager + * No time limit on upsell screens. Reverse order of display of upsell + * bitmaps. + * + * 138 8/05/99 4:17p Dave + * Tweaks to client interpolation. + * + * 137 8/05/99 3:52p Danw + * + * 136 8/05/99 3:01p Danw + * + * 135 8/05/99 2:43a Anoop + * removed duplicate definition. + * + * 134 8/05/99 2:13a Dave + * Fixed build error. + * + * 133 8/05/99 2:05a Dave + * Whee. + * + * 132 8/05/99 1:22a Andsager + * fix upsell bug. + * + * 131 8/04/99 9:51p Andsager + * Add title screen to demo + * + * 130 8/04/99 6:47p Jefff + * fixed link error resulting from #ifdefs + * + * 129 8/04/99 6:26p Dave + * Updated ship tbl checksum. + * + * 128 8/04/99 5:40p Andsager + * Add multiple demo dogs + * + * 127 8/04/99 5:36p Andsager + * Show upsell screens at end of demo campaign before returning to main + * hall. + * + * 126 8/04/99 11:42a Danw + * tone down EAX reverb + * + * 125 8/04/99 11:23a Dave + * Updated demo checksums. + * + * 124 8/03/99 11:02p Dave + * Maybe fixed sync problems in multiplayer. + * + * 123 8/03/99 6:21p Jefff + * minor text change + * + * 122 8/03/99 3:44p Andsager + * Launch laucher if trying to run FS without first having configured + * system. + * + * 121 8/03/99 12:45p Dave + * Update checksums. + * + * 120 8/02/99 9:13p Dave + * Added popup tips. + * + * 119 7/30/99 10:31p Dave + * Added comm menu to the configurable hud files. + * + * 118 7/30/99 5:17p Andsager + * first fs2demo checksums + * + * 117 7/29/99 3:09p Anoop + * + * 116 7/29/99 12:05a Dave + * Nebula speed optimizations. + * + * 115 7/27/99 8:59a Andsager + * Make major, minor version consistent for all builds. Only show major + * and minor for launcher update window. + * + * 114 7/26/99 5:50p Dave + * Revised ingame join. Better? We'll see.... + * + * 113 7/26/99 5:27p Andsager + * Add training mission as builtin to demo build + * + * 112 7/24/99 1:54p Dave + * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert + * missions. + * + * 111 7/22/99 4:00p Dave + * Fixed beam weapon muzzle glow rendering. Externalized hud shield info. + * + * 110 7/21/99 8:10p Dave + * First run of supernova effect. + * + * 109 7/20/99 1:49p Dave + * Peter Drake build. Fixed some release build warnings. + * + * 108 7/19/99 2:26p Andsager + * set demo multiplayer missions + * + * 107 7/18/99 5:19p Dave + * Jump node icon. Fixed debris fogging. Framerate warning stuff. + * + * 106 7/16/99 1:50p Dave + * 8 bit aabitmaps. yay. + * + * 105 7/15/99 3:07p Dave + * 32 bit detection support. Mouse coord commandline. + * + * 104 7/15/99 2:13p Dave + * Added 32 bit detection. + * + * 103 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 102 7/14/99 11:02a Dave + * Skill level default back to easy. Blech. + * + * 101 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 100 7/08/99 4:43p Andsager + * New check for sparky_hi and print if not found. + * + * 99 7/08/99 10:53a Dave + * New multiplayer interpolation scheme. Not 100% done yet, but still + * better than the old way. + * + * 98 7/06/99 4:24p Dave + * Mid-level checkin. Starting on some potentially cool multiplayer + * smoothness crap. + * + * 97 7/06/99 3:35p Andsager + * Allow movie to play before red alert mission. + * + * 96 7/03/99 5:50p Dave + * Make rotated bitmaps draw properly in padlock views. + * + * 95 7/02/99 9:55p Dave + * Player engine wash sound. + * + * 94 7/02/99 4:30p Dave + * Much more sophisticated lightning support. + * + * 93 6/29/99 7:52p Dave + * Put in exception handling in FS2. + * + * 92 6/22/99 9:37p Dave + * Put in pof spewing. + * + * 91 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 90 6/15/99 1:56p Andsager + * For release builds, allow start up in high res only with + * sparky_hi._fs2.vp + * + * 89 6/15/99 9:34a Dave + * Fixed key checking in single threaded version of the stamp notification + * screen. + * + * 88 6/09/99 2:55p Andsager + * Allow multiple asteroid subtypes (of large, medium, small) and follow + * family. + * + * 87 6/08/99 1:14a Dave + * Multi colored hud test. + * + * 86 6/04/99 9:52a Dave + * Fixed some rendering problems. + * + * 85 6/03/99 10:15p Dave + * Put in temporary main hall screen. + * + * 84 6/02/99 6:18p Dave + * Fixed TNT lockup problems! Wheeeee! + * + * 83 6/01/99 3:52p Dave + * View footage screen. Fixed xstrings to not display the & symbol. Popup, + * dead popup, pxo find player popup, pxo private room popup. + * + * 82 5/26/99 1:28p Jasenw + * changed coords for loading ani + * + * 81 5/26/99 11:46a Dave + * Added ship-blasting lighting and made the randomization of lighting + * much more customizable. + * + * 80 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * + */ + +#include + +#include +#include +#include +#include + +#include "pstypes.h" +#include "systemvars.h" +#include "key.h" +#include "vecmat.h" +#include "2d.h" +#include "3d.h" +#include "starfield.h" +#include "lighting.h" +#include "weapon.h" +#include "ship.h" +#include "palman.h" +#include "osapi.h" +#include "fireballs.h" +#include "debris.h" +#include "timer.h" +#include "fix.h" +#include "floating.h" +#include "gamesequence.h" +#include "radar.h" +#include "optionsmenu.h" +#include "playermenu.h" +#include "trainingmenu.h" +#include "techmenu.h" +#include "ai.h" +#include "hud.h" +#include "hudmessage.h" +#include "psnet.h" +#include "missiongoals.h" +#include "missionparse.h" +#include "bmpman.h" +#include "joy.h" +#include "joy_ff.h" +#include "multi.h" +#include "multiutil.h" +#include "multimsgs.h" +#include "multiui.h" +#include "cfile.h" +#include "player.h" +#include "freespace.h" +#include "managepilot.h" +#include "sound.h" +#include "contexthelp.h" +#include "mouse.h" +#include "joy.h" +#include "missionbrief.h" +#include "missiondebrief.h" +#include "ui.h" +#include "missionshipchoice.h" +#include "model.h" +#include "hudconfig.h" +#include "controlsconfig.h" +#include "missionmessage.h" +#include "missiontraining.h" +#include "hudets.h" +#include "hudtarget.h" +#include "gamesnd.h" +#include "rbaudio.h" +#include "winmidi.h" +#include "eventmusic.h" +#include "animplay.h" +#include "missionweaponchoice.h" +#include "missionlog.h" +#include "audiostr.h" +#include "hudlock.h" +#include "missioncampaign.h" +#include "credits.h" +#include "missionhotkey.h" +#include "objectsnd.h" +#include "cmeasure.h" +#include "ai.h" +#include "linklist.h" +#include "shockwave.h" +#include "afterburner.h" +#include "scoring.h" +#include "stats.h" +#include "cmdline.h" +#include "timer.h" +#include "stand_gui.h" +#include "pcxutils.h" +#include "hudtargetbox.h" +#include "multi_xfer.h" +#include "hudescort.h" +#include "multiutil.h" +#include "sexp.h" +#include "medals.h" +#include "multiteamselect.h" +#include "ds3d.h" +#include "shipfx.h" +#include "readyroom.h" +#include "mainhallmenu.h" +#include "multilag.h" +#include "trails.h" +#include "particle.h" +#include "popup.h" +#include "multi_ingame.h" +#include "snazzyui.h" +#include "asteroid.h" +#include "popupdead.h" +#include "multi_voice.h" +#include "missioncmdbrief.h" +#include "redalert.h" +#include "gameplayhelp.h" +#include "multilag.h" +#include "staticrand.h" +#include "multi_pmsg.h" +#include "levelpaging.h" +#include "observer.h" +#include "multi_pause.h" +#include "multi_endgame.h" +#include "cutscenes.h" +#include "multi_respawn.h" +// #include "movie.h" +#include "multi_obj.h" +#include "multi_log.h" +#include "emp.h" +#include "localize.h" +#include "osregistry.h" +#include "barracks.h" +#include "missionpause.h" +#include "font.h" +#include "alphacolors.h" +#include "objcollide.h" +#include "flak.h" +#include "neb.h" +#include "neblightning.h" +#include "shipcontrails.h" +#include "awacs.h" +#include "beam.h" +#include "multi_dogfight.h" +#include "multi_rate.h" +#include "muzzleflash.h" +#include "encrypt.h" +#include "demo.h" +#include "version.h" +#include "mainhalltemp.h" +#include "exceptionhandler.h" +#include "glide.h" +#include "supernova.h" +#include "hudshield.h" +#include +// #include "names.h" +#include "shiphit.h" +#include "missionloopbrief.h" + +#ifdef NDEBUG +#ifdef FRED +#error macro FRED is defined when trying to build release Fred. Please undefine FRED macro in build settings +#endif +#endif + +// Revision history. +// Full version: +// 1.00.04 5/26/98 MWA -- going final (12 pm) +// 1.00.03 5/26/98 MWA -- going final (3 am) +// 1.00.02 5/25/98 MWA -- going final +// 1.00.01 5/25/98 MWA -- going final +// 0.90 5/21/98 MWA -- getting ready for final. +// 0.10 4/9/98. Set by MK. +// +// Demo version: (obsolete since DEMO codebase split from tree) +// 0.03 4/10/98 AL. Interplay rev +// 0.02 4/8/98 MK. Increased when this system was modified. +// 0.01 4/7/98? AL. First release to Interplay QA. +// +// OEM version: +// 1.00 5/28/98 AL. First release to Interplay QA. + +void game_level_init(int seed = -1); +void game_post_level_init(); +void game_do_frame(); +void game_update_missiontime(); // called from game_do_frame() and navmap_do_frame() +void game_reset_time(); +void game_show_framerate(); // draws framerate in lower right corner + +int Game_no_clear = 0; + +int Pofview_running = 0; +int Nebedit_running = 0; + +typedef struct big_expl_flash { + float max_flash_intensity; // max intensity + float cur_flash_intensity; // cur intensity + int flash_start; // start time +} big_expl_flash; + +#define FRAME_FILTER 16 + +#define DEFAULT_SKILL_LEVEL 1 +int Game_skill_level = DEFAULT_SKILL_LEVEL; + +#define VIEWER_ZOOM_DEFAULT 0.75f // Default viewer zoom, 0.625 as per multi-lateral agreement on 3/24/97 +float Viewer_zoom = VIEWER_ZOOM_DEFAULT; + +#define EXE_FNAME ("fs2.exe") +#define LAUNCHER_FNAME ("freespace2.exe") + +// JAS: Code for warphole camera. +// Needs to be cleaned up. +vector Camera_pos = { 0.0f, 0.0f, 0.0f }; +vector Camera_velocity = { 0.0f, 0.0f, 0.0f }; +vector Camera_desired_velocity = { 0.0f, 0.0f, 0.0f }; +matrix Camera_orient = IDENTITY_MATRIX; +float Camera_damping = 1.0f; +float Camera_time = 0.0f; +float Warpout_time = 0.0f; +int Warpout_forced = 0; // Set if this is a forced warpout that cannot be cancelled. +int Warpout_sound = -1; +void camera_move(); +int Use_joy_mouse = 0; +int Use_palette_flash = 1; +#ifndef NDEBUG +int Use_fullscreen_at_startup = 0; +#endif +int Show_area_effect = 0; +object *Last_view_target = NULL; + +int dogfight_blown = 0; + +int frame_int = -1; +float frametimes[FRAME_FILTER]; +float frametotal = 0.0f; +float flFrametime; + +#ifdef RELEASE_REAL + int Show_framerate = 0; +#else + int Show_framerate = 1; +#endif + +int Framerate_cap = 120; +int Show_mem = 0; +int Show_cpu = 0; +int Show_target_debug_info = 0; +int Show_target_weapons = 0; +int Game_font = -1; +static int Show_player_pos = 0; // debug console command to show player world pos on HUD + +int Debug_octant = -1; + +fix Game_time_compression = F1_0; + +// if the ships.tbl the player has is valid +int Game_ships_tbl_valid = 0; + +// if the weapons.tbl the player has is valid +int Game_weapons_tbl_valid = 0; + +#ifndef NDEBUG +int Test_begin = 0; +extern int Player_attacking_enabled; +int Show_net_stats; +#endif + +int Pre_player_entry; + +int Fred_running = 0; +char Game_current_mission_filename[MAX_FILENAME_LEN]; +int game_single_step = 0; +int last_single_step=0; + +extern int MSG_WINDOW_X_START; // used to position mission_time and shields output +extern int MSG_WINDOW_Y_START; +extern int MSG_WINDOW_HEIGHT; + +int game_zbuffer = 1; +//static int Game_music_paused; +static int Game_paused; + +int Game_level_seed; + +#define EXPIRE_BAD_CHECKSUM 1 +#define EXPIRE_BAD_TIME 2 + +extern void ssm_init(); +extern void ssm_level_init(); +extern void ssm_process(); + +// static variable to contain the time this version was built +// commented out for now until +// I figure out how to get the username into the file +//LOCAL char freespace_build_time[] = "Compiled on:"__DATE__" "__TIME__" by "__USER__; + +// defines and variables used for dumping frame for making trailers. +#ifndef NDEBUG +int Debug_dump_frames = 0; // Set to 0 to not dump frames, else equal hz to dump. (15 or 30 probably) +int Debug_dump_trigger = 0; +int Debug_dump_frame_count; +int Debug_dump_frame_num = 0; +#define DUMP_BUFFER_NUM_FRAMES 1 // store every 15 frames +#endif + +// amount of time to wait after the player has died before we display the death died popup +#define PLAYER_DIED_POPUP_WAIT 2500 +int Player_died_popup_wait = -1; +int Player_multi_died_check = -1; + +// builtin mission list stuff +#ifdef FS2_DEMO + int Game_builtin_mission_count = 6; + fs_builtin_mission Game_builtin_mission_list[MAX_BUILTIN_MISSIONS] = { + { "SPDemo-01.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), "" }, + { "SPDemo-02.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), "" }, + { "DemoTrain.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), "" }, + { "Demo.fc2", (FSB_FROM_VOLITION | FSB_CAMPAIGN_FILE), "" }, + { "MPDemo-01.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "Demo-DOG-01.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + }; +#elif defined(PD_BUILD) + int Game_builtin_mission_count = 4; + fs_builtin_mission Game_builtin_mission_list[MAX_BUILTIN_MISSIONS] = { + { "sm1-01.fs2", (FSB_FROM_VOLITION), "" }, + { "sm1-05.fs2", (FSB_FROM_VOLITION), "" }, + { "sm1-01", (FSB_FROM_VOLITION), "" }, + { "sm1-05", (FSB_FROM_VOLITION), "" }, + }; +#elif defined(MULTIPLAYER_BETA) + int Game_builtin_mission_count = 17; + fs_builtin_mission Game_builtin_mission_list[MAX_BUILTIN_MISSIONS] = { + // multiplayer beta + { "md-01.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "md-02.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "md-03.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "md-04.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "md-05.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "md-06.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "md-07.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-02.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-03.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "m-03.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "m-04.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "m-05.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "templar-01.fs2", (FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN), "" }, + { "templar-02.fs2", (FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN), "" }, + { "templar-03a.fs2", (FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN), "" }, + { "templar-04a.fs2", (FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN), "" }, + { "templar.fc2", (FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN_FILE), "" }, + }; +#elif defined(OEM_BUILD) + int Game_builtin_mission_count = 17; + fs_builtin_mission Game_builtin_mission_list[MAX_BUILTIN_MISSIONS] = { + // oem version - act 1 only + { "freespace2oem.fc2", (FSB_FROM_VOLITION | FSB_CAMPAIGN_FILE), "" }, + + // act 1 + { "sm1-01.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "sm1-02.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "sm1-03.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "sm1-04.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "sm1-05.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "sm1-06.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "sm1-07.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "sm1-08.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "sm1-09.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "sm1-10.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "training-1.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "training-2.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "training-3.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "tsm-104.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "tsm-105.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 }, + { "tsm-106.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_1 } + }; +#else + int Game_builtin_mission_count = 92; + fs_builtin_mission Game_builtin_mission_list[MAX_BUILTIN_MISSIONS] = { + // single player campaign + { "freespace2.fc2", (FSB_FROM_VOLITION | FSB_CAMPAIGN_FILE), "" }, + + // act 1 + { "sm1-01.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "sm1-02.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "sm1-03.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "sm1-04.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "sm1-05.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "sm1-06.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "sm1-07.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "sm1-08.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "sm1-09.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "sm1-10.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "loop1-1.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "loop1-2.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "loop1-3.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "training-1.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "training-2.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "training-3.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "tsm-104.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "tsm-105.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + { "tsm-106.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_2 }, + + // act 2 + { "sm2-01.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm2-02.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm2-03.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm2-04.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm2-05.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm2-06.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm2-07.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm2-08.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm2-09.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm2-10.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + + // act 3 + { "sm3-01.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm3-02.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm3-03.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm3-04.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm3-05.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm3-06.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm3-07.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm3-08.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm3-09.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "sm3-10.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "loop2-1.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + { "loop2-2.fs2", (FSB_FROM_VOLITION | FSB_CAMPAIGN), FS_CDROM_VOLUME_3 }, + + // multiplayer missions + + // gauntlet + { "g-shi.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "g-ter.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "g-vas.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + + // coop + { "m-01.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "m-02.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "m-03.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "m-04.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + + // dogfight + { "mdh-01.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdh-02.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdh-03.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdh-04.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdh-05.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdh-06.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdh-07.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdh-08.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdh-09.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdl-01.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdl-02.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdl-03.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdl-04.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdl-05.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdl-06.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdl-07.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdl-08.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdl-09.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdm-01.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdm-02.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdm-03.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdm-04.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdm-05.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdm-06.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdm-07.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdm-08.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mdm-09.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "osdog.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + + // TvT + { "mt-01.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-02.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-03.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-04.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-05.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-06.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-07.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-08.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-09.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + { "mt-10.fs2", (FSB_FROM_VOLITION | FSB_MULTI), "" }, + + // campaign + { "templar.fc2", (FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN_FILE), "" }, + { "templar-01.fs2", (FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN), "" }, + { "templar-02.fs2", (FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN), "" }, + { "templar-03.fs2", (FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN), "" }, + { "templar-04.fs2", (FSB_FROM_VOLITION | FSB_MULTI | FSB_CAMPAIGN), "" }, + }; +#endif + + +// Internal function prototypes +void game_maybe_draw_mouse(float frametime); +void init_animating_pointer(); +void load_animating_pointer(char *filename, int dx, int dy); +void unload_animating_pointer(); +void game_do_training_checks(); +void game_shutdown(void); +void game_show_event_debug(float frametime); +void game_event_debug_init(); +void game_frame(); +void demo_upsell_show_screens(); +void game_start_subspace_ambient_sound(); +void game_stop_subspace_ambient_sound(); +void verify_ships_tbl(); +void verify_weapons_tbl(); +void display_title_screen(); + +// loading background filenames +static char *Game_loading_bground_fname[GR_NUM_RESOLUTIONS] = { + "LoadingBG", // GR_640 + "2_LoadingBG" // GR_1024 +}; + + +static char *Game_loading_ani_fname[GR_NUM_RESOLUTIONS] = { + "Loading.ani", // GR_640 + "2_Loading.ani" // GR_1024 +}; + +#if defined(FS2_DEMO) +static char *Game_demo_title_screen_fname[GR_NUM_RESOLUTIONS] = { + "PreLoad", + "2_PreLoad" +}; +#elif defined(OEM_BUILD) +static char *Game_demo_title_screen_fname[GR_NUM_RESOLUTIONS] = { + "OEMPreLoad", + "2_OEMPreLoad" +}; +#endif + +// cdrom stuff +char Game_CDROM_dir[MAX_PATH_LEN]; +int init_cdrom(); + +// How much RAM is on this machine. Set in WinMain +uint Freespace_total_ram = 0; + +// game flash stuff +float Game_flash_red = 0.0f; +float Game_flash_green = 0.0f; +float Game_flash_blue = 0.0f; +float Sun_spot = 0.0f; +big_expl_flash Big_expl_flash = {0.0f, 0.0f, 0}; + +// game shudder stuff (in ms) +int Game_shudder_time = -1; +int Game_shudder_total = 0; +float Game_shudder_intensity = 0.0f; // should be between 0.0 and 100.0 + +// EAX stuff +sound_env Game_sound_env; +//sound_env Game_default_sound_env = {SND_ENV_AUDITORIUM, 0.25f, 0.35f, 3.0f}; +sound_env Game_default_sound_env = {SND_ENV_GENERIC, 0.2F,0.2F,1.0F}; + +int Game_sound_env_update_timestamp; + +// WARPIN CRAP BEGIN -------------------------------------------------------------------------------------------- + + +// WARPIN CRAP END -------------------------------------------------------------------------------------------- + +fs_builtin_mission *game_find_builtin_mission(char *filename) +{ + int idx; + + // look through all existing builtin missions + for(idx=0; idx= 4){ + Gf_critical = 15.0f; + } + // voodoo 1 + else { + Gf_critical = 10.0f; + } + } + // d3d. only care about good cards here I guess (TNT) + else { + Gf_critical = 15.0f; + } + } else { + // if this is a glide card + if(gr_screen.mode == GR_GLIDE){ + extern GrHwConfiguration hwconfig; + + // voodoo 2/3 + if(hwconfig.SSTs[0].sstBoard.VoodooConfig.fbRam >= 4){ + Gf_critical = 25.0f; + } + // voodoo 1 + else { + Gf_critical = 20.0f; + } + } + // d3d. only care about good cards here I guess (TNT) + else { + Gf_critical = 25.0f; + } + } +} + +extern float Framerate; +void game_framerate_check() +{ + int y_start = 100; + + // if the current framerate is above the critical level, add frametime + if(Framerate >= Gf_critical){ + Gf_critical_time += flFrametime; + } + + if(!Show_framerate){ + return; + } + + // display if we're above the critical framerate + if(Framerate < Gf_critical){ + gr_set_color_fast(&Color_bright_red); + gr_string(200, y_start, "Framerate warning"); + + y_start += 10; + } + + // display our current pct of good frametime + if(f2fl(Missiontime) >= 0.0f){ + float pct = (Gf_critical_time / f2fl(Missiontime)) * 100.0f; + + if(pct >= 85.0f){ + gr_set_color_fast(&Color_bright_green); + } else { + gr_set_color_fast(&Color_bright_red); + } + + gr_printf(200, y_start, "%d%%", (int)pct); + + y_start += 10; + } +} + + +// Adds a flash effect. These can be positive or negative. +// The range will get capped at around -1 to 1, so stick +// with a range like that. +void game_flash( float r, float g, float b ) +{ + Game_flash_red += r; + Game_flash_green += g; + Game_flash_blue += b; + + if ( Game_flash_red < -1.0f ) { + Game_flash_red = -1.0f; + } else if ( Game_flash_red > 1.0f ) { + Game_flash_red = 1.0f; + } + + if ( Game_flash_green < -1.0f ) { + Game_flash_green = -1.0f; + } else if ( Game_flash_green > 1.0f ) { + Game_flash_green = 1.0f; + } + + if ( Game_flash_blue < -1.0f ) { + Game_flash_blue = -1.0f; + } else if ( Game_flash_blue > 1.0f ) { + Game_flash_blue = 1.0f; + } + +} + +// Adds a flash for Big Ship explosions +// cap range from 0 to 1 +void big_explosion_flash(float flash) +{ + Big_expl_flash.flash_start = timestamp(1); + + if (flash > 1.0f) { + flash = 1.0f; + } else if (flash < 0.0f) { + flash = 0.0f; + } + + Big_expl_flash.max_flash_intensity = flash; + Big_expl_flash.cur_flash_intensity = 0.0f; +} + +// Amount to diminish palette towards normal, per second. +#define DIMINISH_RATE 0.75f +#define SUN_DIMINISH_RATE 6.00f + +int Sun_drew = 0; + +float sn_glare_scale = 1.7f; +DCF(sn_glare, "") +{ + dc_get_arg(ARG_FLOAT); + sn_glare_scale = Dc_arg_float; +} + +float Supernova_last_glare = 0.0f; +void game_sunspot_process(float frametime) +{ + int n_lights, idx; + int sn_stage; + float Sun_spot_goal = 0.0f; + + // supernova + sn_stage = supernova_active(); + if(sn_stage){ + // sunspot differently based on supernova stage + switch(sn_stage){ + // approaching. player still in control + case 1: + float pct; + pct = (1.0f - (supernova_time_left() / SUPERNOVA_CUT_TIME)); + + vector light_dir; + light_get_global_dir(&light_dir, 0); + float dot; + dot = vm_vec_dot( &light_dir, &Eye_matrix.fvec ); + + if(dot >= 0.0f){ + // scale it some more + dot = dot * (0.5f + (pct * 0.5f)); + dot += 0.05f; + + Sun_spot_goal += (dot * sn_glare_scale); + } + + // draw the sun glow + if ( !shipfx_eye_in_shadow( &Eye_position, Viewer_obj, 0 ) ) { + // draw the glow for this sun + stars_draw_sun_glow(0); + } + + Supernova_last_glare = Sun_spot_goal; + break; + + // camera cut. player not in control. note : at this point camera starts out facing the sun. so we can go nice and bright + case 2: + case 3: + Sun_spot_goal = 0.9f; + Sun_spot_goal += (1.0f - (supernova_time_left() / SUPERNOVA_CUT_TIME)) * 0.1f; + + if(Sun_spot_goal > 1.0f){ + Sun_spot_goal = 1.0f; + } + + Sun_spot_goal *= sn_glare_scale; + Supernova_last_glare = Sun_spot_goal; + break; + + // fade to white. display dead popup + case 4: + case 5: + Supernova_last_glare += (2.0f * flFrametime); + if(Supernova_last_glare > 2.0f){ + Supernova_last_glare = 2.0f; + } + + Sun_spot_goal = Supernova_last_glare; + break; + } + + Sun_drew = 0; + } else { + if ( Sun_drew ) { + // check sunspots for all suns + n_lights = light_get_global_count(); + + // check + for(idx=0; idx Sun_spot_goal ) { + Sun_spot = Sun_spot_goal; + } + } else if ( Sun_spot > Sun_spot_goal ) { + Sun_spot -= dec_amount; + if ( Sun_spot < Sun_spot_goal ) { + Sun_spot = Sun_spot_goal; + } + } +} + + +// Call once a frame to diminish the +// flash effect to 0. +void game_flash_diminish(float frametime) +{ + float dec_amount = frametime*DIMINISH_RATE; + + if ( Game_flash_red > 0.0f ) { + Game_flash_red -= dec_amount; + if ( Game_flash_red < 0.0f ) + Game_flash_red = 0.0f; + } else { + Game_flash_red += dec_amount; + if ( Game_flash_red > 0.0f ) + Game_flash_red = 0.0f; + } + + if ( Game_flash_green > 0.0f ) { + Game_flash_green -= dec_amount; + if ( Game_flash_green < 0.0f ) + Game_flash_green = 0.0f; + } else { + Game_flash_green += dec_amount; + if ( Game_flash_green > 0.0f ) + Game_flash_green = 0.0f; + } + + if ( Game_flash_blue > 0.0f ) { + Game_flash_blue -= dec_amount; + if ( Game_flash_blue < 0.0f ) + Game_flash_blue = 0.0f; + } else { + Game_flash_blue += dec_amount; + if ( Game_flash_blue > 0.0f ) + Game_flash_blue = 0.0f; + } + + // update big_explosion_cur_flash +#define TIME_UP 1500 +#define TIME_DOWN 2500 + int duration = TIME_UP + TIME_DOWN; + int time = timestamp_until(Big_expl_flash.flash_start); + if (time > -duration) { + time = -time; + if (time < TIME_UP) { + Big_expl_flash.cur_flash_intensity = Big_expl_flash.max_flash_intensity * time / (float) TIME_UP; + } else { + time -= TIME_UP; + Big_expl_flash.cur_flash_intensity = Big_expl_flash.max_flash_intensity * ((float) TIME_DOWN - time) / (float) TIME_DOWN; + } + } + + if ( Use_palette_flash ) { + int r,g,b; + static int or=0, og=0, ob=0; + + // Change the 200 to change the color range of colors. + r = fl2i( Game_flash_red*128.0f ); + g = fl2i( Game_flash_green*128.0f ); + b = fl2i( Game_flash_blue*128.0f ); + + if ( Sun_spot > 0.0f ) { + r += fl2i(Sun_spot*128.0f); + g += fl2i(Sun_spot*128.0f); + b += fl2i(Sun_spot*128.0f); + } + + if ( Big_expl_flash.cur_flash_intensity > 0.0f ) { + r += fl2i(Big_expl_flash.cur_flash_intensity*128.0f); + g += fl2i(Big_expl_flash.cur_flash_intensity*128.0f); + b += fl2i(Big_expl_flash.cur_flash_intensity*128.0f); + } + + if ( r < 0 ) r = 0; else if ( r > 255 ) r = 255; + if ( g < 0 ) g = 0; else if ( g > 255 ) g = 255; + if ( b < 0 ) b = 0; else if ( b > 255 ) b = 255; + + if ( (r!=0) || (g!=0) || (b!=0) ) { + gr_flash( r, g, b ); + + //mprintf(( "Flash! %d,%d,%d\n", r, g, b )); + + or = r; + og = g; + ob = b; + } + } + +} + + +void game_level_close() +{ + // De-Initialize the game subsystems + message_mission_shutdown(); + event_music_level_close(); + game_stop_looped_sounds(); + snd_stop_all(); + obj_snd_level_close(); // uninit object-linked persistant sounds + gamesnd_unload_gameplay_sounds(); // unload gameplay sounds from memory + anim_level_close(); // stop and clean up any anim instances + shockwave_level_close(); + fireball_level_close(); + shield_hit_close(); + mission_event_shutdown(); + asteroid_level_close(); + model_cache_reset(); // Reset/free all the model caching stuff + flak_level_close(); // unload flak stuff + neb2_level_close(); // shutdown gaseous nebula stuff + ct_level_close(); + beam_level_close(); + mflash_level_close(); + + audiostream_unpause_all(); + Game_paused = 0; +} + + +// intializes game stuff and loads the mission. Returns 0 on failure, 1 on success +// input: seed => DEFAULT PARAMETER (value -1). Only set by demo playback code. +void game_level_init(int seed) +{ + // seed the random number generator + if ( seed == -1 ) { + // if no seed was passed, seed the generator either from the time value, or from the + // netgame security flags -- ensures that all players in multiplayer game will have the + // same randon number sequence (with static rand functions) + if ( Game_mode & GM_NORMAL ) { + Game_level_seed = time(NULL); + } else { + Game_level_seed = Netgame.security; + } + } else { + // mwa 9/17/98 -- maybe this assert isn't needed???? + Assert( !(Game_mode & GM_MULTIPLAYER) ); + Game_level_seed = seed; + } + srand( Game_level_seed ); + + // semirand function needs to get re-initted every time in multiplayer + if ( Game_mode & GM_MULTIPLAYER ){ + init_semirand(); + } + + Framecount = 0; + + Key_normal_game = (Game_mode & GM_NORMAL); + Cheats_enabled = 0; + + Game_shudder_time = -1; + + // Initialize the game subsystems +// timestamp_reset(); // Must be inited before everything else + if(!Is_standalone){ + game_reset_time(); // resets time, and resets saved time too + } + obj_init(); // Must be inited before the other systems + model_free_all(); // Free all existing models + mission_brief_common_init(); // Free all existing briefing/debriefing text + weapon_level_init(); + ai_level_init(); // Call this before ship_init() because it reads ai.tbl. + ship_level_init(); + player_level_init(); + shipfx_flash_init(); // Init the ship gun flash system. + game_flash_reset(); // Reset the flash effect + particle_init(); // Reset the particle system + fireball_init(); + debris_init(); + cmeasure_init(); + shield_hit_init(); // Initialize system for showing shield hits + radar_mission_init(); + mission_init_goals(); + mission_log_init(); + messages_init(); + obj_snd_level_init(); // init object-linked persistant sounds + anim_level_init(); + shockwave_level_init(); + afterburner_level_init(); + scoring_level_init( &Player->stats ); + key_level_init(); + asteroid_level_init(); + control_config_clear_used_status(); + collide_ship_ship_sounds_init(); + Missiontime = 0; + Pre_player_entry = 1; // Means the player has not yet entered. + Entry_delay_time = 0; // Could get overwritten in mission read. + fireball_preload(); // page in warphole bitmaps + observer_init(); + flak_level_init(); // initialize flak - bitmaps, etc + ct_level_init(); // initialize ships contrails, etc + awacs_level_init(); // initialize AWACS + beam_level_init(); // initialize beam weapons + mflash_level_init(); + ssm_level_init(); + supernova_level_init(); + + // multiplayer dogfight hack + dogfight_blown = 0; + + shipfx_engine_wash_level_init(); + + nebl_level_init(); + + Last_view_target = NULL; + Game_paused = 0; + + Game_no_clear = 0; + + // campaign wasn't ended + Campaign_ended_in_mission = 0; +} + +// called when a mission is over -- does server specific stuff. +void freespace_stop_mission() +{ + game_level_close(); + Game_mode &= ~GM_IN_MISSION; +} + +// called at frame interval to process networking stuff +void game_do_networking() +{ + Assert( Net_player != NULL ); + if (!(Game_mode & GM_MULTIPLAYER)){ + return; + } + + // see if this player should be reading/writing data. Bit is set when at join + // screen onward until quits back to main menu. + if ( !(Net_player->flags & NETINFO_FLAG_DO_NETWORKING) ){ + return; + } + + if(gameseq_get_state()!=GS_STATE_MULTI_PAUSED){ + multi_do_frame(); + } else { + multi_pause_do_frame(); + } +} + + +// Loads the best palette for this level, based +// on nebula color and hud color. You could just call palette_load_table with +// the appropriate filename, but who wants to do that. +void game_load_palette() +{ + char palette_filename[1024]; + + // We only use 3 hud colors right now + // Assert( HUD_config.color >= 0 ); + // Assert( HUD_config.color <= 2 ); + + Assert( Mission_palette >= 0 ); + Assert( Mission_palette <= 98 ); + + // if ( The_mission.flags & MISSION_FLAG_SUBSPACE ) { + strcpy( palette_filename, NOX("gamepalette-subspace") ); + // } else { + // sprintf( palette_filename, NOX("gamepalette%d-%02d"), HUD_config.color+1, Mission_palette+1 ); + // } + + mprintf(( "Loading palette %s\n", palette_filename )); + + // palette_load_table(palette_filename); +} + +void game_post_level_init() +{ + // Stuff which gets called after mission is loaded. Because player isn't created until + // after mission loads, some things must get initted after the level loads + + model_level_post_init(); + + HUD_init(); + hud_setup_escort_list(); + mission_hotkey_set_defaults(); // set up the default hotkeys (from mission file) + + stars_level_init(); + neb2_level_init(); + +#ifndef NDEBUG + game_event_debug_init(); +#endif + + training_mission_init(); + asteroid_create_all(); + + game_framerate_check_init(); +} + + +// An estimate as to how high the count passed to game_loading_callback will go. +// This is just a guess, it seems to always be about the same. The count is +// proportional to the code being executed, not the time, so this works good +// for a bar, assuming the code does about the same thing each time you +// load a level. You can find this value by looking at the return value +// of game_busy_callback(NULL), which I conveniently print out to the +// debug output window with the '=== ENDING LOAD ==' stuff. +//#define COUNT_ESTIMATE 3706 +#define COUNT_ESTIMATE 1111 + +int Game_loading_callback_inited = 0; + +int Game_loading_background = -1; +anim * Game_loading_ani = NULL; +anim_instance *Game_loading_ani_instance; +int Game_loading_frame=-1; + +static int Game_loading_ani_coords[GR_NUM_RESOLUTIONS][2] = { + { + 63, 316 // GR_640 + }, + { + 101, 505 // GR_1024 + } +}; + +// This gets called 10x per second and count is the number of times +// game_busy() has been called since the current callback function +// was set. +void game_loading_callback(int count) +{ + game_do_networking(); + + Assert( Game_loading_callback_inited==1 ); + Assert( Game_loading_ani != NULL ); + + int framenum = ((Game_loading_ani->total_frames*count) / COUNT_ESTIMATE)+1; + if ( framenum > Game_loading_ani->total_frames-1 ) { + framenum = Game_loading_ani->total_frames-1; + } else if ( framenum < 0 ) { + framenum = 0; + } + + int cbitmap = -1; + while ( Game_loading_frame < framenum ) { + Game_loading_frame++; + cbitmap = anim_get_next_frame(Game_loading_ani_instance); + } + + + if ( cbitmap > -1 ) { + if ( Game_loading_background > -1 ) { + gr_set_bitmap( Game_loading_background ); + gr_bitmap(0,0); + } + + //mprintf(( "Showing frame %d/%d [ Bitmap=%d ]\n", Game_loading_frame , Game_loading_ani->total_frames, cbitmap )); + gr_set_bitmap( cbitmap ); + gr_bitmap(Game_loading_ani_coords[gr_screen.res][0],Game_loading_ani_coords[gr_screen.res][1]); + + bm_release(cbitmap); + + gr_flip(); + } +} + +void game_loading_callback_init() +{ + Assert( Game_loading_callback_inited==0 ); + + Game_loading_background = bm_load(Game_loading_bground_fname[gr_screen.res]); + //common_set_interface_palette("InterfacePalette"); // set the interface palette + + + Game_loading_ani = anim_load( Game_loading_ani_fname[gr_screen.res]); + Assert( Game_loading_ani != NULL ); + Game_loading_ani_instance = init_anim_instance(Game_loading_ani, 16); + Assert( Game_loading_ani_instance != NULL ); + Game_loading_frame = -1; + + Game_loading_callback_inited = 1; + Mouse_hidden = 1; + game_busy_callback( game_loading_callback, (COUNT_ESTIMATE/Game_loading_ani->total_frames)+1 ); + + +} + +void game_loading_callback_close() +{ + Assert( Game_loading_callback_inited==1 ); + + // Make sure bar shows all the way over. + game_loading_callback(COUNT_ESTIMATE); + + int real_count = game_busy_callback( NULL ); + Mouse_hidden = 0; + + Game_loading_callback_inited = 0; + +#ifndef NDEBUG + mprintf(( "=================== ENDING LOAD ================\n" )); + mprintf(( "Real count = %d, Estimated count = %d\n", real_count, COUNT_ESTIMATE )); + mprintf(( "================================================\n" )); +#else + // to remove warnings in release build + real_count = 0; +#endif + + free_anim_instance(Game_loading_ani_instance); + Game_loading_ani_instance = NULL; + anim_free(Game_loading_ani); + Game_loading_ani = NULL; + + bm_release( Game_loading_background ); + common_free_interface_palette(); // restore game palette + Game_loading_background = -1; + + gr_set_font( FONT1 ); +} + +// Update the sound environment (ie change EAX settings based on proximity to large ships) +// +void game_maybe_update_sound_environment() +{ + // do nothing for now +} + +// Assign the sound environment for the game, based on the current mission +// +void game_assign_sound_environment() +{ + /* + if (The_mission.flags & MISSION_FLAG_SUBSPACE) { + Game_sound_env.id = SND_ENV_DRUGGED; + Game_sound_env.volume = 0.800f; + Game_sound_env.damping = 1.188f; + Game_sound_env.decay = 6.392f; +#ifndef FS2_DEMO + } else if (Num_asteroids > 30) { + Game_sound_env.id = SND_ENV_AUDITORIUM; + Game_sound_env.volume = 0.603f; + Game_sound_env.damping = 0.5f; + Game_sound_env.decay = 4.279f; +#endif + } else { + Game_sound_env = Game_default_sound_env; + } + */ + + Game_sound_env = Game_default_sound_env; + Game_sound_env_update_timestamp = timestamp(1); +} + +// function which gets called before actually entering the mission. It is broken down into a funciton +// since it will get called in one place from a single player game and from another place for +// a multiplayer game +void freespace_mission_load_stuff() +{ + // called if we're not on a freespace dedicated (non rendering, no pilot) server + // IE : we _don't_ want to load any sounds or bitmap/texture info on this machine. + if(!(Game_mode & GM_STANDALONE_SERVER)){ + + mprintf(( "=================== STARTING LEVEL DATA LOAD ==================\n" )); + + game_loading_callback_init(); + + event_music_level_init(); // preloads the first 2 seconds for each event music track + game_busy(); + + gamesnd_unload_interface_sounds(); // unload interface sounds from memory + game_busy(); + + gamesnd_preload_common_sounds(); // load in sounds that are expected to play + game_busy(); + + ship_assign_sound_all(); // assign engine sounds to ships + game_assign_sound_environment(); // assign the sound environment for this mission + game_busy(); + + // call function in missionparse.cpp to fixup player/ai stuff. + mission_parse_fixup_players(); + game_busy(); + + // Load in all the bitmaps for this level + level_page_in(); + + game_busy(); + + game_loading_callback_close(); + } + // the only thing we need to call on the standalone for now. + else { + // call function in missionparse.cpp to fixup player/ai stuff. + mission_parse_fixup_players(); + + // Load in all the bitmaps for this level + level_page_in(); + } +} + +uint load_gl_init; +uint load_mission_load; +uint load_post_level_init; +uint load_mission_stuff; + +// tells the server to load the mission and initialize structures +int game_start_mission() +{ + mprintf(( "=================== STARTING LEVEL LOAD ==================\n" )); + + load_gl_init = time(NULL); + game_level_init(); + load_gl_init = time(NULL) - load_gl_init; + + if (Game_mode & GM_MULTIPLAYER) { + Player->flags |= PLAYER_FLAGS_IS_MULTI; + + // clear multiplayer stats + init_multiplayer_stats(); + } + + load_mission_load = time(NULL); + if (mission_load()) { + if ( !(Game_mode & GM_MULTIPLAYER) ) { + popup(PF_BODY_BIG, 1, POPUP_OK, XSTR( "Attempt to load the mission failed", 169)); + gameseq_post_event(GS_EVENT_MAIN_MENU); + } else { + multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_LOAD_FAIL); + } + + return 0; + } + load_mission_load = time(NULL) - load_mission_load; + + // If this is a red alert mission in campaign mode, bash wingman status + if ( (Game_mode & GM_CAMPAIGN_MODE) && red_alert_mission() ) { + red_alert_bash_wingman_status(); + } + + // the standalone server in multiplayer doesn't do any rendering, so we will not even bother loading the palette + if ( !(Game_mode & GM_STANDALONE_SERVER) ) { + mprintf(( "=================== LOADING GAME PALETTE ================\n" )); + // game_load_palette(); + } + + load_post_level_init = time(NULL); + game_post_level_init(); + load_post_level_init = time(NULL) - load_post_level_init; + + #ifndef NDEBUG + { + void Do_model_timings_test(); + Do_model_timings_test(); + } + #endif + + load_mission_stuff = time(NULL); + freespace_mission_load_stuff(); + load_mission_stuff = time(NULL) - load_mission_stuff; + + return 1; +} + +int Interface_framerate = 0; +#ifndef NDEBUG + +DCF_BOOL( mouse_control, Use_mouse_to_fly ) +DCF_BOOL( show_framerate, Show_framerate ) +DCF_BOOL( show_target_debug_info, Show_target_debug_info ) +DCF_BOOL( show_target_weapons, Show_target_weapons ) +DCF_BOOL( lead_target_cheat, Players[Player_num].lead_target_cheat ) +DCF_BOOL( sound, Sound_enabled ) +DCF_BOOL( zbuffer, game_zbuffer ) +DCF_BOOL( shield_system, New_shield_system ) +DCF_BOOL( show_shield_mesh, Show_shield_mesh) +DCF_BOOL( player_attacking, Player_attacking_enabled ) +DCF_BOOL( show_waypoints, Show_waypoints ) +DCF_BOOL( show_area_effect, Show_area_effect ) +DCF_BOOL( show_net_stats, Show_net_stats ) +DCF_BOOL( log, Log_debug_output_to_file ) +DCF_BOOL( training_msg_method, Training_msg_method ) +DCF_BOOL( show_player_pos, Show_player_pos ) +DCF_BOOL(i_framerate, Interface_framerate ) + +DCF(show_mem,"Toggles showing mem usage") +{ + if ( Dc_command ) { + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); + if ( Dc_arg_type & ARG_TRUE ) Show_mem = 1; + else if ( Dc_arg_type & ARG_FALSE ) Show_mem = 0; + else if ( Dc_arg_type & ARG_NONE ) Show_mem ^= 1; + + if ( Show_mem ) { + Show_cpu = 0; + } + } + if ( Dc_help ) dc_printf( "Usage: Show_mem\nSets show_mem to true or false. If nothing passed, then toggles it.\n" ); + if ( Dc_status ) { + dc_printf( "Show_mem is %s\n", (Show_mem?"TRUE":"FALSE") ); + dc_printf( "Show_cpu is %s\n", (Show_cpu?"TRUE":"FALSE") ); + } +} + +DCF(show_cpu,"Toggles showing cpu usage") +{ + if ( Dc_command ) { + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); + if ( Dc_arg_type & ARG_TRUE ) Show_cpu = 1; + else if ( Dc_arg_type & ARG_FALSE ) Show_cpu = 0; + else if ( Dc_arg_type & ARG_NONE ) Show_cpu ^= 1; + + if ( Show_cpu ) { + Show_mem = 0; + } + } + if ( Dc_help ) dc_printf( "Usage: Show_cpu\nSets show_cpu to true or false. If nothing passed, then toggles it.\n" ); + if ( Dc_status ) { + dc_printf( "Show_mem is %s\n", (Show_mem?"TRUE":"FALSE") ); + dc_printf( "Show_cpu is %s\n", (Show_cpu?"TRUE":"FALSE") ); + + } +} + +#else + + // AL 4-8-98: always allow players to display their framerate + + #ifdef FS2_DEMO + DCF_BOOL( show_framerate, Show_framerate ) + #endif + +#endif // NDEBUG + + int Game_init_seed; + +DCF(use_joy_mouse,"Makes joystick move mouse cursor") +{ + if ( Dc_command ) { + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); + if ( Dc_arg_type & ARG_TRUE ) Use_joy_mouse = 1; + else if ( Dc_arg_type & ARG_FALSE ) Use_joy_mouse = 0; + else if ( Dc_arg_type & ARG_NONE ) Use_joy_mouse ^= 1; + } + if ( Dc_help ) dc_printf( "Usage: use_joy_mouse [bool]\nSets use_joy_mouse to true or false. If nothing passed, then toggles it.\n" ); + if ( Dc_status ) dc_printf( "use_joy_mouse is %s\n", (Use_joy_mouse?"TRUE":"FALSE") ); + + os_config_write_uint( NULL, NOX("JoystickMovesCursor"), Use_joy_mouse ); +} + +DCF(palette_flash,"Toggles palette flash effect on/off") +{ + if ( Dc_command ) { + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); + if ( Dc_arg_type & ARG_TRUE ) Use_palette_flash = 1; + else if ( Dc_arg_type & ARG_FALSE ) Use_palette_flash = 0; + else if ( Dc_arg_type & ARG_NONE ) Use_palette_flash ^= 1; + } + if ( Dc_help ) dc_printf( "Usage: palette_flash [bool]\nSets palette_flash to true or false. If nothing passed, then toggles it.\n" ); + if ( Dc_status ) dc_printf( "palette_flash is %s\n", (Use_palette_flash?"TRUE":"FALSE") ); +} + +int Use_low_mem = 0; + +DCF(low_mem,"Uses low memory settings regardless of RAM") +{ + if ( Dc_command ) { + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); + if ( Dc_arg_type & ARG_TRUE ) Use_low_mem = 1; + else if ( Dc_arg_type & ARG_FALSE ) Use_low_mem = 0; + else if ( Dc_arg_type & ARG_NONE ) Use_low_mem ^= 1; + } + if ( Dc_help ) dc_printf( "Usage: low_mem [bool]\nSets low_mem to true or false. If nothing passed, then toggles it.\n" ); + if ( Dc_status ) dc_printf( "low_mem is %s\n", (Use_low_mem?"TRUE":"FALSE") ); + + os_config_write_uint( NULL, NOX("LowMem"), Use_low_mem ); +} + + +#ifndef NDEBUG + +DCF(force_fullscreen, "Forces game to startup in fullscreen mode") +{ + if ( Dc_command ) { + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); + if ( Dc_arg_type & ARG_TRUE ) Use_fullscreen_at_startup = 1; + else if ( Dc_arg_type & ARG_FALSE ) Use_fullscreen_at_startup = 0; + else if ( Dc_arg_type & ARG_NONE ) Use_fullscreen_at_startup ^= 1; + } + if ( Dc_help ) dc_printf( "Usage: force_fullscreen [bool]\nSets force_fullscreen to true or false. If nothing passed, then toggles it.\n" ); + if ( Dc_status ) dc_printf( "force_fullscreen is %s\n", (Use_fullscreen_at_startup?"TRUE":"FALSE") ); + os_config_write_uint( NULL, NOX("ForceFullscreen"), Use_fullscreen_at_startup ); +} +#endif + +int Framerate_delay = 0; + +float Freespace_gamma = 1.0f; + +DCF(gamma,"Sets Gamma factor") +{ + if ( Dc_command ) { + dc_get_arg(ARG_FLOAT|ARG_NONE); + if ( Dc_arg_type & ARG_FLOAT ) { + Freespace_gamma = Dc_arg_float; + } else { + dc_printf( "Gamma reset to 1.0f\n" ); + Freespace_gamma = 1.0f; + } + if ( Freespace_gamma < 0.1f ) { + Freespace_gamma = 0.1f; + } else if ( Freespace_gamma > 5.0f ) { + Freespace_gamma = 5.0f; + } + gr_set_gamma(Freespace_gamma); + + char tmp_gamma_string[32]; + sprintf( tmp_gamma_string, NOX("%.2f"), Freespace_gamma ); + os_config_write_string( NULL, NOX("Gamma"), tmp_gamma_string ); + } + + if ( Dc_help ) { + dc_printf( "Usage: gamma \n" ); + dc_printf( "Sets gamma in range 1-3, no argument resets to default 1.2\n" ); + Dc_status = 0; // don't print status if help is printed. Too messy. + } + + if ( Dc_status ) { + dc_printf( "Gamma = %.2f\n", Freespace_gamma ); + } +} + +void game_init() +{ + char *ptr; + int depth = 16; + + Game_current_mission_filename[0] = 0; + + // seed the random number generator + Game_init_seed = time(NULL); + srand( Game_init_seed ); + + Framerate_delay = 0; + + #ifndef NDEBUG + load_filter_info(); + #endif + + extern void bm_init(); + bm_init(); + + // encrypt stuff + encrypt_init(); + + // Initialize the timer before the os + timer_init(); + + int s1, e1; + // int s2, e2; + + char whee[1024]; + GetCurrentDirectory(1024, whee); + strcat(whee, "\\"); + strcat(whee, EXE_FNAME); + + //Initialize the libraries + s1 = timer_get_milliseconds(); + if(cfile_init(whee, Game_CDROM_dir)){ // initialize before calling any cfopen stuff!!! + exit(1); + } + e1 = timer_get_milliseconds(); + + // time a bunch of cfopens + /* + s2 = timer_get_milliseconds(); + CFILE *whee; + for(int idx=0; idx<10000; idx++){ + whee = cfopen("capital01.pof", "rb", CFILE_NORMAL, CF_TYPE_MODELS); + if(whee != NULL){ + cfclose(whee); + } + whee = NULL; + //cf_exist("capital01.pof", CF_TYPE_MODELS); + } + e2 = timer_get_milliseconds(); + */ + + if (Is_standalone) { + std_init_standalone(); + } else { + os_init( Osreg_class_name, Osreg_app_name ); + os_set_title(Osreg_title); + } + + // initialize localization module. Make sure this is down AFTER initialzing OS. +// int t1 = timer_get_milliseconds(); + lcl_init(); + lcl_xstr_init(); +// mprintf(("LCL_INIT() TOOK %d MS\n", timer_get_milliseconds()-t1)); + + // verify that he has a valid ships.tbl (will Game_ships_tbl_valid if so) + verify_ships_tbl(); + + // verify that he has a valid weapons.tbl + verify_weapons_tbl(); + + // Output version numbers to registry for auto patching purposes + os_config_write_uint(NOX("Version"), NOX("Major"), FS_VERSION_MAJOR); + os_config_write_uint(NOX("Version"), NOX("Minor"), FS_VERSION_MINOR); + os_config_write_uint(NOX("Version"), NOX("Build"), FS_VERSION_BUILD); + + Use_joy_mouse = 0; //os_config_read_uint( NULL, NOX("JoystickMovesCursor"), 1 ); + //Use_palette_flash = os_config_read_uint( NULL, NOX("PaletteFlash"), 0 ); + Use_low_mem = os_config_read_uint( NULL, NOX("LowMem"), 0 ); + +#ifndef NDEBUG + Use_fullscreen_at_startup = os_config_read_uint( NULL, NOX("ForceFullscreen"), 1 ); +#endif + +#ifndef FS2_DEMO + Asteroids_enabled = 1; +#endif + +///////////////////////////// +// SOUND INIT START +///////////////////////////// + + int use_a3d = 0; + int use_eax = 0; + + ptr = os_config_read_string(NULL, NOX("Soundcard"), NULL); + mprintf(("soundcard = %s\n", ptr ? ptr : "")); + if (ptr) { + if (!stricmp(ptr, NOX("no sound"))) { + Cmdline_freespace_no_sound = 1; + + } else if (!stricmp(ptr, NOX("Aureal A3D"))) { + use_a3d = 1; + } else if (!stricmp(ptr, NOX("EAX"))) { + use_eax = 1; + } + } + + if (!Is_standalone) { + snd_init(use_a3d, use_eax); + } +///////////////////////////// +// SOUND INIT END +///////////////////////////// + + ptr = os_config_read_string(NULL, NOX("Videocard"), NULL); + if (ptr == NULL) { + MessageBox((HWND)os_get_window(), XSTR("Please configure your system in the Launcher before running FS2.\n\n The Launcher will now be started!", 1446), XSTR("Attention!", 1447), MB_OK); + + // fire up the UpdateLauncher executable + STARTUPINFO si; + PROCESS_INFORMATION pi; + + memset( &si, 0, sizeof(STARTUPINFO) ); + si.cb = sizeof(si); + + BOOL ret = CreateProcess( LAUNCHER_FNAME, // pointer to name of executable module + NULL, // pointer to command line string + NULL, // pointer to process security attributes + NULL, // pointer to thread security attributes + FALSE, // handle inheritance flag + CREATE_DEFAULT_ERROR_MODE, // creation flags + NULL, // pointer to new environment block + NULL, // pointer to current directory name + &si, // pointer to STARTUPINFO + &pi // pointer to PROCESS_INFORMATION + ); + + // If the Launcher could not be started up, let the user know + if (!ret) { + MessageBox((HWND)os_get_window(), XSTR("The Launcher could not be restarted.", 1450), XSTR("Error", 1451), MB_OK); + } + exit(1); + } + + if(!Is_standalone){ + + if(!stricmp(ptr, "Aucune accélération 3D") || !stricmp(ptr, "Keine 3D-Beschleunigerkarte") || !stricmp(ptr, "No 3D acceleration")){ + MessageBox((HWND)os_get_window(), XSTR("Warning, Freespace 2 requires Glide or Direct3D hardware accleration. You will not be able to run Freespace 2 without it.", 1448), XSTR("Warning", 1449), MB_OK); + exit(1); + } + } + + // check for hi res pack file + int has_sparky_hi = 0; + + // check if sparky_hi exists -- access mode 0 means does file exist + char dir[128]; + _getcwd(dir, 128); + if ( _access("sparky_hi_fs2.vp", 0) == 0) { + has_sparky_hi = 1; + } else { + mprintf(("No sparky_hi_fs2.vp in directory %s\n", dir)); + } + + // see if we've got 32 bit in the string + if(strstr(ptr, "32 bit")){ + depth = 32; + } + + int trying_d3d = 0; + + if (!Is_standalone && ptr && (strstr(ptr, NOX("3DFX Glide")))) { +#ifdef E3_BUILD + // always 640 for E3 + gr_init(GR_640, GR_GLIDE); +#else + // regular or hi-res ? +#ifdef NDEBUG + if(has_sparky_hi && strstr(ptr, NOX("(1024x768)"))){ +#else + if(strstr(ptr, NOX("(1024x768)"))){ +#endif + gr_init(GR_1024, GR_GLIDE); + } else { + gr_init(GR_640, GR_GLIDE); + } +#endif + } else if (!Is_standalone && ptr && (strstr(ptr, NOX("Direct 3D -") ))) { +#ifdef E3_BUILD + // always 640 for E3 + trying_d3d = 1; + gr_init(GR_640, GR_DIRECT3D, depth); +#else + // regular or hi-res ? +#ifdef NDEBUG + if(has_sparky_hi && strstr(ptr, NOX("(1024x768)"))){ +#else + if(strstr(ptr, NOX("(1024x768)"))){ +#endif + // Direct 3D + trying_d3d = 1; + gr_init(GR_1024, GR_DIRECT3D, depth); + } else { + // Direct 3D + trying_d3d = 1; + gr_init(GR_640, GR_DIRECT3D, depth); + } +#endif + } else { + // Software + #ifndef NDEBUG + if ( Use_fullscreen_at_startup && !Is_standalone) { + gr_init(GR_640, GR_DIRECTDRAW); + } else { + gr_init(GR_640, GR_SOFTWARE); + } + #else + if ( !Is_standalone ) { + gr_init(GR_640, GR_DIRECTDRAW); + } else { + gr_init(GR_640, GR_SOFTWARE); + } + #endif + } + + // tried d3d ? + extern int Gr_inited; + if(trying_d3d && !Gr_inited){ + extern char Device_init_error[512]; + MessageBox( NULL, Device_init_error, "Error intializing Direct3D", MB_OK|MB_TASKMODAL|MB_SETFOREGROUND ); + exit(1); + return; + } + + // Set the gamma + ptr = os_config_read_string(NULL,NOX("Gamma"),NOX("1.80")); + Freespace_gamma = (float)atof(ptr); + if ( Freespace_gamma < 0.1f ) { + Freespace_gamma = 0.1f; + } else if ( Freespace_gamma > 5.0f ) { + Freespace_gamma = 5.0f; + } + char tmp_gamma_string[32]; + sprintf( tmp_gamma_string, NOX("%.2f"), Freespace_gamma ); + os_config_write_string( NULL, NOX("Gamma"), tmp_gamma_string ); + + gr_set_gamma(Freespace_gamma); + +#if defined(FS2_DEMO) || defined(OEM_BUILD) + // add title screen + if(!Is_standalone){ + display_title_screen(); + } +#endif + + // attempt to load up master tracker registry info (login and password) + Multi_tracker_id = -1; + + // pxo login and password + ptr = os_config_read_string(NOX("PXO"),NOX("Login"),NULL); + if(ptr == NULL){ + nprintf(("Network","Error reading in PXO login data\n")); + strcpy(Multi_tracker_login,""); + } else { + strcpy(Multi_tracker_login,ptr); + } + ptr = os_config_read_string(NOX("PXO"),NOX("Password"),NULL); + if(ptr == NULL){ + nprintf(("Network","Error reading PXO password\n")); + strcpy(Multi_tracker_passwd,""); + } else { + strcpy(Multi_tracker_passwd,ptr); + } + + // pxo squad name and password + ptr = os_config_read_string(NOX("PXO"),NOX("SquadName"),NULL); + if(ptr == NULL){ + nprintf(("Network","Error reading in PXO squad name\n")); + strcpy(Multi_tracker_squad_name, ""); + } else { + strcpy(Multi_tracker_squad_name, ptr); + } + + // If less than 48MB of RAM, use low memory model. + if ( (Freespace_total_ram < 48*1024*1024) || Use_low_mem ) { + mprintf(( "Using normal memory settings...\n" )); + bm_set_low_mem(1); // Use every other frame of bitmaps + } else { + mprintf(( "Using high memory settings...\n" )); + bm_set_low_mem(0); // Use all frames of bitmaps + } + + // load non-darkening pixel defs + palman_load_pixels(); + + // hud shield icon stuff + hud_shield_game_init(); + + control_config_common_init(); // sets up localization stuff in the control config + parse_rank_tbl(); + parse_medal_tbl(); + cutscene_init(); + key_init(); + mouse_init(); + gamesnd_parse_soundstbl(); + radar_init(); + gameseq_init(); + multi_init(); + + // standalone's don't use hte joystick and it seems to sometimes cause them to not get shutdown properly + if(!Is_standalone){ + joy_init(); + } + + player_controls_init(); + model_init(); + + //if(!Is_standalone){ + event_music_init(); + //} + + obj_init(); + mflash_game_init(); + weapon_init(); + ai_init(); + ship_init(); // read in ships.tbl + player_init(); + mission_campaign_init(); // load in the default campaign + anim_init(); +// navmap_init(); // init the navigation map system + context_help_init(); + techroom_intel_init(); // parse species.tbl, load intel info + // initialize psnet + psnet_init( Multi_options_g.protocol, Multi_options_g.port ); // initialize the networking code + init_animating_pointer(); + asteroid_init(); + mission_brief_common_init(); // Mark all the briefing structures as empty. + gr_font_init(); // loads up all fonts + + neb2_init(); // fullneb stuff + nebl_init(); + stars_init(); + ssm_init(); + player_tips_init(); // helpful tips + beam_init(); + + // load the list of pilot pic filenames (for barracks and pilot select popup quick reference) + pilot_load_pic_list(); + pilot_load_squad_pic_list(); + + load_animating_pointer(NOX("cursor"), 0, 0); + + // initialize alpha colors + alpha_colors_init(); + + Viewer_mode = 0; +// Game_music_paused = 0; + Game_paused = 0; + + timeBeginPeriod(1); + + nprintf(("General", "Ships.tbl is : %s\n", Game_ships_tbl_valid ? "VALID" : "INVALID!!!!")); + nprintf(("General", "Weapons.tbl is : %s\n", Game_weapons_tbl_valid ? "VALID" : "INVALID!!!!")); + + mprintf(("cfile_init() took %d\n", e1 - s1)); + // mprintf(("1000 cfopens() took %d\n", e2 - s2)); +} + +char transfer_text[128]; + +float Start_time = 0.0f; + +float Framerate = 0.0f; + +float Timing_total = 0.0f; +float Timing_render2 = 0.0f; +float Timing_render3 = 0.0f; +float Timing_flip = 0.0f; +float Timing_clear = 0.0f; + +MONITOR(NumPolysDrawn); +MONITOR(NumPolys); +MONITOR(NumVerts); +MONITOR(BmpUsed); +MONITOR(BmpNew); + +void game_get_framerate() +{ + char text[128] = ""; + + if ( frame_int == -1 ) { + int i; + for (i=0; i= FRAME_FILTER ) + Framerate = FRAME_FILTER / frametotal; + else + Framerate = Framecount / frametotal; + sprintf( text, NOX("FPS: %.1f"), Framerate ); + } else { + sprintf( text, NOX("FPS: ?") ); + } + Framecount++; + + if (Show_framerate) { + gr_set_color_fast(&HUD_color_debug); + gr_string( 570, 2, text ); + } +} + +void game_show_framerate() +{ + float cur_time; + + cur_time = f2fl(timer_get_approx_seconds()); + if (cur_time - Start_time > 30.0f) { + mprintf(("%i frames executed in %7.3f seconds, %7.3f frames per second.\n", Framecount, cur_time - Start_time, Framecount/(cur_time - Start_time))); + Start_time += 1000.0f; + } + + //mprintf(( "%s\n", text )); + +#ifndef NDEBUG + if ( Debug_dump_frames ) + return; +#endif + + // possibly show control checking info + control_check_indicate(); + +// int bitmaps_used_this_frame, bitmaps_new_this_frame; +// bm_get_frame_usage(&bitmaps_used_this_frame,&bitmaps_new_this_frame); +// MONITOR_INC(BmpUsed, bitmaps_used_this_frame); +// MONITOR_INC(BmpNew, bitmaps_new_this_frame); + +#ifndef NDEBUG + if ( Show_cpu == 1 ) { + + int sx,sy,dy; + sx = 530; + sy = 15; + dy = gr_get_font_height() + 1; + + gr_set_color_fast(&HUD_color_debug); + + { + extern int D3D_textures_in; + extern int D3D_textures_in_frame; + extern int Glide_textures_in; + extern int Glide_textures_in_frame; + extern int Glide_explosion_vram; + gr_printf( sx, sy, NOX("VRAM: %d KB\n"), (D3D_textures_in+Glide_textures_in)/1024 ); + sy += dy; + gr_printf( sx, sy, NOX("VRAM: +%d KB\n"), (Glide_textures_in_frame+D3D_textures_in_frame)/1024 ); + sy += dy; + gr_printf( sx, sy, NOX("EXP VRAM: %dKB\n"), (Glide_explosion_vram)/1024 ); + sy += dy; + } +// gr_printf( sx, sy, "BPP: %d", gr_screen.bits_per_pixel ); +// sy += dy; + gr_printf( sx, sy, NOX("DMA: %s"), transfer_text ); + sy += dy; + gr_printf( sx, sy, NOX("POLYP: %d"), modelstats_num_polys ); + sy += dy; + gr_printf( sx, sy, NOX("POLYD: %d"), modelstats_num_polys_drawn ); + sy += dy; + gr_printf( sx, sy, NOX("VERTS: %d"), modelstats_num_verts ); + sy += dy; + + { + + extern int Num_pairs; // Number of object pairs that were checked. + gr_printf( sx, sy, NOX("PAIRS: %d"), Num_pairs ); + sy += dy; + + extern int Num_pairs_checked; // What percent of object pairs were checked. + gr_printf( sx, sy, NOX("FVI: %d"), Num_pairs_checked ); + sy += dy; + Num_pairs_checked = 0; + + } + + gr_printf( sx, sy, NOX("Snds: %d"), snd_num_playing() ); + sy += dy; + + if ( Timing_total > 0.01f ) { + gr_printf( sx, sy, NOX("CLEAR: %.0f%%"), Timing_clear*100.0f/Timing_total ); + sy += dy; + gr_printf( sx, sy, NOX("REND2D: %.0f%%"), Timing_render2*100.0f/Timing_total ); + sy += dy; + gr_printf( sx, sy, NOX("REND3D: %.0f%%"), Timing_render3*100.0f/Timing_total ); + sy += dy; + gr_printf( sx, sy, NOX("FLIP: %.0f%%"), Timing_flip*100.0f/Timing_total ); + sy += dy; + gr_printf( sx, sy, NOX("GAME: %.0f%%"), (Timing_total-(Timing_render2+Timing_render3+Timing_flip+Timing_clear))*100.0f/Timing_total ); + sy += dy; + } + } + + if ( Show_mem ) { + + int sx,sy,dy; + sx = 530; + sy = 15; + dy = gr_get_font_height() + 1; + + gr_set_color_fast(&HUD_color_debug); + + { + extern int TotalRam; + gr_printf( sx, sy, NOX("DYN: %d KB\n"), TotalRam/1024 ); + sy += dy; + } + + { + extern int Model_ram; + gr_printf( sx, sy, NOX("POF: %d KB\n"), Model_ram/1024 ); + sy += dy; + } + + gr_printf( sx, sy, NOX("BMP: %d KB\n"), bm_texture_ram/1024 ); + sy += dy; + gr_printf( sx, sy, NOX("S-SRAM: %d KB\n"), Snd_sram/1024 ); // mem used to store game sound + sy += dy; + gr_printf( sx, sy, NOX("S-HRAM: %d KB\n"), Snd_hram/1024 ); // mem used to store game sound + sy += dy; + { + extern int D3D_textures_in; + extern int Glide_textures_in; + extern int Glide_textures_in_frame; + extern int Glide_explosion_vram; + gr_printf( sx, sy, NOX("VRAM: %d KB\n"), (D3D_textures_in+Glide_textures_in)/1024 ); + sy += dy; + gr_printf( sx, sy, NOX("VRAM: +%d KB\n"), (Glide_textures_in_frame)/1024 ); + sy += dy; + gr_printf( sx, sy, NOX("EXP VRAM: %dKB\n"), (Glide_explosion_vram)/1024 ); + sy += dy; + } + } + + + if ( Show_player_pos ) { + int sx, sy; + sx = 320; + sy = 100; + gr_printf(sx, sy, NOX("Player Pos: (%d,%d,%d)"), fl2i(Player_obj->pos.x), fl2i(Player_obj->pos.y), fl2i(Player_obj->pos.z)); + } + + MONITOR_INC(NumPolys, modelstats_num_polys); + MONITOR_INC(NumPolysDrawn, modelstats_num_polys_drawn ); + MONITOR_INC(NumVerts, modelstats_num_verts ); + + modelstats_num_polys = 0; + modelstats_num_polys_drawn = 0; + modelstats_num_verts = 0; + modelstats_num_sortnorms = 0; +#endif +} + +void game_show_standalone_framerate() +{ + float frame_rate=30.0f; + if ( frame_int == -1 ) { + int i; + for (i=0; i= FRAME_FILTER ){ + frame_rate = FRAME_FILTER / frametotal; + } else { + frame_rate = Framecount / frametotal; + } + } + std_set_standalone_fps(frame_rate); + Framecount++; +} + +// function to show the time remaining in a mission. Used only when the end-mission sexpression is used +void game_show_time_left() +{ + int diff; + + // mission_end_time is a global from missionparse.cpp that contains the mission time at which the + // mission should end (in fixed seconds). There is code in missionparse.cpp which actually handles + // checking how much time is left + + if ( Mission_end_time == -1 ){ + return; + } + + diff = f2i(Mission_end_time - Missiontime); + // be sure to bash to 0. diff could be negative on frame that we quit mission + if ( diff < 0 ){ + diff = 0; + } + + hud_set_default_color(); + gr_printf( 5, 40, XSTR( "Mission time remaining: %d seconds", 179), diff ); +} + +//======================================================================================== +//=================== NEW DEBUG CONSOLE COMMANDS TO REPLACE OLD DEBUG PAUSE MENU ========= +//======================================================================================== + +#ifndef NDEBUG + +DCF(ai_pause,"Pauses ai") +{ + if ( Dc_command ) { + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); + if ( Dc_arg_type & ARG_TRUE ) ai_paused = 1; + else if ( Dc_arg_type & ARG_FALSE ) ai_paused = 0; + else if ( Dc_arg_type & ARG_NONE ) ai_paused = !ai_paused; + + if (ai_paused) { + obj_init_all_ships_physics(); + } + } + if ( Dc_help ) dc_printf( "Usage: ai_paused [bool]\nSets ai_paused to true or false. If nothing passed, then toggles it.\n" ); + if ( Dc_status ) dc_printf( "ai_paused is %s\n", (ai_paused?"TRUE":"FALSE") ); +} + +DCF(single_step,"Single steps the game") +{ + if ( Dc_command ) { + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); + if ( Dc_arg_type & ARG_TRUE ) game_single_step = 1; + else if ( Dc_arg_type & ARG_FALSE ) game_single_step = 0; + else if ( Dc_arg_type & ARG_NONE ) game_single_step = !game_single_step; + + last_single_step = 0; // Make so single step waits a frame before stepping + + } + if ( Dc_help ) dc_printf( "Usage: single_step [bool]\nSets single_step to true or false. If nothing passed, then toggles it.\n" ); + if ( Dc_status ) dc_printf( "single_step is %s\n", (game_single_step?"TRUE":"FALSE") ); +} + +DCF_BOOL(physics_pause, physics_paused) +DCF_BOOL(ai_rendering, Ai_render_debug_flag) +DCF_BOOL(ai_firing, Ai_firing_enabled ) + +// Create some simple aliases to these commands... +debug_command dc_s("s","shortcut for single_step",dcf_single_step); +debug_command dc_p("p","shortcut for physics_pause", dcf_physics_pause ); +debug_command dc_r("r","shortcut for ai_rendering", dcf_ai_rendering ); +debug_command dc_f("f","shortcut for ai_firing", dcf_ai_firing); +debug_command dc_a("a","shortcut for ai_pause", dcf_ai_pause); +#endif + +//======================================================================================== +//======================================================================================== + + +void game_training_pause_do() +{ + int key; + + key = game_check_key(); + if (key > 0){ + gameseq_post_event(GS_EVENT_PREVIOUS_STATE); + } + + gr_flip(); +} + + +void game_increase_skill_level() +{ + Game_skill_level++; + if (Game_skill_level >= NUM_SKILL_LEVELS){ + Game_skill_level = 0; + } +} + +int Player_died_time; + +int View_percent = 100; + + +DCF(view, "Sets the percent of the 3d view to render.") +{ + if ( Dc_command ) { + dc_get_arg(ARG_INT); + if ( (Dc_arg_int >= 5 ) || (Dc_arg_int <= 100) ) { + View_percent = Dc_arg_int; + } else { + dc_printf( "Illegal value for view. (Must be from 5-100) \n\n"); + Dc_help = 1; + } + } + + if ( Dc_help ) { + dc_printf("Usage: view [n]\nwhere n is percent of view to show (5-100).\n"); + } + + if ( Dc_status ) { + dc_printf("View is set to %d%%\n", View_percent ); + } +} + + +// Set the clip region for the 3d rendering window +void game_set_view_clip() +{ + if ((Game_mode & GM_DEAD) || (supernova_active() >= 2)) { + // Set the clip region for the letterbox "dead view" + int yborder = gr_screen.max_h/4; + + // Numeric constants encouraged by J "pig farmer" S, who shall remain semi-anonymous. + // J.S. I've changed my ways!! See the new "no constants" code!!! + gr_set_clip(0, yborder, gr_screen.max_w, gr_screen.max_h - yborder*2 ); + } else { + // Set the clip region for normal view + if ( View_percent >= 100 ) { + gr_reset_clip(); + } else { + int xborder, yborder; + + if ( View_percent < 5 ) { + View_percent = 5; + } + + float fp = i2fl(View_percent)/100.0f; + int fi = fl2i(fl_sqrt(fp)*100.0f); + if ( fi > 100 ) fi=100; + + xborder = ( gr_screen.max_w*(100-fi) )/200; + yborder = ( gr_screen.max_h*(100-fi) )/200; + + gr_set_clip(xborder, yborder, gr_screen.max_w-xborder*2,gr_screen.max_h-yborder*2 ); + } + } +} + + +void show_debug_stuff() +{ + int i; + int laser_count = 0, missile_count = 0; + + for (i=0; i= 0) && (v.sx <= gr_screen.max_w) && (v.sy >= 0) && (v.sy <= gr_screen.max_h))){ + return; + } + + // big ship? always tst + if(tst_big){ + // within 3000 meters + if( vm_vec_dist_quick(&tst_pos, &Eye_position) <= 3000.0f){ + tst = 2; + } + } else { + // within 300 meters + if( (vm_vec_dist_quick(&tst_pos, &Eye_position) <= 300.0f) && ((tst_time == 0) || ((time(NULL) - tst_time) >= 10)) ){ + tst = 2; + } + } + } + +} +void game_tst_frame() +{ + int left = 0; + + if(!Tool_enabled){ + return; + } + + // setup tst + if(tst == 2){ + tst_time = time(NULL); + + // load the tst bitmap + switch((int)frand_range(0.0f, 3.0)){ + case 0: + tst_bitmap = bm_load("ig_jim"); + left = 1; + mprintf(("TST 0\n")); + break; + + case 1: + tst_bitmap = bm_load("ig_kan"); + left = 0; + mprintf(("TST 1\n")); + break; + + case 2: + tst_bitmap = bm_load("ig_jim"); + left = 1; + mprintf(("TST 2\n")); + break; + + default: + tst_bitmap = bm_load("ig_kan"); + left = 0; + mprintf(("TST 3\n")); + break; + } + + if(tst_bitmap < 0){ + tst = 0; + return; + } + + // get the tst bitmap dimensions + int w, h; + bm_get_info(tst_bitmap, &w, &h); + + // tst y + tst_y = frand_range(0.0f, (float)gr_screen.max_h - h); + + snd_play(&Snds[SND_VASUDAN_BUP]); + + // tst x and direction + tst_mode = 0; + if(left){ + tst_x = (float)-w; + tst_offset_total = (float)w; + tst_offset = (float)w; + } else { + tst_x = (float)gr_screen.max_w; + tst_offset_total = (float)-w; + tst_offset = (float)w; + } + + tst = 1; + } + + // run tst + if(tst == 1){ + float diff = (tst_offset_total / 0.5f) * flFrametime; + + // move the bitmap + if(tst_mode == 0){ + tst_x += diff; + + tst_offset -= fl_abs(diff); + } else if(tst_mode == 2){ + tst_x -= diff; + + tst_offset -= fl_abs(diff); + } + + // draw the bitmap + gr_set_bitmap(tst_bitmap); + gr_bitmap((int)tst_x, (int)tst_y); + + if(tst_mode == 1){ + if(timestamp_elapsed_safe(tst_stamp, 1100)){ + tst_mode = 2; + } + } else { + // if we passed the switch point + if(tst_offset <= 0.0f){ + // switch modes + switch(tst_mode){ + case 0: + tst_mode = 1; + tst_stamp = timestamp(1000); + tst_offset = fl_abs(tst_offset_total); + break; + + case 2: + tst = 0; + return; + } + } + } + } +} +void game_tst_mark(object *objp, ship *shipp) +{ + ship_info *sip; + + if(!Tool_enabled){ + return; + } + + // bogus + if((objp == NULL) || (shipp == NULL) || (shipp->ship_info_index < 0) || (shipp->ship_info_index >= Num_ship_types)){ + return; + } + sip = &Ship_info[shipp->ship_info_index]; + + // already tst + if(tst){ + return; + } + + tst_pos = objp->pos; + if(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)){ + tst_big = 1; + } + tst = 3; +} + +extern void render_shields(); + +void player_repair_frame(float frametime) +{ + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + int idx; + for(idx=0;idxplayer_id != Net_players[idx].player_id) && (Net_players[idx].player != NULL) && (Net_players[idx].player->objnum >= 0) && (Net_players[idx].player->objnum < MAX_OBJECTS)){ + + // don't rearm/repair if the player is dead or dying/departing + if ( !NETPLAYER_IS_DEAD(np) && !(Ships[Objects[np->player->objnum].instance].flags & (SF_DYING|SF_DEPARTING)) ) { + ai_do_repair_frame(&Objects[Net_players[idx].player->objnum],&Ai_info[Ships[Objects[Net_players[idx].player->objnum].instance].ai_index],frametime); + } + } + } + } + if ( (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && !(Game_mode & GM_STANDALONE_SERVER) && (Player_ship != NULL) && !(Player_ship->flags & SF_DYING) ) { + ai_do_repair_frame(Player_obj, &Ai_info[Ships[Player_obj->instance].ai_index], frametime); + } +} + + +#ifndef NDEBUG +#define NUM_FRAMES_TEST 300 +#define NUM_MIXED_SOUNDS 16 +void do_timing_test(float flFrametime) +{ + static int framecount = 0; + static int test_running = 0; + static float test_time = 0.0f; + + static int snds[NUM_MIXED_SOUNDS]; + int i; + + if ( test_running ) { + framecount++; + test_time += flFrametime; + if ( framecount >= NUM_FRAMES_TEST ) { + test_running = 0; + nprintf(("General", "%d frames took %.3f seconds\n", NUM_FRAMES_TEST, test_time)); + for ( i = 0; i < NUM_MIXED_SOUNDS; i++ ) + snd_stop(snds[i]); + } + } + + if ( Test_begin == 1 ) { + framecount = 0; + test_running = 1; + test_time = 0.0f; + Test_begin = 0; + + for ( i = 0; i < NUM_MIXED_SOUNDS; i++ ) + snds[i] = -1; + + // start looping digital sounds + for ( i = 0; i < NUM_MIXED_SOUNDS; i++ ) + snds[i] = snd_play_looping( &Snds[i], 0.0f, -1, -1); + } + + +} +#endif + +DCF(dcf_fov, "Change the field of view") +{ + if ( Dc_command ) { + dc_get_arg(ARG_FLOAT|ARG_NONE); + if ( Dc_arg_type & ARG_NONE ) { + Viewer_zoom = VIEWER_ZOOM_DEFAULT; + dc_printf( "Zoom factor reset\n" ); + } + if ( Dc_arg_type & ARG_FLOAT ) { + if (Dc_arg_float < 0.25f) { + Viewer_zoom = 0.25f; + dc_printf("Zoom factor pinned at 0.25.\n"); + } else if (Dc_arg_float > 1.25f) { + Viewer_zoom = 1.25f; + dc_printf("Zoom factor pinned at 1.25.\n"); + } else { + Viewer_zoom = Dc_arg_float; + } + } + } + + if ( Dc_help ) + dc_printf( "Usage: fov [factor]\nFactor is the zoom factor btwn .25 and 1.25\nNo parameter resets it to default.\n" ); + + if ( Dc_status ) + dc_printf("Zoom factor set to %6.3f (original = 0.5, John = 0.75)", Viewer_zoom); +} + + +DCF(framerate_cap, "Sets the framerate cap") +{ + if ( Dc_command ) { + dc_get_arg(ARG_INT); + if ( (Dc_arg_int >= 1 ) || (Dc_arg_int <= 120) ) { + Framerate_cap = Dc_arg_int; + } else { + dc_printf( "Illegal value for framerate cap. (Must be from 1-120) \n\n"); + Dc_help = 1; + } + } + + if ( Dc_help ) { + dc_printf("Usage: framerate_cap [n]\nwhere n is the frames per second to cap framerate at.\n"); + dc_printf("If n is 0 or omitted, then the framerate cap is removed\n"); + dc_printf("[n] must be from 1 to 120.\n"); + } + + if ( Dc_status ) { + if ( Framerate_cap ) + dc_printf("Framerate cap is set to %d fps\n", Framerate_cap ); + else + dc_printf("There is no framerate cap currently active.\n"); + } +} + +#define MIN_DIST_TO_DEAD_CAMERA 50.0f +int Show_viewing_from_self = 0; + +void say_view_target() +{ + object *view_target; + + if ((Viewer_mode & VM_OTHER_SHIP) && (Player_ai->target_objnum != -1)) + view_target = &Objects[Player_ai->target_objnum]; + else + view_target = Player_obj; + + if (Game_mode & GM_DEAD) { + if (Player_ai->target_objnum != -1) + view_target = &Objects[Player_ai->target_objnum]; + } + + if (!(Game_mode & GM_DEAD_DIED) && ((Game_mode & (GM_DEAD_BLEW_UP)) || ((Last_view_target != NULL) && (Last_view_target != view_target)))) { + if (view_target != Player_obj){ + + char *view_target_name = NULL; + switch(Objects[Player_ai->target_objnum].type) { + case OBJ_SHIP: + view_target_name = Ships[Objects[Player_ai->target_objnum].instance].ship_name; + break; + case OBJ_WEAPON: + view_target_name = Weapon_info[Weapons[Objects[Player_ai->target_objnum].instance].weapon_info_index].name; + Viewer_mode &= ~VM_OTHER_SHIP; + break; + case OBJ_JUMP_NODE: { + char jump_node_name[128]; + strcpy(jump_node_name, XSTR( "jump node", 184)); + view_target_name = jump_node_name; + Viewer_mode &= ~VM_OTHER_SHIP; + break; + } + + default: + Int3(); + break; + } + + if ( view_target_name ) { + HUD_fixed_printf(0.0f, XSTR( "Viewing %s%s\n", 185), (Viewer_mode & VM_OTHER_SHIP) ? XSTR( "from ", 186) : "", view_target_name); + Show_viewing_from_self = 1; + } + } else { + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj->type == OBJ_OBSERVER)){ + HUD_fixed_printf(2.0f,XSTR( "Viewing from observer\n", 187)); + Show_viewing_from_self = 1; + } else { + if (Show_viewing_from_self) + HUD_fixed_printf(2.0f, XSTR( "Viewing from self\n", 188)); + } + } + } + + Last_view_target = view_target; +} + + +float Game_hit_x = 0.0f; +float Game_hit_y = 0.0f; + +// Reset at the beginning of each frame +void game_whack_reset() +{ + Game_hit_x = 0.0f; + Game_hit_y = 0.0f; +} + +// Apply a 2d whack to the player +void game_whack_apply( float x, float y ) +{ + // Do some force feedback + joy_ff_play_dir_effect(x * 80.0f, y * 80.0f); + + // Move the eye + Game_hit_x += x; + Game_hit_y += y; + +// mprintf(( "WHACK = %.1f, %.1f\n", Game_hit_x, Game_hit_y )); +} + +// call to apply a "shudder" +void game_shudder_apply(int time, float intensity) +{ + Game_shudder_time = timestamp(time); + Game_shudder_total = time; + Game_shudder_intensity = intensity; +} + +#define FF_SCALE 10000 +void apply_hud_shake(matrix *eye_orient) +{ + if (Viewer_obj == Player_obj) { + physics_info *pi = &Player_obj->phys_info; + + angles tangles; + + tangles.p = 0.0f; + tangles.h = 0.0f; + tangles.b = 0.0f; + + // Make eye shake due to afterburner + if ( !timestamp_elapsed(pi->afterburner_decay) ) { + int dtime; + + dtime = timestamp_until(pi->afterburner_decay); + + int r1 = myrand(); + int r2 = myrand(); + tangles.p += 0.07f * (float) (r1-RAND_MAX/2)/RAND_MAX * (0.5f - fl_abs(0.5f - (float) dtime/ABURN_DECAY_TIME)); + tangles.h += 0.07f * (float) (r2-RAND_MAX/2)/RAND_MAX * (0.5f - fl_abs(0.5f - (float) dtime/ABURN_DECAY_TIME)); + } + + // Make eye shake due to engine wash + extern int Wash_on; + if (Player_obj->type == OBJ_SHIP && (Ships[Player_obj->instance].wash_intensity > 0) && Wash_on ) { + int r1 = myrand(); + int r2 = myrand(); + tangles.p += 0.07f * Ships[Player_obj->instance].wash_intensity * (float) (r1-RAND_MAX/2)/RAND_MAX; + tangles.h += 0.07f * Ships[Player_obj->instance].wash_intensity * (float) (r2-RAND_MAX/2)/RAND_MAX; + + // get the intensity + float intensity = FF_SCALE * Ships[Player_obj->instance].wash_intensity; + + // vector rand_vec + vector rand_vec; + vm_vec_rand_vec_quick(&rand_vec); + + // play the effect + joy_ff_play_dir_effect(intensity*rand_vec.x, intensity*rand_vec.y); + } + + + // make hud shake due to shuddering + if(Game_shudder_time != -1){ + // if the timestamp has elapsed + if(timestamp_elapsed(Game_shudder_time)){ + Game_shudder_time = -1; + } + // otherwise apply some shudder + else { + int dtime; + + dtime = timestamp_until(Game_shudder_time); + + int r1 = myrand(); + int r2 = myrand(); + tangles.p += (Game_shudder_intensity / 200.0f) * (float) (r1-RAND_MAX/2)/RAND_MAX * (0.5f - fl_abs(0.5f - (float) dtime/(float)Game_shudder_total)); + tangles.h += (Game_shudder_intensity / 200.0f) * (float) (r2-RAND_MAX/2)/RAND_MAX * (0.5f - fl_abs(0.5f - (float) dtime/(float)Game_shudder_total)); + } + } + + matrix tm, tm2; + vm_angles_2_matrix(&tm, &tangles); + Assert(vm_vec_mag(&tm.fvec) > 0.0f); + Assert(vm_vec_mag(&tm.rvec) > 0.0f); + Assert(vm_vec_mag(&tm.uvec) > 0.0f); + vm_matrix_x_matrix(&tm2, eye_orient, &tm); + *eye_orient = tm2; + } +} + +extern void compute_slew_matrix(matrix *orient, angles *a); // TODO: move code to proper place and extern in header file + +// Player's velocity just before he blew up. Used to keep camera target moving. +vector Dead_player_last_vel = {1.0f, 1.0f, 1.0f}; + +// Set eye_pos and eye_orient based on view mode. +void game_render_frame_setup(vector *eye_pos, matrix *eye_orient) +{ + vector eye_dir; + + static int last_Viewer_mode = 0; + static int last_Game_mode = 0; + static int last_Viewer_objnum = -1; + + // This code is supposed to detect camera "cuts"... like going between + // different views. + + // determine if we need to regenerate the nebula + if( (!(last_Viewer_mode & VM_EXTERNAL) && (Viewer_mode & VM_EXTERNAL)) || // internal to external + ((last_Viewer_mode & VM_EXTERNAL) && !(Viewer_mode & VM_EXTERNAL)) || // external to internal + (!(last_Viewer_mode & VM_DEAD_VIEW) && (Viewer_mode & VM_DEAD_VIEW)) || // non dead-view to dead-view + ((last_Viewer_mode & VM_DEAD_VIEW) && !(Viewer_mode & VM_DEAD_VIEW)) || // dead-view to non dead-view + (!(last_Viewer_mode & VM_WARP_CHASE) && (Viewer_mode & VM_WARP_CHASE)) || // non warp-chase to warp-chase + ((last_Viewer_mode & VM_WARP_CHASE) && !(Viewer_mode & VM_WARP_CHASE)) || // warp-chase to non warp-chase + (!(last_Viewer_mode & VM_OTHER_SHIP) && (Viewer_mode & VM_OTHER_SHIP)) || // non other-ship to other-ship + ((last_Viewer_mode & VM_OTHER_SHIP) && !(Viewer_mode & VM_OTHER_SHIP)) || // other-ship to non-other ship + ((Viewer_mode & VM_OTHER_SHIP) && (last_Viewer_objnum != Player_ai->target_objnum)) // other ship mode, but targets changes + ) { + + // regenerate the nebula + neb2_eye_changed(); + } + + if ( (last_Viewer_mode != Viewer_mode) || (last_Game_mode != Game_mode) ) { + //mprintf(( "************** Camera cut! ************\n" )); + last_Viewer_mode = Viewer_mode; + last_Game_mode = Game_mode; + + // Camera moved. Tell stars & debris to not do blurring. + stars_camera_cut(); + } + + say_view_target(); + + if ( Viewer_mode & VM_PADLOCK_ANY ) { + player_display_packlock_view(); + } + + game_set_view_clip(); + + if (Game_mode & GM_DEAD) { + vector vec_to_deader, view_pos; + float dist; + + Viewer_mode |= VM_DEAD_VIEW; + + if (Player_ai->target_objnum != -1) { + int view_from_player = 1; + + if (Viewer_mode & VM_OTHER_SHIP) { + // View from target. + Viewer_obj = &Objects[Player_ai->target_objnum]; + + last_Viewer_objnum = Player_ai->target_objnum; + + if ( Viewer_obj->type == OBJ_SHIP ) { + ship_get_eye( eye_pos, eye_orient, Viewer_obj ); + view_from_player = 0; + } + } else { + last_Viewer_objnum = -1; + } + + if ( view_from_player ) { + // View target from player ship. + Viewer_obj = NULL; + *eye_pos = Player_obj->pos; + vm_vec_normalized_dir(&eye_dir, &Objects[Player_ai->target_objnum].pos, eye_pos); + vm_vector_2_matrix(eye_orient, &eye_dir, NULL, NULL); + } + } else { + dist = vm_vec_normalized_dir(&vec_to_deader, &Player_obj->pos, &Dead_camera_pos); + + if (dist < MIN_DIST_TO_DEAD_CAMERA) + dist += flFrametime * 16.0f; + + vm_vec_scale(&vec_to_deader, -dist); + vm_vec_add(&Dead_camera_pos, &Player_obj->pos, &vec_to_deader); + + view_pos = Player_obj->pos; + + if (!(Game_mode & GM_DEAD_BLEW_UP)) { + Viewer_mode &= ~(VM_EXTERNAL | VM_CHASE); + vm_vec_scale_add2(&Dead_camera_pos, &Original_vec_to_deader, 25.0f * flFrametime); + Dead_player_last_vel = Player_obj->phys_info.vel; + //nprintf(("AI", "Player death roll vel = %7.3f %7.3f %7.3f\n", Player_obj->phys_info.vel.x, Player_obj->phys_info.vel.y, Player_obj->phys_info.vel.z)); + } else if (Player_ai->target_objnum != -1) { + view_pos = Objects[Player_ai->target_objnum].pos; + } else { + // Make camera follow explosion, but gradually slow down. + vm_vec_scale_add2(&Player_obj->pos, &Dead_player_last_vel, flFrametime); + view_pos = Player_obj->pos; + vm_vec_scale(&Dead_player_last_vel, 0.99f); + vm_vec_scale_add2(&Dead_camera_pos, &Original_vec_to_deader, min(25.0f, vm_vec_mag_quick(&Dead_player_last_vel)) * flFrametime); + } + + *eye_pos = Dead_camera_pos; + + vm_vec_normalized_dir(&eye_dir, &Player_obj->pos, eye_pos); + + vm_vector_2_matrix(eye_orient, &eye_dir, NULL, NULL); + Viewer_obj = NULL; + } + } + + // if supernova shockwave + if(supernova_camera_cut()){ + // no viewer obj + Viewer_obj = NULL; + + // call it dead view + Viewer_mode |= VM_DEAD_VIEW; + + // set eye pos and orient + supernova_set_view(eye_pos, eye_orient); + } else { + // If already blown up, these other modes can override. + if (!(Game_mode & (GM_DEAD | GM_DEAD_BLEW_UP))) { + Viewer_mode &= ~VM_DEAD_VIEW; + + Viewer_obj = Player_obj; + + if (Viewer_mode & VM_OTHER_SHIP) { + if (Player_ai->target_objnum != -1){ + Viewer_obj = &Objects[Player_ai->target_objnum]; + last_Viewer_objnum = Player_ai->target_objnum; + } else { + Viewer_mode &= ~VM_OTHER_SHIP; + last_Viewer_objnum = -1; + } + } else { + last_Viewer_objnum = -1; + } + + if (Viewer_mode & VM_EXTERNAL) { + matrix tm, tm2; + + vm_angles_2_matrix(&tm2, &Viewer_external_info.angles); + vm_matrix_x_matrix(&tm, &Viewer_obj->orient, &tm2); + + vm_vec_scale_add(eye_pos, &Viewer_obj->pos, &tm.fvec, 2.0f * Viewer_obj->radius + Viewer_external_info.distance); + + vm_vec_sub(&eye_dir, &Viewer_obj->pos, eye_pos); + vm_vec_normalize(&eye_dir); + vm_vector_2_matrix(eye_orient, &eye_dir, &Viewer_obj->orient.uvec, NULL); + Viewer_obj = NULL; + + // Modify the orientation based on head orientation. + compute_slew_matrix(eye_orient, &Viewer_slew_angles); + + } else if ( Viewer_mode & VM_CHASE ) { + vector move_dir; + + if ( Viewer_obj->phys_info.speed < 0.1 ) + move_dir = Viewer_obj->orient.fvec; + else { + move_dir = Viewer_obj->phys_info.vel; + vm_vec_normalize(&move_dir); + } + + vm_vec_scale_add(eye_pos, &Viewer_obj->pos, &move_dir, -3.0f * Viewer_obj->radius - Viewer_chase_info.distance); + vm_vec_scale_add2(eye_pos, &Viewer_obj->orient.uvec, 0.75f * Viewer_obj->radius); + vm_vec_sub(&eye_dir, &Viewer_obj->pos, eye_pos); + vm_vec_normalize(&eye_dir); + + // JAS: I added the following code because if you slew up using + // Descent-style physics, eye_dir and Viewer_obj->orient.uvec are + // equal, which causes a zero-length vector in the vm_vector_2_matrix + // call because the up and the forward vector are the same. I fixed + // it by adding in a fraction of the right vector all the time to the + // up vector. + vector tmp_up = Viewer_obj->orient.uvec; + vm_vec_scale_add2( &tmp_up, &Viewer_obj->orient.rvec, 0.00001f ); + + vm_vector_2_matrix(eye_orient, &eye_dir, &tmp_up, NULL); + Viewer_obj = NULL; + + // Modify the orientation based on head orientation. + compute_slew_matrix(eye_orient, &Viewer_slew_angles); + } else if ( Viewer_mode & VM_WARP_CHASE ) { + *eye_pos = Camera_pos; + + ship * shipp = &Ships[Player_obj->instance]; + + vm_vec_sub(&eye_dir, &shipp->warp_effect_pos, eye_pos); + vm_vec_normalize(&eye_dir); + vm_vector_2_matrix(eye_orient, &eye_dir, &Player_obj->orient.uvec, NULL); + Viewer_obj = NULL; + } else { + // get an eye position based upon the correct type of object + switch(Viewer_obj->type){ + case OBJ_SHIP: + // make a call to get the eye point for the player object + ship_get_eye( eye_pos, eye_orient, Viewer_obj ); + break; + case OBJ_OBSERVER: + // make a call to get the eye point for the player object + observer_get_eye( eye_pos, eye_orient, Viewer_obj ); + break; + default : + Int3(); + } + + #ifdef JOHNS_DEBUG_CODE + john_debug_stuff(&eye_pos, &eye_orient); + #endif + } + } + } + + apply_hud_shake(eye_orient); + + // setup neb2 rendering + neb2_render_setup(eye_pos, eye_orient); +} + +#ifndef NDEBUG +extern void ai_debug_render_stuff(); +#endif + +int Game_subspace_effect = 0; +DCF_BOOL( subspace, Game_subspace_effect ); + +// Does everything needed to render a frame +void game_render_frame( vector * eye_pos, matrix * eye_orient ) +{ + int dont_offset; + + g3_start_frame(game_zbuffer); + g3_set_view_matrix( eye_pos, eye_orient, Viewer_zoom ); + + // maybe offset the HUD (jitter stuff) + dont_offset = ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)); + HUD_set_offsets(Viewer_obj, !dont_offset); + + // for multiplayer clients, call code in Shield.cpp to set up the Shield_hit array. Have to + // do this becaues of the disjointed nature of this system (in terms of setup and execution). + // must be done before ships are rendered + if ( MULTIPLAYER_CLIENT ) { + shield_point_multi_setup(); + } + + if ( Game_subspace_effect ) { + stars_draw(0,0,0,1); + } else { + stars_draw(1,1,1,0); + } + + obj_render_all(obj_render); + beam_render_all(); // render all beam weapons + particle_render_all(); // render particles after everything else. + trail_render_all(); // render missilie trails after everything else. + mflash_render_all(); // render all muzzle flashes + + // Why do we not show the shield effect in these modes? Seems ok. + //if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW))) { + render_shields(); + //} + + // render nebula lightning + nebl_render_all(); + + // render local player nebula + neb2_render_player(); + +#ifndef NDEBUG + ai_debug_render_stuff(); +#endif + +#ifndef RELEASE_REAL + // game_framerate_check(); +#endif + +#ifndef NDEBUG + extern void snd_spew_debug_info(); + snd_spew_debug_info(); +#endif + + //================ END OF 3D RENDERING STUFF ==================== + + hud_show_radar(); + + if( (Game_detail_flags & DETAIL_FLAG_HUD) && !(Game_mode & GM_MULTIPLAYER) || ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_OBSERVER) ) ) { + hud_maybe_clear_head_area(); + anim_render_all(0, flFrametime); + } + + extern int Multi_display_netinfo; + if(Multi_display_netinfo){ + extern void multi_display_netinfo(); + multi_display_netinfo(); + } + + game_tst_frame_pre(); + +#ifndef NDEBUG + do_timing_test(flFrametime); +#endif + +#ifndef NDEBUG + extern int OO_update_index; + multi_rate_display(OO_update_index, 375, 0); +#endif + +#ifndef NDEBUG + // test + extern void oo_display(); + oo_display(); +#endif + + g3_end_frame(); +} + +//#define JOHNS_DEBUG_CODE 1 + +#ifdef JOHNS_DEBUG_CODE +void john_debug_stuff(vector *eye_pos, matrix *eye_orient) +{ + //if ( keyd_pressed[KEY_LSHIFT] ) + { + ship_subsys *tsys = Players[Player_num].targeted_subobject; + if ( tsys ) { + model_subsystem *turret = tsys->system_info; + + if (turret->type == SUBSYSTEM_TURRET ) { + vector fvec, uvec; + object * tobj = &Objects[Players[Player_num].targeted_subobject_parent]; + + ship_model_start(tobj); + + model_find_world_point(eye_pos, &turret->turret_firing_point[0], turret->model_num, turret->turret_gun_sobj, &tobj->orient, &tobj->pos ); + model_find_world_dir(&fvec, &turret->turret_matrix.fvec, turret->model_num, turret->turret_gun_sobj, &tobj->orient, NULL ); + model_find_world_dir(&uvec, &turret->turret_matrix.uvec, turret->model_num, turret->turret_gun_sobj, &tobj->orient, NULL ); + + vm_vector_2_matrix( eye_orient, &fvec, &uvec, NULL ); + + ship_model_stop(tobj); + + Viewer_obj = NULL; + } + } + + } +} +#endif + +// following function for dumping frames for purposes of building trailers. +#ifndef NDEBUG + +// function to toggle state of dumping every frame into PCX when playing the game +DCF(dump_frames, "Starts/stop frame dumping at 15 hz") +{ + if ( Dc_command ) { + + if ( Debug_dump_frames == 0 ) { + // Turn it on + Debug_dump_frames = 15; + Debug_dump_trigger = 0; + gr_dump_frame_start( Debug_dump_frame_num, DUMP_BUFFER_NUM_FRAMES ); + dc_printf( "Frame dumping at 15 hz is now ON\n" ); + } else { + // Turn it off + Debug_dump_frames = 0; + Debug_dump_trigger = 0; + gr_dump_frame_stop(); + dc_printf( "Frame dumping is now OFF\n" ); + } + + } +} + +DCF(dump_frames_trigger, "Starts/stop frame dumping at 15 hz") +{ + if ( Dc_command ) { + + if ( Debug_dump_frames == 0 ) { + // Turn it on + Debug_dump_frames = 15; + Debug_dump_trigger = 1; + gr_dump_frame_start( Debug_dump_frame_num, DUMP_BUFFER_NUM_FRAMES ); + dc_printf( "Frame dumping at 15 hz is now ON\n" ); + } else { + // Turn it off + Debug_dump_frames = 0; + Debug_dump_trigger = 0; + gr_dump_frame_stop(); + dc_printf( "Frame dumping is now OFF\n" ); + } + + } +} + +DCF(dump_frames30, "Starts/stop frame dumping at 30 hz") +{ + if ( Dc_command ) { + + if ( Debug_dump_frames == 0 ) { + // Turn it on + Debug_dump_frames = 30; + Debug_dump_trigger = 0; + gr_dump_frame_start( Debug_dump_frame_num, DUMP_BUFFER_NUM_FRAMES ); + dc_printf( "Frame dumping at 30 hz is now ON\n" ); + } else { + // Turn it off + Debug_dump_frames = 0; + Debug_dump_trigger = 0; + gr_dump_frame_stop(); + dc_printf( "Frame dumping is now OFF\n" ); + } + + } +} + +DCF(dump_frames30_trigger, "Starts/stop frame dumping at 30 hz") +{ + if ( Dc_command ) { + + if ( Debug_dump_frames == 0 ) { + // Turn it on + Debug_dump_frames = 30; + Debug_dump_trigger = 1; + gr_dump_frame_start( Debug_dump_frame_num, DUMP_BUFFER_NUM_FRAMES ); + dc_printf( "Triggered frame dumping at 30 hz is now ON\n" ); + } else { + // Turn it off + Debug_dump_frames = 0; + Debug_dump_trigger = 0; + gr_dump_frame_stop(); + dc_printf( "Triggered frame dumping is now OFF\n" ); + } + + } +} + +void game_maybe_dump_frame() +{ + if ( !Debug_dump_frames ){ + return; + } + + if( Debug_dump_trigger && !keyd_pressed[KEY_Q] ){ + return; + } + + game_stop_time(); + + gr_dump_frame(); + Debug_dump_frame_num++; + + game_start_time(); +} +#endif + +extern int Player_dead_state; + +// Flip the page and time how long it took. +void game_flip_page_and_time_it() +{ + fix t1, t2,d; + int t; + t1 = timer_get_fixed_seconds(); + gr_flip(); + t2 = timer_get_fixed_seconds(); + d = t2 - t1; + t = (gr_screen.max_w*gr_screen.max_h*gr_screen.bytes_per_pixel)/1024; + sprintf( transfer_text, NOX("%d MB/s"), fixmuldiv(t,65,d) ); +} + +void game_simulation_frame() +{ + // blow ships up in multiplayer dogfight + if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT) && (f2fl(Missiontime) >= 2.0f) && !dogfight_blown){ + // blow up all non-player ships + ship_obj *moveup = GET_FIRST(&Ship_obj_list); + ship *shipp; + ship_info *sip; + while((moveup != END_OF_LIST(&Ship_obj_list)) && (moveup != NULL)){ + // bogus + if((moveup->objnum < 0) || (moveup->objnum >= MAX_OBJECTS) || (Objects[moveup->objnum].type != OBJ_SHIP) || (Objects[moveup->objnum].instance < 0) || (Objects[moveup->objnum].instance >= MAX_SHIPS) || (Ships[Objects[moveup->objnum].instance].ship_info_index < 0) || (Ships[Objects[moveup->objnum].instance].ship_info_index >= Num_ship_types)){ + moveup = GET_NEXT(moveup); + continue; + } + shipp = &Ships[Objects[moveup->objnum].instance]; + sip = &Ship_info[shipp->ship_info_index]; + + // only blow up small ships + if((sip->flags & SIF_SMALL_SHIP) && (multi_find_player_by_object(&Objects[moveup->objnum]) < 0) ){ + // function to simply explode a ship where it is currently at + ship_self_destruct( &Objects[moveup->objnum] ); + } + + moveup = GET_NEXT(moveup); + } + + dogfight_blown = 1; + } + + // process AWACS stuff - do this first thing + awacs_process(); + + // single player, set Player hits_this_frame to 0 + if ( !(Game_mode & GM_MULTIPLAYER) && Player ) { + Player->damage_this_burst -= (flFrametime * MAX_BURST_DAMAGE / (0.001f * BURST_DURATION)); + Player->damage_this_burst = max(Player->damage_this_burst, 0.0f); + } + + // supernova + supernova_process(); + if(supernova_active() >= 5){ + return; + } + + // fire targeting lasers now so that + // 1 - created this frame + // 2 - collide this frame + // 3 - render this frame + // 4 - ignored and deleted next frame + // the basic idea being that because it con be confusing to deal with them on a multi-frame basis, they are only valid for + // frame + ship_process_targeting_lasers(); + + // do this here so that it works for multiplayer + if ( Viewer_obj ) { + // get viewer direction + int viewer_direction = PHYSICS_VIEWER_REAR; + + if(Viewer_mode == 0){ + viewer_direction = PHYSICS_VIEWER_FRONT; + } + if(Viewer_mode & VM_PADLOCK_UP){ + viewer_direction = PHYSICS_VIEWER_UP; + } + else if(Viewer_mode & VM_PADLOCK_REAR){ + viewer_direction = PHYSICS_VIEWER_REAR; + } + else if(Viewer_mode & VM_PADLOCK_LEFT){ + viewer_direction = PHYSICS_VIEWER_LEFT; + } + else if(Viewer_mode & VM_PADLOCK_RIGHT){ + viewer_direction = PHYSICS_VIEWER_RIGHT; + } + + physics_set_viewer( &Viewer_obj->phys_info, viewer_direction ); + } else { + physics_set_viewer( NULL, PHYSICS_VIEWER_FRONT ); + } + +#define VM_PADLOCK_UP (1 << 7) +#define VM_PADLOCK_REAR (1 << 8) +#define VM_PADLOCK_LEFT (1 << 9) +#define VM_PADLOCK_RIGHT (1 << 10) + + // evaluate mission departures and arrivals before we process all objects. + if ( !(Game_mode & GM_MULTIPLAYER) || ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && !multi_endgame_ending()) ) { + + // we don't want to evaluate mission stuff when any ingame joiner in multiplayer is receiving + // ships/wing packets. + if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.flags & NG_FLAG_INGAME_JOINING_CRITICAL)) && !(Game_mode & GM_DEMO_PLAYBACK)){ + mission_parse_eval_stuff(); + } + + // if we're an observer, move ourselves seperately from the standard physics + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){ + obj_observer_move(flFrametime); + } + + // move all the objects now + obj_move_all(flFrametime); + + // check for cargo reveal (this has an internal timestamp, so only runs every N ms) + // AL: 3-15-98: It was decided to not let AI ships inspect cargo + // ship_check_cargo_all(); + if(!(Game_mode & GM_DEMO_PLAYBACK)){ + mission_eval_goals(); + } + } + + // always check training objectives, even in multiplayer missions. we need to do this so that the directives gauge works properly on clients + if(!(Game_mode & GM_DEMO_PLAYBACK)){ + training_check_objectives(); + } + + // do all interpolation now + if ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && !multi_endgame_ending() && !(Netgame.flags & NG_FLAG_SERVER_LOST)) { + // client side processing of warping in effect stages + multi_do_client_warp(flFrametime); + + // client side movement of an observer + if((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)){ + obj_observer_move(flFrametime); + } + + // move all objects - does interpolation now as well + obj_move_all(flFrametime); + } + + // only process the message queue when the player is "in" the game + if ( !Pre_player_entry ){ + message_queue_process(); // process any messages send to the player + } + + if(!(Game_mode & GM_DEMO_PLAYBACK)){ + message_maybe_distort(); // maybe distort incoming message if comms damaged + player_repair_frame(flFrametime); // AI objects get repaired in ai_process, called from move code...deal with player. + player_process_pending_praise(); // maybe send off a delayed praise message to the player + player_maybe_play_all_alone_msg(); // mabye tell the player he is all alone + } + + if(!(Game_mode & GM_STANDALONE_SERVER)){ + // process some stuff every frame (before frame is rendered) + emp_process_local(); + + hud_update_frame(); // update hud systems + + if (!physics_paused) { + // Move particle system + particle_move_all(flFrametime); + + // Move missile trails + trail_move_all(flFrametime); + + // process muzzle flashes + mflash_process_all(); + + // Flash the gun flashes + shipfx_flash_do_frame(flFrametime); + + shockwave_move_all(flFrametime); // update all the shockwaves + } + + // subspace missile strikes + ssm_process(); + + obj_snd_do_frame(); // update the object-linked persistant sounds + game_maybe_update_sound_environment(); + snd_update_listener(&View_position, &Player_obj->phys_info.vel, &Player_obj->orient); + +// AL: debug code used for testing ambient subspace sound (ie when enabling subspace through debug console) +#ifndef NDEBUG + if ( Game_subspace_effect ) { + game_start_subspace_ambient_sound(); + } +#endif + } +} + +// Maybe render and process the dead-popup +void game_maybe_do_dead_popup(float frametime) +{ + if ( popupdead_is_active() ) { + int leave_popup=1; + int choice = popupdead_do_frame(frametime); + + if ( Game_mode & GM_NORMAL ) { + switch(choice) { + case 0: + // CD CHECK + if(game_do_cd_mission_check(Game_current_mission_filename)){ + gameseq_post_event(GS_EVENT_ENTER_GAME); + } else { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + break; + + case 1: + gameseq_post_event(GS_EVENT_END_GAME); + break; + + case 2: + // CD CHECK + if(game_do_cd_mission_check(Game_current_mission_filename)){ + gameseq_post_event(GS_EVENT_START_GAME); + } else { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + break; + + // this should only happen during a red alert mission + case 3: + // bogus? + Assert(The_mission.red_alert); + if(!The_mission.red_alert){ + // CD CHECK + if(game_do_cd_mission_check(Game_current_mission_filename)){ + gameseq_post_event(GS_EVENT_START_GAME); + } else { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + break; + } + + // choose the previous mission + mission_campaign_previous_mission(); + // CD CHECK + if(game_do_cd_mission_check(Game_current_mission_filename)){ + gameseq_post_event(GS_EVENT_START_GAME); + } else { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + break; + + default: + leave_popup=0; + break; + } + } else { + switch( choice ) { + + case POPUPDEAD_DO_MAIN_HALL: + multi_quit_game(PROMPT_NONE,-1); + break; + + case POPUPDEAD_DO_RESPAWN: + multi_respawn_normal(); + event_music_player_respawn(); + break; + + case POPUPDEAD_DO_OBSERVER: + multi_respawn_observer(); + event_music_player_respawn_as_observer(); + break; + + default: + leave_popup = 0; + break; + } + } + + if ( leave_popup ) { + popupdead_close(); + } + } +} + +// returns true if player is actually in a game_play stats +int game_actually_playing() +{ + int state; + + state = gameseq_get_state(); + if ( (state != GS_STATE_GAME_PLAY) && (state != GS_STATE_DEATH_DIED) && (state != GS_STATE_DEATH_BLEW_UP) ) + return 0; + else + return 1; +} + +// Draw the 2D HUD gauges +void game_render_hud_2d() +{ + if ( !(Game_detail_flags & DETAIL_FLAG_HUD) ) { + return; + } + + HUD_render_2d(flFrametime); + gr_reset_clip(); +} + +// Draw the 3D-dependant HUD gauges +void game_render_hud_3d(vector *eye_pos, matrix *eye_orient) +{ + g3_start_frame(0); // 0 = turn zbuffering off + g3_set_view_matrix( eye_pos, eye_orient, Viewer_zoom ); + + if ( (Game_detail_flags & DETAIL_FLAG_HUD) && (supernova_active() < 3)/* && !(Game_mode & GM_MULTIPLAYER) || ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_OBSERVER) )*/ ) { + HUD_render_3d(flFrametime); + } + + // Do the sunspot + game_sunspot_process(flFrametime); + + // Diminish the palette effect + game_flash_diminish(flFrametime); + + g3_end_frame(); +} + + +void game_frame() +{ + int actually_playing; + fix total_time1, total_time2; + fix render2_time1=0, render2_time2=0; + fix render3_time1=0, render3_time2=0; + fix flip_time1=0, flip_time2=0; + fix clear_time1=0, clear_time2=0; + + vector eye_pos; + matrix eye_orient; + +#ifndef NDEBUG + if (Framerate_delay) { + int start_time = timer_get_milliseconds(); + while (timer_get_milliseconds() < start_time + Framerate_delay) + ; + } +#endif + +#ifdef DEMO_SYSTEM + demo_do_frame_start(); + if(Demo_error){ + mprintf(("Error (%d) while processing demo!\n", Demo_error)); + demo_close(); + } +#endif + + // start timing frame + timing_frame_start(); + + total_time1 = timer_get_fixed_seconds(); + + // var to hold which state we are in + actually_playing = game_actually_playing(); + + if ((!(Game_mode & GM_MULTIPLAYER)) || ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_OBSERVER))) { + if (!(Game_mode & GM_STANDALONE_SERVER)){ + Assert( OBJ_INDEX(Player_obj) >= 0 ); + } + } + + if (Missiontime > Entry_delay_time){ + Pre_player_entry = 0; + } else { + ; //nprintf(("AI", "Framecount = %i, time = %7.3f\n", Framecount, f2fl(Missiontime))); + } + + // Note: These are done even before the player enters, else buffers can overflow. + if (! (Game_mode & GM_STANDALONE_SERVER)){ + radar_frame_init(); + } + + shield_frame_init(); + + if ( Player->control_mode != PCM_NORMAL ) + camera_move(); + + if ( !Pre_player_entry && actually_playing ) { + if (! (Game_mode & GM_STANDALONE_SERVER) ) { + + if( (!popup_running_state()) && (!popupdead_is_active()) ){ + game_process_keys(); + + // don't read flying controls if we're playing a demo back + if(!(Game_mode & GM_DEMO_PLAYBACK)){ + read_player_controls( Player_obj, flFrametime); + } + } + + // if we're not the master, we may have to send the server-critical ship status button_info bits + if ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Net_player->flags & NETINFO_FLAG_OBSERVER)){ + multi_maybe_send_ship_status(); + } + } + } + + // Reset the whack stuff + game_whack_reset(); + + // These two lines must be outside of Pre_player_entry code, + // otherwise too many lights are added. + light_reset(); + + if ((Game_mode & GM_MULTIPLAYER) && (Netgame.game_state == NETGAME_STATE_SERVER_TRANSFER)){ + return; + } + + game_simulation_frame(); + + // if not actually in a game play state, then return. This condition could only be true in + // a multiplayer game. + if ( !actually_playing ) { + Assert( Game_mode & GM_MULTIPLAYER ); + return; + } + + if (!Pre_player_entry) { + if (! (Game_mode & GM_STANDALONE_SERVER)) { + clear_time1 = timer_get_fixed_seconds(); + // clear the screen to black + gr_reset_clip(); + if ( (Game_detail_flags & DETAIL_FLAG_CLEAR) ) { + gr_clear(); + } + + clear_time2 = timer_get_fixed_seconds(); + render3_time1 = timer_get_fixed_seconds(); + game_render_frame_setup(&eye_pos, &eye_orient); + game_render_frame( &eye_pos, &eye_orient ); + + // save the eye position and orientation + if ( Game_mode & GM_MULTIPLAYER ) { + Net_player->s_info.eye_pos = eye_pos; + Net_player->s_info.eye_orient = eye_orient; + } + + hud_show_target_model(); + + // check to see if we should display the death died popup + if(Game_mode & GM_DEAD_BLEW_UP){ + if(Game_mode & GM_MULTIPLAYER){ + // catch the situation where we're supposed to be warping out on this transition + if(Net_player->flags & NETINFO_FLAG_WARPING_OUT){ + gameseq_post_event(GS_EVENT_DEBRIEF); + } else if((Player_died_popup_wait != -1) && (timestamp_elapsed(Player_died_popup_wait))){ + Player_died_popup_wait = -1; + popupdead_start(); + } + } else { + if((Player_died_popup_wait != -1) && (timestamp_elapsed(Player_died_popup_wait))){ + Player_died_popup_wait = -1; + popupdead_start(); + } + } + } + + // hack - sometimes this seems to slip by in multiplayer. this should guarantee that we catch it + if((Game_mode & GM_MULTIPLAYER) && (Player_multi_died_check != -1) && (Game_mode & GM_DEAD_BLEW_UP) ){ + if(fl_abs(time(NULL) - Player_multi_died_check) > 4){ + if(!popupdead_is_active()){ + popupdead_start(); + } + + Player_multi_died_check = -1; + } + } + + render3_time2 = timer_get_fixed_seconds(); + render2_time1 = timer_get_fixed_seconds(); + + gr_reset_clip(); + game_get_framerate(); + game_show_framerate(); + + game_show_time_left(); + + // Draw the 2D HUD gauges + if(supernova_active() < 3){ + game_render_hud_2d(); + } + + game_set_view_clip(); + + // Draw 3D HUD gauges + game_render_hud_3d(&eye_pos, &eye_orient); + + game_tst_frame(); + + render2_time2 = timer_get_fixed_seconds(); + + // maybe render and process the dead popup + game_maybe_do_dead_popup(flFrametime); + + // start timing frame + timing_frame_stop(); + // timing_display(30, 10); + + // If a regular popup is active, don't flip (popup code flips) + if( !popup_running_state() ){ + flip_time1 = timer_get_fixed_seconds(); + game_flip_page_and_time_it(); + flip_time2 = timer_get_fixed_seconds(); + } + +#ifndef NDEBUG + game_maybe_dump_frame(); // used to dump pcx files for building trailers +#endif + } else { + game_show_standalone_framerate(); + } + } + + game_do_training_checks(); + asteroid_frame(); + + // process lightning (nebula only) + nebl_process(); + + total_time2 = timer_get_fixed_seconds(); + + // Got some timing numbers + Timing_total = f2fl( total_time2 - total_time1 ) * 1000.0f; + Timing_clear = f2fl( clear_time2 - clear_time1 ) * 1000.0f; + Timing_render2 = f2fl( render2_time2- render2_time1 ) * 1000.0f; + Timing_render3 = f2fl( render3_time2- render3_time1 ) * 1000.0f; + Timing_flip = f2fl( flip_time2 - flip_time1 ) * 1000.0f; + +#ifdef DEMO_SYSTEM + demo_do_frame_end(); + if(Demo_error){ + mprintf(("Error (%d) while processing demo!\n", Demo_error)); + demo_close(); + } +#endif +} + +#define MAX_FRAMETIME (F1_0/4) // Frametime gets saturated at this. Changed by MK on 11/1/97. + // Some bug was causing Frametime to always get saturated at 2.0 seconds after the player + // died. This resulted in screwed up death sequences. + +fix Last_time = 0; // The absolute time of game at end of last frame (beginning of this frame) +fix Last_delta_time = 0; // While game is paused, this keeps track of how much elapsed in the frame before paused. +static int timer_paused=0; +static int stop_count,start_count; +static int time_stopped,time_started; +int saved_timestamp_ticker = -1; + +void game_reset_time() +{ + if((Game_mode & GM_MULTIPLAYER) && (Netgame.game_state == NETGAME_STATE_SERVER_TRANSFER)){ + return ; + } + + // Last_time = timer_get_fixed_seconds(); + game_start_time(); + timestamp_reset(); + game_stop_time(); +} + +void game_stop_time() +{ + if (timer_paused==0) { + fix time; + time = timer_get_fixed_seconds(); + // Save how much time progressed so far in the frame so we can + // use it when we unpause. + Last_delta_time = time - Last_time; + + //mprintf(("Last_time in game_stop_time = %7.3f\n", f2fl(Last_delta_time))); + if (Last_delta_time < 0) { + #if defined(TIMER_TEST) && !defined(NDEBUG) + Int3(); //get Matt!!!! + #endif + Last_delta_time = 0; + } + #if defined(TIMER_TEST) && !defined(NDEBUG) + time_stopped = time; + #endif + + // Stop the timer_tick stuff... + // Normally, you should never access 'timestamp_ticker', consider this a low-level routine + saved_timestamp_ticker = timestamp_ticker; + } + timer_paused++; + + #if defined(TIMER_TEST) && !defined(NDEBUG) + stop_count++; + #endif +} + +void game_start_time() +{ + timer_paused--; + Assert(timer_paused >= 0); + if (timer_paused==0) { + fix time; + time = timer_get_fixed_seconds(); + #if defined(TIMER_TEST) && !defined(NDEBUG) + if (Last_time < 0) + Int3(); //get Matt!!!! + } + #endif + // Take current time, and set it backwards to account for time + // that the frame already executed, so that timer_get_fixed_seconds() - Last_time + // will be correct when it goes to calculate the frametime next + // frame. + Last_time = time - Last_delta_time; + #if defined(TIMER_TEST) && !defined(NDEBUG) + time_started = time; + #endif + + // Restore the timer_tick stuff... + // Normally, you should never access 'timestamp_ticker', consider this a low-level routine + Assert( saved_timestamp_ticker > -1 ); // Called out of order, get JAS + timestamp_ticker = saved_timestamp_ticker; + saved_timestamp_ticker = -1; + } + + #if defined(TIMER_TEST) && !defined(NDEBUG) + start_count++; + #endif +} + + +void game_set_frametime(int state) +{ + fix thistime; + float frame_cap_diff; + + thistime = timer_get_fixed_seconds(); + + if ( Last_time == 0 ) + Frametime = F1_0 / 30; + else + Frametime = thistime - Last_time; + +// Frametime = F1_0 / 30; + + fix debug_frametime = Frametime; // Just used to display frametime. + + // If player hasn't entered mission yet, make frame take 1/4 second. + if ((Pre_player_entry) && (state == GS_STATE_GAME_PLAY)) + Frametime = F1_0/4; +#ifndef NDEBUG + else if ((Debug_dump_frames) && (state == GS_STATE_GAME_PLAY)) { // note link to above if!!!!! + + fix frame_speed = F1_0 / Debug_dump_frames; + + if (Frametime > frame_speed ){ + nprintf(("warning","slow frame: %x\n",Frametime)); + } else { + do { + thistime = timer_get_fixed_seconds(); + Frametime = thistime - Last_time; + } while (Frametime < frame_speed ); + } + Frametime = frame_speed; + } +#endif + + Assert( Framerate_cap > 0 ); + + // Cap the framerate so it doesn't get too high. + { + fix cap; + + cap = F1_0/Framerate_cap; + if (Frametime < cap) { + thistime = cap - Frametime; + //mprintf(("Sleeping for %6.3f seconds.\n", f2fl(thistime))); + Sleep( DWORD(f2fl(thistime) * 1000.0f) ); + Frametime = cap; + thistime = timer_get_fixed_seconds(); + } + } + + if((Game_mode & GM_STANDALONE_SERVER) && + (f2fl(Frametime) < ((float)1.0/(float)Multi_options_g.std_framecap))){ + + frame_cap_diff = ((float)1.0/(float)Multi_options_g.std_framecap) - f2fl(Frametime); + Sleep((DWORD)(frame_cap_diff*1000)); + + thistime += fl2f((frame_cap_diff)); + + Frametime = thistime - Last_time; + } + + // If framerate is too low, cap it. + if (Frametime > MAX_FRAMETIME) { +#ifndef NDEBUG + mprintf(("Frame %2i too long!!: frametime = %.3f (%.3f)\n", Framecount, f2fl(Frametime), f2fl(debug_frametime))); +#else + // to remove warnings in release build + debug_frametime = fl2f(flFrametime); +#endif + Frametime = MAX_FRAMETIME; + } + + Frametime = fixmul(Frametime, Game_time_compression); + + Last_time = thistime; + //mprintf(("Frame %i, Last_time = %7.3f\n", Framecount, f2fl(Last_time))); + + flFrametime = f2fl(Frametime); + //if(!(Game_mode & GM_PLAYING_DEMO)){ + timestamp_inc(flFrametime); + +/* if ((Framecount > 0) && (Framecount < 10)) { + mprintf(("Frame %2i: frametime = %.3f (%.3f)\n", Framecount, f2fl(Frametime), f2fl(debug_frametime))); + } +*/ +} + +// This is called from game_do_frame(), and from navmap_do_frame() +void game_update_missiontime() +{ + // TODO JAS: Put in if and move this into game_set_frametime, + // fix navmap to call game_stop/start_time + //if ( !timer_paused ) + Missiontime += Frametime; +} + +void game_do_frame() +{ + game_set_frametime(GS_STATE_GAME_PLAY); + game_update_missiontime(); + + if (Game_mode & GM_STANDALONE_SERVER) { + std_multi_set_standalone_missiontime(f2fl(Missiontime)); + } + + if ( game_single_step && (last_single_step == game_single_step) ) { + os_set_title( NOX("SINGLE STEP MODE (Pause exits, any other key steps)") ); + while( key_checkch() == 0 ) + os_sleep(10); + os_set_title( XSTR( "FreeSpace", 171) ); + Last_time = timer_get_fixed_seconds(); + } + + last_single_step = game_single_step; + + if ((gameseq_get_state() == GS_STATE_GAME_PLAY) && Use_mouse_to_fly){ + Keep_mouse_centered = 1; // force mouse to center of our window (so we don't hit movement limits) + } + game_frame(); + + Keep_mouse_centered = 0; + monitor_update(); // Update monitor variables +} + +void multi_maybe_do_frame() +{ + if ( (Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_IN_MISSION) && !Multi_pause_status){ + game_do_frame(); + } +} + +int Joymouse_button_status = 0; + +// Flush all input devices +void game_flush() +{ + key_flush(); + mouse_flush(); + joy_flush(); + snazzy_flush(); + + Joymouse_button_status = 0; + + //mprintf(("Game flush!\n" )); +} + +// function for multiplayer only which calls game_do_state_common() when running the +// debug console +void game_do_dc_networking() +{ + Assert( Game_mode & GM_MULTIPLAYER ); + + game_do_state_common( gameseq_get_state() ); +} + +// Call this whenever in a loop, or when you need to check for a keystroke. +int game_check_key() +{ + int k; + + k = game_poll(); + + // convert keypad enter to normal enter + if ((k & KEY_MASK) == KEY_PADENTER) + k = (k & ~KEY_MASK) | KEY_ENTER; + + return k; +} + +#ifdef FS2_DEMO + +#define DEMO_TRAILER_TIMEOUT_MS 45000 // 45 seconds of no input, play trailer +static int Demo_show_trailer_timestamp = 0; + +void demo_reset_trailer_timer() +{ + Demo_show_trailer_timestamp = timer_get_milliseconds(); +} + +void demo_maybe_show_trailer(int k) +{ + /* + // if key pressed, reset demo trailer timer + if ( k > 0 ) { + demo_reset_trailer_timer(); + return; + } + + // if mouse moved, reset demo trailer timer + int dx = 0, dy = 0; + + mouse_get_delta(&dx, &dy); + if ( (dx > 0) || (dy > 0) ) { + demo_reset_trailer_timer(); + return; + } + + // if joystick has moved, reset demo trailer timer + dx = 0; + dy = 0; + joy_get_delta(&dx, &dy); + if ( (dx > 0) || (dy > 0) ) { + demo_reset_trailer_timer(); + return; + } + + // NOTE: reseting the trailer timer on mouse/joystick presses is handled in + // the low-level code. Ugly, I know... but was the simplest and most + // robust solution. + + // if 30 seconds since last demo trailer time reset, launch movie + if ( os_foreground() ) { + int now = timer_get_milliseconds(); + if ( (now - Demo_show_trailer_timestamp) > DEMO_TRAILER_TIMEOUT_MS ) { +// if ( (now - Demo_show_trailer_timestamp) > 10000 ) { + // play movie here + movie_play( NOX("fstrailer2.mve") ); + demo_reset_trailer_timer(); + } + } + */ +} + +#endif + +// same as game_check_key(), except this is used while actually in the game. Since there +// generally are differences between game control keys and general UI keys, makes sense to +// have seperate functions for each case. If you are not checking a game control while in a +// mission, you should probably be using game_check_key() instead. +int game_poll() +{ + int k, state; + + if (!os_foreground()) { + game_stop_time(); + os_sleep(100); + game_start_time(); + + // If we're in a single player game, pause it. + if (!(Game_mode & GM_MULTIPLAYER)){ + if ( (gameseq_get_state() == GS_STATE_GAME_PLAY) && (!popup_active()) && (!popupdead_is_active()) ) { + game_process_pause_key(); + } + } + } + + k = key_inkey(); + +#ifdef FS2_DEMO + demo_maybe_show_trailer(k); +#endif + + // Move the mouse cursor with the joystick. + if (os_foreground() && (!Mouse_hidden) && (Use_joy_mouse) ) { + // Move the mouse cursor with the joystick + int mx, my, dx, dy; + int jx, jy, jz, jr; + + joy_get_pos( &jx, &jy, &jz, &jr ); + + dx = fl2i(f2fl(jx)*flFrametime*500.0f); + dy = fl2i(f2fl(jy)*flFrametime*500.0f); + + if ( dx || dy ) { + mouse_get_real_pos( &mx, &my ); + mouse_set_pos( mx+dx, my+dy ); + } + + int j, m; + j = joy_down(0); + m = mouse_down(MOUSE_LEFT_BUTTON); + + if ( j != Joymouse_button_status ) { + //mprintf(( "Joy went from %d to %d, mouse is %d\n", Joymouse_button_status, j, m )); + Joymouse_button_status = j; + if ( j && (!m) ) { + mouse_mark_button( MOUSE_LEFT_BUTTON, 1 ); + } else if ( (!j) && (m) ) { + mouse_mark_button( MOUSE_LEFT_BUTTON, 0 ); + } + } + } + + // if we should be ignoring keys because of some multiplayer situations + if((Game_mode & GM_MULTIPLAYER) && multi_ignore_controls(k)){ + return 0; + } + + // If a popup is running, don't process all the Fn keys + if( popup_active() ) { + return k; + } + + state = gameseq_get_state(); + +// if ( k ) nprintf(( "General", "Key = %x\n", k )); + + switch (k) { + case KEY_DEBUGGED + KEY_BACKSP: + Int3(); + break; + + case KEY_F1: + launch_context_help(); + k = 0; + break; + + case KEY_F2: +// if (state != GS_STATE_INITIAL_PLAYER_SELECT) { + + // don't allow f2 while warping out in multiplayer + if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_WARPING_OUT)){ + break; + } + + switch (state) { + case GS_STATE_INITIAL_PLAYER_SELECT: + case GS_STATE_OPTIONS_MENU: + case GS_STATE_HUD_CONFIG: + case GS_STATE_CONTROL_CONFIG: + case GS_STATE_DEATH_DIED: + case GS_STATE_DEATH_BLEW_UP: + case GS_STATE_VIEW_MEDALS: + break; + + default: + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + k = 0; + break; + } + + break; + + // hotkey selection screen -- only valid from briefing and beyond. + case KEY_F3: + #ifndef FS2_DEMO + if ( (state == GS_STATE_TEAM_SELECT) || (state == GS_STATE_BRIEFING) || (state == GS_STATE_SHIP_SELECT) || (state == GS_STATE_WEAPON_SELECT) || (state == GS_STATE_GAME_PLAY) || (state == GS_STATE_GAME_PAUSED) ) { + gameseq_post_event( GS_EVENT_HOTKEY_SCREEN ); + k = 0; + } + #endif + break; + + case KEY_DEBUGGED + KEY_F3: + gameseq_post_event( GS_EVENT_TOGGLE_FULLSCREEN ); + break; + + case KEY_DEBUGGED + KEY_F4: + gameseq_post_event( GS_EVENT_TOGGLE_GLIDE ); + break; + + case KEY_F4: + if(Game_mode & GM_MULTIPLAYER){ + if((state == GS_STATE_GAME_PLAY) || (state == GS_STATE_MULTI_PAUSED)){ + gameseq_post_event( GS_EVENT_MISSION_LOG_SCROLLBACK ); + k = 0; + } + } else { + if ((state == GS_STATE_GAME_PLAY) || (state == GS_STATE_DEATH_DIED) || (state == GS_STATE_DEATH_BLEW_UP) || (state == GS_STATE_GAME_PAUSED) ) { + gameseq_post_event( GS_EVENT_MISSION_LOG_SCROLLBACK ); + k = 0; + } + } + break; + + case KEY_ESC | KEY_SHIFTED: + // make sure to quit properly out of multiplayer + if(Game_mode & GM_MULTIPLAYER){ + multi_quit_game(PROMPT_NONE); + } + + gameseq_post_event( GS_EVENT_QUIT_GAME ); + k = 0; + + break; + + case KEY_DEBUGGED + KEY_P: + break; + + case KEY_PRINT_SCRN: + { + static counter = 0; + char tmp_name[127]; + + game_stop_time(); + + sprintf( tmp_name, NOX("screen%02d"), counter ); + counter++; + mprintf(( "Dumping screen to '%s'\n", tmp_name )); + gr_print_screen(tmp_name); + + game_start_time(); + } + + k = 0; + break; + + case KEY_SHIFTED | KEY_ENTER: { + +#if !defined(NDEBUG) + + if ( Game_mode & GM_NORMAL ){ + game_stop_time(); + } + + // if we're in multiplayer mode, do some special networking + if(Game_mode & GM_MULTIPLAYER){ + debug_console(game_do_dc_networking); + } else { + debug_console(); + } + + game_flush(); + + if ( Game_mode & GM_NORMAL ) + game_start_time(); + +#endif + + break; + } + } + + return k; +} + +void os_close() +{ + gameseq_post_event(GS_EVENT_QUIT_GAME); +} + +void apply_physics( float damping, float desired_vel, float initial_vel, float t, float * new_vel, float * delta_pos ); + + +void camera_set_position( vector *pos ) +{ + Camera_pos = *pos; +} + +void camera_set_orient( matrix *orient ) +{ + Camera_orient = *orient; +} + +void camera_set_velocity( vector *vel, int instantaneous ) +{ + Camera_desired_velocity.x = 0.0f; + Camera_desired_velocity.y = 0.0f; + Camera_desired_velocity.z = 0.0f; + + vm_vec_scale_add2( &Camera_desired_velocity, &Camera_orient.rvec, vel->x ); + vm_vec_scale_add2( &Camera_desired_velocity, &Camera_orient.uvec, vel->y ); + vm_vec_scale_add2( &Camera_desired_velocity, &Camera_orient.fvec, vel->z ); + + if ( instantaneous ) { + Camera_velocity = Camera_desired_velocity; + } + +} + +// +void camera_move() +{ + vector new_vel, delta_pos; + + apply_physics( Camera_damping, Camera_desired_velocity.x, Camera_velocity.x, flFrametime, &new_vel.x, &delta_pos.x ); + apply_physics( Camera_damping, Camera_desired_velocity.y, Camera_velocity.y, flFrametime, &new_vel.y, &delta_pos.y ); + apply_physics( Camera_damping, Camera_desired_velocity.z, Camera_velocity.z, flFrametime, &new_vel.z, &delta_pos.z ); + + Camera_velocity = new_vel; + +// mprintf(( "Camera velocity = %.1f,%.1f, %.1f\n", Camera_velocity.x, Camera_velocity.y, Camera_velocity.z )); + + vm_vec_add2( &Camera_pos, &delta_pos ); + + float ot = Camera_time+0.0f; + + Camera_time += flFrametime; + + if ( (ot < 0.667f) && ( Camera_time >= 0.667f ) ) { + vector tmp; + + tmp.z = 4.739f; // always go this fast forward. + + // pick x and y velocities so they are always on a + // circle with a 25 m radius. + + float tmp_angle = frand()*PI2; + + tmp.x = 22.0f * (float)sin(tmp_angle); + tmp.y = -22.0f * (float)cos(tmp_angle); + + //mprintf(( "Angle = %.1f, vx=%.1f, vy=%.1f\n", tmp_angle, tmp.x, tmp.y )); + + //mprintf(( "Changing velocity!\n" )); + camera_set_velocity( &tmp, 0 ); + } + + if ( (ot < 3.0f ) && ( Camera_time >= 3.0f ) ) { + vector tmp = { 0.0f, 0.0f, 0.0f }; + camera_set_velocity( &tmp, 0 ); + } + +} + +void end_demo_campaign_do() +{ +#if defined(FS2_DEMO) + // show upsell screens + demo_upsell_show_screens(); +#elif defined(OEM_BUILD) + // show oem upsell screens + oem_upsell_show_screens(); +#endif + + // drop into main hall + gameseq_post_event( GS_EVENT_MAIN_MENU ); +} + +// All code to process events. This is the only place +// that you should change the state of the game. +void game_process_event( int current_state, int event ) +{ + mprintf(("Got event %s in state %s\n", GS_event_text[event], GS_state_text[current_state])); + + switch (event) { + case GS_EVENT_SIMULATOR_ROOM: + gameseq_set_state(GS_STATE_SIMULATOR_ROOM); + break; + + case GS_EVENT_MAIN_MENU: + gameseq_set_state(GS_STATE_MAIN_MENU); + break; + + case GS_EVENT_OPTIONS_MENU: + gameseq_push_state( GS_STATE_OPTIONS_MENU ); + break; + + case GS_EVENT_BARRACKS_MENU: + gameseq_set_state(GS_STATE_BARRACKS_MENU); + break; + + case GS_EVENT_TECH_MENU: + gameseq_set_state(GS_STATE_TECH_MENU); + break; + + case GS_EVENT_TRAINING_MENU: + gameseq_set_state(GS_STATE_TRAINING_MENU); + break; + + case GS_EVENT_START_GAME: + Select_default_ship = 0; + Player_multi_died_check = -1; + gameseq_set_state(GS_STATE_CMD_BRIEF); + break; + + case GS_EVENT_START_BRIEFING: + gameseq_set_state(GS_STATE_BRIEFING); + break; + + case GS_EVENT_DEBRIEF: + // did we end the campaign in the main freespace 2 single player campaign? + if(Campaign_ended_in_mission && (Game_mode & GM_CAMPAIGN_MODE) && !stricmp(Campaign.filename, "freespace2")) { + gameseq_post_event(GS_EVENT_END_CAMPAIGN); + } else { + gameseq_set_state(GS_STATE_DEBRIEF); + } + + Player_multi_died_check = -1; + break; + + case GS_EVENT_SHIP_SELECTION: + gameseq_set_state( GS_STATE_SHIP_SELECT ); + break; + + case GS_EVENT_WEAPON_SELECTION: + gameseq_set_state( GS_STATE_WEAPON_SELECT ); + break; + + case GS_EVENT_ENTER_GAME: +#ifdef DEMO_SYSTEM + // maybe start recording a demo + if(Demo_make){ + demo_start_record("test.fsd"); + } +#endif + + if (Game_mode & GM_MULTIPLAYER) { + // if we're respawning, make sure we change the view mode so that the hud shows up + if (current_state == GS_STATE_DEATH_BLEW_UP) { + Viewer_mode = 0; + } + + gameseq_set_state(GS_STATE_GAME_PLAY); + } else { + gameseq_set_state(GS_STATE_GAME_PLAY, 1); + } + + Player_multi_died_check = -1; + + // clear multiplayer button info + extern button_info Multi_ship_status_bi; + memset(&Multi_ship_status_bi, 0, sizeof(button_info)); + + Start_time = f2fl(timer_get_approx_seconds()); + //Framecount = 0; + mprintf(("Entering game at time = %7.3f\n", Start_time)); + break; + + + case GS_EVENT_START_GAME_QUICK: + Select_default_ship = 1; + gameseq_post_event(GS_EVENT_ENTER_GAME); + break; + + + case GS_EVENT_END_GAME: + if ( (current_state == GS_STATE_GAME_PLAY) || (current_state == GS_STATE_DEATH_DIED) || + (current_state == GS_STATE_DEATH_BLEW_UP) || (current_state == GS_STATE_DEBRIEF) || (current_state == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) { + gameseq_set_state(GS_STATE_MAIN_MENU); + + } else + Int3(); + + Player_multi_died_check = -1; + break; + + case GS_EVENT_QUIT_GAME: + main_hall_stop_music(); + main_hall_stop_ambient(); + gameseq_set_state(GS_STATE_QUIT_GAME); + + Player_multi_died_check = -1; + break; + + case GS_EVENT_GAMEPLAY_HELP: + gameseq_push_state( GS_STATE_GAMEPLAY_HELP ); + break; + + case GS_EVENT_PAUSE_GAME: + gameseq_push_state(GS_STATE_GAME_PAUSED); + break; + + case GS_EVENT_DEBUG_PAUSE_GAME: + gameseq_push_state(GS_STATE_DEBUG_PAUSED); + break; + + case GS_EVENT_TRAINING_PAUSE: + gameseq_push_state(GS_STATE_TRAINING_PAUSED); + break; + + case GS_EVENT_PREVIOUS_STATE: + gameseq_pop_state(); + break; + + case GS_EVENT_TOGGLE_FULLSCREEN: + #ifndef HARDWARE_ONLY + #ifndef NDEBUG + if ( gr_screen.mode == GR_SOFTWARE ) { + gr_init( GR_640, GR_DIRECTDRAW ); + } else if ( gr_screen.mode == GR_DIRECTDRAW ) { + gr_init( GR_640, GR_SOFTWARE ); + } + #endif + #endif + break; + + case GS_EVENT_TOGGLE_GLIDE: + #ifndef NDEBUG + if ( gr_screen.mode != GR_GLIDE ) { + gr_init( GR_640, GR_GLIDE ); + } else { + gr_init( GR_640, GR_SOFTWARE ); + } + #endif + break; + + case GS_EVENT_LOAD_MISSION_MENU: + gameseq_set_state(GS_STATE_LOAD_MISSION_MENU); + break; + + case GS_EVENT_MISSION_LOG_SCROLLBACK: + gameseq_push_state( GS_STATE_MISSION_LOG_SCROLLBACK ); + break; + + case GS_EVENT_HUD_CONFIG: + gameseq_push_state( GS_STATE_HUD_CONFIG ); + break; + + case GS_EVENT_CONTROL_CONFIG: + gameseq_push_state( GS_STATE_CONTROL_CONFIG ); + break; + + case GS_EVENT_DEATH_DIED: + gameseq_set_state( GS_STATE_DEATH_DIED ); + break; + + case GS_EVENT_DEATH_BLEW_UP: + if ( current_state == GS_STATE_DEATH_DIED ) { + gameseq_set_state( GS_STATE_DEATH_BLEW_UP ); + event_music_player_death(); + + // multiplayer clients set their extra check here + if(Game_mode & GM_MULTIPLAYER){ + // set the multi died absolute last chance check + Player_multi_died_check = time(NULL); + } + } else { + mprintf(( "Ignoring GS_EVENT_DEATH_BLEW_UP because we're in state %d\n", current_state )); + } + break; + + case GS_EVENT_NEW_CAMPAIGN: + if (!mission_load_up_campaign()){ + readyroom_continue_campaign(); + } + + Player_multi_died_check = -1; + break; + + case GS_EVENT_CAMPAIGN_CHEAT: + if (!mission_load_up_campaign()){ + /* + // bash campaign value + extern char Main_hall_campaign_cheat[512]; + int idx; + + // look for the mission + for(idx=0; idxsaved_viewer_mode = Viewer_mode; + Player->control_mode = PCM_WARPOUT_STAGE1; + Warpout_sound = snd_play(&Snds[SND_PLAYER_WARP_OUT]); + Warpout_time = 0.0f; // Start timer! + break; + + case GS_EVENT_PLAYER_WARPOUT_START: + if ( Player->control_mode != PCM_NORMAL ) { + mprintf(( "Player isn't in normal mode; cannot warp out.\n" )); + } else { + Player->saved_viewer_mode = Viewer_mode; + Player->control_mode = PCM_WARPOUT_STAGE1; + Warpout_sound = snd_play(&Snds[SND_PLAYER_WARP_OUT]); + Warpout_time = 0.0f; // Start timer! + Warpout_forced = 0; // If non-zero, bash the player to speed and go through effect + } + break; + + case GS_EVENT_PLAYER_WARPOUT_STOP: + if ( Player->control_mode != PCM_NORMAL ) { + if ( !Warpout_forced ) { // cannot cancel forced warpout + Player->control_mode = PCM_NORMAL; + Viewer_mode = Player->saved_viewer_mode; + hud_subspace_notify_abort(); + mprintf(( "Player put back to normal mode.\n" )); + if ( Warpout_sound > -1 ) { + snd_stop( Warpout_sound ); + Warpout_sound = -1; + } + } + } + break; + + case GS_EVENT_PLAYER_WARPOUT_DONE_STAGE1: // player ship got up to speed + if ( Player->control_mode != PCM_WARPOUT_STAGE1 ) { + gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP ); + mprintf(( "Player put back to normal mode, because of invalid sequence in stage1.\n" )); + } else { + mprintf(( "Hit target speed. Starting warp effect and moving to stage 2!\n" )); + shipfx_warpout_start( Player_obj ); + Player->control_mode = PCM_WARPOUT_STAGE2; + Player->saved_viewer_mode = Viewer_mode; + Viewer_mode |= VM_WARP_CHASE; + + vector tmp = Player_obj->pos; + matrix tmp_m; + ship_get_eye( &tmp, &tmp_m, Player_obj ); + vm_vec_scale_add2( &tmp, &Player_obj->orient.rvec, 0.0f ); + vm_vec_scale_add2( &tmp, &Player_obj->orient.uvec, 0.952f ); + vm_vec_scale_add2( &tmp, &Player_obj->orient.fvec, -1.782f ); + Camera_time = 0.0f; + camera_set_position( &tmp ); + camera_set_orient( &Player_obj->orient ); + vector tmp_vel = { 0.0f, 5.1919f, 14.7f }; + + //mprintf(( "Rad = %.1f\n", Player_obj->radius )); + camera_set_velocity( &tmp_vel, 1); + } + break; + + case GS_EVENT_PLAYER_WARPOUT_DONE_STAGE2: // player ship got into the warp effect + if ( Player->control_mode != PCM_WARPOUT_STAGE2 ) { + gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP ); + mprintf(( "Player put back to normal mode, because of invalid sequence in stage2.\n" )); + } else { + mprintf(( "Hit warp effect. Moving to stage 3!\n" )); + Player->control_mode = PCM_WARPOUT_STAGE3; + } + break; + + case GS_EVENT_PLAYER_WARPOUT_DONE: // player ship got through the warp effect + mprintf(( "Player warped out. Going to debriefing!\n" )); + Player->control_mode = PCM_NORMAL; + Viewer_mode = Player->saved_viewer_mode; + Warpout_sound = -1; + + // we have a special debriefing screen for multiplayer furballs + if((Game_mode & GM_MULTIPLAYER) && (The_mission.game_type & MISSION_TYPE_MULTI_DOGFIGHT)){ + gameseq_post_event(GS_EVENT_MULTI_DOGFIGHT_DEBRIEF); + } + // do the normal debriefing for all other situations + else { + gameseq_post_event(GS_EVENT_DEBRIEF); + } + break; + + case GS_EVENT_STANDALONE_POSTGAME: + gameseq_set_state(GS_STATE_STANDALONE_POSTGAME); + break; + + case GS_EVENT_INITIAL_PLAYER_SELECT: + gameseq_set_state(GS_STATE_INITIAL_PLAYER_SELECT); + break; + + case GS_EVENT_GAME_INIT: + #if defined(FS2_DEMO) || defined(OEM_BUILD) + gameseq_set_state(GS_STATE_INITIAL_PLAYER_SELECT); + #else + // see if the command line option has been set to use the last pilot, and act acoordingly + if( player_select_get_last_pilot() ) { + // always enter the main menu -- do the automatic network startup stuff elsewhere + // so that we still have valid checks for networking modes, etc. + gameseq_set_state(GS_STATE_MAIN_MENU); + } else { + gameseq_set_state(GS_STATE_INITIAL_PLAYER_SELECT); + } + #endif + break; + + case GS_EVENT_MULTI_MISSION_SYNC: + gameseq_set_state(GS_STATE_MULTI_MISSION_SYNC); + break; + + case GS_EVENT_MULTI_START_GAME: + gameseq_set_state(GS_STATE_MULTI_START_GAME); + break; + + case GS_EVENT_MULTI_HOST_OPTIONS: + gameseq_set_state(GS_STATE_MULTI_HOST_OPTIONS); + break; + + case GS_EVENT_MULTI_DOGFIGHT_DEBRIEF: + gameseq_set_state(GS_STATE_MULTI_DOGFIGHT_DEBRIEF); + break; + + case GS_EVENT_TEAM_SELECT: + gameseq_set_state(GS_STATE_TEAM_SELECT); + break; + + case GS_EVENT_END_CAMPAIGN: + gameseq_set_state(GS_STATE_END_OF_CAMPAIGN); + break; + + case GS_EVENT_END_DEMO: + gameseq_set_state(GS_STATE_END_DEMO); + break; + + case GS_EVENT_LOOP_BRIEF: + gameseq_set_state(GS_STATE_LOOP_BRIEF); + break; + + default: + Int3(); + break; + } +} + +// Called when a state is being left. +// The current state is still at old_state, but as soon as +// this function leaves, then the current state will become +// new state. You should never try to change the state +// in here... if you think you need to, you probably really +// need to post an event, not change the state. +void game_leave_state( int old_state, int new_state ) +{ + int end_mission = 1; + + switch (new_state) { + case GS_STATE_GAME_PAUSED: + case GS_STATE_DEBUG_PAUSED: + case GS_STATE_OPTIONS_MENU: + case GS_STATE_CONTROL_CONFIG: + case GS_STATE_MISSION_LOG_SCROLLBACK: + case GS_STATE_DEATH_DIED: + case GS_STATE_SHOW_GOALS: + case GS_STATE_HOTKEY_SCREEN: + case GS_STATE_MULTI_PAUSED: + case GS_STATE_TRAINING_PAUSED: + case GS_STATE_EVENT_DEBUG: + case GS_STATE_GAMEPLAY_HELP: + end_mission = 0; // these events shouldn't end a mission + break; + } + + switch (old_state) { + case GS_STATE_BRIEFING: + brief_stop_voices(); + if ( (new_state != GS_STATE_OPTIONS_MENU) && (new_state != GS_STATE_WEAPON_SELECT) + && (new_state != GS_STATE_SHIP_SELECT) && (new_state != GS_STATE_HOTKEY_SCREEN) + && (new_state != GS_STATE_TEAM_SELECT) ){ + common_select_close(); + if ( new_state == GS_STATE_MAIN_MENU ) { + freespace_stop_mission(); + } + } + + // COMMAND LINE OPTION + if (Cmdline_multi_stream_chat_to_file){ + cfwrite_string(NOX("-------------------------------------------\n"),Multi_chat_stream); + cfclose(Multi_chat_stream); + } + break; + + case GS_STATE_DEBRIEF: + if ( (new_state != GS_STATE_VIEW_MEDALS) && (new_state != GS_STATE_OPTIONS_MENU) ) { + debrief_close(); + } + break; + + case GS_STATE_MULTI_DOGFIGHT_DEBRIEF: + multi_df_debrief_close(); + break; + + case GS_STATE_LOAD_MISSION_MENU: + mission_load_menu_close(); + break; + + case GS_STATE_SIMULATOR_ROOM: + sim_room_close(); + break; + + case GS_STATE_CAMPAIGN_ROOM: + campaign_room_close(); + break; + + case GS_STATE_CMD_BRIEF: + if (new_state == GS_STATE_OPTIONS_MENU) { + cmd_brief_hold(); + + } else { + cmd_brief_close(); + if (new_state == GS_STATE_MAIN_MENU) + freespace_stop_mission(); + } + + break; + + case GS_STATE_RED_ALERT: + red_alert_close(); + break; + + case GS_STATE_SHIP_SELECT: + if ( new_state != GS_STATE_OPTIONS_MENU && new_state != GS_STATE_WEAPON_SELECT && + new_state != GS_STATE_HOTKEY_SCREEN && + new_state != GS_STATE_BRIEFING && new_state != GS_STATE_TEAM_SELECT) { + common_select_close(); + if ( new_state == GS_STATE_MAIN_MENU ) { + freespace_stop_mission(); + } + } + break; + + case GS_STATE_WEAPON_SELECT: + if ( new_state != GS_STATE_OPTIONS_MENU && new_state != GS_STATE_SHIP_SELECT && + new_state != GS_STATE_HOTKEY_SCREEN && + new_state != GS_STATE_BRIEFING && new_state != GS_STATE_TEAM_SELECT) { + common_select_close(); + if ( new_state == GS_STATE_MAIN_MENU ) { + freespace_stop_mission(); + } + } + break; + + case GS_STATE_TEAM_SELECT: + if ( new_state != GS_STATE_OPTIONS_MENU && new_state != GS_STATE_SHIP_SELECT && + new_state != GS_STATE_HOTKEY_SCREEN && + new_state != GS_STATE_BRIEFING && new_state != GS_STATE_WEAPON_SELECT) { + common_select_close(); + if ( new_state == GS_STATE_MAIN_MENU ) { + freespace_stop_mission(); + } + } + break; + + case GS_STATE_MAIN_MENU: +#if defined(PRESS_TOUR_BUILD) || defined(PD_BUILD) + mht_close(); +#else + main_hall_close(); +#endif + break; + + case GS_STATE_OPTIONS_MENU: + //game_start_time(); + if(new_state == GS_STATE_MULTI_JOIN_GAME){ + multi_join_clear_game_list(); + } + options_menu_close(); + break; + + case GS_STATE_BARRACKS_MENU: + if(new_state != GS_STATE_VIEW_MEDALS){ + barracks_close(); + } + break; + + case GS_STATE_MISSION_LOG_SCROLLBACK: + hud_scrollback_close(); + break; + + case GS_STATE_TRAINING_MENU: + training_menu_close(); + break; + + case GS_STATE_GAME_PLAY: + if ( !(Game_mode & GM_STANDALONE_SERVER) ) { + player_save_target_and_weapon_link_prefs(); + game_stop_looped_sounds(); + } + + sound_env_disable(); + joy_ff_stop_effects(); + + // stop game time under certain conditions + if ( end_mission || (Game_mode & GM_NORMAL) || ((Game_mode & GM_MULTIPLAYER) && (new_state == GS_STATE_MULTI_PAUSED)) ){ + game_stop_time(); + } + + if (end_mission) { + // shut down any recording or playing demos +#ifdef DEMO_SYSTEM + demo_close(); +#endif + + // when in multiplayer and going back to the main menu, send a leave game packet + // right away (before calling stop mission). stop_mission was taking to long to + // close mission down and I want people to get notified ASAP. + if ( (Game_mode & GM_MULTIPLAYER) && (new_state == GS_STATE_MAIN_MENU) ){ + multi_quit_game(PROMPT_NONE); + } + + freespace_stop_mission(); + Game_time_compression = F1_0; + } + break; + + case GS_STATE_TECH_MENU: + techroom_close(); + break; + + case GS_STATE_TRAINING_PAUSED: + Training_num_lines = 0; + // fall through to GS_STATE_GAME_PAUSED + + case GS_STATE_GAME_PAUSED: + game_start_time(); + if ( end_mission ) { + pause_close(0); + } + break; + + case GS_STATE_DEBUG_PAUSED: + #ifndef NDEBUG + game_start_time(); + pause_debug_close(); + #endif + break; + + case GS_STATE_HUD_CONFIG: + hud_config_close(); + break; + + // join/start a game + case GS_STATE_MULTI_JOIN_GAME: + if(new_state != GS_STATE_OPTIONS_MENU){ + multi_join_game_close(); + } + break; + + case GS_STATE_MULTI_HOST_SETUP: + case GS_STATE_MULTI_CLIENT_SETUP: + // if this is just the host going into the options screen, don't do anything + if((new_state == GS_STATE_MULTI_HOST_OPTIONS) || (new_state == GS_STATE_OPTIONS_MENU)){ + break; + } + + // close down the proper state + if(old_state == GS_STATE_MULTI_HOST_SETUP){ + multi_create_game_close(); + } else { + multi_game_client_setup_close(); + } + + // COMMAND LINE OPTION + if (Cmdline_multi_stream_chat_to_file){ + if( (new_state != GS_STATE_TEAM_SELECT) && (Multi_chat_stream!=NULL) ) { + cfwrite_string(NOX("-------------------------------------------\n"),Multi_chat_stream); + cfclose(Multi_chat_stream); + } + } + break; + + case GS_STATE_CONTROL_CONFIG: + control_config_close(); + break; + + case GS_STATE_DEATH_DIED: + Game_mode &= ~GM_DEAD_DIED; + + // early end while respawning or blowing up in a multiplayer game + if((Game_mode & GM_MULTIPLAYER) && ((new_state == GS_STATE_DEBRIEF) || (new_state == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ){ + game_stop_time(); + freespace_stop_mission(); + } + break; + + case GS_STATE_DEATH_BLEW_UP: + Game_mode &= ~GM_DEAD_BLEW_UP; + + // for single player, we might reload mission, etc. For multiplayer, look at my new state + // to determine if I should do anything. + if ( !(Game_mode & GM_MULTIPLAYER) ) { + if ( end_mission ){ + freespace_stop_mission(); + } + } else { + // if we are not respawing as an observer or as a player, our new state will not + // be gameplay state. + if ( (new_state != GS_STATE_GAME_PLAY) && (new_state != GS_STATE_MULTI_PAUSED) ) { + game_stop_time(); // hasn't been called yet!! + freespace_stop_mission(); + } + } + break; + + + case GS_STATE_CREDITS: + credits_close(); + break; + + case GS_STATE_VIEW_MEDALS: + medal_main_close(); + break; + + case GS_STATE_SHOW_GOALS: + mission_show_goals_close(); + break; + + case GS_STATE_HOTKEY_SCREEN: + if ( new_state != GS_STATE_OPTIONS_MENU ) { + mission_hotkey_close(); + } + break; + + case GS_STATE_MULTI_MISSION_SYNC: + // if we're moving into the options menu, don't do anything + if(new_state == GS_STATE_OPTIONS_MENU){ + break; + } + + Assert( Game_mode & GM_MULTIPLAYER ); + multi_sync_close(); + if ( new_state == GS_STATE_GAME_PLAY ){ + // palette_restore_palette(); + + // change a couple of flags to indicate our state!!! + Net_player->state = NETPLAYER_STATE_IN_MISSION; + send_netplayer_update_packet(); + + // set the game mode + Game_mode |= GM_IN_MISSION; + } + break; + + case GS_STATE_VIEW_CUTSCENES: + cutscenes_screen_close(); + break; + + case GS_STATE_MULTI_STD_WAIT: + multi_standalone_wait_close(); + break; + + case GS_STATE_STANDALONE_MAIN: + standalone_main_close(); + if(new_state == GS_STATE_MULTI_STD_WAIT){ + init_multiplayer_stats(); + } + break; + + case GS_STATE_MULTI_PAUSED: + // if ( end_mission ){ + pause_close(1); + // } + break; + + case GS_STATE_INGAME_PRE_JOIN: + multi_ingame_select_close(); + break; + + case GS_STATE_STANDALONE_POSTGAME: + multi_standalone_postgame_close(); + break; + + case GS_STATE_INITIAL_PLAYER_SELECT: + player_select_close(); + break; + + case GS_STATE_MULTI_START_GAME: + multi_start_game_close(); + break; + + case GS_STATE_MULTI_HOST_OPTIONS: + multi_host_options_close(); + break; + + case GS_STATE_END_OF_CAMPAIGN: + mission_campaign_end_close(); + break; + + case GS_STATE_LOOP_BRIEF: + loop_brief_close(); + break; + } +} + +// Called when a state is being entered. +// The current state is set to the state we're entering at +// this point, and old_state is set to the state we're coming +// from. You should never try to change the state +// in here... if you think you need to, you probably really +// need to post an event, not change the state. + +void game_enter_state( int old_state, int new_state ) +{ + switch (new_state) { + case GS_STATE_MAIN_MENU: + // in multiplayer mode, be sure that we are not doing networking anymore. + if ( Game_mode & GM_MULTIPLAYER ) { + Assert( Net_player != NULL ); + Net_player->flags &= ~NETINFO_FLAG_DO_NETWORKING; + } + + Game_time_compression = F1_0; + + // determine which ship this guy is currently based on +#if defined(PRESS_TOUR_BUILD) || defined(PD_BUILD) + mht_init(); +#else + if (Player->on_bastion) { + main_hall_init(1); + } else { + main_hall_init(0); + } +#endif + break; + + case GS_STATE_BRIEFING: + main_hall_stop_music(); + main_hall_stop_ambient(); + + if (Game_mode & GM_NORMAL) { + // AL: Don't call freespace_start_mission() if re-entering from ship or weapon select + // MWA: or from options or hotkey screens + // JH: or if the command brief state already did this + if ( (old_state != GS_STATE_OPTIONS_MENU) && (old_state != GS_STATE_HOTKEY_SCREEN) + && (old_state != GS_STATE_SHIP_SELECT) && (old_state != GS_STATE_WEAPON_SELECT) + && (old_state != GS_STATE_CMD_BRIEF) ) { + if ( !game_start_mission() ) // this should put us into a new state on failure! + break; + } + } + // maybe play a movie before the briefing. don't play if entering briefing screen from ship or weapon select. + if ( (old_state == GS_STATE_DEBRIEF) || (old_state == GS_STATE_SIMULATOR_ROOM) || (old_state == GS_STATE_MAIN_MENU)) + mission_campaign_maybe_play_movie(CAMPAIGN_MOVIE_PRE_MISSION); + + Game_time_compression = F1_0; + + if ( red_alert_mission() ) { + gameseq_post_event(GS_EVENT_RED_ALERT); + } else { + brief_init(); + } + + break; + + case GS_STATE_DEBRIEF: + game_stop_looped_sounds(); + mission_goal_fail_incomplete(); // fail all incomplete goals before entering debriefing + if ( (old_state != GS_STATE_VIEW_MEDALS) && (old_state != GS_STATE_OPTIONS_MENU) ){ + debrief_init(); + } + break; + + case GS_STATE_MULTI_DOGFIGHT_DEBRIEF: + multi_df_debrief_init(); + break; + + case GS_STATE_LOAD_MISSION_MENU: + mission_load_menu_init(); + break; + + case GS_STATE_SIMULATOR_ROOM: + sim_room_init(); + break; + + case GS_STATE_CAMPAIGN_ROOM: + campaign_room_init(); + break; + + case GS_STATE_RED_ALERT: + mission_campaign_maybe_play_movie(CAMPAIGN_MOVIE_PRE_MISSION); + red_alert_init(); + break; + + case GS_STATE_CMD_BRIEF: { + int team_num = 0; // team number used as index for which cmd brief to use. + + if (old_state == GS_STATE_OPTIONS_MENU) { + cmd_brief_unhold(); + + } else { + main_hall_stop_music(); + main_hall_stop_ambient(); + + if (Game_mode & GM_NORMAL) { + // AL: Don't call freespace_start_mission() if re-entering from ship or weapon select + // MWA: or from options or hotkey screens + // JH: or if the command brief state already did this + if ( (old_state != GS_STATE_OPTIONS_MENU) && (old_state != GS_STATE_HOTKEY_SCREEN) + && (old_state != GS_STATE_SHIP_SELECT) && (old_state != GS_STATE_WEAPON_SELECT) ) { + if ( !game_start_mission() ) // this should put us into a new state on failure! + break; + } + } + + // maybe play a movie before the briefing. don't play if entering briefing screen from ship or weapon select. + if ( (old_state == GS_STATE_DEBRIEF) || (old_state == GS_STATE_SIMULATOR_ROOM) || (old_state == GS_STATE_MAIN_MENU)) + mission_campaign_maybe_play_movie(CAMPAIGN_MOVIE_PRE_MISSION); + + cmd_brief_init(team_num); + } + + break; + } + + case GS_STATE_SHIP_SELECT: + ship_select_init(); + break; + + case GS_STATE_WEAPON_SELECT: + weapon_select_init(); + break; + + case GS_STATE_TEAM_SELECT: + multi_ts_init(); + break; + + case GS_STATE_GAME_PAUSED: + game_stop_time(); + pause_init(0); + break; + + case GS_STATE_DEBUG_PAUSED: + // game_stop_time(); + // os_set_title("FreeSpace - PAUSED"); + // break; + // + case GS_STATE_TRAINING_PAUSED: + #ifndef NDEBUG + game_stop_time(); + pause_debug_init(); + #endif + break; + + case GS_STATE_OPTIONS_MENU: + //game_stop_time(); + options_menu_init(); + break; + + case GS_STATE_GAME_PLAY: + // coming from the gameplay state or the main menu, we might need to load the mission + if ( (Game_mode & GM_NORMAL) && ((old_state == GS_STATE_MAIN_MENU) || (old_state == GS_STATE_GAME_PLAY) || (old_state == GS_STATE_DEATH_BLEW_UP)) ) { + if ( !game_start_mission() ) // this should put us into a new state. + // Failed!!! + break; + } + + // if we are coming from the briefing, ship select, weapons loadout, or main menu (in the + // case of quick start), then do bitmap loads, etc Don't do any of the loading stuff + // if we are in multiplayer -- this stuff is all handled in the multi-wait section + if ( !(Game_mode & GM_MULTIPLAYER) && (old_state == GS_STATE_BRIEFING) || (old_state == GS_STATE_SHIP_SELECT) || + (old_state == GS_STATE_WEAPON_SELECT) || (old_state == GS_STATE_MAIN_MENU) || (old_state == GS_STATE_MULTI_STD_WAIT) || (old_state == GS_STATE_SIMULATOR_ROOM) ) { + // JAS: Used to do all paging here. + + #ifndef NDEBUG + //XSTR:OFF + HUD_printf("Skill level is set to ** %s **", Skill_level_names(Game_skill_level)); + //XSTR:ON + #endif + + main_hall_stop_music(); + main_hall_stop_ambient(); + event_music_first_pattern(); // start the first pattern + } + + // special code that restores player ship selection and weapons loadout when doing a quick start + if ( !(Game_mode & GM_MULTIPLAYER) && (old_state == GS_STATE_MAIN_MENU) || (old_state == GS_STATE_DEATH_BLEW_UP) || (old_state == GS_STATE_GAME_PLAY) ) { + if ( !stricmp(Player_loadout.filename, Game_current_mission_filename) ) { + wss_direct_restore_loadout(); + } + } + + // single-player, quick-start after just died... we need to set weapon linking and kick off the event music + if (!(Game_mode & GM_MULTIPLAYER) && (old_state == GS_STATE_DEATH_BLEW_UP) ) { + event_music_first_pattern(); // start the first pattern + } + + if ( !(Game_mode & GM_STANDALONE_SERVER) && (old_state != GS_STATE_GAME_PAUSED) && (old_state != GS_STATE_MULTI_PAUSED) ) { + event_music_first_pattern(); // start the first pattern + } + player_restore_target_and_weapon_link_prefs(); + + Game_mode |= GM_IN_MISSION; + +#ifndef NDEBUG + // required to truely make mouse deltas zeroed in debug mouse code +void mouse_force_pos(int x, int y); + mouse_force_pos(gr_screen.max_w / 2, gr_screen.max_h / 2); +#endif + + game_flush(); + + // only start time if in single player, or coming from multi wait state + if ( + ( + (Game_mode & GM_NORMAL) && + (old_state != GS_STATE_VIEW_CUTSCENES) + ) || ( + (Game_mode & GM_MULTIPLAYER) && ( + (old_state == GS_STATE_MULTI_PAUSED) || + (old_state == GS_STATE_MULTI_MISSION_SYNC) + ) + ) + ) + game_start_time(); + + // when coming from the multi paused state, reset the timestamps + if ( (Game_mode & GM_MULTIPLAYER) && (old_state == GS_STATE_MULTI_PAUSED) ){ + multi_reset_timestamps(); + } + + if ((Game_mode & GM_MULTIPLAYER) && (old_state != GS_STATE_DEATH_BLEW_UP) ) { + // initialize all object update details + multi_oo_gameplay_init(); + } + + // under certain circumstances, the server should reset the object update rate limiting stuff + if( ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)) && + (old_state == GS_STATE_MULTI_PAUSED) || (old_state == GS_STATE_MULTI_MISSION_SYNC) ){ + + // reinitialize the rate limiting system for all clients + multi_oo_rate_init_all(); + } + + // multiplayer clients should always re-initialize their control info rate limiting system + if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + multi_oo_rate_init_all(); + } + + // reset ping times + if(Game_mode & GM_MULTIPLAYER){ + multi_ping_reset_players(); + } + + Game_subspace_effect = 0; + if (The_mission.flags & MISSION_FLAG_SUBSPACE) { + Game_subspace_effect = 1; + if( !(Game_mode & GM_STANDALONE_SERVER) ){ + game_start_subspace_ambient_sound(); + } + } + + sound_env_set(&Game_sound_env); + joy_ff_mission_init(Ship_info[Player_ship->ship_info_index].rotation_time); + + // clear multiplayer button info i + extern button_info Multi_ship_status_bi; + memset(&Multi_ship_status_bi, 0, sizeof(button_info)); + break; + + case GS_STATE_HUD_CONFIG: + hud_config_init(); + break; + + case GS_STATE_MULTI_JOIN_GAME: + multi_join_clear_game_list(); + + if (old_state != GS_STATE_OPTIONS_MENU) { + multi_join_game_init(); + } + + break; + + case GS_STATE_MULTI_HOST_SETUP: + // don't reinitialize if we're coming back from the host options screen + if ((old_state != GS_STATE_MULTI_HOST_OPTIONS) && (old_state != GS_STATE_OPTIONS_MENU)) { + multi_create_game_init(); + } + + break; + + case GS_STATE_MULTI_CLIENT_SETUP: + if (old_state != GS_STATE_OPTIONS_MENU) { + multi_game_client_setup_init(); + } + + break; + + case GS_STATE_CONTROL_CONFIG: + control_config_init(); + break; + + case GS_STATE_TECH_MENU: + techroom_init(); + break; + + case GS_STATE_BARRACKS_MENU: + if(old_state != GS_STATE_VIEW_MEDALS){ + barracks_init(); + } + break; + + case GS_STATE_MISSION_LOG_SCROLLBACK: + hud_scrollback_init(); + break; + + case GS_STATE_DEATH_DIED: + Player_died_time = timestamp(10); + + if(!(Game_mode & GM_MULTIPLAYER)){ + player_show_death_message(); + } + Game_mode |= GM_DEAD_DIED; + break; + + case GS_STATE_DEATH_BLEW_UP: + if ( !popupdead_is_active() ) { + Player_ai->target_objnum = -1; + } + + // stop any local EMP effect + emp_stop_local(); + + Players[Player_num].flags &= ~PLAYER_FLAGS_AUTO_TARGETING; // Prevent immediate switch to a hostile ship. + Game_mode |= GM_DEAD_BLEW_UP; + Show_viewing_from_self = 0; + + // timestamp how long we should wait before displaying the died popup + if ( !popupdead_is_active() ) { + Player_died_popup_wait = timestamp(PLAYER_DIED_POPUP_WAIT); + } + break; + + case GS_STATE_GAMEPLAY_HELP: + gameplay_help_init(); + break; + + case GS_STATE_CREDITS: + main_hall_stop_music(); + main_hall_stop_ambient(); + credits_init(); + break; + + case GS_STATE_VIEW_MEDALS: + medal_main_init(Player); + break; + + case GS_STATE_SHOW_GOALS: + mission_show_goals_init(); + break; + + case GS_STATE_HOTKEY_SCREEN: + mission_hotkey_init(); + break; + + case GS_STATE_MULTI_MISSION_SYNC: + // if we're coming from the options screen, don't do any + if(old_state == GS_STATE_OPTIONS_MENU){ + break; + } + + switch(Multi_sync_mode){ + case MULTI_SYNC_PRE_BRIEFING: + // if moving from game forming to the team select state + multi_sync_init(); + break; + case MULTI_SYNC_POST_BRIEFING: + // if moving from briefing into the mission itself + multi_sync_init(); + + // tell everyone that we're now loading data + Net_player->state = NETPLAYER_STATE_DATA_LOAD; + send_netplayer_update_packet(); + + // JAS: Used to do all paging here!!!! + + Net_player->state = NETPLAYER_STATE_WAITING; + send_netplayer_update_packet(); + Missiontime = 0; + Game_time_compression = F1_0; + break; + case MULTI_SYNC_INGAME: + multi_sync_init(); + break; + } + break; + + case GS_STATE_VIEW_CUTSCENES: + cutscenes_screen_init(); + break; + + case GS_STATE_MULTI_STD_WAIT: + multi_standalone_wait_init(); + break; + + case GS_STATE_STANDALONE_MAIN: + // don't initialize if we're coming from one of these 2 states unless there are no + // players left (reset situation) + if((old_state != GS_STATE_STANDALONE_POSTGAME) || multi_endgame_ending()){ + standalone_main_init(); + } + break; + + case GS_STATE_MULTI_PAUSED: + pause_init(1); + break; + + case GS_STATE_INGAME_PRE_JOIN: + multi_ingame_select_init(); + break; + + case GS_STATE_STANDALONE_POSTGAME: + multi_standalone_postgame_init(); + break; + + case GS_STATE_INITIAL_PLAYER_SELECT: + player_select_init(); + break; + + case GS_STATE_MULTI_START_GAME: + multi_start_game_init(); + break; + + case GS_STATE_MULTI_HOST_OPTIONS: + multi_host_options_init(); + break; + + case GS_STATE_END_OF_CAMPAIGN: + mission_campaign_end_init(); + break; + + case GS_STATE_LOOP_BRIEF: + loop_brief_init(); + break; + + } // end switch +} + +// do stuff that may need to be done regardless of state +void game_do_state_common(int state,int no_networking) +{ + game_maybe_draw_mouse(flFrametime); // determine if to draw the mouse this frame + snd_do_frame(); // update sound system + event_music_do_frame(); // music needs to play across many states + + multi_log_process(); + + if (no_networking) { + return; + } + + // maybe do a multiplayer frame based on game mode and state type + if (Game_mode & GM_MULTIPLAYER) { + switch (state) { + case GS_STATE_OPTIONS_MENU: + case GS_STATE_GAMEPLAY_HELP: + case GS_STATE_HOTKEY_SCREEN: + case GS_STATE_HUD_CONFIG: + case GS_STATE_CONTROL_CONFIG: + case GS_STATE_MISSION_LOG_SCROLLBACK: + case GS_STATE_SHOW_GOALS: + case GS_STATE_VIEW_CUTSCENES: + case GS_STATE_EVENT_DEBUG: + multi_maybe_do_frame(); + break; + } + + game_do_networking(); + } +} + +// Called once a frame. +// You should never try to change the state +// in here... if you think you need to, you probably really +// need to post an event, not change the state. +int Game_do_state_should_skip = 0; +void game_do_state(int state) +{ + // always lets the do_state_common() function determine if the state should be skipped + Game_do_state_should_skip = 0; + + // legal to set the should skip state anywhere in this function + game_do_state_common(state); // do stuff that may need to be done regardless of state + + if(Game_do_state_should_skip){ + return; + } + + switch (state) { + case GS_STATE_MAIN_MENU: + game_set_frametime(GS_STATE_MAIN_MENU); +#if defined(PRESS_TOUR_BUILD) || defined(PD_BUILD) + mht_do(); +#else + main_hall_do(flFrametime); +#endif + break; + + case GS_STATE_OPTIONS_MENU: + game_set_frametime(GS_STATE_OPTIONS_MENU); + options_menu_do_frame(flFrametime); + break; + + case GS_STATE_BARRACKS_MENU: + game_set_frametime(GS_STATE_BARRACKS_MENU); + barracks_do_frame(flFrametime); + break; + + case GS_STATE_TRAINING_MENU: + game_set_frametime(GS_STATE_TRAINING_MENU); + training_menu_do_frame(flFrametime); + break; + + case GS_STATE_TECH_MENU: + game_set_frametime(GS_STATE_TECH_MENU); + techroom_do_frame(flFrametime); + break; + + case GS_STATE_GAMEPLAY_HELP: + game_set_frametime(GS_STATE_GAMEPLAY_HELP); + gameplay_help_do_frame(flFrametime); + break; + + case GS_STATE_GAME_PLAY: // do stuff that should be done during gameplay + game_do_frame(); + break; + + case GS_STATE_GAME_PAUSED: + pause_do(0); + break; + + case GS_STATE_DEBUG_PAUSED: + #ifndef NDEBUG + game_set_frametime(GS_STATE_DEBUG_PAUSED); + pause_debug_do(); + #endif + break; + + case GS_STATE_TRAINING_PAUSED: + game_training_pause_do(); + break; + + case GS_STATE_LOAD_MISSION_MENU: + game_set_frametime(GS_STATE_LOAD_MISSION_MENU); + mission_load_menu_do(); + break; + + case GS_STATE_BRIEFING: + game_set_frametime(GS_STATE_BRIEFING); + brief_do_frame(flFrametime); + break; + + case GS_STATE_DEBRIEF: + game_set_frametime(GS_STATE_DEBRIEF); + debrief_do_frame(flFrametime); + break; + + case GS_STATE_MULTI_DOGFIGHT_DEBRIEF: + game_set_frametime(GS_STATE_MULTI_DOGFIGHT_DEBRIEF); + multi_df_debrief_do(); + break; + + case GS_STATE_SHIP_SELECT: + game_set_frametime(GS_STATE_SHIP_SELECT); + ship_select_do(flFrametime); + break; + + case GS_STATE_WEAPON_SELECT: + game_set_frametime(GS_STATE_WEAPON_SELECT); + weapon_select_do(flFrametime); + break; + + case GS_STATE_MISSION_LOG_SCROLLBACK: + game_set_frametime(GS_STATE_MISSION_LOG_SCROLLBACK); + hud_scrollback_do_frame(flFrametime); + break; + + case GS_STATE_HUD_CONFIG: + game_set_frametime(GS_STATE_HUD_CONFIG); + hud_config_do_frame(flFrametime); + break; + + case GS_STATE_MULTI_JOIN_GAME: + game_set_frametime(GS_STATE_MULTI_JOIN_GAME); + multi_join_game_do_frame(); + break; + + case GS_STATE_MULTI_HOST_SETUP: + game_set_frametime(GS_STATE_MULTI_HOST_SETUP); + multi_create_game_do(); + break; + + case GS_STATE_MULTI_CLIENT_SETUP: + game_set_frametime(GS_STATE_MULTI_CLIENT_SETUP); + multi_game_client_setup_do_frame(); + break; + + case GS_STATE_CONTROL_CONFIG: + game_set_frametime(GS_STATE_CONTROL_CONFIG); + control_config_do_frame(flFrametime); + break; + + case GS_STATE_DEATH_DIED: + game_do_frame(); + break; + + case GS_STATE_DEATH_BLEW_UP: + game_do_frame(); + break; + + case GS_STATE_SIMULATOR_ROOM: + game_set_frametime(GS_STATE_SIMULATOR_ROOM); + sim_room_do_frame(flFrametime); + break; + + case GS_STATE_CAMPAIGN_ROOM: + game_set_frametime(GS_STATE_CAMPAIGN_ROOM); + campaign_room_do_frame(flFrametime); + break; + + case GS_STATE_RED_ALERT: + game_set_frametime(GS_STATE_RED_ALERT); + red_alert_do_frame(flFrametime); + break; + + case GS_STATE_CMD_BRIEF: + game_set_frametime(GS_STATE_CMD_BRIEF); + cmd_brief_do_frame(flFrametime); + break; + + case GS_STATE_CREDITS: + game_set_frametime(GS_STATE_CREDITS); + credits_do_frame(flFrametime); + break; + + case GS_STATE_VIEW_MEDALS: + game_set_frametime(GS_STATE_VIEW_MEDALS); + medal_main_do(); + break; + + case GS_STATE_SHOW_GOALS: + game_set_frametime(GS_STATE_SHOW_GOALS); + mission_show_goals_do_frame(flFrametime); + break; + + case GS_STATE_HOTKEY_SCREEN: + game_set_frametime(GS_STATE_HOTKEY_SCREEN); + mission_hotkey_do_frame(flFrametime); + break; + + case GS_STATE_VIEW_CUTSCENES: + game_set_frametime(GS_STATE_VIEW_CUTSCENES); + cutscenes_screen_do_frame(); + break; + + case GS_STATE_MULTI_STD_WAIT: + game_set_frametime(GS_STATE_STANDALONE_MAIN); + multi_standalone_wait_do(); + break; + + case GS_STATE_STANDALONE_MAIN: + game_set_frametime(GS_STATE_STANDALONE_MAIN); + standalone_main_do(); + break; + + case GS_STATE_MULTI_PAUSED: + game_set_frametime(GS_STATE_MULTI_PAUSED); + pause_do(1); + break; + + case GS_STATE_TEAM_SELECT: + game_set_frametime(GS_STATE_TEAM_SELECT); + multi_ts_do(); + break; + + case GS_STATE_INGAME_PRE_JOIN: + game_set_frametime(GS_STATE_INGAME_PRE_JOIN); + multi_ingame_select_do(); + break; + + case GS_STATE_EVENT_DEBUG: + #ifndef NDEBUG + game_set_frametime(GS_STATE_EVENT_DEBUG); + game_show_event_debug(flFrametime); + #endif + break; + + case GS_STATE_STANDALONE_POSTGAME: + game_set_frametime(GS_STATE_STANDALONE_POSTGAME); + multi_standalone_postgame_do(); + break; + + case GS_STATE_INITIAL_PLAYER_SELECT: + game_set_frametime(GS_STATE_INITIAL_PLAYER_SELECT); + player_select_do(); + break; + + case GS_STATE_MULTI_MISSION_SYNC: + game_set_frametime(GS_STATE_MULTI_MISSION_SYNC); + multi_sync_do(); + break; + + case GS_STATE_MULTI_START_GAME: + game_set_frametime(GS_STATE_MULTI_START_GAME); + multi_start_game_do(); + break; + + case GS_STATE_MULTI_HOST_OPTIONS: + game_set_frametime(GS_STATE_MULTI_HOST_OPTIONS); + multi_host_options_do(); + break; + + case GS_STATE_END_OF_CAMPAIGN: + mission_campaign_end_do(); + break; + + case GS_STATE_END_DEMO: + game_set_frametime(GS_STATE_END_DEMO); + end_demo_campaign_do(); + break; + + case GS_STATE_LOOP_BRIEF: + game_set_frametime(GS_STATE_LOOP_BRIEF); + loop_brief_do(); + break; + + } // end switch(gs_current_state) +} + + +// return 0 if there is enough RAM to run FreeSpace, otherwise return -1 +int game_do_ram_check(int ram_in_bytes) +{ + if ( ram_in_bytes < 30*1024*1024 ) { + int allowed_to_run = 1; + if ( ram_in_bytes < 25*1024*1024 ) { + allowed_to_run = 0; + } + + char tmp[1024]; + int Freespace_total_ram_MB; + Freespace_total_ram_MB = fl2i(ram_in_bytes/(1024*1024)); + + if ( allowed_to_run ) { + + sprintf( tmp, XSTR( "FreeSpace has detected that you only have %dMB of free memory.\n\nFreeSpace requires at least 32MB of memory to run. If you think you have more than %dMB of physical memory, ensure that you aren't running SmartDrive (SMARTDRV.EXE). Any memory allocated to SmartDrive is not usable by applications\n\nPress 'OK' to continue running with less than the minimum required memory\n", 193), Freespace_total_ram_MB, Freespace_total_ram_MB); + + int msgbox_rval; + msgbox_rval = MessageBox( NULL, tmp, XSTR( "Not Enough RAM", 194), MB_OKCANCEL ); + if ( msgbox_rval == IDCANCEL ) { + return -1; + } + + } else { + sprintf( tmp, XSTR( "FreeSpace has detected that you only have %dMB of free memory.\n\nFreeSpace requires at least 32MB of memory to run. If you think you have more than %dMB of physical memory, ensure that you aren't running SmartDrive (SMARTDRV.EXE). Any memory allocated to SmartDrive is not usable by applications\n", 195), Freespace_total_ram_MB, Freespace_total_ram_MB); + MessageBox( NULL, tmp, XSTR( "Not Enough RAM", 194), MB_OK ); + return -1; + } + } + + return 0; +} + +// Check if there is a freespace.exe in the /update directory (relative to where fs.exe is installed). +// If so, copy it over and remove the update directory. +void game_maybe_update_launcher(char *exe_dir) +{ + char src_filename[MAX_PATH]; + char dest_filename[MAX_PATH]; + + strcpy(src_filename, exe_dir); + strcat(src_filename, NOX("\\update\\freespace.exe")); + + strcpy(dest_filename, exe_dir); + strcat(dest_filename, NOX("\\freespace.exe")); + + // see if src_filename exists + FILE *fp; + fp = fopen(src_filename, "rb"); + if ( !fp ) { + return; + } + fclose(fp); + + SetFileAttributes(dest_filename, FILE_ATTRIBUTE_NORMAL); + + // copy updated freespace.exe to freespace exe dir + if ( CopyFile(src_filename, dest_filename, 0) == 0 ) { + MessageBox( NULL, XSTR("Unable to copy freespace.exe from update directory to installed directory. You should copy freespace.exe from the update directory (located in your FreeSpace install directory) to your install directory", 988), NULL, MB_OK|MB_TASKMODAL|MB_SETFOREGROUND ); + return; + } + + // delete the file in the update directory + DeleteFile(src_filename); + + // safe to assume directory is empty, since freespace.exe should only be the file ever in the update dir + char update_dir[MAX_PATH]; + strcpy(update_dir, exe_dir); + strcat(update_dir, NOX("\\update")); + RemoveDirectory(update_dir); +} + +void game_spew_pof_info_sub(int model_num, polymodel *pm, int sm, CFILE *out, int *out_total, int *out_destroyed_total) +{ + int i; + int sub_total = 0; + int sub_total_destroyed = 0; + int total = 0; + char str[255] = ""; + + // get the total for all his children + for (i=pm->submodel[sm].first_child; i>-1; i = pm->submodel[i].next_sibling ) { + game_spew_pof_info_sub(model_num, pm, i, out, &sub_total, &sub_total_destroyed); + } + + // find the # of faces for this _individual_ object + total = submodel_get_num_polys(model_num, sm); + if(strstr(pm->submodel[sm].name, "-destroyed")){ + sub_total_destroyed = total; + } + + // write out total + sprintf(str, "Submodel %s total : %d faces\n", pm->submodel[sm].name, total); + cfputs(str, out); + + *out_total += total + sub_total; + *out_destroyed_total += sub_total_destroyed; +} + +#define BAIL() do { int idx; for(idx=0; idx= 0){ + pm = model_get(model_num); + + // if we have a real model + if(pm != NULL){ + cfputs(str, out); + cfputs("\n", out); + + // go through and print all raw submodels + cfputs("RAW\n", out); + total = 0; + model_total = 0; + for (i=0; in_models; i++) { + total = submodel_get_num_polys(model_num, i); + + model_total += total; + sprintf(str, "Submodel %s total : %d faces\n", pm->submodel[i].name, total); + cfputs(str, out); + } + sprintf(str, "Model total %d\n", model_total); + cfputs(str, out); + + // now go through and do it by LOD + cfputs("BY LOD\n\n", out); + for(i=0; in_detail_levels; i++){ + sprintf(str, "LOD %d\n", i); + cfputs(str, out); + + // submodels + root_total = submodel_get_num_polys(model_num, pm->detail[i] ); + total = 0; + destroyed_total = 0; + for (j=pm->submodel[pm->detail[i]].first_child; j>-1; j = pm->submodel[j].next_sibling ) { + game_spew_pof_info_sub(model_num, pm, j, out, &total, &destroyed_total); + } + + sprintf(str, "Submodel %s total : %d faces\n", pm->submodel[pm->detail[i]].name, root_total); + cfputs(str, out); + + sprintf(str, "TOTAL: %d\n", total + root_total); + cfputs(str, out); + sprintf(str, "TOTAL not counting destroyed faces %d\n", (total + root_total) - destroyed_total); + cfputs(str, out); + sprintf(str, "TOTAL destroyed faces %d\n\n", destroyed_total); + cfputs(str, out); + } + cfputs("------------------------------------------------------------------------\n\n", out); + } + } + + if(counted >= MAX_POLYGON_MODELS - 5){ + model_free_all(); + counted = 0; + } + } + + cfclose(out); + model_free_all(); + BAIL(); +} + +DCF(pofspew, "") +{ + game_spew_pof_info(); +} + +int PASCAL WinMainSub(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow) +{ + int state; + + // Don't let more than one instance of Freespace run. + HWND hwnd = FindWindow( NOX( "FreeSpaceClass" ), NULL ); + if ( hwnd ) { + SetForegroundWindow(hwnd); + return 0; + } + + // Find out how much RAM is on this machine + MEMORYSTATUS ms; + ms.dwLength = sizeof(MEMORYSTATUS); + GlobalMemoryStatus(&ms); + Freespace_total_ram = ms.dwTotalPhys; + + if ( game_do_ram_check(Freespace_total_ram) == -1 ) { + return 0; + } + + if ( ms.dwTotalVirtual < 1024 ) { + MessageBox( NULL, XSTR( "FreeSpace requires virtual memory to run.\r\n", 196), XSTR( "No Virtual Memory", 197), MB_OK ); + return 0; + } + + if (!vm_init(24*1024*1024)) { + MessageBox( NULL, XSTR( "Not enough memory to run Freespace.\r\nTry closing down some other applications.\r\n", 198), XSTR( "Not Enough Memory", 199), MB_OK ); + return 0; + } + + char *tmp_mem = (char *) malloc(16 * 1024 * 1024); + if (!tmp_mem) { + MessageBox(NULL, XSTR( "Not enough memory to run Freespace.\r\nTry closing down some other applications.\r\n", 198), XSTR( "Not Enough Memory", 199), MB_OK); + return 0; + } + + free(tmp_mem); + tmp_mem = NULL; + +/* this code doesn't work, and we will hit an error about being unable to load the direct draw + dll before we get here anyway if it's not installed (unless we load it manually, which doesn't + seem worth bothering with. + + LONG lResult; + + lResult = RegOpenKeyEx( + HKEY_LOCAL_MACHINE, // Where it is + "Software\\Microsoft\\DirectX", // name of key + NULL, // DWORD reserved + KEY_QUERY_VALUE, // Allows all changes + &hKey // Location to store key + ); + + if (lResult == ERROR_SUCCESS) { + char version[32]; + DWORD dwType, dwLen; + + dwLen = 32; + lResult = RegQueryValueEx( + hKey, // Handle to key + "Version", // The values name + NULL, // DWORD reserved + &dwType, // What kind it is + (ubyte *) version, // value to set + &dwLen // How many bytes to set + ); + + if (lResult == ERROR_SUCCESS) { + dx_version = atoi(strstr(version, ".") + 1); + + } else { + int val; + DWORD dwType, dwLen; + + dwLen = 4; + lResult = RegQueryValueEx( + hKey, // Handle to key + "InstalledVersion", // The values name + NULL, // DWORD reserved + &dwType, // What kind it is + (ubyte *) &val, // value to set + &dwLen // How many bytes to set + ); + + if (lResult == ERROR_SUCCESS) { + dx_version = val; + } + } + + RegCloseKey(hKey); + } + + if (dx_version < 3) { + MessageBox(NULL, "DirectX 3.0 or higher is required and wasn't detected. You can get the\n" + "latest version of DirectX at:\n\n" + "http://www.microsoft.com/msdownload/directx/dxf/enduser5.0/default.htm", "DirectX required", MB_OK); + + MessageBox(NULL, "DirectX 3.0 or higher is required and wasn't detected. You can install\n" + "DirectX 5.2 by pressing the 'Install DirectX' button on the FreeSpace Launcher", "DirectX required", MB_OK); + + return 0; + } +*/ + //===================================================== + // Make sure we're running in the right directory. + char exe_dir[1024]; + + if ( GetModuleFileName( hInst, exe_dir, 1023 ) > 0 ) { + char *p = exe_dir + strlen(exe_dir); + + // chop off the filename + while( (p>exe_dir) && (*p!='\\') && (*p!='/') && (*p!=':') ) { + p--; + } + *p = 0; + + // Set directory + if ( strlen(exe_dir) > 0 ) { + SetCurrentDirectory(exe_dir); + } + + // check for updated freespace.exe + game_maybe_update_launcher(exe_dir); + } + + + #ifndef NDEBUG + { + extern void windebug_memwatch_init(); + windebug_memwatch_init(); + } + #endif + + parse_cmdline(szCmdLine); + +#ifdef STANDALONE_ONLY_BUILD + Is_standalone = 1; + nprintf(("Network", "Standalone running")); +#else + if (Is_standalone){ + nprintf(("Network", "Standalone running")); + } +#endif + + init_cdrom(); + game_init(); + game_stop_time(); + + // maybe spew pof stuff + if(Cmdline_spew_pof_info){ + game_spew_pof_info(); + game_shutdown(); + return 1; + } + + // non-demo, non-standalone, play the intro movie + if(!Is_standalone){ +#ifndef DEMO +#ifdef RELEASE_REAL + char *plist[5]; + if( (cf_get_file_list(2, plist, CF_TYPE_MULTI_PLAYERS, NOX("*.plr")) <= 0) && (cf_get_file_list(2, plist, CF_TYPE_SINGLE_PLAYERS, NOX("*.plr")) <= 0) ){ + // prompt for cd 2 +#if defined(OEM_BUILD) + game_do_cd_check_specific(FS_CDROM_VOLUME_1, 1); +#else + game_do_cd_check_specific(FS_CDROM_VOLUME_2, 2); +#endif // defined(OEM_BUILD) + } +#endif + } + + if ( !Is_standalone ) { + + // release -- movies always play + #if defined(NDEBUG) + + // in RELEASE_REAL builds make the user stick in CD2 if there are no pilots on disk so that we guarantee he plays the movie + // no soup for you! + // movie_play( NOX("intro.mve"), 0 ); + + // debug version, movie will only play with -showmovies + #else if !defined(NDEBUG) + + // no soup for you! + // movie_play( NOX("intro.mve"), 0); +/* +#ifndef NDEBUG + if ( Cmdline_show_movies ) + movie_play( NOX("intro.mve"), 0 ); +#endif +*/ + #endif + } + +#endif + + if (Is_standalone){ + gameseq_post_event(GS_EVENT_STANDALONE_MAIN); + } else { + gameseq_post_event(GS_EVENT_GAME_INIT); // start the game rolling -- check for default pilot, or go to the pilot select screen + } + + while (1) { + // only important for non THREADED mode + os_poll(); + + state = gameseq_process_events(); + if ( state == GS_STATE_QUIT_GAME ){ + break; + } + } + +#ifdef FS2_DEMO + if(!Is_standalone){ + demo_upsell_show_screens(); + } +#elif defined(OEM_BUILD) + // show upsell screens on exit + oem_upsell_show_screens(); +#endif + + game_shutdown(); + return 1; +} + +int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow) +{ + int result = -1; + + __try + { + result = WinMainSub(hInst, hPrev, szCmdLine, nCmdShow); + } + __except(RecordExceptionInfo(GetExceptionInformation(), "Freespace 2 Main Thread")) + { + // Do nothing here - RecordExceptionInfo() has already done + // everything that is needed. Actually this code won't even + // get called unless you return EXCEPTION_EXECUTE_HANDLER from + // the __except clause. + } + return result; +} + +// launcher the fslauncher program on exit +void game_launch_launcher_on_exit() +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + char cmd_line[2048]; + char original_path[1024] = ""; + + memset( &si, 0, sizeof(STARTUPINFO) ); + si.cb = sizeof(si); + + // directory + _getcwd(original_path, 1023); + + // set up command line + strcpy(cmd_line, original_path); + strcat(cmd_line, "\\"); + strcat(cmd_line, LAUNCHER_FNAME); + strcat(cmd_line, " -straight_to_update"); + + BOOL ret = CreateProcess( NULL, // pointer to name of executable module + cmd_line, // pointer to command line string + NULL, // pointer to process security attributes + NULL, // pointer to thread security attributes + FALSE, // handle inheritance flag + CREATE_DEFAULT_ERROR_MODE, // creation flags + NULL, // pointer to new environment block + NULL, // pointer to current directory name + &si, // pointer to STARTUPINFO + &pi // pointer to PROCESS_INFORMATION + ); + // to eliminate build warnings + ret; +} + + +// game_shutdown() +// +// This function is called when FreeSpace terminates normally. +// +void game_shutdown(void) +{ + timeEndPeriod(1); + + // don't ever flip a page on the standalone! + if(!(Game_mode & GM_STANDALONE_SERVER)){ + gr_reset_clip(); + gr_clear(); + gr_flip(); + } + + // if the player has left the "player select" screen and quit the game without actually choosing + // a player, Player will be NULL, in which case we shouldn't write the player file out! + if (!(Game_mode & GM_STANDALONE_SERVER) && (Player!=NULL) && !Is_standalone){ + write_pilot_file(); + } + + // load up common multiplayer icons + multi_unload_common_icons(); + + shockwave_close(); // release any memory used by shockwave system + fireball_close(); // free fireball system + ship_close(); // free any memory that was allocated for the ships + hud_free_scrollback_list();// free space allocated to store hud messages in hud scrollback + unload_animating_pointer();// frees the frames used for the animating mouse pointer + bm_unload_all(); // free bitmaps + mission_campaign_close(); // close out the campaign stuff + multi_voice_close(); // close down multiplayer voice (including freeing buffers, etc) + multi_log_close(); +#ifdef MULTI_USE_LAG + multi_lag_close(); +#endif + + // the menu close functions will unload the bitmaps if they were displayed during the game +#if !defined(PRESS_TOUR_BUILD) && !defined(PD_BUILD) + main_hall_close(); +#endif + training_menu_close(); + gr_close(); + + extern void joy_close(); + joy_close(); + + audiostream_close(); + snd_close(); + event_music_close(); + psnet_close(); + os_cleanup(); + + // HACKITY HACK HACK + // if this flag is set, we should be firing up the launcher when exiting freespace + extern int Multi_update_fireup_launcher_on_exit; + if(Multi_update_fireup_launcher_on_exit){ + game_launch_launcher_on_exit(); + } +} + +// game_stop_looped_sounds() +// +// This function will call the appropriate stop looped sound functions for those +// modules which use looping sounds. It is not enough just to stop a looping sound +// at the DirectSound level, the game is keeping track of looping sounds, and this +// function is used to inform the game that looping sounds are being halted. +// +void game_stop_looped_sounds() +{ + hud_stop_looped_locking_sounds(); + hud_stop_looped_engine_sounds(); + afterburner_stop_sounds(); + player_stop_looped_sounds(); + obj_snd_stop_all(); // stop all object-linked persistant sounds + game_stop_subspace_ambient_sound(); + snd_stop(Radar_static_looping); + Radar_static_looping = -1; + snd_stop(Target_static_looping); + shipfx_stop_engine_wash_sound(); + Target_static_looping = -1; +} + +////////////////////////////////////////////////////////////////////////// +// +// Code for supporting an animating mouse pointer +// +// +////////////////////////////////////////////////////////////////////////// + +typedef struct animating_obj +{ + int first_frame; + int num_frames; + int current_frame; + float time; + float elapsed_time; +} animating_obj; + +static animating_obj Animating_mouse; + +// ---------------------------------------------------------------------------- +// init_animating_pointer() +// +// Called by load_animating_pointer() to ensure the Animating_mouse struct +// gets properly initialized +// +void init_animating_pointer() +{ + Animating_mouse.first_frame = -1; + Animating_mouse.num_frames = 0; + Animating_mouse.current_frame = -1; + Animating_mouse.time = 0.0f; + Animating_mouse.elapsed_time = 0.0f; +} + +// ---------------------------------------------------------------------------- +// load_animating_pointer() +// +// Called at game init to load in the frames for the animating mouse pointer +// +// input: filename => filename of animation file that holds the animation +// +void load_animating_pointer(char *filename, int dx, int dy) +{ + int fps; + animating_obj *am; + + init_animating_pointer(); + + am = &Animating_mouse; + am->first_frame = bm_load_animation(filename, &am->num_frames, &fps); + if ( am->first_frame == -1 ) + Error(LOCATION, "Could not load animation %s for the mouse pointer\n", filename); + am->current_frame = 0; + am->time = am->num_frames / i2fl(fps); +} + +// ---------------------------------------------------------------------------- +// unload_animating_pointer() +// +// Called at game shutdown to free the memory used to store the animation frames +// +void unload_animating_pointer() +{ + int i; + animating_obj *am; + + am = &Animating_mouse; + for ( i = 0; i < am->num_frames; i++ ) { + Assert( (am->first_frame+i) >= 0 ); + bm_release(am->first_frame + i); + } + + am->first_frame = -1; + am->num_frames = 0; + am->current_frame = -1; +} + +// draw the correct frame of the game mouse... called from game_maybe_draw_mouse() +void game_render_mouse(float frametime) +{ + int mx, my; + animating_obj *am; + + // if animating cursor exists, play the next frame + am = &Animating_mouse; + if ( am->first_frame != -1 ) { + mouse_get_pos(&mx, &my); + am->elapsed_time += frametime; + am->current_frame = fl2i( ( am->elapsed_time / am->time ) * (am->num_frames-1) ); + if ( am->current_frame >= am->num_frames ) { + am->current_frame = 0; + am->elapsed_time = 0.0f; + } + gr_set_cursor_bitmap(am->first_frame + am->current_frame); + } +} + +// ---------------------------------------------------------------------------- +// game_maybe_draw_mouse() +// +// determines whether to draw the mouse pointer at all, and what frame of +// animation to use if the mouse is animating +// +// Sets mouse.cpp globals Mouse_hidden and Mouse_moved based on the state of the game. +// +// input: frametime => elapsed frame time in seconds since last call +// +void game_maybe_draw_mouse(float frametime) +{ + int game_state; + + game_state = gameseq_get_state(); + + switch ( game_state ) { + case GS_STATE_GAME_PAUSED: + // case GS_STATE_MULTI_PAUSED: + case GS_STATE_GAME_PLAY: + case GS_STATE_DEATH_DIED: + case GS_STATE_DEATH_BLEW_UP: + if ( popup_active() || popupdead_is_active() ) { + Mouse_hidden = 0; + } else { + Mouse_hidden = 1; + } + break; + + default: + Mouse_hidden = 0; + break; + } // end switch + + if ( !Mouse_hidden ) + game_render_mouse(frametime); + +} + +void game_do_training_checks() +{ + int i, s; + float d; + waypoint_list *wplp; + + if (Training_context & TRAINING_CONTEXT_SPEED) { + s = (int) Player_obj->phys_info.fspeed; + if ((s >= Training_context_speed_min) && (s <= Training_context_speed_max)) { + if (!Training_context_speed_set) { + Training_context_speed_set = 1; + Training_context_speed_timestamp = timestamp(); + } + + } else + Training_context_speed_set = 0; + } + + if (Training_context & TRAINING_CONTEXT_FLY_PATH) { + wplp = &Waypoint_lists[Training_context_path]; + if (wplp->count > Training_context_goal_waypoint) { + i = Training_context_goal_waypoint; + do { + d = vm_vec_dist(&wplp->waypoints[i], &Player_obj->pos); + if (d <= Training_context_distance) { + Training_context_at_waypoint = i; + if (Training_context_goal_waypoint == i) { + Training_context_goal_waypoint++; + snd_play(&Snds[SND_CARGO_REVEAL], 0.0f); + } + + break; + } + + i++; + if (i == wplp->count) + i = 0; + + } while (i != Training_context_goal_waypoint); + } + } + + if ((Players_target == UNINITIALIZED) || (Player_ai->target_objnum != Players_target) || (Player_ai->targeted_subsys != Players_targeted_subsys)) { + Players_target = Player_ai->target_objnum; + Players_targeted_subsys = Player_ai->targeted_subsys; + Players_target_timestamp = timestamp(); + } +} + +/////////// Following is for event debug view screen + +#ifndef NDEBUG + +#define EVENT_DEBUG_MAX 5000 +#define EVENT_DEBUG_EVENT 0x8000 + +int Event_debug_index[EVENT_DEBUG_MAX]; +int ED_count; + +void game_add_event_debug_index(int n, int indent) +{ + if (ED_count < EVENT_DEBUG_MAX) + Event_debug_index[ED_count++] = n | (indent << 16); +} + +void game_add_event_debug_sexp(int n, int indent) +{ + if (n < 0) + return; + + if (Sexp_nodes[n].first >= 0) { + game_add_event_debug_sexp(Sexp_nodes[n].first, indent); + game_add_event_debug_sexp(Sexp_nodes[n].rest, indent); + return; + } + + game_add_event_debug_index(n, indent); + if (Sexp_nodes[n].subtype == SEXP_ATOM_OPERATOR) + game_add_event_debug_sexp(Sexp_nodes[n].rest, indent + 1); + else + game_add_event_debug_sexp(Sexp_nodes[n].rest, indent); +} + +void game_event_debug_init() +{ + int e; + + ED_count = 0; + for (e=0; e y_max) + break; + + z = Event_debug_index[k]; + if (z & EVENT_DEBUG_EVENT) { + z &= 0x7fff; + sprintf(buf, NOX("%s%s (%s) %s%d %d"), (Mission_events[z].flags & MEF_CURRENT) ? NOX("* ") : "", + Mission_events[z].name, Mission_events[z].result ? NOX("True") : NOX("False"), + (Mission_events[z].chain_delay < 0) ? "" : NOX("x "), + Mission_events[z].repeat_count, Mission_events[z].interval); + + } else { + i = (z >> 16) * 3; + buf[i] = 0; + while (i--) + buf[i] = ' '; + + strcat(buf, Sexp_nodes[z & 0x7fff].text); + switch (Sexp_nodes[z & 0x7fff].value) { + case SEXP_TRUE: + strcat(buf, NOX(" (True)")); + break; + + case SEXP_FALSE: + strcat(buf, NOX(" (False)")); + break; + + case SEXP_KNOWN_TRUE: + strcat(buf, NOX(" (Always true)")); + break; + + case SEXP_KNOWN_FALSE: + strcat(buf, NOX(" (Always false)")); + break; + + case SEXP_CANT_EVAL: + strcat(buf, NOX(" (Can't eval)")); + break; + + case SEXP_NAN: + case SEXP_NAN_FOREVER: + strcat(buf, NOX(" (Not a number)")); + break; + } + } + + gr_printf(10, y_index, buf); + y_index += font_height; + k++; + } + + gr_flip(); +} + +#endif // NDEBUG + +#ifndef NDEBUG +FILE * Time_fp; +FILE * Texture_fp; + +extern int Tmap_npixels; + +int Tmap_num_too_big = 0; +int Num_models_needing_splitting = 0; + +void Time_model( int modelnum ) +{ +// mprintf(( "Timing ship '%s'\n", si->name )); + + vector eye_pos, model_pos; + matrix eye_orient, model_orient; + + polymodel *pm = model_get( modelnum ); + + int l = strlen(pm->filename); + while( (l>0) ) { + if ( (l == '/') || (l=='\\') || (l==':')) { + l++; + break; + } + l--; + } + char *pof_file = &pm->filename[l]; + + int model_needs_splitting = 0; + + //fprintf( Texture_fp, "Model: %s\n", pof_file ); + int i; + for (i=0; in_textures; i++ ) { + char filename[1024]; + ubyte pal[768]; + + int bmp_num = pm->original_textures[i]; + if ( bmp_num > -1 ) { + bm_get_palette(pm->original_textures[i], pal, filename ); + int w,h; + bm_get_info( pm->original_textures[i],&w, &h ); + + + if ( (w > 512) || (h > 512) ) { + fprintf( Texture_fp, "%s\t%s\t%d\t%d\n", pof_file, filename, w, h ); + Tmap_num_too_big++; + model_needs_splitting++; + } + } else { + //fprintf( Texture_fp, "\tTexture %d is bogus\n", i ); + } + } + + if ( model_needs_splitting ) { + Num_models_needing_splitting++; + } + eye_orient = model_orient = vmd_identity_matrix; + eye_pos = model_pos = vmd_zero_vector; + + eye_pos.z = -pm->rad*2.0f; + + vector eye_to_model; + + vm_vec_sub( &eye_to_model, &model_pos, &eye_pos ); + vm_vector_2_matrix( &eye_orient, &eye_to_model, NULL, NULL ); + + fix t1 = timer_get_fixed_seconds(); + + angles ta; + ta.p = ta.b = ta.h = 0.0f; + int framecount = 0; + + Tmap_npixels = 0; + + int bitmaps_used_this_frame, bitmaps_new_this_frame; + + bm_get_frame_usage(&bitmaps_used_this_frame,&bitmaps_new_this_frame); + + modelstats_num_polys = modelstats_num_verts = 0; + + while( ta.h < PI2 ) { + + matrix m1; + vm_angles_2_matrix(&m1, &ta ); + vm_matrix_x_matrix( &model_orient, &vmd_identity_matrix, &m1 ); + + gr_reset_clip(); +// gr_clear(); + + g3_start_frame(1); + + g3_set_view_matrix( &eye_pos, &eye_orient, Viewer_zoom ); + + model_clear_instance( modelnum ); + model_set_detail_level(0); // use highest detail level + model_render( modelnum, &model_orient, &model_pos, MR_LOCK_DETAIL); //|MR_NO_POLYS ); + + g3_end_frame(); +// gr_flip(); + + framecount++; + ta.h += 0.1f; + + int k = key_inkey(); + if ( k == KEY_ESC ) { + exit(1); + } + } + + fix t2 = timer_get_fixed_seconds(); + + bm_get_frame_usage(&bitmaps_used_this_frame,&bitmaps_new_this_frame); + //bitmaps_used_this_frame /= framecount; + + modelstats_num_polys /= framecount; + modelstats_num_verts /= framecount; + + Tmap_npixels /=framecount; + + + mprintf(( "'%s' is %.2f FPS\n", pof_file, i2fl(framecount)/f2fl(t2-t1) )); + fprintf( Time_fp, "\"%s\"\t%.0f\t%d\t%d\t%d\t%d\n", pof_file, i2fl(framecount)/f2fl(t2-t1), bitmaps_used_this_frame, modelstats_num_polys, modelstats_num_verts, Tmap_npixels ); +// fprintf( Time_fp, "%.0f\t%d\t%d\t%d\t%d\n", i2fl(framecount)/f2fl(t2-t1), bitmaps_used_this_frame, modelstats_num_polys, modelstats_num_verts, Tmap_npixels ); + + +// key_getch(); +} + +int Time_models = 0; +DCF_BOOL( time_models, Time_models ); + +void Do_model_timings_test() +{ + + + if ( !Time_models ) return; + + mprintf(( "Timing models!\n" )); + + int i; + + ubyte model_used[MAX_POLYGON_MODELS]; + int model_id[MAX_POLYGON_MODELS]; + for (i=0; i 0){ + mtime -= (3600.0f * (float)hours); + } + seconds = (int)mtime%60; + minutes = (int)mtime/60; + + // print the hour if necessary + if(hours > 0){ + sprintf(time_str,XSTR( "%d:", 201),hours); + // if there are less than 10 minutes, print a leading 0 + if(minutes < 10){ + strcpy(tmp,NOX("0")); + strcat(time_str,tmp); + } + } + + // print the minutes + if(hours){ + sprintf(tmp,XSTR( "%d:", 201),minutes); + strcat(time_str,tmp); + } else { + sprintf(time_str,XSTR( "%d:", 201),minutes); + } + + // print the seconds + if(seconds < 10){ + strcpy(tmp,NOX("0")); + strcat(time_str,tmp); + } + sprintf(tmp,"%d",seconds); + strcat(time_str,tmp); +} + +// Stuff version string in *str. +void get_version_string(char *str) +{ +//XSTR:OFF +if ( FS_VERSION_BUILD == 0 ) { + sprintf(str,"v%d.%02d",FS_VERSION_MAJOR, FS_VERSION_MINOR); +} else { + sprintf(str,"v%d.%02d.%02d",FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD ); +} + +#if defined (FS2_DEMO) + strcat(str, " D"); +#elif defined (OEM_BUILD) + strcat(str, " (OEM)"); +#endif +//XSTR:ON + /* + HMODULE hMod; + DWORD bogus_handle; + char myname[_MAX_PATH]; + int namelen, major, minor, build, waste; + unsigned int buf_size; + DWORD version_size; + char *infop; + VOID *bufp; + BOOL result; + + // Find my EXE file name + hMod = GetModuleHandle(NULL); + namelen = GetModuleFileName( hMod, myname, _MAX_PATH ); + + version_size = GetFileVersionInfoSize(myname, &bogus_handle ); + infop = (char *)malloc(version_size); + result = GetFileVersionInfo( myname, 0, version_size, (LPVOID)infop ); + + // get the product version + result = VerQueryValue((LPVOID)infop, TEXT("\\StringFileInfo\\040904b0\\ProductVersion"), &bufp, &buf_size ); + sscanf( (char *)bufp, "%d, %d, %d, %d", &major, &minor, &build, &waste ); +#ifdef DEMO + sprintf(str,"Dv%d.%02d",major, minor); +#else + sprintf(str,"v%d.%02d",major, minor); +#endif + */ +} + +void get_version_string_short(char *str) +{ + sprintf(str,"v%d.%02d",FS_VERSION_MAJOR, FS_VERSION_MINOR); +} + +// ---------------------------------------------------------------- +// +// OEM UPSELL SCREENS BEGIN +// +// ---------------------------------------------------------------- +#if defined(OEM_BUILD) + +#define NUM_OEM_UPSELL_SCREENS 3 +#define OEM_UPSELL_SCREEN_DELAY 10000 + +static int Oem_upsell_bitmaps_loaded = 0; +static int Oem_upsell_bitmaps[GR_NUM_RESOLUTIONS][NUM_OEM_UPSELL_SCREENS]; +static int Oem_upsell_screen_number = 0; +static int Oem_upsell_show_next_bitmap_time; + +//XSTR:OFF +static char *Oem_upsell_bitmap_filenames[GR_NUM_RESOLUTIONS][NUM_OEM_UPSELL_SCREENS] = +{ + { "OEMUpSell02", + "OEMUpSell01", + "OEMUpSell03", + }, + { "2_OEMUpSell02", + "2_OEMUpSell01", + "2_OEMUpSell03", + }, +}; +//XSTR:ON + +static int Oem_normal_cursor = -1; +static int Oem_web_cursor = -1; +//#define OEM_UPSELL_URL "http://www.interplay-store.com/" +#define OEM_UPSELL_URL "http://www.interplay.com/cgi-bin/oemlinks.pl/pid=483421&cid=18384" + +void oem_upsell_next_screen() +{ + Oem_upsell_screen_number++; + if ( Oem_upsell_screen_number == (NUM_OEM_UPSELL_SCREENS-1) ) { + // extra long delay, mouse shown on last upsell + Oem_upsell_show_next_bitmap_time = timer_get_milliseconds() + OEM_UPSELL_SCREEN_DELAY*2; + Mouse_hidden = 0; + + } else { + Oem_upsell_show_next_bitmap_time = timer_get_milliseconds() + OEM_UPSELL_SCREEN_DELAY; + } +} + +void oem_upsell_load_bitmaps() +{ + int i; + + for ( i = 0; i < NUM_OEM_UPSELL_SCREENS; i++ ) { + Oem_upsell_bitmaps[gr_screen.res][i] = bm_load(Oem_upsell_bitmap_filenames[gr_screen.res][i]); + } +} + +void oem_upsell_unload_bitmaps() +{ + int i; + + for ( i = 0; i < NUM_OEM_UPSELL_SCREENS; i++ ) { + if(Oem_upsell_bitmaps[gr_screen.res][i] >= 0){ + bm_unload(Oem_upsell_bitmaps[gr_screen.res][i]); + } + } + + // unloaded + Oem_upsell_bitmaps_loaded = 0; +} + +// clickable hotspot on 3rd OEM upsell screen +static int Oem_upsell3_button_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 28, 350, 287, 96 // x, y, w, h + }, + { // GR_1024 + 45, 561, 460, 152 // x, y, w, h + } +}; + +void oem_upsell_show_screens() +{ + int current_time, k; + int done = 0; + + if ( !Oem_upsell_bitmaps_loaded ) { + oem_upsell_load_bitmaps(); + Oem_upsell_bitmaps_loaded = 1; + } + + // may use upsell screens more than once + Oem_upsell_show_next_bitmap_time = timer_get_milliseconds() + OEM_UPSELL_SCREEN_DELAY; + Oem_upsell_screen_number = 0; + + key_flush(); + Mouse_hidden = 1; + + // set up cursors + int nframes; // used to pass, not really needed (should be 1) + Oem_normal_cursor = gr_get_cursor_bitmap(); + Oem_web_cursor = bm_load_animation("cursorweb", &nframes); + Assert(Oem_web_cursor >= 0); + if (Oem_web_cursor < 0) { + Oem_web_cursor = Oem_normal_cursor; + } + + while(!done) { + + //oem_reset_trailer_timer(); + + current_time = timer_get_milliseconds(); + + os_poll(); + k = key_inkey(); + + // advance screen on keypress or timeout + if (( k > 0 ) || (mouse_up_count(MOUSE_LEFT_BUTTON) > 0) || (current_time > Oem_upsell_show_next_bitmap_time)) { + oem_upsell_next_screen(); + } + + // check if we are done + if ( Oem_upsell_screen_number >= NUM_OEM_UPSELL_SCREENS ) { + Oem_upsell_screen_number--; + done = 1; + } else { + if ( Oem_upsell_bitmaps[gr_screen.res][Oem_upsell_screen_number] < 0 ) { + done = 1; + } + } + + // show me the upsell + if ( Oem_upsell_bitmaps[gr_screen.res][Oem_upsell_screen_number] >= 0 ) { + gr_set_bitmap(Oem_upsell_bitmaps[gr_screen.res][Oem_upsell_screen_number]); + gr_bitmap(0,0); + } + + // if this is the 3rd upsell, make it clickable, d00d + if ( Oem_upsell_screen_number == NUM_OEM_UPSELL_SCREENS-1 ) { + int mx, my; + int button_state = mouse_get_pos(&mx, &my); + if ( (mx >= Oem_upsell3_button_coords[gr_screen.res][0]) && (mx <= Oem_upsell3_button_coords[gr_screen.res][0] + Oem_upsell3_button_coords[gr_screen.res][2]) + && (my >= Oem_upsell3_button_coords[gr_screen.res][1]) && (my <= Oem_upsell3_button_coords[gr_screen.res][1] + Oem_upsell3_button_coords[gr_screen.res][3]) ) + { + // switch cursors + gr_set_cursor_bitmap(Oem_web_cursor); //, GR_CURSOR_LOCK); + + // check for clicks + if (button_state & MOUSE_LEFT_BUTTON) { + // launch URL + multi_pxo_url(OEM_UPSELL_URL); + done = 1; + } + } else { + // switch cursor back to normal one + gr_set_cursor_bitmap(Oem_normal_cursor); //, GR_CURSOR_UNLOCK); + } + } + + if ( done ) { + if (gameseq_get_state() != GS_STATE_END_DEMO) { + gr_fade_out(0); + Sleep(300); + } + } + + gr_flip(); + } + + // unload bitmap + oem_upsell_unload_bitmaps(); + + // switch cursor back to normal one + gr_set_cursor_bitmap(Oem_normal_cursor); //, GR_CURSOR_UNLOCK); + +} + +#endif // defined(OEM_BUILD) +// ---------------------------------------------------------------- +// +// OEM UPSELL SCREENS END +// +// ---------------------------------------------------------------- + + + +// ---------------------------------------------------------------- +// +// DEMO UPSELL SCREENS BEGIN +// +// ---------------------------------------------------------------- + +#ifdef FS2_DEMO + +//#define NUM_DEMO_UPSELL_SCREENS 4 + +#define NUM_DEMO_UPSELL_SCREENS 2 +#define DEMO_UPSELL_SCREEN_DELAY 3000 + +static int Demo_upsell_bitmaps_loaded = 0; +static int Demo_upsell_bitmaps[GR_NUM_RESOLUTIONS][NUM_DEMO_UPSELL_SCREENS]; +static int Demo_upsell_screen_number = 0; +static int Demo_upsell_show_next_bitmap_time; + +//XSTR:OFF +static char *Demo_upsell_bitmap_filenames[GR_NUM_RESOLUTIONS][NUM_DEMO_UPSELL_SCREENS] = +{ + { "UpSell02", + "UpSell01", + }, + { "2_UpSell02", + "2_UpSell01", + }, + // "DemoUpsell3", + // "DemoUpsell4", +}; +//XSTR:ON + +void demo_upsell_next_screen() +{ + Demo_upsell_screen_number++; + if ( Demo_upsell_screen_number == (NUM_DEMO_UPSELL_SCREENS-1) ) { + Demo_upsell_show_next_bitmap_time = timer_get_milliseconds() + DEMO_UPSELL_SCREEN_DELAY*4; + } else { + Demo_upsell_show_next_bitmap_time = timer_get_milliseconds() + DEMO_UPSELL_SCREEN_DELAY; + } + + /* + if ( Demo_upsell_screen_number < NUM_DEMO_UPSELL_SCREENS ) { + if ( Demo_upsell_bitmap_filenames[gr_screen.res][Demo_upsell_screen_number] >= 0 ) { +#ifndef HARDWARE_ONLY + palette_use_bm_palette(Demo_upsell_bitmaps[gr_screen.res][Demo_upsell_screen_number]); +#endif + } + } + */ +} + +void demo_upsell_load_bitmaps() +{ + int i; + + for ( i = 0; i < NUM_DEMO_UPSELL_SCREENS; i++ ) { + Demo_upsell_bitmaps[gr_screen.res][i] = bm_load(Demo_upsell_bitmap_filenames[gr_screen.res][i]); + } +} + +void demo_upsell_unload_bitmaps() +{ + int i; + + for ( i = 0; i < NUM_DEMO_UPSELL_SCREENS; i++ ) { + if(Demo_upsell_bitmaps[gr_screen.res][i] >= 0){ + bm_unload(Demo_upsell_bitmaps[gr_screen.res][i]); + } + } + + // unloaded + Demo_upsell_bitmaps_loaded = 0; +} + +void demo_upsell_show_screens() +{ + int current_time, k; + int done = 0; + + if ( !Demo_upsell_bitmaps_loaded ) { + demo_upsell_load_bitmaps(); + Demo_upsell_bitmaps_loaded = 1; + } + + // may use upsell screens more than once + Demo_upsell_show_next_bitmap_time = timer_get_milliseconds() + DEMO_UPSELL_SCREEN_DELAY; + Demo_upsell_screen_number = 0; + + key_flush(); + Mouse_hidden = 1; + + while(!done) { + + demo_reset_trailer_timer(); + + current_time = timer_get_milliseconds(); + +// #ifndef THREADED + os_poll(); +// #endif + k = key_inkey(); + + // don't time out, wait for keypress + /* + if ( current_time > Demo_upsell_show_next_bitmap_time ) { + demo_upsell_next_screen(); + k = 0; + }*/ + + if ( k > 0 ) { + demo_upsell_next_screen(); + } + + if ( Demo_upsell_screen_number >= NUM_DEMO_UPSELL_SCREENS ) { + Demo_upsell_screen_number--; + done = 1; + } else { + if ( Demo_upsell_bitmaps[gr_screen.res][Demo_upsell_screen_number] < 0 ) { + done = 1; + } + } + + if ( Demo_upsell_bitmaps[gr_screen.res][Demo_upsell_screen_number] >= 0 ) { + gr_set_bitmap(Demo_upsell_bitmaps[gr_screen.res][Demo_upsell_screen_number]); + gr_bitmap(0,0); + } + + if ( done ) { + if (gameseq_get_state() != GS_STATE_END_DEMO) { + gr_fade_out(0); + Sleep(300); + } + } + + gr_flip(); + } + + // unload bitmap + demo_upsell_unload_bitmaps(); +} + +#endif // DEMO + +// ---------------------------------------------------------------- +// +// DEMO UPSELL SCREENS END +// +// ---------------------------------------------------------------- + + +// ---------------------------------------------------------------- +// +// Subspace Ambient Sound START +// +// ---------------------------------------------------------------- + +static int Subspace_ambient_left_channel = -1; +static int Subspace_ambient_right_channel = -1; + +// +void game_start_subspace_ambient_sound() +{ + if ( Subspace_ambient_left_channel < 0 ) { + Subspace_ambient_left_channel = snd_play_looping(&Snds[SND_SUBSPACE_LEFT_CHANNEL], -1.0f); + } + + if ( Subspace_ambient_right_channel < 0 ) { + Subspace_ambient_right_channel = snd_play_looping(&Snds[SND_SUBSPACE_RIGHT_CHANNEL], 1.0f); + } +} + +void game_stop_subspace_ambient_sound() +{ + if ( Subspace_ambient_left_channel >= 0 ) { + snd_stop(Subspace_ambient_left_channel); + Subspace_ambient_left_channel = -1; + } + + if ( Subspace_ambient_right_channel >= 0 ) { + snd_stop(Subspace_ambient_right_channel); + Subspace_ambient_right_channel = -1; + } +} + +// ---------------------------------------------------------------- +// +// Subspace Ambient Sound END +// +// ---------------------------------------------------------------- + +// ---------------------------------------------------------------- +// +// CDROM detection code START +// +// ---------------------------------------------------------------- + +#define CD_SIZE_72_MINUTE_MAX (697000000) + +uint game_get_cd_used_space(char *path) +{ + uint total = 0; + char use_path[512] = ""; + char sub_path[512] = ""; + WIN32_FIND_DATA find; + HANDLE find_handle; + + // recurse through all files and directories + strcpy(use_path, path); + strcat(use_path, "*.*"); + find_handle = FindFirstFile(use_path, &find); + + // bogus + if(find_handle == INVALID_HANDLE_VALUE){ + return 0; + } + + // whee + do { + // subdirectory. make sure to ignore . and .. + if((find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && stricmp(find.cFileName, ".") && stricmp(find.cFileName, "..")){ + // subsearch + strcpy(sub_path, path); + strcat(sub_path, find.cFileName); + strcat(sub_path, "\\"); + total += game_get_cd_used_space(sub_path); + } else { + total += (uint)find.nFileSizeLow; + } + } while(FindNextFile(find_handle, &find)); + + // close + FindClose(find_handle); + + // total + return total; +} + + +// if volume_name is non-null, the CD name must match that +int find_freespace_cd(char *volume_name) +{ + char oldpath[MAX_PATH]; + char volume[256]; + int i; + int cdrom_drive=-1; + int volume_match = 0; + _finddata_t find; + int find_handle; + + GetCurrentDirectory(MAX_PATH, oldpath); + + for (i = 0; i < 26; i++) + { +//XSTR:OFF + char path[]="d:\\"; +//XSTR:ON + + path[0] = (char)('A'+i); + if (GetDriveType(path) == DRIVE_CDROM) { + cdrom_drive = -3; + if ( GetVolumeInformation(path, volume, 256, NULL, NULL, NULL, NULL, 0) == TRUE ) { + nprintf(("CD", "CD volume: %s\n", volume)); + + // check for any CD volume + int volume1_present = 0; + int volume2_present = 0; + int volume3_present = 0; + + char full_check[512] = ""; + + // look for setup.exe + strcpy(full_check, path); + strcat(full_check, "setup.exe"); + find_handle = _findfirst(full_check, &find); + if(find_handle != -1){ + volume1_present = 1; + _findclose(find_handle); + } + + // look for intro.mve + strcpy(full_check, path); + strcat(full_check, "intro.mve"); + find_handle = _findfirst(full_check, &find); + if(find_handle != -1){ + volume2_present = 1; + _findclose(find_handle); + } + + // look for endpart1.mve + strcpy(full_check, path); + strcat(full_check, "endpart1.mve"); + find_handle = _findfirst(full_check, &find); + if(find_handle != -1){ + volume3_present = 1; + _findclose(find_handle); + } + + // see if we have the specific CD we're looking for + if ( volume_name ) { + // volume 1 + if ( !stricmp(volume_name, FS_CDROM_VOLUME_1) && volume1_present) { + volume_match = 1; + } + // volume 2 + if ( !stricmp(volume_name, FS_CDROM_VOLUME_2) && volume2_present) { + volume_match = 1; + } + // volume 3 + if ( !stricmp(volume_name, FS_CDROM_VOLUME_3) && volume3_present) { + volume_match = 1; + } + } else { + if ( volume1_present || volume2_present || volume3_present ) { + volume_match = 1; + } + } + + // here's where we make sure that CD's 2 and 3 are not just ripped - check to make sure its capacity is > 697,000,000 bytes + if ( volume_match ){ +#ifdef RELEASE_REAL + // we don't care about CD1 though. let it be whatever size it wants, since the game will demand CD's 2 and 3 at the proper time + if(volume2_present || volume3_present) { + // first step - check to make sure its a cdrom + if(GetDriveType(path) != DRIVE_CDROM){ + break; + } + +#if !defined(OEM_BUILD) + // oem not on 80 min cds, so dont check tha size + // check its size + uint used_space = game_get_cd_used_space(path); + if(used_space < CD_SIZE_72_MINUTE_MAX){ + break; + } +#endif // !defined(OEM_BUILD) + } + + cdrom_drive = i; + break; +#else + cdrom_drive = i; + break; +#endif // RELEASE_REAL + } + } + } + } + + SetCurrentDirectory(oldpath); + return cdrom_drive; +} + +int set_cdrom_path(int drive_num) +{ + int rval; + + if (drive_num < 0) { //no CD +// #ifndef NDEBUG +// strcpy(CDROM_dir,"j:\\FreeSpaceCD\\"); //set directory +// rval = 1; +// #else + strcpy(Game_CDROM_dir,""); //set directory + rval = 0; +// #endif + } else { + sprintf(Game_CDROM_dir,NOX("%c:\\"), 'a' + drive_num ); //set directory + rval = 1; + } + + return rval; +} + +int init_cdrom() +{ + int i, rval; + + //scan for CD, etc. + + rval = 1; + + #ifndef DEMO + i = find_freespace_cd(); + + rval = set_cdrom_path(i); + + /* + if ( rval ) { + nprintf(("CD", "Using %s for FreeSpace CD\n", CDROM_dir)); + } else { + nprintf(("CD", "FreeSpace CD not found\n")); + } + */ + #endif + + return rval; +} + +int Last_cd_label_found = 0; +char Last_cd_label[256]; + +int game_cd_changed() +{ + char label[256]; + int found; + int changed = 0; + + if ( strlen(Game_CDROM_dir) == 0 ) { + init_cdrom(); + } + + found = GetVolumeInformation(Game_CDROM_dir, label, 256, NULL, NULL, NULL, NULL, 0); + + if ( found != Last_cd_label_found ) { + Last_cd_label_found = found; + if ( found ) { + mprintf(( "CD '%s' was inserted\n", label )); + changed = 1; + } else { + mprintf(( "CD '%s' was removed\n", Last_cd_label )); + changed = 1; + } + } else { + if ( Last_cd_label_found ) { + if ( !stricmp( Last_cd_label, label )) { + //mprintf(( "CD didn't change\n" )); + } else { + mprintf(( "CD was changed from '%s' to '%s'\n", Last_cd_label, label )); + changed = 1; + } + } else { + // none found before, none found now. + //mprintf(( "still no CD...\n" )); + } + } + + Last_cd_label_found = found; + if ( found ) { + strcpy( Last_cd_label, label ); + } else { + strcpy( Last_cd_label, "" ); + } + + return changed; +} + +// check if _any_ FreeSpace2 CDs are in the drive +// return: 1 => CD now in drive +// 0 => Could not find CD, they refuse to put it in the drive +int game_do_cd_check(char *volume_name) +{ +#if !defined(GAME_CD_CHECK) + return 1; +#else + int cd_present = 0; + int cd_drive_num; + + int num_attempts = 0; + int refresh_files = 0; + while(1) { + int path_set_ok, popup_rval; + + cd_drive_num = find_freespace_cd(volume_name); + path_set_ok = set_cdrom_path(cd_drive_num); + if ( path_set_ok ) { + cd_present = 1; + if ( refresh_files ) { + cfile_refresh(); + refresh_files = 0; + } + break; + } + + // standalone mode + if(Is_standalone){ + cd_present = 0; + break; + } else { + // no CD found, so prompt user + popup_rval = popup(PF_BODY_BIG, 1, POPUP_OK, XSTR( "FreeSpace 2 CD not found\n\nInsert a FreeSpace 2 CD to continue", 202)); + refresh_files = 1; + if ( popup_rval != 1 ) { + cd_present = 0; + break; + } + + if ( num_attempts++ > 5 ) { + cd_present = 0; + break; + } + } + } + + return cd_present; +#endif +} + +// check if _any_ FreeSpace2 CDs are in the drive +// return: 1 => CD now in drive +// 0 => Could not find CD, they refuse to put it in the drive +int game_do_cd_check_specific(char *volume_name, int cdnum) +{ + int cd_present = 0; + int cd_drive_num; + + int num_attempts = 0; + int refresh_files = 0; + while(1) { + int path_set_ok, popup_rval; + + cd_drive_num = find_freespace_cd(volume_name); + path_set_ok = set_cdrom_path(cd_drive_num); + if ( path_set_ok ) { + cd_present = 1; + if ( refresh_files ) { + cfile_refresh(); + refresh_files = 0; + } + break; + } + + if(Is_standalone){ + cd_present = 0; + break; + } else { + // no CD found, so prompt user +#if defined(DVD_MESSAGE_HACK) + popup_rval = popup(PF_BODY_BIG, 1, POPUP_OK, XSTR("Please insert DVD", 1468)); +#else + popup_rval = popup(PF_BODY_BIG, 1, POPUP_OK, XSTR("Please insert CD %d", 1468), cdnum); +#endif + refresh_files = 1; + if ( popup_rval != 1 ) { + cd_present = 0; + break; + } + + if ( num_attempts++ > 5 ) { + cd_present = 0; + break; + } + } + } + + return cd_present; +} + +// only need to do this in RELEASE_REAL +int game_do_cd_mission_check(char *filename) +{ +#ifdef RELEASE_REAL + int cd_num; + int cd_present = 0; + int cd_drive_num; + fs_builtin_mission *m = game_find_builtin_mission(filename); + + // check for changed CD + if(game_cd_changed()){ + cfile_refresh(); + } + + // multiplayer + if((Game_mode & GM_MULTIPLAYER) || Is_standalone){ + return 1; + } + + // not builtin, so do a general check (any FS2 CD will do) + if(m == NULL){ + return game_do_cd_check(); + } + + // does not have any CD requirement, do a general check + if(strlen(m->cd_volume) <= 0){ + return game_do_cd_check(); + } + + // get the volume + if(!stricmp(m->cd_volume, FS_CDROM_VOLUME_1)){ + cd_num = 1; + } else if(!stricmp(m->cd_volume, FS_CDROM_VOLUME_2)){ + cd_num = 2; + } else if(!stricmp(m->cd_volume, FS_CDROM_VOLUME_3)){ + cd_num = 3; + } else { + return game_do_cd_check(); + } + + // did we find the cd? + if(find_freespace_cd(m->cd_volume) >= 0){ + return 1; + } + + // make sure the volume exists + int num_attempts = 0; + int refresh_files = 0; + while(1){ + int path_set_ok, popup_rval; + + cd_drive_num = find_freespace_cd(m->cd_volume); + path_set_ok = set_cdrom_path(cd_drive_num); + if ( path_set_ok ) { + cd_present = 1; + if ( refresh_files ) { + cfile_refresh(); + refresh_files = 0; + } + break; + } + + // no CD found, so prompt user +#if defined(DVD_MESSAGE_HACK) + popup_rval = popup(PF_BODY_BIG, 1, POPUP_OK, XSTR("Please insert DVD", 1468)); +#else + popup_rval = popup(PF_BODY_BIG, 1, POPUP_OK, XSTR("Please insert CD %d", 1468), cd_num); +#endif + + refresh_files = 1; + if ( popup_rval != 1 ) { + cd_present = 0; + break; + } + + if ( num_attempts++ > 5 ) { + cd_present = 0; + break; + } + } + + return cd_present; +#else + return 1; +#endif +} + +// ---------------------------------------------------------------- +// +// CDROM detection code END +// +// ---------------------------------------------------------------- + +// ---------------------------------------------------------------- +// SHIPS TBL VERIFICATION STUFF +// + +// checksums, just keep a list of all valid ones, if it matches any of them, keep it +#define NUM_SHIPS_TBL_CHECKSUMS 1 +/* +int Game_ships_tbl_checksums[NUM_SHIPS_TBL_CHECKSUMS] = { + -463907578, // US - beta 1 + 1696074201, // FS2 demo +}; +*/ +int Game_ships_tbl_checksums[NUM_SHIPS_TBL_CHECKSUMS] = { +// -1022810006, // 1.0 FULL + -1254285366 // 1.2 FULL (German) +}; + +void verify_ships_tbl() +{ + /* +#ifdef NDEBUG + Game_ships_tbl_valid = 1; +#else + */ + uint file_checksum; + int idx; + + // detect if the packfile exists + CFILE *detect = cfopen("ships.tbl", "rb"); + Game_ships_tbl_valid = 0; + + // not mission-disk + if(!detect){ + Game_ships_tbl_valid = 0; + return; + } + + // get the long checksum of the file + file_checksum = 0; + cfseek(detect, 0, SEEK_SET); + cf_chksum_long(detect, &file_checksum); + cfclose(detect); + detect = NULL; + + // now compare the checksum/filesize against known #'s + for(idx=0; idx +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "FreespaceResource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include \0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +DEBUG_MENU DIALOG DISCARDABLE 0, 0, 185, 159 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Debug Menu" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_LIST_DEBUG,7,7,171,121,LBS_OWNERDRAWFIXED | + LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Ok",IDOK,7,137,171,14 +END + +FIND_DIALOG DIALOG DISCARDABLE 0, 0, 119, 47 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Find Text" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_TEXT,7,7,105,14,ES_AUTOHSCROLL + DEFPUSHBUTTON "Find",IDOK,7,26,50,14 + PUSHBUTTON "Cancel",IDCANCEL,62,26,50,14 +END + +IDD_CONNECT DIALOG DISCARDABLE 0, 0, 215, 253 +STYLE DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Server" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "# Connections :",IDC_CON_COUNT,7,54,148,10 + LISTBOX IDC_CONPING,7,79,148,167,WS_VSCROLL | WS_TABSTOP + LTEXT "Address and Ping",IDC_STATIC,7,66,143,10 + PUSHBUTTON "Reset All",IDC_RESET_MULTI,162,174,46,72 + PUSHBUTTON "Kick",IDC_KICK_BUTTON,161,80,47,19 + CONTROL "",IDC_STATIC,"Static",SS_GRAYRECT,7,48,201,1 + EDITTEXT IDC_STD_NAME,64,14,144,14,ES_AUTOHSCROLL + LTEXT "Server Name",IDC_STATIC,7,17,42,8 + EDITTEXT IDC_STD_HOST_PASSWD,64,32,144,14,ES_AUTOHSCROLL + LTEXT "Host Password",IDC_STATIC,7,33,48,8 + PUSHBUTTON "Refresh Missions",IDC_PXO_REFRESH,161,116,47,36, + BS_MULTILINE +END + +IDD_MULTI DIALOG DISCARDABLE 0, 0, 215, 253 +STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Multi-Player" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Frame Cap :",10,7,28,43,8 + LTEXT "",IDC_FRAMECAP_STATIC,53,29,46,10,SS_SUNKEN + LTEXT "Realized FPS :",IDC_STATIC_A,119,30,49,12 + LTEXT "",IDC_STANDALONE_FPS,172,29,34,11,SS_SUNKEN + LTEXT "Mission Time :",IDC_STATIC_A2,13,91,49,12 + LTEXT "",IDC_STANDALONE_MTIME,71,90,125,11,SS_SUNKEN + CONTROL "",IDC_STATIC,"Static",SS_WHITERECT | SS_SUNKEN,7,43,199, + 1 + LTEXT "Mission Name :",IDC_STATIC,13,80,52,11 + LTEXT "",IDC_MISSION_NAME,71,78,125,11,SS_SUNKEN + LTEXT "Mission Goals",IDC_STATIC,7,140,74,8 + LTEXT "Netgame Information",IDC_STATIC,133,140,66,8 + LTEXT "Max Players",IDC_STATIC,117,158,42,8 + LTEXT "Max Observers",IDC_STATIC,117,184,48,8 + LTEXT "Security",IDC_STATIC,117,210,26,8 + LTEXT "Respawns",IDC_STATIC,117,236,34,8 + LTEXT "",IDC_NG_MAXPLAYERS,178,158,28,8 + LTEXT "",IDC_NG_MAXOBSERVERS,178,184,28,8 + LTEXT "",IDC_NG_SECURITY,178,210,28,8 + LTEXT "",IDC_NG_RESPAWNS,178,236,28,8 +END + +IDD_PLAYER_DIALOG DIALOG DISCARDABLE 0, 0, 215, 253 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Player Info" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_PLAYER_LIST,77,6,112,169,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Ship Type",IDC_STATIC,7,24,38,10 + CONTROL "",IDC_STATIC,"Static",SS_WHITERECT | SS_NOTIFY | + SS_SUNKEN,104,24,1,54 + CONTROL "",IDC_STATIC,"Static",SS_WHITERECT | SS_NOTIFY | + SS_SUNKEN,7,77,201,1 + LTEXT "",IDC_PSHIP_TYPE,53,24,49,10,SS_SUNKEN + LTEXT "Mission Stats",IDC_STATIC,133,79,44,10 + LTEXT "Primary Shots",IDC_STATIC,7,89,45,9 + LTEXT "Primary Hits",IDC_STATIC,7,100,40,9 + LTEXT "Primary BH Hits",IDC_STATIC,7,112,50,9 + LTEXT "Primary Hit %",IDC_STATIC,7,123,43,9 + LTEXT "Primary BH Hit %",IDC_STATIC,7,134,55,9 + CONTROL "",IDC_STATIC,"Static",SS_WHITERECT | SS_NOTIFY | + SS_SUNKEN,104,78,1,168 + LTEXT "All Time Stats",IDC_STATIC,33,79,44,10 + LTEXT "Primary Shots",IDC_STATIC,107,88,45,9 + LTEXT "Primary Hits",IDC_STATIC,107,99,40,9 + LTEXT "Primary BH Hits",IDC_STATIC,107,111,50,9 + LTEXT "Primary Hit %",IDC_STATIC,107,122,43,9 + LTEXT "Primary BH Hit %",IDC_STATIC,107,133,55,9 + LTEXT "Secondary Shots",IDC_STATIC,7,145,56,9 + LTEXT "Secondary Hits",IDC_STATIC,7,156,49,9 + LTEXT "Secondary BH Hits",IDC_STATIC,7,167,63,9 + LTEXT "Secondary Hit %",IDC_STATIC,7,177,55,9 + LTEXT "Secondary BH Hit %",IDC_STATIC,7,189,65,9 + LTEXT "Secondary Shots",IDC_STATIC,107,144,56,9 + LTEXT "Secondary Hits",IDC_STATIC,107,155,49,9 + LTEXT "Secondary BH Hits",IDC_STATIC,107,166,63,9 + LTEXT "Secondary Hit %",IDC_STATIC,107,176,55,9 + LTEXT "Secondary BH Hit %",IDC_STATIC,107,187,65,9 + LTEXT "",IDC_PSHOTS,75,90,28,8,SS_SUNKEN + LTEXT "",IDC_PHITS,75,101,28,8,SS_SUNKEN + LTEXT "",IDC_PBHHITS,75,112,28,8,SS_SUNKEN + LTEXT "",IDC_PPCT,75,123,28,8,SS_SUNKEN + LTEXT "",IDC_PBHPCT,75,134,28,8,SS_SUNKEN + LTEXT "",IDC_SSHOTS,75,145,28,8,SS_SUNKEN + LTEXT "",IDC_SECHITS,75,156,28,8,SS_SUNKEN + LTEXT "",IDC_SBHHITS,75,166,28,9,SS_SUNKEN + LTEXT "",IDC_SPCT,75,178,28,8,SS_SUNKEN + LTEXT "",IDC_SBHPCT,75,190,28,8,SS_SUNKEN + LTEXT "",IDC_MPSHOTS,180,89,28,8,SS_SUNKEN + LTEXT "",IDC_MPHITS,180,100,28,8,SS_SUNKEN + LTEXT "",IDC_MPBHHITS,180,111,28,8,SS_SUNKEN + LTEXT "",IDC_MPPCT,180,122,28,8,SS_SUNKEN + LTEXT "",IDC_MPBHPCT,180,134,28,8,SS_SUNKEN + LTEXT "",IDC_MSSHOTS,180,145,28,8,SS_SUNKEN + LTEXT "",IDC_MSECHITS,180,156,28,8,SS_SUNKEN + LTEXT "",IDC_MSBHHITS,180,167,28,8,SS_SUNKEN + LTEXT "",IDC_MSPCT,180,178,28,8,SS_SUNKEN + LTEXT "",IDC_MSBHPCT,180,189,28,8,SS_SUNKEN + CONTROL "",IDC_PINFO_PING,"Static",SS_WHITERECT | SS_NOTIFY | + SS_SUNKEN,7,215,201,1 + LTEXT "Player",IDC_STATIC,44,6,20,8 + LTEXT "",IDC_PING_TIME,53,38,49,10,SS_SUNKEN + LTEXT "Avg Ping",IDC_STATIC,7,38,32,9 + LTEXT "Assists",IDC_STATIC,7,200,54,10 + LTEXT "Assists",IDC_STATIC,107,200,54,10 + LTEXT "",IDC_ASSISTS,75,201,28,9,SS_SUNKEN + LTEXT "",IDC_MASSISTS,180,200,28,9,SS_SUNKEN +END + +IDD_GODSTUFF DIALOG DISCARDABLE 0, 0, 215, 253 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "God Stuff" +FONT 8, "MS Sans Serif" +BEGIN + COMBOBOX IDC_PLAYER_GOD_LIST,39,7,101,211,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "",IDC_GODSTUFF_FPS,177,7,31,8,SS_SUNKEN + LTEXT "FPS",IDC_STATIC,154,7,16,8 + LTEXT "Player ",IDC_STATIC,7,7,20,12 + EDITTEXT IDC_GODSTUFF_BROADCAST,7,47,201,12,ES_MULTILINE + LTEXT "Server Message",IDC_STATIC,7,39,54,8 + PUSHBUTTON "Send",IDC_GODSTUFF_SENDMESS,7,59,70,13 + LISTBOX IDC_GOD_CHAT,7,84,201,162,LBS_NOINTEGRALHEIGHT | + LBS_NOSEL | WS_VSCROLL | WS_TABSTOP +END + +IDD_DEBUG_DIALOG DIALOG DISCARDABLE 0, 0, 215, 253 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Debug" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "Standalone State :",IDC_STATIC,7,7,61,11 + LTEXT "",IDC_STANDALONE_STATE,71,7,125,11,SS_SUNKEN +END + +IDD_GEN DIALOG DISCARDABLE 0, 0, 186, 95 +STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | WS_POPUP | WS_VISIBLE | + WS_CAPTION | WS_SYSMENU +CAPTION "Dialog" +FONT 8, "MS Sans Serif" +BEGIN + CTEXT "",IDC_FIELD1,27,11,136,8 + CTEXT "",IDC_FIELD2,28,30,130,8 + CTEXT "",IDC_FIELD3,27,49,138,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + "DEBUG_MENU", DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 178 + TOPMARGIN, 7 + BOTTOMMARGIN, 152 + END + + "FIND_DIALOG", DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 112 + TOPMARGIN, 7 + BOTTOMMARGIN, 40 + END + + IDD_CONNECT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 208 + TOPMARGIN, 7 + BOTTOMMARGIN, 246 + END + + IDD_MULTI, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 206 + TOPMARGIN, 7 + BOTTOMMARGIN, 246 + END + + IDD_PLAYER_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 208 + TOPMARGIN, 6 + BOTTOMMARGIN, 246 + END + + IDD_GODSTUFF, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 208 + TOPMARGIN, 7 + BOTTOMMARGIN, 246 + END + + IDD_DEBUG_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 208 + TOPMARGIN, 7 + BOTTOMMARGIN, 246 + END + + IDD_GEN, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 5 + BOTTOMMARGIN, 88 + END +END +#endif // APSTUDIO_INVOKED + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Volition Inc.\0" + VALUE "FileDescription", "FreeSpace\0" + VALUE "FileVersion", "1.03.01\0" + VALUE "InternalName", "FreeSpace\0" + VALUE "LegalCopyright", "Copyright © 1998\0" + VALUE "OriginalFilename", "fs.exe\0" + VALUE "ProductName", "FreeSpace\0" + VALUE "ProductVersion", "1.03.01\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_GOAL_INC BITMAP DISCARDABLE "goal_inc.bmp" +IDB_GOAL_COMP BITMAP DISCARDABLE "goal_com.bmp" +IDB_GOAL_ORD BITMAP DISCARDABLE "goal_ord.bmp" +IDB_GOAL_NONE BITMAP DISCARDABLE "goal_none.bmp" +IDB_GOAL_FAIL BITMAP DISCARDABLE "goal_fail.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON DISCARDABLE "app_icon.ico" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/freespace2/goal_com.bmp b/src/freespace2/goal_com.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b71da03e9cc2edf693124bd2bb4f2cdde546725d GIT binary patch literal 246 zcmZ?r{l)+RWk5;;hy|dSk%0v)(Eui~5kMJ`WJ3dl0+K)`!+`?_fHX+rKNvCm{|{mT vF%W`;K>YvzSFHjP|9`Clg7qLC2tc?%0kAL<4Uxu8qN|7TVdlc^f!YfITtRh7 literal 0 HcmV?d00001 diff --git a/src/freespace2/goal_fail.bmp b/src/freespace2/goal_fail.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c5e0722156fc3dfad43b8a37fd18776398dc584d GIT binary patch literal 246 zcmZ?r{l)+RWk5;;hy|dSk%0v)(Eui~5kMJ`WJ3dl0+K)`!+`?_fHX+rKNvCm{|{mT wF%W`;K>YvzXU+r?|9{Q|g83jG2tc?%fuCS5f`)K$lj!PUe3-d#d!Y6L0I^qYr2qf` literal 0 HcmV?d00001 diff --git a/src/freespace2/goal_inc.bmp b/src/freespace2/goal_inc.bmp new file mode 100644 index 0000000000000000000000000000000000000000..60490d1f6037613bba3b09607e2e3975832e7859 GIT binary patch literal 246 zcmZ`yu?>JQ3=0wma0Yj<^A25$q<3lf5~w&S>Jw*8W<1F`~3$0TzE8vI2_ v7^YvzJ34^G{~sMd&=2B)0E7z^01G405NX^bx_THNW-i +#include + +#include "contexthelp.h" +#include "gamesequence.h" +#include "freespace.h" +#include "mainhallmenu.h" +#include "key.h" +#include "bmpman.h" +#include "2d.h" +#include "timer.h" +#include "math.h" +#include "mouse.h" +#include "controlsconfig.h" +#include "techmenu.h" +#include "parselo.h" +#include "localize.h" +#include "alphacolors.h" + + +//////////////////////////////////////////////////////////////////// +// private function prototypes / structs +//////////////////////////////////////////////////////////////////// +void parse_helptbl(); +void help_overlay_blit(int overlay_id); +void help_overlay_init(); + + +typedef struct { + int x_begin, y_begin, x_end, y_end; +} help_line; + +typedef struct { + vector vtx[HELP_MAX_PLINE_VERTICES]; + vector *pvtx[HELP_MAX_PLINE_VERTICES]; + int vtxcount; +} help_pline; + +typedef struct { + int x_coord, y_coord; + char* string; +} help_text; + +typedef struct { + int x_coord, y_coord; +} help_left_bracket; + +typedef struct { + int x_coord, y_coord; +} help_right_bracket; + +typedef struct { + help_pline plinelist[GR_NUM_RESOLUTIONS][HELP_MAX_ITEM]; + help_text textlist[GR_NUM_RESOLUTIONS][HELP_MAX_ITEM]; + help_left_bracket lbracketlist[GR_NUM_RESOLUTIONS][HELP_MAX_ITEM]; + help_right_bracket rbracketlist[GR_NUM_RESOLUTIONS][HELP_MAX_ITEM]; + int plinecount; + int textcount; + int lbracketcount; + int rbracketcount; +} help_overlay; + +// new help.tbl file way +char *help_overlay_section_names[MAX_HELP_OVERLAYS] = { + "$ship", // ship_help + "$weapon", // weapon_help + "$briefing", // briefing + "$main", // main help overlay + "$barracks", // barracks + "$control", // control help + "$debrief", // debrief help + "$multicreate", // multicreate help + "$multistart", // multistart help + "$multijoin", // multijoin help + "$main2", // main help overlay2 + "$hotkey", // hotkey help + "$campaign", // campaign help + "$simulator", // simulator help + "$tech", // tech help + "$command" // command help +}; + +//////////////////////////////////////////////////////////////////// +// Game-wide globals +//////////////////////////////////////////////////////////////////// +shader Grey_shader; + +//////////////////////////////////////////////////////////////////// +// Module globals +//////////////////////////////////////////////////////////////////// +static int help_left_bracket_bitmap; +static int help_right_bracket_bitmap; +static help_overlay help_overlaylist[MAX_HELP_OVERLAYS]; + +static int current_helpid = -1; // the currently active overlay_id, only really used for the debug console funxions +int Help_overlay_flags; +static int Source_game_state; // state from where F1 was pressed + +//////////////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////////////// + + +// query whether a help overlay is active (ie being displayed) +int help_overlay_active(int overlay_id) +{ + Assert(overlay_id >= 0 && overlay_id < MAX_HELP_OVERLAYS); + return Help_overlay_flags & (1<= 0 && overlay_id < MAX_HELP_OVERLAYS); + + if ( state > 0 ) { + Help_overlay_flags |= (1<= 0 && overlay_id < MAX_HELP_OVERLAYS); + + if ( Help_overlay_flags & (1< There is no context help available for state %s\n", GS_state_text[Source_game_state-1])); + break; + + } // end switch +} + + +// Called once at the beginning of the game to load help bitmaps & data +void help_overlay_init() +{ + // load right_bracket bitmap + help_right_bracket_bitmap = bm_load("right_bracket"); + if(help_right_bracket_bitmap < 0){ + // we failed to load the bitmap - this is very bad + Int3(); + } + + // load left_bracket bitmap + help_left_bracket_bitmap = bm_load("left_bracket"); + if(help_left_bracket_bitmap < 0){ + // we failed to load the bitmap - this is very bad + Int3(); + } + + // parse help.tbl + parse_helptbl(); +} + + +// parses help.tbl and populates help_overlaylist[] +void parse_helptbl() +{ + int overlay_id, currcount; + char buf[HELP_MAX_STRING_LENGTH + 1]; + int i; + + // open localization + lcl_ext_open(); + + read_file_text(HELP_OVERLAY_FILENAME); + + // for each overlay... + for (overlay_id=0; overlay_id= 0 && overlay_id < MAX_HELP_OVERLAYS); + + // this draws each line of help text with white on black text (use the GR_640 index for the string) + for (idx = 0; idx < textcount; idx++) { + gr_set_color_fast(&Color_black); + gr_get_string_size(&width, &height, help_overlaylist[overlay_id].textlist[GR_640][idx].string, strlen(help_overlaylist[overlay_id].textlist[GR_640][idx].string)); + gr_rect(help_overlaylist[overlay_id].textlist[gr_screen.res][idx].x_coord-2*HELP_PADDING, help_overlaylist[overlay_id].textlist[gr_screen.res][idx].y_coord-3*HELP_PADDING, width+4*HELP_PADDING, height+4*HELP_PADDING); + gr_set_color_fast(&Color_bright_white); + gr_printf(help_overlaylist[overlay_id].textlist[gr_screen.res][idx].x_coord, help_overlaylist[overlay_id].textlist[gr_screen.res][idx].y_coord, help_overlaylist[overlay_id].textlist[GR_640][idx].string); + } + + // this draws each right bracket + for (idx = 0; idx < rbracketcount; idx++) { + gr_set_bitmap(help_right_bracket_bitmap); + gr_bitmap(help_overlaylist[overlay_id].rbracketlist[gr_screen.res][idx].x_coord, help_overlaylist[overlay_id].rbracketlist[gr_screen.res][idx].y_coord); + } + + // this draws each left bracket + for (idx = 0; idx < lbracketcount; idx++) { + gr_set_bitmap(help_left_bracket_bitmap); + gr_bitmap(help_overlaylist[overlay_id].lbracketlist[gr_screen.res][idx].x_coord, help_overlaylist[overlay_id].lbracketlist[gr_screen.res][idx].y_coord); + } + + // this draws each 2d line for the help screen + //gr_set_color_fast(&Color_yellow); + gr_set_color(255, 255, 0); + for (idx = 0; idx index for control item within Control_config[] +// buf => buffer with enough space to hold ouput string +char *gameplay_help_control_text(int id, char *buf) +{ + int has_key=0, has_joy=0; + config_item *ci; + + ci = &Control_config[id]; + + if ( ci->key_id >= 0 ) { + sprintf(buf, textify_scancode(ci->key_id)); + has_key=1; + } + + if ( ci->joy_id >= 0 ) { + if ( has_key ) { + strcat(buf, XSTR( ", ", 129)); + } + strcat(buf, Joy_button_text[ci->joy_id]); + has_joy=1; + } + + if ( !has_key && !has_joy ) { + strcpy(buf, XSTR( "no binding", 130)); + } + + strcat(buf, XSTR( " - ", 131)); + strcat(buf, ci->text); + + return buf; +} + +void gameplay_help_blit_control_line(int x, int y, int id) +{ + int has_key=0, has_joy=0; + char buf[256]; + config_item *ci; + + ci = &Control_config[id]; + + buf[0] = 0; + + if ( ci->key_id >= 0 ) { + sprintf(buf, textify_scancode(ci->key_id)); + has_key=1; + } + + if ( ci->joy_id >= 0 ) { + if ( has_key ) { + strcat(buf, XSTR( ", ", 129)); + } + strcat(buf, Joy_button_text[ci->joy_id]); + has_joy=1; + } + + if ( !has_key && !has_joy ) { + strcpy(buf, XSTR( "no binding", 130)); + } + + gr_string(x,y,buf); + +// gr_string(x+KEY_DESCRIPTION_OFFSET,y,ci->text); + gr_string(x+KEY_DESCRIPTION_OFFSET, y, XSTR(ci->text, CONTROL_CONFIG_XSTR + id)); +} + +void gameplay_help_blit_control_line_raw(int x, int y, char *control_text, char *control_description) +{ + gr_string(x,y,control_text); + gr_string(x+KEY_DESCRIPTION_OFFSET,y,control_description); +} + +// game_play_help_set_title() will display the title for the help screen and +// set the font for the rest of the screen +void gameplay_help_set_title(char *title) +{ + int sy=TITLE_Y; + char buf[128]; + + gr_set_color_fast(&Color_bright); + gr_printf(0x8000,sy,title); + sprintf(buf, XSTR( "Page %d of %d", 132), Current_help_page+1, Gp_last_screen+1); + gr_printf(0x8000,sy+gr_get_font_height()+2,buf); + gr_set_color_fast(&Color_normal); +} + +// called once when the gameplay help state is entered +void gameplay_help_init() +{ + int i; + gameplay_help_buttons *b; + + if ( Gameplay_help_inited ) { + return; + } + + common_set_interface_palette("InterfacePalette"); // set the interface palette + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Ui_window.set_mask_bmap(Game_help_mask_filename[gr_screen.res]); + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, 0, 1); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // add all xstrs + for (i=0; i Gp_last_screen) { + Current_help_page = GP_FIRST_SCREEN; + } + gamesnd_play_iface(SND_SWITCH_SCREENS); +} + +// called when the screen is exited +void gameplay_help_leave() +{ + // unpause all game sounds + beam_unpause_sounds(); + audiostream_unpause_all(); + + gameseq_post_event(GS_EVENT_PREVIOUS_STATE); + game_flush(); +} + +// deal with a keypress on the gameplay help screen +void gameplay_help_process_key(int k) +{ + switch ( k ) { + case KEY_ESC: + gameplay_help_leave(); + break; + + case KEY_ENTER: + case KEY_SPACEBAR: + case KEY_TAB: + //gameplay_help_goto_next_screen(); + Buttons[gr_screen.res][NEXT_PAGE_BUTTON].button.press_button(); + break; + + case KEY_SHIFTED | KEY_TAB: + Buttons[gr_screen.res][PREVIOUS_PAGE_BUTTON].button.press_button(); +// gameplay_help_goto_prev_screen(); + break; + + default: + break; + + } // end switch +} + +// deal with buttons being pressed on the gameplay help screen +void gameplay_help_button_pressed(int n) +{ + switch (n) { + + case PREVIOUS_PAGE_BUTTON: + gameplay_help_goto_prev_screen(); + break; + + case NEXT_PAGE_BUTTON: + gameplay_help_goto_next_screen(); + break; + + case CONTINUE_BUTTON: + gameplay_help_leave(); + gamesnd_play_iface(SND_COMMIT_PRESSED); + break; + + default: + Int3(); + break; + } +} + +// Draw the help text onto the screen +void gameplay_help_draw_text() +{ + int x_offset, y_offset, separation; + + separation = gr_get_font_height() + 3; + + switch ( Current_help_page ) { + + case GP_HELP_BASIC_KEYS: + gameplay_help_set_title(XSTR( "Basic Keys", 133)); + x_offset=X_OFFSET_1; + y_offset=Y_START; + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,END_MISSION); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,FIRE_PRIMARY); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,MAX_THROTTLE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,ZERO_THROTTLE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_NEXT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_PREV); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TOGGLE_AUTO_TARGETING); + + y_offset += separation; + y_offset += separation; + + gr_set_color_fast(&Color_bright); + gr_printf(0x8000,y_offset,XSTR( "Function Keys", 134)); + gr_set_color_fast(&Color_normal); + + y_offset += separation; + y_offset += separation; + + gameplay_help_blit_control_line_raw(x_offset,y_offset, XSTR( "F1", 135), XSTR( "context-sensitive help", 136)); + + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset,y_offset, XSTR( "F2", 137), XSTR( "options screen (available anywhere in game)", 138)); + + #ifndef FS2_DEMO + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset,y_offset, XSTR( "F3", 139), XSTR( "hotkey assignment", 140)); + #endif + + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset,y_offset, XSTR( "F4", 141), XSTR( "HUD message scroll-back", 142)); + + #ifndef FS2_DEMO + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset,y_offset, XSTR( "F5...F12", 143), XSTR( "hotkeys", 144)); + #endif + + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset,y_offset, XSTR( "Shift-Esc", 145), XSTR( "quit FreeSpace 2 immediately", 146)); + + break; + + case GP_HELP_MOVEMENT_KEYS: + gameplay_help_set_title(XSTR( "Movement Keys", 147)); + x_offset=X_OFFSET_1; + y_offset=Y_START; + + gameplay_help_blit_control_line(x_offset, y_offset,FORWARD_THRUST); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,MAX_THROTTLE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,ONE_THIRD_THROTTLE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TWO_THIRDS_THROTTLE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,MINUS_5_PERCENT_THROTTLE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,PLUS_5_PERCENT_THROTTLE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,ZERO_THROTTLE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,AFTERBURNER); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,REVERSE_THRUST); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,PITCH_FORWARD); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,PITCH_BACK); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,YAW_LEFT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,YAW_RIGHT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,BANK_LEFT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,BANK_RIGHT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,BANK_WHEN_PRESSED); + break; + + case GP_HELP_COMMON_TARGET_KEYS: + gameplay_help_set_title(XSTR( "Basic Targeting Keys", 148)); + x_offset=X_OFFSET_1; + y_offset=Y_START; + + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_NEXT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_PREV); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TOGGLE_AUTO_TARGETING); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_SHIP_IN_RETICLE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_NEXT_CLOSEST_HOSTILE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_PREV_CLOSEST_HOSTILE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_NEXT_CLOSEST_FRIENDLY); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_PREV_CLOSEST_FRIENDLY); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_NEXT_SUBOBJECT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_PREV_SUBOBJECT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_SUBOBJECT_IN_RETICLE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,MATCH_TARGET_SPEED); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TOGGLE_AUTO_MATCH_TARGET_SPEED); + break; + + case GP_HELP_ADVANCED_TARGET_KEYS: + gameplay_help_set_title(XSTR( "Advanced Targeting Keys", 149)); + x_offset=X_OFFSET_1; + y_offset=Y_START; + + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_CLOSEST_SHIP_ATTACKING_SELF); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_CLOSEST_SHIP_ATTACKING_TARGET); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_LAST_TRANMISSION_SENDER); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_TARGETS_TARGET); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_NEXT_ESCORT_SHIP); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_CLOSEST_REPAIR_SHIP); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_NEXT_UNINSPECTED_CARGO); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_PREV_UNINSPECTED_CARGO); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_NEWEST_SHIP); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_NEXT_BOMB); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_PREV_BOMB); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,STOP_TARGETING_SHIP); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_NEXT_LIVE_TURRET); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TARGET_PREV_LIVE_TURRET); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,STOP_TARGETING_SUBSYSTEM); + + #ifndef FS2_DEMO + + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset, y_offset, XSTR( "F5...F12", 143), XSTR( "Select target assigned to that hotkey", 150)); + + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset, y_offset, XSTR( "Shift-F5...F12", 151), XSTR( "Add/remove target from that hotkey", 152)); + + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset, y_offset, XSTR( "Alt-Shift-F5...F12", 153), XSTR( "Clear that hotkey", 154)); + + #endif + + break; + + case GP_HELP_MESSAGING: + gameplay_help_set_title(XSTR( "Messaging Keys", 155)); + x_offset=X_OFFSET_1; + y_offset=Y_START; + + gameplay_help_blit_control_line(x_offset, y_offset,SQUADMSG_MENU); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,REARM_MESSAGE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,ATTACK_MESSAGE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,DISARM_MESSAGE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,DISABLE_MESSAGE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,CAPTURE_MESSAGE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,ENGAGE_MESSAGE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,FORM_MESSAGE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,IGNORE_MESSAGE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,PROTECT_MESSAGE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,COVER_MESSAGE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,WARP_MESSAGE); + + #ifndef FS2_DEMO + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset, y_offset, XSTR( "F5...F12", 143), XSTR( "send specified order to these target(s)", 156)); + #endif + break; + + case GP_HELP_WEAPON_KEYS: + gameplay_help_set_title(XSTR( "Weapon Keys", 157)); + x_offset=X_OFFSET_1; + y_offset=Y_START; + + gameplay_help_blit_control_line(x_offset, y_offset,FIRE_PRIMARY); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,FIRE_SECONDARY); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,CYCLE_NEXT_PRIMARY); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,CYCLE_PREV_PRIMARY); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,CYCLE_SECONDARY); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,CYCLE_NUM_MISSLES); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,LAUNCH_COUNTERMEASURE); + break; + + case GP_HELP_THROTTLE_AND_ETS_KEYS: + gameplay_help_set_title(XSTR( "Energy Management Keys", 158)); + x_offset=X_OFFSET_1; + y_offset=Y_START; + + gameplay_help_blit_control_line(x_offset, y_offset,SHIELD_EQUALIZE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,SHIELD_XFER_TOP); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,SHIELD_XFER_BOTTOM); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,SHIELD_XFER_LEFT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,SHIELD_XFER_RIGHT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,INCREASE_WEAPON); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,DECREASE_WEAPON); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,INCREASE_SHIELD); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,DECREASE_SHIELD); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,INCREASE_ENGINE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,DECREASE_ENGINE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,ETS_EQUALIZE); +/* + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,XFER_LASER); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,XFER_SHIELD); +*/ + break; + + case GP_HELP_MISC_KEYS: + gameplay_help_set_title(XSTR( "Miscellaneous Keys", 159)); + x_offset=X_OFFSET_1; + y_offset=Y_START; + + // ending mission + // + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,END_MISSION); + + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset,y_offset, XSTR( "ESC", 160), XSTR( "invoke abort mission popup", 161)); + + // time compression + // + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TIME_SPEED_UP); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,TIME_SLOW_DOWN); + + // radar keys + // + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,RADAR_RANGE_CYCLE); + + // escort view keys + // + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,ADD_REMOVE_ESCORT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,ESCORT_CLEAR); + + // Pause, Print Screenshot + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset,y_offset, XSTR( "Pause", 162), XSTR( "pause game", 163)); + + y_offset += separation; + gameplay_help_blit_control_line_raw(x_offset,y_offset, XSTR( "Print Scrn", 164), XSTR( "take screen shot", 165)); + + break; + + case GP_HELP_VIEW_KEYS: + gameplay_help_set_title(XSTR( "View Keys", 166)); + x_offset=X_OFFSET_1; + y_offset=Y_START; + + gameplay_help_blit_control_line(x_offset, y_offset,PADLOCK_UP); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,PADLOCK_DOWN); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,PADLOCK_LEFT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,PADLOCK_RIGHT); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,VIEW_CHASE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,VIEW_EXTERNAL); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,VIEW_SLEW); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,VIEW_DIST_INCREASE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,VIEW_DIST_DECREASE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,VIEW_CENTER); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,VIEW_OTHER_SHIP); + break; + + case GP_HELP_MULTI_KEYS: + gameplay_help_set_title(XSTR( "Multiplayer Keys", 167)); + x_offset=X_OFFSET_1; + y_offset=Y_START; + + // ingame messaging + gr_set_color_fast(&Color_bright); + gr_printf(0x8000,y_offset,XSTR( "Ingame messaging keys (tap for text, hold for voice)", 168)); + gr_set_color_fast(&Color_normal); + + y_offset += separation; + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,MULTI_MESSAGE_ALL); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,MULTI_MESSAGE_FRIENDLY); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,MULTI_MESSAGE_HOSTILE); + + y_offset += separation; + gameplay_help_blit_control_line(x_offset, y_offset,MULTI_MESSAGE_TARGET); + + break; + + } // end switch +} + +// gameplay_help_do_frame() is the function that displays help when acutally playing the game +void gameplay_help_do_frame(float frametime) +{ + int i, k; + + // ensure the gameplay help interface has been initialized + if (!Gameplay_help_inited) { + Int3(); + return; + } + + // make sure game sounds are paused + beam_pause_sounds(); + audiostream_pause_all(); + + k = Ui_window.process() & ~KEY_DEBUGGED; + gameplay_help_process_key(k); + + for (i=0; i= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + Ui_window.draw(); + + gameplay_help_draw_text(); + gr_flip(); +} + + +// called once when leaving the gameplay help state +void gameplay_help_close() +{ + if ( Gameplay_help_inited ) { + if (Background_bitmap >= 0) { + bm_unload(Background_bitmap); + } + + Ui_window.destroy(); + common_free_interface_palette(); // restore game palette + game_flush(); + } + + Gameplay_help_inited = 0; +} diff --git a/src/gamesequence/gamesequence.cpp b/src/gamesequence/gamesequence.cpp new file mode 100644 index 0000000..8a57e9b --- /dev/null +++ b/src/gamesequence/gamesequence.cpp @@ -0,0 +1,614 @@ +/* + * $Logfile: /Freespace2/code/GameSequence/GameSequence.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * File to control Game Sequencing + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 65 5/15/98 12:09a Dave + * New tracker api code. New game tracker code. Finished up first run of + * the PXO screen. Fixed a few game server list exceptions. + * + * 64 5/12/98 2:46a Dave + * Rudimentary communication between Parallax Online and freespace. Can + * get and store channel lists. + * + * 63 5/06/98 1:12a Allender + * fix sequencing names, added nprintf to help respawn debugging + * + * 62 4/25/98 7:39p Allender + * fixd some small hotkey stuff. Worked on turret orientation being + * correct for multiplayer. new sexpression called end-campaign will will + * end the main campaign + * + * 61 4/23/98 7:08p John + * Removed some obsoleted states. + * + * 60 4/16/98 4:31p Hoffoss + * Changed demo screen referenced to view cutscenes screen, which is now + * what it's called. + * + * 59 4/02/98 5:40p Hoffoss + * Added the Load Mission screen to FreeSpace. + * + * 58 3/11/98 5:32p Lawrance + * Fix up text arrays for events/states + * + * 57 3/09/98 12:13a Lawrance + * Add support for Red Alert missions + * + * 56 3/05/98 4:12p John + * Made Debug+F4 switch Glide and windowed. + * + * 55 3/03/98 1:00p Hoffoss + * Added new command briefing event and state. + * + * 54 3/02/98 4:23p Hoffoss + * Forgot to add state label. + * + * 53 3/02/98 3:44p Hoffoss + * Added new Campaign Room state and event. + * + * 52 2/21/98 11:58a John + * Put in some stuff to externalize strings + * + * 51 2/19/98 6:26p Dave + * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in + * initial support for player data uploading. + * + * 50 2/18/98 10:21p Dave + * Ripped out old file xfer system. Put in brand new xfer system. + * + * 49 2/08/98 5:07p Dave + * Put in support for multiplayer furball mode. + * + * 48 1/28/98 6:21p Dave + * Made the standalone use ~8 megs less memory. Fixed multiplayer submenu + * endgame problem. + * + * 47 1/23/98 5:43p Dave + * Finished bringing standalone up to speed. Coded in new host options + * screen. + * + * 46 1/22/98 5:25p Dave + * Modified some pregame sequencing packets. Starting to repair broken + * standalone stuff. + * + * 45 1/22/98 4:15p Hoffoss + * Added code to allow popup to tell player he needs to bind a key for the + * training mission. + * + * 44 1/20/98 5:42p Dave + * Moved ingame join to its own module. Improved it a bit. + * + * 43 1/19/98 12:57p Allender + * removed confusing comment + * + * 42 1/19/98 12:56p Allender + * fix problem of freespace_start_misison possibly failing due to mission + * not properly loading (for single player only right now). + * + * 41 1/15/98 6:12p Dave + * Fixed weapons loadout bugs with multiplayer respawning. Added + * multiplayer start screen. Fixed a few chatbox bugs. + * + * 40 1/15/98 6:00p Hoffoss + * Added option to quit menu (in game) to restart the mission. Doesn't + * seem to quite work, though. Checking code in so someone else can look + * into it. + * + * 39 1/05/98 10:05a Dave + * Big re-sequencing of server transfer. Centralized _all_ server transfer + * code to one module. + * + * 38 12/30/97 4:28p Lawrance + * Give text descriptions for events, change debug output to give text + * desciption of event/state + * + * 37 12/24/97 8:56p Lawrance + * took out obsolete state used for non-existant sound config screen + * + * 36 11/19/97 8:28p Dave + * Hooked in Main Hall screen. Put in Anim support for ping ponging + * animations as well as general reversal of anim direction. + * + * 35 11/15/97 2:36p Dave + * Added more multiplayer campaign support. + * + * 34 11/13/97 7:01p Hoffoss + * Fixed GS_state_text[], which didn't match the current states we have + * available. + * + * 33 11/10/97 6:02p Hoffoss + * Added new debug paused state. + * + * 32 11/03/97 10:12p Hoffoss + * Finished up work on the hud message/mission log scrollback screen. + * + * 31 10/22/97 11:00p Lawrance + * modify pop_and_discard() to allow discarding of all states on the stack + * + * 30 10/22/97 5:08p John + * fixed a whole slew of bugs and clean up a bunch of stuff dealing with + * end of mission stuff. + * + * 29 10/02/97 9:49p Hoffoss + * Added event evaluation analysis debug screen so we can determine the + * state of events and their sexp trees to track down logic problems and + * such. + * + * 28 9/23/97 11:53p Lawrance + * add state do perform multiplayer on-line help + * + * 27 9/22/97 4:55p Hoffoss + * Added a training message window display thingy. + * + * 26 9/19/97 4:24p Allender + * added team selection state -- initialze player* variable in + * player_level_init + * + * 25 9/18/97 10:17p Lawrance + * add help state for briefing + * + * 24 9/18/97 10:15p Lawrance + * add help state for briefing + * + * 23 7/14/97 12:03a Lawrance + * added navmap state + * + * 22 6/13/97 2:30p Lawrance + * Added debriefings + * + * 21 5/20/97 10:02a Lawrance + * added view medals screen + * + * 20 4/28/97 2:17p Lawrance + * added help state for hotkey assignment screen + * + * 19 4/25/97 3:41p Lawrance + * added support for hotkey assignment screen + * + * 18 4/23/97 9:54a Lawrance + * made show goals screen a separate state + * + * 17 4/22/97 11:06a Lawrance + * added credits state + * + * 16 4/17/97 9:01p Allender + * start of campaign stuff. Campaigns now stored in external file (no + * filenames in code). Continuing campaign won't work at this point + * + * 15 4/03/97 8:40p Lawrance + * add new player death states to GS_state_text[] + * + * 14 3/05/97 5:04p Lawrance + * added new states for different context help + * + * 13 1/09/97 12:41a Lawrance + * added function to pop a state without restoring that state + * + * 12 12/22/96 3:41p Lawrance + * integrating energy transfer system + * + * 11 12/09/96 2:35p Allender + * modifed game sequencing so that game_leave_state and game_enter_state + * are *always* called. + * + * 10 12/08/96 1:54a Lawrance + * put check in to see if a state change request is invalid (ie already in + * that state) + * + * 9 11/18/96 5:07p John + * Changed sequencing code to call entry,leave functions for each state + * change. Added Shift+Pause debug pause thing. + * + * 8 11/13/96 4:02p Lawrance + * complete over-haul of the menu system and the states associated with + * them + * + * 7 10/23/96 9:08a Allender + * Removed primary and secondary goal complete states -- to be implemented + * later. + * +*/ + +/* + * All states for game sequencing are defined in GameSequence.h. + * States should always be referred to using the macros. +*/ + +#include "freespace.h" +#include "gamesequence.h" + +// local defines +#define MAX_GAMESEQ_EVENTS 20 // maximum number of events on the game sequencing queue +#define GS_STACK_SIZE 10 // maximum number of stacked states + +// local variables +typedef struct state_stack { + int current_state; + int event_queue[MAX_GAMESEQ_EVENTS]; + int queue_tail, queue_head; +} state_stack; + +// DO NOT MAKE THIS NON-STATIC!!!! +LOCAL state_stack gs[GS_STACK_SIZE]; +LOCAL int gs_current_stack = -1; // index of top state on stack. + +static int state_reentry = 0; // set if we are already in state processing +static int state_processing_event_post = 0; // set if we are already processing an event to switch states +static int state_in_event_processer = 0; + +// Text of state, corresponding to #define values for GS_STATE_* +//XSTR:OFF +char *GS_event_text[] = +{ + "GS_EVENT_MAIN_MENU", + "GS_EVENT_START_GAME", + "GS_EVENT_ENTER_GAME", + "GS_EVENT_START_GAME_QUICK", + "GS_EVENT_END_GAME", + "GS_EVENT_QUIT_GAME", // 5 + "GS_EVENT_PAUSE_GAME", + "GS_EVENT_PREVIOUS_STATE", + "GS_EVENT_OPTIONS_MENU", + "GS_EVENT_BARRACKS_MENU", + "GS_EVENT_TRAINING_MENU", // 10 + "GS_EVENT_TECH_MENU", + "GS_EVENT_LOAD_MISSION_MENU", + "GS_EVENT_SHIP_SELECTION", + "GS_EVENT_TOGGLE_FULLSCREEN", + "GS_EVENT_WEAPON_SELECT_HELP", // 15 + "GS_EVENT_START_BRIEFING", + "GS_EVENT_DEBUG_PAUSE_GAME", + "GS_EVENT_HUD_CONFIG", + "GS_EVENT_MULTI_SETUP", + "GS_EVENT_MULTI_JOIN_GAME", // 20 + "GS_EVENT_CONTROL_CONFIG", + "GS_EVENT_EVENT_DEBUG", + "GS_EVENT_MULTI_PROTO_CHOICE", + "GS_EVENT_SAVE_RESTORE", + "GS_EVENT_CHOOSE_SAVE_OR_RESTORE", // 25 + "GS_EVENT_WEAPON_SELECTION", + "GS_EVENT_MISSION_LOG_SCROLLBACK", + "GS_EVENT_MAIN_MENU_HELP", + "GS_EVENT_GAMEPLAY_HELP", + "GS_EVENT_SHIP_SELECT_HELP", // 30 + "GS_EVENT_DEATH_DIED", + "GS_EVENT_DEATH_BLEW_UP", + "GS_EVENT_NEW_CAMPAIGN", + "GS_EVENT_CREDITS", + "GS_EVENT_SHOW_GOALS", // 35 + "GS_EVENT_HOTKEY_SCREEN", + "GS_EVENT_HOTKEY_SCREEN_HELP", + "GS_EVENT_VIEW_MEDALS", + "GS_EVENT_MULTI_HOST_SETUP", + "GS_EVENT_MULTI_CLIENT_SETUP", // 40 + "GS_EVENT_DEBRIEF", + "GS_EVENT_NAVMAP", + "GS_EVENT_MULTI_JOIN_TRACKER", + "GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN", + "GS_EVENT_MULTI_STD_WAIT", // 45 + "GS_EVENT_STANDALONE_MAIN", + "GS_EVENT_MULTI_PAUSE", + "GS_EVENT_BRIEFING_HELP", + "GS_EVENT_TEAM_SELECT", + "GS_EVENT_TRAINING_PAUSE", // 50 + "GS_EVENT_MULTI_HELP", + "GS_EVENT_INGAME_PRE_JOIN", + "GS_EVENT_PLAYER_WARPOUT_START", + "GS_EVENT_PLAYER_WARPOUT_START_FORCED", + "GS_EVENT_PLAYER_WARPOUT_STOP", // 55 + "GS_EVENT_PLAYER_WARPOUT_DONE_STAGE1", + "GS_EVENT_PLAYER_WARPOUT_DONE_STAGE2", + "GS_EVENT_PLAYER_WARPOUT_DONE", + "GS_EVENT_STANDALONE_POSTGAME", + "GS_EVENT_INITIAL_PLAYER_SELECT", // 60 + "GS_EVENT_GAME_INIT", + "GS_EVENT_MULTI_MISSION_SYNC", + "GS_EVENT_MULTI_CAMPAIGN_SELECT", + "GS_EVENT_MULTI_SERVER_TRANSFER", + "GS_EVENT_MULTI_START_GAME", // 65 + "GS_EVENT_MULTI_HOST_OPTIONS", + "GS_EVENT_MULTI_DOGFIGHT_DEBRIEF", + "GS_EVENT_CAMPAIGN_ROOM", + "GS_EVENT_CMD_BRIEF", + "GS_EVENT_TOGGLE_GLIDE", // 70 + "GS_EVENT_RED_ALERT", + "GS_EVENT_SIMULATOR_ROOM", + "GS_EVENT_EMD_CAMPAIGN", +}; +//XSTR:ON + +// Text of state, corresponding to #define values for GS_STATE_* +//XSTR:OFF +char *GS_state_text[] = +{ + "NOT A VALID STATE", + "GS_STATE_MAIN_MENU", // 1 + "GS_STATE_GAME_PLAY", + "GS_STATE_GAME_PAUSED", + "GS_STATE_QUIT_GAME", + "GS_STATE_OPTIONS_MENU", // 5 + "GS_EVENT_WEAPON_SELECT_HELP", + "GS_STATE_BARRACKS_MENU", + "GS_STATE_TECH_MENU", + "GS_STATE_TRAINING_MENU", + "GS_STATE_LOAD_MISSION_MENU", // 10 + "GS_STATE_BRIEFING", + "GS_STATE_SHIP_SELECT", + "GS_STATE_DEBUG_PAUSED", + "GS_STATE_HUD_CONFIG", + "GS_STATE_MULTI_SETUP", // 15 + "GS_STATE_MULTI_JOIN_GAME", + "GS_STATE_CONTROL_CONFIG", + "GS_STATE_MULTI_PROTO_CHOICE", + "GS_STATE_SAVE_RESTORE", + "GS_STATE_WEAPON_SELECT", // 20 + "GS_STATE_MISSION_LOG_SCROLLBACK", + "GS_STATE_MAIN_MENU_HELP", + "GS_STATE_GAMEPLAY_HELP", + "GS_STATE_SHIP_SELECT_HELP", + "GS_STATE_DEATH_DIED", // 25 + "GS_STATE_DEATH_BLEW_UP", + "GS_STATE_SIMULATOR_ROOM", + "GS_STATE_CREDITS", + "GS_STATE_SHOW_GOALS", + "GS_STATE_HOTKEY_SCREEN", // 30 + "GS_STATE_HOTKEY_SCREEN_HELP", + "GS_STATE_VIEW_MEDALS", + "GS_STATE_MULTI_HOST_SETUP", + "GS_STATE_MULTI_CLIENT_SETUP", + "GS_STATE_DEBRIEF", // 35 + "GS_STATE_NAVMAP", + "GS_STATE_MULTI_JOIN_TRACKER", + "GS_STATE_VIEW_CUTSCENES", + "GS_STATE_MULTI_STD_WAIT", + "GS_STATE_STANDALONE_MAIN", // 40 + "GS_STATE_MULTI_PAUSED", + "GS_STATE_BRIEFING_HELP", + "GS_STATE_TEAM_SELECT", + "GS_STATE_TRAINING_PAUSED", + "GS_STATE_MULTI_HELP", // 45 + "GS_STATE_INGAME_PRE_JOIN", + "GS_STATE_EVENT_DEBUG", + "GS_STATE_STANDALONE_POSTGAME", + "GS_STATE_INITIAL_PLAYER_SELECT", + "GS_STATE_MULTI_MISSION_SYNC", // 50 + "GS_STATE_MULTI_SERVER_TRANSFER", + "GS_STATE_MULTI_START_GAME", + "GS_STATE_MULTI_HOST_OPTIONS", + "GS_STATE_MULTI_DOGFIGHT_DEBRIEF", + "GS_STATE_CAMPAIGN_ROOM", // 55 + "GS_STATE_CMD_BRIEF", + "GS_STATE_RED_ALERT", + "GS_STATE_END_OF_CAMPAIGN", +}; +//XSTR:ON + +void gameseq_init() +{ + int i; + + for (i=0; i= 1) { + int old_state; + + // set the old state to be the state which is about to be popped off the queue + old_state = gs[gs_current_stack].current_state; + + // set the popped_state to be the state which is going to be moved into + popped_state = gs[gs_current_stack-1].current_state; + + // leave the current state + state_reentry++; + game_leave_state(gs[gs_current_stack].current_state,popped_state); + + // set the popped_state to be the one we moved into + gs_current_stack--; + popped_state = gs[gs_current_stack].current_state; + + // swap all remaining events from the state which just got popped to this new state + while(gs[gs_current_stack+1].queue_head != gs[gs_current_stack+1].queue_tail){ + gameseq_post_event(gs[gs_current_stack+1].event_queue[gs[gs_current_stack+1].queue_head++]); + } + + game_enter_state(old_state, gs[gs_current_stack].current_state); + state_reentry--; + + } + +} + +// gameseq_pop_and_discard_state() is used to remove a state that was pushed onto the stack, but +// will never need to be popped. An example of this is entering a state that may require returning +// to the previous state (then you would call gameseq_pop_state). Or you may simply continue to +// another part of the game, to avoid filling up the stack with states that may never be popped, you +// call this function to discard the top of the gs. +// + +void gameseq_pop_and_discard_state() +{ + if (gs_current_stack > 0 ) { + gs_current_stack--; + } +} + +// Returns the last state pushed on stack +int gameseq_get_pushed_state() +{ + if (gs_current_stack >= 1) { + return gs[gs_current_stack-1].current_state; + } else + return -1; +} + +// gameseq_process_events gets called every time through high level loops +// (i.e. game loops, main menu loop). Function is responsible for pulling +// game sequence events off the queue and changing the state when necessary. +// Returns the current state. + // pull events game sequence events off of the queue. Process one at a time + // based on the current state and the new event. + +int gameseq_process_events() +{ + int event, old_state; + old_state = gs[gs_current_stack].current_state; + + Assert(state_reentry == 0); // Get John! (Invalid state sequencing!) + + while ( (event = gameseq_get_event()) != -1 ) { + state_reentry++; + state_in_event_processer++; + game_process_event(gs[gs_current_stack].current_state, event); + state_in_event_processer--; + state_reentry--; + // break when state changes so that code will get called at + // least one frame for each state. + if (old_state != gs[gs_current_stack].current_state) + break; + } + + state_reentry++; + game_do_state(gs[gs_current_stack].current_state); + state_reentry--; + + return gs[gs_current_stack].current_state; +} + diff --git a/src/gamesnd/eventmusic.cpp b/src/gamesnd/eventmusic.cpp new file mode 100644 index 0000000..2a46bca --- /dev/null +++ b/src/gamesnd/eventmusic.cpp @@ -0,0 +1,1480 @@ +/* + * $Logfile: /Freespace2/code/Gamesnd/EventMusic.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for high-level control of event driven music + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 14 8/19/99 9:41a Alanl + * don't play victory 2 music if there are no goals in the mission + * + * 13 8/11/99 5:33p Jefff + * added 3rd debrief music track + * + * 12 8/01/99 2:11p Alanl + * make hull value to play more intense battle music a constant + * + * 11 8/01/99 2:06p Alanl + * tweak battle music track switching + * + * 10 7/25/99 9:57p Alanl + * change battle music track selection after enemy arrivals to make battle + * tracks less repetitive + * + * 9 7/19/99 10:13p Andsager + * Tie in hud_targeting to hostile_ships_present() used to determine next + * song. + * + * 8 6/24/99 10:47p Alanl + * loop battle between tracks 2 and 3 if hull integrity < 70% + * + * 7 6/21/99 1:34p Alanl + * event music tweaks + * + * 6 6/20/99 12:06a Alanl + * new event music changes + * + * 5 11/23/98 11:55a Johnson + * return -1 if a score isn't found + * + * 4 11/20/98 4:08p Dave + * Fixed flak effect in multiplayer. + * + * 3 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 107 6/09/98 5:15p Lawrance + * French/German localization + * + * 106 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 105 5/24/98 5:28p Dan + * let event_music_level_init() over-ride Event_music_enabled + * + * 104 5/24/98 4:42p Dan + * AL: Fix several bugs related to pausing and enabling/disabling event + * music + * + * 103 5/23/98 3:17a Lawrance + * Tweak how battle music gets restarted + * + * 102 5/22/98 10:43a Lawrance + * If mission doesn't have event music, don't choose random track + * + * 101 5/21/98 6:56p Lawrance + * Tweak how victory music plays + * + * 100 5/21/98 2:47a Lawrance + * Fix some problems with event music + * + * 99 5/18/98 3:21p Mike + * Don't play arrival and goals tracks in a training mission. + * + * 98 5/04/98 3:15p Duncan + * AL: remove bogus assert in event_music_player_respawn() + * + * 97 5/03/98 1:54a Lawrance + * Fix event music problems related to respawning + * + * 96 4/03/98 12:56a Lawrance + * Fix bug with music not starting in the first mission + * + * 95 4/01/98 6:46p Lawrance + * Lower default music volume + * + * $NoKeywords: $ + */ + + +#include "pstypes.h" +#include "eventmusic.h" +#include "object.h" +#include "ship.h" +#include "linklist.h" +#include "missionload.h" /* for Builtin_mission_names[] */ +#include "missionparse.h" +#include "timer.h" +#include "freespace.h" +#include "audiostr.h" +#include "missioncampaign.h" +#include "sound.h" +#include "time.h" +#include "cmdline.h" +#include "missiongoals.h" +#include "localize.h" + +#pragma optimize("", off) + +#define DEFAULT_MASTER_EVENT_MUSIC_VOLUME 0.5f + +#define HULL_VALUE_TO_PLAY_INTENSE_BATTLE_MUSIC 0.75f + +//////////////////////////// +// Globals +//////////////////////////// +int Event_Music_battle_started = 0; +float Master_event_music_volume = DEFAULT_MASTER_EVENT_MUSIC_VOLUME; // range is 0->1 + +// array that holds which soundtrack is used for which mission (data comes from music.tbl) +//int Mission_soundtracks[MAX_CAMPAIGN_MISSIONS]; + +// array that holds which briefing track to play for which mission. Index into Spooled_music[][]. +//int Mission_briefingmusic[MAX_CAMPAIGN_MISSIONS]; + +typedef struct tagSNDPATTERN { + int default_next_pattern; // Needed so the next_pattern member can be reset + int next_pattern; // Next pattern to play at loop time (can be same pattern) + int default_loop_for; // Needed so the loop_for variable can be reset + int loop_for; // Number of times to loop before switching to next pattern + int handle; // handle to open audio stream + int force_pattern; // flag to indicate that we want to not continue loop, but go to next_pattern + int can_force; // whether this pattern can be interrupted + int bytes_per_measure; // number of bytes in a measure + float num_measures; // number of measures in wave file +} SNDPATTERN; + +SNDPATTERN Patterns[MAX_PATTERNS]; // holds data on sections of a SoundTrack + +// Holds filenames for the different sections of a soundtrack +SOUNDTRACK_INFO Soundtracks[MAX_SOUNDTRACKS]; +int Num_soundtracks; +int Current_soundtrack_num; // Active soundtrack for the current mission.. index into Soundtracks[] + +#define PATTERN_DELAY 1000 // in ms +int Current_pattern = -1; // currently playing part of track +int Pending_pattern = -1; +int Pattern_timer_id = 0; + +// File Globals +static int Num_enemy_arrivals; +static int Num_friendly_arrivals; + +#define ARRIVAL_INTERVAL_TIMESTAMP 5000 +#define BATTLE_CHECK_INTERVAL 15000 +static int Battle_over_timestamp; +static int Mission_over_timestamp; +static int Victory2_music_played; +static int Next_arrival_timestamp; +static int Check_for_battle_music; + +// stores the number of measures for the different patterns (data from music.tbl) +float Pattern_num_measures[MAX_SOUNDTRACKS][MAX_PATTERNS]; + +// stores the number of bytes per measure (data from music.tbl) +int Pattern_bytes_per_measure[MAX_SOUNDTRACKS][MAX_PATTERNS]; + +char* Pattern_names[MAX_PATTERNS] = +{ +//XSTR:OFF + "NRML_1", // Normal Song 1 + "AARV_1", // Allied Arrival 1 + "EARV_1", // Enemy Arrival 1 + "BTTL_1", // Battle Song 1 + "BTTL_2", // Battle Song 2 + "BTTL_3", // Battle Song 3 + "AARV_2", // Allied Arrival 2 + "EARV_2", // Enemy Arrival 2 + "VICT_1", // Victory Song 1 + "VICT_2", // Victory Song 2 + "FAIL_1" // Goal Failed 1 + "DEAD_1" // Death Song 1 +//XSTR:ON +}; + +char* Pattern_description[MAX_PATTERNS] = +{ +//XSTR:OFF + "normal 1", + "friendly arrival 1", + "enemy arrival 2", + "battle 1", + "battle 2", + "battle 3", + "friendly arrival 2", + "enemey arrival 2", + "victory 1", + "victory 2", + "goal failed 1" + "death " +//XSTR:ON +}; + +int Pattern_loop_for[MAX_PATTERNS] = +{ + 1, // Normal Song 1 + 1, // Allied Arrival 1 + 1, // Enemy Arrival 1 + 1, // Battle Song 1 + 1, // Battle Song 2 + 1, // Battle Song 3 + 1, // Allied Arrival 2 + 1, // Enemy Arrival 2 + 1, // Victory Song 1 + 1, // Victory Song 2 + 1, // Goal Failed 1 + 1 // Death Song 1 +}; + +int Pattern_default_next[MAX_PATTERNS] = +{ + SONG_NRML_1, // NRML_1 progresses to NRML_1 by default + SONG_NRML_1, // AARV_1 progresses to NRML_1 by default + SONG_BTTL_1, // EARV_1 progresses to BTTL_1 by default + SONG_BTTL_2, // BTTL_1 progresses to BTTL_2 by default + SONG_BTTL_3, // BTTL_2 progresses to BTTL_3 by default + SONG_BTTL_1, // BTTL_3 progresses to BTTL_1 by default + SONG_BTTL_2, // AARV_2 progresses to BTTL_2 by default + SONG_BTTL_3, // EARV_2 progresses to BTTL_3 by default + SONG_NRML_1, // VICT_1 progresses to NRML_1 by default + SONG_NRML_1, // VICT_2 progresses to NRML_1 by default + SONG_NRML_1, // FAIL_1 progresses to NRML_1 by default + -1 // no music plays after dead +}; + + +// Certain patterns can be interrupted (such as the long-playing NRML and BTTL tracks). +// Other shorter tracks (such as arrivals) play their entire duration. +int Pattern_can_force[MAX_PATTERNS] = +{ + TRUE, // NRML_1 + FALSE, // AARV_1 + FALSE, // EARV_1 + TRUE, // BTTL_1 + TRUE, // BTTL_2 + TRUE, // BTTL_3 + FALSE, // AARV_2 + FALSE, // EARV_2 + FALSE, // VICT_1 + TRUE, // VICT_2 + FALSE, // FAIL_1 + TRUE // DEAD_1 +}; + + +int Event_music_enabled = TRUE; +static int Event_music_inited = FALSE; +static int Event_music_level_inited = FALSE; +static int Event_music_begun = FALSE; + +// forward function declarations +int hostile_ships_present(); +int hostile_ships_to_arrive(); +extern int hud_target_invalid_awacs(object *objp); + +// Holds file names of spooled music that is played at menus, briefings, credits etc. +// Indexed into by a #define enumeration of the different kinds of spooled music +menu_music Spooled_music[MAX_SPOOLED_MUSIC]; +int Num_music_files; // Number of spooled music files + +// Array that holds indicies into Spooled_music[], these specify which music is played in breifing/debriefing +int Mission_music[NUM_SCORES]; + +// ------------------------------------------------------------------------------------------------- +// event_music_init() +// +// Called once at game start-up to parse music.tbl and set some state variables +// +void event_music_init() +{ + if ( snd_is_inited() == FALSE ) { + Event_music_enabled = FALSE; + return; + } + + if ( Cmdline_freespace_no_music ) { + return; + } + + if ( Event_music_inited == TRUE ) + return; + + event_music_parse_musictbl(); + Event_music_inited = TRUE; + Event_music_begun = FALSE; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_close() +// +// Called once at game end +// +void event_music_close() +{ + if ( Event_music_inited == FALSE ) + return; + + Event_music_inited = FALSE; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_force_switch() +// +// Performs a switch between patterns. Sets the cutoff limit for the pattern that is being +// switch to. +// +void event_music_force_switch() +{ + if ( Event_music_enabled == FALSE ) + return; + + if ( Event_music_level_inited == FALSE ) + return; + + int new_pattern; + Patterns[Current_pattern].loop_for = Patterns[Current_pattern].default_loop_for; + + new_pattern = Patterns[Current_pattern].next_pattern; + + if ( (new_pattern == SONG_BTTL_3) && (Patterns[SONG_BTTL_3].handle == -1) ) { + if ( Current_pattern == SONG_BTTL_2 ) { + new_pattern = SONG_BTTL_1; + } else { + new_pattern = SONG_BTTL_2; + } + } else { + if (Current_pattern == SONG_BTTL_3 && new_pattern == SONG_BTTL_1) { + // AL 06-24-99: maybe switch to battle 2 if hull is less than 70% + if (Player_obj != NULL && Player_ship != NULL) { + Assert(Player_ship->ship_info_index >= 0); + Assert(Ship_info[Player_ship->ship_info_index].initial_hull_strength != 0); + float integrity = Player_obj->hull_strength / Ship_info[Player_ship->ship_info_index].initial_hull_strength; + if (integrity < HULL_VALUE_TO_PLAY_INTENSE_BATTLE_MUSIC) { + new_pattern = SONG_BTTL_2; + } + } + } + } + + if ( new_pattern == -1 ) { + return; + } + + if ( Patterns[new_pattern].num_measures == 0 ) + return; // invalid pattern + + Assert(new_pattern >= 0 && new_pattern < MAX_PATTERNS); + audiostream_play(Patterns[new_pattern].handle, Master_event_music_volume, 0); // no looping + audiostream_set_byte_cutoff(Patterns[new_pattern].handle, fl2i(Patterns[new_pattern].num_measures * Patterns[new_pattern].bytes_per_measure) ); + Patterns[Current_pattern].next_pattern = Patterns[Current_pattern].default_next_pattern; + Patterns[Current_pattern].force_pattern = FALSE; + nprintf(("EVENTMUSIC", "EVENTMUSIC => switching to %s from %s\n", Pattern_names[new_pattern], Pattern_names[Current_pattern])); + Current_pattern = new_pattern; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_do_frame() +// +// Called once per game frame, to check for transitions of patterns (and to start the music off at +// the beginning). +// +void event_music_do_frame() +{ + if ( Event_music_level_inited == FALSE ) { + return; + } + + if ( Event_music_enabled == FALSE ) { + return; + } + + // start off the music delayed + if ( timestamp_elapsed(Pattern_timer_id) ) { + Pattern_timer_id = 0; + Event_music_begun = TRUE; + if ( Current_pattern != -1 ) { + Assert(Patterns[Current_pattern].handle >= 0 ); + audiostream_play(Patterns[Current_pattern].handle, Master_event_music_volume, 0); // no looping + audiostream_set_byte_cutoff(Patterns[Current_pattern].handle, fl2i(Patterns[Current_pattern].num_measures * Patterns[Current_pattern].bytes_per_measure) ); + } + } + + if ( Event_music_begun == FALSE ) { + return; + } + + if ( Current_pattern != -1 ) { + SNDPATTERN *pat; + pat = &Patterns[Current_pattern]; + + // First case: switching to a different track since first track is almost at end + if ( audiostream_done_reading(pat->handle) ) { + event_music_force_switch(); + } + // Second case: looping back to start of same track since at the end + else if ( !audiostream_is_playing(pat->handle) && !audiostream_is_paused(pat->handle) ) { + audiostream_stop(pat->handle); // stop current and rewind + pat->loop_for--; + if ( pat->loop_for > 0 ) { + audiostream_play(pat->handle, Master_event_music_volume, 0); // no looping + audiostream_set_byte_cutoff(Patterns[Current_pattern].handle, fl2i(Patterns[Current_pattern].num_measures * Patterns[Current_pattern].bytes_per_measure) ); + } + else { + event_music_force_switch(); + } + } + // Third case: switching to a different track by interruption + else if ( (pat->force_pattern == TRUE && pat->can_force == TRUE) ) { + int bytes_streamed = audiostream_get_bytes_committed(pat->handle); + int measures_played = bytes_streamed / pat->bytes_per_measure; + if ( measures_played < pat->num_measures ) { + audiostream_set_byte_cutoff(pat->handle, pat->bytes_per_measure * (measures_played+1) ); + pat->force_pattern = FALSE; + pat->loop_for = 0; + } + } + + // We want to go back to NRML track music if all the hostiles have been + // destroyed, and we are still playing the battle music + if ( Current_pattern == SONG_BTTL_1 || Current_pattern == SONG_BTTL_2 || Current_pattern == SONG_BTTL_3 ) { + if ( timestamp_elapsed(Battle_over_timestamp) && Event_Music_battle_started == 1) { + //Battle_over_timestamp = timestamp(BATTLE_CHECK_INTERVAL); + if ( hostile_ships_present() == FALSE ) { + if ( Patterns[Current_pattern].next_pattern != SONG_VICT_2 ) { + Patterns[Current_pattern].next_pattern = SONG_NRML_1; + Patterns[Current_pattern].force_pattern = TRUE; + Event_Music_battle_started = 0; + } + } + } + } + + if (Event_Music_battle_started == 0) { + if (Current_pattern == SONG_NRML_1) { + if (timestamp_elapsed(Check_for_battle_music)) { + Check_for_battle_music = timestamp(1000); + if (hostile_ships_present() == TRUE) { + Patterns[Current_pattern].next_pattern = SONG_BTTL_1; + Patterns[Current_pattern].force_pattern = TRUE; + } + } + } + } + + if ( !Victory2_music_played ) { + if ( timestamp_elapsed(Mission_over_timestamp) ) { + Mission_over_timestamp = timestamp(BATTLE_CHECK_INTERVAL); + if ( mission_goals_met() && (!hostile_ships_present()) ) { + Patterns[Current_pattern].next_pattern = SONG_VICT_2; + Patterns[Current_pattern].force_pattern = TRUE; + Victory2_music_played = 1; + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------- +// event_music_level_init() +// +// Called at the start of a mission (level). Sets up the pattern data, and kicks off the +// first track to play(). +// +// input: force_soundtrack => OPTIONAL parameter (default value -1) +// forces the soundtrack to ignore the music.tbl assignment +// +void event_music_level_init(int force_soundtrack) +{ + int i; + SOUNDTRACK_INFO *strack; + + if ( Cmdline_freespace_no_music ) { + return; + } + + if ( !audiostream_is_inited() ) + return; + + if ( Event_music_level_inited == TRUE ) + return; + + Current_pattern = -1; + + if ( Event_music_inited == FALSE ) + return; + + + if ( force_soundtrack != -1 ) { + Current_soundtrack_num = force_soundtrack; + } + + if ( Current_soundtrack_num < 0 ) { + return; +/* + // okay, assign a random soundtrack if one exists + if ( Num_soundtracks > 0 ) { + Current_soundtrack_num = rand()%Num_soundtracks; + nprintf(("EVENTMUSIC", "EVENTMUSIC ==> Picking random event music soundtrack: %s\n", Soundtracks[Current_soundtrack_num].name)); + } else { + return; + } +*/ + } + + Assert(Current_soundtrack_num >= 0 && Current_soundtrack_num < Num_soundtracks); + strack = &Soundtracks[Current_soundtrack_num]; + + // open the pattern files, and get ready to play them + for ( i = 0; i < strack->num_patterns; i++ ) { + if ( !stricmp(NOX("none.wav"), strack->pattern_fnames[i]) ) { + Patterns[i].handle = -1; + continue; + } + + Patterns[i].handle = audiostream_open( strack->pattern_fnames[i], ASF_EVENTMUSIC ); + + if ( Patterns[i].handle >= 0 ) { + Event_music_level_inited = TRUE; + Event_music_enabled = TRUE; + } + + Patterns[i].next_pattern = Pattern_default_next[i]; + Patterns[i].default_next_pattern = Pattern_default_next[i]; + Patterns[i].loop_for = Pattern_loop_for[i]; + Patterns[i].default_loop_for = Pattern_loop_for[i]; + Patterns[i].force_pattern = FALSE; + Patterns[i].can_force = Pattern_can_force[i]; + Patterns[i].bytes_per_measure = Pattern_bytes_per_measure[Current_soundtrack_num][i]; + Patterns[i].num_measures = Pattern_num_measures[Current_soundtrack_num][i]; + } + + Num_enemy_arrivals = 0; + Num_friendly_arrivals = 0; + Battle_over_timestamp = timestamp(BATTLE_CHECK_INTERVAL); + Mission_over_timestamp = timestamp(BATTLE_CHECK_INTERVAL); + Next_arrival_timestamp = timestamp(1); + Victory2_music_played = 0; + Check_for_battle_music = 0; + + if ( Event_music_level_inited ) { + if ( force_soundtrack != -1 ) { + event_music_first_pattern(); + } + } +} + +// ------------------------------------------------------------------------------------------------- +// event_music_first_pattern() +// +// Picks the first pattern to play, based on whether the battle has started. Delay start +// by PATTERN_DELAY +// +void event_music_first_pattern() +{ + if ( Event_music_inited == FALSE ) { + return; + } + + if ( Event_music_enabled == FALSE ) { + return; + } + + if ( Event_music_level_inited == FALSE ) { + event_music_level_init(); + } + + if ( Event_music_level_inited == FALSE ) { + return; + } + + if ( Event_music_begun == TRUE ) { + return; + } + + if ( Current_pattern != -1 ) { + if ( audiostream_is_playing(Patterns[Current_pattern].handle) ) + audiostream_stop( Patterns[Current_pattern].handle ); + } + + Pattern_timer_id = 2000; // start music delay + + Event_music_begun = FALSE; + if ( Event_Music_battle_started == TRUE ) { + Current_pattern = SONG_BTTL_1; + } + else { + Current_pattern = SONG_NRML_1; + } +} + +// ------------------------------------------------------------------------------------------------- +// event_music_level_close() +// +// Called at the end of each mission (level). Stops any playing patterns by fading them out. +// +void event_music_level_close() +{ + int i; + + if ( Event_music_level_inited == FALSE ) + return; + + if ( Current_soundtrack_num >= 0 && Current_soundtrack_num < MAX_SOUNDTRACKS ) { + SOUNDTRACK_INFO *strack; + + Assert( Current_soundtrack_num >= 0 && Current_soundtrack_num < MAX_SOUNDTRACKS ); + strack = &Soundtracks[Current_soundtrack_num]; + + // close the pattern files + for ( i = 0; i < strack->num_patterns; i++ ) { + if ( i == Current_pattern ) { + if ( audiostream_is_playing(Patterns[Current_pattern].handle) ) + audiostream_close_file( Patterns[i].handle ); + else + audiostream_close_file( Patterns[i].handle, 0 ); + } + else + audiostream_close_file( Patterns[i].handle, 0 ); + } + } else { + // close em all down then + audiostream_close_all(0); + } + + Current_pattern = -1; + Event_music_level_inited = FALSE; + Event_Music_battle_started = FALSE; + Event_music_enabled = 0; + Event_music_begun = FALSE; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_battle_start() +// +// Start the battle music. If the music is already started before, do nothing. +// +int event_music_battle_start() +{ + if ( !hostile_ships_present() ) { + return 0; + } + + // No special tracks in training. + if ( The_mission.game_type & MISSION_TYPE_TRAINING ) + return -1; + + // Check to see if we've already started off the battle song + if ( Event_Music_battle_started == 1 ) { + return 0; + } + + if ( Event_music_enabled == FALSE ) + return -1; + + if ( Event_music_level_inited == FALSE ) + return -1; + + if ( Current_pattern == SONG_BTTL_1 ) + return 0; // already playing + + if ( Current_pattern == SONG_DEAD_1 ) + return 0; // death is the last song to play + + if ( Current_pattern != -1 ) { + Patterns[Current_pattern].next_pattern = SONG_BTTL_1; + Patterns[Current_pattern].force_pattern = TRUE; + } + + Event_Music_battle_started = 1; // keep track of this state though, need on restore + Battle_over_timestamp = timestamp(BATTLE_CHECK_INTERVAL); + + return 0; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_enemy_arrival() +// +// An enemy has arrived, play an enemy arrival pattern. +// +int event_music_enemy_arrival() +{ + if ( Event_music_enabled == FALSE ) { + return -1; + } + + if ( Event_music_level_inited == FALSE ) { + return -1; + } + + int next_pattern; + if ( Event_Music_battle_started == TRUE ) { + next_pattern = SONG_EARV_2; + } + else { + next_pattern = SONG_EARV_1; + } + + if ( Current_pattern == next_pattern ) + return 0; // already playing + + if ( Current_pattern == SONG_DEAD_1 ) + return 0; // death is the last song to play + + if ( (Current_pattern == SONG_VICT_1) || (Current_pattern == SONG_VICT_1) ) + return 0; + + if ( Patterns[Current_pattern].next_pattern != Patterns[Current_pattern].default_next_pattern ) + return 0; // don't squash a pending pattern + + Num_enemy_arrivals++; + + // AL 7-25-99: If hull is less than 70% then switch to battle 2 or 3, otherwise switch to 1 or 2 + bool play_intense_battle_music = false; + if (Player_obj != NULL && Player_ship != NULL) { + Assert(Player_ship->ship_info_index >= 0); + Assert(Ship_info[Player_ship->ship_info_index].initial_hull_strength != 0); + float integrity = Player_obj->hull_strength / Ship_info[Player_ship->ship_info_index].initial_hull_strength; + if (integrity < HULL_VALUE_TO_PLAY_INTENSE_BATTLE_MUSIC) { + play_intense_battle_music = true; + } + } + + if (play_intense_battle_music == true) { + if (Current_pattern == SONG_BTTL_2) { + Patterns[next_pattern].next_pattern = SONG_BTTL_3; + } else { + Patterns[next_pattern].next_pattern = SONG_BTTL_2; + } + } else { + if (Current_pattern == SONG_BTTL_1) { + Patterns[next_pattern].next_pattern = SONG_BTTL_2; + } else if (Current_pattern == SONG_BTTL_2) { + Patterns[next_pattern].next_pattern = SONG_BTTL_3; + } else { + Patterns[next_pattern].next_pattern = SONG_BTTL_1; + } + } + + /* + // AL 11-03-97: + // Alternate between BTTL_2 and BTTL_3 following enemy arrivals + if ( Num_enemy_arrivals & 1 ) { + Patterns[next_pattern].next_pattern = SONG_BTTL_2; + } else { + if ( Patterns[SONG_BTTL_3].handle != -1 ) { + Patterns[next_pattern].next_pattern = SONG_BTTL_3; + } else { + Patterns[next_pattern].next_pattern = SONG_BTTL_2; + } + } + */ + + if ( Current_pattern != -1 ) { + Patterns[Current_pattern].next_pattern = next_pattern; + Patterns[Current_pattern].force_pattern = TRUE; + } + + Battle_over_timestamp = timestamp(BATTLE_CHECK_INTERVAL); + + return 0; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_friendly_arrival() +// +// An friendly has arrived, play a friendly arrival pattern. +// +int event_music_friendly_arrival() +{ + if ( Event_music_enabled == FALSE ) + return -1; + + if ( Event_music_level_inited == FALSE ) + return -1; + + if (timestamp_elapsed(Next_arrival_timestamp) == false) { + return 0; + } + + int next_pattern; + if ( Event_Music_battle_started == TRUE ) { + next_pattern = SONG_AARV_2; + } + else { + next_pattern = SONG_AARV_1; + } + + if ( Current_pattern == next_pattern ) + return 0; // already playing + + if ( Current_pattern == SONG_DEAD_1 ) + return 0; // death is the last song to play + + if ( (Current_pattern == SONG_VICT_1) || (Current_pattern == SONG_VICT_1) ) + return 0; + + if ( Patterns[Current_pattern].next_pattern != Patterns[Current_pattern].default_next_pattern ) + return 0; // don't squash a pending pattern + + // After the second friendly arrival, default to SONG_BTTL_3 + Num_friendly_arrivals++; + + if ( Current_pattern != -1 ) { + // AL 06-24-99: always overlay allied arrivals + /* + if (next_pattern == SONG_AARV_1) { + Patterns[Current_pattern].next_pattern = next_pattern; + Patterns[Current_pattern].force_pattern = TRUE; + } else { + */ + Assert(Patterns[SONG_AARV_1].handle >= 0 ); + audiostream_play(Patterns[SONG_AARV_1].handle, Master_event_music_volume, 0); // no looping + audiostream_set_byte_cutoff(Patterns[SONG_AARV_1].handle, fl2i(Patterns[SONG_AARV_1].num_measures * Patterns[SONG_AARV_1].bytes_per_measure) ); + //} + } + + Next_arrival_timestamp = timestamp(ARRIVAL_INTERVAL_TIMESTAMP); + + Battle_over_timestamp = timestamp(BATTLE_CHECK_INTERVAL); + + return 0; +} + +// Play arrival music keyed to team "team". +void event_music_arrival(int team) +{ + // No friendly arrival music in a training mission. + if ( The_mission.game_type & MISSION_TYPE_TRAINING ) + return; + + if ( Player_ship->team == team ) { + event_music_friendly_arrival(); + } else { + event_music_enemy_arrival(); + } +} + +// ------------------------------------------------------------------------------------------------- +// event_music_primary_goals_met() +// +// A primary goal has failed +// +int event_music_primary_goal_failed() +{ + int next_pattern; + + // No special tracks in training. + if ( The_mission.game_type & MISSION_TYPE_TRAINING ) + return -1; + + if ( Event_music_enabled == FALSE ) + return -1; + + if ( Event_music_level_inited == FALSE ) + return -1; + + if ( Current_pattern == SONG_DEAD_1 ) + return 0; // death is the last song to play + + if ( Patterns[SONG_FAIL_1].handle < 0 ) // can't play if music file doesn't exist + return 0; + + if ( hostile_ships_present() ) { + next_pattern = SONG_BTTL_1; + } + else { + next_pattern = SONG_NRML_1; + Event_Music_battle_started = 0; + } + + if ( Current_pattern != -1 ) { + Patterns[Current_pattern].next_pattern = next_pattern; + Patterns[Current_pattern].force_pattern = TRUE; + } + + return 0; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_primary_goals_met() +// +// A goal has been achieved, play the appropriate victory music. +// +int event_music_primary_goals_met() +{ + int next_pattern = SONG_VICT_1; + + // No special tracks in training. + if ( The_mission.game_type & MISSION_TYPE_TRAINING ) + return -1; + + if ( Event_music_enabled == FALSE ) + return -1; + + if ( Event_music_level_inited == FALSE ) + return -1; + + if ( (Current_pattern == SONG_VICT_1) || (Current_pattern == SONG_VICT_2) ) + return 0; // already playing + + if ( Current_pattern == SONG_DEAD_1 ) + return 0; // death is the last song to play + + if ( hostile_ships_present() ) { + Patterns[SONG_VICT_1].next_pattern = SONG_BTTL_1; + } + else { + Patterns[SONG_VICT_1].next_pattern = SONG_VICT_2; + Event_Music_battle_started = 0; + + // If the mission goals aren't met (or there are no goals), or if victory 2 music has already played, then go + // to the next default track + if ( !mission_goals_met() || Victory2_music_played || (Num_goals == 0)) { + Patterns[next_pattern].next_pattern = Patterns[next_pattern].default_next_pattern; + } else { + Victory2_music_played = 1; + } + } + + if ( Current_pattern != -1 ) { + Patterns[Current_pattern].next_pattern = next_pattern; + Patterns[Current_pattern].force_pattern = TRUE; + } + + return 0; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_player_death() +// +// The player has died, play death pattern. +// +int event_music_player_death() +{ + if ( Event_music_enabled == FALSE ) + return -1; + + if ( Event_music_level_inited == FALSE ) + return -1; + + if ( Current_pattern == SONG_DEAD_1 ) + return 0; // already playing + + if ( Current_pattern != -1 ) { + Patterns[Current_pattern].next_pattern = SONG_DEAD_1; + Patterns[Current_pattern].force_pattern = TRUE; + } + + return 0; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_player_respawn() +// +// Player has respawned (multiplayer only) +// +int event_music_player_respawn() +{ + if ( Event_music_enabled == FALSE ) + return -1; + + if ( Event_music_level_inited == FALSE ) + return -1; + +// Assert(Current_pattern == SONG_DEAD_1); + + Event_Music_battle_started = 0; + Patterns[Current_pattern].next_pattern = SONG_NRML_1; + Patterns[Current_pattern].force_pattern = TRUE; + + return 0; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_player_respawn_as_observer() +// +// Player has respawned (multiplayer only) +// +int event_music_player_respawn_as_observer() +{ + if ( Event_music_enabled == FALSE ) + return -1; + + if ( Event_music_level_inited == FALSE ) + return -1; + + if ( Current_pattern >= 0 ) { + if ( audiostream_is_playing(Patterns[Current_pattern].handle) ) { + audiostream_stop(Patterns[Current_pattern].handle); + Current_pattern = -1; + } + } + + return 0; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_parse_musictbl() will parse the music.tbl file, and set up the Mission_songs[] +// array +// +void event_music_parse_musictbl() +{ + char fname[MAX_FILENAME_LEN]; + char line_buf[128]; + int rval; + + int num_patterns = 0; + + Num_music_files = 0; + Num_soundtracks = 0; // Global + event_music_reset_choices(); + + if ((rval = setjmp(parse_abort)) != 0) { + Error(LOCATION, "Unable to parse music.tbl! Code = %i.\n", rval); + + } else { + // open localization + lcl_ext_open(); + + read_file_text("music.tbl"); + reset_parse(); + + // Loop through all the sound-tracks + while (required_string_either("#Menu Music Start","#SoundTrack Start")) { + Assert(Num_soundtracks < MAX_SOUNDTRACKS); + required_string("#SoundTrack Start"); + required_string("$SoundTrack Name:"); + stuff_string(Soundtracks[Num_soundtracks].name, F_NAME, NULL); + while (required_string_either("#SoundTrack End","$Name:")) { + Assert( num_patterns < MAX_PATTERNS ); + required_string("$Name:"); + stuff_string(line_buf, F_NAME, NULL); + + // line_buf holds 3 fields: filename, num measures, bytes per measure + + char *token; + int count = 0; + token = strtok( line_buf, NOX(" ,\t")); + strcpy(fname, token); + while ( token != NULL ) { + token = strtok( NULL, NOX(" ,\t") ); + if ( token == NULL ) { + Assert(count == 2 ); + break; + } + + if ( count == 0 ) { + Pattern_num_measures[Num_soundtracks][num_patterns] = (float)atof(token); + + } else { + Pattern_bytes_per_measure[Num_soundtracks][num_patterns] = atoi(token); + } + + count++; + } // end while + + // convert from samples per measure to bytes per measure + Pattern_bytes_per_measure[Num_soundtracks][num_patterns] *= 2; + strcpy(Soundtracks[Num_soundtracks].pattern_fnames[num_patterns], fname); + num_patterns++; + } + + required_string("#SoundTrack End"); + Soundtracks[Num_soundtracks].num_patterns = num_patterns; + Num_soundtracks++; + num_patterns = 0; + } + + // Parse the menu music section + required_string("#Menu Music Start"); + while (required_string_either("#Menu Music End","$Name:")) { + Assert( Num_music_files < MAX_SPOOLED_MUSIC ); + + required_string("$Name:"); + stuff_string(fname, F_PATHNAME, NULL); + Assert( strlen(fname) < (NAME_LENGTH-1) ); + strcpy( Spooled_music[Num_music_files].name, fname ); + + required_string("$Filename:"); + stuff_string(fname, F_PATHNAME, NULL); + if ( stricmp(fname, NOX("none.wav")) ) { + Assert( strlen(fname) < (MAX_FILENAME_LEN-1) ); + strcpy( Spooled_music[Num_music_files].filename, fname ); + } + + Num_music_files++; + } + + required_string("#Menu Music End"); + + // close localization + lcl_ext_close(); + } +} + +// ------------------------------------------------------------------------------------------------- +// event_music_change_pattern() +// +// Force a particular pattern to play. This is used for debugging purposes. +// +void event_music_change_pattern(int new_pattern) +{ + if ( Event_music_enabled == FALSE ) { + nprintf(("EVENTMUSIC", "EVENTMUSIC ==> Requested a song switch when event music is not enabled\n")); + return; + } + + if ( Event_music_level_inited == FALSE ) { + nprintf(("EVENTMUSIC", "EVENTMUSIC ==> Event music is not enabled\n")); + return; + } + + if ( Current_pattern == new_pattern ) + return; // already playing + + if ( Current_pattern != -1 ) { + Patterns[Current_pattern].next_pattern = new_pattern; + Patterns[Current_pattern].force_pattern = TRUE; + } +} + +// ------------------------------------------------------------------------------------------------- +// event_music_return_current_pattern() +// +// Simply return what the current pattern being played is. Don't want to make global. +// +int event_music_return_current_pattern() +{ + return Current_pattern; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_disable() +// +// Stop any patterns that are playing, and prevent any further patterns from playing (ie +// set Event_music_enabled = FALSE). We don't uninit event music, since it might be toggled +// back on this level. +// +void event_music_disable() +{ + if ( Event_music_level_inited == FALSE ) + return; + + if ( Event_music_enabled == FALSE ) + return; + + if (Current_pattern == -1) + return; + + Assert( Current_pattern >= 0 && Current_pattern < MAX_PATTERNS ); + if ( audiostream_is_playing(Patterns[Current_pattern].handle) ) { + audiostream_stop(Patterns[Current_pattern].handle); // stop current and rewind + } + + Event_music_begun = FALSE; + Event_music_enabled = FALSE; + Current_pattern = -1; +} + +// ------------------------------------------------------------------------------------------------- +// event_music_enable() +// +// Init the event music (ie load the patterns) if required. Set up the first song to play, and +// set Event_music_enabled = TRUE to allow patterns to play. +// +void event_music_enable() +{ + if ( Event_music_enabled == TRUE ) + return; + + Event_music_enabled = TRUE; + + if ( Event_music_level_inited == FALSE ) { + event_music_level_init(); + // start the first pattern to play (delayed) + if ( Game_mode & GM_IN_MISSION ) { + event_music_first_pattern(); + } + } + else { + // start a new pattern + Event_music_begun = FALSE; + Pattern_timer_id = timestamp(150); + event_music_start_default(); + } +} + +// ------------------------------------------------------------------------------------------------- +// event_music_start_default() +// +// Start playing a default track, based on how far the mission has progressed +// +void event_music_start_default() +{ + int next_pattern; + + if ( Event_Music_battle_started == TRUE ) { + if ( hostile_ships_present() ) { + next_pattern = SONG_BTTL_1; + } + else { + Event_Music_battle_started = FALSE; + next_pattern = SONG_NRML_1; + } + } + else + next_pattern = SONG_NRML_1; + + if ( Current_pattern == -1 ) { + Current_pattern = next_pattern; + } + else { + Patterns[Current_pattern].next_pattern = next_pattern; + Patterns[Current_pattern].force_pattern = TRUE; + } + +} + +// ------------------------------------------------------------------------------------------------- +// event_music_pause() +// +// Stop any playing pattern, but don't rewind. +// +void event_music_pause() +{ + if ( Event_music_enabled == FALSE ) { + nprintf(("EVENTMUSIC", "EVENTMUSIC ==> Requested a song switch when event music is not enabled\n")); + return; + } + + if ( Event_music_level_inited == FALSE ) { + nprintf(("EVENTMUSIC", "EVENTMUSIC ==> Event music is not enabled\n")); + return; + } + + if (Current_pattern == -1) + return; + + Assert( Current_pattern >= 0 && Current_pattern < MAX_PATTERNS ); + if ( audiostream_is_playing(Patterns[Current_pattern].handle) ) { + audiostream_stop(Patterns[Current_pattern].handle, 0); // stop current and don't rewind + } +} + +// ------------------------------------------------------------------------------------------------- +// event_music_unpause() +// +// Start the Current_pattern if it is paused. +// +void event_music_unpause() +{ + if ( Event_music_enabled == FALSE ) { + nprintf(("EVENTMUSIC", "EVENTMUSIC ==> Requested a song switch when event music is not enabled\n")); + return; + } + + if ( Event_music_level_inited == FALSE ) { + nprintf(("EVENTMUSIC", "EVENTMUSIC ==> Event music is not enabled\n")); + return; + } + + if (Current_pattern == -1) + return; + + Assert( Current_pattern >= 0 && Current_pattern < MAX_PATTERNS ); + if ( audiostream_is_paused(Patterns[Current_pattern].handle) == TRUE ) { + audiostream_play(Patterns[Current_pattern].handle, Master_event_music_volume, 0); // no looping + audiostream_set_byte_cutoff(Patterns[Current_pattern].handle, fl2i(Patterns[Current_pattern].num_measures * Patterns[Current_pattern].bytes_per_measure) ); + } +} + +// ------------------------------------------------------------------------------------------------- +// event_music_set_volume_all() +// +// Set the volume of the event driven music. Used when using the game-wide music volume is changed +// by the user. +// +void event_music_set_volume_all(float volume) +{ + audiostream_set_volume_all(volume, ASF_EVENTMUSIC); +} + +// ---------------------------------------------------------------------- +// hostile_ships_present() +// +// Determine if there are any non-friendly ships in existance +// +// returns: 1 => there are non-friendly ships in existance +// 0 => any ships in existance are friendly +int hostile_ships_present() +{ + ship *shipp; + ship_obj *so; + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + shipp = &Ships[Objects[so->objnum].instance]; + + // check if ship if enemy ship + if ( (shipp->team == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) + continue; + + // check if ship is threatening + if ( (shipp->flags & (SF_DISABLED|SF_DYING|SF_DEPARTING)) || (ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE)) ) + continue; + + // check if ship is flyable + if ( Ship_info[shipp->ship_info_index].flags & SIF_NOT_FLYABLE ) { + continue; + } + + // check if ship is visible by player's team + if ( hud_target_invalid_awacs(&Objects[so->objnum]) == 1 ) { + continue; + } + + return 1; + } + return 0; +} + +// ---------------------------------------------------------------------- +// hostile_ships_to_arrive() +// +// Determine if there are any non-friendly ships yet to arrive +// +// NOTE: neutral ships are considered hostile for the purpose of event music +// +int hostile_ships_to_arrive() +{ + p_object *p_objp; + + p_objp = GET_FIRST(&ship_arrival_list); + while( p_objp != END_OF_LIST(&ship_arrival_list) ) { + if ( (p_objp->team != Player_ship->team) && !(p_objp->flags & P_SF_CANNOT_ARRIVE) ) { + return 1; + } + p_objp = GET_NEXT(p_objp); + } + return 0; +} + +// ---------------------------------------------------------------- +// event_music_get_info() +// +// Return information about the event music in the buffer outbuf +// NOTE: callers to this function are advised to allocate a 256 byte buffer +void event_music_get_info(char *outbuf) +{ + if ( Event_music_enabled == FALSE || Event_music_level_inited == FALSE || Current_pattern == -1 ) { + sprintf(outbuf,XSTR( "Event music is not playing", 213)); + } + else { + sprintf(outbuf,XSTR( "soundtrack: %s [%s]", 214), Soundtracks[Current_soundtrack_num].name, Pattern_description[Current_pattern]); + } +} + +// ---------------------------------------------------------------- +// event_music_next_soundtrack() +// +// input: delta => 1 or -1, depending if you want to go to next or previous song +// +// returns: New soundtrack number if successfully changed, otherwise return -1 +// +int event_music_next_soundtrack(int delta) +{ + int new_soundtrack; + + if ( Event_music_enabled == FALSE || Event_music_level_inited == FALSE ) { + return -1; + } + + new_soundtrack = Current_soundtrack_num + delta; + if ( new_soundtrack >= Num_soundtracks ) + new_soundtrack = 0; + + event_music_level_close(); + event_music_level_init(new_soundtrack); + + return Current_soundtrack_num; +} + +// ---------------------------------------------------------------- +// event_music_get_soundtrack_name() +// +// Return information about the event music in the buffer outbuf +// NOTE: callers to this function are advised to allocate a NAME_LENGTH buffer +void event_music_get_soundtrack_name(char *outbuf) +{ + if ( Event_music_enabled == FALSE || Event_music_level_inited == FALSE ) { + strcpy(outbuf, XSTR( "Event music is not playing", 213)); + } + else { + sprintf(outbuf, Soundtracks[Current_soundtrack_num].name); + } +} + +// set the current soundtrack based on name +void event_music_set_soundtrack(char *name) +{ + int i; + + // find the correct index for the event music + for ( i = 0; i < Num_soundtracks; i++ ) { + if ( !stricmp(name, Soundtracks[i].name) ) { + Current_soundtrack_num = i; + break; + } + } + + if ( i == Num_soundtracks ) { + Current_soundtrack_num = -1; + mprintf(("Current soundtrack set to -1 in event_music_set_soundtrack\n")); + } +} + +int event_music_get_spooled_music_index(char *name) +{ + // find the correct index for the event music + for ( int i = 0; i < Num_music_files; i++ ) { + if ( !stricmp(name, Spooled_music[i].name) ) { + return i; + } + } + + return -1; +} + +// set a score based on name +void event_music_set_score(int score_index, char *name) +{ + Assert(score_index < NUM_SCORES); + + // find the correct index for the event music + Mission_music[score_index] = event_music_get_spooled_music_index(name); +} + +// reset what sort of music is to be used for this mission +void event_music_reset_choices() +{ + Current_soundtrack_num = -1; + mprintf(("Current soundtrack set to -1 in event_music_reset_choices\n")); + Mission_music[SCORE_BRIEFING] = -1; + event_music_set_score(SCORE_DEBRIEF_SUCCESS, "Success"); + event_music_set_score(SCORE_DEBRIEF_AVERAGE, "Average"); + event_music_set_score(SCORE_DEBRIEF_FAIL, "Failure"); + //Mission_music[SCORE_DEBRIEF_SUCCESS] = MUSIC_DEBRIEF_SUCCESS_1; + //Mission_music[SCORE_DEBRIEF_FAIL] = MUSIC_DEBRIEF_FAIL_1; +} + +void event_music_hostile_ship_destroyed() +{ + Battle_over_timestamp = timestamp(BATTLE_CHECK_INTERVAL); +} + + +#pragma optimize("", on) diff --git a/src/gamesnd/gamesnd.cpp b/src/gamesnd/gamesnd.cpp new file mode 100644 index 0000000..c052cf3 --- /dev/null +++ b/src/gamesnd/gamesnd.cpp @@ -0,0 +1,414 @@ +/* + * $Logfile: /Freespace2/code/Gamesnd/GameSnd.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to keep track of which sound files go where + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 6 7/01/99 11:44a Dave + * Updated object sound system to allow multiple obj sounds per ship. + * Added hit-by-beam sound. Added killed by beam sound. + * + * 5 6/25/99 3:08p Dave + * Multiple flyby sounds. + * + * 4 5/23/99 8:11p Alanl + * Added support for EAX + * + * 3 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 39 5/05/98 4:49p Lawrance + * Put in code to authenticate A3D, improve A3D support + * + * 38 4/25/98 1:25p Lawrance + * Make function for playing generic error beep + * + * 37 4/18/98 9:12p Lawrance + * Added Aureal support. + * + * 36 3/29/98 12:56a Lawrance + * preload the warp in and explosions sounds before a mission. + * + * 35 3/25/98 6:10p Lawrance + * Work on DirectSound3D + * + * 34 2/22/98 2:48p John + * More String Externalization Classification + * + * 33 1/17/98 12:33p John + * Made the game_busy function be called a constant amount of times per + * level load, making the bar prediction easier. + * + * 32 1/17/98 12:14p John + * Added loading... bar to freespace. + * + * 31 1/11/98 11:14p Lawrance + * Preload sounds that we expect will get played. + * + * 30 12/24/97 8:54p Lawrance + * Integrating new popup code + * + * 29 12/19/97 3:44p Mike + * Fix parse code. Would improperly read a number through a comma. Lots + * of ships.tbl problems. + * + * 28 12/01/97 5:25p Hoffoss + * Routed interface sound playing through a special function that will + * only allow one instance of the sound to play at a time, avoiding + * over-mixing problems. + * + * 27 11/20/97 1:06a Lawrance + * Add Master_voice_volume, make voices play back at correctly scaled + * volumes + * + * 26 10/17/97 1:36p Lawrance + * load/unload interface sounds + * + * 25 10/14/97 11:35p Lawrance + * change snd_load parameters + * + * 24 7/05/97 1:46p Lawrance + * improve robustness of gameplay and interface sound loading/unloading + * + * 23 6/09/97 11:50p Lawrance + * integrating DirectSound3D + * + * 22 6/08/97 5:59p Lawrance + * flag sounds as 3D + * + * 21 6/05/97 11:25a Lawrance + * use sound signatures to ensure correct sound is loaded + * + * 20 6/05/97 1:07a Lawrance + * changes to support sound interface + * + * 19 6/04/97 1:18p Lawrance + * added hooks for shield impacts + * + * 18 6/02/97 1:50p Lawrance + * supporting new format of sounds in table + * + * 17 5/14/97 9:54a Lawrance + * supporting mission-specific briefing music + * + * 16 5/08/97 1:56p Lawrance + * supporting ship-specific engine sounds + * + * 15 5/06/97 9:36a Lawrance + * added support for min and max distances for 3d sounds + * + * 14 4/23/97 5:19p Lawrance + * split up misc sounds into: gamewide, ingame, and interface + * + * 13 4/20/97 11:48a Lawrance + * added array of filenames for misc sounds. Will be useful if we want to + * unload then re-load sounds + * + * 12 4/20/97 11:19a Lawrance + * sndman_ interface obsolete. Using snd_ functions to load, play, and + * manage static sound fx + * + * 11 4/18/97 4:31p Mike + * Add support for default volume levels. + * + * 10 3/20/97 11:04a Lawrance + * using incorrect constant when loading music filenames + * + * 9 3/19/97 5:53p Lawrance + * integrating new Misc_sounds[] array (replaces old Game_sounds + * structure) + * + * 8 3/17/97 3:47p Mike + * Homing missile lock sound. + * More on AI ships firing missiles. + * + * 7 3/10/97 8:54a Lawrance + * added gamesnd_init_looping_sounds() + * + * 6 2/28/97 8:41a Lawrance + * added afterburner engage and burn sounds + * + * 5 2/14/97 12:37a Lawrance + * added hooks to play docking/undocking sounds + * + * 4 2/13/97 12:03p Lawrance + * hooked in throttle sounds + * + * 3 2/05/97 10:35a Lawrance + * supporting spooled music at menus, briefings, credits etc. + * + * 2 1/20/97 7:58p John + * Fixed some link errors with testcode. + * + * 1 1/20/97 7:08p John + * + * $NoKeywords: $ + */ + +#include "pstypes.h" +#include "gamesnd.h" +#include "sound.h" +#include "parselo.h" +#include "localize.h" + +// Global array that holds data about the gameplay sound effects. +game_snd Snds[MAX_GAME_SOUNDS]; + +// Global array that holds data about the interface sound effects. +game_snd Snds_iface[MAX_INTERFACE_SOUNDS]; +int Snds_iface_handle[MAX_INTERFACE_SOUNDS]; + +// flyby sounds - 2 for each species (fighter and bomber flybys) +game_snd Snds_flyby[MAX_SPECIES_NAMES][2]; + + +void gamesnd_play_iface(int n) +{ + if (Snds_iface_handle[n] >= 0) + snd_stop(Snds_iface_handle[n]); + + Snds_iface_handle[n] = snd_play(&Snds_iface[n]); +} + +// load in sounds that we expect will get played +// +// The method currently used is to load all those sounds that have the hardware flag +// set. This works well since we don't want to try and load hardware sounds in on the +// fly (too slow). +void gamesnd_preload_common_sounds() +{ + int i; + game_snd *gs; + + for ( i = 0; i < MAX_GAME_SOUNDS; i++ ) { + gs = &Snds[i]; + if ( gs->filename[0] != 0 && stricmp(gs->filename, NOX("none.wav")) ) { + if ( gs->preload ) { + gs->id = snd_load(gs); + } + } + game_busy(); // Animate loading cursor... does nothing if loading screen not active. + } +} + +// ------------------------------------------------------------------------------------------------- +// gamesnd_load_gameplay_sounds() +// +// Load the ingame sounds into memory +// +void gamesnd_load_gameplay_sounds() +{ + int i; + game_snd *gs; + + for ( i = 0; i < MAX_GAME_SOUNDS; i++ ) { + gs = &Snds[i]; + if ( gs->filename[0] != 0 && stricmp(gs->filename, NOX("none.wav")) ) { + gs->id = snd_load(gs); + } + } +} + +// ------------------------------------------------------------------------------------------------- +// gamesnd_unload_gameplay_sounds() +// +// Unload the ingame sounds from memory +// +void gamesnd_unload_gameplay_sounds() +{ + int i; + game_snd *gs; + + for ( i = 0; i < MAX_GAME_SOUNDS; i++ ) { + gs = &Snds[i]; + if ( gs->id != -1 ) { + snd_unload( gs->id ); + gs->id = -1; + } + } +} + +// ------------------------------------------------------------------------------------------------- +// gamesnd_load_interface_sounds() +// +// Load the interface sounds into memory +// +void gamesnd_load_interface_sounds() +{ + int i; + game_snd *gs; + + for ( i = 0; i < MAX_INTERFACE_SOUNDS; i++ ) { + gs = &Snds_iface[i]; + if ( gs->filename[0] != 0 && stricmp(gs->filename, NOX("none.wav")) ) { + gs->id = snd_load(gs); + } + } +} + +// ------------------------------------------------------------------------------------------------- +// gamesnd_unload_interface_sounds() +// +// Unload the interface sounds from memory +// +void gamesnd_unload_interface_sounds() +{ + int i; + game_snd *gs; + + for ( i = 0; i < MAX_INTERFACE_SOUNDS; i++ ) { + gs = &Snds_iface[i]; + if ( gs->id != -1 ) { + snd_unload( gs->id ); + gs->id = -1; + gs->id_sig = -1; + } + } +} + +// ------------------------------------------------------------------------------------------------- +// gamesnd_parse_line() +// +// Parse a sound effect line +// +void gamesnd_parse_line(game_snd *gs, char *tag) +{ + int is_3d; + + required_string(tag); + stuff_int(&gs->sig); + stuff_string(gs->filename, F_NAME, ","); + if ( !stricmp(gs->filename,NOX("empty")) ) { + gs->filename[0] = 0; + advance_to_eoln(NULL); + return; + } + Mp++; + stuff_int(&gs->preload); + stuff_float(&gs->default_volume); + stuff_int(&is_3d); + if ( is_3d ) { + gs->flags |= GAME_SND_USE_DS3D; + stuff_int(&gs->min); + stuff_int(&gs->max); + } + advance_to_eoln(NULL); +} + +// ------------------------------------------------------------------------------------------------- +// gamesnd_parse_soundstbl() will parse the sounds.tbl file, and load the specified sounds. +// +// +void gamesnd_parse_soundstbl() +{ + int rval; + int num_game_sounds = 0; + int num_iface_sounds = 0; + + // open localization + lcl_ext_open(); + + gamesnd_init_sounds(); + + if ((rval = setjmp(parse_abort)) != 0) { + Error(LOCATION, "Unable to parse sounds.tbl! Code = %i.\n", rval); + } + else { + read_file_text("sounds.tbl"); + reset_parse(); + } + + // Parse the gameplay sounds section + required_string("#Game Sounds Start"); + while (required_string_either("#Game Sounds End","$Name:")) { + Assert( num_game_sounds < MAX_GAME_SOUNDS); + gamesnd_parse_line( &Snds[num_game_sounds], "$Name:" ); + num_game_sounds++; + } + required_string("#Game Sounds End"); + + // Parse the interface sounds section + required_string("#Interface Sounds Start"); + while (required_string_either("#Interface Sounds End","$Name:")) { + Assert( num_iface_sounds < MAX_INTERFACE_SOUNDS); + gamesnd_parse_line(&Snds_iface[num_iface_sounds], "$Name:"); + num_iface_sounds++; + } + required_string("#Interface Sounds End"); + + // parse flyby sound section + required_string("#Flyby Sounds Start"); + + // read 2 terran sounds + gamesnd_parse_line(&Snds_flyby[SPECIES_TERRAN][0], "$Terran:"); + gamesnd_parse_line(&Snds_flyby[SPECIES_TERRAN][1], "$Terran:"); + + // 2 vasudan sounds + gamesnd_parse_line(&Snds_flyby[SPECIES_VASUDAN][0], "$Vasudan:"); + gamesnd_parse_line(&Snds_flyby[SPECIES_VASUDAN][1], "$Vasudan:"); + + gamesnd_parse_line(&Snds_flyby[SPECIES_SHIVAN][0], "$Shivan:"); + gamesnd_parse_line(&Snds_flyby[SPECIES_SHIVAN][1], "$Shivan:"); + + required_string("#Flyby Sounds End"); + + // close localization + lcl_ext_close(); +} + + +// ------------------------------------------------------------------------------------------------- +// gamesnd_init_struct() +// +void gamesnd_init_struct(game_snd *gs) +{ + gs->filename[0] = 0; + gs->id = -1; + gs->id_sig = -1; +// gs->is_3d = 0; +// gs->use_ds3d = 0; + gs->flags = 0; +} + +// ------------------------------------------------------------------------------------------------- +// gamesnd_init_sounds() will initialize the Snds[] and Snds_iface[] arrays +// +void gamesnd_init_sounds() +{ + int i; + + // init the gameplay sounds + for ( i = 0; i < MAX_GAME_SOUNDS; i++ ) { + gamesnd_init_struct(&Snds[i]); + } + + // init the interface sounds + for ( i = 0; i < MAX_INTERFACE_SOUNDS; i++ ) { + gamesnd_init_struct(&Snds_iface[i]); + Snds_iface_handle[i] = -1; + } +} + +// callback function for the UI code to call when the mouse first goes over a button. +void common_play_highlight_sound() +{ + gamesnd_play_iface(SND_USER_OVER); +} + +void gamesnd_play_error_beep() +{ + gamesnd_play_iface(SND_GENERAL_FAIL); +} \ No newline at end of file diff --git a/src/glide/glide.cpp b/src/glide/glide.cpp new file mode 100644 index 0000000..0c80c16 --- /dev/null +++ b/src/glide/glide.cpp @@ -0,0 +1,455 @@ +/* + * $Logfile: /Freespace2/code/glide/Glide.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for dynamically loading glide libraries + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 11/11/98 5:37p Dave + * Checkin for multiplayer testing. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 4 3/18/98 1:57p John + * Made non-perspective correct textures do 2d clipping before rendering. + * + * 3 2/21/98 11:58a John + * Put in some stuff to externalize strings + * + * 2 2/19/98 6:13p John + * Made Glide do texturing & zbuffering. + * + * 1 1/05/98 10:38a John + * + * $NoKeywords: $ + */ + +#define __MSC__ +#include +#include + +#include "glide.h" +#include "pstypes.h" + +static HMODULE Glide_lib_handle; + +void vglide_close() +{ + if ( Glide_lib_handle ) { + FreeLibrary(Glide_lib_handle); + Glide_lib_handle = NULL; + } +} + +// Returns 1 if Glide found, else 0 if not +int vglide_init() +{ + HMODULE lib_handle; + Glide_lib_handle = LoadLibrary(NOX("glide2x.dll")); + if (Glide_lib_handle == NULL) { + //No lib so no glide + return 0; + } + + lib_handle = Glide_lib_handle; + + //XSTR:OFF + + grDrawLine = (void (__stdcall*)(const GrVertex *, const GrVertex *))GetProcAddress(lib_handle, "_grDrawLine@8"); + grDrawPlanarPolygon = (void (__stdcall *)(int,const int [],const GrVertex []))GetProcAddress(lib_handle, "_grDrawPlanarPolygon@12"); + grDrawPlanarPolygonVertexList = (void (__stdcall *)(int,const GrVertex []))GetProcAddress(lib_handle, "_grDrawPlanarPolygonVertexList@8"); + grDrawPoint = (void (__stdcall *)(const GrVertex *))GetProcAddress(lib_handle, "_grDrawPoint@4"); + grDrawPolygon = (void (__stdcall *)(int,const int [],const GrVertex []))GetProcAddress(lib_handle, "_grDrawPolygon@12"); + grDrawPolygonVertexList = (void (__stdcall *)(int,const GrVertex []))GetProcAddress(lib_handle, "_grDrawPolygonVertexList@8"); + grDrawTriangle = (void (__stdcall *)(const GrVertex *,const GrVertex *,const GrVertex *))GetProcAddress(lib_handle, "_grDrawTriangle@12"); + guDrawTriangleWithClip = (void (__stdcall *)(const GrVertex *,const GrVertex *,const GrVertex *))GetProcAddress(lib_handle, "_guDrawTriangleWithClip@12"); + grBufferClear = (void (__stdcall *)(unsigned long,unsigned char,unsigned short))GetProcAddress(lib_handle, "_grBufferClear@12"); + grBufferNumPending = GetProcAddress(lib_handle, "_grBufferNumPending@0"); + grBufferSwap = (void (__stdcall *)(int))GetProcAddress(lib_handle, "_grBufferSwap@4"); + grRenderBuffer = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grRenderBuffer@4"); + + grErrorSetCallback = (void (__stdcall *)(void (__cdecl *)(const char *,int)))GetProcAddress(lib_handle, "_grErrorSetCallback@4"); + grSstIdle = (void (__stdcall *)(void))GetProcAddress(lib_handle, "_grSstIdle@0"); + grSstVideoLine = (unsigned long (__stdcall *)(void))GetProcAddress(lib_handle, "_grSstVideoLine@0"); + grSstVRetraceOn = (int (__stdcall *)(void))GetProcAddress(lib_handle, "_grSstVRetraceOn@0"); + grSstIsBusy = (int (__stdcall *)(void))GetProcAddress(lib_handle, "_grSstIsBusy@0"); + grSstWinOpen = (int (__stdcall *)(unsigned long,long,long,long,long,int,int))GetProcAddress(lib_handle, "_grSstWinOpen@28"); + grSstWinClose = (void (__stdcall *)(void))GetProcAddress(lib_handle, "_grSstWinClose@0"); + grSstControl = (int (__stdcall *)(unsigned long))GetProcAddress(lib_handle, "_grSstControl@4"); + grSstQueryHardware = (int (__stdcall *)(GrHwConfiguration *))GetProcAddress(lib_handle, "_grSstQueryHardware@4"); + grSstQueryBoards = (int (__stdcall *)(GrHwConfiguration *))GetProcAddress(lib_handle, "_grSstQueryBoards@4"); + + grSstOrigin = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grSstOrigin@4"); + grSstSelect = (void (__stdcall *)(int))GetProcAddress(lib_handle, "_grSstSelect@4"); + grSstScreenHeight = (unsigned long (__stdcall *)(void))GetProcAddress(lib_handle, "_grSstScreenHeight@0"); + grSstScreenWidth = (unsigned long (__stdcall *)(void))GetProcAddress(lib_handle, "_grSstScreenWidth@0"); + grSstStatus = (unsigned long (__stdcall *)(void))GetProcAddress(lib_handle, "_grSstStatus@0"); + grSstPerfStats = (void (__stdcall *)(struct GrSstPerfStats_s *))GetProcAddress(lib_handle, "_grSstPerfStats@4"); + grSstResetPerfStats = (void (__stdcall *)(void))GetProcAddress(lib_handle, "_grSstResetPerfStats@0"); + grResetTriStats = (void (__stdcall *)(void))GetProcAddress(lib_handle, "_grResetTriStats@0"); + grTriStats = (void (__stdcall *)(unsigned long *,unsigned long *))GetProcAddress(lib_handle, "_grTriStats@8"); + grAlphaBlendFunction = (void (__stdcall *)(long,long,long,long))GetProcAddress(lib_handle, "_grAlphaBlendFunction@16"); + + grAlphaCombine = (void (__stdcall *)(long,long,long,long,int))GetProcAddress(lib_handle, "_grAlphaCombine@20"); + grAlphaControlsITRGBLighting = (void (__stdcall *)(int))GetProcAddress(lib_handle, "_grAlphaControlsITRGBLighting@4"); + grAlphaTestFunction = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grAlphaTestFunction@4"); + grAlphaTestReferenceValue = (void (__stdcall *)(unsigned char))GetProcAddress(lib_handle, "_grAlphaTestReferenceValue@4"); + grChromakeyMode = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grChromakeyMode@4"); + grChromakeyValue = (void (__stdcall *)(unsigned long))GetProcAddress(lib_handle, "_grChromakeyValue@4"); + grClipWindow = (void (__stdcall *)(unsigned long,unsigned long,unsigned long,unsigned long))GetProcAddress(lib_handle, "_grClipWindow@16"); + + grColorCombine = (void (__stdcall *)(long,long,long,long,int))GetProcAddress(lib_handle, "_grColorCombine@20"); + grColorMask = (void (__stdcall *)(int,int))GetProcAddress(lib_handle, "_grColorMask@8"); + grCullMode = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grCullMode@4"); + grConstantColorValue = (void (__stdcall *)(unsigned long))GetProcAddress(lib_handle, "_grConstantColorValue@4"); + grConstantColorValue4 = (void (__stdcall *)(float,float,float,float))GetProcAddress(lib_handle, "_grConstantColorValue4@16"); + grDepthBiasLevel = (void (__stdcall *)(short))GetProcAddress(lib_handle, "_grDepthBiasLevel@4"); + grDepthBufferFunction = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grDepthBufferFunction@4"); + grDepthBufferMode = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grDepthBufferMode@4"); + grDepthMask = (void (__stdcall *)(int))GetProcAddress(lib_handle, "_grDepthMask@4"); + grDisableAllEffects = (void (__stdcall *)(void))GetProcAddress(lib_handle, "_grDisableAllEffects@0"); + grDitherMode = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grDitherMode@4"); + + grFogColorValue = (void (__stdcall *)(unsigned long))GetProcAddress(lib_handle, "_grFogColorValue@4"); + grFogMode = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grFogMode@4"); + grFogTable = (void (__stdcall *)(const unsigned char []))GetProcAddress(lib_handle, "_grFogTable@4"); + grGammaCorrectionValue = (void (__stdcall *)(float))GetProcAddress(lib_handle, "_grGammaCorrectionValue@4"); + grSplash = (void (__stdcall *)(float,float,float,float,unsigned long))GetProcAddress(lib_handle, "_grSplash@20"); + + grTexCalcMemRequired = (unsigned long (__stdcall *)(long,long,long,long))GetProcAddress(lib_handle, "_grTexCalcMemRequired@16"); + grTexTextureMemRequired = (unsigned long (__stdcall *)(unsigned long,GrTexInfo *))GetProcAddress(lib_handle, "_grTexTextureMemRequired@8"); + grTexMinAddress = (unsigned long (__stdcall *)(long))GetProcAddress(lib_handle, "_grTexMinAddress@4"); + grTexMaxAddress = (unsigned long (__stdcall *)(long))GetProcAddress(lib_handle, "_grTexMaxAddress@4"); + grTexNCCTable = (void (__stdcall *)(long,unsigned long))GetProcAddress(lib_handle, "_grTexNCCTable@8"); + grTexSource = (void (__stdcall *)(long,unsigned long,unsigned long,GrTexInfo *))GetProcAddress(lib_handle, "_grTexSource@16"); + grTexClampMode = (void (__stdcall *)(long,long,long))GetProcAddress(lib_handle, "_grTexClampMode@12"); + grTexCombine = (void (__stdcall *)(long,long,long,long,long,int,int))GetProcAddress(lib_handle, "_grTexCombine@28"); + grTexCombineFunction = (void (__stdcall *)(long,long))GetProcAddress(lib_handle, "_grTexCombineFunction@8"); + grTexDetailControl = (void (__stdcall *)(long,int,unsigned char,float))GetProcAddress(lib_handle, "_grTexDetailControl@16"); + grTexFilterMode = (void (__stdcall *)(long,long,long))GetProcAddress(lib_handle, "_grTexFilterMode@12"); + grTexLodBiasValue = (void (__stdcall *)(long,float))GetProcAddress(lib_handle, "_grTexLodBiasValue@8"); + grTexDownloadMipMap = (void (__stdcall *)(long,unsigned long,unsigned long,GrTexInfo *))GetProcAddress(lib_handle, "_grTexDownloadMipMap@16"); + grTexDownloadMipMapLevel = (void (__stdcall *)(long,unsigned long,long,long,long,long,unsigned long,void *))GetProcAddress(lib_handle, "_grTexDownloadMipMapLevel@32"); + grTexDownloadMipMapLevelPartial = (void (__stdcall *)(long,unsigned long,long,long,long,long,unsigned long,void *,int,int))GetProcAddress(lib_handle, "_grTexDownloadMipMapLevelPartial@40"); + ConvertAndDownloadRle = (void (__stdcall *)(long,unsigned long,long,long,long,long,unsigned long,unsigned char *,long,unsigned long,unsigned long,unsigned long,unsigned long,unsigned long,unsigned long,unsigned short *))GetProcAddress(lib_handle, "_ConvertAndDownloadRle@64"); + + grCheckForRoom = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grCheckForRoom@4"); + grTexDownloadTable = (void (__stdcall *)(long,unsigned long,void *))GetProcAddress(lib_handle, "_grTexDownloadTable@12"); + grTexDownloadTablePartial = (void (__stdcall *)(long,unsigned long,void *,int,int))GetProcAddress(lib_handle, "_grTexDownloadTablePartial@20"); + grTexMipMapMode = (void (__stdcall *)(long,long,int))GetProcAddress(lib_handle, "_grTexMipMapMode@12"); + grTexMultibase = (void (__stdcall *)(long,int))GetProcAddress(lib_handle, "_grTexMultibase@8"); + grTexMultibaseAddress = (void (__stdcall *)(long,unsigned long,unsigned long,unsigned long,GrTexInfo *))GetProcAddress(lib_handle, "_grTexMultibaseAddress@20"); + + guTexAllocateMemory = (unsigned long (__stdcall *)(long,unsigned char,int,int,long,long,long,long,long,long,long,long,long,float,int))GetProcAddress(lib_handle, "_guTexAllocateMemory@60"); + guTexChangeAttributes = (int (__stdcall *)(unsigned long,int,int,long,long,long,long,long,long,long,long,long))GetProcAddress(lib_handle, "_guTexChangeAttributes@48"); + guTexCombineFunction = (void (__stdcall *)(long,long))GetProcAddress(lib_handle, "_guTexCombineFunction@8"); + guTexGetCurrentMipMap = (unsigned long (__stdcall *)(long))GetProcAddress(lib_handle, "_guTexGetCurrentMipMap@4"); + guTexGetMipMapInfo = (GrMipMapInfo *(__stdcall *)(unsigned long))GetProcAddress(lib_handle, "_guTexGetMipMapInfo@4"); + guTexMemQueryAvail = (unsigned long (__stdcall *)(long))GetProcAddress(lib_handle, "_guTexMemQueryAvail@4"); + guTexMemReset = (void (__stdcall *)(void))GetProcAddress(lib_handle, "_guTexMemReset@0"); + guTexDownloadMipMap = (void (__stdcall *)(unsigned long,const void *,const GuNccTable *))GetProcAddress(lib_handle, "_guTexDownloadMipMap@12"); + guTexDownloadMipMapLevel = (void (__stdcall *)(unsigned long,long,const void ** ))GetProcAddress(lib_handle, "_guTexDownloadMipMapLevel@12"); + guTexSource = (void (__stdcall *)(unsigned long))GetProcAddress(lib_handle, "_guTexSource@4"); + guColorCombineFunction = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_guColorCombineFunction@4"); + + grLfbLock = (int (__stdcall *)(unsigned long,long,long,long,int,GrLfbInfo_t *))GetProcAddress(lib_handle, "_grLfbLock@24"); + grLfbUnlock = (int (__stdcall *)(unsigned long,long))GetProcAddress(lib_handle, "_grLfbUnlock@8"); + grLfbConstantAlpha = (void (__stdcall *)(unsigned char))GetProcAddress(lib_handle, "_grLfbConstantAlpha@4"); + grLfbConstantDepth = (void (__stdcall *)(unsigned short))GetProcAddress(lib_handle, "_grLfbConstantDepth@4"); + grLfbWriteColorSwizzle = (void (__stdcall *)(int,int))GetProcAddress(lib_handle, "_grLfbWriteColorSwizzle@8"); + grLfbWriteColorFormat = (void (__stdcall *)(long))GetProcAddress(lib_handle, "_grLfbWriteColorFormat@4"); + grLfbWriteRegion = (int (__stdcall *)(long,unsigned long,unsigned long,unsigned long,unsigned long,unsigned long,long,void *))GetProcAddress(lib_handle, "_grLfbWriteRegion@32"); + grLfbReadRegion = (int (__stdcall *)(long,unsigned long,unsigned long,unsigned long,unsigned long,unsigned long,void *))GetProcAddress(lib_handle, "_grLfbReadRegion@28"); + + grAADrawLine = (void (__stdcall *)(const GrVertex *,const GrVertex *))GetProcAddress(lib_handle, "_grAADrawLine@8"); + grAADrawPoint = (void (__stdcall *)(const GrVertex *))GetProcAddress(lib_handle, "_grAADrawPoint@4"); + grAADrawPolygon = (void (__stdcall *)(const int,const int [],const GrVertex []))GetProcAddress(lib_handle, "_grAADrawPolygon@12"); + grAADrawPolygonVertexList = (void (__stdcall *)(const int,const GrVertex []))GetProcAddress(lib_handle, "_grAADrawPolygonVertexList@8"); + grAADrawTriangle = (void (__stdcall *)(const GrVertex *,const GrVertex *,const GrVertex *,int,int,int))GetProcAddress(lib_handle, "_grAADrawTriangle@24"); + + grGlideInit = (void (__stdcall *)(void))GetProcAddress(lib_handle, "_grGlideInit@0"); + grGlideShutdown = (void (__stdcall *)(void))GetProcAddress(lib_handle, "_grGlideShutdown@0"); + grGlideGetVersion = (void (__stdcall *)(char []))GetProcAddress(lib_handle, "_grGlideGetVersion@4"); + grGlideGetState = (void (__stdcall *)(struct _GrState_s *))GetProcAddress(lib_handle, "_grGlideGetState@4"); + grGlideSetState = (void (__stdcall *)(const struct _GrState_s *))GetProcAddress(lib_handle, "_grGlideSetState@4"); + grGlideShamelessPlug = (void (__stdcall *)(const int))GetProcAddress(lib_handle, "_grGlideShamelessPlug@4"); + grHints = (void (__stdcall *)(unsigned long,unsigned long))GetProcAddress(lib_handle, "_grHints@8"); + + guFogTableIndexToW = (float (__stdcall *)(int))GetProcAddress(lib_handle, "_guFogTableIndexToW@4"); + guFogGenerateExp = (void (__stdcall *)( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float))GetProcAddress(lib_handle, "_guFogGenerateExp@8"); + guFogGenerateExp2 = (void (__stdcall *)(GrFog_t fogtable[GR_FOG_TABLE_SIZE], float))GetProcAddress(lib_handle, "_guFogGenerateExp2@8"); + guFogGenerateLinear = (void (__stdcall *)(GrFog_t fogtable[GR_FOG_TABLE_SIZE], float , float))GetProcAddress(lib_handle, "_guFogGenerateLinear@12"); + + //XSTR:ON + + return 1; +} + + +void (__stdcall *grDrawLine)(const GrVertex *v1, const GrVertex *v2) = NULL; +void (__stdcall *grDrawPlanarPolygon)(int nverts, const int ilist[], const GrVertex vlist[]) = NULL; +void (__stdcall *grDrawPlanarPolygonVertexList)(int nverts, const GrVertex vlist[]) = NULL; +void (__stdcall *grDrawPoint)(const GrVertex *pt) = NULL; +void (__stdcall *grDrawPolygon)(int nverts, const int ilist[], const GrVertex vlist[]) = NULL; +void (__stdcall *grDrawPolygonVertexList)(int nverts, const GrVertex vlist[]) = NULL; +void (__stdcall *grDrawTriangle)(const GrVertex *a, const GrVertex *b, const GrVertex *c) = NULL; +void (__stdcall *guDrawTriangleWithClip)(const GrVertex *a, const GrVertex *b, const GrVertex *c) = NULL; +void (__stdcall *grBufferClear)(GrColor_t color, GrAlpha_t alpha, FxU16 depth) = NULL; +int (__stdcall *grBufferNumPending)(void) = NULL; +void (__stdcall *grBufferSwap)(int swap_interval) = NULL; +void (__stdcall *grRenderBuffer)(GrBuffer_t buffer) = NULL; + +typedef void (*GrErrorCallbackFnc_t)(const char *string, FxBool fatal); + +void (__stdcall *grErrorSetCallback)(GrErrorCallbackFnc_t fnc) = NULL; +void (__stdcall *grSstIdle)(void) = NULL; +FxU32 (__stdcall *grSstVideoLine)(void) = NULL; +FxBool (__stdcall *grSstVRetraceOn)(void) = NULL; +FxBool (__stdcall *grSstIsBusy)(void) = NULL; +FxBool (__stdcall *grSstWinOpen)( + FxU32 hWnd, + GrScreenResolution_t screen_resolution, + GrScreenRefresh_t refresh_rate, + GrColorFormat_t color_format, + GrOriginLocation_t origin_location, + int nColBuffers, + int nAuxBuffers) = NULL; +void (__stdcall *grSstWinClose)(void) = NULL; +FxBool (__stdcall *grSstControl)(FxU32 code) = NULL; +FxBool (__stdcall *grSstQueryHardware)(GrHwConfiguration *hwconfig) = NULL; +FxBool (__stdcall *grSstQueryBoards)(GrHwConfiguration *hwconfig) = NULL; +void (__stdcall *grSstOrigin)(GrOriginLocation_t origin) = NULL; +void (__stdcall *grSstSelect)(int which_sst) = NULL; +FxU32 (__stdcall *grSstScreenHeight)(void) = NULL; +FxU32 (__stdcall *grSstScreenWidth)(void) = NULL; +FxU32 (__stdcall *grSstStatus)(void) = NULL; +void (__stdcall *grSstPerfStats)(GrSstPerfStats_t *pStats) = NULL; +void (__stdcall *grSstResetPerfStats)(void) = NULL; +void (__stdcall *grResetTriStats)() = NULL; +void (__stdcall *grTriStats)(FxU32 *trisProcessed, FxU32 *trisDrawn) = NULL; +void (__stdcall *grAlphaBlendFunction)(GrAlphaBlendFnc_t rgb_sf, GrAlphaBlendFnc_t rgb_df, GrAlphaBlendFnc_t alpha_sf, GrAlphaBlendFnc_t alpha_df) = NULL; +void (__stdcall *grAlphaCombine)(GrCombineFunction_t function, GrCombineFactor_t factor, GrCombineLocal_t local, GrCombineOther_t other, FxBool invert) = NULL; +void (__stdcall *grAlphaControlsITRGBLighting)(FxBool enable) = NULL; +void (__stdcall *grAlphaTestFunction)(GrCmpFnc_t function) = NULL; +void (__stdcall *grAlphaTestReferenceValue)(GrAlpha_t value) = NULL; +void (__stdcall *grChromakeyMode)(GrChromakeyMode_t mode) = NULL; +void (__stdcall *grChromakeyValue)(GrColor_t value) = NULL; +void (__stdcall *grClipWindow)(FxU32 minx, FxU32 miny, FxU32 maxx, FxU32 maxy) = NULL; +void (__stdcall *grColorCombine)( + GrCombineFunction_t function, GrCombineFactor_t factor, + GrCombineLocal_t local, GrCombineOther_t other, + FxBool invert) = NULL; +void (__stdcall *grColorMask)(FxBool rgb, FxBool a) = NULL; +void (__stdcall *grCullMode)(GrCullMode_t mode) = NULL; +void (__stdcall *grConstantColorValue)(GrColor_t value) = NULL; +void (__stdcall *grConstantColorValue4)(float a, float r, float g, float b) = NULL; +void (__stdcall *grDepthBiasLevel)(FxI16 level) = NULL; +void (__stdcall *grDepthBufferFunction)(GrCmpFnc_t function) = NULL; +void (__stdcall *grDepthBufferMode)(GrDepthBufferMode_t mode) = NULL; +void (__stdcall *grDepthMask)(FxBool mask) = NULL; +void (__stdcall *grDisableAllEffects)(void) = NULL; +void (__stdcall *grDitherMode)( GrDitherMode_t mode) = NULL; +void (__stdcall *grFogColorValue)( GrColor_t fogcolor) = NULL; +void (__stdcall *grFogMode)(GrFogMode_t mode) = NULL; +void (__stdcall *grFogTable)(const GrFog_t ft[GR_FOG_TABLE_SIZE]) = NULL; +void (__stdcall *grGammaCorrectionValue)(float value) = NULL; +void (__stdcall *grSplash)(float x, float y, float width, float height, FxU32 frame) = NULL; +FxU32 (__stdcall *grTexCalcMemRequired)( + GrLOD_t lodmin, GrLOD_t lodmax, + GrAspectRatio_t aspect, GrTextureFormat_t fmt) = NULL; +FxU32 (__stdcall *grTexTextureMemRequired)(FxU32 evenOdd, GrTexInfo *info) = NULL; +FxU32 (__stdcall *grTexMinAddress)(GrChipID_t tmu) = NULL; +FxU32 (__stdcall *grTexMaxAddress)(GrChipID_t tmu) = NULL; +void (__stdcall *grTexNCCTable)(GrChipID_t tmu, GrNCCTable_t table) = NULL; +void (__stdcall *grTexSource)(GrChipID_t tmu, FxU32 startAddress, FxU32 evenOdd, GrTexInfo *info) = NULL; +void (__stdcall *grTexClampMode)(GrChipID_t tmu, GrTextureClampMode_t s_clampmode, GrTextureClampMode_t t_clampmode) = NULL; +void (__stdcall *grTexCombine)( + GrChipID_t tmu, + GrCombineFunction_t rgb_function, + GrCombineFactor_t rgb_factor, + GrCombineFunction_t alpha_function, + GrCombineFactor_t alpha_factor, + FxBool rgb_invert, + FxBool alpha_invert + ) = NULL; +void (__stdcall *grTexCombineFunction)( + GrChipID_t tmu, + GrTextureCombineFnc_t fnc + ) = NULL; +void (__stdcall *grTexDetailControl)( + GrChipID_t tmu, + int lod_bias, + FxU8 detail_scale, + float detail_max + ) = NULL; +void (__stdcall *grTexFilterMode)( + GrChipID_t tmu, + GrTextureFilterMode_t minfilter_mode, + GrTextureFilterMode_t magfilter_mode + ) = NULL; +void (__stdcall *grTexLodBiasValue)(GrChipID_t tmu, float bias) = NULL; +void (__stdcall *grTexDownloadMipMap)(GrChipID_t tmu, + FxU32 startAddress, + FxU32 evenOdd, + GrTexInfo *info) = NULL; + +void (__stdcall *grTexDownloadMipMapLevel)(GrChipID_t tmu, + FxU32 startAddress, + GrLOD_t thisLod, + GrLOD_t largeLod, + GrAspectRatio_t aspectRatio, + GrTextureFormat_t format, + FxU32 evenOdd, + void *data) = NULL; + +void (__stdcall *grTexDownloadMipMapLevelPartial)(GrChipID_t tmu, + FxU32 startAddress, + GrLOD_t thisLod, + GrLOD_t largeLod, + GrAspectRatio_t aspectRatio, + GrTextureFormat_t format, + FxU32 evenOdd, + void *data, + int start, + int end) = NULL; + + +void (__stdcall *ConvertAndDownloadRle)(GrChipID_t tmu, + FxU32 startAddress, + GrLOD_t thisLod, + GrLOD_t largeLod, + GrAspectRatio_t aspectRatio, + GrTextureFormat_t format, + FxU32 evenOdd, + FxU8 *bm_data, + long bm_h, + FxU32 u0, + FxU32 v0, + FxU32 width, + FxU32 height, + FxU32 dest_width, + FxU32 dest_height, + FxU16 *tlut) = NULL; + +void (__stdcall *grCheckForRoom)(FxI32 n); + +void (__stdcall *grTexDownloadTable)(GrChipID_t tmu, + GrTexTable_t type, + void *data) = NULL; + +void (__stdcall *grTexDownloadTablePartial)(GrChipID_t tmu, + GrTexTable_t type, + void *data, + int start, + int end) = NULL; + +void (__stdcall *grTexMipMapMode)(GrChipID_t tmu, + GrMipMapMode_t mode, + FxBool lodBlend) = NULL; + +void (__stdcall *grTexMultibase)(GrChipID_t tmu, + FxBool enable) = NULL; + +void (__stdcall *grTexMultibaseAddress)(GrChipID_t tmu, + GrTexBaseRange_t range, + FxU32 startAddress, + FxU32 evenOdd, + GrTexInfo *info) = NULL; + +GrMipMapId_t (__stdcall *guTexAllocateMemory)( + GrChipID_t tmu, + FxU8 odd_even_mask, + int width, int height, + GrTextureFormat_t fmt, + GrMipMapMode_t mm_mode, + GrLOD_t smallest_lod, GrLOD_t largest_lod, + GrAspectRatio_t aspect, + GrTextureClampMode_t s_clamp_mode, + GrTextureClampMode_t t_clamp_mode, + GrTextureFilterMode_t minfilter_mode, + GrTextureFilterMode_t magfilter_mode, + float lod_bias, + FxBool trilinear + ) = NULL; + +FxBool (__stdcall *guTexChangeAttributes)( + GrMipMapId_t mmid, + int width, int height, + GrTextureFormat_t fmt, + GrMipMapMode_t mm_mode, + GrLOD_t smallest_lod, GrLOD_t largest_lod, + GrAspectRatio_t aspect, + GrTextureClampMode_t s_clamp_mode, + GrTextureClampMode_t t_clamp_mode, + GrTextureFilterMode_t minFilterMode, + GrTextureFilterMode_t magFilterMode + ) = NULL; +void (__stdcall *guTexCombineFunction)( + GrChipID_t tmu, + GrTextureCombineFnc_t fnc + ) = NULL; +GrMipMapId_t (__stdcall *guTexGetCurrentMipMap)(GrChipID_t tmu) = NULL; +GrMipMapInfo* (__stdcall *guTexGetMipMapInfo)(GrMipMapId_t mmid); +FxU32 (__stdcall *guTexMemQueryAvail)(GrChipID_t tmu) = NULL; +void (__stdcall *guTexMemReset)(void) = NULL; +void (__stdcall *guTexDownloadMipMap)( + GrMipMapId_t mmid, + const void *src, + const GuNccTable *table + ) = NULL; +void (__stdcall *guTexDownloadMipMapLevel)( + GrMipMapId_t mmid, + GrLOD_t lod, + const void **src + ) = NULL; +void (__stdcall *guTexSource)(GrMipMapId_t id) = NULL; +void (__stdcall *guColorCombineFunction)(GrColorCombineFnc_t fnc) = NULL; + +FxBool (__stdcall *grLfbLock)( GrLock_t type, GrBuffer_t buffer, GrLfbWriteMode_t writeMode, + GrOriginLocation_t origin, FxBool pixelPipeline, + GrLfbInfo_t *info) = NULL; +FxBool (__stdcall *grLfbUnlock)(GrLock_t type, GrBuffer_t buffer) = NULL; +void (__stdcall *grLfbConstantAlpha)(GrAlpha_t alpha) = NULL; +void (__stdcall *grLfbConstantDepth)(FxU16 depth) = NULL; +void (__stdcall *grLfbWriteColorSwizzle)(FxBool swizzleBytes, FxBool swapWords) = NULL; +void (__stdcall *grLfbWriteColorFormat)(GrColorFormat_t colorFormat) = NULL; +FxBool (__stdcall *grLfbWriteRegion)(GrBuffer_t dst_buffer, + FxU32 dst_x, FxU32 dst_y, + GrLfbSrcFmt_t src_format, + FxU32 src_width, FxU32 src_height, + FxI32 src_stride, void *src_data) = NULL; + +FxBool (__stdcall *grLfbReadRegion)(GrBuffer_t src_buffer, + FxU32 src_x, FxU32 src_y, + FxU32 src_width, FxU32 src_height, + FxU32 dst_stride, void *dst_data) = NULL; + +void (__stdcall *grAADrawLine)(const GrVertex *v1, const GrVertex *v2) = NULL; +void (__stdcall *grAADrawPoint)(const GrVertex *pt) = NULL; +void (__stdcall *grAADrawPolygon)(const int nverts, const int ilist[], const GrVertex vlist[]) = NULL; +void (__stdcall *grAADrawPolygonVertexList)(const int nverts, const GrVertex vlist[]) = NULL; +void (__stdcall *grAADrawTriangle)( + const GrVertex *a, const GrVertex *b, const GrVertex *c, + FxBool ab_antialias, FxBool bc_antialias, FxBool ca_antialias + ) = NULL; +void (__stdcall *grGlideInit)(void) = NULL; +void (__stdcall *grGlideShutdown)(void) = NULL; +void (__stdcall *grGlideGetVersion)(char version[80]) = NULL; +void (__stdcall *grGlideGetState)(GrState *state) = NULL; +void (__stdcall *grGlideSetState)( const GrState *state) = NULL; +void (__stdcall *grGlideShamelessPlug)(const FxBool on) = NULL; +void (__stdcall *grHints)(GrHint_t hintType, FxU32 hintMask) = NULL; + +float (__stdcall *guFogTableIndexToW)(int i) = NULL; +void (__stdcall *guFogGenerateExp)( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float density ) = NULL; +void (__stdcall *guFogGenerateExp2)( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float density ) = NULL; +void (__stdcall *guFogGenerateLinear)( GrFog_t fogtable[GR_FOG_TABLE_SIZE], float nearZ, float farZ ) = NULL; diff --git a/src/globalincs/alphacolors.cpp b/src/globalincs/alphacolors.cpp new file mode 100644 index 0000000..aa9512d --- /dev/null +++ b/src/globalincs/alphacolors.cpp @@ -0,0 +1,65 @@ +#include "2d.h" +#include "alphacolors.h" + +// ----------------------------------------------------------------------------------- +// ALPHA DEFINES/VARS +// + +color Color_black, Color_grey, Color_blue, Color_bright_blue, Color_violet_gray; +color Color_green, Color_bright_green, Color_bright_white, Color_white; +color Color_red, Color_bright_red, Color_yellow, Color_bright_yellow, Color_dim_red; +color Color_ui_light_green, Color_ui_green; +color Color_ui_light_pink, Color_ui_pink; + +// netplayer colors +color *Color_netplayer[12] = { + &Color_white, + &Color_bright_white, + &Color_bright_blue, + &Color_red, + &Color_bright_red, + &Color_blue, + &Color_bright_green, + &Color_bright_blue, + &Color_yellow, + &Color_bright_yellow, + &Color_ui_green, + &Color_ui_pink +}; + + +// ----------------------------------------------------------------------------------- +// ALPHA FUNCTIONS +// + +// initialize all alpha colors (call at startup) +void alpha_colors_init() +{ + // See the variable declarations above for color usage + gr_init_alphacolor( &Color_blue, 93, 93, 128, 255 ); + gr_init_alphacolor( &Color_bright_blue, 185, 185, 255, 255 ); + + gr_init_alphacolor( &Color_green, 0, 120, 0, 255 ); + gr_init_alphacolor( &Color_bright_green, 50, 190, 50, 255 ); + + gr_init_alphacolor( &Color_black, 0, 0, 0, 255 ); + gr_init_alphacolor( &Color_grey, 50, 50, 50, 255 ); + //gr_init_alphacolor( &Color_white, 185, 185, 185, 255 ); + gr_init_alphacolor( &Color_white, 105, 105, 105, 255 ); + gr_init_alphacolor( &Color_bright_white, 255, 255, 255, 255 ); + + gr_init_alphacolor( &Color_violet_gray, 160, 144, 160, 255 ); + + gr_init_alphacolor( &Color_dim_red, 80, 6, 6, 255 ); + gr_init_alphacolor( &Color_red, 126, 6, 6, 255 ); + gr_init_alphacolor( &Color_bright_red, 200, 0, 0, 255 ); + + gr_init_alphacolor( &Color_yellow, 113, 184, 124, 255 ); + gr_init_alphacolor( &Color_bright_yellow, 162, 210, 162, 255 ); + + gr_init_alphacolor( &Color_ui_light_green, 161, 184, 161, 255 ); + gr_init_alphacolor( &Color_ui_green, 190, 228, 190, 255 ); + + gr_init_alphacolor( &Color_ui_light_pink, 184, 161, 161, 255 ); + gr_init_alphacolor( &Color_ui_pink, 228, 190, 190, 255 ); +} diff --git a/src/globalincs/crypt.cpp b/src/globalincs/crypt.cpp new file mode 100644 index 0000000..9f60840 --- /dev/null +++ b/src/globalincs/crypt.cpp @@ -0,0 +1,49 @@ +/* + * $Logfile: /Freespace2/code/GlobalIncs/crypt.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Files for cypting stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 2 3/30/98 9:33p Allender + * string encryption stuff for cheat codes + * + * $NoKeywords: $ + */ + +#include +#include "crypt.h" + +char *jcrypt (char *plainstring) +{ + int i,t,len; + static char cryptstring[CRYPT_STRING_LENGTH + 1]; + + len=strlen (plainstring); + if (len > CRYPT_STRING_LENGTH) + len = CRYPT_STRING_LENGTH; + + for (i = 0;i < len; i++) { + cryptstring[i]=0; + + for (t = 0; t < len; t++) { + cryptstring[i]^=(plainstring[t] ^ plainstring[i%(t+1)]); + cryptstring[i]%=90; + cryptstring[i]+=33; + } + } + + cryptstring[i]=0; + return ((char *)cryptstring); +} diff --git a/src/globalincs/systemvars.cpp b/src/globalincs/systemvars.cpp new file mode 100644 index 0000000..1c91e86 --- /dev/null +++ b/src/globalincs/systemvars.cpp @@ -0,0 +1,700 @@ +/* + * $Logfile: /Freespace2/code/GlobalIncs/SystemVars.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Variables and constants common to FreeSpace and Fred. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 12 9/09/99 8:53p Dave + * Fixed multiplayer degenerate orientation case problem. Make sure warp + * effect never goes lower than LOD 1. + * + * 11 9/06/99 11:25a Mikek + * Decrease some settings for High detail level. + * + * 10 8/05/99 2:05a Dave + * Whee. + * + * 9 7/29/99 10:47p Dave + * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs. + * + * 8 7/29/99 12:05a Dave + * Nebula speed optimizations. + * + * 7 6/22/99 7:03p Dave + * New detail options screen. + * + * 6 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 5 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * 4 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 3 10/07/98 6:27p Dave + * Globalized mission and campaign file extensions. Removed Silent Threat + * special code. Moved \cache \players and \multidata into the \data + * directory. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 38 9/21/98 8:46p Dave + * Put in special check in fred for identifying unknown ships. + * + * 37 8/17/98 5:07p Dave + * First rev of corkscrewing missiles. + * + * 36 5/13/98 11:34p Mike + * Model caching system. + * + * 35 4/25/98 4:06p John + * Made defaults make a little more sense + * + * 34 4/20/98 8:41p John + * Made debris culling actually reduce Glide texture resolution. + * + * 33 4/12/98 9:56a John + * Made lighting detail flags work. Made explosions cast light on + * highest. + * + * 32 4/08/98 8:34p Lawrance + * Fix up how custom button works on the detail tab. + * + * 31 4/01/98 5:34p John + * Made only the used POFs page in for a level. Reduced some interp + * arrays. Made custom detail level work differently. + * + * 30 3/31/98 5:18p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * + * 29 3/30/98 2:38p Mike + * Add asteroid_density to detail level support. + * No force explosion damage in training missions. + * Make cargo deathrolls shorter. + * + * 28 3/24/98 8:16a John + * made highest detail different than next one down + * + * 27 3/23/98 5:19p John + * Upped number of default detail levels to 4 + * + * 26 3/22/98 4:11p Andsager + * Remove global Freespace_running + * + * 25 3/22/98 3:28p John + * Added in stippled alpha for lower details. Made medium detail use + * engine glow. + * + * 24 3/22/98 2:41p John + * Added lighting into the detail structure. + * + * 23 3/22/98 11:06a John + * Changed default detail levels + * + * 22 3/22/98 11:02a John + * Made a bunch of the detail levels actually do something + * + * 21 3/22/98 9:53a John + * Added in first stage of new detail level stuff + * + * 20 3/09/98 12:13a Andsager + * Add code to help find jumps in position. + * + * 19 2/28/98 7:03p Lawrance + * get slew working in any view + * + * 18 2/16/98 4:16p John + * Added loading animation + * + * 17 2/10/98 9:06a John + * Added variables for restoring + * + * 16 2/05/98 9:27p John + * took out ending min/max etc on monitor + * + * 15 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 14 2/03/98 9:25p John + * Upgraded Direct3D code to new version 5.0 code. Separated the D3D code + * more. Added a global variable D3D_enabled flag. + * + * 13 1/17/98 12:17p John + * fixed erroneous print + * + * 12 1/17/98 12:14p John + * Added loading... bar to freespace. + * + * 11 1/11/98 2:15p John + * Changed a lot of stuff that had to do with bitmap loading. Made cfile + * not do callbacks, I put that in global code. Made only bitmaps that + * need to load for a level load. + * + * 10 12/16/97 6:20p Hoffoss + * Added more debugging code for demos, and fixed a bug in demo + * recording/playback. + * + * 9 12/05/97 3:46p John + * made ship thruster glow scale instead of being an animation. + * + * 8 8/05/97 10:18a Lawrance + * my_rand() being used temporarily instead of rand() + * + * 7 8/04/97 10:21a Dave + * Added Is_standalone global var + * + * 6 6/24/97 6:21p John + * added detail flags. + * sped up motion debris system a bit. + * + * 5 4/15/97 11:28p Mike + * External view system + * + * 4 4/15/97 4:00p Mike + * Intermediate checkin caused by getting other files. Working on camera + * slewing system. + * + * 3 4/08/97 8:47a John + * Added a global varible for detail level + * + * 2 4/01/97 11:07p Mike + * Clean up game sequencing functions. Get rid of Multiplayer and add + * Game_mode. Add SystemVars.cpp + * + * 1 4/01/97 10:59p Mike + * + * $NoKeywords: $ + */ + +#include "pstypes.h" +#include "systemvars.h" +#include "timer.h" +#include "neb.h" + +fix Missiontime; +fix Frametime; +int Framecount=0; + +int Game_mode; + +int Game_restoring = 0; // If set, this means we are restoring data from disk + +int Viewer_mode; // Viewer's mode, see VM_xxxx flags. + +// The detail level. Anything below zero draws simple models earlier than it +// should. Anything above zero draws higher detail models longer than it should. +// -2=lowest +// -1=low +// 0=normal (medium) +// 1=high +// 2=extra high +int Game_detail_level = 0; +uint Game_detail_flags = DETAIL_DEFAULT; // see systemvars.h for explanation + +angles Viewer_slew_angles; // Angles of viewer relative to forward. +vei Viewer_external_info; // Viewer angles to ship in external view. +vci Viewer_chase_info; // View chase camera information + +int Is_standalone; +int Rand_count; + +int Interface_last_tick = -1; // last timer tick on flip + +// for notifying players of unknown ship types +int Fred_found_unknown_ship_during_parsing = 0; + +// If true, then we are using Direct3D hardware. This is used for game type stuff +// that changes when you're using hardware. +int D3D_enabled = 0; + +// Values used for noise for thruster animations +float Noise[NOISE_NUM_FRAMES] = { + 0.468225f, + 0.168765f, + 0.318945f, + 0.292866f, + 0.553357f, + 0.468225f, + 0.180456f, + 0.418465f, + 0.489958f, + 1.000000f, + 0.468225f, + 0.599820f, + 0.664718f, + 0.294215f, + 0.000000f +}; + + +int myrand() +{ + int rval; + rval = rand(); + Rand_count++; +// nprintf(("Alan","RAND: %d\n", rval)); + return rval; +} + + +// Variables for the loading callback hooks +static int cf_timestamp = -1; +static void (*cf_callback)(int count) = NULL; +static int cf_in_callback = 0; +static int cb_counter = 0; +static int cb_last_counter = 0; +static int cb_delta_step = -1; + +// Call this with the name of a function. That function will +// then get called around 10x per second. The callback function +// gets passed a 'count' which is how many times game_busy has +// been called since the callback was set. It gets called +// one last time with count=-1 when you turn off the callback +// by calling game_busy_callback(NULL). Game_busy_callback +// returns the current count, so you can tell how many times +// game_busy got called. +int game_busy_callback( void (*callback)(int count), int delta_step ) +{ + if ( !callback ) { + + // Call it once more to finalize things + cf_in_callback++; + (*cf_callback)(-1); + cf_in_callback--; + + cf_timestamp = -1; + cf_callback = NULL; + } else { + cb_counter = 0; + cb_last_counter = 0; + cb_delta_step = delta_step; + cf_timestamp = timer_get_milliseconds()+(1000/10); + cf_callback = callback; + + // Call it once + cf_in_callback++; + (*cf_callback)(0); // pass 0 first time! + cf_in_callback--; + + } + + return cb_counter; +} + +// Call whenever loading to display cursor +void game_busy() +{ + if ( cf_in_callback != 0 ) return; // don't call callback if we're already in it. + if ( cf_timestamp < 0 ) return; + if ( !cf_callback ) return; + + cb_counter++; + +// mprintf(( "CB_COUNTER=%d\n", cb_counter )); + + int t1 = timer_get_milliseconds(); + + if ( (t1 > cf_timestamp) || ((cb_counter>cb_last_counter+155)&&(cb_delta_step>0)) ) { + cb_last_counter = cb_counter; + cf_in_callback++; + (*cf_callback)(cb_counter); + cf_in_callback--; + cf_timestamp = t1 + +(1000/10); + } +} + +//======================== CODE TO MONITOR EVENTS ====================== + +#ifndef NDEBUG + +#define MAX_MONITORS 64 + +static int Num_monitors = 0; +static monitor *Monitor[MAX_MONITORS]; + +monitor::monitor( char *_name ) +{ + int i; + + if ( Num_monitors >= MAX_MONITORS ) { + Int3(); // Too many monitor variables!! Increase MAX_MONITORS!! + return; + } + + for (i=0; iname, _name ); + + if ( ret == 0) { + Int3(); // This monitor variable already exists!!!! + return; + } else if ( ret > 0 ) { + break; // Insert it here + + } else if ( ret < 0 ) { + // do nothing + } + } + + if ( i < Num_monitors ) { + // Insert it at element i + int j; + for (j=Num_monitors; j>i; j-- ) { + Monitor[j] = Monitor[j-1]; + } + Monitor[i] = this; + Num_monitors++; + } else { + Monitor[Num_monitors] = this; + Num_monitors++; + } + + name = _name; + value = 0; +} + + +int Monitor_inited = 0; +char Monitor_filename[128]; +fix monitor_last_time = -1; + +DCF(monitor,"Monitors game performace") +{ + if ( Dc_command ) { + dc_get_arg(ARG_STRING|ARG_NONE); + if ( Dc_arg_type == ARG_NONE ) { + if ( Monitor_inited ) { + Monitor_inited = 0; + +/* + FILE *fp = fopen( Monitor_filename, "at" ); + if ( fp ) { + fprintf( fp, "\n\n" ); + fprintf( fp, "Name\tMin\tMax\tAvg\n" ); + for (int i=0; icnt > 0 ) { + fprintf( fp, "%s\t%d\t%d\t%d\n", Monitor[i]->name, Monitor[i]->min, Monitor[i]->max, Monitor[i]->sum / Monitor[i]->cnt ); + } else { + fprintf( fp, "%s\t%d\t%d\t?\n", Monitor[i]->name, Monitor[i]->min, Monitor[i]->max ); + } + } + fclose(fp); + } +*/ + + dc_printf( "Monitor to file '%s' turned off\n", Monitor_filename ); + } else { + dc_printf( "Monitor isn't on\n" ); + } + } else { + if ( Monitor_inited ) { + dc_printf( "Monitor already on\n" ); + } else { + Monitor_inited = 1; + + strcpy( Monitor_filename, Dc_arg ); + + // Reset them all + int i; + for (i=0; ivalue = 0; + Monitor[i]->sum = 0; + Monitor[i]->cnt = 0; + Monitor[i]->min = 0; + Monitor[i]->max = 0; + } + + FILE *fp = fopen( Monitor_filename, "wt" ); + if ( fp ) { + for (i=0; i 0 ) { + fprintf( fp, "\t" ); + } + fprintf( fp, "%s", Monitor[i]->name ); + + } + fprintf( fp, "\n" ); + fclose(fp); + } + dc_printf( "Monitor outputting to file '%s'\n", Monitor_filename ); + monitor_last_time = -1; + } + } + } + if ( Dc_help ) { + dc_printf( "Usage: monitor filename\nOutputs monitoring info to filename. No filename turns it off\n" ); + } + +} + + +MONITOR(FrameRateX100); + +void monitor_update() +{ + int i; + FILE * fp; + + fix this_time = timer_get_fixed_seconds(); + fix frametime; + + if ( monitor_last_time != -1 ) { + frametime = this_time - monitor_last_time; + } else { + frametime = 0; + } + + if ( frametime > 0 ) { + MONITOR_INC(FrameRateX100, (F1_0*100) / frametime ); + } else { + MONITOR_INC(FrameRateX100, 0 ); + } + + + if ( !Monitor_inited ) { + return; + } + + if ( frametime != 0 ) { + fp = fopen( Monitor_filename, "at" ); + if ( fp ) { + + for (i=0; i0) fprintf( fp, "\t" ); + fprintf( fp, "%d", Monitor[i]->value ); + } + fprintf( fp, "\n" ); + fclose(fp); + } + + for (i=0; isum += Monitor[i]->value; + + if ( (Monitor[i]->cnt < 1) || (Monitor[i]->value < Monitor[i]->min )) { + Monitor[i]->min = Monitor[i]->value; + } + + if ( (Monitor[i]->cnt < 1) || (Monitor[i]->value > Monitor[i]->max )) { + Monitor[i]->max = Monitor[i]->value; + } + + Monitor[i]->cnt++; + + // Reset the value + Monitor[i]->value = 0; + } + } else { + for (i=0; ivalue = 0; + } + } + + monitor_last_time = timer_get_fixed_seconds(); + +} +#endif //NDEBUG + + +#if MAX_DETAIL_LEVEL != 4 +#error MAX_DETAIL_LEVEL is assumed to be 4 in SystemVars.cpp +#endif + +#if NUM_DEFAULT_DETAIL_LEVELS != 4 +#error NUM_DEFAULT_DETAIL_LEVELS is assumed to be 4 in SystemVars.cpp +#endif + +// Detail level stuff +detail_levels Detail_defaults[NUM_DEFAULT_DETAIL_LEVELS] = { + { // Low + 0, // setting + // ===== Analogs (0-MAX_DETAIL_LEVEL) ==== + 0, // nebula_detail; // 0=lowest detail, MAX_DETAIL_LEVEL=highest detail + 0, // detail_distance; // 0=lowest MAX_DETAIL_LEVEL=highest + 0, // hardware_textures; // 0=max culling, MAX_DETAIL_LEVEL=no culling + 0, // num_small_debris; // 0=min number, MAX_DETAIL_LEVEL=max number + 0, // num_particles; // 0=min number, MAX_DETAIL_LEVEL=max number + 0, // num_stars; // 0=min number, MAX_DETAIL_LEVEL=max number + 0, // shield_effects; // 0=min, MAX_DETAIL_LEVEL=max + 2, // lighting; // 0=min, MAX_DETAIL_LEVEL=max + + // ==== Booleans ==== + 0, // targetview_model; // 0=off, 1=on + 0, // planets_suns; // 0=off, 1=on + 0, // weapon_extras + }, + { // Medium + 1, // setting + // ===== Analogs (0-MAX_DETAIL_LEVEL) ==== + 1, // nebula_detail; // 0=lowest detail, MAX_DETAIL_LEVEL=highest detail + 1, // detail_distance; // 0=lowest MAX_DETAIL_LEVEL=highest + 1, // hardware_textures; // 0=max culling, MAX_DETAIL_LEVEL=no culling + 2, // num_small_debris; // 0=min number, MAX_DETAIL_LEVEL=max number + 2, // num_particles; // 0=min number, MAX_DETAIL_LEVEL=max number + 2, // num_stars; // 0=min number, MAX_DETAIL_LEVEL=max number + 1, // shield_effects; // 0=min, MAX_DETAIL_LEVEL=max + 3, // lighting; // 0=min, MAX_DETAIL_LEVEL=max + + // ==== Booleans ==== + 1, // targetview_model; // 0=off, 1=on + 1, // planets_suns; // 0=off, 1=on + 1, // weapon extras + }, + { // High level + 2, // setting + // ===== Analogs (0-MAX_DETAIL_LEVEL) ==== + 2, // nebula_detail; // 0=lowest detail, MAX_DETAIL_LEVEL=highest detail + 3, // detail_distance; // 0=lowest MAX_DETAIL_LEVEL=highest + 3, // hardware_textures; // 0=max culling, MAX_DETAIL_LEVEL=no culling + 3, // num_small_debris; // 0=min number, MAX_DETAIL_LEVEL=max number + 3, // num_particles; // 0=min number, MAX_DETAIL_LEVEL=max number + 4, // num_stars; // 0=min number, MAX_DETAIL_LEVEL=max number + 3, // shield_effects; // 0=min, MAX_DETAIL_LEVEL=max + 4, // lighting; // 0=min, MAX_DETAIL_LEVEL=max + + // ==== Booleans ==== + 1, // targetview_model; // 0=off, 1=on + 1, // planets_suns; // 0=off, 1=on + 1, // weapon_extras + }, + { // Highest level + 3, // setting + // ===== Analogs (0-MAX_DETAIL_LEVEL) ==== + 3, // nebula_detail; // 0=lowest detail, MAX_DETAIL_LEVEL=highest detail + 3, // detail_distance; // 0=lowest MAX_DETAIL_LEVEL=highest + 4, // hardware_textures; // 0=max culling, MAX_DETAIL_LEVEL=no culling + 4, // num_small_debris; // 0=min number, MAX_DETAIL_LEVEL=max number + 3, // num_particles; // 0=min number, MAX_DETAIL_LEVEL=max number + 4, // num_stars; // 0=min number, MAX_DETAIL_LEVEL=max number + 4, // shield_effects; // 0=min, MAX_DETAIL_LEVEL=max + 4, // lighting; // 0=min, MAX_DETAIL_LEVEL=max + + // ==== Booleans ==== + 1, // targetview_model; // 0=off, 1=on + 1, // planets_suns; // 0=off, 1=on + 1, // weapon_extras + }, +}; + + +// Global used to access detail levels in game and libs +detail_levels Detail = Detail_defaults[NUM_DEFAULT_DETAIL_LEVELS-1]; + +// Call this with: +// 0 - lowest +// NUM_DETAIL_LEVELS - highest +// To set the parameters in Detail to some set of defaults +void detail_level_set(int level) +{ + if ( level < 0 ) { + Detail.setting = -1; + return; + } + Assert( level >= 0 ); + Assert( level < NUM_DEFAULT_DETAIL_LEVELS ); + + Detail = Detail_defaults[level]; + + // reset nebula stuff + neb2_set_detail_level(level); +} + +// Returns the current detail level or -1 if custom. +int current_detail_level() +{ +// return Detail.setting; + int i; + + for (i=0; i +#include +#include "version.h" +#include "osregistry.h" + +// ---------------------------------------------------------------------------------------------------------------- +// VERSION DEFINES/VARS +// + +// Defines +#define VER(major, minor, build) (100*100*major+100*minor+build) +#define MAX_LINE_LENGTH 512 + + +// ---------------------------------------------------------------------------------------------------------------- +// VERSION FUNCTIONS +// + +// compare version against the passed version file +// returns -1 on error +// 0 if we are an earlier version +// 1 if same version +// 2 if higher version +// fills in user version and latest version values if non-NULL +int version_compare(char *filename, int *u_major, int *u_minor, int *u_build, int *l_major, int *l_minor, int *l_build) +{ + int usr_major, usr_minor, usr_build; + int latest_major, latest_minor, latest_build; + + // open file and try backup, if needed + FILE *f = fopen(filename, "rt"); + if (f == NULL) { + return -1; + } + + // grab the last line in file which isn't empty and isn't a comment + char buffer[MAX_LINE_LENGTH+1], verbuffer[MAX_LINE_LENGTH+1]; + + strcpy(verbuffer,""); + strcpy(buffer,""); + while ( !feof(f) ) { + // Read the line into a temporary buffer + fgets(buffer, MAX_LINE_LENGTH, f); + + // take the \n off the end of it + if (strlen(buffer)>0 && buffer[strlen(buffer) - 1] == '\n') + buffer[strlen(buffer) - 1] = 0; + + // If the line is empty, go get another one + if (strlen(buffer) == 0) continue; + + // If the line is a comment, go get another one + if (buffer[0] == VERSION_FILE_COMMENT_CHAR) continue; + + // Line is a good one, so save it... + strcpy(verbuffer, buffer); + } + fclose(f); + + // Make sure a version line was found + if (strlen(verbuffer) == 0) { + // MessageBox(XSTR("Couldn't parse Version file!", 1205), XSTR("Error!", 1185), MB_OK|MB_ICONERROR); + return -1; + } + + // Get the most up to date Version number + latest_major = 0; + latest_minor = 0; + latest_build = 0; + + if (sscanf(verbuffer, "%i %i %i", &latest_major, &latest_minor, &latest_build) != 3) { + // MessageBox(XSTR("Couldn't parse Version file!", 1205), XSTR("Error!", 1185), MB_OK|MB_ICONERROR); + return -1; + } + + // retrieve the user's current version + usr_major = os_config_read_uint("Version", "Major", 0); + usr_minor = os_config_read_uint("Version", "Minor", 0); + usr_build = os_config_read_uint("Version", "Build", 0); + + // Make sure the user's Version was found! + if ( VER(usr_major, usr_minor, usr_build) == 0 ) { + // MessageBox(XSTR("The Freespace 2 Auto-Update program could not find your current game Version in the system registry.\n\nThis should be corrected by starting up the game, exiting the game, and then running the Auto-Update program.", 1206), XSTR("Unable to Determine User's Version", 1207), MB_OK|MB_ICONERROR); + return NO_VERSION_IN_REGISTRY; + } + + // stuff outgoing values + if(u_major != NULL){ + *u_major = usr_major; + } + if(u_minor != NULL){ + *u_minor = usr_minor; + } + if(u_build != NULL){ + *u_build = usr_build; + } + if(l_major != NULL){ + *l_major = latest_major; + } + if(l_minor != NULL){ + *l_minor = latest_minor; + } + if(l_build != NULL){ + *l_build = latest_build; + } + + // check to see if the user's version is up to date + if (VER(usr_major, usr_minor, usr_build) < VER(latest_major, latest_minor, latest_build)) { + return 0; + } + + // same version + return 1; +} diff --git a/src/globalincs/windebug.cpp b/src/globalincs/windebug.cpp new file mode 100644 index 0000000..416047d --- /dev/null +++ b/src/globalincs/windebug.cpp @@ -0,0 +1,1201 @@ +/* + * $Logfile: /Freespace2/code/GlobalIncs/WinDebug.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Debug stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 12/01/98 4:46p Dave + * Put in targa bitmap support (16 bit). + * + * 3 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 32 5/25/98 1:39p John + * added code to give error and abort if malloc fails. + * + * 31 5/06/98 8:03p Allender + * AL: Do early out if trying to free NULL pointer in vm_free(). Print + * out warning message. + * + * 30 4/30/98 12:02a Lawrance + * compile out Warnings() on NDEBUG builds + * + * 29 4/22/98 5:15p Lawrance + * fix bug in strdup + * + * 28 4/21/98 1:02p John + * fixed bug where the clipboard text dumped wasn't null terminated. + * + * 27 4/20/98 3:22p John + * fixed bug that displayed wrong amount of allocated ram. + * + * 26 4/17/98 3:27p Allender + * + * 25 4/17/98 1:41p Allender + * made to compile with NDEBUG defined + * + * 24 4/17/98 7:00a John + * Added in hooks for new memory allocator. I haven't tried compiling + * under NDEBUG, but I tried to put in code for it. + * + * 23 4/01/98 9:21p John + * Made NDEBUG, optimized build with no warnings or errors. + * + * 22 3/31/98 8:17p John + * Added code to dump memory contents + * + * 21 3/31/98 11:17a Allender + * fix bug with INTERPLAYQA Int3's and Warning dialogs + * + * 20 3/30/98 4:02p John + * Made machines with < 32 MB of RAM use every other frame of certain + * bitmaps. Put in code to keep track of how much RAM we've malloc'd. + * + * 19 3/14/98 3:35p John + * cleaned up call stack code. Exited out on not properly aligned EBP's. + * + * 18 3/14/98 3:25p John + * Added code to check the parentEBP pointer for validity before + * derefrencing. + * + * 17 3/11/98 5:34p Lawrance + * Fix typo in error dialog box + * + * 16 3/06/98 2:21p John + * Correct some ok/cancel messages. Made call stack info prettier for + * modules with no debugging info + * + * 15 3/05/98 3:04p John + * Made Errors, Assert, Warning info paste to clipboard. + * + * 14 3/05/98 9:17a John + * Limited stack dump depth to 16 + * + * 13 3/05/98 9:14a John + * Added in a simple function name unmangler + * + * 12 3/04/98 7:08p John + * Made Freespace generate COFF files. Made Assert,Error, and Warning + * display the call stack. + * + * 11 2/22/98 2:48p John + * More String Externalization Classification + * + * $NoKeywords: $ + */ + +// Nothing in this module should be externalized!!! +//XSTR:OFF + +//#define DUMPRAM // This dumps all symbol sizes. See John for more info + +#include +#include +#include +#include + +#include "pstypes.h" + +//Uncomment SHOW_CALL_STACK to show the call stack in Asserts, Warnings, and Errors +#define SHOW_CALL_STACK + +#ifdef SHOW_CALL_STACK + +class DumpBuffer + { + public : + enum { BUFFER_SIZE = 32000 } ; + DumpBuffer() ; + void Clear() ; + void Printf( const char* format, ... ) ; + void SetWindowText( HWND hWnd ) const ; + char buffer[ BUFFER_SIZE ] ; + private : + char* current ; + } ; + + + +DumpBuffer :: DumpBuffer() + { + Clear() ; + } + + +void DumpBuffer :: Clear() + { + current = buffer ; + } + + +void DumpBuffer :: Printf( const char* format, ... ) + { + // protect against obvious buffer overflow + if( current - buffer < BUFFER_SIZE ) + { + va_list argPtr ; + va_start( argPtr, format ) ; + int count = vsprintf( current, format, argPtr ) ; + va_end( argPtr ) ; + current += count ; + } + } + + +void DumpBuffer :: SetWindowText( HWND hWnd ) const + { + SendMessage( hWnd, WM_SETTEXT, 0, (LPARAM)buffer ) ; + } + + + + + +class PE_Debug + { + public : + PE_Debug() ; + ~PE_Debug() ; + void ClearReport() ; + int DumpDebugInfo( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance ) ; + void Display() ; + private : + // Report data + enum { MAX_MODULENAME_LEN = 512, VA_MAX_FILENAME_LEN = 256 } ; + char latestModule[ MAX_MODULENAME_LEN ] ; + char latestFile[ VA_MAX_FILENAME_LEN ] ; + // File mapping data + HANDLE hFile ; + HANDLE hFileMapping ; + PIMAGE_DOS_HEADER fileBase ; + // Pointers to debug information + PIMAGE_NT_HEADERS NT_Header ; + PIMAGE_COFF_SYMBOLS_HEADER COFFDebugInfo ; + PIMAGE_SYMBOL COFFSymbolTable ; + int COFFSymbolCount ; + const char* stringTable ; + + void ClearFileCache() ; + void ClearDebugPtrs() ; + void MapFileInMemory( const char* module ) ; + void FindDebugInfo() ; + void DumpSymbolInfo( DumpBuffer& dumpBuffer, DWORD relativeAddress ) ; + void DumpLineNumber( DumpBuffer& dumpBuffer, DWORD relativeAddress ) ; + PIMAGE_COFF_SYMBOLS_HEADER GetDebugHeader() ; + PIMAGE_SECTION_HEADER SectionHeaderFromName( const char* name ) ; + const char* GetSymbolName( PIMAGE_SYMBOL sym ) ; + } ; + + +// Add an offset to a pointer and cast to a given type; may be +// implemented as a template function but Visual C++ has some problems. +#define BasedPtr( type, ptr, ofs ) (type)( (DWORD)(ptr) + (DWORD)(ofs) ) + + +PE_Debug :: PE_Debug() + { + // Init file mapping cache + hFileMapping = 0 ; + hFile = INVALID_HANDLE_VALUE ; + fileBase = 0 ; + ClearDebugPtrs() ; + ClearReport() ; + } + + +PE_Debug :: ~PE_Debug() + { + ClearFileCache() ; + } + + +void PE_Debug :: ClearReport() + { + latestModule[ 0 ] = 0 ; + latestFile[ 0 ] = 0 ; + } + + +void PE_Debug :: ClearDebugPtrs() + { + NT_Header = NULL ; + COFFDebugInfo = NULL ; + COFFSymbolTable = NULL ; + COFFSymbolCount = 0 ; + stringTable = NULL ; + } + + +void PE_Debug :: ClearFileCache() + { + if( fileBase ) + { + UnmapViewOfFile( fileBase ) ; + fileBase = 0 ; + } + if( hFileMapping != 0 ) + { + CloseHandle( hFileMapping ) ; + hFileMapping = 0 ; + } + if( hFile != INVALID_HANDLE_VALUE ) + { + CloseHandle( hFile ) ; + hFile = INVALID_HANDLE_VALUE ; + } + } + + +void PE_Debug :: DumpLineNumber( DumpBuffer& dumpBuffer, DWORD relativeAddress ) + { + PIMAGE_LINENUMBER line = BasedPtr( PIMAGE_LINENUMBER, COFFDebugInfo, + COFFDebugInfo->LvaToFirstLinenumber ) ; + DWORD lineCount = COFFDebugInfo->NumberOfLinenumbers ; + const DWORD none = (DWORD)-1 ; + DWORD maxAddr = 0 ; + DWORD lineNum = none ; + for( DWORD i=0; i < lineCount; i++ ) + { + if( line->Linenumber != 0 ) // A regular line number + { + // look for line with bigger address <= relativeAddress + if( line->Type.VirtualAddress <= relativeAddress && + line->Type.VirtualAddress > maxAddr ) + { + maxAddr = line->Type.VirtualAddress ; + lineNum = line->Linenumber ; + } + } + line++ ; + } + if( lineNum != none ) + dumpBuffer.Printf( " line %d\r\n", lineNum ) ; +// else +// dumpBuffer.Printf( " line \r\n" ) ; + } + + +const char* PE_Debug :: GetSymbolName( PIMAGE_SYMBOL sym ) + { + const int NAME_MAX_LEN = 64 ; + static char buf[ NAME_MAX_LEN ] ; + if( sym->N.Name.Short != 0 ) + { + strncpy( buf, (const char*)sym->N.ShortName, 8 ) ; + buf[ 8 ] = 0 ; + } + else + { + strncpy( buf, stringTable + sym->N.Name.Long, NAME_MAX_LEN ) ; + buf[ NAME_MAX_LEN - 1 ] = 0 ; + } + return( buf ) ; + } + +void unmangle(char *dst, const char *src) +{ + //strcpy( dst, src ); + //return; + + src++; + while( (*src) && (*src!=' ') && (*src!='@') ) { + *dst++ = *src++; + } + *dst++ = 0; +} + +#ifdef DUMPRAM + +typedef struct MemSymbol { + int section; + int offset; + int size; + char name[132]; +} MemSymbol; + +int Num_symbols = 0; +int Max_symbols = 0; +MemSymbol *Symbols; + +void InitSymbols() +{ + Num_symbols = 0; + Max_symbols = 5000; + Symbols = (MemSymbol *)malloc(Max_symbols*sizeof(MemSymbol)); + if ( !Symbols ) { + Max_symbols = 0; + } +} + +void Add_Symbol( int section, int offset, const char *name, char *module ) +{ + if ( Num_symbols >= Max_symbols ) { + return; + } + + MemSymbol * sym = &Symbols[Num_symbols++]; + + sym->section = section; + sym->offset = offset; + sym->size = -1; + + strcpy( sym->name, name ); + strcat( sym->name, "(" ); + strcat( sym->name, module ); + strcat( sym->name, ")" ); + +} + +int Sym_compare( const void *arg1, const void *arg2 ) +{ + MemSymbol * sym1 = (MemSymbol *)arg1; + MemSymbol * sym2 = (MemSymbol *)arg2; + + if ( sym1->section < sym2->section ) { + return -1; + } else if ( sym1->section > sym2->section ) { + return 1; + } else { + if ( sym1->offset > sym2->offset ) { + return 1; + } else { + return -1; + } + } +} + +int Sym_compare1( const void *arg1, const void *arg2 ) +{ + MemSymbol * sym1 = (MemSymbol *)arg1; + MemSymbol * sym2 = (MemSymbol *)arg2; + + if ( sym1->size < sym2->size ) { + return 1; + } else if ( sym1->size > sym2->size ) { + return -1; + } else { + return 0; + } +} + +void DumpSymbols() +{ + int i; + + qsort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare ); + + for (i=0;isection == sym2->section) ) { + sym1->size = sym2->offset-sym1->offset; + } else { + sym1->size = -1; + } + } + + qsort( Symbols, Num_symbols, sizeof(MemSymbol), Sym_compare1 ); + + + FILE *fp = fopen( "dump", "wt" ); + + fprintf( fp, "%-100s %10s %10s\n", "Name", "Size", "Total" ); + + int total_size = 0; + for (i=0;isize > 0 ) + total_size += sym->size; + fprintf( fp, "%-100s %10d %10d\n", sym->name, sym->size, total_size ); + } + + fclose(fp); + + free( Symbols ); + Symbols = NULL; + _asm int 3 +} +#endif + +void PE_Debug::DumpSymbolInfo( DumpBuffer& dumpBuffer, DWORD relativeAddress ) +{ + // Variables to keep track of function symbols + PIMAGE_SYMBOL currentSym = COFFSymbolTable ; + PIMAGE_SYMBOL fnSymbol = NULL ; + DWORD maxFnAddress = 0 ; + + #ifdef DUMPRAM + InitSymbols(); + #endif + + // Variables to keep track of file symbols + PIMAGE_SYMBOL fileSymbol = NULL ; + PIMAGE_SYMBOL latestFileSymbol = NULL ; + for ( int i = 0; i < COFFSymbolCount; i++ ) { + + // Look for .text section where relativeAddress belongs to. + // Keep track of the filename the .text section belongs to. + if ( currentSym->StorageClass == IMAGE_SYM_CLASS_FILE ) { + latestFileSymbol = currentSym; + } + + // Borland uses "CODE" instead of the standard ".text" entry + // Microsoft uses sections that only _begin_ with .text + const char* symName = GetSymbolName( currentSym ) ; + + if ( strnicmp( symName, ".text", 5 ) == 0 || strcmpi( symName, "CODE" ) == 0 ) { + if ( currentSym->Value <= relativeAddress ) { + PIMAGE_AUX_SYMBOL auxSym = (PIMAGE_AUX_SYMBOL)(currentSym + 1) ; + if ( currentSym->Value + auxSym->Section.Length >= relativeAddress ) { + fileSymbol = latestFileSymbol ; + } + } + } + + + // Look for the function with biggest address <= relativeAddress + BOOL isFunction = ISFCN( currentSym->Type ); // Type == 0x20, See WINNT.H + if ( isFunction && ( currentSym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL || currentSym->StorageClass == IMAGE_SYM_CLASS_STATIC ) ) { + + if ( currentSym->Value <= relativeAddress && currentSym->Value > maxFnAddress ) { + maxFnAddress = currentSym->Value ; + fnSymbol = currentSym ; + } + } + + #ifdef DUMPRAM + if ( !isFunction && (currentSym->SectionNumber>-1) ) { + if ( (symName[0]=='_' && symName[1]!='$') || (symName[0]=='?') ) { + + char pretty_module[1024]; + + if ( fileSymbol ) { + const char* auxSym = (const char*)(latestFileSymbol + 1) ; + char tmpFile[ VA_MAX_FILENAME_LEN ] ; + strcpy( tmpFile, auxSym ) ; + strcpy( pretty_module, tmpFile ); + char *p = pretty_module+strlen(pretty_module)-1; + // Move p to point to first letter of EXE filename + while( (*p!='\\') && (*p!='/') && (*p!=':') ) + p--; + p++; + if ( strlen(p) < 1 ) { + strcpy( pretty_module, "" ); + } else { + memmove( pretty_module, p, strlen(p)+1 ); + } + } else { + strcpy( pretty_module, "" ); + } + + Add_Symbol( currentSym->SectionNumber, currentSym->Value, symName, pretty_module ); + } + } + #endif + + // Advance counters, skip aux symbols + i += currentSym->NumberOfAuxSymbols ; + currentSym += currentSym->NumberOfAuxSymbols ; + currentSym++ ; + } + + #ifdef DUMPRAM + DumpSymbols(); + #endif + + // dump symbolic info if found + if ( fileSymbol ) { + const char* auxSym = (const char*)(fileSymbol + 1) ; + + if( strcmpi( latestFile, auxSym ) ) { + strcpy( latestFile, auxSym ) ; + //JAS dumpBuffer.Printf( " file: %s\r\n", auxSym ) ; + } + } else { + latestFile[ 0 ] = 0 ; + //JAS dumpBuffer.Printf( " file: unknown\r\n" ) ; + } + + if ( fnSymbol ) { + char tmp_name[1024]; + unmangle(tmp_name, GetSymbolName( fnSymbol ) ); + dumpBuffer.Printf( " %s()", tmp_name ) ; + } else { + dumpBuffer.Printf( " " ) ; + } +} + + +PIMAGE_SECTION_HEADER PE_Debug :: SectionHeaderFromName( const char* name ) + { + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( NT_Header ) ; + for( unsigned i = 0; i < NT_Header->FileHeader.NumberOfSections; i++ ) + { + if( strnicmp( (const char*)section->Name, name, IMAGE_SIZEOF_SHORT_NAME ) == 0 ) + return( section ) ; + else + section++ ; + } + return 0; + } + + +PIMAGE_COFF_SYMBOLS_HEADER PE_Debug :: GetDebugHeader() + { + // Some files have a wrong entry in the COFF header, so + // first check if the debug info exists at all + if( NT_Header->FileHeader.PointerToSymbolTable == 0 ) + return( 0 ) ; + DWORD debugDirRVA = NT_Header->OptionalHeader. + DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ]. + VirtualAddress; + if( debugDirRVA == 0 ) + return( 0 ) ; + + // The following values must be calculated differently for MS/Borland files + PIMAGE_DEBUG_DIRECTORY debugDir ; + DWORD size ; + + // Borland files have the debug directory at the beginning of a .debug section + PIMAGE_SECTION_HEADER debugHeader = SectionHeaderFromName( ".debug" ) ; + if( debugHeader && debugHeader->VirtualAddress == debugDirRVA ) + { + debugDir = (PIMAGE_DEBUG_DIRECTORY)(debugHeader->PointerToRawData + (DWORD)fileBase) ; + size = NT_Header->OptionalHeader. + DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].Size * + sizeof( IMAGE_DEBUG_DIRECTORY ) ; + } + else + // Microsoft debug directory is in the .rdata section + { + debugHeader = SectionHeaderFromName( ".rdata" ) ; + if( debugHeader == 0 ) + return( 0 ) ; + size = NT_Header->OptionalHeader. + DataDirectory[ IMAGE_DIRECTORY_ENTRY_DEBUG ].Size ; + DWORD offsetInto_rdata = debugDirRVA - debugHeader->VirtualAddress ; + debugDir = BasedPtr( PIMAGE_DEBUG_DIRECTORY, fileBase, + debugHeader->PointerToRawData + offsetInto_rdata ) ; + } + + // look for COFF debug info + DWORD debugFormats = size / sizeof( IMAGE_DEBUG_DIRECTORY ) ; + for( DWORD i = 0; i < debugFormats; i++ ) + { + if( debugDir->Type == IMAGE_DEBUG_TYPE_COFF ) + return( (PIMAGE_COFF_SYMBOLS_HEADER)((DWORD)fileBase + debugDir->PointerToRawData) ) ; + else + debugDir++ ; + } + return( NULL ) ; + } + + +void PE_Debug :: FindDebugInfo() + { + ClearDebugPtrs() ; + // Put everything into a try/catch in case the file has wrong fields + try + { + // Verify that fileBase is a valid pointer to a DOS header + if( fileBase->e_magic == IMAGE_DOS_SIGNATURE ) + { + // Get a pointer to the PE header + NT_Header = BasedPtr( PIMAGE_NT_HEADERS, fileBase, fileBase->e_lfanew ) ; + // Verify that NT_Header is a valid pointer to a NT header + if( NT_Header->Signature == IMAGE_NT_SIGNATURE ) + { + // Get a pointer to the debug header if any + COFFDebugInfo = GetDebugHeader() ; + // Get a pointer to the symbol table and retrieve the number of symbols + if( NT_Header->FileHeader.PointerToSymbolTable ) + COFFSymbolTable = + BasedPtr( PIMAGE_SYMBOL, fileBase, NT_Header->FileHeader.PointerToSymbolTable ) ; + COFFSymbolCount = NT_Header->FileHeader.NumberOfSymbols ; + // The string table starts right after the symbol table + stringTable = (const char*)(COFFSymbolTable + COFFSymbolCount) ; + } + } + } + catch( ... ) + { + // Header wrong, do nothing + } + } + + +void PE_Debug :: MapFileInMemory( const char* module ) + { + ClearFileCache() ; + hFile = CreateFile( module, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 ) ; + if( hFile != INVALID_HANDLE_VALUE ) + { + hFileMapping = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL ) ; + if( hFileMapping != 0 ) + fileBase = (PIMAGE_DOS_HEADER)MapViewOfFile( hFileMapping, FILE_MAP_READ, 0, 0, 0 ) ; + } + // NB: open files/mapping are closed later in ClearFileCache + } + + +int PE_Debug::DumpDebugInfo( DumpBuffer& dumpBuffer, const BYTE* caller, HINSTANCE hInstance ) +{ + // Avoid to open, map and looking for debug header/symbol table + // by caching the latest and comparing the actual module with + // the latest one. + static char module[ MAX_MODULENAME_LEN ] ; + GetModuleFileName( hInstance, module, MAX_MODULENAME_LEN ) ; + + // New module + if( strcmpi( latestModule, module ) ) { + strcpy( latestModule, module ); + //JAS dumpBuffer.Printf( "Module: %s\r\n", module ); + MapFileInMemory( module ); + FindDebugInfo(); + } + + char pretty_module[1024]; + + strcpy( pretty_module, module ); + char *p = pretty_module+strlen(pretty_module)-1; + // Move p to point to first letter of EXE filename + while( (*p!='\\') && (*p!='/') && (*p!=':') ) + p--; + p++; + if ( strlen(p) < 1 ) { + strcpy( pretty_module, "" ); + } else { + memmove( pretty_module, p, strlen(p)+1 ); + } + + if ( fileBase ) { + // Put everything into a try/catch in case the file has wrong fields + try { + DWORD relativeAddress = caller - (BYTE*)hInstance ; + // Dump symbolic information and line number if available + if( COFFSymbolCount != 0 && COFFSymbolTable != NULL ) { + DumpSymbolInfo( dumpBuffer, relativeAddress ) ; + if( COFFDebugInfo ) + DumpLineNumber( dumpBuffer, relativeAddress ) ; + return 1; + } else { + //dumpBuffer.Printf( "Call stack is unavailable, because there is\r\nno COFF debugging info in this module.\r\n" ) ; + //JAS dumpBuffer.Printf( " no debug information\r\n" ) ; + dumpBuffer.Printf( " %s %08x()\r\n", pretty_module, caller ) ; + return 0; + } + } catch( ... ) { + // Header wrong, do nothing + return 0; + } + } else { + dumpBuffer.Printf( " %s %08x()\r\n", pretty_module, caller ) ; + //JAS dumpBuffer.Printf( " module not accessible\r\n" ) ; + //JAS dumpBuffer.Printf( " address: %8X\r\n", caller ) ; + return 0; + } + + Int3(); + +} + + +void DumpCallsStack( DumpBuffer& dumpBuffer ) +{ + const char* separator = "------------------------------------------------------------------\r\n" ; + static PE_Debug PE_debug ; + + dumpBuffer.Printf( "\r\nCall stack:\r\n" ) ; + dumpBuffer.Printf( separator ) ; + + // The structure of the stack frames is the following: + // EBP -> parent stack frame EBP + // return address for this call ( = caller ) + // The chain can be navigated iteratively, after the + // initial value of EBP is loaded from the register + DWORD parentEBP, retval; + MEMORY_BASIC_INFORMATION mbi ; + HINSTANCE hInstance; + + int depth = 0; + + __asm MOV parentEBP, EBP + + do { + depth++; + if ( depth > 16 ) + break; + + if ( (parentEBP & 3) || IsBadReadPtr((DWORD*)parentEBP, sizeof(DWORD)) ) { + break; + } + parentEBP = *(DWORD*)parentEBP ; + + BYTE **NextCaller = ((BYTE**)parentEBP + 1); + + if (IsBadReadPtr(NextCaller, sizeof(BYTE *))) { + break; + } + + BYTE* caller = *NextCaller; // Error sometimes!!! + + // Skip the first EBP as it points to AssertionFailed, which is + // uninteresting for the user + + if ( depth > 1 ) { + + // Get the instance handle of the module where caller belongs to + retval = VirtualQuery( caller, &mbi, sizeof( mbi ) ) ; + + // The instance handle is equal to the allocation base in Win32 + hInstance = (HINSTANCE)mbi.AllocationBase ; + + if( ( retval == sizeof( mbi ) ) && hInstance ) { + if ( !PE_debug.DumpDebugInfo( dumpBuffer, caller, hInstance ) ) { + //break; + } + } else { + break ; // End of the call chain + } + } + } while( TRUE ) ; + + + dumpBuffer.Printf( separator ) ; + PE_debug.ClearReport() ; // Prepare for future calls +} + + +// This ought to be local to VerboseAssert, but it +// causes problems in Visual C++ (in the CRTL init phase) +static DumpBuffer dumpBuffer ; + +#endif //SHOW_CALL_STACK + + +char AssertText1[1024]; +char AssertText2[1024]; + +uint flags = MB_TASKMODAL|MB_SETFOREGROUND; + +extern void gr_force_windowed(); + +void dump_text_to_clipboard(char *text) +{ + int len = strlen(text)+1024; + + HGLOBAL h_text = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, len ); + if ( !h_text ) return; + char *ptr = (char *)GlobalLock(h_text); + if ( !ptr ) return; + + // copy then, if you find any \n's without \r's, then add in the \r. + char last_char = 0; + while( *text ) { + if ( (*text == '\n') && (last_char != '\r') ) { + *ptr++ = '\r'; + } + last_char = *text; + *ptr++ = last_char; + text++; + } + *ptr++ = 0; + GlobalUnlock(h_text); + OpenClipboard(NULL); + EmptyClipboard(); + SetClipboardData(CF_TEXT, h_text); + CloseClipboard(); +} + + +void _cdecl WinAssert(char * text, char * filename, int linenum ) +{ + int val; + + gr_force_windowed(); + + + sprintf( AssertText1, "Assert: %s\r\nFile: %s\r\nLine: %d", text, filename, linenum ); + + #ifdef SHOW_CALL_STACK + dumpBuffer.Clear(); + dumpBuffer.Printf( AssertText1 ); + dumpBuffer.Printf( "\r\n" ); + DumpCallsStack( dumpBuffer ) ; + dump_text_to_clipboard(dumpBuffer.buffer); + + dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" ); + dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel to exit.\r\n"); + + val = MessageBox(NULL, dumpBuffer.buffer, "Assertion Failed!", MB_OKCANCEL|flags ); + #else + val = MessageBox(NULL, AssertText1, "Assertion Failed!", MB_OKCANCEL|flags ); + #endif + + if (val == IDCANCEL) + exit(1); + +#ifndef INTERPLAYQA + Int3(); +#else + AsmInt3(); +#endif + + +} + +void _cdecl Error( char * filename, int line, char * format, ... ) +{ + int val; + va_list args; + + gr_force_windowed(); + + va_start(args, format); + vsprintf(AssertText1,format,args); + va_end(args); + sprintf(AssertText2,"Error: %s\r\nFile:%s\r\nLine: %d", AssertText1, filename, line ); + + #ifdef SHOW_CALL_STACK + dumpBuffer.Clear(); + dumpBuffer.Printf( AssertText2 ); + dumpBuffer.Printf( "\r\n" ); + DumpCallsStack( dumpBuffer ) ; + dump_text_to_clipboard(dumpBuffer.buffer); + + dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" ); + dumpBuffer.Printf( "\r\n\r\nUse Ok to break into Debugger, Cancel exits.\r\n"); + + val = MessageBox(NULL, dumpBuffer.buffer, "Error!", flags|MB_OKCANCEL ); + #else + strcat(AssertText2,"\r\n\r\nUse Ok to break into Debugger, Cancel exits.\r\n"); + val = MessageBox(NULL, AssertText2, "Error!", flags|MB_OKCANCEL ); + #endif + + if (val == IDCANCEL ) { + exit(1); + } else { +#ifndef INTERPLAYQA + Int3(); +#else + AsmInt3(); +#endif + } +} + +void _cdecl Warning( char * filename, int line, char * format, ... ) +{ +#ifndef NDEBUG + + int id; + + va_list args; + + gr_force_windowed(); + + va_start(args, format); + vsprintf(AssertText1,format,args); + va_end(args); + sprintf(AssertText2,"Warning: %s\r\nFile:%s\r\nLine: %d", AssertText1, filename, line ); + + + #ifdef SHOW_CALL_STACK + dumpBuffer.Clear(); + dumpBuffer.Printf( AssertText2 ); + dumpBuffer.Printf( "\r\n" ); + DumpCallsStack( dumpBuffer ) ; + dump_text_to_clipboard(dumpBuffer.buffer); + + dumpBuffer.Printf( "\r\n[ This info is in the clipboard so you can paste it somewhere now ]\r\n" ); + dumpBuffer.Printf("\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit"); + + id = MessageBox(NULL, dumpBuffer.buffer, "Warning!", MB_YESNOCANCEL|flags ); + #else + strcat(AssertText2,"\r\n\r\nUse Yes to break into Debugger, No to continue.\r\nand Cancel to Quit"); + id = MessageBox(NULL, AssertText2, "Warning!", MB_YESNOCANCEL|flags ); + #endif + if ( id == IDCANCEL ) + exit(1); + else if ( id == IDYES ) { +#ifndef INTERPLAYQA + Int3(); +#else + AsmInt3(); +#endif + } +#endif // NDEBUG +} + + + +//================= memory stuff +/* +char *format_mem( DWORD num ) +{ + if ( num < 1024 ) { + sprintf( tmp_mem, "%d bytes", num ); + } else if ( num < 1024*1024 ) { + sprintf( tmp_mem, "%.3f KB", (float)num/1024.0f ); + } else { + sprintf( tmp_mem, "%.3f MB", (float)(num/1024)/(1024.0f) ); + } + return tmp_mem; +} +*/ + +/* +void getmem() +{ + DWORD retval; + MEMORY_BASIC_INFORMATION mbi ; + HINSTANCE hInstance; + + MEMORYSTATUS ms; + + ms.dwLength = sizeof(MEMORYSTATUS); + GlobalMemoryStatus(&ms); + + printf( "Percent of memory in use: %d%%\n", ms.dwMemoryLoad ); + printf( "Bytes of physical memory: %s\n", format_mem(ms.dwTotalPhys) ); + printf( "Free physical memory bytes: %s\n", format_mem(ms.dwAvailPhys) ); // + printf( "Bytes of paging file: %s\n", format_mem(ms.dwTotalPageFile) ); // bytes of paging file + printf( "Free bytes of paging file: %s\n", format_mem(ms.dwAvailPageFile) ); // free bytes of paging file + printf( "User bytes of address space: %s\n", format_mem(ms.dwTotalVirtual) ); // + printf( "Free user bytes: %s\n", format_mem(ms.dwAvailVirtual) ); // free user bytes + + + // Get the instance handle of the module where caller belongs to + retval = VirtualQuery( getmem, &mbi, sizeof( mbi ) ) ; + + // The instance handle is equal to the allocation base in Win32 + hInstance = (HINSTANCE)mbi.AllocationBase ; + + if( ( retval == sizeof( mbi ) ) && hInstance ) { + printf( "Virtual Query succeeded...\n" ); + } else { + printf( "Virtual Query failed...\n" ); + } + +} +*/ + +#ifndef NDEBUG + +#include + +int TotalRam = 0; + +#define nNoMansLandSize 4 + +typedef struct _CrtMemBlockHeader +{ + struct _CrtMemBlockHeader * pBlockHeaderNext; + struct _CrtMemBlockHeader * pBlockHeaderPrev; + char * szFileName; + int nLine; + size_t nDataSize; + int nBlockUse; + long lRequest; + unsigned char gap[nNoMansLandSize]; + /* followed by: + * unsigned char data[nDataSize]; + * unsigned char anotherGap[nNoMansLandSize]; + */ +} _CrtMemBlockHeader; + +#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1) + +int __cdecl MyAllocHook( + int nAllocType, + void * pvData, + size_t nSize, + int nBlockUse, + long lRequest, + const char * szFileName, + int nLine + ) +{ + // char *operation[] = { "", "allocating", "re-allocating", "freeing" }; + // char *blockType[] = { "Free", "Normal", "CRT", "Ignore", "Client" }; + + if ( nBlockUse == _CRT_BLOCK ) // Ignore internal C runtime library allocations + return( TRUE ); + + _ASSERT( ( nAllocType > 0 ) && ( nAllocType < 4 ) ); + _ASSERT( ( nBlockUse >= 0 ) && ( nBlockUse < 5 ) ); + + if ( nAllocType == 3 ) { + _CrtMemBlockHeader *phd = pHdr(pvData); + nSize = phd->nDataSize; + } + +// mprintf(( "Total RAM = %d\n", TotalRam )); + +/* mprintf(( "Memory operation in %s, line %d: %s a %d-byte '%s' block (# %ld)\n", + szFileName, nLine, operation[nAllocType], nSize, + blockType[nBlockUse], lRequest )); + if ( pvData != NULL ) + mprintf(( " at %X", pvData )); + + mprintf(("\n" )); +*/ + + return( TRUE ); // Allow the memory operation to proceed +} + +void windebug_memwatch_init() +{ + //_CrtSetAllocHook(MyAllocHook); + TotalRam = 0; +} + +#endif + +#define NEW_MALLOC + + +int Watch_malloc = 0; +DCF_BOOL(watch_malloc, Watch_malloc ); + +HANDLE Heap = 0; + +#define HEAP_FLAG HEAP_NO_SERIALIZE +// #define HEAP_FLAG HEAP_GENERATE_EXCEPTIONS + +// Returns 0 if not enough RAM. +int vm_init(int min_heap_size) +{ + #ifndef NDEBUG + TotalRam = 0; + #endif + + #ifdef NEW_MALLOC + Heap = HeapCreate( HEAP_FLAG, min_heap_size, 0 ); + if ( Heap == NULL ) { + return 0; + } + #endif + return 1; +} + +char *clean_filename(char *name) +{ + char *p = name+strlen(name)-1; + // Move p to point to first letter of EXE filename + while( (*p!='\\') && (*p!='/') && (*p!=':') ) + p--; + p++; + + return p; +} + +#ifndef NDEBUG +void *vm_malloc( int size, char *filename, int line ) +#else +void *vm_malloc( int size ) +#endif +{ + #ifndef NDEBUG + if ( !Heap ) { + TotalRam += size; + + return _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__ ); + } + #endif + + void *ptr = HeapAlloc(Heap, HEAP_FLAG, size ); + + if ( ptr == NULL ) { + mprintf(( "HeapAlloc failed!!!!!!!!!!!!!!!!!!!\n" )); + + Error(LOCATION, "Out of memory. Try closing down other applications, increasing your\n" + "virtual memory size, or installing more physical RAM.\n"); + + } + #ifndef NDEBUG + int actual_size = HeapSize(Heap, HEAP_FLAG, ptr); + if ( Watch_malloc ) { + mprintf(( "Malloc %d bytes [%s(%d)]\n", actual_size, clean_filename(filename), line )); + } + TotalRam += actual_size; + #endif + return ptr; +} + +#ifndef NDEBUG +char *vm_strdup( const char *ptr, char *filename, int line ) +#else +char *vm_strdup( const char *ptr ) +#endif +{ + char *dst; + int len = strlen(ptr); + #ifndef NDEBUG + dst = (char *)vm_malloc( len+1, filename, line ); + #else + dst = (char *)vm_malloc( len+1 ); + #endif + strcpy( dst, ptr ); + return dst; +} + +#ifndef NDEBUG +void vm_free( void *ptr, char *filename, int line ) +#else +void vm_free( void *ptr ) +#endif +{ + if ( !ptr ) { + #ifndef NDEBUG + mprintf(("Why are you trying to free a NULL pointer? [%s(%d)]\n", clean_filename(filename), line)); + #else + mprintf(("Why are you trying to free a NULL pointer?\n")); + #endif + return; + } + + #ifndef NDEBUG + if ( !Heap ) { + #ifndef NDEBUG + _CrtMemBlockHeader *phd = pHdr(ptr); + int nSize = phd->nDataSize; + + TotalRam -= nSize; + #endif + + _free_dbg(ptr,_NORMAL_BLOCK); + return; + } + #endif + + #ifndef NDEBUG + int actual_size = HeapSize(Heap, HEAP_FLAG, ptr); + if ( Watch_malloc ) { + mprintf(( "Free %d bytes [%s(%d)]\n", actual_size, clean_filename(filename), line )); + } + TotalRam -= actual_size; + #endif + HeapFree( Heap, HEAP_FLAG, ptr ); + HeapCompact(Heap, HEAP_FLAG); +} + +void vm_free_all() +{ +} + + + + diff --git a/src/graphics/2d.cpp b/src/graphics/2d.cpp new file mode 100644 index 0000000..47bd57d --- /dev/null +++ b/src/graphics/2d.cpp @@ -0,0 +1,1363 @@ +/* + * $Logfile: /Freespace2/code/Graphics/2d.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Main file for 2d primitives. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 25 8/20/99 2:09p Dave + * PXO banner cycling. + * + * 24 8/16/99 9:45a Jefff + * changes to cursor management to allow a 2nd temporary cursor + * + * 23 7/27/99 3:52p Dave + * Make star drawing a bit more robust to help lame D3D cards. + * + * 22 7/18/99 12:32p Dave + * Randomly oriented shockwaves. + * + * 21 7/15/99 3:07p Dave + * 32 bit detection support. Mouse coord commandline. + * + * 20 7/14/99 9:42a Dave + * Put in clear_color debug function. Put in base for 3dnow stuff / P3 + * stuff + * + * 19 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 18 7/12/99 11:42a Jefff + * Made rectangle drawing smarter in D3D. Made plines draw properly on Ati + * Rage Pro. + * + * 17 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 16 7/02/99 3:05p Anoop + * Oops. Fixed g3_draw_2d_poly() so that it properly handles poly bitmap + * and LFB bitmap calls. + * + * 15 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 14 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 13 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 12 1/29/99 12:47a Dave + * Put in sounds for beam weapon. A bunch of interface screens (tech + * database stuff). + * + * 11 1/15/99 11:29a Neilk + * Fixed D3D screen/texture pixel formatting problem. + * + * 10 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * 9 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 8 12/18/98 1:49a Dave + * Fixed Fred initialization problem resulting from hi-res mode changes. + * + * 7 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 6 12/01/98 10:32a Johnson + * Fixed direct3d font problems. Fixed sun bitmap problem. Fixed direct3d + * starfield problem. + * + * 5 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 4 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 3 11/11/98 5:37p Dave + * Checkin for multiplayer testing. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 122 6/13/98 3:18p Hoffoss + * NOX()ed out a bunch of strings that shouldn't be translated. + * + * 121 5/23/98 11:26a Hoffoss + * Fixed bug where optimized code used EDX and my code trashed it. + * + * 120 5/22/98 11:09p John + * put in code to hopefull not crash cyrix cpus + * + * 119 5/20/98 9:45p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 118 5/20/98 12:59p Hoffoss + * Changed temporary crossfade code to just pop the image again. + * + * 117 5/16/98 1:18p John + * Made softtware DirectDraw reset palette after Alt+TAB. + * + * 116 5/14/98 5:42p John + * Revamped the whole window position/mouse code for the graphics windows. + * + * 115 5/08/98 10:49a John + * Made 'gr d' go into direct3d mode. + * + * 114 5/06/98 11:21p John + * Fixed a bitmap bug with Direct3D. Started adding new caching code into + * D3D. + * + * 113 4/21/98 9:28a John + * Added stub for cross-fade. + * + * 112 4/10/98 5:20p John + * Changed RGB in lighting structure to be ubytes. Removed old + * not-necessary 24 bpp software stuff. + * + * 111 4/06/98 12:55p John + * Upped the gamma for Fred. + * + * 110 3/25/98 8:07p John + * Restructured software rendering into two modules; One for windowed + * debug mode and one for DirectX fullscreen. + * + * 109 3/24/98 3:58p John + * Put in (hopefully) final gamma setting code. + * + * 108 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 107 2/25/98 2:37p John + * Made afterburner shake 2x less noticable. Made 'gr a' leave Glide mode + * properly. Made model_caching never work in hardware mode. + * + * 106 2/23/98 2:27p John + * Made int3,asserts, etc pass video through. + * + * 105 2/20/98 3:13p John + * Made popup save code print an error an exit out if 3d accelerated. + * + * 104 2/19/98 6:13p John + * Made Glide do texturing & zbuffering. + * + * 103 1/26/98 5:12p John + * Added in code for Pentium Pro specific optimizations. Speed up + * zbuffered correct tmapper about 35%. Speed up non-zbuffered scalers + * by about 25%. + * + * 102 1/14/98 11:39a Dave + * Polished up a bunch of popup support items. + * + * 101 1/10/98 1:14p John + * Added explanation to debug console commands + * + * 100 12/21/97 4:33p John + * Made debug console functions a class that registers itself + * automatically, so you don't need to add the function to + * debugfunctions.cpp. + * + * 99 12/04/97 12:09p John + * Made glows use scaler instead of tmapper so they don't rotate. Had to + * add a zbuffered scaler. + * + * 98 12/03/97 10:47a John + * added functions to save/restore entire screens. + * + * 97 12/02/97 3:59p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 96 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 95 11/24/97 11:20a John + * added new mode + * + * 94 11/14/97 3:54p John + * Added triple buffering. + * + * 93 11/14/97 12:30p John + * Fixed some DirectX bugs. Moved the 8-16 xlat tables into Graphics + * libs. Made 16-bpp DirectX modes know what bitmap format they're in. + * + * 92 10/09/97 5:23p John + * Added support for more 16-bpp functions + * + * 91 9/20/97 8:16a John + * Made .clr files go into the Cache directory. Replaced cfopen(name,NULL) + * to delete a file with cf_delete. + * + * 90 9/09/97 11:01a Sandeep + * fixed warning level 4 bugs + * + * 89 9/07/97 10:01p Lawrance + * add in support for animating mouse pointer + * + * 88 9/03/97 4:32p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 87 8/20/97 4:19p John + * added delay to hopefully fix mike's problems when getting int3's in + * fullscreen. + * + * 86 7/16/97 3:07p John + * + * 85 6/18/97 12:07p John + * fixed some color bugs + * + * 84 6/17/97 7:04p John + * added d3d support for gradients. + * fixed some color bugs by adding screen signatures instead of watching + * flags and palette changes. + * + * 83 6/17/97 12:03p John + * Moved color/alphacolor functions into their own module. Made all color + * functions be part of the low-level graphics drivers, not just the + * grsoft. + * + * 82 6/12/97 5:04p John + * Initial rev of Glide support + * + * 81 6/11/97 5:49p John + * Changed palette code to only recalculate alphacolors when needed, not + * when palette changes. + * + * 80 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 79 6/06/97 5:03p John + * fixed bug withalpha colors failing after gr_init + * + * 78 6/06/97 4:53p John + * made gr_init not bash current color + * + * 77 6/05/97 4:53p John + * First rev of new antialiased font stuff. + * + * 76 5/20/97 10:36a John + * Fixed problem with user bitmaps and direct3d caching. + * + * 75 5/16/97 9:11a John + * fixed bug that made Ctrl+Break in fullscreen hang + * + * 74 5/14/97 4:38p John + * Fixed print_screen bug. + * + * 73 5/14/97 2:10p John + * added rudimentary support for windowed direct3d. + * + * 72 5/14/97 10:53a John + * fixed some discrepencies between d3d and software palette setting. + * + * 71 5/13/97 4:39p John + * Added console function to set graphics modes. + * + * 70 5/13/97 12:39p John + * Got fullscreen mode working. + * + * 69 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 68 5/07/97 2:59p John + * Initial rev of D3D texturing. + * + * 67 5/01/97 3:32p John + * oops... forced 2d to always be in 8bpp, + * + * 66 5/01/97 3:23p John + * first hooks for some direct 3d setup stuff. + * + * 65 4/28/97 5:33p John + * fixed a newly introduced bug with fullscreen toggle. + * + * 64 4/28/97 4:46p John + * + * 63 4/22/97 12:20p John + * fixed more resource leaks + * + * 62 4/22/97 10:33a John + * fixed the 2d resource leaks that Alan found. + * + * 61 3/31/97 9:45a Allender + * start of new font stuff + * + * 60 3/26/97 10:52a Lawrance + * mouse always on in menus, disappears in gameplay after 1 second + * + * 59 3/14/97 3:55p John + * Made tiled tmapper not always be zbuffered. + * + * 58 3/13/97 9:09a Allender + * variable to allow trashing of the palette. kind of temporary code + * right now...checking needed for undefined references + * + * 57 3/12/97 2:51p John + * Added some test code for tmapper. + * + * 56 3/12/97 9:25a John + * fixed a bug with zbuffering. Reenabled it by default. + * + * 55 3/11/97 5:13p John + * made top up bitmaps work. + * + * 54 2/26/97 11:59a Allender + * comment out warning about display settings not being optimal + * + * 53 1/09/97 2:16p John + * took out the "Bing!" message + * + * 52 1/09/97 11:35a John + * Added some 2d functions to get/put screen images. + * + * 51 1/07/97 2:01p John + * Fairly fast zbuffering for object sorting. + * + * 50 1/06/97 2:44p John + * Added in slow (but correct) zbuffering + * + * 49 12/11/96 12:41p John + * Added new code to draw 3d laser using 2d ellipses. + * + * 48 12/03/96 5:42p John + * took out debug code. + * + * 47 12/03/96 5:42p John + * made gr_bitmap clip properly. + * + * 46 12/03/96 5:06p John + * Added code to draw our own cursor. + * + * 45 11/26/96 6:50p John + * Added some more hicolor primitives. Made windowed mode run as current + * bpp, if bpp is 8,16,or 32. + * + * 44 11/21/96 11:23a John + * fixed bug with previous. + * + * 43 11/21/96 11:21a John + * Made gr_get_string_size handle multi line text. + * Took out gr_get_multiline_string_size + * + * 42 11/21/96 11:06a John + * fixed warnings. + * + * 41 11/20/96 10:01a Hoffoss + * A few minor improvements. + * + * 40 11/19/96 2:47p Allender + * add support for xparent bitmaps in 32bpp + * + * 39 11/18/96 4:46p Allender + * changed warning message about bit depth to appear when requested + * doesn't equal actual + * + * 38 11/18/96 4:35p Allender + * new 16bpp gradient functions + * + * 37 11/18/96 3:09p John + * made gr_clear always clear to black. + * + * 36 11/18/96 1:48p Allender + * added 16 bit version of shader + * + * 35 11/18/96 12:36p John + * Added code to dump screen to a PCX file. + * + * 34 11/18/96 11:40a John + * Added faster gr_set_color method. + * + * 33 11/15/96 11:26a John + * Added code to clip text to clipping region. + * + * 32 11/15/96 11:26a Allender + * fixed up 16 bbp version of gr_bitmap + * + * 31 11/14/96 9:11a John + * Fixed bug that didn't offset the gr_string text by the current clip + * region's offset. + * + * 30 11/13/96 6:47p John + * Added gr_flip function. + * + * 29 11/13/96 10:10a John + * Increases MAX_WIDTH & HEIGHT for Jasen's massive 1600x1200 display. + * + * 28 11/07/96 6:19p John + * Added a bunch of 16bpp primitives so the game sort of runs in 16bpp + * mode. + * + * 27 10/30/96 10:36a Lawrance + * added gr_diamond function + * + * 26 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#ifndef PLAT_UNIX +#include +#include +#endif + +#include "osapi.h" +#include "2d.h" +#include "3d.h" +#include "bmpman.h" +#include "palman.h" +#include "font.h" +#include "grinternal.h" +#include "systemvars.h" +#include "cmdline.h" + +// 3dnow stuff +// #include "amd3d.h" + +// Includes for different rendering systems +#include "grsoft.h" +#include "grd3d.h" +#include "grglide.h" +#include "gropengl.h" +#include "grdirectdraw.h" + +screen gr_screen; + +color_gun Gr_red, Gr_green, Gr_blue, Gr_alpha; +color_gun Gr_t_red, Gr_t_green, Gr_t_blue, Gr_t_alpha; +color_gun Gr_ta_red, Gr_ta_green, Gr_ta_blue, Gr_ta_alpha; +color_gun *Gr_current_red, *Gr_current_green, *Gr_current_blue, *Gr_current_alpha; + + +ubyte Gr_original_palette[768]; // The palette +ubyte Gr_current_palette[768]; +char Gr_current_palette_name[128] = NOX("none"); + +// cursor stuff +int Gr_cursor = -1; +int Web_cursor_bitmap = -1; + +int Gr_inited = 0; + +// cpu types +int Gr_cpu = 0; +int Gr_amd3d = 0; +int Gr_katmai = 0; +int Gr_mmx = 0; + +uint Gr_signature = 0; + +float Gr_gamma = 1.8f; +int Gr_gamma_int = 180; +int Gr_gamma_lookup[256]; + +void gr_close() +{ + if ( !Gr_inited ) return; + + palette_flush(); + + switch( gr_screen.mode ) { + case GR_SOFTWARE: + gr_soft_cleanup(); + break; + case GR_DIRECTDRAW: + Int3(); + gr_directdraw_cleanup(); + break; + case GR_DIRECT3D: + gr_d3d_cleanup(); + break; + case GR_GLIDE: + gr_glide_cleanup(); + break; + case GR_OPENGL: + Int3(); + gr_opengl_cleanup(); + break; + default: + Int3(); // Invalid graphics mode + } + + gr_font_close(); + + Gr_inited = 0; +} + +//XSTR:OFF +DCF(gr,"Changes graphics mode") +{ +#ifndef HARDWARE_ONLY + int mode = gr_screen.mode; + + if ( Dc_command ) { + dc_get_arg(ARG_STRING); + + if ( !strcmp( Dc_arg, "a")) { + Int3(); + mode = GR_SOFTWARE; + } else if ( !strcmp( Dc_arg, "b")) { + Int3(); + mode = GR_DIRECTDRAW; + } else if ( !strcmp( Dc_arg, "d")) { + mode = GR_DIRECT3D; + } else if ( !strcmp( Dc_arg, "g")) { + mode = GR_GLIDE; + } else if ( !strcmp( Dc_arg, "o")) { + Int3(); + mode = GR_OPENGL; + } else { + // print usage, not stats + Dc_help = 1; + } + + /* + if ( mode != gr_screen.mode ) { + dc_printf( "Setting new video mode...\n" ); + int errcode = gr_init( gr_screen.max_w, gr_screen.max_h, mode ); + if (errcode) { + dc_printf( "Error %d. Graphics unchanged.\n", errcode ); + } + } + */ + } + + if ( Dc_help ) { + dc_printf( "Usage: gr mode\n" ); + dc_printf( "The options can be:\n" ); + dc_printf( "Macros: A=software win32 window (obsolete)\n" ); + dc_printf( " B=software directdraw fullscreen (obsolete)\n" ); + dc_printf( " D=Direct3d\n" ); + dc_printf( " G=Glide\n" ); + dc_printf( " O=OpenGl (obsolete)\n" ); + Dc_status = 0; // don't print status if help is printed. Too messy. + } + + if ( Dc_status ) { + switch( gr_screen.mode ) { + case GR_SOFTWARE: + Int3(); + dc_printf( "Win32 software windowed\n" ); + break; + case GR_DIRECTDRAW: + Int3(); + dc_printf( "DirectDraw software windowed\n" ); + break; + case GR_DIRECT3D: + dc_printf( "Direct3D\n" ); + break; + case GR_GLIDE: + dc_printf( "3Dfx Glide\n" ); + break; + case GR_OPENGL: + Int3(); + dc_printf( "OpenGl\n" ); + break; + default: + Int3(); // Invalid graphics mode + } + } +#endif +} +//XSTR:ON + +// set screen clear color +DCF(clear_color, "set clear color r, g, b") +{ + int r, g, b; + + dc_get_arg(ARG_INT); + r = Dc_arg_int; + dc_get_arg(ARG_INT); + g = Dc_arg_int; + dc_get_arg(ARG_INT); + b = Dc_arg_int; + + // set the color + gr_set_clear_color(r, g, b); +} + +void gr_set_palette_internal( char *name, ubyte * palette, int restrict_font_to_128 ) +{ + if ( palette == NULL ) { + // Create a default palette + int r,g,b,i; + i = 0; + + for (r=0; r<6; r++ ) + for (g=0; g<6; g++ ) + for (b=0; b<6; b++ ) { + Gr_current_palette[i*3+0] = (unsigned char)(r*51); + Gr_current_palette[i*3+1] = (unsigned char)(g*51); + Gr_current_palette[i*3+2] = (unsigned char)(b*51); + i++; + } + for ( i=216;i<256; i++ ) { + Gr_current_palette[i*3+0] = (unsigned char)((i-216)*6); + Gr_current_palette[i*3+1] = (unsigned char)((i-216)*6); + Gr_current_palette[i*3+2] = (unsigned char)((i-216)*6); + } + memmove( Gr_original_palette, Gr_current_palette, 768 ); + } else { + memmove( Gr_original_palette, palette, 768 ); + memmove( Gr_current_palette, palette, 768 ); + } + +// mprintf(("Setting new palette\n" )); + + if ( Gr_inited ) { + if (gr_screen.gf_set_palette) { + (*gr_screen.gf_set_palette)(Gr_current_palette, restrict_font_to_128 ); + + // Since the palette set code might shuffle the palette, + // reload it into the source palette + if ( palette ) + memmove( palette, Gr_current_palette, 768 ); + } + + // Update Palette Manager tables + memmove( gr_palette, Gr_current_palette, 768 ); + palette_update(name, restrict_font_to_128); + } +} + + +void gr_set_palette( char *name, ubyte * palette, int restrict_font_to_128 ) +{ + char *p; + palette_flush(); + strcpy( Gr_current_palette_name, name ); + p = strchr( Gr_current_palette_name, '.' ); + if ( p ) *p = 0; + gr_screen.signature = Gr_signature++; + gr_set_palette_internal( name, palette, restrict_font_to_128 ); +} + + +//void gr_test(); + +#define CPUID _asm _emit 0fh _asm _emit 0a2h + +// ----------------------------------------------------------------------- +// Returns cpu type. +void gr_detect_cpu(int *cpu, int *mmx, int *amd3d, int *katmai ) +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + DWORD RegEDX; + DWORD RegEAX; + + // Set defaults + *cpu = 0; + *mmx = 0; + *amd3d = 0; + *katmai = 0; + + char cpu_vender[16]; + memset( cpu_vender, 0, sizeof(cpu_vender) ); + + _asm { + + // Check for prescence of + push eax + push ebx + push ecx + push edx + + pushfd // get extended flags + pop eax + mov ebx, eax // save current flags + xor eax, 200000h // toggle bit 21 + push eax // push new flags on stack + popfd // flags updated now in flags + pushfd // get extended flags + pop eax // store extended flags in eax + xor eax, ebx // if bit 21 r/w then eax <> 0 + je no_cpuid + + mov eax, 0 // setup CPUID to return vender id + CPUID // code bytes = 0fh, 0a2h + mov DWORD PTR cpu_vender[0], ebx + mov DWORD PTR cpu_vender[4], edx + mov DWORD PTR cpu_vender[8], ecx + + mov eax, 1 // setup CPUID to return features + + CPUID // code bytes = 0fh, 0a2h + + mov RegEAX, eax // family, etc returned in eax + mov RegEDX, edx // features returned in edx + jmp done_checking_cpuid + + +no_cpuid: + mov RegEAX, 4<<8 // family, etc returned in eax + mov RegEDX, 0 // features returned in edx + +done_checking_cpuid: + pop edx + pop ecx + pop ebx + pop eax + + } + + + + //RegEAX . Bits 11:8 is family + *cpu = (RegEAX >>8) & 0xF; + + if ( *cpu < 5 ) { + *cpu = 4; // processor does not support CPUID + *mmx = 0; + } + + //RegEAX . Bits 11:8 is family + *cpu = (RegEAX >>8) & 0xF; + + // Check for MMX + BOOL retval = TRUE; + if (RegEDX & 0x800000) // bit 23 is set for MMX technology + { + + __try { _asm emms } // try executing an MMX instruction "emms" + + __except(EXCEPTION_EXECUTE_HANDLER) { retval = FALSE; } + + } else { + retval = FALSE; + } + if ( retval ) { + *mmx = 1; // processor supports CPUID but does not support MMX technology + } + + // Check for Katmai + if (RegEDX & (1<<25) ) // bit 25 is set for Katmai technology + { + *katmai = 1; + } + + // Check for Amd 3dnow + /* + if ( !stricmp( cpu_vender, NOX("AuthenticAMD")) ) { + + _asm { + mov eax, 0x80000000 // setup CPUID to return extended number of functions + + CPUID // code bytes = 0fh, 0a2h + + mov RegEAX, eax // highest extended function value + } + + if ( RegEAX > 0x80000000 ) { + + _asm { + mov eax, 0x80000001 // setup CPUID to return extended flags + + CPUID // code bytes = 0fh, 0a2h + + mov RegEAX, eax // family, etc returned in eax + mov RegEDX, edx // flags in edx + } + + if (RegEDX & 0x80000000) // bit 31 is set for AMD-3D technology + { + // try executing some 3Dnow instructions + __try { + + float x = (float)1.25; + float y = (float)1.25; + float z; + + _asm { + movd mm1, x + movd mm2, y + PFMUL(AMD_M1, AMD_M2); + movd z, mm1 + femms + emms + } + + int should_be_156 = int(z*100); + + if ( should_be_156 == 156 ) { + *amd3d = 1; + } + + } + + __except(EXCEPTION_EXECUTE_HANDLER) { } + } + + } + } + */ +#endif +} + +// -------------------------------------------------------------------------- + +int gr_init(int res, int mode, int depth, int fred_x, int fred_y) +{ + int first_time = 0; + int max_w, max_h; + + gr_detect_cpu(&Gr_cpu, &Gr_mmx, &Gr_amd3d, &Gr_katmai ); + + mprintf(( "GR_CPU: Family %d, MMX=%s\n", Gr_cpu, (Gr_mmx?"Yes":"No") )); + +// gr_test(); + + if ( !Gr_inited ) + atexit(gr_close); + + // If already inited, shutdown the previous graphics + if ( Gr_inited ) { + switch( gr_screen.mode ) { + case GR_SOFTWARE: + gr_soft_cleanup(); + break; + case GR_DIRECTDRAW: + Int3(); + gr_directdraw_cleanup(); + break; + case GR_DIRECT3D: + gr_d3d_cleanup(); + break; + case GR_GLIDE: + gr_glide_cleanup(); + break; + case GR_OPENGL: + Int3(); + gr_opengl_cleanup(); + break; + default: + Int3(); // Invalid graphics mode + } + } else { + first_time = 1; + } + +#if defined(HARDWARE_ONLY) + if(!Fred_running && !Pofview_running && !Nebedit_running && !Is_standalone){ + if((mode != GR_GLIDE) && (mode != GR_DIRECT3D)){ + mprintf(("Forcing glide startup!\n")); + mode = GR_GLIDE; + } + } +#endif + + D3D_enabled = 0; + Gr_inited = 1; + + max_w = -1; + max_h = -1; + if(!Fred_running && !Pofview_running){ + // set resolution based on the res type + switch(res){ + case GR_640: + max_w = 640; + max_h = 480; + break; + + case GR_1024: + max_w = 1024; + max_h = 768; + break; + + default : + Int3(); + } + } else { + max_w = fred_x; + max_h = fred_y; + } + + // Make w a multiple of 8 + max_w = ( max_w / 8 )*8; + if ( max_w < 8 ) max_w = 8; + if ( max_h < 8 ) max_h = 8; + + memset( &gr_screen, 0, sizeof(screen) ); + + gr_screen.signature = Gr_signature++; + gr_screen.mode = mode; + gr_screen.res = res; + gr_screen.max_w = max_w; + gr_screen.max_h = max_h; + gr_screen.aspect = 1.0f; // Normal PC screen + gr_screen.offset_x = 0; + gr_screen.offset_y = 0; + gr_screen.clip_left = 0; + gr_screen.clip_top = 0; + gr_screen.clip_right = gr_screen.max_w - 1; + gr_screen.clip_bottom = gr_screen.max_h - 1; + gr_screen.clip_width = gr_screen.max_w; + gr_screen.clip_height = gr_screen.max_h; + + switch( gr_screen.mode ) { + case GR_SOFTWARE: + Assert(Fred_running || Pofview_running || Is_standalone || Nebedit_running); + gr_soft_init(); + break; + case GR_DIRECTDRAW: + Int3(); + gr_directdraw_init(); + break; + case GR_DIRECT3D: + // we only care about possible 32 bit stuff here + Cmdline_force_32bit = 0; + if(depth == 32){ + Cmdline_force_32bit = 1; + } + + gr_d3d_init(); + + // bad startup - stupid D3D + extern int D3D_inited; + if(!D3D_inited){ + Gr_inited = 0; + return 1; + } + + break; + case GR_GLIDE: + // if we're in high-res. force polygon interface + if(gr_screen.res == GR_1024){ + Gr_bitmap_poly = 1; + } + gr_glide_init(); + break; + case GR_OPENGL: + Int3(); + gr_opengl_init(); + break; + default: + Int3(); // Invalid graphics mode + } + + memmove( Gr_current_palette, Gr_original_palette, 768 ); + gr_set_palette_internal(Gr_current_palette_name, Gr_current_palette,0); + + gr_set_gamma(Gr_gamma); + + if ( Gr_cursor == -1 ){ + Gr_cursor = bm_load( "cursor" ); + } + + // load the web pointer cursor bitmap + if (Web_cursor_bitmap < 0) { + int nframes; // used to pass, not really needed (should be 1) + Web_cursor_bitmap = bm_load_animation("cursorweb", &nframes); + Assert(Web_cursor_bitmap >= 0); // if bitmap didnt load, thats not good (this is protected for in release tho) + } + + gr_set_color(0,0,0); + + gr_set_clear_color(0, 0, 0); + + // Call some initialization functions + gr_set_shader(NULL); + + return 0; +} + +void gr_force_windowed() +{ + if ( !Gr_inited ) return; + + switch( gr_screen.mode ) { + case GR_SOFTWARE: + { + extern void gr_soft_force_windowed(); + gr_soft_force_windowed(); + } + break; + case GR_DIRECTDRAW: + { + Int3(); + extern void gr_directdraw_force_windowed(); + gr_directdraw_force_windowed(); + } + break; + case GR_DIRECT3D: + break; + case GR_GLIDE: + { + extern void gr_glide_force_windowed(); + gr_glide_force_windowed(); + } + break; + case GR_OPENGL: + Int3(); + break; + + default: + Int3(); // Invalid graphics mode + } + + if ( Os_debugger_running ) +#ifdef PLAT_UNIX + usleep(1); +#else + Sleep(1000); +#endif + +} + +void gr_activate(int active) +{ + if ( !Gr_inited ) return; + + switch( gr_screen.mode ) { + case GR_SOFTWARE: + { + extern void gr_soft_activate(int active); + gr_soft_activate(active); + return; + } + break; + case GR_DIRECTDRAW: + { + Int3(); + extern void gr_dd_activate(int active); + gr_dd_activate(active); + return; + } + break; + case GR_DIRECT3D: + { + extern void gr_d3d_activate(int active); + gr_d3d_activate(active); + return; + } + break; + case GR_GLIDE: + { + extern void gr_glide_activate(int active); + gr_glide_activate(active); + return; + } + break; + case GR_OPENGL: + Int3(); + break; + default: + Int3(); // Invalid graphics mode + } + +} + +// ----------------------------------------------------------------------- +// gr_set_cursor_bitmap() +// +// Set the bitmap for the mouse pointer. This is called by the animating mouse +// pointer code. +// +// The lock parameter just locks basically disables the next call of this function that doesnt +// have an unlock feature. If adding in more cursor-changing situations, be aware of +// unexpected results. You have been warned. +// +// TODO: investigate memory leak of original Gr_cursor bitmap when this is called +void gr_set_cursor_bitmap(int n, int lock) +{ + static int locked = 0; + Assert(n >= 0); + + if (!locked || (lock == GR_CURSOR_UNLOCK)) { + Gr_cursor = n; + } else { + locked = 0; + } + + if (lock == GR_CURSOR_LOCK) { + locked = 1; + } +} + +// retrieves the current bitmap +// used in UI_GADGET to save/restore current cursor state +int gr_get_cursor_bitmap() +{ + return Gr_cursor; +} + + +int Gr_bitmap_poly = 0; +DCF(bmap, "") +{ + Gr_bitmap_poly = !Gr_bitmap_poly; + + if(Gr_bitmap_poly){ + dc_printf("Using poly bitmaps\n"); + } else { + dc_printf("Using LFB bitmaps\n"); + } +} + +// new bitmap functions +void gr_bitmap(int x, int y) +{ + int section_x, section_y; + int x_line, y_line; + int w, h; + + // d3d and glide support texture poly shiz + if(((gr_screen.mode == GR_DIRECT3D) || (gr_screen.mode == GR_GLIDE)) && Gr_bitmap_poly){ + int idx, s_idx; + // float u_scale, v_scale; + bitmap_section_info *sections; + + // render all sections + bm_get_info(gr_screen.current_bitmap, &w, &h, NULL, NULL, NULL, §ions); + y_line = 0; + section_y = 0; + for(idx=0; idxnum_y; idx++){ + x_line = 0; + for(s_idx=0; s_idxnum_x; s_idx++){ + // get the section as a texture in vram + gr_set_bitmap(gr_screen.current_bitmap, gr_screen.current_alphablend_mode, gr_screen.current_bitblt_mode, gr_screen.current_alpha, s_idx, idx); + + // determine the width and height of this section + bm_get_section_size(gr_screen.current_bitmap, s_idx, idx, §ion_x, §ion_y); + + // draw as a poly + g3_draw_2d_poly_bitmap(x + x_line, y + y_line, section_x, section_y, TMAP_FLAG_BITMAP_SECTION); + x_line += section_x; + } + y_line += section_y; + } + + // done. whee! + return; + } + + // old school bitmaps + switch(gr_screen.mode){ + case GR_SOFTWARE: + case GR_DIRECTDRAW: + grx_bitmap(x, y); + break; + + case GR_DIRECT3D: + gr_d3d_bitmap(x, y); + break; + + case GR_GLIDE: + gr_glide_bitmap(x, y); + break; + + case GR_OPENGL: + gr_opengl_bitmap(x, y); + break; + } +} + +void gr_bitmap_ex(int x, int y, int w, int h, int sx, int sy) +{ + switch(gr_screen.mode){ + case GR_SOFTWARE: + case GR_DIRECTDRAW: + grx_bitmap_ex(x, y, w, h, sx, sy); + break; + + case GR_DIRECT3D: + gr_d3d_bitmap_ex(x, y, w, h, sx, sy); + break; + + case GR_GLIDE: + gr_glide_bitmap_ex(x, y, w, h, sx, sy); + break; + + case GR_OPENGL: + gr_opengl_bitmap_ex(x, y, w, h, sx, sy); + } +} + +// given endpoints, and thickness, calculate coords of the endpoint +void gr_pline_helper(vector *out, vector *in1, vector *in2, int thickness) +{ + vector slope; + + // slope of the line + if(vm_vec_same(in1, in2)){ + slope = vmd_zero_vector; + } else { + vm_vec_sub(&slope, in2, in1); + float temp = -slope.x; + slope.x = slope.y; + slope.y = temp; + vm_vec_normalize(&slope); + } + + // get the points + vm_vec_scale_add(out, in1, &slope, (float)thickness); +} + +// special function for drawing polylines. this function is specifically intended for +// polylines where each section is no more than 90 degrees away from a previous section. +// Moreover, it is _really_ intended for use with 45 degree angles. +void gr_pline_special(vector **pts, int num_pts, int thickness) +{ + vector s1, s2, e1, e2, dir; + vector last_e1, last_e2; + vertex v[4]; + vertex *verts[4] = {&v[0], &v[1], &v[2], &v[3]}; + int saved_zbuffer_mode, idx; + int started_frame = 0; + + // Assert(0); + + // if we have less than 2 pts, bail + if(num_pts < 2){ + return; + } + + extern int G3_count; + if(G3_count == 0){ + g3_start_frame(1); + started_frame = 1; + } + + // turn off zbuffering + saved_zbuffer_mode = gr_zbuffer_get(); + gr_zbuffer_set(GR_ZBUFF_NONE); + + // turn off culling + gr_set_cull(0); + + // draw each section + last_e1 = vmd_zero_vector; + last_e2 = vmd_zero_vector; + for(idx=0; idx 0){ + // stuff coords + v[0].sx = (float)ceil(s1.x); + v[0].sy = (float)ceil(s1.y); + v[0].sw = 0.0f; + v[0].u = 0.5f; + v[0].v = 0.5f; + v[0].flags = PF_PROJECTED; + v[0].codes = 0; + v[0].r = gr_screen.current_color.red; + v[0].g = gr_screen.current_color.green; + v[0].b = gr_screen.current_color.blue; + + v[1].sx = (float)ceil(s2.x); + v[1].sy = (float)ceil(s2.y); + v[1].sw = 0.0f; + v[1].u = 0.5f; + v[1].v = 0.5f; + v[1].flags = PF_PROJECTED; + v[1].codes = 0; + v[1].r = gr_screen.current_color.red; + v[1].g = gr_screen.current_color.green; + v[1].b = gr_screen.current_color.blue; + + + v[2].sx = (float)ceil(last_e2.x); + v[2].sy = (float)ceil(last_e2.y); + v[2].sw = 0.0f; + v[2].u = 0.5f; + v[2].v = 0.5f; + v[2].flags = PF_PROJECTED; + v[2].codes = 0; + v[2].r = gr_screen.current_color.red; + v[2].g = gr_screen.current_color.green; + v[2].b = gr_screen.current_color.blue; + + g3_draw_poly_constant_sw(3, verts, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB, 0.1f); + } + + // store our endpoints + last_e1 = e1; + last_e2 = e2; + } + + if(started_frame){ + g3_end_frame(); + } + + // restore zbuffer mode + gr_zbuffer_set(saved_zbuffer_mode); + + // restore culling + gr_set_cull(1); +} diff --git a/src/graphics/aaline.cpp b/src/graphics/aaline.cpp new file mode 100644 index 0000000..73cdff1 --- /dev/null +++ b/src/graphics/aaline.cpp @@ -0,0 +1,677 @@ +/* + * $Logfile: /Freespace2/code/Graphics/aaline.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to draw antialiased lines + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 12/02/98 5:47p Dave + * Put in interface xstr code. Converted barracks screen to new format. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 13 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 12 3/24/98 4:03p Lawrance + * JOHN: Fix up outline drawing code to support different colors + * + * 11 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 10 1/19/98 6:15p John + * Fixed all my Optimized Build compiler warnings + * + * 9 11/30/97 4:26p John + * Added 32-bpp antialiased line. Took gamma out of alphacolor + * calculation. + * + * 8 11/29/97 2:06p John + * added mode 16-bpp support + * + * 7 10/20/97 8:47a John + * fixed gr_lock bug in aaline + * + * 6 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 5 10/14/97 8:08a John + * added a bunch more 16 bit support + * + * 4 10/04/97 11:27a John + * took out debug code + * + * 3 10/03/97 9:50a John + * enabled antialiasing lines in alphacolor set. + * + * 2 10/03/97 9:10a John + * added better antialiased line drawer + * + * 1 10/03/97 9:07a John + * + * $NoKeywords: $ + */ + +/* + + Code for drawing antialiased lines. Taken from some code + published in the Journal of Graphic Tools at www.acm.org/jgt + + Here is the README that came with the source code: + + Sample code to draw antialiased lines as described in the Journal of + Graphic Tools article High Quality Hardware Line Antialiasing by + Scott R. Nelson of Sun Microsystems. + + The code is written in C and designed to run on any machine with the + addition of a proper "display" module. Currently, display modules + exist for Macintosh, Unix, and Wintel machines. Thanks to Sanjay Gupta + (sanjay.gupta@eng.sun.com) for the Unix X11 display code and Chris + Babcock (babcock@rtp.idt.com) for the Windows code. + + This code is not 100% bug free and is definitely not optimized for + performance. It does, however, illustrate all of the points made in + the JGT article. +*/ + + +#include +#include + +#include "pstypes.h" +#include "2d.h" +#include "line.h" +#include "grinternal.h" +#include "palman.h" + +// Convert from floating-point to internal fixed-point formats +#define ONE_XY (long int) 0x00100000 +#define FIX_XY_SHIFT (long int) 20 +#define ONEHALF_XY (long int) 0x00080000 +#define ONE_Z (long int) 0x40000000 +#define ONE_RGB (long int) 0x40000000 +#define ONE_16 (long int) 0x4000 + +#define FLOAT_TO_FIX_XY(x) ((long int) ((x) * (float) ONE_XY)) + +#define FLOAT_TO_FIX_RGB(x) ((long int) ((x) * (float) ONE_RGB)) +#define FLOAT_TO_FIX_16(x) ((long int) ((x) * (float) ONE_16)) +#define FIX_TO_INT_XY(x) ((x) >> FIX_XY_SHIFT) +#define FIX_16_TO_FLOAT(x) ((float) (x) / (float) ONE_16) +#define FIX_TO_FLOAT_XY(x) ((float) (x) / (float) ONE_XY) +#define FIX_TO_FLOAT_RGB(x) ((float) (x) / (float) ONE_RGB) + +// Get fractional part, next lowest integer part +#define FRACT_XY(x) ((x) & (long int) 0x000fffff) +#define FLOOR_XY(x) ((x) & (long int) 0xfff00000) +#define FIX_XY_TO_INT(x) ((long int) (x) >> (long int) FIX_XY_SHIFT) + +// Sizes for tables in Draw +#define FILTER_WIDTH 0.75 // Line filter width adjustment // .75 // .5 works good with 5.0 gamma +#define F_TABLE_SIZE 64 // Filter table size +#define SC_TABLE_SIZE 32 // Slope correction table size +#define SRT_INT 5 // Sqrt table index integer bits +#define SRT_FRACT 4 // ...fraction bits +#define SR_INT 3 // Square root result integer bits +#define SR_FRACT 5 // ...fraction bits +#define SR_TABLE_SIZE (1 << (SRT_INT + SRT_FRACT)) +#define INV_FILTER 47 + +#define EP_MASK (long int) 0x000f0000u // AA line end-point filter mask +#define EP_SHIFT 13u // Number of bits to shift end-point + + +typedef long int fix_xy; // S11.20 + +// One vertex at any of the various stages of the pipeline + +typedef struct aa_vertex { + float x, y; +} aa_vertex; + +// All values needed to draw one line +typedef struct aa_setup_line { + int x_major; + int negative; + + fix_xy vs; // Starting point + fix_xy us; + fix_xy ue; // End (along major axis) + fix_xy dvdu; // Delta for minor axis step + +} aa_setup_line; + + +// Tables that need to be initialized +long int slope_corr_table[SC_TABLE_SIZE]; +long int filter_table[F_TABLE_SIZE]; +long int sqrt_table[SR_TABLE_SIZE]; + +ubyte new_table[F_TABLE_SIZE*512]; + +int aaline_inited = 0; + +// Initialize the tables normally found in ROM in the hardware. +void aaline_init_tables() +{ + int i,j; // Iterative counter + double m; // Slope + double d; // Distance from center of curve + double v; // Value to put in table + double sr; // The square root value + + aaline_inited = 1; + + // Build slope correction table. The index into this table + // is the truncated 5-bit fraction of the slope used to draw + // the line. Round the computed values here to get the closest + // fit for all slopes matching an entry. + + for (i = 0; i < SC_TABLE_SIZE; i++) { + // Round and make a fraction + m = ((double) i + 0.5) / (float) SC_TABLE_SIZE; + v = sqrt(m * m + 1) * 0.707106781; /* (m + 1)^2 / sqrt(2) */ + slope_corr_table[i] = (long int) (v * 256.0); + } + + // Build the Gaussian filter table, round to the middle of the sample region. + for (i = 0; i < F_TABLE_SIZE; i++) { + d = ((double) i + 0.5) / (float) (F_TABLE_SIZE / 2.0); + d = d / FILTER_WIDTH; + v = 1.0 / exp(d * d); // Gaussian function + filter_table[i] = (long int) (v * 256.0); + } + + for ( i=0; i<512; i++ ) { + long int corr_slope = i<<8; + for (j=0; j> (16+4)); + if (new_table[i*F_TABLE_SIZE+j]==15 ) { + // HACK!!! Account for "glass" pixel for hud bitmaps. + new_table[i*F_TABLE_SIZE+j] = 14; + } + } + } + + + // Build the square root table for big dots. + for (i = 0; i < SR_TABLE_SIZE; i++) { + v = (double) ((i << 1) + 1) / (double) (1 << (SRT_FRACT + 1)); + sr = sqrt(v); + sqrt_table[i] = (long int) (sr * (double) (1 << SR_FRACT)); + } + + +} + + + +// Multiply a fixed-point number by a s11.20 fixed-point +// number. The actual multiply uses less bits for the +// multiplier, since it always represents a fraction +// less than 1.0 and less total bits are sufficient. +// Some of the steps here are not needed. This was originally +// written to simulate exact hardware behavior. +long int fix_xy_mult(long int oa, fix_xy ob) +{ + int retval; + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + mov edx, oa + mov eax, ob + imul edx + shrd eax,edx,20 + mov retval, eax + } +#endif + return retval; +} + + + +// Draw one span of an antialiased line (for horizontal lines). +void draw_aa_hspan8(fix_xy x, fix_xy y, long int ep_corr, long int slope) +{ +#ifndef HARDWARE_ONLY + long int sample_dist; // Distance from line to sample point + long int filter_index; // Index into filter table + long int i; // Count pixels across span + long int index; // Final filter table index + int a; // Alpha + + sample_dist = (FRACT_XY(y) >> (FIX_XY_SHIFT - 5)) - 16; + y = y - ONE_XY; + filter_index = sample_dist + 32; + + + int yi = FIX_XY_TO_INT( y ); + int xi = FIX_XY_TO_INT( x ); + + if ( xi < gr_screen.clip_left ) return; + if ( xi > gr_screen.clip_right ) return; + + int clipped = 0; + + if ( yi < gr_screen.clip_top ) clipped++; + if ( yi+3 > gr_screen.clip_bottom ) clipped++; + + long int corr_slope = (slope * ep_corr) & 0x1ff00; + + ubyte * lookup = (ubyte *)&Current_alphacolor->table.lookup[0][0]; + + ubyte * filter_lookup = (ubyte *)&new_table[(corr_slope>>8)*F_TABLE_SIZE]; + + + if ( clipped ) { + ubyte * dptr; + + for (i = 0; i < 4; i++) { + if (filter_index < 0) + index = ~filter_index; // Invert when negative + else + index = filter_index; + + if (index > INV_FILTER) { + Assert( i == 3 ); + return; // Not a valid pixel + } + + //a = ((corr_slope * filter_table[index]) & 0xf00000) >> (16+4-8); + a = filter_lookup[index]<<8; + + // Should include the alpha value as well... + if ( (yi >= gr_screen.clip_top) && (yi <= gr_screen.clip_bottom) ) { + dptr = GR_SCREEN_PTR(ubyte,xi, yi); + + *dptr = lookup[*dptr+a]; + } + + filter_index -= 32; + //y += ONE_XY; + yi++; + } + } else { + ubyte * dptr; + + dptr = GR_SCREEN_PTR(ubyte,xi, yi); + + for (i = 0; i < 4; i++) { + if (filter_index < 0) + index = ~filter_index; // Invert when negative + else + index = filter_index; + + if (index > INV_FILTER) { + Assert( i == 3 ); + return; // Not a valid pixel + } + + a = filter_lookup[index]<<8; + + // Should include the alpha value as well... + *dptr = lookup[*dptr+a]; + + dptr += gr_screen.rowsize; + + filter_index -= 32; + } + + + } +#else + Int3(); +#endif +} + +// draw_aa_vspan +// Draw one span of an antialiased line (for vertical lines). + +void draw_aa_vspan8(fix_xy x, fix_xy y, long int ep_corr, long int slope) +{ +#ifndef HARDWARE_ONLY + long int sample_dist; // Distance from line to sample point + long int filter_index; // Index into filter table + long int i; // Count pixels across span + long int index; // Final filter table index + int a; // Alpha + + sample_dist = (FRACT_XY(x) >> (FIX_XY_SHIFT - 5)) - 16; + x = x - ONE_XY; + filter_index = sample_dist + 32; + + int yi = FIX_XY_TO_INT( y ); + int xi = FIX_XY_TO_INT( x ); + + if ( yi < gr_screen.clip_top ) return; + if ( yi > gr_screen.clip_bottom ) return; + + int clipped = 0; + + if ( xi < gr_screen.clip_left ) clipped++; + if ( xi+3 > gr_screen.clip_right ) clipped++; + + long int corr_slope = (slope * ep_corr) & 0x1ff00; + + ubyte * lookup = (ubyte *)&Current_alphacolor->table.lookup[0][0]; + + ubyte * filter_lookup = (ubyte *)&new_table[(corr_slope>>8)*F_TABLE_SIZE]; + + + if ( clipped ) { + ubyte * dptr; + + for (i = 0; i < 4; i++) { + if (filter_index < 0) + index = ~filter_index; // Invert when negative + else + index = filter_index; + + if (index > INV_FILTER) { + Assert( i == 3 ); + return; // Not a valid pixel + } + + //a = ((corr_slope * filter_table[index]) & 0xf00000) >> (16+4-8); + a = filter_lookup[index]<<8; + + // Draw the pixel + if ( (xi >= gr_screen.clip_left) && (xi <= gr_screen.clip_right) ) { + dptr = GR_SCREEN_PTR(ubyte,xi, yi); + + *dptr = lookup[*dptr+a]; + } + + filter_index -= 32; + xi++; + } + } else { + + ubyte *dptr = GR_SCREEN_PTR(ubyte,xi, yi); + + for (i = 0; i < 4; i++) { + if (filter_index < 0) + index = ~filter_index; // Invert when negative + else + index = filter_index; + + if (index > INV_FILTER) { + Assert( i == 3 ); + return; // Not a valid pixel + } + + a = filter_lookup[index]<<8; + + // Should include the alpha value as well... + + // Draw the pixel + *dptr = lookup[*dptr+a]; + + filter_index -= 32; + dptr++; + } + } +#else + Int3(); +#endif +} + + +void draw_line(aa_setup_line *line) +{ + fix_xy x, y; // Start value + fix_xy dudu; // Constant 1 or -1 for step + fix_xy dx, dy; // Steps in X and Y + fix_xy u_off; // Offset to starting sample grid + fix_xy us, vs, ue; // Start and end for drawing + fix_xy count; // How many pixels to draw + long int slope_index; // Index into slope correction table + long int slope; // Slope correction value + long int ep_corr; // End-point correction value + long int scount, ecount; // Start/end count for endpoints + long int sf, ef; // Sand and end fractions + long int ep_code; // One of 9 endpoint codes + + // Get directions + if (line->negative) { + dudu = -ONE_XY; + } else { + dudu = ONE_XY; + } + + if (line->x_major) { + dx = dudu; + dy = line->dvdu; + } else { + dx = line->dvdu; + dy = dudu; + } + + // Get initial values and count + if (line->negative) { + u_off = FRACT_XY(line->us) - ONE_XY; + us = line->us + ONE_XY; + ue = line->ue; + count = FLOOR_XY(us) - FLOOR_XY(ue); + } else { + u_off = 0 - FRACT_XY(line->us); + us = line->us; + ue = line->ue + ONE_XY; + count = FLOOR_XY(ue) - FLOOR_XY(us); + } + + vs = line->vs + fix_xy_mult(line->dvdu, u_off) + ONEHALF_XY; + + if (line->x_major) { + x = us; + y = vs; + } else { + x = vs; + y = us; + } + + //a = line->as + fix_xy_mult(line->dadu, u_off); + + // Compute slope correction once per line + slope_index = (line->dvdu >> (FIX_XY_SHIFT - 5)) & 0x3fu; + + if (line->dvdu < 0) { + slope_index ^= 0x3fu; + } + + if ((slope_index & 0x20u) == 0) { + slope = slope_corr_table[slope_index]; + } else { + slope = 0x100; /* True 1.0 */ + } + + // Set up counters for determining endpoint regions + scount = 0; + ecount = FIX_TO_INT_XY(count); + + // Get 4-bit fractions for end-point adjustments + sf = (us & EP_MASK) >> EP_SHIFT; + ef = (ue & EP_MASK) >> EP_SHIFT; + + // Interpolate the edges + while (count >= 0) { + + /*- + * Compute end-point code (defined as follows): + * 0 = 0, 0: short, no boundary crossing + * 1 = 0, 1: short line overlap (< 1.0) + * 2 = 0, 2: 1st pixel of 1st endpoint + * 3 = 1, 0: short line overlap (< 1.0) + * 4 = 1, 1: short line overlap (> 1.0) + * 5 = 1, 2: 2nd pixel of 1st endpoint + * 6 = 2, 0: last of 2nd endpoint + * 7 = 2, 1: first of 2nd endpoint + * 8 = 2, 2: regular part of line + */ + + ep_code = ((scount < 2) ? scount : 2) * 3 + ((ecount < 2) ? ecount : 2); + + if (line->negative) { + + // Drawing in the negative direction + + // Compute endpoint information + switch (ep_code) { + case 0: ep_corr = 0; break; + case 1: ep_corr = ((sf - ef) & 0x78) | 4; break; + case 2: ep_corr = sf | 4; break; + case 3: ep_corr = ((sf - ef) & 0x78) | 4; break; + case 4: ep_corr = ((sf - ef) + 0x80) | 4; break; + case 5: ep_corr = (sf + 0x80) | 4; break; + case 6: ep_corr = (0x78 - ef) | 4; break; + case 7: ep_corr = ((0x78 - ef) + 0x80) | 4; break; + case 8: ep_corr = 0x100; break; + default: ep_corr = 0; break; + } + + } else { + // Drawing in the positive direction + + // Compute endpoint information + switch (ep_code) { + case 0: ep_corr = 0; break; + case 1: ep_corr = ((ef - sf) & 0x78) | 4; break; + case 2: ep_corr = (0x78 - sf) | 4; break; + case 3: ep_corr = ((ef - sf) & 0x78) | 4; break; + case 4: ep_corr = ((ef - sf) + 0x80) | 4; break; + case 5: ep_corr = ((0x78 - sf) + 0x80) | 4; break; + case 6: ep_corr = ef | 4; break; + case 7: ep_corr = (ef + 0x80) | 4; break; + case 8: ep_corr = 0x100; break; + default: ep_corr = 0; break; + } + } + + if (line->x_major) { + draw_aa_hspan8(x, y, ep_corr, slope); + } else { + draw_aa_vspan8(x, y, ep_corr, slope); + } + + x += dx; + y += dy; + //a += line->dadu; + + scount++; + ecount--; + count -= ONE_XY; + } + +} + + +// Perform the setup operation for a line, then draw it + +void aaline_setup(aa_vertex *v1, aa_vertex *v2) +{ + float dx, dy; // Deltas in X and Y + float udx, udy; // Positive version of deltas + float one_du; // 1.0 / udx or udy + aa_setup_line line; + + if ( !aaline_inited ) + aaline_init_tables(); + + + dx = v1->x - v2->x; + dy = v1->y - v2->y; + + if (dx < 0.0) { + udx = -dx; + } else { + udx = dx; + } + + if (dy < 0.0) { + udy = -dy; + } else { + udy = dy; + } + + if (udx > udy) { + // X major line + line.x_major = 1; + line.negative = (dx < 0.0); + line.us = FLOAT_TO_FIX_XY(v2->x); + line.vs = FLOAT_TO_FIX_XY(v2->y); + line.ue = FLOAT_TO_FIX_XY(v1->x); + one_du = 1.0f / udx; + line.dvdu = FLOAT_TO_FIX_XY(dy * one_du); + } else { + // Y major line + line.x_major = 0; + line.negative = (dy < 0.0); + line.us = FLOAT_TO_FIX_XY(v2->y); + line.vs = FLOAT_TO_FIX_XY(v2->x); + line.ue = FLOAT_TO_FIX_XY(v1->y); + one_du = 1.0f / udy; + line.dvdu = FLOAT_TO_FIX_XY(dx * one_du); + } + + // Convert colors to fixed-point + //line.as = FLOAT_TO_FIX_RGB(v2->a); + + // Compute delta values for colors + //line.dadu = FLOAT_TO_FIX_RGB((v1->a - v2->a) * one_du); + + // Now go draw it + + gr_lock(); + draw_line(&line); + gr_unlock(); +} + + +void gr8_aaline( vertex *v1, vertex *v2 ) +{ + aa_vertex aa1, aa2; + + if ( !Current_alphacolor ) { + gr_line(fl2i(v1->sx),fl2i(v1->sy),fl2i(v2->sx),fl2i(v2->sy)); + return; + } + +// return; + + aa1.x = v1->sx; + aa1.y = v1->sy; + + aa2.x = v2->sx; + aa2.y = v2->sy; + + { + int clipped = 0, swapped = 0; + float a1, b1, a2, b2; + a1 = (float)gr_screen.clip_left; + b1 = (float)gr_screen.clip_top; + a2 = (float)gr_screen.clip_right; + b2 = (float)gr_screen.clip_bottom; + + FL_CLIPLINE(aa1.x,aa1.y,aa2.x,aa2.y,a1,b1,a2,b2,return,clipped=1,swapped=1); + } + + aaline_setup( &aa1, &aa2 ); +} diff --git a/src/graphics/bitblt.cpp b/src/graphics/bitblt.cpp new file mode 100644 index 0000000..2a3665b --- /dev/null +++ b/src/graphics/bitblt.cpp @@ -0,0 +1,490 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Bitblt.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to do software bitblt type stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 2 4/14/98 12:15p John + * Made 16-bpp movies work. + * + * 1 3/25/98 8:07p John + * Split software renderer into Win32 and DirectX + * + * $NoKeywords: $ + */ + +#include "osapi.h" +#include "2d.h" +#include "bmpman.h" +#include "key.h" +#include "floating.h" +#include "palman.h" +#include "grsoft.h" +#include "grinternal.h" + +// Headers for 2d functions +#include "bitblt.h" + +MONITOR( Num2dBitmaps ); + +void gr8_aabitmap_ex(int x,int y,int w,int h,int sx,int sy) +{ +#if 0 + int hi; + bitmap * bmp; + + if ( w < 1 ) return; + if ( h < 1 ) return; + + MONITOR_INC( Num2dBitmaps, 1 ); + + if ( !Current_alphacolor ) return; + + // mprintf(( "x=%d, y=%d, w=%d, h=%d\n", x, y, w, h )); + // mprintf(( "sx=%d, sy=%d, bw=%d, bh=%d\n", sx, sy, bmp->w, bmp->h )); + + bmp = bm_lock( gr_screen.current_bitmap, 8, BMP_RLE|BMP_NO_PALETTE_MAP ); + + gr_lock(); + + if (bmp->flags & BMP_RLE) { + int * offsets = (int *)(bmp->data); + ubyte *lookup = &Current_alphacolor->table.lookup[0][0]; + + for (hi=0; hi < h; hi++ ) { + ubyte * dp = GR_SCREEN_PTR(ubyte,x,y+hi); + ubyte * sp = (ubyte *)bmp->data + offsets[sy+hi]; + + int x1 = sx; + int x2 = sx+w; + int i = 0; + + while(i x1 ) { + // This span crosses X1.. so skip some and draw some + if ( i+count >= x2 ) { + count = x2 - i; + } + + if ( run_span ) { + // RLE'd data + ubyte c = *sp++; + + if ( c > 0 ) { + // We have 'count' pixels of c + ubyte *tmp_lookup = &lookup[c<<8]; + while( count-- ) { + if ( i >= x1 ) { + *dp = tmp_lookup[*dp]; + dp++; + } + i++; + } + } else { + while( count-- ) { + if ( i >= x1 ) { + dp++; + } + i++; + } + } + } else { + // non RLE'd data + + // We have 'count' un-rle'd pixels + while( count-- ) { + if ( i >= x1 ) { + ubyte c = *sp; + *dp = lookup[(c<<8) | *dp]; + dp++; + } + sp++; + i++; + } + } + + } else { + i += count; + if ( run_span ) { + // RLE'd data + sp++; + } else { + sp += count; + } + } + } + + while(i= x2 ) { + count = x2 - i; + } + i += count; + + ubyte *end_ptr = dp + count; + + if ( count > 0 ) { + if ( run_span ) { + // RLE'd data + ubyte c = *sp++; + + if ( c > 0 ) { + // We have 'count' pixels of c + ubyte *tmp_lookup = &lookup[c<<8]; + + while( count >= 4 ) { + // We have to plot at least 4 pixels of constant color starting at 'dp' + dp[0] = tmp_lookup[dp[0]]; + dp[1] = tmp_lookup[dp[1]]; + dp[2] = tmp_lookup[dp[2]]; + dp[3] = tmp_lookup[dp[3]]; + + count -= 4; + dp += 4; + } + + while ( count > 0 ) { + *dp = tmp_lookup[*dp]; + dp++; + count--; + } + } + } else { + // non RLE'd data + + // We have 'count' un-rle'd pixels + do { + ubyte c = *sp++; + *dp = lookup[(c<<8) | *dp]; + dp++; + } while ( dp < end_ptr ); + } + } + + dp = end_ptr; + } + + } + } else { + ubyte * sptr = (ubyte *)( bmp->data + (sy*bmp->w+sx) ); + ubyte *lookup = (ubyte *)&Current_alphacolor->table.lookup[0][0]; + + for (hi=0; hi 0 ) { + *dp = lookup[(c<<8) | *dp]; + } + dp++; + } + sptr += bmp->w; + } + } + + gr_unlock(); + bm_unlock(gr_screen.current_bitmap); +#endif +} + +void grx_aabitmap_ex(int x,int y,int w,int h,int sx,int sy) +{ + int reclip; + #ifndef NDEBUG + int count = 0; + #endif + + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + + int bw, bh; + bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL ); + + do { + reclip = 0; + #ifndef NDEBUG + if ( count > 1 ) Int3(); + count++; + #endif + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx += gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy += gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) { + dx1 -= sx; + sx = 0; + reclip = 1; + } + + if ( sy < 0 ) { + dy1 -= sy; + sy = 0; + reclip = 1; + } + + w = dx2-dx1+1; + h = dy2-dy1+1; + + if ( sx + w > bw ) { + w = bw - sx; + dx2 = dx1 + w - 1; + } + + if ( sy + h > bh ) { + h = bh - sy; + dy2 = dy1 + h - 1; + } + + if ( w < 1 ) return; // clipped away! + if ( h < 1 ) return; // clipped away! + + } while (reclip); + + // Make sure clipping algorithm works + #ifndef NDEBUG + Assert( w > 0 ); + Assert( h > 0 ); + Assert( w == (dx2-dx1+1) ); + Assert( h == (dy2-dy1+1) ); + Assert( sx >= 0 ); + Assert( sy >= 0 ); + Assert( sx+w <= bw ); + Assert( sy+h <= bh ); + Assert( dx2 >= dx1 ); + Assert( dy2 >= dy1 ); + Assert( (dx1 >= gr_screen.clip_left ) && (dx1 <= gr_screen.clip_right) ); + Assert( (dx2 >= gr_screen.clip_left ) && (dx2 <= gr_screen.clip_right) ); + Assert( (dy1 >= gr_screen.clip_top ) && (dy1 <= gr_screen.clip_bottom) ); + Assert( (dy2 >= gr_screen.clip_top ) && (dy2 <= gr_screen.clip_bottom) ); + #endif + + // We now have dx1,dy1 and dx2,dy2 and sx, sy all set validly within clip regions. + + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + + switch( gr_screen.bits_per_pixel ) { + case 8: + gr8_aabitmap_ex(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); + break; + + default: + Error( LOCATION, "Unsupported BPP=%d in grx_aabitmap_ex!\n", gr_screen.bytes_per_pixel ); + } + + +} + + +void gr8_bitmap_ex(int x,int y,int w,int h,int sx,int sy) +{ +#if 0 + MONITOR_INC( Num2dBitmaps, 1 ); + + gr_lock(); + + // Normal bitblt + int i; + bitmap * bmp; + ubyte * sptr; + + bmp = bm_lock( gr_screen.current_bitmap, 8, 0 ); + sptr = (ubyte *)( bmp->data + (sy*bmp->w+sx) ); + + //mprintf(( "x=%d, y=%d, w=%d, h=%d\n", x, y, w, h )); + //mprintf(( "sx=%d, sy=%d, bw=%d, bh=%d\n", sx, sy, bmp->w, bmp->h )); + + if ( bmp->flags & BMP_XPARENT ) { + for (i=0; iw; + } + } else { + for (i=0; iw; + } + } + bm_unlock(gr_screen.current_bitmap); + + gr_unlock(); +#endif +} + + + +void grx_bitmap_ex(int x,int y,int w,int h,int sx,int sy) +{ + int reclip; + #ifndef NDEBUG + int count = 0; + #endif + + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + + int bw, bh; + bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL ); + + do { + reclip = 0; + #ifndef NDEBUG + if ( count > 1 ) Int3(); + count++; + #endif + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx += gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy += gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) { + dx1 -= sx; + sx = 0; + reclip = 1; + } + + if ( sy < 0 ) { + dy1 -= sy; + sy = 0; + reclip = 1; + } + + w = dx2-dx1+1; + h = dy2-dy1+1; + + if ( sx + w > bw ) { + w = bw - sx; + dx2 = dx1 + w - 1; + } + + if ( sy + h > bh ) { + h = bh - sy; + dy2 = dy1 + h - 1; + } + + if ( w < 1 ) return; // clipped away! + if ( h < 1 ) return; // clipped away! + + } while (reclip); + + // Make sure clipping algorithm works + #ifndef NDEBUG + Assert( w > 0 ); + Assert( h > 0 ); + Assert( w == (dx2-dx1+1) ); + Assert( h == (dy2-dy1+1) ); + Assert( sx >= 0 ); + Assert( sy >= 0 ); + Assert( sx+w <= bw ); + Assert( sy+h <= bh ); + Assert( dx2 >= dx1 ); + Assert( dy2 >= dy1 ); + Assert( (dx1 >= gr_screen.clip_left ) && (dx1 <= gr_screen.clip_right) ); + Assert( (dx2 >= gr_screen.clip_left ) && (dx2 <= gr_screen.clip_right) ); + Assert( (dy1 >= gr_screen.clip_top ) && (dy1 <= gr_screen.clip_bottom) ); + Assert( (dy2 >= gr_screen.clip_top ) && (dy2 <= gr_screen.clip_bottom) ); + #endif + + // We now have dx1,dy1 and dx2,dy2 and sx, sy all set validly within clip regions. + + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + + gr8_bitmap_ex(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + + + +void grx_bitmap(int x,int y) +{ + int w, h; + + bm_get_info( gr_screen.current_bitmap, &w, &h, NULL ); + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + int sx=0, sy=0; + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx = gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy = gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) return; + if ( sy < 0 ) return; + if ( sx >= w ) return; + if ( sy >= h ) return; + + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + + gr8_bitmap_ex(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + +void grx_aabitmap(int x,int y) +{ + int w, h; + + bm_get_info( gr_screen.current_bitmap, &w, &h, NULL ); + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + int sx=0, sy=0; + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx = gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy = gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) return; + if ( sy < 0 ) return; + if ( sx >= w ) return; + if ( sy >= h ) return; + + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + + gr8_aabitmap_ex(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + diff --git a/src/graphics/circle.cpp b/src/graphics/circle.cpp new file mode 100644 index 0000000..64b5788 --- /dev/null +++ b/src/graphics/circle.cpp @@ -0,0 +1,96 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Circle.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to draw circles. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:48a Dave + * + * 9 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 8 11/06/97 11:18a John + * fixed bug with some scanlines being drawn twice + * + * 7 11/26/96 6:50p John + * Added some more hicolor primitives. Made windowed mode run as current + * bpp, if bpp is 8,16,or 32. + * + * 6 11/26/96 4:30p John + * Put in some better quality circle_empty code. + * + * 5 11/07/96 6:19p John + * Added a bunch of 16bpp primitives so the game sort of runs in 16bpp + * mode. + * + * 4 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#include "2d.h" +#include "pixel.h" +#include "circle.h" + +// THIS COULD BE OPTIMIZED BY MOVING THE GR_RECT CODE INTO HERE!!!!!!!! + +#define circle_fill(x,y,w) gr_rect((x),(y),(w),1) + +void gr8_circle( int xc, int yc, int d ) +{ + int p,x, y, r; + + r = d/2; + p=3-d; + x=0; + y=r; + + // Big clip + if ( (xc+r) < gr_screen.clip_left ) return; + if ( (xc-r) > gr_screen.clip_right ) return; + if ( (yc+r) < gr_screen.clip_top ) return; + if ( (yc-r) > gr_screen.clip_bottom ) return; + + while(x8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 24 2/07/98 7:50p John + * Added code so that we can use the old blending type of alphacolors if + * we want to. Made the stars use them. + * + * 23 1/13/98 10:20a John + * Added code to support "glass" in alphacolors + * + * 22 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 21 12/30/97 4:32p John + * + * 20 12/02/97 3:59p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 19 11/30/97 4:26p John + * Added 32-bpp antialiased line. Took gamma out of alphacolor + * calculation. + * + * 18 11/29/97 2:06p John + * added mode 16-bpp support + * + * 17 11/21/97 11:32a John + * Added nebulas. Fixed some warpout bugs. + * + * 16 11/14/97 12:30p John + * Fixed some DirectX bugs. Moved the 8-16 xlat tables into Graphics + * libs. Made 16-bpp DirectX modes know what bitmap format they're in. + * + * 15 11/05/97 11:20p Lawrance + * increase number of alpha colors to 52 + * + * 14 11/04/97 6:32p Hoffoss + * Changes to hotkey screen. Needed to add new colors for + * Color_text_active*. + * + * 13 10/31/97 10:47a John + * upped clr version to force rebuild after changing palette code. + * + * 12 10/14/97 4:50p John + * more 16 bpp stuff. + * + * 11 10/14/97 8:08a John + * added a bunch more 16 bit support + * + * 10 10/09/97 5:23p John + * Added support for more 16-bpp functions + * + * 9 10/03/97 9:10a John + * added better antialiased line drawer + * + * 8 9/20/97 8:16a John + * Made .clr files go into the Cache directory. Replaced cfopen(name,NULL) + * to delete a file with cf_delete. + * + * 7 9/09/97 10:46a Sandeep + * fixed warning level 4 + * + * 6 7/18/97 12:40p John + * cached alphacolors to disk. Also made cfopen be able to delete a file + * by passing NULL for mode. + * + * 5 7/16/97 3:07p John + * + * 4 6/18/97 12:07p John + * fixed some color bugs + * + * 3 6/18/97 9:46a John + * added first rev of d3d shader. Made HUD target use medium detail + * models. Took out some color debug messages. + * + * 2 6/17/97 7:04p John + * added d3d support for gradients. + * fixed some color bugs by adding screen signatures instead of watching + * flags and palette changes. + * + * 1 6/17/97 12:01p John + * + * $NoKeywords: $ + */ + +#include "pstypes.h" +#include "2d.h" +#include "grinternal.h" +#include "colors.h" +#include "palman.h" +#include "cfile.h" +#include "systemvars.h" + +//#define MAX_ALPHACOLORS 36 +#define MAX_ALPHACOLORS 72 + +alphacolor Alphacolors[MAX_ALPHACOLORS]; +static int Alphacolors_intited = 0; + +alphacolor * Current_alphacolor = NULL; + + +void calc_alphacolor_hud_type( alphacolor * ac ) +{ +#ifndef HARDWARE_ONLY + int i,j; + int tr,tg,tb, Sr, Sg, Sb; + ubyte * pal; + int r, g, b, alpha; + float falpha; + + Assert(Alphacolors_intited); + +// mprintf(( "Calculating alphacolor for %d,%d,%d,%d\n", ac->r, ac->g, ac->b, ac->alpha )); + + falpha = i2fl(ac->alpha)/255.0f; + if ( falpha<0.0f ) falpha = 0.0f; else if ( falpha > 1.0f ) falpha = 1.0f; + + alpha = ac->alpha >> 4; + if (alpha < 0 ) alpha = 0; else if (alpha > 15 ) alpha = 15; + r = ac->r; + if (r < 0 ) r = 0; else if (r > 255 ) r = 255; + g = ac->g; + if (g < 0 ) g = 0; else if (g > 255 ) g = 255; + b = ac->b; + if (b < 0 ) b = 0; else if (b > 255 ) b = 255; + + int ii[16]; + + for (j=1; j<15; j++ ) { + + // JAS: Use 1.5/Gamma instead of 1/Gamma because on Adam's + // PC a gamma of 1.2 makes text look good, but his gamma is + // really 1.8. 1.8/1.2 = 1.5 + float factor = falpha * (float)pow(i2fl(j)/14.0f, 1.5f/Gr_gamma); + //float factor = i2fl(j)/14.0f; + + tr = fl2i( i2fl(r) * factor ); + tg = fl2i( i2fl(g) * factor ); + tb = fl2i( i2fl(b) * factor ); + + ii[j] = tr; + if ( tg > ii[j] ) ii[j] = tg; + if ( tb > ii[j] ) ii[j] = tb; + } + + pal = gr_palette; + + int m = r; + if ( g > m ) m = g; + if ( b > m ) m = b; + + ubyte ri[256], gi[256], bi[256]; + + if ( m > 0 ) { + for (i=0; i<256; i++ ) { + ri[i] = ubyte((i*r)/m); + gi[i] = ubyte((i*g)/m); + bi[i] = ubyte((i*b)/m); + } + } else { + for (i=0; i<256; i++ ) { + ri[i] = 0; + gi[i] = 0; + bi[i] = 0; + } + } + + for (i=0; i<256; i++ ) { + Sr = pal[0]; + Sg = pal[1]; + Sb = pal[2]; + pal += 3; + + int dst_intensity = Sr; + if ( Sg > dst_intensity ) dst_intensity = Sg; + if ( Sb > dst_intensity ) dst_intensity = Sb; + + ac->table.lookup[0][i] = (unsigned char)i; + + for (j=1; j<15; j++ ) { + + int tmp_i = max( ii[j], dst_intensity ); + + ac->table.lookup[j][i] = (unsigned char)palette_find(ri[tmp_i],gi[tmp_i],bi[tmp_i]); + } + + float di = (i2fl(Sr)*.30f+i2fl(Sg)*0.60f+i2fl(Sb)*.10f)/255.0f; + float factor = 0.0f + di*0.75f; + + tr = fl2i( factor*i2fl(r)*falpha ); + tg = fl2i( factor*i2fl(g)*falpha ); + tb = fl2i( factor*i2fl(b)*falpha ); + + if ( tr > 255 ) tr = 255; else if ( tr < 0 ) tr = 0; + if ( tg > 255 ) tg = 255; else if ( tg < 0 ) tg = 0; + if ( tb > 255 ) tb = 255; else if ( tb < 0 ) tb = 0; + + ac->table.lookup[15][i] = (unsigned char)palette_find(tr,tg,tb); + //ac->table.lookup[15][i] = (unsigned char)palette_find(255,0,0); + + } +#endif +} + +// Old way to calculate alpha colors + +void calc_alphacolor_blend_type( alphacolor * ac ) +{ +#ifndef HARDWARE_ONLY + int i,j; + int tr,tg,tb, Sr, Sg, Sb; + int Dr, Dg, Db; + ubyte * pal; + int r, g, b, alpha; + + Assert(Alphacolors_intited); + +// mprintf(( "Calculating alphacolor for %d,%d,%d,%d\n", ac->r, ac->g, ac->b, ac->alpha )); + + alpha = ac->alpha >> 4; + if (alpha < 0 ) alpha = 0; else if (alpha > 15 ) alpha = 15; + r = ac->r; + if (r < 0 ) r = 0; else if (r > 255 ) r = 255; + g = ac->g; + if (g < 0 ) g = 0; else if (g > 255 ) g = 255; + b = ac->b; + if (b < 0 ) b = 0; else if (b > 255 ) b = 255; + + int gamma_j1[16]; + + for (j=1; j<16; j++ ) { + // JAS: Use 1.5/Gamma instead of 1/Gamma because on Adam's + // PC a gamma of 1.2 makes text look good, but his gamma is + // really 1.8. 1.8/1.2 = 1.5 + gamma_j1[j] = (int)((pow(i2fl(j)/15.0f, 1.5f/Gr_gamma)*16.0f) + 0.5); + } + + pal = gr_palette; + + for (i=0; i<256; i++ ) { + Sr = pal[0]; + Sg = pal[1]; + Sb = pal[2]; + pal += 3; + + Dr = ( Sr*(16-alpha) + (r*alpha) ) >> 4; + Dg = ( Sg*(16-alpha) + (g*alpha) ) >> 4; + Db = ( Sb*(16-alpha) + (b*alpha) ) >> 4; + + ac->table.lookup[0][i] = (unsigned char)i; + + for (j=1; j<16; j++ ) { + + int j1 = gamma_j1[j]; + + tr = ( Sr*(16-j1) + (Dr*j1) ) >> 4; + tg = ( Sg*(16-j1) + (Dg*j1) ) >> 4; + tb = ( Sb*(16-j1) + (Db*j1) ) >> 4; + + if ( tr > 255 ) tr = 255; else if ( tr < 0 ) tr = 0; + if ( tg > 255 ) tg = 255; else if ( tg < 0 ) tg = 0; + if ( tb > 255 ) tb = 255; else if ( tb < 0 ) tb = 0; + + ac->table.lookup[j][i] = (unsigned char)palette_find(tr,tg,tb); + } + } +#endif +} + +void calc_alphacolor( alphacolor * ac ) +{ + switch(ac->type) { + case AC_TYPE_HUD: + calc_alphacolor_hud_type(ac); + break; + case AC_TYPE_BLEND: + calc_alphacolor_blend_type(ac); + break; + default: + Int3(); // Passing an invalid type of alphacolor! + } +} + +void grx_init_alphacolors() +{ + int i; + + Alphacolors_intited = 1; + + for (i=0; i 255 ) alpha = 255; + + n = -1; + if ( (clr->magic == 0xAC01) && (clr->is_alphacolor) ) { + if ( (clr->alphacolor >= 0) && (clr->alphacolor < MAX_ALPHACOLORS)) { + if ( Alphacolors[clr->alphacolor].used && (Alphacolors[clr->alphacolor].clr==clr) ) { + n = clr->alphacolor; + } + } + } + + int changed = 0; + + if ( n==-1 ) { + for (n=0; nr!=r || ac->g!=g || ac->b!=b || ac->alpha!=alpha || ac->type != type ) ) { + // we're changing the color, so delete the old cache file + //mprintf(( "Changing ac from %d,%d,%d,%d to %d,%d,%d,%d\n", ac->r, ac->g, ac->b, ac->alpha, r, g, b, alpha )); + //ac_delete_cached(ac); + } + + ac->used = 1; + ac->r = r; + ac->g = g; + ac->b = b; + ac->alpha = alpha; + ac->type = type; + ac->clr=clr; + + calc_alphacolor(ac); + + grx_init_color( clr, r, g, b ); + + // Link the alphacolor to the color + clr->alpha = (unsigned char)alpha; + clr->ac_type = (ubyte)type; + clr->alphacolor = n; + clr->is_alphacolor = 1; +} + + +void grx_get_color( int * r, int * g, int * b ) +{ + if (r) *r = gr_screen.current_color.red; + if (g) *g = gr_screen.current_color.green; + if (b) *b = gr_screen.current_color.blue; +} + +void grx_init_color( color * dst, int r, int g, int b ) +{ + dst->screen_sig = gr_screen.signature; + dst->red = (unsigned char)r; + dst->green = (unsigned char)g; + dst->blue = (unsigned char)b; + dst->alpha = 255; + dst->ac_type = AC_TYPE_NONE; + dst->is_alphacolor = 0; + dst->alphacolor = -1; + dst->magic = 0xAC01; + + dst->raw8 = (unsigned char)palette_find( r, g, b ); +} + +void grx_set_color_fast( color * dst ) +{ + if ( dst->magic != 0xAC01 ) return; + + if ( dst->screen_sig != gr_screen.signature ) { + if ( dst->is_alphacolor ) { + grx_init_alphacolor( dst, dst->red, dst->green, dst->blue, dst->alpha, dst->ac_type ); + } else { + grx_init_color( dst, dst->red, dst->green, dst->blue ); + } + } + + gr_screen.current_color = *dst; + + if ( dst->is_alphacolor ) { + Assert( dst->alphacolor > -1 ); + Assert( dst->alphacolor <= MAX_ALPHACOLORS ); + Assert( Alphacolors[dst->alphacolor].used ); + + // Current_alphacolor = &Alphacolors[dst->alphacolor]; + Current_alphacolor = NULL; + } else { + Current_alphacolor = NULL; + } +} + + +void grx_set_color( int r, int g, int b ) +{ + Assert((r >= 0) && (r < 256)); + Assert((g >= 0) && (g < 256)); + Assert((b >= 0) && (b < 256)); + +// if ( r!=0 || g!=0 || b!=0 ) { +// mprintf(( "Setcolor: %d,%d,%d\n", r,g,b )); +// } + grx_init_color( &gr_screen.current_color, r, g, b ); + Current_alphacolor = NULL; +} + +void calc_alphacolor_hud_type_old( alphacolor_old * ac ) +{ + int i,j; + int tr,tg,tb, Sr, Sg, Sb; + ubyte * pal; + int r, g, b, alpha; + float falpha; + + // Assert(Alphacolors_intited); + +// mprintf(( "Calculating alphacolor for %d,%d,%d,%d\n", ac->r, ac->g, ac->b, ac->alpha )); + + falpha = i2fl(ac->alpha)/255.0f; + if ( falpha<0.0f ) falpha = 0.0f; else if ( falpha > 1.0f ) falpha = 1.0f; + + alpha = ac->alpha >> 4; + if (alpha < 0 ) alpha = 0; else if (alpha > 15 ) alpha = 15; + r = ac->r; + if (r < 0 ) r = 0; else if (r > 255 ) r = 255; + g = ac->g; + if (g < 0 ) g = 0; else if (g > 255 ) g = 255; + b = ac->b; + if (b < 0 ) b = 0; else if (b > 255 ) b = 255; + + int ii[16]; + + for (j=1; j<15; j++ ) { + + // JAS: Use 1.5/Gamma instead of 1/Gamma because on Adam's + // PC a gamma of 1.2 makes text look good, but his gamma is + // really 1.8. 1.8/1.2 = 1.5 + float factor = falpha * (float)pow(i2fl(j)/14.0f, 1.5f/Gr_gamma); + //float factor = i2fl(j)/14.0f; + + tr = fl2i( i2fl(r) * factor ); + tg = fl2i( i2fl(g) * factor ); + tb = fl2i( i2fl(b) * factor ); + + ii[j] = tr; + if ( tg > ii[j] ) ii[j] = tg; + if ( tb > ii[j] ) ii[j] = tb; + } + + pal = gr_palette; + + int m = r; + if ( g > m ) m = g; + if ( b > m ) m = b; + + ubyte ri[256], gi[256], bi[256]; + + if ( m > 0 ) { + for (i=0; i<256; i++ ) { + ri[i] = ubyte((i*r)/m); + gi[i] = ubyte((i*g)/m); + bi[i] = ubyte((i*b)/m); + } + } else { + for (i=0; i<256; i++ ) { + ri[i] = 0; + gi[i] = 0; + bi[i] = 0; + } + } + + for (i=0; i<256; i++ ) { + Sr = pal[0]; + Sg = pal[1]; + Sb = pal[2]; + pal += 3; + + int dst_intensity = Sr; + if ( Sg > dst_intensity ) dst_intensity = Sg; + if ( Sb > dst_intensity ) dst_intensity = Sb; + + ac->table.lookup[0][i] = (unsigned char)i; + + for (j=1; j<15; j++ ) { + + int tmp_i = max( ii[j], dst_intensity ); + + ac->table.lookup[j][i] = (unsigned char)palette_find(ri[tmp_i],gi[tmp_i],bi[tmp_i]); + } + + float di = (i2fl(Sr)*.30f+i2fl(Sg)*0.60f+i2fl(Sb)*.10f)/255.0f; + float factor = 0.0f + di*0.75f; + + tr = fl2i( factor*i2fl(r)*falpha ); + tg = fl2i( factor*i2fl(g)*falpha ); + tb = fl2i( factor*i2fl(b)*falpha ); + + if ( tr > 255 ) tr = 255; else if ( tr < 0 ) tr = 0; + if ( tg > 255 ) tg = 255; else if ( tg < 0 ) tg = 0; + if ( tb > 255 ) tb = 255; else if ( tb < 0 ) tb = 0; + + ac->table.lookup[15][i] = (unsigned char)palette_find(tr,tg,tb); + //ac->table.lookup[15][i] = (unsigned char)palette_find(255,0,0); + } +} + +void calc_alphacolor_old(alphacolor_old *ac) +{ + Assert(Fred_running); + calc_alphacolor_hud_type_old(ac); +} \ No newline at end of file diff --git a/src/graphics/font.cpp b/src/graphics/font.cpp new file mode 100644 index 0000000..69c45e7 --- /dev/null +++ b/src/graphics/font.cpp @@ -0,0 +1,896 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Font.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * source file for font stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 9 7/09/99 10:32p Dave + * Command brief and red alert screens. + * + * 8 12/14/98 9:21a Dan + * Put gr8_string() back in + * + * 7 12/02/98 5:47p Dave + * Put in interface xstr code. Converted barracks screen to new format. + * + * 6 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 5 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 4 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 3 10/13/98 9:19a Andsager + * Add localization support to cfile. Optional parameter with cfopen that + * looks for localized files. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 49 7/02/98 3:41p Allender + * fix nasty assembler bug where pushf wasn't accompanied by corresponding + * pop in certain clipping conditions -- manifested only in software and + * usually on the PXO chat screen. + * + * 48 6/18/98 10:10a Allender + * fixed compiler warnings + * + * 47 6/13/98 10:48p Lawrance + * Changed code to utilize proper fixed-space 1 character. + * + * 46 6/13/98 3:18p Hoffoss + * NOX()ed out a bunch of strings that shouldn't be translated. + * + * 45 5/25/98 10:32a John + * Took out redundant code for font bitmap offsets that converted to a + * float, then later on converted back to an integer. Quite unnecessary. + * + * 44 5/12/98 11:45a John + * Fixed a bug with the length of string being passed to + * gr_get_string_size. The bug occurred if you had two or more'\n'''s in + * a row. + * + * 43 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 42 3/09/98 6:06p John + * Restructured font stuff to avoid duplicate code in Direct3D and Glide. + * Restructured Glide to avoid redundent state setting. + * + * 41 3/02/98 10:33a John + * Fixed bug where Y clip was using X value. + * + * 40 2/19/98 9:04a John + * Fixed fonts with glide + * + * 39 2/17/98 7:27p John + * Got fonts and texturing working in Direct3D + * + * 38 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 37 1/20/98 2:25p Hoffoss + * Fixed bug where timestamp printing incorrectly increments an hour at 6 + * minutes. + * + * 36 1/19/98 6:15p John + * Fixed all my Optimized Build compiler warnings + * + * 35 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 34 11/29/97 2:24p John + * fixed warnings + * + * 33 11/29/97 2:06p John + * added mode 16-bpp support + * + * 32 11/10/97 2:35p Hoffoss + * Changed timestamp printing code to utilize new fixed-space '1' + * character. + * + * 31 11/06/97 5:42p Hoffoss + * Added support for fixed size timstamp rendering. + * + * 30 11/03/97 10:08p Hoffoss + * Changed gr_get_string_size to utilize an optional length specifier, if + * you want to use non-null terminated strings. + * + * 29 11/03/97 10:59a John + * added support for more than one font. + * + * 28 10/24/97 12:13p Hoffoss + * Added gr_force_fit_string(). + * + * 27 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 26 10/14/97 4:50p John + * more 16 bpp stuff. + * + * 25 10/14/97 8:08a John + * added a bunch more 16 bit support + * + * 24 10/09/97 5:23p John + * Added support for more 16-bpp functions + * + * 23 9/23/97 10:53a Hoffoss + * Fixed bug in assumptions made half the time, and not the other half. + * + * 22 9/09/97 3:39p Sandeep + * warning level 4 bugs + * + * 21 8/19/97 5:46p Hoffoss + * Changed font used in Fred, and added display to show current eye + * position. + * + * 20 6/25/97 2:35p John + * added some functions to use the windows font for Fred. + * + * 19 6/17/97 12:03p John + * Moved color/alphacolor functions into their own module. Made all color + * functions be part of the low-level graphics drivers, not just the + * grsoft. + * + * 18 6/13/97 2:37p John + * fixed a string bug that printed weird characters sometimes. + * + * 17 6/11/97 6:00p John + * sped up alpha matching a bit. + * + * 16 6/11/97 5:49p John + * Changed palette code to only recalculate alphacolors when needed, not + * when palette changes. + * + * 15 6/11/97 5:01p John + * Fixed mission log text. Added fast clipped font drawer. + * + * 14 6/11/97 4:11p John + * addec function to get font height + * + * 13 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 12 6/09/97 9:24a John + * Changed the way fonts are set. + * + * 11 6/06/97 4:41p John + * Fixed alpha colors to be smoothly integrated into gr_set_color_fast + * code. + * + * 10 6/06/97 2:40p John + * Made all the radar dim in/out + * + * 9 6/05/97 4:53p John + * First rev of new antialiased font stuff. + * + * 8 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 7 4/24/97 11:28a Hoffoss + * added code to handle gr_String being called before gr_init_font ever + * was called + * + * 6 4/23/97 5:26p John + * First rev of new debug console stuff. + * + * 5 4/22/97 12:20p John + * fixed more resource leaks + * + * 4 4/22/97 10:33a John + * fixed the 2d resource leaks that Alan found. + * + * 3 4/04/97 11:21a Hoffoss + * JOHN: Fixed invalid param that caused a trap in BoundsChecker. + * + * 2 4/01/97 9:26a Allender + * added support for descent style fonts although they are not used in the + * game yet + * + * 1 3/31/97 9:42a Allender + * + * $NoKeywords: $ + */ + +#ifndef PLAT_UNIX +#include +#include +#endif +#include +#include +#include "grinternal.h" +#include "2d.h" +#include "cfile.h" +#include "font.h" +#include "palman.h" +#include "key.h" +#include "bmpman.h" +#include "localize.h" + +int Num_fonts = 0; +font Fonts[MAX_FONTS]; +font *Current_font = NULL; + + +// crops a string if required to force it to not exceed max_width pixels when printed. +// Does this by dropping characters at the end of the string and adding '...' to the end. +// str = string to crop. Modifies this string directly +// max_str = max characters allowed in str +// max_width = number of pixels to limit string to (less than or equal to). +// Returns: returns same pointer passed in for str. +char *gr_force_fit_string(char *str, int max_str, int max_width) +{ + int w; + + gr_get_string_size(&w, NULL, str); + if (w > max_width) { + if ((int) strlen(str) > max_str - 3) { + Assert(max_str >= 3); + str[max_str - 3] = 0; + } + + strcpy(str + strlen(str) - 1, "..."); + gr_get_string_size(&w, NULL, str); + while (w > max_width) { + Assert(strlen(str) >= 4); // if this is hit, a bad max_width was passed in and the calling function needs fixing. + strcpy(str + strlen(str) - 4, "..."); + gr_get_string_size(&w, NULL, str); + } + } + + return str; +} + +//takes the character BEFORE being offset into current font +// Returns the letter code +int get_char_width(ubyte c1,ubyte c2,int *width,int *spacing) +{ + int i, letter; + + Assert ( Current_font != NULL ); + letter = c1-Current_font->first_ascii; + + if (letter<0 || letter>=Current_font->num_chars) { //not in font, draw as space + *width=0; + *spacing = Current_font->w; + return -1; + } + + *width = Current_font->char_data[letter].byte_width; + *spacing = Current_font->char_data[letter].spacing; + + i = Current_font->char_data[letter].kerning_entry; + if ( i > -1) { + if (!(c2==0 || c2=='\n')) { + int letter2; + + letter2 = c2-Current_font->first_ascii; + + if ((letter2>=0) && (letter2num_chars) ) { //not in font, draw as space + font_kernpair *k = &Current_font->kern_data[i]; + while( (k->c1 == letter) && (k->c2num_kern_pairs) ) { + i++; + k++; + } + if ( k->c2 == letter2 ) { + *spacing += k->offset; + } + } + } + } + return letter; +} + +int get_centered_x(char *s) +{ + int w,w2,s2; + + for (w=0;*s!=0 && *s!='\n';s++) { + get_char_width(s[0],s[1],&w2,&s2); + w += s2; + } + + return ((gr_screen.clip_width - w) / 2); +} + +// draws a character centered on x +void gr_char_centered(int x, int y, char chr) +{ + char str[2]; + int w, sc; + + sc = Lcl_special_chars; + if (chr == '1') + chr = (char)(sc + 1); + + str[0] = chr; + str[1] = 0; + gr_get_string_size(&w, NULL, str); + gr_string(x - w / 2, y, str); +} + +void gr_print_timestamp(int x, int y, int timestamp) +{ + char h[2], m[3], s[3]; + int w, c; + + // format the time information into strings + sprintf(h, "%0.1d", (timestamp / 3600000) % 10); + sprintf(m, "%0.2d", (timestamp / 60000) % 60); + sprintf(s, "%0.2d", (timestamp / 1000) % 60); + + gr_get_string_size(&w, NULL, "0"); + gr_get_string_size(&c, NULL, ":"); + + gr_string(x + w, y, ":"); + gr_string(x + w * 3 + c, y, ":"); + + x += w / 2; + gr_char_centered(x, y, h[0]); + x += w + c; + gr_char_centered(x, y, m[0]); + x += w; + gr_char_centered(x, y, m[1]); + x += w + c; + gr_char_centered(x, y, s[0]); + x += w; + gr_char_centered(x, y, s[1]); +} + +int gr_get_font_height() +{ + if (Current_font) { + return Current_font->h; + } else { + return 16; + } +} + +void gr_get_string_size(int *w1, int *h1, char *text, int len) +{ + int longest_width; + int width,spacing; + int w, h; + + if (!Current_font) { + if ( w1) + *w1 = 16; + + if ( h1 ) + *h1 = 16; + + return; + } + + w = 0; + h = 0; + longest_width = 0; + + if (text != NULL ) { + h += Current_font->h; + while (*text && (len>0) ) { + + // Process one or more + while ((*text == '\n') && (len>0) ) { + text++; + len--; + if ( *text ) { + h += Current_font->h; + } + w = 0; + } + + if (*text == 0) { + break; + } + + get_char_width(text[0], text[1], &width, &spacing); + w += spacing; + if (w > longest_width) + longest_width = w; + + text++; + len--; + } + } + + if ( h1 ) + *h1 = h; + + if ( w1 ) + *w1 = longest_width; +} + + +MONITOR( FontChars ); + + +/* + +void gr8_char(int x,int y,int letter) +{ + font_char *ch; + + ch = &Current_font->char_data[letter]; + + gr_aabitmap_ex( x, y, ch->byte_width, Current_font->h, Current_font->u[letter], Current_font->v[letter] ); + +// mprintf(( "String = %s\n", text )); +} + + +void gr8_string( int sx, int sy, char *s ) +{ + int width, spacing, letter; + int x, y; + + if ( !Current_font ) return; + if ( !s ) return; + + gr_set_bitmap(Current_font->bitmap); + + x = sx; + y = sy; + + if (sx==0x8000) { //centered + x = get_centered_x(s); + } else { + x = sx; + } + + while (*s) { + while (*s== '\n' ) { + s++; + y += Current_font->h; + if (sx==0x8000) { //centered + x = get_centered_x(s); + } else { + x = sx; + } + } + if (*s == 0 ) break; + + letter = get_char_width(s[0],s[1],&width,&spacing); + + if (letter<0) { //not in font, draw as space + x += spacing; + s++; + continue; + } + gr8_char( x, y, letter ); + + x += spacing; + s++; + } +} +*/ + +void gr8_string(int sx, int sy, char *s ) +{ + int row,width, spacing, letter; + int x, y; + + if ( !Current_font ) return; + if ( !s ) return; + + x = sx; + y = sy; + + if (sx==0x8000) { //centered + x = get_centered_x(s); + } else { + x = sx; + } + + spacing = 0; + + gr_lock(); + + + while (*s) { + MONITOR_INC( FontChars, 1 ); + + x += spacing; + while (*s== '\n' ) { + s++; + y += Current_font->h; + if (sx==0x8000) { //centered + x = get_centered_x(s); + } else { + x = sx; + } + } + if (*s == 0 ) break; + + letter = get_char_width(s[0],s[1],&width,&spacing); + s++; + + //If not in font, draw as space + if (letter<0) continue; + + int xd, yd, xc, yc; + int wc, hc; + + // Check if this character is totally clipped + if ( x + width < gr_screen.clip_left ) continue; + if ( y + Current_font->h < gr_screen.clip_top ) continue; + if ( x > gr_screen.clip_right ) continue; + if ( y > gr_screen.clip_bottom ) continue; + + xd = yd = 0; + if ( x < gr_screen.clip_left ) xd = gr_screen.clip_left - x; + if ( y < gr_screen.clip_top ) yd = gr_screen.clip_top - y; + xc = x+xd; + yc = y+yd; + + wc = width - xd; hc = Current_font->h - yd; + if ( xc + wc > gr_screen.clip_right ) wc = gr_screen.clip_right - xc; + if ( yc + hc > gr_screen.clip_bottom ) hc = gr_screen.clip_bottom - yc; + + if ( wc < 1 ) continue; + if ( hc < 1 ) continue; + + ubyte *fp = Current_font->pixel_data + Current_font->char_data[letter].offset + xd + yd*width; + ubyte *dptr = GR_SCREEN_PTR(ubyte, xc, yc); + +#ifndef HARDWARE_ONLY + if ( Current_alphacolor ) { + for (row=0; rowtable.lookup[0][0]; + _asm mov edx, lookup + _asm xor eax, eax + _asm mov ecx, wc + _asm xor ebx, ebx + _asm mov edi, dptr + _asm mov esi, fp + _asm shr ecx, 1 + _asm jz OnlyOne + _asm pushf + InnerFontLoop: + _asm mov al, [edi] + _asm mov bl, [edi+1] + _asm add edi,2 + + _asm mov ah, [esi] + _asm mov bh, [esi+1] + _asm add esi,2 + + _asm mov al, [edx+eax] + _asm mov ah, [edx+ebx] + + _asm mov [edi-2], ax + + _asm dec ecx + _asm jnz InnerFontLoop + + _asm popf + _asm jnc NotOdd + + OnlyOne: + _asm mov al, [edi] + _asm mov ah, [esi] + _asm mov al, [edx+eax] + _asm mov [edi], al + + NotOdd: + dptr += gr_screen.rowsize; + fp += width; + #else + int i; + for (i=0; i< wc; i++ ) { + *dptr++ = Current_alphacolor->table.lookup[*fp++][*dptr]; + } + fp += width - wc; + dptr += gr_screen.rowsize - wc; + #endif + } + } else { // No alpha color +#endif + for (row=0; row 5 ) + *dptr = gr_screen.current_color.raw8; + dptr++; + fp++; + } + fp += width - wc; + dptr += gr_screen.rowsize - wc; + } + // } + } + gr_unlock(); +} + +#ifndef PLAT_UNIX +HFONT MyhFont = NULL; +extern HDC hDibDC; +#endif + +void gr_string_win(int x, int y, char *s) +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + char *ptr; + SIZE size; + + if ( MyhFont==NULL ) { + MyhFont = CreateFont(14, 0, 0, 0, // height,width,?,? + 700, + FALSE, + FALSE, + FALSE, // strikeout? + ANSI_CHARSET, // character set + OUT_DEVICE_PRECIS, + CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, + DEFAULT_PITCH | FF_DONTCARE, +// NULL ); +// "Times New Roman" ); +//XSTR:OFF + "Ariel" ); +//XSTR:ON + } + + SelectObject( hDibDC, MyhFont ); + + if ( gr_screen.bits_per_pixel==8 ) + SetTextColor(hDibDC, PALETTEINDEX(gr_screen.current_color.raw8)); + else + SetTextColor(hDibDC, RGB(gr_screen.current_color.red,gr_screen.current_color.green,gr_screen.current_color.blue)); + + SetBkMode(hDibDC,TRANSPARENT); + + + HRGN hclip; + hclip = CreateRectRgn( gr_screen.offset_x, + gr_screen.offset_y, + gr_screen.offset_x+gr_screen.clip_width-1, + gr_screen.offset_y+gr_screen.clip_height-1 ); + + SelectClipRgn(hDibDC, hclip ); + x += gr_screen.offset_x; + y += gr_screen.offset_y; + //ptr = strchr(s,'\n); + while ((ptr = strchr(s, '\n'))!=NULL) { + TextOut(hDibDC, x, y, s, ptr - s); + GetTextExtentPoint32(hDibDC, s, ptr - s, &size); + y += size.cy; + s = ptr + 1; + } + + TextOut(hDibDC, x, y, s, strlen(s)); + SelectClipRgn(hDibDC, NULL); + DeleteObject(hclip); +#endif +} + +void gr_get_string_size_win(int *w, int *h, char *text) +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + char *ptr; + SIZE size; + + ptr = strchr(text, '\n'); + + if (MyhFont==NULL) { + if (w) *w = 0; + if (h) *h = 0; + return; + } + + SelectObject( hDibDC, MyhFont ); + + if (!ptr) { + GetTextExtentPoint32( hDibDC, text, strlen(text), &size); + if (w) *w = size.cx; + if (h) *h = size.cy; + return; + } + + GetTextExtentPoint32(hDibDC, text, ptr - text, &size); + gr_get_string_size_win(w, h, ptr+1); + if (w && (size.cx > *w) ) + *w = size.cx; + + if (h) + *h += size.cy; +#endif +} + +char grx_printf_text[2048]; + +void _cdecl gr_printf( int x, int y, char * format, ... ) +{ + va_list args; + + if ( !Current_font ) return; + + va_start(args, format); + vsprintf(grx_printf_text,format,args); + va_end(args); + + gr_string(x,y,grx_printf_text); +} + +void gr_font_close() +{ + +} + +// Returns -1 if couldn't init font, otherwise returns the +// font id number. +int gr_create_font(char * typeface) +{ + CFILE *fp; + font *fnt; + int n, fontnum; + + fnt = Fonts; + n = -1; + for (fontnum=0; fontnumid != 0 ) { + if ( !_strnicmp( fnt->filename, typeface, MAX_FILENAME_LEN ) ) { + return fontnum; + } + } else { + if ( n < 0 ) { + n = fontnum; + } + } + fnt++; + } + + if ( fontnum==MAX_FONTS ) { + Error( LOCATION, "Too many fonts!\nSee John, or change MAX_FONTS in Graphics\\Font.h\n" ); + } + + if ( fontnum == Num_fonts ) { + Num_fonts++; + } + + bool localize = true; + + fp = cfopen( typeface, "rb", CFILE_NORMAL, CF_TYPE_ANY, localize ); + if ( fp == NULL ) return -1; + + strncpy( fnt->filename, typeface, MAX_FILENAME_LEN ); + cfread( &fnt->id, 4, 1, fp ); + cfread( &fnt->version, sizeof(int), 1, fp ); + cfread( &fnt->num_chars, sizeof(int), 1, fp ); + cfread( &fnt->first_ascii, sizeof(int), 1, fp ); + cfread( &fnt->w, sizeof(int), 1, fp ); + cfread( &fnt->h, sizeof(int), 1, fp ); + cfread( &fnt->num_kern_pairs, sizeof(int), 1, fp ); + cfread( &fnt->kern_data_size, sizeof(int), 1, fp ); + cfread( &fnt->char_data_size, sizeof(int), 1, fp ); + cfread( &fnt->pixel_data_size, sizeof(int), 1, fp ); + + if ( fnt->kern_data_size ) { + fnt->kern_data = (font_kernpair *)malloc( fnt->kern_data_size ); + Assert(fnt->kern_data!=NULL); + cfread( fnt->kern_data, fnt->kern_data_size, 1, fp ); + } else { + fnt->kern_data = NULL; + } + if ( fnt->char_data_size ) { + fnt->char_data = (font_char *)malloc( fnt->char_data_size ); + Assert( fnt->char_data != NULL ); + cfread( fnt->char_data, fnt->char_data_size, 1, fp ); + } else { + fnt->char_data = NULL; + } + if ( fnt->pixel_data_size ) { + fnt->pixel_data = (ubyte *)malloc( fnt->pixel_data_size ); + Assert(fnt->pixel_data!=NULL); + cfread( fnt->pixel_data, fnt->pixel_data_size, 1, fp ); + } else { + fnt->pixel_data = NULL; + } + cfclose(fp); + + // Create a bitmap for hardware cards. + // JAS: Try to squeeze this into the smallest square power of two texture. + // This should probably be done at font generation time, not here. + int w, h; + if ( fnt->pixel_data_size < 64*64 ) { + w = h = 64; + } else if ( fnt->pixel_data_size < 128*128 ) { + w = h = 128; + } else { + w = h = 256; + } + + fnt->bm_w = w; + fnt->bm_h = h; + fnt->bm_data = (ubyte *)malloc(fnt->bm_w*fnt->bm_h); + fnt->bm_u = (int *)malloc(sizeof(int)*fnt->num_chars); + fnt->bm_v = (int *)malloc(sizeof(int)*fnt->num_chars); + + int i,x,y; + x = y = 0; + for (i=0; inum_chars; i++ ) { + ubyte * fp; + int x1, y1; + fp = &fnt->pixel_data[fnt->char_data[i].offset]; + if ( x + fnt->char_data[i].byte_width >= fnt->bm_w ) { + x = 0; + y += fnt->h; + if ( y+fnt->h > fnt->bm_h ) { + Error( LOCATION, "Font too big!\n" ); + } + } + fnt->bm_u[i] = x; + fnt->bm_v[i] = y; + + for( y1=0; y1h; y1++ ) { + for (x1=0; x1char_data[i].byte_width; x1++ ) { + uint c = *fp++; + if ( c > 14 ) c = 14; + fnt->bm_data[(x+x1)+(y+y1)*fnt->bm_w] = (unsigned char)(c); + } + } + x += fnt->char_data[i].byte_width; + } + + fnt->bitmap_id = bm_create( 8, fnt->bm_w, fnt->bm_h, fnt->bm_data, BMP_AABITMAP ); + + return fontnum; +} + +void grx_set_font(int fontnum) +{ + if ( fontnum < 0 ) { + Current_font = NULL; + return; + } + + if ( fontnum >= 0 ) { + Current_font = &Fonts[fontnum]; + } +} + +void gr_font_init() +{ + gr_init_font( NOX("font01.vf") ); + gr_init_font( NOX("font02.vf") ); + gr_init_font( NOX("font03.vf") ); +} + +// Returns -1 if couldn't init font, otherwise returns the +// font id number. +int gr_init_font(char * typeface) +{ + int Loaded_fontnum; + + Loaded_fontnum = gr_create_font(typeface); + + Assert( Loaded_fontnum > -1 ); + + gr_set_font( Loaded_fontnum ); + + return Loaded_fontnum; +} diff --git a/src/graphics/gradient.cpp b/src/graphics/gradient.cpp new file mode 100644 index 0000000..9e4f654 --- /dev/null +++ b/src/graphics/gradient.cpp @@ -0,0 +1,196 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Gradient.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to draw rectangular gradients. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 12/02/98 5:47p Dave + * Put in interface xstr code. Converted barracks screen to new format. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 17 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 16 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 15 1/19/98 6:15p John + * Fixed all my Optimized Build compiler warnings + * + * 14 11/30/97 4:04p John + * + * 13 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 12 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 11 10/14/97 4:50p John + * more 16 bpp stuff. + * + * 10 10/14/97 8:08a John + * added a bunch more 16 bit support + * + * 9 10/09/97 5:23p John + * Added support for more 16-bpp functions + * + * 8 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 7 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 6 12/12/96 4:59p Lawrance + * made clipping for horizontal and vertical gradient lines right + * + * 5 11/19/96 2:46p Allender + * fix up gradient support for 15bpp + * + * 4 11/18/96 4:34p Allender + * new 16 bit gradient functions + * + * 3 10/27/96 1:21a Lawrance + * added check to avoid divide by zero when calculating gradients + * + * 2 10/26/96 2:56p John + * Got gradient code working. + * + * 1 10/26/96 1:45p John + * Initial skeletion code. + * + * $NoKeywords: $ + */ + +#ifndef PLAT_UNIX +#include +#include +#endif + +#include "2d.h" +#include "grinternal.h" +#include "gradient.h" +#include "floating.h" +#include "line.h" +#include "palman.h" + +void gr8_gradient(int x1,int y1,int x2,int y2) +{ +#ifndef HARDWARE_ONLY + int i; + int xstep,ystep; + int clipped = 0, swapped=0; + ubyte *xlat_table; + + int l=0, dl=0; + + if (!Current_alphacolor) { + gr_line( x1, y1, x2, y2 ); + return; + } + + INT_CLIPLINE(x1,y1,x2,y2,gr_screen.clip_left,gr_screen.clip_top,gr_screen.clip_right,gr_screen.clip_bottom,return,clipped=1,swapped=1); + + int dy=y2-y1; + int dx=x2-x1; + int error_term=0; + + if ( dx==0 && dy==0 ) { + return; + } + + gr_lock(); + + ubyte *dptr = GR_SCREEN_PTR(ubyte,x1,y1); + + xlat_table = (ubyte *)&Current_alphacolor->table.lookup[0][0]; + + + if(dy<0) { + dy=-dy; + ystep=-gr_screen.rowsize / gr_screen.bytes_per_pixel; + } else { + ystep=gr_screen.rowsize / gr_screen.bytes_per_pixel; + } + + if(dx<0) { + dx=-dx; + xstep=-1; + } else { + xstep=1; + } + + if(dx>dy) { + + if (!swapped) { + l = 14<<8; + dl = (-14<<8) / dx; + } else { + l = 0; + dl = (14<<8) / dx; + } + + for(i=0;idx) { + error_term-=dx; + dptr+=ystep; + } + } + *dptr = xlat_table[(l&0xF00)|*dptr]; + + } else { + + if (!swapped) { + l = 14<<8; + dl = (-14<<8) / dy; + } else { + l = 0; + dl = (14<<8) / dy; + } + + for(i=0;i0) { + error_term-=dy; + dptr+=xstep; + } + + } + *dptr = xlat_table[(l&0xF00)|*dptr]; + + } + gr_unlock(); +#else + Int3(); +#endif +} + + + + + diff --git a/src/graphics/grd3d.cpp b/src/graphics/grd3d.cpp new file mode 100644 index 0000000..764d0bc --- /dev/null +++ b/src/graphics/grd3d.cpp @@ -0,0 +1,3045 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrD3D.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for our Direct3D renderer + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 42 10/13/99 3:49p Jefff + * fixed unnumbered XSTRs + * + * 41 9/13/99 11:25p Dave + * Fixed problem with mode-switching and D3D movies. + * + * 40 9/13/99 11:30a Dave + * Added checkboxes and functionality for disabling PXO banners as well as + * disabling d3d zbuffer biasing. + * + * 39 9/10/99 11:53a Dave + * Shutdown graphics before sound to eliminate apparent lockups when + * Directsound decides to be lame. Fix TOPMOST problem with D3D windows. + * + * 38 9/04/99 8:00p Dave + * Fixed up 1024 and 32 bit movie support. + * + * 37 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 36 8/20/99 2:09p Dave + * PXO banner cycling. + * + * 35 8/18/99 9:35a Dave + * Made d3d shutdown more stable. + * + * 34 8/11/99 3:30p Dave + * Fixed window focus problems. + * + * 33 8/04/99 5:36p Dave + * Make glide and D3D switch out properly. + * + * 32 8/02/99 6:25p Dave + * Fixed d3d screen save/popup problem. + * + * 31 7/30/99 7:01p Dave + * Dogfight escort gauge. Fixed up laser rendering in Glide. + * + * 30 7/29/99 10:47p Dave + * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs. + * + * 29 7/27/99 3:09p Dave + * Made g400 work. Whee. + * + * 28 7/24/99 4:19p Dave + * Fixed dumb code with briefing bitmaps. Made d3d zbuffer work much + * better. Made model code use zbuffer more intelligently. + * + * 27 7/16/99 1:49p Dave + * 8 bit aabitmaps. yay. + * + * 26 7/14/99 9:42a Dave + * Put in clear_color debug function. Put in base for 3dnow stuff / P3 + * stuff + * + * 25 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 24 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 23 6/03/99 6:37p Dave + * More TNT fun. Made perspective bitmaps more flexible. + * + * 22 5/05/99 9:02p Dave + * Fixed D3D aabitmap rendering. Spiffed up nebula effect a bit (added + * rotations, tweaked values, made bitmap selection more random). Fixed + * D3D beam weapon clipping problem. Added D3d frame dumping. + * + * 21 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 20 1/24/99 11:36p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 19 1/15/99 11:29a Neilk + * Fixed D3D screen/texture pixel formatting problem. + * + * 18 1/11/99 6:21p Neilk + * Fixed broken D3D card fog-capability check. + * + * 17 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 16 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 15 12/09/98 7:34p Dave + * Cleanup up nebula effect. Tweaked many values. + * + * 14 12/08/98 7:30p Dave + * Fixed broken compile. + * + * 13 12/08/98 7:03p Dave + * Much improved D3D fogging. Also put in vertex fogging for the cheesiest + * of 3d cards. + * + * 12 12/08/98 2:47p Johnson + * Made D3D fogging use eye-relative depth instead of z-depth. Works like + * Glide w-buffer now. + * + * 11 12/08/98 9:36a Dave + * Almost done nebula effect for D3D. Looks 85% as good as Glide. + * + * 10 12/07/98 5:51p Dave + * Finally got d3d fog working! Now we just need to tweak values. + * + * 9 12/06/98 6:53p Dave + * + * 8 12/06/98 3:08p Dave + * Fixed grx_tmapper to handle pixel fog flag. First run fog support for + * D3D. + * + * 7 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 6 12/01/98 10:25a Johnson + * Fixed direct3d texture coord/font problems. + * + * 5 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 4 11/11/98 5:37p Dave + * Checkin for multiplayer testing. + * + * 3 10/09/98 2:57p Dave + * Starting splitting up OS stuff. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 110 6/13/98 6:01p Hoffoss + * Externalized all new (or forgot to be added) strings to all the code. + * + * 109 6/13/98 3:18p Hoffoss + * NOX()ed out a bunch of strings that shouldn't be translated. + * + * 108 5/24/98 9:41p John + * changed allender's previous fix to actually not draw the lines on + * NDEBUG. + * + * 107 5/24/98 9:16p Allender + * put in previously non-NDEBUG code to draw bogus cursor when Gr_cursor + * wasn't defined. Caused d3d to crash before playing movies + * + * 106 5/22/98 10:29p John + * fixed some mode switching and line offset detection bugs. + * + * 105 5/22/98 1:11p John + * Added code to actually detect which offset a line needs + * + * + * $NoKeywords: $ + */ + +#include + +#include "grd3dinternal.h" + +#include "osapi.h" +#include "2d.h" +#include "bmpman.h" +#include "key.h" +#include "floating.h" +#include "palman.h" +#include "osregistry.h" +#include "grd3d.h" +#include "line.h" +#include "font.h" +#include "grinternal.h" +#include "mouse.h" +#include "alphacolors.h" +#include "systemvars.h" +#include "cfile.h" +#include "cmdline.h" + + +LPDIRECTDRAW lpDD1 = NULL; +LPDIRECTDRAW2 lpDD = NULL; +LPDIRECT3D2 lpD3D = NULL; +LPDIRECT3DDEVICE2 lpD3DDevice = NULL; +// LPDIRECT3DDEVICE lpD3DDeviceEB = NULL; +LPDIRECTDRAWSURFACE lpBackBuffer = NULL; +LPDIRECTDRAWSURFACE lpFrontBuffer = NULL; +LPDIRECTDRAWSURFACE lpZBuffer = NULL; + +LPDIRECT3DVIEWPORT2 lpViewport; + +DDPIXELFORMAT AlphaTextureFormat; +int Largest_alpha = 0; +DDPIXELFORMAT NonAlphaTextureFormat; +DDPIXELFORMAT NonAlphaTextureFormat_1555; +DDPIXELFORMAT NonAlphaTextureFormat_565; +int Largest_rgb = 0; +DDPIXELFORMAT ScreenFormat; + +static RECT D3D_cursor_clip_rect; + +D3DDEVICEDESC D3DHWDevDesc, D3DHELDevDesc; +LPD3DDEVICEDESC lpDevDesc = NULL; + +DDCAPS DD_driver_caps; +DDCAPS DD_hel_caps; + +int D3D_texture_divider = 1; + +int D3D_window = 0; + +char Device_init_error[512] = ""; + +// -1 == no fog, bad bad bad +// 0 == vertex fog +// 1 == table fog +int D3D_fog_mode = -1; + +static int In_frame = 0; + +int D3D_inited = 0; + +int DrawPrim = 0; + +int D3d_rendition_uvs = 0; + +int D3D_32bit = 0; + +int D3D_zbias = 1; +DCF(zbias, "") +{ + D3D_zbias = !D3D_zbias; +} + +#define MAX_D2D_DEVICES 8 +#define MAX_D3D_DEVICES 16 + +typedef struct d3d_device { + GUID guid_2d; + LPGUID pguid_2d; + + GUID guid_3d; + LPGUID pguid_3d; + + char name[1024]; +} d3d_device; + +d3d_device D2D_devices[MAX_D2D_DEVICES]; +d3d_device D3D_devices[MAX_D3D_DEVICES]; + +int Num_d2d_devices = 0; +int Num_d3d_devices = 0; + +d3d_device *D3D_device; + +void mprint_guid( LPGUID lpGuid ) +{ + /* + if ( !lpGuid ) { + mprintf(( "None\n" )); + } else { + int i; + for (i=0; ibOpcode = op; \ + ((LPD3DINSTRUCTION) ptr)->bSize = sz; \ + ((LPD3DINSTRUCTION) ptr)->wCount = cnt; \ + ptr = (void *)(((LPD3DINSTRUCTION) ptr) + 1); } while (0) + +#define VERTEX_DATA(loc, cnt, ptr) do { \ + if ((ptr) != (loc)) memcpy((ptr), (loc), sizeof(D3DVERTEX) * (cnt)); \ + ptr = (void *)(((LPD3DVERTEX) (ptr)) + (cnt)); } while (0) + +// OP_MATRIX_MULTIPLY size: 4 (sizeof D3DINSTRUCTION) +#define OP_MATRIX_MULTIPLY(cnt, ptr) \ + PUTD3DINSTRUCTION(D3DOP_MATRIXMULTIPLY, sizeof(D3DMATRIXMULTIPLY), cnt, ptr) + +// MATRIX_MULTIPLY_DATA size: 12 (sizeof MATRIXMULTIPLY) +#define MATRIX_MULTIPLY_DATA(src1, src2, dest, ptr) do { \ + ((LPD3DMATRIXMULTIPLY) ptr)->hSrcMatrix1 = src1; \ + ((LPD3DMATRIXMULTIPLY) ptr)->hSrcMatrix2 = src2; \ + ((LPD3DMATRIXMULTIPLY) ptr)->hDestMatrix = dest; \ + ptr = (void *)(((LPD3DMATRIXMULTIPLY) ptr) + 1); } while (0) + +// OP_STATE_LIGHT size: 4 (sizeof D3DINSTRUCTION) +#define OP_STATE_LIGHT(cnt, ptr) \ + PUTD3DINSTRUCTION(D3DOP_STATELIGHT, sizeof(D3DSTATE), cnt, ptr) + +// OP_STATE_TRANSFORM size: 4 (sizeof D3DINSTRUCTION) +#define OP_STATE_TRANSFORM(cnt, ptr) \ + PUTD3DINSTRUCTION(D3DOP_STATETRANSFORM, sizeof(D3DSTATE), cnt, ptr) + +// OP_STATE_RENDER size: 4 (sizeof D3DINSTRUCTION) +#define OP_STATE_RENDER(cnt, ptr) \ + PUTD3DINSTRUCTION(D3DOP_STATERENDER, sizeof(D3DSTATE), cnt, ptr) + +// STATE_DATA size: 8 (sizeof D3DSTATE) +#define STATE_DATA(type, arg, ptr) do { \ + ((LPD3DSTATE) ptr)->drstRenderStateType = (D3DRENDERSTATETYPE)type; \ + ((LPD3DSTATE) ptr)->dwArg[0] = arg; \ + ptr = (void *)(((LPD3DSTATE) ptr) + 1); } while (0) + +// OP_PROCESS_VERTICES size: 4 (sizeof D3DINSTRUCTION) +#define OP_PROCESS_VERTICES(cnt, ptr) \ + PUTD3DINSTRUCTION(D3DOP_PROCESSVERTICES, sizeof(D3DPROCESSVERTICES), cnt, ptr) + +// PROCESSVERTICES_DATA size: 16 (sizeof D3DPROCESSVERTICES) +#define PROCESSVERTICES_DATA(flgs, strt, cnt, ptr) do { \ + ((LPD3DPROCESSVERTICES) ptr)->dwFlags = flgs; \ + ((LPD3DPROCESSVERTICES) ptr)->wStart = strt; \ + ((LPD3DPROCESSVERTICES) ptr)->wDest = strt; \ + ((LPD3DPROCESSVERTICES) ptr)->dwCount = cnt; \ + ((LPD3DPROCESSVERTICES) ptr)->dwReserved = 0; \ + ptr = (void *)(((LPD3DPROCESSVERTICES) ptr) + 1); } while (0) + +// OP_TRIANGLE_LIST size: 4 (sizeof D3DINSTRUCTION) +#define OP_TRIANGLE_LIST(cnt, ptr) \ + PUTD3DINSTRUCTION(D3DOP_TRIANGLE, sizeof(D3DTRIANGLE), cnt, ptr) + +#define TRIANGLE_LIST_DATA(loc, count, ptr) do { \ + if ((ptr) != (loc)) memcpy((ptr), (loc), sizeof(D3DTRIANGLE) * (count)); \ + ptr = (void *)(((LPD3DTRIANGLE) (ptr)) + (count)); } while (0) + +// OP_LINE_LIST size: 4 (sizeof D3DINSTRUCTION) +#define OP_LINE_LIST(cnt, ptr) \ + PUTD3DINSTRUCTION(D3DOP_LINE, sizeof(D3DLINE), cnt, ptr) + +#define LINE_LIST_DATA(loc, count, ptr) do { \ + if ((ptr) != (loc)) memcpy((ptr), (loc), sizeof(D3DLINE) * (count)); \ + ptr = (void *)(((LPD3DLINE) (ptr)) + (count)); } while (0) + +// OP_POINT_LIST size: 8 (sizeof D3DINSTRUCTION + sizeof D3DPOINT) +#define OP_POINT_LIST(first, cnt, ptr) do { \ + PUTD3DINSTRUCTION(D3DOP_POINT, sizeof(D3DPOINT), 1, ptr); \ + ((LPD3DPOINT)(ptr))->wCount = cnt; \ + ((LPD3DPOINT)(ptr))->wFirst = first; \ + ptr = (void*)(((LPD3DPOINT)(ptr)) + 1); } while(0) + +// OP_SPAN_LIST size: 8 (sizeof D3DINSTRUCTION + sizeof D3DSPAN) +#define OP_SPAN_LIST(first, cnt, ptr) \ + PUTD3DINSTRUCTION(D3DOP_SPAN, sizeof(D3DSPAN), 1, ptr); \ + ((LPD3DSPAN)(ptr))->wCount = cnt; \ + ((LPD3DSPAN)(ptr))->wFirst = first; \ + ptr = (void*)(((LPD3DSPAN)(ptr)) + 1); } while(0) + +// OP_BRANCH_FORWARD size: 18 (sizeof D3DINSTRUCTION + sizeof D3DBRANCH) +#define OP_BRANCH_FORWARD(tmask, tvalue, tnegate, toffset, ptr) \ + PUTD3DINSTRUCTION(D3DOP_BRANCHFORWARD, sizeof(D3DBRANCH), 1, ptr); \ + ((LPD3DBRANCH) ptr)->dwMask = tmask; \ + ((LPD3DBRANCH) ptr)->dwValue = tvalue; \ + ((LPD3DBRANCH) ptr)->bNegate = tnegate; \ + ((LPD3DBRANCH) ptr)->dwOffset = toffset; \ + ptr = (void *)(((LPD3DBRANCH) (ptr)) + 1); } while (0) + +// OP_SET_STATUS size: 20 (sizeof D3DINSTRUCTION + sizeof D3DSTATUS) +#define OP_SET_STATUS(flags, status, _x1, _y1, _x2, _y2, ptr) \ + PUTD3DINSTRUCTION(D3DOP_SETSTATUS, sizeof(D3DSTATUS), 1, ptr); \ + ((LPD3DSTATUS)(ptr))->dwFlags = flags; \ + ((LPD3DSTATUS)(ptr))->dwStatus = status; \ + ((LPD3DSTATUS)(ptr))->drExtent.x1 = _x1; \ + ((LPD3DSTATUS)(ptr))->drExtent.y1 = _y1; \ + ((LPD3DSTATUS)(ptr))->drExtent.x2 = _x2; \ + ((LPD3DSTATUS)(ptr))->drExtent.y2 = _y2; \ + ptr = (void *)(((LPD3DSTATUS) (ptr)) + 1); } while (0) + +// OP_NOP size: 4 +#define OP_NOP(ptr) \ + PUTD3DINSTRUCTION(D3DOP_TRIANGLE, sizeof(D3DTRIANGLE), 0, ptr) + +#define OP_EXIT(ptr) \ + PUTD3DINSTRUCTION(D3DOP_EXIT, 0, 0, ptr) + +#define QWORD_ALIGNED(ptr) \ + (!(0x00000007L & (ULONG)(ptr))) + +#define D3D_MAX_VERTS 512 +#define D3D_EXBUFFER_SIZE 65536 +static int D3D_num_verts; +//static D3DTLVERTEX D3D_vertices[D3D_MAX_VERTS]; +static D3DTLVERTEX *D3D_vertices; +//static ubyte D3D_exbuffer[D3D_EXBUFFER_SIZE]; +static void *D3D_ex_ptr, *D3D_ex_end; +LPDIRECT3DEXECUTEBUFFER lpExBuf = NULL; + +LPVOID lpBufStart, lpPointer, lpInsStart; +int Exb_size; + + +void gr_d3d_exb_init() +{ + /* + HRESULT ddrval; + D3DEXECUTEBUFFERDESC debDesc; + + if ( DrawPrim ) { + return; + } + + Exb_size = D3D_EXBUFFER_SIZE + sizeof(D3DTLVERTEX)*D3D_MAX_VERTS; + + // create a D3DEXECUTEBUFFERDESC + memset( &debDesc, 0, sizeof( debDesc ) ); + debDesc.dwSize = sizeof( debDesc ); + debDesc.dwFlags = D3DDEB_BUFSIZE; + debDesc.dwBufferSize = Exb_size; + + // create the buffer + ddrval = lpD3DDeviceEB->CreateExecuteBuffer( &debDesc, &lpExBuf, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_D3D_INIT: CreateExecuteBuffer failed. size=%d, '%s'\n", Exb_size, d3d_error_string(ddrval) )); + goto D3DError; + } + + memset( &debDesc, 0, sizeof( debDesc ) ); + debDesc.dwSize = sizeof( debDesc ); + ddrval = lpExBuf->Lock( &debDesc ); + if ( ddrval != DD_OK ) { + mprintf(( "Failed to lock the execute buffer!\n" )); + goto D3DError; + } + + lpPointer = lpBufStart = lpInsStart = debDesc.lpData; + + lpPointer = (void *)((uint)lpPointer+sizeof(D3DTLVERTEX)*D3D_MAX_VERTS); + lpInsStart = lpPointer; + + OP_PROCESS_VERTICES( 1, lpPointer ); + PROCESSVERTICES_DATA( D3DPROCESSVERTICES_COPY, 0, 1, lpPointer ); + + D3D_num_verts = 0; + D3D_ex_ptr = lpPointer; + D3D_ex_end = (void *)((uint)lpBufStart + Exb_size - 1024); + D3D_vertices = (D3DTLVERTEX *)lpBufStart; + return; + + +D3DError: + // Reset everything + if ( lpExBuf ) { + lpExBuf->Release(); + lpExBuf = NULL; + } + gr_d3d_cleanup(); + exit(1); + */ +} + +HRESULT set_wbuffer_planes(LPDIRECT3DDEVICE2 lpDev, float dvWNear, float dvWFar) +{ + HRESULT res; + D3DMATRIX matWorld; + D3DMATRIX matView; + D3DMATRIX matProj; + + memset(&matWorld, 0, sizeof(matWorld)); + memset(&matView, 0, sizeof(matWorld)); + memset(&matProj, 0, sizeof(matWorld)); + matWorld._11 = 1; matWorld._22 = 1; matWorld._33 = 1; matWorld._44 = 1; + matView._11 = 1; matView._22 = 1; matView._33 = 1; matView._44 = 1; + matProj._11 = 1; matProj._22 = 1; matProj._33 = 1; matProj._44 = 1; + + res = lpDev->SetTransform( D3DTRANSFORMSTATE_WORLD, &matWorld ); + if (res) return res; + res = lpDev->SetTransform( D3DTRANSFORMSTATE_VIEW, &matView ); + if (res) return res; + + matProj._43 = 0; + matProj._34 = 1; + matProj._44 = dvWNear; // not used + matProj._33 = dvWNear / (dvWFar - dvWNear) + 1; + + res = lpDev->SetTransform( D3DTRANSFORMSTATE_PROJECTION, &matProj ); + return res; +} + +DCF(wplanes, "") +{ + dc_get_arg(ARG_FLOAT); + float n = Dc_arg_float; + dc_get_arg(ARG_FLOAT); + float f = Dc_arg_float; + set_wbuffer_planes(lpD3DDevice, n, f); +} + +void gr_d3d_exb_flush(int end_of_frame) +{ + /* + HRESULT ddrval; + D3DEXECUTEBUFFERDESC debDesc; + D3DEXECUTEDATA d3dExData; + + if ( DrawPrim ) { + return; + } + + if (!lpExBuf) return; + + OP_EXIT( D3D_ex_ptr ); + + lpPointer = lpInsStart; + OP_PROCESS_VERTICES( 1, lpPointer ); + PROCESSVERTICES_DATA( D3DPROCESSVERTICES_COPY, 0, D3D_num_verts, lpPointer ); + + ddrval = lpExBuf->Unlock(); + if (ddrval != DD_OK ) { + mprintf(( "Failed to unlock the execute buffer!\n" )); + goto D3DError; + } + + memset(&d3dExData, 0, sizeof(D3DEXECUTEDATA)); + d3dExData.dwSize = sizeof(D3DEXECUTEDATA); + d3dExData.dwVertexCount = D3D_num_verts; + d3dExData.dwInstructionOffset = (ULONG)((char*)lpInsStart - (char*)lpBufStart); + d3dExData.dwInstructionLength = (ULONG)((char*)D3D_ex_ptr - (char*)lpInsStart); + +// if (end_of_frame==0) { +// mprintf(( "Flushing execute buffer in frame, %d verts, %d data size!\n", D3D_num_verts, d3dExData.dwInstructionLength )); +// } else if (end_of_frame==2) { +// mprintf(( "Flushing execute buffer in frame, because of VRAM flush!\n" )); +// } + + ddrval = lpExBuf->SetExecuteData(&d3dExData); + if (ddrval != DD_OK ) { + mprintf(( "Failed to SetExecuteData!\n" )); + goto D3DError; + } + + ddrval = lpD3DDeviceEB->Execute( lpExBuf, lpViewport, D3DEXECUTE_UNCLIPPED ); + if (ddrval != DD_OK ) { + mprintf(( "Failed to Execute! nverts=%d\n", D3D_num_verts)); + mprintf(( "(%s)\n", d3d_error_string(ddrval) )); + goto D3DError; + } + + + memset( &debDesc, 0, sizeof( debDesc ) ); + debDesc.dwSize = sizeof( debDesc ); + ddrval = lpExBuf->Lock( &debDesc ); + if ( ddrval != DD_OK ) { + mprintf(( "Failed to lock the execute buffer!\n" )); + goto D3DError; + } + + lpPointer = lpBufStart = lpInsStart = debDesc.lpData; + + lpPointer = (void *)((uint)lpPointer+sizeof(D3DTLVERTEX)*D3D_MAX_VERTS); + lpInsStart = lpPointer; + + OP_PROCESS_VERTICES( 1, lpPointer ); + PROCESSVERTICES_DATA( D3DPROCESSVERTICES_COPY, 0, 1, lpPointer ); + + D3D_num_verts = 0; + D3D_ex_ptr = lpPointer; + D3D_ex_end = (void *)((uint)lpBufStart + Exb_size - 1024); + D3D_vertices = (D3DTLVERTEX *)lpBufStart; + return; + + +D3DError: + // Reset everything + + if ( lpExBuf ) { + lpExBuf->Release(); + lpExBuf = NULL; + } +// gr_d3d_cleanup(); +// exit(1); +*/ +} + + + +HRESULT d3d_SetRenderState( D3DRENDERSTATETYPE dwRenderStateType, DWORD dwRenderState ) +{ + if ( DrawPrim ) { + return lpD3DDevice->SetRenderState(dwRenderStateType, dwRenderState ); + } else { + if ( (uint)D3D_ex_ptr > (uint)D3D_ex_end ) { + gr_d3d_exb_flush(0); + } + OP_STATE_RENDER(1, D3D_ex_ptr); + STATE_DATA(dwRenderStateType, dwRenderState, D3D_ex_ptr); + return DD_OK; + } + +} + +HRESULT d3d_DrawPrimitive( D3DPRIMITIVETYPE dptPrimitiveType, D3DVERTEXTYPE dvtVertexType, LPVOID lpvVertices, DWORD dwVertexCount, DWORD dwFlags ) +{ + if ( DrawPrim ) { + return lpD3DDevice->DrawPrimitive(dptPrimitiveType, dvtVertexType, lpvVertices, dwVertexCount, dwFlags ); + } else { + + switch( dptPrimitiveType ) { + + case D3DPT_TRIANGLEFAN: + if ( dvtVertexType == D3DVT_TLVERTEX ) { + + D3DTLVERTEX *Verts = (D3DTLVERTEX *)lpvVertices; + + if ( D3D_num_verts + dwVertexCount > D3D_MAX_VERTS ) gr_d3d_exb_flush(0); + if ( (uint)D3D_ex_ptr > (uint)D3D_ex_end ) gr_d3d_exb_flush(0); + + D3DTLVERTEX *src_v = &D3D_vertices[D3D_num_verts]; + + int i; + for (i=0; i<(int)dwVertexCount; i++ ) { + *src_v++ = Verts[i]; + } + + // triangle data must be QWORD aligned, so we need to make sure + // that the OP_TRIANGLE_LIST is unaligned! Note that you MUST have + // the braces {} around the OP_NOP since the macro in D3DMACS.H will + // fail if you remove them. + + if ( QWORD_ALIGNED( D3D_ex_ptr ) ) { + OP_NOP( D3D_ex_ptr ); + } + + OP_TRIANGLE_LIST( unsigned short(dwVertexCount-2), D3D_ex_ptr ); + + LPD3DTRIANGLE tri = ( LPD3DTRIANGLE )D3D_ex_ptr; + + for (i=0; i<(int)dwVertexCount-2; i++ ) { + tri->v1 = unsigned short(D3D_num_verts+0); + tri->v2 = unsigned short(D3D_num_verts+i+1); + tri->v3 = unsigned short(D3D_num_verts+i+2); + tri->wFlags = D3DTRIFLAG_EDGEENABLETRIANGLE; + tri++; + + } + D3D_ex_ptr = ( LPVOID )tri; + D3D_num_verts += (int)dwVertexCount; + } + break; + + case D3DPT_LINELIST: + if ( dvtVertexType == D3DVT_TLVERTEX ) { + + D3DTLVERTEX *Verts = (D3DTLVERTEX *)lpvVertices; + + if ( D3D_num_verts + dwVertexCount > D3D_MAX_VERTS ) gr_d3d_exb_flush(0); + if ( (uint)D3D_ex_ptr > (uint)D3D_ex_end ) gr_d3d_exb_flush(0); + + D3DTLVERTEX *src_v = &D3D_vertices[D3D_num_verts]; + + int i; + for (i=0; i<(int)dwVertexCount; i++ ) { + *src_v++ = Verts[i]; + } + + // triangle data must be QWORD aligned, so we need to make sure + // that the OP_TRIANGLE_LIST is unaligned! Note that you MUST have + // the braces {} around the OP_NOP since the macro in D3DMACS.H will + // fail if you remove them. + + if ( QWORD_ALIGNED( D3D_ex_ptr ) ) { + OP_NOP( D3D_ex_ptr ); + } + + ushort nlines = ushort(dwVertexCount/2); + + OP_LINE_LIST( nlines, D3D_ex_ptr ); + + for (i=0; i<(int)nlines; i++ ) { + LPD3DLINE line = (LPD3DLINE )D3D_ex_ptr; + line->v1 = unsigned short(D3D_num_verts); + line->v2 = unsigned short(D3D_num_verts+1); + D3D_ex_ptr = (void *)(((LPD3DLINE)D3D_ex_ptr) + 1); + } + + D3D_num_verts += (int)dwVertexCount; + } + break; + } + return DD_OK; + } +} + +volatile int D3D_running = 0; +volatile int D3D_activate = 0; +volatile int D3D_deactivate = 0; + +void gr_d3d_activate(int active) +{ + if ( !D3D_running ) { + return; + } + mprintf(( "Direct3D activate: %d\n", active )); + + HWND hwnd = (HWND)os_get_window(); + + if ( active ) { + D3D_activate++; + + if ( hwnd ) { + ClipCursor(&D3D_cursor_clip_rect); + ShowWindow(hwnd,SW_RESTORE); + } + } else { + D3D_deactivate++; + + if ( hwnd ) { + ClipCursor(NULL); + ShowWindow(hwnd,SW_MINIMIZE); + } + } +} + + +void d3d_start_frame() +{ + HRESULT ddrval; + + if (!D3D_inited) return; + + if ( In_frame < 0 || In_frame > 1 ) { + mprintf(( "Start frame error! (%d)\n", In_frame )); + return; + } + if ( In_frame == 1 ) return; + +// gr_d3d_clear_surface(lpBackBuffer, 0.0f, 0.0f, 0.0f); + + In_frame++; + + ddrval = lpD3DDevice->BeginScene(); + if (ddrval != D3D_OK ) { + //mprintf(( "Failed to begin scene!\n%s\n", d3d_error_string(ddrval) )); + return; + } + + +} + + +void d3d_stop_frame() +{ + HRESULT ddrval; + + if (!D3D_inited) return; + + if ( In_frame < 0 || In_frame > 1 ) { + mprintf(( "Stop frame error! (%d)\n", In_frame )); + return; + } + + if ( In_frame == 0 ) return; + + gr_d3d_exb_flush(1); + + In_frame--; + + ddrval = lpD3DDevice->EndScene(); + if (ddrval != D3D_OK ) { + //mprintf(( "Failed to end scene!\n%s\n", d3d_error_string(ddrval) )); + return; + } + +} + +void d3d_flush() +{ + d3d_stop_frame(); + d3d_start_frame(); +} + +static void d3d_dump_format(DDPIXELFORMAT *pf) +{ + unsigned long m; + int r, g, b, a; + for (r = 0, m = pf->dwRBitMask; !(m & 1); r++, m >>= 1); + for (r = 0; m & 1; r++, m >>= 1); + for (g = 0, m = pf->dwGBitMask; !(m & 1); g++, m >>= 1); + for (g = 0; m & 1; g++, m >>= 1); + for (b = 0, m = pf->dwBBitMask; !(m & 1); b++, m >>= 1); + for (b = 0; m & 1; b++, m >>= 1); + if ( pf->dwFlags & DDPF_ALPHAPIXELS ) { + for (a = 0, m = pf->dwRGBAlphaBitMask; !(m & 1); a++, m >>= 1); + for (a = 0; m & 1; a++, m >>= 1); + mprintf(( "ARGB, %d:%d:%d:%d\n", a, r, g, b )); + } else { + a = 0; + mprintf(( "RGB, %d:%d:%d\n", r, g, b )); + } +} + +int D3D_found_1555_tex = 0; +int D3D_found_565_tex = 0; +static HRESULT WINAPI EnumTextureFormatsCallback(LPDDSURFACEDESC lpDDSD, LPVOID lpContext) +{ + if (lpDDSD->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXEDTO8) { + mprintf(( "Palettized to an 8 bpp palette\n" )); + } + + if (lpDDSD->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) { + mprintf(( "Palettized 8 bpp\n" )); +// TextureFormat = *lpDDSD; +// mprintf(( " ^-- And I'm using this one!\n" )); + } else if (lpDDSD->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED4) { + mprintf(( "Palettized 4 bpp\n" )); + return DDENUMRET_OK; + } else if (lpDDSD->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED2) { + mprintf(( "Palettized 2 bpp\n" )); + return DDENUMRET_OK; + } else if (lpDDSD->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED1) { + mprintf(( "Palettized 1 bpp\n" )); + } else if (lpDDSD->ddpfPixelFormat.dwFlags & DDPF_ALPHA ) { + mprintf(( "Alpha something or other\n" )); + return DDENUMRET_OK; + } else if (lpDDSD->ddpfPixelFormat.dwFlags & DDPF_YUV ) { + mprintf(( "YUV?\n" )); + return DDENUMRET_OK; + } else if (lpDDSD->ddpfPixelFormat.dwFlags & DDPF_ZBUFFER ) { + mprintf(( "ZBUFFER?\n" )); + return DDENUMRET_OK; + } else if (lpDDSD->ddpfPixelFormat.dwFlags & DDPF_COMPRESSED ) { + mprintf(( "Compressed?\n" )); + return DDENUMRET_OK; + } else if (lpDDSD->ddpfPixelFormat.dwFlags & DDPF_FOURCC) { + mprintf(( "FourCC?\n" )); + return DDENUMRET_OK; + } else { + unsigned long m; + int r, g, b, a; + for (r = 0, m = lpDDSD->ddpfPixelFormat.dwRBitMask; !(m & 1) && (r < 32); r++, m >>= 1); + for (r = 0; m & 1; r++, m >>= 1); + for (g = 0, m = lpDDSD->ddpfPixelFormat.dwGBitMask; !(m & 1) && (g < 32); g++, m >>= 1); + for (g = 0; m & 1; g++, m >>= 1); + for (b = 0, m = lpDDSD->ddpfPixelFormat.dwBBitMask; !(m & 1) && (b < 32); b++, m >>= 1); + for (b = 0; m & 1; b++, m >>= 1); + if ( lpDDSD->ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS ) { + for (a = 0, m = lpDDSD->ddpfPixelFormat.dwRGBAlphaBitMask; !(m & 1) && (a < 32); a++, m >>= 1); + for (a = 0; m & 1; a++, m >>= 1); + mprintf(( "ARGB, %d:%d:%d:%d\n", a, r, g, b )); + } else { + a = 0; + mprintf(( "RGB, %d:%d:%d\n", r, g, b )); + } + + // if we found a nice 1555 texture format + if( (r == 5) && (g == 5) && (b == 5) && (a == 1)){ + Largest_rgb = 15; + NonAlphaTextureFormat_1555 = lpDDSD->ddpfPixelFormat; + NonAlphaTextureFormat = lpDDSD->ddpfPixelFormat; + D3D_found_1555_tex = 1; + } + // 565 textures + else if ( (r == 5) && (g == 6) && (b == 5) ){ + NonAlphaTextureFormat_565 = lpDDSD->ddpfPixelFormat; + D3D_found_565_tex = 1; + } + // otherwise keep looking + else if(!D3D_found_1555_tex) { + //if ( (r==4) && (g==4) && (b==4) && (a==4) && (lpDDSD->ddpfPixelFormat.dwRGBBitCount==16) ) { + if ( (r>0) && (g>0) && (b>0) && (lpDDSD->ddpfPixelFormat.dwRGBBitCount==16) ) { + if ( r+g+b > Largest_rgb ) { + Largest_rgb = r+g+b; + NonAlphaTextureFormat = lpDDSD->ddpfPixelFormat; + } + } + } + + // HACK!!! Some 3dfx cards (Allender, Whiteside, Johnson) choose + // ARGB=8:3:3:2 as a texture format but when you go to create a surface + // in system RAM with this format, it fails, saying invalid surface format... + // So I'll just force 4:4:4:4 which seems to work on all cards I've seen. + if ( (a>0) && (a<8) && (lpDDSD->ddpfPixelFormat.dwRGBBitCount<=16) ) { + if ( a > Largest_alpha ) { + Largest_alpha = a; + AlphaTextureFormat = lpDDSD->ddpfPixelFormat; + } + } + + } + return DDENUMRET_OK; +} + +HRESULT WINAPI gr_d3d_enum( LPGUID lpGUID, + LPSTR lpDeviceDescription, + LPSTR lpDeviceName, + LPD3DDEVICEDESC lpHWDesc, + LPD3DDEVICEDESC lpHELDesc, + LPVOID lpContext ) +{ + int use_it = 0; + +// mprintf(( "Found 3d device %s: %s\n", lpDeviceName, lpDeviceDescription )); + + if ( lpHWDesc && lpHWDesc->dwFlags != 0 ) { + use_it = 1; + } //else if ( lpHELDesc ) { + + + if ( use_it ) { + d3d_device *d2d = (d3d_device *)lpContext; + d3d_device *d3d = (d3d_device *)&D3D_devices[Num_d3d_devices++]; + + if ( lpGUID ) { + memmove( &d3d->guid_3d, lpGUID, sizeof(GUID) ); + d3d->pguid_3d = &d3d->guid_3d; + } else { + memset( &d3d->guid_3d, 0, sizeof(GUID) ); + d3d->pguid_3d = NULL; + } + + memmove( &d3d->guid_2d, &d2d->guid_2d, sizeof(GUID) ); + if ( d2d->pguid_2d ) { + d3d->pguid_2d = &d3d->guid_2d; + } else { + d3d->pguid_2d = NULL; + } + + //strcpy( d3d->name, lpDeviceName ); + //strcat( d3d->name, " - " ); + strcpy( d3d->name, NOX("Direct 3D - ") ); + strcat( d3d->name, d2d->name ); + //strcat( d3d->name, lpDeviceDescription ); + } + + return D3DENUMRET_OK; +} + +BOOL WINAPI gr_d2d_enum( LPGUID lpGUID, + LPSTR lpDeviceDescription, + LPSTR lpDeviceName, + LPVOID lpContext ) +{ + d3d_device *d2d = (d3d_device *)&D2D_devices[Num_d2d_devices++]; + +// mprintf(( "Found 2d device %s: %s\n", lpDeviceName, lpDeviceDescription )); + + if ( lpGUID ) { + memmove( &d2d->guid_2d, lpGUID, sizeof(GUID) ); + d2d->pguid_2d = &d2d->guid_2d; + } else { + memset( &d2d->guid_2d, 0, sizeof(GUID) ); + d2d->pguid_2d = NULL; + } + + strcpy( d2d->name, lpDeviceDescription ); +// strcat( d2d->name, lpDeviceName ); + + return D3DENUMRET_OK; +} + +d3d_device *d3d_poll_devices() +{ + int i; + HRESULT ddrval; + + Num_d2d_devices = 0; + Num_d3d_devices = 0; + + ddrval = DirectDrawEnumerate( gr_d2d_enum, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_D3D_INIT: DirectDrawEnumerate failed.\n" )); + goto D3DError; + } + + for ( i=0; ipguid_2d, &lpDD1, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_D3D_INIT: DirectDrawCreate failed.\n" )); + goto D3DError; + } + + ddrval = lpDD1->QueryInterface( IID_IDirect3D2, ( LPVOID *) &lpD3D ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_D3D_INIT: QueryInterface failed.\n" )); + goto D3DError; + } + + ddrval = lpD3D->EnumDevices(gr_d3d_enum, d2d ); + if ( ddrval != DD_OK ) { + mprintf(( "WIN_DD32: D3D enum devices failed. (0x%x)\n", ddrval )); + } + + lpD3D->Release(); + lpD3D = NULL; + + lpDD1->Release(); + lpDD1 = NULL; + } + + for ( i=0; i 0 ) { + //mprintf(( "More than one D3D device found!\n" )); + + char *name = os_config_read_string( NULL, "VideoCard", NULL ); + + if ( name && (strlen(name)>0) ) { + for ( i=0; iCreateSurface( &ddsd, &lpFrontBuffer, NULL ); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: CreateSurface (Front) failed.\n" )); + strcpy(Device_init_error, "CreateSurface (Front) failed."); + return 0; + } + + // create a clipper and a backbuffer in windowed mode + if(D3D_window){ + // create a clipper for windowed mode + LPDIRECTDRAWCLIPPER pcClipper; + HRESULT hr = lpDD->CreateClipper(0, &pcClipper, NULL); + if(hr != DD_OK){ + return 0; + } + + // Associate the clipper with our window. + pcClipper->SetHWnd(0, (HWND)os_get_window()); + lpFrontBuffer->SetClipper(pcClipper); + pcClipper->Release(); + + // backbuffer + ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE; + ddsd.dwWidth = gr_screen.max_w; + ddsd.dwHeight = gr_screen.max_h; + hr = lpDD->CreateSurface(&ddsd, &lpBackBuffer, NULL); + if(hr != DD_OK){ + strcpy(Device_init_error, "Windowed backbuffer create failed."); + return 0; + } + } + // backbuffer is already created for us in fullscreen mode + else { + ddscaps.dwCaps = DDSCAPS_BACKBUFFER; + ddrval = lpFrontBuffer->GetAttachedSurface( &ddscaps, &lpBackBuffer ); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: GetAttachedSurface (Back) failed.\n" )); + strcpy(Device_init_error, "GetAttachedSurface (Back) failed."); + return 0; + } + } + } + + // Create a z-buffer and attach it to the backbuffer + { + DDSURFACEDESC ddsd; + + memset( &ddsd, 0, sizeof( ddsd ) ); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_ZBUFFERBITDEPTH; + ddsd.dwWidth = gr_screen.max_w; + ddsd.dwHeight = gr_screen.max_h; + ddsd.dwZBufferBitDepth = 16; + + ddsd.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | DDSCAPS_VIDEOMEMORY; + if (lpDD->CreateSurface(&ddsd, &lpZBuffer, NULL) != DD_OK) { + // mprintf(( "GR_D3D_INIT: Create Zbuffer failed.\n" )); + strcpy(Device_init_error, "Create Zbuffer failed."); + return 0; + } + + if (lpBackBuffer->AddAttachedSurface(lpZBuffer) != DD_OK) { + // mprintf(( "GR_D3D_INIT: Attach Zbuffer failed.\n" )); + strcpy(Device_init_error, "Attach Zbuffer failed."); + return 0; + } + } + + // blit all the buffers clear + if(clear){ + // Clear the surface + DDBLTFX ddBltFx; + ddBltFx.dwSize = sizeof(DDBLTFX); + ddBltFx.dwFillColor = 0; + lpFrontBuffer->Blt(NULL, NULL, NULL, DDBLT_COLORFILL, &ddBltFx); + lpBackBuffer->Blt(NULL, NULL, NULL, DDBLT_COLORFILL, &ddBltFx); + } + + // Create the D3D device + lpD3DDevice = NULL; + ddrval = lpD3D->CreateDevice( D3D_device->guid_3d, lpBackBuffer, &lpD3DDevice ); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: Create D3D Device2 failed. %s\n", d3d_error_string(ddrval) )); + sprintf(Device_init_error, "Create D3D Device2 failed. %s\n", d3d_error_string(ddrval)); + return 0; + } + + // Create and add viewport + ddrval = lpD3D->CreateViewport( &lpViewport, NULL ); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: CreateViewport failed.\n" )); + strcpy(Device_init_error, "CreateViewport failed."); + return 0; + } + + ddrval = lpD3DDevice->AddViewport( lpViewport ); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: AddViewport failed.\n" )); + strcpy(Device_init_error, "CreateViewport failed."); + return 0; + } + + + // Setup the viewport for a reasonable viewing area + D3DVIEWPORT viewdata; + DWORD largest_side; + + memset( &viewdata, 0, sizeof( viewdata ) ); + + // Compensate for aspect ratio + if ( gr_screen.max_w > gr_screen.max_h ){ + largest_side = gr_screen.max_w; + } else { + largest_side = gr_screen.max_h; + } + + // this sets up W data so the fogging can work + extern float z_mult; + set_wbuffer_planes(lpD3DDevice, 0.0f, z_mult); + + viewdata.dwSize = sizeof( viewdata ); + viewdata.dwX = viewdata.dwY = 0; + viewdata.dwWidth = gr_screen.max_w; + viewdata.dwHeight = gr_screen.max_h; + viewdata.dvScaleX = largest_side / 2.0F; + viewdata.dvScaleY = largest_side / 2.0F; + viewdata.dvMaxX = ( float ) ( viewdata.dwWidth / ( 2.0F * viewdata.dvScaleX ) ); + viewdata.dvMaxY = ( float ) ( viewdata.dwHeight / ( 2.0F * viewdata.dvScaleY ) ); + viewdata.dvMinZ = 0.0f; + viewdata.dvMaxZ = 1.0f; // choose something appropriate here! + + ddrval = lpViewport->SetViewport( &viewdata ); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: SetViewport failed.\n" )); + strcpy(Device_init_error, "SetViewport failed."); + return 0; + } + + ddrval = lpD3DDevice->SetCurrentViewport(lpViewport ); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: SetCurrentViewport failed.\n" )); + strcpy(Device_init_error, "SetCurrentViewport failed."); + return 0; + } + + return 1; +} + +void gr_d3d_release_rendering_objects() +{ + if ( lpViewport ) { + lpViewport->Release(); + lpViewport = NULL; + } + + if ( lpD3DDevice ) { + lpD3DDevice->Release(); + lpD3DDevice = NULL; + } + + if (lpZBuffer) { + lpZBuffer->Release(); + lpZBuffer = NULL; + } + + if (lpBackBuffer) { + lpBackBuffer->Release(); + lpBackBuffer = NULL; + } + + if (lpFrontBuffer) { + lpFrontBuffer->Release(); + lpFrontBuffer = NULL; + } +} + +void gr_d3d_set_initial_render_state() +{ + d3d_SetRenderState(D3DRENDERSTATE_DITHERENABLE, TRUE ); + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREMIN, D3DFILTER_LINEAR ); + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREMAG, D3DFILTER_LINEAR ); + d3d_SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD ); + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREPERSPECTIVE, TRUE ); + d3d_SetRenderState(D3DRENDERSTATE_SPECULARENABLE, FALSE ); +} + +void gr_d3d_init_device(int screen_width, int screen_height) +{ + HRESULT ddrval; + HWND hwnd; + + if ( os_config_read_uint( NULL, NOX("D3DUseExecuteBuffers"), 0 )) { + DrawPrim = 0; + } else { + DrawPrim = 1; + } + + os_suspend(); + d3d_device *dd = d3d_poll_devices(); + os_resume(); + if (!dd ) { + // Error( LOCATION, "No Direct3D devices found!\n" ); + strcpy(Device_init_error, "No Direct3D devices found!"); + goto D3DError; + } + + // Let things catch up.... +// Sleep(1000); + + hwnd = (HWND)os_get_window(); + if ( !hwnd ) { + // mprintf(( "gr_d3d_init_device: No window handle.\n" )); + strcpy(Device_init_error, "Could not get application window handle"); + return; + } + + // windowed + if(D3D_window){ + SetWindowPos(hwnd, HWND_TOP, 0, 0, gr_screen.max_w, gr_screen.max_h, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_DRAWFRAME); + SetForegroundWindow(hwnd); + SetActiveWindow(hwnd); + + D3D_cursor_clip_rect.left = 0; + D3D_cursor_clip_rect.top = 0; + D3D_cursor_clip_rect.right = gr_screen.max_w-1; + D3D_cursor_clip_rect.bottom = gr_screen.max_h-1; + } else { + // Prepare the window to go full screen + #ifndef NDEBUG + mprintf(( "Window in debugging mode... mouse clicking may cause problems!\n" )); + SetWindowLong( hwnd, GWL_EXSTYLE, 0 ); + SetWindowLong( hwnd, GWL_STYLE, WS_POPUP ); + ShowWindow(hwnd, SW_SHOWNORMAL ); + RECT work_rect; + SystemParametersInfo( SPI_GETWORKAREA, 0, &work_rect, 0 ); + SetWindowPos( hwnd, HWND_TOPMOST, work_rect.left, work_rect.top, gr_screen.max_w, gr_screen.max_h, 0 ); + SetActiveWindow(hwnd); + SetForegroundWindow(hwnd); + D3D_cursor_clip_rect.left = work_rect.left; + D3D_cursor_clip_rect.top = work_rect.top; + D3D_cursor_clip_rect.right = work_rect.left + gr_screen.max_w - 1; + D3D_cursor_clip_rect.bottom = work_rect.top + gr_screen.max_h - 1; + #else + SetWindowLong( hwnd, GWL_EXSTYLE, 0 ); + SetWindowLong( hwnd, GWL_STYLE, WS_POPUP ); + ShowWindow(hwnd, SW_SHOWNORMAL ); + // SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ), 0 ); + SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, gr_screen.max_w, gr_screen.max_h, 0 ); + SetActiveWindow(hwnd); + SetForegroundWindow(hwnd); + D3D_cursor_clip_rect.left = 0; + D3D_cursor_clip_rect.top = 0; + D3D_cursor_clip_rect.right = gr_screen.max_w-1; + D3D_cursor_clip_rect.bottom = gr_screen.max_h-1; + #endif + } + + // active d3d device + D3D_device = dd; + + os_suspend(); + ddrval = DirectDrawCreate( dd->pguid_2d, &lpDD1, NULL ); + os_resume(); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: DirectDrawCreate failed.\n" )); + strcpy(Device_init_error, "DirectDrawCreate failed"); + goto D3DError; + } + + ddrval = lpDD1->QueryInterface(IID_IDirectDraw2, (LPVOID *)&lpDD); + if(ddrval != DD_OK) { + // mprintf(( "GR_D3D_INIT: DirectDrawCreate2 failed.\n" )); + strcpy(Device_init_error, "DirectDrawCreate2 failed"); + goto D3DError; + } + + if(D3D_window){ + ddrval = lpDD->SetCooperativeLevel( hwnd, DDSCL_NORMAL ); + } else { + ddrval = lpDD->SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_NOWINDOWCHANGES ); + } + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: SetCooperativeLevel EXCLUSIVE failed.\n" )); + strcpy(Device_init_error, "SetCooperativeLevel EXCLUSIVE failed."); + goto D3DError; + } + + // Go to full screen! + if(!D3D_window){ + os_suspend(); + ddrval = lpDD->SetDisplayMode( screen_width, screen_height, gr_screen.bits_per_pixel, 0, 0 ); + os_resume(); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: SetDisplayMode failed.\n" )); + strcpy(Device_init_error, "SetDisplayMode failed."); + goto D3DError; + } + } + + gr_d3d_clip_cursor(1); + + ddrval = lpDD->QueryInterface( IID_IDirect3D2, ( LPVOID *) &lpD3D ); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: QueryInterface failed.\n" )); + strcpy(Device_init_error, "QueryInterface failed."); + goto D3DError; + } + + // create all surfaces here + if(!gr_d3d_create_rendering_objects(1)){ + goto D3DError; + } + + { + extern void dd_get_shift_masks( DDSURFACEDESC *ddsd ); + DDSURFACEDESC ddsd; + memset( &ddsd, 0, sizeof( ddsd ) ); + ddsd.dwSize = sizeof(ddsd); + lpBackBuffer->GetSurfaceDesc(&ddsd); + dd_get_shift_masks( &ddsd ); + ScreenFormat = ddsd.ddpfPixelFormat; + } + + // if we're in windowed mode, fill in bits per pixel and bytes per pixel here + if(D3D_window){ + gr_screen.bits_per_pixel = ScreenFormat.dwRGBBitCount; + gr_screen.bytes_per_pixel = gr_screen.bits_per_pixel / 8; + } + + // Init values so we can choose the largest of each format + Largest_alpha = 0; + Largest_rgb = 0; + D3D_found_1555_tex = 0; + D3D_found_565_tex = 0; + ddrval = lpD3DDevice->EnumTextureFormats(EnumTextureFormatsCallback, NULL ); + if ( ddrval != DD_OK ) { + // mprintf(( "GR_D3D_INIT: EnumTextureFormats failed.\n" )); + strcpy(Device_init_error, "EnumTextureFormats failed."); + goto D3DError; + } + + // if we found our ideal texture, set the global pixel format + if(D3D_found_1555_tex){ + Bm_pixel_format = BM_PIXEL_FORMAT_ARGB_D3D; + } + + if ( Largest_alpha == 0 ) { + gr_d3d_cleanup(); + MessageBox( NULL, XSTR("Alpha channel textures",620), XSTR("Missing Features",621), MB_OK|MB_TASKMODAL|MB_SETFOREGROUND ); + exit(1); + } + if ( Largest_rgb == 0 ) { + gr_d3d_cleanup(); + MessageBox( NULL, XSTR("16-bpp RGB textures",622), XSTR("Missing Features",621), MB_OK|MB_TASKMODAL|MB_SETFOREGROUND ); + exit(1); + } + + // setup texture pixel formats + { + DDPIXELFORMAT *surface_desc; + int s; + // RGB decoder + unsigned long m; + + // Determine the red, green and blue masks' shift and scale. + surface_desc = &NonAlphaTextureFormat; + for (s = 0, m = surface_desc->dwRBitMask; !(m & 1); s++, m >>= 1); + Gr_t_red.mask = surface_desc->dwRBitMask; + Gr_t_red.shift = s; + Gr_t_red.scale = 255 / (surface_desc->dwRBitMask >> s); + for (s = 0, m = surface_desc->dwGBitMask; !(m & 1); s++, m >>= 1); + Gr_t_green.mask = surface_desc->dwRBitMask; + Gr_t_green.shift = s; + Gr_t_green.scale = 255 / (surface_desc->dwGBitMask >> s); + for (s = 0, m = surface_desc->dwBBitMask; !(m & 1); s++, m >>= 1); + Gr_t_blue.mask = surface_desc->dwRBitMask; + Gr_t_blue.shift = s; + Gr_t_blue.scale = 255 / (surface_desc->dwBBitMask >> s); + Gr_t_alpha.mask = surface_desc->dwRGBAlphaBitMask; + if ( surface_desc->dwFlags & DDPF_ALPHAPIXELS ) { + for (s = 0, m = surface_desc->dwRGBAlphaBitMask; !(m & 1); s++, m >>= 1); + Gr_t_alpha.shift = s; + Gr_t_alpha.scale = 255 / (surface_desc->dwRGBAlphaBitMask >> s); + } else { + Gr_t_alpha.shift = 0; + Gr_t_alpha.scale = 256; + } + + // Determine the red, green and blue masks' shift and scale. + surface_desc = &AlphaTextureFormat; + for (s = 0, m = surface_desc->dwRBitMask; !(m & 1); s++, m >>= 1); + Gr_ta_red.mask = surface_desc->dwRBitMask; + Gr_ta_red.shift = s; + Gr_ta_red.scale = 255 / (surface_desc->dwRBitMask >> s); + for (s = 0, m = surface_desc->dwGBitMask; !(m & 1); s++, m >>= 1); + Gr_ta_green.mask = surface_desc->dwRBitMask; + Gr_ta_green.shift = s; + Gr_ta_green.scale = 255 / (surface_desc->dwGBitMask >> s); + for (s = 0, m = surface_desc->dwBBitMask; !(m & 1); s++, m >>= 1); + Gr_ta_blue.mask = surface_desc->dwRBitMask; + Gr_ta_blue.shift = s; + Gr_ta_blue.scale = 255 / (surface_desc->dwBBitMask >> s); + Gr_ta_alpha.mask = surface_desc->dwRGBAlphaBitMask; + if ( surface_desc->dwFlags & DDPF_ALPHAPIXELS ) { + for (s = 0, m = surface_desc->dwRGBAlphaBitMask; !(m & 1); s++, m >>= 1); + Gr_ta_alpha.shift = s; + Gr_ta_alpha.scale = 255 / (surface_desc->dwRGBAlphaBitMask >> s); + } else { + Gr_ta_alpha.shift = 0; + Gr_ta_alpha.scale = 256; + } + } + + mprintf(( "Alpha texture format = " )); + d3d_dump_format(&AlphaTextureFormat); + + mprintf(( "Non-alpha texture format = " )); + d3d_dump_format(&NonAlphaTextureFormat); + + mprintf(( "Screen format = " )); + d3d_dump_format(&ScreenFormat); + +// key_getch(); + + // gr_d3d_exb_init(); + + memset( &D3DHWDevDesc, 0, sizeof( D3DHWDevDesc ) ); + D3DHWDevDesc.dwSize = sizeof(D3DHELDevDesc); + + memset( &D3DHELDevDesc, 0, sizeof( D3DHELDevDesc ) ); + D3DHELDevDesc.dwSize = sizeof(D3DHELDevDesc); + + ddrval = lpD3DDevice->GetCaps( &D3DHWDevDesc, &D3DHELDevDesc ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_D3D_INIT: 3DDevice->GetCaps failed.\n" )); + goto D3DError; + } + lpDevDesc = &D3DHWDevDesc; + + lpDD->GetCaps(&DD_driver_caps,&DD_hel_caps); + + { + int not_good = 0; + + char missing_features[128*1024]; + + strcpy( missing_features, XSTR("Your video card is missing the following features required by FreeSpace:\r\n\r\n",623) ); + + // fog + if ( !(lpDevDesc->dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGVERTEX) && !(lpDevDesc->dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGTABLE)){ + strcat( missing_features, XSTR("Vertex fog or Table fog\r\n", 1499) ); + not_good++; + } + + // Texture blending values + if ( !(lpDevDesc->dpcTriCaps.dwTextureBlendCaps & D3DPTBLENDCAPS_MODULATE )) { + strcat( missing_features, XSTR("Texture blending mode = Modulate\r\n", 624) ); + not_good++; + } + + // Source blending values +// if ( !(lpDevDesc->dpcTriCaps.dwSrcBlendCaps & D3DPBLENDCAPS_ONE) ) { +// strcat( missing_features, "Source blending mode = ONE\r\n" ); +// not_good++; +// } + + if ( !(lpDevDesc->dpcTriCaps.dwSrcBlendCaps & (D3DPBLENDCAPS_SRCALPHA|D3DPBLENDCAPS_BOTHSRCALPHA)) ) { + strcat( missing_features, XSTR("Source blending mode = SRCALPHA or BOTHSRCALPHA\r\n", 625) ); + not_good++; + } + +// if ( !(lpDevDesc->dpcTriCaps.dwDestBlendCaps & D3DPBLENDCAPS_ZERO ) ) { +// strcat( missing_features, "Destination blending mode = ZERO\r\n" ); +// not_good++; +// } + +// if ( !(lpDevDesc->dpcTriCaps.dwDestBlendCaps & D3DPBLENDCAPS_ONE ) ) { +// strcat( missing_features, "Destination blending mode = ONE\r\n" ); +// not_good++; +// } + + // Dest blending values + if ( !(lpDevDesc->dpcTriCaps.dwDestBlendCaps & (D3DPBLENDCAPS_INVSRCALPHA|D3DPBLENDCAPS_BOTHINVSRCALPHA)) ) { + strcat( missing_features, XSTR("Destination blending mode = INVSRCALPHA or BOTHINVSRCALPHA\r\n",626) ); + not_good++; + } + + // If card is Mystique 220, turn off modulaalpha since it doesn't work... + if ( Largest_alpha < 4 ) { + lpDevDesc->dpcTriCaps.dwTextureBlendCaps &= (~D3DPTBLENDCAPS_MODULATEALPHA); + } + + if ( not_good ) { + gr_d3d_cleanup(); + MessageBox( NULL, missing_features, XSTR("Missing Features",621), MB_OK|MB_TASKMODAL|MB_SETFOREGROUND ); + exit(1); + } + } + + // fog info - for now we'll prefer table fog over vertex fog + D3D_fog_mode = 1; + // if the user wants to force w-fog, maybe do it + if(os_config_read_uint(NULL, "ForceWFOG", 0) && (lpDevDesc->dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGTABLE)){ + D3D_fog_mode = 2; + } + // if the card does not have vertex fog, but has table fog, let it go + if(!(lpDevDesc->dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGVERTEX) && (lpDevDesc->dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGTABLE)){ + D3D_fog_mode = 2; + } + + { + DDSCAPS ddsCaps; + DWORD dwFree, dwTotal; + + memset(&ddsCaps,0,sizeof(ddsCaps) ); + ddsCaps.dwCaps = DDSCAPS_TEXTURE; + HRESULT ddrval = lpDD->GetAvailableVidMem(&ddsCaps, &dwTotal, &dwFree); + if ( ddrval != DD_OK ) { + mprintf(( "GR_D3D_INIT: GetAvailableVidMem failed.\n" )); + dwFree = 0; + } + + if ( dwFree < (1024*1024) ) { + gr_d3d_cleanup(); + MessageBox( NULL, XSTR("At least 1 MB of available video memory required.",627), XSTR("Missing Features",621), MB_OK|MB_TASKMODAL|MB_SETFOREGROUND ); + exit(1); + } + } + + // setup proper render state + gr_d3d_set_initial_render_state(); + + mprintf(( "Direct3D Initialized OK!\n" )); + + D3D_inited = 1; + return; + +D3DError: + mprintf(( "Direct3D Initialization failed.\n" )); + + gr_d3d_cleanup(); +} + + +int Gr_d3d_mouse_saved = 0; +int Gr_d3d_mouse_saved_x1 = 0; +int Gr_d3d_mouse_saved_y1 = 0; +int Gr_d3d_mouse_saved_x2 = 0; +int Gr_d3d_mouse_saved_y2 = 0; +int Gr_d3d_mouse_saved_w = 0; +int Gr_d3d_mouse_saved_h = 0; +#define MAX_SAVE_SIZE (32*32) +ushort Gr_d3d_mouse_saved_data[MAX_SAVE_SIZE]; + +// Clamps X between R1 and R2 +#define CLAMP(x,r1,r2) do { if ( (x) < (r1) ) (x) = (r1); else if ((x) > (r2)) (x) = (r2); } while(0) + +void gr_d3d_save_mouse_area(int x, int y, int w, int h ) +{ + // bail in 32 bit + if(D3D_32bit){ + return; + } + + Gr_d3d_mouse_saved_x1 = x; + Gr_d3d_mouse_saved_y1 = y; + Gr_d3d_mouse_saved_x2 = x+w-1; + Gr_d3d_mouse_saved_y2 = y+h-1; + + CLAMP(Gr_d3d_mouse_saved_x1, gr_screen.clip_left, gr_screen.clip_right ); + CLAMP(Gr_d3d_mouse_saved_x2, gr_screen.clip_left, gr_screen.clip_right ); + CLAMP(Gr_d3d_mouse_saved_y1, gr_screen.clip_top, gr_screen.clip_bottom ); + CLAMP(Gr_d3d_mouse_saved_y2, gr_screen.clip_top, gr_screen.clip_bottom ); + + Gr_d3d_mouse_saved_w = Gr_d3d_mouse_saved_x2 - Gr_d3d_mouse_saved_x1 + 1; + Gr_d3d_mouse_saved_h = Gr_d3d_mouse_saved_y2 - Gr_d3d_mouse_saved_y1 + 1; + + if ( Gr_d3d_mouse_saved_w < 1 ) return; + if ( Gr_d3d_mouse_saved_h < 1 ) return; + + // Make sure we're not saving too much! + Assert( (Gr_d3d_mouse_saved_w*Gr_d3d_mouse_saved_h) <= MAX_SAVE_SIZE ); + + HRESULT ddrval; + DDSURFACEDESC ddsd; + + memset( &ddsd, 0, sizeof( ddsd ) ); + ddsd.dwSize = sizeof( ddsd ); + + ddrval = lpBackBuffer->Lock( NULL, &ddsd, DDLOCK_WAIT, NULL ); + if ( ddrval == DD_OK ) { + + ushort *rptr; + int short_per_row=ddsd.lPitch/2; + + rptr = (ushort *)ddsd.lpSurface; + + ushort *sptr, *dptr; + + dptr = Gr_d3d_mouse_saved_data; + + for (int i=0; iUnlock( NULL ); + + Gr_d3d_mouse_saved = 1; + + } else { + mprintf(( "Couldn't get read-only lock to backbuffer for d3d mouse save\n" )); + } + +} + + +void gr_d3d_flip() +{ + int mx, my; + HRESULT ddrval; + + + gr_reset_clip(); + + mouse_eval_deltas(); + + Gr_d3d_mouse_saved = 0; // assume not saved + + if(!Gr_bitmap_poly){ + d3d_stop_frame(); + } + + if ( mouse_is_visible() ) { + gr_reset_clip(); + mouse_get_pos( &mx, &my ); + + gr_d3d_save_mouse_area(mx,my,32,32); + if ( Gr_cursor == -1 ) { + #ifndef NDEBUG + gr_set_color(255,255,255); + gr_line( mx, my, mx+7, my + 7 ); + gr_line( mx, my, mx+5, my ); + gr_line( mx, my, mx, my+5 ); + #endif + } else { + gr_set_bitmap(Gr_cursor); + gr_bitmap( mx, my ); + } + } + + // Move stop frame before drawing cursor so cursor always appears on PowerVR. + if(Gr_bitmap_poly){ + d3d_stop_frame(); + } + +// ddrval = lpFrontBuffer->Flip( NULL, 1 ); +// if (ddrval != DD_OK ) { +// mprintf(( "Fullscreen flip failed!\n" )); +// return; +// } + + if(D3D_window){ + // blt region + RECT rect, view_rect; + GetClientRect( (HWND)os_get_window(), &rect); + ClientToScreen( (HWND)os_get_window(), (POINT*)&rect.left ); + ClientToScreen( (HWND)os_get_window(), (POINT*)&rect.right ); + view_rect.left = 0; + view_rect.top = 0; + view_rect.right = gr_screen.max_w - 1; + view_rect.bottom = gr_screen.max_h - 1; + + // do the blit + lpFrontBuffer->Blt(&rect, lpBackBuffer, &view_rect, 0, NULL ); + while(lpFrontBuffer->GetBltStatus(DDGBS_ISBLTDONE) != DD_OK){} + } else { + TryFlipAgain: + if ( lpFrontBuffer->IsLost() == DDERR_SURFACELOST ) { + lpFrontBuffer->Restore(); + } + if ( lpBackBuffer->IsLost() == DDERR_SURFACELOST ) { + lpBackBuffer->Restore(); + } + if ( lpZBuffer->IsLost() == DDERR_SURFACELOST ) { + lpZBuffer->Restore(); + } + ddrval = lpFrontBuffer->Flip( NULL, DDFLIP_WAIT ); + if ( ddrval == DDERR_SURFACELOST ) { + mprintf(( "Front surface lost... attempting to restore...\n" )); + os_sleep(1000); // Wait a second + + // os poll? + #ifndef THREADED + os_poll(); + #endif + + goto TryFlipAgain; + } else if (ddrval != DD_OK ) { + mprintf(( "Fullscreen flip failed!\n" )); + } + } + + d3d_tcache_frame(); + + int cnt = D3D_activate; + if ( cnt ) { + D3D_activate-=cnt; + d3d_tcache_flush(); + gr_d3d_clip_cursor(1); + } + + cnt = D3D_deactivate; + if ( cnt ) { + D3D_deactivate-=cnt; + gr_d3d_clip_cursor(0); + } + + d3d_start_frame(); +} + +void gr_d3d_flip_cleanup() +{ + d3d_stop_frame(); + + if (lpFrontBuffer->Flip( NULL, 1 ) != DD_OK ) { + mprintf(( "Flip failed!\n" )); + return; + } +} + + +void gr_d3d_flip_window(uint _hdc, int x, int y, int w, int h ) +{ +} + +void gr_d3d_cleanup() +{ + if (!D3D_inited) return; + + d3d_tcache_cleanup(); + + // release surfaces + gr_d3d_release_rendering_objects(); + + if ( lpD3D ) { + lpD3D->Release(); + lpD3D = NULL; + } + + if ( lpDD1 ) { + + HRESULT ddrval; + HWND hwnd = (HWND)os_get_window(); + + ddrval = lpDD->RestoreDisplayMode(); + if( ddrval != DD_OK ) { + mprintf(( "WIN_DD32: RestoreDisplayMode failed (0x%x)\n", ddrval )); + } + + ddrval = lpDD->SetCooperativeLevel( hwnd, DDSCL_NORMAL ); + if( ddrval != DD_OK ) { + mprintf(( "WIN_DD32: SetCooperativeLevel W Failed (0x%x)\n", ddrval )); + } + + // restore windows clipping rectangle + ClipCursor(NULL); + +// SetWindowLong( hwnd, GWL_STYLE, WS_CAPTION | WS_SYSMENU ); +// SetWindowLong( hwnd, GWL_EXSTYLE, 0 ); +// SetWindowPos( hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE ); + + // gr_d3d_clip_cursor(0); + + os_suspend(); + lpDD1->Release(); + os_resume(); + lpDD1 = NULL; + } + + D3D_inited = 0; +} + + +void gr_d3d_fade_in(int instantaneous) +{ +} + +void gr_d3d_fade_out(int instantaneous) +{ +} + + + +void gr_d3d_force_windowed() +{ + HWND hwnd = (HWND)os_get_window(); + + // Simulate Alt+Tab + PostMessage(hwnd,WM_SYSKEYUP, 0x9, 0xa00f0001 ); + PostMessage(hwnd,WM_SYSKEYUP, 0x12, 0xc0380001 ); + + gr_d3d_clip_cursor(0); + + // Wait a second to give things a change to settle down. + Sleep(1000); +} + +static char *Gr_saved_screen = NULL; + +int gr_d3d_save_screen() +{ + gr_reset_clip(); + + if ( Gr_saved_screen ) { + mprintf(( "Screen alread saved!\n" )); + return -1; + } + + Gr_saved_screen = (char*)malloc( gr_screen.max_w * gr_screen.max_h * gr_screen.bytes_per_pixel ); + if (!Gr_saved_screen) { + mprintf(( "Couldn't get memory for saved screen!\n" )); + return -1; + } + + HRESULT ddrval; + DDSURFACEDESC ddsd; + + memset( &ddsd, 0, sizeof( ddsd ) ); + ddsd.dwSize = sizeof( ddsd ); + + ddrval = lpFrontBuffer->Lock( NULL, &ddsd, DDLOCK_WAIT, NULL ); + if ( ddrval != DD_OK ) { + free(Gr_saved_screen); + Gr_saved_screen = NULL; + mprintf(( "Error locking surface for save_screen, %s\n", d3d_error_string(ddrval) )); + return -1; + } + + // 32 bit + if(D3D_32bit){ + uint *dptr = (uint*)ddsd.lpSurface; + int i; + for (i=0; iUnlock( NULL ); + + if ( Gr_d3d_mouse_saved && !D3D_32bit) { + ushort *sptr, *dptr; + + sptr = Gr_d3d_mouse_saved_data; + + for (int i=0; iLock( NULL, &ddsd, DDLOCK_WAIT, NULL ); + if ( ddrval != DD_OK ) { + free(Gr_saved_screen); + Gr_saved_screen = NULL; + mprintf(( "Error locking surface for restore_screen, %s\n", d3d_error_string(ddrval) )); + return; + } + + // restore + if(D3D_32bit){ + uint *dptr = (uint *)ddsd.lpSurface; + int i; + for (i=0; iUnlock( NULL ); +} + +void gr_d3d_free_screen(int id) +{ + if ( Gr_saved_screen ) { + free( Gr_saved_screen ); + Gr_saved_screen = NULL; + } +} + +static int D3d_dump_frames = 0; +static ubyte *D3d_dump_buffer = NULL; +static int D3d_dump_frame_number = 0; +static int D3d_dump_frame_count = 0; +static int D3d_dump_frame_count_max = 0; +static int D3d_dump_frame_size = 0; +void gr_d3d_dump_frame_start(int first_frame, int frames_between_dumps) +{ + if ( D3d_dump_frames ) { + Int3(); // We're already dumping frames. See John. + return; + } + D3d_dump_frames = 1; + D3d_dump_frame_number = first_frame; + D3d_dump_frame_count = 0; + D3d_dump_frame_count_max = frames_between_dumps; + D3d_dump_frame_size = gr_screen.max_w * gr_screen.max_h * 2; + + if ( !D3d_dump_buffer ) { + int size = D3d_dump_frame_count_max * D3d_dump_frame_size; + D3d_dump_buffer = (ubyte *)malloc(size); + if ( !D3d_dump_buffer ) { + Error(LOCATION, "Unable to malloc %d bytes for dump buffer", size ); + } + } +} + +extern int tga_compress(char *out, char *in, int bytecount); +void gr_d3d_flush_frame_dump() +{ + int i,j; + char filename[MAX_PATH_LEN], *movie_path = ".\\"; + ubyte outrow[1024*3*4]; + + if ( gr_screen.max_w > 1024) { + mprintf(( "Screen too wide for frame_dump\n" )); + return; + } + + for (i = 0; i < D3d_dump_frame_count; i++) { + + int w = gr_screen.max_w; + int h = gr_screen.max_h; + + sprintf(filename, NOX("%sfrm%04d.tga"), movie_path, D3d_dump_frame_number ); + D3d_dump_frame_number++; + + CFILE *f = cfopen(filename, "wb"); + + // Write the TGA header + cfwrite_ubyte( 0, f ); // IDLength; + cfwrite_ubyte( 0, f ); // ColorMapType; + cfwrite_ubyte( 10, f ); // ImageType; // 2 = 24bpp, uncompressed, 10=24bpp rle compressed + cfwrite_ushort( 0, f ); // CMapStart; + cfwrite_ushort( 0, f ); // CMapLength; + cfwrite_ubyte( 0, f ); // CMapDepth; + cfwrite_ushort( 0, f ); // XOffset; + cfwrite_ushort( 0, f ); // YOffset; + cfwrite_ushort( (ushort)w, f ); // Width; + cfwrite_ushort( (ushort)h, f ); // Height; + cfwrite_ubyte( 24, f ); //PixelDepth; + cfwrite_ubyte( 0, f ); //ImageDesc; + + // Go through and write our pixels + for (j=0;jLock( NULL, &ddsd, DDLOCK_WAIT | DDLOCK_READONLY, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "Error locking surface for get_region, %s\n", d3d_error_string(ddrval) )); + return; + } + + ushort *sptr; + ubyte *rptr = (ubyte*)ddsd.lpSurface; + + for (int i=0; i> 3) << 11); + pixel |= ((g >> 2) << 5); + pixel |= (b >> 3); + + *dst++ = pixel; + } + } + + // Unlock the buffer + surf->Unlock( NULL ); +} + +void gr_d3d_dump_frame() +{ + // A hacked function to dump the frame buffer contents + gr_d3d_dump_screen_hack( (ushort *)(D3d_dump_buffer+(D3d_dump_frame_count*D3d_dump_frame_size)) ); + + D3d_dump_frame_count++; + + if ( D3d_dump_frame_count == D3d_dump_frame_count_max ) { + gr_d3d_flush_frame_dump(); + } +} + +uint gr_d3d_lock() +{ + return 1; +} + +void gr_d3d_unlock() +{ +} + +void gr_d3d_fog_set(int fog_mode, int r, int g, int b, float fog_near, float fog_far) +{ + D3DCOLOR color = 0; + + Assert((r >= 0) && (r < 256)); + Assert((g >= 0) && (g < 256)); + Assert((b >= 0) && (b < 256)); + + // turning fog off + if(fog_mode == GR_FOGMODE_NONE){ + // only change state if we need to + if(gr_screen.current_fog_mode != fog_mode){ + d3d_SetRenderState(D3DRENDERSTATE_FOGENABLE, FALSE ); + } + gr_screen.current_fog_mode = fog_mode; + + // to prevent further state changes + return; + } + + // maybe switch fogging on + if(gr_screen.current_fog_mode != fog_mode){ + d3d_SetRenderState(D3DRENDERSTATE_FOGENABLE, TRUE); + + // if we're using table fog, enable table fogging + if(D3D_fog_mode == 2){ + d3d_SetRenderState( D3DRENDERSTATE_FOGTABLEMODE, D3DFOG_LINEAR ); + } + + gr_screen.current_fog_mode = fog_mode; + } + + // is color changing? + if( (gr_screen.current_fog_color.red != r) || (gr_screen.current_fog_color.green != g) || (gr_screen.current_fog_color.blue != b) ){ + // store the values + gr_d3d_init_color( &gr_screen.current_fog_color, r, g, b ); + + color = RGB_MAKE(r, g, b); + d3d_SetRenderState(D3DRENDERSTATE_FOGCOLOR, color); + } + + // planes changing? + if( (fog_near >= 0.0f) && (fog_far >= 0.0f) && ((fog_near != gr_screen.fog_near) || (fog_far != gr_screen.fog_far)) ){ + gr_screen.fog_near = fog_near; + gr_screen.fog_far = fog_far; + + // only generate a new fog table if we have to (wfog/table fog mode) + if(D3D_fog_mode == 2){ + d3d_SetRenderState( D3DRENDERSTATE_FOGTABLESTART, *((DWORD *)(&fog_near))); + d3d_SetRenderState( D3DRENDERSTATE_FOGTABLEEND, *((DWORD *)(&fog_far))); + } + } +} + +void gr_d3d_set_gamma(float gamma) +{ + Gr_gamma = gamma; + Gr_gamma_int = int(Gr_gamma*100); + + // Create the Gamma lookup table + int i; + for (i=0; i<256; i++ ) { + int v = fl2i(pow(i2fl(i)/255.0f, 1.0f/Gr_gamma)*255.0f); + if ( v > 255 ) { + v = 255; + } else if ( v < 0 ) { + v = 0; + } + Gr_gamma_lookup[i] = v; + } + + // Flush any existing textures + d3d_tcache_flush(); +} + +void d3d_get_pixel(int x, int y, ubyte *pixel) +{ + HRESULT ddrval; + DDSURFACEDESC ddsd; + + memset( &ddsd, 0, sizeof( ddsd ) ); + ddsd.dwSize = sizeof( ddsd ); + + ddrval = lpFrontBuffer->Lock( NULL, &ddsd, DDLOCK_WAIT, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "Error locking surface for uv test, %s\n", d3d_error_string(ddrval) )); + return; + } + + ubyte *dptr = (ubyte *)((uint)ddsd.lpSurface + y*ddsd.lPitch + (x * gr_screen.bytes_per_pixel)); + memcpy(pixel, dptr, gr_screen.bytes_per_pixel); + + // Unlock the buffer + lpFrontBuffer->Unlock( NULL ); +} + +void gr_d3d_get_pixel(int x, int y, int *r, int *g, int *b) +{ +} + +// resolution checking +int gr_d3d_supports_res_interface(int res) +{ + return 1; +} + +int gr_d3d_supports_res_ingame(int res) +{ + return 1; +} + +void gr_d3d_set_cull(int cull) +{ + // switch culling on or off + if(cull){ + d3d_SetRenderState( D3DRENDERSTATE_CULLMODE, D3DCULL_CCW ); + } else { + d3d_SetRenderState( D3DRENDERSTATE_CULLMODE, D3DCULL_NONE ); + } +} + +// cross fade +void gr_d3d_cross_fade(int bmap1, int bmap2, int x1, int y1, int x2, int y2, float pct) +{ + if ( pct <= 50 ) { + gr_set_bitmap(bmap1); + gr_bitmap(x1, y1); + } else { + gr_set_bitmap(bmap2); + gr_bitmap(x2, y2); + } +} + +// filtering +void gr_d3d_filter_set(int filter) +{ +} + +// set clear color +void gr_d3d_set_clear_color(int r, int g, int b) +{ + gr_init_color(&gr_screen.current_clear_color, r, g, b); +} + +// JAS: Need to turn optimizations off or Alan's machine, with 3dfx direct3d hangs... +#pragma optimize("",off) + +void d3d_detect_texture_origin_32() +{ + int test_bmp = -1; + ubyte data[32*32]; + color ac; + uint pix1a, pix2a; + uint pix1b, pix2b; + + mprintf(( "Detecting uv type...\n" )); + + gr_set_gamma(1.0f); + gr_init_alphacolor(&ac,255,255,255,255); + + memset( data, 0, 32*32 ); + data[15*32+15] = 14; + + test_bmp = bm_create( 8, 32, 32, data, BMP_AABITMAP ); + + mprintf(( "Trial #1\n" )); + D3d_rendition_uvs = 0; + gr_reset_clip(); + gr_clear(); + gr_set_color_fast(&ac); + gr_set_bitmap( test_bmp ); + gr_aabitmap_ex(0, 0, 32, 32, 15, 15); + Mouse_hidden++; + gr_flip(); + d3d_get_pixel(0, 0, (ubyte*)&pix1a); + d3d_get_pixel(1, 1, (ubyte*)&pix1b); + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; + + mprintf(( "Trial #2\n" )); + D3d_rendition_uvs = 1; + gr_reset_clip(); + gr_clear(); + gr_set_color_fast(&ac); + gr_set_bitmap( test_bmp ); + gr_aabitmap_ex(0, 0, 32, 32, 15, 15); + Mouse_hidden++; + gr_flip(); + d3d_get_pixel(0, 0, (ubyte*)&pix2a); + d3d_get_pixel(1, 1, (ubyte*)&pix2b); + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; + + bm_release(test_bmp); + + mprintf(( "Pixel 1 = %x , %x\n", pix1a, pix1b )); + mprintf(( "Pixel 2 = %x , %x\n", pix2a, pix2b )); + + if ( (pix1b!=0) || (pix2b!=0) ) { + D3d_rendition_uvs = 1; + } else { + D3d_rendition_uvs = 0; + } + + mprintf(( "Rendition uvs: %d\n", D3d_rendition_uvs )); +} + +void d3d_detect_texture_origin_16() +{ + int test_bmp = -1; + ubyte data[32*32]; + color ac; + ushort pix1a, pix2a; + ushort pix1b, pix2b; + + mprintf(( "Detecting uv type...\n" )); + + gr_set_gamma(1.0f); + gr_init_alphacolor(&ac,255,255,255,255); + + memset( data, 0, 32*32 ); + data[15*32+15] = 14; + + test_bmp = bm_create( 8, 32, 32, data, BMP_AABITMAP ); + + mprintf(( "Trial #1\n" )); + D3d_rendition_uvs = 0; + gr_reset_clip(); + gr_clear(); + gr_set_color_fast(&ac); + gr_set_bitmap( test_bmp ); + gr_aabitmap_ex(0, 0, 32, 32, 15, 15); + Mouse_hidden++; + gr_flip(); + d3d_get_pixel(0, 0, (ubyte*)&pix1a); + d3d_get_pixel(1, 1, (ubyte*)&pix1b); + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; + + mprintf(( "Trial #2\n" )); + D3d_rendition_uvs = 1; + gr_reset_clip(); + gr_clear(); + gr_set_color_fast(&ac); + gr_set_bitmap( test_bmp ); + gr_aabitmap_ex(0, 0, 32, 32, 15, 15); + Mouse_hidden++; + gr_flip(); + d3d_get_pixel(0, 0, (ubyte*)&pix2a); + d3d_get_pixel(1, 1, (ubyte*)&pix2b); + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; + + bm_release(test_bmp); + + mprintf(( "Pixel 1 = %x , %x\n", pix1a, pix1b )); + mprintf(( "Pixel 2 = %x , %x\n", pix2a, pix2b )); + + if ( (pix1b!=0) || (pix2b!=0) ) { + D3d_rendition_uvs = 1; + } else { + D3d_rendition_uvs = 0; + } + + mprintf(( "Rendition uvs: %d\n", D3d_rendition_uvs )); +} + +void gr_d3d_get_region(int front, int w, int h, ubyte *data) +{ + HRESULT ddrval; + DDSURFACEDESC ddsd; + LPDIRECTDRAWSURFACE surf = front ? lpFrontBuffer : lpBackBuffer; + + memset( &ddsd, 0, sizeof( ddsd ) ); + ddsd.dwSize = sizeof( ddsd ); + + // try and lock the buffer + ddrval = surf->Lock( NULL, &ddsd, DDLOCK_WAIT | DDLOCK_READONLY, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "Error locking surface for get_region, %s\n", d3d_error_string(ddrval) )); + return; + } + + ubyte *sptr; + ubyte *dptr = data; + ubyte *rptr = (ubyte*)ddsd.lpSurface; + + for (int i=0; iUnlock( NULL ); +} + +extern float D3D_line_offset; + +void d3d_detect_line_offset_32() +{ + color ac; + uint pix1a, pix2a; + uint pix1b, pix2b; + + mprintf(( "Detecting line offset...\n" )); + + gr_set_gamma(1.0f); + gr_init_alphacolor(&ac, 255,255, 255, 255); + + mprintf(( "Trial #1\n" )); + D3D_line_offset = 0.0f; + gr_reset_clip(); + gr_clear(); + gr_set_color_fast(&ac); + gr_line( 0,0,0,0 ); + Mouse_hidden++; + gr_flip(); + d3d_get_pixel(0, 0, (ubyte*)&pix1a); + d3d_get_pixel(1, 1, (ubyte*)&pix1b); + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; + + mprintf(( "Trial #2\n" )); + D3D_line_offset = 0.5f; + gr_reset_clip(); + gr_clear(); + gr_set_color_fast(&ac); + gr_line( 0,0,0,0 ); + Mouse_hidden++; + gr_flip(); + d3d_get_pixel(0, 0, (ubyte*)&pix2a); + d3d_get_pixel(1, 1, (ubyte*)&pix2b); + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; + + mprintf(( "Pixel 1 = %x , %x\n", pix1a, pix1b )); + mprintf(( "Pixel 2 = %x , %x\n", pix2a, pix2b )); + + if ( (pix1a!=0) && (pix2a==0) ) { + D3D_line_offset = 0.0f; + } else if ( (pix1a==0) && (pix2a!=0) ) { + D3D_line_offset = 0.5f; + } else { + D3D_line_offset = 0.0f; + } + + mprintf(( "Line offset: %.1f\n", D3D_line_offset )); +} + +void d3d_detect_line_offset_16() +{ + color ac; + ushort pix1a, pix2a; + ushort pix1b, pix2b; + + mprintf(( "Detecting line offset...\n" )); + + gr_set_gamma(1.0f); + gr_init_alphacolor(&ac, 255,255, 255, 255); + + mprintf(( "Trial #1\n" )); + D3D_line_offset = 0.0f; + gr_reset_clip(); + gr_clear(); + gr_set_color_fast(&ac); + gr_line( 0,0,0,0 ); + Mouse_hidden++; + gr_flip(); + d3d_get_pixel(0, 0, (ubyte*)&pix1a); + d3d_get_pixel(1, 1, (ubyte*)&pix1b); + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; + + mprintf(( "Trial #2\n" )); + D3D_line_offset = 0.5f; + gr_reset_clip(); + gr_clear(); + gr_set_color_fast(&ac); + gr_line( 0,0,0,0 ); + Mouse_hidden++; + gr_flip(); + d3d_get_pixel(0, 0, (ubyte*)&pix2a); + d3d_get_pixel(1, 1, (ubyte*)&pix2b); + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; + + mprintf(( "Pixel 1 = %x , %x\n", pix1a, pix1b )); + mprintf(( "Pixel 2 = %x , %x\n", pix2a, pix2b )); + + if ( (pix1a!=0) && (pix2a==0) ) { + D3D_line_offset = 0.0f; + } else if ( (pix1a==0) && (pix2a!=0) ) { + D3D_line_offset = 0.5f; + } else { + D3D_line_offset = 0.0f; + } + + mprintf(( "Line offset: %.1f\n", D3D_line_offset )); +} + +#pragma optimize("",on) + +void gr_d3d_init() +{ + D3D_enabled = 1; // Tell Freespace code that we're using Direct3D. + D3D_running = 0; + + Assert( !D3D_inited ); + + // pixel format + Bm_pixel_format = BM_PIXEL_FORMAT_D3D; + + D3D_32bit = 0; + D3D_window = Cmdline_window; + + // windowed? + if(D3D_window){ + gr_d3d_init_device(gr_screen.max_w, gr_screen.max_h); + } else { + // 32 bit mode + if(Cmdline_force_32bit){ + gr_screen.bits_per_pixel = 32; + gr_screen.bytes_per_pixel = 4; + gr_d3d_init_device(gr_screen.max_w, gr_screen.max_h); + } else { + // 16 bit mode + gr_screen.bits_per_pixel = 16; + gr_screen.bytes_per_pixel = 2; + gr_d3d_init_device(gr_screen.max_w, gr_screen.max_h); + } + } + + // determine 32 bit status + D3D_32bit = gr_screen.bits_per_pixel == 32 ? 1 : 0; + d3d_tcache_init(D3D_32bit ? 1 : 0); + Gr_bitmap_poly = D3D_32bit ? 1 : 0; + + // did we initialize properly? + if(!D3D_inited){ + return; + } + + // zbiasing? + if(os_config_read_uint(NULL, "DisableZbias", 0)){ + D3D_zbias = 0; + } + + d3d_start_frame(); + + Gr_current_red = &Gr_red; + Gr_current_blue = &Gr_blue; + Gr_current_green = &Gr_green; + Gr_current_alpha = &Gr_alpha; + + gr_screen.gf_flip = gr_d3d_flip; + gr_screen.gf_flip_window = gr_d3d_flip_window; + gr_screen.gf_set_clip = gr_d3d_set_clip; + gr_screen.gf_reset_clip = gr_d3d_reset_clip; + gr_screen.gf_set_font = grx_set_font; + + gr_screen.gf_get_color = gr_d3d_get_color; + gr_screen.gf_init_color = gr_d3d_init_color; + gr_screen.gf_set_color_fast = gr_d3d_set_color_fast; + gr_screen.gf_set_color = gr_d3d_set_color; + gr_screen.gf_init_color = gr_d3d_init_color; + gr_screen.gf_init_alphacolor = gr_d3d_init_alphacolor; + + gr_screen.gf_set_bitmap = gr_d3d_set_bitmap; + gr_screen.gf_create_shader = gr_d3d_create_shader; + gr_screen.gf_set_shader = gr_d3d_set_shader; + gr_screen.gf_clear = gr_d3d_clear; + // gr_screen.gf_bitmap = gr_d3d_bitmap; + // gr_screen.gf_bitmap_ex = gr_d3d_bitmap_ex; + gr_screen.gf_aabitmap = gr_d3d_aabitmap; + gr_screen.gf_aabitmap_ex = gr_d3d_aabitmap_ex; + + gr_screen.gf_rect = gr_d3d_rect; + gr_screen.gf_shade = gr_d3d_shade; + gr_screen.gf_string = gr_d3d_string; + gr_screen.gf_circle = gr_d3d_circle; + + gr_screen.gf_line = gr_d3d_line; + gr_screen.gf_aaline = gr_d3d_aaline; + gr_screen.gf_pixel = gr_d3d_pixel; + gr_screen.gf_scaler = gr_d3d_scaler; + gr_screen.gf_aascaler = gr_d3d_aascaler; + gr_screen.gf_tmapper = gr_d3d_tmapper; + + gr_screen.gf_gradient = gr_d3d_gradient; + + gr_screen.gf_set_palette = gr_d3d_set_palette; + gr_screen.gf_print_screen = gr_d3d_print_screen; + + gr_screen.gf_fade_in = gr_d3d_fade_in; + gr_screen.gf_fade_out = gr_d3d_fade_out; + gr_screen.gf_flash = gr_d3d_flash; + + gr_screen.gf_zbuffer_get = gr_d3d_zbuffer_get; + gr_screen.gf_zbuffer_set = gr_d3d_zbuffer_set; + gr_screen.gf_zbuffer_clear = gr_d3d_zbuffer_clear; + + gr_screen.gf_save_screen = gr_d3d_save_screen; + gr_screen.gf_restore_screen = gr_d3d_restore_screen; + gr_screen.gf_free_screen = gr_d3d_free_screen; + + // Screen dumping stuff + gr_screen.gf_dump_frame_start = gr_d3d_dump_frame_start; + gr_screen.gf_dump_frame_stop = gr_d3d_dump_frame_stop; + gr_screen.gf_dump_frame = gr_d3d_dump_frame; + + gr_screen.gf_set_gamma = gr_d3d_set_gamma; + + // Lock/unlock stuff + gr_screen.gf_lock = gr_d3d_lock; + gr_screen.gf_unlock = gr_d3d_unlock; + + // screen region + gr_screen.gf_get_region = gr_d3d_get_region; + + // fog stuff + gr_screen.gf_fog_set = gr_d3d_fog_set; + + // pixel get + gr_screen.gf_get_pixel = gr_d3d_get_pixel; + + // poly culling + gr_screen.gf_set_cull = gr_d3d_set_cull; + + // cross fade + gr_screen.gf_cross_fade = gr_d3d_cross_fade; + + // filtering + gr_screen.gf_filter_set = gr_d3d_filter_set; + + // texture cache + gr_screen.gf_tcache_set = d3d_tcache_set; + + // set clear color + gr_screen.gf_set_clear_color = gr_d3d_set_clear_color; + + uint tmp = os_config_read_uint( NULL, "D3DTextureOrigin", 0xFFFF ); + + if ( tmp != 0xFFFF ) { + if ( tmp ) { + D3d_rendition_uvs = 1; + } else { + D3d_rendition_uvs = 0; + } + } else { + if(D3D_32bit){ + d3d_detect_texture_origin_32(); + } else { + d3d_detect_texture_origin_16(); + } + } + + tmp = os_config_read_uint( NULL, "D3DLineOffset", 0xFFFF ); + + if ( tmp != 0xFFFF ) { + if ( tmp ) { + D3D_line_offset = 0.5f; + } else { + D3D_line_offset = 0.0f; + } + } else { + if(D3D_32bit){ + d3d_detect_line_offset_32(); + } else { + d3d_detect_line_offset_16(); + } + } + + D3D_running = 1; + + Mouse_hidden++; + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; +} + +char* d3d_error_string(HRESULT error) +{ +//XSTR:OFF + switch(error) { + case DD_OK: + return "No error.\0"; + case DDERR_ALREADYINITIALIZED: + return "This object is already initialized.\0"; + case DDERR_BLTFASTCANTCLIP: + return "Return if a clipper object is attached to the source surface passed into a BltFast call.\0"; + case DDERR_CANNOTATTACHSURFACE: + return "This surface can not be attached to the requested surface.\0"; + case DDERR_CANNOTDETACHSURFACE: + return "This surface can not be detached from the requested surface.\0"; + case DDERR_CANTCREATEDC: + return "Windows can not create any more DCs.\0"; + case DDERR_CANTDUPLICATE: + return "Can't duplicate primary & 3D surfaces, or surfaces that are implicitly created.\0"; + case DDERR_CLIPPERISUSINGHWND: + return "An attempt was made to set a cliplist for a clipper object that is already monitoring an hwnd.\0"; + case DDERR_COLORKEYNOTSET: + return "No src color key specified for this operation.\0"; + case DDERR_CURRENTLYNOTAVAIL: + return "Support is currently not available.\0"; + case DDERR_DIRECTDRAWALREADYCREATED: + return "A DirectDraw object representing this driver has already been created for this process.\0"; + case DDERR_EXCEPTION: + return "An exception was encountered while performing the requested operation.\0"; + case DDERR_EXCLUSIVEMODEALREADYSET: + return "An attempt was made to set the cooperative level when it was already set to exclusive.\0"; + case DDERR_GENERIC: + return "Generic failure.\0"; + case DDERR_HEIGHTALIGN: + return "Height of rectangle provided is not a multiple of reqd alignment.\0"; + case DDERR_HWNDALREADYSET: + return "The CooperativeLevel HWND has already been set. It can not be reset while the process has surfaces or palettes created.\0"; + case DDERR_HWNDSUBCLASSED: + return "HWND used by DirectDraw CooperativeLevel has been subclassed, this prevents DirectDraw from restoring state.\0"; + case DDERR_IMPLICITLYCREATED: + return "This surface can not be restored because it is an implicitly created surface.\0"; + case DDERR_INCOMPATIBLEPRIMARY: + return "Unable to match primary surface creation request with existing primary surface.\0"; + case DDERR_INVALIDCAPS: + return "One or more of the caps bits passed to the callback are incorrect.\0"; + case DDERR_INVALIDCLIPLIST: + return "DirectDraw does not support the provided cliplist.\0"; + case DDERR_INVALIDDIRECTDRAWGUID: + return "The GUID passed to DirectDrawCreate is not a valid DirectDraw driver identifier.\0"; + case DDERR_INVALIDMODE: + return "DirectDraw does not support the requested mode.\0"; + case DDERR_INVALIDOBJECT: + return "DirectDraw received a pointer that was an invalid DIRECTDRAW object.\0"; + case DDERR_INVALIDPARAMS: + return "One or more of the parameters passed to the function are incorrect.\0"; + case DDERR_INVALIDPIXELFORMAT: + return "The pixel format was invalid as specified.\0"; + case DDERR_INVALIDPOSITION: + return "Returned when the position of the overlay on the destination is no longer legal for that destination.\0"; + case DDERR_INVALIDRECT: + return "Rectangle provided was invalid.\0"; + case DDERR_LOCKEDSURFACES: + return "Operation could not be carried out because one or more surfaces are locked.\0"; + case DDERR_NO3D: + return "There is no 3D present.\0"; + case DDERR_NOALPHAHW: + return "Operation could not be carried out because there is no alpha accleration hardware present or available.\0"; + case DDERR_NOBLTHW: + return "No blitter hardware present.\0"; + case DDERR_NOCLIPLIST: + return "No cliplist available.\0"; + case DDERR_NOCLIPPERATTACHED: + return "No clipper object attached to surface object.\0"; + case DDERR_NOCOLORCONVHW: + return "Operation could not be carried out because there is no color conversion hardware present or available.\0"; + case DDERR_NOCOLORKEY: + return "Surface doesn't currently have a color key\0"; + case DDERR_NOCOLORKEYHW: + return "Operation could not be carried out because there is no hardware support of the destination color key.\0"; + case DDERR_NOCOOPERATIVELEVELSET: + return "Create function called without DirectDraw object method SetCooperativeLevel being called.\0"; + case DDERR_NODC: + return "No DC was ever created for this surface.\0"; + case DDERR_NODDROPSHW: + return "No DirectDraw ROP hardware.\0"; + case DDERR_NODIRECTDRAWHW: + return "A hardware-only DirectDraw object creation was attempted but the driver did not support any hardware.\0"; + case DDERR_NOEMULATION: + return "Software emulation not available.\0"; + case DDERR_NOEXCLUSIVEMODE: + return "Operation requires the application to have exclusive mode but the application does not have exclusive mode.\0"; + case DDERR_NOFLIPHW: + return "Flipping visible surfaces is not supported.\0"; + case DDERR_NOGDI: + return "There is no GDI present.\0"; + case DDERR_NOHWND: + return "Clipper notification requires an HWND or no HWND has previously been set as the CooperativeLevel HWND.\0"; + case DDERR_NOMIRRORHW: + return "Operation could not be carried out because there is no hardware present or available.\0"; + case DDERR_NOOVERLAYDEST: + return "Returned when GetOverlayPosition is called on an overlay that UpdateOverlay has never been called on to establish a destination.\0"; + case DDERR_NOOVERLAYHW: + return "Operation could not be carried out because there is no overlay hardware present or available.\0"; + case DDERR_NOPALETTEATTACHED: + return "No palette object attached to this surface.\0"; + case DDERR_NOPALETTEHW: + return "No hardware support for 16 or 256 color palettes.\0"; + case DDERR_NORASTEROPHW: + return "Operation could not be carried out because there is no appropriate raster op hardware present or available.\0"; + case DDERR_NOROTATIONHW: + return "Operation could not be carried out because there is no rotation hardware present or available.\0"; + case DDERR_NOSTRETCHHW: + return "Operation could not be carried out because there is no hardware support for stretching.\0"; + case DDERR_NOT4BITCOLOR: + return "DirectDrawSurface is not in 4 bit color palette and the requested operation requires 4 bit color palette.\0"; + case DDERR_NOT4BITCOLORINDEX: + return "DirectDrawSurface is not in 4 bit color index palette and the requested operation requires 4 bit color index palette.\0"; + case DDERR_NOT8BITCOLOR: + return "DirectDrawSurface is not in 8 bit color mode and the requested operation requires 8 bit color.\0"; + case DDERR_NOTAOVERLAYSURFACE: + return "Returned when an overlay member is called for a non-overlay surface.\0"; + case DDERR_NOTEXTUREHW: + return "Operation could not be carried out because there is no texture mapping hardware present or available.\0"; + case DDERR_NOTFLIPPABLE: + return "An attempt has been made to flip a surface that is not flippable.\0"; + case DDERR_NOTFOUND: + return "Requested item was not found.\0"; + case DDERR_NOTLOCKED: + return "Surface was not locked. An attempt to unlock a surface that was not locked at all, or by this process, has been attempted.\0"; + case DDERR_NOTPALETTIZED: + return "The surface being used is not a palette-based surface.\0"; + case DDERR_NOVSYNCHW: + return "Operation could not be carried out because there is no hardware support for vertical blank synchronized operations.\0"; + case DDERR_NOZBUFFERHW: + return "Operation could not be carried out because there is no hardware support for zbuffer blitting.\0"; + case DDERR_NOZOVERLAYHW: + return "Overlay surfaces could not be z layered based on their BltOrder because the hardware does not support z layering of overlays.\0"; + case DDERR_OUTOFCAPS: + return "The hardware needed for the requested operation has already been allocated.\0"; + case DDERR_OUTOFMEMORY: + return "DirectDraw does not have enough memory to perform the operation.\0"; + case DDERR_OUTOFVIDEOMEMORY: + return "DirectDraw does not have enough memory to perform the operation.\0"; + case DDERR_OVERLAYCANTCLIP: + return "The hardware does not support clipped overlays.\0"; + case DDERR_OVERLAYCOLORKEYONLYONEACTIVE: + return "Can only have ony color key active at one time for overlays.\0"; + case DDERR_OVERLAYNOTVISIBLE: + return "Returned when GetOverlayPosition is called on a hidden overlay.\0"; + case DDERR_PALETTEBUSY: + return "Access to this palette is being refused because the palette is already locked by another thread.\0"; + case DDERR_PRIMARYSURFACEALREADYEXISTS: + return "This process already has created a primary surface.\0"; + case DDERR_REGIONTOOSMALL: + return "Region passed to Clipper::GetClipList is too small.\0"; + case DDERR_SURFACEALREADYATTACHED: + return "This surface is already attached to the surface it is being attached to.\0"; + case DDERR_SURFACEALREADYDEPENDENT: + return "This surface is already a dependency of the surface it is being made a dependency of.\0"; + case DDERR_SURFACEBUSY: + return "Access to this surface is being refused because the surface is already locked by another thread.\0"; + case DDERR_SURFACEISOBSCURED: + return "Access to surface refused because the surface is obscured.\0"; + case DDERR_SURFACELOST: + return "Access to this surface is being refused because the surface memory is gone. The DirectDrawSurface object representing this surface should have Restore called on it.\0"; + case DDERR_SURFACENOTATTACHED: + return "The requested surface is not attached.\0"; + case DDERR_TOOBIGHEIGHT: + return "Height requested by DirectDraw is too large.\0"; + case DDERR_TOOBIGSIZE: + return "Size requested by DirectDraw is too large, but the individual height and width are OK.\0"; + case DDERR_TOOBIGWIDTH: + return "Width requested by DirectDraw is too large.\0"; + case DDERR_UNSUPPORTED: + return "Action not supported.\0"; + case DDERR_UNSUPPORTEDFORMAT: + return "FOURCC format requested is unsupported by DirectDraw.\0"; + case DDERR_UNSUPPORTEDMASK: + return "Bitmask in the pixel format requested is unsupported by DirectDraw.\0"; + case DDERR_VERTICALBLANKINPROGRESS: + return "Vertical blank is in progress.\0"; + case DDERR_WASSTILLDRAWING: + return "Informs DirectDraw that the previous Blt which is transfering information to or from this Surface is incomplete.\0"; + case DDERR_WRONGMODE: + return "This surface can not be restored because it was created in a different mode.\0"; + case DDERR_XALIGN: + return "Rectangle provided was not horizontally aligned on required boundary.\0"; + case D3DERR_BADMAJORVERSION: + return "D3DERR_BADMAJORVERSION\0"; + case D3DERR_BADMINORVERSION: + return "D3DERR_BADMINORVERSION\0"; + case D3DERR_EXECUTE_LOCKED: + return "D3DERR_EXECUTE_LOCKED\0"; + case D3DERR_EXECUTE_NOT_LOCKED: + return "D3DERR_EXECUTE_NOT_LOCKED\0"; + case D3DERR_EXECUTE_CREATE_FAILED: + return "D3DERR_EXECUTE_CREATE_FAILED\0"; + case D3DERR_EXECUTE_DESTROY_FAILED: + return "D3DERR_EXECUTE_DESTROY_FAILED\0"; + case D3DERR_EXECUTE_LOCK_FAILED: + return "D3DERR_EXECUTE_LOCK_FAILED\0"; + case D3DERR_EXECUTE_UNLOCK_FAILED: + return "D3DERR_EXECUTE_UNLOCK_FAILED\0"; + case D3DERR_EXECUTE_FAILED: + return "D3DERR_EXECUTE_FAILED\0"; + case D3DERR_EXECUTE_CLIPPED_FAILED: + return "D3DERR_EXECUTE_CLIPPED_FAILED\0"; + case D3DERR_TEXTURE_NO_SUPPORT: + return "D3DERR_TEXTURE_NO_SUPPORT\0"; + case D3DERR_TEXTURE_NOT_LOCKED: + return "D3DERR_TEXTURE_NOT_LOCKED\0"; + case D3DERR_TEXTURE_LOCKED: + return "D3DERR_TEXTURELOCKED\0"; + case D3DERR_TEXTURE_CREATE_FAILED: + return "D3DERR_TEXTURE_CREATE_FAILED\0"; + case D3DERR_TEXTURE_DESTROY_FAILED: + return "D3DERR_TEXTURE_DESTROY_FAILED\0"; + case D3DERR_TEXTURE_LOCK_FAILED: + return "D3DERR_TEXTURE_LOCK_FAILED\0"; + case D3DERR_TEXTURE_UNLOCK_FAILED: + return "D3DERR_TEXTURE_UNLOCK_FAILED\0"; + case D3DERR_TEXTURE_LOAD_FAILED: + return "D3DERR_TEXTURE_LOAD_FAILED\0"; + case D3DERR_MATRIX_CREATE_FAILED: + return "D3DERR_MATRIX_CREATE_FAILED\0"; + case D3DERR_MATRIX_DESTROY_FAILED: + return "D3DERR_MATRIX_DESTROY_FAILED\0"; + case D3DERR_MATRIX_SETDATA_FAILED: + return "D3DERR_MATRIX_SETDATA_FAILED\0"; + case D3DERR_SETVIEWPORTDATA_FAILED: + return "D3DERR_SETVIEWPORTDATA_FAILED\0"; + case D3DERR_MATERIAL_CREATE_FAILED: + return "D3DERR_MATERIAL_CREATE_FAILED\0"; + case D3DERR_MATERIAL_DESTROY_FAILED: + return "D3DERR_MATERIAL_DESTROY_FAILED\0"; + case D3DERR_MATERIAL_SETDATA_FAILED: + return "D3DERR_MATERIAL_SETDATA_FAILED\0"; + case D3DERR_LIGHT_SET_FAILED: + return "D3DERR_LIGHT_SET_FAILED\0"; + default: + return "Unrecognized error value.\0"; + } +//XSTR:ON +} diff --git a/src/graphics/grd3drender.cpp b/src/graphics/grd3drender.cpp new file mode 100644 index 0000000..62adec4 --- /dev/null +++ b/src/graphics/grd3drender.cpp @@ -0,0 +1,2258 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrD3DRender.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to actually render stuff using Direct3D + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 27 9/13/99 11:30a Dave + * Added checkboxes and functionality for disabling PXO banners as well as + * disabling d3d zbuffer biasing. + * + * 26 9/08/99 12:03a Dave + * Make squad logos render properly in D3D all the time. Added intel anim + * directory. + * + * 25 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 24 7/30/99 4:04p Anoop + * Fixed D3D shader. + * + * 23 7/29/99 10:47p Dave + * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs. + * + * 22 7/27/99 3:09p Dave + * Made g400 work. Whee. + * + * 21 7/24/99 4:19p Dave + * Fixed dumb code with briefing bitmaps. Made d3d zbuffer work much + * better. Made model code use zbuffer more intelligently. + * + * 20 7/24/99 1:54p Dave + * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert + * missions. + * + * 19 7/19/99 3:29p Dave + * Fixed gamma bitmap in the options screen. + * + * 18 7/14/99 9:42a Dave + * Put in clear_color debug function. Put in base for 3dnow stuff / P3 + * stuff + * + * 17 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 16 7/12/99 11:42a Jefff + * Made rectangle drawing smarter in D3D. Made plines draw properly on Ati + * Rage Pro. + * + * 15 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 14 5/05/99 9:02p Dave + * Fixed D3D aabitmap rendering. Spiffed up nebula effect a bit (added + * rotations, tweaked values, made bitmap selection more random). Fixed + * D3D beam weapon clipping problem. Added D3d frame dumping. + * + * 13 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 12 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 11 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 10 12/08/98 7:03p Dave + * Much improved D3D fogging. Also put in vertex fogging for the cheesiest + * of 3d cards. + * + * 9 12/08/98 2:47p Johnson + * Made D3D fog use eye-relative fog instead of z depth fog. + * + * 8 12/08/98 9:36a Dave + * Almost done nebula effect for D3D. Looks 85% as good as Glide. + * + * 7 12/07/98 5:51p Dave + * Finally got d3d fog working! Now we just need to tweak values. + * + * 6 12/07/98 9:00a Dave + * Fixed d3d rendered. Still don't have fog working. + * + * 5 12/06/98 6:53p Dave + * + * 4 12/01/98 5:54p Dave + * Simplified the way pixel data is swizzled. Fixed tga bitmaps to work + * properly in D3D and Glide. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 54 5/25/98 10:32a John + * Took out redundant code for font bitmap offsets that converted to a + * float, then later on converted back to an integer. Quite unnecessary. + * + * 53 5/24/98 6:45p John + * let direct3d do all clipping. + * + * 52 5/24/98 3:42p John + * Let Direct3D do clipping on any linear textures, like lasers. + * + * 51 5/23/98 7:18p John + * optimized the uv bashing a bit. + * + * 50 5/22/98 1:11p John + * Added code to actually detect which offset a line needs + * + * 49 5/22/98 12:54p John + * added .5 to each pixel of a line. This seemed to make single pixel + * lines draw on all cards. + * + * 48 5/22/98 9:00a John + * Fixed problem of no fading out of additive textures due to Permedia2 + * fix. Did this by dimming out the vertex RGB values. + * + * 47 5/21/98 9:56p John + * Made Direct3D work with classic alpha-blending only devices, like the + * Virge. Added a texture type XPARENT that fills the alpha in in the + * bitmap for Virge. Added support for Permedia by making making + * additive alphablending be one/one instead of alpha/one, which didn't + * work, and there is no way to tell this from caps. + * + * 46 5/20/98 9:45p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 45 5/20/98 3:10p John + * Made lines work even if no alphagouraud capabilities on the card. + * + * 44 5/19/98 4:50p Lawrance + * JAS: Fixed some bugs on Alan's nVidia Riva128 PCI where some + * unitiallized fields, namely vertex->shw were causing glitches. + * + * 43 5/19/98 1:46p John + * Fixed Rendition/Riva128 uv problems. + * + * 42 5/19/98 12:34p John + * added code to fix uv's on rendition. added code to fix zbuffering + * problem on rendition. + * + * 41 5/18/98 8:26p John + * Made scanline be line. Made lines work if no line alpha blending + * supported. Made no alpha mode use alpha off. + * + * 40 5/17/98 4:13p John + * Made zbuffer clear only clear current clip region + * + * 39 5/17/98 3:23p John + * Took out capibility check for additive blending. Made gr_bitmap_ex + * clip properly in glide and direct3d. + * + * 38 5/15/98 8:48a John + * Fixed bug where one-pixel line was getting left on right and bottom. + * + * 37 5/12/98 8:43p John + * fixed particle zbuffering. + * + * 36 5/12/98 10:34a John + * Added d3d_shade functionality. Added d3d_flush function, since the + * shader seems to get reorganzed behind the overlay text stuff! + * + * 35 5/12/98 10:06a John + * Made all tmaps "clamp-clip". This fixed bug with offscreen hud + * indicators not rendering. + * + * 34 5/12/98 8:18a John + * Put in code to use a different texture format for alpha textures and + * normal textures. Turned off filtering for aabitmaps. Took out + * destblend=invsrccolor alpha mode that doesn't work on riva128. + * + * 33 5/11/98 10:58a John + * Fixed pilot name cursor bug. Started adding in code for alphachannel + * textures. + * + * 32 5/09/98 12:37p John + * More texture caching + * + * 31 5/09/98 12:16p John + * Even better texture caching. + * + * 30 5/08/98 10:12a John + * took out an mprintf + * + * 29 5/07/98 11:31a John + * Removed DEMO defines + * + * 28 5/07/98 10:28a John + * Made texture format use 4444. Made fonts use alpha to render. + * + * 27 5/07/98 10:09a John + * Fixed some bugs with short lines in D3D. + * + * 26 5/07/98 9:54a John + * Added in palette flash functionallity. + * + * 25 5/07/98 9:40a John + * Fixed some bitmap transparency issues with Direct3D. + * + * 24 5/06/98 11:21p John + * Fixed a bitmap bug with Direct3D. Started adding new caching code into + * D3D. + * + * 23 5/06/98 8:41p John + * Fixed some font clipping bugs. Moved texture handle set code for d3d + * into the texture module. + * + * 22 5/06/98 8:07p John + * made d3d clear work correctly. + * + * 21 5/06/98 8:00p John + * Got stars working under D3D. + * + * 20 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 19 5/05/98 10:37p John + * Added code to optionally use execute buffers. + * + * 18 5/04/98 3:36p John + * Got zbuffering working with Direct3D. + * + * 17 5/03/98 10:52a John + * Made D3D sort of work on 3dfx. + * + * 16 5/03/98 10:43a John + * Working on Direct3D. + * + * 15 4/14/98 12:15p John + * Made 16-bpp movies work. + * + * 14 4/10/98 5:20p John + * Changed RGB in lighting structure to be ubytes. Removed old + * not-necessary 24 bpp software stuff. + * + * 13 4/09/98 11:05a John + * Removed all traces of Direct3D out of the demo version of Freespace and + * the launcher. + * + * 12 3/12/98 5:36p John + * Took out any unused shaders. Made shader code take rgbc instead of + * matrix and vector since noone used it like a matrix and it would have + * been impossible to do in hardware. Made Glide implement a basic + * shader for online help. + * + * 11 3/11/98 1:55p John + * Fixed warnings + * + * 10 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 9 3/08/98 12:33p John + * Added more lines, tris, and colored flat polys (lasers!) correctly. + * + * 8 3/08/98 10:25a John + * Added in lines + * + * 7 3/07/98 8:29p John + * Put in some Direct3D features. Transparency on bitmaps. Made fonts & + * aabitmaps render nice. + * + * 6 3/06/98 5:39p John + * Started adding in aabitmaps + * + * 5 3/02/98 5:42p John + * Removed WinAVI stuff from Freespace. Made all HUD gauges wriggle from + * afterburner. Made gr_set_clip work good with negative x &y. Made + * model_caching be on by default. Made each cached model have it's own + * bitmap id. Made asteroids not rotate when model_caching is on. + * + * 4 2/26/98 3:24p John + * fixed optimized warning + * + * 3 2/17/98 7:28p John + * Got fonts and texturing working in Direct3D + * + * 2 2/07/98 7:50p John + * Added code so that we can use the old blending type of alphacolors if + * we want to. Made the stars use them. + * + * 1 2/03/98 9:24p John + * + * $NoKeywords: $ + */ + +#include "grd3dinternal.h" +#include "2d.h" +#include "pstypes.h" +#include "bmpman.h" +#include "palman.h" +#include "line.h" +#include "cfile.h" +#include "neb.h" +#include "3d.h" + +typedef enum gr_texture_source { + TEXTURE_SOURCE_NONE, + TEXTURE_SOURCE_DECAL, + TEXTURE_SOURCE_NO_FILTERING, +} gr_texture_source; + +typedef enum gr_alpha_blend { + ALPHA_BLEND_NONE, // 1*SrcPixel + 0*DestPixel + ALPHA_BLEND_ALPHA_ADDITIVE, // Alpha*SrcPixel + 1*DestPixel + ALPHA_BLEND_ALPHA_BLEND_ALPHA, // Alpha*SrcPixel + (1-Alpha)*DestPixel + ALPHA_BLEND_ALPHA_BLEND_SRC_COLOR, // Alpha*SrcPixel + (1-SrcPixel)*DestPixel +} gr_alpha_blend; + +typedef enum gr_zbuffer_type { + ZBUFFER_TYPE_NONE, + ZBUFFER_TYPE_READ, + ZBUFFER_TYPE_WRITE, + ZBUFFER_TYPE_FULL, +} gr_zbuffer_type; + +int D3d_last_state = -1; + +// Hack! move to another file! +extern int D3d_rendition_uvs; + +// Hack! move to another file! +extern int D3D_fog_mode; + +void gr_d3d_set_state( gr_texture_source ts, gr_alpha_blend ab, gr_zbuffer_type zt ) +{ + int current_state = 0; + + current_state = current_state | (ts<<0); + current_state = current_state | (ab<<5); + current_state = current_state | (zt<<10); + + if ( current_state == D3d_last_state ) { + return; + } + D3d_last_state = current_state; + + switch( ts ) { + case TEXTURE_SOURCE_NONE: + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE, NULL ); + // Let the texture cache system know whe set the handle to NULL + gr_tcache_set(-1, -1, NULL, NULL ); + + break; + case TEXTURE_SOURCE_DECAL: + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREMIN, D3DFILTER_LINEAR ); + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREMAG, D3DFILTER_LINEAR ); + + if ( lpDevDesc->dpcTriCaps.dwTextureBlendCaps & D3DPTBLENDCAPS_MODULATEALPHA ) { + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND, D3DTBLEND_MODULATEALPHA ); + } else { + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND, D3DTBLEND_MODULATE ); + } + break; + + case TEXTURE_SOURCE_NO_FILTERING: + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREMIN, D3DFILTER_NEAREST ); + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREMAG, D3DFILTER_NEAREST ); + if ( lpDevDesc->dpcTriCaps.dwTextureBlendCaps & D3DPTBLENDCAPS_MODULATEALPHA ) { + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND, D3DTBLEND_MODULATEALPHA ); + } else { + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREMAPBLEND, D3DTBLEND_MODULATE ); + } + break; + + default: + Int3(); + } + + switch( ab ) { + case ALPHA_BLEND_NONE: // 1*SrcPixel + 0*DestPixel + d3d_SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, FALSE ); + break; + + case ALPHA_BLEND_ALPHA_ADDITIVE: // Alpha*SrcPixel + 1*DestPixel + case ALPHA_BLEND_ALPHA_BLEND_SRC_COLOR: // Alpha*SrcPixel + (1-SrcPixel)*DestPixel + if ( lpDevDesc->dpcTriCaps.dwDestBlendCaps & D3DPBLENDCAPS_ONE ) { + d3d_SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE ); + // Must use ONE:ONE as the Permedia2 can't do SRCALPHA:ONE. + // But I lower RGB values so we don't loose anything. + d3d_SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_ONE ); + //d3d_SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA ); + d3d_SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_ONE ); + break; + } + // Fall through to normal alpha blending mode... + + case ALPHA_BLEND_ALPHA_BLEND_ALPHA: // Alpha*SrcPixel + (1-Alpha)*DestPixel + if ( lpDevDesc->dpcTriCaps.dwSrcBlendCaps & D3DPBLENDCAPS_SRCALPHA ) { + d3d_SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE ); + d3d_SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA ); + d3d_SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA ); + } else { + d3d_SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE ); + d3d_SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_BOTHSRCALPHA ); + } + break; + + + default: + Int3(); + } + + switch( zt ) { + + case ZBUFFER_TYPE_NONE: + d3d_SetRenderState(D3DRENDERSTATE_ZENABLE,FALSE); + d3d_SetRenderState(D3DRENDERSTATE_ZWRITEENABLE,FALSE); + break; + + case ZBUFFER_TYPE_READ: + d3d_SetRenderState(D3DRENDERSTATE_ZENABLE,TRUE); + d3d_SetRenderState(D3DRENDERSTATE_ZWRITEENABLE,FALSE); + break; + + case ZBUFFER_TYPE_WRITE: + d3d_SetRenderState(D3DRENDERSTATE_ZENABLE,FALSE); + d3d_SetRenderState(D3DRENDERSTATE_ZWRITEENABLE,TRUE); + break; + + case ZBUFFER_TYPE_FULL: + d3d_SetRenderState(D3DRENDERSTATE_ZENABLE,TRUE); + d3d_SetRenderState(D3DRENDERSTATE_ZWRITEENABLE,TRUE); + break; + + default: + Int3(); + } + +} + +extern int D3D_zbias; +void d3d_zbias(int bias) +{ + if(D3D_zbias){ + d3d_SetRenderState(D3DRENDERSTATE_ZBIAS, bias); + } +} + +// If mode is FALSE, turn zbuffer off the entire frame, +// no matter what people pass to gr_zbuffer_set. +void gr_d3d_zbuffer_clear(int mode) +{ + if ( mode ) { + gr_zbuffering = 1; + gr_zbuffering_mode = GR_ZBUFF_FULL; + gr_global_zbuffering = 1; + + // Make sure zbuffering is on + gr_d3d_set_state( TEXTURE_SOURCE_NONE, ALPHA_BLEND_NONE, ZBUFFER_TYPE_FULL ); + + + // An application can clear z-buffers by using the IDirectDrawSurface2::Blt method. + // The DDBLT_DEPTHFILL flag indicates that the blit clears z-buffers. If this flag + // is specified, the DDBLTFX structure passed to the IDirectDrawSurface2::Blt method + // should have its dwFillDepth member set to the required z-depth. If the DirectDraw device + // driver for a 3D-accelerated display card is designed to provide support for z-buffer + // clearing in hardware, it should export the DDCAPS_BLTDEPTHFILL flag and should + // handle DDBLT_DEPTHFILL blits. The destination surface of a depth-fill blit must + // be a z-buffer. + // Note The actual interpretation of a depth value is specific to the 3D renderer. + + D3DRECT rect; + + rect.x1 = gr_screen.clip_left + gr_screen.offset_x; + rect.y1 = gr_screen.clip_top + gr_screen.offset_y; + rect.x2 = gr_screen.clip_right + gr_screen.offset_x; + rect.y2 = gr_screen.clip_bottom + gr_screen.offset_y; + + if (lpViewport->Clear( 1, &rect, D3DCLEAR_ZBUFFER ) != D3D_OK ) { + mprintf(( "Failed to clear zbuffer!\n" )); + return; + } + + + } else { + gr_zbuffering = 0; + gr_zbuffering_mode = GR_ZBUFF_NONE; + gr_global_zbuffering = 0; + } +} + +// internal d3d rect function +void gr_d3d_rect_internal(int x, int y, int w, int h, int r, int g, int b, int a) +{ + int saved_zbuf; + vertex v[4]; + vertex *verts[4] = {&v[0], &v[1], &v[2], &v[3]}; + + saved_zbuf = gr_zbuffer_get(); + + // start the frame, no zbuffering, no culling + g3_start_frame(1); + gr_zbuffer_set(GR_ZBUFF_NONE); + gr_set_cull(0); + + // stuff coords + v[0].sx = i2fl(x); + v[0].sy = i2fl(y); + v[0].sw = 0.0f; + v[0].u = 0.0f; + v[0].v = 0.0f; + v[0].flags = PF_PROJECTED; + v[0].codes = 0; + v[0].r = (ubyte)r; + v[0].g = (ubyte)g; + v[0].b = (ubyte)b; + v[0].a = (ubyte)a; + + v[1].sx = i2fl(x + w); + v[1].sy = i2fl(y); + v[1].sw = 0.0f; + v[1].u = 0.0f; + v[1].v = 0.0f; + v[1].flags = PF_PROJECTED; + v[1].codes = 0; + v[1].r = (ubyte)r; + v[1].g = (ubyte)g; + v[1].b = (ubyte)b; + v[1].a = (ubyte)a; + + v[2].sx = i2fl(x + w); + v[2].sy = i2fl(y + h); + v[2].sw = 0.0f; + v[2].u = 0.0f; + v[2].v = 0.0f; + v[2].flags = PF_PROJECTED; + v[2].codes = 0; + v[2].r = (ubyte)r; + v[2].g = (ubyte)g; + v[2].b = (ubyte)b; + v[2].a = (ubyte)a; + + v[3].sx = i2fl(x); + v[3].sy = i2fl(y + h); + v[3].sw = 0.0f; + v[3].u = 0.0f; + v[3].v = 0.0f; + v[3].flags = PF_PROJECTED; + v[3].codes = 0; + v[3].r = (ubyte)r; + v[3].g = (ubyte)g; + v[3].b = (ubyte)b; + v[3].a = (ubyte)a; + + // draw the polys + g3_draw_poly_constant_sw(4, verts, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB | TMAP_FLAG_ALPHA, 0.1f); + + g3_end_frame(); + + // restore zbuffer and culling + gr_zbuffer_set(saved_zbuf); + gr_set_cull(1); +} + +int gr_d3d_zbuffer_get() +{ + if ( !gr_global_zbuffering ) { + return GR_ZBUFF_NONE; + } + return gr_zbuffering_mode; +} + +int gr_d3d_zbuffer_set(int mode) +{ + /* + if ( !gr_global_zbuffering ) { + gr_zbuffering = 0; + return GR_ZBUFF_NONE; + } + */ + + int tmp = gr_zbuffering_mode; + + gr_zbuffering_mode = mode; + + if ( gr_zbuffering_mode == GR_ZBUFF_NONE ) { + gr_zbuffering = 0; + } else { + gr_zbuffering = 1; + } + return tmp; +} + +float D3D_line_offset = 0.0f; + +void d3d_make_rect( D3DTLVERTEX *a, D3DTLVERTEX *b, int x1, int y1, int x2, int y2 ) +{ + // Alan's nvidia riva128 PCI screws up targetting brackets if + // rhw are uninitialized. + a->rhw = 1.0f; + b->rhw = 1.0f; + + // just for completeness, initialize specular and sz. + a->specular = 0; + b->specular = 0; + + a->sz = 0.99f; + b->sz = 0.99f; + + a->sx = i2fl(x1 + gr_screen.offset_x)+D3D_line_offset; + a->sy = i2fl(y1 + gr_screen.offset_y)+D3D_line_offset; + + b->sx = i2fl(x2 + gr_screen.offset_x)+D3D_line_offset; + b->sy = i2fl(y2 + gr_screen.offset_y)+D3D_line_offset; + + if ( x1 == x2 ) { + // Verticle line + if ( a->sy < b->sy ) { + b->sy += 0.5f; + } else { + a->sy += 0.5f; + } + } else if ( y1 == y2 ) { + // Horizontal line + if ( a->sx < b->sx ) { + b->sx += 0.5f; + } else { + a->sx += 0.5f; + } + } + +} + +// basically just fills in the alpha component of the specular color. Hardware does the rest +// when rendering the poly +void gr_d3d_stuff_fog_value(float z, D3DCOLOR *spec) +{ + float f_float; + *spec = 0; + + // linear fog formula + f_float = (gr_screen.fog_far - z) / (gr_screen.fog_far - gr_screen.fog_near); + if(f_float < 0.0f){ + f_float = 0.0f; + } else if(f_float > 1.0f){ + f_float = 1.0f; + } + *spec = D3DRGBA(0.0f, 0.0f, 0.0f, f_float); +} + +float z_mult = 30000.0f; +DCF(zmult, "") +{ + dc_get_arg(ARG_FLOAT); + z_mult = Dc_arg_float; +} + +float flCAP( float x, float minx, float maxx) +{ + if ( x < minx ) { + return minx; + } else if ( x > maxx ) { + return maxx; + } + return x; +} + +#define NEBULA_COLORS 20 + +void gr_d3d_tmapper_internal( int nverts, vertex **verts, uint flags, int is_scaler ) +{ + int i; + float u_scale = 1.0f, v_scale = 1.0f; + int bw = 1, bh = 1; + + // Make nebula use the texture mapper... this blends the colors better. + if ( flags & TMAP_FLAG_NEBULA ){ + Int3(); + /* + flags |= TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT; + + static int test_bmp = -1; + static ushort data[16]; + if ( test_bmp == -1 ){ + ushort pix; + ubyte a, r, g, b; + int idx; + + // stuff the fake bitmap + a = 1; r = 255; g = 255; b = 255; + pix = 0; + bm_set_components((ubyte*)&pix, &r, &g, &b, &a); + for(idx=0; idx<16; idx++){ + data[idx] = pix; + } + test_bmp = bm_create( 16, 4, 4, data ); + } + gr_set_bitmap( test_bmp ); + + for (i=0; iu = verts[i]->v = 0.5f; + } + */ + } + + gr_texture_source texture_source = (gr_texture_source)-1; + gr_alpha_blend alpha_blend = (gr_alpha_blend)-1; + gr_zbuffer_type zbuffer_type = (gr_zbuffer_type)-1; + + + if ( gr_zbuffering ) { + if ( is_scaler || (gr_screen.current_alphablend_mode == GR_ALPHABLEND_FILTER) ) { + zbuffer_type = ZBUFFER_TYPE_READ; + } else { + zbuffer_type = ZBUFFER_TYPE_FULL; + } + } else { + zbuffer_type = ZBUFFER_TYPE_NONE; + } + + int alpha; + + int tmap_type = TCACHE_TYPE_NORMAL; + + int r, g, b; + + if ( flags & TMAP_FLAG_TEXTURED ) { + r = 255; + g = 255; + b = 255; + } else { + r = gr_screen.current_color.red; + g = gr_screen.current_color.green; + b = gr_screen.current_color.blue; + } + + if ( gr_screen.current_alphablend_mode == GR_ALPHABLEND_FILTER ) { + + if ( lpDevDesc->dpcTriCaps.dwDestBlendCaps & D3DPBLENDCAPS_ONE ) { + tmap_type = TCACHE_TYPE_NORMAL; + alpha_blend = ALPHA_BLEND_ALPHA_ADDITIVE; + + // Blend with screen pixel using src*alpha+dst + float factor = gr_screen.current_alpha; + + alpha = 255; + + if ( factor <= 1.0f ) { + int tmp_alpha = fl2i(gr_screen.current_alpha*255.0f); + r = (r*tmp_alpha)/255; + g = (g*tmp_alpha)/255; + b = (b*tmp_alpha)/255; + } + } else { + + tmap_type = TCACHE_TYPE_XPARENT; + + alpha_blend = ALPHA_BLEND_ALPHA_BLEND_ALPHA; + + // Blend with screen pixel using src*alpha+dst + float factor = gr_screen.current_alpha; + + if ( factor > 1.0f ) { + alpha = 255; + } else { + alpha = fl2i(gr_screen.current_alpha*255.0f); + } + } + } else { + if(Bm_pixel_format == BM_PIXEL_FORMAT_ARGB_D3D){ + alpha_blend = ALPHA_BLEND_ALPHA_BLEND_ALPHA; + } else { + alpha_blend = ALPHA_BLEND_NONE; + } + alpha = 255; + } + + if(flags & TMAP_FLAG_BITMAP_SECTION){ + tmap_type = TCACHE_TYPE_BITMAP_SECTION; + } + + texture_source = TEXTURE_SOURCE_NONE; + + if ( flags & TMAP_FLAG_TEXTURED ) { + if ( !gr_tcache_set(gr_screen.current_bitmap, tmap_type, &u_scale, &v_scale, 0, gr_screen.current_bitmap_sx, gr_screen.current_bitmap_sy )) { + mprintf(( "Not rendering a texture because it didn't fit in VRAM!\n" )); + return; + } + + // use nonfiltered textures for bitmap sections + if(flags & TMAP_FLAG_BITMAP_SECTION){ + texture_source = TEXTURE_SOURCE_NO_FILTERING; + } else { + texture_source = TEXTURE_SOURCE_DECAL; + } + } + + gr_d3d_set_state( texture_source, alpha_blend, zbuffer_type ); + + D3DTLVERTEX d3d_verts[32]; + D3DTLVERTEX *src_v = d3d_verts; + + int x1, y1, x2, y2; + x1 = gr_screen.clip_left*16; + x2 = gr_screen.clip_right*16+15; + y1 = gr_screen.clip_top*16; + y2 = gr_screen.clip_bottom*16+15; + + float uoffset = 0.0f; + float voffset = 0.0f; + + float minu=0.0f, minv=0.0f, maxu=1.0f, maxv=1.0f; + + if ( flags & TMAP_FLAG_TEXTURED ) { + if ( D3d_rendition_uvs ) { + bm_get_info(gr_screen.current_bitmap, &bw, &bh); + + uoffset = 2.0f/i2fl(bw); + voffset = 2.0f/i2fl(bh); + + minu = uoffset; + minv = voffset; + + maxu = 1.0f - uoffset; + maxv = 1.0f - voffset; + } + } + + // turn on pixel fog if we're rendering against a fullneb background + // if(flags & TMAP_FLAG_PIXEL_FOG){ + // set fog + // gr_fog_set(GR_FOGMODE_FOG, gr_screen.current_fog_color.red, gr_screen.current_fog_color.green, gr_screen.current_fog_color.blue); + // } + + for (i=0; isz = va->z / z_mult; // For zbuffering and fogging + if ( src_v->sz > 0.98f ) { + src_v->sz = 0.98f; + } + } else { + src_v->sz = 0.99f; + } + + if ( flags & TMAP_FLAG_CORRECT ) { + src_v->rhw = va->sw; // For texture correction + } else { + src_v->rhw = 1.0f; // For texture correction + } + + int a; + + if ( flags & TMAP_FLAG_ALPHA ) { + a = verts[i]->a; + } else { + a = alpha; + } + + if ( flags & TMAP_FLAG_NEBULA ) { + int pal = (verts[i]->b*(NEBULA_COLORS-1))/255; + r = gr_palette[pal*3+0]; + g = gr_palette[pal*3+1]; + b = gr_palette[pal*3+2]; + } else if ( (flags & TMAP_FLAG_RAMP) && (flags & TMAP_FLAG_GOURAUD) ) { + r = Gr_gamma_lookup[verts[i]->b]; + g = Gr_gamma_lookup[verts[i]->b]; + b = Gr_gamma_lookup[verts[i]->b]; + } else if ( (flags & TMAP_FLAG_RGB) && (flags & TMAP_FLAG_GOURAUD) ) { + // Make 0.75 be 256.0f + r = Gr_gamma_lookup[verts[i]->r]; + g = Gr_gamma_lookup[verts[i]->g]; + b = Gr_gamma_lookup[verts[i]->b]; + } else { + // use constant RGB values... + } + + src_v->color = RGBA_MAKE(r, g, b, a); + + // if we're fogging and we're doing vertex fog + if((gr_screen.current_fog_mode != GR_FOGMODE_NONE) && (D3D_fog_mode == 1)){ + gr_d3d_stuff_fog_value(va->z, &src_v->specular); + } else { + src_v->specular = 0; + } + + int x, y; + x = fl2i(va->sx*16.0f); + y = fl2i(va->sy*16.0f); + + x += gr_screen.offset_x*16; + y += gr_screen.offset_y*16; + + src_v->sx = i2fl(x) / 16.0f; + src_v->sy = i2fl(y) / 16.0f; + + if ( flags & TMAP_FLAG_TEXTURED ) { + // argh. rendition + if ( D3d_rendition_uvs ){ + // tiled texture (ships, etc), bitmap sections + if(flags & TMAP_FLAG_TILED){ + src_v->tu = va->u*u_scale; + src_v->tv = va->v*v_scale; + } + // sectioned + else if(flags & TMAP_FLAG_BITMAP_SECTION){ + int sw, sh; + bm_get_section_size(gr_screen.current_bitmap, gr_screen.current_bitmap_sx, gr_screen.current_bitmap_sy, &sw, &sh); + + src_v->tu = (va->u + (0.5f / i2fl(sw))) * u_scale; + src_v->tv = (va->v + (0.5f / i2fl(sh))) * v_scale; + } + // all else. + else { + src_v->tu = flCAP(va->u, minu, maxu); + src_v->tv = flCAP(va->v, minv, maxv); + } + } + // yay. non-rendition + else { + src_v->tu = va->u*u_scale; + src_v->tv = va->v*v_scale; + } + } else { + src_v->tu = 0.0f; + src_v->tv = 0.0f; + } + src_v++; + } + + // if we're rendering against a fullneb background + if(flags & TMAP_FLAG_PIXEL_FOG){ + int r, g, b; + int ra, ga, ba; + ra = ga = ba = 0; + + // get the average pixel color behind the vertices + for(i=0; isx; y0 = va->sy; + x1 = vb->sx; y1 = vb->sy; + + xmin = i2fl(gr_screen.clip_left); ymin = i2fl(gr_screen.clip_top); + xmax = i2fl(gr_screen.clip_right); ymax = i2fl(gr_screen.clip_bottom); + + u0 = va->u; v0 = va->v; + u1 = vb->u; v1 = vb->v; + + // Check for obviously offscreen bitmaps... + if ( (y1<=y0) || (x1<=x0) ) return; + if ( (x1xmax) ) return; + if ( (y1ymax) ) return; + + clipped_u0 = u0; clipped_v0 = v0; + clipped_u1 = u1; clipped_v1 = v1; + + clipped_x0 = x0; clipped_y0 = y0; + clipped_x1 = x1; clipped_y1 = y1; + + // Clip the left, moving u0 right as necessary + if ( x0 < xmin ) { + clipped_u0 = FIND_SCALED_NUM(xmin,x0,x1,u0,u1); + clipped_x0 = xmin; + } + + // Clip the right, moving u1 left as necessary + if ( x1 > xmax ) { + clipped_u1 = FIND_SCALED_NUM(xmax,x0,x1,u0,u1); + clipped_x1 = xmax; + } + + // Clip the top, moving v0 down as necessary + if ( y0 < ymin ) { + clipped_v0 = FIND_SCALED_NUM(ymin,y0,y1,v0,v1); + clipped_y0 = ymin; + } + + // Clip the bottom, moving v1 up as necessary + if ( y1 > ymax ) { + clipped_v1 = FIND_SCALED_NUM(ymax,y0,y1,v0,v1); + clipped_y1 = ymax; + } + + dx0 = fl2i(clipped_x0); dx1 = fl2i(clipped_x1); + dy0 = fl2i(clipped_y0); dy1 = fl2i(clipped_y1); + + if (dx1<=dx0) return; + if (dy1<=dy0) return; + + //============= DRAW IT ===================== + + vertex v[4]; + vertex *vl[4]; + + vl[0] = &v[0]; + v->sx = clipped_x0; + v->sy = clipped_y0; + v->sw = va->sw; + v->z = va->z; + v->u = clipped_u0; + v->v = clipped_v0; + + vl[1] = &v[1]; + v[1].sx = clipped_x1; + v[1].sy = clipped_y0; + v[1].sw = va->sw; + v[1].z = va->z; + v[1].u = clipped_u1; + v[1].v = clipped_v0; + + vl[2] = &v[2]; + v[2].sx = clipped_x1; + v[2].sy = clipped_y1; + v[2].sw = va->sw; + v[2].z = va->z; + v[2].u = clipped_u1; + v[2].v = clipped_v1; + + vl[3] = &v[3]; + v[3].sx = clipped_x0; + v[3].sy = clipped_y1; + v[3].sw = va->sw; + v[3].z = va->z; + v[3].u = clipped_u0; + v[3].v = clipped_v1; + + gr_d3d_tmapper_internal( 4, vl, TMAP_FLAG_TEXTURED, 1 ); +} + +void gr_d3d_aascaler(vertex *va, vertex *vb ) +{ +} + + +void gr_d3d_pixel(int x, int y) +{ + gr_line(x,y,x,y); +} + + +void gr_d3d_clear() +{ + // Turn off zbuffering so this doesn't clear the zbuffer + gr_d3d_set_state( TEXTURE_SOURCE_NONE, ALPHA_BLEND_NONE, ZBUFFER_TYPE_NONE ); + + RECT dst; + DDBLTFX ddbltfx; + DDSURFACEDESC ddsd; + + // Get the surface desc + ddsd.dwSize = sizeof(ddsd); + lpBackBuffer->GetSurfaceDesc(&ddsd); + + memset(&ddbltfx, 0, sizeof(ddbltfx)); + ddbltfx.dwSize = sizeof(DDBLTFX); + + ddbltfx.dwFillColor = RGB_MAKE(gr_screen.current_clear_color.red, gr_screen.current_clear_color.green, gr_screen.current_clear_color.blue); + + dst.left = gr_screen.clip_left+gr_screen.offset_x; + dst.top = gr_screen.clip_top+gr_screen.offset_y; + dst.right = gr_screen.clip_right+1+gr_screen.offset_x; + dst.bottom = gr_screen.clip_bottom+1+gr_screen.offset_y; + + if ( lpBackBuffer->Blt( &dst, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx ) != DD_OK ) { + return; + } +} + + +// sets the clipping region & offset +void gr_d3d_set_clip(int x,int y,int w,int h) +{ + gr_screen.offset_x = x; + gr_screen.offset_y = y; + + gr_screen.clip_left = 0; + gr_screen.clip_right = w-1; + + gr_screen.clip_top = 0; + gr_screen.clip_bottom = h-1; + + // check for sanity of parameters + if ( gr_screen.clip_left+x < 0 ) { + gr_screen.clip_left = -x; + } else if ( gr_screen.clip_left+x > gr_screen.max_w-1 ) { + gr_screen.clip_left = gr_screen.max_w-1-x; + } + if ( gr_screen.clip_right+x < 0 ) { + gr_screen.clip_right = -x; + } else if ( gr_screen.clip_right+x >= gr_screen.max_w-1 ) { + gr_screen.clip_right = gr_screen.max_w-1-x; + } + + if ( gr_screen.clip_top+y < 0 ) { + gr_screen.clip_top = -y; + } else if ( gr_screen.clip_top+y > gr_screen.max_h-1 ) { + gr_screen.clip_top = gr_screen.max_h-1-y; + } + + if ( gr_screen.clip_bottom+y < 0 ) { + gr_screen.clip_bottom = -y; + } else if ( gr_screen.clip_bottom+y > gr_screen.max_h-1 ) { + gr_screen.clip_bottom = gr_screen.max_h-1-y; + } + + gr_screen.clip_width = gr_screen.clip_right - gr_screen.clip_left + 1; + gr_screen.clip_height = gr_screen.clip_bottom - gr_screen.clip_top + 1; + + // Setup the viewport for a reasonable viewing area + D3DVIEWPORT viewdata; + DWORD largest_side; + HRESULT ddrval; + + // Compensate for aspect ratio + if ( gr_screen.clip_width > gr_screen.clip_height ) + largest_side = gr_screen.clip_width; + else + largest_side = gr_screen.clip_height; + + viewdata.dwSize = sizeof( viewdata ); + viewdata.dwX = gr_screen.clip_left+x; + viewdata.dwY = gr_screen.clip_top+y; + viewdata.dwWidth = gr_screen.clip_width; + viewdata.dwHeight = gr_screen.clip_height; + viewdata.dvScaleX = largest_side / 2.0F; + viewdata.dvScaleY = largest_side / 2.0F; + viewdata.dvMaxX = ( float ) ( viewdata.dwWidth / ( 2.0F * viewdata.dvScaleX ) ); + viewdata.dvMaxY = ( float ) ( viewdata.dwHeight / ( 2.0F * viewdata.dvScaleY ) ); + viewdata.dvMinZ = 0.0F; + viewdata.dvMaxZ = 0.0F; // choose something appropriate here! + + ddrval = lpViewport->SetViewport( &viewdata ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_D3D_SET_CLIP: SetViewport failed.\n" )); + } + +} + + +void gr_d3d_reset_clip() +{ + gr_screen.offset_x = 0; + gr_screen.offset_y = 0; + gr_screen.clip_left = 0; + gr_screen.clip_top = 0; + gr_screen.clip_right = gr_screen.max_w - 1; + gr_screen.clip_bottom = gr_screen.max_h - 1; + gr_screen.clip_width = gr_screen.max_w; + gr_screen.clip_height = gr_screen.max_h; + + // Setup the viewport for a reasonable viewing area + D3DVIEWPORT viewdata; + DWORD largest_side; + HRESULT ddrval; + + // Compensate for aspect ratio + if ( gr_screen.clip_width > gr_screen.clip_height ) + largest_side = gr_screen.clip_width; + else + largest_side = gr_screen.clip_height; + + viewdata.dwSize = sizeof( viewdata ); + viewdata.dwX = gr_screen.clip_left; + viewdata.dwY = gr_screen.clip_top; + viewdata.dwWidth = gr_screen.clip_width; + viewdata.dwHeight = gr_screen.clip_height; + viewdata.dvScaleX = largest_side / 2.0F; + viewdata.dvScaleY = largest_side / 2.0F; + viewdata.dvMaxX = ( float ) ( viewdata.dwWidth / ( 2.0F * viewdata.dvScaleX ) ); + viewdata.dvMaxY = ( float ) ( viewdata.dwHeight / ( 2.0F * viewdata.dvScaleY ) ); + viewdata.dvMinZ = 0.0F; + viewdata.dvMaxZ = 0.0F; // choose something appropriate here! + + ddrval = lpViewport->SetViewport( &viewdata ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_D3D_SET_CLIP: SetViewport failed.\n" )); + } + +} + +void gr_d3d_init_color(color *c, int r, int g, int b) +{ + c->screen_sig = gr_screen.signature; + c->red = unsigned char(r); + c->green = unsigned char(g); + c->blue = unsigned char(b); + c->alpha = 255; + c->ac_type = AC_TYPE_NONE; + c->alphacolor = -1; + c->is_alphacolor = 0; + c->magic = 0xAC01; +} + +void gr_d3d_init_alphacolor( color *clr, int r, int g, int b, int alpha, int type ) +{ + if ( r < 0 ) r = 0; else if ( r > 255 ) r = 255; + if ( g < 0 ) g = 0; else if ( g > 255 ) g = 255; + if ( b < 0 ) b = 0; else if ( b > 255 ) b = 255; + if ( alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255; + + gr_d3d_init_color( clr, r, g, b ); + + clr->alpha = unsigned char(alpha); + clr->ac_type = (ubyte)type; + clr->alphacolor = -1; + clr->is_alphacolor = 1; +} + +void gr_d3d_set_color( int r, int g, int b ) +{ + Assert((r >= 0) && (r < 256)); + Assert((g >= 0) && (g < 256)); + Assert((b >= 0) && (b < 256)); + + gr_d3d_init_color( &gr_screen.current_color, r, g, b ); +} + +void gr_d3d_get_color( int * r, int * g, int * b ) +{ + if (r) *r = gr_screen.current_color.red; + if (g) *g = gr_screen.current_color.green; + if (b) *b = gr_screen.current_color.blue; +} + +void gr_d3d_set_color_fast(color *dst) +{ + if ( dst->screen_sig != gr_screen.signature ) { + if ( dst->is_alphacolor ) { + gr_d3d_init_alphacolor( dst, dst->red, dst->green, dst->blue, dst->alpha, dst->ac_type ); + } else { + gr_d3d_init_color( dst, dst->red, dst->green, dst->blue ); + } + } + gr_screen.current_color = *dst; +} + +void gr_d3d_set_bitmap( int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha, int sx, int sy ) +{ + gr_screen.current_alpha = alpha; + gr_screen.current_alphablend_mode = alphablend_mode; + gr_screen.current_bitblt_mode = bitblt_mode; + gr_screen.current_bitmap = bitmap_num; + gr_screen.current_bitmap_sx = sx; + gr_screen.current_bitmap_sy = sy; +} + +void gr_d3d_bitmap_ex_internal(int x,int y,int w,int h,int sx,int sy) +{ + int i,j; + bitmap * bmp; + ushort * sptr; + ushort * dptr; + HRESULT ddrval; + DDSURFACEDESC ddsd; + + memset( &ddsd, 0, sizeof( ddsd ) ); + ddsd.dwSize = sizeof( ddsd ); + + ddrval = lpBackBuffer->Lock( NULL, &ddsd, DDLOCK_WAIT, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "Error locking surface for bitmap_ex, %s\n", d3d_error_string(ddrval) )); + return; + } + + dptr = (ushort *)((int)ddsd.lpSurface+ddsd.lPitch*(y+gr_screen.offset_y)+(x+gr_screen.offset_x)*2); + + bmp = bm_lock( gr_screen.current_bitmap, 16, 0 ); + sptr = (ushort *)( bmp->data + (sy*bmp->w + sx) ); + + // nice and speedy compared to the old way + for (i=0; iw; + } + + bm_unlock(gr_screen.current_bitmap); + + // Unlock the back buffer + lpBackBuffer->Unlock( NULL ); +} + +void gr_d3d_bitmap_ex(int x,int y,int w,int h,int sx,int sy) +{ + int reclip; + #ifndef NDEBUG + int count = 0; + #endif + + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + + int bw, bh; + bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL ); + + do { + reclip = 0; + #ifndef NDEBUG + if ( count > 1 ) Int3(); + count++; + #endif + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx += gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy += gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) { + dx1 -= sx; + sx = 0; + reclip = 1; + } + + if ( sy < 0 ) { + dy1 -= sy; + sy = 0; + reclip = 1; + } + + w = dx2-dx1+1; + h = dy2-dy1+1; + + if ( sx + w > bw ) { + w = bw - sx; + dx2 = dx1 + w - 1; + } + + if ( sy + h > bh ) { + h = bh - sy; + dy2 = dy1 + h - 1; + } + + if ( w < 1 ) return; // clipped away! + if ( h < 1 ) return; // clipped away! + + } while (reclip); + + // Make sure clipping algorithm works + #ifndef NDEBUG + Assert( w > 0 ); + Assert( h > 0 ); + Assert( w == (dx2-dx1+1) ); + Assert( h == (dy2-dy1+1) ); + Assert( sx >= 0 ); + Assert( sy >= 0 ); + Assert( sx+w <= bw ); + Assert( sy+h <= bh ); + Assert( dx2 >= dx1 ); + Assert( dy2 >= dy1 ); + Assert( (dx1 >= gr_screen.clip_left ) && (dx1 <= gr_screen.clip_right) ); + Assert( (dx2 >= gr_screen.clip_left ) && (dx2 <= gr_screen.clip_right) ); + Assert( (dy1 >= gr_screen.clip_top ) && (dy1 <= gr_screen.clip_bottom) ); + Assert( (dy2 >= gr_screen.clip_top ) && (dy2 <= gr_screen.clip_bottom) ); + #endif + + // We now have dx1,dy1 and dx2,dy2 and sx, sy all set validly within clip regions. + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + + gr_d3d_bitmap_ex_internal(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + +void gr_d3d_bitmap(int x, int y) +{ + int w, h; + + + bm_get_info( gr_screen.current_bitmap, &w, &h, NULL ); + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + int sx=0, sy=0; + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx = gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy = gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) return; + if ( sy < 0 ) return; + if ( sx >= w ) return; + if ( sy >= h ) return; + + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + + gr_d3d_bitmap_ex_internal(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + + + +void gr_d3d_aabitmap_ex_internal(int x,int y,int w,int h,int sx,int sy) +{ + if ( w < 1 ) return; + if ( h < 1 ) return; + + if ( !gr_screen.current_color.is_alphacolor ) return; + + float u_scale, v_scale; + + gr_d3d_set_state( TEXTURE_SOURCE_NO_FILTERING, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE ); + + if ( !gr_tcache_set( gr_screen.current_bitmap, TCACHE_TYPE_AABITMAP, &u_scale, &v_scale ) ) { + // Couldn't set texture + //mprintf(( "GLIDE: Error setting aabitmap texture!\n" )); + return; + } + + LPD3DTLVERTEX src_v; + D3DTLVERTEX d3d_verts[4]; + + float u0, u1, v0, v1; + float x1, x2, y1, y2; + int bw, bh; + + bm_get_info( gr_screen.current_bitmap, &bw, &bh ); + + // Rendition + if ( D3d_rendition_uvs ) { + u0 = u_scale*(i2fl(sx)+0.5f)/i2fl(bw); + v0 = v_scale*(i2fl(sy)+0.5f)/i2fl(bh); + + u1 = u_scale*(i2fl(sx+w)+0.5f)/i2fl(bw); + v1 = v_scale*(i2fl(sy+h)+0.5f)/i2fl(bh); + } else { + u0 = u_scale*i2fl(sx)/i2fl(bw); + v0 = v_scale*i2fl(sy)/i2fl(bh); + + u1 = u_scale*i2fl(sx+w)/i2fl(bw); + v1 = v_scale*i2fl(sy+h)/i2fl(bh); + } + + x1 = i2fl(x+gr_screen.offset_x); + y1 = i2fl(y+gr_screen.offset_y); + x2 = i2fl(x+w+gr_screen.offset_x); + y2 = i2fl(y+h+gr_screen.offset_y); + + src_v = d3d_verts; + + uint color; + + if ( gr_screen.current_color.is_alphacolor ) { + if ( lpDevDesc->dpcTriCaps.dwTextureBlendCaps & D3DPTBLENDCAPS_MODULATEALPHA ) { + color = RGBA_MAKE(gr_screen.current_color.red, gr_screen.current_color.green, gr_screen.current_color.blue,gr_screen.current_color.alpha); + } else { + int r = (gr_screen.current_color.red*gr_screen.current_color.alpha)/255; + int g = (gr_screen.current_color.green*gr_screen.current_color.alpha)/255; + int b = (gr_screen.current_color.blue*gr_screen.current_color.alpha)/255; + + color = RGBA_MAKE(r,g,b, 255 ); + } + } else { + color = RGB_MAKE(gr_screen.current_color.red, gr_screen.current_color.green, gr_screen.current_color.blue); + } + + src_v->sz = 0.99f; + src_v->rhw = 1.0f; + src_v->color = color; + src_v->specular = 0; + src_v->sx = x1; + src_v->sy = y1; + src_v->tu = u0; + src_v->tv = v0; + src_v++; + + src_v->sz = 0.99f; + src_v->rhw = 1.0f; + src_v->color = color; + src_v->specular = 0; + src_v->sx = x2; + src_v->sy = y1; + src_v->tu = u1; + src_v->tv = v0; + src_v++; + + src_v->sz = 0.99f; + src_v->rhw = 1.0f; + src_v->color = color; + src_v->specular = 0; + src_v->sx = x2; + src_v->sy = y2; + src_v->tu = u1; + src_v->tv = v1; + src_v++; + + src_v->sz = 0.99f; + src_v->rhw = 1.0f; + src_v->color = color; + src_v->specular = 0; + src_v->sx = x1; + src_v->sy = y2; + src_v->tu = u0; + src_v->tv = v1; + + d3d_DrawPrimitive(D3DPT_TRIANGLEFAN,D3DVT_TLVERTEX,(LPVOID)d3d_verts,4,NULL); +} + +void gr_d3d_aabitmap_ex(int x,int y,int w,int h,int sx,int sy) +{ + int reclip; + #ifndef NDEBUG + int count = 0; + #endif + + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + + int bw, bh; + bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL ); + + do { + reclip = 0; + #ifndef NDEBUG + if ( count > 1 ) Int3(); + count++; + #endif + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx += gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy += gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) { + dx1 -= sx; + sx = 0; + reclip = 1; + } + + if ( sy < 0 ) { + dy1 -= sy; + sy = 0; + reclip = 1; + } + + w = dx2-dx1+1; + h = dy2-dy1+1; + + if ( sx + w > bw ) { + w = bw - sx; + dx2 = dx1 + w - 1; + } + + if ( sy + h > bh ) { + h = bh - sy; + dy2 = dy1 + h - 1; + } + + if ( w < 1 ) return; // clipped away! + if ( h < 1 ) return; // clipped away! + + } while (reclip); + + // Make sure clipping algorithm works + #ifndef NDEBUG + Assert( w > 0 ); + Assert( h > 0 ); + Assert( w == (dx2-dx1+1) ); + Assert( h == (dy2-dy1+1) ); + Assert( sx >= 0 ); + Assert( sy >= 0 ); + Assert( sx+w <= bw ); + Assert( sy+h <= bh ); + Assert( dx2 >= dx1 ); + Assert( dy2 >= dy1 ); + Assert( (dx1 >= gr_screen.clip_left ) && (dx1 <= gr_screen.clip_right) ); + Assert( (dx2 >= gr_screen.clip_left ) && (dx2 <= gr_screen.clip_right) ); + Assert( (dy1 >= gr_screen.clip_top ) && (dy1 <= gr_screen.clip_bottom) ); + Assert( (dy2 >= gr_screen.clip_top ) && (dy2 <= gr_screen.clip_bottom) ); + #endif + + // We now have dx1,dy1 and dx2,dy2 and sx, sy all set validly within clip regions. + gr_d3d_aabitmap_ex_internal(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + +void gr_d3d_aabitmap(int x, int y) +{ + int w, h; + + bm_get_info( gr_screen.current_bitmap, &w, &h, NULL ); + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + int sx=0, sy=0; + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx = gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy = gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) return; + if ( sy < 0 ) return; + if ( sx >= w ) return; + if ( sy >= h ) return; + + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + gr_aabitmap_ex(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + + +void gr_d3d_string( int sx, int sy, char *s ) +{ + int width, spacing, letter; + int x, y; + + if ( !Current_font ) { + return; + } + + gr_set_bitmap(Current_font->bitmap_id); + + x = sx; + y = sy; + + if (sx==0x8000) { //centered + x = get_centered_x(s); + } else { + x = sx; + } + + spacing = 0; + + while (*s) { + x += spacing; + + while (*s== '\n' ) { + s++; + y += Current_font->h; + if (sx==0x8000) { //centered + x = get_centered_x(s); + } else { + x = sx; + } + } + if (*s == 0 ) break; + + letter = get_char_width(s[0],s[1],&width,&spacing); + s++; + + //not in font, draw as space + if (letter<0) { + continue; + } + + int xd, yd, xc, yc; + int wc, hc; + + // Check if this character is totally clipped + if ( x + width < gr_screen.clip_left ) continue; + if ( y + Current_font->h < gr_screen.clip_top ) continue; + if ( x > gr_screen.clip_right ) continue; + if ( y > gr_screen.clip_bottom ) continue; + + xd = yd = 0; + if ( x < gr_screen.clip_left ) xd = gr_screen.clip_left - x; + if ( y < gr_screen.clip_top ) yd = gr_screen.clip_top - y; + xc = x+xd; + yc = y+yd; + + wc = width - xd; hc = Current_font->h - yd; + if ( xc + wc > gr_screen.clip_right ) wc = gr_screen.clip_right - xc; + if ( yc + hc > gr_screen.clip_bottom ) hc = gr_screen.clip_bottom - yc; + + if ( wc < 1 ) continue; + if ( hc < 1 ) continue; + + font_char *ch; + + ch = &Current_font->char_data[letter]; + + int u = Current_font->bm_u[letter]; + int v = Current_font->bm_v[letter]; + + gr_d3d_aabitmap_ex_internal( xc, yc, wc, hc, u+xd, v+yd ); + } +} + +void gr_d3d_rect(int x,int y,int w,int h) +{ + gr_d3d_rect_internal(x, y, w, h, gr_screen.current_color.red, gr_screen.current_color.green, gr_screen.current_color.blue, gr_screen.current_color.alpha); +} + +void gr_d3d_flash(int r, int g, int b) +{ + CAP(r,0,255); + CAP(g,0,255); + CAP(b,0,255); + + if ( r || g || b ) { + uint color; + if ( lpDevDesc->dpcTriCaps.dwDestBlendCaps & D3DPBLENDCAPS_ONE ) { + gr_d3d_set_state( TEXTURE_SOURCE_NONE, ALPHA_BLEND_ALPHA_ADDITIVE, ZBUFFER_TYPE_NONE ); + color = RGBA_MAKE(r, g, b, 255); + } else { + gr_d3d_set_state( TEXTURE_SOURCE_NONE, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE ); + + int a = (r+g+b)/3; + color = RGBA_MAKE(r,g,b,a); + } + + float x1, x2, y1, y2; + x1 = i2fl(gr_screen.clip_left+gr_screen.offset_x); + y1 = i2fl(gr_screen.clip_top+gr_screen.offset_y); + x2 = i2fl(gr_screen.clip_right+gr_screen.offset_x); + y2 = i2fl(gr_screen.clip_bottom+gr_screen.offset_y); + + LPD3DTLVERTEX src_v; + D3DTLVERTEX d3d_verts[4]; + + src_v = d3d_verts; + + src_v->sz = 0.99f; + src_v->rhw = 1.0f; + src_v->color = color; + src_v->specular = 0; + src_v->sx = x1; + src_v->sy = y1; + src_v++; + + src_v->sz = 0.99f; + src_v->rhw = 1.0f; + src_v->color = color; + src_v->specular = 0; + src_v->sx = x2; + src_v->sy = y1; + src_v++; + + src_v->sz = 0.99f; + src_v->rhw = 1.0f; + src_v->color = color; + src_v->specular = 0; + src_v->sx = x2; + src_v->sy = y2; + src_v++; + + src_v->sz = 0.99f; + src_v->rhw = 1.0f; + src_v->color = color; + src_v->specular = 0; + src_v->sx = x1; + src_v->sy = y2; + + d3d_DrawPrimitive(D3DPT_TRIANGLEFAN,D3DVT_TLVERTEX,(LPVOID)d3d_verts,4,NULL); + } +} + + + +void gr_d3d_create_shader(shader * shade, float r, float g, float b, float c ) +{ + shade->screen_sig = gr_screen.signature; + shade->r = r; + shade->g = g; + shade->b = b; + shade->c = c; +} + +void gr_d3d_set_shader( shader * shade ) +{ + if ( shade ) { + if (shade->screen_sig != gr_screen.signature) { + gr_create_shader( shade, shade->r, shade->g, shade->b, shade->c ); + } + gr_screen.current_shader = *shade; + } else { + gr_create_shader( &gr_screen.current_shader, 0.0f, 0.0f, 0.0f, 0.0f ); + } +} + +void gr_d3d_shade(int x,int y,int w,int h) +{ + int r,g,b,a; + + float shade1 = 1.0f; + float shade2 = 6.0f; + + r = fl2i(gr_screen.current_shader.r*255.0f*shade1); + if ( r < 0 ) r = 0; else if ( r > 255 ) r = 255; + g = fl2i(gr_screen.current_shader.g*255.0f*shade1); + if ( g < 0 ) g = 0; else if ( g > 255 ) g = 255; + b = fl2i(gr_screen.current_shader.b*255.0f*shade1); + if ( b < 0 ) b = 0; else if ( b > 255 ) b = 255; + a = fl2i(gr_screen.current_shader.c*255.0f*shade2); + if ( a < 0 ) a = 0; else if ( a > 255 ) a = 255; + + gr_d3d_rect_internal(x, y, w, h, r, g, b, a); +} + +void gr_d3d_circle( int xc, int yc, int d ) +{ + + int p,x, y, r; + + r = d/2; + p=3-d; + x=0; + y=r; + + // Big clip + if ( (xc+r) < gr_screen.clip_left ) return; + if ( (xc-r) > gr_screen.clip_right ) return; + if ( (yc+r) < gr_screen.clip_top ) return; + if ( (yc-r) > gr_screen.clip_bottom ) return; + + while(xdpcLineCaps.dwSrcBlendCaps & D3DPBLENDCAPS_SRCALPHA) && (lpDevDesc->dpcLineCaps.dwDestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA) ) { + gr_d3d_set_state( TEXTURE_SOURCE_NONE, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE ); + color = RGBA_MAKE(gr_screen.current_color.red, gr_screen.current_color.green, gr_screen.current_color.blue, gr_screen.current_color.alpha ); + } else { + // Matrox MGA-G200 doesn't support alpha-blended lines. + gr_d3d_set_state( TEXTURE_SOURCE_NONE, ALPHA_BLEND_NONE, ZBUFFER_TYPE_NONE ); + + int r = (gr_screen.current_color.red*gr_screen.current_color.alpha)/255; + int g = (gr_screen.current_color.green*gr_screen.current_color.alpha)/255; + int b = (gr_screen.current_color.blue*gr_screen.current_color.alpha)/255; + + color = RGBA_MAKE(r,g,b, 255 ); + } + + INT_CLIPLINE(x1,y1,x2,y2,gr_screen.clip_left,gr_screen.clip_top,gr_screen.clip_right,gr_screen.clip_bottom,return,clipped=1,swapped=1); + + D3DTLVERTEX d3d_verts[2]; + D3DTLVERTEX *a = d3d_verts; + D3DTLVERTEX *b = d3d_verts+1; + + d3d_make_rect(a,b,x1,y1,x2,y2); + + a->color = color; + b->color = color; + + d3d_DrawPrimitive(D3DPT_LINELIST,D3DVT_TLVERTEX,(LPVOID)d3d_verts,2,NULL); +} + +void gr_d3d_aaline(vertex *v1, vertex *v2) +{ + gr_d3d_line( fl2i(v1->sx), fl2i(v1->sy), fl2i(v2->sx), fl2i(v2->sy) ); +} + + +void gr_d3d_gradient(int x1,int y1,int x2,int y2) +{ + int clipped = 0, swapped=0; + + if ( !gr_screen.current_color.is_alphacolor ) { + gr_line( x1, y1, x2, y2 ); + return; + } + + INT_CLIPLINE(x1,y1,x2,y2,gr_screen.clip_left,gr_screen.clip_top,gr_screen.clip_right,gr_screen.clip_bottom,return,clipped=1,swapped=1); + + uint color1, color2; + + // Set up Render State - flat shading - alpha blending + if ( (lpDevDesc->dpcLineCaps.dwSrcBlendCaps & D3DPBLENDCAPS_SRCALPHA) && (lpDevDesc->dpcLineCaps.dwDestBlendCaps & D3DPBLENDCAPS_INVSRCALPHA) ) { + gr_d3d_set_state( TEXTURE_SOURCE_NONE, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE ); + + if (lpDevDesc->dpcLineCaps.dwShadeCaps & D3DPSHADECAPS_ALPHAGOURAUDBLEND ) { + color1 = RGBA_MAKE(gr_screen.current_color.red, gr_screen.current_color.green, gr_screen.current_color.blue, gr_screen.current_color.alpha ); + color2 = RGBA_MAKE(gr_screen.current_color.red, gr_screen.current_color.green, gr_screen.current_color.blue, 0 ); + } else if (lpDevDesc->dpcLineCaps.dwShadeCaps & D3DPSHADECAPS_COLORGOURAUDRGB ) { + color1 = RGBA_MAKE(gr_screen.current_color.red,gr_screen.current_color.green,gr_screen.current_color.blue,gr_screen.current_color.alpha); + color2 = RGBA_MAKE(0,0,0,gr_screen.current_color.alpha); + } else { + color1 = RGBA_MAKE(gr_screen.current_color.red,gr_screen.current_color.green,gr_screen.current_color.blue,gr_screen.current_color.alpha); + color2 = RGBA_MAKE(gr_screen.current_color.red,gr_screen.current_color.green,gr_screen.current_color.blue,gr_screen.current_color.alpha); + } + } else { + // Matrox MGA-G200 doesn't support alpha-blended lines. + gr_d3d_set_state( TEXTURE_SOURCE_NONE, ALPHA_BLEND_NONE, ZBUFFER_TYPE_NONE ); + + int r = (gr_screen.current_color.red*gr_screen.current_color.alpha)/255; + int g = (gr_screen.current_color.green*gr_screen.current_color.alpha)/255; + int b = (gr_screen.current_color.blue*gr_screen.current_color.alpha)/255; + + if (lpDevDesc->dpcLineCaps.dwShadeCaps & D3DPSHADECAPS_COLORGOURAUDRGB ) { + color1 = RGBA_MAKE(r,g,b, 255 ); + color2 = RGBA_MAKE(0,0,0, 255 ); + } else { + color1 = RGBA_MAKE(r,g,b, 255 ); + color2 = RGBA_MAKE(r,g,b, 255 ); + } + } + +// gr_d3d_set_state( TEXTURE_SOURCE_NONE, ALPHA_BLEND_NONE, ZBUFFER_TYPE_NONE ); +// color1 = RGBA_MAKE(gr_screen.current_color.red,gr_screen.current_color.green,gr_screen.current_color.blue,255); +// color2 = RGBA_MAKE(gr_screen.current_color.red,gr_screen.current_color.green,gr_screen.current_color.blue,255); + + D3DTLVERTEX d3d_verts[2]; + D3DTLVERTEX *a = d3d_verts; + D3DTLVERTEX *b = d3d_verts+1; + + d3d_make_rect( a, b, x1, y1, x2, y2 ); + + if ( swapped ) { + b->color = color1; + a->color = color2; + } else { + a->color = color1; + b->color = color2; + } + d3d_DrawPrimitive(D3DPT_LINELIST,D3DVT_TLVERTEX,(LPVOID)d3d_verts,2,NULL); +} + + +void gr_d3d_set_palette(ubyte *new_palette, int restrict_alphacolor) +{ +} + + +// copy from one pixel buffer to another +// +// from pointer to source buffer +// to pointer to dest. buffet +// pixels number of pixels to copy +// fromsize source pixel size +// tosize dest. pixel size + +static int tga_copy_data(char *to, char *from, int pixels, int fromsize, int tosize) +{ + if ( (fromsize == 2) && (tosize == 3) ) { + ushort *src = (ushort *)from; + ubyte *dst = (ubyte *)to; + + int i; + for (i=0; i>Gr_blue.shift)*Gr_blue.scale); + *dst++ = ubyte(((pixel & Gr_green.mask)>>Gr_green.shift)*Gr_green.scale); + *dst++ = ubyte(((pixel & Gr_red.mask)>>Gr_red.shift)*Gr_red.scale); + } + return tosize*pixels; + } else if( (fromsize == 4) && (tosize == 3) ){ + uint *src = (uint *)from; + ubyte *dst = (ubyte *)to; + + int i; + for (i=0; i>Gr_blue.shift)*Gr_blue.scale); + *dst++ = ubyte(((pixel & Gr_green.mask)>>Gr_green.shift)*Gr_green.scale); + *dst++ = ubyte(((pixel & Gr_red.mask)>>Gr_red.shift)*Gr_red.scale); + } + return tosize*pixels; + } else { + Int3(); + return tosize*pixels; + } +} + +// +// tga_pixels_equal -- Test if two pixels are identical +// +// Returns: +// 0 if No Match +// 1 if Match + +static int tga_pixels_equal(char *pix1, char *pix2, int pixbytes) +{ + do { + if ( *pix1++ != *pix2++ ) { + return 0; + } + } while ( --pixbytes > 0 ); + + return 1; +} + + +// tga_compress - Do the Run Length Compression +// +// Usage: +// out Buffer to write it out to +// in Buffer to compress +// bytecount Number of bytes input +// pixsize Number of bytes in input pixel +// outsize Number of bytes in output buffer + +static int tga_compress(char *out, char *in, int bytecount, int pixsize ) +{ + #define outsize 3 + + int pixcount; // number of pixels in the current packet + char *inputpixel=NULL; // current input pixel position + char *matchpixel=NULL; // pixel value to match for a run + char *flagbyte=NULL; // location of last flag byte to set + int rlcount; // current count in r.l. string + int rlthresh; // minimum valid run length + char *copyloc; // location to begin copying at + + // set the threshold -- the minimum valid run length + + #if outsize == 1 + rlthresh = 2; // for 8bpp, require a 2 pixel span before rle'ing + #else + rlthresh = 1; + #endif + + // set the first pixel up + + flagbyte = out; // place to put next flag if run + inputpixel = in; + pixcount = 1; + rlcount = 0; + copyloc = (char *)0; + + // loop till data processing complete + do { + + // if we have accumulated a 128-byte packet, process it + if ( pixcount == 129 ) { + *flagbyte = 127; + + // set the run flag if this is a run + + if ( rlcount >= rlthresh ) { + *flagbyte |= 0x80; + pixcount = 2; + } + + // copy the data into place + ++flagbyte; + flagbyte += tga_copy_data(flagbyte, copyloc, pixcount-1, pixsize, outsize); + pixcount = 1; + + // set up for next packet + continue; + } + + // if zeroth byte, handle as special case + if ( pixcount == 1 ) { + rlcount = 0; + copyloc = inputpixel; /* point to 1st guy in packet */ + matchpixel = inputpixel; /* set pointer to pix to match */ + pixcount = 2; + inputpixel += pixsize; + continue; + } + + // assembling a packet -- look at next pixel + + // current pixel == match pixel? + if ( tga_pixels_equal(inputpixel, matchpixel, outsize) ) { + + // establishing a run of enough length to + // save space by doing it + // -- write the non-run length packet + // -- start run-length packet + + if ( ++rlcount == rlthresh ) { + + // close a non-run packet + + if ( pixcount > (rlcount+1) ) { + // write out length and do not set run flag + + *flagbyte++ = (char)(pixcount - 2 - rlthresh); + + flagbyte += tga_copy_data(flagbyte, copyloc, (pixcount-1-rlcount), pixsize, outsize); + + copyloc = inputpixel; + pixcount = rlcount + 1; + } + } + } else { + + // no match -- either break a run or continue without one + // if a run exists break it: + // write the bytes in the string (outsize+1) + // start the next string + + if ( rlcount >= rlthresh ) { + + *flagbyte++ = (char)(0x80 | rlcount); + flagbyte += tga_copy_data(flagbyte, copyloc, 1, pixsize, outsize); + pixcount = 1; + continue; + } else { + + // not a match and currently not a run + // - save the current pixel + // - reset the run-length flag + rlcount = 0; + matchpixel = inputpixel; + } + } + pixcount++; + inputpixel += pixsize; + } while ( inputpixel < (in + bytecount)); + + // quit this buffer without loosing any data + + if ( --pixcount >= 1 ) { + *flagbyte = (char)(pixcount - 1); + if ( rlcount >= rlthresh ) { + *flagbyte |= 0x80; + pixcount = 1; + } + + // copy the data into place + ++flagbyte; + flagbyte += tga_copy_data(flagbyte, copyloc, pixcount, pixsize, outsize); + } + return(flagbyte-out); +} + +void gr_d3d_print_screen(char *filename) +{ + HRESULT ddrval; + DDSURFACEDESC ddsd; + ubyte outrow[1024*3*4]; + + if ( gr_screen.max_w > 1024 ) { + mprintf(( "Screen too wide for print_screen\n" )); + return; + } + + memset( &ddsd, 0, sizeof( ddsd ) ); + ddsd.dwSize = sizeof( ddsd ); + + ddrval = lpBackBuffer->Lock( NULL, &ddsd, DDLOCK_WAIT, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "Error locking surface for print_screen, %s\n", d3d_error_string(ddrval) )); + } + + ubyte *dptr = (ubyte *)ddsd.lpSurface; + + char tmp[1024]; + + strcpy( tmp, NOX(".\\")); // specify a path mean files goes in root + strcat( tmp, filename ); + strcat( tmp, NOX(".tga")); + + CFILE *f = cfopen(tmp, "wb"); + + // Write the TGA header + cfwrite_ubyte( 0, f ); // IDLength; + cfwrite_ubyte( 0, f ); // ColorMapType; + cfwrite_ubyte( 10, f ); // ImageType; // 2 = 24bpp, uncompressed, 10=24bpp rle compressed + cfwrite_ushort( 0, f ); // CMapStart; + cfwrite_ushort( 0, f ); // CMapLength; + cfwrite_ubyte( 0, f ); // CMapDepth; + cfwrite_ushort( 0, f ); // XOffset; + cfwrite_ushort( 0, f ); // YOffset; + cfwrite_ushort( (ushort)gr_screen.max_w, f ); // Width; + cfwrite_ushort( (ushort)gr_screen.max_h, f ); // Height; + cfwrite_ubyte( 24, f ); //PixelDepth; + cfwrite_ubyte( 0, f ); //ImageDesc; + + // Go through and read our pixels + int i; + for (i=0;iUnlock( NULL ); + +} + + + diff --git a/src/graphics/grd3dtexture.cpp b/src/graphics/grd3dtexture.cpp new file mode 100644 index 0000000..edc9aa2 --- /dev/null +++ b/src/graphics/grd3dtexture.cpp @@ -0,0 +1,1403 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrD3DTexture.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to manage loading textures into VRAM for Direct3D + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 22 9/09/99 8:53p Dave + * Fixed multiplayer degenerate orientation case problem. Make sure warp + * effect never goes lower than LOD 1. + * + * 21 9/05/99 11:19p Dave + * Made d3d texture cache much more safe. Fixed training scoring bug where + * it would backout scores without ever having applied them in the first + * place. + * + * 20 8/16/99 4:04p Dave + * Big honking checkin. + * + * 19 7/29/99 10:47p Dave + * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs. + * + * 18 7/16/99 1:49p Dave + * 8 bit aabitmaps. yay. + * + * 17 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 16 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 15 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 14 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 13 5/05/99 9:02p Dave + * Fixed D3D aabitmap rendering. Spiffed up nebula effect a bit (added + * rotations, tweaked values, made bitmap selection more random). Fixed + * D3D beam weapon clipping problem. Added D3d frame dumping. + * + * 12 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 11 2/05/99 12:52p Dave + * Fixed Glide nondarkening textures. + * + * 10 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 9 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 8 1/15/99 11:29a Neilk + * Fixed D3D screen/texture pixel formatting problem. + * + * 7 1/12/99 12:53a Dave + * More work on beam weapons - made collision detection very efficient - + * collide against all object types properly - made 3 movement types + * smooth. Put in test code to check for possible non-darkening pixels on + * object textures. + * + * 6 12/01/98 6:12p Johnson + * Make sure to page in weapon impact animations as xparent textures. + * + * 5 12/01/98 10:32a Johnson + * Fixed direct3d font problems. Fixed sun bitmap problem. Fixed direct3d + * starfield problem. + * + * 4 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 3 10/09/98 2:57p Dave + * Starting splitting up OS stuff. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 37 6/13/98 3:18p Hoffoss + * NOX()ed out a bunch of strings that shouldn't be translated. + * + * 36 5/23/98 6:12p John + * added code to use registry to force preloading of textures or not. + * + * 35 5/23/98 5:17p John + * added reg key to set texture divider + * + * 34 5/23/98 5:01p John + * made agp preloading happen if >= 6 MB of VRAM. + * + * 33 5/23/98 4:14p John + * Added code to preload textures to video card for AGP. Added in code + * to page in some bitmaps that weren't getting paged in at level start. + * + * 32 5/22/98 10:29p John + * made direct3d textures scale as little as glide. + * + * 31 5/22/98 12:54p John + * forced all cards to use a max of 256 pixel wide textures, but added a + * registry setting to disable it. + * + * 30 5/21/98 9:56p John + * Made Direct3D work with classic alpha-blending only devices, like the + * Virge. Added a texture type XPARENT that fills the alpha in in the + * bitmap for Virge. Added support for Permedia by making making + * additive alphablending be one/one instead of alpha/one, which didn't + * work, and there is no way to tell this from caps. + * + * 29 5/20/98 10:23a John + * put in code to fix an optimized build problem. + * + * 28 5/18/98 8:26p John + * Made cards with only 1bpp alpha fonts work. + * + * 27 5/12/98 7:53p John + * Fixed some 3dfx d3d bugs on allenders, jasen and johnson's computers + * caused by 8:3:3:2 format being used, but not liked by the card. + * + * 26 5/12/98 8:18a John + * Put in code to use a different texture format for alpha textures and + * normal textures. Turned off filtering for aabitmaps. Took out + * destblend=invsrccolor alpha mode that doesn't work on riva128. + * + * 25 5/11/98 10:58a John + * Fixed pilot name cursor bug. Started adding in code for alphachannel + * textures. + * + * 24 5/09/98 12:37p John + * More texture caching + * + * 23 5/09/98 12:16p John + * Even better texture caching. + * + * 22 5/09/98 11:07a John + * Better Direct3D texture caching + * + * 21 5/08/98 5:41p John + * + * 20 5/08/98 5:36p John + * MAde texturing blink white but not crash + * + * 19 5/07/98 3:02p John + * Mpre texture cleanup. You can now reinit d3d without a crash. + * + * 18 5/07/98 11:31a John + * Removed DEMO defines + * + * 17 5/07/98 10:28a John + * Made texture format use 4444. Made fonts use alpha to render. + * + * 16 5/07/98 9:40a John + * Fixed some bitmap transparency issues with Direct3D. + * + * 15 5/06/98 11:21p John + * Fixed a bitmap bug with Direct3D. Started adding new caching code into + * D3D. + * + * 14 5/06/98 8:41p John + * Fixed some font clipping bugs. Moved texture handle set code for d3d + * into the texture module. + * + * 13 5/03/98 10:52a John + * Made D3D sort of work on 3dfx. + * + * 12 5/03/98 10:43a John + * Working on Direct3D. + * + * 11 4/09/98 11:05a John + * Removed all traces of Direct3D out of the demo version of Freespace and + * the launcher. + * + * 10 3/12/98 5:36p John + * Took out any unused shaders. Made shader code take rgbc instead of + * matrix and vector since noone used it like a matrix and it would have + * been impossible to do in hardware. Made Glide implement a basic + * shader for online help. + * + * 9 3/08/98 10:25a John + * Made textures in VRAM reload if they changed + * + * 8 3/07/98 8:29p John + * Put in some Direct3D features. Transparency on bitmaps. Made fonts & + * aabitmaps render nice. + * + * 7 3/06/98 5:39p John + * Started adding in aabitmaps + * + * 6 3/02/98 6:00p John + * Moved MAX_BITMAPS into BmpMan.h so the stuff in the graphics code that + * is dependent on it won't break if it changes. Made ModelCache slots + * be equal to MAX_OBJECTS which is what it is. + * + * 5 2/17/98 7:46p John + * Took out debug code + * + * 4 2/17/98 7:28p John + * Got fonts and texturing working in Direct3D + * + * 3 2/06/98 4:56p John + * Turned off texturing + * + * 2 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 1 2/03/98 9:24p John + * + * $NoKeywords: $ + */ + +#include "grd3d.h" +#include "grd3dinternal.h" +#include "2d.h" +#include "pstypes.h" +#include "bmpman.h" +#include "key.h" +#include "systemvars.h" +#include "osregistry.h" + +#include "multi_log.h" + +typedef struct tcache_slot_d3d { + LPDIRECTDRAWSURFACE vram_texture_surface; + LPDIRECT3DTEXTURE2 vram_texture; + D3DTEXTUREHANDLE texture_handle; + float u_scale, v_scale; + int bitmap_id; + int size; + char used_this_frame; + int time_created; + ushort w, h; + + // sections + tcache_slot_d3d *data_sections[MAX_BMAP_SECTIONS_X][MAX_BMAP_SECTIONS_Y]; + tcache_slot_d3d *parent; +} tcache_slot_d3d; + +static void *Texture_sections = NULL; +tcache_slot_d3d *Textures = NULL; + + +int D3D_texture_sections = 0; +int D3D_texture_ram = 0; +int D3D_frame_count = 0; +int D3D_min_texture_width = 0; +int D3D_max_texture_width = 0; +int D3D_min_texture_height = 0; +int D3D_max_texture_height = 0; +int D3D_square_textures = 0; +int D3D_pow2_textures = 0; +int D3D_textures_in = 0; +int D3D_textures_in_frame = 0; +int D3D_last_bitmap_id = -1; +int D3D_last_detail = -1; +int D3D_last_bitmap_type = -1; +int D3D_last_section_x = -1; +int D3D_last_section_y = -1; + + +int vram_full = 0; + +int d3d_free_texture( tcache_slot_d3d *t ) +{ + int idx, s_idx; + + // Bitmap changed!! + if ( t->bitmap_id > -1 ) { + // if I, or any of my children have been used this frame, bail + if(t->used_this_frame){ + return 0; + } + for(idx=0; idxdata_sections[idx][s_idx] != NULL) && (t->data_sections[idx][s_idx]->used_this_frame)){ + return 0; + } + } + } + + // ok, now we know its legal to free everything safely + if ( t->vram_texture ) { + t->vram_texture->Release(); + t->vram_texture = NULL; + } + + if ( t->vram_texture_surface ) { + t->vram_texture_surface->Release(); + t->vram_texture_surface = NULL; + } + + t->texture_handle = NULL; + + if ( D3D_last_bitmap_id == t->bitmap_id ) { + D3D_last_bitmap_id = -1; + } + + // if this guy has children, free them too, since the children + // actually make up his size + for(idx=0; idxdata_sections[idx][s_idx] != NULL){ + d3d_free_texture(t->data_sections[idx][s_idx]); + } + } + } + + t->bitmap_id = -1; + t->used_this_frame = 0; + D3D_textures_in -= t->size; + } + + return 1; +} + +// we must make sure we never free my parent or any of my siblings!!!!! +int d3d_older_test(tcache_slot_d3d *new_slot, tcache_slot_d3d *test, tcache_slot_d3d *oldest) +{ + if ( (test != new_slot) && (test != new_slot->parent) && (test->bitmap_id > -1) && (!test->used_this_frame)) { + if ( (oldest == NULL) || (test->time_created < oldest->time_created)) { + return 1; + } + } + + // not older + return 0; +} + +int d3d_free_some_texture_ram(tcache_slot_d3d *t, int size) +{ + tcache_slot_d3d *oldest = NULL; + + // Go through all the textures... find the oldest one + // that was not used this frame yet. + int i; + + int goal_size = D3D_textures_in - size*2; + if ( goal_size < 0 ) { + goal_size = 0; + } else if ( goal_size > D3D_texture_ram*3/4 ) { + goal_size = D3D_texture_ram*3/4; + } + + while( D3D_textures_in > goal_size ) { + oldest = NULL; + for( i=0; inum_x; idx++){ + for(s_idx=0; s_idxnum_y; s_idx++){ + // bogus + if(t->data_sections[idx][s_idx] == NULL){ + return 0; + } + + // given a bitmap and a section, return the size (w, h) + bm_get_section_size(bitmap_id, idx, s_idx, &w, &h); + + // same ? + if((t->data_sections[idx][s_idx]->w != w) || (t->data_sections[idx][s_idx]->h != h)){ + return 0; + } + } + } + } + // non-sectioned bitmap + else { + if((t->w != w) || (t->h != h)){ + return 0; + } + } + + // all good + return 1; +} + +// get the final texture size (the one which will get allocated as a surface) +void d3d_tcache_get_adjusted_texture_size(int w_in, int h_in, int *w_out, int *h_out) +{ + int tex_w, tex_h; + + // bogus + if((w_out == NULL) || (h_out == NULL)){ + return; + } + + // starting size + tex_w = w_in; + tex_h = h_in; + + if ( D3D_pow2_textures ) { + int i; + for (i=0; i<16; i++ ) { + if ( (tex_w > (1< (1< D3D_max_texture_width ) { + tex_w = D3D_max_texture_width; + } + + if ( tex_h < D3D_min_texture_height ) { + tex_h = D3D_min_texture_height; + } else if ( tex_h > D3D_max_texture_height ) { + tex_h = D3D_max_texture_height; + } + + if ( D3D_square_textures ) { + int new_size; + // Make the both be equal to larger of the two + new_size = max(tex_w, tex_h); + tex_w = new_size; + tex_h = new_size; + } + + // store the outgoing size + *w_out = tex_w; + *h_out = tex_h; +} + +// data == start of bitmap data +// sx == x offset into bitmap +// sy == y offset into bitmap +// src_w == absolute width of section on source bitmap +// src_h == absolute height of section on source bitmap +// bmap_w == width of source bitmap +// bmap_h == height of source bitmap +// tex_w == width of final texture +// tex_h == height of final texture +int d3d_create_texture_sub(int bitmap_type, int texture_handle, ushort *data, int sx, int sy, int src_w, int src_h, int bmap_w, int bmap_h, int tex_w, int tex_h, tcache_slot_d3d *t, int reload, int fail_on_full) +{ + LPDIRECTDRAWSURFACE sys_texture_surface = NULL; + LPDIRECT3DTEXTURE2 sys_texture = NULL; + int ret_val = 1; + + #ifndef NDEBUG + if ( Show_uploads ) { + if ( reload ) { + mprintf(( "Reloading '%s'\n", bm_get_filename(texture_handle) )); + } else { + mprintf(( "Uploading '%s'\n", bm_get_filename(texture_handle) )); + } + } + #endif + + // bogus + if(t == NULL){ + return 0; + } + + if ( t->used_this_frame ) { + mprintf(( "ARGHH!!! Texture already used this frame! Cannot free it!\n" )); + return 0; + } + if ( !reload ) { + // gah + if(!d3d_free_texture(t)){ + return 0; + } + } + + DDSURFACEDESC ddsd; + HRESULT ddrval; + DWORD dwHeight, dwWidth; + int i,j; + ushort *bmp_data; + + DDPIXELFORMAT *surface_desc; + + switch( bitmap_type ) { + case TCACHE_TYPE_AABITMAP: + surface_desc = &AlphaTextureFormat; + break; + + case TCACHE_TYPE_XPARENT: + Int3(); + + default: + surface_desc = &NonAlphaTextureFormat; + } + + // get final texture size + d3d_tcache_get_adjusted_texture_size(tex_w, tex_h, &tex_w, &tex_h); + + if ( (tex_w < 1) || (tex_h < 1) ) { + mprintf(("Bitmap is to small at %dx%d.\n", tex_w, tex_h )); + return 0; + } + + if ( bitmap_type == TCACHE_TYPE_AABITMAP ) { + t->u_scale = (float)bmap_w / (float)tex_w; + t->v_scale = (float)bmap_h / (float)tex_h; + } else if(bitmap_type == TCACHE_TYPE_BITMAP_SECTION){ + t->u_scale = (float)src_w / (float)tex_w; + t->v_scale = (float)src_h / (float)tex_h; + } else { + t->u_scale = 1.0f; + t->v_scale = 1.0f; + } + + dwHeight = tex_h; + dwWidth = tex_w; + bmp_data = (ushort *)data; + ubyte *bmp_data_byte = (ubyte*)data; + + // Create a surface in system memory and load texture into it. + + // Create a surface of the given format using the dimensions of the bitmap + memset(&ddsd, 0, sizeof(DDSURFACEDESC)); + + ddsd.dwSize = sizeof(DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; + ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_SYSTEMMEMORY; + ddsd.dwHeight = dwHeight; + ddsd.dwWidth = dwWidth; + ddsd.ddpfPixelFormat = *surface_desc; + + sys_texture_surface = NULL; + ddrval = lpDD->CreateSurface(&ddsd, &sys_texture_surface, NULL); + if ( (ddrval != DD_OK) || (sys_texture_surface == NULL) ) { + mprintf(("CreateSurface for texture failed (loadtex), w=%d, h=%d, %s\n", tex_w, tex_h, d3d_error_string(ddrval) )); + mprintf(( "Texture RAM = %d KB\n", D3D_textures_in / 1024 )); + // bm_unlock(bitmap_handle); + return 0; + } + + // Lock the surface so it can be filled with the bitmap data + memset(&ddsd, 0, sizeof(DDSURFACEDESC)); + ddsd.dwSize = sizeof(DDSURFACEDESC); + ddrval = sys_texture_surface->Lock(NULL, &ddsd, 0, NULL); + if (ddrval != DD_OK) { + sys_texture_surface->Release(); + mprintf(("Lock failed while loading surface (loadtex).\n" )); + return 0; + } + + Assert( surface_desc->dwRGBBitCount == 16 ); + + // Each RGB bit count requires different pointers + ushort *lpSP; + ushort xlat[256]; + int r, g, b, a; + + switch( bitmap_type ) { + case TCACHE_TYPE_AABITMAP: + // setup convenient translation table + for (i=0; i<16; i++ ) { + r = 255; + g = 255; + b = 255; + a = Gr_gamma_lookup[(i*255)/15]; + r /= Gr_ta_red.scale; + g /= Gr_ta_green.scale; + b /= Gr_ta_blue.scale; + a /= Gr_ta_alpha.scale; + xlat[i] = unsigned short(((a<Unlock(NULL); + + sys_texture = NULL; + ddrval = sys_texture_surface->QueryInterface(IID_IDirect3DTexture2, (LPVOID *)&sys_texture); + if ( (ddrval != DD_OK) || (sys_texture == NULL) ) { + mprintf(( "Getting sys surface's texture failed!\n" )); + + // bad return value + ret_val = 0; + + goto FreeSurfacesAndExit; + } + +RetryLoad: + + if ( !reload ) { + // Create a surface of the given format using the dimensions of the bitmap + memset(&ddsd, 0, sizeof(DDSURFACEDESC)); + + ddsd.dwSize = sizeof(DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT; + ddsd.ddsCaps.dwCaps = DDSCAPS_TEXTURE | DDSCAPS_ALLOCONLOAD | DDSCAPS_VIDEOMEMORY; + //| DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM | DDSCAPS_ALLOCONLOAD; + ddsd.dwHeight = dwHeight; + ddsd.dwWidth = dwWidth; + ddsd.ddpfPixelFormat = *surface_desc; + + t->vram_texture_surface = NULL; + ddrval = lpDD->CreateSurface(&ddsd, &t->vram_texture_surface, NULL); + if ( (ddrval != DD_OK) || (t->vram_texture_surface == NULL) ) { + t->vram_texture = NULL; + t->vram_texture_surface = NULL; + t->texture_handle = NULL; + + if ( ddrval==DDERR_OUTOFVIDEOMEMORY ) { + mprintf(("Out of VRAM (w=%d, h=%d, used=%d KB)\n", tex_w, tex_h, D3D_textures_in / 1024 )); + if ( fail_on_full ) { + // bad return value + ret_val = 0; + + goto FreeSurfacesAndExit; + } + if ( d3d_free_some_texture_ram(t,dwHeight*dwWidth*2)) { + goto RetryLoad; + } + } else { + mprintf(("CreateSurface for VRAM texture failed, w=%d, h=%d\n%s\n", tex_w, tex_h, d3d_error_string(ddrval) )); + mprintf(( "Texture RAM = %d KB\n", D3D_textures_in / 1024 )); + } + vram_full = 1; + + // bad return value + ret_val = 0; + + goto FreeSurfacesAndExit; + //goto RetryLoad; + } + + t->vram_texture = NULL; + ddrval = t->vram_texture_surface->QueryInterface(IID_IDirect3DTexture2, (LPVOID *)&t->vram_texture); + if ( (ddrval != DD_OK) || (t->vram_texture == NULL) ) { + mprintf(( "GR_D3D_INIT: TextureSurface->QueryInterface failed.\n" )); + vram_full = 1; + + // bad return value + ret_val = 0; + + goto FreeSurfacesAndExit; + } + + // char *name = bm_get_filename(bitmap_handle); + // mprintf(( "Uploading '%s'\n", name )); + t->texture_handle = NULL; + ddrval = t->vram_texture->GetHandle(lpD3DDevice, &t->texture_handle ); + if ( (ddrval != DD_OK) || (t->texture_handle == NULL) ) { + mprintf(( "GR_D3D_INIT: Texture->GetHandle failed.\n" )); + t->texture_handle = NULL; + vram_full = 1; + + // bad return value + ret_val = 0; + + goto FreeSurfacesAndExit; + } + } + + // argh. this texture appears to be bogus. lets free it + if(t->vram_texture == NULL){ + d3d_free_texture(t); + + // bad + ret_val = 0; + + goto FreeSurfacesAndExit; + } + + ddrval = t->vram_texture->Load( sys_texture ); + if ( ddrval != DD_OK ) { + mprintf(("VRAM Load failed, w=%d, h=%d, %s\n", tex_w, tex_h, d3d_error_string(ddrval) )); + mprintf(( "Texture RAM = %d KB\n", D3D_textures_in / 1024 )); + vram_full = 1; + + // bad return value + ret_val = 0; + + goto FreeSurfacesAndExit; + } + + t->bitmap_id = texture_handle; + t->time_created = D3D_frame_count; + t->used_this_frame = 0; + t->size = tex_w * tex_h * 2; + t->w = (ushort)tex_w; + t->h = (ushort)tex_h; + D3D_textures_in_frame += t->size; + if ( !reload ) { + D3D_textures_in += t->size; + } + +FreeSurfacesAndExit: + if ( sys_texture ) { + sys_texture->Release(); + sys_texture = NULL; + } + + if ( sys_texture_surface ) { + sys_texture_surface->Release(); + sys_texture_surface = NULL; + } + + // hopefully this is 1 :) + return ret_val; +} + +int d3d_create_texture(int bitmap_handle, int bitmap_type, tcache_slot_d3d *tslot, int fail_on_full) +{ + ubyte flags; + bitmap *bmp; + int final_w, final_h; + ubyte bpp = 16; + int reload = 0; + + // setup texture/bitmap flags + flags = 0; + switch(bitmap_type){ + case TCACHE_TYPE_AABITMAP: + flags |= BMP_AABITMAP; + bpp = 8; + break; + case TCACHE_TYPE_NORMAL: + flags |= BMP_TEX_OTHER; + case TCACHE_TYPE_XPARENT: + flags |= BMP_TEX_XPARENT; + break; + case TCACHE_TYPE_NONDARKENING: + Int3(); + flags |= BMP_TEX_NONDARK; + break; + } + + // lock the bitmap into the proper format + bmp = bm_lock(bitmap_handle, bpp, flags); + if ( bmp == NULL ) { + mprintf(("Couldn't lock bitmap %d.\n", bitmap_handle )); + return 0; + } + + int max_w = bmp->w; + int max_h = bmp->h; + + if ( bitmap_type != TCACHE_TYPE_AABITMAP ) { + max_w /= D3D_texture_divider; + max_h /= D3D_texture_divider; + + // Detail.debris_culling goes from 0 to 4. + max_w /= 16 >> Detail.hardware_textures; + max_h /= 16 >> Detail.hardware_textures; + } + + // get final texture size as it will be allocated as a DD surface + d3d_tcache_get_adjusted_texture_size(max_w, max_h, &final_w, &final_h); + + // if this tcache slot has no bitmap + if ( tslot->bitmap_id < 0) { + reload = 0; + } + // different bitmap altogether - determine if the new one can use the old one's slot + else if (tslot->bitmap_id != bitmap_handle) { + if((final_w == tslot->w) && (final_h == tslot->h)){ + reload = 1; + + ml_printf("Reloading texture %d\n", bitmap_handle); + } else { + reload = 0; + } + } + + // call the helper + int ret_val = d3d_create_texture_sub(bitmap_type, bitmap_handle, (ushort*)bmp->data, 0, 0, bmp->w, bmp->h, bmp->w, bmp->h, max_w, max_h, tslot, reload, fail_on_full); + + // unlock the bitmap + bm_unlock(bitmap_handle); + + return ret_val; +} + +int d3d_create_texture_sectioned(int bitmap_handle, int bitmap_type, tcache_slot_d3d *tslot, int sx, int sy, int fail_on_full) +{ + ubyte flags; + bitmap *bmp; + int final_w, final_h; + int section_x, section_y; + int reload = 0; + + // setup texture/bitmap flags + Assert(bitmap_type == TCACHE_TYPE_BITMAP_SECTION); + if(bitmap_type != TCACHE_TYPE_BITMAP_SECTION){ + bitmap_type = TCACHE_TYPE_BITMAP_SECTION; + } + flags = BMP_TEX_XPARENT; + + // lock the bitmap in the proper format + bmp = bm_lock(bitmap_handle, 16, flags); + if ( bmp == NULL ) { + mprintf(("Couldn't lock bitmap %d.\n", bitmap_handle )); + return 0; + } + + // determine the width and height of this section + bm_get_section_size(bitmap_handle, sx, sy, §ion_x, §ion_y); + + // get final texture size as it will be allocated as a DD surface + d3d_tcache_get_adjusted_texture_size(section_x, section_y, &final_w, &final_h); + + // if this tcache slot has no bitmap + if ( tslot->bitmap_id < 0) { + reload = 0; + } + // different bitmap altogether - determine if the new one can use the old one's slot + else if (tslot->bitmap_id != bitmap_handle) { + if((final_w == tslot->w) && (final_h == tslot->h)){ + reload = 1; + } else { + reload = 0; + } + } + + // call the helper + int ret_val = d3d_create_texture_sub(bitmap_type, bitmap_handle, (ushort*)bmp->data, bmp->sections.sx[sx], bmp->sections.sy[sy], section_x, section_y, bmp->w, bmp->h, section_x, section_y, tslot, reload, fail_on_full); + + // unlock the bitmap + bm_unlock(bitmap_handle); + + return ret_val; +} + +extern int bm_get_cache_slot( int bitmap_id, int separate_ani_frames ); +int d3d_tcache_set(int bitmap_id, int bitmap_type, float *u_scale, float *v_scale, int fail_on_full, int sx, int sy, int force ) +{ + bitmap *bmp = NULL; + int idx, s_idx; + int ret_val = 1; + + if ( bitmap_id < 0 ) { + D3D_last_bitmap_id = -1; + return 0; + } + + if ( D3D_last_detail != Detail.hardware_textures ) { + D3D_last_detail = Detail.hardware_textures; + d3d_tcache_flush(); + } + + //mprintf(( "Setting texture %d\n", bitmap_handle )); + if ( vram_full ) { + return 0; + } + + int n = bm_get_cache_slot( bitmap_id, 1 ); + tcache_slot_d3d * t = &Textures[n]; + + if ( (D3D_last_bitmap_id == bitmap_id) && (D3D_last_bitmap_type==bitmap_type) && (t->bitmap_id == bitmap_id) && (D3D_last_section_x == sx) && (D3D_last_section_y == sy)) { + t->used_this_frame++; + + // mark all children as used + if(D3D_texture_sections){ + for(idx=0; idxdata_sections[idx][s_idx] != NULL){ + t->data_sections[idx][s_idx]->used_this_frame++; + } + } + } + } + + *u_scale = t->u_scale; + *v_scale = t->v_scale; + return 1; + } + + // if this is a sectioned bitmap + if(bitmap_type == TCACHE_TYPE_BITMAP_SECTION){ + Assert((sx >= 0) && (sy >= 0) && (sx < MAX_BMAP_SECTIONS_X) && (sy < MAX_BMAP_SECTIONS_Y)); + if(!((sx >= 0) && (sy >= 0) && (sx < MAX_BMAP_SECTIONS_X) && (sy < MAX_BMAP_SECTIONS_Y))){ + return 0; + } + + ret_val = 1; + + // if the texture sections haven't been created yet + if((t->bitmap_id < 0) || (t->bitmap_id != bitmap_id)){ + // lock the bitmap in the proper format + bmp = bm_lock(bitmap_id, 16, BMP_TEX_XPARENT); + bm_unlock(bitmap_id); + + // now lets do something for each texture + for(idx=0; idxsections.num_x; idx++){ + for(s_idx=0; s_idxsections.num_y; s_idx++){ + // hmm. i'd rather we didn't have to do it this way... + if(!d3d_create_texture_sectioned(bitmap_id, bitmap_type, t->data_sections[idx][s_idx], idx, s_idx, fail_on_full)){ + ret_val = 0; + } + + // not used this frame + t->data_sections[idx][s_idx]->used_this_frame = 0; + } + } + + // zero out pretty much everything in the parent struct since he's just the root + t->bitmap_id = bitmap_id; + t->texture_handle = NULL; + t->time_created = t->data_sections[sx][sy]->time_created; + t->used_this_frame = 0; + t->vram_texture = NULL; + t->vram_texture_surface = NULL; + } + + // argh. we failed to upload. free anything we can + if(!ret_val){ + d3d_free_texture(t); + } + // swap in the texture we want + else { + t = t->data_sections[sx][sy]; + } + } + // all other "normal" textures + else if((bitmap_id < 0) || (bitmap_id != t->bitmap_id)){ + ret_val = d3d_create_texture( bitmap_id, bitmap_type, t, fail_on_full ); + } + + // everything went ok + if(ret_val && (t->texture_handle != NULL) && !vram_full){ + *u_scale = t->u_scale; + *v_scale = t->v_scale; + + d3d_SetRenderState(D3DRENDERSTATE_TEXTUREHANDLE, t->texture_handle ); + + D3D_last_bitmap_id = t->bitmap_id; + D3D_last_bitmap_type = bitmap_type; + D3D_last_section_x = sx; + D3D_last_section_y = sy; + + t->used_this_frame++; + } + // gah + else { + return 0; + } + + return 1; +} + +int D3D_should_preload = 0; + +void d3d_tcache_init(int use_sections) +{ + int i, idx, s_idx; + + D3D_should_preload = 0; + + { + DDSCAPS ddsCaps; + DWORD dwFree, dwTotal; + + memset(&ddsCaps,0,sizeof(ddsCaps) ); + ddsCaps.dwCaps = DDSCAPS_TEXTURE; + HRESULT ddrval = lpDD->GetAvailableVidMem(&ddsCaps, &dwTotal, &dwFree); + if ( ddrval != DD_OK ) { + mprintf(( "GR_D3D_INIT: GetAvailableVidMem failed.\n" )); + dwFree = 0; +// goto D3DError; + } + + D3D_texture_ram = dwFree; + + uint tmp_pl = os_config_read_uint( NULL, NOX("D3DPreloadTextures"), 255 ); + + if ( tmp_pl == 0 ) { + D3D_should_preload = 0; + } else if ( tmp_pl == 1 ) { + D3D_should_preload = 1; + } else { + if ( D3D_texture_ram >= 6*1024*1024 ) { + D3D_should_preload = 1; + } else { + D3D_should_preload = 0; + } + } + + /* + int megs = dwFree / (1024*1024); + uint tmp_val = os_config_read_uint( NULL, NOX("D3DTextureDivider"), 255 ); + + if ( tmp_val == 0 ) { + // auto set + if ( megs <= 4 ) { + D3D_texture_divider = 4; + } else if ( megs <=8 ) { + D3D_texture_divider = 3; + } else if ( megs <=16 ) { + D3D_texture_divider = 2; + } else { + D3D_texture_divider = 1; + } + } else if ( tmp_val < 5 ) { + D3D_texture_divider = tmp_val; + } else { + // force to 4 + D3D_texture_divider = 4; + } + */ + + // setup texture divider + uint tmp_val = os_config_read_uint( NULL, NOX("D3DFast"), 1 ); + D3D_texture_divider = 1; + if(tmp_val){ + D3D_texture_divider = 4; + } + +#ifndef NDEBUG + int megs = dwFree / (1024*1024); + mprintf(( "TEXTURE RAM: %d bytes (%d MB) available, size divisor = %d\n", dwFree, megs, D3D_texture_divider )); +#endif + } + + + D3D_min_texture_width = (int)lpDevDesc->dwMinTextureWidth; + D3D_max_texture_width = (int)lpDevDesc->dwMaxTextureWidth; + D3D_min_texture_height = (int)lpDevDesc->dwMinTextureHeight; + D3D_max_texture_height = (int)lpDevDesc->dwMaxTextureHeight; + + // The following checks are needed because the PowerVR reports + // it's texture caps as 0,0 up to 0,0. + if ( D3D_min_texture_width < 16 ) { + D3D_min_texture_width = 16; + } + if ( D3D_min_texture_height < 16 ) { + D3D_min_texture_height = 16; + } + if ( D3D_max_texture_width < 16 ) { + mprintf(( "Driver claims to have a max texture width of %d. Bashing to 256.\n(Is this a PowerVR?)\n", D3D_max_texture_width )); + D3D_max_texture_width = 256; // Can we assume 256? + } + + if ( D3D_max_texture_height < 16 ) { + mprintf(( "Driver claims to have a max texture height of %d. Bashing to 256.\n(Is this a PowerVR?)\n", D3D_max_texture_height )); + D3D_max_texture_height = 256; // Can we assume 256? + } + + if (lpDevDesc->dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_SQUAREONLY) { + mprintf(( "Darn. Driver needs square textures.\n" )); + D3D_square_textures = 1; + } else { + mprintf(( "Woohoo! Driver doesn't need square textures!\n" )); + D3D_square_textures = 0; + } + + if ( lpDevDesc->dpcTriCaps.dwTextureCaps & D3DPTEXTURECAPS_POW2 ) { + mprintf(( "Textures must be power of 2.\n" )); + D3D_pow2_textures = 1; + } else { + mprintf(( "Textures can be any size.\n" )); + D3D_pow2_textures = 0; + } + + + if ( !(DD_driver_caps.dwCaps2 & DDCAPS2_WIDESURFACES) ) { + if ( D3D_max_texture_width > gr_screen.max_w ) { + D3D_max_texture_width = gr_screen.max_w; + + if ( D3D_pow2_textures ) { + for (i=0; i<16; i++ ) { + if ( (D3D_max_texture_width>= (1< D3D_max_texture_width ) { + D3D_max_texture_height = D3D_max_texture_width; + } + + mprintf(( "Doesn't support wide surfaces. Bashing max down to %d\n", D3D_max_texture_width )); + } + } + + if ( !os_config_read_uint( NULL, NOX("D3DUseLargeTextures"), 0 )) { + mprintf(( "No large texture flag specified, so using max of 256\n" )); + if ( D3D_max_texture_width > 256 ) { + D3D_max_texture_width = 256; + } + + if ( D3D_max_texture_height > 256 ) { + D3D_max_texture_height = 256; + } + } else { + mprintf(( "Large textures enabled!\n" )); + } + + Textures = (tcache_slot_d3d *)malloc(MAX_BITMAPS*sizeof(tcache_slot_d3d)); + if ( !Textures ) { + exit(1); + } + + if(use_sections){ + Texture_sections = (tcache_slot_d3d*)malloc(MAX_BITMAPS * MAX_BMAP_SECTIONS_X * MAX_BMAP_SECTIONS_Y * sizeof(tcache_slot_d3d)); + if(!Texture_sections){ + exit(1); + } + memset(Texture_sections, 0, MAX_BITMAPS * MAX_BMAP_SECTIONS_X * MAX_BMAP_SECTIONS_Y * sizeof(tcache_slot_d3d)); + } + + // Init the texture structures + int section_count = 0; + for( i=0; iparent = &Textures[i]; + + Textures[i].data_sections[idx][s_idx]->vram_texture = NULL; + Textures[i].data_sections[idx][s_idx]->vram_texture_surface = NULL; + Textures[i].data_sections[idx][s_idx]->texture_handle = NULL; + + Textures[i].data_sections[idx][s_idx]->bitmap_id = -1; + Textures[i].data_sections[idx][s_idx]->size = 0; + Textures[i].data_sections[idx][s_idx]->used_this_frame = 0; + } + } + } else { + for(idx=0; idxused_this_frame = 0; + } + } + } + } + } + } + + if ( vram_full ) { + d3d_tcache_flush(); + vram_full = 0; + } +} + + +void gr_d3d_preload_init() +{ + if ( gr_screen.mode != GR_DIRECT3D ) { + return; + } + d3d_tcache_flush(); +} + +int gr_d3d_preload(int bitmap_num, int is_aabitmap) +{ + if ( gr_screen.mode != GR_DIRECT3D ) { + return 0; + } + + if ( !D3D_should_preload ) { + return 0; + } + + float u_scale, v_scale; + + int retval; + + if ( is_aabitmap ) { + retval = gr_tcache_set(bitmap_num, TCACHE_TYPE_AABITMAP, &u_scale, &v_scale, 1 ); + } else { + retval = gr_tcache_set(bitmap_num, TCACHE_TYPE_NORMAL, &u_scale, &v_scale, 1 ); + } + + if ( !retval ) { + mprintf(("Texture upload failed!\n" )); + } + + return retval; +} + +// call this to safely fill in the texture shift and scale values for the specified texture type (Gr_t_*) +void gr_d3d_get_tex_format(int alpha) +{ + /* + DDPIXELFORMAT *surface_desc; + int s; + // RGB decoder + unsigned long m; + + // get the proper texture format + if(alpha){ + surface_desc = &AlphaTextureFormat; + } else { + surface_desc = &NonAlphaTextureFormat; + } + + // Determine the red, green and blue masks' shift and scale. + for (s = 0, m = surface_desc->dwRBitMask; !(m & 1); s++, m >>= 1); + Gr_t_red_shift = s; + Gr_t_red_scale = 255 / (surface_desc->dwRBitMask >> s); + for (s = 0, m = surface_desc->dwGBitMask; !(m & 1); s++, m >>= 1); + Gr_t_green_shift = s; + Gr_t_green_scale = 255 / (surface_desc->dwGBitMask >> s); + for (s = 0, m = surface_desc->dwBBitMask; !(m & 1); s++, m >>= 1); + Gr_t_blue_shift = s; + Gr_t_blue_scale = 255 / (surface_desc->dwBBitMask >> s); + + if ( surface_desc->dwFlags & DDPF_ALPHAPIXELS ) { + for (s = 0, m = surface_desc->dwRGBAlphaBitMask; !(m & 1); s++, m >>= 1); + Gr_t_alpha_shift = s; + Gr_t_alpha_scale = 255 / (surface_desc->dwRGBAlphaBitMask >> s); + } else { + Gr_t_alpha_shift = 0; + Gr_t_alpha_scale = 256; + } + */ +} \ No newline at end of file diff --git a/src/graphics/grdirectdraw.cpp b/src/graphics/grdirectdraw.cpp new file mode 100644 index 0000000..34e2204 --- /dev/null +++ b/src/graphics/grdirectdraw.cpp @@ -0,0 +1,1286 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrDirectDraw.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for software 8-bpp rendering using DirectDraw + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 9 7/14/99 9:42a Dave + * Put in clear_color debug function. Put in base for 3dnow stuff / P3 + * stuff + * + * 8 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 7 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 6 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 5 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 4 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 17 5/22/98 10:28p John + * Made software movies not click your monitor when switching to 16-bpp. + * Total hack around my restrictive code, but at this point... + * + * 16 5/20/98 9:45p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 15 5/17/98 5:03p John + * Fixed some bugs that make the taskbar interfere with the DEBUG-only + * mouse cursor. + * + * 14 5/16/98 1:18p John + * Made softtware DirectDraw reset palette after Alt+TAB. + * + * 13 5/15/98 9:34p John + * Removed the initial ugly little cursor part that drew right at program + * start. + * + * 12 5/14/98 5:42p John + * Revamped the whole window position/mouse code for the graphics windows. + * + * 11 5/12/98 8:15a John + * Made dd code not print out rgb surface info. + * + * 10 5/07/98 6:58p Hoffoss + * Made changes to mouse code to fix a number of problems. + * + * 9 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 8 4/23/98 8:24a John + * Changed the way palette effect works so that: + * 1. If gr_flash isn't called this frame, screen shows no flash. + * 2. With hardware, only 3d portion of screen gets flashed. + * + * 7 4/21/98 5:22p John + * Fixed all the &^#@$ cursor bugs with popups. For Glide, had problem + * with mouse restoring assuming back buffer was same buffer last frame, + * for software, problems with half drawn new frames, then mouse got + * restored on top of that with old data. + * + * 6 4/17/98 3:56p Mike + * Fix palette trashing on MK's computer when he Alt-Tabs out of FreeSpace + * in software. At John's suggestion. + * + * 5 4/14/98 12:15p John + * Made 16-bpp movies work. + * + * 4 4/13/98 8:08p John + * Made surface restoration also restore the palette. + * + * 3 4/09/98 6:56p John + * Put in better code to restore surfaces when restoring them. Worth a + * try for an Interplay QA bug I can't find. + * + * 2 4/09/98 11:05a John + * Removed all traces of Direct3D out of the demo version of Freespace and + * the launcher. + * + * 1 3/25/98 8:07p John + * Split software renderer into Win32 and DirectX + * + * $NoKeywords: $ + */ + +#include +#include +#include + +#include "vddraw.h" + +#include "osapi.h" +#include "2d.h" +#include "bmpman.h" +#include "key.h" +#include "floating.h" +#include "palman.h" +#include "grsoft.h" +#include "grinternal.h" + +// Headers for 2d functions +#include "pixel.h" +#include "line.h" +#include "scaler.h" +#include "tmapper.h" +#include "circle.h" +#include "shade.h" +#include "rect.h" +#include "gradient.h" +#include "pcxutils.h" +#include "osapi.h" +#include "mouse.h" +#include "font.h" +#include "timer.h" +#include "colors.h" +#include "bitblt.h" +#include "grzbuffer.h" + +static LPDIRECTDRAW lpDD = NULL; +static LPDIRECTDRAWSURFACE lpBackBuffer = NULL; +static LPDIRECTDRAWSURFACE lpFrontBuffer = NULL; +static LPDIRECTDRAWCLIPPER lpClipper = NULL; +static LPDIRECTDRAWPALETTE lpPalette = NULL; + +void gr_directdraw_unlock(); +void gr_directdraw_set_palette_internal( ubyte * new_pal ); + +void directdraw_clear_all_vram() +{ +#ifndef HARDWARE_ONLY + DDSURFACEDESC ddsd; + HRESULT ddrval; + LPDIRECTDRAWSURFACE buffer[16]; + + int i; + int num_buffers = 0; + for (i=0; i<16; i++ ) { + memset( &ddsd, 0, sizeof( ddsd ) ); + + ddsd.dwSize = sizeof( ddsd ); + ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; + ddsd.dwWidth = gr_screen.max_w; + ddsd.dwHeight = gr_screen.max_h; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; + + // create the back buffer + ddrval = lpDD->CreateSurface( &ddsd, &buffer[i], NULL ); + if ( ddrval == DD_OK ) { + // Clear the surface + DDBLTFX ddBltFx; + + memset(&ddBltFx, 0, sizeof(DDBLTFX)); + ddBltFx.dwSize = sizeof(DDBLTFX); + ddBltFx.dwFillColor = 0; + + buffer[i]->Blt(NULL, NULL, NULL, DDBLT_COLORFILL, &ddBltFx); + + } else { + num_buffers = i; + buffer[i] = NULL; + break; + } + } + + for (i=0; iRelease(); + } +#else + Int3(); +#endif +} + +LPDIRECTDRAW gr_directdraw_start_mve() +{ +#ifndef HARDWARE_ONLY + ubyte tmp_pal[768]; + memset( tmp_pal, 0, 768 ); + + gr_directdraw_set_palette_internal(tmp_pal); + + // clear both buffers + gr_reset_clip(); + gr_clear(); + gr_flip(); + + gr_reset_clip(); + gr_clear(); + gr_flip(); + + gr_directdraw_unlock(); + + // Clear all of VRAM... + directdraw_clear_all_vram(); + + if ( lpPalette ) { + lpPalette->Release(); + lpPalette = NULL; + } + + if (lpFrontBuffer) { + lpFrontBuffer->Release(); + lpFrontBuffer = NULL; + } + + + return lpDD; +#else + Int3(); + return 0; +#endif +} + + +void gr_directdraw_stop_mve() +{ +#ifndef HARDWARE_ONLY + DDSURFACEDESC ddsd; + HRESULT ddrval; + + memset( &ddsd, 0, sizeof( ddsd ) ); + + ddsd.dwSize = sizeof( ddsd ); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + ddrval = lpDD->CreateSurface( &ddsd, &lpFrontBuffer, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: CreateSurface (Front) failed.\n" )); + lpDD->Release(); + exit(1); + } + + int i; + PALETTEENTRY pe[256]; + for (i=0; i<256; i++ ) { + pe[i].peFlags = PC_NOCOLLAPSE|PC_RESERVED; + pe[i].peRed = gr_palette[i*3+0]; + pe[i].peGreen = gr_palette[i*3+1]; + pe[i].peBlue = gr_palette[i*3+2]; + } + + ddrval = lpDD->CreatePalette( DDPCAPS_8BIT | DDPCAPS_ALLOW256, pe, &lpPalette, NULL); + if (ddrval != DD_OK) { + mprintf(( "Error creating palette\n" )); + } + + ddrval = lpFrontBuffer->SetPalette( lpPalette ); + if (ddrval != DD_OK) { + mprintf(( "Error setting palette in gr_directdraw_set_palette\n" )); + } + + gr_reset_clip(); + gr_clear(); + gr_flip(); +#else + Int3(); +#endif +} + +//#define DD_FLIP 1 + +static int Locked = 0; + +uint gr_directdraw_lock() +{ +#ifndef HARDWARE_ONLY + if ( !Locked ) { + + HRESULT ddrval; + DDSURFACEDESC ddsd; + + memset( &ddsd, 0, sizeof( ddsd ) ); + ddsd.dwSize = sizeof( ddsd ); + + ddrval = lpBackBuffer->Lock( NULL, &ddsd, DDLOCK_WAIT, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "Error locking surface in gr_d3d_lock\n" )); + gr_screen.offscreen_buffer_base = NULL; //(void *)fake_vram; + gr_screen.rowsize = 0; + gr_screen.offscreen_buffer = gr_screen.offscreen_buffer_base; + } else { + gr_screen.offscreen_buffer_base = (void *)ddsd.lpSurface; + gr_screen.rowsize = ddsd.lPitch; + gr_screen.offscreen_buffer = gr_screen.offscreen_buffer_base; + Locked = 1; + } + + } + return 1; +#else + Int3(); + return 0; +#endif +} + +void gr_directdraw_unlock() +{ +#ifndef HARDWARE_ONLY + if ( Locked ) { + // Unlock the back buffer + lpBackBuffer->Unlock( NULL ); + Locked = 0; + } +#else + Int3(); +#endif +} + +void gr_directdraw_unlock_fake() +{ +} + + +static int Palette_flashed = 0; +static int Palette_flashed_last_frame = 0; + + +static volatile int Gr_dd_activated = 0; + +void gr_dd_activate(int active) +{ +#ifndef HARDWARE_ONLY + if ( active ) { + Gr_dd_activated++; + } +#else + Int3(); +#endif +} + + +void gr_directdraw_flip() +{ +#ifndef HARDWARE_ONLY + if ( Gr_dd_activated || ((!Palette_flashed) && (Palette_flashed_last_frame)) ) { + // Reset flash + gr_directdraw_set_palette_internal( gr_palette ); + Gr_dd_activated = 0; + } + + Palette_flashed_last_frame = Palette_flashed; + Palette_flashed = 0; + + gr_reset_clip(); + + int mx, my; + + Grx_mouse_saved = 0; // assume not saved + + mouse_eval_deltas(); + if ( mouse_is_visible() ) { + gr_reset_clip(); + mouse_get_pos( &mx, &my ); + grx_save_mouse_area(mx,my,32,32); + if ( Gr_cursor == -1 ) { + gr_set_color(255,255,255); + gr_line( mx, my, mx+7, my + 7 ); + gr_line( mx, my, mx+5, my ); + gr_line( mx, my, mx, my+5 ); + } else { + gr_set_bitmap(Gr_cursor); + gr_bitmap( mx, my ); + } + } + + gr_directdraw_unlock(); + +#ifdef DD_FLIP +TryFlipAgain: + + if ( lpFrontBuffer->IsLost() == DDERR_SURFACELOST ) { + lpFrontBuffer->Restore(); + ddrval = lpFrontBuffer->SetPalette( lpPalette ); + if (ddrval != DD_OK) { + mprintf(( "Error setting palette after restore\n" )); + } + } + if ( lpBackBuffer->IsLost() == DDERR_SURFACELOST ) { + lpBackBuffer->Restore(); + } + + HRESULT ddrval = lpFrontBuffer->Flip( NULL, DDFLIP_WAIT ); + if ( ddrval == DDERR_SURFACELOST ) { + mprintf(( "Front surface lost... attempting to restore...\n" )); + os_sleep(1000); // Wait a second + goto TryFlipAgain; + } else if (ddrval != DD_OK ) { + mprintf(( "Fullscreen flip failed!\n" )); + } + +#else + RECT src_rect, dst_rect; + POINT pt; + HRESULT ddrval; + + HWND hWnd = (HWND)os_get_window(); + + if ( hWnd ) { + // src_rect is relative to offscreen buffer + GetClientRect( hWnd, &src_rect ); + + // dst_rect is relative to screen space so needs translation + pt.x = pt.y = 0; + ClientToScreen( hWnd, &pt ); + dst_rect = src_rect; + dst_rect.left += pt.x; + dst_rect.right += pt.x; + dst_rect.top += pt.y; + dst_rect.bottom += pt.y; + + // perform the blit from backbuffer to primary, using + // src_rect and dst_rect + +TryFlipAgain: + if ( lpFrontBuffer->IsLost() == DDERR_SURFACELOST ) { + lpFrontBuffer->Restore(); + +/* ddrval = lpFrontBuffer->SetPalette( lpPalette ); + if (ddrval != DD_OK) { + mprintf(( "Error setting palette after restore\n" )); + } +*/ } + if ( lpBackBuffer->IsLost() == DDERR_SURFACELOST ) { + lpBackBuffer->Restore(); + } + ddrval = lpFrontBuffer->Blt( &dst_rect, lpBackBuffer, &src_rect, DDBLT_WAIT, 0 ); + if ( ddrval == DDERR_SURFACELOST ) { + mprintf(( "Front surface lost... attempting to restore...\n" )); + os_sleep(1000); // Wait a second + goto TryFlipAgain; + } else if (ddrval != DD_OK ) { + mprintf(( "Flip failed! $%x\n", ddrval )); + } + } +#endif + + if ( Grx_mouse_saved ) { + grx_restore_mouse_area(); + } +#else + Int3(); +#endif +} + +void gr_directdraw_flip_window(uint _hdc, int x, int y, int w, int h ) +{ +} + +void gr_directdraw_start_frame() +{ + +} + +void gr_directdraw_stop_frame() +{ + +} + +void gr_directdraw_set_palette_internal( ubyte * new_pal ) +{ +#ifndef HARDWARE_ONLY + PALETTEENTRY pe[256]; + + gr_directdraw_unlock(); + + int i; + for (i=0; i<256; i++ ) { + pe[i].peFlags = PC_NOCOLLAPSE|PC_RESERVED; + pe[i].peRed = new_pal[i*3+0]; + pe[i].peGreen = new_pal[i*3+1]; + pe[i].peBlue = new_pal[i*3+2]; + } + + HRESULT ddrval; + ddrval = lpPalette->SetEntries( 0, 0, 256, pe ); + if ( ddrval != DD_OK ) { + mprintf(( "Error setting palette\n" )); + } + + gr_lock(); +#else + Int3(); +#endif +} + +void gr_directdraw_set_palette( ubyte * new_pal, int restrict_alphacolor ) +{ +#ifndef HARDWARE_ONLY + if ( !restrict_alphacolor ) { + Mouse_hidden++; + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; + } + + // Make sure color 0 is black + if ( (new_pal[0]!=0) || (new_pal[1]!=0) || (new_pal[2]!=0) ) { + // color 0 isn't black!! switch it! + int i; + int black_index = -1; + + for (i=1; i<256; i++ ) { + if ( (new_pal[i*3+0]==0) && (new_pal[i*3+1]==0) && (new_pal[i*3+2]==0) ) { + black_index = i; + break; + } + } + if ( black_index > -1 ) { + // swap black and color 0, so color 0 is black + ubyte tmp[3]; + tmp[0] = new_pal[black_index*3+0]; + tmp[1] = new_pal[black_index*3+1]; + tmp[2] = new_pal[black_index*3+2]; + + new_pal[black_index*3+0] = new_pal[0]; + new_pal[black_index*3+1] = new_pal[1]; + new_pal[black_index*3+2] = new_pal[2]; + + new_pal[0] = tmp[0]; + new_pal[1] = tmp[1]; + new_pal[2] = tmp[2]; + } else { + // no black in palette, force color 0 to be black. + new_pal[0] = 0; + new_pal[1] = 0; + new_pal[2] = 0; + } + } + + gr_directdraw_set_palette_internal( new_pal ); +#else + Int3(); +#endif +} + +void gr_directdraw_flash( int r, int g, int b ) +{ +#ifndef HARDWARE_ONLY + int t,i; + ubyte new_pal[768]; + + if ( (r==0) && (b==0) && (g==0) ) { + return; + } + + Palette_flashed++; + + for (i=0; i<256; i++ ) { + t = gr_palette[i*3+0] + r; + if ( t < 0 ) t = 0; else if (t>255) t = 255; + new_pal[i*3+0] = (ubyte)t; + + t = gr_palette[i*3+1] + g; + if ( t < 0 ) t = 0; else if (t>255) t = 255; + new_pal[i*3+1] = (ubyte)t; + + t = gr_palette[i*3+2] + b; + if ( t < 0 ) t = 0; else if (t>255) t = 255; + new_pal[i*3+2] = (ubyte)t; + } + + gr_directdraw_set_palette_internal( new_pal ); +#else + Int3(); +#endif +} + + +static int gr_palette_faded_out = 0; + +#define FADE_TIME (F1_0/4) // How long to fade out + +void gr_directdraw_fade_out(int instantaneous) +{ +#ifndef HARDWARE_ONLY + int i; + ubyte new_pal[768]; + + if (!gr_palette_faded_out) { + + if ( !instantaneous ) { + + int count = 0; + fix start_time, stop_time, t1; + + start_time = timer_get_fixed_seconds(); + t1 = 0; + + do { + for (i=0; i<768; i++ ) { + int c = (gr_palette[i]*(FADE_TIME-t1))/FADE_TIME; + if (c < 0 ) + c = 0; + else if ( c > 255 ) + c = 255; + + new_pal[i] = (ubyte)c; + } + gr_directdraw_set_palette_internal( new_pal ); + count++; + + t1 = timer_get_fixed_seconds() - start_time; + + } while ( (t1 < FADE_TIME) && (t1>=0) ); // Loop as long as time not up and timer hasn't rolled + + stop_time = timer_get_fixed_seconds(); + + mprintf(( "Took %d frames (and %.1f secs) to fade out\n", count, f2fl(stop_time-start_time) )); + + } + gr_palette_faded_out = 1; + } + + memset( new_pal, 0, 768 ); + gr_directdraw_set_palette_internal( new_pal ); +#else + Int3(); +#endif +} + + +void gr_directdraw_fade_in(int instantaneous) +{ +#ifndef HARDWARE_ONLY + int i; + ubyte new_pal[768]; + + if (gr_palette_faded_out) { + + if ( !instantaneous ) { + int count = 0; + fix start_time, stop_time, t1; + + start_time = timer_get_fixed_seconds(); + t1 = 0; + + do { + for (i=0; i<768; i++ ) { + int c = (gr_palette[i]*t1)/FADE_TIME; + if (c < 0 ) + c = 0; + else if ( c > 255 ) + c = 255; + + new_pal[i] = (ubyte)c; + } + gr_directdraw_set_palette_internal( new_pal ); + count++; + + t1 = timer_get_fixed_seconds() - start_time; + + } while ( (t1 < FADE_TIME) && (t1>=0) ); // Loop as long as time not up and timer hasn't rolled + + stop_time = timer_get_fixed_seconds(); + + mprintf(( "Took %d frames (and %.1f secs) to fade in\n", count, f2fl(stop_time-start_time) )); + } + gr_palette_faded_out = 0; + } + + memcpy( new_pal, gr_palette, 768 ); + gr_directdraw_set_palette_internal( new_pal ); +#else + Int3(); +#endif +} + +void gr_directdraw_get_region(int front, int w, int h, ubyte *data) +{ +} + +// sets the clipping region & offset +void gr_directdraw_set_clip(int x,int y,int w,int h) +{ +#ifndef HARDWARE_ONLY + gr_screen.offset_x = x; + gr_screen.offset_y = y; + + gr_screen.clip_left = 0; + gr_screen.clip_right = w-1; + + gr_screen.clip_top = 0; + gr_screen.clip_bottom = h-1; + + // check for sanity of parameters + if ( gr_screen.clip_left+x < 0 ) { + gr_screen.clip_left = -x; + } else if ( gr_screen.clip_left+x > gr_screen.max_w-1 ) { + gr_screen.clip_left = gr_screen.max_w-1-x; + } + if ( gr_screen.clip_right+x < 0 ) { + gr_screen.clip_right = -x; + } else if ( gr_screen.clip_right+x >= gr_screen.max_w-1 ) { + gr_screen.clip_right = gr_screen.max_w-1-x; + } + + if ( gr_screen.clip_top+y < 0 ) { + gr_screen.clip_top = -y; + } else if ( gr_screen.clip_top+y > gr_screen.max_h-1 ) { + gr_screen.clip_top = gr_screen.max_h-1-y; + } + + if ( gr_screen.clip_bottom+y < 0 ) { + gr_screen.clip_bottom = -y; + } else if ( gr_screen.clip_bottom+y > gr_screen.max_h-1 ) { + gr_screen.clip_bottom = gr_screen.max_h-1-y; + } + + gr_screen.clip_width = gr_screen.clip_right - gr_screen.clip_left + 1; + gr_screen.clip_height = gr_screen.clip_bottom - gr_screen.clip_top + 1; +#else + Int3(); +#endif +} + +// resolution checking +int gr_directdraw_supports_res_ingame(int res) +{ + return 1; +} + +int gr_directdraw_supports_res_interface(int res) +{ + return 1; +} + +void gr_directdraw_set_cull(int cull) +{ +} + +// resets the clipping region to entire screen +// +// should call this before gr_surface_flip() if you clipped some portion of the screen but still +// want a full-screen display +void gr_directdraw_reset_clip() +{ +#ifndef HARDWARE_ONLY + gr_screen.offset_x = 0; + gr_screen.offset_y = 0; + gr_screen.clip_left = 0; + gr_screen.clip_top = 0; + gr_screen.clip_right = gr_screen.max_w - 1; + gr_screen.clip_bottom = gr_screen.max_h - 1; + gr_screen.clip_width = gr_screen.max_w; + gr_screen.clip_height = gr_screen.max_h; +#else + Int3(); +#endif +} + + +// Sets the current bitmap +void gr_directdraw_set_bitmap( int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha, int sx, int sy ) +{ +#ifndef HARDWARE_ONLY + gr_screen.current_alpha = alpha; + gr_screen.current_alphablend_mode = alphablend_mode; + gr_screen.current_bitblt_mode = bitblt_mode; + gr_screen.current_bitmap = bitmap_num; + gr_screen.current_bitmap_sx = sx; + gr_screen.current_bitmap_sy = sy; +#else + Int3(); +#endif +} + +// clears entire clipping region to black. +void gr_directdraw_clear() +{ +#ifndef HARDWARE_ONLY + +#ifdef DD_FLIP + gr_directdraw_unlock(); + + DDBLTFX ddBltFx; + + ddBltFx.dwSize = sizeof(DDBLTFX); + ddBltFx.dwFillColor = 0; + + //DDBLT_WAIT | + lpBackBuffer->Blt(NULL, NULL, NULL, DDBLT_COLORFILL, &ddBltFx); +// ddBltFx.dwROP = BLACKNESS; +// lpBackBuffer->Blt(NULL, NULL, NULL, DDBLT_ROP, &ddBltFx); + + gr_lock(); + +#else + gr_lock(); + + int i,w; + ubyte *pDestBits; + + w = gr_screen.clip_right-gr_screen.clip_left+1; + for (i=gr_screen.clip_top; i<=gr_screen.clip_bottom; i++) { + pDestBits = GR_SCREEN_PTR(ubyte,gr_screen.clip_left,i); + memset( pDestBits, 0, w ); + } + + gr_directdraw_unlock(); +#endif +#else + Int3(); +#endif +} + +void gr_directdraw_force_windowed() +{ +#ifndef HARDWARE_ONLY + gr_directdraw_unlock(); + + HWND hwnd = (HWND)os_get_window(); + + // Simulate Alt+Tab + PostMessage(hwnd,WM_SYSKEYUP, 0x9, 0xa00f0001 ); + PostMessage(hwnd,WM_SYSKEYUP, 0x12, 0xc0380001 ); + + // Wait a second to give things a change to settle down. + Sleep(1000); +#else + Int3(); +#endif +} + +void gr_directdraw_cleanup() +{ +#ifndef HARDWARE_ONLY + HWND hwnd = (HWND)os_get_window(); + + gr_directdraw_unlock(); + + if ( lpPalette ) { + lpPalette->Release(); + lpPalette = NULL; + } + + if (lpClipper) { + lpClipper->Release(); + lpClipper = NULL; + } + + if (lpBackBuffer) { + lpBackBuffer->Release(); + lpBackBuffer = NULL; + } + + if (lpFrontBuffer) { + lpFrontBuffer->Release(); + lpFrontBuffer = NULL; + } + + +// JAS: I took this out because my taskbar seems to get screwed up +// if I leave this in. Doesn't make sense, hence this comment. + HRESULT ddrval = lpDD->RestoreDisplayMode(); + if( ddrval != DD_OK ) { + mprintf(( "WIN_DD32: RestoreDisplayMode failed (0x%x)\n", ddrval )); + } + ddrval = lpDD->SetCooperativeLevel( hwnd, DDSCL_NORMAL ); + if( ddrval != DD_OK ) { + mprintf(( "WIN_DD32: SetCooperativeLevel W Failed (0x%x)\n", ddrval )); + } + + os_suspend(); + if ( lpDD ) { + lpDD->Release(); + lpDD = NULL; + } + os_resume(); +#else + Int3(); +#endif +} + +void dd_get_shift_masks( DDSURFACEDESC *ddsd ) +{ + unsigned long m; + int s; + int r, red_shift, red_scale, red_mask; + int g, green_shift, green_scale, green_mask; + int b, blue_shift, blue_scale, blue_mask; + int a, alpha_shift, alpha_scale, alpha_mask; + + if (gr_screen.bits_per_pixel == 8 ) { + Gr_red.bits = 8; + Gr_red.shift = 16; + Gr_red.scale = 1; + Gr_red.mask = 0xff0000; + + Gr_green.bits = 8; + Gr_green.shift = 8; + Gr_green.scale = 1; + Gr_green.mask = 0xff00; + + Gr_blue.bits = 8; + Gr_blue.shift = 0; + Gr_blue.scale = 1; + Gr_blue.mask = 0xff; + return; + } + + // Determine the red, green and blue masks' shift and scale. + for (s = 0, m = ddsd->ddpfPixelFormat.dwRBitMask; !(m & 1); s++, m >>= 1); + for (r = 0; m & 1; r++, m >>= 1); + red_shift = s; + red_scale = 255 / (ddsd->ddpfPixelFormat.dwRBitMask >> s); + red_mask = ddsd->ddpfPixelFormat.dwRBitMask; + + for (s = 0, m = ddsd->ddpfPixelFormat.dwGBitMask; !(m & 1); s++, m >>= 1); + for (g = 0; m & 1; g++, m >>= 1); + green_shift = s; + green_scale = 255 / (ddsd->ddpfPixelFormat.dwGBitMask >> s); + green_mask = ddsd->ddpfPixelFormat.dwGBitMask; + + for (s = 0, m = ddsd->ddpfPixelFormat.dwBBitMask; !(m & 1); s++, m >>= 1); + for (b = 0; m & 1; b++, m >>= 1); + blue_shift = s; + blue_scale = 255 / (ddsd->ddpfPixelFormat.dwBBitMask >> s); + blue_mask = ddsd->ddpfPixelFormat.dwBBitMask; + + if ( ddsd->ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS ) { + for (s = 0, m = ddsd->ddpfPixelFormat.dwRGBAlphaBitMask; !(m & 1); s++, m >>= 1); + for (a = 0; m & 1; a++, m >>= 1); + alpha_shift = s; + alpha_scale = 255 / (ddsd->ddpfPixelFormat.dwRGBAlphaBitMask >> s); + alpha_mask = ddsd->ddpfPixelFormat.dwRGBAlphaBitMask; + } else { + alpha_shift = a = alpha_scale = alpha_mask = 0; + } + +// mprintf(( "Red Bits:%2d, Shift:%2d, Scale:%3d, Mask:$%08x\n", r, red_shift, red_scale, red_mask )); +// mprintf(( "Green Bits:%2d, Shift:%2d, Scale:%3d, Mask:$%08x\n", g, green_shift, green_scale, green_mask )); +// mprintf(( "Blue Bits:%2d, Shift:%2d, Scale:%3d, Mask:$%08x\n", b, blue_shift, blue_scale, blue_mask )); +// mprintf(( "Alpha Bits:%2d, Shift:%2d, Scale:%3d, Mask:$%08x\n", a, alpha_shift, alpha_scale, alpha_mask )); + + // we need to set stuff up so that the high bit is always an alpha bit. + + Gr_red.bits = r; + Gr_red.shift = red_shift; + Gr_red.scale = red_scale; + Gr_red.mask = red_mask; + + Gr_green.bits = g; + Gr_green.shift = green_shift; + Gr_green.scale = green_scale; + Gr_green.mask = green_mask; + + Gr_blue.bits = b; + Gr_blue.shift = blue_shift; + Gr_blue.scale = blue_scale; + Gr_blue.mask = blue_mask; +} + +// cross fade +void gr_directdraw_cross_fade(int bmap1, int bmap2, int x1, int y1, int x2, int y2, float pct) +{ + if ( pct <= 50 ) { + gr_set_bitmap(bmap1); + gr_bitmap(x1, y1); + } else { + gr_set_bitmap(bmap2); + gr_bitmap(x2, y2); + } +} + +// filter +void gr_directdraw_filter_set(int filter) +{ +} + +// set clear color +void gr_directdraw_set_clear_color(int r, int g, int b) +{ +} + +void gr_directdraw_init() +{ +#ifndef HARDWARE_ONLY +// int i; + HRESULT ddrval; + + Palette_flashed = 0; + Palette_flashed_last_frame = 0; + + gr_screen.bits_per_pixel = 8; + gr_screen.bytes_per_pixel = 1; + + Gr_dd_activated = 0; + + HWND hwnd = (HWND)os_get_window(); + + if ( !hwnd ) { + mprintf(( "GR_SOFT_INIT: No window handle.\n" )); + exit(1); + } + + // Prepare the window to go full screen +#ifndef NDEBUG + mprintf(( "Window in debugging mode... mouse clicking may cause problems!\n" )); + SetWindowLong( hwnd, GWL_EXSTYLE, 0 ); + SetWindowLong( hwnd, GWL_STYLE, WS_POPUP ); + ShowWindow(hwnd, SW_SHOWNORMAL ); + RECT work_rect; + SystemParametersInfo( SPI_GETWORKAREA, 0, &work_rect, 0 ); + SetWindowPos( hwnd, HWND_NOTOPMOST, work_rect.left, work_rect.top, gr_screen.max_w, gr_screen.max_h, 0 ); + SetActiveWindow(hwnd); + SetForegroundWindow(hwnd); +#else + SetWindowLong( hwnd, GWL_EXSTYLE, 0 ); + SetWindowLong( hwnd, GWL_STYLE, WS_POPUP ); + ShowWindow(hwnd, SW_SHOWNORMAL ); + SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ), 0 ); + SetActiveWindow(hwnd); + SetForegroundWindow(hwnd); +#endif + + os_suspend(); + ddrval = DirectDrawCreate( NULL, &lpDD, NULL ); + os_resume(); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: DirectDrawCreate failed.\n" )); + exit(1); + } + + Assert( lpDD ); + + ddrval = lpDD->SetCooperativeLevel( hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: SetCooperativeLevel EXCLUSIVE failed.\n" )); + lpDD->Release(); + exit(1); + } + + // Go to full screen! + os_suspend(); + ddrval = lpDD->SetDisplayMode( gr_screen.max_w, gr_screen.max_h, gr_screen.bits_per_pixel ); + os_resume(); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: SetDisplayMode failed.\n" )); + lpDD->Release(); + exit(1); + } + + + DDSURFACEDESC ddsd; + +#ifdef DD_FLIP + DDSCAPS ddscaps; + + memset( &ddsd, 0, sizeof( ddsd )); + + ddsd.dwSize = sizeof( ddsd ); + ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; + ddsd.dwBackBufferCount = 2; + + ddrval = lpDD->CreateSurface( &ddsd, &lpFrontBuffer, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: CreateSurface (Front) failed.\n" )); + lpDD->Release(); + exit(1); + } + + ddscaps.dwCaps = DDSCAPS_BACKBUFFER; + + ddrval = lpFrontBuffer->GetAttachedSurface( &ddscaps, &lpBackBuffer ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: GetAttachedSurface (Back) failed.\n" )); + lpDD->Release(); + exit(1); + } + + lpClipper = NULL; +#else + + memset( &ddsd, 0, sizeof( ddsd ) ); + + ddsd.dwSize = sizeof( ddsd ); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + ddrval = lpDD->CreateSurface( &ddsd, &lpFrontBuffer, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: CreateSurface (Front) failed.\n" )); + lpDD->Release(); + exit(1); + } + + ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; + ddsd.dwWidth = gr_screen.max_w; + ddsd.dwHeight = gr_screen.max_h; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + + // If debugging we have to create our surfaces in system memory + // so that our debugger isn't hosed when locking surfaces. + + //ddsd.ddsCaps.dwCaps |= DDSCAPS_NONLOCALVIDMEM; + ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + + // create the back buffer + ddrval = lpDD->CreateSurface( &ddsd, &lpBackBuffer , NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: CreateSurface (Back) failed.\n" )); + goto DDError; + } + + // create a clipper and attach it to our window + ddrval = lpDD->CreateClipper( 0, &lpClipper, NULL ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: CreateClipper failed.\n" )); + goto DDError; + } + + + ddrval = lpClipper->SetHWnd( 0, hwnd ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: SetHWnd failed.\n" )); + goto DDError; + } + + + ddrval = lpFrontBuffer->SetClipper( lpClipper ); + if ( ddrval != DD_OK ) { + mprintf(( "GR_SOFT_INIT: SetClipper failed.\n" )); + goto DDError; + } +#endif + + int i; + PALETTEENTRY pe[256]; + for (i=0; i<256; i++ ) { + pe[i].peFlags = PC_NOCOLLAPSE|PC_RESERVED; + pe[i].peRed = 0; + pe[i].peGreen = 0; + pe[i].peBlue = 0; + } + ddrval = lpDD->CreatePalette( DDPCAPS_8BIT | DDPCAPS_ALLOW256, pe, &lpPalette, NULL); + if (ddrval != DD_OK) { + mprintf(( "Error creating palette\n" )); + goto DDError; + } + + ddrval = lpFrontBuffer->SetPalette( lpPalette ); + if (ddrval != DD_OK) { + mprintf(( "Error setting palette in gr_directdraw_set_palette\n" )); + } + + + memset( &ddsd, 0, sizeof( ddsd ) ); + ddsd.dwSize = sizeof(ddsd); + lpFrontBuffer->GetSurfaceDesc(&ddsd); + dd_get_shift_masks( &ddsd ); + + mprintf(( "Surfaces created...\n" )); + + goto NoDDError; + +DDError: + lpDD->Release(); + exit(1); + + +NoDDError: + ; + + grx_init_alphacolors(); + + gr_screen.gf_flip = gr_directdraw_flip; + gr_screen.gf_flip_window = gr_directdraw_flip_window; + gr_screen.gf_set_clip = gr_directdraw_set_clip; + gr_screen.gf_reset_clip = gr_directdraw_reset_clip; + gr_screen.gf_set_font = grx_set_font; + gr_screen.gf_set_color = grx_set_color; + gr_screen.gf_set_bitmap = gr_directdraw_set_bitmap; + gr_screen.gf_create_shader = grx_create_shader; + gr_screen.gf_set_shader = grx_set_shader; + gr_screen.gf_clear = gr_directdraw_clear; + // gr_screen.gf_bitmap = grx_bitmap; + // gr_screen.gf_bitmap_ex = grx_bitmap_ex; + + gr_screen.gf_aabitmap = grx_aabitmap; + gr_screen.gf_aabitmap_ex = grx_aabitmap_ex; + + gr_screen.gf_rect = grx_rect; + gr_screen.gf_shade = gr8_shade; + gr_screen.gf_string = gr8_string; + gr_screen.gf_circle = gr8_circle; + + gr_screen.gf_line = gr8_line; + gr_screen.gf_aaline = gr8_aaline; + gr_screen.gf_pixel = gr8_pixel; + gr_screen.gf_scaler = gr8_scaler; + gr_screen.gf_aascaler = gr8_aascaler; + gr_screen.gf_tmapper = grx_tmapper; + + gr_screen.gf_gradient = gr8_gradient; + + gr_screen.gf_set_palette = gr_directdraw_set_palette; + gr_screen.gf_get_color = grx_get_color; + gr_screen.gf_init_color = grx_init_color; + gr_screen.gf_init_alphacolor = grx_init_alphacolor; + gr_screen.gf_set_color_fast = grx_set_color_fast; + gr_screen.gf_print_screen = grx_print_screen; + + gr_screen.gf_start_frame = gr_directdraw_start_frame; + gr_screen.gf_stop_frame = gr_directdraw_stop_frame; + + gr_screen.gf_fade_in = gr_directdraw_fade_in; + gr_screen.gf_fade_out = gr_directdraw_fade_out; + gr_screen.gf_flash = gr_directdraw_flash; + + // Retrieves the zbuffer mode. + gr_screen.gf_zbuffer_get = gr8_zbuffer_get; + gr_screen.gf_zbuffer_set = gr8_zbuffer_set; + gr_screen.gf_zbuffer_clear = gr8_zbuffer_clear; + + gr_screen.gf_save_screen = gr8_save_screen; + gr_screen.gf_restore_screen = gr8_restore_screen; + gr_screen.gf_free_screen = gr8_free_screen; + + // Screen dumping stuff + gr_screen.gf_dump_frame_start = gr8_dump_frame_start; + gr_screen.gf_dump_frame_stop = gr8_dump_frame_stop; + gr_screen.gf_dump_frame = gr8_dump_frame; + + // Gamma stuff + gr_screen.gf_set_gamma = gr8_set_gamma; + + // Lock/unlock stuff + gr_screen.gf_lock = gr_directdraw_lock; + gr_screen.gf_unlock = gr_directdraw_unlock_fake; + + // region + gr_screen.gf_get_region = gr_directdraw_get_region; + + // poly culling + gr_screen.gf_set_cull = gr_directdraw_set_cull; + + // cross fade + gr_screen.gf_cross_fade = gr_directdraw_cross_fade; + + // filter + gr_screen.gf_filter_set = gr_directdraw_filter_set; + + // set clear color + gr_screen.gf_set_clear_color = gr_directdraw_set_clear_color; + + gr_lock(); + + mprintf(( "Surfaces locked...\n" )); + + Mouse_hidden++; + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; +#else + Int3(); +#endif +} diff --git a/src/graphics/grglide.cpp b/src/graphics/grglide.cpp new file mode 100644 index 0000000..9a459a7 --- /dev/null +++ b/src/graphics/grglide.cpp @@ -0,0 +1,3758 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrGlide.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code that uses 3DFX's Glide graphics library + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 39 9/14/99 5:14a Dave + * Fixed credits drawing in Glide. + * + * 38 9/08/99 11:38p Dave + * Make V2 minimize/maximize the old way. + * + * 37 9/04/99 8:00p Dave + * Fixed up 1024 and 32 bit movie support. + * + * 36 9/01/99 10:49p Dave + * Added nice SquadWar checkbox to the client join wait screen. + * + * 35 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 34 8/06/99 12:29a Dave + * Multiple bug fixes. + * + * 33 8/04/99 5:36p Dave + * Make glide and D3D switch out properly. + * + * 32 7/19/99 3:29p Dave + * Fixed gamma bitmap in the options screen. + * + * 31 7/14/99 9:42a Dave + * Put in clear_color debug function. Put in base for 3dnow stuff / P3 + * stuff + * + * 30 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 29 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 28 5/05/99 9:02p Dave + * Fixed D3D aabitmap rendering. Spiffed up nebula effect a bit (added + * rotations, tweaked values, made bitmap selection more random). Fixed + * D3D beam weapon clipping problem. Added D3d frame dumping. + * + * 27 2/21/99 1:48p Dave + * Some code for monitoring datarate for multiplayer in detail. + * + * 26 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 25 2/05/99 12:52p Dave + * Fixed Glide nondarkening textures. + * + * 24 2/04/99 6:29p Dave + * First full working rev of FS2 PXO support. Fixed Glide lighting + * problems. + * + * 23 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 22 1/30/99 1:29a Dave + * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot + * screen. Fixed beam weapon death messages. + * + * 21 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 20 1/23/99 5:33p Dave + * Put in support for 1024x768 Glide. + * + * 19 1/15/99 11:29a Neilk + * Fixed D3D screen/texture pixel formatting problem. + * + * 18 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 17 12/14/98 12:13p Dave + * Spiffed up xfer system a bit. Put in support for squad logo file xfer. + * Need to test now. + * + * 16 12/09/98 7:34p Dave + * Cleanup up nebula effect. Tweaked many values. + * + * 15 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 14 12/04/98 10:20a Dave + * Fixed up gr_glide_get_pixel(...) + * + * 13 12/02/98 9:58a Dave + * Got fonttool working under Glide/Direct3d. + * + * 12 12/01/98 5:54p Dave + * Simplified the way pixel data is swizzled. Fixed tga bitmaps to work + * properly in D3D and Glide. + * + * 11 12/01/98 10:32a Johnson + * Fixed direct3d font problems. Fixed sun bitmap problem. Fixed direct3d + * starfield problem. + * + * 10 12/01/98 8:06a Dave + * Temporary checkin to fix some texture transparency problems in d3d. + * + * 9 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 8 11/24/98 4:43p Dave + * Make sure glide starts up in 640x480 + * + * 7 11/20/98 11:16a Dave + * Fixed up IPX support a bit. Making sure that switching modes and + * loading/saving pilot files maintains proper state. + * + * 6 11/14/98 5:32p Dave + * Lots of nebula work. Put in ship contrails. + * + * 5 11/12/98 12:19a Dave + * Removed compiler warning. + * + * 4 11/11/98 5:37p Dave + * Checkin for multiplayer testing. + * + * 3 11/09/98 2:11p Dave + * Nebula optimizations. + * + * 2 10/07/98 10:52a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 116 9/14/98 1:59p Allender + * allow for interlaced movies + * + * 115 5/25/98 10:32a John + * Took out redundant code for font bitmap offsets that converted to a + * float, then later on converted back to an integer. Quite unnecessary. + * + * 114 5/22/98 9:09a John + * fixed type in previous + * + * 113 5/22/98 9:07a John + * added in code that fixed Rush Glide startup problemmo. + * + * 112 5/20/98 9:46p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 111 5/17/98 5:03p John + * Fixed some bugs that make the taskbar interfere with the DEBUG-only + * mouse cursor. + * + * 110 5/17/98 3:23p John + * Took out capibility check for additive blending. Made gr_bitmap_ex + * clip properly in glide and direct3d. + * + * 109 5/15/98 9:34p John + * Removed the initial ugly little cursor part that drew right at program + * start. + * + * 108 5/15/98 8:48a John + * Fixed bug where one-pixel line was getting left on right and bottom. + * + * 107 5/14/98 5:42p John + * Revamped the whole window position/mouse code for the graphics windows. + * + * 106 5/08/98 5:38p John + * Made Glide wait for retrace. + * + * 105 5/08/98 5:37p John + * + * 104 5/07/98 6:58p Hoffoss + * Made changes to mouse code to fix a number of problems. + * + * 103 5/07/98 8:43a John + * Turned off 3dfx gamma correction. + * + * 102 5/07/98 8:36a John + * Fixed Glide gradients + * + * 101 5/06/98 8:41p John + * Fixed some font clipping bugs. Moved texture handle set code for d3d + * into the texture module. + * + * 100 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + */ + +//#define USE_8BPP_TEXTURES + +#include +#include +#include "glide.h" +#include "glideutl.h" + +#include "osapi.h" +#include "2d.h" +#include "bmpman.h" +#include "floating.h" +#include "palman.h" +#include "grinternal.h" +#include "grglide.h" +#include "line.h" +#include "font.h" +#include "mouse.h" +#include "key.h" +#include "systemvars.h" +#include "grglideinternal.h" +#include "cfile.h" +#include "timer.h" +// #include "movie.h" +#include "neb.h" + +#define NEBULA_COLORS 20 + +GrFog_t Glide_linear_fogtable[GR_FOG_TABLE_SIZE]; + +int Glide_textures_in = 0; + +int Glide_voodoo3 = 0; + +static int Inited = 0; + +// voodoo3 is a little sensitive to getting deactivated +#define VOODOO3_DEACTIVATED -1 +#define VOODOO3_INACTIVE() ( (Glide_voodoo3 == 1) && (Glide_deactivate == VOODOO3_DEACTIVATED) ) + +float Gr_gamma_lookup_float[256]; + +volatile int Glide_running = 0; +volatile int Glide_activate = 0; +volatile int Glide_deactivate = 0; + +typedef enum gr_texture_source { + TEXTURE_SOURCE_NONE, + TEXTURE_SOURCE_DECAL, +} gr_texture_source; + +typedef enum gr_color_source { + COLOR_SOURCE_VERTEX, + COLOR_SOURCE_TEXTURE, + COLOR_SOURCE_VERTEX_TIMES_TEXTURE, +} gr_color_source; + +typedef enum gr_alpha_source { + ALPHA_SOURCE_VERTEX, + ALPHA_SOURCE_VERTEX_NONDARKENING, + ALPHA_SOURCE_TEXTURE, + ALPHA_SOURCE_VERTEX_TIMES_TEXTURE, +} gr_alpha_source; + +typedef enum gr_alpha_blend { + ALPHA_BLEND_NONE, // 1*SrcPixel + 0*DestPixel + ALPHA_BLEND_ADDITIVE, // 1*SrcPixel + 1*DestPixel + ALPHA_BLEND_ALPHA_ADDITIVE, // Alpha*SrcPixel + 1*DestPixel + ALPHA_BLEND_ALPHA_BLEND_ALPHA, // Alpha*SrcPixel + (1-Alpha)*DestPixel + ALPHA_BLEND_ALPHA_BLEND_SRC_COLOR, // Alpha*SrcPixel + (1-SrcPixel)*DestPixel +} gr_alpha_blend; + +typedef enum gr_zbuffer_type { + ZBUFFER_TYPE_NONE, + ZBUFFER_TYPE_READ, + ZBUFFER_TYPE_WRITE, + ZBUFFER_TYPE_FULL, +} gr_zbuffer_type; + +int Glide_last_state = -1; + +void gr_glide_set_state( gr_texture_source ts, gr_color_source cs, gr_alpha_source as, gr_alpha_blend ab, gr_zbuffer_type zt ) +{ + int current_state = 0; + + if(VOODOO3_INACTIVE()){ + return; + } + + current_state = current_state | (ts<<0); + current_state = current_state | (cs<<5); + current_state = current_state | (as<<10); + current_state = current_state | (ab<<15); + current_state = current_state | (zt<<20); + + if ( current_state == Glide_last_state ) { + return; + } + Glide_last_state = current_state; + + switch( ts ) { + case TEXTURE_SOURCE_NONE: + grTexCombineFunction( GR_TMU0, GR_TEXTURECOMBINE_ONE ); + break; + case TEXTURE_SOURCE_DECAL: + grTexCombineFunction( GR_TMU0, GR_TEXTURECOMBINE_DECAL ); + break; + default: + Int3(); + } + + switch( cs ) { + case COLOR_SOURCE_VERTEX: + grColorCombine( GR_COMBINE_FUNCTION_LOCAL, GR_COMBINE_FACTOR_NONE, GR_COMBINE_LOCAL_ITERATED, GR_COMBINE_OTHER_NONE, FXFALSE ); + break; + + case COLOR_SOURCE_TEXTURE: + grColorCombine( GR_COMBINE_FUNCTION_SCALE_OTHER, GR_COMBINE_FACTOR_ONE, GR_COMBINE_LOCAL_NONE, GR_COMBINE_OTHER_TEXTURE, FXFALSE); + break; + + case COLOR_SOURCE_VERTEX_TIMES_TEXTURE: + grColorCombine( GR_COMBINE_FUNCTION_SCALE_OTHER, GR_COMBINE_FACTOR_LOCAL, GR_COMBINE_LOCAL_ITERATED, GR_COMBINE_OTHER_TEXTURE, FXFALSE); + break; + + default: + Int3(); + } + + switch( as ) { + case ALPHA_SOURCE_VERTEX: + grAlphaCombine( GR_COMBINE_FUNCTION_LOCAL, GR_COMBINE_FACTOR_NONE, GR_COMBINE_LOCAL_ITERATED, GR_COMBINE_OTHER_NONE, FXFALSE ); + grAlphaControlsITRGBLighting(FXFALSE); + break; + + case ALPHA_SOURCE_VERTEX_NONDARKENING: + grAlphaCombine( GR_COMBINE_FUNCTION_LOCAL, GR_COMBINE_FACTOR_NONE, GR_COMBINE_LOCAL_ITERATED, GR_COMBINE_OTHER_NONE, FXFALSE ); + grAlphaControlsITRGBLighting(FXTRUE); + grConstantColorValue(0xFFFFFFFF); // Non-darkening colors will use this + break; + + case ALPHA_SOURCE_TEXTURE: + grAlphaCombine( GR_COMBINE_FUNCTION_SCALE_OTHER, GR_COMBINE_FACTOR_ONE, GR_COMBINE_LOCAL_NONE, GR_COMBINE_OTHER_TEXTURE, FXFALSE); + grAlphaControlsITRGBLighting(FXFALSE); + break; + + case ALPHA_SOURCE_VERTEX_TIMES_TEXTURE: + grAlphaCombine( GR_COMBINE_FUNCTION_SCALE_OTHER, GR_COMBINE_FACTOR_LOCAL, GR_COMBINE_LOCAL_ITERATED, GR_COMBINE_OTHER_TEXTURE, FXFALSE ); + grAlphaControlsITRGBLighting(FXFALSE); + break; + + default: + Int3(); + } + + + switch( ab ) { + case ALPHA_BLEND_NONE: // 1*SrcPixel + 0*DestPixel + grAlphaBlendFunction( GR_BLEND_ONE, GR_BLEND_ZERO, GR_BLEND_ZERO, GR_BLEND_ZERO ); + break; + + case ALPHA_BLEND_ADDITIVE: // 1*SrcPixel + 1*DestPixel + grAlphaBlendFunction( GR_BLEND_ONE, GR_BLEND_ONE, GR_BLEND_ZERO, GR_BLEND_ZERO ); + break; + + case ALPHA_BLEND_ALPHA_ADDITIVE: // Alpha*SrcPixel + 1*DestPixel + grAlphaBlendFunction( GR_BLEND_SRC_ALPHA, GR_BLEND_ONE, GR_BLEND_ZERO, GR_BLEND_ZERO); + break; + + case ALPHA_BLEND_ALPHA_BLEND_ALPHA: // Alpha*SrcPixel + (1-Alpha)*DestPixel + grAlphaBlendFunction( GR_BLEND_SRC_ALPHA, GR_BLEND_ONE_MINUS_SRC_ALPHA, GR_BLEND_ZERO, GR_BLEND_ZERO ); + break; + + case ALPHA_BLEND_ALPHA_BLEND_SRC_COLOR: // Alpha*SrcPixel + (1-SrcPixel)*DestPixel + grAlphaBlendFunction( GR_BLEND_SRC_ALPHA, GR_BLEND_ONE_MINUS_SRC_COLOR, GR_BLEND_ZERO, GR_BLEND_ZERO ); + break; + + default: + Int3(); + } + + switch( zt ) { + + case ZBUFFER_TYPE_NONE: + grDepthBufferMode(GR_DEPTHBUFFER_DISABLE); + grDepthMask( FXFALSE ); + break; + + case ZBUFFER_TYPE_READ: + grDepthBufferMode(GR_DEPTHBUFFER_WBUFFER); + grDepthBufferFunction(GR_CMP_LEQUAL); + grDepthMask( FXFALSE ); + break; + + case ZBUFFER_TYPE_WRITE: + grDepthBufferMode(GR_DEPTHBUFFER_WBUFFER); + grDepthBufferFunction(GR_CMP_ALWAYS); + grDepthMask( FXTRUE ); + break; + + case ZBUFFER_TYPE_FULL: + grDepthBufferMode(GR_DEPTHBUFFER_WBUFFER); + grDepthBufferFunction(GR_CMP_LEQUAL); + grDepthMask( FXTRUE ); + break; + + default: + Int3(); + } + +} + +/* + gr_glide_set_state( + TEXTURE_SOURCE_NONE, TEXTURE_SOURCE_DECAL, + + COLOR_SOURCE_VERTEX, COLOR_SOURCE_TEXTURE, COLOR_SOURCE_VERTEX_TIMES_TEXTURE, + + ALPHA_SOURCE_VERTEX, ALPHA_SOURCE_TEXTURE, ALPHA_SOURCE_VERTEX_TIMES_TEXTURE, + + ALPHA_BLEND_NONE, ALPHA_BLEND_ADDITIVE, ALPHA_BLEND_ALPHA_ADDITIVE, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ALPHA_BLEND_ALPHA_BLEND_SRC_COLOR, + + ZBUFFER_TYPE_NONE, ZBUFFER_TYPE_READ, ZBUFFER_TYPE_WRITE, ZBUFFER_TYPE_FULL, + + ); + +*/ + + +// If mode is FALSE, turn zbuffer off the entire frame, +// no matter what people pass to gr_zbuffer_set. +void gr_glide_zbuffer_clear(int mode) +{ + if(VOODOO3_INACTIVE()){ + return; + } + + if ( mode ) { + gr_zbuffering = 1; + gr_zbuffering_mode = GR_ZBUFF_FULL; + gr_global_zbuffering = 1; + + // Make sure zbuffering is on + gr_glide_set_state( TEXTURE_SOURCE_NONE, COLOR_SOURCE_VERTEX, ALPHA_SOURCE_VERTEX, ALPHA_BLEND_NONE, ZBUFFER_TYPE_FULL ); + + // Disable writes to color and alpha buffers + grColorMask( FXFALSE, FXFALSE ); + // Clear the zbuffer + grBufferClear( 0x0, 0, GR_WDEPTHVALUE_FARTHEST ); + // Re-enable writes to the color buffer + grColorMask( FXTRUE, FXFALSE ); + } else { + gr_zbuffering = 0; + gr_zbuffering_mode = GR_ZBUFF_NONE; + gr_global_zbuffering = 0; + } +} + +int gr_glide_zbuffer_get() +{ + if ( !gr_global_zbuffering ) { + return GR_ZBUFF_NONE; + } + return gr_zbuffering_mode; +} + +int gr_glide_zbuffer_set(int mode) +{ + if ( !gr_global_zbuffering ) { + gr_zbuffering = 0; + return GR_ZBUFF_NONE; + } + + int tmp = gr_zbuffering_mode; + + gr_zbuffering_mode = mode; + + if ( gr_zbuffering_mode == GR_ZBUFF_NONE ) { + gr_zbuffering = 0; + } else { + gr_zbuffering = 1; + } + return tmp; +} + + +void gr_glide_pixel(int x, int y) +{ + if(VOODOO3_INACTIVE()){ + return; + } + + gr_line(x,y,x,y); +/* + if ( x < gr_screen.clip_left ) return; + if ( x > gr_screen.clip_right ) return; + if ( y < gr_screen.clip_top ) return; + if ( y > gr_screen.clip_bottom ) return; + + // Set up Render State - flat shading - alpha blending + gr_glide_set_state( TEXTURE_SOURCE_NONE, COLOR_SOURCE_VERTEX, ALPHA_SOURCE_VERTEX, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE ); + + GrVertex p; + p.x = 0.5f + i2fl(x + gr_screen.offset_x); + p.y = 0.5f + i2fl(y + gr_screen.offset_y); + p.a = 255.0f; + p.r = i2fl(gr_screen.current_color.red); + p.g = i2fl(gr_screen.current_color.green); + p.b = i2fl(gr_screen.current_color.blue); + + grDrawPoint(&p); +*/ +} + +float snap(float x) +{ + int xi = fl2i(x*16.0f); + return i2fl(xi)/16.0f; +} + +void gr_glide_clear() +{ + GrColor_t clear_color; + + if(VOODOO3_INACTIVE()){ + return; + } + + // Turn off zbuffering so this doesn't clear the zbuffer + gr_glide_set_state( TEXTURE_SOURCE_NONE, COLOR_SOURCE_VERTEX, ALPHA_SOURCE_VERTEX, ALPHA_BLEND_NONE, ZBUFFER_TYPE_NONE ); + + // Clear the screen + clear_color = 0; + clear_color |= ((ubyte)gr_screen.current_clear_color.red); + clear_color |= ((ubyte)gr_screen.current_clear_color.green << 8); + clear_color |= ((ubyte)gr_screen.current_clear_color.blue << 16); + grBufferClear( clear_color, 0, GR_WDEPTHVALUE_FARTHEST ); +} + +static RECT Glide_cursor_clip_rect; + +void gr_glide_clip_cursor(int active) +{ + if ( active ) { + ClipCursor(&Glide_cursor_clip_rect); + } else { + ClipCursor(NULL); + } +} + + +int Gr_glide_mouse_saved = 0; +int Gr_glide_mouse_saved_x1 = 0; +int Gr_glide_mouse_saved_y1 = 0; +int Gr_glide_mouse_saved_x2 = 0; +int Gr_glide_mouse_saved_y2 = 0; +int Gr_glide_mouse_saved_w = 0; +int Gr_glide_mouse_saved_h = 0; +#define MAX_SAVE_SIZE (32*32) +ushort Gr_glide_mouse_saved_data[MAX_SAVE_SIZE]; + +// Clamps X between R1 and R2 +#define CLAMP(x,r1,r2) do { if ( (x) < (r1) ) (x) = (r1); else if ((x) > (r2)) (x) = (r2); } while(0) + +void gr_glide_save_mouse_area(int x, int y, int w, int h ) +{ + if(VOODOO3_INACTIVE()){ + return; + } + + Gr_glide_mouse_saved_x1 = x; + Gr_glide_mouse_saved_y1 = y; + Gr_glide_mouse_saved_x2 = x+w-1; + Gr_glide_mouse_saved_y2 = y+h-1; + + CLAMP(Gr_glide_mouse_saved_x1, gr_screen.clip_left, gr_screen.clip_right ); + CLAMP(Gr_glide_mouse_saved_x2, gr_screen.clip_left, gr_screen.clip_right ); + CLAMP(Gr_glide_mouse_saved_y1, gr_screen.clip_top, gr_screen.clip_bottom ); + CLAMP(Gr_glide_mouse_saved_y2, gr_screen.clip_top, gr_screen.clip_bottom ); + + Gr_glide_mouse_saved_w = Gr_glide_mouse_saved_x2 - Gr_glide_mouse_saved_x1 + 1; + Gr_glide_mouse_saved_h = Gr_glide_mouse_saved_y2 - Gr_glide_mouse_saved_y1 + 1; + + if ( Gr_glide_mouse_saved_w < 1 ) return; + if ( Gr_glide_mouse_saved_h < 1 ) return; + + // Make sure we're not saving too much! + Assert( (Gr_glide_mouse_saved_w*Gr_glide_mouse_saved_h) <= MAX_SAVE_SIZE ); + + GrLfbInfo_t info; + + info.size=sizeof(GrLfbInfo_t); + + // get a read pointer + if ( grLfbLock( GR_LFB_READ_ONLY, GR_BUFFER_BACKBUFFER, GR_LFBWRITEMODE_565, + GR_ORIGIN_UPPER_LEFT, FXFALSE, &info)) { + + ushort *rptr; + int short_per_row=info.strideInBytes/2; + + rptr = (ushort *)info.lfbPtr; + + ushort *sptr, *dptr; + + dptr = Gr_glide_mouse_saved_data; + + for (int i=0; i 0){ + int diff = timer_get_milliseconds() - Interface_last_tick; + gr_printf(10, 10, "%f", 1000.0f / (float)diff); + } + Interface_last_tick = timer_get_milliseconds(); + } +#endif + +#ifndef NDEBUG + grBufferSwap( 0 ); +#else + grBufferSwap( 1 ); +#endif + grSstIdle(); + + glide_tcache_frame(); +} + +void gr_glide_flip_window(uint _hdc, int x, int y, int w, int h ) +{ +} + +void gr_glide_set_clip(int x,int y,int w,int h) +{ + gr_screen.offset_x = x; + gr_screen.offset_y = y; + + gr_screen.clip_left = 0; + gr_screen.clip_right = w-1; + + gr_screen.clip_top = 0; + gr_screen.clip_bottom = h-1; + + if(VOODOO3_INACTIVE()){ + return; + } + + // check for sanity of parameters + if ( gr_screen.clip_left+x < 0 ) { + gr_screen.clip_left = -x; + } else if ( gr_screen.clip_left+x > gr_screen.max_w-1 ) { + gr_screen.clip_left = gr_screen.max_w-1-x; + } + if ( gr_screen.clip_right+x < 0 ) { + gr_screen.clip_right = -x; + } else if ( gr_screen.clip_right+x >= gr_screen.max_w-1 ) { + gr_screen.clip_right = gr_screen.max_w-1-x; + } + + if ( gr_screen.clip_top+y < 0 ) { + gr_screen.clip_top = -y; + } else if ( gr_screen.clip_top+y > gr_screen.max_h-1 ) { + gr_screen.clip_top = gr_screen.max_h-1-y; + } + + if ( gr_screen.clip_bottom+y < 0 ) { + gr_screen.clip_bottom = -y; + } else if ( gr_screen.clip_bottom+y > gr_screen.max_h-1 ) { + gr_screen.clip_bottom = gr_screen.max_h-1-y; + } + + gr_screen.clip_width = gr_screen.clip_right - gr_screen.clip_left + 1; + gr_screen.clip_height = gr_screen.clip_bottom - gr_screen.clip_top + 1; + + grClipWindow( gr_screen.clip_left+x, gr_screen.clip_top+y, gr_screen.clip_right+1+x, gr_screen.clip_bottom+1+y ); +} + +void gr_glide_reset_clip() +{ + if(VOODOO3_INACTIVE()){ + return; + } + + gr_screen.offset_x = 0; + gr_screen.offset_y = 0; + gr_screen.clip_left = 0; + gr_screen.clip_top = 0; + gr_screen.clip_right = gr_screen.max_w - 1; + gr_screen.clip_bottom = gr_screen.max_h - 1; + gr_screen.clip_width = gr_screen.max_w; + gr_screen.clip_height = gr_screen.max_h; + + grClipWindow( gr_screen.clip_left, gr_screen.clip_top, gr_screen.clip_right+1, gr_screen.clip_bottom+1 ); +} + + +void gr_glide_set_bitmap( int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha, int sx, int sy ) +{ + if(VOODOO3_INACTIVE()){ + return; + } + + gr_screen.current_alpha = alpha; + gr_screen.current_alphablend_mode = alphablend_mode; + gr_screen.current_bitblt_mode = bitblt_mode; + gr_screen.current_bitmap = bitmap_num; + + gr_screen.current_bitmap_sx = sx; + gr_screen.current_bitmap_sy = sy; +} + +void gr_glide_create_shader(shader * shade, float r, float g, float b, float c ) +{ + shade->screen_sig = gr_screen.signature; + + shade->r = r; + shade->g = g; + shade->b = b; + shade->c = c; +} + +void gr_glide_set_shader( shader * shade ) +{ + if ( shade ) { + if (shade->screen_sig != gr_screen.signature) { + gr_create_shader( shade, shade->r, shade->g, shade->b, shade->c ); + } + gr_screen.current_shader = *shade; + } else { + gr_create_shader( &gr_screen.current_shader, 0.0f, 0.0f, 0.0f, 0.0f ); + } +} + +void gr_glide_bitmap_ex_internal(int x,int y,int w,int h,int sx,int sy) +{ + int i,j; + bitmap * bmp; + ushort * sptr; + + if(VOODOO3_INACTIVE()){ + return; + } + + bmp = bm_lock( gr_screen.current_bitmap, 16, 0 ); + sptr = (ushort *)( bmp->data + (sy*bmp->w*2) + (sx*2) ); + + if ( x < gr_screen.clip_left ) return; + if ( x > gr_screen.clip_right ) return; + if ( y < gr_screen.clip_top ) return; + if ( y > gr_screen.clip_bottom ) return; + + gr_glide_set_state( TEXTURE_SOURCE_NONE, COLOR_SOURCE_VERTEX, ALPHA_SOURCE_VERTEX, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE ); + + GrLfbInfo_t info; + + if ( grLfbLock( GR_LFB_WRITE_ONLY, GR_BUFFER_BACKBUFFER, GR_LFBWRITEMODE_1555, GR_ORIGIN_UPPER_LEFT, FXFALSE, &info) ) { + + ushort *vram = (ushort *)info.lfbPtr; + int stride = info.strideInBytes / sizeof(ushort); + + for (i=0; iw; + } + + grLfbUnlock(GR_LFB_WRITE_ONLY, GR_BUFFER_BACKBUFFER); + } + + bm_unlock(gr_screen.current_bitmap); +} + + +void gr_glide_bitmap_ex(int x,int y,int w,int h,int sx,int sy) +{ + int reclip; + #ifndef NDEBUG + int count = 0; + #endif + + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + + int bw, bh; + bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL ); + + do { + reclip = 0; + #ifndef NDEBUG + if ( count > 1 ) Int3(); + count++; + #endif + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx += gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy += gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) { + dx1 -= sx; + sx = 0; + reclip = 1; + } + + if ( sy < 0 ) { + dy1 -= sy; + sy = 0; + reclip = 1; + } + + w = dx2-dx1+1; + h = dy2-dy1+1; + + if ( sx + w > bw ) { + w = bw - sx; + dx2 = dx1 + w - 1; + } + + if ( sy + h > bh ) { + h = bh - sy; + dy2 = dy1 + h - 1; + } + + if ( w < 1 ) return; // clipped away! + if ( h < 1 ) return; // clipped away! + + } while (reclip); + + // Make sure clipping algorithm works + #ifndef NDEBUG + Assert( w > 0 ); + Assert( h > 0 ); + Assert( w == (dx2-dx1+1) ); + Assert( h == (dy2-dy1+1) ); + Assert( sx >= 0 ); + Assert( sy >= 0 ); + Assert( sx+w <= bw ); + Assert( sy+h <= bh ); + Assert( dx2 >= dx1 ); + Assert( dy2 >= dy1 ); + Assert( (dx1 >= gr_screen.clip_left ) && (dx1 <= gr_screen.clip_right) ); + Assert( (dx2 >= gr_screen.clip_left ) && (dx2 <= gr_screen.clip_right) ); + Assert( (dy1 >= gr_screen.clip_top ) && (dy1 <= gr_screen.clip_bottom) ); + Assert( (dy2 >= gr_screen.clip_top ) && (dy2 <= gr_screen.clip_bottom) ); + #endif + + // We now have dx1,dy1 and dx2,dy2 and sx, sy all set validly within clip regions. + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + + gr_glide_bitmap_ex_internal(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + + +void gr_glide_bitmap(int x, int y) +{ + int w, h; + + bm_get_info( gr_screen.current_bitmap, &w, &h, NULL ); + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + int sx=0, sy=0; + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx = gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy = gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) return; + if ( sy < 0 ) return; + if ( sx >= w ) return; + if ( sy >= h ) return; + + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + + gr_glide_bitmap_ex_internal(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + + +static void glide_scanline( int x1, int x2, int y ) +{ + int w; + + if(VOODOO3_INACTIVE()){ + return; + } + + if ( x1 > x2 ) { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + + if ( y < gr_screen.clip_top ) return; + if ( y > gr_screen.clip_bottom ) return; + + if ( x1 < gr_screen.clip_left ) x1 = gr_screen.clip_left; + if ( x2 > gr_screen.clip_right ) x2 = gr_screen.clip_right; + + w = x2 - x1 + 1; + + if ( w < 1 ) return; + +// for (i=0; i 0 ) + x2 = x + w - 1; + else + x2 = x + w + 1; + + if ( h > 0 ) + y2 = y + h - 1; + else + y2 = y + h + 1; + + if ( x2 < x1 ) { + int tmp; + tmp = x1; + x1 = x2; + x2 = tmp; + w = -w; + swapped = 1; + } + + if ( y2 < y1 ) { + int tmp; + tmp = y1; + y1 = y2; + y2 = tmp; + h = -h; + swapped = 1; + } + + for (int i=0; i x2 ) { + int tmp = x1; + x1 = x2; + x2 = tmp; + } + + if ( y < gr_screen.clip_top ) return; + if ( y > gr_screen.clip_bottom ) return; + + if ( x1 < gr_screen.clip_left ) x1 = gr_screen.clip_left; + if ( x2 > gr_screen.clip_right ) x2 = gr_screen.clip_right; + + w = x2 - x1 + 1; + + if ( w < 1 ) return; + + + // Set up Render State - flat shading - alpha blending + gr_glide_set_state( TEXTURE_SOURCE_NONE, COLOR_SOURCE_VERTEX, ALPHA_SOURCE_VERTEX, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE ); + + GrVertex v1,v2; + + v1.x = i2fl(x1 + gr_screen.offset_x); + v1.y = i2fl(y + gr_screen.offset_y); + v1.r = i2fl(r); + v1.g = i2fl(g); + v1.b = i2fl(b); + v1.a = i2fl(a); + + v2.x = i2fl(x2 + gr_screen.offset_x); + v2.y = i2fl(y + gr_screen.offset_y); + v2.r = i2fl(r); + v2.g = i2fl(g); + v2.b = i2fl(b); + v2.a = i2fl(a); + + if (v1.x 0 ) + x2 = x + w - 1; + else + x2 = x + w + 1; + + if ( h > 0 ) + y2 = y + h - 1; + else + y2 = y + h + 1; + + if ( x2 < x1 ) { + int tmp; + tmp = x1; + x1 = x2; + x2 = tmp; + w = -w; + swapped = 1; + } + + if ( y2 < y1 ) { + int tmp; + tmp = y1; + y1 = y2; + y2 = tmp; + h = -h; + swapped = 1; + } + + int r,g,b,a; + r = fl2i(gr_screen.current_shader.r*255.0f*shade1); + if ( r < 0 ) r = 0; else if ( r > 255 ) r = 255; + g = fl2i(gr_screen.current_shader.g*255.0f*shade1); + if ( g < 0 ) g = 0; else if ( g > 255 ) g = 255; + b = fl2i(gr_screen.current_shader.b*255.0f*shade1); + if ( b < 0 ) b = 0; else if ( b > 255 ) b = 255; + a = fl2i(gr_screen.current_shader.c*255.0f*shade2); + if ( a < 0 ) a = 0; else if ( a > 255 ) a = 255; + + for (int i=0; ichar_data[letter]; + + int sx = Current_font->bm_u[letter]; + int sy = Current_font->bm_v[letter]; + + gr_glide_aabitmap_ex_new( x, y, ch->byte_width, Current_font->h, sx, sy ); + +// mprintf(( "String = %s\n", text )); +} + + +void gr_glide_string( int sx, int sy, char *s ) +{ + int width, spacing, letter; + int x, y; + + if ( !Current_font ) { + return; + } + + gr_set_bitmap(Current_font->bitmap_id); + + x = sx; + y = sy; + + if (sx==0x8000) { //centered + x = get_centered_x(s); + } else { + x = sx; + } + + spacing = 0; + + while (*s) { + + x += spacing; + + while (*s== '\n' ) { + s++; + y += Current_font->h; + if (sx==0x8000) { //centered + x = get_centered_x(s); + } else { + x = sx; + } + } + if (*s == 0 ) break; + + letter = get_char_width(s[0],s[1],&width,&spacing); + s++; + + if (letter<0) { //not in font, draw as space + continue; + } + + gr_glide_char( x, y, letter ); + } +} + +void gr_glide_circle( int xc, int yc, int d ) +{ + + int p,x, y, r; + + r = d/2; + p=3-d; + x=0; + y=r; + + // Big clip + if ( (xc+r) < gr_screen.clip_left ) return; + if ( (xc-r) > gr_screen.clip_right ) return; + if ( (yc+r) < gr_screen.clip_top ) return; + if ( (yc-r) > gr_screen.clip_bottom ) return; + + while(xsx; + y1 = v1->sy; + + x2 = v2->sx; + y2 = v2->sy; + + a1 = (float)gr_screen.clip_left; + b1 = (float)gr_screen.clip_top; + a2 = (float)gr_screen.clip_right; + b2 = (float)gr_screen.clip_bottom; + + FL_CLIPLINE(x1,y1,x2,y2,a1,b1,a2,b2,return,clipped=1,swapped=1); + + gr_glide_set_state( TEXTURE_SOURCE_NONE, COLOR_SOURCE_VERTEX, ALPHA_SOURCE_VERTEX, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE ); + + GrVertex a,b; + + a.x = i2fl(fl2i(x1)) + gr_screen.offset_x; + a.y = i2fl(fl2i(y1)) + gr_screen.offset_y; + a.r = i2fl(gr_screen.current_color.red); + a.g = i2fl(gr_screen.current_color.green); + a.b = i2fl(gr_screen.current_color.blue); + a.a = i2fl(gr_screen.current_color.alpha); + + b.x = i2fl(fl2i(x2)) + gr_screen.offset_x; + b.y = i2fl(fl2i(y2)) + gr_screen.offset_y; + b.r = i2fl(gr_screen.current_color.red); + b.g = i2fl(gr_screen.current_color.green); + b.b = i2fl(gr_screen.current_color.blue); + b.a = i2fl(gr_screen.current_color.alpha); + + if (a.xu = verts[i]->v = 0.5f; + } + */ + } + + gr_texture_source texture_source = (gr_texture_source)-1; + gr_color_source color_source = (gr_color_source)-1; + gr_alpha_source alpha_source = (gr_alpha_source)-1; + gr_alpha_blend alpha_blend = (gr_alpha_blend)-1; + gr_zbuffer_type zbuffer_type = (gr_zbuffer_type)-1; + + if ( gr_zbuffering ) { + if ( is_scaler || (gr_screen.current_alphablend_mode == GR_ALPHABLEND_FILTER) ) { + zbuffer_type = ZBUFFER_TYPE_READ; + } else { + zbuffer_type = ZBUFFER_TYPE_FULL; + } + } else { + zbuffer_type = ZBUFFER_TYPE_NONE; + } + + float alpha; + + alpha_source = ALPHA_SOURCE_VERTEX; + + switch(gr_screen.current_alphablend_mode){ + case GR_ALPHABLEND_FILTER: + // Blend with screen pixel using src*alpha+dst + if ( gr_screen.current_alpha > 1.0f ) { + alpha_blend = ALPHA_BLEND_ALPHA_BLEND_SRC_COLOR; + alpha = 255.0f; + } else { + alpha_blend = ALPHA_BLEND_ALPHA_ADDITIVE; + alpha = gr_screen.current_alpha*255.0f; + } + break; + + default : + alpha_blend = ALPHA_BLEND_ALPHA_BLEND_ALPHA; + alpha = 255.0f; + break; + } + + if ( flags & TMAP_FLAG_TEXTURED ) { + int texture_type = TCACHE_TYPE_NORMAL; + + if ( flags & TMAP_FLAG_NONDARKENING ) { + alpha_source = ALPHA_SOURCE_VERTEX_NONDARKENING; + //alpha_source = ALPHA_SOURCE_VERTEX_TIMES_TEXTURE; + texture_type = TCACHE_TYPE_NONDARKENING; + } else { + alpha_source = ALPHA_SOURCE_VERTEX_TIMES_TEXTURE; + texture_type = TCACHE_TYPE_XPARENT; + } + + // common settings + color_source = COLOR_SOURCE_VERTEX_TIMES_TEXTURE; + texture_source = TEXTURE_SOURCE_DECAL; + + // force texture type + if(flags & TMAP_FLAG_BITMAP_SECTION){ + texture_type = TCACHE_TYPE_BITMAP_SECTION; + } + + if ( !gr_tcache_set( gr_screen.current_bitmap, texture_type, &Glide_u_ratio, &Glide_v_ratio, 0, gr_screen.current_bitmap_sx, gr_screen.current_bitmap_sy )) { + // Error setting texture! + mprintf(( "GLIDE: Error setting texture!\n" )); + // Mark as no texturing + color_source = COLOR_SOURCE_VERTEX; + texture_source = TEXTURE_SOURCE_NONE; + } + } else { + color_source = COLOR_SOURCE_VERTEX; + texture_source = TEXTURE_SOURCE_NONE; + if(flags & TMAP_FLAG_ALPHA){ + alpha_source = ALPHA_SOURCE_VERTEX; + } + } + +// zbuffer_type = ZBUFFER_TYPE_NONE; +// alpha_source = ALPHA_SOURCE_VERTEX; +// alpha_blend = ALPHA_BLEND_NONE; +// alpha = 255.0f; +// color_source = COLOR_SOURCE_VERTEX; +// texture_source = TEXTURE_SOURCE_NONE; + gr_glide_set_state( texture_source, color_source, alpha_source, alpha_blend, zbuffer_type ); + + int x1, y1, x2, y2; + x1 = gr_screen.clip_left*16; + x2 = gr_screen.clip_right*16+15; + y1 = gr_screen.clip_top*16; + y2 = gr_screen.clip_bottom*16+15; + +// memset( GrVerts, 0, sizeof(GrVertex) * 25 ); + + for (i=0; ia); + } else { + GrVerts[i].a = alpha; + } + + if ( flags & TMAP_FLAG_NEBULA ) { + int pal = (verts[i]->b*(NEBULA_COLORS-1))/255; + GrVerts[i].r = i2fl(gr_palette[pal*3+0]); + GrVerts[i].g = i2fl(gr_palette[pal*3+1]); + GrVerts[i].b = i2fl(gr_palette[pal*3+2]); + } else if ( (flags & TMAP_FLAG_RAMP) && (flags & TMAP_FLAG_GOURAUD) ) { + GrVerts[i].r = Gr_gamma_lookup_float[verts[i]->b]; + GrVerts[i].g = Gr_gamma_lookup_float[verts[i]->b]; + GrVerts[i].b = Gr_gamma_lookup_float[verts[i]->b]; + } else if ( (flags & TMAP_FLAG_RGB) && (flags & TMAP_FLAG_GOURAUD) ) { + // Make 0.75 be 256.0f + GrVerts[i].r = Gr_gamma_lookup_float[verts[i]->r]; + GrVerts[i].g = Gr_gamma_lookup_float[verts[i]->g]; + GrVerts[i].b = Gr_gamma_lookup_float[verts[i]->b]; + } else { + if ( flags & TMAP_FLAG_TEXTURED ) { + GrVerts[i].r = 255.0f; + GrVerts[i].g = 255.0f; + GrVerts[i].b = 255.0f; + } else { + GrVerts[i].r = i2fl(gr_screen.current_color.red); + GrVerts[i].g = i2fl(gr_screen.current_color.green); + GrVerts[i].b = i2fl(gr_screen.current_color.blue); + } + } + + int x, y; + x = fl2i(verts[i]->sx*16.0f); + y = fl2i(verts[i]->sy*16.0f); + + if ( flags & TMAP_FLAG_CORRECT ) { + // "clip" it + if ( x < x1 ) { + x = x1; + } else if ( x > x2 ) { + x = x2; + } + if ( y < y1 ) { + y = y1; + } else if ( y > y2 ) { + y = y2; + } + } + + x += gr_screen.offset_x*16; + y += gr_screen.offset_y*16; + + GrVerts[i].x = i2fl(x) / 16.0f; + GrVerts[i].y = i2fl(y) / 16.0f; + + //verts[i]->sw = 1.0f; + + GrVerts[i].oow=verts[i]->sw; + + if ( flags & TMAP_FLAG_TEXTURED ) { + GrVerts[i].tmuvtx[GR_TMU0].oow=verts[i]->sw; + GrVerts[i].tmuvtx[GR_TMU0].sow=verts[i]->u * verts[i]->sw * Glide_u_ratio; + GrVerts[i].tmuvtx[GR_TMU0].tow=verts[i]->v * verts[i]->sw * Glide_v_ratio; + } + } + + // if we're rendering against a fullneb background + if(flags & TMAP_FLAG_PIXEL_FOG){ + int r, g, b; + int ra, ga, ba; + ra = ga = ba = 0; + + // get the average pixel color behind the vertices + for(i=0; isx; y0 = va->sy; + x1 = vb->sx; y1 = vb->sy; + + xmin = i2fl(gr_screen.clip_left); ymin = i2fl(gr_screen.clip_top); + xmax = i2fl(gr_screen.clip_right); ymax = i2fl(gr_screen.clip_bottom); + + u0 = va->u; v0 = va->v; + u1 = vb->u; v1 = vb->v; + + // Check for obviously offscreen bitmaps... + if ( (y1<=y0) || (x1<=x0) ) return; + if ( (x1xmax) ) return; + if ( (y1ymax) ) return; + + clipped_u0 = u0; clipped_v0 = v0; + clipped_u1 = u1; clipped_v1 = v1; + + clipped_x0 = x0; clipped_y0 = y0; + clipped_x1 = x1; clipped_y1 = y1; + + // Clip the left, moving u0 right as necessary + if ( x0 < xmin ) { + clipped_u0 = FIND_SCALED_NUM(xmin,x0,x1,u0,u1); + clipped_x0 = xmin; + } + + // Clip the right, moving u1 left as necessary + if ( x1 > xmax ) { + clipped_u1 = FIND_SCALED_NUM(xmax,x0,x1,u0,u1); + clipped_x1 = xmax; + } + + // Clip the top, moving v0 down as necessary + if ( y0 < ymin ) { + clipped_v0 = FIND_SCALED_NUM(ymin,y0,y1,v0,v1); + clipped_y0 = ymin; + } + + // Clip the bottom, moving v1 up as necessary + if ( y1 > ymax ) { + clipped_v1 = FIND_SCALED_NUM(ymax,y0,y1,v0,v1); + clipped_y1 = ymax; + } + + dx0 = fl2i(clipped_x0); dx1 = fl2i(clipped_x1); + dy0 = fl2i(clipped_y0); dy1 = fl2i(clipped_y1); + + if (dx1<=dx0) return; + if (dy1<=dy0) return; + + //============= DRAW IT ===================== + + vertex v[4]; + vertex *vl[4]; + + vl[0] = &v[0]; + v->sx = clipped_x0; + v->sy = clipped_y0; + v->sw = va->sw; + v->z = va->z; + v->u = clipped_u0; + v->v = clipped_v0; + + vl[1] = &v[1]; + v[1].sx = clipped_x1; + v[1].sy = clipped_y0; + v[1].sw = va->sw; + v[1].z = va->z; + v[1].u = clipped_u1; + v[1].v = clipped_v0; + + vl[2] = &v[2]; + v[2].sx = clipped_x1; + v[2].sy = clipped_y1; + v[2].sw = va->sw; + v[2].z = va->z; + v[2].u = clipped_u1; + v[2].v = clipped_v1; + + vl[3] = &v[3]; + v[3].sx = clipped_x0; + v[3].sy = clipped_y1; + v[3].sw = va->sw; + v[3].z = va->z; + v[3].u = clipped_u0; + v[3].v = clipped_v1; + + //glide_zbuffering(0); + gr_glide_tmapper_internal( 4, vl, TMAP_FLAG_TEXTURED, 1 ); +} + + +void gr_glide_aabitmap_ex_new(int x,int y,int w,int h,int sx,int sy) +{ + if ( w < 1 ) return; + if ( h < 1 ) return; + + if ( !gr_screen.current_color.is_alphacolor ) return; + + if(VOODOO3_INACTIVE()){ + return; + } + +// mprintf(( "x=%d, y=%d, w=%d, h=%d\n", x, y, w, h )); +// mprintf(( "sx=%d, sy=%d, bw=%d, bh=%d\n", sx, sy, bmp->w, bmp->h )); + + float Glide_u_ratio; + float Glide_v_ratio; + + // Set up Render State - flat shading - alpha blending + gr_glide_set_state( TEXTURE_SOURCE_DECAL, COLOR_SOURCE_VERTEX, ALPHA_SOURCE_VERTEX_TIMES_TEXTURE, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE ); + + if ( !gr_tcache_set( gr_screen.current_bitmap, TCACHE_TYPE_AABITMAP, &Glide_u_ratio, &Glide_v_ratio ) ) { + // Couldn't set texture + mprintf(( "GLIDE: Error setting aabitmap texture!\n" )); + return; + } + + GrVertex GrVerts[4]; + + float u0, u1, v0, v1; + float r,g,b,a; + + r = i2fl(gr_screen.current_color.red); + g = i2fl(gr_screen.current_color.green); + b = i2fl(gr_screen.current_color.blue); + a = i2fl(gr_screen.current_color.alpha); + + int bw, bh; + + bm_get_info( gr_screen.current_bitmap, &bw, &bh ); + + u0 = Glide_u_ratio*i2fl(sx)/i2fl(bw); + v0 = Glide_v_ratio*i2fl(sy)/i2fl(bh); + + u1 = Glide_u_ratio*i2fl(sx+w)/i2fl(bw); + v1 = Glide_v_ratio*i2fl(sy+h)/i2fl(bh); + + float x1, x2, y1, y2; + x1 = i2fl(x+gr_screen.offset_x); + y1 = i2fl(y+gr_screen.offset_y); + x2 = i2fl(x+w+gr_screen.offset_x); + y2 = i2fl(y+h+gr_screen.offset_y); + + int i; + + i = 0; + GrVerts[i].x = x1; + GrVerts[i].y = y1; + GrVerts[i].oow = 1.0f; + GrVerts[i].r = r; + GrVerts[i].g = g; + GrVerts[i].b = b; + GrVerts[i].a = a; + GrVerts[i].tmuvtx[GR_TMU0].sow=u0; + GrVerts[i].tmuvtx[GR_TMU0].tow=v0; + + i = 1; + GrVerts[i].x = x2; + GrVerts[i].y = y1; + GrVerts[i].oow = 1.0f; + GrVerts[i].r = r; + GrVerts[i].g = g; + GrVerts[i].b = b; + GrVerts[i].a = a; + GrVerts[i].tmuvtx[GR_TMU0].sow=u1; + GrVerts[i].tmuvtx[GR_TMU0].tow=v0; + + i = 2; + GrVerts[i].x = x2; + GrVerts[i].y = y2; + GrVerts[i].oow = 1.0f; + GrVerts[i].r = r; + GrVerts[i].g = g; + GrVerts[i].b = b; + GrVerts[i].a = a; + GrVerts[i].tmuvtx[GR_TMU0].sow=u1; + GrVerts[i].tmuvtx[GR_TMU0].tow=v1; + + i = 3; + GrVerts[i].x = x1; + GrVerts[i].y = y2; + GrVerts[i].oow = 1.0f; + GrVerts[i].r = r; + GrVerts[i].g = g; + GrVerts[i].b = b; + GrVerts[i].a = a; + GrVerts[i].tmuvtx[GR_TMU0].sow=u0; + GrVerts[i].tmuvtx[GR_TMU0].tow=v1; + + grDrawPolygonVertexList( 4, GrVerts ); +} + + + +void gr_glide_aabitmap_ex(int x,int y,int w,int h,int sx,int sy) +{ + int reclip; + #ifndef NDEBUG + int count = 0; + #endif + + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + + int bw, bh; + bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL ); + + do { + reclip = 0; + #ifndef NDEBUG + if ( count > 1 ) Int3(); + count++; + #endif + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx += gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy += gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) { + dx1 -= sx; + sx = 0; + reclip = 1; + } + + if ( sy < 0 ) { + dy1 -= sy; + sy = 0; + reclip = 1; + } + + w = dx2-dx1+1; + h = dy2-dy1+1; + + if ( sx + w > bw ) { + w = bw - sx; + dx2 = dx1 + w - 1; + } + + if ( sy + h > bh ) { + h = bh - sy; + dy2 = dy1 + h - 1; + } + + if ( w < 1 ) return; // clipped away! + if ( h < 1 ) return; // clipped away! + + } while (reclip); + + // Make sure clipping algorithm works + #ifndef NDEBUG + Assert( w > 0 ); + Assert( h > 0 ); + Assert( w == (dx2-dx1+1) ); + Assert( h == (dy2-dy1+1) ); + Assert( sx >= 0 ); + Assert( sy >= 0 ); + Assert( sx+w <= bw ); + Assert( sy+h <= bh ); + Assert( dx2 >= dx1 ); + Assert( dy2 >= dy1 ); + Assert( (dx1 >= gr_screen.clip_left ) && (dx1 <= gr_screen.clip_right) ); + Assert( (dx2 >= gr_screen.clip_left ) && (dx2 <= gr_screen.clip_right) ); + Assert( (dy1 >= gr_screen.clip_top ) && (dy1 <= gr_screen.clip_bottom) ); + Assert( (dy2 >= gr_screen.clip_top ) && (dy2 <= gr_screen.clip_bottom) ); + #endif + + // We now have dx1,dy1 and dx2,dy2 and sx, sy all set validly within clip regions. + + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + gr_glide_aabitmap_ex_new(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + +void gr_glide_string_hack( int sx, int sy, char *s ) +{ + int width, spacing, letter; + int x, y; + + if ( !Current_font ) { + return; + } + + gr_set_bitmap(Current_font->bitmap_id); + + x = sx; + y = sy; + + if (sx==0x8000) { //centered + x = get_centered_x(s); + } else { + x = sx; + } + + spacing = 0; + + while (*s) { + + x += spacing; + + while (*s== '\n' ) { + s++; + y += Current_font->h; + if (sx==0x8000) { //centered + x = get_centered_x(s); + } else { + x = sx; + } + } + if (*s == 0 ) break; + + letter = get_char_width(s[0],s[1],&width,&spacing); + s++; + + if (letter<0) { //not in font, draw as space + continue; + } + + // formerly a call to gr_glide_char(...) + { + font_char *ch; + + ch = &Current_font->char_data[letter]; + + int _sx = Current_font->bm_u[letter]; + int _sy = Current_font->bm_v[letter]; + + gr_glide_aabitmap_ex( x, y, ch->byte_width, Current_font->h, _sx, _sy ); + } + } +} + +void gr_glide_aabitmap(int x, int y) +{ + int w, h; + + bm_get_info( gr_screen.current_bitmap, &w, &h, NULL ); + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + int sx=0, sy=0; + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx = gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy = gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) return; + if ( sy < 0 ) return; + if ( sx >= w ) return; + if ( sy >= h ) return; + + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + gr_aabitmap_ex(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + + +void gr_glide_gradient(int x1,int y1,int x2,int y2) +{ + int clipped = 0, swapped=0; + + if(VOODOO3_INACTIVE()){ + return; + } + + if ( !gr_screen.current_color.is_alphacolor ) { + gr_line( x1, y1, x2, y2 ); + return; + } + + INT_CLIPLINE(x1,y1,x2,y2,gr_screen.clip_left,gr_screen.clip_top,gr_screen.clip_right,gr_screen.clip_bottom,return,clipped=1,swapped=1); + + // Set up Render State - flat shading - alpha blending + gr_glide_set_state( TEXTURE_SOURCE_NONE, COLOR_SOURCE_VERTEX, ALPHA_SOURCE_VERTEX, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE ); + + GrVertex a,b; + + a.x = i2fl(x1 + gr_screen.offset_x); + a.y = i2fl(y1 + gr_screen.offset_y); + a.r = i2fl(gr_screen.current_color.red); + a.g = i2fl(gr_screen.current_color.green); + a.b = i2fl(gr_screen.current_color.blue); + + b.x = i2fl(x2 + gr_screen.offset_x); + b.y = i2fl(y2 + gr_screen.offset_y); + b.r = i2fl(gr_screen.current_color.red); + b.g = i2fl(gr_screen.current_color.green); + b.b = i2fl(gr_screen.current_color.blue); + + if ( swapped ) { + a.a = 0.0f; + b.a = i2fl(gr_screen.current_color.alpha); + } else { + b.a = 0.0f; + a.a = i2fl(gr_screen.current_color.alpha); + } + + if (a.xscreen_sig = gr_screen.signature; + c->red = unsigned char(r); + c->green = unsigned char(g); + c->blue = unsigned char(b); + c->alpha = 255; + c->ac_type = AC_TYPE_NONE; + c->alphacolor = -1; + c->is_alphacolor = 0; + c->magic = 0xAC01; +} + +void gr_glide_init_alphacolor( color *clr, int r, int g, int b, int alpha, int type ) +{ + if ( r < 0 ) r = 0; else if ( r > 255 ) r = 255; + if ( g < 0 ) g = 0; else if ( g > 255 ) g = 255; + if ( b < 0 ) b = 0; else if ( b > 255 ) b = 255; + if ( alpha < 0 ) alpha = 0; else if ( alpha > 255 ) alpha = 255; + + gr_glide_init_color( clr, r, g, b ); + + clr->alpha = unsigned char(alpha); + clr->ac_type = (ubyte)type; + clr->alphacolor = -1; + clr->is_alphacolor = 1; +} + +void gr_glide_set_color( int r, int g, int b ) +{ + Assert((r >= 0) && (r < 256)); + Assert((g >= 0) && (g < 256)); + Assert((b >= 0) && (b < 256)); + + gr_glide_init_color( &gr_screen.current_color, r, g, b ); +} + +void gr_glide_get_color( int * r, int * g, int * b ) +{ + if (r) *r = gr_screen.current_color.red; + if (g) *g = gr_screen.current_color.green; + if (b) *b = gr_screen.current_color.blue; +} + +void gr_glide_set_color_fast(color *dst) +{ + if ( dst->screen_sig != gr_screen.signature ) { + if ( dst->is_alphacolor ) { + gr_glide_init_alphacolor( dst, dst->red, dst->green, dst->blue, dst->alpha, dst->ac_type ); + } else { + gr_glide_init_color( dst, dst->red, dst->green, dst->blue ); + } + } + gr_screen.current_color = *dst; +} + + + +void gr_glide_flash(int r, int g, int b) +{ + int Flash_r = r; + int Flash_g = g; + int Flash_b = b; + + if(VOODOO3_INACTIVE()){ + return; + } + + CAP(Flash_r,0,255); + CAP(Flash_g,0,255); + CAP(Flash_b,0,255); + + if ( Flash_r || Flash_g || Flash_b ) { + gr_glide_set_state( TEXTURE_SOURCE_NONE, COLOR_SOURCE_VERTEX, ALPHA_SOURCE_VERTEX, ALPHA_BLEND_ALPHA_ADDITIVE, ZBUFFER_TYPE_NONE ); + + GrVertex GrVerts[4]; + + float r,g,b,a; + + r = i2fl(Flash_r); + g = i2fl(Flash_g); + b = i2fl(Flash_b); + a = i2fl(255); + + float x1, x2, y1, y2; + x1 = i2fl(gr_screen.clip_left+gr_screen.offset_x); + y1 = i2fl(gr_screen.clip_top+gr_screen.offset_y); + x2 = i2fl(gr_screen.clip_right+gr_screen.offset_x); + y2 = i2fl(gr_screen.clip_bottom+gr_screen.offset_y); + + int i; + + i = 0; + GrVerts[i].x = x1; + GrVerts[i].y = y1; + GrVerts[i].r = r; + GrVerts[i].g = g; + GrVerts[i].b = b; + GrVerts[i].a = a; + + i = 1; + GrVerts[i].x = x2; + GrVerts[i].y = y1; + GrVerts[i].r = r; + GrVerts[i].g = g; + GrVerts[i].b = b; + GrVerts[i].a = a; + + i = 2; + GrVerts[i].x = x2; + GrVerts[i].y = y2; + GrVerts[i].r = r; + GrVerts[i].g = g; + GrVerts[i].b = b; + GrVerts[i].a = a; + + i = 3; + GrVerts[i].x = x1; + GrVerts[i].y = y2; + GrVerts[i].r = r; + GrVerts[i].g = g; + GrVerts[i].b = b; + GrVerts[i].a = a; + + grDrawPolygonVertexList( 4, GrVerts ); + } + +} + + + +void gr_glide_activate(int active) +{ + if (!Glide_running) { + return; + } + + mprintf(( "Glide activate: %d\n", active )); + + // voodoo3 + if(Glide_voodoo3){ + // choose resolution + GrScreenResolution_t res_mode; + if((gr_screen.max_w == 1024) && (gr_screen.max_h == 768)){ + res_mode = GR_RESOLUTION_1024x768; + } else { + res_mode = GR_RESOLUTION_640x480; + } + + HWND hwnd = (HWND)os_get_window(); + + if ( active ) { + // already active + if(Glide_deactivate == 0){ + return; + } + + Glide_deactivate = 0; + + if ( hwnd ) { + SetActiveWindow(hwnd); + SetForegroundWindow(hwnd); + grSstWinOpen( (DWORD)hwnd, res_mode, GR_REFRESH_60Hz, GR_COLORFORMAT_ABGR, GR_ORIGIN_UPPER_LEFT, 2, 1 ); + ShowWindow(hwnd,SW_MAXIMIZE); + gr_glide_clip_cursor(1); + glide_tcache_init(); + grGammaCorrectionValue(1.0f); + } + } else { + // already deactivated + if(Glide_deactivate == VOODOO3_DEACTIVATED){ + return; + } + + Glide_deactivate = VOODOO3_DEACTIVATED; + + if ( hwnd ) { + gr_glide_clip_cursor(0); + ShowWindow(hwnd,SW_MINIMIZE); + grSstWinClose(); + } + } + } else { + HWND hwnd = (HWND)os_get_window(); + + if ( active ) { + Glide_activate++; + + if ( hwnd ) { + // SetActiveWindow(hwnd); + // SetForegroundWindow(hwnd); + ShowWindow(hwnd,SW_RESTORE); + // gr_glide_clip_cursor(1); + // grSstControl(GR_CONTROL_ACTIVATE); + } + } else { + Glide_deactivate++; + + if ( hwnd ) { + // grSstControl(GR_CONTROL_DEACTIVATE); + ClipCursor(NULL); + ShowWindow(hwnd,SW_MINIMIZE); + } + } + } +} + + +// copy from one pixel buffer to another +// +// from pointer to source buffer +// to pointer to dest. buffet +// pixels number of pixels to copy +// fromsize source pixel size +// tosize dest. pixel size + +static int tga_copy_data(char *to, char *from, int pixels, int fromsize, int tosize) +{ + int rmask = 0xf800; + int rshift = 11; + int rscale = 8;; + int gmask = 0x7e0; + int gshift = 5; + int gscale = 4; + int bmask = 0x1f; + int bshift = 0; + int bscale = 8;; + + if ( (fromsize == 2) && (tosize == 3) ) { + ushort *src = (ushort *)from; + ubyte *dst = (ubyte *)to; + + int i; + for (i=0; i>bshift)*bscale; + g = ((pixel & gmask)>>gshift)*gscale; + r = ((pixel & rmask)>>rshift)*rscale; + + // Adjust for gamma and output it + *dst++ = ubyte(b); + *dst++ = ubyte(g); + *dst++ = ubyte(r); + } + return tosize*pixels; + } else { + Int3(); + return tosize*pixels; + } +} + + + +// +// tga_pixels_equal -- Test if two pixels are identical +// +// Returns: +// 0 if No Match +// 1 if Match + +static int tga_pixels_equal(char *pix1, char *pix2, int pixbytes) +{ + do { + if ( *pix1++ != *pix2++ ) { + return 0; + } + } while ( --pixbytes > 0 ); + + return 1; +} + + +// tga_compress - Do the Run Length Compression +// +// Usage: +// out Buffer to write it out to +// in Buffer to compress +// bytecount Number of bytes input +// pixsize Number of bytes in input pixel +// outsize Number of bytes in output buffer + +int tga_compress(char *out, char *in, int bytecount ) +{ +#define pixsize 2 +#define outsize 3 + int pixcount; // number of pixels in the current packet + char *inputpixel=NULL; // current input pixel position + char *matchpixel=NULL; // pixel value to match for a run + char *flagbyte=NULL; // location of last flag byte to set + int rlcount; // current count in r.l. string + int rlthresh; // minimum valid run length + char *copyloc; // location to begin copying at + + // set the threshold -- the minimum valid run length + + #if outsize == 1 + rlthresh = 2; // for 8bpp, require a 2 pixel span before rle'ing + #else + rlthresh = 1; + #endif + + // set the first pixel up + + flagbyte = out; // place to put next flag if run + inputpixel = in; + pixcount = 1; + rlcount = 0; + copyloc = (char *)0; + + // loop till data processing complete + do { + + // if we have accumulated a 128-byte packet, process it + if ( pixcount == 129 ) { + *flagbyte = 127; + + // set the run flag if this is a run + + if ( rlcount >= rlthresh ) { + *flagbyte |= 0x80; + pixcount = 2; + } + + // copy the data into place + ++flagbyte; + flagbyte += tga_copy_data(flagbyte, copyloc, pixcount-1, pixsize, outsize); + pixcount = 1; + + // set up for next packet + continue; + } + + // if zeroth byte, handle as special case + if ( pixcount == 1 ) { + rlcount = 0; + copyloc = inputpixel; /* point to 1st guy in packet */ + matchpixel = inputpixel; /* set pointer to pix to match */ + pixcount = 2; + inputpixel += pixsize; + continue; + } + + // assembling a packet -- look at next pixel + + // current pixel == match pixel? + if ( tga_pixels_equal(inputpixel, matchpixel, outsize) ) { + + // establishing a run of enough length to + // save space by doing it + // -- write the non-run length packet + // -- start run-length packet + + if ( ++rlcount == rlthresh ) { + + // close a non-run packet + + if ( pixcount > (rlcount+1) ) { + // write out length and do not set run flag + + *flagbyte++ = (char)(pixcount - 2 - rlthresh); + + flagbyte += tga_copy_data(flagbyte, copyloc, (pixcount-1-rlcount), pixsize, outsize); + + copyloc = inputpixel; + pixcount = rlcount + 1; + } + } + } else { + + // no match -- either break a run or continue without one + // if a run exists break it: + // write the bytes in the string (outsize+1) + // start the next string + + if ( rlcount >= rlthresh ) { + + *flagbyte++ = (char)(0x80 | rlcount); + flagbyte += tga_copy_data(flagbyte, copyloc, 1, pixsize, outsize); + pixcount = 1; + continue; + } else { + + // not a match and currently not a run + // - save the current pixel + // - reset the run-length flag + rlcount = 0; + matchpixel = inputpixel; + } + } + pixcount++; + inputpixel += pixsize; + } while ( inputpixel < (in + bytecount)); + + // quit this buffer without loosing any data + + if ( --pixcount >= 1 ) { + *flagbyte = (char)(pixcount - 1); + if ( rlcount >= rlthresh ) { + *flagbyte |= 0x80; + pixcount = 1; + } + + // copy the data into place + ++flagbyte; + flagbyte += tga_copy_data(flagbyte, copyloc, pixcount, pixsize, outsize); + } + return(flagbyte-out); +} + + + +void gr_glide_print_screen(char *filename) +{ + GrLfbInfo_t info; + int i; + ubyte outrow[1024*3*4]; + + if(VOODOO3_INACTIVE()){ + return; + } + + if ( gr_screen.max_w > 1024 ) { + mprintf(( "Screen too wide for print_screen\n" )); + return; + } + + info.size=sizeof(GrLfbInfo_t); + + // get a read pointer + if ( grLfbLock( GR_LFB_READ_ONLY, GR_BUFFER_BACKBUFFER, GR_LFBWRITEMODE_565, + GR_ORIGIN_UPPER_LEFT, FXFALSE, &info)) { + int w=gr_screen.max_w,h=gr_screen.max_h; + ushort *rptr; + int short_per_row=info.strideInBytes/2; + + rptr = (ushort *)info.lfbPtr; + + + char tmp[1024]; + + strcpy( tmp, NOX(".\\")); // specify a path mean files goes in root + strcat( tmp, filename ); + strcat( tmp, NOX(".tga")); + + CFILE *f = cfopen(tmp, "wb"); + + // Write the TGA header + cfwrite_ubyte( 0, f ); // IDLength; + cfwrite_ubyte( 0, f ); // ColorMapType; + cfwrite_ubyte( 10, f ); // ImageType; // 2 = 24bpp, uncompressed, 10=24bpp rle compressed + cfwrite_ushort( 0, f ); // CMapStart; + cfwrite_ushort( 0, f ); // CMapLength; + cfwrite_ubyte( 0, f ); // CMapDepth; + cfwrite_ushort( 0, f ); // XOffset; + cfwrite_ushort( 0, f ); // YOffset; + cfwrite_ushort( (ushort)w, f ); // Width; + cfwrite_ushort( (ushort)h, f ); // Height; + cfwrite_ubyte( 24, f ); //PixelDepth; + cfwrite_ubyte( 0, f ); //ImageDesc; + + // Go through and read our pixels + for (i=0;i> 8); + g = (ubyte)((bit_16 & 0x07e0) >> 3); + b = (ubyte)((bit_16 & 0x001f) << 3); + + // swizzle the data to 1555 (BM_PIXEL_FORMAT_ARGB) + *dptr = 0; + bm_set_components((ubyte*)dptr++, &r, &g, &b, &a); + } + } + + // Release the lock + grLfbUnlock(GR_LFB_READ_ONLY, front ? GR_BUFFER_FRONTBUFFER : GR_BUFFER_BACKBUFFER); + } +} + +void gr_glide_flush_frame_dump() +{ + int i,j; + char filename[MAX_PATH_LEN], *movie_path = ".\\"; + ubyte outrow[1024*3*4]; + + if(VOODOO3_INACTIVE()){ + return; + } + + if ( gr_screen.max_w > 1024) { + mprintf(( "Screen too wide for frame_dump\n" )); + return; + } + + for (i = 0; i < Glide_dump_frame_count; i++) { + + int w = gr_screen.max_w; + int h = gr_screen.max_h; + + sprintf(filename, NOX("%sfrm%04d.tga"), movie_path, Glide_dump_frame_number ); + Glide_dump_frame_number++; + + CFILE *f = cfopen(filename, "wb"); + + // Write the TGA header + cfwrite_ubyte( 0, f ); // IDLength; + cfwrite_ubyte( 0, f ); // ColorMapType; + cfwrite_ubyte( 10, f ); // ImageType; // 2 = 24bpp, uncompressed, 10=24bpp rle compressed + cfwrite_ushort( 0, f ); // CMapStart; + cfwrite_ushort( 0, f ); // CMapLength; + cfwrite_ubyte( 0, f ); // CMapDepth; + cfwrite_ushort( 0, f ); // XOffset; + cfwrite_ushort( 0, f ); // YOffset; + cfwrite_ushort( (ushort)w, f ); // Width; + cfwrite_ushort( (ushort)h, f ); // Height; + cfwrite_ubyte( 24, f ); //PixelDepth; + cfwrite_ubyte( 0, f ); //ImageDesc; + + // Go through and write our pixels + for (j=0;j 255 ) { + c = 255; + } + + gr_glide_restore_screen_internal(tmp_data); + + for (int i=0; i=0) ); // Loop as long as time not up and timer hasn't rolled + + stop_time = timer_get_fixed_seconds(); + + mprintf(( "Took %d frames (and %.1f secs) to fade out\n", count, f2fl(stop_time-start_time) )); + + } + } + } + + gr_clear(); + gr_flip(); + gr_palette_faded_out = 1; + Mouse_hidden--; + + if ( tmp_data ) { + free(tmp_data); + } +} + +void gr_glide_fade_in(int instantaneous) +{ + ushort *tmp_data; + + if(VOODOO3_INACTIVE()){ + return; + } + + Mouse_hidden++; + gr_reset_clip(); + + tmp_data = (ushort *)malloc( gr_screen.max_w*gr_screen.max_h*sizeof(ushort) ); + + if ( tmp_data ) { + gr_glide_save_screen_internal(tmp_data); + } + + if (gr_palette_faded_out) { + + gr_palette_faded_out = 0; + + if ( !instantaneous ) { + + if ( tmp_data ) { + int count = 0; + fix start_time, stop_time, t1; + + + start_time = timer_get_fixed_seconds(); + t1 = 0; + + do { + int c = (255*(FADE_TIME-t1))/FADE_TIME; + if (c < 0 ) { + c = 0; + } else if ( c > 255 ) { + c = 255; + } + + gr_glide_restore_screen_internal(tmp_data); + + for (int i=0; i=0) ); // Loop as long as time not up and timer hasn't rolled + + stop_time = timer_get_fixed_seconds(); + + mprintf(( "Took %d frames (and %.1f secs) to fade out\n", count, f2fl(stop_time-start_time) )); + + } + } + } + + + if ( tmp_data ) { + gr_glide_restore_screen_internal(tmp_data); + } + gr_flip(); + Mouse_hidden--; + + if ( tmp_data ) { + free(tmp_data); + } +} + +void gr_glide_cleanup() +{ + if ( !Inited ) return; + + + grGlideShutdown(); + + vglide_close(); + + gr_glide_clip_cursor(0); + + glide_tcache_cleanup(); + + Inited = 0; +} + +void gr_glide_set_gamma(float gamma) +{ + Gr_gamma = gamma; + Gr_gamma_int = int(Gr_gamma*100); + + // Create the Gamma lookup table + int i; + for (i=0; i<256; i++ ) { + int v = fl2i(pow(i2fl(i)/255.0f, 1.0f/Gr_gamma)*255.0f); + if ( v > 255 ) { + v = 255; + } else if ( v < 0 ) { + v = 0; + } + Gr_gamma_lookup[i] = v; + } + + for (i=0; i<256; i++ ) { + float v = (float)pow(i2fl(i)/255.0f, 1.0f/Gr_gamma)*255.0f; + if ( v > 255.0f ) { + v = 255.0f; + } else if ( v < 0.0f ) { + v = 0.0f; + } + Gr_gamma_lookup_float[i] = v; + } + + // Flush any existing textures + glide_tcache_flush(); + +} + +void gr_glide_fog_set(int fog_mode, int r, int g, int b, float fog_near, float fog_far) +{ + GrColor_t color = 0; + + if(VOODOO3_INACTIVE()){ + return; + } + + Assert((r >= 0) && (r < 256)); + Assert((g >= 0) && (g < 256)); + Assert((b >= 0) && (b < 256)); + + // store the values + gr_glide_init_color( &gr_screen.current_fog_color, r, g, b ); + if(fog_near >= 0.0f){ + gr_screen.fog_near = fog_near; + } + if(fog_far >= 0.0f){ + gr_screen.fog_far = fog_far; + } + gr_screen.current_fog_mode = fog_mode; + + // enable/disable fog + if(fog_mode == GR_FOGMODE_NONE){ + grFogMode(GR_FOG_DISABLE); + + // always unset the global for value if we're disabling fog + gr_screen.fog_near = -1.0f; + gr_screen.fog_far = -1.0f; + + return; + } + grFogMode(GR_FOG_WITH_TABLE); + + // set the fog color + color |= ((ubyte)r); + color |= ((ubyte)g << 8); + color |= ((ubyte)b << 16); + grFogColorValue(color); + + // only generate a new fog table if we have to + if((fog_near >= 0.0f) && (fog_far > fog_near)){ + guFogGenerateLinear(Glide_linear_fogtable, fog_near, fog_far); + } + + // set the fog table + grFogTable(Glide_linear_fogtable); +} + +void gr_glide_get_pixel(int x, int y, int *r, int *g, int *b) +{ + ushort pixel; + *r = 0; + *g = 0; + *b = 0; + + if(VOODOO3_INACTIVE()){ + return; + } + + // returns data in 565 format + grLfbReadRegion(GR_BUFFER_BACKBUFFER, (FxU32)x, (FxU32)y, 1, 1, 2, &pixel); + + // unpack pixel color + *r = (0xf800 & pixel) >> 8; + *g = (0x07e0 & pixel) >> 3; + *b = (0x001f & pixel) << 3; +} + +// resolution checking +int gr_glide_supports_res_ingame(int res) +{ + return 1; +} + +int gr_glide_supports_res_interface(int res) +{ + return 1; +} + +void gr_glide_set_cull(int cull) +{ +} + +void gr_glide_filter_set(int filter) +{ + if(VOODOO3_INACTIVE()){ + return; + } + + if(filter){ + grTexFilterMode(GR_TMU0, GR_TEXTUREFILTER_BILINEAR, GR_TEXTUREFILTER_BILINEAR); + } else { + grTexFilterMode(GR_TMU0, GR_TEXTUREFILTER_POINT_SAMPLED, GR_TEXTUREFILTER_POINT_SAMPLED); + } +} + +// set clear color +void gr_glide_set_clear_color(int r, int g, int b) +{ + gr_init_color(&gr_screen.current_clear_color, r, g, b); +} + +extern int movie_rendered_flag; +extern int movie_frame_count; + +void __cdecl grglide_MovieShowFrame(void *buf,uint bufw,uint bufh, uint sx,uint sy,uint w,uint h,uint dstx,uint dsty, uint hi_color) +{ + // no soup for you! + + /* + RECT srect, drect; + + if(VOODOO3_INACTIVE()){ + return; + } + + gr_reset_clip(); + gr_clear(); + + ushort *src_data = (ushort *)buf; + + movie_rendered_flag = 1; + + if ( hi_color ) { + bufw >>= 1; + sx >>= 1; + w >>= 1; + dstx >>= 1; + } + + SetRect(&srect, sx, sy, sx+w-1, sy+h-1); + //SetRect(&drect, dstx, dsty, dstx+w-1, dsty+h-1); + dstx = (gr_screen.max_w - w)/2; + dsty = (gr_screen.max_h - h)/2; + SetRect(&drect, dstx, dsty, dstx+w-1, dsty+h-1); + + GrLfbInfo_t info; + int i; + + info.size=sizeof(GrLfbInfo_t); + + // get a read pointer + if ( grLfbLock( GR_LFB_WRITE_ONLY, GR_BUFFER_BACKBUFFER, GR_LFBWRITEMODE_1555, + GR_ORIGIN_UPPER_LEFT, FXFALSE, &info)) { + ushort *rptr; + int short_per_row=info.strideInBytes/2; + + rptr = (ushort *)info.lfbPtr; + + // if doing interlaced mode, then go through every other scanline + if ( Interlace_movies ) { + static int start_at = 0; + + for ( i = start_at; i < (int)h; i += 2 ) { + memcpy( &rptr[(dsty+i)*short_per_row+dstx], &src_data[bufw*i], w*sizeof(short) ); + } + //start_at = (start_at + 1) % 2; + } else { + // Go through and read our pixels + for (i=0;i<(int)h;i++) { + memcpy( &rptr[(dsty+i)*short_per_row+dstx], &src_data[bufw*i], w*sizeof(short) ); + } + } + + // Release the lock + grLfbUnlock( GR_LFB_WRITE_ONLY, GR_BUFFER_BACKBUFFER ); + } else { + mprintf(( "Couldn't get a write lock to glide's back buffer!\n" )); + } + + Mouse_hidden++; + gr_flip(); + Mouse_hidden--; + */ +} + +// cross fade +#define FETCH_A(i, j) { \ + ubyte code = 0; \ + code |= ((i+min_xw) << 2); \ + code |= ((j+min_yh) << 3); \ + if(code && (code < 4)){ \ + pixel_a = sptr1[i - (x1 - min_x)]; \ + } else { \ + pixel_a = pixel_black; \ + } \ +} +#define FETCH_B(i, j) { \ + ubyte code = 0; \ + code |= ((i+min_xw) << 2); \ + code |= ((j+min_yh) << 3); \ + if(code && (code < 4)){ \ + pixel_b = sptr2[i - (x2 - min_x)]; \ + } else { \ + pixel_b = pixel_black; \ + } \ +} +#define MIX(pout, p1, p2) { pout = p1; } +void gr_glide_cross_fade(int bmap1, int bmap2, int x1, int y1, int x2, int y2, float pct) +{ + if ( pct <= 50 ) { + gr_set_bitmap(bmap1); + gr_bitmap(x1, y1); + } else { + gr_set_bitmap(bmap2); + gr_bitmap(x2, y2); + } + /* + int min_x = x1 < x2 ? x1 : x2; + int min_y = y1 < y2 ? y1 : y2; + int max_x = x2 > x1 ? x2 : x1; + int max_y = y2 > y1 ? y2 : y1; + int max_w, max_h; + int i, j; + ushort *sptr1; + ushort *sptr2; + bitmap *bmp1, *bmp2; + ushort pixel_a, pixel_b; + ushort pixel_black; + ushort pixel_out; + ubyte r, g, b, a; + + // stuff the black pixel + r = 0; g = 0; b = 0; a = 255; + pixel_black = 0; + bm_set_components(&pixel_black, &r, &g, &b, &a); + + // lock the first bitmap + bmp1 = bm_lock( bmap1, 16, 0 ); + sptr1 = (ushort *)( bmp1->data ); + + // lock the second bitmap + bmp2 = bm_lock( bmap2, 16, 0 ); + sptr2 = (ushort *)( bmp2->data ); + + if(x1 > x2){ + max_x = x1; + min_x = x2; + } else { + min_x = x1; + max_x = x2; + } + if(y1 > y2){ + max_y = y1; + min_y = y2; + } else { + min_y = y1; + max_y = y2; + } + if(bmp1->w > bmp2->w){ + max_w = bmp1->w; + } else { + max_w = bmp2->w; + } + if(bmp1->h > bmp2->h){ + max_h = bmp1->h; + } else { + max_h = bmp2->h; + } + + GrLfbInfo_t info; + + // lock the framebuffer + if ( grLfbLock( GR_LFB_WRITE_ONLY, GR_BUFFER_BACKBUFFER, GR_LFBWRITEMODE_1555, GR_ORIGIN_UPPER_LEFT, FXFALSE, &info) ) { + + // pointer into vram + ushort *vram = (ushort *)info.lfbPtr; + int stride = info.strideInBytes / sizeof(ushort); + + // for all scanlines + for (i=0; i=x1) << 0); + code |= ((j+min_y>=y1) << 1); + code |= ((i+min_x>x1+bmp1->w) << 2); + code |= ((j+min_y>y1+bmp1->h) << 3); + if(code && (code < 4)){ + pixel_a = sptr1[i - (x1 - min_x)]; + } else { + pixel_a = pixel_black; + } + } + { + ubyte code = 0; + code |= ((i+min_x>=x2) << 0); + code |= ((j+min_y>=y2) << 1); + code |= ((i+min_x>x2+bmp2->w) << 2); + code |= ((j+min_y>y2+bmp2->h) << 3); + if(code && (code < 4)){ + pixel_b = sptr2[i - (x2 - min_x)]; + } else { + pixel_b = pixel_black; + } + } + + + // mix them - for now just always pick pixel A + MIX(pixel_out, pixel_a, pixel_b); + + // write to vram + *dptr = pixel_out; + + // next pixel in vram + dptr++; + } + + // increment if we need to + if((j+min_y >= y1) && (j+min_y < y1+bmp1->h)){ + sptr1 += bmp1->w; + } + // increment if we need to + if((j+min_y >= y2) && (j+min_y < y2+bmp2->h)){ + sptr2 += bmp2->w; + } + } + + grLfbUnlock(GR_LFB_WRITE_ONLY, GR_BUFFER_BACKBUFFER); + } + + bm_unlock(bmap1); + bm_unlock(bmap2); + */ +} + +GrHwConfiguration hwconfig; + +void gr_glide_init() +{ + D3D_enabled = 1; + Glide_running = 0; + int res_mode; + + if ( Inited ) { + gr_glide_cleanup(); + Inited = 0; + } + + // Turn off the 3Dfx splash screen + SetEnvironmentVariable("FX_GLIDE_NO_SPLASH","1"); + + // Turn off the 3Dfx gamma correction + SetEnvironmentVariable("SST_RGAMMA","1.0"); + SetEnvironmentVariable("SST_GGAMMA","1.0"); + SetEnvironmentVariable("SST_BGAMMA","1.0"); + + mprintf(( "Initializing glide graphics device...\n" )); + Inited = 1; + + if ( !vglide_init() ) { + mprintf(( "Glide DLL not found!\n" )); + exit(1); + } + +// os_suspend(); + + // Find the extents of the window + HWND hwnd = (HWND)os_get_window(); + + // Prepare the window to go full screen +#ifndef NDEBUG + mprintf(( "Window in debugging mode... mouse clicking may cause problems!\n" )); + SetWindowLong( hwnd, GWL_EXSTYLE, WS_EX_TRANSPARENT ); + SetWindowLong( hwnd, GWL_STYLE, WS_POPUP ); + ShowWindow(hwnd, SW_SHOWNORMAL ); + RECT work_rect; + SystemParametersInfo( SPI_GETWORKAREA, 0, &work_rect, 0 ); + SetWindowPos( hwnd, HWND_NOTOPMOST, work_rect.left, work_rect.top, gr_screen.max_w, gr_screen.max_h, 0 ); + SetActiveWindow(hwnd); + SetForegroundWindow(hwnd); + Glide_cursor_clip_rect.left = work_rect.left; + Glide_cursor_clip_rect.top = work_rect.top; + Glide_cursor_clip_rect.right = work_rect.left + gr_screen.max_w - 1; + Glide_cursor_clip_rect.bottom = work_rect.top + gr_screen.max_h - 1; +#else + SetWindowLong( hwnd, GWL_EXSTYLE, 0 ); + SetWindowLong( hwnd, GWL_STYLE, WS_POPUP ); + ShowWindow(hwnd, SW_SHOWNORMAL ); + SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, GetSystemMetrics( SM_CXSCREEN ), GetSystemMetrics( SM_CYSCREEN ), 0 ); + SetActiveWindow(hwnd); + SetForegroundWindow(hwnd); + Glide_cursor_clip_rect.left = 0; + Glide_cursor_clip_rect.top = 0; + Glide_cursor_clip_rect.right = gr_screen.max_w; + Glide_cursor_clip_rect.bottom = gr_screen.max_h; +#endif + + // Let things catch up.... + Sleep(2000); + + grGlideInit(); + + gr_screen.bytes_per_pixel = 2; + gr_screen.bits_per_pixel = 16; + + char version[80]; + grGlideGetVersion(version); + mprintf(( "Glide version: %s\n", version )); + + if ( !grSstQueryHardware( &hwconfig )) { + mprintf(( "Glide: Query hardaware failed!\n" )); + os_resume(); + exit(1); + } + + grSstSelect(0); + + // voodoo 3 + Glide_voodoo3 = 0; + if(hwconfig.SSTs[0].sstBoard.Voodoo2Config.fbRam >= 12){ + Glide_voodoo3 = 1; + } + + // choose resolution + if((gr_screen.max_w == 1024) && (gr_screen.max_h == 768)){ + res_mode = GR_RESOLUTION_1024x768; + } else { + res_mode = GR_RESOLUTION_640x480; + } + + int retval = grSstWinOpen( (DWORD)hwnd, res_mode, GR_REFRESH_60Hz, GR_COLORFORMAT_ABGR, GR_ORIGIN_UPPER_LEFT, 2, 1 ); + if ( !retval ) { + mprintf(( "Glide: grSstOpen failed!\n" )); + os_resume(); + exit(1); + } + + // pixel format + Bm_pixel_format = BM_PIXEL_FORMAT_ARGB; + +// grChromakeyMode( GR_CHROMAKEY_ENABLE ); +// grChromakeyValue( 0x00FF00 ); + +// os_resume(); + + // Setup the surface format + Gr_red.bits = 5; + Gr_red.shift = 10; + Gr_red.scale = 256/32; + Gr_red.mask = 0x7C00; + Gr_t_red = Gr_red; + Gr_current_red = &Gr_red; + + Gr_green.bits = 5; + Gr_green.shift = 5; + Gr_green.scale = 256/32; + Gr_green.mask = 0x03e0; + Gr_t_green = Gr_green; + Gr_current_green = &Gr_green; + + Gr_blue.bits = 5; + Gr_blue.shift = 0; + Gr_blue.scale = 256/32; + Gr_blue.mask = 0x1F; + Gr_t_blue = Gr_blue; + Gr_current_blue = &Gr_blue; + + Gr_current_alpha = &Gr_alpha; + + glide_tcache_init(); + + gr_glide_clip_cursor(1); + + grGammaCorrectionValue(1.0f); + + gr_screen.gf_flip = gr_glide_flip; + gr_screen.gf_flip_window = gr_glide_flip_window; + gr_screen.gf_set_clip = gr_glide_set_clip; + gr_screen.gf_reset_clip = gr_glide_reset_clip; + gr_screen.gf_set_font = grx_set_font; + gr_screen.gf_set_color = gr_glide_set_color; + gr_screen.gf_set_bitmap = gr_glide_set_bitmap; + gr_screen.gf_create_shader = gr_glide_create_shader; + gr_screen.gf_set_shader = gr_glide_set_shader; + gr_screen.gf_clear = gr_glide_clear; + + // gr_screen.gf_bitmap = gr_glide_bitmap; + // gr_screen.gf_bitmap_ex = gr_glide_bitmap_ex; + + gr_screen.gf_rect = gr_glide_rect; + gr_screen.gf_shade = gr_glide_shade; + gr_screen.gf_string = gr_glide_string; + gr_screen.gf_circle = gr_glide_circle; + + gr_screen.gf_line = gr_glide_line; + gr_screen.gf_aaline = gr_glide_aaline; + gr_screen.gf_pixel = gr_glide_pixel; + gr_screen.gf_scaler = gr_glide_scaler; + gr_screen.gf_aascaler = gr_glide_aascaler; + gr_screen.gf_tmapper = gr_glide_tmapper; + + gr_screen.gf_gradient = gr_glide_gradient; + + gr_screen.gf_set_palette = gr_glide_set_palette; + gr_screen.gf_get_color = gr_glide_get_color; + gr_screen.gf_init_color = gr_glide_init_color; + gr_screen.gf_init_alphacolor = gr_glide_init_alphacolor; + gr_screen.gf_set_color_fast = gr_glide_set_color_fast; + gr_screen.gf_print_screen = gr_glide_print_screen; + + gr_screen.gf_aabitmap = gr_glide_aabitmap; + gr_screen.gf_aabitmap_ex = gr_glide_aabitmap_ex; + + gr_screen.gf_fade_in = gr_glide_fade_in; + gr_screen.gf_fade_out = gr_glide_fade_out; + gr_screen.gf_flash = gr_glide_flash; + + gr_screen.gf_zbuffer_get = gr_glide_zbuffer_get; + gr_screen.gf_zbuffer_set = gr_glide_zbuffer_set; + gr_screen.gf_zbuffer_clear = gr_glide_zbuffer_clear; + + gr_screen.gf_save_screen = gr_glide_save_screen; + gr_screen.gf_restore_screen = gr_glide_restore_screen; + gr_screen.gf_free_screen = gr_glide_free_screen; + + // Screen dumping stuff + gr_screen.gf_dump_frame_start = gr_glide_dump_frame_start; + gr_screen.gf_dump_frame_stop = gr_glide_dump_frame_stop; + gr_screen.gf_dump_frame = gr_glide_dump_frame; + + gr_screen.gf_set_gamma = gr_glide_set_gamma; + + // Lock/unlock stuff + gr_screen.gf_lock = gr_glide_lock; + gr_screen.gf_unlock = gr_glide_unlock; + + // region + gr_screen.gf_get_region = gr_glide_get_region; + + // fog stuff + gr_screen.gf_fog_set = gr_glide_fog_set; + + // pixel get + gr_screen.gf_get_pixel = gr_glide_get_pixel; + + // poly culling + gr_screen.gf_set_cull = gr_glide_set_cull; + + // cross fade + gr_screen.gf_cross_fade = gr_glide_cross_fade; + + // filter + gr_screen.gf_filter_set = gr_glide_filter_set; + + // texture cache set + gr_screen.gf_tcache_set = glide_tcache_set; + + // set clear color + gr_screen.gf_set_clear_color = gr_glide_set_clear_color; + + Glide_running=1; + + // default linear fog table + guFogGenerateLinear(Glide_linear_fogtable, 1.0f, 1000.0f); + + Mouse_hidden++; + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; +} + diff --git a/src/graphics/grglidetexture.cpp b/src/graphics/grglidetexture.cpp new file mode 100644 index 0000000..c6a8aa8 --- /dev/null +++ b/src/graphics/grglidetexture.cpp @@ -0,0 +1,1052 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrGlideTexture.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to manage Glide texture RAM + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 14 10/13/99 9:22a Daveb + * Fixed Fred jumpnode placing bug. Fixed 1024 glide tiled texture problem + * related to movies. Fixed launcher spawning from PXO screen. + * + * 13 7/16/99 1:49p Dave + * 8 bit aabitmaps. yay. + * + * 12 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 11 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 10 7/08/99 8:10a Mikek + * Suppress compiler warning. Now I get an FS2 programming credit! :) + * + * 9 6/29/99 4:16p Dave + * Temporary speedup for tcache init. + * + * 8 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 7 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 6 2/08/99 5:07p Dave + * FS2 chat server support. FS2 specific validated missions. + * + * 5 1/15/99 11:29a Neilk + * Fixed D3D screen/texture pixel formatting problem. + * + * 4 12/01/98 8:06a Dave + * Temporary checkin to fix some texture transparency problems in d3d. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 18 5/14/98 5:39p John + * Added in code for multiple non-dimming lights. + * + * 17 4/27/98 9:33p John + * Removed mprintf + * + * 16 4/27/98 9:25p John + * Made Glide skip any 2MB boundry, not just first one. + * + * 15 4/27/98 9:09p John + * Fixed bug where texture was crossing a 2MB line on Voodoo2. Added code + * to tell how full VRAM is and how much got paged in each frame. + * + * 14 4/27/98 10:44a John + * Put in a new texture caching algorithm that doesn't flush everything + * every so oftem. + * + * 13 4/26/98 12:02p John + * Made glide texturing do its own allocation rather than using the gu_ + * functions. + * + * 12 4/26/98 11:14a John + * Restructured/Cleaned up the caching code in preparation for the new + * improved caching system. + * + * 11 4/22/98 9:13p John + * Added code to replace frames of animations in vram if so desired. + * + * 10 4/21/98 9:16a John + * Fixed bug with directives display in Glide. + * + * 9 4/20/98 8:41p John + * Made debris culling actually reduce Glide texture resolution. + * + * 8 4/09/98 4:38p John + * Made non-darkening and transparent textures work under Glide. Fixed + * bug with Jim's computer not drawing any bitmaps. + * + * 7 4/09/98 2:21p John + * Fixed transparency bug with Glide + * + * 6 4/08/98 9:16p John + * Made transparency work for textures in Glide by using chromakey. Made + * nondarkening colors work. + * + * 5 4/08/98 8:47a John + * Moved all texture caching into a new module + * + * 4 4/06/98 4:45p John + * Removed some debug code that made textures 2x too small. + * + * 3 3/04/98 5:43p Hoffoss + * Fixed warning. + * + * 2 3/04/98 3:50p John + * Made the Glide texture cache manager of Leighton's work with 256x256 + * maps. + * + * 1 3/03/98 4:42p John + * Added in Leighton's code to do texture caching on Glide. + * + * $NoKeywords: $ + */ + +//#define USE_8BPP_TEXTURES + +#include +#include +#include "glide.h" +#include "glideutl.h" + +#include "osapi.h" +#include "2d.h" +#include "bmpman.h" +#include "floating.h" +#include "palman.h" +#include "grinternal.h" +#include "grglide.h" +#include "line.h" +#include "font.h" +#include "mouse.h" +#include "key.h" +#include "systemvars.h" +#include "grglideinternal.h" + +#define TEXMEM_2MB_EDGE (2*1024*1024) + +// Hardware specific values +typedef struct tcache_data { + GrLOD_t lod; + GrAspectRatio_t aspect; + GrTextureFormat_t format; + uint vram_offset; + int vram_size; +} tcache_data; + +typedef struct tcache_slot { + int bitmap_id; + float uscale; + float vscale; + + tcache_data data; + tcache_slot *data_sections[MAX_BMAP_SECTIONS_X][MAX_BMAP_SECTIONS_Y]; // NULL if no subsections are present. + tcache_slot *parent; +} tcache_slot; + +#define MAX_AUX_TEXTURES 200 +#define MAX_TEXTURES MAX_BITMAPS + +// search for AUX to find all instancees of aux textures which can be put back in. + +// AUX +// static tcache_slot *Textures_aux = NULL; // auxiliary textures for storing sections of a texture +static void *Texture_sections = NULL; +static tcache_slot *Textures = NULL; +ubyte *Glide_tmp_ram = NULL; + +int Glide_last_bitmap_id = -1; +int Glide_last_bitmap_type = -1; +int Glide_last_section_x = -1; +int Glide_last_section_y = -1; +float Glide_last_u_ratio, Glide_last_v_ratio; + +static int Glide_last_detail = -1; + +uint Glide_total_memory; +uint Glide_start_address; +uint Glide_end_address; +uint Glide_current_address; + +int Glide_textures_in_frame = 0; + +int Glide_explosion_vram = 0; + +//======================================================= +// Code to manage texture memory. +// This basically allocates until out of VRAM, and then starts freeing +// blocks at the start of VRAM to make room for new textures. +// Based on code from Jason Scannell's webpage. +// http://members.home.net/jscannell. + +typedef struct { + int next; + uint start_address; + int size; + tcache_slot *texture_ptr; +} tblock; + +tblock *Tblocks = NULL; // Memory tracking blocks +int Tblock_num_blocks = 0; // How many blocks to use + +int Tblock_freelist_start; // First block in free list +int Tblock_usedlist_start; // First block in allocation list +int Tblock_usedlist_head; // Last block in allocation list + +uint Tblock_min_address; // Lowest address in texture memory +uint Tblock_max_address; // Highest address in texture memory + +void FlushBlocks() +{ + int i; + + memset(Tblocks,0,sizeof(tblock)*Tblock_num_blocks); + + for(i = 0; i < Tblock_num_blocks - 1; i++) { + Tblocks[i].next = i + 1; + } + + Tblocks[Tblock_num_blocks - 1].next = -1; + + Tblock_usedlist_start = -1; + Tblock_usedlist_head = -1; + Tblock_freelist_start = 0; +} + +void InitBlocks(uint min, uint max, int num_blocks) +{ + Tblock_num_blocks = num_blocks; + Tblock_min_address = min; + Tblock_max_address = max; + + if(Tblocks == NULL){ + Tblocks = (tblock *)malloc(Tblock_num_blocks*sizeof(tblock)); + Assert(Tblocks!=NULL); + } + + FlushBlocks(); +} + +void ReleaseBlocks() +{ + if ( Tblocks ) { + free(Tblocks); + Tblocks = NULL; + } + Tblock_num_blocks = 0; +} + +void ReleaseSlotSub(tcache_slot *t) +{ + int idx, s_idx; + + if(t == NULL){ + return; + } + + t->bitmap_id = -1; + for(idx=0; idxdata_sections[idx][s_idx] != NULL){ + ReleaseSlotSub(t->data_sections[idx][s_idx]); + t->data_sections[idx][s_idx] = NULL; + } + } + } +} + +void ReleaseSlot(int nBlock) +{ + if ( Tblocks[nBlock].texture_ptr != NULL ) { + // if this guy has a parent (in the case of a sectioned bitmap), unset his bitmap + if(Tblocks[nBlock].texture_ptr->parent != NULL){ + Tblocks[nBlock].texture_ptr->parent->bitmap_id = -1; + } + Tblocks[nBlock].texture_ptr->bitmap_id = -1; + + // AUX + // ReleaseSlotSub(Tblocks[nBlock].texture_ptr); + Tblocks[nBlock].texture_ptr = NULL; + + + Glide_textures_in -= Tblocks[nBlock].size; + } +} + +void FreeBlock(int nBlock) +{ + int nFreeNext; + + ReleaseSlot(nBlock); + + //---- Save next indices ---- + nFreeNext = Tblock_freelist_start; + + //---- Move alloc list block to start of free list ---- + Tblock_freelist_start = nBlock; + Tblocks[nBlock].next = nFreeNext; + +} + +int AllocBlock(void) +{ + int nNewBlock; + + //---- Get block from free list ---- + nNewBlock = Tblock_freelist_start; + + //**** DOS NOT HANDLE EMPTY FREE LIST **** + Tblock_freelist_start = Tblocks[Tblock_freelist_start].next; + + if(Tblock_usedlist_head < 0) { + //---- Alloc list is empty, add to start ---- + Tblock_usedlist_start = nNewBlock; + Tblocks[nNewBlock].next = -1; + } else { + //---- Insert at head of alloc list ---- + Tblocks[nNewBlock].next = Tblocks[Tblock_usedlist_head].next; + Tblocks[Tblock_usedlist_head].next = nNewBlock; + } + + //---- Set new head index ---- + Tblock_usedlist_head = nNewBlock; + + return nNewBlock; +} + +// Macro to compute ending address of block +#define BLOCK_END(b) (Tblocks[b].start_address + Tblocks[b].size) + +uint AllocateTexture(uint size, tcache_slot *texture_ptr) +{ + int nNewBlock; + int next; + + if (Tblock_usedlist_start < 0) { + + //---- Alloc list is empty ---- + nNewBlock = AllocBlock(); + Tblocks[nNewBlock].start_address = Tblock_min_address; + } else { + + uint dwAddress = BLOCK_END(Tblock_usedlist_head); + if ( dwAddress + size < Tblock_max_address ) { + int a1 = dwAddress / TEXMEM_2MB_EDGE; + int a2 = (dwAddress+size) / TEXMEM_2MB_EDGE; + + if ( a2 > a1 ) { + //mprintf(( "GrGlideTexture: Skipping a 2MB edge!\n" )); + dwAddress = a2*TEXMEM_2MB_EDGE; + } + } + + if( (dwAddress + size > Tblock_max_address) || (Tblocks[Tblock_freelist_start].next<0) ) { + #ifndef NDEBUG + if ( Tblocks[Tblock_freelist_start].next < 0 ) { + mprintf(( "GrGlideTexture: AllocateTexture out of blocks! Get John.\n" )); + } + #endif + + //---- No room left, go back to start ---- + ReleaseSlot(Tblock_usedlist_start); + + nNewBlock = Tblock_usedlist_head = Tblock_usedlist_start; + dwAddress = Tblock_min_address; + } else { + + //---- Make new block ---- + nNewBlock = AllocBlock(); + } + + next = Tblocks[Tblock_usedlist_head].next; + + //---- Unlink blocks being overwritten ---- + while((next >= 0) && (dwAddress + size > Tblocks[next].start_address)) { + + int nTemp = Tblocks[next].next; + + FreeBlock(next); + next = nTemp; + } + + //---- Init new block ---- + Tblocks[nNewBlock].next = next; + Tblocks[nNewBlock].start_address = dwAddress; + } + + Tblocks[nNewBlock].size = size; + Tblocks[nNewBlock].texture_ptr = texture_ptr; + + return Tblocks[nNewBlock].start_address; +} + +void glide_tcache_set_initial_texture_mode() +{ + grTexMipMapMode( GR_TMU0, GR_MIPMAP_DISABLE, FXFALSE); + grTexLodBiasValue( GR_TMU0, .5f); + grTexClampMode( GR_TMU0, GR_TEXTURECLAMP_WRAP, GR_TEXTURECLAMP_WRAP); + grTexFilterMode( GR_TMU0, GR_TEXTUREFILTER_BILINEAR, GR_TEXTUREFILTER_BILINEAR); +} + +void glide_tcache_init() +{ + // if we're already inited, allocate nothing new) + if(Textures == NULL){ + Textures = (tcache_slot *)malloc((MAX_TEXTURES)*sizeof(tcache_slot)); + if ( !Textures ) { + exit(1); + } + } + + // AUX + /* + Textures_aux = (tcache_slot*)malloc((MAX_TEXTURES) * MAX_BMAP_SECTIONS_X * MAX_BMAP_SECTIONS_Y sizeof(tcache_slot)); + if( !Textures_aux){ + exit(1); + } + */ + // if we're already inited, allocate nothing new) + if(Texture_sections == NULL){ + Texture_sections = malloc(MAX_TEXTURES * MAX_BMAP_SECTIONS_X * MAX_BMAP_SECTIONS_Y * sizeof(tcache_slot)); + if(!Texture_sections){ + exit(1); + } + } + + // if we're already inited, allocate nothing new) + if(Glide_tmp_ram == NULL){ + Glide_tmp_ram = (ubyte *)malloc(256*256*2); + if ( !Glide_tmp_ram ) { + exit(1); + } + } + + Glide_total_memory = guTexMemQueryAvail(GR_TMU0); + Glide_start_address = grTexMinAddress(GR_TMU0); + Glide_end_address = grTexMaxAddress(GR_TMU0); + Glide_current_address = Glide_start_address; + + mprintf(( "Total texture memory on 3dfx card=%d bytes\n", Glide_total_memory )); + + glide_tcache_set_initial_texture_mode(); + + InitBlocks(Glide_start_address,Glide_end_address, MAX_TEXTURES); + + // Init the texture structures + int i, idx, s_idx, count; + count = 0; + for( i=0; iparent = &Textures[i]; + } + } + } + + // AUX + /* + for( i=0; i 1){ + return NULL; + } + + ga_reentrant++; + + for(idx=0; idx -1) && (Textures[idx].data_sections[0][0] != NULL)){ + ReleaseSlot(&Textures[idx] - Textures); + } + } + + ret = glide_get_aux_slot(); + + ga_reentrant--; + return ret; + } + + // hmm + int s_idx; + Textures_aux[index].bitmap_id = -1; + Textures_aux[index].data.vram_offset = 0; + Textures_aux[index].data.vram_size = 0; + for(idx=0; idxdata.vram_offset == 0) || ((tslot->data.vram_offset>0)&&(bytes_needed>tslot->data.vram_size))) { + tslot->data.vram_offset = AllocateTexture( bytes_needed, tslot ); + tslot->data.vram_size = bytes_needed; + Glide_textures_in += bytes_needed; + // } + + tslot->data.format = tex_format; + tslot->data.lod = lod; + tslot->data.aspect = aspect; + tslot->bitmap_id = bitmap_handle; + tslot->uscale = uscale; + tslot->vscale = vscale; + + grTexDownloadMipMap(GR_TMU0, tslot->data.vram_offset, GR_MIPMAPLEVELMASK_BOTH, &info); + Glide_textures_in_frame += bytes_needed; + + return 1; +} + +int glide_create_texture( int bitmap_handle, int bitmap_type, tcache_slot *tslot ) +{ + int ret; + ubyte flags; + bitmap *bmp; + ubyte bpp = 16; + + // setup texture/bitmap flags + flags = 0; + switch(bitmap_type){ + case TCACHE_TYPE_AABITMAP: + flags |= BMP_AABITMAP; + bpp = 8; + break; + case TCACHE_TYPE_XPARENT: + case TCACHE_TYPE_BITMAP_SECTION: + flags |= BMP_TEX_XPARENT; + break; + case TCACHE_TYPE_NORMAL: + flags |= BMP_TEX_OTHER; + break; + case TCACHE_TYPE_NONDARKENING: + flags |= BMP_TEX_NONDARK; + break; + } + + // lock the bitmap in the proper format + bmp = bm_lock(bitmap_handle, bpp, flags); + if ( bmp == NULL ) { + mprintf(("Couldn't lock bitmap %d.\n", bitmap_handle )); + return 0; + } + + int max_w = bmp->w; + int max_h = bmp->h; + + if ( bitmap_type != TCACHE_TYPE_AABITMAP ) { + // Detail.debris_culling goes from 0 to 4. + max_w /= 16>>Detail.hardware_textures; + max_h /= 16>>Detail.hardware_textures; + } + + // call the helper + ret = glide_create_texture_sub(bitmap_type, bitmap_handle, (ushort*)bmp->data, 0, 0, bmp->w, bmp->h, bmp->w, bmp->h, max_w, max_h, tslot); + + // unlock the bitmap + bm_unlock(bitmap_handle); + + // return + return ret; +} + +// create a sectioned texture +int glide_create_texture_sectioned(int bitmap_handle, int bitmap_type, tcache_slot *tslot, int sx, int sy) +{ + int ret; + ubyte flags; + bitmap *bmp; + int section_x, section_y; + + // setup texture/bitmap flags + Assert(bitmap_type == TCACHE_TYPE_BITMAP_SECTION); + if(bitmap_type != TCACHE_TYPE_BITMAP_SECTION){ + bitmap_type = TCACHE_TYPE_BITMAP_SECTION; + } + flags = BMP_TEX_XPARENT; + + // lock the bitmap in the proper format + bmp = bm_lock(bitmap_handle, 16, flags); + if ( bmp == NULL ) { + mprintf(("Couldn't lock bitmap %d.\n", bitmap_handle )); + return 0; + } + + // determine the width and height of this section + bm_get_section_size(bitmap_handle, sx, sy, §ion_x, §ion_y); + + // call the helper + ret = glide_create_texture_sub(bitmap_type, bitmap_handle, (ushort*)bmp->data, bmp->sections.sx[sx], bmp->sections.sy[sy], section_x, section_y, bmp->w, bmp->h, section_x, section_y, tslot); + + // unlock the bitmap + bm_unlock(bitmap_handle); + + // return + return ret; +} + + +DCF(exp_flush, "") +{ + Glide_explosion_vram = 0; +} + +extern int bm_get_cache_slot( int bitmap_id, int separate_ani_frames ); + +// Returns FALSE if error +int glide_tcache_set( int bitmap_id, int bitmap_type, float *u_ratio, float *v_ratio, int fail_on_full, int sx, int sy, int force ) +{ + bitmap *bmp; + int idx, s_idx; + + if ( Glide_last_detail != Detail.hardware_textures ) { + Glide_last_detail = Detail.hardware_textures; + glide_tcache_flush(); + } + + // Check if this is the same as the last one... if so, we don't need to + // do anything. + if ( (Glide_last_bitmap_id == bitmap_id) && (Glide_last_bitmap_type==bitmap_type) && (Glide_last_section_x == sx) && (Glide_last_section_y == sy) && !force) { + *u_ratio = Glide_last_u_ratio; + *v_ratio = Glide_last_v_ratio; + return 1; + } + + int n; + n = bm_get_cache_slot( bitmap_id, 1 ); + + tcache_slot * t = &Textures[n]; + + // if this is a sectioned bitmap + if(bitmap_type == TCACHE_TYPE_BITMAP_SECTION){ + // if the texture sections haven't been created yet + if((t->bitmap_id < 0) || (t->bitmap_id != bitmap_id) || force){ + + if(t->bitmap_id < 0){ + t->data.vram_offset = 0; + } + + // lock the bitmap in the proper format + bmp = bm_lock(bitmap_id, 16, BMP_TEX_XPARENT); + bm_unlock(bitmap_id); + + // first we need to get enough free aux textures + // AUX + /* + for(idx=0; idxsections.num_x; idx++){ + for(s_idx=0; s_idxsections.num_y; s_idx++){ + // try and get a feww slot + t->data_sections[idx][s_idx] = glide_get_aux_slot(); + if(t->data_sections[idx][s_idx] == NULL){ + Int3(); + return 0; + } + t->data_sections[idx][s_idx]->bitmap_id = bitmap_id; + } + } + */ + + // now lets do something for each texture + for(idx=0; idxsections.num_x; idx++){ + for(s_idx=0; s_idxsections.num_y; s_idx++){ + if(t->bitmap_id < 0){ + t->data_sections[idx][s_idx]->data.vram_offset = 0; + } + + t->data_sections[idx][s_idx]->bitmap_id = bitmap_id; + glide_create_texture_sectioned( bitmap_id, bitmap_type, t->data_sections[idx][s_idx], idx, s_idx); + } + } + + t->bitmap_id = bitmap_id; + } + + // swap in the texture we want + t = t->data_sections[sx][sy]; + } + // all other "normal" textures + else { + // no texture yet + if ( t->bitmap_id < 0) { + t->data.vram_offset = 0; + glide_create_texture( bitmap_id, bitmap_type, t ); + } + // different bitmap altogether + else if ( (t->bitmap_id != bitmap_id) || force) { + glide_create_texture( bitmap_id, bitmap_type, t ); + } + } + + *u_ratio = t->uscale; + *v_ratio = t->vscale; + + GrTexInfo info; + + info.smallLod = t->data.lod; //GR_LOD_256; + info.largeLod = t->data.lod; //GR_LOD_256; + info.aspectRatio = t->data.aspect; //GR_ASPECT_1x1; + info.format = t->data.format; //GR_TEXFMT_ARGB_1555; + info.data = 0; //source data + + grTexSource( GR_TMU0, t->data.vram_offset, GR_MIPMAPLEVELMASK_BOTH, &info ); + + // Save current state so we don't have to do anything time consuming next time + // we set this exact same texture + Glide_last_bitmap_id = bitmap_id; + Glide_last_bitmap_type = bitmap_type; + Glide_last_section_x = sx; + Glide_last_section_y = sy; + Glide_last_u_ratio = t->uscale; + Glide_last_v_ratio = t->vscale; + + return 1; +} + + + + + diff --git a/src/graphics/gropengl.cpp b/src/graphics/gropengl.cpp new file mode 100644 index 0000000..ce89a38 --- /dev/null +++ b/src/graphics/gropengl.cpp @@ -0,0 +1,726 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrOpenGL.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code that uses the OpenGL graphics library + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 10 7/14/99 9:42a Dave + * Put in clear_color debug function. Put in base for 3dnow stuff / P3 + * stuff + * + * 9 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 8 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 7 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 6 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 5 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 4 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 3 11/11/98 5:37p Dave + * Checkin for multiplayer testing. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 14 5/20/98 9:46p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 13 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 12 4/14/98 12:15p John + * Made 16-bpp movies work. + * + * 11 3/12/98 5:36p John + * Took out any unused shaders. Made shader code take rgbc instead of + * matrix and vector since noone used it like a matrix and it would have + * been impossible to do in hardware. Made Glide implement a basic + * shader for online help. + * + * 10 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 9 12/02/97 4:00p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 8 10/03/97 9:10a John + * added better antialiased line drawer + * + * 7 9/23/97 10:45a John + * made so you can tell bitblt code to rle a bitmap by passing flag to + * gr_set_bitmap + * + * 6 9/09/97 11:01a Sandeep + * fixed warning level 4 bugs + * + * 5 7/10/97 2:06p John + * added code to specify alphablending type for bitmaps. + * + * 4 6/17/97 7:04p John + * added d3d support for gradients. + * fixed some color bugs by adding screen signatures instead of watching + * flags and palette changes. + * + * 3 6/12/97 2:50a Lawrance + * bm_unlock() now passed bitmap number, not pointer + * + * 2 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 1 5/12/97 12:14p John + * + * $NoKeywords: $ + */ + +#ifndef PLAT_UNIX +#include +#include +#endif + +#include "osapi.h" +#include "2d.h" +#include "bmpman.h" +#include "floating.h" +#include "palman.h" +#include "grinternal.h" +#include "gropengl.h" +#include "line.h" + +static int Inited = 0; + +void gr_opengl_pixel(int x, int y) +{ + if ( x < gr_screen.clip_left ) return; + if ( x > gr_screen.clip_right ) return; + if ( y < gr_screen.clip_top ) return; + if ( y > gr_screen.clip_bottom ) return; +} + +void gr_opengl_clear() +{ +} + + +void gr_opengl_flip() +{ +} + +void gr_opengl_flip_window(uint _hdc, int x, int y, int w, int h ) +{ +} + +void gr_opengl_set_clip(int x,int y,int w,int h) +{ + // check for sanity of parameters + if (x < 0) + x = 0; + if (y < 0) + y = 0; + + if (x >= gr_screen.max_w) + x = gr_screen.max_w - 1; + if (y >= gr_screen.max_h) + y = gr_screen.max_h - 1; + + if (x + w > gr_screen.max_w) + w = gr_screen.max_w - x; + if (y + h > gr_screen.max_h) + h = gr_screen.max_h - y; + + if (w > gr_screen.max_w) + w = gr_screen.max_w; + if (h > gr_screen.max_h) + h = gr_screen.max_h; + + gr_screen.offset_x = x; + gr_screen.offset_y = y; + gr_screen.clip_left = 0; + gr_screen.clip_right = w-1; + gr_screen.clip_top = 0; + gr_screen.clip_bottom = h-1; + gr_screen.clip_width = w; + gr_screen.clip_height = h; +} + +void gr_opengl_reset_clip() +{ + gr_screen.offset_x = 0; + gr_screen.offset_y = 0; + gr_screen.clip_left = 0; + gr_screen.clip_top = 0; + gr_screen.clip_right = gr_screen.max_w - 1; + gr_screen.clip_bottom = gr_screen.max_h - 1; + gr_screen.clip_width = gr_screen.max_w; + gr_screen.clip_height = gr_screen.max_h; +} + +void gr_opengl_set_font(int fontnum) +{ +} + +void gr_opengl_set_color( int r, int g, int b ) +{ + Assert((r >= 0) && (r < 256)); + Assert((g >= 0) && (g < 256)); + Assert((b >= 0) && (b < 256)); + + gr_screen.current_color.red = (unsigned char)r; + gr_screen.current_color.green = (unsigned char)g; + gr_screen.current_color.blue = (unsigned char)b; +} + +void gr_opengl_set_bitmap( int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha, int sx, int sy ) +{ + gr_screen.current_alpha = alpha; + gr_screen.current_alphablend_mode = alphablend_mode; + gr_screen.current_bitblt_mode = bitblt_mode; + gr_screen.current_bitmap = bitmap_num; + + gr_screen.current_bitmap_sx = sx; + gr_screen.current_bitmap_sy = sy; +} + +void gr_opengl_create_shader(shader * shade, float r, float g, float b, float c ) +{ + shade->screen_sig = gr_screen.signature; + shade->r = r; + shade->g = g; + shade->b = b; + shade->c = c; +} + +void gr_opengl_set_shader( shader * shade ) +{ + if ( shade ) { + if (shade->screen_sig != gr_screen.signature) { + gr_create_shader( shade, shade->r, shade->g, shade->b, shade->c ); + } + gr_screen.current_shader = *shade; + } else { + gr_create_shader( &gr_screen.current_shader, 0.0f, 0.0f, 0.0f, 0.0f ); + } +} + + +void gr_opengl_bitmap_ex(int x,int y,int w,int h,int sx,int sy) +{ + int i,j; + bitmap * bmp; + ubyte * sptr; + + bmp = bm_lock( gr_screen.current_bitmap, 8, 0 ); + sptr = (ubyte *)( bmp->data + (sy*bmp->w+sx) ); + +// mprintf(( "x=%d, y=%d, w=%d, h=%d\n", x, y, w, h )); +// mprintf(( "sx=%d, sy=%d, bw=%d, bh=%d\n", sx, sy, bmp->w, bmp->h )); + + for (i=0; iw; + } + bm_unlock(gr_screen.current_bitmap); +} + +void gr_opengl_bitmap(int x, int y) +{ + int w, h; + + bm_get_info( gr_screen.current_bitmap, &w, &h, NULL ); + int dx1=x, dx2=x+w-1; + int dy1=y, dy2=y+h-1; + int sx=0, sy=0; + + if ((dx1 > gr_screen.clip_right ) || (dx2 < gr_screen.clip_left)) return; + if ((dy1 > gr_screen.clip_bottom ) || (dy2 < gr_screen.clip_top)) return; + if ( dx1 < gr_screen.clip_left ) { sx = gr_screen.clip_left-dx1; dx1 = gr_screen.clip_left; } + if ( dy1 < gr_screen.clip_top ) { sy = gr_screen.clip_top-dy1; dy1 = gr_screen.clip_top; } + if ( dx2 > gr_screen.clip_right ) { dx2 = gr_screen.clip_right; } + if ( dy2 > gr_screen.clip_bottom ) { dy2 = gr_screen.clip_bottom; } + + if ( sx < 0 ) return; + if ( sy < 0 ) return; + if ( sx >= w ) return; + if ( sy >= h ) return; + + // Draw bitmap bm[sx,sy] into (dx1,dy1)-(dx2,dy2) + + gr_bitmap_ex(dx1,dy1,dx2-dx1+1,dy2-dy1+1,sx,sy); +} + +static void opengl_scanline(int x1,int x2,int y) +{ +} + +void gr_opengl_rect(int x,int y,int w,int h) +{ + int i, swapped=0; + int x1 = x, x2; + int y1 = y, y2; + + if ( w > 0 ) + x2 = x + w - 1; + else + x2 = x + w + 1; + + if ( h > 0 ) + y2 = y + h - 1; + else + y2 = y + h + 1; + + if ( x2 < x1 ) { + int tmp; + tmp = x1; + x1 = x2; + x2 = tmp; + w = -w; + swapped = 1; + } + + if ( y2 < y1 ) { + int tmp; + tmp = y1; + y1 = y2; + y2 = tmp; + h = -h; + swapped = 1; + } + + for (i=0; i gr_screen.clip_right ) return; + if ( (yc+r) < gr_screen.clip_top ) return; + if ( (yc-r) > gr_screen.clip_bottom ) return; + + while(xdy) { + + for(i=dx+1;i>0;i--) { + gr_pixel( x1, y1 ); + x1 += xstep; + error_term+=dy; + + if(error_term>dx) { + error_term-=dx; + y1+=ystep; + } + } + } else { + + for(i=dy+1;i>0;i--) { + gr_pixel( x1, y1 ); + y1 += ystep; + error_term+=dx; + if(error_term>0) { + error_term-=dy; + x1+=xstep; + } + + } + + } +} + +#define FIND_SCALED_NUM(x,x0,x1,y0,y1) (((((x)-(x0))*((y1)-(y0)))/((x1)-(x0)))+(y0)) + +void gr_opengl_scaler(vertex *va, vertex *vb ) +{ + float x0, y0, x1, y1; + float u0, v0, u1, v1; + float clipped_x0, clipped_y0, clipped_x1, clipped_y1; + float clipped_u0, clipped_v0, clipped_u1, clipped_v1; + float xmin, xmax, ymin, ymax; + int dx0, dy0, dx1, dy1; + + //============= CLIP IT ===================== + + x0 = va->sx; y0 = va->sy; + x1 = vb->sx; y1 = vb->sy; + + xmin = i2fl(gr_screen.clip_left); ymin = i2fl(gr_screen.clip_top); + xmax = i2fl(gr_screen.clip_right); ymax = i2fl(gr_screen.clip_bottom); + + u0 = va->u; v0 = va->v; + u1 = vb->u; v1 = vb->v; + + // Check for obviously offscreen bitmaps... + if ( (y1<=y0) || (x1<=x0) ) return; + if ( (x1xmax) ) return; + if ( (y1ymax) ) return; + + clipped_u0 = u0; clipped_v0 = v0; + clipped_u1 = u1; clipped_v1 = v1; + + clipped_x0 = x0; clipped_y0 = y0; + clipped_x1 = x1; clipped_y1 = y1; + + // Clip the left, moving u0 right as necessary + if ( x0 < xmin ) { + clipped_u0 = FIND_SCALED_NUM(xmin,x0,x1,u0,u1); + clipped_x0 = xmin; + } + + // Clip the right, moving u1 left as necessary + if ( x1 > xmax ) { + clipped_u1 = FIND_SCALED_NUM(xmax,x0,x1,u0,u1); + clipped_x1 = xmax; + } + + // Clip the top, moving v0 down as necessary + if ( y0 < ymin ) { + clipped_v0 = FIND_SCALED_NUM(ymin,y0,y1,v0,v1); + clipped_y0 = ymin; + } + + // Clip the bottom, moving v1 up as necessary + if ( y1 > ymax ) { + clipped_v1 = FIND_SCALED_NUM(ymax,y0,y1,v0,v1); + clipped_y1 = ymax; + } + + dx0 = fl2i(clipped_x0); dx1 = fl2i(clipped_x1); + dy0 = fl2i(clipped_y0); dy1 = fl2i(clipped_y1); + + if (dx1<=dx0) return; + if (dy1<=dy0) return; + + //============= DRAW IT ===================== + int u, v, du, dv; + int y, w; + ubyte * sbits; + bitmap * bp; + ubyte * spixels; + float tmpu, tmpv; + + tmpu = (clipped_u1-clipped_u0) / (dx1-dx0); + if ( fl_abs(tmpu) < 0.001f ) { + return; // scaled up way too far! + } + tmpv = (clipped_v1-clipped_v0) / (dy1-dy0); + if ( fl_abs(tmpv) < 0.001f ) { + return; // scaled up way too far! + } + + bp = bm_lock( gr_screen.current_bitmap, 8, 0 ); + + du = fl2f(tmpu*(bp->w-1)); + dv = fl2f(tmpv*(bp->h-1)); + + v = fl2f(clipped_v0*(bp->h-1)); + u = fl2f(clipped_u0*(bp->w-1)); + w = dx1 - dx0 + 1; + + spixels = (ubyte *)bp->data; + + for (y=dy0; y<=dy1; y++ ) { + sbits = &spixels[bp->rowsize*(v>>16)]; + + int x, tmp_u; + tmp_u = u; + for (x=0; x> 16 ]; + if ( c != 255 ) { + gr_set_color( gr_palette[c*3+0], gr_palette[c*3+1], gr_palette[c*3+2] ); + gr_pixel( x+dx0, y ); + } + tmp_u += du; + } + v += dv; + } + + bm_unlock(gr_screen.current_bitmap); + +} + + + +void gr_opengl_tmapper( int nv, vertex * verts[], uint flags ) +{ +} + + +void gr_opengl_gradient(int x1,int y1,int x2,int y2) +{ +} + +void gr_opengl_set_palette(ubyte *new_palette, int is_alphacolor) +{ +} + +void gr_opengl_get_color( int * r, int * g, int * b ) +{ + if (r) *r = gr_screen.current_color.red; + if (g) *g = gr_screen.current_color.green; + if (b) *b = gr_screen.current_color.blue; +} + +void gr_opengl_init_color(color *c, int r, int g, int b) +{ + gr_screen.current_color.screen_sig = gr_screen.signature; + gr_screen.current_color.red = (unsigned char)r; + gr_screen.current_color.green = (unsigned char)g; + gr_screen.current_color.blue = (unsigned char)b; +} + +void gr_opengl_set_color_fast(color *dst) +{ + if ( dst->screen_sig != gr_screen.signature ) { + gr_init_color( dst, dst->red, dst->green, dst->blue ); + return; + } + gr_screen.current_color = *dst; +} + + + +void gr_opengl_print_screen(char *filename) +{ + +} + +int gr_opengl_supports_res_ingame(int res) +{ + return 1; +} + +int gr_opengl_supports_res_interface(int res) +{ + return 1; +} + +void gr_opengl_cleanup() +{ + if ( !Inited ) return; + + gr_reset_clip(); + gr_clear(); + gr_flip(); + + Inited = 0; +} + +void gr_opengl_fog_set(int fog_mode, int r, int g, int b, float near, float far) +{ +} + +void gr_opengl_get_pixel(int x, int y, int *r, int *g, int *b) +{ +} + +void gr_opengl_get_region(int front, int w, int g, ubyte *data) +{ +} + +void gr_opengl_set_cull(int cull) +{ +} + +void gr_opengl_filter_set(int filter) +{ +} + +// cross fade +void gr_opengl_cross_fade(int bmap1, int bmap2, int x1, int y1, int x2, int y2, float pct) +{ +} + +int gr_opengl_tcache_set(int bitmap_id, int bitmap_type, float *u_ratio, float *v_ratio, int fail_on_full = 0, int sx = -1, int sy = -1, int force = 0) +{ + return 1; +} + +void gr_opengl_set_clear_color(int r, int g, int b) +{ +} + +void gr_opengl_init() +{ + if ( Inited ) { + gr_opengl_cleanup(); + Inited = 0; + } + + mprintf(( "Initializing opengl graphics device...\n" )); + Inited = 1; + + gr_opengl_clear(); + + gr_screen.gf_flip = gr_opengl_flip; + gr_screen.gf_flip_window = gr_opengl_flip_window; + gr_screen.gf_set_clip = gr_opengl_set_clip; + gr_screen.gf_reset_clip = gr_opengl_reset_clip; + gr_screen.gf_set_font = gr_opengl_set_font; + gr_screen.gf_set_color = gr_opengl_set_color; + gr_screen.gf_set_bitmap = gr_opengl_set_bitmap; + gr_screen.gf_create_shader = gr_opengl_create_shader; + gr_screen.gf_set_shader = gr_opengl_set_shader; + gr_screen.gf_clear = gr_opengl_clear; + // gr_screen.gf_bitmap = gr_opengl_bitmap; + // gr_screen.gf_bitmap_ex = gr_opengl_bitmap_ex; + gr_screen.gf_rect = gr_opengl_rect; + gr_screen.gf_shade = gr_opengl_shade; + gr_screen.gf_string = gr_opengl_string; + gr_screen.gf_circle = gr_opengl_circle; + + gr_screen.gf_line = gr_opengl_line; + gr_screen.gf_pixel = gr_opengl_pixel; + gr_screen.gf_scaler = gr_opengl_scaler; + gr_screen.gf_tmapper = gr_opengl_tmapper; + + gr_screen.gf_gradient = gr_opengl_gradient; + + gr_screen.gf_set_palette = gr_opengl_set_palette; + gr_screen.gf_get_color = gr_opengl_get_color; + gr_screen.gf_init_color = gr_opengl_init_color; + gr_screen.gf_set_color_fast = gr_opengl_set_color_fast; + gr_screen.gf_print_screen = gr_opengl_print_screen; + + gr_screen.gf_fog_set = gr_opengl_fog_set; + + gr_screen.gf_get_region = gr_opengl_get_region; + + gr_screen.gf_get_pixel = gr_opengl_get_pixel; + + gr_screen.gf_set_cull = gr_opengl_set_cull; + + gr_screen.gf_cross_fade = gr_opengl_cross_fade; + + gr_screen.gf_filter_set = gr_opengl_filter_set; + + gr_screen.gf_tcache_set = gr_opengl_tcache_set; + + gr_screen.gf_set_clear_color = gr_opengl_set_clear_color; +} + + + diff --git a/src/graphics/grsoft.cpp b/src/graphics/grsoft.cpp new file mode 100644 index 0000000..5a6b8b5 --- /dev/null +++ b/src/graphics/grsoft.cpp @@ -0,0 +1,1843 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrSoft.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for our software renderer using standard Win32 functions. (Dibsections, etc) + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 12 7/14/99 9:42a Dave + * Put in clear_color debug function. Put in base for 3dnow stuff / P3 + * stuff + * + * 11 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 10 6/29/99 10:35a Dave + * Interface polygon bitmaps! Whee! + * + * 9 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 8 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 7 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 6 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 5 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 4 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 3 11/11/98 5:37p Dave + * Checkin for multiplayer testing. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 84 5/20/98 9:46p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 83 5/18/98 11:17a John + * Fixed some bugs with software window and output window. + * + * 82 5/17/98 5:03p John + * Fixed some bugs that make the taskbar interfere with the DEBUG-only + * mouse cursor. + * + * 81 5/15/98 2:44p John + * Made windowed mode not touch the window if no handle to window found. + * + * 80 5/14/98 5:42p John + * Revamped the whole window position/mouse code for the graphics windows. + * + * 79 5/07/98 6:58p Hoffoss + * Made changes to mouse code to fix a number of problems. + * + * 78 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 77 4/23/98 8:24a John + * Changed the way palette effect works so that: + * 1. If gr_flash isn't called this frame, screen shows no flash. + * 2. With hardware, only 3d portion of screen gets flashed. + * + * 76 4/21/98 5:22p John + * Fixed all the &^#@$ cursor bugs with popups. For Glide, had problem + * with mouse restoring assuming back buffer was same buffer last frame, + * for software, problems with half drawn new frames, then mouse got + * restored on top of that with old data. + * + * 75 4/14/98 12:15p John + * Made 16-bpp movies work. + * + * 74 4/11/98 12:48p John + * Made software fade out actually clear screen to black. + * + * 73 4/01/98 9:21p John + * Made NDEBUG, optimized build with no warnings or errors. + * + * 72 3/25/98 8:07p John + * Restructured software rendering into two modules; One for windowed + * debug mode and one for DirectX fullscreen. + * + * 71 3/24/98 3:58p John + * Put in (hopefully) final gamma setting code. + * + * 70 3/17/98 5:55p John + * Added code to dump Glide frames. Moved Allender's "hack" code out of + * Freespace.cpp into the proper place, graphics lib. + * + * 69 3/14/98 5:46p John + * + * 68 3/14/98 5:43p John + * Saved area under mouse cursor. Made save_Screen restore it so no mouse + * is on those screens. + * + * 67 3/12/98 8:50a John + * took out "allocating zbuffer" mprintf + * + * 66 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 65 3/06/98 4:09p John + * Changed the way we do bitmap RLE'ing... this speds up HUD bitmaps by + * about 2x + * + * 64 3/05/98 4:28p John + * Made mouse cursor hide while setting palette. + * + * 63 3/02/98 5:42p John + * Removed WinAVI stuff from Freespace. Made all HUD gauges wriggle from + * afterburner. Made gr_set_clip work good with negative x &y. Made + * model_caching be on by default. Made each cached model have it's own + * bitmap id. Made asteroids not rotate when model_caching is on. + * + * 62 2/24/98 3:50p John + * Made Alt+Tabbing work with Glide. Made Int3's restore Glide screen on + * next flip. + * + * 61 2/24/98 1:59p John + * Made gr_aabitmap_ex clip properly + * + * 60 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 59 1/19/98 6:15p John + * Fixed all my Optimized Build compiler warnings + * + * 58 1/15/98 4:07p John + * added grx_set_palette_internal which doesn't clear the screen and flip + * before setting the palette. + * + * 57 1/15/98 4:02p John + * Fixed stupid bug on my part where I called graphics rendering functions + * from another thread!!! Duh! Fixed it by setting a variable and then + * resetting the palette at the next flip. + * + * 56 1/08/98 5:06p John + * Replaced Int3 with a mprintf + * + * 55 1/08/98 3:10p John + * Undid my palette changes for interplay rev + * + * 54 1/08/98 3:03p John + * + * 53 1/05/98 11:52a Jasen + * JAS: Made graphics code work if os_get_window returns NULL when trying + * to go fullscreen. + * + * 52 12/30/97 6:47p John + * Made fade time correct + * + * 51 12/30/97 6:46p John + * Added first rev of palette fade in out functions + * + * 50 12/21/97 4:33p John + * Made debug console functions a class that registers itself + * automatically, so you don't need to add the function to + * debugfunctions.cpp. + * + * 49 12/05/97 4:26p John + * made palette set code be like it was before. + * + * 48 12/04/97 8:05p John + * added test code to brighten palette + * + * 47 12/03/97 2:16p John + * Took out blur code and fake_vram buffer. + * + * 46 12/02/97 4:00p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 45 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 44 11/29/97 2:06p John + * added mode 16-bpp support + * + * 43 11/21/97 11:32a John + * Added nebulas. Fixed some warpout bugs. + * + * 42 11/20/97 9:51a John + * added code to force screen to 16-bit even if rendering 8. + * + * 41 11/17/97 10:33a John + * updated force_windowed code. + * + * 40 11/14/97 3:54p John + * Added triple buffering. + * + * 39 11/14/97 3:08p Johnson + * fixed bug with fred. + * + * 38 11/14/97 12:30p John + * Fixed some DirectX bugs. Moved the 8-16 xlat tables into Graphics + * libs. Made 16-bpp DirectX modes know what bitmap format they're in. + * + * 37 11/06/97 4:12p John + * Took out debug code that returned NULL for DD. + * + * 36 11/05/97 12:35p John + * added correct gr_bitmap_ex that does clipping + * + * + * 35 10/31/97 9:43a John + * fixed red background color bug. + * + * 34 10/19/97 1:53p John + * hacked in a fix to a zbuffer error caused by inaccuracies. + * + * 33 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 32 10/15/97 4:48p John + * added 16-bpp aascaler + * + * 31 10/14/97 4:50p John + * more 16 bpp stuff. + * + * 30 10/14/97 8:08a John + * added a bunch more 16 bit support + * + * 29 10/09/97 5:23p John + * Added support for more 16-bpp functions + * + * 28 10/03/97 9:10a John + * added better antialiased line drawer + * + * 27 9/23/97 11:50a Lawrance + * jas: added xparency to rle'd bitblts + * + * 26 9/23/97 10:45a John + * made so you can tell bitblt code to rle a bitmap by passing flag to + * gr_set_bitmap + * + * 25 9/20/97 10:48a John + * added motion blur code. + * + * 24 9/09/97 10:39a Sandeep + * fixed warning level 4 + * + * 23 9/07/97 2:34p John + * fixed bug with fullscreen toggling not working when in > 8bpp mode. + * + * 22 9/03/97 4:32p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 21 8/26/97 11:30a John + * removed call to restoredisplymode when going from fullscreen to + * windowed, which hopefully fixes the screwey taskbar problem. + * + * 20 8/04/97 4:47p John + * added gr_aascaler. + * + * 19 7/17/97 9:31a John + * made all directX header files name start with a v + * + * 18 7/10/97 2:06p John + * added code to specify alphablending type for bitmaps. + * + * 17 6/20/97 1:50p John + * added rle code to bmpman. made gr8_aabitmap use it. + * + * 16 6/17/97 12:03p John + * Moved color/alphacolor functions into their own module. Made all color + * functions be part of the low-level graphics drivers, not just the + * grsoft. + * + * 15 6/13/97 5:35p John + * added some antialiased bitmaps and lines + * + * 14 6/12/97 2:50a Lawrance + * bm_unlock() now passed bitmap number, not pointer + * + * 13 6/11/97 5:49p John + * Changed palette code to only recalculate alphacolors when needed, not + * when palette changes. + * + * 12 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 11 6/06/97 5:03p John + * fixed bug withalpha colors failing after gr_init + * + * 10 6/06/97 4:41p John + * Fixed alpha colors to be smoothly integrated into gr_set_color_fast + * code. + * + * 9 5/29/97 3:09p John + * Took out debug menu. + * Made software scaler draw larger bitmaps. + * Optimized Direct3D some. + * + * 8 5/16/97 9:11a John + * fixed bug that made Ctrl+Break in fullscreen hang + * + * 7 5/14/97 4:40p John + * took out mprintf + * + * 6 5/14/97 4:38p John + * Fixed print_screen bug. + * + * 5 5/14/97 2:09p John + * made fullscreen use 254 palette entries. Used + * CreateCompatibleDC(NULL) Instead of GetDC(HWND_DESKTOP) because the + * GetDC method fails when you change screen depth dynamically under NT. + * + * 4 5/14/97 10:53a John + * fixed some discrepencies between d3d and software palette setting. + * + * 3 5/14/97 8:53a John + * Fixed a palette bug when switching into/outof d3d mode. + * + * 2 5/13/97 12:39p John + * Got fullscreen mode working. + * + * 1 5/12/97 12:14p John + * + * $NoKeywords: $ + */ + +#include +#include +#include + +#include "osapi.h" +#include "2d.h" +#include "bmpman.h" +#include "key.h" +#include "floating.h" +#include "palman.h" +#include "grsoft.h" +#include "grinternal.h" + +// Headers for 2d functions +#include "pixel.h" +#include "line.h" +#include "scaler.h" +#include "tmapper.h" +#include "circle.h" +#include "shade.h" +#include "rect.h" +#include "gradient.h" +#include "pcxutils.h" +#include "osapi.h" +#include "mouse.h" +#include "font.h" +#include "timer.h" +#include "colors.h" +#include "bitblt.h" + + // Window's specific + +// This structure is the same as LOGPALETTE except that LOGPALETTE +// requires you to malloc out space for the palette, which just isn't +// worth the trouble. + +typedef struct { + WORD palVersion; + WORD palNumEntries; + PALETTEENTRY palPalEntry[256]; +} EZ_LOGPALETTE; + +// This structure is the same as BITMAPINFO except that BITMAPINFO +// requires you to malloc out space for the palette, which just isn't +// worth the trouble. I also went ahead and threw a handy union to +// easily reference the hicolor masks in 15/16 bpp modes. +typedef struct { + BITMAPINFOHEADER Header; + union { + RGBQUAD aColors[256]; + ushort PalIndex[256]; + uint hicolor_masks[3]; + } Colors; +} EZ_BITMAPINFO; + +EZ_BITMAPINFO DibInfo; +HBITMAP hDibSection = NULL; +HBITMAP hOldBitmap = NULL; +HDC hDibDC = NULL; +void *lpDibBits=NULL; + +HPALETTE hOldPalette=NULL, hPalette = NULL; + +int Gr_soft_inited = 0; + +static volatile int Grsoft_activated = 0; // If set, that means application got focus, so reset palette + +void gr_buffer_release() +{ + if ( hPalette ) { + if (hDibDC) + SelectPalette( hDibDC, hOldPalette, FALSE ); + if (!DeleteObject(hPalette)) { + mprintf(( "JOHN: Couldn't delete palette object\n" )); + } + hPalette = NULL; + } + + if ( hDibDC ) { + SelectObject(hDibDC, hOldBitmap ); + DeleteDC(hDibDC); + hDibDC = NULL; + } + + if ( hDibSection ) { + DeleteObject(hDibSection); + hDibSection = NULL; + } +} + + +void gr_buffer_create( int w, int h, int bpp ) +{ + int i; + + if (w & 3) { + Int3(); // w must be multiple 4 + return; + } + + gr_buffer_release(); + + memset( &DibInfo, 0, sizeof(EZ_BITMAPINFO)); + DibInfo.Header.biSize = sizeof(BITMAPINFOHEADER); + DibInfo.Header.biWidth = w; + DibInfo.Header.biHeight = h; + DibInfo.Header.biPlanes = 1; + DibInfo.Header.biClrUsed = 0; + + switch( bpp ) { + case 8: + Gr_red.bits = 8; + Gr_red.shift = 16; + Gr_red.scale = 1; + Gr_red.mask = 0xff0000; + + Gr_green.bits = 8; + Gr_green.shift = 8; + Gr_green.scale = 1; + Gr_green.mask = 0xff00; + + Gr_blue.bits = 8; + Gr_blue.shift = 0; + Gr_blue.scale = 1; + Gr_blue.mask = 0xff; + + DibInfo.Header.biCompression = BI_RGB; + DibInfo.Header.biBitCount = 8; + for (i=0; i<256; i++ ) { + DibInfo.Colors.aColors[i].rgbRed = 0; + DibInfo.Colors.aColors[i].rgbGreen = 0; + DibInfo.Colors.aColors[i].rgbBlue = 0; + DibInfo.Colors.aColors[i].rgbReserved = 0; + } + break; + + case 15: + Gr_red.bits = 5; + Gr_red.shift = 10; + Gr_red.scale = 8; + Gr_red.mask = 0x7C00; + + Gr_green.bits = 5; + Gr_green.shift = 5; + Gr_green.scale = 8; + Gr_green.mask = 0x3E0; + + Gr_blue.bits = 5; + Gr_blue.shift = 0; + Gr_blue.scale = 8; + Gr_blue.mask = 0x1F; + + DibInfo.Header.biCompression = BI_BITFIELDS; + DibInfo.Header.biBitCount = 16; + DibInfo.Colors.hicolor_masks[0] = Gr_red.mask; + DibInfo.Colors.hicolor_masks[1] = Gr_green.mask; + DibInfo.Colors.hicolor_masks[2] = Gr_blue.mask; + + + break; + + case 16: + Gr_red.bits = 5; + Gr_red.shift = 11; + Gr_red.scale = 8; + Gr_red.mask = 0xF800; + + Gr_green.bits = 6; + Gr_green.shift = 5; + Gr_green.scale = 4; + Gr_green.mask = 0x7E0; + + Gr_blue.bits = 5; + Gr_blue.shift = 0; + Gr_blue.scale = 8; + Gr_blue.mask = 0x1F; + + DibInfo.Header.biCompression = BI_BITFIELDS; + DibInfo.Header.biBitCount = 16; + DibInfo.Colors.hicolor_masks[0] = Gr_red.mask; + DibInfo.Colors.hicolor_masks[1] = Gr_green.mask; + DibInfo.Colors.hicolor_masks[2] = Gr_blue.mask; + break; + + case 24: + case 32: + Gr_red.bits = 8; + Gr_red.shift = 16; + Gr_red.scale = 1; + Gr_red.mask = 0xff0000; + + Gr_green.bits = 8; + Gr_green.shift = 8; + Gr_green.scale = 1; + Gr_green.mask = 0xff00; + + Gr_blue.bits = 8; + Gr_blue.shift = 0; + Gr_blue.scale = 1; + Gr_blue.mask = 0xff; + + DibInfo.Header.biCompression = BI_RGB; + DibInfo.Header.biBitCount = unsigned short(bpp); + break; + + default: + Int3(); // Illegal bpp + } + + lpDibBits = NULL; + + hDibDC = CreateCompatibleDC(NULL); + hDibSection = CreateDIBSection(hDibDC,(BITMAPINFO *)&DibInfo,DIB_RGB_COLORS,&lpDibBits,NULL,NULL); + hOldBitmap = (HBITMAP)SelectObject(hDibDC, hDibSection ); + + if ( hDibSection == NULL ) { + Int3(); // couldn't allocate dib section + } +} + + +// This makes a best-fit palette from the 256 target colors and +// the system colors which should look better than only using the +// colors in the range 10-246, but it will totally reorder the palette. +// All colors get changed in target_palette. + + +HPALETTE gr_create_palette_0(ubyte * target_palette) +{ + EZ_LOGPALETTE LogicalPalette; + HDC ScreenDC; + HPALETTE hpal,hpalOld; + PALETTEENTRY pe[256]; + int NumSysColors, NumColors; + int i; + + // Create a 1-1 mapping of the system palette + LogicalPalette.palVersion = 0x300; + LogicalPalette.palNumEntries = 256; + + // Pack in all the colors + for (i=0;i<256; i++) { + LogicalPalette.palPalEntry[i].peRed = target_palette[i*3+0]; + LogicalPalette.palPalEntry[i].peGreen = target_palette[i*3+1]; + LogicalPalette.palPalEntry[i].peBlue = target_palette[i*3+2]; + LogicalPalette.palPalEntry[i].peFlags = 0; //PC_EXPLICIT; + } + + hpal = CreatePalette( (LOGPALETTE *)&LogicalPalette ); + + ScreenDC = CreateCompatibleDC(NULL); + + if ( !(GetDeviceCaps(ScreenDC,RASTERCAPS) & RC_PALETTE) ) { + DeleteDC(ScreenDC); + return hpal; + } + + NumSysColors = GetDeviceCaps( ScreenDC, NUMCOLORS ); + NumColors = GetDeviceCaps( ScreenDC, SIZEPALETTE ); + + // Reset all the Palette Manager tables + SetSystemPaletteUse( ScreenDC, SYSPAL_NOSTATIC ); + SetSystemPaletteUse( ScreenDC, SYSPAL_STATIC ); + + // Enter our palette's values into the free slots + hpalOld=SelectPalette( ScreenDC, hpal, FALSE ); + RealizePalette( ScreenDC ); + SelectPalette( ScreenDC, hpalOld, FALSE ); + + GetSystemPaletteEntries(ScreenDC,0,NumColors,pe); + + for (i=0; i= 236 ); + + GetSystemPaletteEntries(ScreenDC,0,NumSysColors/2,LogicalPalette.palPalEntry); + GetSystemPaletteEntries(ScreenDC,UserHighest+1,NumSysColors/2,LogicalPalette.palPalEntry+1+UserHighest); + + DeleteDC(ScreenDC); + + for (i=0; i<256; i++ ) { + + if ( (i >= UserLowest) && (i<=UserHighest) ) { + LogicalPalette.palPalEntry[i].peFlags = PC_NOCOLLAPSE; + } else + LogicalPalette.palPalEntry[i].peFlags = 0; + + target_palette[i*3+0] = LogicalPalette.palPalEntry[i].peRed; + target_palette[i*3+1] = LogicalPalette.palPalEntry[i].peGreen; + target_palette[i*3+2] = LogicalPalette.palPalEntry[i].peBlue; + } + + return CreatePalette( (LOGPALETTE *)&LogicalPalette ); +} + +HPALETTE gr_create_palette_254( ubyte * target_palette ) +{ + EZ_LOGPALETTE LogicalPalette; + HDC ScreenDC; + int NumSysColors, NumColors, UserLowest, UserHighest; + int i; + + // Pack in all the colors + for (i=0;i<256; i++) { + LogicalPalette.palPalEntry[i].peRed = target_palette[i*3+0]; + LogicalPalette.palPalEntry[i].peGreen = target_palette[i*3+1]; + LogicalPalette.palPalEntry[i].peBlue = target_palette[i*3+2]; + LogicalPalette.palPalEntry[i].peFlags = 0; //PC_EXPLICIT; + } + + // Create a 1-1 mapping of the system palette + LogicalPalette.palVersion = 0x300; + LogicalPalette.palNumEntries = 256; + + ScreenDC = CreateCompatibleDC(NULL); + + // Reset all the Palette Manager tables + SetSystemPaletteUse( ScreenDC, SYSPAL_NOSTATIC ); + SetSystemPaletteUse( ScreenDC, SYSPAL_STATIC ); + + if ( !(GetDeviceCaps(ScreenDC,RASTERCAPS) & RC_PALETTE) ) { + DeleteDC(ScreenDC); + return CreatePalette( (LOGPALETTE *)&LogicalPalette ); + } + + SetSystemPaletteUse( ScreenDC, SYSPAL_NOSTATIC ); + NumSysColors = 2; + NumColors = GetDeviceCaps( ScreenDC, SIZEPALETTE ); + + Assert( NumColors <= 256 ); + + UserLowest = NumSysColors/2; // 10 normally + UserHighest = NumColors - NumSysColors/2 - 1; // 245 normally + + Assert( (UserHighest - UserLowest + 1) >= 236 ); + + GetSystemPaletteEntries(ScreenDC,0,NumSysColors/2,LogicalPalette.palPalEntry); + GetSystemPaletteEntries(ScreenDC,UserHighest+1,NumSysColors/2,LogicalPalette.palPalEntry+1+UserHighest); + + DeleteDC(ScreenDC); + + for (i=0; i<256; i++ ) { + + if ( (i >= UserLowest) && (i<=UserHighest) ) { + LogicalPalette.palPalEntry[i].peFlags = PC_NOCOLLAPSE; + } else + LogicalPalette.palPalEntry[i].peFlags = 0; + + target_palette[i*3+0] = LogicalPalette.palPalEntry[i].peRed; + target_palette[i*3+1] = LogicalPalette.palPalEntry[i].peGreen; + target_palette[i*3+2] = LogicalPalette.palPalEntry[i].peBlue; + } + + return CreatePalette( (LOGPALETTE *)&LogicalPalette ); +} + +void grx_set_palette_internal( ubyte * new_pal ) +{ + if ( hPalette ) { + if (hDibDC) + SelectPalette( hDibDC, hOldPalette, FALSE ); + if (!DeleteObject(hPalette)) { + mprintf(( "JOHN: Couldn't delete palette object\n" )); + } + hPalette = NULL; + } + + + // Make sure color 0 is black + if ( (new_pal[0]!=0) || (new_pal[1]!=0) || (new_pal[2]!=0) ) { + // color 0 isn't black!! switch it! + int i; + int black_index = -1; + + for (i=1; i<256; i++ ) { + if ( (new_pal[i*3+0]==0) && (new_pal[i*3+1]==0) && (new_pal[i*3+2]==0) ) { + black_index = i; + break; + } + } + if ( black_index > -1 ) { + // swap black and color 0, so color 0 is black + ubyte tmp[3]; + tmp[0] = new_pal[black_index*3+0]; + tmp[1] = new_pal[black_index*3+1]; + tmp[2] = new_pal[black_index*3+2]; + + new_pal[black_index*3+0] = new_pal[0]; + new_pal[black_index*3+1] = new_pal[1]; + new_pal[black_index*3+2] = new_pal[2]; + + new_pal[0] = tmp[0]; + new_pal[1] = tmp[1]; + new_pal[2] = tmp[2]; + } else { + // no black in palette, force color 0 to be black. + new_pal[0] = 0; + new_pal[1] = 0; + new_pal[2] = 0; + } + } + + + + if ( gr_screen.bits_per_pixel==8 ) { + + // Name n_preserved One-one Speed Windowed? + // ------------------------------------------------------------------- + // gr_create_palette_256 256 0-255 Slow Yes + // gr_create_palette_254 254 1-254 Fast No + // gr_create_palette_236 236 10-245 Fast Yes + // gr_create_palette_0 0 none Fast Yes + +/* + n_preserved = 256; + + if ( n_preserved <= 0 ) { + hPalette = gr_create_palette_0(new_pal); // No colors mapped one-to-one, but probably has close to all 256 colors in it somewhere. + } else if ( n_preserved <= 236 ) { + hPalette = gr_create_palette_236(new_pal); // All colors except low 10 and high 10 mapped one-to-one + } else if ( n_preserved <= 254 ) { + hPalette = gr_create_palette_254(new_pal); // All colors except 0 and 255 mapped one-to-one, but changes system colors. Not pretty in a window. + } else { +*/ + hPalette = gr_create_palette_256(new_pal); // All 256 mapped one-to-one, but BLT's are slow. + + if ( hDibDC ) { + int i; + for (i=0; i<256; i++ ) { + DibInfo.Colors.aColors[i].rgbRed = new_pal[i*3+0]; + DibInfo.Colors.aColors[i].rgbGreen = new_pal[i*3+1]; + DibInfo.Colors.aColors[i].rgbBlue = new_pal[i*3+2]; + DibInfo.Colors.aColors[i].rgbReserved = 0; + } + + hOldPalette = SelectPalette( hDibDC, hPalette, FALSE ); + SetDIBColorTable( hDibDC, 0, 256, DibInfo.Colors.aColors ); + } + } else { + hPalette = NULL; + } +} + + + +void grx_set_palette( ubyte * new_pal, int is_alphacolor ) +{ + if ( hPalette ) { + Mouse_hidden++; + gr_reset_clip(); + gr_clear(); + gr_flip(); + Mouse_hidden--; + } + + grx_set_palette_internal(new_pal); +} + + +void grx_print_screen(char * filename) +{ + int i; + ubyte **row_data = (ubyte **)malloc( gr_screen.max_h * sizeof(ubyte *) ); + if ( !row_data ) { + mprintf(( "couldn't allocate enough memory to dump screen\n" )); + return; + } + + gr_lock(); + + for (i=0; i (r2)) (x) = (r2); } while(0) + +void grx_save_mouse_area(int x, int y, int w, int h ) +{ + Grx_mouse_saved_x1 = x; + Grx_mouse_saved_y1 = y; + Grx_mouse_saved_x2 = x+w-1; + Grx_mouse_saved_y2 = y+h-1; + + CLAMP(Grx_mouse_saved_x1, gr_screen.clip_left, gr_screen.clip_right ); + CLAMP(Grx_mouse_saved_x2, gr_screen.clip_left, gr_screen.clip_right ); + CLAMP(Grx_mouse_saved_y1, gr_screen.clip_top, gr_screen.clip_bottom ); + CLAMP(Grx_mouse_saved_y2, gr_screen.clip_top, gr_screen.clip_bottom ); + + Grx_mouse_saved_w = Grx_mouse_saved_x2 - Grx_mouse_saved_x1 + 1; + Grx_mouse_saved_h = Grx_mouse_saved_y2 - Grx_mouse_saved_y1 + 1; + + if ( Grx_mouse_saved_w < 1 ) return; + if ( Grx_mouse_saved_h < 1 ) return; + + // Make sure we're not saving too much! + Assert( (Grx_mouse_saved_w*Grx_mouse_saved_h) <= MAX_SAVE_SIZE ); + + Grx_mouse_saved = 1; + + gr_lock(); + + ubyte *sptr, *dptr; + + dptr = Grx_mouse_saved_data; + + for (int i=0; i gr_screen.max_w-1 ) { + gr_screen.clip_left = gr_screen.max_w-1-x; + } + if ( gr_screen.clip_right+x < 0 ) { + gr_screen.clip_right = -x; + } else if ( gr_screen.clip_right+x >= gr_screen.max_w-1 ) { + gr_screen.clip_right = gr_screen.max_w-1-x; + } + + if ( gr_screen.clip_top+y < 0 ) { + gr_screen.clip_top = -y; + } else if ( gr_screen.clip_top+y > gr_screen.max_h-1 ) { + gr_screen.clip_top = gr_screen.max_h-1-y; + } + + if ( gr_screen.clip_bottom+y < 0 ) { + gr_screen.clip_bottom = -y; + } else if ( gr_screen.clip_bottom+y > gr_screen.max_h-1 ) { + gr_screen.clip_bottom = gr_screen.max_h-1-y; + } + + gr_screen.clip_width = gr_screen.clip_right - gr_screen.clip_left + 1; + gr_screen.clip_height = gr_screen.clip_bottom - gr_screen.clip_top + 1; +} + +// resets the clipping region to entire screen +// +// should call this before gr_surface_flip() if you clipped some portion of the screen but still +// want a full-screen display +void grx_reset_clip() +{ + gr_screen.offset_x = 0; + gr_screen.offset_y = 0; + gr_screen.clip_left = 0; + gr_screen.clip_top = 0; + gr_screen.clip_right = gr_screen.max_w - 1; + gr_screen.clip_bottom = gr_screen.max_h - 1; + gr_screen.clip_width = gr_screen.max_w; + gr_screen.clip_height = gr_screen.max_h; +} + + +// Sets the current bitmap +void grx_set_bitmap( int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha, int sx, int sy ) +{ + gr_screen.current_alpha = alpha; + gr_screen.current_alphablend_mode = alphablend_mode; + gr_screen.current_bitblt_mode = bitblt_mode; + gr_screen.current_bitmap = bitmap_num; + gr_screen.current_bitmap_sx = sx; + gr_screen.current_bitmap_sy = sy; +} + + +// clears entire clipping region to black. +void grx_clear() +{ + gr_lock(); + + int i,w; + ubyte *pDestBits; + + w = gr_screen.clip_right-gr_screen.clip_left+1; + for (i=gr_screen.clip_top; i<=gr_screen.clip_bottom; i++) { + pDestBits = GR_SCREEN_PTR(ubyte,gr_screen.clip_left,i); + memset( pDestBits, 0, w ); + } + + gr_unlock(); +} + + + +void grx_start_frame() +{ +} + +void grx_stop_frame() +{ +} + +void gr_soft_fog_set(int fog_mode, int r, int g, int b, float near, float far) +{ +} + +void gr_soft_get_pixel(int x, int y, int *r, int *g, int *b) +{ +} + +void grx_fade_in(int instantaneous); +void grx_fade_out(int instantaneous); +void grx_flash(int r, int g, int b); + +static ubyte *Gr_saved_screen = NULL; +static uint Gr_saved_screen_palette_checksum = 0; +static ubyte Gr_saved_screen_palette[768]; + +int gr8_save_screen() +{ + int i; + gr_reset_clip(); + + if (gr_screen.bits_per_pixel != 8) { + mprintf(( "Save Screen only works in 8 bpp!\n" )); + return -1; + } + + if ( Gr_saved_screen ) { + mprintf(( "Screen alread saved!\n" )); + return -1; + } + + Gr_saved_screen = (ubyte *)malloc( gr_screen.max_w*gr_screen.max_h ); + if (!Gr_saved_screen) { + mprintf(( "Couldn't get memory for saved screen!\n" )); + return -1; + } + + Gr_saved_screen_palette_checksum = gr_palette_checksum; + memcpy( Gr_saved_screen_palette, gr_palette, 768 ); + + gr_lock(); + + for (i=0; i 255 ) { + v = 255; + } else if ( v < 0 ) { + v = 0; + } + Gr_gamma_lookup[i] = v; + } + +// ubyte new_pal[768]; +// if ( gr_screen.bits_per_pixel!=8 ) return; +// +// for (i=0; i<768; i++ ) { +// new_pal[i] = ubyte(Gr_gamma_lookup[gr_palette[i]]); +// } +// grx_change_palette( new_pal ); + + gr_screen.signature = Gr_signature++; +} + +void gr_soft_init() +{ +// int i; + HWND hwnd = (HWND)os_get_window(); + + // software mode only supports 640x480 + Assert(gr_screen.res == GR_640); + if(gr_screen.res != GR_640){ + gr_screen.res = GR_640; + gr_screen.max_w = 640; + gr_screen.max_h = 480; + } + + // Prepare the window to go full screen + if ( hwnd ) { + DWORD style, exstyle; + RECT client_rect; + + exstyle = 0; + style = WS_CAPTION | WS_SYSMENU; + + // Create Game Window + client_rect.left = client_rect.top = 0; + client_rect.right = gr_screen.max_w; + client_rect.bottom = gr_screen.max_h; + AdjustWindowRect(&client_rect,style,FALSE); + + RECT work_rect; + SystemParametersInfo( SPI_GETWORKAREA, 0, &work_rect, 0 ); + int x = work_rect.left + (( work_rect.right - work_rect.left )-(client_rect.right - client_rect.left))/2; + int y = work_rect.top; + if ( x < work_rect.left ) { + x = work_rect.left; + } + int WinX = x; + int WinY = y; + int WinW = client_rect.right - client_rect.left; + int WinH = client_rect.bottom - client_rect.top; + + ShowWindow(hwnd, SW_SHOWNORMAL ); + SetWindowLong( hwnd, GWL_STYLE, style ); + SetWindowLong( hwnd, GWL_EXSTYLE, exstyle ); + SetWindowPos( hwnd, HWND_NOTOPMOST, WinX, WinY, WinW, WinH, SWP_SHOWWINDOW ); + SetActiveWindow(hwnd); + SetForegroundWindow(hwnd); + } + + Palette_flashed = 0; + Palette_flashed_last_frame = 0; + + gr_screen.bits_per_pixel = 8; + gr_screen.bytes_per_pixel = 1; + + gr_buffer_create( gr_screen.max_w, gr_screen.max_h, gr_screen.bits_per_pixel ); + + gr_screen.offscreen_buffer_base = lpDibBits; + + gr_screen.rowsize = DibInfo.Header.biWidth*((gr_screen.bits_per_pixel+7)/8); + Assert( DibInfo.Header.biWidth == gr_screen.max_w ); + + if (DibInfo.Header.biHeight > 0) { + // top down + gr_screen.offscreen_buffer = (void *)((uint)gr_screen.offscreen_buffer_base + (gr_screen.max_h - 1) * gr_screen.rowsize); + gr_screen.rowsize *= -1; + } else { + // top up + gr_screen.offscreen_buffer = gr_screen.offscreen_buffer_base; + } + + grx_init_alphacolors(); + + gr_screen.gf_flip = grx_flip; + gr_screen.gf_flip_window = grx_flip_window; + gr_screen.gf_set_clip = grx_set_clip; + gr_screen.gf_reset_clip = grx_reset_clip; + gr_screen.gf_set_font = grx_set_font; + gr_screen.gf_set_color = grx_set_color; + gr_screen.gf_set_bitmap = grx_set_bitmap; + gr_screen.gf_create_shader = grx_create_shader; + gr_screen.gf_set_shader = grx_set_shader; + gr_screen.gf_clear = grx_clear; + // gr_screen.gf_bitmap = grx_bitmap; + // ]gr_screen.gf_bitmap_ex = grx_bitmap_ex; + + gr_screen.gf_aabitmap = grx_aabitmap; + gr_screen.gf_aabitmap_ex = grx_aabitmap_ex; + + gr_screen.gf_rect = grx_rect; + gr_screen.gf_shade = gr8_shade; + gr_screen.gf_string = gr8_string; + gr_screen.gf_circle = gr8_circle; + + gr_screen.gf_line = gr8_line; + gr_screen.gf_aaline = gr8_aaline; + gr_screen.gf_pixel = gr8_pixel; + gr_screen.gf_scaler = gr8_scaler; + gr_screen.gf_aascaler = gr8_aascaler; + gr_screen.gf_tmapper = grx_tmapper; + + gr_screen.gf_gradient = gr8_gradient; + + gr_screen.gf_set_palette = grx_set_palette; + gr_screen.gf_get_color = grx_get_color; + gr_screen.gf_init_color = grx_init_color; + gr_screen.gf_init_alphacolor = grx_init_alphacolor; + gr_screen.gf_set_color_fast = grx_set_color_fast; + gr_screen.gf_print_screen = grx_print_screen; + gr_screen.gf_start_frame = grx_start_frame; + gr_screen.gf_stop_frame = grx_stop_frame; + + gr_screen.gf_fade_in = grx_fade_in; + gr_screen.gf_fade_out = grx_fade_out; + gr_screen.gf_flash = grx_flash; + + + // Retrieves the zbuffer mode. + gr_screen.gf_zbuffer_get = gr8_zbuffer_get; + gr_screen.gf_zbuffer_set = gr8_zbuffer_set; + gr_screen.gf_zbuffer_clear = gr8_zbuffer_clear; + + gr_screen.gf_save_screen = gr8_save_screen; + gr_screen.gf_restore_screen = gr8_restore_screen; + gr_screen.gf_free_screen = gr8_free_screen; + + // Screen dumping stuff + gr_screen.gf_dump_frame_start = gr8_dump_frame_start; + gr_screen.gf_dump_frame_stop = gr8_dump_frame_stop; + gr_screen.gf_dump_frame = gr8_dump_frame; + + // Gamma stuff + gr_screen.gf_set_gamma = gr8_set_gamma; + + // Lock/unlock stuff + gr_screen.gf_lock = gr_soft_lock; + gr_screen.gf_unlock = gr_soft_unlock; + + // region + gr_screen.gf_get_region = grx_get_region; + + // fog stuff + gr_screen.gf_fog_set = gr_soft_fog_set; + + // pixel get + gr_screen.gf_get_pixel = gr_soft_get_pixel; + + // poly culling + gr_screen.gf_set_cull = gr_soft_set_cull; + + // cross fade + gr_screen.gf_cross_fade = gr_soft_cross_fade; + + // filter + gr_screen.gf_filter_set = gr_soft_filter_set; + + // tcache set + gr_screen.gf_tcache_set = gr_soft_tcache_set; + + // set clear color + gr_screen.gf_set_clear_color = gr_soft_set_clear_color; + + gr_reset_clip(); + gr_clear(); + gr_flip(); +} + +void gr_soft_force_windowed() +{ +} + +void gr_soft_cleanup() +{ + if (Gr_soft_inited) { + gr_buffer_release(); + Gr_soft_inited = 0; + } +} + +void grx_change_palette( ubyte * new_pal ) +{ + if ( hPalette ) { + if (hDibDC) + SelectPalette( hDibDC, hOldPalette, FALSE ); + if (!DeleteObject(hPalette)) + Int3(); + hPalette = NULL; + } + + hPalette = gr_create_palette_256(new_pal); // All 256 mapped one-to-one, but BLT's are slow. + + if ( hDibDC ) { + int i; + for (i=0; i<256; i++ ) { + DibInfo.Colors.aColors[i].rgbRed = new_pal[i*3+0]; + DibInfo.Colors.aColors[i].rgbGreen = new_pal[i*3+1]; + DibInfo.Colors.aColors[i].rgbBlue = new_pal[i*3+2]; + DibInfo.Colors.aColors[i].rgbReserved = 0; + } + + hOldPalette = SelectPalette( hDibDC, hPalette, FALSE ); + SetDIBColorTable( hDibDC, 0, 256, DibInfo.Colors.aColors ); + } +} + +void grx_flash( int r, int g, int b ) +{ + int t,i; + ubyte new_pal[768]; + + if ( (r==0) && (g==0) && (b==0) ) { + return; + } + + Palette_flashed++; + + for (i=0; i<256; i++ ) { + t = gr_palette[i*3+0] + r; + if ( t < 0 ) t = 0; else if (t>255) t = 255; + new_pal[i*3+0] = (ubyte)t; + + t = gr_palette[i*3+1] + g; + if ( t < 0 ) t = 0; else if (t>255) t = 255; + new_pal[i*3+1] = (ubyte)t; + + t = gr_palette[i*3+2] + b; + if ( t < 0 ) t = 0; else if (t>255) t = 255; + new_pal[i*3+2] = (ubyte)t; + } + + grx_change_palette( new_pal ); +} + + +static int gr_palette_faded_out = 0; + +#define FADE_TIME (F1_0/4) // How long to fade out + +void grx_fade_out(int instantaneous) +{ +#ifndef HARDWARE_ONLY + int i; + ubyte new_pal[768]; + + if (!gr_palette_faded_out) { + + if ( !instantaneous ) { + + int count = 0; + fix start_time, stop_time, t1; + + start_time = timer_get_fixed_seconds(); + t1 = 0; + + do { + for (i=0; i<768; i++ ) { + int c = (gr_palette[i]*(FADE_TIME-t1))/FADE_TIME; + if (c < 0 ) + c = 0; + else if ( c > 255 ) + c = 255; + + new_pal[i] = (ubyte)c; + } + grx_change_palette( new_pal ); + gr_flip(); + count++; + + t1 = timer_get_fixed_seconds() - start_time; + + } while ( (t1 < FADE_TIME) && (t1>=0) ); // Loop as long as time not up and timer hasn't rolled + + stop_time = timer_get_fixed_seconds(); + + mprintf(( "Took %d frames (and %.1f secs) to fade out\n", count, f2fl(stop_time-start_time) )); + + } + gr_palette_faded_out = 1; + } + + gr_reset_clip(); + gr_clear(); + gr_flip(); + memset( new_pal, 0, 768 ); + grx_change_palette( new_pal ); +#else + Int3(); +#endif +} + + +void grx_fade_in(int instantaneous) +{ +#ifndef HARDWARE_ONLY + int i; + ubyte new_pal[768]; + + if (gr_palette_faded_out) { + + if ( !instantaneous ) { + int count = 0; + fix start_time, stop_time, t1; + + start_time = timer_get_fixed_seconds(); + t1 = 0; + + do { + for (i=0; i<768; i++ ) { + int c = (gr_palette[i]*t1)/FADE_TIME; + if (c < 0 ) + c = 0; + else if ( c > 255 ) + c = 255; + + new_pal[i] = (ubyte)c; + } + grx_change_palette( new_pal ); + gr_flip(); + count++; + + t1 = timer_get_fixed_seconds() - start_time; + + } while ( (t1 < FADE_TIME) && (t1>=0) ); // Loop as long as time not up and timer hasn't rolled + + stop_time = timer_get_fixed_seconds(); + + mprintf(( "Took %d frames (and %.1f secs) to fade in\n", count, f2fl(stop_time-start_time) )); + } + gr_palette_faded_out = 0; + } + + memcpy( new_pal, gr_palette, 768 ); + grx_change_palette( new_pal ); +#else + Int3(); +#endif +} + + diff --git a/src/graphics/grzbuffer.cpp b/src/graphics/grzbuffer.cpp new file mode 100644 index 0000000..4b46e35 --- /dev/null +++ b/src/graphics/grzbuffer.cpp @@ -0,0 +1,110 @@ +/* + * $Logfile: /Freespace2/code/Graphics/GrZbuffer.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for the software renderer's zbuffer + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 1 3/25/98 8:07p John + * Split software renderer into Win32 and DirectX + * + * $NoKeywords: $ + */ + +#include "osapi.h" +#include "2d.h" +#include "floating.h" +#include "grsoft.h" +#include "grinternal.h" + +int gr_zcount=GR_Z_COUNT; +int gr_zoffset=0; + +uint *gr_zbuffer = NULL; +int gr_zbuffer_w = 0; +int gr_zbuffer_h = 0; + +int gr_zbuffering = 0; +int gr_zbuffering_mode = 0; +int gr_global_zbuffering = 0; + +// If mode is FALSE, turn zbuffer off the entire frame, +// no matter what people pass to gr_zbuffer_set. +void gr8_zbuffer_clear(int mode) +{ + if ( mode ) { + gr_zbuffering = 1; + gr_zbuffering_mode = GR_ZBUFF_FULL; + gr_global_zbuffering = 1; + + if ( (!gr_zbuffer) || (gr_screen.max_w!=gr_zbuffer_w) || (gr_screen.max_h!=gr_zbuffer_h) ) { + //mprintf(( "Allocating a %d x %d zbuffer\n", gr_screen.max_w, gr_screen.max_h )); + if ( gr_zbuffer ) { + free(gr_zbuffer); + gr_zbuffer = NULL; + } + gr_zbuffer_w = gr_screen.max_w; + gr_zbuffer_h = gr_screen.max_h; + gr_zbuffer = (uint *)malloc(gr_zbuffer_w*gr_zbuffer_h*sizeof(uint)); + if ( !gr_zbuffer ) { + Error( LOCATION, "Couldn't allocate zbuffer\n" ); + gr_zbuffering = 0; + return; + } + memset( gr_zbuffer, 0, gr_zbuffer_w*gr_zbuffer_h*sizeof(uint) ); + } + + + gr_zcount++; + gr_zoffset += GR_Z_RANGE; + if ( gr_zcount >= (GR_Z_COUNT-16) ) { + //mprintf(( "Bing!\n" )); + memset( gr_zbuffer, 0, gr_zbuffer_w*gr_zbuffer_h*sizeof(uint) ); + gr_zcount = 0; + gr_zoffset = GR_Z_RANGE*16; + } + } else { + gr_zbuffering = 0; + gr_zbuffering_mode = GR_ZBUFF_NONE; + gr_global_zbuffering = 0; + } +} + + +int gr8_zbuffer_get() +{ + if ( !gr_global_zbuffering ) { + return GR_ZBUFF_NONE; + } + return gr_zbuffering_mode; +} + +int gr8_zbuffer_set(int mode) +{ + if ( !gr_global_zbuffering ) { + gr_zbuffering = 0; + return GR_ZBUFF_NONE; + } + + int tmp = gr_zbuffering_mode; + + gr_zbuffering_mode = mode; + + if ( gr_zbuffering_mode == GR_ZBUFF_NONE ) { + gr_zbuffering = 0; + } else { + gr_zbuffering = 1; + } + return tmp; +} diff --git a/src/graphics/line.cpp b/src/graphics/line.cpp new file mode 100644 index 0000000..fb539d1 --- /dev/null +++ b/src/graphics/line.cpp @@ -0,0 +1,188 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Line.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for drawing lines. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 12/02/98 5:47p Dave + * Put in interface xstr code. Converted barracks screen to new format. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 21 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 20 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 19 1/13/98 10:20a John + * Added code to support "glass" in alphacolors + * + * 18 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 17 11/29/97 2:06p John + * added mode 16-bpp support + * + * 16 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 15 10/14/97 8:08a John + * added a bunch more 16 bit support + * + * 14 10/03/97 9:10a John + * added better antialiased line drawer + * + * 13 9/09/97 10:39a Sandeep + * Fixed compiler warnings level 4 (sorta, john is fixing most of it) + * + * 12 6/13/97 5:35p John + * added some antialiased bitmaps and lines + * + * 11 6/06/97 2:40p John + * Made all the radar dim in/out + * + * 10 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 9 11/26/96 6:50p John + * Added some more hicolor primitives. Made windowed mode run as current + * bpp, if bpp is 8,16,or 32. + * + * 8 10/26/96 2:56p John + * Got gradient code working. + * + * 7 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#ifndef PLAT_UNIX +#include +#include +#endif + +#include "2d.h" +#include "grinternal.h" +#include "floating.h" +#include "line.h" +#include "key.h" + + +void gr8_uline(int x1,int y1,int x2,int y2) +{ + int i; + int xstep,ystep; + int dy=y2-y1; + int dx=x2-x1; + int error_term=0; + + gr_lock(); + ubyte *dptr = GR_SCREEN_PTR(ubyte,x1,y1); + ubyte color = gr_screen.current_color.raw8; + + if(dy<0) { + dy=-dy; + ystep=-gr_screen.rowsize / gr_screen.bytes_per_pixel; + } else { + ystep=gr_screen.rowsize / gr_screen.bytes_per_pixel; + } + + if(dx<0) { + dx=-dx; + xstep=-1; + } else { + xstep=1; + } + + /* HARDWARE_ONLY - removed alpha color table stuff + if ( Current_alphacolor ) { + if(dx>dy) { + + for(i=dx+1;i>0;i--) { + *dptr = Current_alphacolor->table.lookup[14][*dptr]; + dptr += xstep; + error_term+=dy; + + if(error_term>dx) { + error_term-=dx; + dptr+=ystep; + } + } + } else { + + for(i=dy+1;i>0;i--) { + *dptr = Current_alphacolor->table.lookup[14][*dptr]; + dptr += ystep; + error_term+=dx; + if(error_term>0) { + error_term-=dy; + dptr+=xstep; + } + + } + + } + } else { + */ + if(dx>dy) { + + for(i=dx+1;i>0;i--) { + *dptr = color; + dptr += xstep; + error_term+=dy; + + if(error_term>dx) { + error_term-=dx; + dptr+=ystep; + } + } + } else { + + for(i=dy+1;i>0;i--) { + *dptr = color; + dptr += ystep; + error_term+=dx; + if(error_term>0) { + error_term-=dy; + dptr+=xstep; + } + + } + + } + gr_unlock(); +} + + + +void gr8_line(int x1,int y1,int x2,int y2) +{ + int clipped = 0, swapped=0; + + INT_CLIPLINE(x1,y1,x2,y2,gr_screen.clip_left,gr_screen.clip_top,gr_screen.clip_right,gr_screen.clip_bottom,return,clipped=1,swapped=1); + + gr8_uline(x1,y1,x2,y2); +} + + + diff --git a/src/graphics/pixel.cpp b/src/graphics/pixel.cpp new file mode 100644 index 0000000..303429f --- /dev/null +++ b/src/graphics/pixel.cpp @@ -0,0 +1,99 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Pixel.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to plot a dot. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 12/02/98 5:47p Dave + * Put in interface xstr code. Converted barracks screen to new format. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 14 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 13 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 12 1/13/98 10:20a John + * Added code to support "glass" in alphacolors + * + * 11 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 10 10/14/97 8:08a John + * added a bunch more 16 bit support + * + * 9 10/09/97 5:23p John + * Added support for more 16-bpp functions + * + * 8 6/06/97 2:40p John + * Made all the radar dim in/out + * + * 7 5/29/97 3:09p John + * Took out debug menu. + * Made software scaler draw larger bitmaps. + * Optimized Direct3D some. + * + * 6 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 5 1/09/97 11:35a John + * Added some 2d functions to get/put screen images. + * + * 4 11/07/96 6:19p John + * Added a bunch of 16bpp primitives so the game sort of runs in 16bpp + * mode. + * + * 3 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#include "2d.h" +#include "grinternal.h" +#include "pixel.h" +#include "palman.h" + +void gr8_pixel( int x, int y ) +{ + ubyte * dptr; + + if ( x < gr_screen.clip_left ) return; + if ( x > gr_screen.clip_right ) return; + if ( y < gr_screen.clip_top ) return; + if ( y > gr_screen.clip_bottom ) return; + + gr_lock(); + + dptr = GR_SCREEN_PTR(ubyte,x, y); + if ( Current_alphacolor ) { + // *dptr = Current_alphacolor->table.lookup[14][*dptr]; + } else { + *dptr = gr_screen.current_color.raw8; + } + + gr_unlock(); +} + + + diff --git a/src/graphics/rect.cpp b/src/graphics/rect.cpp new file mode 100644 index 0000000..6058577 --- /dev/null +++ b/src/graphics/rect.cpp @@ -0,0 +1,126 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Rect.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to draw rectangles. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 12/02/98 5:47p Dave + * Put in interface xstr code. Converted barracks screen to new format. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 3 3/10/98 4:18p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 2 10/26/96 2:56p John + * Got gradient code working. + * + * 1 10/26/96 1:32p John + * Initial rev + * + * $NoKeywords: $ + */ + +#include "2d.h" +#include "grinternal.h" + +void grx_rect(int x,int y,int w,int h) +{ + int i; + int x1 = x, x2; + int y1 = y, y2; + + if ( w > 0 ) + x2 = x + w - 1; + else + x2 = x + w + 1; + + if ( h > 0 ) + y2 = y + h - 1; + else + y2 = y + h + 1; + + if ( x2 < x1 ) { + int tmp; + tmp = x1; + x1 = x2; + x2 = tmp; + } + + if ( y2 < y1 ) { + int tmp; + tmp = y1; + y1 = y2; + y2 = tmp; + } + + // Check for completely offscreen! + if ( x1 > gr_screen.clip_right ) + return; + + if ( x2 < gr_screen.clip_left ) + return; + + if ( y1 > gr_screen.clip_bottom ) + return; + + if ( y2 < gr_screen.clip_top ) + return; + + // Now clip + if ( x1 < gr_screen.clip_left ) + x1 = gr_screen.clip_left; + + if ( x2 > gr_screen.clip_right ) + x2 = gr_screen.clip_right; + + if ( y1 < gr_screen.clip_top ) + y1 = gr_screen.clip_top; + + if ( y2 > gr_screen.clip_bottom ) + y2 = gr_screen.clip_bottom; + + w = x2-x1+1; + if ( w < 1 ) return; + + h = y2-y1+1; + if ( h < 1 ) return; + + gr_lock(); + + ubyte *dptr; + + /* HARDWARE_ONLY + if ( Current_alphacolor ) { + for (i=0; itable.lookup[14][*dptr]; + } + } + } else { + */ + for (i=0; i8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 34 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 33 1/27/98 10:18a John + * fixed warning for optimized build + * + * 32 1/26/98 5:12p John + * Added in code for Pentium Pro specific optimizations. Speed up + * zbuffered correct tmapper about 35%. Speed up non-zbuffered scalers + * by about 25%. + * + * 31 1/19/98 6:15p John + * Fixed all my Optimized Build compiler warnings + * + * 30 12/04/97 12:09p John + * Made glows use scaler instead of tmapper so they don't rotate. Had to + * add a zbuffered scaler. + * + * 29 12/02/97 4:00p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 28 11/30/97 4:33p John + * added 32-bpp aascaler + * + * 27 11/30/97 3:57p John + * Made fixed 32-bpp translucency. Made BmpMan always map translucent + * color into 255 even if you aren't supposed to remap and make it's + * palette black. + * + * 26 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 25 11/29/97 2:06p John + * added mode 16-bpp support + * + * 24 11/14/97 12:30p John + * Fixed some DirectX bugs. Moved the 8-16 xlat tables into Graphics + * libs. Made 16-bpp DirectX modes know what bitmap format they're in. + * + * 23 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 22 10/15/97 4:48p John + * added 16-bpp aascaler + * + * 21 10/14/97 8:08a John + * added a bunch more 16 bit support + * + * 20 10/09/97 5:23p John + * Added support for more 16-bpp functions + * + * 19 8/04/97 4:47p John + * added gr_aascaler. + * + * 18 7/28/97 11:31a John + * made compiled code save all registers that it changes. When building + * optimized, my code was using EBX, and so was the compiler, so weird + * errors happened. Pushing/popping ebx fixed this. + * + * 17 7/16/97 5:29p John + * added palette table caching and made scaler and liner no light tmapper + * do alpha blending in 8 bpp mode. + * + * 16 7/10/97 2:06p John + * added code to specify alphablending type for bitmaps. + * + * 15 6/12/97 2:50a Lawrance + * bm_unlock() now passed bitmap number, not pointer + * + * 14 5/29/97 3:10p John + * Took out debug menu. + * Made software scaler draw larger bitmaps. + * Optimized Direct3D some. + * + * 13 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 12 12/04/96 2:02p John + * Added fast compiled code to the scaler in 8,16,32 bpp modes. + * + * 11 12/03/96 8:08p John + * Added compiled code to 8bpp scaler. Made bitmaps that are trying to + * scale up too big to not draw. + * + * 10 12/03/96 11:12a John + * added commented out "filtering" code to scaler. + * + * 9 11/19/96 2:42p Allender + * fix up 32 bit scaler + * + * 8 11/15/96 11:27a Allender + * 16bpp version of scaler + * + * 7 11/07/96 6:19p John + * Added a bunch of 16bpp primitives so the game sort of runs in 16bpp + * mode. + * + * 6 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#include +#include +#include +#ifndef PLAT_UNIX +#include +#endif +#include + +#include "scaler.h" +#include "2d.h" +#include "grinternal.h" +#include "floating.h" +#include "bmpman.h" +#include "palman.h" +#include "tmapscanline.h" +#include "systemvars.h" +#include "key.h" +#include "colors.h" + +#define MIN_SCALE_FACTOR 0.0001f + +#define USE_COMPILED_CODE + +#define TRANSPARENCY_COLOR_8 0xff +#define TRANSPARENCY_COLOR_16 0xffff +#define TRANSPARENCY_COLOR_32 0xffffffff + +#define FIND_SCALED_NUM(x,x0,x1,y0,y1) (((((x)-(x0))*((y1)-(y0)))/((x1)-(x0)))+(y0)) + +#define MAX_CODE_SIZE 32768 //65536 JAS: Determed to be 8208 on April1,98, 16K seems safe + +ubyte compiled_code[MAX_CODE_SIZE]; + +static int Max_size = 0; + +/* +void test_code() +{ + _asm mov ax, [esi+0xabcdef12] + _asm cmp ax, 255 + _asm je 0xabcdef12 + _asm mov [edi+0xabcdef12], ax + _asm mov ax, [esi+0xabcdef12] +} +*/ + + + +//---------------------------------------------------- +// scaler_create_compiled_code8 +// +// Creates code that looks like: +// +// @@: mov al, [esi+????] +// cmp al, TRANSPARENCY_COLOR_8 +// je @f ; jump to next @@ label +// mov [edi+???], al ; If the source pixel is scaled up +// mov [edi+???], al ; there might be a lot of these lines +// ... +// @@: mov al, [esi+????] +// + +ubyte *scaler_create_compiled_code8( int w, fix u, fix du ) +{ + int last_u, x; + ubyte * cc; + uint * last_jmp_pos; + + cc = compiled_code; + + //if ( abs(du) < F1_0 / 4 ) *cc++ = 0xCC; + +// *cc++ = 0xCC; // Int3 +// *cc++ = 0xc3; // RET + + last_u = -1; + + last_jmp_pos=NULL; + + for (x=0; x= &compiled_code[MAX_CODE_SIZE] ) + Int3(); // GET JOHN NOW! + +#ifdef FIND_MAX_SIZE + int size = cc - compiled_code; + if ( size > Max_size ) { + Max_size = size; + mprintf(( "Max size = %d\n", size )); + } +#endif + + return compiled_code; +} + +ubyte *scaler_create_compiled_code8_stippled( int w, fix u, fix du ) +{ + int last_u, x; + ubyte * cc; + uint * last_jmp_pos; + + cc = compiled_code; + + //if ( abs(du) < F1_0 / 4 ) *cc++ = 0xCC; + +// *cc++ = 0xCC; // Int3 +// *cc++ = 0xc3; // RET + + last_u = -1; + + last_jmp_pos=NULL; + + for (x=0; x= &compiled_code[MAX_CODE_SIZE] ) + Int3(); // GET JOHN NOW! + +#ifdef FIND_MAX_SIZE + int size = cc - compiled_code; + if ( size > Max_size ) { + Max_size = size; + mprintf(( "Max size = %d\n", size )); + } +#endif + + return compiled_code; +} + +void test_code1() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm mov ebx, -1 + _asm xor eax, eax + _asm xor ebx, ebx + _asm mov bl, BYTE PTR [edi-1412567278] + _asm add ebx, eax + _asm mov ebx, [ecx+ebx] ; blend it + _asm cmp ebp, [edx] + _asm add edx, 4 + _asm jl [0xABCDEF12] + +// xor eax, eax ; avoid ppro partial register stall +// mov ah, [esi+????] ; get the foreground pixel +// ; the following lines might be repeated +// xor ebx, ebx ; avoid ppro partial register stall +// mov bl, [edi+????] ; get the background pixel +// mov ebx, [ecx+ebx] ; blend it +// mov [edi+????], bl ; write it +#endif +} + +/* + 00130 b8 00 00 00 00 mov eax, 0 + 00135 8a a6 12 ef cd ab mov ah, BYTE PTR [esi-1412567278] + 0013b 8a 87 12 ef cd ab mov al, BYTE PTR [edi-1412567278] + 00141 8a 1c 01 mov bl, BYTE PTR [ecx+eax] + 00141 8b 1c 01 mov ebx, DWORD PTR [ecx+eax] + 00144 88 9f 12 ef cd ab mov BYTE PTR [edi-1412567278], bl + + + 00130 33 c0 xor eax, eax + 00132 33 db xor ebx, ebx + 00134 8a 9f 12 ef cd ab mov bl, BYTE PTR [edi-1412567278] + 0013a 03 d8 add ebx, eax + 0013c 8b 1c 19 mov ebx, DWORD PTR [ecx+ebx] + + 0013f 3b 2a cmp ebp, DWORD PTR [edx] + 00141 83 c2 04 add edx, 4 + + +*/ + +//---------------------------------------------------- +// scaler_create_compiled_code8_alpha +// +// Creates code that looks like: + +//=============== Pentium ====================== +// mov eax, 0 +// mov ah, [esi+????] ; get the foreground pixel +// ; the following lines might be repeated +// mov al, [edi+????] ; get the background pixel +// mov bl, [ecx+eax] ; blend it +// mov [edi+????], bl ; write it +// ... + +//============= Pentium Pro code ============= +// xor eax, eax ; avoid ppro partial register stall +// mov ah, [esi+????] ; get the foreground pixel +// ; the following lines might be repeated +// xor ebx, ebx ; avoid ppro partial register stall +// mov bl, [edi+????] ; get the background pixel +// mov ebx, [ecx+ebx] ; blend it +// mov [edi+????], bl ; write it + + +ubyte *scaler_create_compiled_code8_alpha( int w, fix u, fix du ) +{ + int last_u, x; + ubyte * cc; + + cc = compiled_code; + + //if ( abs(du) < F1_0 / 4 ) *cc++ = 0xCC; + + //*cc++ = 0xCC; // Int3 + //*cc++ = 0xc3; // RET + + last_u = -1; + + if ( Gr_cpu > 5 ) { + // Pentium Pro optimized code. + + for (x=0; x= &compiled_code[MAX_CODE_SIZE] ) + Int3(); // GET JOHN NOW! + +#ifdef FIND_MAX_SIZE + int size = cc - compiled_code; + if ( size > Max_size ) { + Max_size = size; + mprintf(( "Max size = %d\n", size )); + } +#endif + + return compiled_code; +} + +/* + for (x=0; x *zbuf ) { + uint c = sbits[ tmp_u >> 16 ]<<8; + *dbits = *((ubyte *)(lookup + (*dbits | c))); + } + dbits++; + zbuf++; + tmp_u += du; + } +*/ + +//---------------------------------------------------- +// scaler_create_compiled_code8_alpha_zbuffered +// +// Creates code that looks like: +// mov eax, 0 +// mov ah, [esi+????] ; get the foreground pixel +// ; the following lines might be repeated +// cmp fx_w, [edx+?????] +// jle @f +// mov al, [edi+????] ; get the background pixel +// mov bl, [ecx+eax] ; blend it +// mov [edi+????], bl ; write it +// @@: +// ... + + + + +//void test_code1() +//{ +// _asm cmp 0xFFFFFFFF, [edx+0xabcdef12] +// _asm cmp ebp, [edx+0xabcdef12] +// _asm jle 0xabcdef12 +//} +//; 302 : _asm cmp ebp, [edx+0xabcdef12] +// 00244 3b aa 12 ef cd ab cmp ebp, DWORD PTR [edx-1412567278] +//; 303 : _asm jle 0xabcdef12 +// 0024a 0f 8e 12 ef cd ab jle -1412567278 ; abcdef12H + +ubyte *scaler_create_compiled_code8_alpha_zbuffered( int w, fix u, fix du ) +{ + int last_u, x; + ubyte * cc; + uint *last_jmp_pos=NULL; + + cc = compiled_code; + + // xor eax, eax ; avoid ppro partial register stall +// mov ah, [esi+????] ; get the foreground pixel +// ; the following lines might be repeated +// xor ebx, ebx ; avoid ppro partial register stall +// mov bl, [edi+????] ; get the background pixel +// mov ebx, [ecx+ebx] ; blend it +// mov [edi+????], bl ; write it + + //if ( abs(du) < F1_0 / 4 ) *cc++ = 0xCC; + + //*cc++ = 0xCC; // Int3 + //*cc++ = 0xc3; // RET + last_u = -1; + + if ( Gr_cpu > 5 ) { + // Pentium Pro optimized code. + + for (x=0; x= &compiled_code[MAX_CODE_SIZE] ) + Int3(); // GET JOHN NOW! + +#ifdef FIND_MAX_SIZE + int size = cc - compiled_code; + if ( size > Max_size ) { + Max_size = size; + mprintf(( "Max sizeZ = %d\n", size )); + } +#endif + + return compiled_code; +} + + + +int Gr_scaler_zbuffering = 0; +uint Gr_global_z; + +MONITOR( ScalerNumCalls ); + + +//---------------------------------------------------- +// Scales current bitmap, between va and vb +void gr8_scaler(vertex *va, vertex *vb ) +{ +#if 1 + if(Pofview_running){ + return; + } + + float x0, y0, x1, y1; + float u0, v0, u1, v1; + float clipped_x0, clipped_y0, clipped_x1, clipped_y1; + float clipped_u0, clipped_v0, clipped_u1, clipped_v1; + float xmin, xmax, ymin, ymax; + int dx0, dy0, dx1, dy1; + + MONITOR_INC( ScalerNumCalls, 1 ); + + //============= CLIP IT ===================== + + x0 = va->sx; y0 = va->sy; + x1 = vb->sx; y1 = vb->sy; + + xmin = i2fl(gr_screen.clip_left); ymin = i2fl(gr_screen.clip_top); + xmax = i2fl(gr_screen.clip_right); ymax = i2fl(gr_screen.clip_bottom); + + u0 = va->u; v0 = va->v; + u1 = vb->u; v1 = vb->v; + + // Check for obviously offscreen bitmaps... + if ( (y1<=y0) || (x1<=x0) ) return; + if ( (x1xmax) ) return; + if ( (y1ymax) ) return; + + clipped_u0 = u0; clipped_v0 = v0; + clipped_u1 = u1; clipped_v1 = v1; + + clipped_x0 = x0; clipped_y0 = y0; + clipped_x1 = x1; clipped_y1 = y1; + + // Clip the left, moving u0 right as necessary + if ( x0 < xmin ) { + clipped_u0 = FIND_SCALED_NUM(xmin,x0,x1,u0,u1); + clipped_x0 = xmin; + } + + // Clip the right, moving u1 left as necessary + if ( x1 > xmax ) { + clipped_u1 = FIND_SCALED_NUM(xmax,x0,x1,u0,u1); + clipped_x1 = xmax; + } + + // Clip the top, moving v0 down as necessary + if ( y0 < ymin ) { + clipped_v0 = FIND_SCALED_NUM(ymin,y0,y1,v0,v1); + clipped_y0 = ymin; + } + + // Clip the bottom, moving v1 up as necessary + if ( y1 > ymax ) { + clipped_v1 = FIND_SCALED_NUM(ymax,y0,y1,v0,v1); + clipped_y1 = ymax; + } + + dx0 = fl2i(clipped_x0); dx1 = fl2i(clipped_x1); + dy0 = fl2i(clipped_y0); dy1 = fl2i(clipped_y1); + + if (dx1<=dx0) return; + if (dy1<=dy0) return; + + //============= DRAW IT ===================== + int u, v, du, dv; + int y, w; + ubyte * sbits, * dbits; + bitmap * bp; + ubyte * spixels; + float tmpu, tmpv; + + tmpu = (clipped_u1-clipped_u0) / (dx1-dx0); + if ( fl_abs(tmpu) < MIN_SCALE_FACTOR ) { + return; // scaled up way too far! + } + tmpv = (clipped_v1-clipped_v0) / (dy1-dy0); + if ( fl_abs(tmpv) < MIN_SCALE_FACTOR ) { + return; // scaled up way too far! + } + + int is_stippled = 0; + + /* + if ( !Detail.alpha_effects ) { + is_stippled = 1; + Gr_scaler_zbuffering = 0; + } + */ + + if ( is_stippled ) { + bp = bm_lock( gr_screen.current_bitmap, 8, 0 ); + } else { + bp = bm_lock( gr_screen.current_bitmap, 8, 0 ); + } + + + du = fl2f(tmpu*(bp->w-1)); + dv = fl2f(tmpv*(bp->h-1)); + + v = fl2f(clipped_v0*(bp->h-1)); + u = fl2f(clipped_u0*(bp->w-1)); + w = dx1 - dx0 + 1; + if ( w < 2 ) { + bm_unlock(gr_screen.current_bitmap); + return; + } + + uint fx_w = 0; + if ( Gr_scaler_zbuffering && gr_zbuffering ) { + fx_w = (uint)fl2i(va->sw * GR_Z_RANGE)+gr_zoffset; + Gr_global_z = fx_w; + } + +#ifdef USE_COMPILED_CODE + ubyte *cc=NULL; + + if ( Gr_scaler_zbuffering && gr_zbuffering ) { + if ( gr_screen.current_alphablend_mode == GR_ALPHABLEND_FILTER ) { + cc = scaler_create_compiled_code8_alpha_zbuffered( w, u, du ); + } + } else { + if ( gr_screen.current_alphablend_mode == GR_ALPHABLEND_FILTER ) { + if ( is_stippled ) { + cc = scaler_create_compiled_code8_stippled( w, u, du ); + } else { + cc = scaler_create_compiled_code8_alpha( w, u, du ); + } + } else { + cc = scaler_create_compiled_code8( w, u, du ); + } + } + +#endif + + spixels = (ubyte *)bp->data; + + gr_lock(); + Tmap.pScreenBits = (uint)gr_screen.offscreen_buffer_base; + + uint *zbuf; + + for (y=dy0; y<=dy1; v += dv, y++ ) { + if ( is_stippled && (y&1) ) { + sbits = &spixels[bp->rowsize*(v>>16)+f2i(du)]; + dbits = GR_SCREEN_PTR(ubyte,dx0+1,y); + } else { + sbits = &spixels[bp->rowsize*(v>>16)]; + dbits = GR_SCREEN_PTR(ubyte,dx0,y); + } + uint lookup = 0; + + if ( gr_screen.current_alphablend_mode == GR_ALPHABLEND_FILTER ) { + lookup = (uint)palette_get_blend_table(gr_screen.current_alpha); + } + + if ( Gr_scaler_zbuffering && gr_zbuffering ) { + zbuf = (uint *)&gr_zbuffer[(uint)dbits-(uint)Tmap.pScreenBits]; + } + +#ifdef USE_COMPILED_CODE + // Call the compiled code to draw one scanline + if ( Gr_scaler_zbuffering && gr_zbuffering && (gr_screen.current_alphablend_mode != GR_ALPHABLEND_FILTER)) { + Int3(); + + /* + int x, tmp_u; + tmp_u = u; + + for (x=0; x *zbuf ) { + ubyte c = sbits[ tmp_u >> 16 ]; + if ( c != TRANSPARENCY_COLOR_8 ) *dbits = c; + } + zbuf++; + dbits++; + tmp_u += du; + } + */ + } else { +/* { + int x, tmp_u; + tmp_u = u; + + + for (x=0; x *zbuf ) { + uint c = sbits[ tmp_u >> 16 ]<<8; + *dbits = *((ubyte *)(lookup + (*dbits | c))); + } + dbits++; + zbuf++; + tmp_u += du; + } + } +*/ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm push esi + _asm push edi + _asm push edx + _asm push ecx + _asm push ebx + _asm push eax + _asm mov ecx, lookup + _asm mov esi, sbits + _asm mov edi, dbits + _asm mov eax, cc + _asm mov edx, zbuf + _asm push ebp + _asm mov ebp, Gr_global_z + _asm call eax + _asm pop ebp + _asm pop eax + _asm pop ebx + _asm pop ecx + _asm pop edx + _asm pop edi + _asm pop esi +#endif + } +#else + if ( gr_screen.current_alphablend_mode == GR_ALPHABLEND_FILTER ) { + if ( Gr_scaler_zbuffering && gr_zbuffering ) { + int x, tmp_u; + tmp_u = u; + + for (x=0; x *zbuf ) { + uint c = sbits[ tmp_u >> 16 ]<<8; + *dbits = *((ubyte *)(lookup + (*dbits | c))); + } + dbits++; + zbuf++; + tmp_u += du; + } + } else { + int x, tmp_u; + tmp_u = u; + for (x=0; x> 16 ]<<8; + *dbits++ = palette_blend[*dbits|c]; + tmp_u += du; + } + } + } else { + if ( Gr_scaler_zbuffering && gr_zbuffering ) { + int x, tmp_u; + tmp_u = u; + + for (x=0; x *zbuf ) { + ubyte c = sbits[ tmp_u >> 16 ]; + if ( c != TRANSPARENCY_COLOR_8 ) *dbits = c; + } + zbuf++; + dbits++; + tmp_u += du; + } + } else { + int x, tmp_u; + tmp_u = u; + for (x=0; x> 16 ]; + if ( c != TRANSPARENCY_COLOR_8 ) *dbits = c; + dbits++; + tmp_u += du; + } + } + } +#endif + } + + gr_unlock(); + bm_unlock(gr_screen.current_bitmap); +#endif +} + +int aiee = 0; +alphacolor_old old_alphac; +//---------------------------------------------------- +// Scales current bitmap, between va and vb +void gr8_aascaler(vertex *va, vertex *vb ) +{ + float x0, y0, x1, y1; + float u0, v0, u1, v1; + float clipped_x0, clipped_y0, clipped_x1, clipped_y1; + float clipped_u0, clipped_v0, clipped_u1, clipped_v1; + float xmin, xmax, ymin, ymax; + int dx0, dy0, dx1, dy1; + + //if ( !Current_alphacolor ) return; + + MONITOR_INC( ScalerNumCalls, 1 ); + + Assert(Fred_running); + if(!aiee){ + old_alphac.used = 1; + old_alphac.r = 93; + old_alphac.g = 93; + old_alphac.b = 128; + old_alphac.alpha = 255; + //ac->type = type; + //ac->clr=clr; + //93, 93, 128, 255 + calc_alphacolor_old(&old_alphac); + aiee = 1; + } + + //============= CLIP IT ===================== + + x0 = va->sx; y0 = va->sy; + x1 = vb->sx; y1 = vb->sy; + + xmin = i2fl(gr_screen.clip_left); ymin = i2fl(gr_screen.clip_top); + xmax = i2fl(gr_screen.clip_right); ymax = i2fl(gr_screen.clip_bottom); + + u0 = va->u; v0 = va->v; + u1 = vb->u; v1 = vb->v; + + // Check for obviously offscreen bitmaps... + if ( (y1<=y0) || (x1<=x0) ) return; + if ( (x1xmax) ) return; + if ( (y1ymax) ) return; + + clipped_u0 = u0; clipped_v0 = v0; + clipped_u1 = u1; clipped_v1 = v1; + + clipped_x0 = x0; clipped_y0 = y0; + clipped_x1 = x1; clipped_y1 = y1; + + // Clip the left, moving u0 right as necessary + if ( x0 < xmin ) { + clipped_u0 = FIND_SCALED_NUM(xmin,x0,x1,u0,u1); + clipped_x0 = xmin; + } + + // Clip the right, moving u1 left as necessary + if ( x1 > xmax ) { + clipped_u1 = FIND_SCALED_NUM(xmax,x0,x1,u0,u1); + clipped_x1 = xmax; + } + + // Clip the top, moving v0 down as necessary + if ( y0 < ymin ) { + clipped_v0 = FIND_SCALED_NUM(ymin,y0,y1,v0,v1); + clipped_y0 = ymin; + } + + // Clip the bottom, moving v1 up as necessary + if ( y1 > ymax ) { + clipped_v1 = FIND_SCALED_NUM(ymax,y0,y1,v0,v1); + clipped_y1 = ymax; + } + + dx0 = fl2i(clipped_x0); dx1 = fl2i(clipped_x1); + dy0 = fl2i(clipped_y0); dy1 = fl2i(clipped_y1); + + if (dx1<=dx0) return; + if (dy1<=dy0) return; + + //============= DRAW IT ===================== + int u, v, du, dv; + int y, w; + ubyte * sbits, * dbits; + bitmap * bp; + ubyte * spixels; + float tmpu, tmpv; + + tmpu = (clipped_u1-clipped_u0) / (dx1-dx0); + if ( fl_abs(tmpu) < MIN_SCALE_FACTOR ) { + return; // scaled up way too far! + } + tmpv = (clipped_v1-clipped_v0) / (dy1-dy0); + if ( fl_abs(tmpv) < MIN_SCALE_FACTOR ) { + return; // scaled up way too far! + } + + bp = bm_lock( gr_screen.current_bitmap, 8, BMP_AABITMAP ); + + du = fl2f(tmpu*(bp->w-1)); + dv = fl2f(tmpv*(bp->h-1)); + + v = fl2f(clipped_v0*(bp->h-1)); + u = fl2f(clipped_u0*(bp->w-1)); + w = dx1 - dx0 + 1; + +#ifdef USE_COMPILED_CODE + ubyte *cc; + + if ( Gr_scaler_zbuffering && gr_zbuffering ) { + //cc = scaler_create_compiled_code8_alpha_zbuffered( w, u, du ); + } else { + cc = scaler_create_compiled_code8_alpha( w, u, du ); + } + +#endif + + spixels = (ubyte *)bp->data; + + gr_lock(); + + uint fx_w = 0; + if ( Gr_scaler_zbuffering && gr_zbuffering ) { + fx_w = (uint)fl2i(va->sw * GR_Z_RANGE)+gr_zoffset; + } + + for (y=dy0; y<=dy1; y++ ) { + sbits = &spixels[bp->rowsize*(v>>16)]; + dbits = GR_SCREEN_PTR(ubyte,dx0,y); + // uint lookup = (uint)&Current_alphacolor->table.lookup[0][0]; + uint lookup = (uint)&old_alphac.table.lookup[0][0]; + +#ifdef USE_COMPILED_CODE + // Call the compiled code to draw one scanline + if ( Gr_scaler_zbuffering && gr_zbuffering ) { + int x, tmp_u; + tmp_u = u; + + uint *zbuf = (uint *)&gr_zbuffer[(uint)dbits-(uint)Tmap.pScreenBits]; + + for (x=0; x *zbuf ) { + // uint c = sbits[ tmp_u >> 16 ]; + // *dbits = Current_alphacolor->table.lookup[c][*dbits]; + *dbits = (ubyte)0x00; + } + zbuf++; + dbits++; + tmp_u += du; + } + } else { +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm push esi + _asm push edi + _asm push ecx + _asm push ebx + _asm push eax + _asm mov ecx, lookup + _asm mov esi, sbits + _asm mov edi, dbits + _asm mov eax, cc + _asm call eax + _asm pop eax + _asm pop ebx + _asm pop ecx + _asm pop edi + _asm pop esi +#endif + } +#else + if ( Gr_scaler_zbuffering && gr_zbuffering ) { + int x, tmp_u; + tmp_u = u; + + uint *zbuf = (uint *)&gr_zbuffer[(uint)dbits-(uint)Tmap.pScreenBits]; + + for (x=0; x *zbuf ) { + uint c = sbits[ tmp_u >> 16 ]; + *dbits = Current_alphacolor->table.lookup[c][*dbits]; + } + zbuf++; + dbits++; + tmp_u += du; + } + } else { + int x, tmp_u; + tmp_u = u; + for (x=0; x> 16 ]; + *dbits = Current_alphacolor->table.lookup[c][*dbits]; + dbits++; + tmp_u += du; + } + } +#endif + v += dv; + } + + gr_unlock(); + + bm_unlock(gr_screen.current_bitmap); +} + diff --git a/src/graphics/shade.cpp b/src/graphics/shade.cpp new file mode 100644 index 0000000..398d56a --- /dev/null +++ b/src/graphics/shade.cpp @@ -0,0 +1,254 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Shade.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to shade an area. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 19 3/12/98 5:36p John + * Took out any unused shaders. Made shader code take rgbc instead of + * matrix and vector since noone used it like a matrix and it would have + * been impossible to do in hardware. Made Glide implement a basic + * shader for online help. + * + * 18 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 17 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 16 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 15 10/09/97 5:23p John + * Added support for more 16-bpp functions + * + * 14 10/03/97 12:16p John + * optimized the shader. About 50% faster. + * + * 13 10/03/97 9:10a John + * added better antialiased line drawer + * + * 12 9/09/97 10:41a Sandeep + * fixed warning level 4 + * + * 11 6/18/97 12:07p John + * fixed some color bugs + * + * 10 6/17/97 7:04p John + * added d3d support for gradients. + * fixed some color bugs by adding screen signatures instead of watching + * flags and palette changes. + * + * 9 5/28/97 8:59a John + * Fixed bug with shader not working when switching to fullscreen. + * + * 8 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 7 11/19/96 2:44p Allender + * fix up shader for 15 bpp + * + * 6 11/18/96 2:27p Allender + * made faster hacked version of shader for 16 bits + * + * 5 11/18/96 1:48p Allender + * added 16 bit version of (very slow) shader + * + * 4 11/15/96 3:34p Allender + * started on 16 bit support for the shader + * + * 3 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#include "2d.h" +#include "grinternal.h" +#include "floating.h" +#include "line.h" +#include "palman.h" + +void grx_create_shader(shader * shade, float r, float g, float b, float c ) +{ + int i; + float Sr, Sg, Sb; + float Dr, Dg, Db; + int ri, gi, bi; + + shade->screen_sig = gr_screen.signature; + shade->r = r; + shade->g = g; + shade->b = b; + shade->c = c; + + for (i=0; i<256; i++ ) { + Sr = i2fl( gr_palette[i*3+0] ); + Sg = i2fl( gr_palette[i*3+1] ); + Sb = i2fl( gr_palette[i*3+2] ); + Dr = Sr*r + Sg*r + Sb*r + c*256.0f; + Dg = Sr*g + Sg*g + Sb*g + c*256.0f; + Db = Sr*b + Sg*b + Sb*b + c*256.0f; + ri = fl2i(Dr); if ( ri < 0 ) ri = 0; else if (ri>255) ri = 255; + gi = fl2i(Dg); if ( gi < 0 ) gi = 0; else if (gi>255) gi = 255; + bi = fl2i(Db); if ( bi < 0 ) bi = 0; else if (bi>255) bi = 255; + shade->lookup[i] = (unsigned char)(palette_find(ri,gi,bi)); + } + +} + +void grx_set_shader( shader * shade ) +{ + if ( shade ) { + if (shade->screen_sig != gr_screen.signature) { + gr_create_shader( shade, shade->r, shade->g, shade->b, shade->c ); + } + gr_screen.current_shader = *shade; + } else { + gr_create_shader( &gr_screen.current_shader, 0.0f, 0.0f, 0.0f, 0.0f ); + } +} + + +void gr8_shade(int x,int y,int w,int h) +{ + int x1, y1, x2, y2; + ubyte *xlat_table; + + x1 = x; + if (x1 < gr_screen.clip_left) x1 = gr_screen.clip_left; + if (x1 > gr_screen.clip_right) x1 = gr_screen.clip_right; + + x2 = x+w-1; + if (x2 < gr_screen.clip_left) x2 = gr_screen.clip_left; + if (x2 > gr_screen.clip_right) x2 = gr_screen.clip_right; + + y1 = y; + if (y1 < gr_screen.clip_top) y1 = gr_screen.clip_top; + if (y1 > gr_screen.clip_bottom) y1 = gr_screen.clip_bottom; + + y2 = y+h-1; + if (y2 < gr_screen.clip_top) y2 = gr_screen.clip_top; + if (y2 > gr_screen.clip_bottom) y2 = gr_screen.clip_bottom; + + w = x2 - x1 + 1; + if ( w < 1 ) return; + + h = y2 - y1 + 1; + if ( h < 1 ) return; + + int i; + xlat_table = gr_screen.current_shader.lookup; + + gr_lock(); + + for (i=0; i 0 ) { +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm push eax + _asm push ebx + _asm push ecx + _asm push edx + _asm push edi + _asm push esi + _asm mov esi, xlat_table + _asm mov edi, dp + _asm mov edi, dp + _asm mov ecx, wd4 + _asm mov eax, 0 + _asm mov ebx, 0 + _asm mov edx, 0 + + NextPixel: + _asm mov eax, [edi] + + _asm mov dl, al + _asm mov bl, ah + + _asm add edi, 4 + + _asm mov al, [edx+esi] + _asm mov ah, [ebx+esi] + + _asm ror eax, 16 + + _asm mov dl, al + _asm mov bl, ah + + _asm mov al, [edx+esi] + _asm mov ah, [ebx+esi] + + _asm ror eax, 16 + + _asm mov [edi-4], eax + + _asm dec ecx + _asm jnz NextPixel + + + _asm mov dp, edi + + _asm pop esi + _asm pop edi + _asm pop edx + _asm pop ecx + _asm pop ebx + _asm pop eax +#endif + } + + for (int j=0; j 255 ) v = 255; + rgbtable1[i] = v; + rgbtable2[i] = v<<8; + rgbtable3[i] = v<<16; + } +} + + +void asm_tmap_scanline_lln(); +void asm_tmap_scanline_lln_tiled(); + +void tmapscan_lln8( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + Tmap1.dest_row_data = GR_SCREEN_PTR(ubyte,lx,y); + Tmap1.loop_count = rx - lx; + Tmap1.pixptr = (unsigned char *)tmap_bitmap->data; + Tmap1.bp = tmap_bitmap; + Tmap1.src_offset = tmap_bitmap->w; + + Tmap1.fx_u = fl2f(p->u); + Tmap1.fx_v = fl2f(p->v); + Tmap1.fx_l = fl2f(p->l*32.0); + Tmap1.fx_dl_dx = fl2f(dp->l*32.0); + Tmap1.fx_du_dx = fl2f(dp->u); + Tmap1.fx_dv_dx = fl2f(dp->v); + Tmap1.fx_u_right = fl2f(rp->u); + Tmap1.fx_v_right = fl2f(rp->v); + + asm_tmap_scanline_lln(); +} + +extern void asm_tmap_scanline_lnt(); + +void tmapscan_lnt8( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + Tmap1.dest_row_data = GR_SCREEN_PTR(ubyte,lx,y); + Tmap1.loop_count = rx - lx; + Tmap1.pixptr = (unsigned char *)tmap_bitmap->data; + Tmap1.bp = tmap_bitmap; + Tmap1.src_offset = tmap_bitmap->w; + + Tmap1.fx_u = fl2f(p->u); + Tmap1.fx_v = fl2f(p->v); + Tmap1.fx_du_dx = fl2f(dp->u); + Tmap1.fx_dv_dx = fl2f(dp->v); + Tmap1.fx_u_right = fl2f(rp->u); + Tmap1.fx_v_right = fl2f(rp->v); + + asm_tmap_scanline_lnt(); +} + +extern void asm_tmap_scanline_lnn(); + +void tmapscan_lnn8( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + Tmap1.dest_row_data = GR_SCREEN_PTR(ubyte,lx,y); + Tmap1.loop_count = rx - lx; + Tmap1.pixptr = (unsigned char *)tmap_bitmap->data; + Tmap1.bp = tmap_bitmap; + Tmap1.src_offset = tmap_bitmap->w; + + Tmap1.fx_u = fl2f(p->u); + Tmap1.fx_v = fl2f(p->v); + Tmap1.fx_du_dx = fl2f(dp->u); + Tmap1.fx_dv_dx = fl2f(dp->v); + Tmap1.fx_u_right = fl2f(rp->u); + Tmap1.fx_v_right = fl2f(rp->v); + + asm_tmap_scanline_lnn(); +} + + +void tmapscan_lln8_tiled( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + Tmap1.dest_row_data = GR_SCREEN_PTR(ubyte,lx,y); + Tmap1.loop_count = rx - lx; + Tmap1.fx_u = fl2f(p->u); + Tmap1.fx_v = fl2f(p->v); + Tmap1.fx_l = fl2f(p->l*32.0); + Tmap1.fx_du_dx = fl2f(dp->u); + Tmap1.fx_dv_dx = fl2f(dp->v); + Tmap1.fx_dl_dx = fl2f(dp->l*32.0); + Tmap1.fx_u_right = fl2f(rp->u); + Tmap1.fx_v_right = fl2f(rp->v); + Tmap1.pixptr = (unsigned char *)tmap_bitmap->data; + Tmap1.bp = tmap_bitmap; + Tmap1.src_offset = tmap_bitmap->w; + + Tmap1.BitmapWidth = tmap_bitmap->w; + Tmap1.BitmapHeight = tmap_bitmap->h; + + +// asm_tmap_scanline_lln_tiled(); + + +} + + + +void c_tmap_scanline_per_sub_new(); + +void tmapscan_pln8( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + Tmap1.dest_row_data = GR_SCREEN_PTR(ubyte,lx,y); + Tmap1.loop_count = rx - lx; + Tmap1.fx_l = fl2f(p->l*32.0); + Tmap1.fx_dl_dx = fl2f(dp->l*32.0); + + Tmap1.UOverZ = p->u; + Tmap1.VOverZ = p->v; + Tmap1.OneOverZ = p->sw; + + Tmap1.dUOverZdX8 = dp->u*32.0f; + Tmap1.dVOverZdX8 = dp->v*32.0f; + Tmap1.dOneOverZdX8 = dp->sw*32.0f; + + Tmap1.dUOverZdX = dp->u; + Tmap1.dVOverZdX = dp->v; + Tmap1.dOneOverZdX = dp->sw; + + Tmap1.RightUOverZ = rp->u; + Tmap1.RightVOverZ = rp->v; + Tmap1.RightOneOverZ = rp->sw; + + if ( Tmap1.fx_dl_dx < 0 ) { + Tmap1.fx_dl_dx = -Tmap1.fx_dl_dx; + Tmap1.fx_l = (67*F1_0)-Tmap1.fx_l; + Tmap1.fx_l_right = (67*F1_0)-Tmap1.fx_l_right; +// return; +// Assert( Tmap1.fx_l > 31*F1_0 ); +// Assert( Tmap1.fx_l < 66*F1_0 ); +// Assert( Tmap1.fx_dl_dx >= 0 ); +// Assert( Tmap1.fx_dl_dx < 31*F1_0 ); + } + +// return; + + + if (0) { + ubyte *dest, c; + int x; + fix l, dldx; + + l = Tmap1.fx_l; + dldx = Tmap1.fx_dl_dx; + dest = Tmap1.dest_row_data; + + for (x=Tmap1.loop_count; x >= 0; x-- ) { + //*dest++ = gr_fade_table[ ((l>>8)&(0xff00)) + 35 ]; + c = *dest; + *dest++ = c+1; + l += dldx; + } + return; + } + + + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + + // put the FPU in 32 bit mode + // @todo move this out of here! + + fstcw Tmap1.OldFPUCW // store copy of CW + mov ax,Tmap1.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap1.FPUCW,ax // store it + fldcw Tmap1.FPUCW // load the FPU + + mov ecx, Tmap1.loop_count // ecx = width + inc ecx + mov edi, Tmap1.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? +// jmp Return + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap1.Subdivisions,ecx // store widths + mov Tmap1.WidthModLength,eax + +// mov ebx,pLeft ; get left edge pointer +// mov edx,pGradients ; get gradients pointer + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap1.VOverZ // V/ZL + fld Tmap1.UOverZ // U/ZL V/ZL + fld Tmap1.OneOverZ // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap1.dOneOverZdX8 // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap1.dUOverZdX8 // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap1.dVOverZdX8 // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap1.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap1.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap1.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap1.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap1.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap1.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap1.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap1.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span ; st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms---->; V/ZL 1/ZL U/ZL UL VL + + fadd Tmap1.dVOverZdX8 ; V/ZR 1/ZL U/ZL UL VL + fxch st(1) ; 1/ZL V/ZR U/ZL UL VL + fadd Tmap1.dOneOverZdX8 ; 1/ZR V/ZR U/ZL UL VL + fxch st(2) ; U/ZL V/ZR 1/ZR UL VL + fadd Tmap1.dUOverZdX8 ; U/ZR V/ZR 1/ZR UL VL + fxch st(2) ; 1/ZR V/ZR U/ZR UL VL + fxch st(1) ; V/ZR 1/ZR U/ZR UL VL + + + ; set up affine registers + + ; setup delta values + + mov eax,Tmap1.DeltaV ; get v 16.16 step + mov ebx,eax ; copy it + sar eax,16 ; get v int step + shl ebx,16 ; get v frac step + mov Tmap1.DeltaVFrac,ebx ; store it + imul eax,Tmap1.src_offset ; calculate texture step for v int step + + mov ebx,Tmap1.DeltaU ; get u 16.16 step + mov ecx,ebx ; copy it + sar ebx,16 ; get u int step + shl ecx,16 ; get u frac step + mov Tmap1.DeltaUFrac,ecx ; store it + add eax,ebx ; calculate uint + vint step + mov Tmap1.UVintVfracStepVNoCarry,eax; save whole step in non-v-carry slot + add eax,Tmap1.src_offset ; calculate whole step + v carry + mov Tmap1.UVintVfracStepVCarry,eax ; save in v-carry slot + +; setup initial coordinates + mov esi,Tmap1.UFixed ; get u 16.16 fixedpoint coordinate + + mov ebx,esi ; copy it + sar esi,16 ; get integer part + shl ebx,16 ; get fractional part + + mov ecx,Tmap1.VFixed ; get v 16.16 fixedpoint coordinate + + mov edx,ecx ; copy it + sar edx,16 ; get integer part + shl ecx,16 ; get fractional part + imul edx,Tmap1.src_offset ; calc texture scanline address + add esi,edx ; calc texture offset + add esi,Tmap1.pixptr ; calc address + + mov edx,Tmap1.DeltaUFrac ; get register copy + + mov eax, Tmap1.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap1.fx_dl_dx + shl ebp, 5 //*32 + add Tmap1.fx_l, ebp + + mov ebp, Tmap1.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + + ; calculate right side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 ; 1 V/ZR 1/ZR U/ZR UL VL + fdiv st,st(2) ; ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + + mov al,[esi] // get texture pixel 0 + mov ah, bh + mov al, gr_fade_table[eax] + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry +// mov al, 0 // Uncomment this line to show divisions + mov [edi+0],al // store pixel 0 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+1],al // store pixel 1 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 2 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+2],al // store pixel 2 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 3 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+3],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+4],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+5],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+6],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+7],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+8],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+9],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+10],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+11],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+12],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+13],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+14],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+15],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+16],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+17],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+18],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+19],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+20],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+21],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+22],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+23],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+24],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+25],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + + + sbb ebp,ebp // get -1 if carry + mov [edi+26],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+27],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+28],al // store pixel 4 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 5 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+29],al // store pixel 5 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 6 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+30],al // store pixel 6 + + add ebx,edx // increment u fraction + + mov al,[esi] // get texture pixel 7 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + + mov [edi+31],al // store pixel 7 + + + + ; ************** Okay to Access Stack Frame **************** + ; ************** Okay to Access Stack Frame **************** + ; ************** Okay to Access Stack Frame **************** + + + ; the fdiv is done, finish right ; st0 st1 st2 st3 st4 st5 st6 st7 + ; ZR V/ZR 1/ZR U/ZR UL VL + + fld st ; ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) ; VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) ; ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) ; UR VR V/ZR 1/ZR U/ZR UL VL + + add edi,32 ; increment to next span + dec Tmap1.Subdivisions ; decrement span count + jnz SpanLoop ; loop back + + // save new lighting values +// xor eax, eax +// mov ax, bx +// mov Tmap1.fx_l, eax + +// xor eax, eax +// mov ax, dx +// mov Tmap1.fx_dl_dx, eax + +HandleLeftoverPixels: +// jmp FPUReturn + + mov esi,Tmap1.pixptr ; load texture pointer + + ; edi = dest dib bits + ; esi = current texture dib bits + ; at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + ; inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap1.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + ; convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap1.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap1.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap1.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap1.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap1.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas + + ; calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + ; r -> R+1 + + ; @todo rearrange things so we don't need these two instructions + fstp Tmap1.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap1.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap1.RightVOverZ ; V/Zr inv. inv. inv. UL VL + fsub Tmap1.dVOverZdX ; V/ZR inv. inv. inv. UL VL + fld Tmap1.RightUOverZ ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap1.dUOverZdX ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap1.RightOneOverZ ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap1.dOneOverZdX ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap1.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + ; calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap1.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap1.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap1.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap1.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap1.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap1.DeltaU ; inv. inv. inv. UR VR + + ; @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + +//jmp OldWay + + + ; setup delta values + mov eax, Tmap1.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap1.DeltaVFrac, ebx // store it + imul eax, Tmap1.src_offset // calc texture step for v int step + + mov ebx, Tmap1.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap1.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap1.UVintVfracStepVNoCarry, eax // save whole step in non-v-carry slot + add eax, Tmap1.src_offset // calc whole step + v carry + mov Tmap1.UVintVfracStepVCarry, eax // save in v-carry slot + + + +OnePixelSpan: + +/* +; check coordinate ranges + mov eax, Tmap1.UFixed + cmp eax, Tmap1.MinUFixed + jge UNotTooSmall_2 + mov eax, Tmap1.MinUFixed + mov Tmap1.UFixed, eax + jmp CheckV_2 +UNotTooSmall_2: + cmp eax, Tmap1.MaxUFixed + jle CheckV_2 + mov eax, Tmap1.MaxUFixed + mov Tmap1.UFixed, eax +CheckV_2: + mov eax, Tmap1.VFixed + cmp eax, Tmap1.MinVFixed + jge VNotTooSmall_2 + mov eax, Tmap1.MinVFixed + mov Tmap1.VFixed, eax + jmp DoneCheck_2 +VNotTooSmall_2: + cmp eax, Tmap1.MaxVFixed + jle DoneCheck_2 + mov eax, Tmap1.MaxVFixed + mov Tmap1.VFixed, eax +DoneCheck_2: +*/ + + + + + ; setup initial coordinates + mov esi, Tmap1.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap1.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap1.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap1.pixptr // calc address + + ; set edi = address of first pixel to modify +; mov edi, Tmap1.dest_row_data + + + + + mov eax, Tmap1.fx_l + shr eax, 8 + mov bx, ax + + mov edx, Tmap1.DeltaUFrac + + cmp Tmap1.WidthModLength, 1 + jle NoDeltaLight + + push ebx + + mov ebx, Tmap1.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + +#if 0 + // slow but maybe better + push edx + cdq + mov ebx, Tmap1.WidthModLength + dec ebx + idiv ebx + pop edx +#else + mov eax, Tmap1.fx_dl_dx + shr eax, 8 +#endif + + mov dx, ax + + pop ebx + +NoDeltaLight: + + inc Tmap1.WidthModLength + mov eax,Tmap1.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap1.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +NextPixel: + mov al,[esi] // get texture pixel 0 + mov ah, bh + mov al, gr_fade_table[eax] + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+0],al // store pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+1],al // store pixel 1 + + add edi, 2 + dec Tmap1.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + + mov al,[esi] // get texture pixel 2 + mov ah, bh + mov al, gr_fade_table[eax] + mov [edi],al // store pixel 2 + + + + + + + + + + + + + +/* +OldWay: // This is 6% slower than above + + mov ebx,Tmap1.UFixed ; get starting coordinates + mov ecx,Tmap1.VFixed ; for span + + ; leftover pixels loop + ; edi = dest dib bits + ; esi = texture dib bits + + ; ebx = u 16.16 + ; ecx = v 16.16 + + + mov eax,ecx ; copy v + sar eax,16 ; int(v) + imul eax,Tmap1.src_offset ; scan offset + mov edx,ebx ; copy u + sar edx,16 ; int(u) + add eax,edx ; texture offset + mov al,[esi+eax] ; get source pixel +mov al, 0 + mov [edi],al ; store it + inc edi + add ebx,Tmap1.DeltaU ; increment u coordinate + add ecx,Tmap1.DeltaV ; increment v coordinate + + dec Tmap1.WidthModLength ; decrement loop count + jl FPUReturn ; finish up + + +LeftoverLoop: + mov eax,ecx ; copy v + sar eax,16 ; int(v) + imul eax,Tmap1.src_offset ; scan offset + mov edx,ebx ; copy u + sar edx,16 ; int(u) + add eax,edx ; texture offset + mov al,[esi+eax] ; get source pixel + mov [edi],al ; store it + inc edi + add ebx,Tmap1.DeltaU ; increment u coordinate + add ecx,Tmap1.DeltaV ; increment v coordinate + + dec Tmap1.WidthModLength ; decrement loop count + jge LeftoverLoop ; finish up +*/ + +FPUReturn: + + ; busy FPU registers: ; st0 st1 st2 st3 st4 st5 st6 st7 + ; xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap1.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } + + +} + + +void tmapscan_lln8_old( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + _fx_srcptr = (uint)tmap_bitmap->data; + _fx_destptr = (uint)GR_SCREEN_PTR(ubyte,lx,y); + _loop_count = rx - lx; + _fx_u = fl2f(p->u*64.0f); + _fx_v = fl2f(p->v*64.0f); + _fx_l = fl2f(p->l*32.0+1.0); + _fx_du = fl2f(dp->u*64.0f); + _fx_dv = fl2f(dp->v*64.0f); + _fx_dl = fl2f(dp->l*32.0); + light_table = (uint)&gr_fade_table[0]; + + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; set edi = address of first pixel to modify + mov edi, _fx_destptr + + + mov eax, _fx_v + shr eax, 6 + mov edx, _fx_u + shl edx, 10 + mov dx, ax ; EDX=U:V in 6.10 format + + mov eax, _fx_dv + shr eax, 6 + mov esi, _fx_du + shl esi, 10 + mov si, ax ; ESI=DU:DV in 6.10 format + + mov ebx, _fx_l + sar ebx, 8 + mov ebp, _fx_dl + sar ebp, 8 + + mov ecx, _fx_srcptr + + mov eax, _loop_count + inc eax + mov _loop_count, eax + + shr eax, 3 + je DoLeftOverPixels + + mov num_big_steps, eax + and _loop_count, 7 + +NextPixelBlock: + ; pixel 0 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+0], al + + ; pixel 1 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+1], al + + ; pixel 2 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+2], al + + ; pixel 3 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+3], al + + ; pixel 4 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+4], al + + ; pixel 5 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+5], al + + ; pixel 6 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+6], al + + ; pixel 7 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+7], al + + add edi, 8 + dec num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + mov eax,_loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov _loop_count, eax + pushf + + +NextPixel: + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+0], al + + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+1], al + + + add edi, 2 + dec _loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + mov al, [ecx+eax] + mov ah, bh + mov al, gr_fade_table[eax] + mov [edi], al + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } + + +} + + +void tmapscan_flat16( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + int i; + ushort *pDestBits; + + pDestBits = GR_SCREEN_PTR(ushort,lx,y); + + for (i=0; i<(rx-lx+1); i++ ) + *pDestBits++ = gr_screen.current_color.raw16; +} + +float tmap_max_z = 0.0f; + +void tmapscan_lln8_z( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + int count; + ubyte *pDestBits, tmp; + float u, dudx, v, dvdx, l, dldx; + float z, dzdx; + + pDestBits = GR_SCREEN_PTR(ubyte,lx,y); + + ubyte * cdata = (ubyte *)tmap_bitmap->data; + + u = p->u; + v = p->v; + l = p->l*32.0f; + z = p->nz; + dudx = dp->u; + dvdx = dp->v; + dldx = dp->l*32.0f; + dzdx = dp->nz; + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + if ( z < tmap_max_z ) { + tmp = cdata[fl2i(v)*tmap_bitmap->w+fl2i(u)]; + *pDestBits = gr_fade_table[ fl2i(l)*256+tmp ]; + } + pDestBits++; + u += dudx; + v += dvdx; + l += dldx; + z += dzdx; + } +} + + +void tmapscan_generic8( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + int count; + ubyte *pDestBits, tmp; + int u, dudx, v, dvdx, w, dwdx, l, dldx; + + pDestBits = GR_SCREEN_PTR(ubyte,lx,y); + + if ( Tmap1.flags & TMAP_FLAG_TEXTURED ) { + ubyte * cdata = (ubyte *)tmap_bitmap->data; + if ( flags & TMAP_FLAG_RAMP ) { + if ( Tmap1.flags & TMAP_FLAG_CORRECT ) { + float fu, fv, fw, fdu, fdv, fdw; + + tmapscan_pln8( lx, rx, y, p, dp, rp,Tmap1.flags ); + return; + + + fu = p->u; + fv = p->v; + fw = p->sw; + l = fl2f(p->l*32.0f); + + fdu = dp->u; + fdv = dp->v; + fdw = dp->sw; + dldx = fl2f(dp->l*32.0f); + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + u = fl2i(fu/fw); + v = fl2i(fv/fw); + tmp = cdata[v*tmap_bitmap->w+u]; + *pDestBits++ = tmp; //gr_fade_table[ (l>>16)*256+tmp ]; + //tmp = *pDestBits; + //*pDestBits++ = tmp+1; + fu += fdu; + fv += fdv; + fw += fdw; + l += dldx; + } + + } else { +#if 1 + tmapscan_lln8( lx, rx, y, p, dp, rp, flags ); +#else + u = fl2f(p->u*64.0f); + v = fl2f(p->v*64.0f); + l = fl2f(p->l*32.0f); + dudx = fl2f(dp->u*64.0f); + dvdx = fl2f(dp->v*64.0f); + dldx = fl2f(dp->l*32.0f); + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + + //tmp = cdata[((v>>16)&63)*64+((u>>16)&63)]; + //*pDestBits++ = ;//gr_fade_table[ (l>>16)*256+tmp ]; + (*pDestBits)++; + pDestBits++; + u += dudx; + v += dvdx; + l += dldx; + } +#endif + } + } else { + if ( flags & TMAP_FLAG_CORRECT ) { + u = fl2f(p->u*64.0f); + v = fl2f(p->v*64.0f); + w = fl2f(p->sw*16.0f); + + dudx = fl2f(dp->u*64.0f); + dvdx = fl2f(dp->v*64.0f); + dwdx = fl2f(dp->sw*16.0f); + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + tmp = cdata[((v/w)&63)*64+((u/w)&63)]; + *pDestBits++ = tmp; + u += dudx; + v += dvdx; + w += dwdx; + } + } else { + u = fl2f(p->u*64.0f); + v = fl2f(p->v*64.0f); + dudx = fl2f(dp->u*64.0f); + dvdx = fl2f(dp->v*64.0f); + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + tmp = cdata[((v>>16)&63)*64+((u>>16)&63)]; + *pDestBits++ = tmp; + u += dudx; + v += dvdx; + } + } + } + } else { + if ( Tmap1.flags & TMAP_FLAG_RAMP ) { + l = fl2f(p->l*32.0f); + dldx = fl2f(dp->l*32.0f); + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + *pDestBits++ = gr_fade_table[ (l>>16)*256+gr_screen.current_color.raw8 ]; + l += dldx; + } + } else { + memset( pDestBits, gr_screen.current_color.raw8, (rx-lx+1) ); + } + } +} + +uint testpixel; +uint fsave_area[64]; + +unsigned __int64 packrgb( int r, int g, int b ) +{ + unsigned __int64 tmp; + unsigned int *tmps; + + tmp = 0; + + tmps = (unsigned int *)&r; + tmp |= *tmps & 0xFFFF; + tmp <<= 16; + + tmps = (unsigned int *)&g; + tmp |= *tmps & 0xFFFF; + tmp <<= 16; + + tmps = (unsigned int *)&b; + tmp |= *tmps & 0xFFFF; + + return tmp; +} + + + +void tmapscan_generic( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + int count; + uint *pDestBits, tmp, tmp1; + int u, dudx, v, dvdx, w, dwdx; + int r, g, b, dr, dg, db; + + if ( !rgbtable_inited ) + rgbtable_init(); + + pDestBits = GR_SCREEN_PTR(uint,lx,y); + + if ( Tmap1.flags & TMAP_FLAG_TEXTURED ) { + uint * cdata = (uint *)tmap_bitmap->data; + + if ( Tmap1.flags & TMAP_FLAG_GOURAUD ) { + if ( Tmap1.flags & TMAP_FLAG_CORRECT ) { + u = fl2f(p->u*64.0f); + v = fl2f(p->v*64.0f); + w = fl2f(p->sw); + + r = fl2f(p->r*255.0f); + g = fl2f(p->g*255.0f); + b = fl2f(p->b*255.0f); + + dr = fl2f(dp->r*255.0f); + dg = fl2f(dp->g*255.0f); + db = fl2f(dp->b*255.0f); + + dudx = fl2f(dp->u*64.0f); + dvdx = fl2f(dp->v*64.0f); + dwdx = fl2f(dp->sw); + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + tmp = cdata[((v/w)&63)*64+((u/w)&63)]; + tmp1 = rgbtable1[ (tmp & 0xFF)+ (b>>16) ]; + tmp1 |= rgbtable2[ ((tmp>>8) & 0xFF)+ (g>>16) ]; + tmp1 |= rgbtable3[ ((tmp>>16) & 0xFF)+ (r>>16) ]; + *pDestBits++ = tmp1; + u += dudx; + v += dvdx; + w += dwdx; + r += dr; + g += dg; + b += db; + } + } else { + // MMX!!! + __int64 light, deltalight; + + u = fl2f(p->u*64.0f); + v = fl2f(p->v*64.0f); + dudx = fl2f(dp->u*64.0f); + dvdx = fl2f(dp->v*64.0f); + +#if 0 + r = fl2f(p->r*255.0f)>>8; + g = fl2f(p->g*255.0f)>>8; + b = fl2f(p->b*255.0f)>>8; + + dr = fl2f(dp->r*255.0f)>>8; + dg = fl2f(dp->g*255.0f)>>8; + db = fl2f(dp->b*255.0f)>>8; +#else + r = fl2f(p->r)>>7; + g = fl2f(p->g)>>7; + b = fl2f(p->b)>>7; + + dr = fl2f(dp->r)>>7; + dg = fl2f(dp->g)>>7; + db = fl2f(dp->b)>>7; + + //r = 256*2; + //g = 256*2; + //b = 256*2; + //dr = dg = db = 0; +#endif + + light = packrgb( r, g, b ); + deltalight = packrgb( dr, dg, db ); + + _asm fstenv fsave_area + _asm movq mm3, light + _asm movq mm4, deltalight + _asm pxor mm2, mm2 ; mm0 = 0 + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + testpixel = cdata[((v>>16)&63)*64+((u>>16)&63)]; + + _asm punpcklbw mm2, testpixel ; mm0 = 8.8,8.8, 8.8 rgb + _asm pmulhw mm2, mm3 ; + _asm paddsw mm3, mm4 ; light += deltalight + _asm packuswb mm2, mm2 ;mm2 is who cares + _asm movd testpixel, mm2 ; load tmp + _asm pxor mm2, mm2 ; mm0 = 0 + + *pDestBits++ = testpixel; + u += dudx; + v += dvdx; + } + _asm emms + _asm frstor fsave_area + } + } else { + if ( Tmap1.flags & TMAP_FLAG_CORRECT ) { + u = fl2f(p->u*64.0f); + v = fl2f(p->v*64.0f); + w = fl2f(p->sw); + dudx = fl2f(dp->u*64.0f); + dvdx = fl2f(dp->v*64.0f); + dwdx = fl2f(dp->sw); + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + *pDestBits++ = cdata[((v/w)&63)*64+((u/w)&63)]; + u += dudx; + v += dvdx; + w += dwdx; + } + } else { + u = fl2f(p->u*64.0f); + v = fl2f(p->v*64.0f); + dudx = fl2f(dp->u*64.0f); + dvdx = fl2f(dp->v*64.0f); + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + *pDestBits++ = cdata[((v>>16)&63)*64+((u>>16)&63)]; + u += dudx; + v += dvdx; + } + } + } + } else if ( Tmap1.flags & TMAP_FLAG_GOURAUD ) { + + r = fl2f(p->r*255.0f); + g = fl2f(p->g*255.0f); + b = fl2f(p->b*255.0f); + + dr = fl2f(dp->r*255.0f); + dg = fl2f(dp->g*255.0f); + db = fl2f(dp->b*255.0f); + + for ( count = rx - lx + 1 ; count > 0; count-- ) { + *pDestBits++ = (r&0xFF0000)|((g>>8)&0xFF00)|(b>>16); + r += dr; + g += dg; + b += db; + //*pDestBits++ = 100; + } + } else { + memset( pDestBits, gr_screen.current_color.raw32, (rx-lx+1)*4 ); + } +} + +void tmapscan_flat( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + int w; + uint *pDestBits; + + pDestBits = GR_SCREEN_PTR(uint,lx,y); + w = (rx-lx+1); +#ifdef USE_INLINE_ASM + _asm mov eax, gr_screen.current_color.raw32 + _asm mov ecx, w + _asm mov edi, pDestBits + _asm cld + _asm rep stosd +#else + for (i=0; iz; + dz = dp->z; +//#ifdef USE_INLINE_ASM +#if 0 + _asm mov eax, gr_screen.current_color.raw32 + _asm mov ecx, w + _asm mov edi, pDestBits + _asm cld + _asm rep stosd +#else + { int i; + for (i=0; idata; + _fx_destptr = (uint)GR_SCREEN_PTR(uint,lx,y); + _loop_count = rx - lx; + _fx_u = fl2f(p->u*64.0f); + _fx_v = fl2f(p->v*64.0f); + _fx_w = fl2f(p->sw*16.0); + _fx_du = fl2f(dp->u*64.0f); + _fx_dv = fl2f(dp->v*64.0f); + _fx_dw = fl2f(dp->sw*16.0); + + _fx_u_right = fl2f(rp->u*64.0f); + _fx_v_right = fl2f(rp->v*64.0f); + _fx_w_right = fl2f(rp->sw*16.0); + + r = fl2f(p->r)>>7; + g = fl2f(p->g)>>7; + b = fl2f(p->b)>>7; + + dr = fl2f(dp->r)>>7; + dg = fl2f(dp->g)>>7; + db = fl2f(dp->b)>>7; + + light = ((__int64)r<<32)|((__int64)g<<16)|(__int64)b; + deltalight = ((__int64)dr<<32)|((__int64)dg<<16)|(__int64)db; + + _asm fstenv fsave_area1 + _asm movq mm3, light + _asm movq mm4, deltalight + + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + mov ebx,_fx_u + mov ebp,_fx_v + mov ecx,_fx_w + mov edi,_fx_destptr + +; compute initial v coordinate + mov eax,ebp ; get v + mov edx,eax + sar edx,16 + shl eax,16 + idiv ecx ; eax = (v/z) + mov V0, eax + +; compute initial u coordinate + mov eax,ebx ; get u + mov edx,eax + sar edx,16 + shl eax,16 + idiv ecx ; eax = (v/z) + mov U0, eax + + mov ecx, _fx_w + +; find number of subdivisions + mov eax, _loop_count + inc eax + mov esi, eax + and esi, 15 + sar eax, NBITS + mov num_left_over, esi + jz DoEndPixels ;there are no 2^NBITS chunks, do divide/pixel for whole scanline + mov _loop_count, eax + +; Set deltas to NPIXS pixel increments + mov eax, _fx_du + shl eax, NBITS + mov DU1, eax + mov eax, _fx_dv + shl eax, NBITS + mov DV1, eax + mov eax, _fx_dw + shl eax, NBITS + mov DZ1, eax + + align 4 +TopOfLoop4: + add ebx, DU1 + add ebp, DV1 + add ecx, DZ1 + +; Done with ebx, ebp, ecx until next iteration + push ebx + push ecx + push ebp + push edi + + +; Find fixed U1 + mov eax, ebx + mov edx,eax + sar edx,16 + shl eax,16 + idiv ecx ; eax = (v/z) + mov ebx, eax ; ebx = U1 until pop's + +; Find fixed V1 + mov eax, ebp + mov edx,eax + sar edx,16 + shl eax,16 + idiv ecx ; eax = (v/z) + mov ebp, eax ; ebx = V1 until pop's + +; Get last correct U,Vs + mov ecx, U0 ; ecx = U0 until pop's + mov edi, V0 ; edi = V0 until pop's + +; Make ESI = V0:U0 in 6:10,6:10 format + mov eax, edi + shr eax, 6 + mov esi, ecx + shl esi, 10 + mov si, ax + +; Make EDX = DV:DU in 6:10,6:10 format + mov eax, ebp + sub eax, edi + sar eax, NBITS+6 + mov edx, ebx + sub edx, ecx + shl edx, 10-NBITS ; EDX = V1-V0/ 4 in 6:10 int:frac + mov dx, ax ; put delta u in low word + +; Save the U1 and V1 so we don't have to divide on the next iteration + mov U0, ebx + mov V0, ebp + + pop edi ; Restore EDI before using it + + mov ecx, _fx_srcptr + mov ebx, 1 << NBITS + +PixelRun: + mov eax, esi + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add esi, edx + movd mm1, [eax*4+ecx] + pxor mm2, mm2 ; mm2 = 0 + punpcklbw mm2, mm1 ; mm0 = 8.8,8.8, 8.8 rgb + pmulhw mm2, mm3 + paddsw mm3, mm4 ; light += deltalight + packuswb mm2, mm2 ;mm2 is who cares + movd [edi], mm2 ; load tmp + add edi, 4 + dec ebx + jnz PixelRun + + pop ebp + pop ecx + pop ebx + dec _loop_count + jnz TopOfLoop4 + +;EndOfLoop4: + + test num_left_over, -1 + je _none_to_do + + cmp num_left_over, 4 + ja DoEndPixels + + ; If less than 4, then just keep interpolating without + ; calculating a new DU:DV. + mov ecx, _fx_srcptr + jmp FinishOff + +; ----------------------------------------- Start of LeftOver Pixels ------------------------------------------ +DoEndPixels: + + push edi + mov ecx, _fx_w_right + +; Find fixed U1 + mov eax, _fx_u_right + mov edx,eax + sar edx,16 + shl eax,16 + idiv ecx ; eax = (v/z) + mov ebx, eax ; ebx = U1 until pop's + +; Find fixed V1 + mov eax, _fx_v_right + mov edx,eax + sar edx,16 + shl eax,16 + idiv ecx ; eax = (v/z) + mov ebp, eax ; ebp = V1 until pop's + + mov ecx, U0 ; ecx = U0 until pop's + mov edi, V0 ; edi = V0 until pop's + +; Make EDX = DV:DU in 6:10,6:10 format + mov eax, ebx + sub eax, ecx + mov edx, eax ; These two lines are faster than cdq + sar edx, 31 + idiv num_left_over ; eax = (v1-v0)/num_left_over + shl eax, 16-6 ; go from 16.16 to 6.10, and move into high 16 bits + mov esi, eax ; esi = dvdx<<16 + + mov eax, ebp + sub eax, edi + mov edx, eax ; These two lines are faster than cdq + sar edx, 31 + idiv num_left_over ; eax = (u1-u0)/num_left_over + sar eax, 6 ; go from 16.16 to 6.10 (ax=dvdx in 6.10) + mov si, ax ; esi = dvdx:dudx + mov edx, esi + +; Make ESI = V0:U0 in 6:10,6:10 format + mov eax, edi + shr eax, 6 + mov esi, ecx + shl esi, 10 + mov si, ax + + pop edi ; Restore EDI before using it + +; LIGHTING CODE + mov ecx, _fx_srcptr + +FinishOff: + mov eax, esi + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add esi, edx +; mov eax, [eax*4+ecx] +; mov [edi],eax + movd mm1, [eax*4+ecx] + pxor mm2, mm2 ; mm2 = 0 + punpcklbw mm2, mm1 ; mm0 = 8.8,8.8, 8.8 rgb + pmulhw mm2, mm3 + paddsw mm3, mm4 ; light += deltalight + packuswb mm2, mm2 ;mm2 is who cares + movd [edi], mm2 ; load tmp + add edi, 4 + + dec num_left_over + jnz FinishOff + + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } // end asm + + _asm emms + _asm frstor fsave_area1 +} + + +void tmapscan_lln( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + __int64 light, deltalight; + int r, g, b, dr, dg, db; + _fx_srcptr = (uint)tmap_bitmap->data; + _fx_destptr = (uint)GR_SCREEN_PTR(uint,lx,y); + _loop_count = rx - lx; + _fx_u = fl2f(p->u*64.0f); + _fx_v = fl2f(p->v*64.0f); + _fx_du = fl2f(dp->u*64.0f); + _fx_dv = fl2f(dp->v*64.0f); + + r = fl2f(p->r)>>7; + g = fl2f(p->g)>>7; + b = fl2f(p->b)>>7; + + dr = fl2f(dp->r)>>7; + dg = fl2f(dp->g)>>7; + db = fl2f(dp->b)>>7; + + light = ((__int64)r<<32)|((__int64)g<<16)|(__int64)b; + deltalight = ((__int64)dr<<32)|((__int64)dg<<16)|(__int64)db; + + _asm fstenv fsave_area1 + _asm movq mm3, light + _asm movq mm4, deltalight + + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + mov ebx,_fx_u + mov ebp,_fx_v + mov edi,_fx_destptr + +; find number of subdivisions + mov eax, _loop_count + inc eax + jz none_to_do + mov _loop_count, eax + +; Make ESI = V0:U0 in 6:10,6:10 format + mov eax, edi + shr eax, 6 + mov esi, ecx + shl esi, 10 + mov si, ax + +; Make EDX = DV:DU in 6:10,6:10 format + mov eax, ebp + sub eax, edi + sar eax, NBITS+6 + mov edx, ebx + sub edx, ecx + shl edx, 10-NBITS ; EDX = V1-V0/ 4 in 6:10 int:frac + mov dx, ax ; put delta u in low word + + mov ecx, _fx_srcptr + mov ebx, _loop_count + +PixelRun: + mov eax, esi + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add esi, edx + movd mm1, [eax*4+ecx] + pxor mm2, mm2 ; mm2 = 0 + punpcklbw mm2, mm1 ; mm0 = 8.8,8.8, 8.8 rgb + pmulhw mm2, mm3 + paddsw mm3, mm4 ; light += deltalight + packuswb mm2, mm2 ;mm2 is who cares + movd [edi], mm2 ; load tmp + add edi, 4 + dec ebx + jnz PixelRun + +none_to_do: + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } // end asm + + _asm emms + _asm frstor fsave_area1 +} + + + + +void tmapscan_pln8_tiled( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + Tmap1.dest_row_data = GR_SCREEN_PTR(ubyte,lx,y); + Tmap1.loop_count = rx - lx; + Tmap1.fx_u = fl2f(p->u); + Tmap1.fx_v = fl2f(p->v); + Tmap1.fx_du_dx = fl2f(dp->u); + Tmap1.fx_dv_dx = fl2f(dp->v); + + Tmap1.fx_l = fl2f(p->l*32.0); + Tmap1.fx_dl_dx = fl2f(dp->l*32.0); + + Tmap1.fx_u_right = fl2f(rp->u); + Tmap1.fx_v_right = fl2f(rp->v); + Tmap1.pixptr = (unsigned char *)tmap_bitmap->data; + Tmap1.bp = tmap_bitmap; + Tmap1.src_offset = tmap_bitmap->w; + + + Tmap1.FixedScale = 65536.0f; + Tmap1.FixedScale8 = 2048.0f; //8192.0f; // 2^16 / 8 + Tmap1.One = 1.0f; + + + Tmap1.UOverZ = p->u; + Tmap1.VOverZ = p->v; + Tmap1.OneOverZ = p->sw; + + Tmap1.dUOverZdX8 = dp->u*32.0f; + Tmap1.dVOverZdX8 = dp->v*32.0f; + Tmap1.dOneOverZdX8 = dp->sw*32.0f; + + Tmap1.dUOverZdX = dp->u; + Tmap1.dVOverZdX = dp->v; + Tmap1.dOneOverZdX = dp->sw; + + Tmap1.RightUOverZ = rp->u; + Tmap1.RightVOverZ = rp->v; + Tmap1.RightOneOverZ = rp->sw; + + Tmap1.BitmapWidth = Tmap1.bp->w; + Tmap1.BitmapHeight = Tmap1.bp->h; + + if (Tmap1.BitmapWidth!=64) return; + if (Tmap1.BitmapHeight!=64) return; + + + + if ( Tmap1.fx_dl_dx < 0 ) { + Tmap1.fx_dl_dx = -Tmap1.fx_dl_dx; + Tmap1.fx_l = (67*F1_0)-Tmap1.fx_l; + Tmap1.fx_l_right = (67*F1_0)-Tmap1.fx_l_right; + } + + + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + + // put the FPU in 32 bit mode + // @todo move this out of here! + + fstcw Tmap1.OldFPUCW // store copy of CW + mov ax,Tmap1.OldFPUCW // get it in ax +//hh and eax,NOT 1100000000y // 24 bit precision + and eax, ~0x300L + mov Tmap1.FPUCW,ax // store it + fldcw Tmap1.FPUCW // load the FPU + + mov ecx, Tmap1.loop_count // ecx = width + inc ecx + mov edi, Tmap1.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? +// jmp Return + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap1.Subdivisions,ecx // store widths + mov Tmap1.WidthModLength,eax + +// mov ebx,pLeft ; get left edge pointer +// mov edx,pGradients ; get gradients pointer + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap1.VOverZ // V/ZL + fld Tmap1.UOverZ // U/ZL V/ZL + fld Tmap1.OneOverZ // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap1.dOneOverZdX8 // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap1.dUOverZdX8 // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap1.dVOverZdX8 // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap1.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap1.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap1.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap1.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap1.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap1.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap1.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap1.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span ; st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms---->; V/ZL 1/ZL U/ZL UL VL + + fadd Tmap1.dVOverZdX8 ; V/ZR 1/ZL U/ZL UL VL + fxch st(1) ; 1/ZL V/ZR U/ZL UL VL + fadd Tmap1.dOneOverZdX8 ; 1/ZR V/ZR U/ZL UL VL + fxch st(2) ; U/ZL V/ZR 1/ZR UL VL + fadd Tmap1.dUOverZdX8 ; U/ZR V/ZR 1/ZR UL VL + fxch st(2) ; 1/ZR V/ZR U/ZR UL VL + fxch st(1) ; V/ZR 1/ZR U/ZR UL VL + + ; calculate right side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 ; 1 V/ZR 1/ZR U/ZR UL VL + fdiv st,st(2) ; ZR V/ZR 1/ZR U/ZR UL VL + + + ; ************** Can't Access Stack Frame ****************** + ; ************** Can't Access Stack Frame ****************** + ; ************** Can't Access Stack Frame ****************** + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + //need to have: + // eax = ? + // ebx = l in 24.8 + // ecx = source pixels + // edx = u v in 6.10 6.10 + // esi = du dv in 6.10 6.10 + // edi = dest pixels + // ebp = dldx in 24.8 + + + mov eax, Tmap1.fx_l + shr eax, 8 + mov ebx, eax + + mov ebp, Tmap1.fx_dl_dx + shl ebp, 5 //*32 + add Tmap1.fx_l, ebp + + mov ebp, Tmap1.fx_l + shr ebp, 8 + sub ebp, eax + shr ebp, 5 + + mov ecx, Tmap1.pixptr // ecx = source pixels + +; Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap1.DeltaU + shr eax, 6 + mov esi, Tmap1.DeltaV + shl esi, 10 + mov si, ax + +; Make EDX = DV:DU in 6:10,6:10 format + + mov eax, Tmap1.UFixed + shr eax, 6 + mov edx, Tmap1.VFixed + shl edx, 10 + mov dx, ax + + ; Draw 32 pixels + + ; pixel 0 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+0], al + + ; pixel 1 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+1], al + + ; pixel 2 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+2], al + + ; pixel 3 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+3], al + + ; pixel 4 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+4], al + + ; pixel 5 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+5], al + + ; pixel 6 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+6], al + + ; pixel 7 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+7], al + + ; pixel 8 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+8], al + + ; pixel 9 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+9], al + + ; pixel 10 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+10], al + + ; pixel 11 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+11], al + + ; pixel 12 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+12], al + + ; pixel 13 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+13], al + + ; pixel 14 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+14], al + + ; pixel 15 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+15], al + + ; pixel 16 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+16], al + + ; pixel 17 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+17], al + + ; pixel 18 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+18], al + + ; pixel 19 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+19], al + + ; pixel 20 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+20], al + + ; pixel 21 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+21], al + + ; pixel 22 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+22], al + + ; pixel 23 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+23], al + + ; pixel 24 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+24], al + + ; pixel 25 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+25], al + + ; pixel 26 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+26], al + + ; pixel 27 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+27], al + + ; pixel 28 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+28], al + + ; pixel 29 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+29], al + + ; pixel 30 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+30], al + + ; pixel 31 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+31], al + + + ; ************** Okay to Access Stack Frame **************** + ; ************** Okay to Access Stack Frame **************** + ; ************** Okay to Access Stack Frame **************** + + + ; the fdiv is done, finish right ; st0 st1 st2 st3 st4 st5 st6 st7 + ; ZR V/ZR 1/ZR U/ZR UL VL + + fld st ; ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) ; VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) ; ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) ; UR VR V/ZR 1/ZR U/ZR UL VL + + add edi,32 ; increment to next span + dec Tmap1.Subdivisions ; decrement span count + jnz SpanLoop ; loop back + +HandleLeftoverPixels: + + mov esi,Tmap1.pixptr ; load texture pointer + + ; edi = dest dib bits + ; esi = current texture dib bits + ; at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + ; inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap1.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + ; convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap1.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap1.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap1.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap1.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap1.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas + + ; calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + ; r -> R+1 + + ; @todo rearrange things so we don't need these two instructions + fstp Tmap1.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap1.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap1.RightVOverZ ; V/Zr inv. inv. inv. UL VL + fsub Tmap1.dVOverZdX ; V/ZR inv. inv. inv. UL VL + fld Tmap1.RightUOverZ ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap1.dUOverZdX ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap1.RightOneOverZ ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap1.dOneOverZdX ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap1.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + ; calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap1.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap1.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap1.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap1.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap1.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap1.DeltaU ; inv. inv. inv. UR VR + + ; @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + +OnePixelSpan: + mov eax, Tmap1.fx_l + shr eax, 8 + mov ebx, eax + + mov ebp, Tmap1.fx_dl_dx + shl ebp, 5 //*32 + add Tmap1.fx_l, ebp + + mov ebp, Tmap1.fx_l + shr ebp, 8 + sub ebp, eax + shr ebp, 5 + + +; Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap1.DeltaU + shr eax, 6 + mov esi, Tmap1.DeltaV + shl esi, 10 + mov si, ax + +; Make EDX = DV:DU in 6:10,6:10 format + + mov eax, Tmap1.UFixed + shr eax, 6 + mov edx, Tmap1.VFixed + shl edx, 10 + mov dx, ax + + mov ecx, Tmap1.pixptr // ecx = source pixels + + inc Tmap1.WidthModLength + mov eax,Tmap1.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap1.WidthModLength, eax + + +NextPixel: + + ; Draw two pixels + + ; pixel 0 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+0], al + + ; pixel 1 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+1], al + + + add edi, 2 + dec Tmap1.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + + ; Draw one pixel + ; pixel 0 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+0], al + +FPUReturn: + + ; busy FPU registers: ; st0 st1 st2 st3 st4 st5 st6 st7 + ; xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + +//Return: + + fldcw Tmap1.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } + + +} + +void c_tmap_scanline_flat() +{ + switch( gr_screen.bits_per_pixel ) { + case 8: + #if 1 + memset( Tmap1.dest_row_data, gr_screen.current_color.raw8, Tmap1.loop_count ); + #else + ubyte *dest; + int x; + + dest = Tmap1.dest_row_data; + + for (x=Tmap1.loop_count; x >= 0; x-- ) { + //(*dest)++;dest++; + *dest++ = Tmap1.tmap_flat_color; + } + #endif + break; + case 15: + case 16: + _asm mov ecx, Tmap1.loop_count + _asm mov ax, gr_screen.current_color.raw16; + _asm mov edi, Tmap1.dest_row_data16 + _asm cld + _asm rep stosw + break; + case 24: + _asm mov ecx, Tmap1.loop_count + _asm mov ax, gr_screen.current_color.raw16; + _asm mov edi, Tmap1.dest_row_data16 + _asm cld + _asm rep stosw + break; + case 32: + _asm mov ecx, Tmap1.loop_count + _asm mov eax, gr_screen.current_color.raw32; + _asm mov edi, Tmap1.dest_row_data32 + _asm cld + _asm rep stosd + break; + } + +} + +void c_tmap_scanline_shaded() +{ + int fade; + ubyte *dest; + int x; + + dest = Tmap1.dest_row_data; + + fade = Tmap1.tmap_flat_shade_value<<8; + for (x=Tmap1.loop_count; x >= 0; x-- ) { + *dest++ = gr_fade_table[ fade |(*dest)]; + } +} + +void c_tmap_scanline_lin_nolight() +{ + ubyte *dest; + uint c; + int x; + fix u,v,dudx, dvdx; + + u = Tmap1.fx_u; + v = Tmap1.fx_v*64; + dudx = Tmap1.fx_du_dx; + dvdx = Tmap1.fx_dv_dx*64; + + dest = Tmap1.dest_row_data; + + if (!Tmap1.Transparency_on) { + for (x=Tmap1.loop_count; x >= 0; x-- ) { + *dest++ = (uint)Tmap1.pixptr[ (f2i(v)&(64*63)) + (f2i(u)&63) ]; + u += dudx; + v += dvdx; + } + } else { + for (x=Tmap1.loop_count; x >= 0; x-- ) { + c = (uint)Tmap1.pixptr[ (f2i(v)&(64*63)) + (f2i(u)&63) ]; + if ( c!=255) + *dest = c; + dest++; + u += dudx; + v += dvdx; + } + } +} + + +void c_tmap_scanline_lin() +{ + +} + + + +void c_tmap_scanline_per_nolight() +{ + ubyte *dest; + uint c; + int x; + fix u,v,z,dudx, dvdx, dzdx; + + u = Tmap1.fx_u; + v = Tmap1.fx_v*64; + z = Tmap1.fx_z; + dudx = Tmap1.fx_du_dx; + dvdx = Tmap1.fx_dv_dx*64; + dzdx = Tmap1.fx_dz_dx; + + dest = Tmap1.dest_row_data; + + if (!Tmap1.Transparency_on) { + for (x=Tmap1.loop_count; x >= 0; x-- ) { + *dest++ = (uint)Tmap1.pixptr[ ( (v/z)&(64*63) ) + ((u/z)&63) ]; + u += dudx; + v += dvdx; + z += dzdx; + } + } else { + for (x=Tmap1.loop_count; x >= 0; x-- ) { + c = (uint)Tmap1.pixptr[ ( (v/z)&(64*63) ) + ((u/z)&63) ]; + if ( c!=255) + *dest = c; + dest++; + u += dudx; + v += dvdx; + z += dzdx; + } + } +} + +void c_tmap_scanline_per1() +{ + ubyte *dest; + uint c; + int x; + fix u,v,z,l,dudx, dvdx, dzdx, dldx; + + u = Tmap1.fx_u; + v = Tmap1.fx_v*64; + z = Tmap1.fx_z; + dudx = Tmap1.fx_du_dx; + dvdx = Tmap1.fx_dv_dx*64; + dzdx = Tmap1.fx_dz_dx; + + l = Tmap1.fx_l; + dldx = Tmap1.fx_dl_dx; + dest = Tmap1.dest_row_data; + + if (!Tmap1.Transparency_on) { + for (x=Tmap1.loop_count; x >= 0; x-- ) { + *dest++ = gr_fade_table[ (l&(0xff00)) + (uint)Tmap1.pixptr[ ( (v/z)&(64*63) ) + ((u/z)&63) ] ]; + l += dldx; + u += dudx; + v += dvdx; + z += dzdx; + } + } else { + for (x=Tmap1.loop_count; x >= 0; x-- ) { + c = (uint)Tmap1.pixptr[ ( (v/z)&(64*63) ) + ((u/z)&63) ]; + if ( c!=255) + *dest = gr_fade_table[ (l&(0xff00)) + c ]; + dest++; + l += dldx; + u += dudx; + v += dvdx; + z += dzdx; + } + } +} + +#define zonk 1 + +void c_tmap_scanline_editor() +{ + ubyte *dest; + uint c; + int x; + fix u,v,z,dudx, dvdx, dzdx; + + u = Tmap1.fx_u; + v = Tmap1.fx_v*64; + z = Tmap1.fx_z; + dudx = Tmap1.fx_du_dx; + dvdx = Tmap1.fx_dv_dx*64; + dzdx = Tmap1.fx_dz_dx; + + dest = Tmap1.dest_row_data; + + if (!Tmap1.Transparency_on) { + for (x=Tmap1.loop_count; x >= 0; x-- ) { + *dest++ = zonk; + //(uint)pixptr[ ( (v/z)&(64*63) ) + ((u/z)&63) ]; + u += dudx; + v += dvdx; + z += dzdx; + } + } else { + for (x=Tmap1.loop_count; x >= 0; x-- ) { + c = (uint)Tmap1.pixptr[ ( (v/z)&(64*63) ) + ((u/z)&63) ]; + if ( c!=255) + *dest = zonk; + dest++; + u += dudx; + v += dvdx; + z += dzdx; + } + } +} + +void asm_tmap_scanline_lln_tiled() +{ + if ( Tmap1.BitmapWidth != 64 ) return; + if ( Tmap1.BitmapHeight != 64 ) return; + + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; set edi = address of first pixel to modify + mov edi, Tmap1.dest_row_data + + mov eax, Tmap1.fx_v + shr eax, 6 + mov edx, Tmap1.fx_u + shl edx, 10 + mov dx, ax ; EDX=U:V in 6.10 format + + mov eax, Tmap1.fx_dv_dx + shr eax, 6 + mov esi, Tmap1.fx_du_dx + shl esi, 10 + mov si, ax ; ESI=DU:DV in 6.10 format + + mov ebx, Tmap1.fx_l + sar ebx, 8 + mov ebp, Tmap1.fx_dl_dx + sar ebp, 8 + + mov ecx, Tmap1.pixptr + + mov eax, Tmap1.loop_count + inc eax + mov Tmap1.loop_count, eax + + shr eax, 3 + je DoLeftOverPixels + + mov Tmap1.num_big_steps, eax + and Tmap1.loop_count, 7 + +NextPixelBlock: + ; pixel 0 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+0], al + + ; pixel 1 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+1], al + + ; pixel 2 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+2], al + + ; pixel 3 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+3], al + + ; pixel 4 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+4], al + + ; pixel 5 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+5], al + + ; pixel 6 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+6], al + + ; pixel 7 + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+7], al + + add edi, 8 + dec Tmap1.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + mov eax,Tmap1.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap1.loop_count, eax + pushf + + +NextPixel: + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+0], al + + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + add edx, esi + mov al, [ecx+eax] + mov ah, bh + add ebx, ebp + mov al, gr_fade_table[eax] + mov [edi+1], al + + + add edi, 2 + dec Tmap1.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + mov eax, edx + shr ax, 10 + rol eax, 6 + and eax, 0ffffh + mov al, [ecx+eax] + mov ah, bh + mov al, gr_fade_table[eax] + mov [edi], al + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } + +} + +void asm_tmap_scanline_lln32(); + +void asm_tmap_scanline_lln() +{ + int end; + +// return; + if ( Tmap1.tmap_flags & TMAP_FLAG_TILED ) { + asm_tmap_scanline_lln_tiled(); + return; + } + + end = f2i(Tmap1.fx_u); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v); + if ( end >= Tmap1.bp->h ) return; + + end = f2i(Tmap1.fx_u_right); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v_right); + if ( end >= Tmap1.bp->h ) return; + + if ( Tmap1.fx_dl_dx < 0 ) { + Tmap1.fx_dl_dx = -Tmap1.fx_dl_dx; + Tmap1.fx_l = (67*F1_0)-Tmap1.fx_l; + Tmap1.fx_l_right = (67*F1_0)-Tmap1.fx_l_right; +// return; +// Assert( Tmap1.fx_l > 31*F1_0 ); +// Assert( Tmap1.fx_l < 66*F1_0 ); +// Assert( Tmap1.fx_dl_dx >= 0 ); +// Assert( Tmap1.fx_dl_dx < 31*F1_0 ); + } + + + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap1.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap1.DeltaVFrac, ebx // store it + imul eax, Tmap1.src_offset // calc texture step for v int step + + mov ebx, Tmap1.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap1.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap1.UVintVfracStepVNoCarry, eax // save whole step in non-v-carry slot + add eax, Tmap1.src_offset // calc whole step + v carry + mov Tmap1.UVintVfracStepVCarry, eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap1.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap1.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap1.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap1.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap1.dest_row_data + + mov edx, Tmap1.DeltaUFrac + + mov eax, Tmap1.loop_count + inc eax + mov Tmap1.loop_count, eax + + shr eax, 3 + je DoLeftOverPixels + + mov Tmap1.num_big_steps, eax + and Tmap1.loop_count, 7 + + +NextPixelBlock: + + mov eax, Tmap1.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap1.fx_dl_dx + shl ebp, 3 //*32 + add Tmap1.fx_l, ebp + + mov ebp, Tmap1.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 3 + + mov dx, bp + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov al,[esi] // get texture pixel 0 + mov ah, bh + mov al, gr_fade_table[eax] + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+0],al // store pixel 0 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+1],al // store pixel 1 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 2 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+2],al // store pixel 2 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 3 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+3],al // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+4],al // store pixel 4 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 5 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+5],al // store pixel 5 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 6 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+6],al // store pixel 6 + + add ebx,edx // increment u fraction + + mov al,[esi] // get texture pixel 7 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + + mov [edi+7],al // store pixel 7 + + ; end + + + add edi, 8 + dec Tmap1.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov eax,Tmap1.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap1.loop_count, eax + pushf + + xor eax, eax + + + mov eax, Tmap1.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap1.fx_dl_dx + shr ebp, 8 + mov dx, bp + + mov al,[edi] // preread the destination cache line +// add ebx,edx // increment u fraction + +NextPixel: + + mov al,[esi] // get texture pixel 0 + mov ah, bh + mov al, gr_fade_table[eax] + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+0],al // store pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + mov ah, bh + mov al, gr_fade_table[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+1],al // store pixel 1 + + add edi, 2 + dec Tmap1.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + + mov al,[esi] // get texture pixel 2 + mov ah, bh + mov al, gr_fade_table[eax] + mov [edi],al // store pixel 2 + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +} + + +void asm_tmap_scanline_lln32() +{ + int end; + + end = f2i(Tmap1.fx_u); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v); + if ( end >= Tmap1.bp->h ) return; + + end = f2i(Tmap1.fx_u_right); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v_right); + if ( end >= Tmap1.bp->h ) return; + + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap1.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap1.DeltaVFrac, ebx // store it + imul eax, Tmap1.src_offset // calc texture step for v int step + + mov ebx, Tmap1.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap1.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap1.UVintVfracStepVNoCarry, eax // save whole step in non-v-carry slot + add eax, Tmap1.src_offset // calc whole step + v carry + mov Tmap1.UVintVfracStepVCarry, eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap1.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap1.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap1.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap1.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap1.dest_row_data32 + + mov edx, Tmap1.DeltaUFrac + + mov eax, Tmap1.fx_l // use bx and dx to do lighting + mov bx, ax + mov eax, Tmap1.fx_dl_dx // use bx and dx to do lighting + mov dx, ax + + mov eax, Tmap1.loop_count + inc eax + mov Tmap1.loop_count, eax + + shr eax, 3 + je DoLeftOverPixels + + mov Tmap1.num_big_steps, eax + and Tmap1.loop_count, 7 + + +NextPixelBlock: + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov al,[esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+0],eax // store pixel 0 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+4],al // store pixel 1 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 2 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+8],eax // store pixel 2 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 3 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+12],eax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+16],eax // store pixel 4 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 5 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+20],eax // store pixel 5 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 6 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+24],eax // store pixel 6 + + add ebx,edx // increment u fraction + + mov al,[esi] // get texture pixel 7 + mov ah, bh + mov eax, gr_fade_table32[eax] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + + mov [edi+28],eax // store pixel 7 + + ; end + + + add edi, 8*4 + dec Tmap1.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov eax,Tmap1.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap1.loop_count, eax + pushf + + xor eax, eax + +NextPixel: + mov al,[edi] // preread the destination cache line + + mov al,[esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+0],eax // store pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+1],al // store pixel 1 + + add edi, 2*4 + dec Tmap1.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + + mov al,[esi] // get texture pixel 2 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + mov [edi],eax // store pixel 2 + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +} + +void asm_tmap_scanline_lnt() +{ + int end; + + end = f2i(Tmap1.fx_u); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v); + if ( end >= Tmap1.bp->h ) return; + + end = f2i(Tmap1.fx_u_right); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v_right); + if ( end >= Tmap1.bp->h ) return; + + + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap1.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap1.DeltaVFrac, ebx // store it + imul eax, Tmap1.src_offset // calc texture step for v int step + + mov ebx, Tmap1.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap1.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap1.UVintVfracStepVNoCarry, eax // save whole step in non-v-carry slot + add eax, Tmap1.src_offset // calc whole step + v carry + mov Tmap1.UVintVfracStepVCarry, eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap1.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap1.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap1.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap1.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap1.dest_row_data + + mov edx, Tmap1.DeltaUFrac + + mov eax, Tmap1.loop_count + inc eax + mov Tmap1.loop_count, eax + + shr eax, 3 + je DoLeftOverPixels + + mov Tmap1.num_big_steps, eax + and Tmap1.loop_count, 7 + + +NextPixelBlock: + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov al,[esi] // get texture pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + cmp al, 255 + je skip0 + mov [edi+0],al // store pixel 0 +skip0: + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + cmp al, 255 + je skip1 + mov [edi+1],al // store pixel 0 +skip1: + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 2 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + cmp al, 255 + je skip2 + mov [edi+2],al // store pixel 0 +skip2: + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 3 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + cmp al, 255 + je skip3 + mov [edi+3],al // store pixel 0 +skip3: + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + cmp al, 255 + je skip4 + mov [edi+4],al // store pixel 0 +skip4: + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 5 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + cmp al, 255 + je skip5 + mov [edi+5],al // store pixel 0 +skip5: + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 6 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + cmp al, 255 + je skip6 + mov [edi+6],al // store pixel 0 +skip6: + + add ebx,edx // increment u fraction + + mov al,[esi] // get texture pixel 7 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + + cmp al, 255 + je skip7 + mov [edi+7],al // store pixel 0 +skip7: + + ; end + + + add edi, 8 + dec Tmap1.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov eax,Tmap1.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap1.loop_count, eax + pushf + + xor eax, eax + + mov al,[edi] // preread the destination cache line +// add ebx,edx // increment u fraction + +NextPixel: + + mov al,[esi] // get texture pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + cmp al, 255 + je skipA0 + mov [edi+0],al // store pixel 0 +skipA0: + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + cmp al, 255 + je skipA1 + mov [edi+1],al // store pixel 0 +skipA1: + + add edi, 2 + dec Tmap1.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + + mov al,[esi] // get texture pixel 2 + cmp al, 255 + je skipB + mov [edi],al // store pixel 0 +skipB: + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +} + + +void asm_tmap_scanline_lnn() +{ + int end; + + end = f2i(Tmap1.fx_u); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v); + if ( end >= Tmap1.bp->h ) return; + + end = f2i(Tmap1.fx_u_right); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v_right); + if ( end >= Tmap1.bp->h ) return; + + + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap1.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap1.DeltaVFrac, ebx // store it + imul eax, Tmap1.src_offset // calc texture step for v int step + + mov ebx, Tmap1.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap1.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap1.UVintVfracStepVNoCarry, eax // save whole step in non-v-carry slot + add eax, Tmap1.src_offset // calc whole step + v carry + mov Tmap1.UVintVfracStepVCarry, eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap1.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap1.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap1.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap1.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap1.dest_row_data + + mov edx, Tmap1.DeltaUFrac + + mov eax, Tmap1.loop_count + inc eax + mov Tmap1.loop_count, eax + + shr eax, 3 + je DoLeftOverPixels + + mov Tmap1.num_big_steps, eax + and Tmap1.loop_count, 7 + + +NextPixelBlock: + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov al,[esi] // get texture pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+0],al // store pixel 0 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+1],al // store pixel 0 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 2 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+2],al // store pixel 0 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 3 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+3],al // store pixel 0 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+4],al // store pixel 0 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 5 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+5],al // store pixel 0 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 6 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+6],al // store pixel 0 + + add ebx,edx // increment u fraction + + mov al,[esi] // get texture pixel 7 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + + mov [edi+7],al // store pixel 0 + + ; end + + + add edi, 8 + dec Tmap1.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov eax,Tmap1.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap1.loop_count, eax + pushf + + xor eax, eax + + mov al,[edi] // preread the destination cache line +// add ebx,edx // increment u fraction + +NextPixel: + + mov al,[esi] // get texture pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+0],al // store pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+1],al // store pixel 0 + + add edi, 2 + dec Tmap1.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + + mov al,[esi] // get texture pixel 2 + mov [edi],al // store pixel 0 + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +} + +void tmapscan_pln16( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + Tmap1.dest_row_data = (ubyte *)GR_SCREEN_PTR(ushort,lx,y); + Tmap1.loop_count = rx - lx; + Tmap1.fx_u = fl2f(p->u); + Tmap1.fx_v = fl2f(p->v); + Tmap1.fx_du_dx = fl2f(dp->u); + Tmap1.fx_dv_dx = fl2f(dp->v); + + Tmap1.fx_l = fl2f(p->l*32.0); + Tmap1.fx_dl_dx = fl2f(dp->l*32.0); + + Tmap1.fx_u_right = fl2f(rp->u); + Tmap1.fx_v_right = fl2f(rp->v); + Tmap1.pixptr = (unsigned char *)tmap_bitmap->data; + Tmap1.bp = tmap_bitmap; + Tmap1.src_offset = tmap_bitmap->w; + + + Tmap1.FixedScale = 65536.0f; + Tmap1.FixedScale8 = 2048.0f; //8192.0f; // 2^16 / 8 + Tmap1.One = 1.0f; + + + Tmap1.UOverZ = p->u; + Tmap1.VOverZ = p->v; + Tmap1.OneOverZ = p->sw; + + Tmap1.dUOverZdX8 = dp->u*32.0f; + Tmap1.dVOverZdX8 = dp->v*32.0f; + Tmap1.dOneOverZdX8 = dp->sw*32.0f; + + Tmap1.dUOverZdX = dp->u; + Tmap1.dVOverZdX = dp->v; + Tmap1.dOneOverZdX = dp->sw; + + Tmap1.RightUOverZ = rp->u; + Tmap1.RightVOverZ = rp->v; + Tmap1.RightOneOverZ = rp->sw; + + + + Tmap1.BitmapWidth = Tmap1.bp->w; + Tmap1.BitmapHeight = Tmap1.bp->h; + + + if ( Tmap1.fx_dl_dx < 0 ) { + Tmap1.fx_dl_dx = -Tmap1.fx_dl_dx; + Tmap1.fx_l = (67*F1_0)-Tmap1.fx_l; + Tmap1.fx_l_right = (67*F1_0)-Tmap1.fx_l_right; +// return; +// Assert( Tmap1.fx_l > 31*F1_0 ); +// Assert( Tmap1.fx_l < 66*F1_0 ); +// Assert( Tmap1.fx_dl_dx >= 0 ); +// Assert( Tmap1.fx_dl_dx < 31*F1_0 ); + } + +// return; + + + + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + + // put the FPU in 32 bit mode + // @todo move this out of here! + + fstcw Tmap1.OldFPUCW // store copy of CW + mov ax,Tmap1.OldFPUCW // get it in ax +//hh and eax,NOT 1100000000y // 24 bit precision + and eax, ~0x300L + mov Tmap1.FPUCW,ax // store it + fldcw Tmap1.FPUCW // load the FPU + + mov ecx, Tmap1.loop_count // ecx = width + inc ecx + mov edi, Tmap1.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? +// jmp Return + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap1.Subdivisions,ecx // store widths + mov Tmap1.WidthModLength,eax + +// mov ebx,pLeft ; get left edge pointer +// mov edx,pGradients ; get gradients pointer + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap1.VOverZ // V/ZL + fld Tmap1.UOverZ // U/ZL V/ZL + fld Tmap1.OneOverZ // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap1.dOneOverZdX8 // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap1.dUOverZdX8 // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap1.dVOverZdX8 // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap1.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap1.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap1.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap1.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap1.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap1.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap1.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap1.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span ; st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms---->; V/ZL 1/ZL U/ZL UL VL + + fadd Tmap1.dVOverZdX8 ; V/ZR 1/ZL U/ZL UL VL + fxch st(1) ; 1/ZL V/ZR U/ZL UL VL + fadd Tmap1.dOneOverZdX8 ; 1/ZR V/ZR U/ZL UL VL + fxch st(2) ; U/ZL V/ZR 1/ZR UL VL + fadd Tmap1.dUOverZdX8 ; U/ZR V/ZR 1/ZR UL VL + fxch st(2) ; 1/ZR V/ZR U/ZR UL VL + fxch st(1) ; V/ZR 1/ZR U/ZR UL VL + + ; calculate right side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 ; 1 V/ZR 1/ZR U/ZR UL VL + fdiv st,st(2) ; ZR V/ZR 1/ZR U/ZR UL VL + + + ; set up affine registers + + ; setup delta values + + mov eax,Tmap1.DeltaV ; get v 16.16 step + mov ebx,eax ; copy it + sar eax,16 ; get v int step + shl ebx,16 ; get v frac step + mov Tmap1.DeltaVFrac,ebx ; store it + imul eax,Tmap1.src_offset ; calculate texture step for v int step + + mov ebx,Tmap1.DeltaU ; get u 16.16 step + mov ecx,ebx ; copy it + sar ebx,16 ; get u int step + shl ecx,16 ; get u frac step + mov Tmap1.DeltaUFrac,ecx ; store it + add eax,ebx ; calculate uint + vint step + mov Tmap1.UVintVfracStepVNoCarry,eax; save whole step in non-v-carry slot + add eax,Tmap1.src_offset ; calculate whole step + v carry + mov Tmap1.UVintVfracStepVCarry,eax ; save in v-carry slot + + +/* +; check coordinate ranges + mov eax, Tmap1.UFixed + cmp eax, Tmap1.MinUFixed + jge UNotTooSmall_1 + mov eax, Tmap1.MinUFixed + mov Tmap1.UFixed, eax + jmp CheckV_1 +UNotTooSmall_1: + cmp eax, Tmap1.MaxUFixed + jle CheckV_1 + mov eax, Tmap1.MaxUFixed + mov Tmap1.UFixed, eax +CheckV_1: + mov eax, Tmap1.VFixed + cmp eax, Tmap1.MinVFixed + jge VNotTooSmall_1 + mov eax, Tmap1.MinVFixed + mov Tmap1.VFixed, eax + jmp DoneCheck_1 +VNotTooSmall_1: + cmp eax, Tmap1.MaxVFixed + jle DoneCheck_1 + mov eax, Tmap1.MaxVFixed + mov Tmap1.VFixed, eax +DoneCheck_1: +*/ + +; setup initial coordinates + mov esi,Tmap1.UFixed ; get u 16.16 fixedpoint coordinate + + mov ebx,esi ; copy it + sar esi,16 ; get integer part + shl ebx,16 ; get fractional part + + mov ecx,Tmap1.VFixed ; get v 16.16 fixedpoint coordinate + + mov edx,ecx ; copy it + sar edx,16 ; get integer part + shl ecx,16 ; get fractional part + imul edx,Tmap1.src_offset ; calc texture scanline address + add esi,edx ; calc texture offset + add esi,Tmap1.pixptr ; calc address + + mov edx,Tmap1.DeltaUFrac ; get register copy + + mov eax, Tmap1.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap1.fx_dl_dx + shl ebp, 5 //*32 + add Tmap1.fx_l, ebp + + mov ebp, Tmap1.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + +// add Tmap1.fx_l, eax + + +// mov eax, Tmap1.fx_l // use bx and dx to do lighting +//mov eax, 31*256 +// mov bx, ax +// mov eax, Tmap1.fx_dl_dx // use bx and dx to do lighting +//mov eax, 0 +// mov dx, ax + + + + ; ************** Can't Access Stack Frame ****************** + ; ************** Can't Access Stack Frame ****************** + ; ************** Can't Access Stack Frame ****************** + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + + mov al,[esi] // get texture pixel 0 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry +// mov al, 0 // Uncomment this line to show divisions + mov [edi+0],ax // store pixel 0 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+2],ax // store pixel 1 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 2 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+4],ax // store pixel 2 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 3 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+6],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+8],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+10],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+12],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+14],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+16],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+18],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+20],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+22],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+24],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+26],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+28],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+30],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+32],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+34],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+36],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+38],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+40],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+42],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+44],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+46],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+48],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+50],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + + + sbb ebp,ebp // get -1 if carry + mov [edi+52],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+54],ax // store pixel 3 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 4 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+56],ax // store pixel 4 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 5 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+58],ax // store pixel 5 + + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 6 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+60],ax // store pixel 6 + + add ebx,edx // increment u fraction + + mov al,[esi] // get texture pixel 7 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + + mov [edi+62],ax // store pixel 7 + + + + ; ************** Okay to Access Stack Frame **************** + ; ************** Okay to Access Stack Frame **************** + ; ************** Okay to Access Stack Frame **************** + + + ; the fdiv is done, finish right ; st0 st1 st2 st3 st4 st5 st6 st7 + ; ZR V/ZR 1/ZR U/ZR UL VL + + fld st ; ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) ; VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) ; ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) ; UR VR V/ZR 1/ZR U/ZR UL VL + + add edi,64 ; increment to next span + dec Tmap1.Subdivisions ; decrement span count + jnz SpanLoop ; loop back + + // save new lighting values +// xor eax, eax +// mov ax, bx +// mov Tmap1.fx_l, eax + +// xor eax, eax +// mov ax, dx +// mov Tmap1.fx_dl_dx, eax + +HandleLeftoverPixels: +// jmp FPUReturn + + mov esi,Tmap1.pixptr ; load texture pointer + + ; edi = dest dib bits + ; esi = current texture dib bits + ; at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + ; inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap1.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + ; convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap1.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap1.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap1.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap1.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap1.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas + + ; calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + ; r -> R+1 + + ; @todo rearrange things so we don't need these two instructions + fstp Tmap1.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap1.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap1.RightVOverZ ; V/Zr inv. inv. inv. UL VL + fsub Tmap1.dVOverZdX ; V/ZR inv. inv. inv. UL VL + fld Tmap1.RightUOverZ ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap1.dUOverZdX ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap1.RightOneOverZ ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap1.dOneOverZdX ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap1.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + ; calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap1.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap1.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap1.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap1.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap1.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap1.DeltaU ; inv. inv. inv. UR VR + + ; @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + +//jmp OldWay + + + ; setup delta values + mov eax, Tmap1.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap1.DeltaVFrac, ebx // store it + imul eax, Tmap1.src_offset // calc texture step for v int step + + mov ebx, Tmap1.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap1.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap1.UVintVfracStepVNoCarry, eax // save whole step in non-v-carry slot + add eax, Tmap1.src_offset // calc whole step + v carry + mov Tmap1.UVintVfracStepVCarry, eax // save in v-carry slot + + + +OnePixelSpan: + +/* +; check coordinate ranges + mov eax, Tmap1.UFixed + cmp eax, Tmap1.MinUFixed + jge UNotTooSmall_2 + mov eax, Tmap1.MinUFixed + mov Tmap1.UFixed, eax + jmp CheckV_2 +UNotTooSmall_2: + cmp eax, Tmap1.MaxUFixed + jle CheckV_2 + mov eax, Tmap1.MaxUFixed + mov Tmap1.UFixed, eax +CheckV_2: + mov eax, Tmap1.VFixed + cmp eax, Tmap1.MinVFixed + jge VNotTooSmall_2 + mov eax, Tmap1.MinVFixed + mov Tmap1.VFixed, eax + jmp DoneCheck_2 +VNotTooSmall_2: + cmp eax, Tmap1.MaxVFixed + jle DoneCheck_2 + mov eax, Tmap1.MaxVFixed + mov Tmap1.VFixed, eax +DoneCheck_2: +*/ + + + + + ; setup initial coordinates + mov esi, Tmap1.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap1.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap1.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap1.pixptr // calc address + + ; set edi = address of first pixel to modify +; mov edi, Tmap1.dest_row_data + + + + + mov eax, Tmap1.fx_l + shr eax, 8 + mov bx, ax + + mov edx, Tmap1.DeltaUFrac + + cmp Tmap1.WidthModLength, 1 + jle NoDeltaLight + + push ebx + + mov ebx, Tmap1.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + +#if 0 + // slow but maybe better + push edx + cdq + mov ebx, Tmap1.WidthModLength + dec ebx + idiv ebx + pop edx +#else + mov eax, Tmap1.fx_dl_dx + shr eax, 8 +#endif + + mov dx, ax + + pop ebx + +NoDeltaLight: + + inc Tmap1.WidthModLength + mov eax,Tmap1.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap1.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +NextPixel: + mov al,[esi] // get texture pixel 0 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+0],ax // store pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + mov al,[esi] // get texture pixel 1 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+2],ax // store pixel 1 + + add edi, 4 + dec Tmap1.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + + mov al,[esi] // get texture pixel 2 + mov ah, bh + mov ax, gr_fade_table16[eax*2] + mov [edi],ax // store pixel 2 + + + + + + + + + + + + + +/* +OldWay: // This is 6% slower than above + + mov ebx,Tmap1.UFixed ; get starting coordinates + mov ecx,Tmap1.VFixed ; for span + + ; leftover pixels loop + ; edi = dest dib bits + ; esi = texture dib bits + + ; ebx = u 16.16 + ; ecx = v 16.16 + + + mov eax,ecx ; copy v + sar eax,16 ; int(v) + imul eax,Tmap1.src_offset ; scan offset + mov edx,ebx ; copy u + sar edx,16 ; int(u) + add eax,edx ; texture offset + mov al,[esi+eax] ; get source pixel +mov al, 0 + mov [edi],al ; store it + inc edi + add ebx,Tmap1.DeltaU ; increment u coordinate + add ecx,Tmap1.DeltaV ; increment v coordinate + + dec Tmap1.WidthModLength ; decrement loop count + jl FPUReturn ; finish up + + +LeftoverLoop: + mov eax,ecx ; copy v + sar eax,16 ; int(v) + imul eax,Tmap1.src_offset ; scan offset + mov edx,ebx ; copy u + sar edx,16 ; int(u) + add eax,edx ; texture offset + mov al,[esi+eax] ; get source pixel + mov [edi],al ; store it + inc edi + add ebx,Tmap1.DeltaU ; increment u coordinate + add ecx,Tmap1.DeltaV ; increment v coordinate + + dec Tmap1.WidthModLength ; decrement loop count + jge LeftoverLoop ; finish up +*/ + +FPUReturn: + + ; busy FPU registers: ; st0 st1 st2 st3 st4 st5 st6 st7 + ; xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + +//Return: + + fldcw Tmap1.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } + + +} + + + + +void tmapscan_lnn16( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + Tmap1.dest_row_data = (ubyte *)GR_SCREEN_PTR(ushort,lx,y); + Tmap1.loop_count = rx - lx; + Tmap1.pixptr = (unsigned char *)tmap_bitmap->data; + Tmap1.bp = tmap_bitmap; + Tmap1.src_offset = tmap_bitmap->w; + + Tmap1.fx_u = fl2f(p->u); + Tmap1.fx_v = fl2f(p->v); + Tmap1.fx_du_dx = fl2f(dp->u); + Tmap1.fx_dv_dx = fl2f(dp->v); + Tmap1.fx_u_right = fl2f(rp->u); + Tmap1.fx_v_right = fl2f(rp->v); + + int end; + + end = f2i(Tmap1.fx_u); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v); + if ( end >= Tmap1.bp->h ) return; + + end = f2i(Tmap1.fx_u_right); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v_right); + if ( end >= Tmap1.bp->h ) return; + + + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap1.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap1.DeltaVFrac, ebx // store it + imul eax, Tmap1.src_offset // calc texture step for v int step + + mov ebx, Tmap1.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap1.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap1.UVintVfracStepVNoCarry, eax // save whole step in non-v-carry slot + add eax, Tmap1.src_offset // calc whole step + v carry + mov Tmap1.UVintVfracStepVCarry, eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap1.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap1.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap1.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap1.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap1.dest_row_data + + mov edx, Tmap1.DeltaUFrac + + mov eax, Tmap1.loop_count + inc eax + mov Tmap1.loop_count, eax + + shr eax, 3 + je DoLeftOverPixels + + mov Tmap1.num_big_steps, eax + and Tmap1.loop_count, 7 + + xor eax, eax + +NextPixelBlock: + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + movzx eax,byte ptr [esi] // get texture pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov ax, palman_8_16_xlat[eax*2] + mov [edi+0],ax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov ax, palman_8_16_xlat[eax*2] + mov [edi+2],ax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov ax, palman_8_16_xlat[eax*2] + mov [edi+4],ax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov ax, palman_8_16_xlat[eax*2] + mov [edi+6],ax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov ax, palman_8_16_xlat[eax*2] + mov [edi+8],ax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov ax, palman_8_16_xlat[eax*2] + mov [edi+10],ax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov ax, palman_8_16_xlat[eax*2] + mov [edi+12],ax // store pixel 0 + + add ebx,edx // increment u fraction + + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + + mov ax, palman_8_16_xlat[eax*2] + mov [edi+14],ax // store pixel 0 + + ; end + + + add edi, 16 + dec Tmap1.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov eax,Tmap1.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap1.loop_count, eax + pushf + + xor eax, eax + + mov al,[edi] // preread the destination cache line +// add ebx,edx // increment u fraction + +NextPixel: + + movzx eax,byte ptr [esi] // get texture pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov ax, palman_8_16_xlat[eax*2] + mov [edi+0],ax // store pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov ax, palman_8_16_xlat[eax*2] + mov [edi+2],ax // store pixel 0 + + add edi, 4 + dec Tmap1.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ax, palman_8_16_xlat[eax*2] + mov [edi],ax // store pixel 0 + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +} + + + +void tmapscan_lnn32( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + Tmap1.dest_row_data = (ubyte *)GR_SCREEN_PTR(uint,lx,y); + Tmap1.loop_count = rx - lx; + Tmap1.pixptr = (unsigned char *)tmap_bitmap->data; + Tmap1.bp = tmap_bitmap; + Tmap1.src_offset = tmap_bitmap->w; + + Tmap1.fx_u = fl2f(p->u); + Tmap1.fx_v = fl2f(p->v); + Tmap1.fx_du_dx = fl2f(dp->u); + Tmap1.fx_dv_dx = fl2f(dp->v); + Tmap1.fx_u_right = fl2f(rp->u); + Tmap1.fx_v_right = fl2f(rp->v); + + int end; + + end = f2i(Tmap1.fx_u); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v); + if ( end >= Tmap1.bp->h ) return; + + end = f2i(Tmap1.fx_u_right); + if ( end >= Tmap1.bp->w ) return; + + end = f2i(Tmap1.fx_v_right); + if ( end >= Tmap1.bp->h ) return; + + + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap1.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap1.DeltaVFrac, ebx // store it + imul eax, Tmap1.src_offset // calc texture step for v int step + + mov ebx, Tmap1.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap1.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap1.UVintVfracStepVNoCarry, eax // save whole step in non-v-carry slot + add eax, Tmap1.src_offset // calc whole step + v carry + mov Tmap1.UVintVfracStepVCarry, eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap1.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap1.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap1.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap1.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap1.dest_row_data + + mov edx, Tmap1.DeltaUFrac + + mov eax, Tmap1.loop_count + inc eax + mov Tmap1.loop_count, eax + + shr eax, 3 + je DoLeftOverPixels + + mov Tmap1.num_big_steps, eax + and Tmap1.loop_count, 7 + + xor eax, eax + +NextPixelBlock: + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + movzx eax,byte ptr [esi] // get texture pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov eax, palman_8_32_xlat[eax*4] + mov [edi+0],eax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov eax, palman_8_32_xlat[eax*4] + mov [edi+4],eax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov eax, palman_8_32_xlat[eax*4] + mov [edi+8],eax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov eax, palman_8_32_xlat[eax*4] + mov [edi+12],eax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov eax, palman_8_32_xlat[eax*4] + mov [edi+16],eax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov eax, palman_8_32_xlat[eax*4] + mov [edi+20],eax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov eax, palman_8_32_xlat[eax*4] + mov [edi+24],eax // store pixel 0 + + add ebx,edx // increment u fraction + + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + + mov eax, palman_8_32_xlat[eax*4] + mov [edi+28],eax // store pixel 0 + + ; end + + + add edi, 32 + dec Tmap1.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov eax,Tmap1.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap1.loop_count, eax + pushf + + xor eax, eax + + mov al,[edi] // preread the destination cache line +// add ebx,edx // increment u fraction + +NextPixel: + + movzx eax,byte ptr [esi] // get texture pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov eax, palman_8_32_xlat[eax*4] + mov [edi+0],eax // store pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov eax, palman_8_32_xlat[eax*4] + mov [edi+4],eax // store pixel 0 + + add edi, 8 + dec Tmap1.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + + movzx eax,byte ptr [esi] // get texture pixel 0 + mov eax, palman_8_32_xlat[eax*4] + mov [edi],eax // store pixel 0 + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +} + + +void tmapscan_pln32( int lx, int rx, int y, vertex *p, vertex *dp, vertex * rp,uint flags ) +{ + Tmap1.dest_row_data = (ubyte *)GR_SCREEN_PTR(uint,lx,y); + Tmap1.loop_count = rx - lx; + Tmap1.fx_u = fl2f(p->u); + Tmap1.fx_v = fl2f(p->v); + Tmap1.fx_du_dx = fl2f(dp->u); + Tmap1.fx_dv_dx = fl2f(dp->v); + + Tmap1.fx_l = fl2f(p->l*32.0); + Tmap1.fx_dl_dx = fl2f(dp->l*32.0); + + Tmap1.fx_u_right = fl2f(rp->u); + Tmap1.fx_v_right = fl2f(rp->v); + Tmap1.pixptr = (unsigned char *)tmap_bitmap->data; + Tmap1.bp = tmap_bitmap; + Tmap1.src_offset = tmap_bitmap->w; + + + Tmap1.FixedScale = 65536.0f; + Tmap1.FixedScale8 = 2048.0f; //8192.0f; // 2^16 / 8 + Tmap1.One = 1.0f; + + + Tmap1.UOverZ = p->u; + Tmap1.VOverZ = p->v; + Tmap1.OneOverZ = p->sw; + + Tmap1.dUOverZdX8 = dp->u*32.0f; + Tmap1.dVOverZdX8 = dp->v*32.0f; + Tmap1.dOneOverZdX8 = dp->sw*32.0f; + + Tmap1.dUOverZdX = dp->u; + Tmap1.dVOverZdX = dp->v; + Tmap1.dOneOverZdX = dp->sw; + + Tmap1.RightUOverZ = rp->u; + Tmap1.RightVOverZ = rp->v; + Tmap1.RightOneOverZ = rp->sw; + + + Tmap1.BitmapWidth = Tmap1.bp->w; + Tmap1.BitmapHeight = Tmap1.bp->h; + + + if ( Tmap1.fx_dl_dx < 0 ) { + Tmap1.fx_dl_dx = -Tmap1.fx_dl_dx; + Tmap1.fx_l = (67*F1_0)-Tmap1.fx_l; + Tmap1.fx_l_right = (67*F1_0)-Tmap1.fx_l_right; +// return; +// Assert( Tmap1.fx_l > 31*F1_0 ); +// Assert( Tmap1.fx_l < 66*F1_0 ); +// Assert( Tmap1.fx_dl_dx >= 0 ); +// Assert( Tmap1.fx_dl_dx < 31*F1_0 ); + } + +// return; + + + + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + + // put the FPU in 32 bit mode + // @todo move this out of here! + + fstcw Tmap1.OldFPUCW // store copy of CW + mov ax,Tmap1.OldFPUCW // get it in ax +//hh and eax,NOT 1100000000y // 24 bit precision + and eax, ~0x300L + mov Tmap1.FPUCW,ax // store it + fldcw Tmap1.FPUCW // load the FPU + + mov ecx, Tmap1.loop_count // ecx = width + inc ecx + mov edi, Tmap1.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? +// jmp Return + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap1.Subdivisions,ecx // store widths + mov Tmap1.WidthModLength,eax + +// mov ebx,pLeft ; get left edge pointer +// mov edx,pGradients ; get gradients pointer + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap1.VOverZ // V/ZL + fld Tmap1.UOverZ // U/ZL V/ZL + fld Tmap1.OneOverZ // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap1.dOneOverZdX8 // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap1.dUOverZdX8 // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap1.dVOverZdX8 // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap1.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap1.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap1.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap1.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap1.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap1.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap1.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap1.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span ; st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms---->; V/ZL 1/ZL U/ZL UL VL + + fadd Tmap1.dVOverZdX8 ; V/ZR 1/ZL U/ZL UL VL + fxch st(1) ; 1/ZL V/ZR U/ZL UL VL + fadd Tmap1.dOneOverZdX8 ; 1/ZR V/ZR U/ZL UL VL + fxch st(2) ; U/ZL V/ZR 1/ZR UL VL + fadd Tmap1.dUOverZdX8 ; U/ZR V/ZR 1/ZR UL VL + fxch st(2) ; 1/ZR V/ZR U/ZR UL VL + fxch st(1) ; V/ZR 1/ZR U/ZR UL VL + + ; calculate right side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 ; 1 V/ZR 1/ZR U/ZR UL VL + fdiv st,st(2) ; ZR V/ZR 1/ZR U/ZR UL VL + + + ; set up affine registers + + ; setup delta values + + mov eax,Tmap1.DeltaV ; get v 16.16 step + mov ebx,eax ; copy it + sar eax,16 ; get v int step + shl ebx,16 ; get v frac step + mov Tmap1.DeltaVFrac,ebx ; store it + imul eax,Tmap1.src_offset ; calculate texture step for v int step + + mov ebx,Tmap1.DeltaU ; get u 16.16 step + mov ecx,ebx ; copy it + sar ebx,16 ; get u int step + shl ecx,16 ; get u frac step + mov Tmap1.DeltaUFrac,ecx ; store it + add eax,ebx ; calculate uint + vint step + mov Tmap1.UVintVfracStepVNoCarry,eax; save whole step in non-v-carry slot + add eax,Tmap1.src_offset ; calculate whole step + v carry + mov Tmap1.UVintVfracStepVCarry,eax ; save in v-carry slot + + +/* +; check coordinate ranges + mov eax, Tmap1.UFixed + cmp eax, Tmap1.MinUFixed + jge UNotTooSmall_1 + mov eax, Tmap1.MinUFixed + mov Tmap1.UFixed, eax + jmp CheckV_1 +UNotTooSmall_1: + cmp eax, Tmap1.MaxUFixed + jle CheckV_1 + mov eax, Tmap1.MaxUFixed + mov Tmap1.UFixed, eax +CheckV_1: + mov eax, Tmap1.VFixed + cmp eax, Tmap1.MinVFixed + jge VNotTooSmall_1 + mov eax, Tmap1.MinVFixed + mov Tmap1.VFixed, eax + jmp DoneCheck_1 +VNotTooSmall_1: + cmp eax, Tmap1.MaxVFixed + jle DoneCheck_1 + mov eax, Tmap1.MaxVFixed + mov Tmap1.VFixed, eax +DoneCheck_1: +*/ + +; setup initial coordinates + mov esi,Tmap1.UFixed ; get u 16.16 fixedpoint coordinate + + mov ebx,esi ; copy it + sar esi,16 ; get integer part + shl ebx,16 ; get fractional part + + mov ecx,Tmap1.VFixed ; get v 16.16 fixedpoint coordinate + + mov edx,ecx ; copy it + sar edx,16 ; get integer part + shl ecx,16 ; get fractional part + imul edx,Tmap1.src_offset ; calc texture scanline address + add esi,edx ; calc texture offset + add esi,Tmap1.pixptr ; calc address + + mov edx,Tmap1.DeltaUFrac ; get register copy + + mov eax, Tmap1.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap1.fx_dl_dx + shl ebp, 5 //*32 + add Tmap1.fx_l, ebp + + mov ebp, Tmap1.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + +// add Tmap1.fx_l, eax + + +// mov eax, Tmap1.fx_l // use bx and dx to do lighting +//mov eax, 31*256 +// mov bx, ax +// mov eax, Tmap1.fx_dl_dx // use bx and dx to do lighting +//mov eax, 0 +// mov dx, ax + + + + ; ************** Can't Access Stack Frame ****************** + ; ************** Can't Access Stack Frame ****************** + ; ************** Can't Access Stack Frame ****************** + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry +// mov al, 0 // Uncomment this line to show divisions + mov [edi+0],eax // store pixel 0 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+4],eax // store pixel 1 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+8],eax // store pixel 2 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+12],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+16],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+20],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+24],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+28],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+32],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+36],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+40],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+44],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+48],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+52],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+56],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+60],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+64],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+68],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+72],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+76],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+80],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+84],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+88],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+92],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+96],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+100],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + + + sbb ebp,ebp // get -1 if carry + mov [edi+104],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+108],eax // store pixel 3 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+112],eax // store pixel 4 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+116],eax // store pixel 5 + + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + add ecx,Tmap1.DeltaVFrac // increment v fraction + + sbb ebp,ebp // get -1 if carry + mov [edi+120],eax // store pixel 6 + + add ebx,edx // increment u fraction + + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + + mov [edi+124],eax // store pixel 7 + + + + ; ************** Okay to Access Stack Frame **************** + ; ************** Okay to Access Stack Frame **************** + ; ************** Okay to Access Stack Frame **************** + + + ; the fdiv is done, finish right ; st0 st1 st2 st3 st4 st5 st6 st7 + ; ZR V/ZR 1/ZR U/ZR UL VL + + fld st ; ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) ; VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) ; ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) ; UR VR V/ZR 1/ZR U/ZR UL VL + + add edi,128 ; increment to next span + dec Tmap1.Subdivisions ; decrement span count + jnz SpanLoop ; loop back + + // save new lighting values +// xor eax, eax +// mov ax, bx +// mov Tmap1.fx_l, eax + +// xor eax, eax +// mov ax, dx +// mov Tmap1.fx_dl_dx, eax + +HandleLeftoverPixels: +// jmp FPUReturn + + mov esi,Tmap1.pixptr ; load texture pointer + + ; edi = dest dib bits + ; esi = current texture dib bits + ; at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + ; inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap1.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + ; convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap1.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap1.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap1.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap1.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap1.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas + + ; calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + ; r -> R+1 + + ; @todo rearrange things so we don't need these two instructions + fstp Tmap1.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap1.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap1.RightVOverZ ; V/Zr inv. inv. inv. UL VL + fsub Tmap1.dVOverZdX ; V/ZR inv. inv. inv. UL VL + fld Tmap1.RightUOverZ ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap1.dUOverZdX ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap1.RightOneOverZ ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap1.dOneOverZdX ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap1.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + ; calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap1.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap1.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap1.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap1.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap1.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap1.DeltaU ; inv. inv. inv. UR VR + + ; @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + +//jmp OldWay + + + ; setup delta values + mov eax, Tmap1.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap1.DeltaVFrac, ebx // store it + imul eax, Tmap1.src_offset // calc texture step for v int step + + mov ebx, Tmap1.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap1.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap1.UVintVfracStepVNoCarry, eax // save whole step in non-v-carry slot + add eax, Tmap1.src_offset // calc whole step + v carry + mov Tmap1.UVintVfracStepVCarry, eax // save in v-carry slot + + + +OnePixelSpan: + +/* +; check coordinate ranges + mov eax, Tmap1.UFixed + cmp eax, Tmap1.MinUFixed + jge UNotTooSmall_2 + mov eax, Tmap1.MinUFixed + mov Tmap1.UFixed, eax + jmp CheckV_2 +UNotTooSmall_2: + cmp eax, Tmap1.MaxUFixed + jle CheckV_2 + mov eax, Tmap1.MaxUFixed + mov Tmap1.UFixed, eax +CheckV_2: + mov eax, Tmap1.VFixed + cmp eax, Tmap1.MinVFixed + jge VNotTooSmall_2 + mov eax, Tmap1.MinVFixed + mov Tmap1.VFixed, eax + jmp DoneCheck_2 +VNotTooSmall_2: + cmp eax, Tmap1.MaxVFixed + jle DoneCheck_2 + mov eax, Tmap1.MaxVFixed + mov Tmap1.VFixed, eax +DoneCheck_2: +*/ + + + + + ; setup initial coordinates + mov esi, Tmap1.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap1.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap1.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap1.pixptr // calc address + + ; set edi = address of first pixel to modify +; mov edi, Tmap1.dest_row_data + + + + + mov eax, Tmap1.fx_l + shr eax, 8 + mov bx, ax + + mov edx, Tmap1.DeltaUFrac + + cmp Tmap1.WidthModLength, 1 + jle NoDeltaLight + + push ebx + + mov ebx, Tmap1.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + +#if 0 + // slow but maybe better + push edx + cdq + mov ebx, Tmap1.WidthModLength + dec ebx + idiv ebx + pop edx +#else + mov eax, Tmap1.fx_dl_dx + shr eax, 8 +#endif + + mov dx, ax + + pop ebx + +NoDeltaLight: + + inc Tmap1.WidthModLength + mov eax,Tmap1.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap1.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +NextPixel: + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+0],eax // store pixel 0 + + add ecx,Tmap1.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + + adc esi,UVintVfracStep[4*ebp] // add in step ints & carries + mov [edi+4],eax // store pixel 1 + + add edi, 8 + dec Tmap1.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + + movzx eax,byte ptr [esi] // get texture pixel 0 + mov ah, bh + mov eax, gr_fade_table32[eax*4] + mov [edi],eax // store pixel 2 + + + + + + + + + + + + + +/* +OldWay: // This is 6% slower than above + + mov ebx,Tmap1.UFixed ; get starting coordinates + mov ecx,Tmap1.VFixed ; for span + + ; leftover pixels loop + ; edi = dest dib bits + ; esi = texture dib bits + + ; ebx = u 16.16 + ; ecx = v 16.16 + + + mov eax,ecx ; copy v + sar eax,16 ; int(v) + imul eax,Tmap1.src_offset ; scan offset + mov edx,ebx ; copy u + sar edx,16 ; int(u) + add eax,edx ; texture offset + mov al,[esi+eax] ; get source pixel +mov al, 0 + mov [edi],al ; store it + inc edi + add ebx,Tmap1.DeltaU ; increment u coordinate + add ecx,Tmap1.DeltaV ; increment v coordinate + + dec Tmap1.WidthModLength ; decrement loop count + jl FPUReturn ; finish up + + +LeftoverLoop: + mov eax,ecx ; copy v + sar eax,16 ; int(v) + imul eax,Tmap1.src_offset ; scan offset + mov edx,ebx ; copy u + sar edx,16 ; int(u) + add eax,edx ; texture offset + mov al,[esi+eax] ; get source pixel + mov [edi],al ; store it + inc edi + add ebx,Tmap1.DeltaU ; increment u coordinate + add ecx,Tmap1.DeltaV ; increment v coordinate + + dec Tmap1.WidthModLength ; decrement loop count + jge LeftoverLoop ; finish up +*/ + +FPUReturn: + + ; busy FPU registers: ; st0 st1 st2 st3 st4 st5 st6 st7 + ; xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + +//Return: + + fldcw Tmap1.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } + + +} + + + + +add edx,DeltaVFrac ; Add in 0.32 DeltaVFrac to VFrac +sbb ebp,ebp ; ebp will equal -1 if there was a carry +mov BYTE PTR [edi], al ; blit destination pixel +mov al, BYTE PTR [esi] ; get next texel +add ecx,ebx ; add 0.32 DeltaUFrac to UFrac, plus light +adc esi, [UVStepCarry1+(ebp*4)] +mov ah, ch ; move lighting value into place +mov al, ShadeTable[eax] ; Get shaded pixel + + + + + + + + + + +#endif diff --git a/src/graphics/tmapper.cpp b/src/graphics/tmapper.cpp new file mode 100644 index 0000000..acfa4a4 --- /dev/null +++ b/src/graphics/tmapper.cpp @@ -0,0 +1,881 @@ +/* + * $Logfile: /Freespace2/code/Graphics/Tmapper.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to draw a texture map. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 12/06/98 3:08p Dave + * Fixed grx_tmapper to handle pixel fog flag. First run fog support for + * D3D. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 78 5/13/98 2:53p John + * Made subspace effect work under software. Had to add new inner loop to + * tmapper. Added glows to end of subspace effect. Made subspace effect + * levels use gamepalette-subspace palette. + * + * 77 4/10/98 5:20p John + * Changed RGB in lighting structure to be ubytes. Removed old + * not-necessary 24 bpp software stuff. + * + * 76 4/09/98 7:58p John + * Cleaned up tmapper code a bit. Put NDEBUG around some ndebug stuff. + * Took out XPARENT flag settings in all the alpha-blended texture stuff. + * + * 75 4/09/98 7:16p John + * Fixed bug causing software to not render + * + * 74 3/27/98 8:34p Mike + * Minor optimization. + * + * 73 3/25/98 8:08p John + * Restructured software rendering into two modules; One for windowed + * debug mode and one for DirectX fullscreen. + * + * 72 3/23/98 5:00p John + * Improved missile trails. Made smooth alpha under hardware. Made end + * taper. Made trail touch weapon. + * + * 71 3/22/98 2:33p John + * Took out fx_v/v_right. Made fx_u etc get calculated in tmapper. + * + * 70 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 69 2/10/98 5:34p John + * + * 68 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 67 1/29/98 8:18a John + * Put in some commented out hooks for RGB lighting + * + * 66 1/28/98 1:27p John + * Really fixed bug with exception on mov al, [esi] + * + * 65 1/28/98 1:22p John + * Fixed bug with unitialized dwdx_wide. + * + * 64 1/27/98 5:13p John + * Moved all float to int conversions out of inner loops and into outer. + * Made outer loop use FISTP instead of ftol, saved about 10%. + * + * 63 1/23/98 5:08p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 62 1/19/98 6:15p John + * Fixed all my Optimized Build compiler warnings + * + * 61 12/15/97 11:32a John + * New Laser Code + * + * 60 12/02/97 4:00p John + * Added first rev of thruster glow, along with variable levels of + * translucency, which retquired some restructing of palman. + * + * 59 11/30/97 4:40p John + * made 32-bpp tiled tmapper call scanline + * + * 58 11/30/97 3:57p John + * Made fixed 32-bpp translucency. Made BmpMan always map translucent + * color into 255 even if you aren't supposed to remap and make it's + * palette black. + * + * 57 11/30/97 12:18p John + * added more 24 & 32-bpp primitives + * + * 56 11/29/97 2:06p John + * added mode 16-bpp support + * + * 55 11/21/97 11:32a John + * Added nebulas. Fixed some warpout bugs. + * + * 54 11/14/97 12:30p John + * Fixed some DirectX bugs. Moved the 8-16 xlat tables into Graphics + * libs. Made 16-bpp DirectX modes know what bitmap format they're in. + * + * 53 11/06/97 11:18a John + * added 16-bpp gouraud flat shader + * + * 52 10/19/97 12:55p John + * new code to lock / unlock surfaces for smooth directx integration. + * + * 51 10/16/97 10:55a John + * added tmapper to draw a monochrome alpha blended bitmap. + * + * 50 10/14/97 8:08a John + * added a bunch more 16 bit support + * + * 49 10/09/97 5:23p John + * Added support for more 16-bpp functions + * + * 48 9/30/97 2:30p John + * test code for texture fading + * + * 47 9/24/97 10:37a John + * made tmapper not trash uv values anymore. + * + * 46 9/09/97 11:01a Sandeep + * fixed warning level 4 bugs + * + * 45 7/11/97 11:54a John + * added rotated 3d bitmaps. + * + * 44 6/18/97 5:02p John + * fixed bug with 32x32 and 16x16 tmapper + * + * 43 6/12/97 5:04p John + * Initial rev of Glide support + * + * 42 6/12/97 2:50a Lawrance + * bm_unlock() now passed bitmap number, not pointer + * + * 41 6/02/97 11:45a John + * fixed bugs with 64x64 and 128x128 tmappers. + * + * 40 6/01/97 3:41p John + * made non-tilable textures on tilable models bash uvs to 0-1. + * + * 39 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 38 5/07/97 4:14p John + * Reenabled calls to gr_start/end_frame. + * + * 37 4/21/97 10:06a John + * Got capital ships working again. + * + * 36 4/17/97 6:06p John + * New code/data for v19 of BSPGEN with smoothing and zbuffer + * optimizations. + * + * 35 4/08/97 5:18p John + * First rev of decent (dynamic, correct) lighting in FreeSpace. + * + * 34 3/18/97 9:42a John + * + * 33 3/15/97 2:44p John + * got scanline sorting method working. Bummer it is slower than zbuffer! + * + * 32 3/14/97 3:55p John + * Made tiled tmapper not always be zbuffered. + * + * 31 3/13/97 10:32a John + * Added code for tiled 256x256 textures in certain models. + * + * 30 3/12/97 2:51p John + * Added some test code for tmapper. + * + * 29 3/12/97 9:25a John + * fixed a bug with zbuffering. Reenabled it by default. + * + * 28 3/11/97 4:36p John + * added zbuffering to textest. Made zbuffered tmapper a bit faster by + * rearranging some instructions. + * + * 27 3/10/97 5:20p John + * Differentiated between Gouraud and Flat shading. Since we only do flat + * shading as of now, we don't need to interpolate L in the outer loop. + * This should save a few percent. + * + * 26 3/10/97 2:24p John + * added some commets about precompiled inner loop + * + * 25 3/05/97 7:15p John + * took out the old z stop tmapper used for briefing. + * + * 24 1/20/97 4:17p John + * + * 23 1/06/97 2:44p John + * Added in slow (but correct) zbuffering + * + * 22 12/30/96 3:46p John + * + * 21 12/23/96 10:56a John + * Totally restructured the POF stuff to support multiple + * detail levels in one POF file. + * + * + * 20 12/10/96 10:37a John + * Restructured texture mapper to remove some overhead from each scanline + * setup. This gave about a 30% improvement drawing trans01.pof, which is + * a really complex model. In the process, I cleaned up the scanline + * functions and separated them into different modules for each pixel + * depth. + * + * 19 11/26/96 6:50p John + * Added some more hicolor primitives. Made windowed mode run as current + * bpp, if bpp is 8,16,or 32. + * + * 18 11/07/96 6:19p John + * Added a bunch of 16bpp primitives so the game sort of runs in 16bpp + * mode. + * + * 17 11/07/96 3:08p John + * Inlined more Tmapper functions in preparation for cleaning up the Tmap1 + * interface to the assembly. + * + * 16 11/07/96 2:17p John + * Took out the OldTmapper stuff. + * + * 15 11/07/96 12:04p John + * Sped up outer loop by 35% by inlining the incrementing of the variables + * for each scanline step and inlined the calculation for deltas at the + * start of each scanline. + * + * 14 11/06/96 2:33p John + * Added more asserts for checking that non-tiled UV's are between 0 and + * 1.0. Put code in the model_init code that checks for polys that have + * a vertex duplicated and throws them out. + * + * 13 11/05/96 4:05p John + * Added roller. Added code to draw a distant planet. Made bm_load + * return -1 if invalid bitmap. + * + * 12 10/31/96 7:20p John + * Added per,tiled tmapper. Made models tile if they use 64x64 textures. + * + * 11 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#include +#include +#include +#ifndef PLAT_UNIX +#include +#endif +#include + +#include "2d.h" +#include "grinternal.h" +#include "3d.h" +#include "tmapper.h" +#include "bmpman.h" +#include "tmapscanline.h" +#include "key.h" +#include "floating.h" +#include "palman.h" + +typedef void (* pscanline)(); + +pscanline tmap_scanline; + +int Tmap_screen_flags = -1; +int Tmap_npolys=0; +int Tmap_nverts=0; +int Tmap_nscanlines=0; +int Tmap_npixels=0; +tmapper_data Tmap; +int Tmap_show_layers=0; + +typedef struct tmap_scan_desc { + uint flags; + pscanline scan_func; +} tmap_scan_desc; + +// Convert from a 0-255 byte to a 0-1.0 float. +float Light_table[256]; + + +//====================== 8-BPP SCANLINES ======================== +tmap_scan_desc tmap_scanlines8[] = { + { 0, tmapscan_flat8 }, + { TMAP_FLAG_TEXTURED, tmapscan_lnn8 }, + { TMAP_FLAG_TEXTURED|TMAP_FLAG_XPARENT, tmapscan_lnt8 }, + { TMAP_FLAG_TEXTURED|TMAP_FLAG_RAMP, tmapscan_lln8 }, + { TMAP_FLAG_TEXTURED|TMAP_FLAG_RAMP|TMAP_FLAG_CORRECT, tmapscan_pln8 }, + + { TMAP_FLAG_RAMP|TMAP_FLAG_GOURAUD, tmapscan_flat_gouraud }, + + { TMAP_FLAG_TEXTURED|TMAP_FLAG_RAMP|TMAP_FLAG_GOURAUD, tmapscan_lln8 }, + { TMAP_FLAG_TEXTURED|TMAP_FLAG_RAMP|TMAP_FLAG_GOURAUD|TMAP_FLAG_CORRECT, tmapscan_pln8 }, + + { TMAP_FLAG_TEXTURED|TMAP_FLAG_RAMP|TMAP_FLAG_CORRECT|TMAP_FLAG_TILED, tmapscan_pln8_tiled }, + { TMAP_FLAG_TEXTURED|TMAP_FLAG_RAMP|TMAP_FLAG_CORRECT|TMAP_FLAG_GOURAUD|TMAP_FLAG_TILED, tmapscan_pln8_tiled }, + + { TMAP_FLAG_RAMP|TMAP_FLAG_GOURAUD|TMAP_FLAG_NEBULA, tmapscan_nebula8 }, + +// { TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED, tmapscan_lnn8_tiled_256x256 }, + // Totally non-general specific inner loop for subspace effect + { TMAP_FLAG_TEXTURED|TMAP_FLAG_CORRECT|TMAP_FLAG_TILED, tmapscan_pnn8_tiled_256x256_subspace }, + + + { 0, NULL }, // Dummy element to mark the end of fast scanlines. +}; + + +pscanline tmap_scanline_table[TMAP_MAX_SCANLINES]; + + +// ------------------------------------------------------------------------------------- +// This sets up the tmapper at the start of a given frame, so everything +// can can be pre-calculated should be calculated in here. +// This just fills in the tmap_scanline_table for the +// appropriate scan lines. + +void tmapper_setup() +{ + int i; + tmap_scan_desc * func_table = NULL; + + Tmap_screen_flags = gr_screen.mode; + + // Some constants for the inner loop + Tmap.FixedScale = 65536.0f; + Tmap.FixedScale8 = 2048.0f; //8192.0f; // 2^16 / 8 + Tmap.One = 1.0f; + + // Set tmap_scanline to not call a function + for (i=0; iscan_func != NULL) { + tmap_scanline_table[func_table->flags] = func_table->scan_func; + func_table++; + } + + for (i=0; i<256; i++ ) { + Light_table[i] = i2fl(i)/255.0f; + } + + +} + +// Sets up flat-shaded lighting +void tmapper_set_light(vertex *v, uint flags) +{ + if ( flags & TMAP_FLAG_GOURAUD ) return; + + if ( (flags & (TMAP_FLAG_RAMP|TMAP_FLAG_RGB))==(TMAP_FLAG_RAMP|TMAP_FLAG_RGB)) { + Int3(); // You're doing RGB and RAMP lighting!!! + } + + if ( flags & TMAP_FLAG_RAMP ) { + Tmap.l.b = Tmap.r.b = i2fl(v->b)/255.0f; + Tmap.deltas.b = 0.0f; + } +} + +void tmapper_show_layers() +{ + int i; + ubyte * ptr = (ubyte *)Tmap.dest_row_data; + + for (i=0; i (int)gr_zbuffer[tmp] ) { + gr_zbuffer[tmp] = Tmap.fx_w; + + ui = fl2i( u / w ) % Tmap.bp->w; + vi = fl2i( v / w ) % Tmap.bp->h; + + c = Tmap.pixptr[vi*Tmap.bp->w+ui]; + *dptr = gr_fade_table[fl2i(l*31)*256+c]; + + } + Tmap.fx_w += Tmap.fx_dwdx; + l+=dl; + u+=du; + v+=dv; + w+=dw; + dptr++; + } +} +*/ + + +// Same as ftol except that it might round up or down, +// unlike C's ftol, which must always round down. +// But, in the tmapper, we don't care, since this is +// just for Z and L. +inline int tmap_ftol(float f) +{ + int x; +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm fld f + _asm fistp x +#endif + return x; +} + +/* +#define tmap_ftol(f) ((int)(f)) + +__ftol: +004569c0 push ebp +004569c1 mov ebp,esp +004569c3 add esp,fffffff4 +004569c6 wait +004569c7 fnstcw [ebp-02] +004569ca wait +004569cb mov ax,word ptr [ebp-02] +004569cf or ah,0c +004569d2 mov word ptr [ebp-04],ax +004569d6 fldcw [ebp-04] +004569d9 fistp qword ptr [ebp-0c] +004569dc fldcw [ebp-02] +004569df mov eax,dword ptr [ebp-0c] +004569e2 mov edx,dword ptr [ebp-08] +004569e5 leave +004569e6 ret +*/ + +#ifdef RGB_LIGHTING + +extern ubyte gr_palette[768]; + +uint last_code = 0xf; +void change_fade_table(uint code) +{ + int i,l; + + if ( last_code == code ) return; + last_code = code; + + int r1=0, g1=0, b1=0; + + for (i=0; i<256; i++ ) { + int r, g, b; + int ur, ug, ub; + r = gr_palette[i*3+0]; + g = gr_palette[i*3+1]; + b = gr_palette[i*3+2]; + + if ( (r == 255) && (g == 255) && (b == 255) ) { + // Make pure white not fade + for (l=0; l<32; l++ ) { + gr_fade_table[((l+1)*256)+i] = (unsigned char)i; + } + } else { + for (l=24; l<32; l++ ) { + + int x,y; + int gi, gr, gg, gb; + + gi = (r+g+b)/3; + + //gr = r*2; + //gg = g*2; + //gb = b*2; + + if ( code & 4 ) gr = gi*2; else gr = r; + if ( code & 2 ) gg = gi*2; else gg = g; + if ( code & 1 ) gb = gi*2; else gb = b; + +// gr = r1; +// gg = g1; +// gb = b1; //gi*2; + + x = l-24; // x goes from 0 to 7 + y = 31-l; // y goes from 7 to 0 + + ur = ((gr*x)+(r*y))/7; if ( ur > 255 ) ur = 255; + ug = ((gg*x)+(g*y))/7; if ( ug > 255 ) ug = 255; + ub = ((gb*x)+(b*y))/7; if ( ub > 255 ) ub = 255; + + gr_fade_table[((l+1)*256)+i] = (unsigned char)palette_find( ur, ug, ub ); + + } + } + gr_fade_table[ (0*256)+i ] = gr_fade_table[ (1*256)+i ]; + gr_fade_table[ (33*256)+i ] = gr_fade_table[ (32*256)+i ]; + } + + // Mirror the fade table + for (i=0; i<34; i++ ) { + for ( l = 0; l < 256; l++ ) { + gr_fade_table[ ((67-i)*256)+l ] = gr_fade_table[ (i*256)+l ]; + } + } + +} +#endif + + +void grx_tmapper( int nverts, vertex **verts, uint flags ) +{ + int i, y, li, ri, ly, ry, top, rem; + float ymin; + int next_break; + float ulist[TMAP_MAX_VERTS], vlist[TMAP_MAX_VERTS], llist[TMAP_MAX_VERTS]; + + flags &= (~TMAP_FLAG_ALPHA); + flags &= (~TMAP_FLAG_NONDARKENING); + flags &= (~TMAP_FLAG_PIXEL_FOG); + + // Check for invalid flags +#ifndef NDEBUG + if ( (flags & (TMAP_FLAG_RAMP|TMAP_FLAG_RGB))==(TMAP_FLAG_RAMP|TMAP_FLAG_RGB)) { + Int3(); // You're doing RGB and RAMP lighting!!! + } + + if ( flags & TMAP_FLAG_RGB ) { + Int3(); // RGB not supported! + } + + if ( (flags & TMAP_FLAG_GOURAUD) && (!(flags & TMAP_FLAG_RAMP)) ) { + Int3(); // Ramp mode required for gouraud! + } + + if ( gr_screen.bits_per_pixel != 8 ) { + Int3(); // Only 8-bpp tmapper supported + } + + Tmap_npolys++; + Tmap_nverts += nverts; + + Assert(nverts <= TMAP_MAX_VERTS ); + +#endif + + if ( flags & (TMAP_FLAG_RAMP|TMAP_FLAG_GOURAUD) ) { + for (i=0; ib]; + } + } + + if ( Tmap_screen_flags != gr_screen.mode ) { + tmapper_setup(); + } + + tmap_scanline = tmap_scanline_table[flags]; +// tmap_scanline = tmap_scan_generic; + +#ifndef NDEBUG + Assert( tmap_scanline != NULL ); + + if (Tmap_show_layers) + tmap_scanline = tmapper_show_layers; +#endif + + if ( tmap_scanline == NULL ) return; + + Tmap.FadeLookup = (uint)palette_get_fade_table(); + Tmap.BlendLookup = (uint)palette_get_blend_table(gr_screen.current_alpha); + + if ( flags & TMAP_FLAG_TEXTURED ) { + + Tmap.bp = bm_lock( gr_screen.current_bitmap, 8, 0 ); + + int was_tiled = 0, can_tile = 0; + if ( flags & TMAP_FLAG_TILED ) { + if ( (Tmap.bp->w==16) && (Tmap.bp->h==16) ) can_tile = 1; + if ( (Tmap.bp->w==32) && (Tmap.bp->h==32) ) can_tile = 1; + if ( (Tmap.bp->w==64) && (Tmap.bp->h==64) ) can_tile = 1; + if ( (Tmap.bp->w==128) && (Tmap.bp->h==128) ) can_tile = 1; + if ( (Tmap.bp->w==256) && (Tmap.bp->h==256) ) can_tile = 1; + + if ( !can_tile ) { + was_tiled = 1; + flags &= (~TMAP_FLAG_TILED); + } + } + + float max_u = i2fl(Tmap.bp->w) - 0.5f; + float max_v = i2fl(Tmap.bp->h) - 0.5f; + + for (i=0; iu * Tmap.bp->w; + vlist[i] = verts[i]->v * Tmap.bp->h; + + if ( !(flags & TMAP_FLAG_TILED) ) { + if ( ulist[i] < 1.0f ) ulist[i] = 1.0f; + if ( vlist[i] < 1.0f ) vlist[i] = 1.0f; + if ( ulist[i] > max_u ) ulist[i] = max_u; + if ( vlist[i] > max_v ) vlist[i] = max_v; + } + + // Multiply all u,v's by sw for perspective correction + if ( flags & TMAP_FLAG_CORRECT ) { + ulist[i] *= verts[i]->sw; + vlist[i] *= verts[i]->sw; + } + } + + Tmap.pixptr = (unsigned char *)Tmap.bp->data; + Tmap.src_offset = Tmap.bp->rowsize; + } + + // Find the topmost vertex + //top = -1; // Initialize to dummy value to avoid compiler warning + //ymin = 0.0f; // Initialize to dummy value to avoid compiler warning + // Instead of initializing to avoid compiler warnings, set to first value outside loop and remove (i==0) + // comparison, which otherwise happens nverts times. MK, 3/20/98 (was tracing code figuring out my shield effect bug...) + ymin = verts[0]->sy; + top = 0; + for (i=1; isy < ymin) { + ymin = verts[i]->sy; + top = i; + } + } + + li = ri = top; + rem = nverts; + y = fl_round_2048(ymin); //(int)floor(ymin + 0.5); + ly = ry = y - 1; + + gr_lock(); + Tmap.pScreenBits = (uint)gr_screen.offscreen_buffer_base; + + while( rem > 0 ) { + while ( ly<=y && rem>0 ) { // Advance left edge? + float dy, frac, recip; + rem--; + i = li-1; + if ( i<0 ) i = nverts-1; + ly = fl_round_2048(verts[i]->sy); //(int)floor(verts[i]->sy+0.5); + + dy = verts[i]->sy - verts[li]->sy; + if ( dy == 0.0f ) dy = 1.0f; + + frac = y + 0.5f - verts[li]->sy; + recip = 1.0f / dy; + + Tmap.dl.sx = (verts[i]->sx - verts[li]->sx)*recip; + Tmap.l.sx = verts[li]->sx + Tmap.dl.sx*frac; + + if ( flags & TMAP_FLAG_TEXTURED ) { + Tmap.dl.u = (ulist[i] - ulist[li])*recip; + Tmap.l.u = ulist[li] + Tmap.dl.u*frac; + Tmap.dl.v = (vlist[i] - vlist[li])*recip; + Tmap.l.v = vlist[li] + Tmap.dl.v*frac; + } + + if ( (flags & TMAP_FLAG_CORRECT) || gr_zbuffering ) { + Tmap.dl.sw = (verts[i]->sw - verts[li]->sw)*recip; + Tmap.l.sw = verts[li]->sw + Tmap.dl.sw*frac; + } + + if ( flags & TMAP_FLAG_GOURAUD ) { + if ( flags & TMAP_FLAG_RAMP ) { + Tmap.dl.b = (llist[i] - llist[li])*recip; + Tmap.l.b = llist[li] + Tmap.dl.b*frac; + } + } + + li = i; + } + while ( ry<=y && rem>0 ) { // Advance right edge? + float dy, frac, recip; + rem--; + i = ri+1; + if ( i>=nverts ) i = 0; + ry = fl_round_2048(verts[i]->sy); //(int)floor(verts[i]->sy+0.5); + + dy = verts[i]->sy - verts[ri]->sy; + if ( dy == 0.0f ) dy = 1.0f; + + frac = y + 0.5f - verts[ri]->sy; + recip = 1.0f / dy; + + Tmap.dr.sx = (verts[i]->sx - verts[ri]->sx)*recip; + Tmap.r.sx = verts[ri]->sx + Tmap.dr.sx*frac; + + if ( flags & TMAP_FLAG_TEXTURED ) { + Tmap.dr.u = (ulist[i] - ulist[ri])*recip; + Tmap.r.u = ulist[ri] + Tmap.dr.u*frac; + Tmap.dr.v = (vlist[i] - vlist[ri])*recip; + Tmap.r.v = vlist[ri] + Tmap.dr.v*frac; + } + + if ( (flags & TMAP_FLAG_CORRECT) || gr_zbuffering ) { + Tmap.dr.sw = (verts[i]->sw - verts[ri]->sw)*recip; + Tmap.r.sw = verts[ri]->sw + Tmap.dr.sw*frac; + } + + if ( flags & TMAP_FLAG_GOURAUD ) { + if ( flags & TMAP_FLAG_RAMP ) { + Tmap.dr.b = (llist[i] - llist[ri])*recip; + Tmap.r.b = llist[ri] + Tmap.dr.b*frac; + } + } + + ri = i; + } + + if ( ly < ry ) + next_break = ly; + else + next_break = ry; + + for ( ; y= gr_screen.clip_top) && ( y<=gr_screen.clip_bottom) ) { + int lx, rx; + + + lx = fl_round_2048(Tmap.l.sx); + if ( lx < gr_screen.clip_left ) { + Tmap.clipped_left = i2fl(gr_screen.clip_left) - Tmap.l.sx; + lx = gr_screen.clip_left; + } else { + Tmap.clipped_left = 0.0f; + } + rx = fl_round_2048(Tmap.r.sx-1.0f); + + if ( rx > gr_screen.clip_right ) rx = gr_screen.clip_right; + if ( lx <= rx ) { + float dx, recip; //frac; + + dx = Tmap.r.sx - Tmap.l.sx; + if ( dx == 0.0f ) dx = 1.0f; + + //frac = lx + 0.5f - Tmap.l.sx; + recip = 1.0f / dx; + + Tmap.y = y; + Tmap.rx = rx; + Tmap.lx = lx; + Tmap.loop_count = rx - lx + 1; + #ifndef NDEBUG + Tmap_npixels += Tmap.loop_count; + Tmap_nscanlines++; + #endif + + if ( (flags & TMAP_FLAG_CORRECT) || gr_zbuffering ) { + Tmap.deltas.sw = (Tmap.r.sw - Tmap.l.sw)*recip; + Tmap.fl_dwdx_wide = Tmap.deltas.sw*32.0f; + } + + if ( flags & TMAP_FLAG_TEXTURED ) { + Tmap.deltas.u = (Tmap.r.u - Tmap.l.u)*recip; + Tmap.deltas.v = (Tmap.r.v - Tmap.l.v)*recip; + + if ( flags & TMAP_FLAG_CORRECT ) { + Tmap.fl_dudx_wide = Tmap.deltas.u*32.0f; + Tmap.fl_dvdx_wide = Tmap.deltas.v*32.0f; + } else { + Tmap.fx_u = tmap_ftol((Tmap.l.u+Tmap.clipped_left*Tmap.deltas.u)*65536.0f); + Tmap.fx_v = tmap_ftol((Tmap.l.v+Tmap.clipped_left*Tmap.deltas.v)*65536.0f); + Tmap.fx_du_dx = tmap_ftol(Tmap.deltas.u*65536.0f); + Tmap.fx_dv_dx = tmap_ftol(Tmap.deltas.v*65536.0f); + } + } + + if ( flags & TMAP_FLAG_GOURAUD ) { + if ( flags & TMAP_FLAG_RAMP ) { + Tmap.deltas.b = (Tmap.r.b - Tmap.l.b)*recip; + + Tmap.fx_l = tmap_ftol(Tmap.l.b*32.0f*65536.0f); + Tmap.fx_l_right = tmap_ftol(Tmap.r.b*32.0f*65536.0f); + Tmap.fx_dl_dx = tmap_ftol(Tmap.deltas.b*32.0f*65536.0f); + + if ( Tmap.fx_dl_dx < 0 ) { + Tmap.fx_dl_dx = -Tmap.fx_dl_dx; + Tmap.fx_l = (67*F1_0)-Tmap.fx_l; + Tmap.fx_l_right = (67*F1_0)-Tmap.fx_l_right; + // Assert( Tmap.fx_l > 31*F1_0 ); + // Assert( Tmap.fx_l < 66*F1_0 ); + // Assert( Tmap.fx_dl_dx >= 0 ); + // Assert( Tmap.fx_dl_dx < 31*F1_0 ); + } + } + } + + if ( gr_zbuffering ) { + Tmap.fx_w = tmap_ftol(Tmap.l.sw * GR_Z_RANGE)+gr_zoffset; + Tmap.fx_dwdx = tmap_ftol(Tmap.deltas.sw * GR_Z_RANGE); + } + + Tmap.dest_row_data = GR_SCREEN_PTR_SIZE(gr_screen.bytes_per_pixel,Tmap.lx,Tmap.y); + + (*tmap_scanline)(); + + } + + } + + Tmap.l.sx += Tmap.dl.sx; + Tmap.r.sx += Tmap.dr.sx; + + if ( flags & TMAP_FLAG_TEXTURED ) { + Tmap.l.u += Tmap.dl.u; + Tmap.l.v += Tmap.dl.v; + + Tmap.r.u += Tmap.dr.u; + Tmap.r.v += Tmap.dr.v; + } + + if ( (flags & TMAP_FLAG_CORRECT) || gr_zbuffering ) { + Tmap.l.sw += Tmap.dl.sw; + Tmap.r.sw += Tmap.dr.sw; + } + + if ( flags & TMAP_FLAG_GOURAUD ) { + if ( flags & TMAP_FLAG_RAMP ) { + Tmap.l.b += Tmap.dl.b; + Tmap.r.b += Tmap.dr.b; + } + } + } + } + + gr_unlock(); + + if ( flags & TMAP_FLAG_TEXTURED ) { + bm_unlock(gr_screen.current_bitmap); + } + + +} + + + diff --git a/src/graphics/tmapscanline.cpp b/src/graphics/tmapscanline.cpp new file mode 100644 index 0000000..9d53457 --- /dev/null +++ b/src/graphics/tmapscanline.cpp @@ -0,0 +1,4574 @@ +/* + * $Logfile: /Freespace2/code/Graphics/TmapScanline.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to draw one textured mapped scanline. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 5 12/02/98 5:47p Dave + * Put in interface xstr code. Converted barracks screen to new format. + * + * 4 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 21 4/20/98 4:44p John + * Fixed problems with black being xparent on model cache rneders. Made + * model cache key off of detail level setting and framerate. + * + * 20 4/09/98 7:58p John + * Cleaned up tmapper code a bit. Put NDEBUG around some ndebug stuff. + * Took out XPARENT flag settings in all the alpha-blended texture stuff. + * + * 19 3/22/98 2:33p John + * Took out fx_v/v_right. Made fx_u etc get calculated in tmapper. + * + * 18 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 17 12/10/96 10:37a John + * Restructured texture mapper to remove some overhead from each scanline + * setup. This gave about a 30% improvement drawing trans01.pof, which is + * a really complex model. In the process, I cleaned up the scanline + * functions and separated them into different modules for each pixel + * depth. + * + * 16 12/02/96 4:03p John + * made texture divide pipeline better. 2.5% speedup. + * + * 15 11/26/96 6:50p John + * Added some more hicolor primitives. Made windowed mode run as current + * bpp, if bpp is 8,16,or 32. + * + * 14 11/18/96 9:58a John + * Fixed warnings + * + * 13 11/07/96 6:19p John + * Added a bunch of 16bpp primitives so the game sort of runs in 16bpp + * mode. + * + * 12 11/07/96 3:49p John + * Fixed some old 'c' inner loop code for testing. + * + * 11 11/07/96 2:17p John + * Took out the OldTmapper stuff. + * + * 10 11/05/96 4:05p John + * Added roller. Added code to draw a distant planet. Made bm_load + * return -1 if invalid bitmap. + * + * 9 10/31/96 7:20p John + * Added per,tiled tmapper. Made models tile if they use 64x64 textures. + * + * 8 10/26/96 1:40p John + * Added some now primitives to the 2d library and + * cleaned up some old ones. + * + * $NoKeywords: $ + */ + +#include "3d.h" +#include "2d.h" +#include "grinternal.h" +#include "tmapper.h" +#include "tmapscanline.h" +#include "floating.h" +#include "palman.h" +#include "fix.h" +#include "key.h" + +// Needed to keep warning 4725 to stay away. See PsTypes.h for details why. +void disable_warning_4725_stub_ts32() +{ +} + + +extern void tmapscan_pln8_tiled_256x256(); +extern void tmapscan_pln8_tiled_128x128(); +extern void tmapscan_pln8_tiled_64x64(); +extern void tmapscan_pln8_tiled_32x32(); +extern void tmapscan_pln8_tiled_16x16(); + + +void tmapscan_pln8_tiled() +{ + if ( (Tmap.bp->w == 256) && (Tmap.bp->h == 256) ) { + tmapscan_pln8_tiled_256x256(); + } else if ( (Tmap.bp->w == 128) && (Tmap.bp->h == 128) ) { + tmapscan_pln8_tiled_128x128(); + } else if ( (Tmap.bp->w == 64) && (Tmap.bp->h == 64) ) { + tmapscan_pln8_tiled_64x64(); + } else if ( (Tmap.bp->w == 32) && (Tmap.bp->h == 32) ) { + tmapscan_pln8_tiled_32x32(); + } else if ( (Tmap.bp->w == 16) && (Tmap.bp->h == 16) ) { + tmapscan_pln8_tiled_16x16(); + } else { + // argh! write another texure mapper! + tmapscan_pln8(); + } +} + + +void tmapscan_write_z() +{ + int i; + ubyte * dptr; + uint w, dw; + + dptr = (ubyte *)Tmap.dest_row_data; + + w = Tmap.fx_w; + dw = Tmap.fx_dwdx; + + uint *zbuf = (uint *)&gr_zbuffer[(uint)dptr-(uint)Tmap.pScreenBits]; + + for (i=0; i *zbuf ) { + *zbuf = w; + *dptr = gr_fade_table[(f2i(l)<<8)+c]; + } + zbuf++; + w += dw; + l+=dl; + dptr++; + } +} + +// ADAM: Change Nebula colors here: +#define NEBULA_COLORS 20 + +void tmapscan_nebula8() +{ + ubyte * dptr; + int l1,l2, dldx; + + dptr = (ubyte *)Tmap.dest_row_data; + + float max_neb_color = i2fl(NEBULA_COLORS-1); + + l1 = (int)(Tmap.l.b*max_neb_color*256.0f); + l2 = l1 + 256/2; // dithering + dldx = (int)(Tmap.deltas.b*max_neb_color*2.0f*256.0f); + + #ifdef USE_INLINE_ASM +// memset( dptr, 31, Tmap.loop_count ); +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm push eax + _asm push ebx + _asm push ecx + _asm push edx + _asm push edi + + // eax - l1 + // ebx - l2 + // ecx - count + // edx - dldx + // edi - dest + _asm mov eax, l1 + _asm mov ebx, l2 + _asm mov edx, dldx + _asm mov edi, dptr + + _asm mov ecx, Tmap.loop_count + _asm shr ecx, 1 + _asm jz DoFinal + _asm pushf + + Next2Pixels: + _asm mov [edi], ah + _asm add eax, edx + + _asm mov [edi+1], bh + _asm add ebx, edx + + _asm add edi, 2 + _asm dec ecx + _asm jnz Next2Pixels + + _asm popf + DoFinal: + _asm jnc NotDoFinal + _asm mov [edi], ah + NotDoFinal: + + _asm pop edi + _asm pop edx + _asm pop ecx + _asm pop ebx + _asm pop eax +#endif + + #else + int i; + if ( Tmap.loop_count > 1 ) { + for (i=0; i>8); + l1+=dldx; + dptr[1] = (ubyte)((l2&0xFF00)>>8); + l2+=dldx; + dptr+=2; + } + } + if ( Tmap.loop_count & 1 ) { + dptr[0] = (ubyte)((l1&0xFF00)>>8); + dptr++; + } + #endif +} + + +void tmapscan_flat_gouraud() +{ + if (gr_zbuffering) { + switch(gr_zbuffering_mode) { + case GR_ZBUFF_NONE: + break; + case GR_ZBUFF_FULL: // both + tmapscan_flat_gouraud_zbuffered(); + return; + case GR_ZBUFF_WRITE: // write only + tmapscan_flat_gouraud_zbuffered(); + break; + case GR_ZBUFF_READ: // read only + tmapscan_flat_gouraud_zbuffered(); + return; + } + } + + /* HARDWARE_ONLY + if ( Current_alphacolor ) { + ubyte *lookup = &Current_alphacolor->table.lookup[0][0]; + + int i; + ubyte * dptr; + fix l, dl; + + dptr = (ubyte *)Tmap.dest_row_data; + + l = Tmap.fx_l; + dl = Tmap.fx_dl_dx; + + for (i=0; i (int)gr_zbuffer[tmp] ) { + gr_zbuffer[tmp] = Tmap.fx_w; + *dptr = c; + } + Tmap.fx_w += Tmap.fx_dwdx; + dptr++; + } +} + +void tmapscan_flat8() +{ + if (gr_zbuffering) { + switch(gr_zbuffering_mode) { + case GR_ZBUFF_NONE: + break; + case GR_ZBUFF_FULL: // both + tmapscan_flat8_zbuffered(); + return; + case GR_ZBUFF_WRITE: // write only + tmapscan_write_z(); + break; + case GR_ZBUFF_READ: // read only + tmapscan_flat8_zbuffered(); + return; + } + } + + memset( (ubyte *)Tmap.dest_row_data, gr_screen.current_color.raw8, Tmap.loop_count ); +} + +void tmapscan_pln8_zbuffered(); + + +void tmapscan_pln8_ppro() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + mov edx,Tmap.DeltaUFrac // get register copy + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + xor eax, eax + mov al,[edi] // preread the destination cache line + xor eax, eax + mov al,[esi] // get texture pixel 0 + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + +InnerInnerLoop: + + push ebx + and ebx, 0ff00h + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Get shaded pixel + pop ebx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+0],al // store pixel + xor eax, eax + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + push ebx + and ebx, 0ff00h + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Get shaded pixel + pop ebx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+1],al // store pixel + xor eax, eax + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + push ebx + and ebx, 0ff00h + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Get shaded pixel + pop ebx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+2],al // store pixel + xor eax, eax + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + push ebx + and ebx, 0ff00h + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Get shaded pixel + pop ebx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+3],al // store pixel + xor eax, eax + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + add edi, 4 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, do not do deltas + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov edx, Tmap.DeltaUFrac + + cmp Tmap.WidthModLength, 1 + jle NoDeltaLight + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov dx, ax + + pop ebx + +NoDeltaLight: + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + mov al,[esi] + +NextPixel: + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+0],al // store pixel + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+1],al // store pixel + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + add edi, 2 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + + mov al,[esi] // get texture pixel 2 + mov ah, bh + mov al, gr_fade_table[eax] + mov [edi],al // store pixel 2 + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + + +void tmapscan_pln8_pentium() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + mov edx,Tmap.DeltaUFrac // get register copy + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + + mov al,[edi] // preread the destination cache line + + mov al,[esi] // get texture pixel 0 + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + +InnerInnerLoop: + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + + + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+0],al // store pixel + + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + + + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+1],al // store pixel + + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + + + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+2],al // store pixel + + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + + + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+3],al // store pixel + + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + add edi, 4 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, do not do deltas + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov edx, Tmap.DeltaUFrac + + cmp Tmap.WidthModLength, 1 + jle NoDeltaLight + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov dx, ax + + pop ebx + +NoDeltaLight: + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + mov al,[esi] + +NextPixel: + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+0],al // store pixel + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+1],al // store pixel + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + add edi, 2 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + + mov al,[esi] // get texture pixel 2 + mov ah, bh + mov al, gr_fade_table[eax] + mov [edi],al // store pixel 2 + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + + + + +void tmapscan_pln8() +{ + if (gr_zbuffering) { + switch(gr_zbuffering_mode) { + case GR_ZBUFF_NONE: + break; + case GR_ZBUFF_FULL: // both + tmapscan_pln8_zbuffered(); + return; + case GR_ZBUFF_WRITE: // write only + tmapscan_write_z(); + break; + case GR_ZBUFF_READ: // read only + tmapscan_pln8_zbuffered(); + return; + } + + } + + if ( Gr_cpu > 5 ) { + tmapscan_pln8_ppro(); + } else { + tmapscan_pln8_pentium(); + } +} + + +void tmapscan_lln8() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap.dest_row_data + + mov edx, Tmap.DeltaUFrac + + mov eax, Tmap.loop_count + shr eax, 2 + je DoLeftOverPixels + + mov Tmap.num_big_steps, eax + and Tmap.loop_count, 3 + + mov al,[edi] // preread the destination cache line + mov al,[esi] // get texture pixel 0 + +NextPixelBlock: + + push eax + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 2 //*4 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 2 + + mov dx, bp + pop eax + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+0],al // store pixel + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+1],al // store pixel + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+2],al // store pixel + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+3],al // store pixel + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + add edi, 4 + dec Tmap.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov ebp, Tmap.fx_l + shr ebp, 8 + mov bx, bp + + mov ebp, Tmap.fx_dl_dx + shr ebp, 8 + mov dx, bp + + mov eax,Tmap.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap.loop_count, eax + pushf + + xor eax, eax + + mov al, [edi] // preread the destination cache line + mov al, [esi] // Get first texel + +NextPixel: + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+0],al // store pixel + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + mov ah, bh // move lighting value into place + mov al, gr_fade_table[eax] // Get shaded pixel + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + mov [edi+1],al // store pixel + mov al,[esi] // get texture pixel + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + + add edi, 2 + dec Tmap.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + xor eax, eax + mov al, [esi] // Get first texel + mov ah, bh + mov al, gr_fade_table[eax] + mov [edi],al // store pixel 2 + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + + +void tmapscan_lna8_zbuffered_ppro() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap.dest_row_data + + xor eax, eax + mov al,[edi] // get the destination pixel + + mov ebp, Tmap.fx_w + mov edx, gr_zbuffer + mov eax, edi + sub eax, Tmap.pScreenBits + shl eax, 2 + add edx, eax + + mov eax, Tmap.loop_count + + shr eax, 2 + je DoLeftOverPixels + + mov Tmap.num_big_steps, eax + and Tmap.loop_count, 3 + +NextPixelBlock: + + // 8 pixel span code + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = zbuffer pointer + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // ebp = zvalue + // esp = stack + + cmp ebp, [edx+4*0] // Compare the Z depth of this pixel with zbuffer + jle Skip0a // If pixel is covered, skip drawing +// mov [edx+4*0], ebp // Write new Z value + + // Get pixel and blend it + push ebx + xor ebx, ebx + mov bl, [edi+0] + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov ah, [esi] // Get texel into AL + add eax, Tmap.BlendLookup + mov eax, [eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+0],al // store pixel +Skip0a: + add ebp,Tmap.fx_dwdx // increment z value + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + + cmp ebp, [edx+4*1] // Compare the Z depth of this pixel with zbuffer + jle Skip1a // If pixel is covered, skip drawing +// mov [edx+4*1], ebp // Write new Z value + + // Get pixel and blend it + push ebx + xor ebx, ebx + mov bl, [edi+1] + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov ah, [esi] // Get texel into AL + add eax, Tmap.BlendLookup + mov eax, [eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+1],al // store pixel +Skip1a: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + cmp ebp, [edx+4*2] // Compare the Z depth of this pixel with zbuffer + jle Skip2a // If pixel is covered, skip drawing +// mov [edx+4*2], ebp // Write new Z value + + push ebx + xor ebx, ebx + mov bl, [edi+2] + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov ah, [esi] // Get texel into AL + add eax, Tmap.BlendLookup + mov eax, [eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+2],al // store pixel +Skip2a: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + cmp ebp, [edx+4*3] // Compare the Z depth of this pixel with zbuffer + jle Skip3a // If pixel is covered, skip drawing +// mov [edx+4*3], ebp // Write new Z value + + push ebx + xor ebx, ebx + mov bl, [edi+3] + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov ah, [esi] // Get texel into AL + add eax, Tmap.BlendLookup + mov eax, [eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+3],al // store pixel +Skip3a: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + add edx, 16 + add edi, 4 + dec Tmap.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov eax,Tmap.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap.loop_count, eax + pushf + + xor eax, eax + mov al,[edi] // get the destination pixel + +NextPixel: + + cmp ebp, [edx+4*0] // Compare the Z depth of this pixel with zbuffer + jle Skip0b // If pixel is covered, skip drawing +// mov [edx+4*0], ebp // Write new Z value + mov al,[edi+0] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi+0],al // store pixel +Skip0b: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + cmp ebp, [edx+4*1] // Compare the Z depth of this pixel with zbuffer + jle Skip1b // If pixel is covered, skip drawing +// mov [edx+4*1], ebp // Write new Z value + mov al,[edi+1] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi+1],al // store pixel +Skip1b: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + add edi, 2 + add edx, 8 + dec Tmap.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + cmp ebp, [edx] // Compare the Z depth of this pixel with zbuffer + jle Skip0c // If pixel is covered, skip drawing +// mov [edx], ebp // Write new Z value + mov al,[edi] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi],al // store pixel +Skip0c: + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + +void tmapscan_lna8_zbuffered_pentium() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap.dest_row_data + + xor eax, eax + mov al,[edi] // get the destination pixel + + mov ebp, Tmap.fx_w + mov edx, gr_zbuffer + mov eax, edi + sub eax, Tmap.pScreenBits + shl eax, 2 + add edx, eax + + mov eax, Tmap.loop_count + + shr eax, 2 + je DoLeftOverPixels + + mov Tmap.num_big_steps, eax + and Tmap.loop_count, 3 + +NextPixelBlock: + + // 8 pixel span code + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = zbuffer pointer + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // ebp = zvalue + // esp = stack + + cmp ebp, [edx+4*0] // Compare the Z depth of this pixel with zbuffer + jle Skip0a // If pixel is covered, skip drawing +// mov [edx+4*0], ebp // Write new Z value + mov al,[edi+0] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + + + + + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + + + mov [edi+0],al // store pixel +Skip0a: + add ebp,Tmap.fx_dwdx // increment z value + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + + cmp ebp, [edx+4*1] // Compare the Z depth of this pixel with zbuffer + jle Skip1a // If pixel is covered, skip drawing +// mov [edx+4*1], ebp // Write new Z value + mov al,[edi+1] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + + + + + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + + + mov [edi+1],al // store pixel +Skip1a: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + cmp ebp, [edx+4*2] // Compare the Z depth of this pixel with zbuffer + jle Skip2a // If pixel is covered, skip drawing +// mov [edx+4*2], ebp // Write new Z value + mov al,[edi+2] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + + + + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + + + mov [edi+2],al // store pixel +Skip2a: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + cmp ebp, [edx+4*3] // Compare the Z depth of this pixel with zbuffer + jle Skip3a // If pixel is covered, skip drawing +// mov [edx+4*3], ebp // Write new Z value + mov al,[edi+3] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + + + + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + + + mov [edi+3],al // store pixel +Skip3a: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + add edx, 16 + add edi, 4 + dec Tmap.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov eax,Tmap.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap.loop_count, eax + pushf + + xor eax, eax + mov al,[edi] // get the destination pixel + +NextPixel: + + cmp ebp, [edx+4*0] // Compare the Z depth of this pixel with zbuffer + jle Skip0b // If pixel is covered, skip drawing +// mov [edx+4*0], ebp // Write new Z value + mov al,[edi+0] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi+0],al // store pixel +Skip0b: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + cmp ebp, [edx+4*1] // Compare the Z depth of this pixel with zbuffer + jle Skip1b // If pixel is covered, skip drawing +// mov [edx+4*1], ebp // Write new Z value + mov al,[edi+1] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi+1],al // store pixel +Skip1b: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + add edi, 2 + add edx, 8 + dec Tmap.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + cmp ebp, [edx] // Compare the Z depth of this pixel with zbuffer + jle Skip0c // If pixel is covered, skip drawing +// mov [edx], ebp // Write new Z value + mov al,[edi] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi],al // store pixel +Skip0c: + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + +void tmapscan_lna8_zbuffered() +{ + if ( Gr_cpu > 5 ) { + tmapscan_lna8_zbuffered_ppro(); + } else { + tmapscan_lna8_zbuffered_pentium(); + } +} + + + +extern float Tmap_clipped_left; + +void tmapscan_lna8() +{ + if (gr_zbuffering) { + switch(gr_zbuffering_mode) { + case GR_ZBUFF_NONE: + break; + case GR_ZBUFF_FULL: // both + case GR_ZBUFF_WRITE: // write only + case GR_ZBUFF_READ: // read only + tmapscan_lna8_zbuffered(); + return; + } + + } + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap.dest_row_data + + mov edx, Tmap.DeltaUFrac + + xor eax, eax + mov al,[edi] // get the destination pixel + + mov ebp, Tmap.loop_count + + shr ebp, 2 + je DoLeftOverPixels + + mov Tmap.num_big_steps, ebp + and Tmap.loop_count, 3 + + mov eax, 0 + +NextPixelBlock: + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + + xor eax, eax + mov al,[edi+0] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi+0],al // store pixel + + xor eax, eax + mov al,[edi+1] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi+1],al // store pixel + + xor eax, eax + mov al,[edi+2] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi+2],al // store pixel + + xor eax, eax + mov al,[edi+3] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi+3],al // store pixel + + add edi, 4 + dec Tmap.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov ebp,Tmap.loop_count + test ebp, -1 + jz _none_to_do + shr ebp, 1 + je one_more_pix + mov Tmap.loop_count, ebp + pushf + + mov al,[edi] // get the destination pixel + +NextPixel: + + xor eax, eax + mov al,[edi+0] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi+0],al // store pixel + + xor eax, eax + mov al,[edi+1] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi+1],al // store pixel + + add edi, 2 + dec Tmap.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + mov eax, 0 + mov al,[edi] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add eax, Tmap.BlendLookup + mov al, [eax] // blend them + mov [edi],al // store pixel + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + +// HACKED IN SYSTEM FOR DOING MODEL CACHING +int Tmap_scan_read = 0; // 0 = normal mapper, 1=read, 2=write + +// HACKED IN SYSTEM FOR DOING MODEL CACHING +void tmapscan_lnn8_read() +{ + Tmap.fx_u = fl2f(Tmap.l.u); + Tmap.fx_v = fl2f(Tmap.l.v); + Tmap.fx_du_dx = fl2f(Tmap.deltas.u); + Tmap.fx_dv_dx = fl2f(Tmap.deltas.v); + +/* + int i; + + ubyte * src = (ubyte *)Tmap.pixptr; + ubyte * dst = (ubyte *)Tmap.dest_row_data; + + for (i=0; i// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov edx, Tmap.DeltaUFrac + mov dx, bp + mov Tmap.DeltaUFrac, edx + + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + mov al,[esi] // get texture pixel 0 + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov ebp, Tmap.fx_w + + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + +InnerInnerLoop: + + // Pixel 0 + cmp ebp, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0 // If pixel is covered, skip drawing + mov [edx+0], ebp // Write new Z value + + // Get pixel and light it + push ebx + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov al, [esi] // Get texel into AL + and ebx, 0ff00h // Clear out fractional part of EBX + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+0],al // Write new pixel + +Skip0: add ecx,Tmap.DeltaVFrac + sbb eax,eax + add ebp,Tmap.fx_dwdx + add ebx,Tmap.DeltaUFrac + adc esi,Tmap.uv_delta[4*eax+4] + + + // Pixel 1 + cmp ebp, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1 // If pixel is covered, skip drawing + mov [edx+4], ebp // Write new Z value + + // Get pixel and light it + push ebx + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov al, [esi] // Get texel into AL + and ebx, 0ff00h // Clear out fractional part of EBX + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+1],al // Write new pixel + +Skip1: add ecx,Tmap.DeltaVFrac + sbb eax,eax + add ebp,Tmap.fx_dwdx + add ebx,Tmap.DeltaUFrac + adc esi,Tmap.uv_delta[4*eax+4] + + + // Pixel 2 + cmp ebp, [edx+8] // Compare the Z depth of this pixel with zbuffer + jle Skip2 // If pixel is covered, skip drawing + mov [edx+8], ebp // Write new Z value + + // Get pixel and light it + push ebx + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov al, [esi] // Get texel into AL + and ebx, 0ff00h // Clear out fractional part of EBX + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+2],al // Write new pixel + +Skip2: add ecx,Tmap.DeltaVFrac + sbb eax,eax + add ebp,Tmap.fx_dwdx + add ebx,Tmap.DeltaUFrac + adc esi,Tmap.uv_delta[4*eax+4] + + + // Pixel 3 + cmp ebp, [edx+12] // Compare the Z depth of this pixel with zbuffer + jle Skip3 // If pixel is covered, skip drawing + mov [edx+12], ebp // Write new Z value + + // Get pixel and light it + push ebx + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov al, [esi] // Get texel into AL + and ebx, 0ff00h // Clear out fractional part of EBX + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+3],al // Write new pixel + +Skip3: add ecx,Tmap.DeltaVFrac + sbb eax,eax + add ebp,Tmap.fx_dwdx + add ebx,Tmap.DeltaUFrac + adc esi,Tmap.uv_delta[4*eax+4] + + + + add edi, 4 + add edx, 16 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, ebp + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, do not do deltas + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + cmp Tmap.WidthModLength, 1 + jle NoDeltaLight + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov edx, Tmap.DeltaUFrac + mov dx, ax + mov Tmap.DeltaUFrac, edx + + pop ebx + +NoDeltaLight: + + mov ebp, Tmap.fx_w + + mov eax, edi + sub eax, Tmap.pScreenBits + shl eax, 2 + mov edx, gr_zbuffer + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + mov al,[esi] + + + +NextPixel: + // Pixel 0 + cmp ebp, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0a // If pixel is covered, skip drawing + mov [edx+0], ebp // Write new Z value + + // Get pixel and light it + push ebx + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov al, [esi] // Get texel into AL + and ebx, 0ff00h // Clear out fractional part of EBX + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+0],al // Write new pixel + +Skip0a: add ecx,Tmap.DeltaVFrac + sbb eax,eax + + //add edx, 4 // Go to next + add ebp,Tmap.fx_dwdx + + add ebx,Tmap.DeltaUFrac + + adc esi,Tmap.uv_delta[4*eax+4] + + + // Pixel 1 + cmp ebp, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1a // If pixel is covered, skip drawing + mov [edx+4], ebp // Write new Z value + + // Get pixel and light it + push ebx + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov al, [esi] // Get texel into AL + and ebx, 0ff00h // Clear out fractional part of EBX + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+1],al // Write new pixel + +Skip1a: add ecx,Tmap.DeltaVFrac + sbb eax,eax + + //add edx, 4 // Go to next + add ebp,Tmap.fx_dwdx + + add ebx,Tmap.DeltaUFrac + + adc esi,Tmap.uv_delta[4*eax+4] + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + + cmp ebp, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0c // If pixel is covered, skip drawing + mov [edx+0], ebp // Write new Z value + + // Get pixel and light it + push ebx + xor eax, eax // Clear all bits of EAX. This avoids a partial register stall on Pentium Pros + mov al, [esi] // Get texel into AL + and ebx, 0ff00h // Clear out fractional part of EBX + mov eax, DWORD PTR gr_fade_table[eax+ebx] // Lookup pixel in lighting table + pop ebx + + mov [edi+0],al // Write new pixel + +Skip0c: + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + +void tmapscan_pln8_zbuffered_pentium() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov edx, Tmap.DeltaUFrac + mov dx, bp + mov Tmap.DeltaUFrac, edx + + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + mov al,[esi] // get texture pixel 0 + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov ebp, Tmap.fx_w + + mov edx, gr_zbuffer + + mov eax, edi + sub eax, Tmap.pScreenBits + shl eax, 2 + add edx, eax + +InnerInnerLoop: + + // Pixel 0 + mov eax, ebx // Get lighting value from BH into AH + and eax, 0ffffh; // Clear upper bits of EAX + + cmp ebp, [edx+0] // Compare the Z depth of this pixel with zbuffer + mov al, [esi] // Get texel into AL + jle Skip0 // If pixel is covered, skip drawing + + mov [edx+0], ebp // Write new Z value + + mov al, gr_fade_table[eax] // Lookup pixel in lighting table + mov [edi+0],al // Write new pixel + +Skip0: add ecx,Tmap.DeltaVFrac + sbb eax,eax + + //add edx, 4 // Go to next + add ebp,Tmap.fx_dwdx + + add ebx,Tmap.DeltaUFrac + + adc esi,Tmap.uv_delta[4*eax+4] + + + // Pixel 1 + mov eax, ebx // Get lighting value from BH into AH + and eax, 0ffffh; // Clear upper bits of EAX + + cmp ebp, [edx+4] // Compare the Z depth of this pixel with zbuffer + mov al, [esi] // Get texel into AL + jle Skip1 // If pixel is covered, skip drawing + + + mov [edx+4], ebp // Write new Z value + + mov al, gr_fade_table[eax] // Lookup pixel in lighting table + mov [edi+1],al // Write new pixel + +Skip1: add ecx,Tmap.DeltaVFrac + sbb eax,eax + + //add edx, 4 // Go to next + add ebp,Tmap.fx_dwdx + + add ebx,Tmap.DeltaUFrac + + adc esi,Tmap.uv_delta[4*eax+4] + + // Pixel 2 + + mov eax, ebx // Get lighting value from BH into AH + and eax, 0ffffh; // Clear upper bits of EAX + + + cmp ebp, [edx+8] // Compare the Z depth of this pixel with zbuffer + mov al, [esi] // Get texel into AL + jle Skip2 // If pixel is covered, skip drawing + + + mov [edx+8], ebp // Write new Z value + + mov al, gr_fade_table[eax] // Lookup pixel in lighting table + mov [edi+2],al // Write new pixel + +Skip2: add ecx,Tmap.DeltaVFrac + sbb eax,eax + + //add edx, 4 // Go to next + add ebp,Tmap.fx_dwdx + + add ebx,Tmap.DeltaUFrac + + adc esi,Tmap.uv_delta[4*eax+4] + + // Pixel 3 + mov eax, ebx // Get lighting value from BH into AH + and eax, 0ffffh; // Clear upper bits of EAX + + + cmp ebp, [edx+12] // Compare the Z depth of this pixel with zbuffer + mov al, [esi] // Get texel into AL + jle Skip3 // If pixel is covered, skip drawing + + + mov [edx+12], ebp // Write new Z value + + mov al, gr_fade_table[eax] // Lookup pixel in lighting table + mov [edi+3],al // Write new pixel + +Skip3: add ecx,Tmap.DeltaVFrac + sbb eax,eax + + //add edx, 4 // Go to next + add ebp,Tmap.fx_dwdx + + add ebx,Tmap.DeltaUFrac + + adc esi,Tmap.uv_delta[4*eax+4] + + + add edi, 4 + add edx, 16 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, ebp + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, do not do deltas + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + cmp Tmap.WidthModLength, 1 + jle NoDeltaLight + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov edx, Tmap.DeltaUFrac + mov dx, ax + mov Tmap.DeltaUFrac, edx + + pop ebx + +NoDeltaLight: + + mov ebp, Tmap.fx_w + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + mov al,[esi] + + + +NextPixel: + // Pixel 0 + mov eax, ebx // Get lighting value from BH into AH + and eax, 0ffffh; // Clear upper bits of EAX + + cmp ebp, [edx+0] // Compare the Z depth of this pixel with zbuffer + mov al, [esi] // Get texel into AL + jle Skip0a // If pixel is covered, skip drawing + + + mov [edx+0], ebp // Write new Z value + + mov al, gr_fade_table[eax] // Lookup pixel in lighting table + mov [edi+0],al // Write new pixel + +Skip0a: add ecx,Tmap.DeltaVFrac + sbb eax,eax + + //add edx, 4 // Go to next + add ebp,Tmap.fx_dwdx + + add ebx,Tmap.DeltaUFrac + + adc esi,Tmap.uv_delta[4*eax+4] + + + // Pixel 1 + mov eax, ebx // Get lighting value from BH into AH + and eax, 0ffffh; // Clear upper bits of EAX + + + cmp ebp, [edx+4] // Compare the Z depth of this pixel with zbuffer + mov al, [esi] // Get texel into AL + jle Skip1a // If pixel is covered, skip drawing + + mov [edx+4], ebp // Write new Z value + + mov al, gr_fade_table[eax] // Lookup pixel in lighting table + mov [edi+1],al // Write new pixel + +Skip1a: add ecx,Tmap.DeltaVFrac + sbb eax,eax + + //add edx, 4 // Go to next + add ebp,Tmap.fx_dwdx + + add ebx,Tmap.DeltaUFrac + + adc esi,Tmap.uv_delta[4*eax+4] + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + + mov eax, ebx // Get lighting value from BH into AH + and eax, 0ffffh; // Clear upper bits of EAX + + cmp ebp, [edx+0] // Compare the Z depth of this pixel with zbuffer + mov al, [esi] // Get texel into AL + jle Skip0c // If pixel is covered, skip drawing + + mov al, gr_fade_table[eax] // Lookup pixel in lighting table + + mov [edx+0], ebp // Write new Z value + + mov [edi+0],al // Write new pixel + +Skip0c: + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + +void tmapscan_pln8_zbuffered() +{ + if ( Gr_cpu > 5 ) { + // Pentium Pro optimized code. + tmapscan_pln8_zbuffered_ppro(); + } else { + tmapscan_pln8_zbuffered_pentium(); + } +} + +void tmapscan_lnaa8_zbuffered() +{ +#ifndef HARDWARE_ONLY + Tmap.lookup = (uint)&Current_alphacolor->table.lookup[0][0]; + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap.dest_row_data + + xor eax, eax + mov al,[edi] // get the destination pixel + + mov ebp, Tmap.fx_w + mov edx, gr_zbuffer + mov eax, edi + sub eax, Tmap.pScreenBits + shl eax, 2 + add edx, eax + + mov eax, Tmap.loop_count + + shr eax, 2 + je DoLeftOverPixels + + mov Tmap.num_big_steps, eax + and Tmap.loop_count, 3 + +NextPixelBlock: + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + cmp ebp, [edx+4*0] // Compare the Z depth of this pixel with zbuffer + jle Skip0a // If pixel is covered, skip drawing +// mov [edx+4*0], ebp // Write new Z value + mov al,[edi+0] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+0],al // store pixel +Skip0a: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + + cmp ebp, [edx+4*1] // Compare the Z depth of this pixel with zbuffer + jle Skip1a // If pixel is covered, skip drawing +// mov [edx+4*1], ebp // Write new Z value + mov al,[edi+1] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+1],al // store pixel +Skip1a: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + cmp ebp, [edx+4*2] // Compare the Z depth of this pixel with zbuffer + jle Skip2a // If pixel is covered, skip drawing +// mov [edx+4*2], ebp // Write new Z value + mov al,[edi+2] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+2],al // store pixel +Skip2a: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + cmp ebp, [edx+4*3] // Compare the Z depth of this pixel with zbuffer + jle Skip3a // If pixel is covered, skip drawing +// mov [edx+4*3], ebp // Write new Z value + mov al,[edi+3] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+3],al // store pixel +Skip3a: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + add edx, 16 + add edi, 4 + dec Tmap.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov eax,Tmap.loop_count + test eax, -1 + jz _none_to_do + shr eax, 1 + je one_more_pix + mov Tmap.loop_count, eax + pushf + + xor eax, eax + mov al,[edi] // get the destination pixel + +NextPixel: + + cmp ebp, [edx+4*0] // Compare the Z depth of this pixel with zbuffer + jle Skip0b // If pixel is covered, skip drawing +// mov [edx+4*0], ebp // Write new Z value + mov al,[edi+0] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+0],al // store pixel +Skip0b: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + cmp ebp, [edx+4*1] // Compare the Z depth of this pixel with zbuffer + jle Skip1b // If pixel is covered, skip drawing +// mov [edx+4*1], ebp // Write new Z value + mov al,[edi+1] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+1],al // store pixel +Skip1b: + add ebp, Tmap.fx_dwdx + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb eax,eax // get -1 if carry + add ebx,Tmap.DeltaUFrac // increment u fraction + adc esi,Tmap.uv_delta[4*eax+4] // add in step ints & carries + + add edi, 2 + add edx, 8 + dec Tmap.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + cmp ebp, [edx] // Compare the Z depth of this pixel with zbuffer + jle Skip0c // If pixel is covered, skip drawing +// mov [edx], ebp // Write new Z value + mov al,[edi] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + and eax, 0ffffh + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi],al // store pixel +Skip0c: + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +#else + Int3(); +#endif +} + +void tmapscan_lnaa8() +{ +#ifndef HARDWARE_ONLY + if (gr_zbuffering) { + switch(gr_zbuffering_mode) { + case GR_ZBUFF_NONE: + break; + case GR_ZBUFF_FULL: // both + case GR_ZBUFF_WRITE: // write only + case GR_ZBUFF_READ: // read only + tmapscan_lnaa8_zbuffered(); + return; + } + + } + + Tmap.lookup = (uint)&Current_alphacolor->table.lookup[0][0]; + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + ; setup delta values + mov eax, Tmap.fx_dv_dx // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.fx_du_dx // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + ; setup initial coordinates + mov esi, Tmap.fx_u // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.fx_v // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + ; set edi = address of first pixel to modify + mov edi, Tmap.dest_row_data + + mov edx, Tmap.DeltaUFrac + + xor eax, eax + mov al,[edi] // get the destination pixel + + mov ebp, Tmap.loop_count + + shr ebp, 2 + je DoLeftOverPixels + + mov Tmap.num_big_steps, ebp + and Tmap.loop_count, 3 + + +NextPixelBlock: + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + + mov al,[edi+0] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+0],al // store pixel + + mov al,[edi+1] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+1],al // store pixel + + mov al,[edi+2] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+2],al // store pixel + + mov al,[edi+3] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+3],al // store pixel + + add edi, 4 + dec Tmap.num_big_steps + jne NextPixelBlock + + +DoLeftOverPixels: + + mov ebp,Tmap.loop_count + test ebp, -1 + jz _none_to_do + shr ebp, 1 + je one_more_pix + mov Tmap.loop_count, ebp + pushf + + xor eax, eax + mov al,[edi] // get the destination pixel + +NextPixel: + + mov al,[edi+0] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+0],al // store pixel + + mov al,[edi+1] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add ecx,Tmap.DeltaVFrac // increment v fraction + sbb ebp,ebp // get -1 if carry + add ebx,edx // increment u fraction + adc esi,Tmap.uv_delta[4*ebp+4] // add in step ints & carries + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi+1],al // store pixel + + add edi, 2 + dec Tmap.loop_count + jne NextPixel + + popf + jnc _none_to_do + +one_more_pix: + mov al,[edi] // get the destination pixel + mov ah,[esi] // get texture pixel 0 + add eax, Tmap.lookup + mov al, [eax] // blend them + mov [edi],al // store pixel + +_none_to_do: + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +#else + Int3(); +#endif +} + + + + + diff --git a/src/graphics/tmapscantiled128x128.cpp b/src/graphics/tmapscantiled128x128.cpp new file mode 100644 index 0000000..22cc5b9 --- /dev/null +++ b/src/graphics/tmapscantiled128x128.cpp @@ -0,0 +1,1246 @@ +/* + * $Logfile: /Freespace2/code/Graphics/TmapScanTiled128x128.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for drawing tiled 128x128 textues + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 9 4/23/98 9:55a John + * Fixed some bugs in the tiled tmapper causing bright dots to appear all + * over models. + * + * 8 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 7 1/27/98 5:13p John + * Moved all float to int conversions out of inner loops and into outer. + * Made outer loop use FISTP instead of ftol, saved about 10%. + * + * 6 1/23/98 5:08p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 5 12/04/97 10:38a John + * Fixed tiled texture mappers that were swapping uvs. + * + * 4 10/14/97 9:19a John + * removed fdiv warnings. + * + * 3 6/02/97 11:45a John + * fixed bugs with 64x64 and 128x128 tmappers. + * + * 2 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 1 4/24/97 4:42p John + * Initial version of the tiled texture mappers for 64 & 128 wide + * textures. + * + * $NoKeywords: $ + */ + + +#include "3d.h" +#include "2d.h" +#include "grinternal.h" +#include "tmapper.h" +#include "tmapscanline.h" +#include "floating.h" +#include "palman.h" +#include "fix.h" + +// Needed to keep warning 4725 to stay away. See PsTypes.h for details why. +void disable_warning_4725_stub_tst128() +{ +} + + +void tmapscan_pln8_zbuffered_tiled_128x128() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov edx, gr_zbuffer + + mov eax, edi + sub eax, Tmap.pScreenBits + shl eax, 2 + add edx, eax + +// Make ESI = DV:DU in 7:9,7:9 format + mov eax, Tmap.DeltaV + shr eax, 7 + mov esi, Tmap.DeltaU + shl esi, 9 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 7:9,7:9 format + mov eax, Tmap.VFixed + shr eax, 7 + mov ecx, Tmap.UFixed + shl ecx, 9 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 7.9:7.9 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0 // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 1 + cmp esi, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1 // If pixel is covered, skip drawing + + mov [edx+4], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al +Skip1: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 2 + cmp esi, [edx+8] // Compare the Z depth of this pixel with zbuffer + jle Skip2 // If pixel is covered, skip drawing + + mov [edx+8], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+2], al +Skip2: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 3 + cmp esi, [edx+12] // Compare the Z depth of this pixel with zbuffer + jle Skip3 // If pixel is covered, skip drawing + + mov [edx+12], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+3], al +Skip3: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + + add edi, 4 + add edx, 16 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov bp, ax + + pop ebx + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +// Make ESI = DV:DU in 7:9,7:9 format + mov eax, Tmap.DeltaV + shr eax, 7 + mov esi, Tmap.DeltaU + shl esi, 9 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 7:9,7:9 format + mov eax, Tmap.VFixed + shr eax, 7 + mov ecx, Tmap.UFixed + shl ecx, 9 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 7.9:7.9 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + + +NextPixel: + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0a // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0a: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 1 + cmp esi, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1a // If pixel is covered, skip drawing + + mov [edx+4], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al +Skip1a: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0b // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0b: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + +void tmapscan_pln8_tiled_128x128() +{ + if (gr_zbuffering) { + switch(gr_zbuffering_mode) { + case GR_ZBUFF_NONE: + break; + case GR_ZBUFF_FULL: // both + tmapscan_pln8_zbuffered_tiled_128x128(); + return; + case GR_ZBUFF_WRITE: // write only + tmapscan_pln8_zbuffered_tiled_128x128(); + break; + case GR_ZBUFF_READ: // read only + tmapscan_pln8_zbuffered_tiled_128x128(); + return; + } + } + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov edx, gr_zbuffer + + mov eax, edi + sub eax, Tmap.pScreenBits + shl eax, 2 + add edx, eax + +// Make ESI = DV:DU in 7:9,7:9 format + mov eax, Tmap.DeltaV + shr eax, 7 + mov esi, Tmap.DeltaU + shl esi, 9 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 7:9,7:9 format + mov eax, Tmap.VFixed + shr eax, 7 + mov ecx, Tmap.UFixed + shl ecx, 9 + mov cx, ax + + + // eax = tmp + // ebx = light + // ecx = V:U in 7.9:7.9 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 2 + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+2], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 3 + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+3], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + + add edi, 4 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov bp, ax + + pop ebx + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +// Make ESI = DV:DU in 7:9,7:9 format + mov eax, Tmap.DeltaV + shr eax, 7 + mov esi, Tmap.DeltaU + shl esi, 9 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 7:9,7:9 format + mov eax, Tmap.VFixed + shr eax, 7 + mov ecx, Tmap.UFixed + shl ecx, 9 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 7.9:7.9 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + + +NextPixel: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 7.9:7.9 + shr ax, 9 // EAX = V:U in 7.9:16.0 + rol eax, 7 // EAX = V:U in 0.0:7:7 + and eax, 03fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*128)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + diff --git a/src/graphics/tmapscantiled16x16.cpp b/src/graphics/tmapscantiled16x16.cpp new file mode 100644 index 0000000..3b3eefe --- /dev/null +++ b/src/graphics/tmapscantiled16x16.cpp @@ -0,0 +1,1287 @@ +/* + * $Logfile: /Freespace2/code/Graphics/TmapScanTiled16x16.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for drawing tiled 16x16 textues + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 6 4/23/98 9:55a John + * Fixed some bugs in the tiled tmapper causing bright dots to appear all + * over models. + * + * 5 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 4 1/23/98 5:08p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 3 12/04/97 10:38a John + * Fixed tiled texture mappers that were swapping uvs. + * + * 2 10/14/97 9:19a John + * removed fdiv warnings. + * + * 1 6/18/97 4:02p John + * added new code for 16x16 and 32x32 tiled tmaps. + * + * $NoKeywords: $ + */ + + +#include "3d.h" +#include "2d.h" +#include "grinternal.h" +#include "tmapper.h" +#include "tmapscanline.h" +#include "floating.h" +#include "palman.h" +#include "fix.h" + +// Needed to keep warning 4725 to stay away. See PsTypes.h for details why. +void disable_warning_4725_stub_tst16() +{ +} + +void tmapscan_pln8_zbuffered_tiled_16x16() +{ + Tmap.fx_l = fl2f(Tmap.l.b*32.0); + Tmap.fx_l_right = fl2f(Tmap.r.b*32.0); + Tmap.fx_dl_dx = fl2f(Tmap.deltas.b*32.0); + + if ( Tmap.fx_dl_dx < 0 ) { + Tmap.fx_dl_dx = -Tmap.fx_dl_dx; + Tmap.fx_l = (67*F1_0)-Tmap.fx_l; + Tmap.fx_l_right = (67*F1_0)-Tmap.fx_l_right; +// Assert( Tmap.fx_l > 31*F1_0 ); +// Assert( Tmap.fx_l < 66*F1_0 ); +// Assert( Tmap.fx_dl_dx >= 0 ); +// Assert( Tmap.fx_dl_dx < 31*F1_0 ); + } + + Tmap.fl_dudx_wide = Tmap.deltas.u*32.0f; + Tmap.fl_dvdx_wide = Tmap.deltas.v*32.0f; + Tmap.fl_dwdx_wide = Tmap.deltas.sw*32.0f; + + Tmap.fx_w = fl2i(Tmap.l.sw * GR_Z_RANGE)+gr_zoffset; + Tmap.fx_dwdx = fl2i(Tmap.deltas.sw * GR_Z_RANGE); + +// Assert(Tmap.fx_w < 65536 ); +// Assert(Tmap.fx_w >= 0 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count < 65536 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count >= 0 ); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov edx, gr_zbuffer + + mov eax, edi + sub eax, Tmap.pScreenBits + shl eax, 2 + add edx, eax + +// Make ESI = DV:DU in 5:11,5:11 format + mov eax, Tmap.DeltaV + shr eax, 4 + mov esi, Tmap.DeltaU + shl esi, 12 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 5:11,5:11 format + mov eax, Tmap.VFixed + shr eax, 4 + mov ecx, Tmap.UFixed + shl ecx, 12 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0 // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 1 + cmp esi, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1 // If pixel is covered, skip drawing + + mov [edx+4], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al +Skip1: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 2 + cmp esi, [edx+8] // Compare the Z depth of this pixel with zbuffer + jle Skip2 // If pixel is covered, skip drawing + + mov [edx+8], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+2], al +Skip2: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 3 + cmp esi, [edx+12] // Compare the Z depth of this pixel with zbuffer + jle Skip3 // If pixel is covered, skip drawing + + mov [edx+12], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+3], al +Skip3: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + + add edi, 4 + add edx, 16 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov bp, ax + + pop ebx + + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +// Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap.DeltaV + shr eax, 4 + mov esi, Tmap.DeltaU + shl esi, 12 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 6:10,6:10 format + mov eax, Tmap.VFixed + shr eax, 4 + mov ecx, Tmap.UFixed + shl ecx, 12 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + + +NextPixel: + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0a // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0a: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 1 + cmp esi, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1a // If pixel is covered, skip drawing + + mov [edx+4], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al +Skip1a: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0b // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0b: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + +void tmapscan_pln8_tiled_16x16() +{ + if (gr_zbuffering) { + switch(gr_zbuffering_mode) { + case GR_ZBUFF_NONE: + break; + case GR_ZBUFF_FULL: // both + tmapscan_pln8_zbuffered_tiled_16x16(); + return; + case GR_ZBUFF_WRITE: // write only + tmapscan_pln8_zbuffered_tiled_16x16(); + break; + case GR_ZBUFF_READ: // read only + tmapscan_pln8_zbuffered_tiled_16x16(); + return; + } + } + + Tmap.fx_l = fl2f(Tmap.l.b*32.0); + Tmap.fx_l_right = fl2f(Tmap.r.b*32.0); + Tmap.fx_dl_dx = fl2f(Tmap.deltas.b*32.0); + + if ( Tmap.fx_dl_dx < 0 ) { + Tmap.fx_dl_dx = -Tmap.fx_dl_dx; + Tmap.fx_l = (67*F1_0)-Tmap.fx_l; + Tmap.fx_l_right = (67*F1_0)-Tmap.fx_l_right; +// Assert( Tmap.fx_l > 31*F1_0 ); +// Assert( Tmap.fx_l < 66*F1_0 ); +// Assert( Tmap.fx_dl_dx >= 0 ); +// Assert( Tmap.fx_dl_dx < 31*F1_0 ); + } + + Tmap.fl_dudx_wide = Tmap.deltas.u*32.0f; + Tmap.fl_dvdx_wide = Tmap.deltas.v*32.0f; + Tmap.fl_dwdx_wide = Tmap.deltas.sw*32.0f; + + Tmap.fx_w = fl2i(Tmap.l.sw * GR_Z_RANGE)+gr_zoffset; + Tmap.fx_dwdx = fl2i(Tmap.deltas.sw * GR_Z_RANGE); + +// Assert(Tmap.fx_w < 65536 ); +// Assert(Tmap.fx_w >= 0 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count < 65536 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count >= 0 ); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov edx, gr_zbuffer + + mov eax, edi + sub eax, Tmap.pScreenBits + shl eax, 2 + add edx, eax + +// Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap.DeltaV + shr eax, 4 + mov esi, Tmap.DeltaU + shl esi, 12 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 6:10,6:10 format + mov eax, Tmap.VFixed + shr eax, 4 + mov ecx, Tmap.UFixed + shl ecx, 12 + mov cx, ax + + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 2 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+2], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 3 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+3], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + + add edi, 4 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov bp, ax + + pop ebx + + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +// Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap.DeltaV + shr eax, 4 + mov esi, Tmap.DeltaU + shl esi, 12 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 6:10,6:10 format + mov eax, Tmap.VFixed + shr eax, 4 + mov ecx, Tmap.UFixed + shl ecx, 12 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + + +NextPixel: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 12 // EAX = V:U in 6.10:16.0 + rol eax, 4 // EAX = V:U in 0.0:6:6 + and eax, 0ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} diff --git a/src/graphics/tmapscantiled256x256.cpp b/src/graphics/tmapscantiled256x256.cpp new file mode 100644 index 0000000..beb9bcf --- /dev/null +++ b/src/graphics/tmapscantiled256x256.cpp @@ -0,0 +1,2256 @@ +/* + * $Logfile: /Freespace2/code/Graphics/TmapScanTiled256x256.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for drawing tiled 256x256 textues + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 11 5/13/98 2:53p John + * Made subspace effect work under software. Had to add new inner loop to + * tmapper. Added glows to end of subspace effect. Made subspace effect + * levels use gamepalette-subspace palette. + * + * 10 4/23/98 9:55a John + * Fixed some bugs in the tiled tmapper causing bright dots to appear all + * over models. + * + * 9 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 8 1/27/98 5:13p John + * Moved all float to int conversions out of inner loops and into outer. + * Made outer loop use FISTP instead of ftol, saved about 10%. + * + * 7 1/23/98 5:08p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 6 12/04/97 10:38a John + * Fixed tiled texture mappers that were swapping uvs. + * + * 5 10/14/97 9:19a John + * removed fdiv warnings. + * + * 4 9/10/97 11:38a Sandeep + * + * 3 9/09/97 3:39p Sandeep + * warning level 4 bugs + * + * 2 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 1 4/24/97 4:42p John + * Initial version of the tiled texture mappers for 64 & 128 wide + * textures. + * + * $NoKeywords: $ + */ + + +#include "3d.h" +#include "2d.h" +#include "grinternal.h" +#include "tmapper.h" +#include "tmapscanline.h" +#include "floating.h" +#include "palman.h" +#include "fix.h" +#include "key.h" + +// Needed to keep warning 4725 to stay away. See PsTypes.h for details why. +void disable_warning_4725_stub_tst256() +{ +} + +void tmapscan_pln8_zbuffered_tiled_256x256() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + +// Make ESI = DV:DU in 8:8,8:8 format + mov eax, Tmap.DeltaV + shr eax, 8 + mov esi, Tmap.DeltaU + shl esi, 8 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 8:8,8:8 format + mov eax, Tmap.VFixed + shr eax, 8 + mov ecx, Tmap.UFixed + shl ecx, 8 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.8:8.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0 // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 1 + cmp esi, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1 // If pixel is covered, skip drawing + + mov [edx+4], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al +Skip1: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 2 + cmp esi, [edx+8] // Compare the Z depth of this pixel with zbuffer + jle Skip2 // If pixel is covered, skip drawing + + mov [edx+8], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+2], al +Skip2: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 3 + cmp esi, [edx+12] // Compare the Z depth of this pixel with zbuffer + jle Skip3 // If pixel is covered, skip drawing + + mov [edx+12], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+3], al +Skip3: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + + add edi, 4 + add edx, 16 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov bp, ax + + pop ebx + + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +// Make ESI = DV:DU in 8:8,8:8 format + mov eax, Tmap.DeltaV + shr eax, 8 + mov esi, Tmap.DeltaU + shl esi, 8 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 8:8,8:8 format + mov eax, Tmap.VFixed + shr eax, 8 + mov ecx, Tmap.UFixed + shl ecx, 8 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.8:8.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + + +NextPixel: + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0a // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0a: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 1 + cmp esi, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1a // If pixel is covered, skip drawing + + mov [edx+4], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al +Skip1a: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0b // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0b: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + +void tmapscan_pln8_tiled_256x256() +{ + if (gr_zbuffering) { + switch(gr_zbuffering_mode) { + case GR_ZBUFF_NONE: + break; + case GR_ZBUFF_FULL: // both + tmapscan_pln8_zbuffered_tiled_256x256(); + return; + case GR_ZBUFF_WRITE: // write only + tmapscan_pln8_zbuffered_tiled_256x256(); + break; + case GR_ZBUFF_READ: // read only + tmapscan_pln8_zbuffered_tiled_256x256(); + return; + } + } + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + +// Make ESI = DV:DU in 8:8,8:8 format + mov eax, Tmap.DeltaV + shr eax, 8 + mov esi, Tmap.DeltaU + shl esi, 8 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 8:8,8:8 format + mov eax, Tmap.VFixed + shr eax, 8 + mov ecx, Tmap.UFixed + shl ecx, 8 + mov cx, ax + + + // eax = tmp + // ebx = light + // ecx = V:U in 8.8:8.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 2 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+2], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 3 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+3], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + + add edi, 4 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov bp, ax + + pop ebx + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +// Make ESI = DV:DU in 8:8,8:8 format + mov eax, Tmap.DeltaV + shr eax, 8 + mov esi, Tmap.DeltaU + shl esi, 8 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 8:8,8:8 format + mov eax, Tmap.UFixed + shr eax, 8 + mov ecx, Tmap.VFixed + shl ecx, 8 + mov cx, ax + + // eax = tmp + // ebx = light + // ecx = V:U in 8.8:8.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + + +NextPixel: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*256)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + + +// Totally non-general function specifically made for the subpsace effect +void tmapscan_lnn8_tiled_256x256() +{ + if ( Tmap.src_offset != 256 ) { + Int3(); // This only works on 256 wide textures! + return; + } + +// Tmap.fx_u = fl2f(Tmap.l.u); +// Tmap.fx_v = fl2f(Tmap.l.v); +// Tmap.fx_du_dx = fl2f(Tmap.deltas.u); +// Tmap.fx_dv_dx = fl2f(Tmap.deltas.v); + + int i; + + ubyte * src = (ubyte *)Tmap.pixptr; + ubyte * dst = (ubyte *)Tmap.dest_row_data; + + for (i=0; i// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + // set up affine registers + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + +// Make EDX = DV:DU in 8:8,8:8 format + mov eax, Tmap.DeltaV + shr eax, 8 + mov edx, Tmap.DeltaU + shl edx, 8 + mov dx, ax + +// Make ECX = V:U in 8:8,8:8 format + mov eax, Tmap.VFixed + shr eax, 8 + mov ecx, Tmap.UFixed + shl ecx, 8 + mov cx, ax + + // eax = tmp + // ebx = + // ecx = V:U in 8.8:8.8 + // edx = zbuffer pointer + // esi = + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 +// mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + mov eax, ebx + shr eax, 1 + jnc L1 + xor eax, 0xA3000000 ; This makes 'r' take 2^32 iterations to repeat +L1: mov ebx, eax + and eax, MASK ; mask out all bits except 8.8:8.8 fraction + add eax, ecx + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+0], al + + // pixel 1 +// mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + mov eax, ebx + shr eax, 1 + jnc L2 + xor eax, 0xA3000000 ; This makes 'r' take 2^32 iterations to repeat +L2: mov ebx, eax + and eax, MASK ; mask out all bits except 8.8:8.8 fraction + add eax, ecx + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+1], al + + // pixel 2 +// mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + mov eax, ebx + shr eax, 1 + jnc L3 + xor eax, 0xA3000000 ; This makes 'r' take 2^32 iterations to repeat +L3: mov ebx, eax + and eax, MASK ; mask out all bits except 8.8:8.8 fraction + add eax, ecx + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+2], al + + // pixel 3 +// mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + mov eax, ebx + shr eax, 1 + jnc L4 + xor eax, 0xA3000000 ; This makes 'r' take 2^32 iterations to repeat +L4: mov ebx, eax + and eax, MASK ; mask out all bits except 8.8:8.8 fraction + add eax, ecx + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+3], al + + + add edi, 4 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + +OnePixelSpan: + +// Make EDX = DV:DU in 8:8,8:8 format + mov eax, Tmap.DeltaV + shr eax, 8 + mov edx, Tmap.DeltaU + shl edx, 8 + mov dx, ax + +// Make ECX = V:U in 8:8,8:8 format + mov eax, Tmap.VFixed + shr eax, 8 + mov ecx, Tmap.UFixed + shl ecx, 8 + mov cx, ax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + // eax = tmp + // ebx = light + // ecx = V:U in 8.8:8.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +NextPixel: + // pixel 0 +// mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + mov eax, ebx + shr eax, 1 + jnc L5 + xor eax, 0xA3000000 ; This makes 'r' take 2^32 iterations to repeat +L5: mov ebx, eax + and eax, MASK ; mask out all bits except 8.8:8.8 fraction + add eax, ecx + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+0], al + + // pixel 1 +// mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + mov eax, ebx + shr eax, 1 + jnc L6 + xor eax, 0xA3000000 ; This makes 'r' take 2^32 iterations to repeat +L6: mov ebx, eax + and eax, MASK ; mask out all bits except 8.8:8.8 fraction + add eax, ecx + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+1], al + + + add edi, 2 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + mov eax, ebx + shr eax, 1 + jnc L7 + xor eax, 0xA3000000 ; This makes 'r' take 2^32 iterations to repeat +L7: mov ebx, eax + and eax, MASK ; mask out all bits except 8.8:8.8 fraction + add eax, ecx + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+0], al + +FPUReturn: + + mov Rand_value, ebx + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + + +void tmapscan_pnn8_tiled_256x256_subspace() +{ + if ( Tmap.src_offset != 256 ) { + Int3(); // This only works on 256 wide textures! + return; + } + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Need EDI = pointer to dest row + mov edi, Tmap.dest_row_data + + // Need ESI = pointer to texture + mov esi, Tmap.pixptr + + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + mov ecx, Tmap.loop_count // ecx = width + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + // set up affine registers + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + +// Make EDX = DV:DU in 8:8,8:8 format + mov eax, Tmap.DeltaV + shr eax, 8 + mov edx, Tmap.DeltaU + shl edx, 8 + mov dx, ax + +// Make ECX = V:U in 8:8,8:8 format + mov eax, Tmap.VFixed + shr eax, 8 + mov ecx, Tmap.UFixed + shl ecx, 8 + mov cx, ax + + // eax = tmp + // ebx = + // ecx = V:U in 8.8:8.8 + // edx = zbuffer pointer + // esi = + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+0], al + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+1], al + + // pixel 2 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+2], al + + // pixel 3 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+3], al + + + add edi, 4 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + +OnePixelSpan: + +// Make EDX = DV:DU in 8:8,8:8 format + mov eax, Tmap.DeltaV + shr eax, 8 + mov edx, Tmap.DeltaU + shl edx, 8 + mov dx, ax + +// Make ECX = V:U in 8:8,8:8 format + mov eax, Tmap.VFixed + shr eax, 8 + mov ecx, Tmap.UFixed + shl ecx, 8 + mov cx, ax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + // eax = tmp + // ebx = light + // ecx = V:U in 8.8:8.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +NextPixel: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+0], al + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+1], al + + + add edi, 2 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 8.8:8.8 + add ecx, edx + shr ax, 8 // EAX = V:U in 8.8:8.0 + rol eax, 8 // EAX = V:U in 0.0:8:8 + and eax, 0ffffh // clear upper bits + + mov al, [esi+eax] + mov [edi+0], al + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + + diff --git a/src/graphics/tmapscantiled32x32.cpp b/src/graphics/tmapscantiled32x32.cpp new file mode 100644 index 0000000..f8599fd --- /dev/null +++ b/src/graphics/tmapscantiled32x32.cpp @@ -0,0 +1,1286 @@ +/* + * $Logfile: /Freespace2/code/Graphics/TmapScanTiled32x32.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for drawing tiled 32x32 textues + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 6 4/23/98 9:55a John + * Fixed some bugs in the tiled tmapper causing bright dots to appear all + * over models. + * + * 5 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 4 1/23/98 5:08p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 3 12/04/97 10:38a John + * Fixed tiled texture mappers that were swapping uvs. + * + * 2 10/14/97 9:19a John + * removed fdiv warnings. + * + * 1 6/18/97 4:02p John + * added new code for 16x16 and 32x32 tiled tmaps. + * + * $NoKeywords: $ + */ + + +#include "3d.h" +#include "2d.h" +#include "grinternal.h" +#include "tmapper.h" +#include "tmapscanline.h" +#include "floating.h" +#include "palman.h" +#include "fix.h" + +// Needed to keep warning 4725 to stay away. See PsTypes.h for details why. +void disable_warning_4725_stub_tst32() +{ +} + +void tmapscan_pln8_zbuffered_tiled_32x32() +{ + Tmap.fx_l = fl2f(Tmap.l.b*32.0); + Tmap.fx_l_right = fl2f(Tmap.r.b*32.0); + Tmap.fx_dl_dx = fl2f(Tmap.deltas.b*32.0); + + if ( Tmap.fx_dl_dx < 0 ) { + Tmap.fx_dl_dx = -Tmap.fx_dl_dx; + Tmap.fx_l = (67*F1_0)-Tmap.fx_l; + Tmap.fx_l_right = (67*F1_0)-Tmap.fx_l_right; +// Assert( Tmap.fx_l > 31*F1_0 ); +// Assert( Tmap.fx_l < 66*F1_0 ); +// Assert( Tmap.fx_dl_dx >= 0 ); +// Assert( Tmap.fx_dl_dx < 31*F1_0 ); + } + + Tmap.fl_dudx_wide = Tmap.deltas.u*32.0f; + Tmap.fl_dvdx_wide = Tmap.deltas.v*32.0f; + Tmap.fl_dwdx_wide = Tmap.deltas.sw*32.0f; + + Tmap.fx_w = fl2i(Tmap.l.sw * GR_Z_RANGE)+gr_zoffset; + Tmap.fx_dwdx = fl2i(Tmap.deltas.sw * GR_Z_RANGE); + +// Assert(Tmap.fx_w < 65536 ); +// Assert(Tmap.fx_w >= 0 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count < 65536 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count >= 0 ); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + +// Make ESI = DV:DU in 5:11,5:11 format + mov eax, Tmap.DeltaV + shr eax, 5 + mov esi, Tmap.DeltaU + shl esi, 11 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 5:11,5:11 format + mov eax, Tmap.VFixed + shr eax, 5 + mov ecx, Tmap.UFixed + shl ecx, 11 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0 // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 1 + cmp esi, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1 // If pixel is covered, skip drawing + + mov [edx+4], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al +Skip1: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 2 + cmp esi, [edx+8] // Compare the Z depth of this pixel with zbuffer + jle Skip2 // If pixel is covered, skip drawing + + mov [edx+8], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+2], al +Skip2: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 3 + cmp esi, [edx+12] // Compare the Z depth of this pixel with zbuffer + jle Skip3 // If pixel is covered, skip drawing + + mov [edx+12], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+3], al +Skip3: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + + add edi, 4 + add edx, 16 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov bp, ax + + pop ebx + + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +// Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap.DeltaV + shr eax, 5 + mov esi, Tmap.DeltaU + shl esi, 11 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 6:10,6:10 format + mov eax, Tmap.VFixed + shr eax, 5 + mov ecx, Tmap.UFixed + shl ecx, 11 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + + +NextPixel: + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0a // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0a: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 1 + cmp esi, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1a // If pixel is covered, skip drawing + + mov [edx+4], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al +Skip1a: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0b // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0b: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + +void tmapscan_pln8_tiled_32x32() +{ + if (gr_zbuffering) { + switch(gr_zbuffering_mode) { + case GR_ZBUFF_NONE: + break; + case GR_ZBUFF_FULL: // both + tmapscan_pln8_zbuffered_tiled_32x32(); + return; + case GR_ZBUFF_WRITE: // write only + tmapscan_pln8_zbuffered_tiled_32x32(); + break; + case GR_ZBUFF_READ: // read only + tmapscan_pln8_zbuffered_tiled_32x32(); + return; + } + } + + Tmap.fx_l = fl2f(Tmap.l.b*32.0); + Tmap.fx_l_right = fl2f(Tmap.r.b*32.0); + Tmap.fx_dl_dx = fl2f(Tmap.deltas.b*32.0); + + if ( Tmap.fx_dl_dx < 0 ) { + Tmap.fx_dl_dx = -Tmap.fx_dl_dx; + Tmap.fx_l = (67*F1_0)-Tmap.fx_l; + Tmap.fx_l_right = (67*F1_0)-Tmap.fx_l_right; +// Assert( Tmap.fx_l > 31*F1_0 ); +// Assert( Tmap.fx_l < 66*F1_0 ); +// Assert( Tmap.fx_dl_dx >= 0 ); +// Assert( Tmap.fx_dl_dx < 31*F1_0 ); + } + + Tmap.fl_dudx_wide = Tmap.deltas.u*32.0f; + Tmap.fl_dvdx_wide = Tmap.deltas.v*32.0f; + Tmap.fl_dwdx_wide = Tmap.deltas.sw*32.0f; + + Tmap.fx_w = fl2i(Tmap.l.sw * GR_Z_RANGE)+gr_zoffset; + Tmap.fx_dwdx = fl2i(Tmap.deltas.sw * GR_Z_RANGE); + +// Assert(Tmap.fx_w < 65536 ); +// Assert(Tmap.fx_w >= 0 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count < 65536 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count >= 0 ); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + +// Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap.DeltaU + shr eax, 5 + mov esi, Tmap.DeltaV + shl esi, 11 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 6:10,6:10 format + mov eax, Tmap.UFixed + shr eax, 5 + mov ecx, Tmap.VFixed + shl ecx, 11 + mov cx, ax + + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 2 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+2], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 3 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+3], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + + add edi, 4 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov bp, ax + + pop ebx + + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +// Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap.DeltaU + shr eax, 5 + mov esi, Tmap.DeltaV + shl esi, 11 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 6:10,6:10 format + mov eax, Tmap.UFixed + shr eax, 5 + mov ecx, Tmap.VFixed + shl ecx, 11 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + + +NextPixel: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 11 // EAX = V:U in 6.10:16.0 + rol eax, 5 // EAX = V:U in 0.0:6:6 + and eax, 03ffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} diff --git a/src/graphics/tmapscantiled64x64.cpp b/src/graphics/tmapscantiled64x64.cpp new file mode 100644 index 0000000..cd3bd36 --- /dev/null +++ b/src/graphics/tmapscantiled64x64.cpp @@ -0,0 +1,1292 @@ +/* + * $Logfile: /Freespace2/code/Graphics/TmapScanTiled64x64.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for drawing tiled 64x64 textues + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 11/30/98 5:31p Dave + * Fixed up Fred support for software mode. + * + * 3 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 8 4/23/98 9:55a John + * Fixed some bugs in the tiled tmapper causing bright dots to appear all + * over models. + * + * 7 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 6 1/23/98 5:08p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 5 12/04/97 10:38a John + * Fixed tiled texture mappers that were swapping uvs. + * + * 4 10/14/97 9:19a John + * removed fdiv warnings. + * + * 3 6/02/97 11:45a John + * fixed bugs with 64x64 and 128x128 tmappers. + * + * 2 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 1 4/24/97 4:42p John + * Initial version of the tiled texture mappers for 64 & 128 wide + * textures. + * + * $NoKeywords: $ + */ + + +#include "3d.h" +#include "2d.h" +#include "grinternal.h" +#include "tmapper.h" +#include "tmapscanline.h" +#include "floating.h" +#include "palman.h" +#include "fix.h" + +// Needed to keep warning 4725 to stay away. See PsTypes.h for details why. +void disable_warning_4725_stub_tst64() +{ +} + +void tmapscan_pln8_zbuffered_tiled_64x64() +{ + Tmap.fx_l = fl2f(Tmap.l.b*32.0); + Tmap.fx_l_right = fl2f(Tmap.r.b*32.0); + Tmap.fx_dl_dx = fl2f(Tmap.deltas.b*32.0); + + if ( Tmap.fx_dl_dx < 0 ) { + Tmap.fx_dl_dx = -Tmap.fx_dl_dx; + Tmap.fx_l = (67*F1_0)-Tmap.fx_l; + Tmap.fx_l_right = (67*F1_0)-Tmap.fx_l_right; +// Assert( Tmap.fx_l > 31*F1_0 ); +// Assert( Tmap.fx_l < 66*F1_0 ); +// Assert( Tmap.fx_dl_dx >= 0 ); +// Assert( Tmap.fx_dl_dx < 31*F1_0 ); + } + + Tmap.fl_dudx_wide = Tmap.deltas.u*32.0f; + Tmap.fl_dvdx_wide = Tmap.deltas.v*32.0f; + Tmap.fl_dwdx_wide = Tmap.deltas.sw*32.0f; + + Tmap.fx_w = fl2i(Tmap.l.sw * GR_Z_RANGE)+gr_zoffset; + Tmap.fx_dwdx = fl2i(Tmap.deltas.sw * GR_Z_RANGE); + +// Assert(Tmap.fx_w < 65536 ); +// Assert(Tmap.fx_w >= 0 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count < 65536 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count >= 0 ); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + +// Make ESI = DU:DV in 6:10,6:10 format + mov eax, Tmap.DeltaV + shr eax, 6 + mov esi, Tmap.DeltaU + shl esi, 10 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = U:V in 6:10,6:10 format + mov eax, Tmap.VFixed + shr eax, 6 + mov ecx, Tmap.UFixed + shl ecx, 10 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0 // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 1 + cmp esi, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1 // If pixel is covered, skip drawing + + mov [edx+4], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al +Skip1: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 2 + cmp esi, [edx+8] // Compare the Z depth of this pixel with zbuffer + jle Skip2 // If pixel is covered, skip drawing + + mov [edx+8], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+2], al +Skip2: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 3 + cmp esi, [edx+12] // Compare the Z depth of this pixel with zbuffer + jle Skip3 // If pixel is covered, skip drawing + + mov [edx+12], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+3], al +Skip3: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + + add edi, 4 + add edx, 16 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov bp, ax + + pop ebx + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +// Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap.DeltaV + shr eax, 6 + mov esi, Tmap.DeltaU + shl esi, 10 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 6:10,6:10 format + mov eax, Tmap.VFixed + shr eax, 6 + mov ecx, Tmap.UFixed + shl ecx, 10 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + + +NextPixel: + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0a // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0a: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + // pixel 1 + cmp esi, [edx+4] // Compare the Z depth of this pixel with zbuffer + jle Skip1a // If pixel is covered, skip drawing + + mov [edx+4], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al +Skip1a: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + cmp esi, [edx+0] // Compare the Z depth of this pixel with zbuffer + jle Skip0b // If pixel is covered, skip drawing + + mov [edx+0], esi // Write z + + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al +Skip0b: + add ecx, Tmap.DeltaUFrac + add esi, Tmap.fx_dwdx + add ebx, ebp + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + +void tmapscan_pln8_tiled_64x64() +{ + if (gr_zbuffering) { + switch(gr_zbuffering_mode) { + case GR_ZBUFF_NONE: + break; + case GR_ZBUFF_FULL: // both + tmapscan_pln8_zbuffered_tiled_64x64(); + return; + case GR_ZBUFF_WRITE: // write only + tmapscan_pln8_zbuffered_tiled_64x64(); + break; + case GR_ZBUFF_READ: // read only + tmapscan_pln8_zbuffered_tiled_64x64(); + return; + } + } + + Tmap.fx_l = fl2f(Tmap.l.b*32.0); + Tmap.fx_l_right = fl2f(Tmap.r.b*32.0); + Tmap.fx_dl_dx = fl2f(Tmap.deltas.b*32.0); + + if ( Tmap.fx_dl_dx < 0 ) { + Tmap.fx_dl_dx = -Tmap.fx_dl_dx; + Tmap.fx_l = (67*F1_0)-Tmap.fx_l; + Tmap.fx_l_right = (67*F1_0)-Tmap.fx_l_right; +// Assert( Tmap.fx_l > 31*F1_0 ); +// Assert( Tmap.fx_l < 66*F1_0 ); +// Assert( Tmap.fx_dl_dx >= 0 ); +// Assert( Tmap.fx_dl_dx < 31*F1_0 ); + } + + Tmap.fl_dudx_wide = Tmap.deltas.u*32.0f; + Tmap.fl_dvdx_wide = Tmap.deltas.v*32.0f; + Tmap.fl_dwdx_wide = Tmap.deltas.sw*32.0f; + + Tmap.fx_w = fl2i(Tmap.l.sw * GR_Z_RANGE)+gr_zoffset; + Tmap.fx_dwdx = fl2i(Tmap.deltas.sw * GR_Z_RANGE); + +// Assert(Tmap.fx_w < 65536 ); +// Assert(Tmap.fx_w >= 0 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count < 65536 ); +// Assert(Tmap.fx_w+Tmap.fx_dwdx*Tmap.loop_count >= 0 ); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + _asm { + + push eax + push ecx + push edx + push ebx + push ebp + push esi + push edi + + // Put the FPU in low precision mode + fstcw Tmap.OldFPUCW // store copy of CW + mov ax,Tmap.OldFPUCW // get it in ax + and eax, ~0x300L + mov Tmap.FPUCW,ax // store it + fldcw Tmap.FPUCW // load the FPU + + + mov ecx, Tmap.loop_count // ecx = width + mov edi, Tmap.dest_row_data // edi = dest pointer + + // edi = pointer to start pixel in dest dib + // ecx = spanwidth + + mov eax,ecx // eax and ecx = width + shr ecx,5 // ecx = width / subdivision length + and eax,31 // eax = width mod subdivision length + jnz some_left_over // any leftover? + dec ecx // no, so special case last span + mov eax,32 // it's 8 pixels long +some_left_over: + mov Tmap.Subdivisions,ecx // store widths + mov Tmap.WidthModLength,eax + + // calculate ULeft and VLeft // FPU Stack (ZL = ZLeft) + // st0 st1 st2 st3 st4 st5 st6 st7 + fld Tmap.l.v // V/ZL + fld Tmap.l.u // U/ZL V/ZL + fld Tmap.l.sw // 1/ZL U/ZL V/ZL + fld1 // 1 1/ZL U/ZL V/ZL + fdiv st,st(1) // ZL 1/ZL U/ZL V/ZL + fld st // ZL ZL 1/ZL U/ZL V/ZL + fmul st,st(4) // VL ZL 1/ZL U/ZL V/ZL + fxch st(1) // ZL VL 1/ZL U/ZL V/ZL + fmul st,st(3) // UL VL 1/ZL U/ZL V/ZL + + fstp st(5) // VL 1/ZL U/ZL V/ZL UL + fstp st(5) // 1/ZL U/ZL V/ZL UL VL + + // calculate right side OverZ terms ; st0 st1 st2 st3 st4 st5 st6 st7 + + fadd Tmap.fl_dwdx_wide // 1/ZR U/ZL V/ZL UL VL + fxch st(1) // U/ZL 1/ZR V/ZL UL VL + fadd Tmap.fl_dudx_wide // U/ZR 1/ZR V/ZL UL VL + fxch st(2) // V/ZL 1/ZR U/ZR UL VL + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZR U/ZR UL VL + + // calculate right side coords // st0 st1 st2 st3 st4 st5 st6 st7 + + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // @todo overlap this guy + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + cmp ecx,0 // check for any full spans + jle HandleLeftoverPixels + +SpanLoop: + + // at this point the FPU contains // st0 st1 st2 st3 st4 st5 st6 st7 + // UR VR V/ZR 1/ZR U/ZR UL VL + + // convert left side coords + + fld st(5) ; UL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; UL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.UFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + fld st(6) ; VL UR VR V/ZR 1/ZR U/ZR UL VL + fmul Tmap.FixedScale ; VL16 UR VR V/ZR 1/ZR U/ZR UL VL + fistp Tmap.VFixed ; UR VR V/ZR 1/ZR U/ZR UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR V/ZR 1/ZR U/ZR dU VL + fxch st(1) ; VR UR V/ZR 1/ZR U/ZR dU VL + fsubr st(6),st ; VR UR V/ZR 1/ZR U/ZR dU dV + fxch st(6) ; dV UR V/ZR 1/ZR U/ZR dU VR + + fmul Tmap.FixedScale8 ; dV8 UR V/ZR 1/ZR U/ZR dU VR + fistp Tmap.DeltaV ; UR V/ZR 1/ZR U/ZR dU VR + + fxch st(4) ; dU V/ZR 1/ZR U/ZR UR VR + fmul Tmap.FixedScale8 ; dU8 V/ZR 1/ZR U/ZR UR VR + fistp Tmap.DeltaU ; V/ZR 1/ZR U/ZR UR VR + + // increment terms for next span // st0 st1 st2 st3 st4 st5 st6 st7 + // Right terms become Left terms--->// V/ZL 1/ZL U/ZL UL VL + + fadd Tmap.fl_dvdx_wide // V/ZR 1/ZL U/ZL UL VL + fxch st(1) // 1/ZL V/ZR U/ZL UL VL + fadd Tmap.fl_dwdx_wide // 1/ZR V/ZR U/ZL UL VL + fxch st(2) // U/ZL V/ZR 1/ZR UL VL + fadd Tmap.fl_dudx_wide // U/ZR V/ZR 1/ZR UL VL + fxch st(2) // 1/ZR V/ZR U/ZR UL VL + fxch st(1) // V/ZR 1/ZR U/ZR UL VL + + + // setup delta values + + mov eax,Tmap.DeltaV // get v 16.16 step + mov ebx,eax // copy it + sar eax,16 // get v int step + shl ebx,16 // get v frac step + mov Tmap.DeltaVFrac,ebx // store it + imul eax,Tmap.src_offset // calculate texture step for v int step + + mov ebx,Tmap.DeltaU // get u 16.16 step + mov ecx,ebx // copy it + sar ebx,16 // get u int step + shl ecx,16 // get u frac step + mov Tmap.DeltaUFrac,ecx // store it + add eax,ebx // calculate uint + vint step + mov Tmap.uv_delta[4],eax // save whole step in non-v-carry slot + add eax,Tmap.src_offset // calculate whole step + v carry + mov Tmap.uv_delta[0],eax // save in v-carry slot + + // setup initial coordinates + mov esi,Tmap.UFixed // get u 16.16 fixedpoint coordinate + + mov ebx,esi // copy it + sar esi,16 // get integer part + shl ebx,16 // get fractional part + + mov ecx,Tmap.VFixed // get v 16.16 fixedpoint coordinate + + mov edx,ecx // copy it + sar edx,16 // get integer part + shl ecx,16 // get fractional part + imul edx,Tmap.src_offset // calc texture scanline address + add esi,edx // calc texture offset + add esi,Tmap.pixptr // calc address + + // set up affine registers + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + + mov ebp, Tmap.fx_dl_dx + shl ebp, 5 //*32 + add Tmap.fx_l, ebp + + mov ebp, Tmap.fx_l + shr ebp, 8 + sub bp, ax + shr bp, 5 + + mov dx, bp + + // calculate right side coords st0 st1 st2 st3 st4 st5 st6 st7 + fld1 // 1 V/ZR 1/ZR U/ZR UL VL + // This divide should happen while the pixel span is drawn. + fdiv st,st(2) // ZR V/ZR 1/ZR U/ZR UL VL + + + // 8 pixel span code + // edi = dest dib bits at current pixel + // esi = texture pointer at current u,v + // eax = scratch + // ebx = u fraction 0.32 + // ecx = v fraction 0.32 + // edx = u frac step + // ebp = v carry scratch + + mov al,[edi] // preread the destination cache line + + mov Tmap.InnerLooper, 32/4 // Set up loop counter + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + +// Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap.DeltaV + shr eax, 6 + mov esi, Tmap.DeltaU + shl esi, 10 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 6:10,6:10 format + mov eax, Tmap.VFixed + shr eax, 6 + mov ecx, Tmap.UFixed + shl ecx, 10 + mov cx, ax + + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + +InnerInnerLoop: + + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 2 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+2], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 3 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+3], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + + add edi, 4 + dec Tmap.InnerLooper + jnz InnerInnerLoop + + mov Tmap.fx_w, esi + + // the fdiv is done, finish right // st0 st1 st2 st3 st4 st5 st6 st7 + // ZR V/ZR 1/ZR U/ZR UL VL + + fld st // ZR ZR V/ZR 1/ZR U/ZR UL VL + fmul st,st(2) // VR ZR V/ZR 1/ZR U/ZR UL VL + fxch st(1) // ZR VR V/ZR 1/ZR U/ZR UL VL + fmul st,st(4) // UR VR V/ZR 1/ZR U/ZR UL VL + + dec Tmap.Subdivisions // decrement span count + jnz SpanLoop // loop back + + +HandleLeftoverPixels: + + mov esi,Tmap.pixptr // load texture pointer + + // edi = dest dib bits + // esi = current texture dib bits + // at this point the FPU contains ; st0 st1 st2 st3 st4 st5 st6 st7 + // inv. means invalid numbers ; inv. inv. inv. inv. inv. UL VL + + cmp Tmap.WidthModLength,0 ; are there remaining pixels to draw? + jz FPUReturn ; nope, pop the FPU and bail + + // convert left side coords ; st0 st1 st2 st3 st4 st5 st6 st7 + + fld st(5) ; UL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale ; UL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.UFixed ; inv. inv. inv. inv. inv. UL VL + + fld st(6) ; VL inv. inv. inv. inv. inv. UL VL + fmul Tmap.FixedScale // VL16 inv. inv. inv. inv. inv. UL VL + fistp Tmap.VFixed ; inv. inv. inv. inv. inv. UL VL + + dec Tmap.WidthModLength ; calc how many steps to take + jz OnePixelSpan ; just one, don't do deltas' + + // calculate right edge coordinates ; st0 st1 st2 st3 st4 st5 st6 st7 + // r -> R+1 + + // @todo rearrange things so we don't need these two instructions + fstp Tmap.FloatTemp ; inv. inv. inv. inv. UL VL + fstp Tmap.FloatTemp ; inv. inv. inv. UL VL + + fld Tmap.r.v ; V/Zr inv. inv. inv. UL VL + fsub Tmap.deltas.v ; V/ZR inv. inv. inv. UL VL + fld Tmap.r.u ; U/Zr V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.u ; U/ZR V/ZR inv. inv. inv. UL VL + fld Tmap.r.sw ; 1/Zr U/ZR V/ZR inv. inv. inv. UL VL + fsub Tmap.deltas.sw ; 1/ZR U/ZR V/ZR inv. inv. inv. UL VL + + fdivr Tmap.One ; ZR U/ZR V/ZR inv. inv. inv. UL VL + + fmul st(1),st ; ZR UR V/ZR inv. inv. inv. UL VL + fmulp st(2),st ; UR VR inv. inv. inv. UL VL + + // calculate deltas ; st0 st1 st2 st3 st4 st5 st6 st7 + + fsubr st(5),st ; UR VR inv. inv. inv. dU VL + fxch st(1) ; VR UR inv. inv. inv. dU VL + fsubr st(6),st ; VR UR inv. inv. inv. dU dV + fxch st(6) ; dV UR inv. inv. inv. dU VR + + fidiv Tmap.WidthModLength ; dv UR inv. inv. inv. dU VR + fmul Tmap.FixedScale ; dv16 UR inv. inv. inv. dU VR + fistp Tmap.DeltaV ; UR inv. inv. inv. dU VR + + fxch st(4) ; dU inv. inv. inv. UR VR + fidiv Tmap.WidthModLength ; du inv. inv. inv. UR VR + fmul Tmap.FixedScale ; du16 inv. inv. inv. UR VR + fistp Tmap.DeltaU ; inv. inv. inv. UR VR + + // @todo gross! these are to line up with the other loop + fld st(1) ; inv. inv. inv. inv. UR VR + fld st(2) ; inv. inv. inv. inv. inv. UR VR + + + // setup delta values + mov eax, Tmap.DeltaV // get v 16.16 step + mov ebx, eax // copy it + sar eax, 16 // get v int step + shl ebx, 16 // get v frac step + mov Tmap.DeltaVFrac, ebx // store it + imul eax, Tmap.src_offset // calc texture step for v int step + + mov ebx, Tmap.DeltaU // get u 16.16 step + mov ecx, ebx // copy it + sar ebx, 16 // get the u int step + shl ecx, 16 // get the u frac step + mov Tmap.DeltaUFrac, ecx // store it + add eax, ebx // calc uint + vint step + mov Tmap.uv_delta[4], eax // save whole step in non-v-carry slot + add eax, Tmap.src_offset // calc whole step + v carry + mov Tmap.uv_delta[0], eax // save in v-carry slot + + +OnePixelSpan: + + ; setup initial coordinates + mov esi, Tmap.UFixed // get u 16.16 + mov ebx, esi // copy it + sar esi, 16 // get integer part + shl ebx, 16 // get fractional part + + mov ecx, Tmap.VFixed // get v 16.16 + mov edx, ecx // copy it + sar edx, 16 // get integer part + shl ecx, 16 // get fractional part + imul edx, Tmap.src_offset // calc texture scanline address + add esi, edx // calc texture offset + add esi, Tmap.pixptr // calc address + + + mov eax, Tmap.fx_l + shr eax, 8 + mov bx, ax + +// mov edx, Tmap.DeltaUFrac + + push ebx + + mov ebx, Tmap.fx_l_right + shr ebx, 8 + + sub ebx, eax + mov eax, ebx + + mov eax, Tmap.fx_dl_dx + shr eax, 8 + + mov bp, ax + + pop ebx + + + mov eax, edi + sub eax, Tmap.pScreenBits + mov edx, gr_zbuffer + shl eax, 2 + add edx, eax + + inc Tmap.WidthModLength + mov eax,Tmap.WidthModLength + shr eax, 1 + jz one_more_pix + pushf + mov Tmap.WidthModLength, eax + + xor eax, eax + + mov al,[edi] // preread the destination cache line + +// Make ESI = DV:DU in 6:10,6:10 format + mov eax, Tmap.DeltaU + shr eax, 6 + mov esi, Tmap.DeltaV + shl esi, 10 + mov si, ax + mov Tmap.DeltaUFrac, esi + +// Make ECX = V:U in 6:10,6:10 format + mov eax, Tmap.UFixed + shr eax, 6 + mov ecx, Tmap.VFixed + shl ecx, 10 + mov cx, ax + + mov esi, Tmap.fx_w + + // eax = tmp + // ebx = light + // ecx = V:U in 8.6:10.8 + // edx = zbuffer pointer + // esi = z + // edi = screen data + // ebp = dl_dx + + + +NextPixel: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + // pixel 1 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+1], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + + + add edi, 2 + add edx, 8 + dec Tmap.WidthModLength + jg NextPixel + + popf + jnc FPUReturn + +one_more_pix: + // pixel 0 + mov eax, ecx // EAX = V.VF:U.UF in 6.10:6.10 + shr ax, 10 // EAX = V:U in 6.10:16.0 + rol eax, 6 // EAX = V:U in 0.0:6:6 + and eax, 0fffh // clear upper bits + add eax, Tmap.pixptr // EAX = (V*64)+U + Pixptr + + mov al, [eax] + mov ah, bh + and eax, 0ffffh // clear upper bits + mov al, gr_fade_table[eax] + mov [edi+0], al + add ecx, Tmap.DeltaUFrac + add ebx, ebp + + +FPUReturn: + + // busy FPU registers: // st0 st1 st2 st3 st4 st5 st6 st7 + // xxx xxx xxx xxx xxx xxx xxx + ffree st(0) + ffree st(1) + ffree st(2) + ffree st(3) + ffree st(4) + ffree st(5) + ffree st(6) + + fldcw Tmap.OldFPUCW // restore the FPU + + pop edi + pop esi + pop ebp + pop ebx + pop edx + pop ecx + pop eax + } +#endif +} + diff --git a/src/helped/helped.cpp b/src/helped/helped.cpp new file mode 100644 index 0000000..38617b3 --- /dev/null +++ b/src/helped/helped.cpp @@ -0,0 +1,157 @@ +// HelpEd.cpp : Defines the class behaviors for the application. +// + +#include "stdafx.h" +#include "helped.h" + +#include "mainfrm.h" +#include "helpeddoc.h" +#include "helpedview.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdApp + +BEGIN_MESSAGE_MAP(CHelpEdApp, CWinApp) + //{{AFX_MSG_MAP(CHelpEdApp) + ON_COMMAND(ID_APP_ABOUT, OnAppAbout) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG_MAP + // Standard file based document commands + ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) + ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdApp construction + +CHelpEdApp::CHelpEdApp() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only CHelpEdApp object + +CHelpEdApp theApp; + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdApp initialization + +BOOL CHelpEdApp::InitInstance() +{ + // Standard initialization + // If you are not using these features and wish to reduce the size + // of your final executable, you should remove from the following + // the specific initialization routines you do not need. + +#ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL +#else + Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif + + // Change the registry key under which our settings are stored. + // TODO: You should modify this string to be something appropriate + // such as the name of your company or organization. + SetRegistryKey(_T("Local AppWizard-Generated Applications")); + + LoadStdProfileSettings(); // Load standard INI file options (including MRU) + + // Register the application's document templates. Document templates + // serve as the connection between documents, frame windows and views. + + CSingleDocTemplate* pDocTemplate; + pDocTemplate = new CSingleDocTemplate( + IDR_MAINFRAME, + RUNTIME_CLASS(CHelpEdDoc), + RUNTIME_CLASS(CMainFrame), // main SDI frame window + RUNTIME_CLASS(CHelpEdView)); + AddDocTemplate(pDocTemplate); + + // Enable DDE Execute open + EnableShellOpen(); + RegisterShellFileTypes(TRUE); + + // Parse command line for standard shell commands, DDE, file open + CCommandLineInfo cmdInfo; + ParseCommandLine(cmdInfo); + + // Dispatch commands specified on the command line + if (!ProcessShellCommand(cmdInfo)) + return FALSE; + + // The one and only window has been initialized, so show and update it. + m_pMainWnd->ShowWindow(SW_SHOW); + m_pMainWnd->UpdateWindow(); + + // Enable drag/drop open + m_pMainWnd->DragAcceptFiles(); + + return TRUE; +} + + +///////////////////////////////////////////////////////////////////////////// +// CAboutDlg dialog used for App About + +class CAboutDlg : public CDialog +{ +public: + CAboutDlg(); + +// Dialog Data + //{{AFX_DATA(CAboutDlg) + enum { IDD = IDD_ABOUTBOX }; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAboutDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + //{{AFX_MSG(CAboutDlg) + // No message handlers + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) +{ + //{{AFX_DATA_INIT(CAboutDlg) + //}}AFX_DATA_INIT +} + +void CAboutDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAboutDlg) + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) + //{{AFX_MSG_MAP(CAboutDlg) + // No message handlers + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +// App command to run the dialog +void CHelpEdApp::OnAppAbout() +{ + CAboutDlg aboutDlg; + aboutDlg.DoModal(); +} + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdApp message handlers + diff --git a/src/helped/helped.rc b/src/helped/helped.rc new file mode 100644 index 0000000..c72bbf2 --- /dev/null +++ b/src/helped/helped.rc @@ -0,0 +1,357 @@ +//Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// + +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +#ifdef APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif //_WIN32\r\n" + "#include ""res\\HelpEd.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +IDR_MAINFRAME ICON DISCARDABLE "res\\HelpEd.ico" +IDR_TABLETYPE ICON DISCARDABLE "res\\HelpEdDoc.ico" +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDR_MAINFRAME BITMAP MOVEABLE PURE "res\\Toolbar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Toolbar +// + +IDR_MAINFRAME TOOLBAR DISCARDABLE 16, 15 +BEGIN + BUTTON ID_FILE_NEW + BUTTON ID_FILE_OPEN + BUTTON ID_FILE_SAVE + SEPARATOR + BUTTON ID_EDIT_CUT + BUTTON ID_EDIT_COPY + BUTTON ID_EDIT_PASTE + SEPARATOR + BUTTON ID_FILE_PRINT + SEPARATOR + BUTTON ID_APP_ABOUT +END + + + + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_MAINFRAME MENU PRELOAD DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&New\tCtrl+N", ID_FILE_NEW + MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE + MENUITEM "Save &As...", ID_FILE_SAVE_AS + MENUITEM SEPARATOR + MENUITEM "Recent File", ID_FILE_MRU_FILE1,GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", ID_APP_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO + MENUITEM SEPARATOR + MENUITEM "Cu&t\tCtrl+X", ID_EDIT_CUT + MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY + MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE + END + POPUP "&View" + BEGIN + MENUITEM "&Toolbar", ID_VIEW_TOOLBAR + END + POPUP "&Help" + BEGIN + MENUITEM "&About HelpEd...", ID_APP_ABOUT + END +END + + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_MAINFRAME ACCELERATORS PRELOAD MOVEABLE PURE +BEGIN + "N", ID_FILE_NEW, VIRTKEY,CONTROL + "O", ID_FILE_OPEN, VIRTKEY,CONTROL + "S", ID_FILE_SAVE, VIRTKEY,CONTROL + "Z", ID_EDIT_UNDO, VIRTKEY,CONTROL + "X", ID_EDIT_CUT, VIRTKEY,CONTROL + "C", ID_EDIT_COPY, VIRTKEY,CONTROL + "V", ID_EDIT_PASTE, VIRTKEY,CONTROL + VK_BACK, ID_EDIT_UNDO, VIRTKEY,ALT + VK_DELETE, ID_EDIT_CUT, VIRTKEY,SHIFT + VK_INSERT, ID_EDIT_COPY, VIRTKEY,CONTROL + VK_INSERT, ID_EDIT_PASTE, VIRTKEY,SHIFT + VK_F6, ID_NEXT_PANE, VIRTKEY + VK_F6, ID_PREV_PANE, VIRTKEY,SHIFT +END + + + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 235, 55 +CAPTION "About HelpEd" +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Sans Serif" +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20 + LTEXT "HelpEd Version 1.0",IDC_STATIC,40,10,119,8, + SS_NOPREFIX + LTEXT "Copyright (C) 1999",IDC_STATIC,40,25,119,8 + DEFPUSHBUTTON "OK",IDOK,178,7,50,14,WS_GROUP +END + + +IDR_MAINFRAME DIALOG DISCARDABLE 0, 0, 330, 16 +STYLE WS_CHILD +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "TODO: layout dialog bar ",IDC_STATIC,12,4, + 300,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "HelpEd MFC Application\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "HelpEd\0" + VALUE "LegalCopyright", "Copyright (C) 1999\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename","HelpEd.EXE\0" + VALUE "ProductName", "HelpEd Application\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 48 + END +END +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDR_MAINFRAME "HelpEd\n\nTable\nTable Files (*.tbl)\n.tbl\nHelpEd.Document\nTable Document" + +END +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + AFX_IDS_APP_TITLE "HelpEd" + AFX_IDS_IDLEMESSAGE "Ready" +END +STRINGTABLE DISCARDABLE +BEGIN + ID_INDICATOR_EXT "EXT" + ID_INDICATOR_CAPS "CAP" + ID_INDICATOR_NUM "NUM" + ID_INDICATOR_SCRL "SCRL" + ID_INDICATOR_OVR "OVR" + ID_INDICATOR_REC "REC" +END +STRINGTABLE DISCARDABLE +BEGIN + ID_FILE_NEW "Create a new document\nNew" + ID_FILE_OPEN "Open an existing document\nOpen" + ID_FILE_CLOSE "Close the active document\nClose" + ID_FILE_SAVE "Save the active document\nSave" + ID_FILE_SAVE_AS "Save the active document with a new name\nSave As" + ID_APP_ABOUT "Display program information, version number and copyright\nAbout" + ID_APP_EXIT "Quit the application; prompts to save documents\nExit" + ID_FILE_MRU_FILE1 "Open this document" + ID_FILE_MRU_FILE2 "Open this document" + ID_FILE_MRU_FILE3 "Open this document" + ID_FILE_MRU_FILE4 "Open this document" + ID_FILE_MRU_FILE5 "Open this document" + ID_FILE_MRU_FILE6 "Open this document" + ID_FILE_MRU_FILE7 "Open this document" + ID_FILE_MRU_FILE8 "Open this document" + ID_FILE_MRU_FILE9 "Open this document" + ID_FILE_MRU_FILE10 "Open this document" + ID_FILE_MRU_FILE11 "Open this document" + ID_FILE_MRU_FILE12 "Open this document" + ID_FILE_MRU_FILE13 "Open this document" + ID_FILE_MRU_FILE14 "Open this document" + ID_FILE_MRU_FILE15 "Open this document" + ID_FILE_MRU_FILE16 "Open this document" + ID_NEXT_PANE "Switch to the next window pane\nNext Pane" + ID_PREV_PANE "Switch back to the previous window pane\nPrevious Pane" + ID_WINDOW_SPLIT "Split the active window into panes\nSplit" + ID_EDIT_CLEAR "Erase the selection\nErase" + ID_EDIT_CLEAR_ALL "Erase everything\nErase All" + ID_EDIT_COPY "Copy the selection and put it on the Clipboard\nCopy" + ID_EDIT_CUT "Cut the selection and put it on the Clipboard\nCut" + ID_EDIT_FIND "Find the specified text\nFind" + ID_EDIT_PASTE "Insert Clipboard contents\nPaste" + ID_EDIT_REPEAT "Repeat the last action\nRepeat" + ID_EDIT_REPLACE "Replace specific text with different text\nReplace" + ID_EDIT_SELECT_ALL "Select the entire document\nSelect All" + ID_EDIT_UNDO "Undo the last action\nUndo" + ID_EDIT_REDO "Redo the previously undone action\nRedo" + ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" +END + +STRINGTABLE DISCARDABLE +BEGIN + AFX_IDS_SCSIZE "Change the window size" + AFX_IDS_SCMOVE "Change the window position" + AFX_IDS_SCMINIMIZE "Reduce the window to an icon" + AFX_IDS_SCMAXIMIZE "Enlarge the window to full size" + AFX_IDS_SCNEXTWINDOW "Switch to the next document window" + AFX_IDS_SCPREVWINDOW "Switch to the previous document window" + AFX_IDS_SCCLOSE "Close the active window and prompts to save the documents" + AFX_IDS_SCRESTORE "Restore the window to normal size" + AFX_IDS_SCTASKLIST "Activate Task List" +END + + +#endif + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +#include "res\\HelpEd.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#endif + +#endif // not APSTUDIO_INVOKED diff --git a/src/helped/helpeddoc.cpp b/src/helped/helpeddoc.cpp new file mode 100644 index 0000000..26fa472 --- /dev/null +++ b/src/helped/helpeddoc.cpp @@ -0,0 +1,115 @@ +// HelpEdDoc.cpp : implementation of the CHelpEdDoc class +// + +#include "stdafx.h" +#include "helped.h" + +#include "helpeddoc.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdDoc + +IMPLEMENT_DYNCREATE(CHelpEdDoc, CDocument) + +BEGIN_MESSAGE_MAP(CHelpEdDoc, CDocument) + //{{AFX_MSG_MAP(CHelpEdDoc) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdDoc construction/destruction + +CHelpEdDoc::CHelpEdDoc() +{ + // TODO: add one-time construction code here + +} + +CHelpEdDoc::~CHelpEdDoc() +{ +} + +BOOL CHelpEdDoc::OnNewDocument() +{ + if (!CDocument::OnNewDocument()) + return FALSE; + + // TODO: add reinitialization code here + // (SDI documents will reuse this document) + + return TRUE; +} + + + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdDoc serialization + +void CHelpEdDoc::Serialize(CArchive& ar) +{ + if (ar.IsStoring()) + { + // TODO: add storing code here + } + else + { + // TODO: add loading code here + } +} + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdDoc diagnostics + +#ifdef _DEBUG +void CHelpEdDoc::AssertValid() const +{ + CDocument::AssertValid(); +} + +void CHelpEdDoc::Dump(CDumpContext& dc) const +{ + CDocument::Dump(dc); +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdDoc commands + +HelpEdLine * CHelpEdDoc::AddLine(CPoint pointFrom, CPoint pointTo) +{ + // create a new line + HelpEdLine *newline = new HelpEdLine(pointFrom, pointTo); + try { + // add this line to object array + line_array.Add(newline); + // mark document dirty + SetModifiedFlag(); + } + // check for memory exception + catch (CMemoryException* addlineerror) { + // display error message in pop box + AfxMessageBox("Out of memory", MB_ICONSTOP | MB_OK); + // if we created a HelpEdLine object, we should delete it + if (newline) { + delete newline; + newline = NULL; + } + // delete the error message + addlineerror->Delete(); + } + return newline; +} + +int CHelpEdDoc::get_line_count() +{ + // return line_array size + return line_array.GetSize(); +} diff --git a/src/helped/helpedline.cpp b/src/helped/helpedline.cpp new file mode 100644 index 0000000..10f7f27 --- /dev/null +++ b/src/helped/helpedline.cpp @@ -0,0 +1,41 @@ +// HelpEdLine.cpp: implementation of the HelpEdLine class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "helped.h" +#include "helpedline.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +HelpEdLine::HelpEdLine() +{ + +} + +HelpEdLine::~HelpEdLine() +{ + +} + +HelpEdLine::HelpEdLine(CPoint point_from, CPoint point_to) +{ + // intiliaze start and ending of a line + line_start = point_from; + line_end = point_to; +} + +void HelpEdLine::Draw(CDC *pDC) +{ + // draw the line + pDC->MoveTo(line_start); + pDC->LineTo(line_end); +} diff --git a/src/helped/helpedview.cpp b/src/helped/helpedview.cpp new file mode 100644 index 0000000..9d441d1 --- /dev/null +++ b/src/helped/helpedview.cpp @@ -0,0 +1,100 @@ +// HelpEdView.cpp : implementation of the CHelpEdView class +// + +#include "stdafx.h" +#include "helped.h" + +#include "helpeddoc.h" +#include "helpedview.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdView + +IMPLEMENT_DYNCREATE(CHelpEdView, CView) + +BEGIN_MESSAGE_MAP(CHelpEdView, CView) + //{{AFX_MSG_MAP(CHelpEdView) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdView construction/destruction + +CHelpEdView::CHelpEdView() +{ + // TODO: add construction code here + +} + +CHelpEdView::~CHelpEdView() +{ +} + +BOOL CHelpEdView::PreCreateWindow(CREATESTRUCT& cs) +{ + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + return CView::PreCreateWindow(cs); +} + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdView drawing + +void CHelpEdView::OnDraw(CDC* pDC) +{ + pDC; + CHelpEdDoc* pDoc = GetDocument(); + ASSERT_VALID(pDoc); + // TODO: add draw code for native data here +} + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdView diagnostics + +#ifdef _DEBUG +void CHelpEdView::AssertValid() const +{ + CView::AssertValid(); +} + +void CHelpEdView::Dump(CDumpContext& dc) const +{ + CView::Dump(dc); +} + +CHelpEdDoc* CHelpEdView::GetDocument() // non-debug version is inline +{ + ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CHelpEdDoc))); + return (CHelpEdDoc*)m_pDocument; +} +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CHelpEdView message handlers + +void CHelpEdView::OnLButtonDown(UINT nFlags, CPoint point) +{ + // Capture the mouse, so no other application can + // grab it if the mouse leaves the window area + SetCapture(); + // Save the point + m_ptPrevPos = point; + + // pass mouse event up the chain + CView::OnLButtonDown(nFlags, point); +} + + +//DEL void CHelpEdView::blah() +//DEL { +//DEL +//DEL } diff --git a/src/helped/mainfrm.cpp b/src/helped/mainfrm.cpp new file mode 100644 index 0000000..3a8dbe2 --- /dev/null +++ b/src/helped/mainfrm.cpp @@ -0,0 +1,105 @@ +// MainFrm.cpp : implementation of the CMainFrame class +// + +#include "stdafx.h" +#include "helped.h" + +#include "mainfrm.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame + +IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd) + +BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) + //{{AFX_MSG_MAP(CMainFrame) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code ! + ON_WM_CREATE() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame construction/destruction + +CMainFrame::CMainFrame() +{ + // TODO: add member initialization code here + +} + +CMainFrame::~CMainFrame() +{ +} + +int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) +{ + if (CFrameWnd::OnCreate(lpCreateStruct) == -1) + return -1; + + if (!m_wndToolBar.CreateEx(this) || + !m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) + { + TRACE0("Failed to create toolbar\n"); + return -1; // fail to create + } + if (!m_wndDlgBar.Create(this, IDR_MAINFRAME, + CBRS_ALIGN_TOP, AFX_IDW_DIALOGBAR)) + { + TRACE0("Failed to create dialogbar\n"); + return -1; // fail to create + } + + if (!m_wndReBar.Create(this) || + !m_wndReBar.AddBar(&m_wndToolBar) || + !m_wndReBar.AddBar(&m_wndDlgBar)) + { + TRACE0("Failed to create rebar\n"); + return -1; // fail to create + } + + // TODO: Remove this if you don't want tool tips + m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() | + CBRS_TOOLTIPS | CBRS_FLYBY); + + return 0; +} + +BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) +{ + if( !CFrameWnd::PreCreateWindow(cs) ) + return FALSE; + // TODO: Modify the Window class or styles here by modifying + // the CREATESTRUCT cs + + cs.style = WS_OVERLAPPED | WS_CAPTION | FWS_ADDTOTITLE + | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame diagnostics + +#ifdef _DEBUG +void CMainFrame::AssertValid() const +{ + CFrameWnd::AssertValid(); +} + +void CMainFrame::Dump(CDumpContext& dc) const +{ + CFrameWnd::Dump(dc); +} + +#endif //_DEBUG + +///////////////////////////////////////////////////////////////////////////// +// CMainFrame message handlers + diff --git a/src/helped/res/helped.ico b/src/helped/res/helped.ico new file mode 100644 index 0000000000000000000000000000000000000000..7eef0bcbe6580a6f464d688906172c2d9de44262 GIT binary patch literal 1078 zcmc&zF>b>!3}jLb9s)T}@Kod(893@u8ajANzT`op9^o+)S?=nU(FD@%0s)Sg^oyC8{H z9myetc;MEP)59v(LMa~xK8Yu^jIR*H22uCFiq5%C{s7(PJi>o15i^bmX4(vPxWAio z9ryY#AU_jfnd047-@`)XzL?%iS$gQyFP{44kS9X)fN{{QoL~hO-&=q&20Zr*cxFAt PkaNE{wR~2C$NfnjhSXWT literal 0 HcmV?d00001 diff --git a/src/helped/res/helped.rc2 b/src/helped/res/helped.rc2 new file mode 100644 index 0000000..f390769 --- /dev/null +++ b/src/helped/res/helped.rc2 @@ -0,0 +1,13 @@ +// +// HELPED.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/src/helped/res/helpeddoc.ico b/src/helped/res/helpeddoc.ico new file mode 100644 index 0000000000000000000000000000000000000000..2a1f1ae6ef15e51df8c39bc028bbfb2171822ba5 GIT binary patch literal 1078 zcmcJNF;c@Y5JlGsgIpoERJdY%i*S@2!JS&si6f-)RXoFGDAfg5;z_dQBoi_)1DpI^ z{oS?KlD%25H@>BZ{KJV|_dD9_G1MV<{5a&-}7^W%4AM)k- zx&P*V(j}a@*Y~UhksXTAK!NRyiYN-8NMyKz<)2v9@tUao7A!g+SzkAcsHvdq6!0vQ z#-rA6>0BAr)4*u6Y57EdkamnXf Uh-a7VEuQ2KJb_2>o71HC3-%7w@Bjb+ literal 0 HcmV?d00001 diff --git a/src/helped/res/toolbar.bmp b/src/helped/res/toolbar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d501723c1ceb781cccf04bc42408a2bee6b0eab4 GIT binary patch literal 1078 zcmb_byK2Kg5WFD46)N-_DGd%mbvRHJ@+(`2Y#@z5De^h$T}Yp#;yM?~%&rw%!DZII z_s!1Ct^M-)Tn3AG^p9vgT8;J){9rDyxEsA+?t8wt)o$n@0`>@NK8bGHkdKdb4m#tglAgQo-)Js1!#;+~GSl z37z4Z<|(I?jV44Q_xOeQ!(IFmLY86pVjciLPfC9(?0)$t7hw7W^>>N<@a4LVKPMb~ ze5>=j|BL>!3lPen5hUo~ANKN`l7C&3 z%0PPq3XFRT-e1OO0*EKjOA>DIfd!JKE>l7s8ZOWoALnDl7bEaBMvn{Ll0`lMj_p*2 zeyj@cEB=w(f%+@Ig9SfK=ePQ9c)El>T>d0F2ei{lnHqPg}It<8 literal 0 HcmV?d00001 diff --git a/src/helped/stdafx.cpp b/src/helped/stdafx.cpp new file mode 100644 index 0000000..6d9629a --- /dev/null +++ b/src/helped/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// HelpEd.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + + + diff --git a/src/helped/tga.cpp b/src/helped/tga.cpp new file mode 100644 index 0000000..8b40aeb --- /dev/null +++ b/src/helped/tga.cpp @@ -0,0 +1,27 @@ +// Tga.cpp: implementation of the CTga class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "helped.h" +#include "tga.h" + +#ifdef _DEBUG +#undef THIS_FILE +static char THIS_FILE[]=__FILE__; +#define new DEBUG_NEW +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CTga::CTga() +{ + +} + +CTga::~CTga() +{ + +} diff --git a/src/hud/hud.cpp b/src/hud/hud.cpp new file mode 100644 index 0000000..b047c61 --- /dev/null +++ b/src/hud/hud.cpp @@ -0,0 +1,3156 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUD.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module that contains all the HUD functions at a high level + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 58 10/28/99 2:04a Jefff + * some german specific coords. + * + * 57 10/25/99 5:43p Jefff + * added (and subsequently commented) some scoring debug code. checked in + * to work on later + * + * 56 9/09/99 3:55a Andsager + * Reset Hud_support_objnum to -1 when guage stops displaying + * + * 55 9/01/99 11:16a Andsager + * Fix bug where support ship guage would not show up if second support + * ship called in whlile 1st one dying. + * + * 54 8/23/99 1:49p Dave + * Fixed damage popup (hopefully) + * + * 53 8/23/99 11:34a Dave + * Fixed shield intensity rendering problems. + * + * 52 8/19/99 6:16p Jefff + * + * 51 8/17/99 7:15p Jefff + * auto-target & auto-speed text drawn in code + * + * 50 8/16/99 4:04p Dave + * Big honking checkin. + * + * 49 8/09/99 3:47p Dave + * Fixed incorrect nebula regeneration. Default HUD to low-contrast in + * non-nebula missions. + * + * 48 8/09/99 3:14p Dave + * Make "launch" warning gauge draw in code. + * + * 47 8/05/99 2:05a Dave + * Fixes. Optimized detail level stuff. + * + * 46 8/04/99 2:56p Jefff + * fixed black box behind pilot head in hi-res + * + * 45 8/04/99 9:54a Andsager + * Auto target turrets on big ships. + * + * 44 8/01/99 12:39p Dave + * Added HUD contrast control key (for nebula). + * + * 43 7/31/99 4:15p Dave + * Fixed supernova particle velocities. Handle OBJ_NONE in target + * monitoring view. Properly use objectives notify gauge colors. + * + * 42 7/31/99 1:16p Dave + * Use larger font for 1024 HUD flash text box. Make beam weapons aware of + * weapon subsystem damage on firing ship. + * + * 41 7/26/99 10:41a Jefff + * added call to hud_maybe_show_damage() in hud_render_2d(). not sure how + * this got out in the 1st place. + * + * 40 7/24/99 1:54p Dave + * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert + * missions. + * + * 39 7/22/99 4:00p Dave + * Fixed beam weapon muzzle glow rendering. Externalized hud shield info. + * + * 38 7/21/99 8:10p Dave + * First run of supernova effect. + * + * 37 7/21/99 3:19p Jefff + * adjusted subspace and red alert popup text coords + * + * 36 7/19/99 2:13p Dave + * Added some new strings for Heiko. + * + * 35 7/19/99 11:48a Jefff + * Countermeasure success sound added + * + * 34 7/16/99 12:22p Jefff + * Added sound FX to objective popups + * + * 33 7/15/99 7:16p Jefff + * Red Alert box is now red + * + * 32 7/09/99 12:00a Andsager + * Added target box with distance for remote detonate weapons + * + * 31 6/28/99 4:33p Jasenw + * Fixed coords for hi res engine wash gauge + * + * 30 6/11/99 11:13a Dave + * last minute changes before press tour build. + * + * 29 6/10/99 3:43p Dave + * Do a better job of syncing text colors to HUD gauges. + * + * 28 6/08/99 1:14a Dave + * Multi colored hud test. + * + * 27 6/07/99 4:20p Andsager + * Add HUD color for tagged object. Apply to target and radar. + * + * 26 5/28/99 5:36p Andsager + * stupid comment + * + * 25 5/28/99 10:00a Andsager + * Make player hud target affected by Nebula range + * + * 24 5/22/99 5:35p Dave + * Debrief and chatbox screens. Fixed small hi-res HUD bug. + * + * 23 5/21/99 5:36p Andsager + * Put in high res engine wash gauge and approx coords + * + * 22 5/21/99 1:44p Andsager + * Add engine wash gauge + * + * 21 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 20 2/25/99 4:19p Dave + * Added multiplayer_beta defines. Added cd_check define. Fixed a few + * release build warnings. Added more data to the squad war request and + * response packets. + * + * 19 2/24/99 4:02p Dave + * Fixed weapon locking and homing problems for multiplayer dogfight mode. + * + * 18 2/17/99 2:10p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 17 2/03/99 8:37a Jasen + * Fixed dock in coords + * + * 16 2/01/99 9:24a Jasen + * Fixed subspace and objectives displays for hi res. + * + * 15 1/25/99 5:03a Dave + * First run of stealth, AWACS and TAG missile support. New mission type + * :) + * + * 14 1/21/99 9:28p Dave + * Fixed damage gauge coords. + * + * 13 1/07/99 9:05a Jasen + * coords, coords, coords + * + * 12 1/06/99 3:24p Dave + * Fixed stupid code. + * + * 11 1/06/99 3:14p Jasen + * more new coords + * + * 10 1/06/99 2:33p Jasen + * updated coords + * + * 9 1/06/99 1:27p Dave + * Removed duplicate global var. + * + * 8 1/06/99 1:26p Dave + * Put in seperate X coords for "dock in" and the associated time value + * for the support ship gauge. + * + * 7 12/28/98 3:17p Dave + * Support for multiple hud bitmap filenames for hi-res mode. + * + * 6 12/21/98 5:02p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 5 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 4 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 223 8/28/98 3:28p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 222 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 221 8/09/98 4:45p Lawrance + * center various HUD text - fixes problems in the German version + * + * 220 6/18/98 10:10a Allender + * fixed compiler warnings + * + * 219 6/17/98 11:03a Lawrance + * position subspace notify correctly for german version + * + * 218 6/13/98 10:48p Lawrance + * Changed code to utilize proper fixed-space 1 character. + * + * 217 6/13/98 6:01p Hoffoss + * Externalized all new (or forgot to be added) strings to all the code. + * + * 216 6/12/98 2:49p Dave + * Patch 1.02 changes. + * + * 215 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 214 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 213 5/23/98 4:14p John + * Added code to preload textures to video card for AGP. Added in code + * to page in some bitmaps that weren't getting paged in at level start. + * + * 212 5/17/98 3:32p Lawrance + * Allow red alert orders to get downloaded when in an out-of-cockpit view + * + * 211 5/15/98 8:36p Lawrance + * Add 'target ship that last sent transmission' target key + * + * 210 5/10/98 5:28p Lawrance + * Ensure hud messages and talking heads show up when viewing from another + * ship + * + * 209 5/10/98 12:11a Lawrance + * Fix a couple of problems with 2D gauges showing up in external views + * + * 208 5/09/98 4:52p Lawrance + * Implement padlock view (up/rear/left/right) + * + * 207 5/09/98 12:20a Lawrance + * Show hud messages in all views + * + * 206 5/08/98 5:32p Lawrance + * Allow cargo scanning even if target gauge is disabled + * + * 205 5/08/98 10:13a Lawrance + * Don't allow targeting of ships that have SF_EXPLODED flag set + * + * 204 5/07/98 1:01a Chad + * Yet another hud gauage which shouldn't be rendered as a multiplayer + * observer. + * + * 203 5/04/98 12:08p Ed + * from allender: move hud_target_change_check() after code which does + * possible auto target change. Fixed multiplayer problem where locking + * subsys does not match ship currently targeted + * + * 202 5/04/98 6:12p Lawrance + * Write generic function hud_end_string_at_first_hash_symbol(), to use in + * various spots on the HUD + * + * 201 4/30/98 3:32p Lawrance + * Cull dead/departed ships from escort ship in hud_update_frame() + * + * 200 4/23/98 10:24p Mike + * Int3(), then recover gracefully from some error in which ship to be + * repaired is killed. + * + * $NoKeywords: $ + * +*/ + +#include "pstypes.h" +#include "freespace.h" +#include "systemvars.h" +#include "hud.h" +#include "hudtarget.h" +#include "hudreticle.h" +#include "hudmessage.h" +#include "sound.h" +#include "player.h" +#include "multi.h" +#include "multiutil.h" +#include "gamesnd.h" +#include "hudsquadmsg.h" +#include "timer.h" +#include "eventmusic.h" +#include "hudlock.h" +#include "hudets.h" +#include "2d.h" +#include "3d.h" +#include "ai.h" +#include "aigoals.h" +#include "hudescort.h" +#include "hudshield.h" +#include "linklist.h" +#include "hudtargetbox.h" +#include "missionmessage.h" +#include "missiontraining.h" +#include "bmpman.h" +#include "radar.h" +#include "hudobserver.h" +#include "hudtargetbox.h" +#include "hudconfig.h" +#include "missiongoals.h" +#include "asteroid.h" +#include "starfield.h" +#include "hudwingmanstatus.h" +#include "multi_voice.h" +#include "multi_pmsg.h" +#include "redalert.h" +#include "emp.h" +#include "alphacolors.h" +#include "localize.h" +#include "supernova.h" +#include "font.h" + +// new values for HUD alpha +#define HUD_NEW_ALPHA_DIM 80 +#define HUD_NEW_ALPHA_NORMAL 120 +#define HUD_NEW_ALPHA_BRIGHT 220 + +// high contrast +#define HUD_NEW_ALPHA_DIM_HI 130 +#define HUD_NEW_ALPHA_NORMAL_HI 190 +#define HUD_NEW_ALPHA_BRIGHT_HI 255 + +// globals that will control the color of the HUD gauges +int HUD_color_red = 0; +int HUD_color_green = 255; +int HUD_color_blue = 0; +int HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT; // 1 -> HUD_COLOR_ALPHA_USER_MAX + +int HUD_contrast = 0; // high or lo contrast (for nebula, etc) + +color HUD_color_defaults[HUD_NUM_COLOR_LEVELS]; // array of colors with different alpha blending +color HUD_color_debug; // grey debug text shown on HUD + +static int Player_engine_snd_loop = -1; + +// animations for damages gauges +hud_anim Target_static; +hud_anim Radar_static; + +// HUD render frame offsets +float HUD_offset_x = 0.0f; +float HUD_offset_y = 0.0f; + +// Global: integrity of player's target +float Pl_target_integrity; + +static int Hud_last_can_target; // whether Player is able to target in the last frame +static int Hud_can_target_timer; // timestamp to allow target gauge to draw static once targeting functions are not allowed + +// centered text message gauges (collision, emp, etc) +char Hud_text_flash[512] = ""; +int Hud_text_flash_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + -1, 172 + }, + { // GR_1024 + -1, 275 + } +}; +void hud_init_text_flash_gauge(); +void hud_start_text_flash(char *txt, int t); +void hud_maybe_show_text_flash_icon(); + + +// multiplayer messaging text +int Multi_msg_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 5, 150 + }, + { // GR_1024 + 8, 240 + } +}; + +// multiplayer voice stuff +int Voice_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 5, 165 + }, + { // GR_1024 + 8, 255 + } +}; + +// redalert downloading new orders text +int Red_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + -1, 116 + }, + { // GR_1024 + -1, 186 + } +}; +int Red_text_val_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + -1, 124 + }, + { // GR_1024 + -1, 194 + } +}; + +// subspace popup +int Subspace_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + -1, 116 + }, + { // GR_1024 + -1, 186 + } +}; +int Subspace_text_val_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 100, 124 + }, + { // GR_1024 + 140, 194 + } +}; + +// message text coords +int Head_message_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 7, 37 + }, + { // GR_1024 + 11, 57 + } +}; + +// ping text coords +int Ping_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 560, 3 + }, + { // GR_1024 + 896, 5 + } +}; + +// supernova coords +int Supernova_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 100, 100 + }, + { // GR_1024 + 170, 170 + } +}; + +// used to draw the netlag icon on the HUD +hud_frames Netlag_icon; +int Netlag_icon_loaded=0; +int Netlag_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 386, 331 + }, + { // GR_1024 + 627, 529 + } +}; +char Netlag_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "netlag1", + "netlag1" +}; + +// used to draw the kills gauge +hud_frames Kills_gauge; +int Kills_gauge_loaded = 0; +int Kills_gauge_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 497, 361 + }, + { // GR_1024 + 880, 624 + } +}; +int Kills_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 503, 365 + }, + { // GR_1024 + 886, 628 + } +}; + +#if defined(GERMAN_BUILD) + int Kills_text_val_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 615, 365 + }, + { // GR_1024 + 984, 628 + } + }; +#else + int Kills_text_val_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 571, 365 + }, + { // GR_1024 + 954, 628 + } + }; +#endif + +char Kills_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "kills1", + "kills1" +}; + +// used to draw border around a talking head +static hud_frames Head_frame_gauge; +static int Head_frame_gauge_loaded = 0; +int Head_frame_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 5, 35 + }, + { // GR_1024 + 5, 56 + } +}; +char Head_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "head1", + "head1" +}; + +// mission time frame +static hud_frames Mission_time_gauge; +static int Mission_time_gauge_loaded = 0; +int Mission_time_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 587, 448 + }, + { // GR_1024 + 969, 716 + } +}; +int Mission_time_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 591, 452 + }, + { // GR_1024 + 973, 720 + } +}; +int Mission_time_text_val_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 613, 460 + }, + { // GR_640 + 995, 728 + } +}; +char Mission_time_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "time1", + "time1" +}; + +// used to draw the hud support view +static hud_frames Support_view_gauge; +static int Support_view_gauge_loaded = 0; +static int Hud_support_view_active; +static int Hud_support_view_abort; // active when we need to display abort message +static int Hud_support_view_fade; // timer +static int Hud_support_obj_sig, Hud_support_objnum, Hud_support_target_sig; +int Support_view_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 265, 334 + }, + { // GR_1024 + 459, 534 + } +}; +int Support_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 267, 335 + }, + { // GR_1024 + 462, 536 + } +}; +int Support_text_val_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + -1, 348 + }, + { // GR_1024 + -1, 546 + } +}; +int Support_text_dock_coords[GR_NUM_RESOLUTIONS][2] = { // "dock in" x coord + { // GR_640 + 270, -1 + }, + { // GR_1024 + 465, -1 + } +}; +int Support_text_dock_val_coords[GR_NUM_RESOLUTIONS][2] = { // time value for "dock in" x coord + { // GR_640 + 328, -1 + }, + { // GR_1024 + 524, -1 + } +}; +char Support_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "support1", + "support1" +}; + +// damage gauge stuff +#define NUM_DAMAGE_GAUGES 3 +static hud_frames Damage_gauges[NUM_DAMAGE_GAUGES]; +static int Damage_gauges_loaded = 0; +char *Damage_gauge_fnames[GR_NUM_RESOLUTIONS][NUM_DAMAGE_GAUGES] = +{ + //XSTR:OFF + { // GR_640 + "damage1", + "damage2", + "damage3", + }, + { // GR_1024 + "damage1", + "damage2", + "damage3", + } +//XSTR:ON +}; +int Damage_gauge_line_h[GR_NUM_RESOLUTIONS] = { + 9, + 9 +}; +int Damage_gauge_coords[GR_NUM_RESOLUTIONS][2][2] = { + { // GR_640 + {245, 38}, + {245, 63} + }, + { // GR_1024 + {440, 61}, + {440, 86} + } + // These #'s seem to work, although I really don't know why. Frankly, it frightens me, + // because it means the 640 coords _shouldn't_. This may be due to D3D strangeness, so + // we'll have to investigate when we get hi-res Glide in. +}; +int Damage_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 248, 40 + }, + { // GR_1024 + 443, 63 + } +}; +int Hull_integ_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 249, 53 + }, + { // GR_1024 + 444, 76 + } +}; +int Hull_integ_val_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 387, 53 + }, + { // GR_1024 + 582, 76 + }, +}; +int Damage_subsys_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 249, 65 + }, + { // GR_1024 + 444, 88 + } +}; + + +// flashing gauges +#define HUD_GAUGE_FLASH_DURATION 5000 +#define HUD_GAUGE_FLASH_INTERVAL 200 +int HUD_gauge_flash_duration[NUM_HUD_GAUGES]; +int HUD_gauge_flash_next[NUM_HUD_GAUGES]; +int HUD_gauge_bright; + +// Objective display +typedef struct objective_display_info +{ + int display_timer; + int goal_type; + int goal_status; + int goal_ntotal; + int goal_nresolved; + +} objective_display_info; + +static objective_display_info Objective_display; + +static int Objective_display_gauge_inited=0; +static hud_frames Objective_display_gauge; +int Objective_display_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 245, 114 + }, + { // GR_1024 + 436, 184 + } +}; +int Objective_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + -1, 116 + }, + { // GR_1024 + -1, 186 + } +}; +int Objective_text_val_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + -1, 125 + }, + { // GR_1024 + -1, 195 + } +}; +char Objective_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "objective1", + "objective1" +}; + +// Subspace notify display +static int Subspace_notify_active; +static int Objective_notify_active; +static int HUD_abort_subspace_timer = 1; + +// used to track how player subsystems are getting damaged +typedef struct hud_subsys_info +{ + float last_str; + int flash_duration_timestamp; +} hud_subsys_info; + +static hud_subsys_info Pl_hud_subsys_info[SUBSYSTEM_MAX]; +static int Pl_hud_next_flash_timestamp; +static int Pl_hud_is_bright; + +#define SUBSYS_DAMAGE_FLASH_DURATION 1800 +#define SUBSYS_DAMAGE_FLASH_INTERVAL 100 + +// timers used for popup gauges +int HUD_popup_timers[NUM_HUD_GAUGES]; + +// forward declarations +void update_throttle_sound(); +void hud_show_damage_popup(); +void hud_damage_popup_init(); +void hud_support_view_init(); +void hud_gauge_flash_init(); +void hud_objective_message_init(); +void hud_maybe_display_objective_message(); +void hud_stop_subspace_notify(); +void hud_start_subspace_notify(); +void hud_stop_objective_notify(); +void hud_start_objective_notify(); +int hud_subspace_notify_active(); +int hud_objective_notify_active(); +void hud_subspace_notify_abort(); +void hud_maybe_display_subspace_notify(); +void hud_init_netlag_icon(); +void hud_maybe_show_netlag_icon(); +void hud_maybe_display_red_alert(); +void hud_init_kills_gauge(); +void hud_show_kills_gauge(); +int hud_maybe_render_emp_icon(); +void hud_init_emp_icon(); + +// Saturate a value in minv..maxv. +void saturate(int *i, int minv, int maxv) +{ + if (*i < minv) + *i = minv; + else if (*i > maxv) + *i = maxv; +} + +// init the colors used for the different shades of the HUD +void HUD_init_hud_color_array() +{ + int i; + + for ( i = 0; i < HUD_NUM_COLOR_LEVELS; i++ ) { + gr_init_alphacolor( &HUD_color_defaults[i], HUD_color_red, HUD_color_green, HUD_color_blue, (i+1)*16 ); + } +} + +// HUD_init will call all the various HUD gauge init functions. This function is called at the +// start of each mission (level) +void HUD_init_colors() +{ + saturate(&HUD_color_red, 0, 255); + saturate(&HUD_color_green, 0, 255); + saturate(&HUD_color_blue, 0, 255); + saturate(&HUD_color_alpha, 0, HUD_COLOR_ALPHA_USER_MAX); + + gr_init_alphacolor( &HUD_color_debug, 128, 255, 128, HUD_color_alpha*16 ); + HUD_init_hud_color_array(); + + hud_init_targeting_colors(); + hud_gauge_flash_init(); +} + +// The following global data is used to determine if we should change the engine sound. +// We only check if the throttle has changed every THROTTLE_SOUND_CHECK_INTERVAL ms, and +// then we make sure that the throttle has actually changed. If it has changed, we start +// a new sound and/or adjust the volume. This occurs in update_throttle_sound() +// +static float last_percent_throttle; +#define THROTTLE_SOUND_CHECK_INTERVAL 50 // in ms +static int throttle_sound_check_id; + +// used for the display of damaged subsystems +typedef struct hud_subsys_damage +{ + int str; + int type; + char *name; +} hud_subsys_damage; + +#define DAMAGE_FLASH_TIME 150 +static int Damage_flash_bright; +static int Damage_flash_timer; + +// initialize the timers used for popup gauges +void hud_init_popup_timers() +{ + int i; + for (i=0; iship_info_index].species != SPECIES_TERRAN ) { + //return 1; + //} + + return 0; +} + +// Determine if we should popup the weapons gauge on the HUD. +void hud_maybe_popup_weapons_gauge() +{ + if ( hud_gauge_is_popup(HUD_WEAPONS_GAUGE) ) { + ship_weapon *swp = &Player_ship->weapons; + int i; + + for ( i = 0; i < swp->num_secondary_banks; i++ ) { + if ( swp->secondary_bank_ammo[i] > 0 ) { + int ms_till_fire = timestamp_until(swp->next_secondary_fire_stamp[i]); + if ( ms_till_fire >= 1000 ) { + hud_gauge_popup_start(HUD_WEAPONS_GAUGE, 2500); + } + } + } + } +} + +// hud_update_frame() will update hud systems +// +// This function updates those parts of the hud that are not dependant on the +// rendering of the hud. +void hud_update_frame() +{ + object *targetp; + int can_target; + + update_throttle_sound(); + hud_check_reticle_list(); + hud_wingman_status_update(); + + // Check hotkey selections to see if any ships need to be removed + hud_prune_hotkeys(); + + // Remove dead/departed ships from the escort list + hud_escort_cull_list(); + + hud_update_reticle( Player ); + hud_shield_hit_update(); + hud_maybe_popup_weapons_gauge(); + + // if emp is active we have to allow targeting by the "random emp" system + // we will intercept player targeting requests in hud_sensors_ok() when checking key commands + // DB - 8/24/98 + can_target = hud_sensors_ok(Player_ship, 0); + if(emp_active_local()){ + can_target = 1; + } + if ( !can_target && Hud_last_can_target ) { + Hud_can_target_timer = timestamp(1200); + } + Hud_last_can_target = can_target; + + if ( timestamp_elapsed(Hud_can_target_timer) ) { + if ( (Player_ai->target_objnum != -1) && !can_target ){ + Player_ai->target_objnum = -1; + } + } + + // if there is no target, check if auto-targeting is enabled, and select new target + int retarget = 0; + int retarget_turret = 0; + + if (Player_ai->target_objnum == -1){ + retarget = 1; + } else if (Objects[Player_ai->target_objnum].type == OBJ_SHIP) { + if (Ships[Objects[Player_ai->target_objnum].instance].flags & SF_DYING){ + if (timestamp_elapsed(Ships[Objects[Player_ai->target_objnum].instance].final_death_time)) { + retarget = 1; + } + } + } + + // check if big ship and currently selected subsys is turret and turret is dead + // only do this is not retargeting + if ((!retarget) && (Player_ai->target_objnum != -1)) { + if (Objects[Player_ai->target_objnum].type == OBJ_SHIP) { + if ( !(Ships[Objects[Player_ai->target_objnum].instance].flags & SF_DYING) ) { + if ( Ship_info[Ships[Objects[Player_ai->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP) ) { + ship_subsys *ss = Player_ai->targeted_subsys; + if (ss != NULL) { + if ((ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits == 0)) { + retarget_turret = 1; + } + } + } + } + } + } + + if ( retarget && can_target ) { + Player_ai->current_target_is_locked = 0; + if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) { + Player_ai->target_objnum = -1; + hud_target_auto_target_next(); + } + } + + if (retarget_turret && can_target) { + Assert(!retarget); + // get closest weighted live turret + // hud_target_closest(OBJ_INDEX(Player_obj), FALSE, FALSE); + void hud_update_closest_turret(); + hud_update_closest_turret(); + } + + hud_target_change_check(); + + if (Player_ai->target_objnum == -1) { + if ( Target_static_looping != -1 ) { + snd_stop(Target_static_looping); + } + return; + } + + targetp = &Objects[Player_ai->target_objnum]; + + + int stop_targetting_this_thing = 0; + + // check to see if the target is still alive + if ( targetp->flags&OF_SHOULD_BE_DEAD ) { + stop_targetting_this_thing = 1; + } + + Player->target_is_dying = FALSE; + ship *target_shipp = NULL; + + if ( targetp->type == OBJ_SHIP ) { + Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS ); + target_shipp = &Ships[targetp->instance]; + Player->target_is_dying = target_shipp->flags & SF_DYING; + + // If it is warping out (or exploded), turn off targeting + if ( target_shipp->flags & (SF_DEPART_WARP|SF_EXPLODED) ) { + stop_targetting_this_thing = 1; + } + } + + // Check if can still be seen in Nebula + if ( hud_target_invalid_awacs(targetp) ) { + stop_targetting_this_thing = 1; + } + + // If this was found to be something we shouldn't + // target anymore, just remove it + if ( stop_targetting_this_thing ) { + Player_ai->target_objnum = -1; + Player_ai->targeted_subsys = NULL; + hud_stop_looped_locking_sounds(); + } + + if (Player->target_is_dying) { + hud_stop_looped_locking_sounds(); + if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) { + hud_target_auto_target_next(); + } + } + + // Switch to battle track when a targeted ship is hostile and within BATTLE_START_MIN_TARGET_DIST + if (targetp->type == OBJ_SHIP && Event_Music_battle_started == 0 ) { + Assert( target_shipp != NULL ); + + if (opposing_team_mask(Player_ship->team)) { + float dist_to_target; + + dist_to_target = vm_vec_dist_quick(&targetp->pos, &Player_obj->pos); + if (dist_to_target < BATTLE_START_MIN_TARGET_DIST) { + + // If the target has an AI class of none, it is a Cargo, NavBuoy or other non-aggressive + // ship, so don't start the battle music + if (stricmp(Ai_class_names[Ai_info[target_shipp->ai_index].ai_class], NOX("none"))) + event_music_battle_start(); + } + } + } + + // Since we need to reference the player's target integrity in several places this upcoming + // frame, only calculate once here + if ( target_shipp ) { + float initial_hull; + initial_hull = Ship_info[target_shipp->ship_info_index].initial_hull_strength; + if ( initial_hull <= 0 ) { + Int3(); // illegal initial hull strength + Pl_target_integrity = 0.0f; + } else { + Pl_target_integrity = targetp->hull_strength / initial_hull; + if (Pl_target_integrity < 0) + Pl_target_integrity = 0.0f; + } + } + + hud_update_cargo_scan_sound(); + +} + +void HUD_render_forward_icon(object *objp) +{ + vertex v0; + vector p0; + + vm_vec_scale_add(&p0, &objp->pos, &objp->orient.fvec, 100.0f); + g3_rotate_vertex(&v0, &p0); + + gr_set_color(255, 0, 0); + if ((!(v0.flags & PF_OVERFLOW)) && (v0.codes == 0)) // make sure point projected + g3_draw_sphere(&v0, 1.25f); + else if (v0.codes != 0) { // target center is not on screen + // draw the offscreen indicator at the edge of the screen where the target is closest to + hud_draw_offscreen_indicator(&v0, &p0); + } +} + +// Draw white brackets around asteroids which has the AF_DRAW_BRACKETS flag set +void hud_show_asteroid_brackets() +{ + if ( hud_sensors_ok(Player_ship, 0) ) { + asteroid_show_brackets(); + } +} + +// Draw radar gauge on the HUD +void hud_show_radar() +{ + if ( hud_disabled() ) { + return; + } + + if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY ))) { + if ( Game_detail_flags & DETAIL_FLAG_HUD ) { + if ( hud_gauge_active(HUD_RADAR) ) { + HUD_reset_clip(); + radar_frame_render(flFrametime); + } + } + } +} + +// Render model of target in the target view box +void hud_show_target_model() +{ + if ( hud_disabled() ) { + return; + } + + // display the miniature model of the target in the target box and shade + if ( Game_detail_flags & DETAIL_FLAG_HUD ) { + if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY))) + hud_render_target_model(); + } +} + +void hud_show_common_3d_gauges(float frametime, int in_cockpit) +{ + // draw boxes around current selection set, if any + hud_show_selection_set(); + + // draw the targeting data around any message sender + hud_show_message_sender(); + + // draw brackets around asteroids is necessary + hud_show_asteroid_brackets(); + + // draw targetting data around the current target + hud_show_targeting_gauges(frametime, in_cockpit); + + // draw brackets and distance to remote detonate missile + hud_show_remote_detonate_missile(); +} + +// Render gauges that need to be between a g3_start_frame() and a g3_end_frame() +void HUD_render_3d(float frametime) +{ + Player->subsys_in_view = -1; + + if ( hud_disabled() ) { + return; + } + + if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY))) { + + hud_show_common_3d_gauges(frametime, 1); + + // Show all homing missiles locked onto the player. + // Currently not supporting a way to toggle this as I'm not sure we'll stick wtih this gauge. -- MK, 3/17/97. + if ( hud_gauge_active(HUD_MISSILE_WARNING_ARROW) ) { + hud_show_homing_missiles(); + } + + } else if ( Viewer_mode & (VM_CHASE | VM_EXTERNAL | VM_WARP_CHASE | VM_PADLOCK_ANY ) ) { + // If the player is warping out, don't draw the targeting gauges + Assert(Player != NULL); + if ( Player->control_mode != PCM_NORMAL ) { + return; + } + + hud_show_common_3d_gauges(frametime, 0); + } + + if (Viewer_mode & VM_SLEWED) { + HUD_render_forward_icon(Player_obj); + } +} + + +// call from HUD_render_2d() when in gameplay, and call when in navmap +void hud_show_messages() +{ + // draw the message window + hud_show_msg_window(); + hud_show_fixed_text(); +} + +// decide if we want to blit damage status to the screen +void hud_maybe_show_damage() +{ + if ( !hud_gauge_active(HUD_DAMAGE_GAUGE) ) { + return; + } + + // display the current weapon info for the player ship, with ammo/energy counts + if ( hud_gauge_active(HUD_DAMAGE_GAUGE) ) { + int show_gauge_flag; + + if ( (Ship_info[Player_ship->ship_info_index].initial_hull_strength - Player_obj->hull_strength) > 1.0f ) { + show_gauge_flag = 1; + } else { + show_gauge_flag = 0; + } + + // is gauge configured as a popup? + if ( hud_gauge_is_popup(HUD_DAMAGE_GAUGE) ) { + if ( !hud_gauge_popup_active(HUD_DAMAGE_GAUGE) ) { + show_gauge_flag=0; + } + } + + if ( show_gauge_flag ) { + hud_show_damage_popup(); + } + } +} + +// The damage toggle button was pressed, so change state +void hud_damage_popup_toggle() +{ + snd_play(&Snds[SND_SQUADMSGING_ON]); + + // If gague is disabled (off), make it on all the time + if ( !hud_gauge_active(HUD_DAMAGE_GAUGE) ) { + hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,1,0); + return; + } + + // if gauge is popup, turn it off if it is current up, otherwise force it to be up + if ( hud_gauge_is_popup(HUD_DAMAGE_GAUGE) ) { + if ( Player_obj->hull_strength == Ship_info[Player_ship->ship_info_index].initial_hull_strength ) { + hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,1,0); + } else { + hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,0,0); + } + return; + } + + // gauge is on, without any popup... so force it to be off + hud_config_set_gauge_flags(HUD_DAMAGE_GAUGE,0,0); +} + + +// Display the current mission time in MM:SS format +void hud_show_mission_time() +{ + float mission_time, time_comp; + int minutes=0; + int seconds=0; + + mission_time = f2fl(Missiontime); // convert to seconds + + minutes=(int)(mission_time/60); + seconds=(int)mission_time%60; + + hud_set_gauge_color(HUD_MISSION_TIME); + + // blit background frame + if ( Mission_time_gauge.first_frame >= 0 ) { + GR_AABITMAP(Mission_time_gauge.first_frame, Mission_time_coords[gr_screen.res][0], Mission_time_coords[gr_screen.res][1]); + } + + // print out mission time in MM:SS format + gr_printf(Mission_time_text_coords[gr_screen.res][0], Mission_time_text_coords[gr_screen.res][1], NOX("%02d:%02d"), minutes, seconds); + + // display time compression as xN + time_comp = f2fl(Game_time_compression); + if ( time_comp < 1 ) { + gr_printf(Mission_time_text_val_coords[gr_screen.res][0], Mission_time_text_val_coords[gr_screen.res][1], XSTR( "x%.1f", 215), time_comp); + } else { + gr_printf(Mission_time_text_val_coords[gr_screen.res][0], Mission_time_text_val_coords[gr_screen.res][1], XSTR( "x%.0f", 216), time_comp); + } +} + +// If a head animation is playing, then blit a border around it +void hud_maybe_blit_head_border() +{ + if ( Head_frame_gauge.first_frame == -1 ){ + return; + } + + if ( message_anim_is_playing() ) { + // draw frame + // hud_set_default_color(); + hud_set_gauge_color(HUD_TALKING_HEAD); + + GR_AABITMAP(Head_frame_gauge.first_frame, Head_frame_coords[gr_screen.res][0], Head_frame_coords[gr_screen.res][1]); + + // draw title + gr_string(Head_message_coords[gr_screen.res][0], Head_message_coords[gr_screen.res][1], XSTR("message", 217)); + } +} + +// Black out area behind head animation +void hud_maybe_clear_head_area() +{ + if ( Head_frame_gauge.first_frame == -1 ) { + return; + } + + if ( message_anim_is_playing() ) { + // clear + if (gr_screen.res == GR_640) { + HUD_set_clip(7, 45, 160, 120); // these coords are set in MissionMessage.cpp + } else { + HUD_set_clip(7, 66, 160, 120); + } + gr_clear(); + HUD_reset_clip(); + } +} + +void hud_maybe_display_supernova() +{ + float time_left; + + // if there's a supernova coming + time_left = supernova_time_left(); + if(time_left < 0.0f){ + return; + } + + gr_set_color_fast(&Color_bright_red); + gr_printf(Supernova_coords[gr_screen.res][0], Supernova_coords[gr_screen.res][1], "Supernova Warning : %.2f s", time_left); +} + +// render multiplayer ping time to the server if appropriate +void hud_render_multi_ping() +{ + // if we shouldn't be displaying a ping time, return here + if(!multi_show_ingame_ping()){ + return; + } + + // if we're in multiplayer mode, display our ping time to the server + if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + char ping_str[50]; + memset(ping_str,0,50); + + // if our ping is positive, display it + if((Netgame.server != NULL) && (Netgame.server->s_info.ping.ping_avg > 0)){ + // get the string + if(Netgame.server->s_info.ping.ping_avg >= 1000){ + sprintf(ping_str,XSTR("> 1 sec",628)); + } else { + sprintf(ping_str,XSTR("%d ms",629),Netgame.server->s_info.ping.ping_avg); + } + + // blit the string out + hud_set_default_color(); + gr_string(Ping_coords[gr_screen.res][0], Ping_coords[gr_screen.res][1], ping_str); + } + } +} + +// render all the 2D gauges on the HUD +void HUD_render_2d(float frametime) +{ + int show_gauge_flag; + + HUD_reset_clip(); + +/* + // show some scoring debug stuff + { + extern char Scoring_debug_text[]; + gr_string( 10, 40, Scoring_debug_text ); + } +*/ + if ( hud_disabled() ) { + return; + } + + if (!(Viewer_mode & (VM_EXTERNAL | VM_SLEWED | VM_CHASE | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY ))) { + // display Energy Transfer System gauges + if ( hud_gauge_active(HUD_ETS_GAUGE) ) { + show_gauge_flag=1; + // is gauge configured as a popup? + if ( hud_gauge_is_popup(HUD_ETS_GAUGE) ) { + if ( !hud_gauge_popup_active(HUD_ETS_GAUGE) ) { + show_gauge_flag=0; + } + } + + if ( show_gauge_flag ) { + hud_show_ets(); + } + } + + // display afterburner fuel gauge + if ( hud_gauge_active(HUD_AFTERBURNER_ENERGY) ) { + hud_set_gauge_color(HUD_AFTERBURNER_ENERGY); + hud_show_afterburner_gauge(); + } + + // text flash gauge + hud_maybe_show_text_flash_icon(); + + // maybe show the netlag icon + if(Game_mode & GM_MULTIPLAYER){ + hud_maybe_show_netlag_icon(); + + if(Net_player->flags & NETINFO_FLAG_OBSERVER){ + hud_render_observer(); + } + } + + // draw the reticle + hud_show_reticle(); + + /* + // why is this here twice? + // display Energy Transfer System gauges + if ( hud_gauge_active(HUD_ETS_GAUGE) ) { + show_gauge_flag=1; + // is gauge configured as a popup? + if ( hud_gauge_is_popup(HUD_ETS_GAUGE) ) { + if ( !hud_gauge_popup_active(HUD_ETS_GAUGE) ) { + show_gauge_flag=0; + } + } + + if ( show_gauge_flag ) { + hud_show_ets(); + } + } + */ + + // display info on the ships in the escort list + if ( hud_gauge_active(HUD_ESCORT_VIEW) ) { + show_gauge_flag=1; + // is gauge configured as a popup? + if ( hud_gauge_is_popup(HUD_ESCORT_VIEW) ) { + if ( !hud_gauge_popup_active(HUD_ESCORT_VIEW) ) { + show_gauge_flag=0; + } + } + + if ( show_gauge_flag ) { + hud_set_gauge_color(HUD_ESCORT_VIEW); + hud_display_escort(); + } + } + + // display the current weapon info for the player ship, with ammo/energy counts + if ( hud_gauge_active(HUD_WEAPONS_GAUGE) ) { + show_gauge_flag=1; + // is gauge configured as a popup? + if ( hud_gauge_is_popup(HUD_WEAPONS_GAUGE) ) { + if ( !hud_gauge_popup_active(HUD_WEAPONS_GAUGE) ) { + show_gauge_flag=0; + } + } + + if ( show_gauge_flag ) { + hud_show_weapons(); + } + } + + // display player countermeasures count + if ( hud_gauge_active(HUD_CMEASURE_GAUGE) ) { + show_gauge_flag=1; + // is gauge configured as a popup? + if ( hud_gauge_is_popup(HUD_CMEASURE_GAUGE) ) { + if ( !hud_gauge_popup_active(HUD_CMEASURE_GAUGE) ) { + show_gauge_flag=0; + } + } + + if ( show_gauge_flag ) { + hud_show_cmeasure_gague(); + } + } + + if ( hud_gauge_active(HUD_WEAPONS_ENERGY) ) { + hud_show_weapon_energy_gauge(); + } + + // show the auto-target icons + hud_show_auto_icons(); + + // draw a border around a talking head if it is playing + hud_maybe_blit_head_border(); + + // draw the status of support ship servicing the player + hud_support_view_blit(); + + // draw the damage status + hud_maybe_show_damage(); + + // show mission time + if ( hud_gauge_active(HUD_MISSION_TIME) ) { + hud_show_mission_time(); + } + + // show subspace notify gauge + hud_maybe_display_subspace_notify(); + + // show objective status gauge + if ( hud_gauge_active(HUD_OBJECTIVES_NOTIFY_GAUGE) ) { + hud_maybe_display_objective_message(); + } + + if ( hud_gauge_active(HUD_WINGMEN_STATUS) ) { + hud_wingman_status_render(); + } + + if ( hud_gauge_active(HUD_KILLS_GAUGE) ) { + show_gauge_flag=1; + // is gauge configured as a popup? + if ( hud_gauge_is_popup(HUD_KILLS_GAUGE) ) { + if ( !hud_gauge_popup_active(HUD_KILLS_GAUGE) ) { + show_gauge_flag=0; + } + } + + if ( show_gauge_flag ) { + hud_show_kills_gauge(); + } + } + + // show the player shields + if ( hud_gauge_active(HUD_PLAYER_SHIELD_ICON) ) { + hud_shield_show(Player_obj); + } + + // show the directives popup and/or training popup + message_training_display(); + + // if this is a multiplayer game, blit any icons/bitmaps indicating voice recording or playback + if(Game_mode & GM_MULTIPLAYER){ + hud_show_voice_status(); + } + } + + hud_show_messages(); + + // maybe render any necessary multiplayer text messaging strings being entered + hud_maybe_render_multi_text(); + + // show red alert notify gauge when moving to red alert + hud_maybe_display_red_alert(); + + // display supernova warning + hud_maybe_display_supernova(); + + // check to see if we are in messaging mode. If so, send the key to the code + // to deal with the message. hud_sqaudmsg_do_frame will return 0 if the key + // wasn't used in messaging mode, otherwise 1. In the event the key was used, + // return immediately out of this function. + if ( Players->flags & PLAYER_FLAGS_MSG_MODE ) { + if ( hud_squadmsg_do_frame() ){ + return; + } + } + + hud_render_multi_ping(); +} + + +// hud_stop_looped_engine_sounds() +// +// This function will set the loop id's for the engine noises to -1, this will force any +// looping engine sounds to stop. This should only be called when the game decides to +// stop all looping sounds +// + +void hud_stop_looped_engine_sounds() +{ + if ( Player_engine_snd_loop > -1 ) { + snd_stop(Player_engine_snd_loop); + //snd_chg_loop_status(Player_engine_snd_loop, 0); + Player_engine_snd_loop = -1; + } +} + +#define ZERO_PERCENT 0.01f +#define ENGINE_MAX_VOL 1.0f +#define ENGINE_MAX_PITCH 44100 + +void update_throttle_sound() +{ + // determine what engine sound to play + float percent_throttle; +// int throttle_pitch; + + // if we're a multiplayer observer, stop any engine sounds from playing and return + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){ + // stop engine sound if it is playing + if(Player_engine_snd_loop != -1){ + snd_stop(Player_engine_snd_loop); + Player_engine_snd_loop = -1; + } + + // return + return; + } + + if ( timestamp_elapsed(throttle_sound_check_id) ) { + + throttle_sound_check_id = timestamp(THROTTLE_SOUND_CHECK_INTERVAL); + + if ( Ships[Player_obj->instance].current_max_speed == 0 ) { + percent_throttle = Player_obj->phys_info.fspeed / Ship_info[Ships[Player_obj->instance].ship_info_index].max_speed; + } else { + percent_throttle = Player_obj->phys_info.fspeed / Ships[Player_obj->instance].current_max_speed; + } + + // If the throttle has changed, modify the sound + if ( percent_throttle != last_percent_throttle || Player_engine_snd_loop == -1 ) { + + if ( percent_throttle < ZERO_PERCENT ) { + if ( Player_engine_snd_loop > -1 ) { + snd_chg_loop_status(Player_engine_snd_loop, 0); + Player_engine_snd_loop = -1; + } + } + else { + if ( Player_engine_snd_loop == -1 ){ + Player_engine_snd_loop = snd_play_looping( &Snds[SND_ENGINE], 0.0f , -1, -1, percent_throttle * ENGINE_MAX_VOL ); + } else { + // The sound may have been trashed at the low-level if sound channel overflow. + // TODO: implement system where certain sounds cannot be interrupted (priority?) + if ( snd_is_playing(Player_engine_snd_loop) ) { + snd_set_volume(Player_engine_snd_loop, percent_throttle * ENGINE_MAX_VOL); + } + else { + Player_engine_snd_loop = -1; + } + } + } + +// throttle_pitch = snd_get_pitch(Player_engine_snd_loop); +// if ( percent_throttle > 0.5 ) { +// snd_set_pitch(Player_engine_snd_loop, fl2i(22050 + (percent_throttle-0.5f)*1000)); +// } + + } // end if (percent_throttle != last_percent_throttle) + + last_percent_throttle = percent_throttle; + + } // end if ( timestamp_elapsed(throttle_sound_check_id) ) +} + +// called at the beginning of each level. Loads frame data in once, and initializes any damage +// gauge specific data +void hud_damage_popup_init() +{ + int i; + + if ( !Damage_gauges_loaded ) { + for ( i = 0; i < NUM_DAMAGE_GAUGES; i++ ) { + Damage_gauges[i].first_frame = bm_load_animation(Damage_gauge_fnames[gr_screen.res][i], &Damage_gauges[i].num_frames); + if ( Damage_gauges[i].first_frame == -1 ) { + Warning(LOCATION, "Could not load in the ani: %s\n", Damage_gauge_fnames[gr_screen.res][i]); + return; + } + } + Damage_gauges_loaded = 1; + } + + Damage_flash_bright = 0; + Damage_flash_timer = 1; + + for ( i = 0; i < SUBSYSTEM_MAX; i++ ) { + Pl_hud_subsys_info[i].last_str = 1000.0f; + Pl_hud_subsys_info[i].flash_duration_timestamp = 1; + Pl_hud_next_flash_timestamp = 1; + Pl_hud_is_bright = 0; + } +} + +// --------------------------------------------------------- +// show player damage status via a popup window + +void hud_show_damage_popup() +{ + model_subsystem *psub; + ship_subsys *pss; + ship_info *sip; + int sx, sy, bx, by, w, h, screen_integrity, num, best_str, best_index; + float strength, shield, integrity; + char buf[128]; + hud_subsys_damage hud_subsys_list[SUBSYSTEM_MAX]; + + if ( Damage_gauges[0].first_frame == -1 ) { + return; + } + + if ( (The_mission.game_type & MISSION_TYPE_TRAINING) && Training_msg_visible ){ + return; + } + + sip = &Ship_info[Player_ship->ship_info_index]; + hud_get_target_strength(Player_obj, &shield, &integrity); + screen_integrity = fl2i(integrity*100); + + if ( hud_gauge_is_popup(HUD_DAMAGE_GAUGE) ) { + if ( screen_integrity >= 100 ) { + return; + } + } + + if ( timestamp_elapsed(Damage_flash_timer) ) { + Damage_flash_timer = timestamp(DAMAGE_FLASH_TIME); + Damage_flash_bright ^= 1; + } + + hud_set_gauge_color(HUD_DAMAGE_GAUGE); + + // draw the top of the damage pop-up + GR_AABITMAP(Damage_gauges[0].first_frame, Damage_gauge_coords[gr_screen.res][0][0], Damage_gauge_coords[gr_screen.res][0][1]); + gr_string(Damage_text_coords[gr_screen.res][0], Damage_text_coords[gr_screen.res][1], XSTR( "damage", 218)); + + // show hull integrity + if ( screen_integrity < 100 ) { + if ( screen_integrity == 0 ) { + screen_integrity = 1; + } + sprintf(buf, XSTR( "%d%%", 219), screen_integrity); + hud_num_make_mono(buf); + gr_get_string_size(&w, &h, buf); + if ( screen_integrity < 30 ) { + gr_set_color_fast(&Color_red); + } + gr_string(Hull_integ_coords[gr_screen.res][0], Hull_integ_coords[gr_screen.res][1], XSTR( "Hull Integrity", 220)); + gr_string(Hull_integ_val_coords[gr_screen.res][0] - w, Hull_integ_val_coords[gr_screen.res][1], buf); + } + + // show damaged subsystems + sx = Damage_subsys_text_coords[gr_screen.res][0]; + sy = Damage_subsys_text_coords[gr_screen.res][1]; + bx = Damage_gauge_coords[gr_screen.res][1][0]; + by = Damage_gauge_coords[gr_screen.res][1][1]; + + num = 0; + for ( pss = GET_FIRST(&Player_ship->subsys_list); pss !=END_OF_LIST(&Player_ship->subsys_list); pss = GET_NEXT(pss) ) { + psub = pss->system_info; + strength = ship_get_subsystem_strength(Player_ship, psub->type); + if ( strength < 1 ) { + screen_integrity = fl2i(strength*100); + if ( screen_integrity == 0 ) { + if ( strength > 0 ) { + screen_integrity = 1; + } + } + hud_subsys_list[num].name = psub->name; + hud_subsys_list[num].str = screen_integrity; + hud_subsys_list[num].type = psub->type; + num++; + + if ( strength < Pl_hud_subsys_info[psub->type].last_str ) { + Pl_hud_subsys_info[psub->type].flash_duration_timestamp = timestamp(SUBSYS_DAMAGE_FLASH_DURATION); + } + Pl_hud_subsys_info[psub->type].last_str = strength; + } + } + + int type; + for ( int i = 0; i < num; i++ ) { + best_str = 1000; + best_index = -1; + for ( int j = 0; j < num-i; j++ ) { + if ( hud_subsys_list[j].str < best_str ) { + best_str = hud_subsys_list[j].str; + best_index = j; + } + } + + Assert(best_index >= 0); + Assert(best_str >= 0); + + // display strongest subsystem left in list + // draw the bitmap + // hud_set_default_color(); + hud_set_gauge_color(HUD_DAMAGE_GAUGE); + + GR_AABITMAP(Damage_gauges[1].first_frame, bx, by); + by += Damage_gauge_line_h[gr_screen.res]; + + type = hud_subsys_list[best_index].type; + if ( !timestamp_elapsed( Pl_hud_subsys_info[type].flash_duration_timestamp ) ) { + if ( timestamp_elapsed( Pl_hud_next_flash_timestamp ) ) { + Pl_hud_is_bright ^= 1; + Pl_hud_next_flash_timestamp = timestamp(SUBSYS_DAMAGE_FLASH_INTERVAL); + } + + if ( Pl_hud_is_bright ) { + int alpha_color; + alpha_color = min(HUD_COLOR_ALPHA_MAX,HUD_color_alpha+HUD_BRIGHT_DELTA); + // gr_set_color_fast(&HUD_color_defaults[alpha_color]); + + hud_set_gauge_color(HUD_DAMAGE_GAUGE, alpha_color); + } else { + hud_set_gauge_color(HUD_DAMAGE_GAUGE); + } + } + + // draw the text + if ( best_str < 30 ) { + if ( best_str <= 0 ) { + if ( Damage_flash_bright ) { + gr_set_color_fast(&Color_bright_red); + } else { + gr_set_color_fast(&Color_red); + } + + } else { + gr_set_color_fast(&Color_red); + } + } else { + hud_set_gauge_color(HUD_DAMAGE_GAUGE); + } + + gr_string(sx, sy, hud_targetbox_truncate_subsys_name(hud_subsys_list[best_index].name)); + sprintf(buf, XSTR( "%d%%", 219), best_str); + hud_num_make_mono(buf); + gr_get_string_size(&w, &h, buf); + gr_string(Hull_integ_val_coords[gr_screen.res][0] - w, sy, buf); + sy += Damage_gauge_line_h[gr_screen.res]; + + // remove it from hud_subsys_list + if ( best_index < (num-i-1) ) { + hud_subsys_list[best_index] = hud_subsys_list[num-i-1]; + } + } + + // draw the bottom of the gauge + // hud_set_default_color(); + hud_set_gauge_color(HUD_DAMAGE_GAUGE); + + GR_AABITMAP(Damage_gauges[2].first_frame, bx, by); +} + +// init the members of the hud_anim struct to default values +void hud_anim_init(hud_anim *ha, int sx, int sy, char *filename) +{ + ha->first_frame = -1; + ha->num_frames = 0; + ha->total_time = 0.0f; + ha->time_elapsed = 0.0f; + ha->sx = sx; + ha->sy = sy; + strcpy(ha->name, filename); +} + +// call to unload the targetbox static animation +void hud_anim_release(hud_anim *ha) +{ + int i; + for ( i = 0; i < ha->num_frames; i++ ) { + bm_unload(ha->first_frame + i); + } +} + +// load a hud_anim +// return 0 is successful, otherwise return -1 +int hud_anim_load(hud_anim *ha) +{ + int fps; + + ha->first_frame = bm_load_animation(ha->name, &ha->num_frames, &fps); + if ( ha->first_frame == -1 ) { + Int3(); // couldn't load animation file in + return -1; + } + Assert(fps != 0); + ha->total_time = i2fl(ha->num_frames)/fps; + return 0; +} + +// render out a frame of the targetbox static animation, based on how much time has +// elapsed +// input: ha => pointer to hud anim info +// frametime => seconds elapsed since last frame +// draw_alpha => draw bitmap as alpha-bitmap (default 0) +// loop => anim should loop (default 1) +// hold_last => should last frame be held (default 0) +// reverse => play animation in reverse (default 0) +int hud_anim_render(hud_anim *ha, float frametime, int draw_alpha, int loop, int hold_last, int reverse) +{ + int framenum; + + if ( ha->num_frames <= 0 ) { + if ( hud_anim_load(ha) == -1 ) + return 0; + } + + ha->time_elapsed += frametime; + if ( ha->time_elapsed > ha->total_time ) { + if ( loop ) { + ha->time_elapsed = 0.0f; + } else { + if ( !hold_last ) { + return 0; + } + } + } + + // draw the correct frame of animation + framenum = fl2i( (ha->time_elapsed * ha->num_frames) / ha->total_time ); + if (reverse) { + framenum = (ha->num_frames-1) - framenum; + } + + if ( framenum < 0 ) + framenum = 0; + if ( framenum >= ha->num_frames ) + framenum = ha->num_frames-1; + + // Blit the bitmap for this frame + if(emp_should_blit_gauge()){ + gr_set_bitmap(ha->first_frame + framenum); + if ( draw_alpha ){ + gr_aabitmap(ha->sx, ha->sy); + } else { + gr_bitmap(ha->sx, ha->sy); + } + } + + return 1; +} + +// convert a number string to use mono-spaced 1 character +void hud_num_make_mono(char *num_str) +{ + int len, i, sc; + len = strlen(num_str); + + sc = Lcl_special_chars; + for ( i = 0; i < len; i++ ) { + if ( num_str[i] == '1' ) { + num_str[i] = (char)(sc + 1); + } + } +} + +// flashing text gauge +void hud_init_text_flash_gauge() +{ +} + +void hud_start_text_flash(char *txt, int t) +{ + // bogus + if(txt == NULL){ + strcpy(Hud_text_flash, ""); + return; + } + + // HACK. don't override EMP if its still going :) + if(!strcmp(Hud_text_flash, NOX("Emp")) && !hud_targetbox_flash_expired(TBOX_FLASH_CMEASURE)){ + return; + } + + strncpy(Hud_text_flash, txt, 500); + hud_targetbox_start_flash(TBOX_FLASH_CMEASURE, t); +} + +void hud_maybe_show_text_flash_icon() +{ + int bright; + + if ( hud_targetbox_flash_expired(TBOX_FLASH_CMEASURE) ) { + return; + } + + hud_targetbox_maybe_flash(TBOX_FLASH_CMEASURE); + + // bright? + bright = hud_targetbox_is_bright(TBOX_FLASH_CMEASURE); + + // draw + hud_show_text_flash_icon(Hud_text_flash, Hud_text_flash_coords[gr_screen.res][1], bright); +} + +void hud_show_text_flash_icon(char *txt, int y, int bright) +{ + int w, h; + + // different font size in hi-res + if(gr_screen.res != GR_640){ + gr_set_font(FONT3); + } + + // set color + if(bright){ + hud_set_gauge_color(HUD_TEXT_FLASH, HUD_C_DIM); + } else { + gr_set_color_fast(&Color_black); + } + + // string size + gr_get_string_size(&w, &h, txt); + + // draw the box + gr_rect( (int)((((float)gr_screen.max_w / 2.0f) - ((float)w / 2.0f)) - 1.0f), (int)((float)y - 1.0f), w + 2, h + 1); + + // string + hud_set_gauge_color(HUD_TEXT_FLASH, HUD_C_BRIGHT); + gr_string(0x8000, y, txt); + + // go back to normal font + gr_set_font(FONT1); +} + +// maybe display the kills gauge on the HUD +void hud_show_kills_gauge() +{ + if ( Kills_gauge.first_frame < 0 ) { + return; + } + + // hud_set_default_color(); + hud_set_gauge_color(HUD_KILLS_GAUGE); + + // draw background + GR_AABITMAP(Kills_gauge.first_frame, Kills_gauge_coords[gr_screen.res][0], Kills_gauge_coords[gr_screen.res][1]); + + gr_string(Kills_text_coords[gr_screen.res][0], Kills_text_coords[gr_screen.res][1], XSTR( "kills:", 223)); + + // display how many kills the player has so far + char num_kills_string[32]; + int w,h; + + if ( !Player ) { + Int3(); + return; + } + + sprintf(num_kills_string, "%d", Player->stats.m_kill_count_ok); + + gr_get_string_size(&w, &h, num_kills_string); + gr_string(Kills_text_val_coords[gr_screen.res][0]-w, Kills_text_val_coords[gr_screen.res][1], num_kills_string); +} + +// maybe show the netlag icon on the hud +void hud_maybe_show_netlag_icon() +{ + int lag_status; + + if ( Netlag_icon.first_frame == -1 ) { + Int3(); + return; + } + + lag_status = multi_query_lag_status(); + + switch(lag_status) { + case 0: + // draw the net lag icon flashing + hud_targetbox_start_flash(TBOX_FLASH_NETLAG); + if(hud_targetbox_maybe_flash(TBOX_FLASH_NETLAG)){ + hud_set_gauge_color(HUD_LAG_GAUGE, HUD_C_BRIGHT); + } else { + hud_set_gauge_color(HUD_LAG_GAUGE); + } + gr_set_bitmap(Netlag_icon.first_frame); + break; + case 1: + // draw the disconnected icon flashing fast + if(hud_targetbox_maybe_flash(TBOX_FLASH_NETLAG,1)){ + hud_set_gauge_color(HUD_LAG_GAUGE, HUD_C_BRIGHT); + } else { + hud_set_gauge_color(HUD_LAG_GAUGE); + } + gr_set_bitmap(Netlag_icon.first_frame+1); + break; + default: + // nothing to draw + return; + } + + if(emp_should_blit_gauge()){ + gr_aabitmap(Netlag_coords[gr_screen.res][0], Netlag_coords[gr_screen.res][1]); + } +} + +// load in kills gauge if required +void hud_init_kills_gauge() +{ + if ( !Kills_gauge_loaded ) { + Kills_gauge.first_frame = bm_load_animation(Kills_fname[gr_screen.res], &Kills_gauge.num_frames); + if ( Kills_gauge.first_frame == -1 ) { + Warning(LOCATION, "Could not load in the kills ani: Kills_fname[gr_screen.res]\n"); + return; + } + Kills_gauge_loaded = 1; + } +} + +// load in netlag icon if required +void hud_init_netlag_icon() +{ + if ( !Netlag_icon_loaded ) { + Netlag_icon.first_frame = bm_load_animation(Netlag_fname[gr_screen.res], &Netlag_icon.num_frames); + if ( Netlag_icon.first_frame == -1 ) { + Warning(LOCATION, "Could not load in the netlag ani: Netlag_fname[gr_screen.res]\n"); + return; + } + Netlag_icon_loaded = 1; + } +} + +// called at mission start to init data, and load support view bitmap if required +void hud_support_view_init() +{ + Hud_support_view_fade = 1; + Hud_support_obj_sig = -1; + Hud_support_target_sig = -1; + Hud_support_objnum = -1; + Hud_support_view_active = 0; + Hud_support_view_abort = 0; + + // ensure the talking head border is loaded + if ( !Support_view_gauge_loaded ) { + Support_view_gauge.first_frame = bm_load_animation(Support_fname[gr_screen.res], &Support_view_gauge.num_frames); + if ( Support_view_gauge.first_frame == -1 ) { + Warning(LOCATION, "Could not load in ani: Support_fname[gr_screen.res]\n"); + } + Support_view_gauge_loaded = 1; + } +} + +// start displaying the support view pop-up. This will remain up until hud_support_view_stop is called. +// input: objnum => object number for the support ship +void hud_support_view_start() +{ + Hud_support_view_active = 1; + Hud_support_view_fade = 1; +} + +// stop displaying the support view pop-up +void hud_support_view_stop(int stop_now) +{ + if ( stop_now ) { + Hud_support_view_active = 0; + Hud_support_view_fade = 1; + Hud_support_view_abort = 0; + } else { + Hud_support_view_fade = timestamp(2000); + } + + Hud_support_obj_sig = -1; + Hud_support_target_sig = -1; + Hud_support_objnum = -1; +} + +void hud_support_view_abort() +{ + hud_support_view_stop(0); + Hud_support_view_abort = 1; +} + +// return the number of seconds until repair ship will dock with player, return -1 if error +// +// mwa made this function more general purpose +// +// NOTE: This function is pretty stupid now. It just assumes the player is sitting still, and +// the support ship is moving directly to the player. +int hud_support_get_dock_time( int objnum ) +{ + ai_info *aip; + object *support_objp, *other_objp; + float dist, rel_speed, support_speed; + vector rel_vel; + + support_objp = &Objects[objnum]; + aip = &Ai_info[Ships[support_objp->instance].ai_index]; + + // if the ship is docked, return 0 + if ( aip->ai_flags & AIF_DOCKED ) + return 0; + + // get the dockee object pointer + if (aip->goal_objnum == -1) { + Int3(); // Shouldn't happen, but let's recover gracefully. + return 0; + } + + other_objp = &Objects[aip->goal_objnum]; + + vm_vec_sub(&rel_vel, &support_objp->phys_info.vel, &other_objp->phys_info.vel); + rel_speed = vm_vec_mag_quick(&rel_vel); + + dist = vm_vec_dist_quick(&other_objp->pos, &support_objp->pos); + + support_speed = support_objp->phys_info.speed; + + if ( rel_speed <= support_speed/2.0f) { // This means the player is moving away fast from the support ship. + return (int) (dist/support_speed); + } else { + float d1; + float d = dist; + float time = 0.0f; + + if (rel_speed < 20.0f) + rel_speed = 20.0f; + + // When faraway, use max speed, not current speed. Might not have sped up yet. + if (d > 100.0f) { + time += (d - 100.0f)/support_objp->phys_info.max_vel.z; + } + + // For mid-range, use current speed. + if (d > 60.0f) { + d1 = min(d, 100.0f); + + time += (d1 - 60.0f)/rel_speed; + } + + // For nearby, ship will have to slow down a bit for docking maneuver. + if (d > 30.0f) { + d1 = min(d, 60.0f); + + time += (d1 - 30.0f)/5.0f; + } + + // For very nearby, ship moves quite slowly. + d1 = min(d, 30.0f); + time += d1/7.5f; + + return fl2i(time); + } +} + +// Locate the closest support ship which is trying to dock with player, return -1 if there is no support +// ship currently trying to dock with the player +// MA: 4/22/98 -- pass in objp to find support ship trying to dock with objp +int hud_support_find_closest( int objnum ) +{ + ship_obj *sop; + ai_info *aip; + object *objp; + int i; + + objp = &Objects[objnum]; + + sop = GET_FIRST(&Ship_obj_list); + while(sop != END_OF_LIST(&Ship_obj_list)){ + if ( Ship_info[Ships[Objects[sop->objnum].instance].ship_info_index].flags & SIF_SUPPORT ) { + int pship_index, sindex; + + // make sure support ship is not dying + if ( !(Ships[Objects[sop->objnum].instance].flags & (SF_DYING|SF_EXPLODED)) ) { + + Assert( objp->type == OBJ_SHIP ); + aip = &Ai_info[Ships[Objects[sop->objnum].instance].ai_index]; + pship_index = objp->instance; + + // we must check all goals for this support ship -- not just the first one + for ( i = 0; i < MAX_AI_GOALS; i++ ) { + + // we can use == in the next statement (and should) since a ship will only ever be + // following one order at a time. + if ( aip->goals[i].ai_mode == AI_GOAL_REARM_REPAIR ) { + Assert( aip->goals[i].ship_name ); + sindex = ship_name_lookup( aip->goals[i].ship_name ); + if ( sindex == pship_index ) + return sop->objnum; + } + } + } + } + sop = GET_NEXT(sop); + } + + return -1; +} + +// dipaly the hud_support view popup +void hud_support_view_blit() +{ + int show_time; + char outstr[64]; + + if ( !Hud_support_view_active ) { + return; + } + + // don't render this gauge for multiplayer observers + if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER))){ + return; + } + + // If we haven't determined yet who the rearm ship is, try to! + if (Hud_support_objnum == -1) { + Hud_support_objnum = hud_support_find_closest( OBJ_INDEX(Player_obj) ); + if ( Hud_support_objnum >= 0 ) { + Hud_support_obj_sig = Objects[Hud_support_objnum].signature; + Hud_support_target_sig = Player_obj->signature; + } + } else { + // check to see if support ship is still alive + if ( (Objects[Hud_support_objnum].signature != Hud_support_obj_sig) || (Hud_support_target_sig != Player_obj->signature) ) { + hud_support_view_stop(1); + return; + } + } + + // set hud color + hud_set_gauge_color(HUD_SUPPORT_GAUGE); + + GR_AABITMAP(Support_view_gauge.first_frame, Support_view_coords[gr_screen.res][0], Support_view_coords[gr_screen.res][1]); + + gr_string(Support_text_coords[gr_screen.res][0], Support_text_coords[gr_screen.res][1], XSTR( "support", 224)); + + if ( Hud_support_view_fade > 1 ) { + if ( !timestamp_elapsed(Hud_support_view_fade) ) { + if ( Hud_support_view_abort){ + gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], XSTR( "aborted", 225)); + } else { + gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], XSTR( "complete", 1407)); + } + return; + } else { + Hud_support_view_abort = 0; + Hud_support_view_active = 0; + Hud_support_view_fade = 1; + Hud_support_objnum = -1; + return; + } + } + + show_time = 0; + if ( Player_ai->ai_flags & AIF_BEING_REPAIRED ) { + Assert(Ship_info[Player_ship->ship_info_index].initial_hull_strength > 0); + if ( (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_ENGINE) < 1.0 ) || + (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_SENSORS) < 1.0 ) || + (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_WEAPONS) < 1.0 ) || + (ship_get_subsystem_strength(Player_ship, SUBSYSTEM_COMMUNICATION) < 1.0 ) ) { + sprintf(outstr, XSTR( "repairing", 227)); + } else { + sprintf(outstr, XSTR( "rearming", 228)); + } + gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], outstr); + } else if (Player_ai->ai_flags & AIF_REPAIR_OBSTRUCTED) { + sprintf(outstr, XSTR( "obstructed", 229)); + gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], outstr); + } else { + if ( Hud_support_objnum == -1 ) { + sprintf(outstr, XSTR( "warping in", 230)); + gr_string(0x8000, Support_text_val_coords[gr_screen.res][1], outstr); + } else { + ai_info *aip; + + // display "busy" when support ship isn't actually enroute to me + aip = &Ai_info[Ships[Objects[Hud_support_objnum].instance].ai_index]; + if ( aip->goal_objnum != OBJ_INDEX(Player_obj) ) { + sprintf(outstr, XSTR( "busy", 231)); + show_time = 0; + + } else { + sprintf(outstr, XSTR( "dock in:", 232)); + show_time = 1; + } + + if (!show_time) { + gr_string(Support_text_dock_coords[gr_screen.res][0], Support_text_val_coords[gr_screen.res][1], outstr); + } else { + gr_string(Support_text_dock_coords[gr_screen.res][0], Support_text_val_coords[gr_screen.res][1], outstr); + } + } + } + + if ( show_time ) { + int seconds, minutes; + + Assert( Hud_support_objnum != -1 ); + + // ensure support ship is still alive + if ( (Objects[Hud_support_objnum].signature != Hud_support_obj_sig) || (Hud_support_target_sig != Player_obj->signature) ) { + hud_support_view_stop(1); + seconds = 0; + } else { + seconds = hud_support_get_dock_time( Hud_support_objnum ); + } + + if ( seconds >= 0 ) { + minutes = seconds/60; + seconds = seconds%60; + if ( minutes > 99 ) { + minutes = 99; + seconds = 99; + } + } else { + minutes = 99; + seconds = 99; + } + gr_printf(Support_text_dock_val_coords[gr_screen.res][0], Support_text_val_coords[gr_screen.res][1], NOX("%02d:%02d"), minutes, seconds); + } +} + +// Set the current color to the default HUD color (with default alpha) +void hud_set_default_color() +{ + Assert(HUD_color_alpha >= 0 && HUD_color_alpha < HUD_NUM_COLOR_LEVELS); + gr_set_color_fast(&HUD_color_defaults[HUD_color_alpha]); +} + +// Set the current color to a bright HUD color (ie high alpha) +void hud_set_bright_color() +{ + int alpha_color; + alpha_color = min(HUD_COLOR_ALPHA_MAX,HUD_color_alpha+HUD_BRIGHT_DELTA); + gr_set_color_fast(&HUD_color_defaults[alpha_color]); +} + +// Set the current color to a dim HUD color (ie low alpha) +void hud_set_dim_color() +{ + if ( HUD_color_alpha > 2 ) { + gr_set_color_fast(&HUD_color_defaults[2]); + } +} + +// hud_set_iff_color() will set the color to the IFF color based on the team +// +// input: team => team to base color on +// is_bright => default parameter (value 0) which uses bright version of IFF color +void hud_set_iff_color(object *objp, int is_bright) +{ + // AL 12-26-97: it seems IFF color needs to be set relative to the player team. If + // the team in question is the same as the player, then it should be + // drawn friendly. If the team is different than the players, then draw the + // appropriate IFF. + int team; + team = obj_team(objp); + + if ( ship_is_tagged(objp) ) { + gr_set_color_fast(&IFF_colors[IFF_COLOR_TAGGED][is_bright]); + } else if ( (team == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) { + gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][is_bright]); + } else { + switch (team) { + case TEAM_NEUTRAL: + gr_set_color_fast(&IFF_colors[IFF_COLOR_NEUTRAL][is_bright]); + break; + case TEAM_UNKNOWN: + gr_set_color_fast(&IFF_colors[IFF_COLOR_UNKNOWN][is_bright]); + break; + case TEAM_HOSTILE: + case TEAM_FRIENDLY: + case TEAM_TRAITOR: + gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][is_bright]); + break; + default: + Int3(); + gr_set_color_fast(&IFF_colors[IFF_COLOR_UNKNOWN][is_bright]); + break; + } + } +} + +// Determine if ship team should be ignored, based on +// team filter +// input: team_filter => team mask used to select friendly or hostile ships +// ship_team => team of the ship in question +// exit: 1 => ship_team matches filter from player perspective +// 0 => ship_team does match team filter +int hud_team_matches_filter(int team_filter, int ship_team) +{ + return team_filter & ship_team; +} + + +// reset gauge flashing data +void hud_gauge_flash_init() +{ + int i; + for ( i=0; i=0 && gauge_index < NUM_HUD_GAUGES); + + // AL: Special code: Only show two gauges when not viewing from own ship + if ( Viewer_mode & VM_OTHER_SHIP ) { + for ( int i = 0; i < NUM_VM_OTHER_SHIP_GAUGES; i++ ) { + if ( gauge_index == Vm_other_ship_gauges[i] ) { + return 1; + } + } + return 0; + } + + return hud_config_show_flag_is_set(gauge_index); +} + +// determine if gauge is in pop-up mode or not +int hud_gauge_is_popup(int gauge_index) +{ + Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES); + return hud_config_popup_flag_is_set(gauge_index); +} + +// determine if a popup gauge should be drawn +int hud_gauge_popup_active(int gauge_index) +{ + Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES); + if ( !hud_gauge_is_popup(gauge_index) ) { + return 0; + } + + if ( !timestamp_elapsed(HUD_popup_timers[gauge_index]) ) { + return 1; + } else { + return 0; + } +} + +// start a gauge to popup +void hud_gauge_popup_start(int gauge_index, int time) +{ + Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES); + if ( !hud_gauge_is_popup(gauge_index) ) { + return; + } + + HUD_popup_timers[gauge_index] = timestamp(time); + +} + +// call HUD function to flash gauge +void hud_gauge_start_flash(int gauge_index) +{ + Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES); + HUD_gauge_flash_duration[gauge_index] = timestamp(HUD_GAUGE_FLASH_DURATION); + HUD_gauge_flash_next[gauge_index] = 1; +} + +// Set the HUD color for the gauge, based on whether it is flashing or not +void hud_set_gauge_color(int gauge_index, int bright_index) +{ + color use_color; + int flash_status = hud_gauge_maybe_flash(gauge_index); + use_color = HUD_config.clr[gauge_index]; + int alpha; + + // if we're drawing it as bright + if(bright_index != HUD_C_NONE){ + switch(bright_index){ + case HUD_C_DIM: + alpha = HUD_contrast ? HUD_NEW_ALPHA_DIM_HI : HUD_NEW_ALPHA_DIM; + gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha); + break; + + case HUD_C_NORMAL: + alpha = HUD_contrast ? HUD_NEW_ALPHA_NORMAL_HI : HUD_NEW_ALPHA_NORMAL; + gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha); + break; + + case HUD_C_BRIGHT: + alpha = HUD_contrast ? HUD_NEW_ALPHA_BRIGHT_HI : HUD_NEW_ALPHA_BRIGHT; + gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha); + break; + + // intensity + default: + Assert((bright_index >= 0) && (bright_index < HUD_NUM_COLOR_LEVELS)); + if(bright_index < 0){ + bright_index = 0; + } + if(bright_index >= HUD_NUM_COLOR_LEVELS){ + bright_index = HUD_NUM_COLOR_LEVELS - 1; + } + + // alpha = 255 - (255 / (bright_index + 1)); + // alpha = (int)((float)alpha * 1.5f); + int level = 255 / (HUD_NUM_COLOR_LEVELS); + alpha = level * bright_index; + if(alpha > 255){ + alpha = 255; + } + if(alpha < 0){ + alpha = 0; + } + gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha); + break; + } + } else { + switch(flash_status) { + case 0: + alpha = HUD_contrast ? HUD_NEW_ALPHA_DIM_HI : HUD_NEW_ALPHA_DIM; + gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha); + break; + case 1: + alpha = HUD_contrast ? HUD_NEW_ALPHA_BRIGHT_HI : HUD_NEW_ALPHA_BRIGHT; + gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha); + break; + default: + alpha = HUD_contrast ? HUD_NEW_ALPHA_NORMAL_HI : HUD_NEW_ALPHA_NORMAL; + gr_init_alphacolor(&use_color, use_color.red, use_color.green, use_color.blue, alpha); + break; + } + } + + gr_set_color_fast(&use_color); +} + +// set the color for a gauge that may be flashing +// exit: -1 => gauge is not flashing +// 0 => gauge is flashing, draw dim +// 1 => gauge is flashing, draw bright +int hud_gauge_maybe_flash(int gauge_index) +{ + Assert(gauge_index >=0 && gauge_index < NUM_HUD_GAUGES); + int flash_status=-1; + if ( !timestamp_elapsed(HUD_gauge_flash_duration[gauge_index]) ) { + if ( timestamp_elapsed(HUD_gauge_flash_next[gauge_index]) ) { + HUD_gauge_flash_next[gauge_index] = timestamp(HUD_GAUGE_FLASH_INTERVAL); + HUD_gauge_bright ^= (1< type of goal, one of: PRIMARY_GOAL +// SECONDARY_GOAL +// BONUS_GOAL +// +// status => status of goal, one of: GOAL_FAILED +// GOAL_COMPLETE +// GOAL_INCOMPLETE +// +void hud_add_objective_messsage(int type, int status) +{ + Objective_display.display_timer=timestamp(7000); + Objective_display.goal_type=type; + Objective_display.goal_status=status; + + // if this is a multiplayer tvt game + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL)){ + mission_goal_fetch_num_resolved(type, &Objective_display.goal_nresolved, &Objective_display.goal_ntotal, Net_player->p_info.team); + } else { + mission_goal_fetch_num_resolved(type, &Objective_display.goal_nresolved, &Objective_display.goal_ntotal); + } + + // TODO: play a sound? +} + +// maybe display the 'subspace drive engaged' message +void hud_maybe_display_subspace_notify() +{ + int warp_aborted = 0; + // maybe make gauge active + if ( (Player->control_mode == PCM_WARPOUT_STAGE1) || (Player->control_mode == PCM_WARPOUT_STAGE2) || (Player->control_mode == PCM_WARPOUT_STAGE3) ) { + if (!hud_subspace_notify_active()) { + // keep sound from being played 1e06 times + hud_start_subspace_notify(); + } + } else { + if ( !timestamp_elapsed(HUD_abort_subspace_timer) ) { + warp_aborted = 1; + } else { + hud_stop_subspace_notify(); + } + } + + if ( !hud_subspace_notify_active() ) { + return; + } + + if ( Objective_display_gauge.first_frame < 0 ) { + return; + } + + // blit the background + hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE); + GR_AABITMAP(Objective_display_gauge.first_frame, Objective_display_coords[gr_screen.res][0], Objective_display_coords[gr_screen.res][1]); + + hud_targetbox_start_flash(TBOX_FLASH_OBJECTIVE); + if(hud_targetbox_maybe_flash(TBOX_FLASH_OBJECTIVE)){ + hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE, HUD_C_BRIGHT); + } else { + hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE); + } + + + gr_string(0x8000, Subspace_text_coords[gr_screen.res][1],XSTR( "subspace drive", 233)); + if ( warp_aborted ) { + gr_string(0x8000, Subspace_text_val_coords[gr_screen.res][1],XSTR( "aborted", 225)); + } else { + gr_string(0x8000, Subspace_text_val_coords[gr_screen.res][1],XSTR( "engaged", 234)); + } +} + +// maybe display the 'Downloading new orders' message +void hud_maybe_display_red_alert() +{ + if ( !red_alert_check_status() ) { + return; + } + + if ( Objective_display_gauge.first_frame < 0 ) { + return; + } + + if ( hud_subspace_notify_active() ) { + return; + } + + if ( hud_objective_notify_active() ) { + return; + } + + // blit the background + gr_set_color_fast(&Color_red); // color box red, cuz its an emergency for cryin out loud + + GR_AABITMAP(Objective_display_gauge.first_frame, Objective_display_coords[gr_screen.res][0], Objective_display_coords[gr_screen.res][1]); + + hud_targetbox_start_flash(TBOX_FLASH_OBJECTIVE); + if(hud_targetbox_maybe_flash(TBOX_FLASH_OBJECTIVE)) { + gr_set_color_fast(&Color_red); + } else { + gr_set_color_fast(&Color_bright_red); + } + + gr_string(0x8000, Red_text_coords[gr_screen.res][1], XSTR( "downloading new", 235)); + gr_string(0x8000, Red_text_val_coords[gr_screen.res][1], XSTR( "orders...", 236)); + + // TODO: play a sound? +} + +// Maybe show an objective status update on the HUD +void hud_maybe_display_objective_message() +{ + char buf[128]; + + if ( timestamp_elapsed(Objective_display.display_timer) ) { + hud_stop_objective_notify(); + return; + } + + if ( Objective_display_gauge.first_frame < 0 ) { + return; + } + + if ( hud_subspace_notify_active() ) { + return; + } + + if (!hud_objective_notify_active()) { + hud_start_objective_notify(); + } + + // blit the background + hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE); + GR_AABITMAP(Objective_display_gauge.first_frame, Objective_display_coords[gr_screen.res][0], Objective_display_coords[gr_screen.res][1]); + + hud_targetbox_start_flash(TBOX_FLASH_OBJECTIVE); + if(hud_targetbox_maybe_flash(TBOX_FLASH_OBJECTIVE)){ + hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE, HUD_C_BRIGHT); + } else { + hud_set_gauge_color(HUD_OBJECTIVES_NOTIFY_GAUGE); + } + + // draw the correct goal type + switch(Objective_display.goal_type) { + case PRIMARY_GOAL: + gr_string(0x8000, Objective_text_coords[gr_screen.res][1],XSTR( "primary objective", 237)); + break; + case SECONDARY_GOAL: + gr_string(0x8000, Objective_text_coords[gr_screen.res][1],XSTR( "secondary objective", 238)); + break; + case BONUS_GOAL: + gr_string(0x8000, Objective_text_coords[gr_screen.res][1],XSTR( "bonus objective", 239)); + break; + } + + // show the status + switch(Objective_display.goal_type) { + case PRIMARY_GOAL: + case SECONDARY_GOAL: + switch(Objective_display.goal_status) { + case GOAL_FAILED: + sprintf(buf, XSTR( "failed (%d/%d)", 240), Objective_display.goal_nresolved, Objective_display.goal_ntotal); + gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], buf); + break; + default: + sprintf(buf, XSTR( "complete (%d/%d)", 241), Objective_display.goal_nresolved, Objective_display.goal_ntotal); + gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], buf); + break; + } + break; + case BONUS_GOAL: + switch(Objective_display.goal_status) { + case GOAL_FAILED: + gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], XSTR( "failed", 242)); + break; + default: + gr_string(0x8000, Objective_text_val_coords[gr_screen.res][1], XSTR( "complete", 226)); + break; + } + break; + } +} + +// return wing slot (0->3) based on name of ship. Assumes ship is from Alpha,Beta, or +// Gamma wings +int hud_wing_slot_from_name(char *name) +{ + int rval; + char num[2]; + + num[0]=name[strlen(name)-1]; + num[1]=0; + + rval = num[0] - '1'; + Assert(rval >= 0 && rval < 4); + return rval; +} + +// return index in starting wings (0->11) for specified ship. +int hud_wing_index_from_ship(int shipnum) +{ + int i; + ship *shipp; + + shipp = &Ships[shipnum]; + + int wing_num=0, wing_slot=0; + + for (i=0; i<3; i++) { + if ( Starting_wings[i] < 0 ) { + continue; + } + + if (shipp->wingnum == Starting_wings[i]) { + wing_num=i; + break; + } + } + + if ( i==3 ) { + Int3(); + } + + wing_slot = hud_wing_slot_from_name(shipp->ship_name); + return (i*4+wing_slot); +} + +void hud_show_voice_status() +{ + char play_callsign[CALLSIGN_LEN+5]; + + // if we are currently playing a rtvoice sound stream from another player back + memset(play_callsign,0,CALLSIGN_LEN+5); + switch(multi_voice_status()){ + // the player has been denied the voice token + case MULTI_VOICE_STATUS_DENIED: + // show a red indicator or something + gr_string(Voice_coords[gr_screen.res][0], Voice_coords[gr_screen.res][1], XSTR( "[voice denied]", 243)); + break; + + // the player is currently recording + case MULTI_VOICE_STATUS_RECORDING: + gr_string(Voice_coords[gr_screen.res][0], Voice_coords[gr_screen.res][1], XSTR( "[recording voice]", 244)); + break; + + // the player is current playing back voice from someone + case MULTI_VOICE_STATUS_PLAYING: + gr_string(Voice_coords[gr_screen.res][0], Voice_coords[gr_screen.res][1], XSTR( "[playing voice]", 245)); + break; + + // nothing voice related is happening on my machine + case MULTI_VOICE_STATUS_IDLE: + // probably shouldn't be displaying anything + break; + } +} + +void hud_subspace_notify_abort() +{ + HUD_abort_subspace_timer = timestamp(1500); +} + +void hud_stop_subspace_notify() +{ + Subspace_notify_active=0; +} + +void hud_start_subspace_notify() +{ + + Subspace_notify_active=1; +} + +int hud_subspace_notify_active() +{ + return Subspace_notify_active; +} + +void hud_stop_objective_notify() +{ + Objective_notify_active = 0; +} + +void hud_start_objective_notify() +{ + snd_play(&(Snds[SND_DIRECTIVE_COMPLETE])); + Objective_notify_active = 1; +} + +int hud_objective_notify_active() +{ + return Objective_notify_active; +} + +// render multiplayer text message currently being entered if any +void hud_maybe_render_multi_text() +{ + char txt[MULTI_MSG_MAX_TEXT_LEN+20]; + + // clear the text + memset(txt,0,MULTI_MSG_MAX_TEXT_LEN+1); + + // if there is valid multiplayer message text to be displayed + if(multi_msg_message_text(txt)){ + gr_set_color_fast(&Color_normal); + gr_string(Multi_msg_coords[gr_screen.res][0], Multi_msg_coords[gr_screen.res][1], txt); + } +} + +// cut any text off after (and including) '#' char +void hud_end_string_at_first_hash_symbol(char *src) +{ + char *pointer_to_last_char; + + pointer_to_last_char = strstr(src, NOX("#")); + + if ( pointer_to_last_char ) { + *pointer_to_last_char = 0; + } +} + +// set the offset values for this render frame +void HUD_set_offsets(object *viewer_obj, int wiggedy_wack) +{ + if ( (viewer_obj == Player_obj) && wiggedy_wack ){ + vector tmp; + vertex pt; + ubyte flags; + + HUD_offset_x = 0.0f; + HUD_offset_y = 0.0f; + + vm_vec_scale_add( &tmp, &Viewer_obj->pos, &Viewer_obj->orient.fvec, 100.0f ); + + flags = g3_rotate_vertex(&pt,&tmp); + + if (flags == 0) { + + g3_project_vertex(&pt); + + if (!(pt.flags & PF_OVERFLOW)) { + HUD_offset_x -= 0.45f * (i2fl(gr_screen.clip_width)*0.5f - pt.sx); + HUD_offset_y -= 0.45f * (i2fl(gr_screen.clip_height)*0.5f - pt.sy); + } + } + + if ( HUD_offset_x > 100.0f ) { + HUD_offset_x = 100.0f; + } else if ( HUD_offset_x < -100.0f ) { + HUD_offset_x += 100.0f; + } + + if ( HUD_offset_y > 100.0f ) { + HUD_offset_y = 100.0f; + } else if ( HUD_offset_y < -100.0f ) { + HUD_offset_y += 100.0f; + } + + } else { + HUD_offset_x = 0.0f; + HUD_offset_y = 0.0f; + } +} + +// Basically like gr_reset_clip only it accounts for hud jittering +void HUD_reset_clip() +{ + int hx = fl2i(HUD_offset_x); + int hy = fl2i(HUD_offset_y); + + gr_set_clip(hx, hy, gr_screen.max_w, gr_screen.max_h ); +} + +// Basically like gr_set_clip only it accounts for hud jittering +void HUD_set_clip(int x, int y, int w, int h) +{ + int hx = fl2i(HUD_offset_x); + int hy = fl2i(HUD_offset_y); + + gr_set_clip(hx+x, hy+y, w, h ); +} + +void hud_toggle_contrast() +{ + HUD_contrast = !HUD_contrast; +} + +void hud_set_contrast(int high) +{ + HUD_contrast = high; +} + +// Paging functions for the rest of the hud code +extern void hudwingmanstatus_page_in(); +extern void hudescort_page_in(); +extern void hudets_page_in(); +extern void hudlock_page_in(); +extern void hudreticle_page_in(); +extern void hudshield_page_in(); +extern void hudsquadmsg_page_in(); +extern void hudtarget_page_in(); +extern void hudtargetbox_page_in(); + +// Page in all hud bitmaps +void hud_page_in() +{ + int i; + + bm_page_in_aabitmap( Kills_gauge.first_frame, Kills_gauge.num_frames ); + bm_page_in_aabitmap( Head_frame_gauge.first_frame, Head_frame_gauge.num_frames ); + bm_page_in_aabitmap( Mission_time_gauge.first_frame, Mission_time_gauge.num_frames ); + for ( i = 0; i < NUM_DAMAGE_GAUGES; i++ ) { + bm_page_in_aabitmap( Damage_gauges[i].first_frame, Damage_gauges[i].num_frames); + } + + bm_page_in_aabitmap( Netlag_icon.first_frame, Netlag_icon.num_frames); + bm_page_in_aabitmap( Support_view_gauge.first_frame, Support_view_gauge.num_frames); + bm_page_in_aabitmap( Objective_display_gauge.first_frame, Objective_display_gauge.num_frames); + + // Paging functions for the rest of the hud code + hudwingmanstatus_page_in(); + hudescort_page_in(); + hudets_page_in(); + hudlock_page_in(); + hudreticle_page_in(); + hudshield_page_in(); + hudsquadmsg_page_in(); + hudtarget_page_in(); + hudtargetbox_page_in(); +} + diff --git a/src/hud/hudartillery.cpp b/src/hud/hudartillery.cpp new file mode 100644 index 0000000..7c9dbe3 --- /dev/null +++ b/src/hud/hudartillery.cpp @@ -0,0 +1,355 @@ +/* + * $Logfile: /Freespace2/code/Hud/HudArtillery.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 5 6/01/99 8:35p Dave + * Finished lockarm weapons. Added proper supercap weapons/damage. Added + * awacs-set-radius sexpression. + * + * 4 4/28/99 11:36p Dave + * Tweaked up subspace missile strike a bit, + * + * 3 4/28/99 11:13p Dave + * Temporary checkin of artillery code. + * + * 2 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 1 4/20/99 12:00a Dave + * + * + * $NoKeywords: $ + */ + +#include "hudartillery.h" +#include "ai.h" +#include "player.h" +#include "2d.h" +#include "alphacolors.h" + +// ----------------------------------------------------------------------------------------------------------------------- +// ARTILLERY DEFINES/VARS +// + + +// ----------------------------------------------------------------------------------------------------------------------- +// ARTILLERY FUNCTIONS +// + +#include "linklist.h" +#include "timer.h" +#include "parselo.h" +#include "multi.h" +#include "fireballs.h" +#include "freespace.h" + +// test code for subspace missile strike ------------------------------------------- + +#define MAX_SSM_TYPES 10 + +// global ssm types +typedef struct ssm_info { + char name[NAME_LENGTH+1]; // strike name + int count; // # of missiles in this type of strike + int weapon_info_index; // missile type + float warp_radius; // radius of associated warp effect + float warp_time; // how long the warp effect lasts + float radius; // radius around the shooting ship + float offset; // offset in front of the shooting ship +} ssm_info; + +int Ssm_info_count = 0; +ssm_info Ssm_info[MAX_SSM_TYPES]; + +#define MAX_SSM_STRIKES 10 +#define MAX_SSM_COUNT 10 + +// creation info for the strike (useful for multiplayer) +typedef struct ssm_firing_info { + int delay_stamp[MAX_SSM_COUNT]; // timestamps + vector start_pos[MAX_SSM_COUNT]; // start positions + + int ssm_index; // index info ssm_info array + vector target; // target for the strike +} ssm_firing_info; + +// the strike itself +typedef struct ssm_strike { + int fireballs[MAX_SSM_COUNT]; // warpin effect fireballs + int done_flags[MAX_SSM_COUNT]; // when we've fired off the individual missiles + + // this is the info that controls how the strike behaves (just like for beam weapons) + ssm_firing_info sinfo; + + ssm_strike *next, *prev; // for list +} ssm_strike; + +// list of active/free strikes +ssm_strike Ssm_strikes[MAX_SSM_STRIKES]; +ssm_strike Ssm_free_list; +ssm_strike Ssm_used_list; +int Num_ssm_strikes = 0; + +// game init +void ssm_init() +{ + ssm_info bogus, *s; + char weapon_name[NAME_LENGTH+1] = ""; + + read_file_text("ssm.tbl"); + reset_parse(); + + // parse the table + Ssm_info_count = 0; + while(!optional_string("#end")){ + // another ssm definition + if(optional_string("$SSM:")){ + // pointer to info struct + if(Ssm_info_count >= MAX_SSM_TYPES){ + s = &bogus; + } else { + s = &Ssm_info[Ssm_info_count]; + } + + // name + stuff_string(s->name, F_NAME, NULL); + + // stuff data + required_string("+Weapon:"); + stuff_string(weapon_name, F_NAME, NULL); + required_string("+Count:"); + stuff_int(&s->count); + required_string("+WarpRadius:"); + stuff_float(&s->warp_radius); + required_string("+WarpTime:"); + stuff_float(&s->warp_time); + required_string("+Radius:"); + stuff_float(&s->radius); + required_string("+Offset:"); + stuff_float(&s->offset); + + // see if we have a valid weapon + s->weapon_info_index = -1; + s->weapon_info_index = weapon_name_lookup(weapon_name); + if(s->weapon_info_index >= 0){ + // valid + Ssm_info_count++; + } + } + } +} + +void ssm_get_random_start_pos(vector *out, vector *start, matrix *orient, int ssm_index) +{ + vector temp; + ssm_info *s = &Ssm_info[ssm_index]; + + // get a random vector in the circle of the firing plane + vm_vec_random_in_circle(&temp, start, orient, s->radius, 1); + + // offset it a bit + vm_vec_scale_add(out, &temp, &orient->fvec, s->offset); +} + +// level init +void ssm_level_init() +{ + int i; + + Num_ssm_strikes = 0; + list_init( &Ssm_free_list ); + list_init( &Ssm_used_list ); + + // Link all object slots into the free list + for (i=0; i= MAX_SSM_STRIKES ) { + #ifndef NDEBUG + mprintf(("Ssm creation failed - too many ssms!\n" )); + #endif + return; + } + + // sanity + Assert(target != NULL); + if(target == NULL){ + return; + } + Assert(start != NULL); + if(start == NULL){ + return; + } + if((ssm_index < 0) || (ssm_index >= MAX_SSM_TYPES)){ + return; + } + + // Find next available trail + ssm = GET_FIRST(&Ssm_free_list); + Assert( ssm != &Ssm_free_list ); // shouldn't have the dummy element + + // remove trailp from the free list + list_remove( &Ssm_free_list, ssm ); + + // insert trailp onto the end of used list + list_append( &Ssm_used_list, ssm ); + + // increment counter + Num_ssm_strikes++; + + // Init the ssm data + + // override in multiplayer + if(override != NULL){ + ssm->sinfo = *override; + } + // single player or the server + else { + // forward orientation + vector temp; + vm_vec_sub(&temp, target, start); + vm_vec_normalize(&temp); + vm_vector_2_matrix(&dir, &temp, NULL, NULL); + + // stuff info + ssm->sinfo.ssm_index = ssm_index; + ssm->sinfo.target = *target; + for(idx=0; idxsinfo.delay_stamp[idx] = timestamp(200 + (int)frand_range(-199.0f, 1000.0f)); + ssm_get_random_start_pos(&ssm->sinfo.start_pos[idx], start, &dir, ssm_index); + } + + // if we're the server, send a packet + if(MULTIPLAYER_MASTER){ + // + } + } + + // clear timestamps, handles, etc + for(idx=0; idxdone_flags[idx] = 0; + ssm->fireballs[idx] = -1; + } +} + +// delete a finished ssm effect +void ssm_delete(ssm_strike *ssm) +{ + // remove objp from the used list + list_remove( &Ssm_used_list, ssm ); + + // add objp to the end of the free + list_append( &Ssm_free_list, ssm ); + + // decrement counter + Num_ssm_strikes--; + + nprintf(("General", "Recycling SSM, %d left", Num_ssm_strikes)); +} + +// process subspace missile stuff +void ssm_process() +{ + int idx, finished; + ssm_strike *moveup, *next_one; + ssm_info *si; + + // process all strikes + moveup=GET_FIRST(&Ssm_used_list); + while ( moveup!=END_OF_LIST(&Ssm_used_list) ) { + // get the type + if(moveup->sinfo.ssm_index < 0){ + continue; + } + si = &Ssm_info[moveup->sinfo.ssm_index]; + + // check all the individual missiles + finished = 1; + for(idx=0; idxcount; idx++){ + // if this guy is not marked as done + if(!moveup->done_flags[idx]){ + finished = 0; + + // if he already has the fireball effect + if(moveup->fireballs[idx] >= 0){ + // if the warp effect is half done, fire the missile + if((1.0f - fireball_lifeleft_percent(&Objects[moveup->fireballs[idx]])) >= 0.5f){ + // get an orientation + vector temp; + matrix orient; + + vm_vec_sub(&temp, &moveup->sinfo.target, &moveup->sinfo.start_pos[idx]); + vm_vec_normalize(&temp); + vm_vector_2_matrix(&orient, &temp, NULL, NULL); + + // fire the missile and flash the screen + weapon_create(&moveup->sinfo.start_pos[idx], &orient, si->weapon_info_index, -1, 1, -1, 1); + + // this makes this particular missile done + moveup->done_flags[idx] = 1; + } + } + // maybe create his warpin effect + else if((moveup->sinfo.delay_stamp[idx] >= 0) && timestamp_elapsed(moveup->sinfo.delay_stamp[idx])){ + // get an orientation + vector temp; + matrix orient; + + vm_vec_sub(&temp, &moveup->sinfo.target, &moveup->sinfo.start_pos[idx]); + vm_vec_normalize(&temp); + vm_vector_2_matrix(&orient, &temp, NULL, NULL); + moveup->fireballs[idx] = fireball_create(&moveup->sinfo.start_pos[idx], FIREBALL_WARP_EFFECT, -1, si->warp_radius, 0, &vmd_zero_vector, si->warp_time, 0, &orient); + } + } + } + if(finished){ + next_one = GET_NEXT(moveup); + ssm_delete(moveup); + moveup = next_one; + continue; + } + + moveup=GET_NEXT(moveup); + } +} + + +// test code for subspace missile strike ------------------------------------------- + +// level init +void hud_init_artillery() +{ +} + +// update all hud artillery related stuff +void hud_artillery_update() +{ +} + +// render all hud artillery related stuff +void hud_artillery_render() +{ + // render how long the player has been painting his target + if((Player_ai != NULL) && (Player_ai->artillery_objnum >= 0)){ + gr_set_color_fast(&Color_bright_blue); + gr_printf(10, 50, "%f", Player_ai->artillery_lock_time); + } +} diff --git a/src/hud/hudbrackets.cpp b/src/hud/hudbrackets.cpp new file mode 100644 index 0000000..05b9854 --- /dev/null +++ b/src/hud/hudbrackets.cpp @@ -0,0 +1,767 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDbrackets.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C file that contains functions for drawing target brackets on the HUD + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 6 8/03/99 5:35p Andsager + * Dont draw target dot for instructor in training mission + * + * 5 6/07/99 4:20p Andsager + * Add HUD color for tagged object. Apply to target and radar. + * + * 4 12/28/98 3:17p Dave + * Support for multiple hud bitmap filenames for hi-res mode. + * + * 3 12/21/98 5:02p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 54 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 53 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 52 5/27/98 1:22p Allender + * make targeting dots work in multiplayer -- as well as many other minor + * targeting problems + * + * 51 5/23/98 2:26a Lawrance + * Tweak brackets + * + * 50 5/14/98 11:26a Lawrance + * ensure fighter bays are drawn with correct bracket color + * + * 49 5/12/98 9:27a Mike + * num-ships-attacking: always showed an additional ship attacking + * asteroid or debris. Fixed. + * + * 48 5/08/98 10:16a Lawrance + * Add new "ship attacking count" gauge + * + * 47 5/07/98 4:07p Mike + * Use smaller num-ships-attacking blip. + * + * 46 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 45 5/06/98 2:46p Mike + * Modify num-ships-attacking system. + * + * 44 5/03/98 1:07a Mike + * Show + for ships attacking your target, whether hostile or friendly. + * + * 43 4/05/98 7:42p Lawrance + * fix inconsistent distance display on the HUD + * + * 42 4/01/98 9:21p John + * Made NDEBUG, optimized build with no warnings or errors. + * + * 41 3/30/98 12:20a Lawrance + * Draw subsystem targeting brackets gray if subsystem is destroyed. + * + * 40 3/18/98 6:04p Lawrance + * Improve when brackets get drawn for large objects + * + * 39 3/10/98 6:03p Lawrance + * Ensure proper bracket color gets drawn for traitors + * + * 38 3/06/98 5:13p Johnson + * Put in a check for team traitor in hud_brackets_get_iff_color() + * + * 37 3/02/98 11:32p Lawrance + * Allow asteroids about to impact ships to be bracketed + * + * 36 2/21/98 2:49p Lawrance + * Don't do facing check for subsystems when deciding whether to draw + * brackets + * + * 35 2/19/98 12:48a Lawrance + * Ensure subsystem brackets remain between HUD and target monitor + * + * 34 2/09/98 8:05p Lawrance + * Add new gauges: cmeasure success, warp-out, and missiontime + * + * 33 2/07/98 5:46p Lawrance + * Show target distance on brackets + * + * 32 1/19/98 11:37p Lawrance + * Fixing Optimization build warnings + * + * 31 1/18/98 5:09p Lawrance + * Added support for TEAM_TRAITOR + * + * 30 1/02/98 10:01p Lawrance + * Ensure correct subsystem bracket color gets drawn once player goes + * traitor. + * + * 29 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 28 12/28/97 5:54p Lawrance + * Draw HUD brackets the correct IFF color.. taking into account that the + * player may be TEAM_NEUTRAL + * + * 27 12/18/97 8:46p Lawrance + * Move IFF_color definitions from HUD->ship, so FRED can use them. + * + * 26 12/17/97 11:14p Mike + * Change radar and hud color to red for all neutral. + * + * 25 11/27/97 4:24p Lawrance + * change appearance of subsystem targeting brackets + * + * 24 10/22/97 5:53p Lawrance + * change name of subsystem_in_sight() function + * + * 23 9/06/97 2:13p Mike + * Replace support for TEAM_NEUTRAL + * + * 22 7/24/97 10:45a Mike + * Fix hole in Unknown team support. + * + * 21 7/24/97 10:24a Mike + * Restore support for Unknown team + * + * 20 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 19 5/21/97 11:12a Mike + * Move more stuff out of player struct, mainly subsys stuff. + * + * 18 5/20/97 2:45p Mike + * Move current_target and a bunch of other stuff out of player struct. + * + * 17 4/09/97 3:30p Lawrance + * let target brackets grow to bracket ship entirely + * + * 16 4/08/97 1:28p Lawrance + * get brackets for targeting and messaging drawing right + * + * 15 4/08/97 11:44a Lawrance + * get selection and target brackets drawing right at close and far + * distances + * + * 14 4/08/97 10:55a Allender + * draw purple brackets on ship sending a message + * + * 13 4/08/97 9:58a Lawrance + * center bracket on target center. Give min and max dimensions to + * subsystem target brackets. + * + * 12 4/07/97 6:03p Lawrance + * draw diamond brackets instead of dashed boxes + * + * 11 4/07/97 3:50p Allender + * ability to assign > 1 ship to a hotkey. Enabled use of hotkeys in + * squadmate messaging + * + * 10 4/02/97 10:08a Lawrance + * fixed bracket drawing glitches + * + * 9 3/28/97 2:46p John + * added code to make debris chunks target properly. + * + * 8 3/27/97 5:44p Lawrance + * drawing dashed lines for sub-object targeting box that is not in line + * of sight + * + * 7 3/27/97 3:59p Lawrance + * made brackets draw even if center of target is offscreen + * + * 6 3/27/97 9:29a Lawrance + * If reach maximum bounding box size, use radius targeting box method + * + * 5 3/25/97 3:55p Lawrance + * allowing debris to be targeted and shown on radar + * + * 4 3/23/97 11:55p Lawrance + * made max targeting bracket size of 200x200 + * + * 3 3/07/97 4:37p Mike + * Make rockeye missile home. + * Remove UNKNOWN and NEUTRAL teams. + * + * 2 12/24/96 4:30p Lawrance + * Target bracket drawing code moved to separate files + * + * $NoKeywords: $ + */ + +#include "hudbrackets.h" +#include "hud.h" +#include "player.h" +#include "hudtarget.h" +#include "3d.h" +#include "debris.h" +#include "ai.h" +#include "freespace.h" +#include "bmpman.h" +#include "linklist.h" +#include "emp.h" + +#define FADE_FACTOR 2 // how much the bounding brackets get faded +#define LOWEST_RED 50 // lowest r value for bounding bracket +#define LOWEST_GREEN 50 // lowest g value for bounding bracket +#define LOWEST_BLUE 50 // lowest b value for bounding bracket + +char Ships_attack_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "attacker", + "attacker" +}; + +// resolution adjust factors for the above defines +int Min_target_box_width[GR_NUM_RESOLUTIONS] = { 20, 30 }; +int Min_target_box_height[GR_NUM_RESOLUTIONS] = { 20, 30 }; +int Min_subtarget_box_width[GR_NUM_RESOLUTIONS] = { 12, 24 }; +int Min_subtarget_box_height[GR_NUM_RESOLUTIONS] = { 12, 24 }; + +void hud_init_brackets() +{ +} + +// find IFF color index for brackets based on team +int hud_brackets_get_iff_color(int team) +{ + int color=IFF_COLOR_FRIENDLY; + + switch ( team ) { + case TEAM_FRIENDLY: + case TEAM_HOSTILE: + case TEAM_NEUTRAL: + case TEAM_TRAITOR: + case TEAM_UNKNOWN: + if ( (team == Player_ship->team) && (team != TEAM_TRAITOR) ) { + color = IFF_COLOR_FRIENDLY; + } else { + switch (team) { + case TEAM_NEUTRAL: + color = IFF_COLOR_NEUTRAL; + break; + case TEAM_UNKNOWN: + color = IFF_COLOR_UNKNOWN; + break; + case TEAM_HOSTILE: + case TEAM_FRIENDLY: + case TEAM_TRAITOR: + color = IFF_COLOR_HOSTILE; + break; + } + } + break; + case SELECTION_SET: + color = IFF_COLOR_SELECTION; + break; + + case MESSAGE_SENDER: + color = IFF_COLOR_MESSAGE; + break; + + default: + color = IFF_COLOR_UNKNOWN; + Int3(); + } // end switch + + return color; +} + + +// Called by draw_bounding_brackets. +void draw_brackets_square(int x1, int y1, int x2, int y2) +{ + int width, height; + + width = x2 - x1; + Assert( width > 0); + height = y2 - y1; + Assert( height > 0); + + // make the brackets extend 25% of the way along the width or height + int bracket_width = width/4; + int bracket_height = height/4; + + // horizontal lines + if ( (x1 + bracket_width > 0) && (x1 < gr_screen.clip_width) ){ + gr_gradient(x1,y1,x1+bracket_width-1,y1); // top left + gr_gradient(x1,y2,x1+bracket_width-1,y2); // bottom left + } + + if ( (x2 - bracket_width < gr_screen.clip_width) && (x2 > 0) ) { + gr_gradient(x2, y1, x2-bracket_width+1,y1); // top right + gr_gradient(x2, y2, x2-bracket_width+1,y2); // bottom right + } + + // vertical lines + if ( (y1 + bracket_height > 0) && (y1 < gr_screen.clip_height) ) { + gr_gradient(x1,y1,x1,y1+bracket_height-1); // top left + gr_gradient(x2,y1,x2,y1+bracket_height-1); // top right + } + + if ( (y2 - bracket_height < gr_screen.clip_height) && (y2 > 0) ) { + gr_gradient(x1,y2,x1,y2-bracket_height+1); // bottom left + gr_gradient(x2,y2,x2,y2-bracket_height+1); // bottom right + } +} + +void draw_brackets_square_quick(int x1, int y1, int x2, int y2, int thick) +{ + int width, height; + + width = x2 - x1; + height = y2 - y1; + + // make the brackets extend 25% of the way along the width or height + int bracket_width = width/4; + int bracket_height = height/4; + + // top line + gr_line(x1,y1,x1+bracket_width,y1); + gr_line(x2,y1,x2-bracket_width,y1); + if ( thick ) { + gr_line(x1,y1+1,x1+bracket_width,y1+1); + gr_line(x2,y1+1,x2-bracket_width,y1+1); + } + + // bottom line + gr_line(x1,y2,x1+bracket_width,y2); + gr_line(x2,y2,x2-bracket_width,y2); + if ( thick ) { + gr_line(x1,y2-1,x1+bracket_width,y2-1); + gr_line(x2,y2-1,x2-bracket_width,y2-1); + } + + // left line + if ( thick ) { + gr_line(x1,y1+2,x1,y1+bracket_height); + gr_line(x1,y2-2,x1,y2-bracket_height); + gr_line(x1+1,y1+2,x1+1,y1+bracket_height); + gr_line(x1+1,y2-2,x1+1,y2-bracket_height); + } else { + gr_line(x1,y1+1,x1,y1+bracket_height); + gr_line(x1,y2-1,x1,y2-bracket_height); + } + + // right line + if ( thick ) { + gr_line(x2,y1+2,x2,y1+bracket_height); + gr_line(x2,y2-2,x2,y2-bracket_height); + gr_line(x2-1,y1+2,x2-1,y1+bracket_height); + gr_line(x2-1,y2-2,x2-1,y2-bracket_height); + } else { + gr_line(x2,y1+1,x2,y1+bracket_height); + gr_line(x2,y2-1,x2,y2-bracket_height); + } +} + + +#define NUM_DASHES 2 +void draw_brackets_dashed_square_quick(int x1, int y1, int x2, int y2) +{ + int width, height, i; + + width = x2 - x1; + height = y2 - y1; + + // make the brackets extend 25% of the way along the width or height + float bracket_width = width/4.0f; + float bracket_height = height/4.0f; + + int dash_width; + dash_width = fl2i(bracket_width / ( NUM_DASHES*2 - 1 ) + 0.5f); + + if ( dash_width < 1 ) { + draw_brackets_square_quick(x1, y1, x2, y2); + return; + } + + int dash_height; + dash_height = fl2i(bracket_height / ( NUM_DASHES*2 - 1 ) + 0.5f); + + if ( dash_height < 1 ) { + draw_brackets_square_quick(x1, y1, x2, y2); + return; + } + + int dash_x1, dash_x2, dash_y1, dash_y2; + + dash_x1 = x1; + dash_x2 = x2; + dash_y1 = y1; + dash_y2 = y2; + + for ( i = 0; i < NUM_DASHES; i++ ) { + // top line + gr_line(dash_x1, y1, dash_x1+(dash_width-1), y1); + gr_line(dash_x2, y1, dash_x2-(dash_width-1), y1); + + // bottom line + gr_line(dash_x1, y2, dash_x1+(dash_width-1), y2); + gr_line(dash_x2, y2, dash_x2-(dash_width-1), y2); + + dash_x1 += dash_width*2; + dash_x2 -= dash_width*2; + + // left line + gr_line(x1, dash_y1, x1, dash_y1+(dash_height-1)); + gr_line(x1, dash_y2, x1, dash_y2-(dash_height-1)); + + // right line + gr_line(x2, dash_y1, x2, dash_y1+(dash_height-1)); + gr_line(x2, dash_y2, x2, dash_y2-(dash_height-1)); + + dash_y1 += dash_height*2; + dash_y2 -= dash_height*2; + } + +} + + + +// draw_brackets_diamond() +// Called by draw_bounding_brackets. + +void draw_brackets_diamond(int x1, int y1, int x2, int y2) +{ + int width, height, half_width, half_height; + int center_x, center_y; + int x_delta, y_delta; + + float side_len, bracket_len; + + width = x2 - x1; + height = y2 - y1; + + half_width = fl2i( width/2.0f + 0.5f ); + half_height = fl2i( height/2.0f +0.5f ); + + side_len = (float)_hypot(half_width, half_height); + bracket_len = side_len / 8; + + x_delta = fl2i(bracket_len * width / side_len + 0.5f); + y_delta = fl2i(bracket_len * height / side_len + 0.5f); + + + center_x = x1 + half_width; + center_y = y1 + half_height; + + // top left line + gr_gradient(center_x - x_delta, y1 + y_delta,center_x, y1); + gr_gradient(x1 + x_delta, center_y - y_delta, x1, center_y); + + // top right line + gr_gradient(center_x + x_delta, y1 + y_delta,center_x, y1); + gr_gradient(x2 - x_delta, center_y - y_delta, x2, center_y); + + // bottom left line + gr_gradient(x1 + x_delta, center_y + y_delta, x1, center_y); + gr_gradient(center_x - x_delta, y2 - y_delta, center_x, y2); + + // bottom right line + gr_gradient(x2 - x_delta, center_y + y_delta, x2, center_y); + gr_gradient(center_x + x_delta, y2 - y_delta, center_x, y2); +} + +void draw_brackets_diamond_quick(int x1, int y1, int x2, int y2, int thick) +{ + int width, height, half_width, half_height; + int center_x, center_y; + int x_delta, y_delta; + + float side_len, bracket_len; + + width = x2 - x1; + height = y2 - y1; + + half_width = fl2i( width/2.0f + 0.5f); + half_height = fl2i( height/2.0f + 0.5f); + + side_len = (float)_hypot(half_width, half_height); + bracket_len = side_len / 8; + + x_delta = fl2i(bracket_len * width / side_len + 0.5f); + y_delta = fl2i(bracket_len * height / side_len + 0.5f); + + center_x = x1 + half_width; + center_y = y1 + half_height; + + // top left line + gr_line(center_x - x_delta, y1 + y_delta,center_x, y1); + gr_line(x1 + x_delta, center_y - y_delta, x1, center_y); + + // top right line + gr_line(center_x + x_delta, y1 + y_delta,center_x, y1); + gr_line(x2 - x_delta, center_y - y_delta, x2, center_y); + + // bottom left line + gr_line(x1 + x_delta, center_y + y_delta, x1, center_y); + gr_line(center_x - x_delta, y2 - y_delta, center_x, y2); + + // bottom right line + gr_line(x2 - x_delta, center_y + y_delta, x2, center_y); + gr_line(center_x + x_delta, y2 - y_delta, center_x, y2); + + // draw an 'X' in the middle of the brackets + gr_line(center_x-x_delta, center_y-y_delta, center_x+x_delta, center_y+y_delta); + gr_line(center_x-x_delta, center_y+y_delta, center_x+x_delta, center_y-y_delta); +} + + +int subsys_is_fighterbay(ship_subsys *ss) +{ + if ( !strnicmp(NOX("fighter"), ss->system_info->name, 7) ) { + return 1; + } + + return 0; +} + +// Draw bounding brackets for a subobject. +void draw_bounding_brackets_subobject() +{ + if (Player_ai->targeted_subsys_parent == Player_ai->target_objnum) + if (Player_ai->targeted_subsys != NULL) { + ship_subsys *subsys; + int target_objnum; + object* targetp; + vertex subobj_vertex; + vector subobj_pos; + int x1,x2,y1,y2; + + subsys = Player_ai->targeted_subsys; + target_objnum = Player_ai->target_objnum; + Assert(target_objnum != -1); + targetp = &Objects[target_objnum]; + Assert( targetp->type == OBJ_SHIP ); + + get_subsystem_world_pos(targetp, subsys, &subobj_pos); + + g3_rotate_vertex(&subobj_vertex,&subobj_pos); + + g3_project_vertex(&subobj_vertex); + if (subobj_vertex.flags & PF_OVERFLOW) // if overflow, no point in drawing brackets + return; + + int subobj_x = fl2i(subobj_vertex.sx + 0.5f); + int subobj_y = fl2i(subobj_vertex.sy + 0.5f); + int hud_subtarget_w, hud_subtarget_h, bound_rc; + + bound_rc = subobj_find_2d_bound(subsys->system_info->radius, &targetp->orient, &subobj_pos, &x1,&y1,&x2,&y2); + if ( bound_rc != 0 ) + return; + + hud_subtarget_w = x2-x1+1; + if ( hud_subtarget_w > gr_screen.clip_width ) { + hud_subtarget_w = gr_screen.clip_width; + } + + hud_subtarget_h = y2-y1+1; + if ( hud_subtarget_h > gr_screen.clip_height ) { + hud_subtarget_h = gr_screen.clip_height; + } + + if ( hud_subtarget_w > gr_screen.max_w ) { + x1 = subobj_x - (gr_screen.max_w>>1); + x2 = subobj_x + (gr_screen.max_w>>1); + } + if ( hud_subtarget_h > gr_screen.max_h ) { + y1 = subobj_y - (gr_screen.max_h>>1); + y2 = subobj_y + (gr_screen.max_h>>1); + } + + if ( hud_subtarget_w < Min_subtarget_box_width[gr_screen.res] ) { + x1 = subobj_x - (Min_subtarget_box_width[gr_screen.res]>>1); + x2 = subobj_x + (Min_subtarget_box_width[gr_screen.res]>>1); + } + if ( hud_subtarget_h < Min_subtarget_box_height[gr_screen.res] ) { + y1 = subobj_y - (Min_subtarget_box_height[gr_screen.res]>>1); + y2 = subobj_y + (Min_subtarget_box_height[gr_screen.res]>>1); + } + + // determine if subsystem is on far or near side of the ship + Player->subsys_in_view = ship_subsystem_in_sight(targetp, subsys, &View_position, &subobj_pos, 0); + + // AL 29-3-98: If subsystem is destroyed, draw gray brackets + if ( (Player_ai->targeted_subsys->current_hits <= 0) && (!subsys_is_fighterbay(Player_ai->targeted_subsys)) ) { + gr_set_color_fast(&IFF_colors[IFF_COLOR_MESSAGE][1]); + } else { + hud_set_iff_color( targetp, 1 ); + } + + if ( Player->subsys_in_view ) { + draw_brackets_square_quick(x1, y1, x2, y2); + } else { + draw_brackets_diamond_quick(x1, y1, x2, y2); + } + // mprintf(("Drawing subobject brackets at %4i, %4i\n", sx, sy)); + } +} + +extern int HUD_drew_selection_bracket_on_target; + +// Display the current target distance, right justified at (x,y) +void hud_target_show_dist_on_bracket(int x, int y, float distance) +{ + char text_dist[64]; + int w,h; + + if ( y < 0 || y > gr_screen.clip_height ) { + return; + } + + if ( x < 0 || x > gr_screen.clip_width ) { + return; + } + + sprintf(text_dist, "%d", fl2i(distance+0.5f)); + hud_num_make_mono(text_dist); + gr_get_string_size(&w,&h,text_dist); + + int y_delta = 4; + if ( HUD_drew_selection_bracket_on_target ) { + y += 4; + } + + gr_string(x - w+2, y+y_delta, text_dist); +} + + +// !!!!!!!!!!!!!!! +// Given an object number, return the number of ships attacking it. +// MWA 5/26/98 -- copied from aicode num_attacking_ships()!!! +// !!!!!!!!!!!!!!! +int hud_bracket_num_ships_attacking(int objnum) +{ + object *objp; + ship_obj *so; + int count = 0; + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + objp = &Objects[so->objnum]; + if (objp->instance != -1) { + ai_info *aip; + aip = &Ai_info[Ships[objp->instance].ai_index]; + + // don't count instructor + int is_training_mission(); + if ( is_training_mission() && stricmp(Ships[objp->instance].ship_name, "Instructor") == 0) { + break; + } + + if ( ((Game_mode & GM_MULTIPLAYER) || (aip->mode == AIM_CHASE)) && (aip->target_objnum == objnum)) + if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) + count++; + } + } + + return count; +} + + +int Ships_attacking_bitmap = -1; + +// draw_bounding_brackets() will draw the faded brackets that surround the current target +void draw_bounding_brackets(int x1, int y1, int x2, int y2, int w_correction, int h_correction, float distance, int target_objnum) +{ + int width, height; + + if ( ( x1 < 0 && x2 < 0 ) || ( y1 < 0 && y2 < 0 ) ) + return; + + if ( ( x1 > gr_screen.clip_width && x2 > gr_screen.clip_width ) || + ( y1 > gr_screen.clip_height && y2 > gr_screen.clip_height ) ) + return; + + width = x2-x1; + Assert(width>=0); + + height = y2-y1; + Assert(height>=0); + + if ( (width>(gr_screen.max_w - 1)) && (height>(gr_screen.max_h - 1)) ) { + return; + } + if ( width > 1200 ) { + return; + + } + if ( height > 1200) { + return; + } + + if (width < Min_target_box_width[gr_screen.res]) { + x1 = x1 - (Min_target_box_width[gr_screen.res]-width)/2; + x2 = x2 + (Min_target_box_width[gr_screen.res]-width)/2; + } + + if (height < Min_target_box_height[gr_screen.res]) { + y1 = y1 - (Min_target_box_height[gr_screen.res]-height)/2; + y2 = y2 + (Min_target_box_height[gr_screen.res]-height)/2; + } + + draw_brackets_square(x1-w_correction, y1-h_correction, x2+w_correction, y2+h_correction); + + // draw distance to target in lower right corner of box + if ( distance > 0 ) { + hud_target_show_dist_on_bracket(x2+w_correction,y2+h_correction,distance); + } + + // Maybe show + for each additional fighter or bomber attacking target. + if ( (target_objnum != -1) && hud_gauge_active(HUD_ATTACKING_TARGET_COUNT) ) { + int num_attacking = hud_bracket_num_ships_attacking(target_objnum); + + if (Ships_attacking_bitmap == -1){ + Ships_attacking_bitmap = bm_load(Ships_attack_fname[gr_screen.res]); + } + + if (Ships_attacking_bitmap == -1) { + Int3(); + return; + } + + // If a ship not on player's team, show one fewer plus since it is targeted and attacked by player. + int k=0; + if (Objects[target_objnum].type == OBJ_SHIP) { + if (Ships[Objects[target_objnum].instance].team != Player_ship->team){ + k = 1; + } + } else { + k = 1; + } + + if (num_attacking > k) { + int i, num_blips; + + num_blips = num_attacking-k; + if (num_blips > 4){ + num_blips = 4; + } + + //int bitmap = get_blip_bitmap(); + + if (Ships_attacking_bitmap > -1) { + if (num_blips > 3) + y1 -= 3; + + for (i=0; i. + * + * 40 1/17/98 1:30a Lawrance + * Add countermeasure gauge + * + * 39 1/15/98 5:10p Allender + * ton of interface changes. chatbox in multiplayer now behaves + * differently than before. It's always active in any screen that uses + * it. Only non-printatble characters will get passed back out from + * chatbox + * + * 38 1/14/98 11:07p Lawrance + * Hook in brightness slider to HUD config. + * + * 37 1/14/98 6:42p Hoffoss + * Massive changes to UI code. A lot cleaner and better now. Did all + * this to get the new UI_DOT_SLIDER to work properly, which the old code + * wasn't flexible enough to handle. + * + * 36 1/14/98 12:25p Lawrance + * center gauge text, use correct game palette. + * + * 35 1/13/98 5:33p Lawrance + * Tweaking HUD config. + * + * 34 1/13/98 2:20p John + * Added code to load palette based on hud color. Added code to turn off + * nebulas using detail. Added code in WinMain to time out after waiting + * too long for window creation. + * + * 33 1/13/98 10:22a Lawrance + * Integrate new art changes. + * + * 32 1/12/98 11:16p Lawrance + * Wonderful HUD config. + * + * 31 1/10/98 12:41a Lawrance + * start work on new HUD config + * + * 30 1/05/98 9:38p Lawrance + * Implement flashing HUD gauges. + * + * 29 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 28 12/16/97 9:13p Lawrance + * Integrate new gauges into HUD config. + * + * 27 12/01/97 12:27a Lawrance + * redo default alpha color for HUD, make it easy to modify in the future + * + * + * $NoKeywords: $ + * +*/ + +#include "hud.h" +#include "hudconfig.h" +#include "freespace.h" +#include "gamesequence.h" +#include "player.h" +#include "2d.h" + +#include "key.h" +#include "timer.h" +#include "math.h" +#include "mouse.h" +#include "ui.h" +#include "bmpman.h" +#include "palman.h" +#include "hudobserver.h" +#include "ui.h" +#include "missionshipchoice.h" +#include "gamesnd.h" +#include "alphacolors.h" +#include "popup.h" + + +////////////////////////////////////////////////////////////////////////////// +// Game-wide Globals +////////////////////////////////////////////////////////////////////////////// + +#define MAX_HCF_FILES 30 +int HC_num_files = -1; // num known hcf files +int HC_current_file = -1; // current hcf file +char *HC_filenames[MAX_HCF_FILES]; + +char HC_fname[MAX_FILENAME_LEN+1] = ""; +UI_INPUTBOX HC_fname_input; +int HC_fname_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 44, 449, 218, 17 + }, + { // GR_1024 + 101, 730, 319, 27 + } +}; + +HUD_CONFIG_TYPE HUD_config; // Player HUD configuration + +// specify the max distance that the radar should detect objects +// See RR_ #defines in HUDconfig.h. +float Radar_ranges[RR_MAX_RANGES] = { + 2000.0f, // short + 10000.0f, // med + 10000000.0f, // infinity +}; + +char *Radar_range_text(int n) +{ + #if RR_MAX_RANGES != 3 + #error Number of ranges is wrong! + #endif + + switch(n) { + case 0: + return XSTR( "2000 M", 246); + case 1: + return XSTR( "10,000 M", 247); + case 2: + return XSTR( "infinity", 248); + } + return NULL; +}; + +// default flags for observer HUD +int HUD_observer_default_flags = +{ + (1<= 0){ + HC_color_sliders[HCS_RED].force_currentItem( HCS_CONV(HUD_config.clr[i].red) ); + HC_color_sliders[HCS_GREEN].force_currentItem( HCS_CONV(HUD_config.clr[i].green) ); + HC_color_sliders[HCS_BLUE].force_currentItem( HCS_CONV(HUD_config.clr[i].blue) ); + HC_color_sliders[HCS_ALPHA].force_currentItem( HCS_CONV(HUD_config.clr[i].alpha) ); + } +} + +// reset some ui components based on HUD config data +void hud_config_synch_ui() +{ + // game_load_palette(); + HUD_init_hud_color_array(); + // HC_sliders[gr_screen.res][HC_BRIGHTNESS_SLIDER].slider.pos = HUD_color_alpha-3; // convert to value from 0-10 + + // sync sliders to currently selected gauge + hud_config_synch_sliders(HC_gauge_selected); +} + +// Init the UI components +void hud_config_init_ui() +{ + int i; + struct HC_gauge_region *hg; + struct ui_button_info *hb; + +// common_set_interface_palette("HUDConfigPalette"); // set the interface palette + hud_config_synch_ui(); + HC_background_bitmap = bm_load(Hud_config_fname[gr_screen.res]); + + HC_ui_window.create( 0, 0, gr_screen.max_w, gr_screen.max_h, 0 ); + HC_ui_window.set_mask_bmap(Hud_config_mask_fname[gr_screen.res]); + + for (i=0; ifilename, NOX("none")) ) { + continue; + } + hg->button.create(&HC_ui_window, "", hg->x, hg->y, 60, 30, 0, 1); + // set up callback for when a mouse first goes over a button +// hg->button.set_highlight_action(common_play_highlight_sound); + hg->button.hide(); + hg->button.link_hotspot(hg->hotspot); + + // if ( hg->use_iff ) { +// hg->bitmap = bm_load_animation(hg->filename, &hg->nframes); + // } else { + hg->bitmap = bm_load(hg->filename); + hg->nframes = 1; + // } + } + + // add text + for(i=0; ibutton.create(&HC_ui_window, "", hb->x, hb->y, 60, 30, 0, 1); + // set up callback for when a mouse first goes over a button + hb->button.set_bmaps(hb->filename); + hb->button.set_highlight_action(common_play_highlight_sound); + hb->button.link_hotspot(hb->hotspot); + } + + // config file input name + HC_fname_input.create(&HC_ui_window, HC_fname_coords[gr_screen.res][0], HC_fname_coords[gr_screen.res][1], HC_fname_coords[gr_screen.res][2], MAX_FILENAME_LEN, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_FOC); + HC_fname_input.set_text(""); + + /* + for (i=0; i= 0 ) { + gr_set_bitmap(HC_gauge_regions[gr_screen.res][i].bitmap); + gr_aabitmap(HC_gauge_regions[gr_screen.res][i].x, HC_gauge_regions[gr_screen.res][i].y); + } + + /* + else { + + int offset=0; + // set correct frame if using iff + if ( HC_gauge_regions[i].use_iff ) { + if ( HC_gauge_selected == i ) { + offset=2; + } else if ( HC_gauge_hot == i ) { + offset=1; + } + + // If gauge is disabled, then draw disabled frame + if ( !(hud_config_show_flag_is_set(i)) ) { + offset=3; + } + } + + if ( HC_gauge_regions[i].bitmap >= 0 ) { + Assert(offset < HC_gauge_regions[i].nframes); + gr_set_bitmap(HC_gauge_regions[i].bitmap+offset); + gr_bitmap(HC_gauge_regions[i].x, HC_gauge_regions[i].y); + } + } + */ + } +} + +// hud_config_init() is called when the game enters the state GS_STATE_HUD_CONFIG +void hud_config_init() +{ + hud_config_init_ui(); + hud_config_backup(); // save the HUD configuration in case the player decides to cancel changes + HUD_config_inited = 1; +} + +// check for the mouse over gauge regions +void hud_config_check_regions() +{ + int i; + UI_BUTTON *b; + + for ( i=0; ibutton_hilighted() ) { + HC_gauge_hot = i; + } + + if ( b->pressed() ) { + gamesnd_play_iface(SND_USER_SELECT); + HC_gauge_selected = i; + + // turn off select all + hud_config_select_all_toggle(0); + + // maybe setup rgb sliders + if(HC_gauge_regions[gr_screen.res][i].use_iff){ + HC_color_sliders[HCS_RED].hide(); + HC_color_sliders[HCS_GREEN].hide(); + HC_color_sliders[HCS_BLUE].hide(); + HC_color_sliders[HCS_ALPHA].hide(); + + HC_color_sliders[HCS_RED].disable(); + HC_color_sliders[HCS_GREEN].disable(); + HC_color_sliders[HCS_BLUE].disable(); + HC_color_sliders[HCS_ALPHA].disable(); + } else { + HC_color_sliders[HCS_RED].enable(); + HC_color_sliders[HCS_GREEN].enable(); + HC_color_sliders[HCS_BLUE].enable(); + HC_color_sliders[HCS_ALPHA].enable(); + + HC_color_sliders[HCS_RED].unhide(); + HC_color_sliders[HCS_GREEN].unhide(); + HC_color_sliders[HCS_BLUE].unhide(); + HC_color_sliders[HCS_ALPHA].unhide(); + + HC_color_sliders[HCS_RED].force_currentItem( HCS_CONV(HUD_config.clr[i].red) ); + HC_color_sliders[HCS_GREEN].force_currentItem( HCS_CONV(HUD_config.clr[i].green) ); + HC_color_sliders[HCS_BLUE].force_currentItem( HCS_CONV(HUD_config.clr[i].blue) ); + HC_color_sliders[HCS_ALPHA].force_currentItem( HCS_CONV(HUD_config.clr[i].alpha) ); + } + + // recalc alpha slider + hud_config_recalc_alpha_slider(); + } + } +} + +// set the display flags for a HUD gauge +void hud_config_set_gauge_flags(int gauge_index, int on_flag, int popup_flag) +{ + if ( on_flag ) { + hud_config_show_flag_set(gauge_index); + } else { + hud_config_show_flag_clear(gauge_index); + } + + if ( popup_flag ) { + hud_config_popup_flag_set(gauge_index); + } else { + hud_config_popup_flag_clear(gauge_index); + } +} + +void hud_config_record_color(int color) +{ + HUD_config.main_color = color; + HUD_color_red = HC_colors[color].r; + HUD_color_green = HC_colors[color].g; + HUD_color_blue = HC_colors[color].b; +} + +// Set the HUD color +void hud_config_set_color(int color) +{ + int idx; + + hud_config_record_color(color); + + // game_load_palette(); + + HUD_init_hud_color_array(); + + // apply the color to all gauges + for(idx=0; idxoff->popup +void hud_cycle_gauge_status() +{ + if ( HC_gauge_selected < 0 ) { + return; + } + + // gauge is off, move to popup + if ( !(hud_config_show_flag_is_set(HC_gauge_selected)) ) { + if ( HC_gauge_regions[gr_screen.res][HC_gauge_selected].can_popup ) { + hud_config_set_gauge_flags(HC_gauge_selected, 1, 1); + } else { + hud_config_set_gauge_flags(HC_gauge_selected, 1, 0); + } + return; + } + + // if gauge is popup, move to on + if ( hud_config_popup_flag_is_set(HC_gauge_selected) ) { + hud_config_set_gauge_flags(HC_gauge_selected, 1, 0); + return; + } + + // gauge must be on, move to off + hud_config_set_gauge_flags(HC_gauge_selected, 0, 0); +} + +// handle keyboard input while in hud config +void hud_config_handle_keypresses(int k) +{ + switch(k) { + case KEY_ESC: + hud_config_cancel(); + break; + case KEY_CTRLED+KEY_ENTER: + hud_config_commit(); + break; + case KEY_TAB: + gamesnd_play_iface(SND_USER_SELECT); + hud_cycle_gauge_status(); + break; + } +} + +// Handlers for when buttons get pressed +void hud_config_button_do(int n) +{ + int idx; + char name[256] = ""; + + switch (n) { + case HCB_AMBER: + hud_config_set_color(HUD_COLOR_AMBER); + gamesnd_play_iface(SND_USER_SELECT); + break; + case HCB_BLUE: + hud_config_set_color(HUD_COLOR_BLUE); + gamesnd_play_iface(SND_USER_SELECT); + break; + case HCB_GREEN: + hud_config_set_color(HUD_COLOR_GREEN); + gamesnd_play_iface(SND_USER_SELECT); + break; + case HCB_ON: + if ( HC_gauge_selected < 0 ) { + break; + } + gamesnd_play_iface(SND_USER_SELECT); + hud_config_set_gauge_flags(HC_gauge_selected,1,0); + break; + case HCB_OFF: + if ( HC_gauge_selected < 0 ) { + break; + } + gamesnd_play_iface(SND_USER_SELECT); + hud_config_set_gauge_flags(HC_gauge_selected,0,0); + break; + case HCB_POPUP: + if ( HC_gauge_selected < 0 ) { + break; + } + gamesnd_play_iface(SND_USER_SELECT); + hud_config_set_gauge_flags(HC_gauge_selected,1,1); + break; + case HCB_RESET: + gamesnd_play_iface(SND_RESET_PRESSED); + hud_config_select_all_toggle(0); + hud_set_default_hud_config(Player); + hud_config_synch_ui(); + break; + case HCB_ACCEPT: + hud_config_commit(); + break; + + // new stuff + case HCB_RED_UP: + if( HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()) >= 255){ + gamesnd_play_iface(SND_GENERAL_FAIL); + } else { + gamesnd_play_iface(SND_USER_SELECT); + HC_color_sliders[HCS_RED].force_currentItem( HCS_CONV( HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()) + 1) ); + hud_config_red_slider(); + } + break; + + case HCB_GREEN_UP: + if( HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()) >= 255){ + gamesnd_play_iface(SND_GENERAL_FAIL); + } else { + gamesnd_play_iface(SND_USER_SELECT); + HC_color_sliders[HCS_GREEN].force_currentItem( HCS_CONV( HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()) + 1) ); + hud_config_green_slider(); + } + break; + + case HCB_BLUE_UP: + if( HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) >= 255){ + gamesnd_play_iface(SND_GENERAL_FAIL); + } else { + gamesnd_play_iface(SND_USER_SELECT); + HC_color_sliders[HCS_BLUE].force_currentItem( HCS_CONV( HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) + 1) ); + hud_config_blue_slider(); + } + break; + + case HCB_I_UP: + if( HCS_CONV(HC_color_sliders[HCS_ALPHA].get_currentItem()) >= 255){ + gamesnd_play_iface(SND_GENERAL_FAIL); + } else { + gamesnd_play_iface(SND_USER_SELECT); + HC_color_sliders[HCS_ALPHA].force_currentItem( HCS_CONV( HCS_CONV(HC_color_sliders[HCS_ALPHA].get_currentItem()) + 1) ); + hud_config_alpha_slider_up(); + } + break; + + case HCB_RED_DOWN: + if( HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()) <= 0){ + gamesnd_play_iface(SND_GENERAL_FAIL); + } else { + gamesnd_play_iface(SND_USER_SELECT); + HC_color_sliders[HCS_RED].force_currentItem( HCS_CONV( HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()) - 1) ); + hud_config_red_slider(); + } + break; + + case HCB_GREEN_DOWN: + if( HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()) <= 0){ + gamesnd_play_iface(SND_GENERAL_FAIL); + } else { + gamesnd_play_iface(SND_USER_SELECT); + HC_color_sliders[HCS_GREEN].force_currentItem( HCS_CONV( HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()) - 1) ); + hud_config_green_slider(); + } + break; + + case HCB_BLUE_DOWN: + if( HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) <= 0){ + gamesnd_play_iface(SND_GENERAL_FAIL); + } else { + gamesnd_play_iface(SND_USER_SELECT); + HC_color_sliders[HCS_BLUE].force_currentItem( HCS_CONV( HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) - 1) ); + hud_config_blue_slider(); + } + break; + + case HCB_I_DOWN: + if( HCS_CONV(HC_color_sliders[HCS_ALPHA].get_currentItem()) <= 0){ + gamesnd_play_iface(SND_GENERAL_FAIL); + } else { + gamesnd_play_iface(SND_USER_SELECT); + HC_color_sliders[HCS_ALPHA].force_currentItem( HCS_CONV( HCS_CONV(HC_color_sliders[HCS_ALPHA].get_currentItem()) - 1) ); + hud_config_alpha_slider_down(); + } + break; + + case HCB_SAVE_HCF: + int exists; + char *out; + + // get the text in the input control + exists = 0; + HC_fname_input.get_text(name); + if(strlen(name) > 0){ + // if the filename in there already exists + for(idx=0; idx= MAX_HCF_FILES){ + popup(PF_USE_AFFIRMATIVE_ICON, 1, "OK", "Max number of hud config files reached!"); + break; + } + + // save the file, maybe generating a new filename + if(strlen(name) <= 0){ + sprintf(name, "hud_%d.hcf", HC_num_files + 1); + out = name; + } else { + out = cf_add_ext(name, ".hcf"); + } + HC_filenames[HC_num_files++] = strdup(out); + hud_config_color_save(out); + + HC_fname_input.set_text(out); + break; + + case HCB_PREV_HCF: + if(HC_num_files <= 0){ + break; + } + + if(HC_current_file <= 0){ + HC_current_file = HC_num_files - 1; + } else { + HC_current_file--; + } + // load em up + hud_config_color_load(HC_filenames[HC_current_file]); + hud_config_synch_ui(); + + HC_fname_input.set_text(HC_filenames[HC_current_file]); + break; + + case HCB_NEXT_HCF: + if(HC_num_files <= 0){ + break; + } + + if(HC_current_file >= HC_num_files - 1){ + HC_current_file = 0; + } else { + HC_current_file++; + } + + // load em up + hud_config_color_load(HC_filenames[HC_current_file]); + hud_config_synch_ui(); + + HC_fname_input.set_text(HC_filenames[HC_current_file]); + break; + + case HCB_SELECT_ALL: + hud_config_select_all_toggle(!HC_select_all); + break; + + default: + Int3(); + break; + } +} + +// Check if any buttons have been pressed +void hud_config_check_buttons() +{ + int i; + UI_BUTTON *b; + + for ( i=0; ipressed() ) { + hud_config_button_do(i); + } + } +} + +// set the hud color button +void hud_config_draw_color_status() +{ + if ( HC_buttons[gr_screen.res][HCB_AMBER].button.button_down() || HC_buttons[gr_screen.res][HCB_GREEN].button.button_down() || HC_buttons[gr_screen.res][HCB_BLUE].button.button_down() ) { + return; + } + + switch(HUD_config.main_color) { + case HUD_COLOR_AMBER: + HC_buttons[gr_screen.res][HCB_AMBER].button.draw_forced(2); + break; + case HUD_COLOR_GREEN: + HC_buttons[gr_screen.res][HCB_GREEN].button.draw_forced(2); + break; + case HUD_COLOR_BLUE: + HC_buttons[gr_screen.res][HCB_BLUE].button.draw_forced(2); + break; + } +} + +// set the status (on/off/popup) for the selected gauge +void hud_config_draw_gauge_status() +{ + if ( HC_gauge_selected < 0 ) { + return; + } + + if ( HC_buttons[gr_screen.res][HCB_OFF].button.button_down() || HC_buttons[gr_screen.res][HCB_POPUP].button.button_down() || HC_buttons[gr_screen.res][HCB_ON].button.button_down() ) { + return; + } + + // check if off + if ( !(hud_config_show_flag_is_set(HC_gauge_selected)) ) { + HC_buttons[gr_screen.res][HCB_OFF].button.draw_forced(2); + return; + } + + // check if popup + if ( hud_config_popup_flag_is_set(HC_gauge_selected) ) { + HC_buttons[gr_screen.res][HCB_POPUP].button.draw_forced(2); + return; + } + + // check if on + if ( hud_config_show_flag_is_set(HC_gauge_selected) ) { + HC_buttons[gr_screen.res][HCB_ON].button.draw_forced(2); + return; + } + + Int3(); // should never get here +} + +// disable a HUD config button +void hud_config_button_disable(int index) +{ + // HC_buttons[gr_screen.res][index].button.hide(); + HC_buttons[gr_screen.res][index].button.disable(); +} + +// enable a HUD config button +void hud_config_button_enable(int index) +{ + // HC_buttons[gr_screen.res][index].button.unhide(); + HC_buttons[gr_screen.res][index].button.enable(); +} + +// determine if on/off/popup buttons should be shown +void hud_config_set_button_state() +{ + if ( HC_gauge_selected < 0 ) { + hud_config_button_disable(HCB_ON); + hud_config_button_disable(HCB_OFF); + hud_config_button_disable(HCB_POPUP); + return; + } + + // on/off are always on + hud_config_button_enable(HCB_ON); + hud_config_button_enable(HCB_OFF); + + // popup is maybe available + if ( HC_gauge_regions[gr_screen.res][HC_gauge_selected].can_popup ) { + hud_config_button_enable(HCB_POPUP); + } else { + hud_config_button_disable(HCB_POPUP); + } +} + +void hud_config_render_description() +{ + int w,h,sx,sy; + + if ( HC_gauge_selected >= 0 ) { + gr_set_color_fast(&Color_normal); + + gr_get_string_size(&w, &h, HC_gauge_descriptions(HC_gauge_selected)); + sx = fl2i(HC_gauge_description_coords[gr_screen.res][0] + (HC_gauge_description_coords[gr_screen.res][2] - w)/2.0f); + sy = HC_gauge_description_coords[gr_screen.res][1]; + gr_string(sx, sy, HC_gauge_descriptions(HC_gauge_selected)); + } +} + +void hud_config_render_special_bitmaps() +{ + /* + int i; + for (i=1; i= 0) { + gr_set_bitmap(HC_special_bitmaps[i].bitmap); + gr_bitmap(HC_special_bitmaps[i].x, HC_special_bitmaps[i].y); + } + } + */ +} + +// update HUD_color_alpha based on brightness slider +void hud_config_update_brightness() +{ + // HUD_color_alpha = HC_sliders[gr_screen.res][HC_BRIGHTNESS_SLIDER].slider.pos+3; + // Assert(HUD_color_alpha >= HUD_COLOR_ALPHA_USER_MIN); + // Assert(HUD_color_alpha <= HUD_COLOR_ALPHA_USER_MAX); +} + +// redraw any pressed buttons, needed since the glow on pressed buttons might get clipped off by +// adjacent buttons otherwise +void hud_config_redraw_pressed_buttons() +{ + int i; + UI_BUTTON *b; + + for ( i = 0; i < NUM_HUD_BUTTONS; i++ ) { + b = &HC_buttons[gr_screen.res][i].button; + if ( b->button_down() ) { + b->draw_forced(2); + } + } +} + +// hud_config_do_frame() is called from the main freespace loop while the game is in the state +// GS_STATE_HUD_CONFIG. +// +void hud_config_do_frame(float frametime) +{ + int k; + + if (!HUD_config_inited) { + hud_config_init(); + } + + HC_gauge_hot = -1; + + hud_config_set_button_state(); + + k = HC_ui_window.process(); + + hud_config_handle_keypresses(k); + hud_config_check_regions(); + hud_config_check_buttons(); + hud_config_update_brightness(); + + // set the background + GR_MAYBE_CLEAR_RES(HC_background_bitmap); + if ( HC_background_bitmap > 0 ) { + gr_set_bitmap(HC_background_bitmap); + gr_bitmap(0,0); + } + + // rgb slider/button stuff + hud_config_process_colors(); + + HC_ui_window.draw(); + hud_config_redraw_pressed_buttons(); + + hud_config_draw_gauge_status(); + hud_config_draw_color_status(); + + /* + if (HC_special_bitmaps[HC_SPECIAL_RETICLE].bitmap >= 0) { + hud_set_default_color(); + gr_set_bitmap(HC_special_bitmaps[HC_SPECIAL_RETICLE].bitmap); + gr_aabitmap(HC_special_bitmaps[HC_SPECIAL_RETICLE].x, HC_special_bitmaps[HC_SPECIAL_RETICLE].y); + } + */ + + // maybe force draw the select all button + if(HC_select_all){ + HC_buttons[gr_screen.res][HCB_SELECT_ALL].button.draw_forced(2); + } + + hud_config_render_gauges(); + hud_config_render_special_bitmaps(); + hud_config_render_description(); + + gr_flip(); +} + +void hud_config_unload_gauges() +{ + int i, j; + HC_gauge_region *hg; + + for (i=0; ibitmap >= 0 ) { + for ( j=0; jnframes; j++ ) { + bm_unload(hg->bitmap+j); + } + } + + hg->bitmap=-1; + hg->nframes=0; + } +} + +// hud_config_close() is called when the player leaves the hud configuration screen +// +void hud_config_close() +{ +// common_free_interface_palette(); // restore game palette + hud_config_unload_gauges(); + hud_init_popup_timers(); // ensure no popup gauges are active + + hud_config_color_close(); + + HC_ui_window.destroy(); +} + +// hud_set_default_hud_config() will set the hud configuration to default values +void hud_set_default_hud_config(player *p) +{ + int idx; + + HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT; + HUD_config.main_color = HUD_COLOR_GREEN; + HUD_color_red = HC_colors[HUD_config.main_color].r; + HUD_color_green = HC_colors[HUD_config.main_color].g; + HUD_color_blue = HC_colors[HUD_config.main_color].b; + + for(idx=0; idx= 255){ + HC_color_sliders[HCS_ALPHA].force_currentItem( HCS_CONV(pos - 1) ); + return; + } + + // otherwise bump everybody up by one + HC_color_sliders[HCS_RED].force_currentItem( HCS_CONV(HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()) + 1) ); + HC_color_sliders[HCS_GREEN].force_currentItem( HCS_CONV(HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()) + 1) ); + HC_color_sliders[HCS_BLUE].force_currentItem( HCS_CONV(HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) + 1) ); + + // apply + if(HC_select_all){ + hud_config_stuff_colors( HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) ); + } else { + gr_init_alphacolor(&HUD_config.clr[HC_gauge_selected], HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()), 255); + } +} + +void hud_config_alpha_slider_down() +{ + int pos = HCS_CONV(HC_color_sliders[HCS_ALPHA].get_currentItem()); + int min = min(min( HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()) ), HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) ); + + // if this would put the brightest element past its limit, skip + if(min <= 0){ + HC_color_sliders[HCS_ALPHA].force_currentItem( HCS_CONV(pos + 1) ); + return; + } + + // otherwise bump everybody up by one + HC_color_sliders[HCS_RED].force_currentItem( HCS_CONV(HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()) - 1) ); + HC_color_sliders[HCS_GREEN].force_currentItem( HCS_CONV(HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()) - 1) ); + HC_color_sliders[HCS_BLUE].force_currentItem( HCS_CONV(HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) - 1) ); + + // apply + if(HC_select_all){ + hud_config_stuff_colors( HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()) ); + } else { + gr_init_alphacolor(&HUD_config.clr[HC_gauge_selected], HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_GREEN].get_currentItem()), HCS_CONV(HC_color_sliders[HCS_BLUE].get_currentItem()), 255); + } +} + +void hud_config_recalc_alpha_slider() +{ + int avg =HC_color_sliders[HCS_RED].get_currentItem() + HC_color_sliders[HCS_GREEN].get_currentItem() + HC_color_sliders[HCS_BLUE].get_currentItem(); + avg /= 3; + HC_color_sliders[HCS_ALPHA].force_currentItem( avg ); +} + +void hud_config_red_slider() +{ + int idx; + int pos = HCS_CONV(HC_color_sliders[HCS_RED].get_currentItem()) ; + + // select all ? + if(HC_select_all){ + for(idx=0; idxnp_id); + n2 = find_player_id(escort2->np_id); + if((n1 < 0) || (n2 < 0) || (Net_players[n1].player == NULL) || (Net_players[n2].player == NULL)){ + ret = 0; + } else { + // player 1 is higher than player 2 + if(Net_players[n1].player->stats.m_kill_count_ok >= Net_players[n2].player->stats.m_kill_count_ok){ + ret = -1; + } else { + ret = 1; + } + } + } else { + diff = escort2->priority - escort1->priority; + + if (diff != 0) { + ret = diff; + } else { + char *name1, *name2; + name1 = Ships[Objects[escort1->objnum].instance].ship_name; + name2 = Ships[Objects[escort2->objnum].instance].ship_name; + + ret = stricmp(name1, name2); + } + } + + return ret; +} + +// create complete priority sorted escort list for all active ships +// escorts - array of escort info +// num_escorts - number of escorts requests in field of active ships +// This will be culled to MAX_ESCORTS, selecting the top set from escorts +void hud_create_complete_escort_list(escort_info *escorts, int *num_escorts) +{ + ship_obj *so; + object *objp; + int idx; + + // start with none on list + *num_escorts = 0; + + // multiplayer dogfight + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){ + for(idx=0; idxobjnum >= 0 && so->objnum < MAX_OBJECTS); + if((so->objnum < 0) || (so->objnum >= MAX_OBJECTS)){ + continue; + } + objp = &Objects[so->objnum]; + Assert( objp->type == OBJ_SHIP ); + if(objp->type != OBJ_SHIP){ + continue; + } + + // break out of the loop when we have reached our max + if ( *num_escorts == MAX_COMPLETE_ESCORT_LIST ) { + mprintf(("exceeded max ships in big escort list")); + break; + } + + // only process ships that might be on the list + if ( !(Ships[objp->instance].flags & SF_ESCORT) ){ + continue; + } + + // only process ships that can be seen by sensors + if ( (Ships[objp->instance].flags & SF_HIDDEN_FROM_SENSORS) ){ + continue; + } + + // don't process ships that are dying, or objects that should be dead + if ( (Ships[objp->instance].flags & (SF_DYING|SF_DEPARTING)) || (objp->flags & OF_SHOULD_BE_DEAD) ){ + continue; + } + + // add the ship + escorts[*num_escorts].objnum = so->objnum; + escorts[*num_escorts].obj_signature = objp->signature; + escorts[*num_escorts].priority = Ships[objp->instance].escort_priority; + escorts[*num_escorts].np_id = -1; + (*num_escorts)++; + } + } +} + + +// ---------------------------------------------------------------------- +// hud_init_escort_info() +// +// Set up the escort list +// +void hud_setup_escort_list(int level) +{ + int num_escorts, num_complete_escorts; + escort_info complete_escorts[MAX_COMPLETE_ESCORT_LIST]; + + hud_escort_clear_all(); + + // get complete escort list + hud_create_complete_escort_list(complete_escorts, &num_complete_escorts); + + // sort escort list by priority + qsort(complete_escorts, num_complete_escorts, sizeof(escort_info), escort_compare_func); + + // set number in escort list + num_escorts = num_complete_escorts; + if (num_escorts > MAX_ESCORT_SHIPS) { + num_escorts = MAX_ESCORT_SHIPS; + } + + // add ships to escort list + for (Num_escort_ships=0; Num_escort_ships 1 ship change to list (ie, 2 or 3 culled during same frame) + // set Num_escort_ships and cap + Num_escort_ships = num_complete_escorts; + if (Num_escort_ships > MAX_ESCORT_SHIPS) { + Num_escort_ships = MAX_ESCORT_SHIPS; + } + + // nothing to do + if (Num_escort_ships == 0) { + return; + } + + // check used as a flag whether top slots in complete_escorts were copied + // this is important re. hit info + for (i=0; i MAX_ESCORT_SHIPS) { + top_complete_escorts = MAX_ESCORT_SHIPS; + } + + // copy for Escort_ships to complete_escorts to retain hit_info + if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){ + for (i=0; i=0 && objnum < MAX_OBJECTS ); + if((objnum < 0) || (objnum >= MAX_OBJECTS)){ + continue; + } + if ( !valid_hit_info[i] ) { + shield_info_reset(&Escort_ships[i].hit_info); + } + } + } + + // reset Num_escort_ships + Num_escort_ships = top_complete_escorts; +} + + +// ---------------------------------------------------------------------- +// hud_remove_ship_from_escort_index() +// +// Take a ship out of the escort list +void hud_remove_ship_from_escort_index(int dead_index, int objnum) +{ + int i, count, num_complete_escorts; + escort_info bakup_arr[MAX_ESCORT_SHIPS], complete_escorts[MAX_COMPLETE_ESCORT_LIST]; + + // remove him from escort list + if((objnum >= 0) && (Objects[objnum].type == OBJ_SHIP) && (Objects[objnum].instance >= 0)){ + Ships[Objects[objnum].instance].flags &= ~SF_ESCORT; + } + + count = 0; + for ( i = 0; i < Num_escort_ships; i++ ) { + if ( i != dead_index ) { + bakup_arr[count++] = Escort_ships[i]; + } + } + + for ( i = 0; i < count; i++ ) { + Escort_ships[i] = bakup_arr[i]; + } + + Num_escort_ships--; + Assert(Num_escort_ships >= 0); + + // get complete escort list + hud_create_complete_escort_list(complete_escorts, &num_complete_escorts); + + // sort escort list by priority + qsort(complete_escorts, num_complete_escorts, sizeof(escort_info), escort_compare_func); + + // merge list + merge_escort_lists(complete_escorts, num_complete_escorts); + + hud_gauge_popup_start(HUD_ESCORT_VIEW); + +} + +// called once per frame to remove dead or departed ships from the escort list +void hud_escort_cull_list() +{ + int i, objnum, np_index; + + // multiplayer dogfight + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){ + for ( i = 0; i < Num_escort_ships; i++ ) { + np_index = find_player_id(Escort_ships[i].np_id); + + // maybe remove him if he left + if ( np_index < 0 ) { + hud_remove_ship_from_escort_index(i, -1); + i--; + } + } + } + // everything else + else { + for ( i = 0; i < Num_escort_ships; i++ ) { + objnum = Escort_ships[i].objnum; + Assert( objnum >=0 && objnum < MAX_OBJECTS ); + if ( Objects[objnum].flags & OF_SHOULD_BE_DEAD || Ships[Objects[objnum].instance].flags & SF_HIDDEN_FROM_SENSORS ) { + hud_remove_ship_from_escort_index(i, objnum); + i--; + } + } + } +} + +// Set the color for the text to be displayed +int hud_escort_set_gauge_color(int index, int friendly) +{ + int is_flashing=0; + shield_hit_info *shi; + + shi = &Escort_ships[index].hit_info; + + // multiplayer dogfight + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){ + hud_set_gauge_color(HUD_ESCORT_VIEW); + return 0; + } + + if(friendly){ + hud_set_gauge_color(HUD_ESCORT_VIEW, HUD_C_DIM); + } else { + gr_set_color_fast(&Color_red); + } + + // set flashing color + if ( !timestamp_elapsed(shi->shield_hit_timers[HULL_HIT_OFFSET]) ) { + if ( timestamp_elapsed(shi->shield_hit_next_flash[HULL_HIT_OFFSET]) ) { + shi->shield_hit_next_flash[HULL_HIT_OFFSET] = timestamp(SHIELD_FLASH_INTERVAL); + shi->shield_show_bright ^= (1<shield_show_bright & (1<instance]; + sip = &Ship_info[sp->ship_info_index]; + shi = &Escort_ships[index].hit_info; + + // determine if its "friendly" or not + if(Player_ship != NULL){ + hud_escort_set_gauge_color(index, (sp->team == Player_ship->team) ? 1 : 0); + } else { + hud_escort_set_gauge_color(index, 1); + } + + // draw a 'D' if a ship is disabled + if ( (sp->flags & SF_DISABLED) || (ship_subsys_disrupted(sp, SUBSYSTEM_ENGINE)) ) { + emp_hud_string( Escort_gauge_text_coords[gr_screen.res][index][3][0], Escort_gauge_text_coords[gr_screen.res][index][3][1], EG_NULL, XSTR( "D", 284)); + } + + // print out ship name + strcpy(buf, sp->ship_name); + gr_force_fit_string(buf, 255, 100); + + emp_hud_string( Escort_gauge_text_coords[gr_screen.res][index][0][0], Escort_gauge_text_coords[gr_screen.res][index][0][1], EG_ESCORT1 + index, buf); + + // show ship integrity + hud_get_target_strength(objp, &shields, &integrity); + screen_integrity = fl2i(integrity*100 + 0.5f); + offset = 0; + if ( screen_integrity < 100 ) { + offset = 2; + if ( screen_integrity == 0 ) { + if ( integrity > 0 ) { + screen_integrity = 1; + } + } + } + emp_hud_printf( Escort_gauge_text_coords[gr_screen.res][index][2][0]+offset, Escort_gauge_text_coords[gr_screen.res][index][2][1], EG_NULL, "%d", screen_integrity); +} + +// multiplayer dogfight +void hud_escort_show_icon_dogfight(int index) +{ + int hull_integrity = 100; + char buf[255]; + int np_index; + object *objp; + ship_info *sip; + + int stat_shift = 40; + + // always use the standard color to avoid confusion + hud_set_gauge_color(HUD_ESCORT_VIEW); + + // netplayer index + np_index = find_player_id(Escort_ships[index].np_id); + if((np_index < 0) || (np_index >= MAX_PLAYERS) || (Net_players[np_index].player == NULL)){ + return; + } + + // print out player name + strcpy(buf, Net_players[np_index].player->callsign); + gr_force_fit_string(buf, 255, 100 - stat_shift); + emp_hud_string( Escort_gauge_text_coords[gr_screen.res][index][0][0], Escort_gauge_text_coords[gr_screen.res][index][0][1], EG_ESCORT1 + index, buf); + + // can we get the player object? + objp = NULL; + if((Net_players[np_index].player->objnum >= 0) && (Net_players[np_index].player->objnum < MAX_OBJECTS) && (Objects[Net_players[np_index].player->objnum].type == OBJ_SHIP)){ + objp = &Objects[Net_players[np_index].player->objnum]; + if((objp->instance >= 0) && (objp->instance < MAX_SHIPS) && (Ships[objp->instance].ship_info_index >= 0) && (Ships[objp->instance].ship_info_index < MAX_SHIPS)){ + sip = &Ship_info[Ships[objp->instance].ship_info_index]; + } else { + return; + } + + hull_integrity = (int)(((float)objp->hull_strength / (float)sip->initial_hull_strength) * 100.0f); + if(hull_integrity < 0){ + hull_integrity = 0; + } + } + + // show ship integrity + if(objp == NULL){ + emp_hud_printf( Escort_gauge_text_coords[gr_screen.res][index][2][0] - stat_shift, Escort_gauge_text_coords[gr_screen.res][index][2][1], EG_NULL, "%d", Net_players[np_index].player->stats.m_kill_count_ok); + } else { + emp_hud_printf( Escort_gauge_text_coords[gr_screen.res][index][2][0] - stat_shift, Escort_gauge_text_coords[gr_screen.res][index][2][1], EG_NULL, "(%d%%) %d", hull_integrity, Net_players[np_index].player->stats.m_kill_count_ok); + } +} + + +// ---------------------------------------------------------------------- +// hud_display_escort() +// +// Display the data on ships in the escort list +void hud_display_escort() +{ + int i; + object *objp; + + if ( !Show_escort_view ) { + return; + } + + if ( !Num_escort_ships ) { + return; + } + + // hud_set_default_color(); + hud_set_gauge_color(HUD_ESCORT_VIEW); + + // draw the top of the escort view + GR_AABITMAP(Escort_gauges[0].first_frame, Escort_coords[gr_screen.res][0][0], Escort_coords[gr_screen.res][0][1]); + gr_string(Monitoring_coords[gr_screen.res][0], Monitoring_coords[gr_screen.res][1], XSTR( "monitoring", 285)); + + if ( Num_escort_ships >= 2 ) { + GR_AABITMAP(Escort_gauges[1].first_frame, Escort_coords[gr_screen.res][1][0], Escort_coords[gr_screen.res][1][1]); + } + + if ( Num_escort_ships >= 3 ) { + GR_AABITMAP(Escort_gauges[1].first_frame, Escort_coords[gr_screen.res][2][0], Escort_coords[gr_screen.res][2][1]); + } + + // draw bottom of box + GR_AABITMAP(Escort_gauges[2].first_frame, Escort_coords[gr_screen.res][3][0], Escort_coords[gr_screen.res][Num_escort_ships][1]); + + // multiplayer dogfight + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)){ + // draw the escort ship data + for ( i = 0; i < Num_escort_ships; i++ ) { + // oops. only show top 3 + if(i > 2){ + break; + } + + // draw + hud_escort_show_icon_dogfight(i); + } + } + // everything else + else { + // draw the escort ship data + for ( i = 0; i < Num_escort_ships; i++ ) { + objp = &Objects[Escort_ships[i].objnum]; + hud_escort_show_icon(i, objp); + } + } +} + +// ---------------------------------------------------------------------- +// hud_escort_view_toggle() +// +void hud_escort_view_toggle() +{ + Show_escort_view ^= 1; + if ( Show_escort_view ) { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Escort view enabled", 286)); + } else { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Escort view disabled", 287)); + } +} + +// try to add a ship to the escort list, if slot available +void hud_add_ship_to_escort(int objnum, int supress_feedback) +{ + escort_info complete_escorts[MAX_COMPLETE_ESCORT_LIST]; + int num_complete_escorts, idx, found; + + // get complete escort list + hud_create_complete_escort_list(complete_escorts, &num_complete_escorts); + + // check if ship is already on complete escort list + found = 0; + for (idx=0; idx= 0 ) { + shi->shield_hit_timers[num] = timestamp(SHIELD_HIT_DURATION); + } else { + shi->shield_hit_timers[HULL_HIT_OFFSET] = timestamp(SHIELD_HIT_DURATION); + } + } + } +} + +// target the next ship in the escort list +void hud_escort_target_next() +{ + int objnum; + + if ( Num_escort_ships == 0 ) { + snd_play( &Snds[SND_TARGET_FAIL], 0.0f ); + return; + } + + Last_target_index++; + if ( Last_target_index >= Num_escort_ships ) { + Last_target_index = 0; + } + + objnum = Escort_ships[Last_target_index].objnum; + set_target_objnum( Player_ai, objnum); + hud_restore_subsystem_target(&Ships[Objects[objnum].instance]); +} + +// return the number of ships currently on the escort list +int hud_escort_num_ships_on_list() +{ + return Num_escort_ships; +} + +// Return the object number for the ship at index position in the escort list +int hud_escort_return_objnum(int index) +{ + int escort_objnum, escort_sig; + if ( index >= Num_escort_ships ) { + return -1; + } + + escort_objnum = Escort_ships[index].objnum; + escort_sig = Escort_ships[index].obj_signature; + + if ( escort_objnum < 0 ) { + return -1; + } + + // ensure this is still a valid index + if ( Objects[escort_objnum].signature != escort_sig ) { + return -1; + } + + return Escort_ships[index].objnum; +} + + +void hudescort_page_in() +{ + int i; + + for ( i = 0; i < MAX_ESCORT_SHIPS; i++ ) { + bm_page_in_aabitmap( Escort_gauges[i].first_frame, Escort_gauges[i].num_frames); + } +} + +void hud_escort_add_player(short id) +{ + Assert(Game_mode & GM_MULTIPLAYER); + if(!(Game_mode & GM_MULTIPLAYER)){ + return; + } + + int idx; + + // just go through and add as long as its not a duplicate + for(idx=0; idxweapon or weapon->shield energy transfer +#define INTIAL_SHIELD_RECHARGE_INDEX 4 // default shield charge rate (index in Energy_levels[]) +#define INTIAL_WEAPON_RECHARGE_INDEX 4 // default weapon charge rate (index in Energy_levels[]) +#define INTIAL_ENGINE_RECHARGE_INDEX 4 // default engine charge rate (index in Energy_levels[]) + +#define MAX_SHIELD_REGEN_PER_SECOND 0.02f // max percent/100 of shield energy regenerated per second +#define MAX_WEAPON_REGEN_PER_SECOND 0.04f // max percent/100 of weapon energy regenerated per second + +#define NUM_ENERGY_LEVELS 13 +#define MAX_ENERGY_INDEX (NUM_ENERGY_LEVELS - 1) +float Energy_levels[NUM_ENERGY_LEVELS] = {0.0f, 0.0833f, 0.167f, 0.25f, 0.333f, 0.417f, 0.5f, 0.583f, 0.667f, 0.75f, 0.833f, 0.9167f, 1.0f}; + +#define AI_MODIFY_ETS_INTERVAL 500 // time between ets modifications for ai's (in milliseconds) + +int Weapon_energy_cheat = 0; + +// skill level scaling of the amount of energy that is allocated to the weapons and +// shields +float Skill_level_weapon_energy_scale[NUM_SKILL_LEVELS] = {10.0f, 4.0f, 2.5f, 2.0f, 1.5f}; +float Skill_level_shield_energy_scale[NUM_SKILL_LEVELS] = {4.0f, 2.0f, 1.5f, 1.25f, 1.0f}; + +#define ZERO_INDEX 0 +#define ONE_THIRD_INDEX 4 +#define ONE_HALF_INDEX 6 +#define ALL_INDEX 12 + +#define HAS_ENGINES (1<<0) +#define HAS_SHIELDS (1<<1) +#define HAS_WEAPONS (1<<2) + +int ETS_bar_h[GR_NUM_RESOLUTIONS] = { + 41, + 41 +}; + +typedef struct ets_gauge_info +{ + char letter; + int letter_coords[2]; + int top_coords[2]; + int bottom_coords[2]; +} ets_gauge_info; + +ets_gauge_info Ets_gauge_info_german[GR_NUM_RESOLUTIONS][3] = +{ + { // GR_640 + 'G', {525,422}, {523,380}, {523,430}, + 'S', {542,422}, {540,380}, {540,430}, + 'A', {559,422}, {557,380}, {557,430} + }, + { // GR_1024 + 'G', {882,690}, {880,648}, {880,698}, + 'S', {900,690}, {898,648}, {898,698}, + 'A', {917,690}, {916,648}, {916,698} + } +}; +ets_gauge_info Ets_gauge_info_french[GR_NUM_RESOLUTIONS][3] = +{ + { // GR_640 + 'C', {525,422}, {523,380}, {523,430}, + 'B', {542,422}, {540,380}, {540,430}, + 'M', {560,422}, {557,380}, {557,430} + }, + { // GR_1024 + 'C', {882,690}, {880,648}, {880,698}, + 'B', {900,690}, {898,648}, {898,698}, + 'M', {918,690}, {916,648}, {916,698} + }, +}; +ets_gauge_info Ets_gauge_info_english[GR_NUM_RESOLUTIONS][3] = +{ + { // GR_640 + 'G', {525,422}, {523,380}, {523,430}, + 'S', {542,422}, {540,380}, {540,430}, + 'E', {560,422}, {557,380}, {557,430} + }, + { // GR_1024 + 'G', {882,690}, {880,648}, {880,698}, + 'S', {900,690}, {898,648}, {898,698}, + 'E', {918,690}, {916,648}, {916,698} + } +}; +ets_gauge_info *Ets_gauge_info = NULL; + +char Ets_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "energy1", + "energy1" +}; + +hud_frames Ets_gauge; + +static int Hud_ets_inited = 0; + +void hud_init_ets() +{ + if ( Hud_ets_inited ) + return; + + Ets_gauge.first_frame = bm_load_animation(Ets_fname[gr_screen.res], &Ets_gauge.num_frames); + if ( Ets_gauge.first_frame < 0 ) { + Warning(LOCATION,"Cannot load hud ani: Ets_fname[gr_screen.res]\n"); + } + + if(Lcl_gr){ + Ets_gauge_info = Ets_gauge_info_german[gr_screen.res]; + } else if(Lcl_fr){ + Ets_gauge_info = Ets_gauge_info_french[gr_screen.res]; + } else { + Ets_gauge_info = Ets_gauge_info_english[gr_screen.res]; + } + + Hud_ets_inited = 1; +} + +// ------------------------------------------------------------------------------------------------- +// ets_init_ship() is called by a ship when it is created (effectively, for every ship at the start +// of a mission). This will set the default charge rates for the different systems and initalialize +// the weapon energy reserve. +// +void ets_init_ship(object* obj) +{ + ship* sp; + + // fred should bail here + if(Fred_running){ + return; + } + + Assert(obj->type == OBJ_SHIP); + sp = &Ships[obj->instance]; + + sp->weapon_energy = Ship_info[sp->ship_info_index].max_weapon_reserve; + sp->next_manage_ets = timestamp(AI_MODIFY_ETS_INTERVAL); + set_default_recharge_rates(obj); +} + +// ------------------------------------------------------------------------------------------------- +// update_ets() is called once per frame for every OBJ_SHIP in the game. The amount of energy +// to send to the weapons and shields is calculated, and the top ship speed is calculated. The +// amount of time elapsed from the previous call is passed in as the parameter fl_frametime. +// +// parameters: obj ==> object that is updating their energy system +// fl_frametime ==> game frametime (in seconds) +// +void update_ets(object* objp, float fl_frametime) +{ + float max_new_shield_energy, max_new_weapon_energy, _ss; + + if ( fl_frametime <= 0 ){ + return; + } + + ship* ship_p = &Ships[objp->instance]; + ship_info* sinfo_p = &Ship_info[ship_p->ship_info_index]; + + if ( ship_p->flags & SF_DYING ){ + return; + } + + if ( sinfo_p->power_output == 0 ){ + return; + } + +// new_energy = fl_frametime * sinfo_p->power_output; + + // update weapon energy + max_new_weapon_energy = fl_frametime * MAX_WEAPON_REGEN_PER_SECOND * sinfo_p->max_weapon_reserve; + if ( objp->flags & OF_PLAYER_SHIP ) { + ship_p->weapon_energy += Energy_levels[ship_p->weapon_recharge_index] * max_new_weapon_energy * Skill_level_weapon_energy_scale[Game_skill_level]; + } else { + ship_p->weapon_energy += Energy_levels[ship_p->weapon_recharge_index] * max_new_weapon_energy; + } + + if ( ship_p->weapon_energy > sinfo_p->max_weapon_reserve ){ + ship_p->weapon_energy = sinfo_p->max_weapon_reserve; + } + + float shield_delta; + max_new_shield_energy = fl_frametime * MAX_SHIELD_REGEN_PER_SECOND * sinfo_p->shields; + if ( objp->flags & OF_PLAYER_SHIP ) { + shield_delta = Energy_levels[ship_p->shield_recharge_index] * max_new_shield_energy * Skill_level_shield_energy_scale[Game_skill_level]; + } else { + shield_delta = Energy_levels[ship_p->shield_recharge_index] * max_new_shield_energy; + } + + add_shield_strength(objp, shield_delta); + + if ( (_ss = get_shield_strength(objp)) > sinfo_p->shields ){ + for (int i=0; ishields[i] *= sinfo_p->shields/ _ss; + } + } + + // calculate the top speed of the ship based on the energy flow to engines + float y = Energy_levels[ship_p->engine_recharge_index]; + + // check for a shortcuts first before doing linear interpolation + if ( y == Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX] ){ + ship_p->current_max_speed = sinfo_p->max_speed; + } else if ( y == 0.0f ){ + ship_p->current_max_speed = 0.5f * sinfo_p->max_speed; + } else if ( y == 1.0f ){ + ship_p->current_max_speed = sinfo_p->max_overclocked_speed; + } else { + // do a linear interpolation to find the current max speed, using points (0,1/2 default_max_speed) (.333,default_max_speed) + // x = x1 + (y-y1) * (x2-x1) / (y2-y1); + if ( y < Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX] ){ + ship_p->current_max_speed = 0.5f*sinfo_p->max_speed + (y * (0.5f*sinfo_p->max_speed) ) / Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX]; + } else { + // do a linear interpolation to find the current max speed, using points (.333,default_max_speed) (1,max_overclock_speed) + ship_p->current_max_speed = sinfo_p->max_speed + (y - Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX]) * (sinfo_p->max_overclocked_speed - sinfo_p->max_speed) / (1.0f - Energy_levels[INTIAL_ENGINE_RECHARGE_INDEX]); + } + } + + // AL 11-15-97: Rules for engine strength affecting max speed: + // 1. if strength >= 0.5 no affect + // 2. if strength < 0.5 then max_speed = sqrt(strength) + // + // This will translate to 71% max speed at 50% engines, and 31% max speed at 10% engines + // + float strength = ship_get_subsystem_strength(ship_p, SUBSYSTEM_ENGINE); + + // don't let engine strength affect max speed when playing on lowest skill level + if ( (objp != Player_obj) || (Game_skill_level > 0) ) { + if ( strength < 0.5 ) { + ship_p->current_max_speed *= fl_sqrt(strength); + } + } + + if ( timestamp_elapsed(ship_p->next_manage_ets) ) { + if ( !(objp->flags & OF_PLAYER_SHIP) ) { + ai_manage_ets(objp); + ship_p->next_manage_ets = timestamp(AI_MODIFY_ETS_INTERVAL); + } + else { + if ( Weapon_energy_cheat ){ + ship_p->weapon_energy = sinfo_p->max_weapon_reserve; + } + } + } +} + + +// ------------------------------------------------------------------------------------------------- +// ai_manage_ets() will determine if a ship should modify it's energy transfer percentages, or +// transfer energy from shields->weapons or from weapons->shields +// + +// minimum level rule constants +#define SHIELDS_MIN_LEVEL_PERCENT 0.3f +#define WEAPONS_MIN_LEVEL_PERCENT 0.3f + +// maximum level rule constants +#define SHIELDS_MAX_LEVEL_PERCENT 0.8f +#define WEAPONS_MAX_LEVEL_PERCENT 0.8f + +// emergency rule constants +#define SHIELDS_EMERG_LEVEL_PERCENT 0.10f +#define WEAPONS_EMERG_LEVEL_PERCENT 0.05f + +// need this, or ai's tend to totally eliminate engine power! +#define MIN_ENGINE_RECHARGE_INDEX 3 + +#define DEFAULT_CHARGE_INDEX 4 +#define NORMAL_TOLERANCE_PERCENT .10f + +void ai_manage_ets(object* obj) +{ + ship* ship_p = &Ships[obj->instance]; + ship_info* ship_info_p = &Ship_info[ship_p->ship_info_index]; + + if ( ship_info_p->power_output == 0 ) + return; + + if (ship_p->flags & SF_DYING) + return; + + // check if any of the three systems are not being used. If so, don't allow energy management. + if ( !ship_info_p->shields || !ship_info_p->max_speed || !ship_info_p->max_weapon_reserve) + return; + + float shield_left_percent = get_shield_strength(obj)/ship_info_p->shields; + float weapon_left_percent = ship_p->weapon_energy/ship_info_p->max_weapon_reserve; + + // maximum level check + // MK, changed these, might as well let them go up to 100% if nothing else needs the recharge ability. + if ( weapon_left_percent == 1.0f) { + decrease_recharge_rate(obj, WEAPONS); + } + + if (!(obj->flags & OF_NO_SHIELDS) && (shield_left_percent == 1.0f)) { + decrease_recharge_rate(obj, SHIELDS); + } + + // minimum check + + if (!(obj->flags & OF_NO_SHIELDS) && (shield_left_percent < SHIELDS_MIN_LEVEL_PERCENT)) { + if ( weapon_left_percent > WEAPONS_MIN_LEVEL_PERCENT ) + increase_recharge_rate(obj, SHIELDS); + } + + if ( weapon_left_percent < WEAPONS_MIN_LEVEL_PERCENT ) { + increase_recharge_rate(obj, WEAPONS); + } + + if ( ship_p->engine_recharge_index < MIN_ENGINE_RECHARGE_INDEX ) { + increase_recharge_rate(obj, ENGINES); + } + + // emergency check + if (!(obj->flags & OF_NO_SHIELDS)) { + if ( shield_left_percent < SHIELDS_EMERG_LEVEL_PERCENT ) { + if (ship_p->target_shields_delta == 0.0f) + transfer_energy_to_shields(obj); + } else if ( weapon_left_percent < WEAPONS_EMERG_LEVEL_PERCENT ) { + if ( shield_left_percent > SHIELDS_MIN_LEVEL_PERCENT || weapon_left_percent <= 0.01 ) // dampen ai enthusiasm for sucking energy to weapons + transfer_energy_to_weapons(obj); + } + + + // check for return to normal values + if ( fl_abs( shield_left_percent - 0.5f ) < NORMAL_TOLERANCE_PERCENT ) { + if ( ship_p->shield_recharge_index > DEFAULT_CHARGE_INDEX ) + decrease_recharge_rate(obj, SHIELDS); + else if ( ship_p->shield_recharge_index < DEFAULT_CHARGE_INDEX ) + increase_recharge_rate(obj, SHIELDS); + } + } + + + if ( fl_abs( weapon_left_percent - 0.5f ) < NORMAL_TOLERANCE_PERCENT ) { + if ( ship_p->weapon_recharge_index > DEFAULT_CHARGE_INDEX ) + decrease_recharge_rate(obj, WEAPONS); + else if ( ship_p->weapon_recharge_index < DEFAULT_CHARGE_INDEX ) + increase_recharge_rate(obj, WEAPONS); + } +} + +// ------------------------------------------------------------------------------------------------- +// hud_show_ets() will display the charge rates for the three systems, and the reserve +// energy for shields and weapons. hud_show_ets() is called once per frame. +// +void hud_show_ets() +{ + int i, j, index, y_start, y_end, clip_h, w, h, x, y; + + ship* ship_p = &Ships[Player_obj->instance]; + + if ( Ets_gauge.first_frame < 0 ) { + return; + } + + hud_set_gauge_color(HUD_ETS_GAUGE); + + // draw the letters for the gauges first, before any clipping occurs + i = 0; + for ( j = 0; j < 3; j++ ) { + if ( j == 1 && Player_obj->flags & OF_NO_SHIELDS ) { + continue; + } + Assert(Ets_gauge_info != NULL); + gr_printf(Ets_gauge_info[i].letter_coords[0], Ets_gauge_info[i].letter_coords[1], NOX("%c"), Ets_gauge_info[j].letter); + i++; + } + + // draw the three energy gauges + i = 0; + index = 0; + for ( j = 0; j < 3; j++ ) { + switch (j) { + case 0: + index = ship_p->weapon_recharge_index; + break; + case 1: + index = ship_p->shield_recharge_index; + if ( Player_obj->flags & OF_NO_SHIELDS ) { + continue; + } + break; + case 2: + index = ship_p->engine_recharge_index; + break; + } + + clip_h = fl2i( (1 - Energy_levels[index]) * ETS_bar_h[gr_screen.res] ); + + bm_get_info(Ets_gauge.first_frame,&w,&h); + + if ( index < NUM_ENERGY_LEVELS-1 ) { + // some portion of dark needs to be drawn + + hud_set_gauge_color(HUD_ETS_GAUGE); + + // draw the top portion + + Assert(Ets_gauge_info != NULL); + x = Ets_gauge_info[i].top_coords[0]; + y = Ets_gauge_info[i].top_coords[1]; + + GR_AABITMAP_EX(Ets_gauge.first_frame,x,y,w,clip_h,0,0); + + // draw the bottom portion + Assert(Ets_gauge_info != NULL); + x = Ets_gauge_info[i].bottom_coords[0]; + y = Ets_gauge_info[i].bottom_coords[1]; + + y_start = y + (ETS_bar_h[gr_screen.res] - clip_h); + y_end = y + ETS_bar_h[gr_screen.res]; + + GR_AABITMAP_EX(Ets_gauge.first_frame, x, y_start, w, y_end-y_start, 0, ETS_bar_h[gr_screen.res]-clip_h); + } + + if ( index > 0 ) { + if ( hud_gauge_maybe_flash(HUD_ETS_GAUGE) == 1 ) { + hud_set_gauge_color(HUD_ETS_GAUGE, HUD_C_DIM); + // hud_set_dim_color(); + } else { + hud_set_gauge_color(HUD_ETS_GAUGE, HUD_C_BRIGHT); + // hud_set_bright_color(); + } + // some portion of recharge needs to be drawn + + // draw the top portion + Assert(Ets_gauge_info != NULL); + x = Ets_gauge_info[i].top_coords[0]; + y = Ets_gauge_info[i].top_coords[1]; + + y_start = y + clip_h; + y_end = y + ETS_bar_h[gr_screen.res]; + + GR_AABITMAP_EX(Ets_gauge.first_frame+1, x, y_start, w, y_end-y_start, 0, clip_h); + + // draw the bottom portion + Assert(Ets_gauge_info != NULL); + x = Ets_gauge_info[i].bottom_coords[0]; + y = Ets_gauge_info[i].bottom_coords[1]; + + GR_AABITMAP_EX(Ets_gauge.first_frame+2, x,y,w,ETS_bar_h[gr_screen.res]-clip_h,0,0); + } + i++; + } + + // hud_set_default_color(); +} + +// ------------------------------------------------------------------------------------------------- +// set_default_recharge_rates() will set the charge levels for the weapons, shields and +// engines to their default levels +void set_default_recharge_rates(object* obj) +{ + int ship_properties; + + ship* ship_p = &Ships[obj->instance]; + ship_info* ship_info_p = &Ship_info[ship_p->ship_info_index]; + + if ( ship_info_p->power_output == 0 ) + return; + + ship_properties = 0; + if ( ship_info_p->max_weapon_reserve ) + ship_properties |= HAS_WEAPONS; + + if (!(obj->flags & OF_NO_SHIELDS)) + ship_properties |= HAS_SHIELDS; + + if ( ship_info_p->max_speed ) + ship_properties |= HAS_ENGINES; + + // the default charge rate depends on what systems are on each ship + switch ( ship_properties ) { + case HAS_ENGINES | HAS_WEAPONS | HAS_SHIELDS: + ship_p->shield_recharge_index = INTIAL_SHIELD_RECHARGE_INDEX; + ship_p->weapon_recharge_index = INTIAL_WEAPON_RECHARGE_INDEX; + ship_p->engine_recharge_index = INTIAL_ENGINE_RECHARGE_INDEX; + break; + + case HAS_ENGINES | HAS_SHIELDS: + ship_p->shield_recharge_index = ONE_HALF_INDEX; + ship_p->weapon_recharge_index = ZERO_INDEX; + ship_p->engine_recharge_index = ONE_HALF_INDEX; + break; + + case HAS_WEAPONS | HAS_SHIELDS: + ship_p->shield_recharge_index = ONE_HALF_INDEX; + ship_p->weapon_recharge_index = ONE_HALF_INDEX; + ship_p->engine_recharge_index = ZERO_INDEX; + break; + + case HAS_ENGINES | HAS_WEAPONS: + ship_p->shield_recharge_index = ZERO_INDEX; + ship_p->weapon_recharge_index = ONE_HALF_INDEX; + ship_p->engine_recharge_index = ONE_HALF_INDEX; + break; + + case HAS_SHIELDS: + ship_p->shield_recharge_index = ALL_INDEX; + ship_p->weapon_recharge_index = ZERO_INDEX; + ship_p->engine_recharge_index = ZERO_INDEX; + break; + + case HAS_ENGINES: + ship_p->shield_recharge_index = ZERO_INDEX; + ship_p->weapon_recharge_index = ZERO_INDEX; + ship_p->engine_recharge_index = ALL_INDEX; + break; + + case HAS_WEAPONS: + ship_p->shield_recharge_index = ZERO_INDEX; + ship_p->weapon_recharge_index = ALL_INDEX; + ship_p->engine_recharge_index = ZERO_INDEX; + break; + + default: + Int3(); // if no systems, power output should be zero, and this funtion shouldn't be called + break; + } // end switch +} + +// ------------------------------------------------------------------------------------------------- +// increase_recharge_rate() will increase the energy flow to the specified system (one of +// WEAPONS, SHIELDS or ENGINES). The increase in energy will result in a decrease to +// the other two systems. +void increase_recharge_rate(object* obj, SYSTEM_TYPE ship_system) +{ + int *gain_index=NULL, *lose_index1=NULL, *lose_index2=NULL, *tmp=NULL; + int count=0; + ship *ship_p = &Ships[obj->instance]; + + switch ( ship_system ) { + case WEAPONS: + gain_index = &ship_p->weapon_recharge_index; + lose_index1 = &ship_p->engine_recharge_index; + if ( obj->flags & OF_NO_SHIELDS ) { + lose_index2 = NULL; + } else { + lose_index2 = &ship_p->shield_recharge_index; + } + break; + + case SHIELDS: + if ( obj->flags & OF_NO_SHIELDS ) { + return; + } + gain_index = &ship_p->shield_recharge_index; + lose_index1 = &ship_p->weapon_recharge_index; + lose_index2 = &ship_p->engine_recharge_index; + break; + + case ENGINES: + gain_index = &ship_p->engine_recharge_index; + lose_index1 = &ship_p->weapon_recharge_index; + if ( obj->flags & OF_NO_SHIELDS ) { + lose_index2 = NULL; + } else { + lose_index2 = &ship_p->shield_recharge_index; + } + break; + + } // end switch + + // already full, nothing to do + count = MAX_ENERGY_INDEX - *gain_index; + if ( count > 2 ) + count = 2; + + if ( count <= 0 ) { + if ( obj == Player_obj ) { + snd_play( &Snds[SND_ENERGY_TRANS_FAIL], 0.0f ); + } + return; + } + + *gain_index += count; + + // ensure that the highest lose index takes the first decrease + if ( lose_index1 && lose_index2 ) { + if ( *lose_index1 < *lose_index2 ) { + tmp = lose_index1; + lose_index1 = lose_index2; + lose_index2 = tmp; + } + } + + int sanity = 0; + while(count > 0) { + if ( lose_index1 && *lose_index1 > 0 ) { + *lose_index1 -= 1; + count--; + } + + if ( count <= 0 ) + break; + + if ( lose_index2 && *lose_index2 > 0 ) { + *lose_index2 -= 1; + count--; + } + + if ( sanity++ > 10 ) { + Int3(); // get Alan + break; + } + } + + if ( obj == Player_obj ) + snd_play( &Snds[SND_ENERGY_TRANS], 0.0f ); +} + +// ------------------------------------------------------------------------------------------------- +// decrease_recharge_rate() will decrease the energy flow to the specified system (one of +// WEAPONS, SHIELDS or ENGINES). The decrease in energy will result in an increase to +// the other two systems. +void decrease_recharge_rate(object* obj, SYSTEM_TYPE ship_system) +{ + int *lose_index=NULL, *gain_index1=NULL, *gain_index2=NULL, *tmp=NULL; + int count; + ship *ship_p = &Ships[obj->instance]; + + switch ( ship_system ) { + case WEAPONS: + lose_index = &ship_p->weapon_recharge_index; + + if ( obj->flags & OF_NO_SHIELDS ) { + gain_index1 = NULL; + } else { + gain_index1 = &ship_p->shield_recharge_index; + } + + gain_index2 = &ship_p->engine_recharge_index; + break; + + case SHIELDS: + if ( obj->flags & OF_NO_SHIELDS ) { + return; + } + lose_index = &ship_p->shield_recharge_index; + gain_index1 = &ship_p->weapon_recharge_index; + gain_index2 = &ship_p->engine_recharge_index; + break; + + case ENGINES: + lose_index = &ship_p->engine_recharge_index; + + if ( obj->flags & OF_NO_SHIELDS ) { + gain_index1 = NULL; + } else { + gain_index1 = &ship_p->shield_recharge_index; + } + + gain_index2 = &ship_p->weapon_recharge_index; + break; + } // end switch + + // check how much there is to lose + count = min(2, *lose_index); + if ( count <= 0 ) { + if ( obj == Player_obj ) { + snd_play( &Snds[SND_ENERGY_TRANS_FAIL], 0.0f ); + } + return; + } + + *lose_index -= count; + + // make sure that the gain starts with the system which needs it most + if ( gain_index1 && gain_index2 ) { + if ( *gain_index1 > *gain_index2 ) { + tmp = gain_index1; + gain_index1 = gain_index2; + gain_index2 = tmp; + } + } + + int sanity=0; + while(count > 0) { + if ( gain_index1 && *gain_index1 < MAX_ENERGY_INDEX ) { + *gain_index1 += 1; + count--; + } + + if ( count <= 0 ) + break; + + if ( gain_index2 && *gain_index2 < MAX_ENERGY_INDEX ) { + *gain_index2 += 1; + count--; + } + + if ( sanity++ > 10 ) { + Int3(); // get Alan + break; + } + } + + if ( obj == Player_obj ) + snd_play( &Snds[SND_ENERGY_TRANS], 0.0f ); +} + +void transfer_energy_weapon_common(object *objp, float from_field, float to_field, float *from_delta, float *to_delta, float max, float scale) +{ + float delta; + + delta = from_field * ENERGY_DIVERT_DELTA * scale; + + if (to_field + *to_delta + delta > max) + delta = max - to_field - *to_delta; + + if ( delta > 0 ) { + if ( objp == Player_obj ) + snd_play( &Snds[SND_ENERGY_TRANS], 0.0f ); + + if (delta > from_field) + delta = from_field; + + *to_delta += delta; + *from_delta -= delta; + } else + if ( objp == Player_obj ) + snd_play( &Snds[SND_ENERGY_TRANS_FAIL], 0.0f ); +} + +// ------------------------------------------------------------------------------------------------- +// transfer_energy_to_shields() will transfer ENERGY_DIVERT_DELTA percent of weapon energy +// to shield energy. +void transfer_energy_to_shields(object* obj) +{ + ship* ship_p = &Ships[obj->instance]; + ship_info* sinfo_p = &Ship_info[ship_p->ship_info_index]; + + if (ship_p->flags & SF_DYING) + return; + + if ( obj->flags & OF_NO_SHIELDS ) { + return; + } + + transfer_energy_weapon_common(obj, ship_p->weapon_energy, get_shield_strength(obj), &ship_p->target_weapon_energy_delta, &ship_p->target_shields_delta, sinfo_p->shields, 0.5f); +} + +// ------------------------------------------------------------------------------------------------- +// transfer_energy_to_weapons() will transfer ENERGY_DIVERT_DELTA percent of shield energy +// to weapon energy. +void transfer_energy_to_weapons(object* obj) +{ + ship* ship_p = &Ships[obj->instance]; + ship_info* sinfo_p = &Ship_info[ship_p->ship_info_index]; + + if (ship_p->flags & SF_DYING) + return; + + transfer_energy_weapon_common(obj, get_shield_strength(obj), ship_p->weapon_energy, &ship_p->target_shields_delta, &ship_p->target_weapon_energy_delta, sinfo_p->max_weapon_reserve, 1.0f); +} + +void hudets_page_in() +{ + bm_page_in_aabitmap( Ets_gauge.first_frame, Ets_gauge.num_frames ); +} diff --git a/src/hud/hudlock.cpp b/src/hud/hudlock.cpp new file mode 100644 index 0000000..0b800ba --- /dev/null +++ b/src/hud/hudlock.cpp @@ -0,0 +1,1238 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDlock.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module that controls missile locking + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 19 8/09/99 10:50a Jasenw + * + * 18 7/30/99 5:42p Jasenw + * changed coords to center HUD lock indicator. + * + * 17 7/28/99 2:49p Andsager + * Make hud target speed use vm_vec_mag (not vm_vec_mag_quick) + * + * 16 6/08/99 7:41p Dave + * Animated hud lockup icon a bit differently. + * + * 15 6/08/99 8:35a Jasenw + * new coords for new lock ani + * + * 14 6/07/99 4:20p Andsager + * Add HUD color for tagged object. Apply to target and radar. + * + * 13 6/07/99 1:42p Jasenw + * + * 12 6/04/99 10:58a Dave + * Updated hud lock gauge. + * + * 11 6/03/99 2:32p Jasenw + * changed coords for new HUD stuff + * + * 10 6/02/99 5:41p Andsager + * Reduce range of secondary weapons not fired from turrets in nebula. + * Reduce range of beams fired from turrrets in nebula + * + * 9 4/23/99 12:01p Johnson + * Added SIF_HUGE_SHIP + * + * 8 2/25/99 4:19p Dave + * Added multiplayer_beta defines. Added cd_check define. Fixed a few + * release build warnings. Added more data to the squad war request and + * response packets. + * + * 7 1/07/99 9:07a Jasen + * HUD coords + * + * 6 12/30/98 7:47a Jasen + * added coordinates for hi res + * + * 5 12/28/98 3:17p Dave + * Support for multiple hud bitmap filenames for hi-res mode. + * + * 4 12/21/98 5:02p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 55 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 54 5/11/98 3:48p Lawrance + * Break lock when banks switch + * + * 53 5/10/98 7:05p Dave + * Fix endgame sequencing ESC key. Changed how host options warning popups + * are done. Fixed pause/message scrollback/options screen problems in mp. + * Make sure observer HUD doesn't try to lock weapons. + * + * 52 5/04/98 10:50p Lawrance + * Don't break lock when switching to secondary weapons of the same type + * + * 51 4/20/98 12:36a Mike + * Make team vs. team work when player is hostile. Several targeting + * problems. + * + * 50 4/15/98 12:55a Lawrance + * Make shockwave damage 1/4 if bomb detonated by another weapon. Allow + * missiles to lock on bombs. + * + * 49 4/13/98 4:52p Allender + * remove AI_frametime and us flFrametime instead. Make lock warning work + * in multiplayer for aspect seeking missiles. Debris fixups + * + * 48 4/08/98 8:33p Lawrance + * Make player re-acquire lock when targeting subsystems on a locked ship. + * + * 47 4/03/98 10:29a Mike + * Make aspect seekers home on a ship even if the targeted subsystem is + * backfacing. Too confusing. + * + * 46 3/26/98 5:46p Lawrance + * call obj_team() instead of ship_team_from_obj() + * + * 45 3/26/98 5:26p John + * added new paging code. nonfunctional. + * + * 44 3/10/98 4:19p John + * Cleaned up graphics lib. Took out most unused gr functions. Made D3D + * & Glide have popups and print screen. Took out all >8bpp software + * support. Made Fred zbuffer. Made zbuffer allocate dynamically to + * support Fred. Made zbuffering key off of functions rather than one + * global variable. + * + * 43 3/10/98 11:16a Lawrance + * Fix potential bug with missles not locking on a subsystem that appeared + * in view + * + * 42 2/28/98 7:03p Lawrance + * Change player missile locking to use dot product, so we can use it in + * the external views + * + * 41 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 40 1/25/98 10:31p Lawrance + * Don't draw most hud gauges when viewing from another ship. + * + * 39 1/24/98 4:48p Lawrance + * Add 'locking_subsys_parent', needed to save/restore locking_subsys + * pointer. + * + * 38 1/23/98 6:25p Lawrance + * Change player missile locking to lock on subsystem points automatically + * + * 37 1/21/98 7:20p Lawrance + * Make subsystem locking only work with line-of-sight, cleaned up locking + * code, moved globals to player struct. + * + * 36 1/19/98 10:02p Lawrance + * Fix bug with locking on friendlies + * + * 35 12/30/97 4:28p Lawrance + * remove .ani extensions from filenames + * + * 34 12/10/97 9:57a Lawrance + * allow missile locks on all ships but friendly + * + * 33 11/17/97 6:37p Lawrance + * new gauges: extended target view, new lock triangles, support ship view + * + * 32 11/17/97 5:51p Lawrance + * fix bug that was sometimes causing lock to not occur + * + * 31 11/13/97 10:46p Lawrance + * ensure only attempt lock on HOSTILE + * + * 30 11/11/97 12:59a Lawrance + * fix couple of bugs with lock indicator not going away + * + * 29 11/08/97 11:07p Lawrance + * add in new lock indicator + * + * 28 10/28/97 9:02a Lawrance + * don't loop lock sound, some minor reformatting + * + * 27 10/08/97 5:07p Lawrance + * don't lock on friendly ships + * + * 26 9/07/97 10:02p Lawrance + * don't lock on a ship if player doesn't have any missiles + * + * 25 7/02/97 9:35a Hoffoss + * Changed all references to weapon variables in ships to 'weapons' + * structure variables in ships. + * + * 24 6/05/97 1:07a Lawrance + * changes to support sound interface + * + * 23 5/20/97 2:45p Mike + * Move current_target and a bunch of other stuff out of player struct. + * + * 22 4/24/97 10:55a Lawrance + * added Show_lock_cone to DCF + * + * 21 4/18/97 2:54p Lawrance + * sounds now have a default volume, when playing, pass a scaling factor + * not the actual volume + * + * 20 4/15/97 8:36a Lawrance + * fixed bug where lock indicator was lagging locked target + * + * 19 4/13/97 3:53p Lawrance + * separate out the non-rendering dependant portions of the HUD ( sounds, + * updating lock position, changing targets, etc) and put into + * hud_update_frame() + * + * 18 4/12/97 4:29p Lawrance + * get missle locking and offscreen indicator working properly with + * different sized screens + * + * 17 4/10/97 5:29p Lawrance + * hud rendering split up into hud_render_3d(), hud_render_2d() and + * hud_render_target_model() + * + * 16 4/09/97 4:34p Lawrance + * allow looped sounds to be cut off after they complete the full sample + * duration + * + * 15 3/25/97 8:17p Lawrance + * don't allow ASPECT homing missles to lock on debris + * + * 14 3/19/97 5:53p Lawrance + * integrating new Misc_sounds[] array (replaces old Game_sounds + * structure) + * + * 13 3/10/97 8:53a Lawrance + * using hud_stop_looped_locking_sounds() in place of + * hud_stop_looped_sounds() + * + * 12 3/07/97 4:37p Mike + * Make rockeye missile home. + * Remove UNKNOWN and NEUTRAL teams. + * + * 11 3/04/97 2:27p Lawrance + * check to ensure player ship has missile banks + * + * 10 3/04/97 11:19a Lawrance + * supporting banked weapons + * + * 9 1/23/97 12:00p Lawrance + * made lock sound play at normal volume. + * + * 8 1/20/97 7:58p John + * Fixed some link errors with testcode. + * + * 7 1/13/97 5:36p Lawrance + * integrating new Game_sounds structure for general game sounds + * + * 6 1/06/97 10:43p Lawrance + * Changes to make save/restore functional + * + * 5 1/02/97 7:12p Lawrance + * adding hooks for more sounds + * + * 4 1/02/97 10:32a Lawrance + * fixed some bugs related to stopping looped sounds when targets die and + * going to menus + * + * 3 12/24/96 4:32p Lawrance + * some minor improvements + * + * 2 12/23/96 7:53p Lawrance + * missile locking working in new source files + * + * $NoKeywords: $ + */ + +#include "hud.h" +#include "hudlock.h" +#include "hudtarget.h" +#include "hudreticle.h" +#include "player.h" +#include "ship.h" +#include "weapon.h" +#include "sound.h" +#include "timer.h" +#include "freespace.h" +#include "gamesequence.h" +#include "gamesnd.h" +#include "ai.h" +#include "bmpman.h" +#include "3d.h" +#include "linklist.h" +#include "multi.h" +#include "emp.h" + + +static float Lock_start_dist; +static int Rotate_time_id = 1; // timer id for controlling how often to rotate triangles around lock indicator + +int Missile_track_loop = -1; +int Missile_lock_loop = -1; + +int Lock_target_box_width[GR_NUM_RESOLUTIONS] = { + 19, + 30 +}; +int Lock_target_box_height[GR_NUM_RESOLUTIONS] = { + 19, + 30 +}; + +// the locked triangles (that orbit lock indicator) dimensions +float Lock_triangle_base[GR_NUM_RESOLUTIONS] = { + 4.0f, + 6.5f +}; +float Lock_triangle_height[GR_NUM_RESOLUTIONS] = { + 4.0f, + 6.5f +}; + +int Lock_gauge_half_w[GR_NUM_RESOLUTIONS] = { + 17, + 28 +}; +int Lock_gauge_half_h[GR_NUM_RESOLUTIONS] = { + 15, + 25 +}; + +// hud_frames Lock_gauge; +int Lock_gauge_loaded = 0; +hud_anim Lock_gauge; +int Lock_gauge_draw = 0; +int Lock_gauge_draw_stamp = -1; +#define LOCK_GAUGE_BLINK_RATE 5 // blinks/sec + +int Lockspin_half_w[GR_NUM_RESOLUTIONS] = { + 31, + 50 +}; +int Lockspin_half_h[GR_NUM_RESOLUTIONS] = { + 32, + 52 +}; +hud_anim Lock_anim; + +char Lock_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "lock1", + "2_lock1" +}; + +char Lockspin_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "lockspin", + "2_lockspin" +}; + +void hud_lock_determine_lock_point(vector *lock_world_pos_out); + +// hud_init_missile_lock() is called at the beginning of a mission +// +void hud_init_missile_lock() +{ + Players[Player_num].lock_indicator_start_x = -1; + Players[Player_num].lock_indicator_start_y = -1; + Players[Player_num].lock_indicator_visible = 0; + Player_ai->current_target_is_locked = 0; + + Player_ai->last_secondary_index = -1; + + Rotate_time_id = 1; + + // Load in the frames need for the lead indicator + if (!Lock_gauge_loaded) { + /* + Lock_gauge.first_frame = bm_load_animation(Lock_fname[gr_screen.res], &Lock_gauge.num_frames); + if ( Lock_gauge.first_frame < 0 ) { + Warning(LOCATION,"Cannot load hud ani: Lock_fname[gr_screen.res]\n"); + } + */ + hud_anim_init(&Lock_gauge, 0, 0, Lock_fname[gr_screen.res]); + hud_anim_load(&Lock_gauge); + + hud_anim_init(&Lock_anim, 0, 0, Lockspin_fname[gr_screen.res]); + hud_anim_load(&Lock_anim); + + Lock_gauge_loaded = 1; + + Lock_gauge_draw_stamp = -1; + Lock_gauge_draw = 0; + } +} + +void hud_draw_diamond(int x, int y, int width, int height) +{ + Assert(height>0); + Assert(width>0); + + int x1,x2,x3,x4,y1,y2,y3,y4; + + x1=x; + y1=y-height/2; + + x2=x+width/2; + y2=y; + + x3=x; + y3=y+height/2; + + x4=x-width/2; + y4=y; + + gr_line(x1,y1,x2,y2); + gr_line(x2,y2,x3,y3); + gr_line(x3,y3,x4,y4); + gr_line(x4,y4,x1,y1); +} + + +// hud_show_lock_indicator() will display the lock indicator for homing missiles +void hud_show_lock_indicator(float frametime) +{ + int target_objnum, sx, sy; + object *targetp; + + if (!Players[Player_num].lock_indicator_visible){ + return; + } + + target_objnum = Player_ai->target_objnum; + Assert(target_objnum != -1); + targetp = &Objects[target_objnum]; + + // check to see if there are any missile to fire.. we don't want to show the + // lock indicator if there are missiles to fire. + if ( !ship_secondary_bank_has_ammo(Player_obj->instance) ) { + return; + } + + hud_set_iff_color(targetp); +// nprintf(("Alan","lockx: %d, locky: %d TargetX: %d, TargetY: %d\n", Players[Player_num].lock_indicator_x, Players[Player_num].lock_indicator_y, Player->current_target_sx, Player->current_target_sy)); + + if (Player_ai->current_target_is_locked) { + sx = Player->current_target_sx; + sy = Player->current_target_sy; + // show the rotating triangles if target is locked + hud_draw_lock_triangles(sx, sy, frametime); + } else { + sx = Players[Player_num].lock_indicator_x; + sy = Players[Player_num].lock_indicator_y; + } + + // show locked indicator + /* + if ( Lock_gauge.first_frame >= 0 ) { + gr_set_bitmap(Lock_gauge.first_frame); + gr_aabitmap(sx - Lock_gauge_half_w[gr_screen.res], sy - Lock_gauge_half_h[gr_screen.res]); + } else { + hud_draw_diamond(sx, sy, Lock_target_box_width[gr_screen.res], Lock_target_box_height[gr_screen.res]); + } + */ + Lock_gauge.sx = sx - Lock_gauge_half_w[gr_screen.res]; + Lock_gauge.sy = sy - Lock_gauge_half_h[gr_screen.res]; + if(Player_ai->current_target_is_locked){ + Lock_gauge.time_elapsed = 0.0f; + hud_anim_render(&Lock_gauge, 0.0f, 1); + } else { + hud_anim_render(&Lock_gauge, frametime, 1); + } +} + +// Reset data used for player lock indicator +void hud_lock_reset(float lock_time_scale) +{ + weapon_info *wip; + ship_weapon *swp; + + swp = &Player_ship->weapons; + wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]]; + + Player_ai->current_target_is_locked = 0; + Players[Player_num].lock_indicator_visible = 0; + Player->target_in_lock_cone = 0; + Player->lock_time_to_target = i2fl(wip->min_lock_time*lock_time_scale); + Player->current_target_sx = -1; + Player->current_target_sy = -1; + Player->locking_subsys=NULL; + Player->locking_on_center=0; + Player->locking_subsys_parent=-1; + hud_stop_looped_locking_sounds(); + + Lock_gauge_draw_stamp = -1; + Lock_gauge_draw = 0; + + // reset the lock anim time elapsed + Lock_anim.time_elapsed = 0.0f; +} + +// Determine if the locking code has a point to track +int hud_lock_has_homing_point() +{ + if ( Player_ai->targeted_subsys || Player->locking_subsys || Player->locking_on_center ) { + return 1; + } + return 0; +} + +int Nebula_sec_range = 0; +DCF_BOOL(nebula_sec_range, Nebula_sec_range) + +int hud_lock_world_pos_in_range(vector *target_world_pos, vector *vec_to_target) +{ + float dist_to_target, weapon_range; + weapon_info *wip; + ship_weapon *swp; + + int target_in_range=1; + + swp = &Player_ship->weapons; + wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]]; + + vm_vec_sub(vec_to_target, target_world_pos, &Player_obj->pos); + dist_to_target = vm_vec_mag(vec_to_target); + + // calculate the range of the weapon, and only display the lead target indicator when + // if the weapon can actually hit the target + weapon_range = wip->max_speed * wip->lifetime; + + // reduce firing range in nebula + if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) { + weapon_range *= 0.8f; + } + + if (dist_to_target > weapon_range) { + target_in_range=0; + } + + return target_in_range; +} + +// Determine if point to lock on is in range +int hud_lock_target_in_range() +{ + vector target_world_pos, vec_to_target; + object *targetp; + + if ( !hud_lock_has_homing_point() ) { + return 0; + } + + targetp = &Objects[Player_ai->target_objnum]; + + if ( Player_ai->targeted_subsys != NULL ) { + vm_vec_unrotate(&target_world_pos, &Player_ai->targeted_subsys->system_info->pnt, &targetp->orient); + vm_vec_add2(&target_world_pos, &targetp->pos); + } else { + if ( Player->locking_subsys ) { + vm_vec_unrotate(&target_world_pos, &Player->locking_subsys->system_info->pnt, &targetp->orient); + vm_vec_add2(&target_world_pos, &targetp->pos); + } else { + Assert(Player->locking_on_center); + target_world_pos = targetp->pos; + } + } + + return hud_lock_world_pos_in_range(&target_world_pos, &vec_to_target); +} + +int hud_abort_lock() +{ + int target_team; + + target_team = obj_team(&Objects[Player_ai->target_objnum]); + + if ( Player_ship->weapons.num_secondary_banks <= 0 ) { + return 1; + } + + if ( Player_ship->weapons.current_secondary_bank < 0 ) { + return 1; + } + + // check to see if there are any missile to fire.. we don't want to show the + // lock indicator if there are no missiles to fire. + if ( !ship_secondary_bank_has_ammo(Player_obj->instance) ) { + return 1; + } + + // if the target is friendly, don't lock! + if ( hud_team_matches_filter(Player_ship->team, target_team)) { + // if we're in multiplayer dogfight, ignore this + if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){ + return 1; + } + } + + return 0; +} + +// determine if the subsystem to lock on to has a direct line of sight +int hud_lock_on_subsys_ok() +{ + ship_subsys *subsys; + vector subobj_pos; + object *target_objp; + int in_sight=0; + + Assert(Player_ai->target_objnum >= 0); + target_objp = &Objects[Player_ai->target_objnum]; + + subsys = Player_ai->targeted_subsys; + if ( !subsys ) { + return 0; + } + + vm_vec_unrotate(&subobj_pos, &subsys->system_info->pnt, &target_objp->orient); + vm_vec_add2(&subobj_pos, &target_objp->pos); + + if ( Player->subsys_in_view < 0 ) { + in_sight = ship_subsystem_in_sight(target_objp, subsys, &View_position, &subobj_pos); + } else { + in_sight = Player->subsys_in_view; + } + + return in_sight; +} + +// Determine if locking point is in the locking cone +void hud_lock_check_if_target_in_lock_cone(vector *lock_world_pos) +{ + float dist, dot; + vector vec_to_target; + + dist = vm_vec_normalized_dir(&vec_to_target, lock_world_pos, &Player_obj->pos); + dot = vm_vec_dot(&Player_obj->orient.fvec, &vec_to_target); + + if ( dot > 0.85) { + Player->target_in_lock_cone = 1; + } else { + Player->target_in_lock_cone = 0; + } + +} + +// return 1 if current secondary weapon is different than previous secondary weapon +int hud_lock_secondary_weapon_changed(ship_weapon *swp) +{ + + if ( swp->current_secondary_bank != Player_ai->last_secondary_index ) { + return 1; + } + + return 0; +/* + int last_wi_index = -1; + int current_wi_index = -1; + + + // do a quick out if same bank is selected + if ( swp->current_secondary_bank == Player_ai->last_secondary_index ) { + return 0; + } + + // bank has changed, but it still may be the same weapon type + if ( swp->current_secondary_bank >= 0 ) { + current_wi_index = swp->secondary_bank_weapons[swp->current_secondary_bank]; + } + + if ( Player_ai->last_secondary_index >= 0 ) { + last_wi_index = swp->secondary_bank_weapons[Player_ai->last_secondary_index]; + } + + if ( current_wi_index != last_wi_index ) { + return 1; + } + + return 0; +*/ + +} + +// hud_update_lock_indicator() will manage the non-rendering dependant part of +// missle locking +void hud_update_lock_indicator(float frametime) +{ + ship_weapon *swp; + weapon_info *wip; + vector lock_world_pos; + + // if i'm a multiplayer observer, bail here + if((Game_mode & GM_MULTIPLAYER) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){ + return; + } + + Assert(Player_ai->target_objnum != -1); + + // be sure to unset this flag, then possibly set later in this function so that + // threat indicators work properly. + Player_ai->ai_flags &= ~AIF_SEEK_LOCK; + + if ( hud_abort_lock() ) { + hud_lock_reset(); + return; + } + + // if there is an EMP effect active, never update lock + if(emp_active_local()){ + hud_lock_reset(); + return; + } + + swp = &Player_ship->weapons; + wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]]; + + Lock_start_dist = wip->min_lock_time * wip->lock_pixels_per_sec; + + // if secondary weapons change, reset the lock + if ( hud_lock_secondary_weapon_changed(swp) ) { + hud_lock_reset(); + } + + Player_ai->last_secondary_index = swp->current_secondary_bank; + + if ( !(wip->wi_flags & WIF_HOMING_ASPECT) ) { + hud_lock_reset(); + return; + } + + // Allow locking on ships and bombs (only targeted weapon allowed is a bomb, so don't bother checking flags) + if ( (Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (Objects[Player_ai->target_objnum].type != OBJ_WEAPON) ) { + hud_lock_reset(); + return; + } + + hud_lock_determine_lock_point(&lock_world_pos); + + if ( !hud_lock_has_homing_point() ) { + Player->target_in_lock_cone=0; + } + + hud_lock_check_if_target_in_lock_cone(&lock_world_pos); + + // check if the target is within range of the current secondary weapon. If it is not, + // a lock will not be detected + if ( !hud_lock_target_in_range() ) { + Player->target_in_lock_cone = 0; + } + + // If locking on a subsystem, and not in sight... can't lock + // Changed by MK on 4/3/98. It was confusing me that my hornets would not lock on my target. + // It will now be confusing that they lock, but don't home on your subsystem, but I think that's preferable. + // Often you really care about destroying the target, not just the subsystem. + /*if ( Player_ai->targeted_subsys ) { + if ( !hud_lock_on_subsys_ok() ) { + Player->target_in_lock_cone=0; + } + }*/ + + if ( !Player->target_in_lock_cone ) { + Player->locking_on_center=0; + Player->locking_subsys_parent=-1; + Player->locking_subsys=NULL; + } + + hud_calculate_lock_position(frametime); + + if (!Players[Player_num].lock_indicator_visible) + return; + + if (Player_ai->current_target_is_locked) { + if ( Missile_track_loop > -1 ) { + snd_chg_loop_status(Missile_track_loop, 0); + Missile_track_loop = -1; + Missile_lock_loop = snd_play(&Snds[SND_MISSILE_LOCK]); + } + } + else { + Player_ai->ai_flags |= AIF_SEEK_LOCK; // set this flag so multiplayer's properly track lock on other ships + if ( Missile_lock_loop != -1 && snd_is_playing(Missile_lock_loop) ) { + snd_stop(Missile_lock_loop); + Missile_lock_loop = -1; + } + } +} + +// hud_draw_lock_triangles() will draw the 4 rotating triangles around a lock indicator +// (This is done when a lock has been acquired) +#define ROTATE_DELAY 40 +void hud_draw_lock_triangles_old(int center_x, int center_y, int radius) +{ + static float ang = 0.0f; + + float end_ang = ang + 2*PI; + float x3,y3,x4,y4,xpos,ypos; + + if ( timestamp_elapsed(Rotate_time_id) ) { + Rotate_time_id = timestamp(ROTATE_DELAY); + ang += PI/12; + } + + for (ang; ang <= end_ang; ang += PI/2.0f) { + + // draw the orbiting triangles + + //ang = atan2(target_point.y,target_point.x); + xpos = center_x + (float)cos(ang)*(radius + Lock_triangle_height[gr_screen.res] + 2); + ypos = center_y - (float)sin(ang)*(radius + Lock_triangle_height[gr_screen.res] + 2); + + x3 = xpos - Lock_triangle_base[gr_screen.res] * (float)sin(-ang); + y3 = ypos + Lock_triangle_base[gr_screen.res] * (float)cos(-ang); + x4 = xpos + Lock_triangle_base[gr_screen.res] * (float)sin(-ang); + y4 = ypos - Lock_triangle_base[gr_screen.res] * (float)cos(-ang); + + xpos = xpos - Lock_triangle_base[gr_screen.res] * (float)cos(ang); + ypos = ypos + Lock_triangle_base[gr_screen.res] * (float)sin(ang); + + hud_tri(x3, y3, xpos, ypos, x4, y4); + } // end for +} + +// draw a frame of the rotating lock triangles animation +void hud_draw_lock_triangles(int center_x, int center_y, float frametime) +{ + if ( Lock_anim.first_frame == -1 ) { + hud_draw_lock_triangles_old(center_x, center_y, Lock_target_box_width[gr_screen.res]/2); + } else { + // render the anim + Lock_anim.sx = center_x - Lockspin_half_w[gr_screen.res]; + Lock_anim.sy = center_y - Lockspin_half_h[gr_screen.res]; + + // if its still animating + if(Lock_anim.time_elapsed < Lock_anim.total_time){ + hud_anim_render(&Lock_anim, frametime, 1, 0, 1); + } else { + // if the timestamp is unset or expired + if((Lock_gauge_draw_stamp < 0) || timestamp_elapsed(Lock_gauge_draw_stamp)){ + // reset timestamp + Lock_gauge_draw_stamp = timestamp(1000 / (2 * LOCK_GAUGE_BLINK_RATE)); + + // switch between draw and dont-draw + Lock_gauge_draw = !Lock_gauge_draw; + } + + // maybe draw the anim + Lock_gauge.time_elapsed = 0.0f; + if(Lock_gauge_draw){ + hud_anim_render(&Lock_anim, frametime, 1, 0, 1); + } + } + } +} + +// hud_calculate_lock_position() will determine where on the screen to draw the lock +// indicator, and will determine when a lock has occurred. If the lock indicator is not +// on the screen yet, hud_calculate_lock_start_pos() is called to pick a starting location +void hud_calculate_lock_position(float frametime) +{ + ship_weapon *swp; + weapon_info *wip; + + static float pixels_moved_while_locking; + static float pixels_moved_while_degrading; + static int Need_new_start_pos = 0; + + static double accumulated_x_pixels, accumulated_y_pixels; + double int_portion; + + static float last_dist_to_target; + + static int catching_up; + + static int maintain_lock_count = 0; + + static float catch_up_distance = 0.0f; + + double hypotenuse, delta_x, delta_y; + + swp = &Player_ship->weapons; + wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]]; + + if (Player->target_in_lock_cone) { + if (!Players[Player_num].lock_indicator_visible) { + hud_calculate_lock_start_pos(); + last_dist_to_target = 0.0f; + + Players[Player_num].lock_indicator_x = Players[Player_num].lock_indicator_start_x; + Players[Player_num].lock_indicator_y = Players[Player_num].lock_indicator_start_y; + Players[Player_num].lock_indicator_visible = 1; + + Players[Player_num].lock_time_to_target = i2fl(wip->min_lock_time); + catching_up = 0; + } + + Need_new_start_pos = 1; + + if (Player_ai->current_target_is_locked) { + Players[Player_num].lock_indicator_x = Player->current_target_sx; + Players[Player_num].lock_indicator_y = Player->current_target_sy; + return; + } + + delta_x = Players[Player_num].lock_indicator_x - Player->current_target_sx; + delta_y = Players[Player_num].lock_indicator_y - Player->current_target_sy; + + if (!delta_y && !delta_x) { + hypotenuse = 0.0f; + } + else { + hypotenuse = _hypot(delta_y, delta_x); + } + + Players[Player_num].lock_dist_to_target = (float)hypotenuse; + + if (last_dist_to_target == 0) { + last_dist_to_target = Players[Player_num].lock_dist_to_target; + } + + //nprintf(("Alan","dist to target: %.2f\n",Players[Player_num].lock_dist_to_target)); + //nprintf(("Alan","last to target: %.2f\n\n",last_dist_to_target)); + + if (catching_up) { + //nprintf(("Alan","IN CATCH UP MODE catch_up_dist is %.2f\n",catch_up_distance)); + if ( Players[Player_num].lock_dist_to_target < catch_up_distance ) + catching_up = 0; + } + else { + //nprintf(("Alan","IN NORMAL MODE\n")); + if ( (Players[Player_num].lock_dist_to_target - last_dist_to_target) > 2.0f ) { + catching_up = 1; + catch_up_distance = last_dist_to_target + wip->catchup_pixel_penalty; + } + } + + last_dist_to_target = Players[Player_num].lock_dist_to_target; + + if (!catching_up) { + Players[Player_num].lock_time_to_target -= frametime; + if (Players[Player_num].lock_time_to_target < 0.0f) + Players[Player_num].lock_time_to_target = 0.0f; + } + + float lock_pixels_per_sec; + if (Players[Player_num].lock_time_to_target > 0) { + lock_pixels_per_sec = Players[Player_num].lock_dist_to_target / Players[Player_num].lock_time_to_target; + } else { + lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec); + } + + if (lock_pixels_per_sec > wip->lock_pixels_per_sec) { + lock_pixels_per_sec = i2fl(wip->lock_pixels_per_sec); + } + + if (catching_up) { + pixels_moved_while_locking = wip->catchup_pixels_per_sec * frametime; + } else { + pixels_moved_while_locking = lock_pixels_per_sec * frametime; + } + + if (delta_x != 0) { + accumulated_x_pixels += pixels_moved_while_locking * delta_x/hypotenuse; + } + + if (delta_y != 0) { + accumulated_y_pixels += pixels_moved_while_locking * delta_y/hypotenuse; + } + + if (fl_abs(accumulated_x_pixels) > 1.0f) { + modf(accumulated_x_pixels, &int_portion); + + Players[Player_num].lock_indicator_x -= (int)int_portion; + + if ( fl_abs(Players[Player_num].lock_indicator_x - Player->current_target_sx) < fl_abs(int_portion) ) + Players[Player_num].lock_indicator_x = Player->current_target_sx; + + accumulated_x_pixels -= int_portion; + } + + if (fl_abs(accumulated_y_pixels) > 1.0f) { + modf(accumulated_y_pixels, &int_portion); + + Players[Player_num].lock_indicator_y -= (int)int_portion; + + if ( fl_abs(Players[Player_num].lock_indicator_y - Player->current_target_sy) < fl_abs(int_portion) ) + Players[Player_num].lock_indicator_y = Player->current_target_sy; + + accumulated_y_pixels -= int_portion; + } + + if ( Missile_track_loop == -1 ) { + Missile_track_loop = snd_play_looping( &Snds[SND_MISSILE_TRACKING], 0.0f , -1, -1); + } + + if (!Players[Player_num].lock_time_to_target) { + if ( (Players[Player_num].lock_indicator_x == Player->current_target_sx) && (Players[Player_num].lock_indicator_y == Player->current_target_sy) ) { + if (maintain_lock_count++ > 1) { + Player_ai->current_target_is_locked = 1; + } + } else { + maintain_lock_count = 0; + } + } + + } else { + + if ( Missile_track_loop > -1 ) { + snd_chg_loop_status(Missile_track_loop, 0); + Missile_track_loop = -1; + } + + Player_ai->current_target_is_locked = 0; + + if (!Players[Player_num].lock_indicator_visible) { + return; + } + + catching_up = 0; + last_dist_to_target = 0.0f; + + if (Need_new_start_pos) { + hud_calculate_lock_start_pos(); + Need_new_start_pos = 0; + accumulated_x_pixels = 0.0f; + accumulated_y_pixels = 0.0f; + } + + delta_x = Players[Player_num].lock_indicator_x - Players[Player_num].lock_indicator_start_x; + delta_y = Players[Player_num].lock_indicator_y - Players[Player_num].lock_indicator_start_y; + + if (!delta_y && !delta_x) { + hypotenuse = 0.0f; + } + else { + hypotenuse = _hypot(delta_y, delta_x); + } + + Players[Player_num].lock_time_to_target += frametime; + + if (Players[Player_num].lock_time_to_target > wip->min_lock_time) + Players[Player_num].lock_time_to_target = i2fl(wip->min_lock_time); + + pixels_moved_while_degrading = 2.0f * wip->lock_pixels_per_sec * frametime; + + if (delta_x != 0) + accumulated_x_pixels += pixels_moved_while_degrading * delta_x/hypotenuse; + + if (delta_y != 0) + accumulated_y_pixels += pixels_moved_while_degrading * delta_y/hypotenuse; + + if (fl_abs(accumulated_x_pixels) > 1.0f) { + modf(accumulated_x_pixels, &int_portion); + + Players[Player_num].lock_indicator_x -= (int)int_portion; + + if ( fl_abs(Players[Player_num].lock_indicator_x - Players[Player_num].lock_indicator_start_x) < fl_abs(int_portion) ) + Players[Player_num].lock_indicator_x = Players[Player_num].lock_indicator_start_x; + + accumulated_x_pixels -= int_portion; + } + + if (fl_abs(accumulated_y_pixels) > 1.0f) { + modf(accumulated_y_pixels, &int_portion); + + Players[Player_num].lock_indicator_y -= (int)int_portion; + + if ( fl_abs(Players[Player_num].lock_indicator_y - Players[Player_num].lock_indicator_start_y) < fl_abs(int_portion) ) + Players[Player_num].lock_indicator_y = Players[Player_num].lock_indicator_start_y; + + accumulated_y_pixels -= int_portion; + } + + if ( (Players[Player_num].lock_indicator_x == Players[Player_num].lock_indicator_start_x) && (Players[Player_num].lock_indicator_y == Players[Player_num].lock_indicator_start_y) ) { + Players[Player_num].lock_indicator_visible = 0; + } + } +} + +// hud_calculate_lock_start_pos() will determine where to draw the starting location of the lock +// indicator. It does this by picking a location that is Lock_start_dist pixels away from the current +// target (in 2D). This is accomplished by finding the endpoint of a line that passes through the +// origin, and connects the target and lock indicator postion (and has a magnitude of Lock_start_dist) +void hud_calculate_lock_start_pos() +{ + double hypotenuse; + double delta_y; + double delta_x; + double target_mag, target_x, target_y; + + delta_x = Player->current_target_sx - SCREEN_CENTER_X; + delta_y = Player->current_target_sy - SCREEN_CENTER_Y; + + if (!delta_x && !delta_y) { + Players[Player_num].lock_indicator_start_x = fl2i(SCREEN_CENTER_X + Lock_start_dist); + Players[Player_num].lock_indicator_start_y = fl2i(SCREEN_CENTER_Y); + return; + } + + hypotenuse = _hypot(delta_y, delta_x); + + if (hypotenuse >= Lock_start_dist) { + Players[Player_num].lock_indicator_start_x = fl2i(SCREEN_CENTER_X); + Players[Player_num].lock_indicator_start_y = fl2i(SCREEN_CENTER_Y); + return; + } + + target_mag = Lock_start_dist - hypotenuse; + target_x = target_mag * (delta_x / hypotenuse); + target_y = target_mag * (delta_y / hypotenuse); + + Players[Player_num].lock_indicator_start_x = fl2i(SCREEN_CENTER_X - target_x); + Players[Player_num].lock_indicator_start_y = fl2i(SCREEN_CENTER_Y - target_y); + + if (Players[Player_num].lock_indicator_start_x > gr_screen.clip_right) + Players[Player_num].lock_indicator_start_x = gr_screen.clip_right; + + if (Players[Player_num].lock_indicator_start_y > gr_screen.clip_bottom) + Players[Player_num].lock_indicator_start_y = gr_screen.clip_bottom; + + if (Players[Player_num].lock_indicator_start_x < gr_screen.clip_left) + Players[Player_num].lock_indicator_start_x = gr_screen.clip_left; + + if (Players[Player_num].lock_indicator_start_y < gr_screen.clip_top) + Players[Player_num].lock_indicator_start_y = gr_screen.clip_top; +} + +// hud_stop_looped_locking_sounds() will terminate any hud related looping sounds that are playing +void hud_stop_looped_locking_sounds() +{ + if ( Missile_track_loop > -1 ) { + snd_stop(Missile_track_loop); + Missile_track_loop = -1; + } +} + +// Get a new world pos for the locking point +void hud_lock_update_lock_pos(object *target_objp, vector *lock_world_pos) +{ + if ( Player_ai->targeted_subsys ) { + get_subsystem_world_pos(target_objp, Player_ai->targeted_subsys, lock_world_pos); + return; + } + + if ( Player->locking_on_center ) { + *lock_world_pos = target_objp->pos; + } else { + Assert(Player->locking_subsys); + get_subsystem_world_pos(target_objp, Player->locking_subsys, lock_world_pos); + } +} + +// Try and find a new locking point +void hud_lock_get_new_lock_pos(object *target_objp, vector *lock_world_pos) +{ + ship *target_shipp=NULL; + int lock_in_range=0; + float best_lock_dot=-1.0f, lock_dot=-1.0f; + ship_subsys *ss; + vector subsys_world_pos, vec_to_lock; + + if ( target_objp->type == OBJ_SHIP ) { + target_shipp = &Ships[target_objp->instance]; + } + + // if a large ship, lock to pos closest to center and within range + if ( (target_shipp) && (Ship_info[target_shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) { + // check all the subsystems and the center of the ship + + // assume best lock pos is the center of the ship + *lock_world_pos=target_objp->pos; + Player->locking_on_center=1; + Player->locking_subsys=NULL; + Player->locking_subsys_parent=-1; + lock_in_range = hud_lock_world_pos_in_range(lock_world_pos, &vec_to_lock); + vm_vec_normalize(&vec_to_lock); + if ( lock_in_range ) { + best_lock_dot=vm_vec_dot(&Player_obj->orient.fvec, &vec_to_lock); + } + // take center if reasonable dot + if ( best_lock_dot > 0.95 ) { + return; + } + + // iterate through subsystems to see if we can get a better choice + ss = GET_FIRST(&target_shipp->subsys_list); + while ( ss != END_OF_LIST( &target_shipp->subsys_list ) ) { + + // get world pos of subsystem + get_subsystem_world_pos(target_objp, ss, &subsys_world_pos); + + if ( hud_lock_world_pos_in_range(&subsys_world_pos, &vec_to_lock) ) { + vm_vec_normalize(&vec_to_lock); + lock_dot=vm_vec_dot(&Player_obj->orient.fvec, &vec_to_lock); + if ( lock_dot > best_lock_dot ) { + best_lock_dot=lock_dot; + Player->locking_on_center=0; + Player->locking_subsys=ss; + Player->locking_subsys_parent=Player_ai->target_objnum; + *lock_world_pos=subsys_world_pos; + } + } + ss = GET_NEXT( ss ); + } + } else { + // if small ship (or weapon), just go for the center + *lock_world_pos = target_objp->pos; + Player->locking_on_center=1; + Player->locking_subsys=NULL; + Player->locking_subsys_parent=-1; + } +} + +// Decide which point lock should be homing on +void hud_lock_determine_lock_point(vector *lock_world_pos_out) +{ + vector lock_world_pos; + vertex lock_point; + object *target_objp; + + Assert(Player_ai->target_objnum >= 0); + target_objp = &Objects[Player_ai->target_objnum]; + + Player->current_target_sx = -1; + Player->current_target_sx = -1; + + // If subsystem is targeted, we must try to lock on that + if ( Player_ai->targeted_subsys ) { + hud_lock_update_lock_pos(target_objp, &lock_world_pos); + Player->locking_on_center=0; + Player->locking_subsys=NULL; + Player->locking_subsys_parent=-1; + } else { + // See if we already have a successful locked point + if ( hud_lock_has_homing_point() ) { + hud_lock_update_lock_pos(target_objp, &lock_world_pos); + } else { + hud_lock_get_new_lock_pos(target_objp, &lock_world_pos); + } + } + + *lock_world_pos_out=lock_world_pos; + + g3_rotate_vertex(&lock_point,&lock_world_pos); + g3_project_vertex(&lock_point); + + if (!(lock_point.flags & PF_OVERFLOW)) { // make sure point projected + Player->current_target_sx = (int)lock_point.sx; + Player->current_target_sy = (int)lock_point.sy; + } +} + +void hudlock_page_in() +{ + bm_page_in_aabitmap( Lock_gauge.first_frame, Lock_gauge.num_frames ); +} \ No newline at end of file diff --git a/src/hud/hudmessage.cpp b/src/hud/hudmessage.cpp new file mode 100644 index 0000000..6a8a48d --- /dev/null +++ b/src/hud/hudmessage.cpp @@ -0,0 +1,1563 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDmessage.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module that controls and manages the message window on the HUD + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 23 9/08/99 5:38p Jefff + * + * 22 9/08/99 2:38p Jefff + * sound pausing going to menu from game + * + * 21 9/07/99 9:42p Jefff + * + * 20 9/02/99 11:47a Jefff + * yet another hud message length adjustment. yeesh. + * + * 19 8/25/99 10:08a Jefff + * fixed another messge cut-off bug + * + * 18 8/23/99 11:12a Jefff + * fixed 1024 message cut-off bug + * + * 17 8/20/99 2:26p Jefff + * hud message text wrapping problem in hires fixed + * + * 16 8/03/99 7:27p Jefff + * hud messages go completely across screnn in high res now + * + * 15 8/03/99 6:21p Jefff + * fixed stupid bug with objectives screen key + * + * 14 8/01/99 12:39p Dave + * Added HUD contrast control key (for nebula). + * + * 13 7/29/99 2:58p Jefff + * Ingame objective screen icon key now uses normal objective icons and + * text is drawn in code. + * + * 12 6/16/99 5:26p Dave + * Fixed some bitmap and coordinate problems on the mission scrollback + * screen. + * + * 11 6/10/99 3:43p Dave + * Do a better job of syncing text colors to HUD gauges. + * + * 10 2/02/99 10:13a Neilk + * fixed more coords + * + * 9 2/01/99 5:55p Dave + * Removed the idea of explicit bitmaps for buttons. Fixed text + * highlighting for disabled gadgets. + * + * 8 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 7 1/30/99 3:09p Neilk + * More mission log coord fixes + * + * 6 1/30/99 2:59p Neilk + * Fixed more mission log coords + * + * 5 1/29/99 7:57p Neilk + * Added support for multiresolutions + * + * 4 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 89 6/09/98 5:17p Lawrance + * French/German localization + * + * 88 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 87 5/19/98 8:36p Andsager + * Fix bug where last line of message log would not show if more than one + * screen of text. + * + * 86 4/27/98 8:49p Allender + * make terran command display in white (with correct code anyway). + * + * 85 4/25/98 11:49p Lawrance + * init pos to zero + * + * 84 4/25/98 9:10p Hoffoss + * Fixed bug with scrollback origin. + * + * 83 4/25/98 5:22p Hoffoss + * Fixed some problems with scrolling and positioning, and added code to + * support pageup/pagedown. + * + * 82 4/14/98 5:06p Dave + * Don't load or send invalid pilot pics. Fixed chatbox graphic errors. + * Made chatbox display team icons in a team vs. team game. Fixed up pause + * and endgame sequencing issues. + * + * 81 4/14/98 2:44p Hoffoss + * Made arrow keys do what tab does. + * + * 80 4/08/98 4:10p John + * Removed all remaining traces of the evil gr_init_font_ex. + * + * 79 4/07/98 11:33a Hoffoss + * Fixed bug where scroll offset was wrong when first entering the F4 + * screen. + * + * 78 4/05/98 3:30p Dave + * Print netplayer messages in brighter green on the hud, with + * accompanying sound. Echo netplayer messages on sending machine. Fixed + * standalone sequencing bug where host never get the "enter mission" + * button. + * + * 77 3/27/98 11:57a Dave + * Put in expression checking for text messages. + * + * 76 3/17/98 4:01p Hoffoss + * Added HUD_SOURCE_TERRAN_CMD and changed code to utilize it when a + * message is being sent from Terran Command. + * + * 75 3/16/98 5:55p Lawrance + * Increase width of HUD message line, don't draw lines while comm menu is + * up + * + * 74 3/12/98 4:03p Hoffoss + * Changed formatting used in hug scrollbacl log. + * + * 73 3/09/98 4:47p Hoffoss + * Changed F4 screen to start in objectives mode rather than HUD messages + * mode. + * + * 72 3/09/98 2:50p Hoffoss + * Changed to use different palette file, and fixed bug with text + * overrunning the right edge of screen. + * + * 71 3/02/98 5:42p John + * Removed WinAVI stuff from Freespace. Made all HUD gauges wriggle from + * afterburner. Made gr_set_clip work good with negative x &y. Made + * model_caching be on by default. Made each cached model have it's own + * bitmap id. Made asteroids not rotate when model_caching is on. + * + * 70 2/27/98 4:55p Hoffoss + * Fixed some alignment problems. + * + * 69 2/27/98 4:37p Hoffoss + * Combined Objectives screen into Mission Log screen. + * + * 68 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 67 2/22/98 12:19p John + * Externalized some strings + * + * 66 2/06/98 2:58p Hoffoss + * Fixed bug where mission log scrolls twice when using the arrow keys. + * + * 65 1/29/98 10:26a Hoffoss + * Made changes so arrow buttons repeat scrolling when held down. + * + * 64 1/20/98 4:39p Hoffoss + * Added mission time display to scrollback log screen. + * + * 63 1/18/98 5:09p Lawrance + * Added support for TEAM_TRAITOR + * + * 62 1/12/98 11:16p Lawrance + * Wonderful HUD config. + * + * 61 1/08/98 1:33p Hoffoss + * Made scroll offset reset to bottom of list instead of top. + * + * 60 1/05/98 2:59p Hoffoss + * Fixed bug with messages drawing outside of limits. + * + * 59 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 58 1/02/98 10:23a Hoffoss + * Fixed incorrect scrolling directions in message scrollback log. + * + * 57 12/11/97 10:17p Dave + * Put in some checks to make sure HUD_printfs aren't + * done in certain multiplayer situations. + * + * 56 12/05/97 2:16p Hoffoss + * Made hidden hud messages actually not show up in scrollback. + * + * 55 12/03/97 6:07p Dave + * Added assert that hud scrollback initialized before adding messages to + * it. + * + * 54 12/03/97 4:16p Hoffoss + * Changed sound stuff used in interface screens for interface purposes. + * + * 53 12/03/97 11:35a Hoffoss + * Made changes to HUD messages send throughout the game. + * + * 52 12/02/97 5:57p Hoffoss + * Changed Hud messaging code to align text to right after sending ship's + * name. + * + * 51 12/01/97 4:30p Hoffoss + * Changed code to list hud messages in scrollback from top down. + * + * 50 11/25/97 10:01a Jasen + * Remoced excess buttons from MessageLog screen. + * + * 49 11/24/97 10:03p Jasen + * Dang... had to make an entirely new revision of the last button. :) + * + * 48 11/24/97 9:44p Jasen + * Changed button name and coords to new exit button. + * + * 47 11/20/97 12:02p Lawrance + * change Error to nprintf at warning level + * + * 46 11/17/97 6:37p Lawrance + * new gauges: extended target view, new lock triangles, support ship view + * + * 45 11/14/97 2:46p Lawrance + * decrease width of HUD message line to 435, so it doesn overlap with + * message menu + * + * 44 11/13/97 10:16p Hoffoss + * Added icons to mission log scrollback. + * + * 43 11/13/97 4:05p Hoffoss + * Added hiding code for mission log entries. + * + * 42 11/12/97 6:00p Hoffoss + * Added training messages to hud scrollback log. + * + * 41 11/11/97 11:16a Hoffoss + * Changed hud scrollback to color entire lines. + * + * 40 11/06/97 5:42p Hoffoss + * Added support for fixed size timstamp rendering. + * + * 39 11/05/97 7:11p Hoffoss + * Made changed to the hud message system. Hud messages can now have + * sources so they can be color coded. + * + * 38 11/04/97 4:56p Jasen + * Updated coordinates for buttons + * + * 37 11/03/97 10:12p Hoffoss + * Finished up work on the hud message/mission log scrollback screen. + * + * 36 11/03/97 5:38p Dave + * Cleaned up more multiplayer sequencing. Added OBJ_OBSERVER module/type. + * Restructured HUD_config structs/flags. + * + * 35 10/25/97 4:02p Lawrance + * took out unused hud_message struct members + * + * 34 10/02/97 9:53p Hoffoss + * Added event evaluation analysis debug screen so we can determine the + * state of events and their sexp trees to track down logic problems and + * such. + * + * 33 9/17/97 5:12p John + * Restructured collision routines. Probably broke a lot of stuff. + * + * 32 9/08/97 12:01p Lawrance + * when re-using HUD scrollback entries, ensure memory gets free'ed + * properly + * + * 31 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 30 8/22/97 10:03a Lawrance + * fix exception that occurred when hud scrollback was selected from the + * main menu + * + * 29 6/23/97 12:03p Lawrance + * move split_str() to Parselo + * + * 28 6/17/97 12:25p Lawrance + * HUD message lines are split into multiple lines when they exceed + * display width + * + * 27 6/12/97 10:23a John + * added new colors to freespace. made most menus display the background + * bitmap rather than dull black screen. + * + * 26 6/11/97 1:12p John + * Started fixing all the text colors in the game. + * + * 25 4/22/97 3:14p Lawrance + * only free HUD scrollback messages if they exist + * + * 24 4/15/97 1:26p Lawrance + * using a static array of nodes to store hud scrollback messages, storage + * for text is dynamic + * + * 23 4/14/97 9:55a Mike + * Fixed HUD message system. + * Better game sequencing. + * + * 22 1/28/97 5:33p Lawrance + * saving number of msg window lines in save game and player file + * + * 21 1/28/97 4:59p Lawrance + * allowing number of lines on hud message bar to be configured + * + * 20 1/24/97 9:47a Lawrance + * made number of message lines in HUD message area confiurable + * + * 19 1/22/97 10:56a Lawrance + * added check for NULL after malloc() + * + * 18 1/07/97 5:36p Lawrance + * Enabled save/restore for old/present/pending hud messages + * + * 17 12/10/96 12:28p Lawrance + * adding new offscreen target indicator + * + * 16 12/08/96 1:54a Lawrance + * integrating hud configuration + * + * 15 11/29/96 6:12p Lawrance + * took out duplicate include of timer.h + * + * 14 11/29/96 11:17a Lawrance + * added comments, put check in for zero-length HUD messages in the + * scrollback + * + * 13 11/28/96 6:27p Lawrance + * added some additional comments + * + * 12 11/27/96 3:20p Lawrance + * added scroll-back message code + * + * 11 11/22/96 1:00p Lawrance + * fixed bug when a key held down and created tons of messages + * + * 10 11/22/96 12:35p Lawrance + * + * 9 11/20/96 11:51a Lawrance + * trying out losing the HUD message shaded area + * + * 8 11/19/96 4:46p Lawrance + * fixed problem when too many messages were to be displayed at once + * + * 7 11/19/96 3:55p Lawrance + * modifed HUD message details (scroll speed, fade time etc) + * + * 6 11/19/96 10:16a Lawrance + * changing to new use of color scheme + * + * 5 11/17/96 5:28p Lawrance + * using HUD color globals instead of hard-coded numbers + * + * 4 11/15/96 11:46a Lawrance + * tweaked size of HUD message bar to prevent clipping of g's etc on the + * bottom line + * + * 3 11/15/96 11:39a Lawrance + * got HUD messages scrolling + * + * 2 11/15/96 12:11a Lawrance + * HUD message bar working + * + * $NoKeywords: $ + * +*/ + +#include +#include + +#include "hud.h" +#include "hudmessage.h" +#include "hudtarget.h" +#include "freespace.h" +#include "gamesequence.h" +#include "2d.h" +#include "key.h" +#include "timer.h" +#include "math.h" +#include "mouse.h" +#include "winmidi.h" +#include "player.h" +#include "linklist.h" +#include "missionlog.h" +#include "ui.h" +#include "missionscreencommon.h" +#include "bmpman.h" +#include "font.h" +#include "gamesnd.h" +#include "multi.h" +#include "missiongoals.h" +#include "alphacolors.h" +#include "beam.h" +#include "audiostr.h" + +/* replaced with those static ints that follow +#define LIST_X 46 +#define LIST_X2 108 // second column x start position +#define LIST_Y 60 +#define LIST_W 558 // total width including both columns +#define LIST_W2 (LIST_W + LIST_X - LIST_X2) // width of second column +#define LIST_H 297 +#define LIST_H_O 275 // applies only to objectives mode +*/ + +// 1st column, width includes both columns +static int Hud_mission_log_list_coords[GR_NUM_RESOLUTIONS][4] = { + { + 46,60,558,269 // GR_640 + }, + { + 74,96,558,297 // GR_1024 + } +}; + +// 2nd column, width is just of second column +static int Hud_mission_log_list2_coords[GR_NUM_RESOLUTIONS][4] = { + { + 108, 60, 496, 297 // GR_640 + }, + { + 136, 96, 496, 436 // GR_1024 + } +}; + +static int Hud_mission_log_list_objective_x_coord[GR_NUM_RESOLUTIONS] = { + 275, // GR_640 + 440 // GR_1024 +}; + +static int Hud_mission_log_time_coords[GR_NUM_RESOLUTIONS][2] = { + { + 41, 372 // GR_640 + }, + { + 66, 595 // GR_1024 + } +}; + +static int Hud_mission_log_time2_coords[GR_NUM_RESOLUTIONS][2] = { + { + 103, 372 // GR_640 + }, + { + 128, 595 // GR_1024 + } +}; + + +#define SCROLLBACK_MODE_MSGS_LOG 0 +#define SCROLLBACK_MODE_EVENT_LOG 1 +#define SCROLLBACK_MODE_OBJECTIVES 2 + +#define NUM_BUTTONS 6 + +#define SCROLL_UP_BUTTON 0 +#define SCROLL_DOWN_BUTTON 1 +#define SHOW_MSGS_BUTTON 2 +#define SHOW_EVENTS_BUTTON 3 +#define SHOW_OBJS_BUTTON 4 +#define ACCEPT_BUTTON 5 + +#define HUD_MESSAGE_TOTAL_LIFE 14000 // total time a HUD message is alive (in milliseconds) + +#define HUD_MSG_LENGTH_MAX 2048 +//#define HUD_MSG_MAX_PIXEL_W 439 // maximum number of pixels wide message display area is +//#define HUD_MSG_MAX_PIXEL_W 619 // maximum number of pixels wide message display area is + +static int Hud_mission_log_status_coords[GR_NUM_RESOLUTIONS][2] = { + { + 170, 339 // GR_640 + }, + { + 361, 542 // GR_1024 + } +}; + +struct scrollback_buttons { + char *filename; + int x, y; + int xt, yt; + int hotspot; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + scrollback_buttons(char *name, int x1, int y1, int x2, int y2, int h) : filename(name), x(x1), y(y1), xt(x2), yt(y2), hotspot(h) {} +}; + +int Scroll_time_id; + +int MSG_WINDOW_X_START = 5; +int MSG_WINDOW_Y_START = 5; +int MSG_WINDOW_WIDTH; // initialed in hud_init_msg_window() +int MSG_WINDOW_HEIGHT; +int MSG_WINDOW_FONT_HEIGHT; +int ACTIVE_BUFFER_LINES = 4; // number of HUD messages that can be shown at one time + 1 +int OLD_ACTIVE_BUFFER_LINES; + +int Hud_list_start; // points to the next msg to be printed in the queue +int Hud_list_end; // points to the last msg in the queue + +int Active_index=0; +int Scroll_needed=0; +int Scroll_in_progress=0; + +HUD_message_data HUD_pending[SCROLL_BUFFER_LINES]; +Hud_display_info HUD_active_msgs_list[MAX_ACTIVE_BUFFER_LINES]; + +int HUD_msg_inited = FALSE; + +// There is a maximum number of lines that will be stored in the message scrollback. Oldest +// messages are deleted to make way for newest messages. +#define MAX_MSG_SCROLLBACK_LINES 100 +line_node Msg_scrollback_lines[MAX_MSG_SCROLLBACK_LINES]; + +line_node Msg_scrollback_free_list; +line_node Msg_scrollback_used_list; + +#define MAX_HUD_FT 1 + +typedef struct HUD_ft { + int end_time; // Timestamp at which this message will go away. + char text[MAX_HUD_LINE_LEN]; // Text to display. + int color; // 0rgb color, 8 bit fields. +} HUD_ft; + +HUD_ft HUD_fixed_text[MAX_HUD_FT]; + +static int Num_obj_lines; +static int Scroll_offset; +static int Scroll_max; +static int Scrollback_mode = SCROLLBACK_MODE_OBJECTIVES; +static int Selected_line; +// static int Status_bitmap; +static int Background_bitmap; +static UI_WINDOW Ui_window; + +static char* Hud_mission_log_fname[GR_NUM_RESOLUTIONS] = { + "MissionLog", // GR_640 + "2_MissionLog" // GR_1024 +}; + +static char* Hud_mission_log_status_fname[GR_NUM_RESOLUTIONS] = { + "MLStatus", // GR_640 + "MLStatus" // GR_1024 +}; + +static char* Hud_mission_log_mask_fname[GR_NUM_RESOLUTIONS] = { + "MissionLog-m", // GR_640 + "2_MissionLog-m" // GR_1024 +}; + +static scrollback_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { + { // GR_640 + //XSTR:OFF + scrollback_buttons("LB_00", 1, 67, -1, -1, 0), + scrollback_buttons("LB_01", 1, 307, -1, -1, 1), + scrollback_buttons("LB_02", 111, 376, 108, 413, 2), + scrollback_buttons("LB_03", 209, 376, 205, 413, 3), + scrollback_buttons("LB_04", 12, 376, 7, 413, 4), + scrollback_buttons("CB_05a", 571, 425, 564, 413, 5) + //XSTR:ON + }, + { // GR_1024 + //XSTR:OFF + scrollback_buttons("2_LB_00", 1, 108, -1, -1, 0), + scrollback_buttons("2_LB_01", 1, 492, -1, -1, 1), + scrollback_buttons("2_LB_02", 177, 602, 173, 661, 2), + scrollback_buttons("2_LB_03", 335, 602, 335, 661, 3), + scrollback_buttons("2_LB_04", 20, 602, 11, 661, 4), + scrollback_buttons("2_CB_05a",914, 681, 946, 661, 5) + //XSTR:ON + } +} +; + +// ---------------------------------------------------------------------- +// HUD_init_fixed_text() +// +void HUD_init_fixed_text() +{ + int i; + + for (i=0; i= SCROLL_BUFFER_LINES) + Hud_list_start = 1; + + HUD_active_msgs_list[Active_index].msg = HUD_pending[Hud_list_start]; + HUD_active_msgs_list[Active_index].total_life = timestamp(HUD_MESSAGE_TOTAL_LIFE); + + for (i=Active_index+1; i < Active_index+ACTIVE_BUFFER_LINES; i++) { + index = i % ACTIVE_BUFFER_LINES; + + // determine if there are any existing messages, if so need to scroll them up + + if ( !timestamp_elapsed(HUD_active_msgs_list[index].total_life) ) { + HUD_active_msgs_list[index].target_y -= MSG_WINDOW_FONT_HEIGHT; + Scroll_needed=1; + } + + } + + if (Scroll_needed) { + HUD_active_msgs_list[Active_index].y = (ACTIVE_BUFFER_LINES-1)*MSG_WINDOW_FONT_HEIGHT; + HUD_active_msgs_list[Active_index].target_y = HUD_active_msgs_list[Active_index].y - MSG_WINDOW_FONT_HEIGHT; + } + else { + HUD_active_msgs_list[Active_index].y = (ACTIVE_BUFFER_LINES-2)*MSG_WINDOW_FONT_HEIGHT; + HUD_active_msgs_list[Active_index].target_y = HUD_active_msgs_list[Active_index].y; + } + + Active_index++; + if (Active_index >= ACTIVE_BUFFER_LINES) Active_index = 0; + + if (Hud_list_end == Hud_list_start) { // just printed the last msg + Hud_list_start = Hud_list_end = 0; + } + } + + Scroll_in_progress=0; + Scroll_needed = 0; + + for ( i=0; i < ACTIVE_BUFFER_LINES; i++ ) { + + if ( !timestamp_elapsed(HUD_active_msgs_list[i].total_life) ) { + + if (HUD_active_msgs_list[i].y > HUD_active_msgs_list[i].target_y) { + Scroll_needed=1; + if (timestamp_elapsed(Scroll_time_id) ){ + HUD_active_msgs_list[i].y -= SCROLL_STEP_SIZE; + if (HUD_active_msgs_list[i].y < HUD_active_msgs_list[i].target_y) + HUD_active_msgs_list[i].y = HUD_active_msgs_list[i].target_y; + + Scroll_in_progress=1; + } + + } + + if ( hud_gauge_active(HUD_MESSAGE_LINES) ) { + if ( !(Player->flags & PLAYER_FLAGS_MSG_MODE) ) { + // set the appropriate color + if(HUD_active_msgs_list[i].msg.source){ + hud_set_gauge_color(HUD_MESSAGE_LINES, HUD_C_BRIGHT); + } else { + hud_set_gauge_color(HUD_MESSAGE_LINES); + } + + // print the message out + gr_printf(MSG_WINDOW_X_START + HUD_active_msgs_list[i].msg.x - 2, HUD_active_msgs_list[i].y, "%s", HUD_active_msgs_list[i].msg.text); + } + } + } + + } // end for + + if (Scroll_in_progress) + Scroll_time_id = timestamp(SCROLL_TIME); + + HUD_reset_clip(); +} + +void hud_show_fixed_text() +{ + HUD_ft *hp; + + hp = &HUD_fixed_text[0]; + + if (!timestamp_elapsed(hp->end_time)) { + //gr_set_color((hp->color >> 16) & 0xff, (hp->color >> 8) & 0xff, hp->color & 0xff); + gr_printf(0x8000, MSG_WINDOW_Y_START + MSG_WINDOW_HEIGHT + 8, hp->text); + } +} + +// Similar to HUD printf, but shows only one message at a time, at a fixed location. +void HUD_fixed_printf(float duration, char * format, ...) +{ + va_list args; + char tmp[HUD_MSG_LENGTH_MAX]; + int msg_length; + + // make sure we only print these messages if we're in the correct state + if((Game_mode & GM_MULTIPLAYER) && (Netgame.game_state != NETGAME_STATE_IN_MISSION)){ + nprintf(("Network","HUD_fixed_printf bailing because not in multiplayer game play state\n")); + return; + } + + va_start(args, format); + vsprintf(tmp, format, args); + va_end(args); + + msg_length = strlen(tmp); + Assert(msg_length < HUD_MSG_LENGTH_MAX); // If greater than this, probably crashed anyway. + + if ( !msg_length ) { + nprintf(("Warning", "HUD_fixed_printf ==> attempt to print a 0 length string in msg window\n")); + return; + + } else if (msg_length > MAX_HUD_LINE_LEN - 1){ + nprintf(("Warning", "HUD_fixed_printf ==> Following string truncated to %d chars: %s\n",MAX_HUD_LINE_LEN,tmp)); + } + + if (duration == 0.0f){ + HUD_fixed_text[0].end_time = timestamp(-1); + } else { + HUD_fixed_text[0].end_time = timestamp((int) (1000.0f * duration)); + } + + strncpy(HUD_fixed_text[0].text, tmp, MAX_HUD_LINE_LEN - 1); + HUD_fixed_text[0].color = 0xff0000; +} + +// Clear all pending text. +void HUD_fixed_printf_reset() +{ + HUD_init_fixed_text(); +} + + +// -------------------------------------------------------------------------------------- +// HUD_printf_line() +// +// Print a single line of text to the HUD. We know that the text will fit on the screen, +// since that was taken care of in HUD_printf(); +// +void HUD_printf_line(char *text, int source, int time = 0, int x = 0) +{ + Assert(text != NULL); + + // if the pointer exceeds the array size, wrap around to element 1. element 0 is not used. + Hud_list_end++; + if (Hud_list_end >= SCROLL_BUFFER_LINES) + Hud_list_end = 1; + + if (Hud_list_end == Hud_list_start) { + nprintf(("Warning", "HUD ==> Exceeded msg scroll buffer, discarding message %s\n", text)); + Hud_list_end--; + if (Hud_list_end == 0) + Hud_list_end = SCROLL_BUFFER_LINES - 1; + return; + } + + if ( strlen(text) > MAX_HUD_LINE_LEN - 1 ){ + nprintf(("Warning", "HUD_printf_line() ==> Following string truncated to %d chars: %s\n", MAX_HUD_LINE_LEN, text)); + } + + strncpy(HUD_pending[Hud_list_end].text, text, MAX_HUD_LINE_LEN - 1); + HUD_pending[Hud_list_end].text[MAX_HUD_LINE_LEN - 1] = 0; + HUD_pending[Hud_list_end].source = source; + HUD_pending[Hud_list_end].time = time; + HUD_pending[Hud_list_end].x = x; +} + +// converts a TEAM_* define to a HUD_SOURCE_* define +int HUD_get_team_source(int team) +{ + switch (team) { + case TEAM_FRIENDLY: + return HUD_SOURCE_FRIENDLY; + + case TEAM_HOSTILE: + return HUD_SOURCE_HOSTILE; + + case TEAM_NEUTRAL: + return HUD_SOURCE_NEUTRAL; + + case TEAM_UNKNOWN: + return HUD_SOURCE_UNKNOWN; + + case TEAM_TRAITOR: + return HUD_SOURCE_TRAITOR; + } + + nprintf(("warning", "Unknown TEAM_* define used! (%d)", team)); + return 0; +} + +void HUD_printf(char *format, ...) +{ + va_list args; + char tmp[HUD_MSG_LENGTH_MAX]; + int len; + + // make sure we only print these messages if we're in the correct state + if((Game_mode & GM_MULTIPLAYER) && (Net_player->state != NETPLAYER_STATE_IN_MISSION)){ + nprintf(("Network","HUD_printf bailing because not in multiplayer game play state\n")); + return; + } + + va_start(args, format); + vsprintf(tmp, format, args); + va_end(args); + + len = strlen(tmp); + Assert(len < HUD_MSG_LENGTH_MAX); // If greater than this, probably crashed anyway. + hud_sourced_print(HUD_SOURCE_COMPUTER, tmp); +} + +void HUD_ship_sent_printf(int sh, char *format, ...) +{ + va_list args; + char tmp[HUD_MSG_LENGTH_MAX]; + int len; + + sprintf(tmp, NOX("%s: "), Ships[sh].ship_name); + len = strlen(tmp); + Assert(len < HUD_MSG_LENGTH_MAX); + + va_start(args, format); + vsprintf(tmp + len, format, args); + va_end(args); + + len = strlen(tmp); + Assert(len < HUD_MSG_LENGTH_MAX); // If greater than this, probably crashed anyway. + hud_sourced_print(HUD_get_team_source(Ships[sh].team), tmp); +} + +// -------------------------------------------------------------------------------------- +// HUD_sourced_printf() +// +// HUD_sourced_printf() has the same parameters as printf(), but displays the text as a scrolling +// message on the HUD. Text is split into multiple lines if width exceeds msg display area +// width. 'source' is used to indicate who send the message, and is used to color code text. +// +void HUD_sourced_printf(int source, char *format, ...) +{ + va_list args; + char tmp[HUD_MSG_LENGTH_MAX]; + + // make sure we only print these messages if we're in the correct state + if((Game_mode & GM_MULTIPLAYER) && (Net_player->state != NETPLAYER_STATE_IN_MISSION)){ + nprintf(("Network","HUD_sourced_printf bailing because not in multiplayer game play state\n")); + return; + } + + va_start(args, format); + vsprintf(tmp, format, args); + va_end(args); + Assert(strlen(tmp) < HUD_MSG_LENGTH_MAX); // If greater than this, probably crashed anyway. + hud_sourced_print(source, tmp); +} + +void hud_sourced_print(int source, char *msg) +{ + char *ptr, *str; + //char *src_str, *msg_str; + int sw, t, x, offset = 0; + //int fudge = (gr_screen.res == GR_640) ? 15 : 50; // prevents string from running off screen + + if ( !strlen(msg) ) { + nprintf(("Warning", "HUD ==> attempt to print a 0 length string in msg window\n")); + return; + } + + // add message to the scrollback log first + hud_add_msg_to_scrollback(msg, source, timestamp()); + + ptr = strstr(msg, NOX(": ")) + 2; + if (ptr) { + gr_get_string_size(&sw, NULL, msg, ptr - msg); // get width of the speaker field + //if (sw < MSG_WINDOW_WIDTH - 20) + offset = sw; + } + + x = 0; + t = timestamp(); + str = msg; + while ((ptr = split_str_once(str, MSG_WINDOW_WIDTH - x - 7)) != NULL) { // the 7 is a fudge hack + HUD_printf_line(str, source, t, x); + str = ptr; + x = offset; + t = 0; + } + + HUD_printf_line(str, source, t, x); +} + +int hud_query_scrollback_size() +{ + int count = 0, y_add = 0; + int font_height = gr_get_font_height(); + line_node *ptr; + + if (EMPTY(&Msg_scrollback_used_list) || !HUD_msg_inited) + return 0; + + ptr = GET_FIRST(&Msg_scrollback_used_list); + while (ptr != END_OF_LIST(&Msg_scrollback_used_list)) { + if (ptr->source != HUD_SOURCE_HIDDEN) { + y_add = ptr->y; + count += font_height + ptr->y; + } + + ptr = GET_NEXT(ptr); + } + + count -= y_add; + return count; +} + +// add text directly to the hud scrollback log, without displaying on the hud +void HUD_add_to_scrollback(char *text, int source) +{ + if (!strlen(text)) { + nprintf(("Warning", "HUD ==> attempt to print a 0 length string in msg window\n")); + return; + } + + hud_add_msg_to_scrollback(text, source, timestamp()); +} + +// hud_add_msg_to_scrollback() adds the new_msg to the scroll-back message list. If there +// are no more free slots, the first slot is released to make room for the new message. +// +void hud_add_line_to_scrollback(char *text, int source, int t, int x, int y, int underline_width) +{ + line_node *new_line; + + Assert(HUD_msg_inited); + if (!text || !strlen(text)) + return; + + if ( EMPTY(&Msg_scrollback_free_list) ) { + new_line = GET_FIRST(&Msg_scrollback_used_list); + list_remove(&Msg_scrollback_used_list, new_line); + free(new_line->text); + + } else { + new_line = GET_FIRST(&Msg_scrollback_free_list); + list_remove(&Msg_scrollback_free_list, new_line); + } + + new_line->x = x; + new_line->y = y; + new_line->underline_width = underline_width; + new_line->time = t; + new_line->source = source; + new_line->text = (char *) malloc( strlen(text) + 1 ); + strcpy(new_line->text, text); + list_append(&Msg_scrollback_used_list, new_line); +} + +void hud_add_msg_to_scrollback(char *text, int source, int t) +{ + char buf[HUD_MSG_LENGTH_MAX], *ptr, *str; + int msg_len, w, max_width, x, offset = 0; + + max_width = Hud_mission_log_list2_coords[gr_screen.res][2]; + msg_len = strlen(text); + if (msg_len == 0) + return; + + w = 0; + Assert(msg_len < HUD_MSG_LENGTH_MAX); + strcpy(buf, text); + ptr = strstr(buf, NOX(": ")); + if (ptr) { + gr_get_string_size(&w, NULL, buf, ptr - buf); + } + +// if (ptr) { +// gr_get_string_size(&w, NULL, buf, ptr - buf + 2); +// if (w < max_width - 20) +// offset = w; +// } + + x = 0; + str = buf; + while ((ptr = split_str_once(str, max_width - x)) != NULL) { + hud_add_line_to_scrollback(str, source, t, x, 1, w); + str = ptr; + x = offset; + t = w = 0; + } + + hud_add_line_to_scrollback(str, source, t, x, 3, w); +} + +// hud_free_scrollback_list() will free the memory that was allocated to store the messages +// for the scroll-back list +// +void hud_free_scrollback_list() +{ + line_node *A; + + // check if the list has been inited yet. If not, return with doing nothing. + if ( Msg_scrollback_used_list.next == NULL || Msg_scrollback_used_list.prev == NULL ) + return; + + A = GET_FIRST(&Msg_scrollback_used_list); + while( A !=END_OF_LIST(&Msg_scrollback_used_list) ) { + if ( A->text != NULL ) { + free(A->text); + A->text = NULL; + } + + A = GET_NEXT(A); + } +} + +// how many lines to skip +int hud_get_scroll_max_pos() +{ + int max = 0, font_height = gr_get_font_height(); + + if (Scrollback_mode == SCROLLBACK_MODE_MSGS_LOG) { + int count = 0; + line_node *ptr; + // number of pixels in excess of what can be displayed + int excess = Scroll_max - Hud_mission_log_list_coords[gr_screen.res][3]; + + if (EMPTY(&Msg_scrollback_used_list) || !HUD_msg_inited) { + max = 0; + + } else { + ptr = GET_FIRST(&Msg_scrollback_used_list); + while (ptr != END_OF_LIST(&Msg_scrollback_used_list)) { + if (ptr->source != HUD_SOURCE_HIDDEN) { + + if (excess > 0) { + excess -= font_height; + count++; + } + + if (excess <= 0) { + max = count; + break; + } + + // spacing between lines + excess -= ptr->y; + + } + + ptr = GET_NEXT(ptr); + } + } + + } else { + max = (Scroll_max - Hud_mission_log_list_coords[gr_screen.res][3]) / font_height; + } + + if (max < 0) + max = 0; + + return max; +} + +void hud_scroll_reset() +{ + if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) { + Scroll_offset = 0; + + } else { + Scroll_offset = hud_get_scroll_max_pos(); + } +} + +void hud_scroll_list(int dir) +{ + if (dir) { + if (Scroll_offset) { + Scroll_offset--; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); + + } else { + if (Scroll_offset < hud_get_scroll_max_pos()) { + Scroll_offset++; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +void hud_goto_pos(int delta) +{ + int pos=0, font_height = gr_get_font_height(); + + if (Scrollback_mode == SCROLLBACK_MODE_MSGS_LOG) { + int count = 0, y = 0; + line_node *ptr; + + if (EMPTY(&Msg_scrollback_used_list) || !HUD_msg_inited) + return; + + ptr = GET_FIRST(&Msg_scrollback_used_list); + while (ptr != END_OF_LIST(&Msg_scrollback_used_list)) { + if (ptr->source != HUD_SOURCE_HIDDEN) { + if (count == Scroll_offset) { + pos = y; + break; + } + + y += font_height + ptr->y; + count++; + } + + ptr = GET_NEXT(ptr); + } + + Scroll_offset = count = y = 0; + ptr = GET_FIRST(&Msg_scrollback_used_list); + while (ptr != END_OF_LIST(&Msg_scrollback_used_list)) { + if (ptr->source != HUD_SOURCE_HIDDEN) { + if (y <= pos + delta) + Scroll_offset = count; + + y += font_height + ptr->y; + count++; + } + + ptr = GET_NEXT(ptr); + } + + } else { + pos = Scroll_offset * font_height; + pos += delta; + Scroll_offset = pos / font_height; + } +} + +void hud_page_scroll_list(int dir) +{ + int max = hud_get_scroll_max_pos(); + + if (dir) { + if (Scroll_offset) { + hud_goto_pos(-Hud_mission_log_list_coords[gr_screen.res][3]); + if (Scroll_offset < 0) + Scroll_offset = 0; + + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); + + } else { + if (Scroll_offset < max) { + hud_goto_pos(Hud_mission_log_list_coords[gr_screen.res][3]); + if (Scroll_offset > max) + Scroll_offset = max; + + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +void hud_scrollback_button_pressed(int n) +{ + switch (n) { + case SCROLL_UP_BUTTON: + hud_scroll_list(1); + break; + + case SCROLL_DOWN_BUTTON: + hud_scroll_list(0); + break; + + case SHOW_MSGS_BUTTON: + Scrollback_mode = SCROLLBACK_MODE_MSGS_LOG; + Scroll_max = hud_query_scrollback_size(); + hud_scroll_reset(); + break; + + case SHOW_EVENTS_BUTTON: + Scrollback_mode = SCROLLBACK_MODE_EVENT_LOG; + Scroll_max = Num_log_lines * gr_get_font_height(); + hud_scroll_reset(); + break; + + case SHOW_OBJS_BUTTON: + Scrollback_mode = SCROLLBACK_MODE_OBJECTIVES; + Scroll_max = Num_obj_lines * gr_get_font_height(); + Scroll_offset = 0; + break; + + case ACCEPT_BUTTON: + hud_scrollback_exit(); + break; + } +} + +void hud_scrollback_init() +{ + int i; + scrollback_buttons *b; + + // pause all game sounds + beam_pause_sounds(); + audiostream_pause_all(); + + common_set_interface_palette("BriefingPalette"); // set the interface palette + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Ui_window.set_mask_bmap(Hud_mission_log_mask_fname[gr_screen.res]); + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // add all strings + Ui_window.add_XSTR("Continue", 1069, Buttons[gr_screen.res][ACCEPT_BUTTON].xt, Buttons[gr_screen.res][ACCEPT_BUTTON].yt, &Buttons[gr_screen.res][ACCEPT_BUTTON].button, UI_XSTR_COLOR_PINK); + Ui_window.add_XSTR("Events", 1070, Buttons[gr_screen.res][SHOW_EVENTS_BUTTON].xt, Buttons[gr_screen.res][SHOW_EVENTS_BUTTON].yt, &Buttons[gr_screen.res][SHOW_EVENTS_BUTTON].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Objectives", 1071, Buttons[gr_screen.res][SHOW_OBJS_BUTTON].xt, Buttons[gr_screen.res][SHOW_OBJS_BUTTON].yt, &Buttons[gr_screen.res][SHOW_OBJS_BUTTON].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Messages", 1072, Buttons[gr_screen.res][SHOW_MSGS_BUTTON].xt, Buttons[gr_screen.res][SHOW_MSGS_BUTTON].yt, &Buttons[gr_screen.res][SHOW_MSGS_BUTTON].button, UI_XSTR_COLOR_GREEN); + + // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed + Buttons[gr_screen.res][SCROLL_UP_BUTTON].button.set_hotkey(KEY_UP); + Buttons[gr_screen.res][SCROLL_DOWN_BUTTON].button.set_hotkey(KEY_DOWN); + + Background_bitmap = bm_load(Hud_mission_log_fname[gr_screen.res]); + // Status_bitmap = bm_load(Hud_mission_log_status_fname[gr_screen.res]); + + message_log_init_scrollback(Hud_mission_log_list_coords[gr_screen.res][2]); + if (Scrollback_mode == SCROLLBACK_MODE_EVENT_LOG) + Scroll_max = Num_log_lines * gr_get_font_height(); + else if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) + Scroll_max = Num_obj_lines * gr_get_font_height(); + else + Scroll_max = hud_query_scrollback_size(); + + Num_obj_lines = ML_objectives_init(Hud_mission_log_list_coords[gr_screen.res][0], Hud_mission_log_list_coords[gr_screen.res][1], Hud_mission_log_list_coords[gr_screen.res][2], Hud_mission_log_list_objective_x_coord[gr_screen.res]); + hud_scroll_reset(); +} + +void hud_scrollback_close() +{ + ML_objectives_close(); + message_log_shutdown_scrollback(); + if (Background_bitmap >= 0) + bm_unload(Background_bitmap); + //if (Status_bitmap >= 0) + // bm_unload(Status_bitmap); + + Ui_window.destroy(); + common_free_interface_palette(); // restore game palette + game_flush(); + + // unpause all game sounds + beam_unpause_sounds(); + audiostream_unpause_all(); + +} + +void hud_scrollback_do_frame(float frametime) +{ + int i, k, x, y; + int font_height = gr_get_font_height(); + + k = Ui_window.process(); + switch (k) { + case KEY_RIGHT: + case KEY_TAB: + if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) { + Scrollback_mode = SCROLLBACK_MODE_MSGS_LOG; + Scroll_max = hud_query_scrollback_size(); + hud_scroll_reset(); + + } else if (Scrollback_mode == SCROLLBACK_MODE_MSGS_LOG) { + Scrollback_mode = SCROLLBACK_MODE_EVENT_LOG; + Scroll_max = Num_log_lines * gr_get_font_height(); + hud_scroll_reset(); + + } else { + Scrollback_mode = SCROLLBACK_MODE_OBJECTIVES; + Scroll_max = Num_obj_lines * gr_get_font_height(); + Scroll_offset = 0; + } + + break; + + case KEY_LEFT: + case KEY_SHIFTED | KEY_TAB: + if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) { + Scrollback_mode = SCROLLBACK_MODE_EVENT_LOG; + Scroll_max = Num_log_lines * gr_get_font_height(); + hud_scroll_reset(); + + } else if (Scrollback_mode == SCROLLBACK_MODE_MSGS_LOG) { + Scrollback_mode = SCROLLBACK_MODE_OBJECTIVES; + Scroll_max = Num_obj_lines * gr_get_font_height(); + Scroll_offset = 0; + + } else { + Scrollback_mode = SCROLLBACK_MODE_MSGS_LOG; + Scroll_max = hud_query_scrollback_size(); + hud_scroll_reset(); + } + + break; + + case KEY_PAGEUP: + hud_page_scroll_list(1); + break; + + case KEY_PAGEDOWN: + hud_page_scroll_list(0); + break; + + case KEY_ENTER: + case KEY_CTRLED | KEY_ENTER: + case KEY_ESC: + hud_scrollback_exit(); + break; + + case KEY_F1: // show help overlay + break; + + case KEY_F2: // goto options screen + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + break; + } // end switch + + for (i=0; i= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + /* + if ((Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) && (Status_bitmap >= 0)) { + gr_set_bitmap(Status_bitmap); + gr_bitmap(Hud_mission_log_status_coords[gr_screen.res][0], Hud_mission_log_status_coords[gr_screen.res][1]); + } + */ + + // draw the objectives key at the bottom of the ingame objectives screen + if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) { + ML_render_objectives_key(); + } + + Ui_window.draw(); + + if (Scrollback_mode == SCROLLBACK_MODE_EVENT_LOG) { + Buttons[gr_screen.res][SHOW_EVENTS_BUTTON].button.draw_forced(2); + mission_log_scrollback(Scroll_offset, Hud_mission_log_list_coords[gr_screen.res][0], Hud_mission_log_list_coords[gr_screen.res][1], Hud_mission_log_list_coords[gr_screen.res][2], Hud_mission_log_list_coords[gr_screen.res][3]); + + } else if (Scrollback_mode == SCROLLBACK_MODE_OBJECTIVES) { + Buttons[gr_screen.res][SHOW_OBJS_BUTTON].button.draw_forced(2); + ML_objectives_do_frame(Scroll_offset); + + } else { + line_node *node_ptr; + + Buttons[gr_screen.res][SHOW_MSGS_BUTTON].button.draw_forced(2); +// y = ((LIST_H / font_height) - 1) * font_height; + y = 0; + if ( !EMPTY(&Msg_scrollback_used_list) && HUD_msg_inited ) { + node_ptr = GET_FIRST(&Msg_scrollback_used_list); + i = 0; + while ( node_ptr != END_OF_LIST(&Msg_scrollback_used_list) ) { + if ((node_ptr->source == HUD_SOURCE_HIDDEN) || (i++ < Scroll_offset)) { + node_ptr = GET_NEXT(node_ptr); + + } else { + switch (node_ptr->source) { + case HUD_SOURCE_FRIENDLY: + SET_COLOR_FRIENDLY; + break; + + case HUD_SOURCE_HOSTILE: + SET_COLOR_HOSTILE; + break; + + case HUD_SOURCE_NEUTRAL: + SET_COLOR_NEUTRAL; + break; + + case HUD_SOURCE_UNKNOWN: + SET_COLOR_UNKNOWN; + break; + + case HUD_SOURCE_TRAINING: + gr_set_color_fast(&Color_bright_blue); + break; + + case HUD_SOURCE_TERRAN_CMD: + gr_set_color_fast(&Color_bright_white); + break; + + case HUD_SOURCE_IMPORTANT: + case HUD_SOURCE_FAILED: + case HUD_SOURCE_SATISFIED: + gr_set_color_fast(&Color_bright_white); + break; + + default: + gr_set_color_fast(&Color_text_normal); + break; + } + + if (node_ptr->time) + gr_print_timestamp(Hud_mission_log_list_coords[gr_screen.res][0], Hud_mission_log_list_coords[gr_screen.res][1] + y, node_ptr->time); + + x = Hud_mission_log_list2_coords[gr_screen.res][0] + node_ptr->x; + gr_printf(x, Hud_mission_log_list_coords[gr_screen.res][1] + y, "%s", node_ptr->text); + if (node_ptr->underline_width) + gr_line(x, Hud_mission_log_list_coords[gr_screen.res][1] + y + font_height - 1, x + node_ptr->underline_width, Hud_mission_log_list_coords[gr_screen.res][1] + y + font_height - 1); + + if ((node_ptr->source == HUD_SOURCE_FAILED) || (node_ptr->source == HUD_SOURCE_SATISFIED)) { + // draw goal icon + if (node_ptr->source == HUD_SOURCE_FAILED) + gr_set_color_fast(&Color_bright_red); + else + gr_set_color_fast(&Color_bright_green); + + i = Hud_mission_log_list_coords[gr_screen.res][1] + y + font_height / 2 - 1; + gr_circle(Hud_mission_log_list2_coords[gr_screen.res][0] - 6, i, 5); + + gr_set_color_fast(&Color_bright); + gr_line(Hud_mission_log_list2_coords[gr_screen.res][0] - 10, i, Hud_mission_log_list2_coords[gr_screen.res][0] - 8, i); + gr_line(Hud_mission_log_list2_coords[gr_screen.res][0] - 6, i - 4, Hud_mission_log_list2_coords[gr_screen.res][0] - 6, i - 2); + gr_line(Hud_mission_log_list2_coords[gr_screen.res][0] - 4, i, Hud_mission_log_list2_coords[gr_screen.res][0] - 2, i); + gr_line(Hud_mission_log_list2_coords[gr_screen.res][0] - 6, i + 2, Hud_mission_log_list2_coords[gr_screen.res][0] - 6, i + 4); + } + + y += font_height + node_ptr->y; + node_ptr = GET_NEXT(node_ptr); + if (y + font_height > Hud_mission_log_list_coords[gr_screen.res][3]) + break; + } + } + } + } + + gr_set_color_fast(&Color_text_heading); + gr_print_timestamp(Hud_mission_log_time_coords[gr_screen.res][0], Hud_mission_log_time_coords[gr_screen.res][1] - font_height, (int) (f2fl(Missiontime) * 1000)); + gr_string(Hud_mission_log_time2_coords[gr_screen.res][0], Hud_mission_log_time_coords[gr_screen.res][1] - font_height, XSTR( "Current time", 289)); + gr_flip(); +} + +void hud_scrollback_exit() +{ + gameseq_post_event(GS_EVENT_PREVIOUS_STATE); +} \ No newline at end of file diff --git a/src/hud/hudobserver.cpp b/src/hud/hudobserver.cpp new file mode 100644 index 0000000..eb99d41 --- /dev/null +++ b/src/hud/hudobserver.cpp @@ -0,0 +1,73 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDObserver.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $NoKeywords: $ + * + */ + +#include "hud.h" +#include "hudobserver.h" +#include "freespace.h" +#include "multi.h" +#include "font.h" +#include "missiongoals.h" +#include "3d.h" +#include "linklist.h" +#include "debris.h" +#include "hudtargetbox.h" +#include "sound.h" +#include "gamesnd.h" +#include "radar.h" +#include "hudconfig.h" +#include "alphacolors.h" + +// use these to redirect Player_ship and Player_ai when switching into ai mode +ship Hud_obs_ship; +ai_info Hud_obs_ai; + + +// initialize observer hud stuff +void hud_observer_init(ship *shipp,ai_info *aip) +{ + // setup the pseduo ship and ai + memcpy(&Hud_obs_ship,shipp,sizeof(ship)); + memcpy(&Hud_obs_ai,aip,sizeof(ai_info)); + + HUD_config.is_observer = 1; + HUD_config.show_flags = HUD_observer_default_flags; + HUD_config.show_flags2 = HUD_observer_default_flags2; + + HUD_config.popup_flags = 0x0; + HUD_config.popup_flags2 = 0x0; + + // shutdown any playing static animations + hud_targetbox_static_init(); +} + +void hud_obs_render_player(int loc,net_player *pl) +{ +} + +void hud_obs_render_players_all() +{ + int idx,count; + + // render kills and stats information for all players + count = 0; + for(idx=0;idxflags & NETINFO_FLAG_OBSERVER)); + + // render individual player text + hud_obs_render_players_all(); +} diff --git a/src/hud/hudresource.cpp b/src/hud/hudresource.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/hud/hudreticle.cpp b/src/hud/hudreticle.cpp new file mode 100644 index 0000000..41e30b6 --- /dev/null +++ b/src/hud/hudreticle.cpp @@ -0,0 +1,834 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDreticle.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module to draw and manage the recticle + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 14 10/27/99 10:04p Jefff + * changed german match speed indicator + * + * 13 10/14/99 2:50p Jefff + * localization fixes + * + * 12 8/28/99 4:54p Dave + * Fixed directives display for multiplayer clients for wings with + * multiple waves. Fixed hud threat indicator rendering color. + * + * 11 8/09/99 3:14p Dave + * Make "launch" warning gauge draw in code. + * + * 10 6/10/99 3:43p Dave + * Do a better job of syncing text colors to HUD gauges. + * + * 9 6/08/99 10:48a Jasenw + * new coords for new HUD stuff + * + * 8 6/03/99 2:31p Jasenw + * changed coords and removed weapon indicators. + * + * 7 1/07/99 2:21p Jasen + * fixed toparc + * + * 6 1/07/99 9:07a Jasen + * HUD coords + * + * 5 12/28/98 3:17p Dave + * Support for multiple hud bitmap filenames for hi-res mode. + * + * 4 12/21/98 5:02p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 3 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 69 8/28/98 3:28p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 68 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 67 6/12/98 4:52p Hoffoss + * Added support for special characters in in forgeign languages. + * + * 66 6/09/98 5:17p Lawrance + * French/German localization + * + * 65 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 64 5/21/98 7:18p Lawrance + * Don't flash 'launch' for attempted lock + * + * 63 5/13/98 1:53p Lawrance + * If no secondary banks on a ship, show correct reticle gauge + * + * 62 5/06/98 8:05p Dave + * Made standalone reset properly under weird conditions. Tweak + * optionsmulti screen. Upped MAX_WEAPONS to 350. Put in new launch + * countdown anim. Minro ui fixes/tweaks. + * + * 61 5/05/98 5:51p Lawrance + * Draw HUD center crosshair with bright alpha index + * + * 60 5/04/98 6:12p Lawrance + * fix progblems with weapon gauges in situation when weapons are on ship + * + * 59 5/01/98 4:22p Lawrance + * Use ship->weapons instead of sip when displaying weapon linking gauge + * + * 58 4/13/98 11:19a Lawrance + * fix launch threat indicator + * + * 57 4/08/98 10:34p Allender + * make threat indicators work in multiplayer. Fix socket problem (once + * and for all???) + * + * 56 3/26/98 5:26p John + * added new paging code. nonfunctional. + * + * 55 2/23/98 6:49p Lawrance + * Use gr_aabitmap_ex() instead of clipping regions + * + * 54 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 53 2/22/98 12:19p John + * Externalized some strings + * + * 52 2/12/98 4:58p Lawrance + * Change to new flashing method. + * + * 51 2/09/98 9:07p Lawrance + * Ensure 'evaded' popup has precedence over 'launch' popup + * + * 50 1/25/98 10:31p Lawrance + * Don't draw most hud gauges when viewing from another ship. + * + * 49 1/19/98 11:37p Lawrance + * Fixing Optimization build warnings + * + * 48 1/14/98 10:21a Lawrance + * Draw reticle correctly when gauges are disabled. + * + * 47 1/08/98 3:20p Johnson + * ALAN: Ensure entire throttle gauge flashes + * + * 46 1/05/98 9:38p Lawrance + * Implement flashing HUD gauges. + * + * 45 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 44 12/16/97 9:13p Lawrance + * Integrate new gauges into HUD config. + * + * 43 12/10/97 10:02p Lawrance + * Change weapon linking to use flags. + * + * 42 12/08/97 1:49p Lawrance + * only play threat lock sound when a lock is being attempted + * + * 41 11/17/97 6:37p Lawrance + * new gauges: extended target view, new lock triangles, support ship view + * + * 40 11/11/97 10:25p Lawrance + * add sound hook for when missile threat flashes + * + * 39 11/11/97 5:05p Lawrance + * get threat indicator working more reliably + * + * 38 11/05/97 11:20p Lawrance + * add speed numbers to the reticle + * + * 37 11/04/97 7:49p Lawrance + * integrating new HUD reticle and shield icons + * + * 36 11/03/97 5:38p Dave + * Cleaned up more multiplayer sequencing. Added OBJ_OBSERVER module/type. + * Restructured HUD_config structs/flags. + * + * $NoKeywords: $ + * +*/ + +#include "2d.h" + +#include "hudreticle.h" +#include "hud.h" +#include "pixel.h" +#include "math.h" +#include "player.h" +#include "ship.h" +#include "freespace.h" +#include "ai.h" +#include "bmpman.h" +#include "key.h" +#include "timer.h" +#include "math.h" +#include "gamesnd.h" +#include "hudtargetbox.h" +#include "multi.h" +#include "emp.h" +#include "localize.h" + +static int Reticle_inited = 0; + +#define NUM_RETICLE_ANIS 6 // keep up to date when modifying the number of reticle ani files + +#define RETICLE_TOP_ARC 0 +#define RETICLE_LASER_WARN 1 +#define RETICLE_LOCK_WARN 2 +#define RETICLE_LEFT_ARC 3 +#define RETICLE_RIGHT_ARC 4 +//#define RETICLE_ONE_PRIMARY 5 +//#define RETICLE_TWO_PRIMARY 6 +//#define RETICLE_ONE_SECONDARY 7 +//#define RETICLE_TWO_SECONDARY 8 +//#define RETICLE_THREE_SECONDARY 9 +// #define RETICLE_LAUNCH_LABEL 5 +#define RETICLE_CENTER 5 + +int Hud_throttle_frame_h[GR_NUM_RESOLUTIONS] = { + 85, + 136 +}; +int Hud_throttle_frame_w[GR_NUM_RESOLUTIONS] = { + 49, + 78 +}; +int Hud_throttle_frame_bottom_y[GR_NUM_RESOLUTIONS] = { + 325, + 520 +}; +int Hud_throttle_h[GR_NUM_RESOLUTIONS] = { + 50, + 80 +}; +int Hud_throttle_bottom_y[GR_NUM_RESOLUTIONS] = { + 307, + 491 +}; +int Hud_throttle_aburn_h[GR_NUM_RESOLUTIONS] = { + 17, + 27 +}; +int Hud_throttle_aburn_button[GR_NUM_RESOLUTIONS] = { + 259, + 414 +}; + +int Outer_circle_radius[GR_NUM_RESOLUTIONS] = { + 104, + 166 +}; + +int Hud_reticle_center[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 320, 242 + }, + { // GR_1024 + 512, 385 + } +}; + +char Reticle_frame_names[GR_NUM_RESOLUTIONS][NUM_RETICLE_ANIS][MAX_FILENAME_LEN] = +{ +//XSTR:OFF + { // GR_640 + "toparc1", + "toparc2", + "toparc3", + "leftarc", + "rightarc1", +/* "rightarc2", + "rightarc3", + "rightarc4", + "rightarc5", + "rightarc6", + "toparc4", */ + "reticle1", + }, + { // GR_1024 + "2_toparc1", + "2_toparc2", + "2_toparc3", + "2_leftarc", + "2_rightarc1", +/* "2_rightarc2", + "2_rightarc3", + "2_rightarc4", + "2_rightarc5", + "2_rightarc6", + "2_toparc4", */ + "2_reticle1", + } +//XSTR:ON +}; + +// reticle frame coords +int Reticle_frame_coords[GR_NUM_RESOLUTIONS][NUM_RETICLE_ANIS][2] = { + { // GR_640 + {241, 137}, + {400, 245}, + {394, 261}, + {216, 168}, + {359, 168}, +// {406, 253}, +// {406, 253}, +// {391, 276}, +// {391, 276}, +// {391, 276}, +// {297, 161}, + {308, 235} + }, + { // GR_1024 + {386, 219}, + {640, 393}, + {631, 419}, + {346, 269}, + {574, 269}, +// {649, 401}, +// {649, 401}, +// {625, 438}, +// {625, 438}, +// {625, 438}, +// {475, 258}, + {493, 370} + } +}; + +// "launch" gauge coords +int Reticle_launch_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 297, 161 + }, + { // GR_1024 + 475, 258 + } +}; + +hud_frames Reticle_gauges[NUM_RETICLE_ANIS]; + +#define THREAT_DUMBFIRE (1<<0) +#define THREAT_ATTEMPT_LOCK (1<<1) +#define THREAT_LOCK (1<<2) + +#define THREAT_UPDATE_DUMBFIRE_TIME 1000 // time between checking for dumbfire threats +#define THREAT_UPDATE_LOCK_TIME 500 // time between checking for lock threats + +#define THREAT_DUMBFIRE_FLASH 180 +#define THREAT_LOCK_FLASH 180 +static int Threat_dumbfire_timer; // timestamp for when to show next flashing frame for dumbfire threat +static int Threat_lock_timer; // timestamp for when to show next flashing frame for lock threat + +static int Threat_dumbfire_frame; // frame offset of current dumbfire flashing warning +static int Threat_lock_frame; // frame offset of current lock flashing warning + +// coordinates +static int Max_speed_coords[GR_NUM_RESOLUTIONS][2] = +{ + { // GR_640 + 236, 254 + }, + { // GR_1024 + 377, 406 + } +}; +static int Zero_speed_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 252, 303 + }, + { // GR_1024 + 403, 485 + } +}; + +// Called at the start of each level.. use Reticle_inited so we only load frames once +void hud_init_reticle() +{ + int i; + hud_frames *hfp; + + Threat_dumbfire_timer = timestamp(0); + Threat_lock_timer = timestamp(0); + Threat_dumbfire_frame = 1; + Threat_lock_frame = 1; + Player->threat_flags = 0; + Player->update_dumbfire_time = timestamp(0); + Player->update_lock_time = timestamp(0); + + if ( Reticle_inited ) { + return; + } + + for ( i = 0; i < NUM_RETICLE_ANIS; i++ ) { + hfp = &Reticle_gauges[i]; + hfp->first_frame = bm_load_animation(Reticle_frame_names[gr_screen.res][i], &hfp->num_frames); + if ( hfp->first_frame < 0 ) { + Warning(LOCATION,"Cannot load hud ani: %s\n", Reticle_frame_names[gr_screen.res][i]); + } + } + + Reticle_inited = 1; +} + +// called once per frame to update the reticle gauges. Makes calls to +// ship_dumbfire_threat() and ship_lock_threat() and updates Threat_flags. +void hud_update_reticle( player *pp ) +{ + int rval; + ship *shipp; + + // multiplayer clients won't call this routine + if ( MULTIPLAYER_CLIENT || MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])) + return; + + shipp = &Ships[Objects[pp->objnum].instance]; + + if ( ship_dumbfire_threat(shipp) ) { + pp->threat_flags |= THREAT_DUMBFIRE; + pp->update_dumbfire_time = timestamp(THREAT_UPDATE_DUMBFIRE_TIME); + } + + if ( timestamp_elapsed(pp->update_dumbfire_time) ) { + pp->update_dumbfire_time = timestamp(THREAT_UPDATE_DUMBFIRE_TIME); + pp->threat_flags &= ~THREAT_DUMBFIRE; + } + + if ( timestamp_elapsed(pp->update_lock_time) ) { + pp->threat_flags &= ~(THREAT_LOCK | THREAT_ATTEMPT_LOCK); + pp->update_lock_time = timestamp(THREAT_UPDATE_LOCK_TIME); + rval = ship_lock_threat(shipp); + if ( rval == 1 ) { + pp->threat_flags |= THREAT_ATTEMPT_LOCK; + } else if ( rval == 2 ) { + pp->threat_flags |= THREAT_LOCK; + } + } +} + +// draw left arc (the dark portion of the throttle gauge) +void hud_render_throttle_background(int y_end) +{ + int x,y,w,h; + + hud_set_gauge_color(HUD_THROTTLE_GAUGE); + + x = Reticle_frame_coords[gr_screen.res][RETICLE_LEFT_ARC][0]; + y = Reticle_frame_coords[gr_screen.res][RETICLE_LEFT_ARC][1]; + + bm_get_info( Reticle_gauges[RETICLE_LEFT_ARC].first_frame+1,&w,&h); + + if ( y_end > y ) { + GR_AABITMAP_EX(Reticle_gauges[RETICLE_LEFT_ARC].first_frame+1, x, y, w, y_end-y+1, 0, 0); + } +} + +// draw left arc (the bright portion of the throttle gauge) +void hud_render_throttle_foreground(int y_end) +{ + int x,y,w,h; + + hud_set_gauge_color(HUD_THROTTLE_GAUGE); + + x = Reticle_frame_coords[gr_screen.res][RETICLE_LEFT_ARC][0]; + y = Reticle_frame_coords[gr_screen.res][RETICLE_LEFT_ARC][1]; + + bm_get_info( Reticle_gauges[RETICLE_LEFT_ARC].first_frame+1,&w,&h); + + if ( y_end < Hud_throttle_frame_bottom_y[gr_screen.res] ) { + GR_AABITMAP_EX(Reticle_gauges[RETICLE_LEFT_ARC].first_frame+2, x, y_end, w, h-(y_end-y), 0, y_end-y); + } +} + +// Draw the throttle speed number +void hud_render_throttle_speed(float current_speed, int y_end) +{ + char buf[32]; + int sx, sy, x_pos, y_pos, w, h; + + hud_set_gauge_color(HUD_THROTTLE_GAUGE); + + // y_end is the y-coordinate of the current throttle setting, calc x-coordinate for edge of + // circle (x^2 + y^2 = r^2) + y_pos = Hud_reticle_center[gr_screen.res][1] - y_end; + x_pos = (int)sqrt(double(Outer_circle_radius[gr_screen.res] * Outer_circle_radius[gr_screen.res] - y_pos * y_pos) ); + x_pos = Hud_reticle_center[gr_screen.res][0] - x_pos; + + // draw current speed at (x_pos, y_end); + sprintf(buf, "%d", fl2i(current_speed+0.5f)); + hud_num_make_mono(buf); + gr_get_string_size(&w, &h, buf); + sx = x_pos - w - 2; + sy = fl2i(y_end - h/2.0f + 1.5); + gr_printf(sx, sy, buf); + + if ( Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET ) { + int offset; + if ( current_speed <= 9.5 ) { + offset = 0; + } else { + offset = 3; + } +#if defined(GERMAN_BUILD) + // print an m, cuz the voice says its an m. + // its a normal m cuz the german font has no special m (its an a) + gr_string(sx+offset, sy + h, "m"); +#else + gr_printf(sx+offset, sy + h, "%c", Lcl_special_chars + 3); +#endif + } +} + +// draw the "desired speed" bar on the throttle +void hud_render_throttle_line(int y) +{ + // hud_set_bright_color(); + hud_set_gauge_color(HUD_THROTTLE_GAUGE, HUD_C_BRIGHT); + + GR_AABITMAP_EX(Reticle_gauges[RETICLE_LEFT_ARC].first_frame+3, Reticle_frame_coords[gr_screen.res][RETICLE_LEFT_ARC][0], y, Hud_throttle_frame_w[gr_screen.res], 1, 0, y-Reticle_frame_coords[gr_screen.res][RETICLE_LEFT_ARC][1]); +} + +// Draw the throttle gauge along the left arc of the reticle +void hud_show_throttle() +{ + float desired_speed, max_speed, current_speed, percent_max, percent_aburn_max; + int desired_y_pos, y_end; + + ship_info *sip; + sip = &Ship_info[Player_ship->ship_info_index]; + + current_speed = Player_obj->phys_info.fspeed; + if ( current_speed < 0.0f){ + current_speed = 0.0f; + } + + max_speed = Ships[Player_obj->instance].current_max_speed; + if ( max_speed <= 0 ) { + max_speed = sip->max_vel.z; + } + + desired_speed = Player->ci.forward * max_speed; + if ( desired_speed < 0.0f ){ // so ships that go backwards don't force the indicators below where they can go + desired_speed = 0.0f; + } + + desired_y_pos = Hud_throttle_bottom_y[gr_screen.res] - fl2i(Hud_throttle_h[gr_screen.res]*desired_speed/max_speed+0.5f) - 1; + + Assert(max_speed != 0); + percent_max = current_speed / max_speed; + + percent_aburn_max = 0.0f; + if ( percent_max > 1 ) { + percent_max = 1.0f; + percent_aburn_max = (current_speed - max_speed) / (sip->afterburner_max_vel.z - max_speed); + if ( percent_aburn_max > 1.0f ) { + percent_aburn_max = 1.0f; + } + if ( percent_aburn_max < 0 ) { + percent_aburn_max = 0.0f; + } + } + + y_end = Hud_throttle_bottom_y[gr_screen.res] - fl2i(Hud_throttle_h[gr_screen.res]*percent_max+0.5f); + if ( percent_aburn_max > 0 ) { + y_end -= fl2i(percent_aburn_max * Hud_throttle_aburn_h[gr_screen.res] + 0.5f); + } + + if ( Player_obj->phys_info.flags & PF_AFTERBURNER_ON ) { + desired_y_pos = 240; + } + + // draw left arc (the dark portion of the throttle gauge) + // hud_render_throttle_background(y_end); + + // draw throttle speed number + hud_render_throttle_speed(current_speed, y_end); + + // draw the "desired speed" bar on the throttle + hud_render_throttle_line(desired_y_pos); + + // draw left arc (the bright portion of the throttle gauge) + hud_render_throttle_foreground(y_end); + + gr_printf(Max_speed_coords[gr_screen.res][0], Max_speed_coords[gr_screen.res][1], "%d",fl2i(max_speed)); + gr_printf(Zero_speed_coords[gr_screen.res][0], Zero_speed_coords[gr_screen.res][1], XSTR( "0", 292)); +} + +/* +// Draw the primary and secondary weapon indicators along the right arc of the reticle +void hud_show_reticle_weapons() +{ + int gauge_index=0, frame_offset=0; + ship_weapon *swp; + + swp = &Player_ship->weapons; + + switch( swp->num_primary_banks ) { + case 0: + gauge_index = -1; + break; + + case 1: + gauge_index = RETICLE_ONE_PRIMARY; + if ( Player_ship->weapons.current_primary_bank == -1 ) { + frame_offset = 0; + } else { + frame_offset = 1; + } + break; + + case 2: + gauge_index = RETICLE_TWO_PRIMARY; + if ( swp->current_primary_bank == -1 ) { + frame_offset = 0; + } else { + if ( Player_ship->flags & SF_PRIMARY_LINKED ) { + frame_offset = 3; + } else { + if ( swp->current_primary_bank == 0 ) { + frame_offset = 1; + } else { + frame_offset = 2; + } + } + } + break; + + default: + Int3(); // shouldn't happen (get Alan if it does) + return; + break; + } + + if ( gauge_index != -1 ) { + GR_AABITMAP(Reticle_gauges[gauge_index].first_frame+frame_offset, Reticle_frame_coords[gr_screen.res][gauge_index][0], Reticle_frame_coords[gr_screen.res][gauge_index][1]); + } + + int num_banks = swp->num_secondary_banks; + if ( num_banks <= 0 ) { + num_banks = Ship_info[Player_ship->ship_info_index].num_secondary_banks; + } + + switch( num_banks ) { + case 0: + Int3(); + gauge_index = -1; + break; + + case 1: + gauge_index = RETICLE_ONE_SECONDARY; + break; + + case 2: + gauge_index = RETICLE_TWO_SECONDARY; + break; + + case 3: + gauge_index = RETICLE_THREE_SECONDARY; + break; + + default: + Int3(); // shouldn't happen (get Alan if it does) + return; + break; + } + + if ( gauge_index != -1 ) { + if ( swp->num_secondary_banks <= 0 ) { + frame_offset = 0; + } else { + frame_offset = swp->current_secondary_bank+1; + } + + GR_AABITMAP(Reticle_gauges[gauge_index].first_frame+frame_offset, Reticle_frame_coords[gr_screen.res][gauge_index][0], Reticle_frame_coords[gr_screen.res][gauge_index][1]); + } +} +*/ + +// Draw the lock threat gauge on the HUD. Use Threat_flags to determine if a +// threat exists, and draw flashing frames. +void hud_show_lock_threat() +{ + int frame_offset; + + if ( Player->threat_flags & (THREAT_LOCK | THREAT_ATTEMPT_LOCK) ) { + if ( timestamp_elapsed(Threat_lock_timer) ) { + if ( Player->threat_flags & THREAT_LOCK ) { + Threat_lock_timer = timestamp(fl2i(THREAT_LOCK_FLASH/2.0f)); + } else { + Threat_lock_timer = timestamp(THREAT_LOCK_FLASH); + } + Threat_lock_frame++; + if ( Threat_lock_frame > 2 ) { + Threat_lock_frame = 1; + } + if ( (Threat_lock_frame == 2) && (Player->threat_flags & THREAT_ATTEMPT_LOCK ) ) { + snd_play( &Snds[SND_THREAT_FLASH]); + } + } + frame_offset = Threat_lock_frame; + } else { + frame_offset = 0; + } + + hud_set_gauge_color(HUD_THREAT_GAUGE); + + GR_AABITMAP(Reticle_gauges[RETICLE_LOCK_WARN].first_frame+frame_offset, Reticle_frame_coords[gr_screen.res][RETICLE_LOCK_WARN][0], Reticle_frame_coords[gr_screen.res][RETICLE_LOCK_WARN][1]); + + // "launch" flash + if ( (frame_offset > 0) && (Player->threat_flags & THREAT_LOCK) ) { + if ( hud_targetbox_flash_expired(TBOX_FLASH_CMEASURE) ) { + // hack + int bright; + if(frame_offset % 2){ + bright = 1; + } else { + bright = 0; + } + // GR_AABITMAP(Reticle_gauges[RETICLE_LAUNCH_LABEL].first_frame+frame_offset%2, Reticle_frame_coords[gr_screen.res][RETICLE_LAUNCH_LABEL][0], Reticle_frame_coords[gr_screen.res][RETICLE_LAUNCH_LABEL][1]); + + // use hud text flash gauge code + hud_show_text_flash_icon(XSTR("Launch", 1507), Reticle_launch_coords[gr_screen.res][1], bright); + } + } +} + +// Draw the dumbfire threat gauge on the HUD. Use Threat_flags to determine if a +// threat exists, and draw flashing frames. +void hud_show_dumbfire_threat() +{ + int frame_offset; + + if ( Player->threat_flags & THREAT_DUMBFIRE ) { + if ( timestamp_elapsed(Threat_dumbfire_timer) ) { + Threat_dumbfire_timer = timestamp(THREAT_DUMBFIRE_FLASH); + Threat_dumbfire_frame++; + if ( Threat_dumbfire_frame > 2 ) { + Threat_dumbfire_frame = 1; + } + } + frame_offset = Threat_dumbfire_frame; + } else { + frame_offset = 0; + } + + hud_set_gauge_color(HUD_THREAT_GAUGE); + + GR_AABITMAP(Reticle_gauges[RETICLE_LASER_WARN].first_frame + frame_offset, Reticle_frame_coords[gr_screen.res][RETICLE_LASER_WARN][0], Reticle_frame_coords[gr_screen.res][RETICLE_LASER_WARN][1]); +} + +// Draw the center of the reticle +void hud_show_center_reticle() +{ + Assert(Reticle_gauges[RETICLE_CENTER].first_frame != -1 ); + +// hud_set_default_color(); + // hud_set_bright_color(); + hud_set_gauge_color(HUD_CENTER_RETICLE, HUD_C_BRIGHT); + + GR_AABITMAP(Reticle_gauges[RETICLE_CENTER].first_frame, Reticle_frame_coords[gr_screen.res][RETICLE_CENTER][0], Reticle_frame_coords[gr_screen.res][RETICLE_CENTER][1]); +} + +// Draw top portion of reticle +void hud_show_top_arc() +{ + hud_set_gauge_color(HUD_CENTER_RETICLE); + + // hud_set_default_color(); + if ( hud_gauge_active(HUD_THREAT_GAUGE) ) { + // draw top arc + GR_AABITMAP(Reticle_gauges[RETICLE_TOP_ARC].first_frame+1, Reticle_frame_coords[gr_screen.res][RETICLE_TOP_ARC][0], Reticle_frame_coords[gr_screen.res][RETICLE_TOP_ARC][1]); + + // draw dumbfire threat + hud_show_dumbfire_threat(); + + // draw lock threat + hud_show_lock_threat(); + } else { + // draw top arc without any holes + GR_AABITMAP(Reticle_gauges[RETICLE_TOP_ARC].first_frame, Reticle_frame_coords[gr_screen.res][RETICLE_TOP_ARC][0], Reticle_frame_coords[gr_screen.res][RETICLE_TOP_ARC][1]); + } +} + +// Draw right portion of reticle +void hud_show_right_arc() +{ + hud_set_gauge_color(HUD_CENTER_RETICLE); + + GR_AABITMAP(Reticle_gauges[RETICLE_RIGHT_ARC].first_frame+1, Reticle_frame_coords[gr_screen.res][RETICLE_RIGHT_ARC][0], Reticle_frame_coords[gr_screen.res][RETICLE_RIGHT_ARC][1]); + + // draw the weapons indicators in the holes along the right arc + /* + if ( hud_gauge_active(HUD_WEAPON_LINKING_GAUGE) ) { + // draw right arc with holes in it + GR_AABITMAP(Reticle_gauges[RETICLE_RIGHT_ARC].first_frame+1, Reticle_frame_coords[gr_screen.res][RETICLE_RIGHT_ARC][0], Reticle_frame_coords[gr_screen.res][RETICLE_RIGHT_ARC][1]); + +// the following line was removed by Jasen to get rid of "undeclared identifier" +// hehe - DB +// hud_show_reticle_weapons(); + } else { + // draw right arc without any holes + GR_AABITMAP(Reticle_gauges[RETICLE_RIGHT_ARC].first_frame, Reticle_frame_coords[gr_screen.res][RETICLE_RIGHT_ARC][0], Reticle_frame_coords[gr_screen.res][RETICLE_RIGHT_ARC][1]); + } + */ +} + +// Draw the left portion of the reticle +void hud_show_left_arc() +{ + // draw left arc (the dark portion of the throttle gauge) + hud_set_gauge_color(HUD_CENTER_RETICLE); + GR_AABITMAP(Reticle_gauges[RETICLE_LEFT_ARC].first_frame, Reticle_frame_coords[gr_screen.res][RETICLE_LEFT_ARC][0], Reticle_frame_coords[gr_screen.res][RETICLE_LEFT_ARC][1]); + + // draw the throttle + if ( hud_gauge_active(HUD_THROTTLE_GAUGE) ) { + hud_set_gauge_color(HUD_THROTTLE_GAUGE); + hud_show_throttle(); + } +} + +// called once per frame from HUD_render_2d() to draw the reticle gauges +void hud_show_reticle() +{ + if ( !(Viewer_mode & VM_OTHER_SHIP) ) { + hud_show_top_arc(); + hud_show_right_arc(); + hud_show_left_arc(); + } + + // draw the center of the reticle + if ( hud_gauge_active(HUD_CENTER_RETICLE) ) { + hud_show_center_reticle(); + } +} + +void hudreticle_page_in() +{ + hud_frames *hfp; + + int i; + for ( i = 0; i < NUM_RETICLE_ANIS; i++ ) { + hfp = &Reticle_gauges[i]; + bm_page_in_aabitmap( hfp->first_frame, hfp->num_frames); + } + +} \ No newline at end of file diff --git a/src/hud/hudshield.cpp b/src/hud/hudshield.cpp new file mode 100644 index 0000000..dabd353 --- /dev/null +++ b/src/hud/hudshield.cpp @@ -0,0 +1,845 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDshield.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C file for the display and management of the HUD shield + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 12 8/27/99 10:36a Dave + * Impose a 2% penalty for hitting the shield balance key. + * + * 11 8/23/99 11:34a Dave + * Fixed shield intensity rendering problems. + * + * 10 8/01/99 12:39p Dave + * Added HUD contrast control key (for nebula). + * + * 9 7/22/99 4:00p Dave + * Fixed beam weapon muzzle glow rendering. Externalized hud shield info. + * + * 8 6/10/99 3:43p Dave + * Do a better job of syncing text colors to HUD gauges. + * + * 7 1/07/99 9:06a Jasen + * coords... + * + * 6 12/30/98 8:57a Jasen + * updated coords for hi res + * + * 5 12/28/98 3:17p Dave + * Support for multiple hud bitmap filenames for hi-res mode. + * + * 4 12/21/98 5:02p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 3 12/14/98 1:15p Jasen + * added new HUD shield gauges + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 35 9/19/98 3:11p Adam + * Added new hardcoded values for Hud_shield_filenames + * + * 34 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 33 5/17/98 3:32p Lawrance + * Make shield gauge more readable when flashing + * + * 32 4/25/98 5:39p Dave + * Removed an unneeded assert. + * + * 31 4/25/98 3:56p Mike + * Make player's shield icon flash when Fred tells it to. + * + * 30 4/25/98 2:00p Dave + * Installed a bunch of multiplayer context help screens. Reworked ingame + * join ship select screen. Fix places where network timestamps get hosed. + * + * 29 4/21/98 12:19a Allender + * only play equalize shield sound if player equalizes shields + * + * 28 3/26/98 5:26p John + * added new paging code. nonfunctional. + * + * 27 3/21/98 3:35p Lawrance + * Tweak position of numeric integrity for target + * + * 26 3/14/98 4:59p Lawrance + * Flash shield/ship icons when ships are hit + * + * 25 3/02/98 5:42p John + * Removed WinAVI stuff from Freespace. Made all HUD gauges wriggle from + * afterburner. Made gr_set_clip work good with negative x &y. Made + * model_caching be on by default. Made each cached model have it's own + * bitmap id. Made asteroids not rotate when model_caching is on. + * + * 24 2/22/98 12:19p John + * Externalized some strings + * + * 23 2/12/98 4:58p Lawrance + * Change to new flashing method. + * + * 22 1/12/98 11:16p Lawrance + * Wonderful HUD config. + * + * 21 1/08/98 4:36p Lawrance + * Fix bug in shield drawing code. + * + * 20 1/05/98 9:38p Lawrance + * Implement flashing HUD gauges. + * + * 19 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 18 12/29/97 9:48p Mike + * Prevent indexing before array start when quadrant_num = -1. + * + * 17 12/01/97 12:27a Lawrance + * redo default alpha color for HUD, make it easy to modify in the future + * + * 16 11/18/97 5:58p Lawrance + * flash escort view info when that ship is taking hits + * + * 15 11/18/97 1:21p Mitri + * ALAN: be sure to only draw shield icons for targets that are ships + * + * 14 11/17/97 6:37p Lawrance + * new gauges: extended target view, new lock triangles, support ship view + * + * 13 11/13/97 10:46p Lawrance + * implemented new escort view, damage view and weapons + * + * 12 11/12/97 9:42a Lawrance + * show player ship integrity above shield icon + * + * 11 11/11/97 5:06p Lawrance + * fix bug with flashing frequency of hull + * + * 10 11/09/97 11:27p Lawrance + * move target shield icon closer to center + * + * 9 11/09/97 4:39p Lawrance + * don't draw mini ship icon anymore + * + * 8 11/08/97 11:08p Lawrance + * implement new "mini-shield" view that sits near bottom of reticle + * + * 7 11/05/97 11:21p Lawrance + * implement dynamic alpha on the shields + * + * 6 11/04/97 8:34p Lawrance + * fix warning: remove unused variable + * + * 5 11/04/97 7:49p Lawrance + * integrating new HUD reticle and shield icons + * + * 4 10/24/97 5:51p Lawrance + * don't show shield % if ship has no shields + * + * 3 9/03/97 4:32p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 2 8/25/97 12:24a Lawrance + * implemented HUD shield management + * + * 1 8/24/97 10:31p Lawrance + * + * $NoKeywords: $ + */ + +#include "2d.h" +#include "object.h" +#include "hud.h" +#include "hudtarget.h" +#include "hudtargetbox.h" +#include "hudets.h" +#include "player.h" +#include "gamesnd.h" +#include "freespace.h" +#include "bmpman.h" +#include "timer.h" +#include "hudshield.h" +#include "hudescort.h" +#include "emp.h" +#include "multi.h" + +#define NUM_SHIELD_LEVELS 8 + +#define SHIELD_TRANSFER_PERCENT 0.083f // 1/12 total shield strength + +#define SHIELD_HIT_DURATION_SHORT 300 // time a shield quadrant flashes after being hit +#define SHIELD_FLASH_INTERVAL_FAST 200 // time between shield quadrant flashes + +// now read in from hud.tbl +#define MAX_SHIELD_ICONS 40 +int Hud_shield_filename_count = 0; +char Hud_shield_filenames[MAX_SHIELD_ICONS][MAX_FILENAME_LEN]; + +char Shield_mini_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "targhit1", + "targhit1" +}; + +hud_frames Shield_gauges[MAX_SHIELD_ICONS]; + +static int Player_shield_coords[GR_NUM_RESOLUTIONS][2] = +{ + { // GR_640 + 396, 379 + }, + { // GR_1024 + 634, 670 + } +}; + +static int Target_shield_coords[GR_NUM_RESOLUTIONS][2] = +{ + { // GR_640 + 142, 379 + }, + { // GR_1024 + 292, 670 + } +}; + +static int Hud_shield_inited = 0; + +int Shield_mini_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 305, 291 + }, + { // GR_1024 + 497, 470 + } +}; + +// draw on the mini shield icon what the ship integrity is +int Hud_mini_3digit[GR_NUM_RESOLUTIONS][3] = { + { // GR_640 + 310, 298, 0 + }, + { // GR_1024 + 502, 477, 0 + } +}; +int Hud_mini_2digit[GR_NUM_RESOLUTIONS][3] = { + { // GR_640 + 213, 298, 2 + }, + { // GR_1024 + 346, 477, 2 + } +}; +int Hud_mini_1digit[GR_NUM_RESOLUTIONS][3] = { + { // GR_640 + 316, 298, 6 + }, + { // GR_1024 + 511, 477, 6 + } +}; +int Hud_mini_base[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 310, 298 + }, + { // GR_1024 + 502, 477 + } +}; + +int Shield_mini_loaded = 0; +hud_frames Shield_mini_gauge; + +#define SHIELD_HIT_PLAYER 0 +#define SHIELD_HIT_TARGET 1 +static shield_hit_info Shield_hit_data[2]; + +// translate between clockwise-from-top shield quadrant ordering to way quadrants are numbered in the game +ubyte Quadrant_xlate[4] = {1,0,2,3}; + +void hud_shield_game_init() +{ + char name[MAX_FILENAME_LEN+1] = ""; + + // read in hud.tbl + read_file_text("hud.tbl"); + reset_parse(); + + Hud_shield_filename_count = 0; + required_string("#Shield Icons Begin"); + while(!optional_string("#End")){ + required_string("$Shield:"); + + stuff_string(name, F_NAME, NULL); + + // maybe store + Assert(Hud_shield_filename_count < MAX_SHIELD_ICONS); + if(Hud_shield_filename_count < MAX_SHIELD_ICONS){ + strcpy(Hud_shield_filenames[Hud_shield_filename_count++], name); + } + } +} + +// called at the start of each level from HUD_init. Use Hud_shield_init so we only init Shield_gauges[] once. +void hud_shield_level_init() +{ + int i; + + hud_shield_hit_reset(1); // reset for the player + + if ( Hud_shield_inited ) { + return; + } + + for ( i = 0; i < MAX_SHIELD_ICONS; i++ ) { + Shield_gauges[i].first_frame = -1; + Shield_gauges[i].num_frames = 0; + } + + Hud_shield_inited = 1; + + if ( !Shield_mini_loaded ) { + Shield_mini_gauge.first_frame = bm_load_animation(Shield_mini_fname[gr_screen.res], &Shield_mini_gauge.num_frames); + if ( Shield_mini_gauge.first_frame == -1 ) { + Warning(LOCATION, "Could not load in the HUD shield ani: Shield_mini_fname[gr_screen.res]\n"); + return; + } + Shield_mini_loaded = 1; + } +} + +int hud_shield_maybe_flash(int gauge, int target_index, int shield_offset) +{ + int flashed = 0; + shield_hit_info *shi; + + shi = &Shield_hit_data[target_index]; + + if ( !timestamp_elapsed(shi->shield_hit_timers[shield_offset]) ) { + if ( timestamp_elapsed(shi->shield_hit_next_flash[shield_offset]) ) { + shi->shield_hit_next_flash[shield_offset] = timestamp(SHIELD_FLASH_INTERVAL_FAST); + shi->shield_show_bright ^= (1<shield_show_bright & (1<type != OBJ_SHIP ) + return; + + sp = &Ships[objp->instance]; + sip = &Ship_info[sp->ship_info_index]; + + if ( sip->shield_icon_index == 255 ) { + return; + } + + if (objp == Player_obj) { + hud_set_gauge_color(HUD_PLAYER_SHIELD_ICON); + } else { + hud_set_gauge_color(HUD_TARGET_SHIELD_ICON); + } + + // load in shield frames if not already loaded + Assert(sip->shield_icon_index >= 0 && sip->shield_icon_index < Hud_shield_filename_count); + sgp = &Shield_gauges[sip->shield_icon_index]; + + if ( sgp->first_frame == -1 ) { + sgp->first_frame = bm_load_animation(Hud_shield_filenames[sip->shield_icon_index], &sgp->num_frames); + if ( sgp->first_frame == -1 ) { + Warning(LOCATION, "Could not load in the HUD shield ani: %s\n", Hud_shield_filenames[sip->shield_icon_index]); + return; + } + } + + if ( objp == Player_obj ) { + sx = Player_shield_coords[gr_screen.res][0]; + sy = Player_shield_coords[gr_screen.res][1]; + } else { + sx = Target_shield_coords[gr_screen.res][0]; + sy = Target_shield_coords[gr_screen.res][1]; + } + + sx += fl2i(HUD_offset_x); + sy += fl2i(HUD_offset_y); + + // draw the ship first + if ( objp == Player_obj ) { + hud_shield_maybe_flash(HUD_PLAYER_SHIELD_ICON, SHIELD_HIT_PLAYER, HULL_HIT_OFFSET); + } else { + hud_shield_maybe_flash(HUD_TARGET_SHIELD_ICON, SHIELD_HIT_TARGET, HULL_HIT_OFFSET); + } + + GR_AABITMAP(sgp->first_frame, sx, sy); + + // draw the four quadrants + // + // Draw shield quadrants at one of NUM_SHIELD_LEVELS + max_shield = sip->shields/4.0f; + + for ( i = 0; i < 4; i++ ) { + + if ( objp->flags & OF_NO_SHIELDS ) { + break; + } + + if ( objp->shields[Quadrant_xlate[i]] < 0.1f ) { + continue; + } + + range = max(HUD_COLOR_ALPHA_MAX, HUD_color_alpha + 4); + hud_color_index = fl2i( (objp->shields[Quadrant_xlate[i]] / max_shield) * range + 0.5); + Assert(hud_color_index >= 0 && hud_color_index <= range); + + if ( hud_color_index < 0 ) { + hud_color_index = 0; + } + if ( hud_color_index >= HUD_NUM_COLOR_LEVELS ) { + hud_color_index = HUD_NUM_COLOR_LEVELS - 1; + } + + int flash=0; + if ( objp == Player_obj ) { + flash = hud_shield_maybe_flash(HUD_PLAYER_SHIELD_ICON, SHIELD_HIT_PLAYER, i); + } else { + flash = hud_shield_maybe_flash(HUD_TARGET_SHIELD_ICON, SHIELD_HIT_TARGET, i); + } + + if ( !flash ) { + // gr_set_color_fast(&HUD_color_defaults[hud_color_index]); + if ( objp == Player_obj ) { + hud_set_gauge_color(HUD_PLAYER_SHIELD_ICON, hud_color_index); + } else { + hud_set_gauge_color(HUD_TARGET_SHIELD_ICON, hud_color_index); + } + + GR_AABITMAP(sgp->first_frame+i+1, sx, sy); + } + } + + // hud_set_default_color(); +} + +// called at beginning of level to page in all ship icons +// used in this level +void hud_ship_icon_page_in(ship_info *sip) +{ + hud_frames *sgp; + + if ( sip->shield_icon_index == 255 ) { + return; + } + + // load in shield frames if not already loaded + Assert(sip->shield_icon_index >= 0 && sip->shield_icon_index < Hud_shield_filename_count); + sgp = &Shield_gauges[sip->shield_icon_index]; + + if ( sgp->first_frame == -1 ) { + sgp->first_frame = bm_load_animation(Hud_shield_filenames[sip->shield_icon_index], &sgp->num_frames); + if ( sgp->first_frame == -1 ) { + Warning(LOCATION, "Could not load in the HUD shield ani: %s\n", Hud_shield_filenames[sip->shield_icon_index]); + return; + } + } + + int i; + for (i=0; inum_frames; i++ ) { + bm_page_in_aabitmap(sgp->first_frame+i); + } + +} + +// ------------------------------------------------------------------ +// hud_shield_equalize() +// +// Equalize the four shield quadrants for an object +// +void hud_shield_equalize(object *objp, player *pl) +{ + float strength; + int idx; + int all_equal = 1; + + Assert(objp != NULL); + if(objp == NULL){ + return; + } + Assert(pl != NULL); + if(pl == NULL){ + return; + } + Assert(objp->type == OBJ_SHIP); + if(objp->type != OBJ_SHIP){ + return; + } + + // are all quadrants equal? + for(idx=0; idxshields[idx] != objp->shields[idx+1]){ + all_equal = 0; + break; + } + } + + // not all equal + if(!all_equal){ + strength = get_shield_strength(objp); + if ( strength != 0 ) { + // maybe impose a 2% penalty - server side and single player only + if(!MULTIPLAYER_CLIENT && (pl->shield_penalty_stamp < 0) || timestamp_elapsed_safe(pl->shield_penalty_stamp, 1000) ){ + strength *= 0.98f; + + // reset the penalty timestamp + pl->shield_penalty_stamp = timestamp(1000); + } + + set_shield_strength(objp, strength); + } + } + + // beep + if ( objp == Player_obj ){ + snd_play( &Snds[SND_SHIELD_XFER_OK] ); + } +} + +// ------------------------------------------------------------------ +// hud_augment_shield_quadrant() +// +// Transfer shield energy to a shield quadrant from the three other +// quadrants. Works by trying to transfer a fixed amount of shield +// energy from the other three quadrants, taking the same percentage +// from each quadrant. +// +// input: objp => object to perform shield transfer on +// direction => which quadrant to augment: +// 0 - right +// 1 - top +// 2 - bottom +// 3 - left +// +void hud_augment_shield_quadrant(object *objp, int direction) +{ + float full_shields, xfer_amount, energy_avail, percent_to_take, delta; + float max_quadrant_val; + int i; + + Assert(direction >= 0 && direction < 4); + Assert(objp->type == OBJ_SHIP); + full_shields = Ship_info[Ships[objp->instance].ship_info_index].shields; + + xfer_amount = full_shields * SHIELD_TRANSFER_PERCENT; + max_quadrant_val = full_shields/4.0f; + + if ( (objp->shields[direction] + xfer_amount) > max_quadrant_val ) + xfer_amount = max_quadrant_val - objp->shields[direction]; + + Assert(xfer_amount >= 0); + if ( xfer_amount == 0 ) { + // TODO: provide a feedback sound + return; + } + else { + snd_play( &Snds[SND_SHIELD_XFER_OK] ); + } + + energy_avail = 0.0f; + for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) { + if ( i == direction ) + continue; + energy_avail += objp->shields[i]; + } + + percent_to_take = xfer_amount/energy_avail; + if ( percent_to_take > 1.0f ) + percent_to_take = 1.0f; + + for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) { + if ( i == direction ) + continue; + delta = percent_to_take * objp->shields[i]; + objp->shields[i] -= delta; + Assert(objp->shields[i] >= 0 ); + objp->shields[direction] += delta; + if ( objp->shields[direction] > max_quadrant_val ) + objp->shields[direction] = max_quadrant_val; + } +} + +// Try to find a match between filename and the names inside +// of Hud_shield_filenames. This will provide us with an +// association of ship class to shield icon information. +void hud_shield_assign_info(ship_info *sip, char *filename) +{ + ubyte i; + + for ( i = 0; i < Hud_shield_filename_count; i++ ) { + if ( !stricmp(filename, Hud_shield_filenames[i]) ) { + sip->shield_icon_index = i; + } + } +} + +void hud_show_mini_ship_integrity(object *objp, int x_force, int y_force) +{ + char text_integrity[64]; + int numeric_integrity; + float p_target_integrity,initial_hull; + int nx, ny; + + initial_hull = Ship_info[Ships[objp->instance].ship_info_index].initial_hull_strength; + if ( initial_hull <= 0 ) { + Int3(); // illegal initial hull strength + p_target_integrity = 0.0f; + } else { + p_target_integrity = objp->hull_strength / initial_hull; + if (p_target_integrity < 0){ + p_target_integrity = 0.0f; + } + } + + numeric_integrity = fl2i(p_target_integrity*100 + 0.5f); + if(numeric_integrity > 100){ + numeric_integrity = 100; + } + // Assert(numeric_integrity <= 100); + + // base coords + nx = (x_force == -1) ? Hud_mini_base[gr_screen.res][0] : x_force; + ny = (y_force == -1) ? Hud_mini_base[gr_screen.res][1] : y_force; + + // 3 digit hull strength + if ( numeric_integrity == 100 ) { + nx += Hud_mini_3digit[gr_screen.res][2]; + } + // 2 digit hull strength + else if ( numeric_integrity < 10 ) { + nx += Hud_mini_1digit[gr_screen.res][2]; + } + // 1 digit hull strength + else { + nx += Hud_mini_2digit[gr_screen.res][2]; + } + + if ( numeric_integrity == 0 ) { + if ( p_target_integrity > 0 ) { + numeric_integrity = 1; + } + } + + nx += fl2i( HUD_offset_x ); + ny += fl2i( HUD_offset_y ); + + sprintf(text_integrity, "%d", numeric_integrity); + if ( numeric_integrity < 100 ) { + hud_num_make_mono(text_integrity); + } + + gr_string(nx, ny, text_integrity); +} + +// Draw the miniature shield icon that is drawn near the reticle +void hud_shield_show_mini(object *objp, int x_force, int y_force, int x_hull_offset, int y_hull_offset) +{ + float max_shield; + int hud_color_index, range, frame_offset; + int sx, sy, i; + ship *sp; + ship_info *sip; + shield_hit_info *shi; + + shi = &Shield_hit_data[SHIELD_HIT_TARGET]; + + if ( objp->type != OBJ_SHIP ) { + return; + } + + sp = &Ships[objp->instance]; + sip = &Ship_info[sp->ship_info_index]; + + hud_set_gauge_color(HUD_TARGET_MINI_ICON); + + if (!Shield_mini_loaded) + return; + + sx = (x_force == -1) ? Shield_mini_coords[gr_screen.res][0]+fl2i(HUD_offset_x) : x_force; + sy = (y_force == -1) ? Shield_mini_coords[gr_screen.res][1]+fl2i(HUD_offset_y) : y_force; + + // draw the ship first + hud_shield_maybe_flash(HUD_TARGET_MINI_ICON, SHIELD_HIT_TARGET, HULL_HIT_OFFSET); + hud_show_mini_ship_integrity(objp,x_force + x_hull_offset,y_force + y_hull_offset); + + // draw the four quadrants + // Draw shield quadrants at one of NUM_SHIELD_LEVELS + max_shield = sip->shields/4.0f; + + for ( i = 0; i < 4; i++ ) { + + if ( objp->flags & OF_NO_SHIELDS ) { + break; + } + + if ( objp->shields[Quadrant_xlate[i]] < 0.1f ) { + continue; + } + + if ( hud_shield_maybe_flash(HUD_TARGET_MINI_ICON, SHIELD_HIT_TARGET, i) ) { + frame_offset = i+4; + } else { + frame_offset = i; + } + + range = HUD_color_alpha; + hud_color_index = fl2i( (objp->shields[Quadrant_xlate[i]] / max_shield) * range + 0.5); + Assert(hud_color_index >= 0 && hud_color_index <= range); + + if ( hud_color_index < 0 ) { + hud_color_index = 0; + } + if ( hud_color_index >= HUD_NUM_COLOR_LEVELS ) { + hud_color_index = HUD_NUM_COLOR_LEVELS - 1; + } + + if ( hud_gauge_maybe_flash(HUD_TARGET_MINI_ICON) == 1) { + // hud_set_bright_color(); + hud_set_gauge_color(HUD_TARGET_MINI_ICON, HUD_C_BRIGHT); + } else { + // gr_set_color_fast(&HUD_color_defaults[hud_color_index]); + hud_set_gauge_color(HUD_TARGET_MINI_ICON, hud_color_index); + } + + GR_AABITMAP(Shield_mini_gauge.first_frame + frame_offset, sx, sy); + } + + // hud_set_default_color(); +} + +// reset the shield_hit_info data structure +void shield_info_reset(shield_hit_info *shi) +{ + int i; + + shi->shield_hit_status = 0; + shi->shield_show_bright = 0; + for ( i = 0; i < NUM_SHIELD_HIT_MEMBERS; i++ ) { + shi->shield_hit_timers[i] = 1; + shi->shield_hit_next_flash[i] = 1; + } +} + +// reset the timers and hit flags for the shield gauges +// +// This needs to be called whenever the player selects a new target +// +// input: player => optional parameter (default value 0). This is to indicate that player shield hit +// info should be reset. This is normally not the case. +// is for the player's current target +void hud_shield_hit_reset(int player) +{ + shield_hit_info *shi; + + if (player) { + shi = &Shield_hit_data[SHIELD_HIT_PLAYER]; + } else { + shi = &Shield_hit_data[SHIELD_HIT_TARGET]; + } + + shield_info_reset(shi); +} + +// called once per frame to update the state of Shield_hit_status based on the Shield_hit_timers[] +void hud_shield_hit_update() +{ + int i, j, limit; + + limit = 1; + if ( Player_ai->target_objnum >= 0 ) { + limit = 2; + } + + for ( i = 0; i < limit; i++ ) { + for ( j = 0; j < NUM_SHIELD_HIT_MEMBERS; j++ ) { + if ( timestamp_elapsed(Shield_hit_data[i].shield_hit_timers[j]) ) { + Shield_hit_data[i].shield_hit_status &= ~(1< object pointer for ship that has been hit +// quadrant => quadrant of shield getting hit (-1 if no shield is present) +void hud_shield_quadrant_hit(object *objp, int quadrant) +{ + shield_hit_info *shi; + int num; + + if ( objp->type != OBJ_SHIP ) + return; + + hud_escort_ship_hit(objp, quadrant); + hud_gauge_popup_start(HUD_TARGET_MINI_ICON); + + if ( OBJ_INDEX(objp) == Player_ai->target_objnum ) { + shi = &Shield_hit_data[SHIELD_HIT_TARGET]; + } else if ( objp == Player_obj ) { + shi = &Shield_hit_data[SHIELD_HIT_PLAYER]; + } else { + return; + } + + if ( quadrant >= 0 ) { + num = Quadrant_xlate[quadrant]; + shi->shield_hit_timers[num] = timestamp(300); + } else { + shi->shield_hit_timers[HULL_HIT_OFFSET] = timestamp(SHIELD_HIT_DURATION_SHORT); + hud_targetbox_start_flash(TBOX_FLASH_HULL); + } +} + + +void hudshield_page_in() +{ + bm_page_in_aabitmap( Shield_mini_gauge.first_frame, Shield_mini_gauge.num_frames ); +} diff --git a/src/hud/hudsquadmsg.cpp b/src/hud/hudsquadmsg.cpp new file mode 100644 index 0000000..72f731b --- /dev/null +++ b/src/hud/hudsquadmsg.cpp @@ -0,0 +1,2737 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDsquadmsg.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * File to control sqaudmate messaging + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 16 9/06/99 10:45a Andsager + * Add freighter to player override of protected status. + * + * 15 9/06/99 10:32a Andsager + * Allow attack of protected fighter, bomber, freighters, by player's + * orders. + * + * 14 8/26/99 8:51p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 13 7/30/99 10:31p Dave + * Added comm menu to the configurable hud files. + * + * 12 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 11 6/16/99 10:20a Dave + * Added send-message-list sexpression. + * + * 10 6/09/99 9:53a Andsager + * 1st pass at grey menu items when no ships/wings/fighters accepting + * orders. + * + * 9 4/23/99 12:01p Johnson + * Added SIF_HUGE_SHIP + * + * 8 4/16/99 5:54p Dave + * Support for on/off style "stream" weapons. Real early support for + * target-painting lasers. + * + * 7 3/30/99 5:40p Dave + * Fixed reinforcements for TvT in multiplayer. + * + * 6 3/28/99 5:58p Dave + * Added early demo code. Make objects move. Nice and framerate + * independant, but not much else. Don't use yet unless you're me :) + * + * 5 1/07/99 9:07a Jasen + * HUD coords + * + * 4 12/28/98 3:17p Dave + * Support for multiple hud bitmap filenames for hi-res mode. + * + * 3 12/21/98 5:02p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 198 9/11/98 2:05p Allender + * make reinforcements work correctly in multiplayer games. There still + * may be a team vs team issue that I haven't thought of yet :-( + * + * 197 8/28/98 3:28p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 196 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 195 6/30/98 2:17p Dave + * Revised object update system. Removed updates for all weapons. Put + * button info back into control info packet. + * + * 194 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 193 5/26/98 11:54a Allender + * fix multiplayer problems and sexpression crash + * + * 192 5/24/98 4:27p Allender + * fix bug when determine who could message in multiplayer + * + * 191 5/23/98 2:34a Lawrance + * Fix problems with HUD squad messaging, don't save/restore bindings + * + * 190 5/22/98 12:41a Allender + * don't save/restore key presses in comm menu + * + * 189 5/21/98 9:38p Allender + * don't save/clear key bindings + * + * 188 5/21/98 3:32p Allender + * don't allow comm menu in observer mode + * + * 187 5/19/98 12:19p Mike + * Cheat codes! + * + * 186 5/18/98 10:08a Lawrance + * increase MSG_KEY_EAT_TIME to 300ms + * + * 185 5/18/98 12:41a Allender + * fixed subsystem problems on clients (i.e. not reporting properly on + * damage indicator). Fixed ingame join problem with respawns. minor + * comm menu stuff + * + * 184 5/13/98 5:08p Allender + * fix code in which sometimes wings wouldn't respond in multiplayer when + * doing a message all fighters + * + * 183 5/08/98 4:38p Allender + * always allow player ships to count when counting fighters for + * messaging. Terran command will now always issue the shooting at + * friendlies message + * + * 182 5/08/98 2:11p Mike + * Add "/Repair Subsys" to "Rearm" option in Comm menu. + * + * 181 5/06/98 2:57p Allender + * always allow rearm ship to be called in + * + * 180 5/05/98 2:04a Mike + * Fix bug in support ship code. + * + * 179 5/05/98 1:41a Mike + * Improve support ship availability. + * + * 178 5/04/98 12:59a Allender + * players who are traitors shouldn't be allowed to rearm or use messaging + * shortcuts + * + * 177 5/04/98 12:39a Allender + * make page up and page down only active when > 10 items on menu + * + * 176 4/29/98 10:56p Allender + * don't allow shortcuts in mutliplayer when player cannot message (except + * for rearm repair) + * + * 175 4/23/98 10:06a Allender + * don't use the word "player" in event log for rearm event. Send + * shipname instead (players only) + * + * 174 4/23/98 9:15a Allender + * make rearm shortcut work for clients + * + * 173 4/23/98 1:49a Allender + * major rearm/repair fixes for multiplayer. Fixed respawning of AI ships + * to not respawn until 5 seconds after they die. Send escort information + * to ingame joiners + * + * 172 4/22/98 4:59p Allender + * new multiplayer dead popup. big changes to the comm menu system for + * team vs. team. Start of debriefing stuff for team vs. team Make form + * on my wing work with individual ships who have high priority orders + * + * 171 4/21/98 12:15a Allender + * don't allow observers to use shortcut messaging keys + * + * 170 4/20/98 12:36a Mike + * Make team vs. team work when player is hostile. Several targeting + * problems. + * + * 169 4/13/98 12:51p Allender + * made countermeasure succeed indicator work in multiplayer. Make rearm + * shortcut work more appropriately. + * + * 168 4/10/98 2:42p Johnson + * (from allender) when sending wing command, don't assert if ship to + * send message not found -- don't send message. Allow rearm message + * shortcut even if comm destroyed + * + * 167 4/10/98 2:39p Johnson + * + * 166 4/10/98 12:47p Allender + * changed working on replay popup. Don't reference repair in comm menu. + * Added Shift-R for repair me + * + * 165 4/09/98 12:35p Allender + * disallow messaging to departing wings and departing/dying ships + * + * 164 4/08/98 4:06p Allender + * make selection of wing Player team based, not TEAM_FRIENDLY. + * + * 163 4/07/98 5:30p Lawrance + * Player can't send/receive messages when comm is destroyed. Garble + * messages when comm is damaged. + * + * 162 4/07/98 1:53p Lawrance + * Fix uninitialized data bug. + * + * 161 4/06/98 12:11a Allender + * prevent the comm menu keys from being held over after menu goes away + * + * 160 4/05/98 3:06p Allender + * don't allow ships/wings to act on orders which they shouldn't receive + * + * 159 4/03/98 12:17a Allender + * new sexpression to detect departed or destroyed. optionally disallow + * support ships. Allow docking with escape pods + * + * 158 4/02/98 5:50p Dave + * Put in support for standard comm messages to get sent to netplayers as + * well as ai ships. Make critical button presses not get evaluated on the + * observer. + * + * $NoKeywords: $ +*/ + + +#include "freespace.h" +#include "2d.h" +#include "hud.h" +#include "ship.h" +#include "player.h" +#include "key.h" +#include "hudtarget.h" +#include "timer.h" +#include "hudsquadmsg.h" +#include "controlsconfig.h" +#include "parselo.h" +#include "aigoals.h" +#include "missionparse.h" +#include "sexp.h" +#include "linklist.h" +#include "missionlog.h" +#include "missionmessage.h" +#include "hudtarget.h" +#include "gamesnd.h" +#include "sound.h" +#include "missionparse.h" +#include "multimsgs.h" +#include "multiutil.h" +#include "bmpman.h" +#include "hudtargetbox.h" +#include "multi_pmsg.h" +#include "subsysdamage.h" +#include "emp.h" + +// defines for different modes in the squad messaging system + +#define SM_MODE_TYPE_SELECT 1 //am I going to message a ship or a wing +#define SM_MODE_SHIP_SELECT 2 //choosing actual ship +#define SM_MODE_WING_SELECT 3 //choosing actual wing +#define SM_MODE_SHIP_COMMAND 4 //which command to send to a ship +#define SM_MODE_WING_COMMAND 5 //which command to send to a wing +#define SM_MODE_REINFORCEMENTS 6 //call for reinforcements +#define SM_MODE_REPAIR_REARM 7 //repair/rearm player ship +#define SM_MODE_REPAIR_REARM_ABORT 8 //abort repair/rearm of player ship +#define SM_MODE_ALL_FIGHTERS 9 //message all fighters/bombers + +#define DEFAULT_MSG_TIMEOUT (8 * 1000) // number of seconds * 1000 to get milliseconds +#define MSG_KEY_EAT_TIME (300) + +LOCAL int Squad_msg_mode; // current mode that the messaging system is in +LOCAL int Msg_key_used; // local variable which tells if the key being processed + // with the messaging system was actually used +LOCAL int Msg_key; // global which indicates which key was currently pressed +LOCAL int Msg_mode_timestamp; +LOCAL int Msg_instance; // variable which holds ship/wing instance to send the message to +LOCAL int Msg_shortcut_command; // holds command when using a shortcut key +LOCAL int Msg_target_objnum; // id of the current target of the player +LOCAL ship_subsys *Msg_targeted_subsys;// pointer to current subsystem which is targeted +//#ifndef NDEBUG +LOCAL int Msg_enemies; // tells us whether or not to message enemy ships or friendlies +//#endif + +LOCAL int Msg_eat_key_timestamp; // used to temporarily "eat" keys + +// defined to position the messaging box +int Mbox_item_h[GR_NUM_RESOLUTIONS] = { + 10, + 10 +}; +int Mbox_item_xoffset[GR_NUM_RESOLUTIONS] = { + 17, + 17 +}; + +// top of the message box gauge +int Mbox_top_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 445, 5 + }, + { // GR_1024 + 827, 5 + } +}; + +int Mbox_bmap_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 445, 17 + }, + { // GR_1024 + 827, 17 + } +}; + +// squadmsg menu pgup and pgdn +int Menu_pgup_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 590, 9 + }, + { // GR_1024 + 937, 9 + } +}; +int Menu_pgdn_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 590, 120 + }, + { // GR_1024 + 937, 120 + } +}; + +// ----------- +// following defines/vars are used to build menus that are used in messaging mode + +typedef struct mmode_item { + int instance; // instance in Ships/Wings array of this menu item + int active; // active items are in bold text -- inactive items greyed out + char text[NAME_LENGTH]; // text to display on the menu +} mmode_item; + +#define MAX_MENU_ITEMS 50 // max number of items in the menu +#define MAX_MENU_DISPLAY 10 // max number that can be displayed + +mmode_item MsgItems[MAX_MENU_ITEMS]; +int Num_menu_items = -1; // number of items for a message menu +int First_menu_item= -1; // index of first item in the menu + +// ----------- +// following set of vars/defines are used to store/restore key bindings for keys that +// are used in messaging mode + +// array to temporarily store key bindings that will be in use for the messaging +// system +typedef struct key_store { + int option_num; // which element in the Control_config array is this + int id; // which id (1 or 2) is this key. + int key_value; // which key value to put there. +} key_store; + +#define MAX_KEYS_NO_SCROLL 10 +#define MAX_KEYS_USED 12 // maximum number of keys used for the messaging system + +key_store key_save[MAX_KEYS_USED]; // array to save the key information during messaging mode +int num_keys_saved = 0; // number of keys that are saved. + +// next array is the array of MAX_KEYS_USED size which are the keys to use for messaging mode + +int keys_used[] = { KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, + KEY_PAGEUP, KEY_PAGEDOWN }; + +#define ID1 1 +#define ID2 2 + +// following are defines and character strings that are used as part of messaging mode + +#define TYPE_SHIP_ITEM 0 +#define TYPE_WING_ITEM 1 +#define TYPE_ALL_FIGHTERS_ITEM 2 +#define TYPE_REINFORCEMENT_ITEM 3 +#define TYPE_REPAIR_REARM_ITEM 4 +#define TYPE_REPAIR_REARM_ABORT_ITEM 5 + +#define NUM_TYPE_SELECT 6 + +char *type_select_str(int n) +{ + #if NUM_TYPE_SELECT != 6 + #error type_select_Str is not up to date + #endif + + switch(n) { + case TYPE_SHIP_ITEM: + return XSTR( "Ships", 293); + case TYPE_WING_ITEM: + return XSTR( "Wings", 294); + case TYPE_ALL_FIGHTERS_ITEM: + return XSTR( "All Fighters", 295); + case TYPE_REINFORCEMENT_ITEM: + return XSTR( "Reinforcements", 296); + case TYPE_REPAIR_REARM_ITEM: + return XSTR( "Rearm/Repair Subsys", 297); + case TYPE_REPAIR_REARM_ABORT_ITEM: + return XSTR( "Abort Rearm", 298); + } + + return NULL; +} + +// data structure to hold character string of commands for comm menu +typedef struct comm_order { + int value; // used to match which command to display on the menu +} comm_order; + +// note: If you change this table at all, keep it in sync with version in IgnoreOrdersDlg.cpp +// Also make sure you update comm_order_menu_text below this. +// Also make sure you update MAX_SHIP_ORDERS in HUDsquadmsg.h +comm_order Comm_orders[MAX_SHIP_ORDERS] = { + ATTACK_TARGET_ITEM, + DISABLE_TARGET_ITEM, + DISARM_TARGET_ITEM, + DISABLE_SUBSYSTEM_ITEM, + PROTECT_TARGET_ITEM, + IGNORE_TARGET_ITEM, + FORMATION_ITEM, + COVER_ME_ITEM, + ENGAGE_ENEMY_ITEM, + CAPTURE_TARGET_ITEM, + REARM_REPAIR_ME_ITEM, + ABORT_REARM_REPAIR_ITEM, + DEPART_ITEM, +}; + +// Text to display on the menu +// Given an index into the Comm_orders array, return the text associated with it. +// MUST BE 1:1 with Comm_orders. +char *comm_order_menu_text(int index) +{ + switch( index ) { + case 0: return XSTR( "Destroy my target", 299); break; + case 1: return XSTR( "Disable my target", 300); break; + case 2: return XSTR( "Disarm my target", 301); break; + case 3: return XSTR( "Destroy subsystem", 302); break; + case 4: return XSTR( "Protect my target", 303); break; + case 5: return XSTR( "Ignore my target", 304); break; + case 6: return XSTR( "Form on my wing", 305); break; + case 7: return XSTR( "Cover me", 306); break; + case 8: return XSTR( "Engage enemy", 307); break; + case 9: return XSTR( "Capture my target", 308); break; + case 10: return XSTR( "Rearm me", 309); break; + case 11: return XSTR( "Abort rearm", 310); break; + case 12: return XSTR( "Depart", 311); break; + default: + Assert(0); + } + return NULL; +} + +// Text to display on the messaging menu when using the shortcut keys +char *comm_order_hotkey_text( int index ) +{ + int i; + + for (i = 0; i < MAX_SHIP_ORDERS; i++ ) { + if ( Comm_orders[i].value == index ) + return comm_order_menu_text(i); + } + + Int3(); + return NULL; +} + +// a define of who can receive message +#define CAN_MESSAGE (SIF_FIGHTER | SIF_BOMBER | SIF_CRUISER | SIF_FREIGHTER | SIF_TRANSPORT | SIF_CAPITAL | SIF_SUPPORT | SIF_SUPERCAP | SIF_DRYDOCK | SIF_GAS_MINER | SIF_AWACS | SIF_CORVETTE) + +int squadmsg_history_index = 0; +squadmsg_history Squadmsg_history[SQUADMSG_HISTORY_MAX] = { 0 }; + +// used for Message box gauge +#define NUM_MBOX_FRAMES 3 + +static hud_frames Mbox_gauge[NUM_MBOX_FRAMES]; +static int Mbox_frames_loaded = 0; +static char *Mbox_fnames[GR_NUM_RESOLUTIONS][NUM_MBOX_FRAMES] = +{ +//XSTR:OFF + { // GR_640 + "message1", // top part of menu + "message2", // middle part + "message3" // bottom part + }, + { // GR_1024 + "message1", // top part of menu + "message2", // middle part + "message3" // bottom part + } +//XSTR:ON +}; + +static int Mbox_title_coord[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 447, 6 + }, + { // GR_1024 + 829, 6 + } +}; +static int Mbox_item_coord[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 449, 18 + }, + { // GR_1024 + 831, 18 + } +}; + +// define for trapping messages send to "all fighters" +#define MESSAGE_ALL_FIGHTERS -999 + +// forward declarations +void hud_add_issued_order(char *name, int order, char *target); +int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip = NULL ); +int hud_squadmsg_ship_order_valid( int shipnum, int order ); + +// function to set up variables needed when messaging mode is started +void hud_squadmsg_start() +{ +// int i; + + //if ( num_keys_saved < 0 ) // save the keys if they haven't been saved yet + hud_squadmsg_save_keys(); + + Msg_key = -1; + +/* + for (i=0; itarget_objnum; // save the players target object number + Msg_targeted_subsys = Player_ai->targeted_subsys; // save the players currently targted subsystem +#ifndef NDEBUG + Msg_enemies = 0; // tells us if we are messaging enemy ships +#endif + + snd_play( &Snds[SND_SQUADMSGING_ON] ); +} + +// functions which will restore all of the key binding stuff when messaging mode is done +void hud_squadmsg_end() +{ +/* + int i; + key_store *ksp; + + // move through all keys saved and restore their orignal values. + for ( i=0; ioption_num].key_id = (short) ksp->key_value; + } +*/ + + if ( message_is_playing() == FALSE ) + snd_play( &Snds[SND_SQUADMSGING_OFF] ); +} + +// function which returns true if there are fighters/bombers on the players team +// in the mission +int hud_squadmsg_count_fighters( ) +{ + int count; + int team; + object *objp; + ship *shipp; + + // set up the team to compare for messaging. In debug versions, we will allow messaging to enemies + //team = TEAM_FRIENDLY; + team = Player_ship->team; +#ifndef NDEBUG + if ( Msg_enemies ) + team = opposing_team_mask(Player_ship->team); +#endif + + count = 0; + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if ( objp->type != OBJ_SHIP ) + continue; + + shipp = &Ships[objp->instance]; + // check fighter is accepting orders + if (shipp->orders_accepted != 0) { + // be sure ship is on correct team, not the player, and is a fighter/bomber + if ( (shipp->team == team) && (objp != Player_obj) && (Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) { + return 1; + } + } + } + + return 0; +} + + +// function which counts the number of ships available for messaging. Used to determine if +// we should grey out a menu or allow a shortcut command to apply. parameter "flag" is used +// to tell us whether or not we should add the ship to a menu item or not. We include the +// flag so that we don't have to have conditions for messaging ships/wings in two places. +int hud_squadmsg_count_ships( int add_to_menu ) +{ + int count; + int team; + ship *shipp; + ship_obj *so; + + // set up the team to compare for messaging. In debug versions, we will allow messaging to enemies + //team = TEAM_FRIENDLY; + team = Player_ship->team; +#ifndef NDEBUG + if ( Msg_enemies ) + team = opposing_team_mask(Player_ship->team); +#endif + + count = 0; + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + + shipp = &Ships[Objects[so->objnum].instance]; + Assert ( shipp->objnum != -1 ); + + // ships must be able to receive a message + if ( !(Ship_info[shipp->ship_info_index].flags & CAN_MESSAGE) ) + continue; + + // must be on the same team + if ( shipp->team != team ) + continue; + + // departing or dying ships cannot be on list + if ( shipp->flags & (SF_DEPARTING|SF_DYING) ) + continue; + + // MULTI - changed to allow messaging of netplayers + + // cannot be my ship or an instructor + if ( (&Objects[so->objnum] == Player_obj) || is_instructor(&Objects[so->objnum]) ) + continue; + + // ship must be accepting ship type orders + if ( shipp->orders_accepted == 0) + continue; + + // if it is a player ship, we must be in multiplayer + if ( (Objects[so->objnum].flags & OF_PLAYER_SHIP) && !(Game_mode & GM_MULTIPLAYER) ) + continue; + + // if a messaging shortcut, be sure this ship can process the order + if ( Msg_shortcut_command != -1 ) { + if ( !(shipp->orders_accepted & Msg_shortcut_command) ) + continue; + else if ( !hud_squadmsg_ship_order_valid(Objects[so->objnum].instance, Msg_shortcut_command) ) + continue; + } + + count++; + if ( add_to_menu ) { + Assert ( Num_menu_items < MAX_MENU_ITEMS ); + strcpy( MsgItems[Num_menu_items].text, shipp->ship_name ); + MsgItems[Num_menu_items].instance = SHIP_INDEX(shipp); + MsgItems[Num_menu_items].active = 1; + Num_menu_items++; + + } + } + + // if adding to the menu and we have > 10 items, then don't allow page up and page down to be used. + if ( add_to_menu && (Num_menu_items > MAX_MENU_DISPLAY) ) + hud_squadmsg_save_keys(1); + return count; +} + +// routine to return true if a wing should be put onto the messaging menu +int hud_squadmsg_wing_valid( wing *wingp, int team ) +{ + // int player_count, j; + + // a couple of special cases to account for before adding to count (or to menu). The wing gone + // flags is firm indication to skip this particular wing. Also, skip if enemy wing + if ( (wingp->flags & WF_WING_GONE) || (wingp->current_count == 0) ) + return 0; + + // departing wings don't get attention either + if ( wingp->flags & WF_WING_DEPARTING ) + return 0; + + // sanity check on ship_index field -- if check is successful, then check the team. + Assert (wingp->ship_index[0] != -1 ); + if ( Ships[wingp->ship_index[0]].team != team ) + return 0; + + // if this wing is the players wing, and there is only one ship in the wing, then skip past it + if ( (Ships[Player_obj->instance].wingnum == WING_INDEX(wingp)) && (wingp->current_count == 1) ) + return 0; + + // check if wing commander is accepting orders + if ( Ships[wingp->ship_index[0]].orders_accepted == 0) + return 0; + + // if doing a message shortcut is being used, be sure the wing can "accept" the command. Only need + // to look at the first ship in the wing. + if ( Msg_shortcut_command != -1 ) { + if ( !(Ships[wingp->ship_index[0]].orders_accepted & Msg_shortcut_command) ) + return 0; + } + // MULTI - changed to allow messaging of netplayers + // don't count wings where all ships are player ships + /* + player_count = 0; + for ( j = 0; j < wingp->current_count; j++ ) { + if ( Objects[Ships[wingp->ship_index[j]].objnum].flags & OF_PLAYER_SHIP ) + player_count++; + } + if ( player_count == wingp->current_count ) + return 0; + */ + + return 1; +} + +// function like above, except for wings +int hud_squadmsg_count_wings( int add_to_menu ) +{ + int count, i, j; + int team; + + // set up the team to compare for messaging. In debug versions, we will allow messaging to enemies + //team = TEAM_FRIENDLY; + team = Player_ship->team; +#ifndef NDEBUG + if ( Msg_enemies ) + team = opposing_team_mask(Player_ship->team); +#endif + + count = 0; + + // add the player starting wings first + for ( i = 0; i < MAX_STARTING_WINGS; i++ ) { + int wingnum; + + wingnum = Starting_wings[i]; + if ( wingnum == -1 ) + continue; + + if ( hud_squadmsg_wing_valid(&Wings[wingnum], team) ) { + count++; + if ( add_to_menu ) { + Assert ( Num_menu_items < MAX_MENU_ITEMS ); + strcpy( MsgItems[Num_menu_items].text, Wings[wingnum].name ); + MsgItems[Num_menu_items].instance = wingnum; + MsgItems[Num_menu_items].active = 1; + Num_menu_items++; + } + } + } + + for ( i = 0; i < num_wings; i++ ) { + // if this wing is a player starting wing, skip it since we added it above + for ( j = 0; j < MAX_STARTING_WINGS; j++ ) { + if ( i == Starting_wings[j] ) + break; + } + if ( j < MAX_STARTING_WINGS ) + continue; + + if ( hud_squadmsg_wing_valid(&Wings[i], team) ) { + count++; + if ( add_to_menu ) { + Assert ( Num_menu_items < MAX_MENU_ITEMS ); + strcpy( MsgItems[Num_menu_items].text, Wings[i].name ); + MsgItems[Num_menu_items].instance = i; + MsgItems[Num_menu_items].active = 1; + Num_menu_items++; + } + } + } + return count; +} + + +// function to set the current submode in message mode -- also resets variables that +// should be reset inbetween submodes +void hud_squadmsg_do_mode( int mode ) +{ + Squad_msg_mode = mode; + Num_menu_items = -1; + First_menu_item = 0; +} + +void hud_squadmsg_page_down() +{ + if ( (First_menu_item + MAX_MENU_DISPLAY) < Num_menu_items ) { + First_menu_item += MAX_MENU_DISPLAY; + Assert ( First_menu_item < Num_menu_items ); + } +} + +void hud_squadmsg_page_up() +{ + if ( First_menu_item > 0 ) { + First_menu_item -= MAX_MENU_DISPLAY; + Assert (First_menu_item >= 0 ); + } +} + +int hud_squadmsg_get_total_keys() +{ + int num_keys_used; + + num_keys_used = MAX_KEYS_NO_SCROLL; + if ( Num_menu_items > MAX_MENU_DISPLAY ) + num_keys_used = MAX_KEYS_USED; + + return num_keys_used; +} + +// function called from high level keyboard read code to give the squadmsg code a key. +// return 1 is the key was used by the messaging code, 0 otherwise +int hud_squadmsg_read_key( int k ) +{ + int i, key_found, num_keys_used; + + num_keys_used = hud_squadmsg_get_total_keys(); + + if ( !(Player->flags & PLAYER_FLAGS_MSG_MODE) ) { + // check to see if any messaging keys are still down for some length of time + // after messaging is over. Return true for a while. + if ( !timestamp_elapsed(Msg_eat_key_timestamp) ) { + for (i = 0; i < num_keys_used; i++ ) { + if ( keyd_pressed[keys_used[i]] ) + return 1; + } + } + + return 0; + } + + key_found = 0; + for (i = 0; i < num_keys_used; i++ ) { + if ( k == keys_used[i] ) { + if ( key_down_count(k) ) { + Msg_key = k; + key_found = 1; + } + + if ( keyd_pressed[k] ) { + key_found = 1; + } + +// key_down_count(k); + break; + } + } + + if ( key_found ) + return 1; + + return 0; +} + +// function which reads the keyboard array and determines if a menu key has been hit +int hud_squadmsg_get_key() +{ + int k, i, num_keys_used; + + if ( Msg_key == -1 ) + return -1; + + k = Msg_key; + Msg_key = -1; + + num_keys_used = hud_squadmsg_get_total_keys(); + + // if the emp effect is active, never accept keypresses + if(emp_active_local()){ + return -1; + } + + for ( i = 0; i < num_keys_used; i++ ) { + if ( k == keys_used[i] ) { + Msg_key_used = 1; // this variable will extend the timer + + // use a timestamp to prevent top level key code from possibly reprocessing this key + Msg_eat_key_timestamp = timestamp(MSG_KEY_EAT_TIME); + if ( k == KEY_PAGEDOWN ) { // pageup and pagedown scroll the menu -- deal with these seperately!! + hud_squadmsg_page_down(); + return -1; + } else if ( k == KEY_PAGEUP ) { + hud_squadmsg_page_up(); + return -1; + } else if ( k == KEY_ESC ) { + hud_squadmsg_toggle(); + return -1; + } else if ( (i < Num_menu_items) && (Squad_msg_mode == SM_MODE_REINFORCEMENTS) ) // return any key if selecting reinforcement + return i; + + // play general fail sound if inactive item hit. + else if ( (i < Num_menu_items) && !(MsgItems[i].active) ) + gamesnd_play_iface(SND_GENERAL_FAIL); + + else if ( (i < Num_menu_items) && (MsgItems[i].active) ) // only return keys that are associated with menu items + return i; + + else { + Msg_key_used = 0; // if no #-key pressed for visible item, break and allow timer to + break; // to continue as if no key was pressed + } + } + } + + return -1; +} + +// function which will essentially print out the contents of the current state of the messaging +// menu. Parameters will be a title. The menu items and the number of items will be +// in global vars since they don't get recomputed every frame. +void hud_squadmsg_display_menu( char *title ) +{ + int bx, by, sx, sy, i, nitems, none_valid, messaging_allowed; + + // hud_set_bright_color(); + hud_set_gauge_color(HUD_MESSAGE_BOX, HUD_C_BRIGHT); + if ( title ) { + gr_string(Mbox_title_coord[gr_screen.res][0], Mbox_title_coord[gr_screen.res][1], title); + } + + if ( Num_menu_items < MAX_MENU_DISPLAY ) + nitems = Num_menu_items; + else { + if ( First_menu_item == 0 ) // First_menu_item == 0 means first page of items + nitems = MAX_MENU_DISPLAY; + else if ( (Num_menu_items - First_menu_item) <= MAX_MENU_DISPLAY ) // check if remaining items fit on one page + nitems = Num_menu_items - First_menu_item; + else { + nitems = MAX_MENU_DISPLAY; + } + } + + sx = Mbox_item_coord[gr_screen.res][0]; + sy = Mbox_item_coord[gr_screen.res][1]; + bx = Mbox_bmap_coords[gr_screen.res][0]; // global x-offset where bitmap gets drawn + by = Mbox_bmap_coords[gr_screen.res][1]; // global y-offset where bitmap gets drawn + + none_valid = 1; // variable to tell us whether all items in the menu are valid or not + + // use another variable to tell us whether we can message or not. + messaging_allowed = 1; + if ( (Game_mode & GM_MULTIPLAYER) && !multi_can_message(Net_player) ){ + messaging_allowed = 0; + } + + for ( i = 0; i < nitems; i++ ) { + int item_num; + char *text = MsgItems[First_menu_item+i].text; + + // blit the background + // hud_set_default_color(); + hud_set_gauge_color(HUD_MESSAGE_BOX); + if ( Mbox_gauge[1].first_frame >= 0 ) { + GR_AABITMAP(Mbox_gauge[1].first_frame, bx, by); + } + by += Mbox_item_h[gr_screen.res]; + + // set the text color + if ( MsgItems[First_menu_item+i].active ) { + // hud_set_bright_color(); + hud_set_gauge_color(HUD_MESSAGE_BOX, HUD_C_BRIGHT); + } else { + /* + dim_index = min(5, HUD_color_alpha - 2); + if ( dim_index < 0 ) { + dim_index = 0; + } + gr_set_color_fast(&HUD_color_defaults[dim_index]); + */ + + hud_set_gauge_color(HUD_MESSAGE_BOX, HUD_C_DIM); + } + + // first do the number + item_num = (i+1) % MAX_MENU_DISPLAY; + emp_hud_printf(sx, sy, EG_SQ1 + i, NOX("%1d."), item_num ); + + // then the text + emp_hud_string(sx+Mbox_item_xoffset[gr_screen.res], sy, EG_SQ1 + i, text); + + sy += Mbox_item_h[gr_screen.res]; + + // if we have at least one item active, then set the variable so we don't display any + // message about no active items + if ( MsgItems[First_menu_item+i].active ) + none_valid = 0; + } + + // maybe draw an extra line in to make room for [pgdn], or for the 'no active items' + // display + if ( !messaging_allowed || none_valid || ((First_menu_item + nitems) < Num_menu_items) || (Msg_shortcut_command != -1) ) { + // blit the background + // hud_set_default_color(); + hud_set_gauge_color(HUD_MESSAGE_BOX); + if ( Mbox_gauge[1].first_frame >= 0 ) { + + GR_AABITMAP(Mbox_gauge[1].first_frame, bx, by); + } + by += Mbox_item_h[gr_screen.res]; + } + + // draw the bottom of the frame + // hud_set_default_color(); + hud_set_gauge_color(HUD_MESSAGE_BOX); + if ( Mbox_gauge[2].first_frame >= 0 ) { + + GR_AABITMAP(Mbox_gauge[2].first_frame, bx, by); + } + + // determine if we should put the text "[more]" at top or bottom to indicate you can page up or down + hud_targetbox_start_flash(TBOX_FLASH_SQUADMSG); + hud_targetbox_maybe_flash(TBOX_FLASH_SQUADMSG); + if ( First_menu_item > 0 ) { + gr_printf( Menu_pgup_coords[gr_screen.res][0], Menu_pgup_coords[gr_screen.res][1], XSTR( "[pgup]", 312) ); + } + + if ( (First_menu_item + nitems) < Num_menu_items ) { + gr_printf( Menu_pgdn_coords[gr_screen.res][0], Menu_pgdn_coords[gr_screen.res][1], XSTR( "[pgdn]", 313)); + } + + if ( messaging_allowed ) { + if ( none_valid ){ + gr_printf( sx, by - Mbox_item_h[gr_screen.res] + 2, XSTR( "No valid items", 314)); + } else if ( !none_valid && (Msg_shortcut_command != -1) ){ + gr_printf( sx, by - Mbox_item_h[gr_screen.res] + 2, "%s", comm_order_hotkey_text(Msg_shortcut_command)); + } + } else { + // if this player is not allowed to message, then display message saying so + gr_printf( sx, by - Mbox_item_h[gr_screen.res] + 2, XSTR( "Not allowed to message", 315)); + } + +} + +// function to return true or false if the given ship can rearm, or be repaired +int hud_squadmsg_can_rearm( ship *shipp ) +{ + // player ships which turns traitor cannot rearm + if ( (shipp == Player_ship) && (Player_ship->team == TEAM_TRAITOR) ) + return 0; + + // 5/6/98 -- MWA Decided to always be able to call in support. + return 1; +} + +// calls for repair/rearm of the player ship. Checks for the presense of the support +// ship and does the appropriate action if found +void hud_squadmsg_repair_rearm( int toggle_state, object *objp) +{ + int robjnum; + object *robjp; + object *tobj; + int multi_player_num; + + // this is essentially a check for multiplayer server/client mode + // in multiplayer mode, the server may have to issue this command when received from a client + if(objp == NULL) { + tobj = Player_obj; + multi_player_num = -1; + } else { + tobj = objp; + multi_player_num = multi_find_player_by_object(objp); + Assert(multi_player_num != -1); + } + + // see if player is already scheduled on arriving support ship. If so, issues appripriate + // message and bail + if ( is_support_allowed(tobj) ) { + if ( mission_is_repair_scheduled( tobj ) ) { + message_send_builtin_to_player( MESSAGE_REARM_ON_WAY, NULL, MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 ); + } else { + robjnum = hud_support_find_closest(OBJ_INDEX(tobj)); + if ( robjnum != -1 ) { + message_send_builtin_to_player( MESSAGE_REARM_ON_WAY, &Ships[Objects[robjnum].instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 ); + } else { + // request a rearm. Next function returns -1 if ship is warping in, objnum of repair ship otherwise + robjnum = ai_issue_rearm_request( tobj ); + if ( robjnum != -1) { + robjp = &Objects[robjnum]; + message_send_builtin_to_player( MESSAGE_ON_WAY, &Ships[robjp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 ); + + } else { + // if we are in this part of the if statment, a support ship has been warped in to + // service us. Issue appropriate message + message_send_builtin_to_player( MESSAGE_REARM_WARP, NULL, MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, multi_player_num, -1 ); + } + + mission_log_add_entry(LOG_PLAYER_REARM, Ships[tobj->instance].ship_name, NULL); + } + } + } + + //if ( multi_player_num == -1 ) // only do the hud display if it is for me! + // hud_support_view_start(); + + if ( toggle_state ) + hud_squadmsg_toggle(); // take us out of message mode +} + +// function which gets called from keyboard code to issues a shortcut command for rearming. +void hud_squadmsg_rearm_shortcut() +{ + if ( !hud_squadmsg_can_rearm(Player_ship) ) + return; + + // multiplayer clients need to send this message to the server + if ( MULTIPLAYER_CLIENT ) { + send_player_order_packet(SQUAD_MSG_SHIP, 0, REARM_REPAIR_ME_ITEM); + return; + } + + hud_squadmsg_repair_rearm(0); +} + +// code which is called when a player aborts his rearm request +void hud_squadmsg_repair_rearm_abort( int toggle_state, object *obj) +{ +// ai_info *aip; +// object *robjp; + object *tobj; + + // this is essentially a check for multiplayer server/client mode + // in multiplayer mode, the server may have to issue this command when received from a client + if(obj == NULL) + tobj = Player_obj; + else + tobj = obj; + + // try to abort the request. We shoudln't be in this function unless we are actually + // queued for repair. Send a message from support ship if the support ship is in the mission + ai_abort_rearm_request( tobj ); + + // move the next statements outside of the above if-statement. Seems like this place + // is the right place, since we want to change state of the messaging system regardless + // of what happened above. + if ( toggle_state ) + hud_squadmsg_toggle(); // take us out of message mode +} + +// returns 1 if an order is valid for a ship. Applies to things like departure when engines are blown, etc. +int hud_squadmsg_ship_order_valid( int shipnum, int order ) +{ + // disabled ships can't depart. + if ( (order == DEPART_ITEM) && (Ships[shipnum].flags & SF_DISABLED) ) + return 0; + + return 1; +} + +// returns true or false if the Players target is valid for the given order +// find_order is true when we need to search the comm_orders array for the order entry. We have +// to do this action in some cases since all we know is the actual "value" of the order +int hud_squadmsg_is_target_order_valid(int order, int find_order, ai_info *aip ) +{ + int target_objnum, i; + ship *shipp, *ordering_shipp; + object *objp; + + if ( aip == NULL ) + aip = Player_ai; + + // find the comm_menu item for this command + if ( find_order ) { + for (i = 0; i < MAX_SHIP_ORDERS; i++ ) { + if ( Comm_orders[i].value == order ) + break; + } + Assert( i < MAX_SHIP_ORDERS ); + order = i; + } + + // orders which don't operate on targets are always valid + if ( !(Comm_orders[order].value & TARGET_MESSAGES) ) + return 1; + + target_objnum = aip->target_objnum; + + // order isn't valid if there is no player target + if ( target_objnum == -1 ) { + return 0; + } + + objp = &Objects[target_objnum]; + + ordering_shipp = &Ships[aip->shipnum]; + + // target isn't a ship, then return 0 + if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_WEAPON) ) + return 0; + + // if it's a weapon, then it needs to be a WIF_BOMB weapon. Only attack order valid, and only + // valid on bombs not on the player's team + if ( objp->type == OBJ_WEAPON ) { + if ( (Comm_orders[order].value == ATTACK_TARGET_ITEM ) + && (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB) + && (Weapons[objp->instance].team != ordering_shipp->team) ) + + return 1; + + return 0; + } + + Assert( objp->type == OBJ_SHIP ); + + shipp = &Ships[objp->instance]; + + // if target is a navbouy, return 0 + if ( Ship_info[shipp->ship_info_index].flags & SIF_NAVBUOY ){ + return 0; + } + + // if we are messaging a ship, and that ship is our target, no target type orders are ever active + if ( (Squad_msg_mode == SM_MODE_SHIP_COMMAND) && (target_objnum == Msg_instance) ){ + return 0; + } + + // if the order is a disable order or depart, and the ship is disabled, order isn't active + if ( (Comm_orders[order].value == DISABLE_TARGET_ITEM) && (shipp->flags & SF_DISABLED) ){ + return 0; + } + + // same as above except for disabled. + if ( (Comm_orders[order].value == DISARM_TARGET_ITEM) && ((shipp->subsys_info[SUBSYSTEM_TURRET].num > 0) && (shipp->subsys_info[SUBSYSTEM_TURRET].current_hits == 0.0f)) ){ + return 0; + } + + // if order is disable subsystem, and no subsystem targeted or no hits, then order not valid + if ( (Comm_orders[order].value == DISABLE_SUBSYSTEM_ITEM) && ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) ){ + return 0; + } + + // check based on target's and player's team + if ( (shipp->team == ordering_shipp->team) && (Comm_orders[order].value & FRIENDLY_TARGET_MESSAGES) ){ + return 1; + } else if ( (shipp->team != ordering_shipp->team) && (Comm_orders[order].value & ENEMY_TARGET_MESSAGES) ){ + return 1; + } else { + return 0; + } +} + +// function to send an order to all fighters/bombers. +void hud_squadmsg_send_to_all_fighters( int command, int player_num ) +{ + ai_info *aip; + ship *shipp, *ordering_shipp; + int i, send_message, to_everyone, do_ship; + object *objp; + + // quick short circuit here because of actually showing comm menu even though you cannot message. + // just a safety net. + if ( (Game_mode & GM_MULTIPLAYER) && (player_num != -1) ) { + if ( !multi_can_message(&Net_players[player_num]) ) { + return; + } + } + + // check for multiplayer mode + if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)) { + send_player_order_packet(SQUAD_MSG_ALL, 0, command); + return; + } + + // to_everyone tells us whether the command should apply to all ships, or just to fighets/bombers. + // when true, command goes to *all* friendlies. + send_message = 1; // internal flag to dictate who sends message + to_everyone = 0; + do_ship = 0; + aip = Player_ai; + + if ( player_num != -1 ) + aip = &Ai_info[Ships[Objects[Net_players[player_num].player->objnum].instance].ai_index]; + + Assert( aip->shipnum != -1 ); + ordering_shipp = &Ships[aip->shipnum]; + + if ( command == IGNORE_TARGET_ITEM ) { + to_everyone = 1; + // if we were messaging a ship directly, set flag to send no messages. We will send one + // specifically from the ship player is ordering + if ( (Msg_instance != MESSAGE_ALL_FIGHTERS) && (Squad_msg_mode == SM_MODE_SHIP_COMMAND) ) { + do_ship = 1; + send_message = 0; + } + } + + for ( i = 0; i < num_wings; i++ ) { + int shipnum; + + if ( (Wings[i].flags & WF_WING_GONE) || (Wings[i].current_count == 0) ) + continue; + + if ( Wings[i].flags & WF_WING_DEPARTING ) + continue; + + // get the first ship on the wing list and look at it's team and then it's type + shipnum = Wings[i].ship_index[0]; + Assert( shipnum != -1 ); + shipp = &Ships[shipnum]; + + // can't message if not on players team + if ( shipp->team != ordering_shipp->team ) + continue; + + // can't message if ship not fighter/bomber if the command isn't to everyone. + if ( !to_everyone && !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) + continue; + + // don't send the command if the "wing" won't accept the command. We do this by looking at + // the set of orders accepted for the first ship in the wing. + if ( !(command & shipp->orders_accepted) ) + continue; + + // send the command to the wing + if ( Wings[i].current_count > 1 ) { + if ( hud_squadmsg_send_wing_command(i, command, send_message, player_num) ) { + send_message = 0; + } + } + } + + // now find any friendly fighter/bomber ships not in wings + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if ( objp->type != OBJ_SHIP ) + continue; + + // don't send messge to ships not on player's team, or that are in a wing. + shipp = &Ships[objp->instance]; + if ( (shipp->team != ordering_shipp->team) || (shipp->wingnum != -1) ) + continue; + + // don't send message to non fighter wings + if ( !to_everyone && !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) + continue; + + // skip departing/dying ships + if ( shipp->flags & (SF_DEPARTING|SF_DYING) ) + continue; + + // don't send command if ship won't accept if + if ( !(command & shipp->orders_accepted) ) + continue; + + if ( hud_squadmsg_send_ship_command(objp->instance, command, send_message, player_num) ) { + send_message = 0; + } + } + + // we might send the ship command again if we are ignoring a target, and the guy + // we ordered directly is a ship -- we want the response to come directly from the + // guy we orders + if ( do_ship ) { + Assert( Msg_instance != MESSAGE_ALL_FIGHTERS ); + hud_squadmsg_send_ship_command( Msg_instance, command, 1 ); + } +} + +// Check if any enemy ships are in the mission +int hud_squadmsg_enemies_present() +{ + ship *shipp; + ship_obj *so; + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + shipp = &Ships[Objects[so->objnum].instance]; + if ( shipp->team != Player_ship->team ) + return 1; + } + + return 0; +} + +#define OVERRIDE_PROTECT_SHIP_TYPE (SIF_FIGHTER|SIF_BOMBER|SIF_FREIGHTER|SIF_TRANSPORT) +// function which sends a message to a specific ship. This routine can be called from one of two +// places. Either after selecting a ship when using a hotkey, or after selecting a command when +// using the entire messaging menu system +// +// if local and addr are non-null, it means the function is being called by the (multiplayer) server in response to +// a PLAYER_COMMAND_PACKET +int hud_squadmsg_send_ship_command( int shipnum, int command, int send_message, int player_num ) +{ + ai_info *ainfo; + int ai_mode, ai_submode; // ai mode and submode needed for ship commands + char *target_shipname; // ship number of possible targets + int message; + int target_team, ship_team; // team id's for the ship getting message and any target the player has + ship *ordering_shipp; + + // quick short circuit here because of actually showing comm menu even though you cannot message. + // just a safety net. + if ( (Game_mode & GM_MULTIPLAYER) && (player_num != -1) ) { + if ( !multi_can_message(&Net_players[player_num]) ) { + return 0; + } + } + + // check for multiplayer mode + if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + send_player_order_packet(SQUAD_MSG_SHIP, shipnum, command); + return 0; + } + + ai_mode = AI_GOAL_NONE; // needs to be initialized + ai_submode = -1234567; + ainfo = Player_ai; + + if ( player_num != -1 ){ + ainfo = &Ai_info[Ships[Objects[Net_players[player_num].player->objnum].instance].ai_index]; + } + + Assert( ainfo->shipnum != -1 ); + ordering_shipp = &Ships[ainfo->shipnum]; + + // a shortcut to save on repetitive coding. If the order is a 'target' order, make the default + // mesage be "no target" + message = MESSAGE_NOSIR; + if ( (command & TARGET_MESSAGES) && (ainfo->target_objnum == -1) ) + message = MESSAGE_NO_TARGET; + + if ( hud_squadmsg_is_target_order_valid(command, 1, ainfo) ) { + + target_shipname = NULL; + target_team = -1; + if ( ainfo->target_objnum != -1) { + if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) { + if ( ainfo->target_objnum != Ships[shipnum].objnum ) { + target_shipname = Ships[Objects[ainfo->target_objnum].instance].ship_name; // I think this is right + target_team = Ships[Objects[ainfo->target_objnum].instance].team; + } + } + } + + Assert ( ainfo->shipnum != -1 ); + ship_team = Ships[ainfo->shipnum].team; // team of the ship issuing the message + + switch ( command ) { // value of k matches the #defines for ship messages + case ATTACK_TARGET_ITEM: + if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) { + Assert( target_shipname ); + Assert( ship_team != target_team ); + + // Orders to override protect + if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) { + Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED; + } + + ai_mode = AI_GOAL_CHASE; + ai_submode = SM_ATTACK; + } else if ( Objects[ainfo->target_objnum].type == OBJ_WEAPON ) { + ai_mode = AI_GOAL_CHASE_WEAPON; + ai_submode = Objects[ainfo->target_objnum].instance; // store the instance of the weapon -- ai goals code will deal with it + } else + Int3(); + message = MESSAGE_ATTACK_TARGET; + break; + + case DISABLE_TARGET_ITEM: + Assert( target_shipname ); + Assert( ship_team != target_team ); + + // Orders to override protect + if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) { + Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED; + } + + ai_mode = AI_GOAL_DISABLE_SHIP; + ai_submode = -SUBSYSTEM_ENGINE; + message = MESSAGE_DISABLE_TARGET; + break; + + case DISARM_TARGET_ITEM: + Assert( target_shipname ); + Assert( ship_team != target_team ); + + // Orders to override protect + if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) { + Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED; + } + + ai_mode = AI_GOAL_DISARM_SHIP; + ai_submode = -SUBSYSTEM_TURRET; + message = MESSAGE_DISARM_TARGET; + break; + + case DISABLE_SUBSYSTEM_ITEM: + Assert( target_shipname ); + Assert( ship_team != target_team ); + Assert( ainfo->targeted_subsys != NULL ); + Assert( ainfo->targeted_subsys->current_hits > 0.0f); + + // Orders to override protect + if (Ship_info[Ships[Objects[ainfo->target_objnum].instance].ship_info_index].flags & OVERRIDE_PROTECT_SHIP_TYPE) { + Objects[ainfo->target_objnum].flags &= ~OF_PROTECTED; + } + + ai_mode = AI_GOAL_DESTROY_SUBSYSTEM; + ai_submode = ship_get_subsys_index( &Ships[Objects[ainfo->target_objnum].instance], ainfo->targeted_subsys->system_info->subobj_name ); + message = MESSAGE_ATTACK_TARGET; + break; + + case CAPTURE_TARGET_ITEM: + Assert( target_shipname ); + Assert( ship_team != target_team ); + + Assert(ainfo->target_objnum > -1); + + Objects[ainfo->target_objnum].flags |= OF_PROTECTED; + + ai_mode = AI_GOAL_DOCK; + ai_submode = AIS_DOCK_0; + message = MESSAGE_DOCK_YES; + break; + + case PROTECT_TARGET_ITEM: + + // AL 31-3-98: Can't protect self... this can happen if all fighters + // are told to protect another friendly ship + if ( ainfo->target_objnum == Ships[shipnum].objnum ) { + return 0; + } + + Assert( target_shipname ); + Assert( ship_team == target_team ); + + ai_mode = AI_GOAL_GUARD; + ai_submode = AIS_GUARD_PATROL; + message = MESSAGE_YESSIR; + break; + + case IGNORE_TARGET_ITEM: + Assert( target_shipname ); + Assert( ship_team != target_team ); + + ai_mode = AI_GOAL_IGNORE; + ai_submode = 0; + message = MESSAGE_YESSIR; + break; + + case FORMATION_ITEM: + message = MESSAGE_YESSIR; + target_shipname = ordering_shipp->ship_name; + ai_mode = AI_GOAL_FORM_ON_WING; + ai_submode = 0; + break; + + case COVER_ME_ITEM: + ai_mode = AI_GOAL_GUARD; + ai_submode = AIS_GUARD_PATROL; + target_shipname = ordering_shipp->ship_name; + message = MESSAGE_YESSIR; + break; + + case ENGAGE_ENEMY_ITEM: + ai_mode = AI_GOAL_CHASE_ANY; + ai_submode = SM_ATTACK; + // if no enemies present, use the affirmative, instead of engaging enemies message + if ( hud_squadmsg_enemies_present() ) + message = MESSAGE_YESSIR; + else + message = MESSAGE_ENGAGE; + target_shipname = NULL; + break; + + case DEPART_ITEM: + ai_mode = AI_GOAL_WARP; + ai_submode = -1; + message = MESSAGE_WARP_OUT; + break; + + // the following are support ship options!!! + case REARM_REPAIR_ME_ITEM: + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (player_num != -1) ){ + hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]); + } else { + hud_squadmsg_repair_rearm(0); // note we return right away. repair/rearm code handles messaging, etc + } + return 0; + + case ABORT_REARM_REPAIR_ITEM: + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (player_num != -1) ){ + hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]); + } else { + hud_squadmsg_repair_rearm_abort(0); // note we return right away. repair/rearm code handles messaging, etc + } + return 0; + + case STAY_NEAR_ME_ITEM: + case STAY_NEAR_TARGET_ITEM: // can't attack anything on same team + + // cannot stay near a hostile ship(?) + if ( (command == STAY_NEAR_TARGET_ITEM) && (ship_team != target_team) ) + break; + + ai_mode = AI_GOAL_STAY_NEAR_SHIP; + ai_submode = -1; + message = MESSAGE_YESSIR; + if ( command == STAY_NEAR_ME_ITEM ) + target_shipname = ordering_shipp->ship_name; + break; + + case KEEP_SAFE_DIST_ITEM: + ai_mode = AI_GOAL_KEEP_SAFE_DISTANCE; + ai_submode = -1; + message = MESSAGE_YESSIR; + break; + + default: + Int3(); // get Allender -- illegal message + break; + + } + + // handle case of messaging one ship. Deal with messaging all fighters next. + if ( ai_mode != AI_GOAL_NONE ) { + Assert(ai_submode != -1234567); + ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, ai_mode, ai_submode, target_shipname, &Ai_info[Ships[shipnum].ai_index] ); + if( player_num == -1 ) + hud_add_issued_order(Ships[shipnum].ship_name, command, target_shipname); + } + } + + // if we're in multiplayer mode, and we're the server, determine if this virtual squadmate order should be + // sent to other players in the game as an actual "order" + if((Game_mode & GM_MULTIPLAYER) && (message != MESSAGE_NOSIR)){ + // if the multi_msg system processed and sent this order to a player, we should not play a response + if(multi_msg_eval_ship_squadmsg(shipnum,command,ainfo,player_num)){ + send_message = 0; + } + } + + // this is the _response_ + if ( send_message ){ + message_send_builtin_to_player( message, &Ships[shipnum], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_ANYTIME, 0, 0, player_num, -1 ); + } + + return send_message; +} + +// function to send a command to a wing. Like above function, called from one of two places +// +// if local and addr are non-null, it means the function is being called by the (multiplayer) server in response to +// a PLAYER_COMMAND_PACKET +// +// returns whether or not a message was sent +int hud_squadmsg_send_wing_command( int wingnum, int command, int send_message, int player_num ) +{ + ai_info *ainfo; + int ai_mode, ai_submode; // ai mode and submode needed for ship commands + char *target_shipname; // ship number of possible targets + int message_sent, message; + int target_team, wing_team; // team for the wing and the player's target + ship *ordering_shipp; + + // quick short circuit here because of actually showing comm menu even though you cannot message. + // just a safety net. + if ( (Game_mode & GM_MULTIPLAYER) && (player_num != -1) ) { + if ( !multi_can_message(&Net_players[player_num]) ) { + return 0; + } + } + + // check for multiplayer mode + if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + send_player_order_packet(SQUAD_MSG_WING, wingnum,command); + return 0; + } + + ai_mode = AI_GOAL_NONE; // needs to be initialized + ai_submode = -1234567; + ainfo = Player_ai; + + if ( player_num != -1 ) + ainfo = &Ai_info[Ships[Objects[Net_players[player_num].player->objnum].instance].ai_index]; + + Assert( ainfo->shipnum != -1 ); + ordering_shipp = &Ships[ainfo->shipnum]; + + // get the shipnum of the ship the player has targeted. Used in enough places to do this just + // once. If the ship targeted is part of the wing that was messages -- bail out!!! + + // a shortcut to save on repetative coding + message = MESSAGE_NOSIR; + if ( (command & TARGET_MESSAGES) && (ainfo->target_objnum == -1) ) + message = MESSAGE_NO_TARGET; + + if ( hud_squadmsg_is_target_order_valid(command, 1, ainfo) ) { + + target_shipname = NULL; + target_team = -1; + if ( ainfo->target_objnum != -1) { + if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) { + target_shipname = Ships[Objects[ainfo->target_objnum].instance].ship_name; // I think this is right + target_team = Ships[Objects[ainfo->target_objnum].instance].team; + } + } + + Assert ( ainfo->shipnum != -1 ); + Assert ( (wingnum >= 0) && (wingnum < num_wings) ); + + // get the team for the wing + Assert ( Wings[wingnum].ship_index[0] != -1 ); + wing_team = Ships[Wings[wingnum].ship_index[0]].team; + + switch ( command ) { // value of k matches the #defines for ship messages + case ATTACK_TARGET_ITEM: + if ( Objects[ainfo->target_objnum].type == OBJ_SHIP ) { + Assert( target_shipname ); + Assert( wing_team != target_team ); + if ( (Ships[Objects[ainfo->target_objnum].instance].wingnum != -1) && (Ships[Objects[ainfo->target_objnum].instance].wingnum == wingnum) ) { + message = MESSAGE_NOSIR; + ai_mode = AI_GOAL_NONE; + } else { + ai_mode = AI_GOAL_CHASE; + ai_submode = SM_ATTACK; + message = MESSAGE_ATTACK_TARGET; + } + } else if ( Objects[ainfo->target_objnum].type == OBJ_WEAPON ) { + ai_mode = AI_GOAL_CHASE_WEAPON; + ai_submode = Objects[ainfo->target_objnum].instance; // store the instance of the weapon -- ai goals code will deal with it + message = MESSAGE_ATTACK_TARGET; + } else + Int3(); + + break; + + case DISABLE_TARGET_ITEM: + Assert( target_shipname ); + Assert( wing_team != target_team ); + + ai_mode = AI_GOAL_DISABLE_SHIP; + ai_submode = -SUBSYSTEM_ENGINE; + message = MESSAGE_DISABLE_TARGET; + break; + + case DISARM_TARGET_ITEM: + Assert( target_shipname ); + Assert( wing_team != target_team ); + + ai_mode = AI_GOAL_DISARM_SHIP; + ai_submode = -SUBSYSTEM_TURRET; + message = MESSAGE_DISARM_TARGET; + break; + + case DISABLE_SUBSYSTEM_ITEM: + Assert( target_shipname ); + Assert( wing_team != target_team ); + Assert( ainfo->targeted_subsys != NULL ); + Assert( ainfo->targeted_subsys->current_hits > 0.0f); + + ai_mode = AI_GOAL_DESTROY_SUBSYSTEM; + ai_submode = ship_get_subsys_index( &Ships[Objects[ainfo->target_objnum].instance], ainfo->targeted_subsys->system_info->subobj_name ); + message = MESSAGE_ATTACK_TARGET; + break; + + + case PROTECT_TARGET_ITEM: + Assert( target_shipname ); + Assert( wing_team == target_team ); + + ai_mode = AI_GOAL_GUARD; + ai_submode = AIS_GUARD_PATROL; + message = MESSAGE_YESSIR; + break; + + case IGNORE_TARGET_ITEM: + Assert( target_shipname ); + Assert( wing_team != target_team ); + + ai_mode = AI_GOAL_IGNORE; + ai_submode = 0; // actually, a don't care. + message = MESSAGE_YESSIR; + break; + + case FORMATION_ITEM: + message = MESSAGE_YESSIR; + target_shipname = ordering_shipp->ship_name; + ai_mode = AI_GOAL_FORM_ON_WING; + ai_submode = 0; + break; + + case COVER_ME_ITEM: + ai_mode = AI_GOAL_GUARD; + ai_submode = AIS_GUARD_PATROL; + target_shipname = ordering_shipp->ship_name; + message = MESSAGE_YESSIR; + break; + + case ENGAGE_ENEMY_ITEM: + ai_mode = AI_GOAL_CHASE_ANY; + ai_submode = SM_ATTACK; + if ( hud_squadmsg_enemies_present() ) + message = MESSAGE_YESSIR; + else + message = MESSAGE_ENGAGE; + target_shipname = NULL; + break; + + case DEPART_ITEM: + ai_mode = AI_GOAL_WARP; + ai_submode = -1; + message = MESSAGE_WARP_OUT; + Wings[wingnum].flags |= WF_DEPARTURE_ORDERED; + break; + + case REARM_REPAIR_ME_ITEM: + case ABORT_REARM_REPAIR_ITEM: + case STAY_NEAR_ME_ITEM: + case STAY_NEAR_TARGET_ITEM: + case KEEP_SAFE_DIST_ITEM: + return 0; + + default: + Int3(); // get Allender -- illegal message + break; + + } + + if ( ai_mode != AI_GOAL_NONE ) { + Assert(ai_submode != -1234567); + ai_add_wing_goal_player( AIG_TYPE_PLAYER_WING, ai_mode, ai_submode, target_shipname, wingnum ); + } + } + + // if we're in multiplayer mode, and we're the server, determine if this virtual squadmate order should be + // sent to other players in the game as an actual "order" + if((Game_mode & GM_MULTIPLAYER) && (message != MESSAGE_NOSIR)){ + // if there's at least one ai ship which got the command, let the response come through + if(multi_msg_eval_wing_squadmsg(wingnum,command,ainfo,player_num)){ + send_message = 0; + } + } + + // this is the _response_ + message_sent = 0; + if ( send_message ) { + int ship_num; + + // get a random ship in the wing to send the message to the player + ship_num = ship_get_random_ship_in_wing( wingnum, SHIP_GET_NO_PLAYERS ); + + // in multiplayer, its possible that all ships in a wing are players. so we'll just send from a random ship + if(ship_num == -1){ + ship_num = ship_get_random_ship_in_wing(wingnum); + } + + // only send message if ship is found. There appear to be cases where all ships + // in a wing die in the same frame causing the wing to appear valid in the message + // menu, but the get_random_ship* functions won't return dying ships. + if ( ship_num != -1 ) { + message_send_builtin_to_player( message, &Ships[ship_num], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_ANYTIME, 0, 0, player_num, -1 ); + message_sent = 1; + } + } + + return message_sent; +} + + +// return number of available reinforcements, 0 if none available +int hud_squadmsg_reinforcements_available(int team) +{ + int i, count = 0; + + for (i = 0; i < Num_reinforcements; i++) { + int wingnum; + + // no more left + if ( Reinforcements[i].num_uses >= Reinforcements[i].uses ){ + continue; + } + + // incorrect team + if ( team != ship_get_reinforcement_team(i) ){ + continue; + } + + // check the arrival cue sexpression of the ship/wing of this reinforcement. If known + // false, it doesn't count either + if ( (wingnum = wing_name_lookup(Reinforcements[i].name, 1)) != -1 ) { + Assert ( Wings[wingnum].arrival_cue >= 0 ); + if ( Sexp_nodes[Wings[wingnum].arrival_cue].value == SEXP_KNOWN_FALSE ){ + continue; + } + } else { + p_object *p_objp; + + p_objp = mission_parse_get_arrival_ship( Reinforcements[i].name ); + if ( p_objp != NULL ) { + if ( Sexp_nodes[p_objp->arrival_cue].value == SEXP_KNOWN_FALSE ){ + continue; + } + } else { + Int3(); // allender says bogus! reinforcement should be here since it wasn't a wing! + continue; + } + } + count++; + } + + return count; +} + +// function to put up window in upper right to allow for player to select the type +// of entity to select for a message (i.e. a wing or a ship) +void hud_squadmsg_type_select( ) +{ + int k, i; + + First_menu_item = 0; + + // Add the items + for (i=0; iteam == TEAM_TRAITOR ) { + for (i = 0; i < MAX_MENU_ITEMS; i++ ) + MsgItems[i].active = 0; + goto do_main_menu; + } + + // based on ship counts, wing counts, shortcut active, grey out possible menu choices + if ( !hud_squadmsg_count_ships(0) ) + MsgItems[TYPE_SHIP_ITEM].active = 0; + + if ( !hud_squadmsg_count_wings(0) ) + MsgItems[TYPE_WING_ITEM].active = 0; + + // check to be sure that we have some fighters/bombers on the players team that we + // can message + if ( !hud_squadmsg_count_fighters() ){ + MsgItems[TYPE_ALL_FIGHTERS_ITEM].active = 0; + } + + if ((Player_ship != NULL) && !hud_squadmsg_reinforcements_available(Player_ship->team)) { + MsgItems[TYPE_REINFORCEMENT_ITEM].active = 0; + } + + MsgItems[TYPE_REPAIR_REARM_ITEM].active = 1; // this item will always be available (I think) + MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 0; + + // AL: 10/13/97 + // If the player ship communications are severely damaged, then the player + // will only be able to call for repair/rearm ships + // + // also, only allow support ship if this player is not allowed to messaage. + if ( (hud_communications_state(Player_ship) != COMM_OK) || ((Game_mode & GM_MULTIPLAYER) && !multi_can_message(Net_player)) ) { + for ( i = 0; i < MAX_MENU_ITEMS; i++ ){ + MsgItems[i].active = 0; + } + + MsgItems[TYPE_REPAIR_REARM_ITEM].active = 1; + } + + // check to see if the player is awaiting repair or being repaired. Active the abort and inactive the repair items + // check to see if the player is scheduled to be repaired by incoming ship + if ( Ai_info[Ships[Player_obj->instance].ai_index].ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) { + MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0; + MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 1; + } else if ( mission_is_repair_scheduled(Player_obj) ) { + MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0; + MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 1; + } + + // if no support available, can't call one in + if ( !is_support_allowed(Player_obj) ) { + MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0; + MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 0; + } + + // de-activate the rearm/repair item if the player has a full load of missiles and + // all subsystems at full strength. We will only check if this item hasn't been marked + // inactive because of some other reason + if ( MsgItems[TYPE_REPAIR_REARM_ITEM].active ) { + + if ( !hud_squadmsg_can_rearm(Player_ship) ){ + MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0; + } + } + + // if using keyboard shortcut, these items are always inactive + if ( Msg_shortcut_command != -1 ) { + MsgItems[TYPE_REPAIR_REARM_ITEM].active = 0; + MsgItems[TYPE_REINFORCEMENT_ITEM].active = 0; + MsgItems[TYPE_REPAIR_REARM_ABORT_ITEM].active = 0; + } + +do_main_menu: + hud_squadmsg_display_menu( XSTR( "Message What", 316) ); + k = hud_squadmsg_get_key(); + if ( k != -1 ) { // when k != -1, we have a key that associates with menu item + Assert ( k < Num_menu_items ); + if ( k == TYPE_SHIP_ITEM ){ + hud_squadmsg_do_mode( SM_MODE_SHIP_SELECT ); + } else if ( k == TYPE_WING_ITEM ) { + hud_squadmsg_do_mode( SM_MODE_WING_SELECT ); + } else if ( k == TYPE_ALL_FIGHTERS_ITEM ) { + hud_squadmsg_do_mode( SM_MODE_ALL_FIGHTERS ); + } + + if ( Msg_shortcut_command == -1 ) { + if ( k == TYPE_REINFORCEMENT_ITEM ) { + hud_squadmsg_do_mode( SM_MODE_REINFORCEMENTS ); + player_set_next_all_alone_msg_timestamp(); + } else if ( k == TYPE_REPAIR_REARM_ITEM ){ + hud_squadmsg_do_mode( SM_MODE_REPAIR_REARM ); + } else if ( k == TYPE_REPAIR_REARM_ABORT_ITEM ) { + hud_squadmsg_do_mode( SM_MODE_REPAIR_REARM_ABORT ); + } + } + } +} + +// function to display a list of ships to send a command to +void hud_squadmsg_ship_select() +{ + int k; + + if ( Num_menu_items == -1 ) { + Num_menu_items = 0; + hud_squadmsg_count_ships( 1 ); + } + + hud_squadmsg_display_menu( XSTR( "Select Ship", 317) ); + k = hud_squadmsg_get_key(); + if ( k != -1 ) { // if true, we have selected a ship. + if ( Msg_shortcut_command == -1 ) { + Msg_instance = MsgItems[First_menu_item + k].instance; // store the instance id in a global + hud_squadmsg_do_mode( SM_MODE_SHIP_COMMAND ); // and move to a new mode + } else { + // we must convert the Msg_shortcut_command value to a value that the message + // system normally uses to select a command. Since the menu + Assert( Msg_shortcut_command != IGNORE_TARGET_ITEM ); + hud_squadmsg_send_ship_command( MsgItems[First_menu_item+k].instance, Msg_shortcut_command, 1 ); + hud_squadmsg_toggle(); + } + } + +} + +// function to display a list of ships to send a command to +void hud_squadmsg_wing_select() +{ + int k; + + if ( Num_menu_items == -1 ) { + Num_menu_items = 0; + hud_squadmsg_count_wings( 1 ); + } + + hud_squadmsg_display_menu( XSTR( "Select Wing", 318) ); + k = hud_squadmsg_get_key(); + if ( k != -1 ) { // if true, we have selected a ship. + if ( Msg_shortcut_command == -1 ) { // do normal menu stuff when no hoykey active + Msg_instance = MsgItems[First_menu_item + k].instance; // store the instance id in a global + hud_squadmsg_do_mode( SM_MODE_WING_COMMAND ); // and move to a new mode + } else { + Assert( Msg_shortcut_command != IGNORE_TARGET_ITEM ); + hud_squadmsg_send_wing_command( MsgItems[First_menu_item+k].instance, Msg_shortcut_command, 1 ); + hud_squadmsg_toggle(); + } + } + +} + +// code which gives an order to all fighters/bombers. If there is a message shortcut active, then +// make that order apply to all fighters/bombers. Otherwise, move to the ship_command menu +void hud_squadmsg_msg_all_fighters() +{ + if ( Msg_shortcut_command == -1 ) { + Msg_instance = MESSAGE_ALL_FIGHTERS; + hud_squadmsg_do_mode( SM_MODE_SHIP_COMMAND ); + } else { + hud_squadmsg_send_to_all_fighters( Msg_shortcut_command ); + hud_squadmsg_toggle(); + } +} + +// called to actually bring in a reinforcement. For single player games, always gets called. +// for multiplayer games, always called on the server side. Clients should never get here +void hud_squadmsg_call_reinforcement(int reinforcement_num, int player_num) +{ + int i, delay; + reinforcements *rp; + p_object *p_objp; + + rp = &Reinforcements[reinforcement_num]; + + // safety net mainly for multiplayer servers in case some odd data desync occurs between + // server and clients + if ( MULTIPLAYER_MASTER && (rp->num_uses == rp->uses) ) { + return; + } + + // check to see if the reinforcement called was a wing. + for (i = 0; i < num_wings; i++ ) { + if ( !stricmp(rp->name, Wings[i].name) ) { + // found a wingname. Call the parse function to create all the ships in this wing + // we must set the arrival cue of the wing to true, otherwise, this won't work!! + Wings[i].flags &= ~WF_REINFORCEMENT; + Wings[i].flags |= WF_RESET_REINFORCEMENT; + + // set up the arrival delay. If it is 0, then make is some random number of seconds + delay = rp->arrival_delay; + if ( delay == 0 ) + delay = (int)(frand() * 3.0) + 3; + Wings[i].arrival_delay = timestamp(delay * 1000); + break; + } + } + + // if we found no wing name that matched the reinforcement name, then look for a ship + // of the same name + if ( i == num_wings ) { + p_objp = mission_parse_get_arrival_ship( rp->name ); + if ( p_objp ) { + // by resetting the reinforcement flag, we will allow code which normally handles arrivals + // to make this reinforcement arrive. Doing so keeps the data structures clean. + p_objp->flags &= ~P_SF_REINFORCEMENT; + + // set up the arrival delay + delay = rp->arrival_delay; + if ( delay == 0 ) + delay = (int)(frand() * 3.0) + 3; // between 3 and 6 seconds to arrive + p_objp->arrival_delay = timestamp(delay * 1000); + } else { + Int3(); // get allender -- I don't think that this can happen!!!! + return; + } + } + + // increment the number of times this is used. Incremented here on single player and multiplayer + // server side only. Clients keep track of own count when they actually call something in. + rp->num_uses++; + + // commented out on 9/9/98 because these messages simply are not used + /* + // now play a message (if there is one to play) for this reinforcement arrival. The first for loop + // determine how many messages there are to play, since the array is packet. Then, if >= 1 message + // to play, play one + for (i = 0; i < MAX_REINFORCEMENT_MESSAGES; i++ ) + if ( !strlen(rp->yes_messages[i]) ) + break; + + //if ( i > 0 ) + // message_send_to_player( rp->yes_messages[myrand() % i], rp->name, MESSAGE_PRIORITY_NORMAL, HUD_SOURCE_FRIENDLY ); + */ + + mission_log_add_entry(LOG_PLAYER_REINFORCEMENT, rp->name, NULL); +} + +// function to display a list of reinforcements available to the player +void hud_squadmsg_reinforcement_select() +{ + int i, k; + reinforcements *rp; + + if ( Num_menu_items == -1 ) { + Num_menu_items = 0; + for (i = 0; i < Num_reinforcements; i++) { + rp = &Reinforcements[i]; + + // don't put reinforcements onto the list that have already been used up. + if ( (rp->num_uses == rp->uses) ){ + continue; + } + + // don't put items which are not on my team + if((Player_ship != NULL) && (ship_get_reinforcement_team(i) != Player_ship->team)){ + continue; + } + + Assert ( Num_menu_items < MAX_MENU_ITEMS ); + strcpy( MsgItems[Num_menu_items].text, rp->name ); + MsgItems[Num_menu_items].instance = i; + MsgItems[Num_menu_items].active = 0; + + if ( rp->flags & RF_IS_AVAILABLE ) { + MsgItems[Num_menu_items].active = 1; + } + + Num_menu_items++; + } + } + +// hud_squadmsg_display_menu( "Select Reinforcement" ); + hud_squadmsg_display_menu( XSTR( "Select Ship/Wing", 319) ); // AL 11-14-97: Reinforcement didn't fit, so using this for now + k = hud_squadmsg_get_key(); + if (k != -1) { + int rnum; + + hud_squadmsg_toggle(); // take us out of message mode + + rnum = MsgItems[First_menu_item + k].instance; + + // check to see if trying to call a reinforcement not yet available. If so, maybe play message, but + // definately bail + if ( MsgItems[First_menu_item + k].active == 0 ) { + return; + } + + // in single player, or a multiplayer master, call in the reinforcement. Clients send a packet to the + // server + if ( MULTIPLAYER_CLIENT ) { + Reinforcements[rnum].num_uses++; // increment this variable here since clients need to maintain a valid count + send_player_order_packet(SQUAD_MSG_REINFORCEMENT, rnum, 0); + } else { + hud_squadmsg_call_reinforcement(rnum); + } + } +} + +// function to display list of commands for a ship +void hud_squadmsg_ship_command() +{ + int k; + int i, orders, default_orders; + + // when adding ship commands, we must look at the type of ship, and what messages that + // ship allows. First, place all messages that are possible onto the menu, then + + // see if messaging all ships or just one. Messaging all ships will mean all default orders + // show on comm menu. + if ( Msg_instance != MESSAGE_ALL_FIGHTERS ) { + orders = Ships[Msg_instance].orders_accepted; + default_orders = ship_get_default_orders_accepted( &Ship_info[Ships[Msg_instance].ship_info_index] ); + } else { + + default_orders = FIGHTER_MESSAGES; + orders = default_orders; + } + + First_menu_item = 0; + Num_menu_items = 0; + for ( i = 0; i < MAX_SHIP_ORDERS; i++ ) { + // check to see if the comm order should even be added to the menu -- if so, then add it + // the order will be activated if the bit is set for the ship. + if ( default_orders & Comm_orders[i].value ) { + Assert ( Num_menu_items < MAX_MENU_ITEMS ); + strcpy( MsgItems[Num_menu_items].text, comm_order_menu_text(i) ); + MsgItems[Num_menu_items].instance = Comm_orders[i].value; + MsgItems[Num_menu_items].active = 0; + // check the bit to see if the command is active + if ( orders & Comm_orders[i].value ) + MsgItems[Num_menu_items].active = 1; + + // if the order cannot be carried out by the ship, then item should be inactive + if ( (Msg_instance != MESSAGE_ALL_FIGHTERS) && !hud_squadmsg_ship_order_valid( Msg_instance, Comm_orders[i].value ) ) + MsgItems[Num_menu_items].active = 0; + + // do some other checks to possibly gray out other items. + // if no target, remove any items which are associated with the players target + if ( !hud_squadmsg_is_target_order_valid(i, 0) ) + MsgItems[Num_menu_items].active = 0; + + // if messaging all fighters, see if we should gray out the order if no one will accept it, + // or modify the text if only some of the ships will accept it + if ( Msg_instance == MESSAGE_ALL_FIGHTERS ) { + ship_obj *so; + ship *shipp; + int partial_accept, all_accept; // value which tells us what to do with menu item + + all_accept = Comm_orders[i].value; + partial_accept = 0; + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + + // don't send messge to ships not on player's team, or that are in a wing. + shipp = &Ships[Objects[so->objnum].instance]; + if ( shipp->team != Player_ship->team ) + continue; + + // don't send message to non fighter wings + if ( !(Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) + continue; + + all_accept &= shipp->orders_accepted; // 'and'ing will either keep this bit set or zero it properly + partial_accept |= (shipp->orders_accepted & Comm_orders[i].value); // 'or'ing will tell us if at least one accepts + } + + if ( !all_accept ) { + // either modify the text if a partial accept, or grey it out if no one accepts + if ( partial_accept ) { + strcat( MsgItems[Num_menu_items].text, XSTR( "(*)", 320) ); + } else { + MsgItems[Num_menu_items].active = 0; + } + } + } + + Num_menu_items++; + } + } + + hud_squadmsg_display_menu( XSTR( "What Command", 321) ); + k = hud_squadmsg_get_key(); + + // when we get a valid goal, we must add the goal to the ai ship's goal list + + if ( k != -1 ) { + Assert ( k < Num_menu_items ); + // when messaging all fighters or ignoring target, call the send_to_all_fighters routine + if ( (Msg_instance != MESSAGE_ALL_FIGHTERS) && (MsgItems[k].instance != IGNORE_TARGET_ITEM) ) + hud_squadmsg_send_ship_command( Msg_instance, MsgItems[k].instance, 1 ); + else + hud_squadmsg_send_to_all_fighters( MsgItems[k].instance ); + hud_squadmsg_toggle(); + } +} + +// function to display list of command for a wing +void hud_squadmsg_wing_command() +{ + int k; + wing *wingp; + int default_orders, i, orders; + + // when adding commands for wings, we will look at all of the ships in the wing, and create + // the order list from that set of ships. In the future, we may want to do something else.... + + wingp = &Wings[Msg_instance]; + + // or together all of the orders for all the ships in the wing + default_orders = 0; + for ( i = 0; i < wingp->current_count; i++ ) { + orders = ship_get_default_orders_accepted( &Ship_info[Ships[wingp->ship_index[i]].ship_info_index] ); + default_orders |= orders; + } + default_orders &= ~CAPTURE_TARGET_ITEM; // we cannot capture any target with a wing. + + Num_menu_items = 0; + orders = Ships[wingp->ship_index[0]].orders_accepted; // get the orders that the first ship in the wing will accept + for ( i = 0; i < MAX_SHIP_ORDERS; i++ ) { + // add the set of default orders to the comm menu. We will currently allow all messages + // to be available in the wing. + if ( default_orders & Comm_orders[i].value ) { + Assert ( Num_menu_items < MAX_MENU_ITEMS ); + strcpy( MsgItems[Num_menu_items].text, comm_order_menu_text(i) ); + MsgItems[Num_menu_items].instance = Comm_orders[i].value; + MsgItems[Num_menu_items].active = 0; + + // possibly grey out the menu item depending on whether or not the "wing" will accept this order + // the "wing" won't accept the order if the first ship in the wing doesn't accept it. + if ( orders & Comm_orders[i].value ) + MsgItems[Num_menu_items].active = 1; + + // do some other checks to possibly gray out other items. + // if no target, remove any items which are associated with the players target + if ( !hud_squadmsg_is_target_order_valid(i, 0) ) + MsgItems[Num_menu_items].active = 0; + + Num_menu_items++; + } + } + + hud_squadmsg_display_menu( XSTR( "What Command", 321) ); + k = hud_squadmsg_get_key(); + if ( k != -1 ) { + + // ignore target gets sent to everyone. + if ( MsgItems[k].instance != IGNORE_TARGET_ITEM ) + hud_squadmsg_send_wing_command( Msg_instance, MsgItems[k].instance, 1 ); + else + hud_squadmsg_send_to_all_fighters( MsgItems[k].instance ); + hud_squadmsg_toggle(); + } +} + + + +//---------------------------------------------------------- +// external entry points below!!!! + +// when starting messaging mode, we must remove old bindings from the +// keys that are used for messaging mode (which will get restored when +// messaging mode is done). + +// this code below will get called only the key config changes (from ControlsConfig.cpp) +// or if the bindings haven't been saved yet. This code doesn't remove the bindings +// but just sets up the array so that the bindings can be removed when messaging +// mode is entered. +// +// do_scroll indicates whether we should save the page up and page down keys +void hud_squadmsg_save_keys( int do_scroll ) +{ +// int i, j; + + num_keys_saved = 0; + +/* + for ( j=0; jflags & PLAYER_FLAGS_MSG_MODE) ) { + if ( Game_mode & GM_DEAD ){ + return; + } + if ( (Game_mode & GM_MULTIPLAYER) && NETPLAYER_IS_OBSERVER(Net_player) ){ + return; + } + hud_squadmsg_start(); + } else { + hud_squadmsg_end(); + } + + Player->flags ^= PLAYER_FLAGS_MSG_MODE; +} + +//#ifndef NDEBUG +// extern entry point to allow messaging of enemies +void hud_enemymsg_toggle() +{ + hud_squadmsg_toggle(); + // if we just entered message mode, turn on var that says to message enemies + if ( Player->flags & PLAYER_FLAGS_MSG_MODE ) + Msg_enemies = 1; +} +//#endif + +// external entry point into code when a keyboard shortcut is used for a command +// we are passed in an ID for the command to set internal variables. This command +// will be used in place of the last menu in the messaging code +void hud_squadmsg_shortcut( int command ) +{ + // check if the communications system is capable of sending a message + if ( (hud_communications_state(Player_ship, 1) != COMM_OK) && (command != REARM_REPAIR_ME_ITEM) ) { + return; + } + + // observers in multiplayer games cannot have this active either + if ( (Game_mode & GM_MULTIPLAYER) && NETPLAYER_IS_OBSERVER(Net_player) ) + return; + + // in multiplayer and I cannot message, don't allow anything except calling in for rearm + if ( (Game_mode & GM_MULTIPLAYER) && !multi_can_message(Net_player) && (command != REARM_REPAIR_ME_ITEM) ) + gamesnd_play_error_beep(); + + // player ships which turns traitor cannot rearm + if ( Player_ship->team == TEAM_TRAITOR ) + return; + + if ( Player->flags & PLAYER_FLAGS_MSG_MODE ) // we are already in messaging mode -- maybe do sometime more interesting? + return; + hud_squadmsg_toggle(); + Msg_shortcut_command = command; // save the command for later use + if ( Msg_shortcut_command == CAPTURE_TARGET_ITEM ) // some commands only apply to wings or ships + Squad_msg_mode = SM_MODE_SHIP_SELECT; // -- don't offer choice + else if ( Msg_shortcut_command == IGNORE_TARGET_ITEM ) { // ignoring target applied to all ships + hud_squadmsg_toggle(); // turns off mode which was turned on above + hud_squadmsg_send_to_all_fighters( Msg_shortcut_command ); + } +} + +// external entry point which is called when the player hits a selection key (1-0) while in messaging +// mode. If we are in messaging mode, send the shortcut command to the ships that are part of the +// passed in selection set. If there is no shortcut command, do nothing. Returns 1 if the key +// was used, else 0. This return value is used to tell the key control system that it should +// call the normal targeting selection stuff. +int hud_squadmsg_hotkey_select( int k ) +{ + htarget_list *hitem, *plist; + int send_message; + object *objp; + + Assert ( Player->flags & PLAYER_FLAGS_MSG_MODE ); + + if ( Msg_shortcut_command == -1 ) + return 0; + + Assert ( (k >= 0) && (k < MAX_KEYED_TARGETS) ); + plist = &(Player->keyed_targets[k]); + + if ( EMPTY(plist) ) // be sure that we have at least one ship in the list + return 0; + + send_message = 1; + // for each ship in the selection set list, send the shortcut command that the player + // previously entered. Be sure to check that we are not trying to send a command to + // an enemy ship. + for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) { + objp = hitem->objp; + Assert ( objp->type == OBJ_SHIP ); + if ( Ships[objp->instance].team != TEAM_FRIENDLY ) + continue; + + // be sure that this ship can accept this command + if ( !(Msg_shortcut_command, Ships[objp->instance].orders_accepted) ) + continue; + + hud_squadmsg_send_ship_command( objp->instance, Msg_shortcut_command, send_message ); + send_message = 0; + } + + hud_squadmsg_toggle(); // turn off messaging mode + return 1; +} + + +// the next function is called once a frame when the player is messaging someone +// in his squad. After a period of 5 seconds of inactivity (i.e. no keypress to +// select something in the menu), the menu will disappear. This function will only +// get called if the player flag PLAYER_FLAG_MSG_MODE is set. Parameter is the key +// that was hit this frame + +int hud_squadmsg_do_frame( ) +{ + int target_changed; + + Assert ( Player->flags & PLAYER_FLAGS_MSG_MODE ); // be sure that messaging mode is set!!! + + // if the player is now dead, or the timestamp elapsed, then get out of messaging mode. + if ( (Game_mode & GM_DEAD) || timestamp_elapsed(Msg_mode_timestamp) ) { + hud_squadmsg_toggle(); + return 0; + } + + Msg_key_used = 0; + + // check the player's current target. Change in target resets the timer + target_changed = 0; + if ( Msg_target_objnum != Player_ai->target_objnum ) { + Msg_target_objnum = Player_ai->target_objnum; + target_changed = 1; + } + + if ( Msg_targeted_subsys != Player_ai->targeted_subsys ) { + Msg_targeted_subsys = Player_ai->targeted_subsys; + target_changed = 1; + } + + // setup color/font info + // hud_set_default_color(); + hud_set_gauge_color(HUD_MESSAGE_BOX); + + // check for multiplayer mode - this is really a special case checker for support ship requesting and aborting + if((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Squad_msg_mode == SM_MODE_REPAIR_REARM || Squad_msg_mode == SM_MODE_REPAIR_REARM_ABORT)){ + char *subsys_name; +// int who_to_sig; + ushort net_sig; + + // who_to_sig = Objects[Ships[shipnum].objnum].net_signature; + if(Player_ai->target_objnum != -1) + net_sig = Objects[Player_ai->target_objnum].net_signature; + else + net_sig = 0; + + if ((Player_ai->targeted_subsys != NULL) && (Player_ai->targeted_subsys->current_hits > 0.0f)) + subsys_name = Player_ai->targeted_subsys->system_info->subobj_name; + else + subsys_name = NULL; + + // send the correct packet + if(Squad_msg_mode == SM_MODE_REPAIR_REARM) + send_player_order_packet(SQUAD_MSG_SHIP, 0, REARM_REPAIR_ME_ITEM); + else + send_player_order_packet(SQUAD_MSG_SHIP, 0, ABORT_REARM_REPAIR_ITEM); + + // make sure to toggle the mode off + hud_squadmsg_toggle(); + + return 1; + } + + // draw top of frame + if ( Mbox_gauge[0].first_frame >= 0 ) { + GR_AABITMAP(Mbox_gauge[0].first_frame, Mbox_top_coords[gr_screen.res][0], Mbox_top_coords[gr_screen.res][1]); + } + + switch( Squad_msg_mode ) { + + case SM_MODE_TYPE_SELECT: + hud_squadmsg_type_select(); + break; + + case SM_MODE_SHIP_SELECT: + hud_squadmsg_ship_select(); + break; + + case SM_MODE_WING_SELECT: + hud_squadmsg_wing_select(); + break; + + case SM_MODE_SHIP_COMMAND: + hud_squadmsg_ship_command(); + break; + + case SM_MODE_WING_COMMAND: + hud_squadmsg_wing_command(); + break; + + case SM_MODE_REINFORCEMENTS: + hud_squadmsg_reinforcement_select(); + break; + + case SM_MODE_REPAIR_REARM: + //if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (addr != NULL)){ + // hud_squadmsg_repair_rearm(1,&Objects[Net_players[player_num].player->objnum]); + //} else { + hud_squadmsg_repair_rearm(1); // note we return right away. repair/rearm code handles messaging, etc + //} + break; + + case SM_MODE_REPAIR_REARM_ABORT: + //if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (addr != NULL)){ + // hud_squadmsg_repair_rearm_abort(1,&Objects[Net_players[player_num].player->objnum]); + //} else { + hud_squadmsg_repair_rearm_abort(1); // note we return right away. repair/rearm code handles messaging, etc + //} + break; + + case SM_MODE_ALL_FIGHTERS: + hud_squadmsg_msg_all_fighters(); + break; + + default: + Int3(); // get allender -- invalid mode in messaging system + break; + + } + + // be sure to reset the clip region + HUD_reset_clip(); // JAS: Is this needed? + + if ( Msg_key_used || target_changed ) { + Msg_mode_timestamp = timestamp(DEFAULT_MSG_TIMEOUT); + return 1; + } else + return 0; +} + +void hud_add_issued_order(char *name, int order, char *target) +{ + Squadmsg_history[squadmsg_history_index].ship = get_parse_name_index(name); + Squadmsg_history[squadmsg_history_index].order = order; + if (target) + Squadmsg_history[squadmsg_history_index].target = get_parse_name_index(target); + else + Squadmsg_history[squadmsg_history_index].target = -1; + + squadmsg_history_index++; + if (squadmsg_history_index >= SQUADMSG_HISTORY_MAX) + squadmsg_history_index = 0; +} + +int hud_query_order_issued(char *name, char *order, char *target) +{ + int i, o=-1, ship, t; + + ship = get_parse_name_index(name); + t = -1; + if (target) + t = get_parse_name_index(target); + + for (i=0; iflags are set, ignore this ship when targetting +int TARGET_SHIP_IGNORE_FLAGS = (SF_EXPLODED|SF_DEPART_WARP|SF_DYING|SF_ARRIVING_STAGE_1|SF_HIDDEN_FROM_SENSORS); + +// Global values for the target bracket width and height, used for debugging +int Hud_target_w, Hud_target_h; + +// offscreen triangle that point the the off-screen target +float Offscreen_tri_base[GR_NUM_RESOLUTIONS] = { + 6.0f, + 9.5f +}; +float Offscreen_tri_height[GR_NUM_RESOLUTIONS] = { + 7.0f, + 11.0f +}; +float Max_offscreen_tri_seperation[GR_NUM_RESOLUTIONS] = { + 10.0f, + 16.0f +}; +float Max_front_seperation[GR_NUM_RESOLUTIONS] = { + 10.0f, + 16.0f +}; + +// The following variables are global to this file, and do not need to be persistent from frame-to-frame +// This means the variables are not player-specific +static int Target_in_reticle = 0; + +extern object obj_used_list; // dummy node in linked list of active objects +extern char *Cargo_names[]; + +// shader is used to shade the target box +shader Training_msg_glass; + +// the target triangle (that orbits the reticle) dimensions +float Target_triangle_base[GR_NUM_RESOLUTIONS] = { + 6.0f, + 9.5f +}; +float Target_triangle_height[GR_NUM_RESOLUTIONS] = { + 7.0f, + 11.0f +}; + +// stuff for hotkey targeting lists +htarget_list htarget_items[MAX_HOTKEY_TARGET_ITEMS]; +htarget_list htarget_free_list; + +// coordinates and widths used to render the HUD afterburner energy gauge +int Aburn_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 171, 265, 60, 60 + }, + { // GR_1024 + 274, 424, 86, 96 + } +}; + +// coordinates and widths used to render the HUD weapons energy gauge +int Wenergy_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 416, 265, 60, 60 + }, + { // GR_1024 + 666, 424, 86, 96 + } +}; + +#define MIN_DISTANCE_TO_CONSIDER_THREAT 1500 // min distance to show hostile warning triangle + +////////////////////////////////////////////////////////////////////////// +// lists for target in reticle cycling +////////////////////////////////////////////////////////////////////////// +#define RL_USED (1<<0) +#define RL_USE_DOT (1<<1) // use dot product result, not distance + +typedef struct _reticle_list { + _reticle_list *next, *prev; + object *objp; + float dist, dot; + int flags; +} reticle_list; + +#define RESET_TARGET_IN_RETICLE 750 +int Reticle_save_timestamp; +reticle_list Reticle_cur_list; +reticle_list Reticle_save_list; +#define MAX_RETICLE_TARGETS 50 +reticle_list Reticle_list[MAX_RETICLE_TARGETS]; + +////////////////////////////////////////////////////////////////////////// +// used for closest target cycling +////////////////////////////////////////////////////////////////////////// +#define TL_RESET 1500 +#define TURRET_RESET 1000 +static int Tl_hostile_reset_timestamp; +static int Tl_friendly_reset_timestamp; +static int Target_next_uninspected_object_timestamp; +static int Target_newest_ship_timestamp; +static int Target_next_turret_timestamp; + +// animation frames for the hud targeting gauges +// frames: 0 => out of range lead +// 1 => in range lead +float Lead_indicator_half[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 8.0f, // half-width + 8.0f // half-height + }, + { // GR_1024 + 13.0f, // half-width + 13.0f // half-height + } +}; +hud_frames Lead_indicator_gauge; +int Lead_indicator_gauge_loaded = 0; +char Lead_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "lead1", + "2_lead1" +}; + +// animation frames for the afterburner gauge and the weapon energy gauge +// frames: 0 => afterburner dark +// 1 => afterburner light +// 2 => gun energy dark +// 3 => gun energy light +hud_frames Energy_bar_gauges; +int Energy_bar_gauges_loaded = 0; +char Energy_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "energy2", + "2_energy2" +}; +int Weapon_energy_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 439, 318 + }, + { // GR_1024 + 708, 509 + } +}; + +// animation frames for the countermeasures gauge +// frames: 0 => background +hud_frames Cmeasure_gauge; +int Cmeasure_gauge_loaded = 0; +int Cm_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 497, 343 + }, + { // GR_1024 + 880, 602 + } +}; +int Cm_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 533, 347 + }, + { // GR_1024 + 916, 606 + } +}; +int Cm_text_val_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 506, 347 + }, + { // GR_1024 + 889, 606 + } +}; +char Cm_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "countermeasure1", + "countermeasure1" +}; + +// animation frames for the auto-target and auto-match_speed icons +// frames: 0 => auto-target off +// 1 => auto-target on +// 2 => auto-match-speed on +// 3 => auto-match-speed off +hud_frames Toggle_gauge; +int Toggle_gauge_loaded = 0; +int Toggle_target_gauge_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 577, 380 + }, + { // GR_1024 + 960, 648 + } +}; +int Toggle_speed_gauge_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 577, 404 + }, + { // GR_1024 + 960, 672 + } +}; +char Toggle_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "toggle1", + "toggle1" +}; + +#define TOGGLE_TEXT_AUTOT 0 +#define TOGGLE_TEXT_TARGET 1 +#define TOGGLE_TEXT_AUTOS 2 +#define TOGGLE_TEXT_SPEED 3 +static int Hud_toggle_coords[GR_NUM_RESOLUTIONS][4][2] = { + { // GR_640 + { 590, 382 }, + { 584, 390 }, + { 590, 406 }, + { 587, 414 } + }, + { // GR_1024 + { 973, 650 }, + { 967, 658 }, + { 973, 674 }, + { 970, 682 } + } +}; + +static int Toggle_text_alpha = 255; + + +// animation files for the weapons gauge +#define NUM_WEAPON_GAUGES 5 +hud_frames Weapon_gauges[NUM_WEAPON_GAUGES]; +int Weapon_gauges_loaded = 0; +// for primaries +int Weapon_gauge_primary_coords[GR_NUM_RESOLUTIONS][3][2] = { + { // GR_640 + // based on the # of primaries + {509, 273}, // top of weapon gauge, first frame, always + {497, 293}, // for the first primary + {497, 305} // for the second primary + }, + { // GR_1024 + // based on the # of primaries + {892, 525}, // top of weapon gauge, first frame, always + {880, 545}, // for the first primary + {880, 557} // for the second primary + } +}; +int Weapon_gauge_secondary_coords[GR_NUM_RESOLUTIONS][5][2] = { + { // GR_640 + // based on the # of secondaries + {497, 318}, // bottom of gauge, 0 secondaries + {497, 318}, // bottom of gauge, 1 secondaries + {497, 317}, // middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries + {497, 326}, // bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries + {497, 335} // bottom of gauge, 3 secondaries + }, + { // GR_1024 + // based on the # of secondaries + {880, 570}, // bottom of gauge, 0 secondaries + {880, 570}, // bottom of gauge, 1 secondaries + {880, 569}, // middle of gauge, 2 secondaries AND middle of gauge, 3 secondaries + {880, 578}, // bottom of gauge, 2 secondaries AND middle of gauge, 3 secondaries + {880, 587} // bottom of gauge, 3 secondaries + } +}; +int Weapon_title_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 518, 275 + }, + { // GR_1024 + 901, 527 + } +}; +int Weapon_plink_coords[GR_NUM_RESOLUTIONS][2][2] = { + { // GR_640 + {530, 285}, // fire-linked thingie, for the first primary + {530, 295} // fire-linked thingie, for the second primary + }, + { // GR_1024 + {913, 537}, // fire-linked thingie, for the first primary + {913, 547} // fire-linked thingie, for the second primary + } +}; +int Weapon_pname_coords[GR_NUM_RESOLUTIONS][2][2] = { + { // GR_640 + {536, 285}, // weapon name, first primary + {536, 295} // weapon name, second primary + }, + { // GR_1024 + {919, 537}, // weapon name, first primary + {919, 547} // weapon name, second primary + } +}; +int Weapon_slinked_x[GR_NUM_RESOLUTIONS] = { + 525, // where to draw the second thingie if this weapon is fire-linked + 908 +}; +int Weapon_sunlinked_x[GR_NUM_RESOLUTIONS] = { + 530, // where to draw the first thingie if this weapon is selected at all (fire-linked or not) + 913 +}; +int Weapon_secondary_y[GR_NUM_RESOLUTIONS][3] = { + { // GR_640 + 309, // y location of where to draw text for the first secondary + 318, // y location of where to draw text for the second secondary + 327 // y location of where to draw text for the third secondary + }, + { // GR_1024 + 561, // y location of where to draw text for the third secondary + 570, // y location of where to draw text for the third secondary + 579 // y location of where to draw text for the third secondary + } +}; +int Weapon_secondary_name_x[GR_NUM_RESOLUTIONS] = { + 536, // x location of where to draw weapon name + 919 +}; +int Weapon_secondary_ammo_x[GR_NUM_RESOLUTIONS] = { + 525, // x location of where to draw weapon ammo count + 908 +}; +int Weapon_secondary_reload_x[GR_NUM_RESOLUTIONS] = { + 615, // x location of where to draw the weapon reload time + 998 +}; +char *Weapon_gauge_fnames[GR_NUM_RESOLUTIONS][NUM_WEAPON_GAUGES] = +{ +//XSTR:OFF + { // GR_640 + "weapons1", + "weapons2", + "weapons3", + "weapons4", + "weapons5" + }, + { // GR_1024 + "weapons1", + "weapons2", + "weapons3", + "weapons4", + "weapons5" + } +//XSTR:ON +}; + +// Flash the line for a weapon. This normally occurs when the player tries to fire that +// weapon, but the firing fails (due to lack of energy or damaged weapons subsystem). +#define MAX_WEAPON_FLASH_LINES 7 // 3 primary and 4 secondary +typedef struct weapon_flash +{ + int flash_duration[MAX_WEAPON_FLASH_LINES]; + int flash_next[MAX_WEAPON_FLASH_LINES]; + int is_bright; +} weapon_flash; +weapon_flash Weapon_flash_info; + +// Data used for the proximity warning +typedef struct homing_beep_info +{ + int snd_handle; // sound handle for last played beep + fix last_time_played; // time beep was last played + int min_cycle_time; // time (in ms) for fastest cycling of the sound + int max_cycle_time; // time (in ms) for slowest cycling of the sound + float min_cycle_dist; // distance at which fastest cycling occurs + float max_cycle_dist; // distance at which slowest cycling occurs + float precalced_interp; // a precalculated value used in a linear interpretation +} homing_beep_info; + +homing_beep_info Homing_beep = { -1, 0, 150, 1000, 30.0f, 1500.0f }; + +// Set at the start of a mission, used to decide how to draw the separation for the warning missile indicators +float Min_warning_missile_dist; +float Max_warning_missile_dist; + +void hud_maybe_flash_weapon(int index); + +// if a given object should be ignored because of AWACS effects +int hud_target_invalid_awacs(object *objp) +{ + // if objp is ship object, first check if can be targeted with team info + if (objp->type == OBJ_SHIP) { + if (Player_ship != NULL) { + if (ship_is_visible_by_team(objp->instance, Player_ship->team)) { + return 0; + } + } + } + + // check for invalid status + if((Player_ship != NULL) && (awacs_get_level(objp, Player_ship) < 1.0f)){ + return 1; + } + + // valid + return 0; +} + +ship_subsys *advance_subsys(ship_subsys *cur, int next_flag) +{ + if (next_flag) { + return GET_NEXT(cur); + } else { + return GET_LAST(cur); + } +} + +// select a sorted turret subsystem on a ship if no other subsys has been selected +void hud_maybe_set_sorted_turret_subsys(ship *shipp) +{ + Assert((Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS)); + if (!((Player_ai->target_objnum >= 0) && (Player_ai->target_objnum < MAX_OBJECTS))) { + return; + } + Assert(Objects[Player_ai->target_objnum].type == OBJ_SHIP); + if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) { + return; + } + + if (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) { + if (shipp->last_targeted_subobject[Player_num] == NULL) { + hud_target_live_turret(1, 1); + } + } + +} + +// ----------------------------------------------------------------------- +// clear out the linked list of targets in the reticle +void hud_reticle_clear_list(reticle_list *rlist) +{ + reticle_list *cur; + for ( cur = GET_FIRST(rlist); cur != END_OF_LIST(rlist); cur = GET_NEXT(cur) ) { + cur->flags = 0; + } + list_init(rlist); +} + +// -------------------------------------------------------------------------------------- +// hud_reticle_list_init() +void hud_reticle_list_init() +{ + int i; + + for ( i = 0; i < MAX_RETICLE_TARGETS; i++ ) { + Reticle_list[i].flags = 0; + } + + Reticle_save_timestamp = 1; + list_init(&Reticle_save_list); + list_init(&Reticle_cur_list); +} + +// -------------------------------------------------------------------------------------- +// hud_check_reticle_list() +// +// +void hud_check_reticle_list() +{ + reticle_list *rl, *temp; + + // cull dying objects from reticle list + rl = GET_FIRST(&Reticle_cur_list); + while( rl !=END_OF_LIST(&Reticle_cur_list) ) { + temp = GET_NEXT(rl); + if ( rl->objp->flags & OF_SHOULD_BE_DEAD ) { + list_remove( &Reticle_cur_list, rl ); + rl->flags = 0; + } + rl = temp; + } + + if ( timestamp_elapsed(Reticle_save_timestamp) ) { + hud_reticle_clear_list(&Reticle_save_list); + Reticle_save_timestamp = timestamp(RESET_TARGET_IN_RETICLE); + } +} + +// -------------------------------------------------------------------------------------- +// hud_reticle_list_find_free() +// +// +int hud_reticle_list_find_free() +{ + int i; + + // find a free reticle_list element + for ( i = 0; i < MAX_RETICLE_TARGETS; i++ ) { + if ( !(Reticle_list[i].flags & RL_USED) ) { + break; + } + } + + if ( i == MAX_RETICLE_TARGETS ) { +// nprintf(("Warning","Warning ==> Ran out of reticle target elements...\n")); + return -1; + } + + return i; +} + +// -------------------------------------------------------------------------------------- +// hud_stuff_reticle_list() +// +// +#define RETICLE_DEFAULT_DIST 100000.0f +#define RETICLE_DEFAULT_DOT 1.0f +void hud_stuff_reticle_list(reticle_list *rl, object *objp, float measure, int dot_flag) +{ + if ( dot_flag ) { + rl->dot = measure; + rl->dist = RETICLE_DEFAULT_DIST; + rl->flags |= RL_USE_DOT; + } + else { + rl->dist = measure; + rl->dot = RETICLE_DEFAULT_DOT; + } + rl->objp = objp; +} + +// -------------------------------------------------------------------------------------- +// hud_reticle_list_update() +// +// Update Reticle_cur_list with an object that lies in the reticle +// +// parmeters: objp => object pointer to target +// measure => distance or dot product, depending on dot_flag +// dot_flag => if 0, measure is distance, if 1 measure is dot +// +void hud_reticle_list_update(object *objp, float measure, int dot_flag) +{ + reticle_list *rl, *new_rl; + int i; + + for ( rl = GET_FIRST(&Reticle_cur_list); rl != END_OF_LIST(&Reticle_cur_list); rl = GET_NEXT(rl) ) { + if ( rl->objp == objp ) + return; + } + + i = hud_reticle_list_find_free(); + if ( i == -1 ) + return; + + new_rl = &Reticle_list[i]; + new_rl->flags |= RL_USED; + hud_stuff_reticle_list(new_rl, objp, measure, dot_flag); + + int was_inserted = 0; + + if ( EMPTY(&Reticle_cur_list) ) { + list_insert(&Reticle_cur_list, new_rl); + was_inserted = 1; + } + else { + for ( rl = GET_FIRST(&Reticle_cur_list); rl != END_OF_LIST(&Reticle_cur_list); rl = GET_NEXT(rl) ) { + if ( !dot_flag ) { + // compare based on distance + if ( measure < rl->dist ) { + list_insert_before(rl, new_rl); + was_inserted = 1; + break; + } + } + else { + // compare based on dot + if ( measure > rl->dot ) { + list_insert_before(rl, new_rl); + was_inserted = 1; + break; + } + } + } // end for + } + + if ( !was_inserted ) { + list_append(&Reticle_cur_list, new_rl); + } +} + +// -------------------------------------------------------------------------------------- +// hud_reticle_pick_target() +// +// Pick a target from Reticle_cur_list, based on what is in Reticle_save_list +// +// +object *hud_reticle_pick_target() +{ + reticle_list *cur_rl, *save_rl, *new_rl; + object *return_objp; + int in_save_list, i; + + return_objp = NULL; + + // As a first step, see if both ships and debris are in the list. If so, cull the debris. + int debris_in_list = 0; + int ship_in_list = 0; + for ( cur_rl = GET_FIRST(&Reticle_cur_list); cur_rl != END_OF_LIST(&Reticle_cur_list); cur_rl = GET_NEXT(cur_rl) ) { + if ( (cur_rl->objp->type == OBJ_SHIP) || (cur_rl->objp->type == OBJ_JUMP_NODE) ) { + ship_in_list = 1; + continue; + } + + if ( cur_rl->objp->type == OBJ_WEAPON ) { + if ( Weapon_info[Weapons[cur_rl->objp->instance].weapon_info_index].subtype == WP_MISSILE ) { + ship_in_list = 1; + continue; + } + } + + if ( (cur_rl->objp->type == OBJ_DEBRIS) || (cur_rl->objp->type == OBJ_ASTEROID) ) { + debris_in_list = 1; + continue; + } + } + + if ( ship_in_list && debris_in_list ) { + // cull debris + reticle_list *rl, *next; + + rl = GET_FIRST(&Reticle_cur_list); + while ( rl != &Reticle_cur_list ) { + next = rl->next; + if ( (rl->objp->type == OBJ_DEBRIS) || (rl->objp->type == OBJ_ASTEROID) ){ + list_remove(&Reticle_cur_list,rl); + rl->flags = 0; + } + rl = next; + } + } + + for ( cur_rl = GET_FIRST(&Reticle_cur_list); cur_rl != END_OF_LIST(&Reticle_cur_list); cur_rl = GET_NEXT(cur_rl) ) { + in_save_list = 0; + for ( save_rl = GET_FIRST(&Reticle_save_list); save_rl != END_OF_LIST(&Reticle_save_list); save_rl = GET_NEXT(save_rl) ) { + if ( cur_rl->objp == save_rl->objp ) { + in_save_list = 1; + break; + } + } + + if ( !in_save_list ) { + return_objp = cur_rl->objp; + i = hud_reticle_list_find_free(); + if ( i == -1 ) + break; + + new_rl = &Reticle_list[i]; + new_rl->flags |= RL_USED; + if ( cur_rl->flags & RL_USE_DOT ) { + hud_stuff_reticle_list(new_rl, cur_rl->objp, cur_rl->dot, 1); + } + else { + hud_stuff_reticle_list(new_rl, cur_rl->objp, cur_rl->dist, 0); + } + + list_append(&Reticle_save_list, new_rl); + break; + } + } // end for + + if ( return_objp == NULL && !EMPTY(&Reticle_cur_list) ) { + i = hud_reticle_list_find_free(); + if ( i == -1 ) + return NULL; + new_rl = &Reticle_list[i]; + cur_rl = GET_FIRST(&Reticle_cur_list); + *new_rl = *cur_rl; + return_objp = cur_rl->objp; + hud_reticle_clear_list(&Reticle_save_list); + list_append(&Reticle_save_list, new_rl); + } + + return return_objp; +} + +// hud_target_hotkey_add_remove takes as it's parameter which hotkey (1-0) to add/remove the current +// target from. This functio behaves like the Shift- does in Windows -- using shift # will toggle +// the current target in and out of the selection set. +void hud_target_hotkey_add_remove( int k, object *ctarget, int how_to_add ) +{ + htarget_list *hitem, *plist; + + // don't do anything if a standalone multiplayer server + if ( MULTIPLAYER_STANDALONE ) + return; + + if ( k < 0 || k > 7 ) { + nprintf(("Warning", "Bogus hotkey %d sent to hud_target_hotkey_add_remove\n")); + return; + } + + plist = &(Players[Player_num].keyed_targets[k]); + + // we must operate only on ships + if ( ctarget->type != OBJ_SHIP ) + return; + + // don't allow player into hotkey set + if ( ctarget == Player_obj ) + return; + + // don't put dying or departing + if ( Ships[ctarget->instance].flags & (SF_DYING|SF_DEPARTING) ) + return; + + // don't add mission file added hotkey assignments if there are player added assignments + // already in the list + if ( (how_to_add == HOTKEY_MISSION_FILE_ADDED) && NOT_EMPTY(plist) ) { + for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) { + if ( hitem->how_added == HOTKEY_USER_ADDED ) + return; + } + } + + // determine if the current target is currently in the set or not + for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) { + if ( hitem->objp == ctarget ) + break; + } + + // if hitem == end of the list, then the target should be added, else it should be removed + if ( hitem == END_OF_LIST(plist) ) { + if ( EMPTY(&htarget_free_list) ) { + Int3(); // get Allender -- no more free hotkey target items + return; + } + + nprintf(("network", "Hotkey: Adding %s\n", Ships[ctarget->instance].ship_name)); + hitem = GET_FIRST( &htarget_free_list ); + list_remove( &htarget_free_list, hitem ); + list_append( plist, hitem ); + hitem->objp = ctarget; + hitem->how_added = how_to_add; + } else { + nprintf(("network", "Hotkey: Removing %s\n", Ships[ctarget->instance].ship_name)); + list_remove( plist, hitem ); + list_append( &htarget_free_list, hitem ); + hitem->objp = NULL; // for safety + } +} + +// the following function clears the hotkey set given by parameter passed in +void hud_target_hotkey_clear( int k ) +{ + htarget_list *hitem, *plist, *temp; + + plist = &(Players[Player_num].keyed_targets[k]); + hitem = GET_FIRST(plist); + while ( hitem != END_OF_LIST(plist) ) { + temp = GET_NEXT(hitem); + list_remove( plist, hitem ); + list_append( &htarget_free_list, hitem ); + hitem->objp = NULL; + hitem = temp; + } + if ( Players[Player_num].current_hotkey_set == k ) // clear this variable if we removed the bindings + Players[Player_num].current_hotkey_set = -1; +} + +// the next function sets the current selected set to be N. If there is just one ship in the selection +// set, this ship will become the new target. If there is more than one ship in the selection set, +// then the current_target will remain what it was. +void hud_target_hotkey_select( int k ) +{ + int visible_count = 0; + htarget_list *hitem, *plist, *target, *next_target, *first_target; + int target_objnum; + + plist = &(Players[Player_num].keyed_targets[k]); + + if ( EMPTY( plist ) ) // no items in list, then do nothing + return; + + // a simple walk of the list to get the count + for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ){ + if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) { + visible_count++; + } + } + + // no visible ships in list + if (visible_count == 0) { + return; + } + + // set the current target to be the "next" ship in the list. Scan the list to see if our + // current target is in the set. If so, target the next ship in the list, otherwise target + // the first + // set first_target - first visible item in list + // target - item in list that is the player's currently selected target + // next_target - next visible item in list following target + target_objnum = Player_ai->target_objnum; + target = NULL; + next_target = NULL; + first_target = NULL; + for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) { + + if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) { + // get the first valid target + if (first_target == NULL) { + first_target = hitem; + } + + // get the next target in the list following the player currently selected target + if (target != NULL) { + next_target = hitem; + break; + } + } + + // mark the player currently selected target + if ( OBJ_INDEX(hitem->objp) == target_objnum ) { + target = hitem; + } + } + + // if current target is not in list, then target and next_target will be NULL + // so we use the first found target + if (target == NULL) { + Assert(first_target != NULL); + if (first_target != NULL) { + target = first_target; + next_target = first_target; + } else { + // this should not happen + return; + } + } + + // update target if more than 1 is visible + if (visible_count > 1) { + // next already found (after current target in list) + if (next_target != NULL) { + target = next_target; + } else { + + // next is before current target, so search from start of list + for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) { + if (awacs_get_level(hitem->objp, Player_ship, 1) > 1) { + target = hitem; + break; + } + } + } + } + + Assert( target != END_OF_LIST(plist) ); + + if ( Player_obj != target->objp ){ + set_target_objnum( Player_ai, OBJ_INDEX(target->objp) ); + } + + Players[Player_num].current_hotkey_set = k; +} + +// hud_init_targeting_colors() will initalize the shader and gradient objects used +// on the HUD +// + +color HUD_color_homing_indicator; + +void hud_make_shader(shader *sh, int r, int g, int b, float dimmer = 1000.0f) +{ + float rf,gf,bf,cf; + + // The m matrix converts all colors to shades of green + float tmp = 0.0015625f * i2fl(HUD_color_alpha+1.0f) / 16.0f; + + rf = tmp*r; + gf = tmp*r; + bf = tmp*r; + cf = (i2fl(r) / dimmer)*(i2fl(HUD_color_alpha) / 15.0f); + + gr_create_shader( sh, rf, gf, bf, cf ); +} + +void hud_init_targeting_colors() +{ + gr_init_color( &HUD_color_homing_indicator, 0x7f, 0x7f, 0 ); // yellow + + hud_make_shader(&Training_msg_glass, 61, 61, 85, 500.0f); + + hud_init_brackets(); +} + +void hud_keyed_targets_clear() +{ + int i; + + // clear out the keyed target list + for (i = 0; i < MAX_KEYED_TARGETS; i++ ) + list_init( &(Players[Player_num].keyed_targets[i]) ); + Players[Player_num].current_hotkey_set = -1; + + // place all of the hoykey target items back onto the free list + list_init( &htarget_free_list ); + for ( i = 0; i < MAX_HOTKEY_TARGET_ITEMS; i++ ) + list_append( &htarget_free_list, &htarget_items[i] ); +} + +// Init data used for the weapons display on the HUD +void hud_weapons_init() +{ + int i; + + Weapon_flash_info.is_bright = 0; + for ( i = 0; i < MAX_WEAPON_FLASH_LINES; i++ ) { + Weapon_flash_info.flash_duration[i] = 1; + Weapon_flash_info.flash_next[i] = 1; + } + + if ( !Weapon_gauges_loaded ) { + for ( i = 0; i < NUM_WEAPON_GAUGES; i++ ) { + Weapon_gauges[i].first_frame = bm_load_animation(Weapon_gauge_fnames[gr_screen.res][i], &Weapon_gauges[i].num_frames); + if ( Weapon_gauges[i].first_frame < 0 ) { + Warning(LOCATION,"Cannot load hud ani: %s\n",Weapon_gauge_fnames[gr_screen.res][i]); + } + } + Weapon_gauges_loaded = 1; + } +} + +// init data used to play the homing "proximity warning" sound +void hud_init_homing_beep() +{ + Homing_beep.snd_handle = -1; + Homing_beep.last_time_played = 0; + Homing_beep.precalced_interp = (Homing_beep.max_cycle_dist-Homing_beep.min_cycle_dist) / (Homing_beep.max_cycle_time - Homing_beep.min_cycle_time ); +} + +// hud_init_targeting() will set the current target to point to the dummy node +// in the object used list +// +void hud_init_targeting() +{ + Assert(Player_ai != NULL); + + // make sure there is no current target + set_target_objnum( Player_ai, -1 ); + Player_ai->last_target = -1; + Player_ai->last_subsys_target = NULL; + Player_ai->last_dist = 0.0f; + Player_ai->last_speed = 0.0f; + + hud_keyed_targets_clear(); + hud_init_missile_lock(); + hud_init_artillery(); + + // Init the lists that hold targets in reticle (to allow cycling of targets in reticle) + hud_reticle_list_init(); + hud_init_homing_beep(); + + // Load in the frames need for the lead indicator + if (!Lead_indicator_gauge_loaded) { + Lead_indicator_gauge.first_frame = bm_load_animation(Lead_fname[gr_screen.res], &Lead_indicator_gauge.num_frames); + if ( Lead_indicator_gauge.first_frame < 0 ) { + Warning(LOCATION,"Cannot load hud ani: %s\n", Lead_fname[gr_screen.res]); + } + Lead_indicator_gauge_loaded = 1; + } + + if (!Energy_bar_gauges_loaded) { + Energy_bar_gauges.first_frame = bm_load_animation(Energy_fname[gr_screen.res], &Energy_bar_gauges.num_frames); + if ( Energy_bar_gauges.first_frame < 0 ) { + Warning(LOCATION,"Cannot load hud ani: %s\n", Energy_fname[gr_screen.res]); + } + Energy_bar_gauges_loaded = 1; + } + + if (!Toggle_gauge_loaded) { + Toggle_gauge.first_frame = bm_load_animation(Toggle_fname[gr_screen.res], &Toggle_gauge.num_frames); + if ( Toggle_gauge.first_frame < 0 ) { + Warning(LOCATION,"Cannot load hud ani: %s\n", Toggle_fname[gr_screen.res]); + } + Toggle_gauge_loaded = 1; + } + + if (!Cmeasure_gauge_loaded) { + Cmeasure_gauge.first_frame = bm_load_animation(Cm_fname[gr_screen.res], &Cmeasure_gauge.num_frames); + if ( Cmeasure_gauge.first_frame < 0 ) { + Warning(LOCATION,"Cannot load hud ani: %s\n", Cm_fname[gr_screen.res]); + } + Cmeasure_gauge_loaded = 1; + } + + + hud_weapons_init(); + + Min_warning_missile_dist = 2.5f*Player_obj->radius; + Max_warning_missile_dist = 1500.0f; + + Tl_hostile_reset_timestamp = timestamp(0); + Tl_friendly_reset_timestamp = timestamp(0); + Target_next_uninspected_object_timestamp = timestamp(0); + Target_newest_ship_timestamp = timestamp(0); + Target_next_turret_timestamp = timestamp(0); + + if(The_mission.flags & MISSION_FLAG_FULLNEB) { + Toggle_text_alpha = 127; + } else { + Toggle_text_alpha = 160; + } + + +} + +// Target the next or previous subobject on the currently selected ship, based on next_flag. +void hud_target_subobject_common(int next_flag) +{ + if (Player_ai->target_objnum == -1) { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No target selected.", 322)); + snd_play( &Snds[SND_TARGET_FAIL] ); + return; + } + + if (Objects[Player_ai->target_objnum].type != OBJ_SHIP) { + snd_play( &Snds[SND_TARGET_FAIL]); + return; + } + + ship_subsys *start, *start2, *A; + ship_subsys *subsys_to_target=NULL; + ship *target_shipp; + + target_shipp = &Ships[Objects[Player_ai->target_objnum].instance]; + + if (!Player_ai->targeted_subsys) { + start = GET_FIRST(&target_shipp->subsys_list); + } else { + start = Player_ai->targeted_subsys; + } + + start2 = advance_subsys(start, next_flag); + + for ( A = start2; A != start; A = advance_subsys(A, next_flag) ) { + + if ( A == &target_shipp->subsys_list ) { + continue; + } + + // ignore turrets + if ( A->system_info->type == SUBSYSTEM_TURRET ) { + continue; + } + + subsys_to_target = A; + break; + + } // end for + + if ( subsys_to_target == NULL ) { + snd_play( &Snds[SND_TARGET_FAIL]); + } else { + set_targeted_subsys(Player_ai, subsys_to_target, Player_ai->target_objnum); + target_shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys; + } +} + +object *advance_fb(object *objp, int next_flag) +{ + if (next_flag) + return GET_NEXT(objp); + else + return GET_LAST(objp); +} + +// Target the previous subobject on the currently selected ship. +// + +void hud_target_prev_subobject() +{ + hud_target_subobject_common(0); +} + +void hud_target_next_subobject() +{ + hud_target_subobject_common(1); +} + +// hud_target_next() will set the Players[Player_num].current_target to the next target in the object +// used list whose team matches the team parameter. The player is NOT included in the target list. +// +// parameters: team => team of ship to target next. Default value is -1, if team doesn't matter. +// + +void hud_target_common(int team, int next_flag) +{ + object *A, *start, *start2; + ship *shipp; + int is_ship, target_found = FALSE; + + if (Player_ai->target_objnum == -1) + start = &obj_used_list; + else + start = &Objects[Player_ai->target_objnum]; + + start2 = advance_fb(start, next_flag); + + for ( A = start2; A != start; A = advance_fb(A, next_flag) ) { + is_ship=0; + + if ( A == &obj_used_list ) { + continue; + } + + if (A == Player_obj || ( A->type != OBJ_SHIP && A->type != OBJ_WEAPON && A->type != OBJ_JUMP_NODE) ){ + continue; + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + if ( A->type == OBJ_WEAPON ) { + if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags & WIF_BOMB) ){ + continue; + } + } + + if ( A->type == OBJ_SHIP ) { + if ( Ships[A->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){ + continue; + } + is_ship=1; + } + + if ( vm_vec_same( &A->pos, &Eye_position ) ) { + continue; + } + + if ( is_ship ) { + shipp = &Ships[A->instance]; // get a pointer to the ship information + + if ( !hud_team_matches_filter(team, shipp->team) ) { + // if we're in multiplayer dogfight, ignore this + if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){ + continue; + } + } + + if ( A == Player_obj || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ){ + continue; + } + + // if we've reached here, it is a valid next target + if ( Player_ai->target_objnum != A-Objects ) { + target_found = TRUE; + set_target_objnum( Player_ai, OBJ_INDEX(A) ); + // if ship is BIG|HUGE and last subsys is NULL, get turret + hud_maybe_set_sorted_turret_subsys(shipp); + hud_restore_subsystem_target(shipp); + } + } else { + target_found = TRUE; + set_target_objnum( Player_ai, OBJ_INDEX(A) ); + } + + break; + } + + if ( target_found == FALSE ) { + snd_play( &Snds[SND_TARGET_FAIL] ); + } +} + +void hud_target_next(int team) +{ + hud_target_common(team, 1); +} + +void hud_target_prev(int team) +{ + hud_target_common(team, 0); +} + +// ------------------------------------------------------------------- +// advance_missile_obj() +// +missile_obj *advance_missile_obj(missile_obj *mo, int next_flag) +{ + if (next_flag){ + return GET_NEXT(mo); + } + + return GET_LAST(mo); +} + +ship_obj *advance_ship(ship_obj *so, int next_flag) +{ + if (next_flag){ + return GET_NEXT(so); + } + + return GET_LAST(so); +} + +ship_obj *get_ship_obj_ptr_from_index(int index); +// ------------------------------------------------------------------- +// hud_target_missile() +// +// Target the closest locked missile that is locked on locked_obj +// +// input: source_obj => pointer to object that fired weapon +// next_flag => 0 -> previous 1 -> next +// +// NOTE: this function is only allows targeting bombs +void hud_target_missile(object *source_obj, int next_flag) +{ + missile_obj *end, *start, *mo; + object *A, *target_objp; + ai_info *aip; + weapon *wp; + weapon_info *wip; + int target_found = 0; + + if ( source_obj->type != OBJ_SHIP ) + return; + + Assert( Ships[source_obj->instance].ai_index != -1 ); + aip = &Ai_info[Ships[source_obj->instance].ai_index]; + + end = &Missile_obj_list; + if (aip->target_objnum != -1) { + target_objp = &Objects[aip->target_objnum]; + if ( target_objp->type == OBJ_WEAPON && Weapon_info[Weapons[target_objp->instance].weapon_info_index].subtype == WP_MISSILE ) { // must be a missile + end = missile_obj_return_address(Weapons[target_objp->instance].missile_list_index); + } + } + + start = advance_missile_obj(end, next_flag); + + for ( mo = start; mo != end; mo = advance_missile_obj(mo, next_flag) ) { + if ( mo == &Missile_obj_list ){ + continue; + } + + Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS); + A = &Objects[mo->objnum]; + + Assert(A->type == OBJ_WEAPON); + Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS)); + wp = &Weapons[A->instance]; + wip = &Weapon_info[wp->weapon_info_index]; + + // only allow targeting of bombs + if ( !(wip->wi_flags & WIF_BOMB) ) { + continue; + } + + // only allow targeting of hostile bombs + if ( (obj_team(A) == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) { + continue; + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + // if we've reached here, got a new target + target_found = TRUE; + set_target_objnum( aip, OBJ_INDEX(A) ); + break; + } // end for + + if ( !target_found ) { + // if no bomb is found, search for bombers + ship_obj *start, *so; + + extern ship_obj *Ship_objs; + if ( (aip->target_objnum != -1) && (Objects[aip->target_objnum].type == OBJ_SHIP) && (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & SIF_BOMBER) ) { + int index = Ships[Objects[aip->target_objnum].instance].ship_list_index; + start = get_ship_obj_ptr_from_index(index); + } else { + start = GET_FIRST(&Ship_obj_list); + } + + for (so=advance_ship(start, next_flag); so!=start; so=advance_ship(so, next_flag)) { + object *ship_obj = &Objects[so->objnum]; + + // don't look at header + if (so == &Ship_obj_list) { + continue; + } + + // only allow targeting of hostile bombs + if ( (obj_team(ship_obj) == Player_ship->team) && (Player_ship->team != TEAM_TRAITOR) ) { + continue; + } + + if(hud_target_invalid_awacs(ship_obj)){ + continue; + } + + // check if ship type is bomber + if ( !(Ship_info[Ships[ship_obj->instance].ship_info_index].flags & SIF_BOMBER) ) { + continue; + } + + // check if ignore + if ( Ships[ship_obj->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){ + continue; + } + + // found a good one + target_found = TRUE; + set_target_objnum( aip, OBJ_INDEX(ship_obj) ); + break; + } + } + + if ( !target_found ) { + snd_play( &Snds[SND_TARGET_FAIL], 0.0f ); + } +} + +// Return !0 if shipp can be scanned, otherwise return 0 +int hud_target_ship_can_be_scanned(ship *shipp) +{ + ship_info *sip; + + sip = &Ship_info[shipp->ship_info_index]; + + // ignore cargo that has already been scanned + if ( shipp->flags & SF_CARGO_REVEALED ) { + return 0; + } + + // allow ships with scannable flag set + if ( shipp->flags & SF_SCANNABLE ) { + return 1; + } + + // ignore ships that don't carry cargo + if ( !(sip->flags & (SIF_CARGO|SIF_FREIGHTER)) ) { + return 0; + } + + return 1; +} + +// target the next/prev uninspected cargo container +void hud_target_uninspected_cargo(int next_flag) +{ + object *A, *start, *start2; + ship *shipp; + int target_found = 0; + + if (Player_ai->target_objnum == -1) { + start = &obj_used_list; + } else { + start = &Objects[Player_ai->target_objnum]; + } + + start2 = advance_fb(start, next_flag); + + for ( A = start2; A != start; A = advance_fb(A, next_flag) ) { + if ( A == &obj_used_list ) { + continue; + } + + if (A == Player_obj || (A->type != OBJ_SHIP) ) { + continue; + } + + shipp = &Ships[A->instance]; // get a pointer to the ship information + + if ( shipp->flags & TARGET_SHIP_IGNORE_FLAGS ) { + continue; + } + + // ignore all non-cargo carrying craft + if ( !hud_target_ship_can_be_scanned(shipp) ) { + continue; + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + // if we've reached here, it is a valid next target + if ( Player_ai->target_objnum != OBJ_INDEX(A) ) { + target_found = TRUE; + set_target_objnum( Player_ai, OBJ_INDEX(A) ); + } + } + + if ( target_found == FALSE ) { + snd_play( &Snds[SND_TARGET_FAIL]); + } +} + +// target the newest ship in the area +void hud_target_newest_ship() +{ + object *A, *player_target_objp; + object *newest_obj=NULL; + ship *shipp; + ship_obj *so; + uint current_target_arrived_time = 0xffffffff, newest_time = 0; + + if ( Player_ai->target_objnum >= 0 ) { + player_target_objp = &Objects[Player_ai->target_objnum]; + if ( player_target_objp->type == OBJ_SHIP ) { + current_target_arrived_time = Ships[player_target_objp->instance].create_time; + } + } else { + player_target_objp = NULL; + } + + // If no target is selected, then simply target the closest uninspected cargo + if ( Player_ai->target_objnum == -1 || timestamp_elapsed(Target_newest_ship_timestamp) ) { + current_target_arrived_time = 0xffffffff; + } + + Target_newest_ship_timestamp = timestamp(TL_RESET); + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + A = &Objects[so->objnum]; + shipp = &Ships[A->instance]; // get a pointer to the ship information + + if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ) + continue; + + // ignore navbuoys + if ( Ship_info[shipp->ship_info_index].flags & SIF_NAVBUOY ) { + continue; + } + + if ( A == player_target_objp ) { + continue; + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + if ( (shipp->create_time >= newest_time) && (shipp->create_time <= current_target_arrived_time) ) { + newest_time = shipp->create_time; + newest_obj = A; + } + } + + if (newest_obj) { + set_target_objnum( Player_ai, OBJ_INDEX(newest_obj) ); + // if BIG|HUGE and no selected subsystem, get sorted turret + hud_maybe_set_sorted_turret_subsys(&Ships[newest_obj->instance]); + hud_restore_subsystem_target(&Ships[newest_obj->instance]); + } + else { + snd_play( &Snds[SND_TARGET_FAIL]); + } +} + +#define TYPE_NONE 0 +#define TYPE_FACING_BEAM 1 +#define TYPE_FACING_FLAK 2 +#define TYPE_FACING_MISSILE 3 +#define TYPE_FACING_LASER 4 +#define TYPE_NONFACING_BEAM 5 +#define TYPE_NONFACING_FLAK 6 +#define TYPE_NONFACING_MISSILE 7 +#define TYPE_NONFACING_LASER 8 +#define TYPE_NONFACING_INC 4 + +typedef struct eval_next_turret { + ship_subsys *ss; + int type; + float dist; +} eval_next_turret; + +int sort_turret_func(const void *e1, const void *e2) +{ + eval_next_turret *p1 = (eval_next_turret*)e1; + eval_next_turret *p2 = (eval_next_turret*)e2; + + Assert(p1->type != TYPE_NONE); + Assert(p2->type != TYPE_NONE); + + if (p1->type != p2->type) { + return (p1->type - p2->type); + } else { + float delta_dist = p1->dist - p2->dist; + if (delta_dist < 0) { + return -1; + } else if (delta_dist > 0) { + return 1; + } else { + return 0; + } + } +} + +// target the next/prev live turret on the current target +// auto_advance from hud_update_closest_turret +void hud_target_live_turret(int next_flag, int auto_advance, int only_player_target) +{ + ship_subsys *A; + ship_subsys *live_turret=NULL; + ship *target_shipp; + object *objp; + eval_next_turret ent[MAX_MODEL_SUBSYSTEMS]; + int num_live_turrets = 0; + + // make sure we're targeting a ship + if (Player_ai->target_objnum == -1 && !auto_advance) { + snd_play(&Snds[SND_TARGET_FAIL]); + return; + } + + // only targeting subsystems on ship + if ((Objects[Player_ai->target_objnum].type != OBJ_SHIP) && (!auto_advance)) { + snd_play( &Snds[SND_TARGET_FAIL]); + return; + } + + // set some pointers + objp = &Objects[Player_ai->target_objnum]; + target_shipp = &Ships[objp->instance]; + + // set timestamp + int timestamp_val = 0; + if (!auto_advance) { + timestamp_val = Target_next_turret_timestamp; + Target_next_turret_timestamp = timestamp(TURRET_RESET); + } + + // If no target is selected, then simply target the closest (or facing) turret + int last_subsys_turret = FALSE; + if (Player_ai->targeted_subsys != NULL) { + if (Player_ai->targeted_subsys->system_info->type == SUBSYSTEM_TURRET) { + if (Player_ai->targeted_subsys->system_info->turret_weapon_type >= 0) { + last_subsys_turret = TRUE; + } + } + } + + // do we want the closest turret (or the one our ship is pointing at) + int get_closest_turret = (auto_advance || !last_subsys_turret || timestamp_elapsed(timestamp_val)); + + // initialize eval struct + memset(ent,0, sizeof(ent)); + int use_straigh_ahead_turret = FALSE; + + // go through list of turrets + for (A=GET_FIRST(&target_shipp->subsys_list); A!=END_OF_LIST(&target_shipp->subsys_list); A=GET_NEXT(A)) { + // get a turret + if (A->system_info->type == SUBSYSTEM_TURRET) { + // check turret has hit points and has a weapon + if ( (A->current_hits > 0) && (A->system_info->turret_weapon_type >= 0) ) { + if ( !only_player_target || (A->turret_enemy_objnum == OBJ_INDEX(Player_obj)) ) { + vector gsubpos, vec_to_subsys; + float distance, dot; + // get world pos of subsystem and its distance + get_subsystem_world_pos(objp, A, &gsubpos); + distance = vm_vec_normalized_dir(&vec_to_subsys, &gsubpos, &View_position); + + // check if facing and in view + int facing = ship_subsystem_in_sight(objp, A, &View_position, &gsubpos, 0); + + if (!auto_advance && get_closest_turret && !only_player_target) { + // if within 3 degrees and not previous subsys, use subsys in front + dot = vm_vec_dotprod(&vec_to_subsys, &Player_obj->orient.fvec); + if ((dot > 0.9986) && facing) { + use_straigh_ahead_turret = TRUE; + break; + } + } + + // set weapon_type to allow sort of ent on type + if (Weapon_info[A->system_info->turret_weapon_type].wi_flags & WIF_BEAM) { + ent[num_live_turrets].type = TYPE_FACING_BEAM; + } else if (Weapon_info[A->system_info->turret_weapon_type].wi_flags & WIF_FLAK) { + ent[num_live_turrets].type = TYPE_FACING_FLAK; + } else { + if (Weapon_info[A->system_info->turret_weapon_type].subtype == WP_MISSILE) { + ent[num_live_turrets].type = TYPE_FACING_MISSILE; + } else if (Weapon_info[A->system_info->turret_weapon_type].subtype == WP_LASER) { + ent[num_live_turrets].type = TYPE_FACING_LASER; + } else { + Int3(); + ent[num_live_turrets].type = TYPE_FACING_LASER; + } + } + + // fill out ent struct + ent[num_live_turrets].ss = A; + ent[num_live_turrets].dist = distance; + if (!facing) { + ent[num_live_turrets].type += TYPE_NONFACING_INC; + } + num_live_turrets++; + } + } + } + } + + // sort the list if we're not using turret straigh ahead of us + if (!use_straigh_ahead_turret) { + qsort(ent, num_live_turrets, sizeof(eval_next_turret), sort_turret_func); + } + + if (use_straigh_ahead_turret) { + // use the straight ahead turret + live_turret = A; + } else { + // check if we have a currently targeted turret and find its position after the sort + int i, start_index, next_index; + if (get_closest_turret) { + start_index = 0; + } else { + start_index = -1; + for (i=0; itargeted_subsys) { + start_index = i; + break; + } + } + // check that we started with a turret + if (start_index == -1) { + start_index = 0; + } + } + + // set next live turret + if (num_live_turrets == 0) { + // no live turrets + live_turret = NULL; + } else if (num_live_turrets == 1 || get_closest_turret) { + // only 1 live turret, so set it + live_turret = ent[0].ss; + } else { + if (next_flag) { + // advance to next closest turret + next_index = start_index + 1; + if (next_index == num_live_turrets) { + next_index = 0; + } + } else { + // go to next farther turret + next_index = start_index - 1; + if (next_index == -1) { + next_index = num_live_turrets - 1; + } + } + + // set the next turret to be targeted based on next_index + live_turret = ent[next_index].ss; + } + + //if (live_turret) { + // debug info + // mprintf(("name %s, index: %d, type: %d\n", live_turret->system_info->subobj_name, next_index, ent[next_index].type)); + //} + } + + if ( live_turret != NULL ) { + set_targeted_subsys(Player_ai, live_turret, Player_ai->target_objnum); + target_shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys; + } else { + if (!auto_advance) { + snd_play( &Snds[SND_TARGET_FAIL]); + } + } +} + + +// ------------------------------------------------------------------- +// hud_target_closest_locked_missile() +// +// Target the closest locked missile that is locked on locked_obj +// +// input: locked_obj => pointer to object that you want to find +// closest missile to +// +void hud_target_closest_locked_missile(object *locked_obj) +{ + object *A, *nearest_obj=NULL; + weapon *wp; + weapon_info *wip; + float nearest_dist, dist; + int target_found = FALSE; + missile_obj *mo; + + nearest_dist = 10000.0f; + + for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) { + Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS); + A = &Objects[mo->objnum]; + + if (A->type != OBJ_WEAPON){ + continue; + } + + Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS)); + wp = &Weapons[A->instance]; + wip = &Weapon_info[wp->weapon_info_index]; + + if ( wip->subtype != WP_MISSILE ){ + continue; + } + + if ( !(wip->wi_flags & (WIF_HOMING_ASPECT|WIF_HOMING_HEAT) ) ){ + continue; + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + if (wp->homing_object == locked_obj) { + dist = vm_vec_dist_quick(&A->pos, &locked_obj->pos); // Find distance! + + if (dist < nearest_dist) { + nearest_obj = A; + nearest_dist = dist; + } + } + } // end for + + if (nearest_dist < 10000.0f) { + Assert(nearest_obj); + set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) ); + target_found = TRUE; + } + + if ( !target_found ){ + snd_play( &Snds[SND_TARGET_FAIL], 0.0f ); + } +} + +// Return bitmask of all opponents. +int opposing_team_mask(int team_mask) +{ + return ((TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE) & ~team_mask) | TEAM_TRAITOR; +} + +// select a new target, by auto-targeting +void hud_target_auto_target_next() +{ + if ( Framecount < 2 ) { + return; + } + + // No auto-targeting after dead. + if (Game_mode & (GM_DEAD | GM_DEAD_BLEW_UP)) + return; + + int valid_team; + + valid_team = opposing_team_mask(Player_ship->team); + + // try target closest ship attacking player + hud_target_closest(valid_team, OBJ_INDEX(Player_obj), FALSE, TRUE ); + + // if none, try targeting closest hostile fighter/bomber + if ( Player_ai->target_objnum == -1 ){ + hud_target_closest(valid_team, -1, FALSE, TRUE); + } + + // No fighter/bombers exists, so go ahead an target the closest hostile + if ( Player_ai->target_objnum == -1 ){ + hud_target_closest(valid_team, -1, FALSE); + } + + // um, ok. Try targeting asteroids that are on a collision course for an escort ship + if ( Player_ai->target_objnum == -1 ) { + asteroid_target_closest_danger(); + } +} + + +// Given that object 'targeter' is targetting object 'targetee', +// how far are they? This uses the point closest to the targeter +// object on the targetee's bounding box. So if targeter is inside +// targtee's bounding box, the distance is 0. +float hud_find_target_distance( object *targetee, object *targeter ) +{ + vector tmp_pnt; + + int model_num = -1; + + // Which model is it? + switch( targetee->type ) { + case OBJ_SHIP: + model_num = Ships[targetee->instance].modelnum; + break; + case OBJ_DEBRIS: +// model_num = Debris[targetee->instance].model_num; + break; + case OBJ_WEAPON: + // Don't find model_num since circles would work better + //model_num = Weapon_info[Weapons[targetee->instance].weapon_info_index].model_num; + break; + case OBJ_ASTEROID: + // Don't find model_num since circles would work better + //model_num = Asteroid_info[Asteroids[targetee->instance].type].model_num; + break; + case OBJ_JUMP_NODE: + // Don't find model_num since circles would work better + //model_num = Jump_nodes[targetee->instance].modelnum; + break; + } + + float dist = 0.0f; + + // New way, that uses bounding box. + if ( model_num > -1 ) { + dist = model_find_closest_point( &tmp_pnt, model_num, -1, &targetee->orient, &targetee->pos, &targeter->pos ); + } else { + // Old way, that uses radius. + dist = vm_vec_dist_quick(&targetee->pos, &targeter->pos) - targetee->radius; + if ( dist < 0.0f ) { + dist = 0.0f; + } + } + return dist; +} + +// hud_target_closest() will set the Players[Player_num].current_target to the closest +// ship to the player that matches the team passed as a paramater +// +// The current algorithm is to simply iterate through the objects and calculate the +// magnitude of the vector that connects the player to the target. The smallest magnitude +// is tracked, and then used to locate the closest hostile ship. Note only the square of the +// magnitude is required, since we are only comparing magnitudes +// +// parameters: team => team of closest ship that should be targeted. +// Default value is -1, if team doesn't matter. +// +// attacked_objnum => object number of ship that is being attacked +// play_fail_snd => boolean, whether to play SND_TARGET_FAIL +// (needed, since function called repeatedly when auto-targeting is +// enabled, and we don't want a string of fail sounds playing). +// This is a default parameter with a value of TRUE +// filter => OPTIONAL parameter (default value 0): when set to TRUE, only +// fighters and bombers are considered for new targets +// +// returns: TRUE ==> a target was acquired +// FALSE ==> no target was acquired +// +// eval target as closest struct +typedef struct esct +{ + int team; + int filter; + ship* shipp; + float min_distance; + int check_nearest_turret; + int attacked_objnum; + int check_all_turrets; + int turret_attacking_target; // check that turret is actually attacking the attacked_objnum +} esct; + +// evaluate a ship (and maybe turrets) as a potential target +// check if shipp (or its turrets) is attacking attacked_objnum +// special case for player trying to select target (don't check if turrets are aimed at player) +void evaluate_ship_as_closest_target(esct *esct) +{ + int targeting_player, turret_is_attacking; + ship_subsys *ss; + float new_distance; + + // initialize + esct->min_distance = FLT_MAX; + esct->check_nearest_turret = FALSE; + turret_is_attacking = FALSE; + + + object *objp = &Objects[esct->shipp->objnum]; + Assert(objp->type == OBJ_SHIP); + if (objp->type != OBJ_SHIP) { + return; + } + + // player being targeted, so we will want closest distance from player + targeting_player = (esct->attacked_objnum == OBJ_INDEX(Player_obj)); + + // filter on team, except in multiplayer + if ( !hud_team_matches_filter(esct->team, esct->shipp->team) ) { + // if we're in multiplayer dogfight, ignore this + if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){ + return; + } + } + + // check if player or ignore ship + if ( (esct->shipp->objnum == OBJ_INDEX(Player_obj)) || (esct->shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ) { + return; + } + + // bail if harmless + if ( Ship_info[esct->shipp->ship_info_index].flags & SIF_HARMLESS ) { + return; + } + + // only look at targets that are AWACS valid + if (hud_target_invalid_awacs(&Objects[esct->shipp->objnum])) { + return; + } + + // If filter is set, only target fighters and bombers + if ( esct->filter ) { + if ( !(Ship_info[esct->shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) { + return; + } + } + + // find closest turret to player if BIG or HUGE ship + if (Ship_info[esct->shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) { + for (ss=GET_FIRST(&esct->shipp->subsys_list); ss!=END_OF_LIST(&esct->shipp->subsys_list); ss=GET_NEXT(ss)) { + if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) { + + if (esct->check_all_turrets || (ss->turret_enemy_objnum == esct->attacked_objnum)) { + turret_is_attacking = 1; + esct->check_nearest_turret = TRUE; + + if ( !esct->turret_attacking_target || (esct->turret_attacking_target && (ss->turret_enemy_objnum == esct->attacked_objnum)) ) { + vector gsubpos; + // get world pos of subsystem + vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &objp->orient); + vm_vec_add2(&gsubpos, &objp->pos); + new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos); + + /* + // GET TURRET TYPE, FAVOR BEAM, FLAK, OTHER + int turret_type = ss->system_info->turret_weapon_type; + if (Weapon_info[turret_type].wi_flags & WIF_BEAM) { + new_distance *= 0.3f; + } else if (Weapon_info[turret_type].wi_flags & WIF_FLAK) { + new_distance *= 0.6f; + } */ + + // get the closest distance + if (new_distance <= esct->min_distance) { + esct->min_distance = new_distance; + } + } + } + } + } + } + + // If no turret is attacking, check if objp is actually targetting attacked_objnum + // dont bail if targeting is for player + if ( !targeting_player && !turret_is_attacking ) { + ai_info *aip = &Ai_info[esct->shipp->ai_index]; + + if (aip->target_objnum != esct->attacked_objnum) { + return; + } + + if ( (Game_mode & GM_NORMAL) && ( aip->mode != AIM_CHASE ) && (aip->mode != AIM_STRAFE) && (aip->mode != AIM_EVADE) && (aip->mode != AIM_EVADE_WEAPON) && (aip->mode != AIM_AVOID)) { + return; + } + } + + // consider the ship alone if there are no attacking turrets + if ( !turret_is_attacking ) { + //new_distance = hud_find_target_distance(objp, Player_obj); + new_distance = vm_vec_dist_quick(&objp->pos, &Player_obj->pos); + + if (new_distance <= esct->min_distance) { + esct->min_distance = new_distance; + esct->check_nearest_turret = FALSE; + } + } +} + +int hud_target_closest(int team, int attacked_objnum, int play_fail_snd, int filter, int get_closest_turret_attacking_player) +{ + object *A; + object *nearest_obj = &obj_used_list; + ship *shipp; + ship_obj *so; + int check_nearest_turret = FALSE; + + // evaluate ship closest target struct + esct esct; + + float min_distance = FLT_MAX; + int target_found = FALSE; + + int player_obj_index = OBJ_INDEX(Player_obj); + ship_subsys *ss; //*nearest_turret_subsys = NULL, *ss; + + if ( (attacked_objnum >= 0) && (attacked_objnum != player_obj_index) ) { + // bail if player does not have target + if ( Player_ai->target_objnum == -1) { + if ( Objects[attacked_objnum].type != OBJ_SHIP ) { + goto Target_closest_done; + } + + // bail if ship is to be ignored + if (!(Ships[Objects[attacked_objnum].instance].flags & TARGET_SHIP_IGNORE_FLAGS)) { + goto Target_closest_done; + } + } + } + + if (attacked_objnum == -1) { + attacked_objnum = player_obj_index; + } + + // check all turrets if for player. + esct.check_all_turrets = (attacked_objnum == player_obj_index); + esct.filter = filter; + esct.team = team; + esct.attacked_objnum = attacked_objnum; + esct.turret_attacking_target = get_closest_turret_attacking_player; + + for ( so=GET_FIRST(&Ship_obj_list); so!=END_OF_LIST(&Ship_obj_list); so=GET_NEXT(so) ) { + + A = &Objects[so->objnum]; + shipp = &Ships[A->instance]; // get a pointer to the ship information + + // fill in rest of esct + esct.shipp = shipp; + + // check each shipp on list and update nearest obj and subsys + evaluate_ship_as_closest_target(&esct); + if (esct.min_distance < min_distance) { + target_found = TRUE; + min_distance = esct.min_distance; + nearest_obj = A; + check_nearest_turret = esct.check_nearest_turret; + } + } + + Target_closest_done: + + // maybe ignore target if too far away + // DKA 9/8/99 Remove distance check + /* + if (target_found) { + // get distance to nearest attacker + float dist = vm_vec_dist_quick(&Objects[attacked_objnum].pos, &nearest_obj->pos); + + // no distance limit for player obj + if ((attacked_objnum != player_obj_index) && (dist > MIN_DISTANCE_TO_CONSIDER_THREAT)) { + target_found = FALSE; + } + } */ + + if (target_found) { + set_target_objnum(Player_ai, OBJ_INDEX(nearest_obj)); + if ( check_nearest_turret ) { + + // if former subobject was not a turret do, not change subsystem + ss = Ships[nearest_obj->instance].last_targeted_subobject[Player_num]; + if (ss == NULL || get_closest_turret_attacking_player) { + // set_targeted_subsys(Player_ai, nearest_turret_subsys, OBJ_INDEX(nearest_obj)); + // update nearest turret with later func + hud_target_live_turret(1, 1, get_closest_turret_attacking_player); + Ships[nearest_obj->instance].last_targeted_subobject[Player_num] = Player_ai->targeted_subsys; + } + } else { + hud_restore_subsystem_target(&Ships[nearest_obj->instance]); + } + } else { + // no target found, maybe play fail sound + if (play_fail_snd == TRUE) { + snd_play(&Snds[SND_TARGET_FAIL]); + } + } + + return target_found; +} + +// auto update closest turret to attack on big or huge ships +void hud_update_closest_turret() +{ + hud_target_live_turret(1, 1); + +/* + float nearest_distance, new_distance; + ship_subsys *ss, *closest_subsys; + ship *shipp; + object *objp; + + nearest_distance = FLT_MAX; + objp = &Objects[Player_ai->target_objnum]; + shipp = &Ships[objp->instance]; + closest_subsys = NULL; + + + Assert(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)); + + for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) { + if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) { + // make sure turret is not "unused" + if (ss->system_info->turret_weapon_type >= 0) { + vector gsubpos; + // get world pos of subsystem + vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &objp->orient); + vm_vec_add2(&gsubpos, &objp->pos); + new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos); + + // GET TURRET TYPE, FAVOR BEAM, FLAK, OTHER + int turret_type = ss->system_info->turret_weapon_type; + if (Weapon_info[turret_type].wi_flags & WIF_BEAM) { + new_distance *= 0.3f; + } else if (Weapon_info[turret_type].wi_flags & WIF_FLAK) { + new_distance *= 0.6f; + } + + // check if facing and in view + int facing = ship_subsystem_in_sight(objp, ss, &View_position, &gsubpos, 0); + + if (facing) { + new_distance *= 0.5f; + } + + // get the closest distance + if (new_distance <= nearest_distance) { + nearest_distance = new_distance; + closest_subsys = ss; + } + } + } + } + + // check if new subsys to target + if (Player_ai->targeted_subsys != NULL) { + set_targeted_subsys(Player_ai, closest_subsys, Player_ai->target_objnum); + shipp->last_targeted_subobject[Player_num] = Player_ai->targeted_subsys; + } + */ +} + + +// -------------------------------------------------------------------- +// hud_target_targets_target() +// +// Target your target's target. Your target is specified by objnum passed +// as a parameter. +// +void hud_target_targets_target() +{ + object *objp; + int tt_objnum; + + if ( Player_ai->target_objnum < 0 || Player_ai->target_objnum >= MAX_OBJECTS ) { + goto ttt_fail; + } + + objp = &Objects[Player_ai->target_objnum]; + if ( objp->type != OBJ_SHIP ) { + goto ttt_fail; + } + + if (hud_target_invalid_awacs(objp)) { + goto ttt_fail; + } + + if ( Ships[objp->instance].flags & TARGET_SHIP_IGNORE_FLAGS ) { + goto ttt_fail; + } + + tt_objnum = Ai_info[Ships[objp->instance].ai_index].target_objnum; + if ( tt_objnum < 0 || tt_objnum >= MAX_OBJECTS ) { + goto ttt_fail; + } + + if ( tt_objnum == OBJ_INDEX(Player_obj) ) { + goto ttt_fail; + } + + // if we've reached here, found player target's target + set_target_objnum( Player_ai, tt_objnum ); + if (Objects[tt_objnum].type == OBJ_SHIP) { + hud_maybe_set_sorted_turret_subsys(&Ships[Objects[tt_objnum].instance]); + } + hud_restore_subsystem_target(&Ships[Objects[tt_objnum].instance]); + return; + + ttt_fail: + snd_play( &Snds[SND_TARGET_FAIL], 0.0f ); +} + +// Return !0 if target_objp is a valid object type for targeting in reticle, otherwise return 0 +int object_targetable_in_reticle(object *target_objp) +{ + int obj_type; + if (target_objp == Player_obj ) { + return 0; + } + + obj_type = target_objp->type; + + if ( (obj_type == OBJ_SHIP) || (obj_type == OBJ_DEBRIS) || (obj_type == OBJ_WEAPON) || (obj_type == OBJ_ASTEROID) || (obj_type == OBJ_JUMP_NODE) ) { + return 1; + } + + return 0; +} + + +// hud_target_in_reticle_new() will target the object that is closest to the player, and who is +// intersected by a ray passed from the center of the reticle out along the forward vector of the +// player. +// +// targeting of objects of type OBJ_SHIP and OBJ_DEBRIS are supported +// +// Method: A ray is cast from the center of the reticle, and we keep track of any eligible object +// the ray intersects. We take the ship closest to us that intersects an object. +// +// Since this method may work poorly with objects that are far away, hud_target_in_reticle_old() +// is called if no intersections are found. +// +// +#define TARGET_IN_RETICLE_DISTANCE 10000.0f + +void hud_target_in_reticle_new() +{ + vector terminus; + object *A; + mc_info mc; + float dist; + + hud_reticle_clear_list(&Reticle_cur_list); + Reticle_save_timestamp = timestamp(RESET_TARGET_IN_RETICLE); + + // Get 3d vector through center of reticle + vm_vec_scale_add(&terminus, &Eye_position, &Player_obj->orient.fvec, TARGET_IN_RETICLE_DISTANCE); + + mc.model_num = 0; + for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) { + + if ( !object_targetable_in_reticle(A) ) { + continue; + } + + if ( A->type == OBJ_WEAPON ) { + if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags & WIF_BOMB) ){ + continue; + } + } + + if ( A->type == OBJ_SHIP ) { + if ( Ships[A->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){ + continue; + } + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + switch (A->type) { + case OBJ_SHIP: + mc.model_num = Ships[A->instance].modelnum; + break; + case OBJ_DEBRIS: + mc.model_num = Debris[A->instance].model_num; + break; + case OBJ_WEAPON: + mc.model_num = Weapon_info[Weapons[A->instance].weapon_info_index].model_num; + break; + case OBJ_ASTEROID: + { +#ifndef FS2_DEMO + int subtype = 0; + subtype = Asteroids[A->instance].asteroid_subtype; + mc.model_num = Asteroid_info[Asteroids[A->instance].type].model_num[subtype]; +#endif + } + break; + case OBJ_JUMP_NODE: + mc.model_num = Jump_nodes[A->instance].modelnum; + break; + default: + Int3(); // Illegal object type. + } + + model_clear_instance( mc.model_num ); + mc.orient = &A->orient; // The object's orient + mc.pos = &A->pos; // The object's position + mc.p0 = &Eye_position; // Point 1 of ray to check + mc.p1 = &terminus; // Point 2 of ray to check + mc.flags = MC_CHECK_MODEL; // | MC_ONLY_BOUND_BOX; // check the model, but only its bounding box + + model_collide(&mc); + if ( mc.num_hits ) { + dist = vm_vec_dist_squared(&mc.hit_point_world, &Eye_position); + hud_reticle_list_update(A, dist, 0); + } + } // end for (go to next object) + + hud_target_in_reticle_old(); // try the old method (works well with ships far away) +} + +// hud_target_in_reticle_old() will target the object that is closest to the reticle center and inside +// the reticle +// +// targeting of objects of type OBJ_SHIP and OBJ_DEBRIS are supported +// +// +// Method: take the dot product of the foward vector and the vector to target. Take +// the one that is closest to 1 and at least MIN_DOT_FOR_TARGET +// +// IMPORTANT: The MIN_DOT_FOR_TARGET value was arrived at by trial and error and +// is only valid for the HUD reticle in use at that time. + +#define MIN_DOT_FOR_TARGET 0.9726// fov for targeting in reticle + +void hud_target_in_reticle_old() +{ + object *A, *target_obj; + float dist, dot; + vector vec_to_target; + + for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) { + + if ( !object_targetable_in_reticle(A) ) { + continue; + } + + if ( A->type == OBJ_WEAPON ) { + if ( !(Weapon_info[Weapons[A->instance].weapon_info_index].wi_flags & WIF_BOMB) ){ + continue; + } + } + + if ( A->type == OBJ_SHIP ) { + if ( Ships[A->instance].flags & TARGET_SHIP_IGNORE_FLAGS ){ + continue; + } + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + if ( vm_vec_same( &A->pos, &Eye_position ) ) { + continue; + } + + dist = vm_vec_normalized_dir(&vec_to_target, &A->pos, &Eye_position); + dot = vm_vec_dot(&Player_obj->orient.fvec, &vec_to_target); + + if ( dot > MIN_DOT_FOR_TARGET ) { + hud_reticle_list_update(A, dot, 1); + } + } + + target_obj = hud_reticle_pick_target(); + if ( target_obj != NULL ) { + set_target_objnum( Player_ai, OBJ_INDEX(target_obj) ); + if ( target_obj->type == OBJ_SHIP ) { + // if BIG|HUGE, maybe set subsys to turret + hud_maybe_set_sorted_turret_subsys(&Ships[target_obj->instance]); + hud_restore_subsystem_target(&Ships[target_obj->instance]); + } + } + else { + snd_play( &Snds[SND_TARGET_FAIL], 0.0f ); + } +} + +// hud_target_subsystem_in_reticle() will target the subsystem that is within the reticle and +// is closest to the reticle center. The current target is the only object that is searched for +// subsystems +// +// Method: take the dot product of the foward vector and the vector to target. Take +// the one that is closest to 1 and at least MIN_DOT_FOR_TARGET +// +// IMPORTANT: The MIN_DOT_FOR_TARGET value was arrived at by trial and error and +// is only valid for the HUD reticle in use at that time. +// + +void hud_target_subsystem_in_reticle() +{ + object* targetp; + ship_subsys *subsys; + ship_subsys *nearest_subsys = NULL; + vector subobj_pos; + + float dist, dot, best_dot; + vector vec_to_target; + best_dot = -1.0f; + + if ( Player_ai->target_objnum == -1){ + hud_target_in_reticle_old(); + } + + if ( Player_ai->target_objnum == -1) { + snd_play( &Snds[SND_TARGET_FAIL]); + return; + } + + targetp = &Objects[Player_ai->target_objnum]; + + if ( targetp->type != OBJ_SHIP ){ // only targeting subsystems on ship + return; + } + + int shipnum = targetp->instance; + + for (subsys = GET_FIRST(&Ships[shipnum].subsys_list); subsys != END_OF_LIST(&Ships[shipnum].subsys_list) ; subsys = GET_NEXT( subsys ) ) { + get_subsystem_world_pos(targetp, subsys, &subobj_pos); + + dist = vm_vec_normalized_dir(&vec_to_target, &subobj_pos, &Eye_position); + dot = vm_vec_dot(&Player_obj->orient.fvec, &vec_to_target); + + if ( dot > best_dot ) { + best_dot = dot; + if ( best_dot > MIN_DOT_FOR_TARGET ) + nearest_subsys = subsys; + } + + Assert(best_dot <= 1.0f); + } // end for + + if ( nearest_subsys != NULL ) { + set_targeted_subsys(Player_ai, nearest_subsys, Player_ai->target_objnum); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Targeting subsystem %s.", 323), Player_ai->targeted_subsys->system_info->name); + Ships[shipnum].last_targeted_subobject[Player_num] = Player_ai->targeted_subsys; + } + else { + snd_play( &Snds[SND_TARGET_FAIL]); + } +} + +#define T_LENGTH 8 +#define T_OFFSET_FROM_CIRCLE -13 +#define T_BASE_LENGTH 4 + +// On entry: +// color set +void hud_render_orientation_tee(object *from_objp, object *to_objp, matrix *from_orientp) +{ + float dot_product; + vector target_to_obj; + float x1,y1,x2,y2,x3,y3,x4,y4; + + vm_vec_sub(&target_to_obj, &from_objp->pos, &to_objp->pos); + + vm_vec_normalize(&target_to_obj); + + // calculate the dot product between the target_to_player vector and the targets forward vector + // + // 0 - vectors are perpendicular + // 1 - vectors are collinear and in the same direction (target is facing player) + // -1 - vectors are collinear and in the opposite direction (target is facing away from player) + dot_product = vm_vec_dotprod(&from_orientp->fvec, &target_to_obj); + + if (vm_vec_dotprod(&from_orientp->rvec, &target_to_obj) >= 0) { + if (dot_product >= 0){ + dot_product = -PI/2*dot_product + PI; + } else { + dot_product = -PI/2*dot_product - PI; + } + } + else { + dot_product *= PI/2; //(range is now -PI/2 => PI/2) + } + + y1 = (float)sin(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE); + x1 = (float)cos(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE); + + y1 += Hud_reticle_center[gr_screen.res][1]; + x1 += Hud_reticle_center[gr_screen.res][0]; + + x1 += HUD_offset_x; + y1 += HUD_offset_y; + + y2 = (float)sin(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE - T_LENGTH); + x2 = (float)cos(dot_product) * (Outer_circle_radius[gr_screen.res] - T_OFFSET_FROM_CIRCLE - T_LENGTH); + + y2 += Hud_reticle_center[gr_screen.res][1]; + x2 += Hud_reticle_center[gr_screen.res][0]; + + x2 += HUD_offset_x; + y2 += HUD_offset_y; + + x3 = x1 - T_BASE_LENGTH * (float)sin(dot_product); + y3 = y1 + T_BASE_LENGTH * (float)cos(dot_product); + x4 = x1 + T_BASE_LENGTH * (float)sin(dot_product); + y4 = y1 - T_BASE_LENGTH * (float)cos(dot_product); + + // HACK! Should be antialiased! + gr_line(fl2i(x3),fl2i(y3),fl2i(x4),fl2i(y4)); // bottom of T + gr_line(fl2i(x1),fl2i(y1),fl2i(x2),fl2i(y2)); // part of T pointing towards center + +} + +void hud_tri(float x1,float y1,float x2,float y2,float x3,float y3) +{ + int i; + + // Make the triangle always be the correct handiness so + // the tmapper won't think its back-facing and throw it out. + float det = (y2-y1)*(x3-x1) - (x2-x1)*(y3-y1); + if ( det >= 0.0f ) { + float tmp; + + // swap y1 & y3 + tmp = y1; + y1 = y3; + y3 = tmp; + + // swap x1 & x3 + tmp = x1; + x1 = x3; + x3 = tmp; + } + + vertex * vertlist[3]; + vertex verts[3]; + + for (i=0; i<3; i++ ) + vertlist[i] = &verts[i]; + + verts[0].sx = x1; verts[0].sy = y1; + verts[1].sx = x2; verts[1].sy = y2; + verts[2].sx = x3; verts[2].sy = y3; + + uint saved_mode = gr_zbuffer_get(); + + gr_zbuffer_set( GR_ZBUFF_NONE ); + + gr_tmapper( 3, vertlist, 0 ); + + gr_zbuffer_set( saved_mode ); +} + + +void hud_tri_empty(float x1,float y1,float x2,float y2,float x3,float y3) +{ + gr_line(fl2i(x1),fl2i(y1),fl2i(x2),fl2i(y2)); + gr_line(fl2i(x2),fl2i(y2),fl2i(x3),fl2i(y3)); + gr_line(fl2i(x3),fl2i(y3),fl2i(x1),fl2i(y1)); +} + + +// Render a missile warning triangle that has a tail on it to indicate distance +void hud_render_tail_missile_triangle(float ang, float xpos, float ypos, float cur_dist, int draw_solid, int draw_inside) +{ + float x1=0.0f; + float x2=0.0f; + float y1=0.0f; + float y2=0.0f; + float xtail=0.0f; + float ytail=0.0f; + + float sin_ang, cos_ang, tail_len; + + float max_tail_len=20.0f; + + sin_ang=(float)sin(ang); + cos_ang=(float)cos(ang); + + if ( cur_dist < Min_warning_missile_dist ) { + tail_len = 0.0f; + } else if ( cur_dist > Max_warning_missile_dist ) { + tail_len = max_tail_len; + } else { + tail_len = cur_dist/Max_warning_missile_dist * max_tail_len; + } + + if ( draw_inside ) { + x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang; + y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang; + x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang; + y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang; + + xpos -= Target_triangle_height[gr_screen.res] * cos_ang; + ypos += Target_triangle_height[gr_screen.res] * sin_ang; + + if ( tail_len > 0 ) { + xtail = xpos - tail_len * cos_ang; + ytail = ypos + tail_len * sin_ang; + } + + } else { + x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang; + y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang; + x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang; + y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang; + + xpos += Target_triangle_height[gr_screen.res] * cos_ang; + ypos -= Target_triangle_height[gr_screen.res] * sin_ang; + + if ( tail_len > 0 ) { + xtail = xpos + tail_len * cos_ang; + ytail = ypos - tail_len * sin_ang; + } + } + + if (draw_solid) { + hud_tri(xpos,ypos,x1,y1,x2,y2); + } else { + hud_tri_empty(xpos,ypos,x1,y1,x2,y2); + } + + // draw the tail indicating length + if ( tail_len > 0 ) { + gr_line(fl2i(xpos), fl2i(ypos), fl2i(xtail), fl2i(ytail)); + } +} + +// Render a missile warning triangle, that splits apart to indicate distance +void hud_render_split_missile_triangle(float ang, float xpos, float ypos, float cur_dist, int draw_solid, int draw_inside) +{ + // points to draw triangles + float x1=0.0f; + float y1=0.0f; + float x2=0.0f; + float y2=0.0f; + float x3=0.0f; + float y3=0.0f; + float x4=0.0f; + float y4=0.0f; + float x5=0.0f; + float y5=0.0f; + float x6=0.0f; + float y6=0.0f; + + float triangle_sep, half_triangle_sep,sin_ang,cos_ang; + + sin_ang=(float)sin(ang); + cos_ang=(float)cos(ang); + + if ( cur_dist < Min_warning_missile_dist ) { + triangle_sep = 0.0f; + } else if ( cur_dist > Max_warning_missile_dist ) { + triangle_sep = Max_offscreen_tri_seperation[gr_screen.res]+Max_front_seperation[gr_screen.res]; + } else { + triangle_sep = (cur_dist/Max_warning_missile_dist) * (Max_offscreen_tri_seperation[gr_screen.res]+Max_front_seperation[gr_screen.res]); + } + + // calculate these values only once, since it will be used in several places + half_triangle_sep = 0.5f * triangle_sep; + + xpos = (float)floor(xpos); + ypos = (float)floor(ypos); + + if ( triangle_sep == 0 ) { + x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang; + y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang; + x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang; + y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang; + if ( draw_inside ) { + } else { + xpos += Target_triangle_height[gr_screen.res] * cos_ang; + ypos -= Target_triangle_height[gr_screen.res] * sin_ang; + } + if (draw_solid) { + hud_tri(xpos,ypos,x1,y1,x2,y2); + } else { + hud_tri_empty(xpos,ypos,x1,y1,x2,y2); + } + } else { + // calc left side points + x5 = xpos - half_triangle_sep * -sin_ang; + y5 = ypos + half_triangle_sep * cos_ang; + + x6 = x5 - Target_triangle_base[gr_screen.res] * -sin_ang; + y6 = y5 + Target_triangle_base[gr_screen.res] * cos_ang; + + x4=x5; + y4=y5; + if ( draw_inside ) { + x4 -= Target_triangle_height[gr_screen.res] * cos_ang; + y4 += Target_triangle_height[gr_screen.res] * sin_ang; + } else { + x4 += Target_triangle_height[gr_screen.res] * cos_ang; + y4 -= Target_triangle_height[gr_screen.res] * sin_ang; + } + + // calc right side points + x2 = xpos + half_triangle_sep * -sin_ang; + y2 = ypos - half_triangle_sep * cos_ang; + + x3 = x2 + Target_triangle_base[gr_screen.res] * -sin_ang; + y3 = y2 - Target_triangle_base[gr_screen.res] * cos_ang; + + x1=x2; + y1=y2; + if ( draw_inside ) { + x1 -= Target_triangle_height[gr_screen.res] * cos_ang; + y1 += Target_triangle_height[gr_screen.res] * sin_ang; + } else { + x1 += Target_triangle_height[gr_screen.res] * cos_ang; + y1 -= Target_triangle_height[gr_screen.res] * sin_ang; + } + + // draw both tris with a line connecting them + if ( draw_solid ) { + hud_tri(x3,y3,x2,y2,x1,y1); + hud_tri(x4,y4,x5,y5,x6,y6); + } else { + hud_tri_empty(x3,y3,x2,y2,x1,y1); + hud_tri_empty(x4,y4,x5,y5,x6,y6); + } + gr_line(fl2i(x2+0.5f),fl2i(y2+0.5f),fl2i(x5+0.5f),fl2i(y5+0.5f)); + } +} + +// Render a triangle on the outside of the targeting circle. +// Must be inside a g3_start_frame(). +// If aspect_flag !0, then render filled, indicating aspect lock. +// If show_interior !0, then point inwards to positions inside reticle +void hud_render_triangle(vector *hostile_pos, int aspect_flag, int show_interior, int split_tri) +{ + vertex hostile_vertex; + float ang; + float xpos,ypos,cur_dist,sin_ang,cos_ang; + int draw_inside=0; + + // determine if the closest firing object is within the targeting reticle (which means the triangle + // is not drawn) + + cur_dist = vm_vec_dist_quick(&Player_obj->pos, hostile_pos); + + g3_rotate_vertex(&hostile_vertex, hostile_pos); + + if (hostile_vertex.codes == 0) {// on screen + float projected_x, projected_y; + + g3_project_vertex(&hostile_vertex); + + if (!(hostile_vertex.flags & PF_OVERFLOW)) { // make sure point projected + float mag_squared; + + projected_x = hostile_vertex.sx; + projected_y = hostile_vertex.sy; + + mag_squared = (projected_x-Hud_reticle_center[gr_screen.res][0])*(projected_x-Hud_reticle_center[gr_screen.res][0]) + + (projected_y-Hud_reticle_center[gr_screen.res][1])*(projected_y-Hud_reticle_center[gr_screen.res][1]); + + if ( mag_squared < Outer_circle_radius[gr_screen.res]*Outer_circle_radius[gr_screen.res] ) { + if ( !show_interior ) { + return; + } else { + draw_inside=1; + } + } + } + } + + ang = atan2_safe(hostile_vertex.y,hostile_vertex.x); + sin_ang=(float)sin(ang); + cos_ang=(float)cos(ang); + + if ( draw_inside ) { + xpos = Hud_reticle_center[gr_screen.res][0] + cos_ang*(Outer_circle_radius[gr_screen.res]-7); + ypos = Hud_reticle_center[gr_screen.res][1] - sin_ang*(Outer_circle_radius[gr_screen.res]-7); + } else { + xpos = Hud_reticle_center[gr_screen.res][0] + cos_ang*(Outer_circle_radius[gr_screen.res]+4); + ypos = Hud_reticle_center[gr_screen.res][1] - sin_ang*(Outer_circle_radius[gr_screen.res]+4); + } + + xpos += HUD_offset_x; + ypos += HUD_offset_y; + + if ( split_tri ) { +// hud_render_split_missile_triangle(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside); + hud_render_tail_missile_triangle(ang, xpos, ypos, cur_dist, aspect_flag, draw_inside); + } else { + float x1=0.0f; + float x2=0.0f; + float y1=0.0f; + float y2=0.0f; + + if ( draw_inside ) { + x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang; + y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang; + x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang; + y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang; + + xpos -= Target_triangle_height[gr_screen.res] * cos_ang; + ypos += Target_triangle_height[gr_screen.res] * sin_ang; + + } else { + x1 = xpos - Target_triangle_base[gr_screen.res] * -sin_ang; + y1 = ypos + Target_triangle_base[gr_screen.res] * cos_ang; + x2 = xpos + Target_triangle_base[gr_screen.res] * -sin_ang; + y2 = ypos - Target_triangle_base[gr_screen.res] * cos_ang; + + xpos += Target_triangle_height[gr_screen.res] * cos_ang; + ypos -= Target_triangle_height[gr_screen.res] * sin_ang; + } + + if (aspect_flag) { + hud_tri(xpos,ypos,x1,y1,x2,y2); + } else { + hud_tri_empty(xpos,ypos,x1,y1,x2,y2); + } + } +} + +// Show all homing missiles locked onto the player. +// Also, play the beep! +void hud_show_homing_missiles() +{ + object *A; + missile_obj *mo; + weapon *wp; + float dist, nearest_dist; + int closest_is_aspect=0; + + gr_set_color_fast(&HUD_color_homing_indicator); + nearest_dist = Homing_beep.max_cycle_dist; + + for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) { + A = &Objects[mo->objnum]; + Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS)); + + wp = &Weapons[A->instance]; + + if (wp->homing_object == Player_obj) { + hud_render_triangle(&A->pos, Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING_ASPECT, 1, 1); + dist = vm_vec_dist_quick(&A->pos, &Player_obj->pos); + + if (dist < nearest_dist) { + nearest_dist = dist; + if ( Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING_ASPECT ) { + closest_is_aspect=1; + } else { + closest_is_aspect=0; + } + } + } + } + + // See if need to play warning beep. + if (nearest_dist < Homing_beep.max_cycle_dist ) { + float delta_time; + float cycle_time; + + delta_time = f2fl(Missiontime - Homing_beep.last_time_played); + + // figure out the cycle time by doing a linear interpretation + cycle_time = Homing_beep.min_cycle_time + (nearest_dist-Homing_beep.min_cycle_dist) * Homing_beep.precalced_interp; + + // play a new 'beep' if cycle time has elapsed + if ( (delta_time*1000) > cycle_time ) { + Homing_beep.last_time_played = Missiontime; + if ( snd_is_playing(Homing_beep.snd_handle) ) { + snd_stop(Homing_beep.snd_handle); + } + + if ( closest_is_aspect ) { + Homing_beep.snd_handle = snd_play(&Snds[SND_PROXIMITY_ASPECT_WARNING]); + } else { + Homing_beep.snd_handle = snd_play(&Snds[SND_PROXIMITY_WARNING]); + } + } + } +} + +// hud_show_orientation_tee() will draw the orientation gauge that orbits the inside of the +// outer reticle ring. If the T is at 12 o'clock, the target is facing the player, if the T +// is at 6 o'clock the target is facing away from the player. If the T is at 3 or 9 o'clock +// the target is facing 90 away from the player. +void hud_show_orientation_tee() +{ + object* targetp; + + if (Player_ai->target_objnum == -1) + return; + + targetp = &Objects[Player_ai->target_objnum]; + + if ( hud_gauge_maybe_flash(HUD_ORIENTATION_TEE) == 1 ) { + hud_set_iff_color( targetp ); + } else { + hud_set_iff_color( targetp, 1); + } + hud_render_orientation_tee(targetp, Player_obj, &targetp->orient); +} + +// routine to draw a bounding box around a remote detonate missile and distance to +void hud_show_remote_detonate_missile() +{ + missile_obj *mo; + object *mobjp; + float distance; + vertex target_point; + int x1, x2, y1, y2; + + // check for currently locked missiles (highest precedence) + for ( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) { + Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS); + mobjp = &Objects[mo->objnum]; + + if ((Player_obj != NULL) && (mobjp->parent_sig == Player_obj->parent_sig)) { + if (Weapon_info[Weapons[mobjp->instance].weapon_info_index].wi_flags & WIF_REMOTE) { + // get distance + distance = hud_find_target_distance(mobjp, Player_obj); + + // get box center point + g3_rotate_vertex(&target_point,&mobjp->pos); + + // project vertex + g3_project_vertex(&target_point); + + if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected + int modelnum, bound_rval; + + switch ( mobjp->type ) { + case OBJ_WEAPON: + modelnum = Weapon_info[Weapons[mobjp->instance].weapon_info_index].model_num; + bound_rval = model_find_2d_bound_min( modelnum, &mobjp->orient, &mobjp->pos,&x1,&y1,&x2,&y2 ); + break; + + default: + Int3(); // should never happen + return; + } + + if ( bound_rval == 0 ) { + // draw brackets and distance + int color; + color = hud_brackets_get_iff_color(MESSAGE_SENDER); + gr_set_color_fast(&IFF_colors[color][1]); + draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,0,0, distance, OBJ_INDEX(mobjp)); + } + + // do only for the first remote detonate missile + break; + } + } + } + } +} + +// routine to possibly draw a bouding box around a ship sending a message to the player +void hud_show_message_sender() +{ + object *targetp; + vertex target_point; // temp vertex used to find screen position for 3-D object; + ship *target_shipp; + int x1,x2,y1,y2; + + + // don't draw brackets if no ship sending a message + if ( Message_shipnum == -1 ) + return; + + targetp = &Objects[Ships[Message_shipnum].objnum]; + Assert ( targetp != NULL ); + + Assert ( targetp->type == OBJ_SHIP ); + + // Don't do this for the ship you're flying! + if ( targetp == Player_obj ) { + return; + } + + Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS ); + target_shipp = &Ships[Message_shipnum]; + + // check the object flags to see if this ship is gone. If so, then don't do this stuff anymore + if ( targetp->flags & OF_SHOULD_BE_DEAD ) { + Message_shipnum = -1; + return; + } + + // find the current target vertex + // + g3_rotate_vertex(&target_point,&targetp->pos); + + hud_set_iff_color( targetp, 1); + + g3_project_vertex(&target_point); + + if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected + int modelnum, bound_rval; + + switch ( targetp->type ) { + case OBJ_SHIP: + modelnum = target_shipp->modelnum; + bound_rval = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 ); + break; + + default: + Int3(); // should never happen + return; + } + + if ( bound_rval == 0 ) { + int color; + color = hud_brackets_get_iff_color(MESSAGE_SENDER); + gr_set_color_fast(&IFF_colors[color][1]); + draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,10,10); + } + } + + if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) { + if (target_point.codes != 0) { // target center is not on screen + // draw the offscreen indicator at the edge of the screen where the target is closest to + // AL 11-19-97: only show offscreen indicator if player sensors are functioning + if ( (OBJ_INDEX(targetp) != Player_ai->target_objnum) || (Message_shipnum == Objects[Player_ai->target_objnum].instance) ) { + if ( hud_sensors_ok(Player_ship, 0) ) { + float dist; + gr_set_color_fast(&IFF_colors[IFF_COLOR_MESSAGE][1]); + //dist = vm_vec_dist_quick(&Player_obj->pos, &targetp->pos); + dist = hud_find_target_distance( targetp, Player_obj ); + hud_draw_offscreen_indicator(&target_point, &targetp->pos, dist); + } + } + } + } +} + +// hud_prune_hotkeys() +// +// Check for ships that are dying, departed or dead. These should be removed from the player's +// hotkey lists. +void hud_prune_hotkeys() +{ + int i; + htarget_list *hitem, *plist; + object *objp; + ship *sp; + + for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) { + plist = &(Players[Player_num].keyed_targets[i]); + if ( EMPTY( plist ) ) // no items in list, then do nothing + continue; + + hitem = GET_FIRST(plist); + while ( hitem != END_OF_LIST(plist) ) { + int remove_item; + + remove_item = 0; + + objp = hitem->objp; + Assert ( objp != NULL ); + if ( objp->type == OBJ_SHIP ) { + Assert ( objp->instance >=0 && objp->instance < MAX_SHIPS ); + sp = &Ships[objp->instance]; + } else { + // if the object isn't a ship, it shouldn't be on the list, so remove it without question + remove_item = 1; + sp = NULL; + } + + // check to see if the object is dying -- if so, remove it from the list + // check to see if the ship is departing -- if so, remove it from the list + if ( remove_item || (objp->flags & OF_SHOULD_BE_DEAD) || (sp->flags & (SF_DEPARTING|SF_DYING)) ) { + if ( sp != NULL ) { + nprintf(("Network", "Hotkey: Pruning %s\n", sp->ship_name)); + } + + htarget_list *temp; + temp = GET_NEXT(hitem); + list_remove( plist, hitem ); + list_append( &htarget_free_list, hitem ); + hitem->objp = NULL; + hitem = temp; + continue; + } + hitem = GET_NEXT( hitem ); + } // end while + } // end for + + // save the hotkey sets with mission time reaches a certain point. Code was put here because this + // function always called for both single/multiplayer. Maybe not the best location, but whatever. + mission_hotkey_maybe_save_sets(); +} + +int HUD_drew_selection_bracket_on_target; + +// hud_show_selection_set draws some indicator around all the ships in the current selection set. No +// indicators will be drawn if there is only 1 ship in the set. +void hud_show_selection_set() +{ + htarget_list *hitem, *plist; + object *targetp; + int set, count; + vertex target_point; // temp vertex used to find screen position for 3-D object; + vector target_vec; + + HUD_drew_selection_bracket_on_target = 0; + + set = Players[Player_num].current_hotkey_set; + if ( set == -1 ) + return; + + Assert ( (set >= 0) && (set < MAX_KEYED_TARGETS) ); + plist = &(Players[Player_num].keyed_targets[set]); + + count = 0; + for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) + count++; + + if ( count == 0 ) { // only one ship, do nothing + Players[Player_num].current_hotkey_set = -1; + return; + } + + for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) { + targetp = hitem->objp; + Assert ( targetp != NULL ); + + ship *target_shipp = NULL; + + Assert ( targetp->type == OBJ_SHIP ); + Assert ( targetp->instance >=0 && targetp->instance < MAX_SHIPS ); + target_shipp = &Ships[targetp->instance]; + + if ( (Game_mode & GM_MULTIPLAYER) && (target_shipp == Player_ship) ) { + continue; + } + + // find the current target vertex + // + g3_rotate_vertex(&target_point,&targetp->pos); + + vm_vec_sub(&target_vec,&targetp->pos,&Player_obj->pos); + + int x1,x2,y1,y2; + + hud_set_iff_color( targetp, 1 ); + + g3_project_vertex(&target_point); + + if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected + int modelnum, bound_rval; + + switch ( targetp->type ) { + case OBJ_SHIP: + modelnum = target_shipp->modelnum; + bound_rval = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 ); + break; + + default: + Int3(); // should never happen + return; + } + + if ( bound_rval == 0 ) { + gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]); + draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,5,5); + if ( OBJ_INDEX(targetp) == Player_ai->target_objnum ) { + HUD_drew_selection_bracket_on_target = 1; + } + } + } + + if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) { + if (target_point.codes != 0) { // target center is not on screen + // draw the offscreen indicator at the edge of the screen where the target is closest to + // AL 11-19-97: only show offscreen indicator if player sensors are functioning + + if ( OBJ_INDEX(targetp) != Player_ai->target_objnum ) { + if ( hud_sensors_ok(Player_ship, 0) ) { + float dist; + gr_set_color_fast(&IFF_colors[IFF_COLOR_SELECTION][1]); + //dist = vm_vec_dist_quick(&Player_obj->pos, &targetp->pos); + dist = hud_find_target_distance( targetp, Player_obj ); + hud_draw_offscreen_indicator(&target_point, &targetp->pos, dist); + } + } + } + } + } +} + +void hud_show_brackets(object *targetp, vertex *projected_v) +{ + int x1,x2,y1,y2; + int draw_box = TRUE; + int team, bound_rc; + + if ( Player->target_is_dying <= 0 ) { + int modelnum; + + switch ( targetp->type ) { + case OBJ_SHIP: + modelnum = Ships[targetp->instance].modelnum; + bound_rc = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 ); + if ( bound_rc != 0 ) { + draw_box = FALSE; + } + break; + + case OBJ_DEBRIS: + modelnum = Debris[targetp->instance].model_num; + bound_rc = submodel_find_2d_bound_min( modelnum, Debris[targetp->instance].submodel_num, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 ); + if ( bound_rc != 0 ) { + draw_box = FALSE; + } + break; + + case OBJ_WEAPON: + Assert(Weapon_info[Weapons[targetp->instance].weapon_info_index].subtype == WP_MISSILE); + modelnum = Weapon_info[Weapons[targetp->instance].weapon_info_index].model_num; + bound_rc = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 ); + break; + +#ifndef FS2_DEMO + case OBJ_ASTEROID: + { + int subtype = 0; + subtype = Asteroids[targetp->instance].asteroid_subtype; + modelnum = Asteroid_info[Asteroids[targetp->instance].type].model_num[subtype]; + bound_rc = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 ); + } + break; +#endif + + case OBJ_JUMP_NODE: + modelnum = Jump_nodes[targetp->instance].modelnum; + bound_rc = model_find_2d_bound_min( modelnum, &targetp->orient, &targetp->pos,&x1,&y1,&x2,&y2 ); + break; + + default: + Int3(); // should never happen + return; + } + + Hud_target_w = x2-x1+1; + if ( Hud_target_w > gr_screen.clip_width ) { + Hud_target_w = gr_screen.clip_width; + } + + Hud_target_h = y2-y1+1; + if ( Hud_target_h > gr_screen.clip_height ) { + Hud_target_h = gr_screen.clip_height; + } + + if ( targetp->type == OBJ_ASTEROID ) { + if ( OBJ_INDEX(targetp) == Player_ai->target_objnum ) { + team = TEAM_TRAITOR; + } else { + team = SELECTION_SET; + } + } else { + team = obj_team(targetp); + } + + if ( draw_box == TRUE ) { + float distance; + int color; + color = hud_brackets_get_iff_color(team); + // maybe color as tagged + if ( ship_is_tagged(targetp) ) { + color = IFF_COLOR_TAGGED; + } + distance = hud_find_target_distance( targetp, Player_obj ); + gr_set_color_fast(&IFF_colors[color][1]); + draw_bounding_brackets(x1-5,y1-5,x2+5,y2+5,0,0,distance, OBJ_INDEX(targetp)); + } + + if ( targetp->type == OBJ_SHIP ) { + draw_bounding_brackets_subobject(); + } + } +} + +void hud_update_target_in_reticle(vertex *projected_v) +{ + float mag_squared; + mag_squared = (projected_v->sx-Hud_reticle_center[gr_screen.res][0])*(projected_v->sx-Hud_reticle_center[gr_screen.res][0]) + + (projected_v->sy-Hud_reticle_center[gr_screen.res][1])*(projected_v->sy-Hud_reticle_center[gr_screen.res][1]); + + if (mag_squared < Outer_circle_radius[gr_screen.res]*Outer_circle_radius[gr_screen.res]) { + // this information can be used elsewhere + Target_in_reticle = 1; + } + else { + // this information can be used elsewhere + Target_in_reticle = 0; + } +} + + + + +// hud_show_targeting_gauges() will display the targeting information on the HUD. Called once per frame. +// +// Must be inside a g3_start_frame() +// input: frametime => time in seconds since last update +// in_cockpit => flag (default value 1) indicating whether viewpoint is from cockpit or external +void hud_show_targeting_gauges(float frametime, int in_cockpit) +{ + vertex target_point; // temp vertex used to find screen position for 3-D object; + vector target_pos; + + // draw the triangle that points to the closest hostile ship that is firing on the player + // This is always drawn, even if there is no current target. There is also a hook that will + // maybe warn the player via a voice message of an attacking ship. + if ( in_cockpit ) { + hud_show_hostile_triangle(); + } + + if (Player_ai->target_objnum == -1) + return; + + object * targetp = &Objects[Player_ai->target_objnum]; + Players[Player_num].lead_indicator_active = 0; + + // check to see if there is even a current target + if ( targetp == &obj_used_list ) { + return; + } + + Target_in_reticle = 0; + + // AL 1/20/97: Point to targted subsystem if one exists + if ( Player_ai->targeted_subsys != NULL ) { + get_subsystem_world_pos(targetp, Player_ai->targeted_subsys, &target_pos); + + Player_ai->current_target_distance = vm_vec_dist_quick(&target_pos,&Player_obj->pos); + } else { + target_pos = targetp->pos; + + Player_ai->current_target_distance = hud_find_target_distance(targetp,Player_obj); + } + + // find the current target vertex + // + // The 2D screen pos depends on the current viewer position and orientation. + g3_rotate_vertex(&target_point,&target_pos); + + + hud_set_iff_color( targetp, 1 ); + g3_project_vertex(&target_point); + + if (!(target_point.flags & PF_OVERFLOW)) { // make sure point projected + if (target_point.codes == 0) { // target center is not on screen + hud_show_brackets(targetp, &target_point); + } + hud_update_target_in_reticle(&target_point); + } + else { + Hud_target_w = 0; + Hud_target_h = 0; + } + + // show the leading target indicator + if ((hud_gauge_active(HUD_LEAD_INDICATOR)) && (!Player->target_is_dying)) { + hud_show_lead_indicator(&target_pos); + } + + if ( in_cockpit ) { + // show the indicator that orbits the outer reticle and points in the direction of the target + hud_show_target_triangle_indicator(&target_point); + + // draw the orientation tee that orbits the inside of the outer circle of the reticle + if ((hud_gauge_active(HUD_ORIENTATION_TEE)) && (!Player->target_is_dying)) { + hud_show_orientation_tee(); + } + + // display the information about the target + if ( hud_gauge_active(HUD_TARGET_MONITOR) ){ + if ( !hud_targetbox_static_maybe_blit(frametime) ) + hud_show_target_data(frametime); + } + + // update cargo scanning + hud_cargo_scan_update(targetp, frametime); + + // draw the shield icon for the current target + if ( hud_gauge_active(HUD_TARGET_SHIELD_ICON) ) { + hud_shield_show(targetp); + } + + // draw the mini target+shield gauge that sits near the bottom of the retcle + if ( hud_gauge_active(HUD_TARGET_MINI_ICON) ) { + int show_gauge_flag=1; + // is gauge configured as a popup? + if ( hud_gauge_is_popup(HUD_TARGET_MINI_ICON) ) { + if ( !hud_gauge_popup_active(HUD_TARGET_MINI_ICON) ) { + show_gauge_flag=0; + } + } + + if ( show_gauge_flag ) { + hud_shield_show_mini(targetp); + } + } + } else { + Player->cargo_inspect_time = 0; + player_stop_cargo_scan_sound(); + } + + // display the lock indicator + if (!Player->target_is_dying) { + hud_update_lock_indicator(frametime); + hud_show_lock_indicator(frametime); + + // update and render artillery + hud_artillery_update(); + hud_artillery_render(); + } + + // Point to offscreen target + if ( hud_gauge_active(HUD_OFFSCREEN_INDICATOR) ) { + if (target_point.codes != 0) { // target center is not on screen + // draw the offscreen indicator at the edge of the screen where the target is closest to + Assert(Player_ai->target_objnum != -1); + + // AL 11-11-97: don't draw the indicator if the ship is messaging, the indicator is drawn + // in the message sending color in hud_show_message_sender() + if ( Message_shipnum != Objects[Player_ai->target_objnum].instance ) { + if ( hud_gauge_maybe_flash(HUD_OFFSCREEN_INDICATOR) != 1) { + float dist; + hud_set_iff_color( targetp, 1 ); + //dist = vm_vec_dist_quick(&Player_obj->pos, &target_pos); + dist = hud_find_target_distance( targetp, Player_obj ); + hud_draw_offscreen_indicator(&target_point, &target_pos, dist); + } + } + } + } +} + +// hud_show_hostile_triangle() will draw an empty triangle that oribits around the outer +// circle of the reticle. It will point to the closest enemy that is firing on the player. +// Currently, it points to the closest enemy that has the player as its target_objnum and has +// SM_ATTACK or SM_SUPER_ATTACK as its ai submode. + +void hud_show_hostile_triangle() +{ + object* A; + float min_distance=1e20f; + float new_distance=0.0f; + object* hostile_obj = &obj_used_list; + object* nearest_obj = &obj_used_list; + ai_info *aip; + ship_obj *so; + ship *sp; + ship_subsys *ss, *nearest_turret_subsys = NULL; + + int player_obj_index = OBJ_INDEX(Player_obj); + int turret_is_attacking = 0; + + so = GET_FIRST(&Ship_obj_list); + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + + A = &Objects[so->objnum]; + sp = &Ships[A->instance]; + + // only look at ships on other team + if ( (A == Player_obj) || (Ships[A->instance].team & Player_ship->team) ) { + continue; + } + + aip = &Ai_info[Ships[A->instance].ai_index]; + + // dont look at ignore ships + if ( sp->flags & TARGET_SHIP_IGNORE_FLAGS ) { + continue; + } + + // always ignore cargo containers and navbuoys + if ( Ship_info[sp->ship_info_index].flags & SIF_HARMLESS ) { + continue; + } + + // check if ship is stealthy + if (awacs_get_level(&Objects[sp->objnum], Player_ship, 1) < 1) { + continue; + } + + turret_is_attacking = 0; + + // check if any turrets on ship are firing at the player (only on non fighter-bombers) + if ( !(Ship_info[sp->ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ) { + for (ss = GET_FIRST(&sp->subsys_list); ss != END_OF_LIST(&sp->subsys_list); ss = GET_NEXT(ss) ) { + if ( (ss->system_info->type == SUBSYSTEM_TURRET) && (ss->current_hits > 0) ) { + + if ( ss->turret_enemy_objnum == player_obj_index ) { + turret_is_attacking = 1; + + vector gsubpos; + // get world pos of subsystem + vm_vec_unrotate(&gsubpos, &ss->system_info->pnt, &A->orient); + vm_vec_add2(&gsubpos, &A->pos); + new_distance = vm_vec_dist_quick(&gsubpos, &Player_obj->pos); + + if (new_distance <= min_distance) { + min_distance=new_distance; + nearest_obj = A; + nearest_turret_subsys = ss; + } + } + } + } + } + + if ( !turret_is_attacking ) { + // check for ships attacking the player + if ( aip->target_objnum != Player_ship->objnum ) { + continue; + } + + // ignore enemy if not in chase mode + if ( (Game_mode & GM_NORMAL) && (aip->mode != AIM_CHASE) ) { + continue; + } + + new_distance = vm_vec_dist_quick(&A->pos, &Player_obj->pos); + + if (new_distance <= min_distance) { + min_distance=new_distance; + nearest_obj = A; + nearest_turret_subsys = NULL; + } + } + } + + if ( nearest_obj == &obj_used_list ) { + return; + } + + if ( min_distance > MIN_DISTANCE_TO_CONSIDER_THREAT ) { + return; + } + + hostile_obj = nearest_obj; + + // hook to maybe warn player about this attacking ship + ship_maybe_warn_player(&Ships[nearest_obj->instance], min_distance); + + // check if the closest firing hostile is the current target, if so return + if (OBJ_INDEX(hostile_obj) == Player_ai->target_objnum) + return; + + if ( hud_gauge_active(HUD_HOSTILE_TRIANGLE) ) { + if ( hud_gauge_maybe_flash(HUD_HOSTILE_TRIANGLE) != 1 ) { +// hud_set_iff_color( TEAM_HOSTILE, 1 ); // Note: This should really be TEAM_HOSTILE, not opposite of Player_ship->team. + hud_set_iff_color( hostile_obj, 1 ); + hud_render_triangle(&hostile_obj->pos, 0, 1, 0); + } + } +} + +// Return the bank number for the primary weapon that can fire the farthest, from +// the number of active primary weapons +// input: range => output parameter... it is the range of the selected bank +int hud_get_best_primary_bank(float *range) +{ + int i, best_bank, bank_to_fire, num_to_test; + float weapon_range, farthest_weapon_range; + ship_weapon *swp; + weapon_info *wip; + + swp = &Player_ship->weapons; + + farthest_weapon_range = 0.0f; + best_bank = -1; + + if ( Player_ship->flags & SF_PRIMARY_LINKED ) { + num_to_test = swp->num_primary_banks; + } else { + num_to_test = min(1, swp->num_primary_banks); + } + + for ( i = 0; i < num_to_test; i++ ) { + + bank_to_fire = (swp->current_primary_bank+i)%2; // Max supported banks is 2 + + // calculate the range of the weapon, and only display the lead target indicator when + // if the weapon can actually hit the target + Assert(bank_to_fire >= 0); + Assert(swp->primary_bank_weapons[bank_to_fire] >= 0); + wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]]; + weapon_range = wip->max_speed * wip->lifetime; + + if ( weapon_range > farthest_weapon_range ) { + best_bank = bank_to_fire; + farthest_weapon_range = weapon_range; + } + } + + *range = farthest_weapon_range; + return best_bank; +} + +// ----------------------------------------------------------------------------- +// polish_predicted_target_pos() +// +// Called by the draw lead indicator code to predict where the enemy is going to be +// +void polish_predicted_target_pos(vector *enemy_pos, vector *predicted_enemy_pos, float dist_to_enemy, vector *last_delta_vec, int num_polish_steps) +{ + int iteration; + vector player_pos = Player_obj->pos; + float time_to_enemy; + vector last_predicted_enemy_pos = *predicted_enemy_pos; + + ship *shipp; + shipp = &Ships[Player_obj->instance]; + Assert(shipp->weapons.current_primary_bank < shipp->weapons.num_primary_banks); + weapon_info *wip = &Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]]; + + float weapon_speed = wip->max_speed; + + vm_vec_zero(last_delta_vec); + + for (iteration=0; iteration < num_polish_steps; iteration++) { + dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos); + time_to_enemy = dist_to_enemy/weapon_speed; +// vm_vec_scale_add(predicted_enemy_pos, enemy_pos, &Objects[Player_ai->target_objnum].orient.fvec, en_physp->speed * time_to_enemy); + vm_vec_scale_add(predicted_enemy_pos, enemy_pos, &Objects[Player_ai->target_objnum].phys_info.vel, time_to_enemy); + vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos); + last_predicted_enemy_pos= *predicted_enemy_pos; + } +} + +// determine the correct frame to draw for the lead indicator +// 0 -> center only (in secondary range only) +// 1 -> full (in secondary and primary range) +// 2 -> oustide only (in primary range only) +// +// input: prange => range of current primary weapon +// srange => range of current secondary weapon +// dist_to_target => current dist to target +// +// exit: 0-2 => frame offset +// -1 => don't draw anything +int hudtarget_lead_indicator_pick_frame(float prange, float srange, float dist_to_target) +{ + int frame_offset=-1; + int in_prange=0, in_srange=0; + + if ( dist_to_target < prange ) { + in_prange=1; + } + + if ( dist_to_target < srange ) { + in_srange=1; + } + + if ( in_prange && in_srange ) { + frame_offset=1; + } else if ( in_prange && !in_srange ) { + frame_offset=2; + } else if ( !in_prange && in_srange ) { + frame_offset=0; + } else { + frame_offset=-1; + } + + return frame_offset; +} + + // decide what frame of lead indicator to draw + +// hud_show_lead_indicator() determine where to draw the lead target box and display it +void hud_show_lead_indicator(vector *target_world_pos) +{ + vector target_moving_direction, last_delta_vector, source_pos; + vector *rel_pos; + vertex lead_target_vertex; + object *targetp; + polymodel *po; + ship_weapon *swp; + weapon_info *wip; + float dist_to_target, time_to_target, target_moved_dist, prange, srange; + int bank_to_fire, indicator_frame, frame_offset; + + if (Player_ai->target_objnum == -1) + return; + + targetp = &Objects[Player_ai->target_objnum]; + if ( (targetp->type != OBJ_SHIP) && (targetp->type != OBJ_WEAPON) && (targetp->type != OBJ_ASTEROID) ) { + return; + } + + // only allow bombs to have lead indicator displayed + if ( targetp->type == OBJ_WEAPON ) { + if ( !(Weapon_info[Weapons[targetp->instance].weapon_info_index].wi_flags & WIF_BOMB) ) { + return; + } + } + + // If the target is out of range, then draw the correct frame for the lead indicator + if ( Lead_indicator_gauge.first_frame == -1 ) { + Int3(); + return; + } + + po = model_get( Player_ship->modelnum ); + swp = &Player_ship->weapons; + + // Added to take care of situation where there are no primary banks on the player ship + // (this may not be possible, depending on what we decide for the weapons loadout rules) + if ( swp->num_primary_banks == 0 ) + return; + + bank_to_fire = hud_get_best_primary_bank(&prange); + if ( bank_to_fire < 0 ) + return; + wip = &Weapon_info[swp->primary_bank_weapons[bank_to_fire]]; + + if (po->n_guns && bank_to_fire != -1 ) { + rel_pos = &po->gun_banks[bank_to_fire].pnt[0]; + } else { + rel_pos = NULL; + } + + // source_pos will contain the world coordinate of where to base the lead indicator prediction + // from. Normally, this will be the world pos of the gun turret of the currently selected primary + // weapon. + source_pos = Player_obj->pos; + if (rel_pos != NULL) { + vector gun_point; + vm_vec_unrotate(&gun_point, rel_pos, &Player_obj->orient); + vm_vec_add2(&source_pos, &gun_point); + } + + // Determine "accurate" distance to target. This is the distance from the player ship + // to the closest point on the bounding box of the target + dist_to_target = hud_find_target_distance(targetp, Player_obj); + + srange = ship_get_secondary_weapon_range(Player_ship); + + if ( swp->current_secondary_bank >= 0 ) { + weapon_info *wip; + int bank = swp->current_secondary_bank; + wip = &Weapon_info[swp->secondary_bank_weapons[bank]]; + if ( wip->wi_flags & WIF_HOMING_ASPECT ) { + if ( !Player->target_in_lock_cone ) { + srange = -1.0f; + } + } + } + + frame_offset=hudtarget_lead_indicator_pick_frame(prange, srange, dist_to_target); + if ( frame_offset < 0 ) { + return; + } + + indicator_frame = Lead_indicator_gauge.first_frame + frame_offset; + + Assert(wip->max_speed != 0); + time_to_target = dist_to_target / wip->max_speed; + + target_moved_dist = targetp->phys_info.speed * time_to_target; + + target_moving_direction = targetp->phys_info.vel; + + // if we've reached here, the lead target indicator will be displayed + Players[Player_num].lead_indicator_active = 1; + + // test if the target is moving at all + if ( vm_vec_mag_quick(&targetp->phys_info.vel) < 0.1f) // Find distance! + Players[Player_num].lead_target_pos = *target_world_pos; + else { + vm_vec_normalize(&target_moving_direction); + vm_vec_scale(&target_moving_direction,target_moved_dist); + vm_vec_add(&Players[Player_num].lead_target_pos, target_world_pos, &target_moving_direction ); + polish_predicted_target_pos(target_world_pos, &Players[Player_num].lead_target_pos, dist_to_target, &last_delta_vector, 1); // Not used:, float time_to_enemy) + } + + g3_rotate_vertex(&lead_target_vertex,&Players[Player_num].lead_target_pos); + + if (lead_target_vertex.codes == 0) { // on screen + + g3_project_vertex(&lead_target_vertex); + if (!(lead_target_vertex.flags & PF_OVERFLOW)) { + + if ( hud_gauge_maybe_flash(HUD_LEAD_INDICATOR) == 1 ) { + hud_set_iff_color(targetp, 0); + } else { + hud_set_iff_color(targetp, 1); + } + + if ( indicator_frame >= 0 ) { + GR_AABITMAP(indicator_frame, fl2i(lead_target_vertex.sx - Lead_indicator_half[gr_screen.res][0]), fl2i(lead_target_vertex.sy - Lead_indicator_half[gr_screen.res][1])); + } + } + } +} + +// hud_cease_subsystem_targeting() will cease targeting the current targets subsystems +// +void hud_cease_subsystem_targeting(int print_message) +{ + int ship_index; + + ship_index = Objects[Player_ai->target_objnum].instance; + if ( ship_index < 0 ) + return; + + Ships[ship_index].last_targeted_subobject[Player_num] = NULL; + Player_ai->targeted_subsys = NULL; + Player_ai->targeted_subsys_parent = -1; + if ( print_message ) { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Deactivating sub-system targeting", 324)); + } + + hud_stop_looped_locking_sounds(); + hud_lock_reset(); +} + +// hud_cease_targeting() will cease all targeting (main target and subsystem) +// +void hud_cease_targeting() +{ + set_target_objnum( Player_ai, -1 ); + Players[Player_num].flags &= ~PLAYER_FLAGS_AUTO_TARGETING; + hud_cease_subsystem_targeting(0); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Deactivating targeting system", 325)); + hud_lock_reset(); +} + +// hud_restore_subsystem_target() will remember the last targeted subsystem +// on a target. +// +void hud_restore_subsystem_target(ship* shipp) +{ + // check if there was a previously targeted sub-system for this target + if ( shipp->last_targeted_subobject[Player_num] != NULL ) { + Player_ai->targeted_subsys = shipp->last_targeted_subobject[Player_num]; + Player_ai->targeted_subsys_parent = Player_ai->target_objnum; + } + else { + Player_ai->targeted_subsys = NULL; + Player_ai->targeted_subsys_parent = -1; + } +} + +// -------------------------------------------------------------------------------- +// get_subsystem_world_pos() returns the world position for a given subobject on a ship +// +vector* get_subsystem_world_pos(object* parent_obj, ship_subsys* subsys, vector* world_pos) +{ + if (subsys == NULL) { + *world_pos = parent_obj->pos; + return world_pos; + } + + vm_vec_unrotate(world_pos, &subsys->system_info->pnt, &parent_obj->orient); + vm_vec_add2(world_pos, &parent_obj->pos); + + return world_pos; +} + +// If Pl_objp is docking, see if it (or the dockee) are in the target view. If so, flash dock +// text on the HUD. +void hud_maybe_flash_docking_text(object *objp) +{ + ai_info *aip; + int docker_objnum, dockee_objnum; + + if ( Player_ai->target_objnum < 0 ) { + return; + } + + if ( objp->type != OBJ_SHIP ) { + return; + } + + aip = &Ai_info[Ships[objp->instance].ai_index]; + docker_objnum = -1; + dockee_objnum = -1; + + if ( aip->ai_flags & AIF_DOCKED ) { + docker_objnum = OBJ_INDEX(objp); + dockee_objnum = aip->dock_objnum; + } + + if ( (Player_ai->target_objnum == docker_objnum) || (Player_ai->target_objnum == dockee_objnum) ) { + hud_targetbox_start_flash(TBOX_FLASH_DOCKED, 2000); + } +} + + +// ---------------------------------------------------------------------------- +// hud_target_change_check() +// +// called once per frame to account for when the target changes +// +void hud_target_change_check() +{ + float current_speed=0.0f; + + // Check if player subsystem target has changed, and reset necessary player flag + if ( Player_ai->targeted_subsys != Player_ai->last_subsys_target ) { + Player->subsys_in_view=-1; + } + + // check if the main target has changed + if (Player_ai->last_target != Player_ai->target_objnum) { + + if ( Player_ai->target_objnum != -1){ + snd_play( &Snds[SND_TARGET_ACQUIRE], 0.0f ); + } + + // if we have a hotkey set active, see if new target is in set. If not in + // set, deselect the current hotkey set. + if ( Player->current_hotkey_set != -1 ) { + htarget_list *hitem, *plist; + + plist = &(Player->keyed_targets[Player->current_hotkey_set]); + for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) { + if ( OBJ_INDEX(hitem->objp) == Player_ai->target_objnum ){ + break; + } + } + if ( hitem == END_OF_LIST(plist) ){ + Player->current_hotkey_set = -1; + } + } + + player_stop_cargo_scan_sound(); + hud_shield_hit_reset(); + hud_targetbox_init_flash(); + hud_targetbox_start_flash(TBOX_FLASH_NAME); + hud_gauge_popup_start(HUD_TARGET_MINI_ICON); + Player->cargo_inspect_time=0; + Player->locking_subsys=NULL; + Player->locking_on_center=0; + Player->locking_subsys_parent=-1; + + Player_ai->current_target_dist_trend = NO_CHANGE; + Player_ai->current_target_speed_trend = NO_CHANGE; + + if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) { + Players[Player_num].flags &= ~PLAYER_FLAGS_MATCH_TARGET; +// player_match_target_speed("", "", XSTR("Matching speed of newly acquired target",-1)); + player_match_target_speed(); + } + else { + if ( Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET ) + Players[Player_num].flags &= ~PLAYER_FLAGS_MATCH_TARGET; // no more target matching. + } + + hud_lock_reset(); + + if ( Player_ai->target_objnum != -1) { + if ( Objects[Player_ai->target_objnum].type == OBJ_SHIP ) { + hud_restore_subsystem_target(&Ships[Objects[Player_ai->target_objnum].instance]); + } + } + + // if this target is docked, then flash DOCKING on the hud for a couple of seconds + hud_targetbox_end_flash(TBOX_FLASH_DOCKED); + if ( Player_ai->target_objnum >= 0 ) { + hud_maybe_flash_docking_text(&Objects[Player_ai->target_objnum]); + } + } + else { + if (Player_ai->current_target_distance < Player_ai->last_dist-0.01){ + Player_ai->current_target_dist_trend = DECREASING; + } else if (Player_ai->current_target_distance > Player_ai->last_dist+0.01){ + Player_ai->current_target_dist_trend = INCREASING; + } else { + Player_ai->current_target_dist_trend = NO_CHANGE; + } + + current_speed = Objects[Player_ai->target_objnum].phys_info.speed; + + if (current_speed < Player_ai->last_speed-0.01){ + Player_ai->current_target_speed_trend = DECREASING; + } else if (current_speed > Player_ai->last_speed+0.01) { + Player_ai->current_target_speed_trend = INCREASING; + } else { + Player_ai->current_target_speed_trend = NO_CHANGE; + } + + if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) { + if ( !(Players[Player_num].flags & PLAYER_FLAGS_MATCH_TARGET) ) { +// player_match_target_speed("", "", XSTR("Matching target speed",-1)); + player_match_target_speed(); + } + } + } + + Player_ai->last_dist = Player_ai->current_target_distance; + Player_ai->last_speed = current_speed; + + Player_ai->last_target = Player_ai->target_objnum; + Player_ai->last_subsys_target = Player_ai->targeted_subsys; +} + +// --------------------------------------------------------------------- +// hud_draw_offscreen_indicator() +// +// draws the offscreen target indicator +// +void hud_draw_offscreen_indicator(vertex* target_point, vector *tpos, float distance) +{ + char buf[32]; + int w = 0, h = 0; + int on_top, on_right, on_left, on_bottom; + float target_x, target_y; + + float xpos,ypos; + // points to draw triangles + float x1=0.0f; + float y1=0.0f; + float x2=0.0f; + float y2=0.0f; + float x3=0.0f; + float y3=0.0f; + float x4=0.0f; + float y4=0.0f; + float x5=0.0f; + float y5=0.0f; + float x6=0.0f; + float y6=0.0f; + + vector targ_to_player; + float dist_behind; + float triangle_sep; + float half_gauge_length, half_triangle_sep; + int in_front; + + // calculate the dot product between the players forward vector and the vector connecting + // the player to the target. Normalize targ_to_player since we want the dot product + // to range between 0 -> 1. + vm_vec_sub(&targ_to_player, &Player_obj->pos, tpos); + vm_vec_normalize(&targ_to_player); + dist_behind = vm_vec_dot(&Player_obj->orient.fvec, &targ_to_player); + + in_front = 0; + + if (dist_behind < 0) { // still in front of player, but not in view + in_front = 1; + dist_behind = dist_behind + 1.0f; + if (dist_behind > 0.2 ){ + triangle_sep = ( dist_behind ) * Max_front_seperation[gr_screen.res]; + } else { + triangle_sep = 0.0f; + } + } + else { + triangle_sep = dist_behind * Max_offscreen_tri_seperation[gr_screen.res] + Max_offscreen_tri_seperation[gr_screen.res]; + } + + if ( triangle_sep > Max_offscreen_tri_seperation[gr_screen.res] + Max_front_seperation[gr_screen.res]){ + triangle_sep = Max_offscreen_tri_seperation[gr_screen.res] + Max_front_seperation[gr_screen.res]; + } + + // calculate these values only once, since it will be used in several places + half_triangle_sep = 0.5f * triangle_sep; + half_gauge_length = half_triangle_sep + Offscreen_tri_base[gr_screen.res]; + + target_x = target_point->x; + target_y = target_point->y; + + // We need to find the screen (x,y) for where to draw the offscreen indicator + // + // The best way I've found is to draw a line from the eye_pos to the target, and + // then use clip_line() to find the screen (x,y) for where the line hits the edge + // of the screen. + // + // The weird thing about clip_line() is that is flips around the two verticies, + // so I use eye_vertex->sx and eye_vertex->sy for the off-screen indicator (x,y) + // + vertex *eye_vertex = NULL; + vertex real_eye_vertex; + eye_vertex = &real_eye_vertex; // this is needed since clip line takes a **vertex + vector eye_pos; + vm_vec_add( &eye_pos, &Eye_position, &View_matrix.fvec); + g3_rotate_vertex(eye_vertex, &eye_pos); + + ubyte codes_or; + codes_or = (ubyte)(target_point->codes | eye_vertex->codes); + clip_line(&target_point,&eye_vertex,codes_or,0); + + if (!(target_point->flags&PF_PROJECTED)) + g3_project_vertex(target_point); + + if (!(eye_vertex->flags&PF_PROJECTED)) + g3_project_vertex(eye_vertex); + + if (eye_vertex->flags&PF_OVERFLOW) { + Int3(); // This is unlikely to happen, but can if a clip goes through the player's eye. + Player_ai->target_objnum = -1; + return; + } + + if (target_point->flags & PF_TEMP_POINT) + free_temp_point(target_point); + + if (eye_vertex->flags & PF_TEMP_POINT) + free_temp_point(eye_vertex); + + xpos = eye_vertex->sx; + ypos = eye_vertex->sy; + + on_left = on_right = on_top = on_bottom = 0; + xpos = (xpos<1) ? 0 : xpos; + ypos = (ypos<1) ? 0 : ypos; + + if ( xpos <= gr_screen.clip_left ) { + xpos = i2fl(gr_screen.clip_left); + on_left = TRUE; + + if ( ypos < (half_gauge_length - gr_screen.clip_top) ) + ypos = half_gauge_length; + + + if ( ypos > (gr_screen.clip_bottom - half_gauge_length) ) + ypos = gr_screen.clip_bottom - half_gauge_length; + + } + else if ( xpos >= gr_screen.clip_right) { + xpos = i2fl(gr_screen.clip_right); + on_right = TRUE; + + if ( ypos < (half_gauge_length - gr_screen.clip_top) ) + ypos = half_gauge_length; + + if ( ypos > (gr_screen.clip_bottom - half_gauge_length) ) + ypos = gr_screen.clip_bottom - half_gauge_length; + + } + else if ( ypos <= gr_screen.clip_top ) { + ypos = i2fl(gr_screen.clip_top); + on_top = TRUE; + + if ( xpos < ( half_gauge_length - gr_screen.clip_left) ) + xpos = half_gauge_length; + + if ( xpos > (gr_screen.clip_right - half_gauge_length) ) + xpos = gr_screen.clip_right - half_gauge_length; + + } + else if ( ypos >= gr_screen.clip_bottom ) { + ypos = i2fl(gr_screen.clip_bottom); + on_bottom = TRUE; + + if ( xpos < ( half_gauge_length - gr_screen.clip_left) ) + xpos = half_gauge_length; + + if ( xpos > (gr_screen.clip_right - half_gauge_length) ) + xpos = gr_screen.clip_right - half_gauge_length; + } + else { + Int3(); + return; + } + + // The offscreen target triangles are drawn according the the diagram below + // + // + // + // x3 x3 + // / | | \. + // / | | \. + // x1___x2 x2___x1 + // | | + // ......|...........|...............(xpos,ypos) + // | | + // x4___x5 x5___x4 + // \ | | / + // \ | | / + // x6 x6 + // + // + + xpos = (float)floor(xpos); + ypos = (float)floor(ypos); + + if ( hud_gauge_active(HUD_OFFSCREEN_RANGE) && (distance > 0) ) { + sprintf(buf,"%d",fl2i(distance+0.5f)); + hud_num_make_mono(buf); + gr_get_string_size(&w, &h, buf); + } else { + buf[0] = 0; + } + + if (on_right) { + x1 = x4 = (xpos+2); + + x2 = x3 = x5 = x6 = x1 - Offscreen_tri_height[gr_screen.res]; + y1 = y2 = ypos - half_triangle_sep; + y3 = y2 - Offscreen_tri_base[gr_screen.res]; + + y4 = y5 = ypos + half_triangle_sep; + y6 = y5 + Offscreen_tri_base[gr_screen.res]; + + if ( buf[0] ) { + gr_string( fl2i(xpos - w - 10), fl2i(ypos - h/2.0f+0.5f), buf); + } + } + else if (on_left) { + x1 = x4 = (xpos-1); + + x2 = x3 = x5 = x6 = x1 + Offscreen_tri_height[gr_screen.res]; + y1 = y2 = ypos - half_triangle_sep; + y3 = y2 - Offscreen_tri_base[gr_screen.res]; + + y4 = y5 = ypos + half_triangle_sep; + y6 = y5 + Offscreen_tri_base[gr_screen.res]; + + if ( buf[0] ) { + gr_string(fl2i(xpos + 10), fl2i(ypos - h/2.0f+0.5f), buf); + } + } + else if (on_top) { + y1 = y4 = (ypos-1); + + y2 = y3 = y5 = y6 = y1 + Offscreen_tri_height[gr_screen.res]; + x1 = x2 = xpos - half_triangle_sep; + x3 = x2 - Offscreen_tri_base[gr_screen.res]; + + x4 = x5 = xpos + half_triangle_sep; + x6 = x5 + Offscreen_tri_base[gr_screen.res]; + + if ( buf[0] ) { + gr_string(fl2i(xpos - w/2.0f+0.5f), fl2i(ypos+10), buf); + } + } + else if (on_bottom) { + y1 = y4 = (ypos+2); + + y2 = y3 = y5 = y6 = y1 - Offscreen_tri_height[gr_screen.res]; + x1 = x2 = xpos - half_triangle_sep; + x3 = x2 - Offscreen_tri_base[gr_screen.res]; + + x4 = x5 = xpos + half_triangle_sep; + x6 = x5 + Offscreen_tri_base[gr_screen.res]; + + if ( buf[0] ) { + gr_string(fl2i(xpos - w/2.0f+0.5f), fl2i(ypos-h-10), buf); + } + } + + hud_tri(x3,y3,x2,y2,x1,y1); + hud_tri(x4,y4,x5,y5,x6,y6); + if (on_right || on_bottom){ + gr_line(fl2i(x2),fl2i(y2),fl2i(x5),fl2i(y5)); + } else if (on_left) { + gr_line(fl2i(x2-1),fl2i(y2),fl2i(x5-1),fl2i(y5)); + } else { + gr_line(fl2i(x2),fl2i(y2-1),fl2i(x5),fl2i(y5-1)); + } + +} + +// Render the HUD afterburner energy gauge +void hud_show_afterburner_gauge() +{ + float percent_left; + int clip_h,w,h; + + if ( Energy_bar_gauges.first_frame == -1 ){ + return; + } + + Assert(Player_ship); + if ( !(Ship_info[Player_ship->ship_info_index].flags & SIF_AFTERBURNER) ) { + percent_left = 0.0f; + } else { + percent_left = Player_ship->afterburner_fuel/Ship_info[Player_ship->ship_info_index].afterburner_fuel_capacity; + } + + if ( percent_left > 1 ) { + percent_left = 1.0f; + } + + clip_h = fl2i( (1.0f - percent_left) * Aburn_coords[gr_screen.res][3] + 0.5f ); + + bm_get_info(Energy_bar_gauges.first_frame,&w,&h); + + if ( clip_h > 0) { + GR_AABITMAP_EX(Energy_bar_gauges.first_frame, Aburn_coords[gr_screen.res][0], Aburn_coords[gr_screen.res][1],w,clip_h,0,0); + } + + if ( clip_h <= Aburn_coords[gr_screen.res][3] ) { + GR_AABITMAP_EX(Energy_bar_gauges.first_frame+1, Aburn_coords[gr_screen.res][0], Aburn_coords[gr_screen.res][1]+clip_h,w,h-clip_h,0,clip_h); + } +} + +// Render the player weapon energy on the HUD +void hud_show_weapon_energy_gauge() +{ + float percent_left; + int clip_h, i, w, h; + + if ( Energy_bar_gauges.first_frame == -1 ){ + return; + } + + if ( Player_ship->weapons.num_primary_banks <= 0 ){ + return; + } + + percent_left = Player_ship->weapon_energy/Ship_info[Player_ship->ship_info_index].max_weapon_reserve; + if ( percent_left > 1 ) { + percent_left = 1.0f; + } + + if ( percent_left <= 0.3 ) { + char buf[32]; + if ( percent_left < 0.1 ) { + gr_set_color_fast(&Color_bright_red); + } + sprintf(buf,XSTR( "%d%%", 326), fl2i(percent_left*100+0.5f)); + hud_num_make_mono(buf); + gr_string(Weapon_energy_text_coords[gr_screen.res][0], Weapon_energy_text_coords[gr_screen.res][1], buf); + } + + hud_set_gauge_color(HUD_WEAPONS_ENERGY); + for ( i = 0; i < Player_ship->weapons.num_primary_banks; i++ ) { + if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[i]) ) { + if ( Weapon_flash_info.is_bright & (1< 0 ) { + GR_AABITMAP_EX(Energy_bar_gauges.first_frame+2, Wenergy_coords[gr_screen.res][0], Wenergy_coords[gr_screen.res][1], w,clip_h,0,0); + } + + if ( clip_h <= Wenergy_coords[gr_screen.res][3] ) { + GR_AABITMAP_EX(Energy_bar_gauges.first_frame+3, Wenergy_coords[gr_screen.res][0], Wenergy_coords[gr_screen.res][1] + clip_h, w,h-clip_h,0,clip_h); + } + + // hud_set_default_color(); +} + +// -------------------------------------------------------------------------------------- +// hud_show_target_triangle_indicator() +// +// Draw the solid triangle that orbits the reticle and points to the nearest target +// +void hud_show_target_triangle_indicator(vertex *projected_v) +{ + float x3,y3,x4,y4; + float xpos,ypos,ang; + + if ( Player_ai->target_objnum == -1) + return; + + object *targetp = &Objects[Player_ai->target_objnum]; + + // draw the targeting triangle that orbits the outside of the outer circle of the reticle + if ((hud_gauge_active(HUD_TARGET_TRIANGLE)) && !Player->target_is_dying && !Target_in_reticle) { + if ( hud_gauge_maybe_flash(HUD_TARGET_TRIANGLE) == 1 ) { + return; + } + + hud_set_iff_color(targetp, 1); + + ang = atan2_safe(projected_v->y,projected_v->x); + xpos = Hud_reticle_center[gr_screen.res][0] + (float)cos(ang)*(Outer_circle_radius[gr_screen.res]+4); + ypos = Hud_reticle_center[gr_screen.res][1] - (float)sin(ang)*(Outer_circle_radius[gr_screen.res]+4); + + xpos += HUD_offset_x; + ypos += HUD_offset_y; + + x3 = xpos - Target_triangle_base[gr_screen.res] * (float)sin(-ang); + y3 = ypos + Target_triangle_base[gr_screen.res] * (float)cos(-ang); + x4 = xpos + Target_triangle_base[gr_screen.res] * (float)sin(-ang); + y4 = ypos - Target_triangle_base[gr_screen.res] * (float)cos(-ang); + + xpos += Target_triangle_height[gr_screen.res] * (float)cos(ang); + ypos -= Target_triangle_height[gr_screen.res] * (float)sin(ang); + + hud_tri(xpos,ypos,x3,y3,x4,y4); + } +} + +// called from hud_show_weapons() to plot out the secondary weapon name and amo +void hud_show_secondary_weapon(int count, ship_weapon *sw, int dual_fire) +{ + char ammo_str[32]; + char weapon_name[NAME_LENGTH + 10]; + int i, w, h, np; + weapon_info *wip; + + np = 1; + if ( sw->num_primary_banks == 2 ) { + np = 0; + } + + for ( i = 0; i < count; i++ ) { + hud_maybe_flash_weapon(sw->num_primary_banks+i); + wip = &Weapon_info[sw->secondary_bank_weapons[i]]; + + // HACK - make Cluster Bomb fit on the HUD. + if(!stricmp(wip->name,"cluster bomb")){ + strcpy(weapon_name, NOX("Cluster")); + } else { + strcpy(weapon_name, wip->name); + } + + hud_end_string_at_first_hash_symbol(weapon_name); + + if ( sw->current_secondary_bank == i ) { + emp_hud_printf(Weapon_sunlinked_x[gr_screen.res], Weapon_secondary_y[gr_screen.res][i] - np*12, EG_NULL, "%c", Lcl_special_chars + 2); + + if ( dual_fire ) { + emp_hud_printf(Weapon_slinked_x[gr_screen.res], Weapon_secondary_y[gr_screen.res][i] - np*12, EG_NULL, "%c", Lcl_special_chars + 2); + } + + emp_hud_string(Weapon_secondary_name_x[gr_screen.res], Weapon_secondary_y[gr_screen.res][i] - np*12, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name); + + if ( (sw->secondary_bank_ammo[i] > 0) && (sw->current_secondary_bank >= 0) ) { + int ms_till_fire = timestamp_until(sw->next_secondary_fire_stamp[sw->current_secondary_bank]); + if ( (ms_till_fire >= 500) && ((wip->fire_wait >= 1 ) || (ms_till_fire > wip->fire_wait*1000)) ) { + emp_hud_printf(Weapon_secondary_reload_x[gr_screen.res], Weapon_secondary_y[gr_screen.res][i] - np*12, EG_NULL, "%d", fl2i(ms_till_fire/1000.0f +0.5f)); + } + } + } else { + emp_hud_string(Weapon_secondary_name_x[gr_screen.res], Weapon_secondary_y[gr_screen.res][i] - np*12, i ? EG_WEAPON_S1 : EG_WEAPON_S2, weapon_name); + } + + // print out the ammo right justified + sprintf(ammo_str, "%d", sw->secondary_bank_ammo[i]); + hud_num_make_mono(ammo_str); + gr_get_string_size(&w, &h, ammo_str); + + emp_hud_string(Weapon_secondary_ammo_x[gr_screen.res] - w, Weapon_secondary_y[gr_screen.res][i] - np*12, EG_NULL, ammo_str); + + hud_set_gauge_color(HUD_WEAPONS_GAUGE); + } +} + +// start the weapon line (on the HUD) flashing +void hud_start_flash_weapon(int index) +{ + if ( index >= MAX_WEAPON_FLASH_LINES ) { + Int3(); // Get Alan + return; + } + + if ( timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) { + Weapon_flash_info.flash_next[index] = timestamp(TBOX_FLASH_INTERVAL); + Weapon_flash_info.is_bright &= ~(1<= MAX_WEAPON_FLASH_LINES ) { + Int3(); // Get Alan + return; + } + + // hud_set_default_color(); + hud_set_gauge_color(HUD_WEAPONS_GAUGE); + if ( !timestamp_elapsed(Weapon_flash_info.flash_duration[index]) ) { + if ( timestamp_elapsed(Weapon_flash_info.flash_next[index]) ) { + Weapon_flash_info.flash_next[index] = timestamp(TBOX_FLASH_INTERVAL); + Weapon_flash_info.is_bright ^= (1<cmeasure_count); +} + + +// ------------------------------------------------------------------ +// hud_show_weapons() +// +// Show the player's primary and secondary weapons, along with ammo and % energy +// +void hud_show_weapons() +{ + ship_weapon *sw; + int np, ns; // np == num primary, ns == num secondary + char name[NAME_LENGTH]; + + if(Player_obj->type == OBJ_OBSERVER) + return; + + Assert(Player_obj->type == OBJ_SHIP); + Assert(Player_obj->instance >= 0 && Player_obj->instance < MAX_SHIPS); + + sw = &Ships[Player_obj->instance].weapons; + + np = sw->num_primary_banks; + ns = sw->num_secondary_banks; + + // NOTE: I hate to hard-code numbers, but there is no clean way to organize these coords... they + // are all over the place. UGLY. + + // BAH. You're a moron, above guy. :) + + hud_set_gauge_color(HUD_WEAPONS_GAUGE); + + // draw top of primary display + GR_AABITMAP(Weapon_gauges[0].first_frame, Weapon_gauge_primary_coords[gr_screen.res][0][0], Weapon_gauge_primary_coords[gr_screen.res][0][1]); + + emp_hud_string(Weapon_title_coords[gr_screen.res][0], Weapon_title_coords[gr_screen.res][1], EG_WEAPON_TITLE, XSTR( "weapons", 328)); + + switch ( np ) { + case 0: + // draw bottom of border + GR_AABITMAP(Weapon_gauges[2].first_frame, Weapon_gauge_primary_coords[gr_screen.res][1][0], Weapon_gauge_primary_coords[gr_screen.res][1][1]); + + emp_hud_string(Weapon_pname_coords[gr_screen.res][0][0], Weapon_pname_coords[gr_screen.res][0][1], EG_WEAPON_P1, XSTR( "", 329)); + + np = 1; + break; + + case 1: + // draw bottom of border + GR_AABITMAP(Weapon_gauges[2].first_frame, Weapon_gauge_primary_coords[gr_screen.res][1][0], Weapon_gauge_primary_coords[gr_screen.res][1][1]); + + strcpy(name, Weapon_info[sw->primary_bank_weapons[0]].name); + if (Lcl_gr) { + lcl_translate_wep_name(name); + } + + // maybe modify name here to fit + if ( hud_gauge_maybe_flash(HUD_WEAPONS_GAUGE) == 1 ) { + // hud_set_bright_color(); + hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT); + } else { + hud_maybe_flash_weapon(0); + } + + emp_hud_printf(Weapon_plink_coords[gr_screen.res][0][0], Weapon_plink_coords[gr_screen.res][0][1], EG_NULL, "%c", Lcl_special_chars + 2); + emp_hud_printf(Weapon_pname_coords[gr_screen.res][0][0], Weapon_pname_coords[gr_screen.res][0][1], EG_WEAPON_P2, "%s", name); + break; + + case 2: + // draw border to accomodate second primary weapon + GR_AABITMAP(Weapon_gauges[1].first_frame, Weapon_gauge_primary_coords[gr_screen.res][1][0], Weapon_gauge_primary_coords[gr_screen.res][1][1]); + + // draw bottom of border + GR_AABITMAP(Weapon_gauges[2].first_frame, Weapon_gauge_primary_coords[gr_screen.res][2][0], Weapon_gauge_primary_coords[gr_screen.res][2][1]); + + strcpy(name, Weapon_info[sw->primary_bank_weapons[0]].name); + if (Lcl_gr) { + lcl_translate_wep_name(name); + } + // maybe modify name here to fit + + if ( hud_gauge_maybe_flash(HUD_WEAPONS_GAUGE) == 1 ) { + // hud_set_bright_color(); + hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT); + } else { + hud_maybe_flash_weapon(0); + } + if ( (sw->current_primary_bank == 0) || (Player_ship->flags & SF_PRIMARY_LINKED) ) { + emp_hud_printf(Weapon_plink_coords[gr_screen.res][0][0], Weapon_plink_coords[gr_screen.res][0][1], EG_NULL, "%c", Lcl_special_chars + 2); + } + emp_hud_printf(Weapon_pname_coords[gr_screen.res][0][0], Weapon_pname_coords[gr_screen.res][0][1], EG_WEAPON_P1, "%s", name); + + strcpy(name, Weapon_info[sw->primary_bank_weapons[1]].name); + if (Lcl_gr) { + lcl_translate_wep_name(name); + } + // maybe modify name here to fit + if ( hud_gauge_maybe_flash(HUD_WEAPONS_GAUGE) == 1 ) { + // hud_set_bright_color(); + hud_set_gauge_color(HUD_WEAPONS_GAUGE, HUD_C_BRIGHT); + } else { + hud_maybe_flash_weapon(1); + } + if ( sw->current_primary_bank == 1 || (Player_ship->flags & SF_PRIMARY_LINKED) ) { + emp_hud_printf(Weapon_plink_coords[gr_screen.res][1][0], Weapon_plink_coords[gr_screen.res][1][1], EG_NULL, "%c", Lcl_special_chars + 2); + } + emp_hud_printf(Weapon_pname_coords[gr_screen.res][1][0], Weapon_pname_coords[gr_screen.res][1][1], EG_WEAPON_P2, "%s", name); + np = 0; + break; + + default: + Int3(); // can't happen - get Alan + return; + + } // end switch + + hud_set_gauge_color(HUD_WEAPONS_GAUGE); + + switch ( ns ) { + case 0: + // draw the bottom of the secondary weapons + GR_AABITMAP(Weapon_gauges[4].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][0][0], Weapon_gauge_secondary_coords[gr_screen.res][0][1] - 12*np - 1); + + emp_hud_string(Weapon_pname_coords[gr_screen.res][0][0], Weapon_secondary_y[gr_screen.res][0] - np*12, EG_WEAPON_S1, XSTR( "", 329)); + break; + + case 1: + // draw the bottom of the secondary weapons + GR_AABITMAP(Weapon_gauges[4].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][1][0], Weapon_gauge_secondary_coords[gr_screen.res][1][1] - 12*np - 1); + + hud_show_secondary_weapon(1, sw, Player_ship->flags & SF_SECONDARY_DUAL_FIRE); + break; + + case 2: + // draw the middle border, only present when there are 2 or more secondaries + GR_AABITMAP(Weapon_gauges[3].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][2][0], Weapon_gauge_secondary_coords[gr_screen.res][2][1] - np*12); + + // draw the bottom of the secondary weapons + GR_AABITMAP(Weapon_gauges[4].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][3][0], Weapon_gauge_secondary_coords[gr_screen.res][3][1] - 12*np); + + hud_show_secondary_weapon(2, sw, Player_ship->flags & SF_SECONDARY_DUAL_FIRE); + break; + + case 3: + // draw the middle border, only present when there are 2 or more secondaries + GR_AABITMAP(Weapon_gauges[3].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][2][0], Weapon_gauge_secondary_coords[gr_screen.res][2][1] - np*12); + + // draw the bottm border, only present when there are 3 secondaries + GR_AABITMAP(Weapon_gauges[3].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][3][0], Weapon_gauge_secondary_coords[gr_screen.res][3][1] - np*12); + + // draw the bottom of the secondary weapons + GR_AABITMAP(Weapon_gauges[4].first_frame, Weapon_gauge_secondary_coords[gr_screen.res][4][0], Weapon_gauge_secondary_coords[gr_screen.res][4][1] - 12*np); + + hud_show_secondary_weapon(3, sw, Player_ship->flags & SF_SECONDARY_DUAL_FIRE); + break; + + default: + Int3(); // can't happen - get Alan + return; + + } // end switch +} + +// check if targeting is possible based on sensors strength +int hud_sensors_ok(ship *sp, int show_msg) +{ + float sensors_str; + + // If playing on lowest skill level, sensors don't affect targeting + // If dead, still allow player to target, despite any subsystem damage + // If i'm a multiplayer observer, allow me to target + if ( (Game_skill_level == 0) || (Game_mode & GM_DEAD) || ((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)) ) { + return 1; + } + + // if the ship is currently being affected by EMP + if(emp_active_local()){ + return 0; + } + + // ensure targeting functions are not disabled through damage + sensors_str = ship_get_subsystem_strength( sp, SUBSYSTEM_SENSORS ); + if ( (sensors_str < MIN_SENSOR_STR_TO_TARGET) || (ship_subsys_disrupted(sp, SUBSYSTEM_SENSORS)) ) { + if ( show_msg ) { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Targeting is disabled due to sensors damage", 330)); + snd_play(&Snds[SND_TARGET_FAIL]); + } + return 0; + } else { + return 1; + } +} + +int hud_communications_state(ship *sp, int show_msg) +{ + float str; + int comm_state = COMM_OK; + + // If playing on the lowest skill level, communications always ok + // If dead, still allow player to communicate, despite any subsystem damage + if ( Game_skill_level == 0 || (Game_mode & GM_DEAD) ) { + return comm_state; + } + + str = ship_get_subsystem_strength( sp, SUBSYSTEM_COMMUNICATION ); +// str = 1.0f; // DEBUG CODE! MK, change, 11/12/97, comm system could be taken out by one laser, too frustrating. + // Change this back when comm systems have been better placed. + + if ( (str <= 0.01) || ship_subsys_disrupted(sp, SUBSYSTEM_COMMUNICATION) ) { + if ( show_msg ) { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Messaging is restricted due to communications damage", 331)); + } + comm_state = COMM_DESTROYED; + } else if ( str < MIN_COMM_STR_TO_MESSAGE ) { + comm_state = COMM_DAMAGED; + } + + return comm_state; +} + +// target the next or previous hostile/friendly ship +void hud_target_next_list(int hostile, int next_flag) +{ + object *A, *min_obj, *max_obj, *nearest_obj; + ship *shipp; + ship_obj *so; +// vector target_vec; + float cur_dist, min_dist, max_dist, new_dist, nearest_dist, diff; + int timestamp_val, valid_team; + + if ( hostile ) { + timestamp_val = Tl_hostile_reset_timestamp; + Tl_hostile_reset_timestamp = timestamp(TL_RESET); + valid_team = opposing_team_mask(Player_ship->team); + } else { + timestamp_val = Tl_friendly_reset_timestamp; + Tl_friendly_reset_timestamp = timestamp(TL_RESET); + valid_team = Player_ship->team; + } + + // If no target is selected, then simply target the closest ship + if ( Player_ai->target_objnum == -1 || timestamp_elapsed(timestamp_val) ) { + hud_target_closest(valid_team); + return; + } + + cur_dist = hud_find_target_distance(&Objects[Player_ai->target_objnum],Player_obj); + + min_obj = max_obj = nearest_obj = NULL; + min_dist = 1e20f; + max_dist = 0.0f; + if ( next_flag ) { + nearest_dist = 1e20f; + } else { + nearest_dist = 0.0f; + } + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + A = &Objects[so->objnum]; + shipp = &Ships[A->instance]; // get a pointer to the ship information + + if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ) + continue; + + // choose from the correct team + if ( !hud_team_matches_filter(valid_team, shipp->team) ) { + // if we're in multiplayer dogfight, ignore this + if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){ + continue; + } + } + + // always ignore navbuoys and cargo + if ( Ship_info[shipp->ship_info_index].flags & (SIF_CARGO | SIF_NAVBUOY) ) { + continue; + } + + // don't use object if it is already a target + if ( OBJ_INDEX(A) == Player_ai->target_objnum ) { + continue; + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + new_dist = hud_find_target_distance(A,Player_obj); + + if (new_dist <= min_dist) { + min_dist = new_dist; + min_obj = A; + } + + if (new_dist >= max_dist) { + max_dist = new_dist; + max_obj = A; + } + + if ( next_flag ) { + diff = new_dist - cur_dist; + if ( diff > 0 ) { + if ( diff < ( nearest_dist - cur_dist ) ) { + nearest_dist = new_dist; + nearest_obj = A; + } + } + } else { + diff = cur_dist - new_dist; + if ( diff > 0 ) { + if ( diff < ( cur_dist - nearest_dist ) ) { + nearest_dist = new_dist; + nearest_obj = A; + } + } + } + } + + if ( nearest_obj == NULL ) { + + if ( next_flag ) { + if ( min_obj != NULL ) { + nearest_obj = min_obj; + } + } else { + if ( max_obj != NULL ) { + nearest_obj = max_obj; + } + } + } + + if (nearest_obj != NULL) { + // set new target + set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) ); + + // maybe set new turret subsystem + hud_maybe_set_sorted_turret_subsys(&Ships[nearest_obj->instance]); + hud_restore_subsystem_target(&Ships[nearest_obj->instance]); + } + else { + snd_play( &Snds[SND_TARGET_FAIL], 0.0f ); + } +} + +// draw auto-target icon +void hud_auto_target_icon() +{ + int frame_offset; + + if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) { + frame_offset = 1; + } else { + frame_offset = 0; + } + + // draw the box background + hud_set_gauge_color(HUD_AUTO_TARGET); + GR_AABITMAP(Toggle_gauge.first_frame+frame_offset, Toggle_target_gauge_coords[gr_screen.res][0], Toggle_target_gauge_coords[gr_screen.res][1]); + + // draw the text on top + if (frame_offset == 1) { + color text_color; + gr_init_alphacolor(&text_color, 0, 0, 0, Toggle_text_alpha); + gr_set_color_fast(&text_color); + + } + gr_string(Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_AUTOT][0], Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_AUTOT][1], XSTR("auto", 1463)); + gr_string(Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_TARGET][0], Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_TARGET][1], XSTR("target", 1465)); +} + +// draw auto-speed match icon +void hud_auto_speed_match_icon() +{ + int frame_offset; + + if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) { + frame_offset = 3; + } else { + frame_offset = 2; + } + + hud_set_gauge_color(HUD_AUTO_SPEED); + + GR_AABITMAP(Toggle_gauge.first_frame+frame_offset, Toggle_speed_gauge_coords[gr_screen.res][0], Toggle_speed_gauge_coords[gr_screen.res][1]); + + // draw the text on top + if (frame_offset == 3) { + color text_color; + gr_init_alphacolor(&text_color, 0, 0, 0, Toggle_text_alpha); + gr_set_color_fast(&text_color); + } + gr_string(Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_AUTOS][0], Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_AUTOS][1], XSTR("auto", 1463)); + gr_string(Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_SPEED][0], Hud_toggle_coords[gr_screen.res][TOGGLE_TEXT_SPEED][1], XSTR("speed", 1464)); +} + +// display the auto-targeting and auto-speed-matching icons on the HUD +void hud_show_auto_icons() +{ + int show_flag; + if ( Toggle_gauge.first_frame == -1 ) + return; + + // display auto target icon + if ( hud_gauge_active(HUD_AUTO_TARGET) ) { + show_flag=1; + // is gauge configured as a popup? + if ( hud_gauge_is_popup(HUD_AUTO_TARGET) ) { + if ( !hud_gauge_popup_active(HUD_AUTO_TARGET) ) { + show_flag=0; + } + } + + if ( show_flag ) { + hud_auto_target_icon(); + } + } + + // display auto speed match icon + if ( hud_gauge_active(HUD_AUTO_SPEED) ) { + show_flag=1; + // is gauge configured as a popup? + if ( hud_gauge_is_popup(HUD_AUTO_SPEED) ) { + if ( !hud_gauge_popup_active(HUD_AUTO_SPEED) ) { + show_flag=0; + } + } + + if ( show_flag ) { + hud_auto_speed_match_icon(); + } + } +} + +// Set the player target to the closest friendly repair ship +// input: goal_objnum => Try to find repair ship where aip->goal_objnum matches this +// output: 1 => A repair ship was targeted +// 0 => No targeting change +int hud_target_closest_repair_ship(int goal_objnum) +{ + object *A; + object *nearest_obj=&obj_used_list; + ship *shipp; + ship_obj *so; + float min_distance=1e20f; + float new_distance=0.0f; + int rval=0; + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + A = &Objects[so->objnum]; + shipp = &Ships[A->instance]; // get a pointer to the ship information + + // ignore all ships that aren't repair ships + if ( !(Ship_info[shipp->ship_info_index].flags & SIF_SUPPORT) ) { + continue; + } + + if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ) + continue; + + // only consider friendly ships + if ( !hud_team_matches_filter(Player_ship->team, shipp->team)) { + // if we're in multiplayer dogfight, ignore this + if(!((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT))){ + continue; + } + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + if ( goal_objnum >= 0 ) { + if ( Ai_info[shipp->ai_index].goal_objnum != goal_objnum ) { + continue; + } + } + + new_distance = hud_find_target_distance(A,Player_obj); + + if (new_distance <= min_distance) { + min_distance=new_distance; + nearest_obj = A; + } + } + + if (nearest_obj != &obj_used_list) { + set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) ); + hud_restore_subsystem_target(&Ships[nearest_obj->instance]); + rval=1; + } + else { + // inform player how to get a support ship + if ( goal_objnum == -1 ) { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No support ships in area. Use messaging to call one in.", 332)); + } + rval=0; + } + + return rval; +} + +void hud_target_toggle_hidden_from_sensors() +{ + if ( TARGET_SHIP_IGNORE_FLAGS & SF_HIDDEN_FROM_SENSORS ) { + TARGET_SHIP_IGNORE_FLAGS &= ~SF_HIDDEN_FROM_SENSORS; + HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Target hiding from sensors disabled")); + } else { + TARGET_SHIP_IGNORE_FLAGS |= SF_HIDDEN_FROM_SENSORS; + HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("Target hiding from sensors enabled")); + } +} + +// target the closest uninspected object +void hud_target_closest_uninspected_object() +{ + object *A, *nearest_obj = NULL; + ship *shipp; + ship_obj *so; + float min_distance = 1e20f; + float new_distance = 0.0f; + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + + A = &Objects[so->objnum]; + shipp = &Ships[A->instance]; // get a pointer to the ship information + + if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ){ + continue; + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + // ignore all non-cargo carrying craft + if ( !hud_target_ship_can_be_scanned(shipp) ) { + continue; + } + + new_distance = hud_find_target_distance(A,Player_obj); + + if (new_distance <= min_distance) { + min_distance=new_distance; + nearest_obj = A; + } + } + + if (nearest_obj != NULL) { + set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) ); + hud_restore_subsystem_target(&Ships[nearest_obj->instance]); + } + else { + snd_play( &Snds[SND_TARGET_FAIL] ); + } +} + +// target the next or previous uninspected/unscanned object +void hud_target_uninspected_object(int next_flag) +{ + object *A, *min_obj, *max_obj, *nearest_obj; + ship *shipp; + ship_obj *so; + float cur_dist, min_dist, max_dist, new_dist, nearest_dist, diff; + + // If no target is selected, then simply target the closest uninspected cargo + if ( Player_ai->target_objnum == -1 || timestamp_elapsed(Target_next_uninspected_object_timestamp) ) { + Target_next_uninspected_object_timestamp = timestamp(TL_RESET); + hud_target_closest_uninspected_object(); + return; + } + + Target_next_uninspected_object_timestamp = timestamp(TL_RESET); + + cur_dist = hud_find_target_distance(&Objects[Player_ai->target_objnum], Player_obj); + + min_obj = max_obj = nearest_obj = NULL; + min_dist = 1e20f; + max_dist = 0.0f; + if ( next_flag ) { + nearest_dist = 1e20f; + } else { + nearest_dist = 0.0f; + } + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + A = &Objects[so->objnum]; + shipp = &Ships[A->instance]; // get a pointer to the ship information + + if ( (A == Player_obj) || (shipp->flags & TARGET_SHIP_IGNORE_FLAGS) ) + continue; + + // ignore all non-cargo carrying craft + if ( !hud_target_ship_can_be_scanned(shipp) ) { + continue; + } + + // don't use object if it is already a target + if ( OBJ_INDEX(A) == Player_ai->target_objnum ) { + continue; + } + + if(hud_target_invalid_awacs(A)){ + continue; + } + + new_dist = hud_find_target_distance(A, Player_obj); + + if (new_dist <= min_dist) { + min_dist = new_dist; + min_obj = A; + } + + if (new_dist >= max_dist) { + max_dist = new_dist; + max_obj = A; + } + + if ( next_flag ) { + diff = new_dist - cur_dist; + if ( diff > 0 ) { + if ( diff < ( nearest_dist - cur_dist ) ) { + nearest_dist = new_dist; + nearest_obj = A; + } + } + } else { + diff = cur_dist - new_dist; + if ( diff > 0 ) { + if ( diff < ( cur_dist - nearest_dist ) ) { + nearest_dist = new_dist; + nearest_obj = A; + } + } + } + } + + if ( nearest_obj == NULL ) { + + if ( next_flag ) { + if ( min_obj != NULL ) { + nearest_obj = min_obj; + } + } else { + if ( max_obj != NULL ) { + nearest_obj = max_obj; + } + } + } + + if (nearest_obj != NULL) { + set_target_objnum( Player_ai, OBJ_INDEX(nearest_obj) ); + hud_restore_subsystem_target(&Ships[nearest_obj->instance]); + } + else { + snd_play( &Snds[SND_TARGET_FAIL] ); + } +} + +// ---------------------------------------------------------------- +// +// Target Last Transmission Sender code START +// +// ---------------------------------------------------------------- + +typedef struct transmit_target +{ + int objnum; + int objsig; +} transmit_target; + +static int Transmit_target_next_slot = 0; +static int Transmit_target_current_slot = -1; +static int Transmit_target_reset_timer = timestamp(0); + +#define MAX_TRANSMIT_TARGETS 10 +static transmit_target Transmit_target_list[MAX_TRANSMIT_TARGETS]; + +// called once per level to initialize the target last transmission sender list +void hud_target_last_transmit_level_init() +{ + int i; + + for ( i = 0; i < MAX_TRANSMIT_TARGETS; i++ ) { + Transmit_target_list[i].objnum = -1; + Transmit_target_list[i].objsig = -1; + } + + Transmit_target_next_slot = 0; + Transmit_target_current_slot = 0; + Transmit_target_reset_timer = timestamp(0); +} + +// internal function only.. used to find index for last recorded ship transmission +int hud_target_last_transmit_newest() +{ + int latest_slot; + + latest_slot = Transmit_target_next_slot - 1; + if ( latest_slot < 0 ) { + latest_slot = MAX_TRANSMIT_TARGETS - 1; + } + + return latest_slot; +} + +// called externally to set the player target to the last ship which sent a tranmission to the player +void hud_target_last_transmit() +{ + int i; + + if ( Transmit_target_current_slot < 0 ) { + Transmit_target_current_slot = hud_target_last_transmit_newest(); + } + + // If timed out, then simply target the last ship to transmit + if ( timestamp_elapsed(Transmit_target_reset_timer) ) { + Transmit_target_current_slot = hud_target_last_transmit_newest(); + } + + Transmit_target_reset_timer = timestamp(TL_RESET); + + int play_fail_sound = 1; + int transmit_index = Transmit_target_current_slot; + Assert(transmit_index >= 0); + for ( i = 0; i < MAX_TRANSMIT_TARGETS; i++ ) { + if ( Transmit_target_list[transmit_index].objnum >= 0 ) { + int transmit_objnum = Transmit_target_list[transmit_index].objnum; + + if ( Player_ai->target_objnum == transmit_objnum ) { + play_fail_sound = 0; + } else { + if ( Transmit_target_list[transmit_index].objsig == Objects[Transmit_target_list[transmit_index].objnum].signature ) { + if ( !(Ships[Objects[transmit_objnum].instance].flags & TARGET_SHIP_IGNORE_FLAGS) ) { + Transmit_target_current_slot = transmit_index-1; + if ( Transmit_target_current_slot < 0 ) { + Transmit_target_current_slot = MAX_TRANSMIT_TARGETS - 1; + } + break; + } + } + } + } + + transmit_index--; + if ( transmit_index < 0 ) { + transmit_index = MAX_TRANSMIT_TARGETS - 1; + } + } + + if ( i == MAX_TRANSMIT_TARGETS ) { + if ( play_fail_sound ) { + snd_play( &Snds[SND_TARGET_FAIL] ); + } + Transmit_target_current_slot = -1; + return; + } + + if(hud_target_invalid_awacs(&Objects[Transmit_target_list[transmit_index].objnum])){ + return; + } + + // target new ship! + // Fix bug in targeting due to Alt-Y (target last ship sending transmission). + // Was just bogus code in the call to hud_restore_subsystem_target(). -- MK, 9/15/99, 1:59 pm. + int targeted_objnum; + targeted_objnum = Transmit_target_list[transmit_index].objnum; + Assert((targeted_objnum >= 0) && (targeted_objnum < MAX_OBJECTS)); + + if ((targeted_objnum >= 0) && (targeted_objnum < MAX_OBJECTS)) { + set_target_objnum( Player_ai, Transmit_target_list[transmit_index].objnum ); + hud_restore_subsystem_target(&Ships[Objects[Transmit_target_list[transmit_index].objnum].instance]); + } +} + +// called externally to add a message sender to the list +void hud_target_last_transmit_add(int ship_num) +{ + object *ship_objp; + int ship_objnum; + + ship_objnum = Ships[ship_num].objnum; + Assert(ship_objnum >= 0 && ship_objnum < MAX_OBJECTS); + ship_objp = &Objects[ship_objnum]; + Assert(ship_objp->type == OBJ_SHIP); + + Transmit_target_list[Transmit_target_next_slot].objnum = ship_objnum; + Transmit_target_list[Transmit_target_next_slot].objsig = ship_objp->signature; + Transmit_target_next_slot++; + if ( Transmit_target_next_slot >= MAX_TRANSMIT_TARGETS ) { + Transmit_target_next_slot = 0; + } +} + +// target a random ship (useful for EMP stuff) +void hud_target_random_ship() +{ + int shipnum; + int objnum; + + shipnum = ship_get_random_ship(); + if((shipnum < 0) || (Ships[shipnum].objnum < 0)){ + return; + } + objnum = Ships[shipnum].objnum; + + if((objnum >= 0) && (Player_ai != NULL) && !hud_target_invalid_awacs(&Objects[objnum])){ + // never target yourself + if(objnum == OBJ_INDEX(Player_obj)){ + set_target_objnum(Player_ai, -1); + } else { + set_target_objnum(Player_ai, objnum); + } + } +} + +// ---------------------------------------------------------------- +// +// Target Last Transmission Sender code END +// +// ---------------------------------------------------------------- + +void hudtarget_page_in() +{ + int i; + + for ( i = 0; i < NUM_WEAPON_GAUGES; i++ ) { + bm_page_in_aabitmap( Weapon_gauges[i].first_frame, Weapon_gauges[i].num_frames); + } + bm_page_in_aabitmap( Lead_indicator_gauge.first_frame, Lead_indicator_gauge.num_frames); + bm_page_in_aabitmap( Energy_bar_gauges.first_frame, Energy_bar_gauges.num_frames); + bm_page_in_aabitmap( Toggle_gauge.first_frame, Toggle_gauge.num_frames); + bm_page_in_aabitmap( Cmeasure_gauge.first_frame, Cmeasure_gauge.num_frames); +} diff --git a/src/hud/hudtargetbox.cpp b/src/hud/hudtargetbox.cpp new file mode 100644 index 0000000..519e629 --- /dev/null +++ b/src/hud/hudtargetbox.cpp @@ -0,0 +1,1919 @@ +/* + * $Logfile: /Freespace2/code/Hud/HUDtargetbox.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for drawing the target monitor box on the HUD + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 31 11/02/99 3:22p Jefff + * translation of targetbox text + * + * 30 10/29/99 10:41p Jefff + * more subsystem fixes + * + * 29 10/28/99 11:17p Jefff + * used escape seqs for some special German chars + * + * 28 10/28/99 2:02a Jefff + * revised the subsystem localization + * + * 27 9/14/99 11:03p Jefff + * dont draw target names from # on (weapons case) + * + * 26 9/04/99 5:17p Andsager + * Make event log record name of destroyed subsytem, and use this name for + * different types of turrets + * + * 25 8/25/99 11:35a Andsager + * Move hud render ship subsystem target box for ships with Autocenter + * + * 24 8/17/99 7:32p Jefff + * models use autocenter in target view + * + * 23 8/01/99 12:39p Dave + * Added HUD contrast control key (for nebula). + * + * 22 7/31/99 4:15p Dave + * Fixed supernova particle velocities. Handle OBJ_NONE in target + * monitoring view. Properly use objectives notify gauge colors. + * + * 21 7/28/99 2:49p Andsager + * Make hud target speed use vm_vec_mag (not vm_vec_mag_quick) + * + * 20 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 19 7/02/99 10:56a Andsager + * Put in big ship - big ship attack mode. Modify stealth sweep ai. + * + * 18 6/29/99 3:16p Andsager + * Debug stuff. + * + * 17 6/10/99 3:43p Dave + * Do a better job of syncing text colors to HUD gauges. + * + * 16 6/09/99 2:55p Andsager + * Allow multiple asteroid subtypes (of large, medium, small) and follow + * family. + * + * 15 6/07/99 4:20p Andsager + * Add HUD color for tagged object. Apply to target and radar. + * + * 14 6/03/99 11:43a Dave + * Added the ability to use a different model when rendering to the HUD + * target box. + * + * 13 5/24/99 9:02a Andsager + * Remove Int3() in turret subsys name code when turret has no weapon. + * + * 12 5/21/99 1:42p Andsager + * Added error checking for HUD turret name + * + * 11 5/20/99 7:00p Dave + * Added alternate type names for ships. Changed swarm missile table + * entries. + * + * 10 5/19/99 3:50p Andsager + * Show type of debris is debris field (species debris or asteroid). Show + * type of subsystem turret targeted (laser, missile, flak, beam). + * + * 9 5/14/99 4:22p Andsager + * Modify hud_render_target_ship to show damaged subsystems in their + * actual state. Now also shows rotation of subsystems. + * + * 8 4/16/99 5:54p Dave + * Support for on/off style "stream" weapons. Real early support for + * target-painting lasers. + * + * 7 1/07/99 9:08a Jasen + * HUD coords + * + * 6 12/28/98 3:17p Dave + * Support for multiple hud bitmap filenames for hi-res mode. + * + * 5 12/21/98 5:03p Dave + * Modified all hud elements to be multi-resolution friendly. + * + * 4 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 108 8/28/98 3:28p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 107 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 106 6/19/98 3:49p Lawrance + * localization tweaks + * + * 105 6/17/98 11:04a Lawrance + * localize subsystem names that appear on the HUD + * + * 104 6/09/98 5:18p Lawrance + * French/German localization + * + * 103 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 102 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 101 5/20/98 3:52p Allender + * fixed compiler warnings + * + * 100 5/20/98 12:59p John + * Turned optimizations on for debug builds. Also turning on automatic + * function inlining. Turned off the unreachable code warning. + * + * 99 5/15/98 8:36p Lawrance + * Add 'target ship that last sent transmission' target key + * + * 98 5/14/98 11:26a Lawrance + * ensure fighter bays are drawn with correct bracket color + * + * 97 5/08/98 5:32p Lawrance + * Allow cargo scanning even if target gauge is disabled + * + * 96 5/04/98 10:51p Lawrance + * remove unused local + * + * 95 5/04/98 9:17p Lawrance + * Truncate ship class names at # char when displaying debris on target + * moniter + * + * 94 5/04/98 6:12p Lawrance + * Write generic function hud_end_string_at_first_hash_symbol(), to use in + * various spots on the HUD + * + * 93 4/15/98 12:55a Lawrance + * Show time to impact for bombs + * + * 92 4/02/98 6:31p Lawrance + * remove asteroid references if DEMO defined + * + * 91 3/31/98 5:18p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * + * 90 3/30/98 1:08a Lawrance + * Implement "blast" icon. Blink HUD icon when player ship is hit by a + * blast. + * + * $NoKeywords: $ + */ + +#include "2d.h" +#include "3d.h" +#include "3dinternal.h" +#include "object.h" +#include "hud.h" +#include "hudtarget.h" +#include "hudbrackets.h" +#include "hudets.h" +#include "model.h" +#include "missionparse.h" +#include "debris.h" +#include "weapon.h" +#include "player.h" +#include "gamesnd.h" +#include "freespace.h" +#include "bmpman.h" +#include "timer.h" +#include "subsysdamage.h" +#include "hudtargetbox.h" +#include "font.h" +#include "asteroid.h" +#include "jumpnode.h" +#include "multi.h" +#include "emp.h" +#include "localize.h" + +int Target_window_coords[GR_NUM_RESOLUTIONS][4] = +{ + { // GR_640 + 8, 362, 131, 112 + }, + { // GR_1024 + 8, 629, 131, 112 + } +}; + +object *Enemy_attacker = NULL; + +static int Target_static_next; +static int Target_static_playing; +int Target_static_looping; + +#ifndef NDEBUG +extern int Show_target_debug_info; +extern int Show_target_weapons; +#endif + +// used to print out + or - after target distance and speed +char* modifiers[] = { +//XSTR:OFF +"+", +"-", +"" +//XSTR:ON +}; + +char Target_view_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "targetview1", + "targetview1" +}; +char Target_integ_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "targetview2", + "targetview2" +}; +char Target_extra_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = { + "targetview3", + "targetview3" +}; + +// animation frames for the target view monitor +// frames: 0 => background of target monitor +// 1 => foreground of target monitor +hud_frames Target_view_gauge; +int Target_view_gauge_loaded = 0; + +// animation frames for the extended target information +// frames: 0 => normal gague +hud_frames Target_view_extra; +int Target_view_extra_loaded = 0; + +// animation frames for the target view monitor integrity bar +// frames: 0 => dark bar +// 1 => bright bar +hud_frames Target_view_integrity_gauge; +int Target_view_integrity_gauge_loaded = 0; + +#define NUM_TBOX_COORDS 11 // keep up to date +#define TBOX_BACKGROUND 0 +#define TBOX_NAME 1 +#define TBOX_CLASS 2 +#define TBOX_DIST 3 +#define TBOX_SPEED 4 +#define TBOX_CARGO 5 +#define TBOX_HULL 6 +#define TBOX_EXTRA 7 +#define TBOX_EXTRA_ORDERS 8 +#define TBOX_EXTRA_TIME 9 +#define TBOX_EXTRA_DOCK 10 + +int Targetbox_coords[GR_NUM_RESOLUTIONS][NUM_TBOX_COORDS][2] = +{ + { // GR_640 + {5,319}, + {13,316}, + {13,325}, + {13,337}, + {90,337}, + {13,349}, + {139,361}, + {5,283}, + {13,280}, + {13,290}, + {13,299} + }, + { // GR_1024 + {5,590}, + {13,587}, + {13,597}, + {13,608}, + {90,608}, + {13,620}, + {139,632}, + {5,555}, + {13,552}, + {13,561}, + {13,570} + } +}; + +int Integrity_bar_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 138, 371, 4, 88 + }, + { // GR_1024 + 138, 642, 4, 88 + } +}; +int Integrity_string_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 112, 372 + }, + { // GR_1024 + 112, 643 + } +}; + +// cargo scanning extents +int Cargo_scan_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 7, 364, 130, 109 + }, + { // GR_1024 + 7, 635, 130, 109 + } +}; + +// first element is time flashing expires, second element is time of next flash +int Targetbox_flash_timers[NUM_TBOX_FLASH_TIMERS][2]; +int Targetbox_flash_flags; + +// flag to indicate whether to show the extra information about a target +// The HUD_config controls whether this can be shown... but the player can still toggle it on/off +// during the game. +int Targetbox_show_extra_info = 1; + +// Different target states. This drives the text display right below the hull integrity on the targetbox. +#define TS_DIS 0 +#define TS_OK 1 +#define TS_DMG 2 +#define TS_CRT 3 + +static int Last_ts; // holds last target status. + +void hud_blit_target_integrity(int disabled,int force_obj_num = -1); + +// cut down long subsystem names to a more manageable length +char *hud_targetbox_truncate_subsys_name(char *outstr) +{ + if(Lcl_gr){ + if ( strstr(outstr, "communication") ) { + strcpy(outstr, "Komm"); + } else if ( !stricmp(outstr, "weapons") ) { + strcpy(outstr, "Waffen"); + } else if ( strstr(outstr, "engine") || strstr(outstr, "Engine")) { + strcpy(outstr, "Antrieb"); + } else if ( !stricmp(outstr, "sensors") ) { + strcpy(outstr, "Sensoren"); + } else if ( strstr(outstr, "navigat") ) { + strcpy(outstr, "Nav"); + } else if ( strstr(outstr, "fighterbay") || strstr(outstr, "Fighterbay") ) { + strcpy(outstr, "J\x84gerhangar"); + } else if ( strstr(outstr, "missile") ) { + strcpy(outstr, "Raketenwerfer"); + } else if ( strstr(outstr, "laser") || strstr(outstr, "turret") ) { + strcpy(outstr, "Gesch\x81tzturm"); + } else if ( strstr(outstr, "Command Tower") || strstr(outstr, "Bridge") ) { + strcpy(outstr, "Br\x81""cke"); + } else if ( strstr(outstr, "Barracks") ) { + strcpy(outstr, "Quartiere"); + } else if ( strstr(outstr, "Reactor") ) { + strcpy(outstr, "Reaktor"); + } else if ( strstr(outstr, "RadarDish") ) { + strcpy(outstr, "Radarantenne"); + } else if (!stricmp(outstr, "Gas Collector")) { + strcpy(outstr, "Sammler"); + } + } else if(Lcl_fr){ + if ( strstr(outstr, "communication") ) { + strcpy(outstr, "comm"); + } else if ( !stricmp(outstr, "weapons") ) { + strcpy(outstr, "armes"); + } else if ( strstr(outstr, "engine") ) { + strcpy(outstr, "moteur"); + } else if ( !stricmp(outstr, "sensors") ) { + strcpy(outstr, "detecteurs"); + } else if ( strstr(outstr, "navi") ) { + strcpy(outstr, "nav"); + } else if ( strstr(outstr, "missile") ) { + strcpy(outstr, "lanceur de missiles"); + } else if ( strstr(outstr, "fighter") ) { + strcpy(outstr, "baie de chasse"); + } else if ( strstr(outstr, "laser") || strstr(outstr, "turret") || strstr(outstr, "missile") ) { + strcpy(outstr, "tourelle"); + } + } else { + if (!strnicmp(outstr, XSTR( "communication", 333), 3)) { + strcpy( outstr, XSTR( "comm", 334) ); + } else if (!strnicmp(outstr, XSTR( "navigation", 335), 3)) { + strcpy( outstr, XSTR( "nav", 336) ); + } else if (!stricmp(outstr, "Gas Collector")) { + strcpy(outstr, "Collector"); + } + } + + return outstr; +} + +// init a specific targetbox timer +void hud_targetbox_init_flash_timer(int index) +{ + Targetbox_flash_timers[index][0] = 1; + Targetbox_flash_timers[index][1] = 1; + Targetbox_flash_flags &= ~(1< item to flash +// flash_fast => optional param (default value 0), flash twice as fast +// exit: 1 => set bright color +// 0 => set default color +int hud_targetbox_maybe_flash(int index, int flash_fast) +{ + int draw_bright=0; + + // hud_set_default_color(); + hud_set_gauge_color(HUD_TARGET_MONITOR); + if ( !timestamp_elapsed(Targetbox_flash_timers[index][0]) ) { + if ( timestamp_elapsed(Targetbox_flash_timers[index][1]) ) { + if ( flash_fast ) { + Targetbox_flash_timers[index][1] = timestamp(fl2i(TBOX_FLASH_INTERVAL/2.0f)); + } else { + Targetbox_flash_timers[index][1] = timestamp(TBOX_FLASH_INTERVAL); + } + Targetbox_flash_flags ^= (1< pointer to ship object that you want strength values for +// shields => OUTPUT parameter: percentage value of shields (0->1.0) +// integrity => OUTPUT parameter: percentage value of integrity (0->1.0) +// +void hud_get_target_strength(object *objp, float *shields, float *integrity) +{ + ship_info *sip; + + if ( objp->type != OBJ_SHIP ) { + Int3(); + return; + } + + sip = &Ship_info[Ships[objp->instance].ship_info_index]; + + if (!( sip->shields == 0.0f )){ + *shields = get_shield_strength(objp) / sip->shields; + } else { + *shields = 0.0f; + } + + if (*shields < 0.0f){ + *shields = 0.0f; + } + + if ( sip->initial_hull_strength == 0 ) { + Int3(); // illegal initial hull strength + *integrity = 0.0f; + return; + } + + *integrity = objp->hull_strength / sip->initial_hull_strength; + if (*integrity < 0) + *integrity = 0.0f; +} + +// maybe draw the extra targeted ship information above the target monitor +void hud_targetbox_show_extra_ship_info(ship *target_shipp, ai_info *target_aip) +{ + char outstr[256], tmpbuf[256]; + int has_orders = 0; + int not_training; + int extra_data_shown=0; + + hud_set_gauge_color(HUD_TARGET_MONITOR_EXTRA_DATA); + + not_training = !(The_mission.game_type & MISSION_TYPE_TRAINING); + if ( not_training && (hud_gauge_active(HUD_TARGET_MONITOR_EXTRA_DATA)) && (Targetbox_show_extra_info) ) { + // Print out current orders if the targeted ship is friendly + // AL 12-26-97: only show orders and time to target for friendly ships + if ( (Player_ship->team == target_shipp->team) && !(ship_get_SIF(target_shipp) & SIF_NOT_FLYABLE) ) { + extra_data_shown=1; + if ( ship_return_orders(outstr, target_shipp) ) { + gr_force_fit_string(outstr, 255, 162); + has_orders = 1; + } else { + strcpy(outstr, XSTR( "no orders", 337)); + } + + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_EXTRA_ORDERS][0], Targetbox_coords[gr_screen.res][TBOX_EXTRA_ORDERS][1], EG_TBOX_EXTRA1, outstr); + } + + if ( has_orders ) { + sprintf(outstr, XSTR( "time to: ", 338)); + if ( ship_return_time_to_goal(tmpbuf, target_shipp) ) { + strcat(outstr, tmpbuf); + + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_EXTRA_TIME][0], Targetbox_coords[gr_screen.res][TBOX_EXTRA_TIME][1], EG_TBOX_EXTRA2, outstr); + } + } + } + + // Print out dock status + if ( target_aip->ai_flags & AIF_DOCKED ) { + if ( target_aip->dock_objnum >= 0 ) { + sprintf(outstr, XSTR( "Docked: %s", 339), Ships[Objects[target_aip->dock_objnum].instance].ship_name); + gr_force_fit_string(outstr, 255, 173); + hud_targetbox_maybe_flash(TBOX_FLASH_DOCKED); + + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_EXTRA_DOCK][0], Targetbox_coords[gr_screen.res][TBOX_EXTRA_DOCK][1], EG_TBOX_EXTRA3, outstr); + extra_data_shown=1; + } + } + + if ( extra_data_shown ) { + // hud_set_default_color(); + + GR_AABITMAP(Target_view_extra.first_frame, Targetbox_coords[gr_screen.res][TBOX_EXTRA][0],Targetbox_coords[gr_screen.res][TBOX_EXTRA][1]); + } +} + +// Render a jump node on the target monitor +void hud_render_target_jump_node(object *target_objp) +{ + char outstr[256]; + vector obj_pos = {0.0f,0.0f,0.0f}; + vector camera_eye = {0.0f,0.0f,0.0f}; + matrix camera_orient = IDENTITY_MATRIX; + vector orient_vec, up_vector; + float factor, dist; + int hx, hy, w, h; + + if ( Detail.targetview_model ) { + // take the forward orientation to be the vector from the player to the current target + vm_vec_sub(&orient_vec, &target_objp->pos, &Player_obj->pos); + vm_vec_normalize(&orient_vec); + + factor = target_objp->radius*4.0f; + + // use the player's up vector, and construct the viewers orientation matrix + up_vector = Player_obj->orient.uvec; + vm_vector_2_matrix(&camera_orient,&orient_vec,&up_vector,NULL); + + // normalize the vector from the player to the current target, and scale by a factor to calculate + // the objects position + vm_vec_copy_scale(&obj_pos,&orient_vec,factor); + + hud_render_target_setup(&camera_eye, &camera_orient, 0.5f); + jumpnode_render( target_objp, &obj_pos ); + hud_render_target_close(); + } + + HUD_reset_clip(); + hud_blit_target_foreground(); + hud_blit_target_integrity(1); + // hud_set_default_color(); + hud_set_gauge_color(HUD_TARGET_MONITOR); + + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_NAME][0], Targetbox_coords[gr_screen.res][TBOX_NAME][1], EG_TBOX_NAME, Jump_nodes[target_objp->instance].name); + + dist = vm_vec_dist_quick(&target_objp->pos, &Player_obj->pos); + + // account for hud shaking + hx = fl2i(HUD_offset_x); + hy = fl2i(HUD_offset_y); + + sprintf(outstr,XSTR( "d: %.0f", 340), dist); + hud_num_make_mono(outstr); + gr_get_string_size(&w,&h,outstr); + + emp_hud_printf(Targetbox_coords[gr_screen.res][TBOX_DIST][0]+hx, Targetbox_coords[gr_screen.res][TBOX_DIST][1]+hy, EG_TBOX_DIST, outstr); +} + +// ------------------------------------------------------------------------------------- +// hud_render_target_asteroid() +// +// Render a piece of asteroid on the target monitor +// +void hud_render_target_asteroid(object *target_objp) +{ +#ifndef FS2_DEMO + vector obj_pos = {0.0f,0.0f,0.0f}; + vector camera_eye = {0.0f,0.0f,0.0f}; + matrix camera_orient = IDENTITY_MATRIX; + asteroid *asteroidp; + vector orient_vec, up_vector; + int target_team; + float time_to_impact, factor; + int subtype; + + asteroidp = &Asteroids[target_objp->instance]; + + target_team = obj_team(target_objp); + + subtype = asteroidp->asteroid_subtype; + + if ( Detail.targetview_model ) { + // take the forward orientation to be the vector from the player to the current target + vm_vec_sub(&orient_vec, &target_objp->pos, &Player_obj->pos); + vm_vec_normalize(&orient_vec); + + factor = 2*target_objp->radius; + + // use the player's up vector, and construct the viewers orientation matrix + up_vector = Player_obj->orient.uvec; + vm_vector_2_matrix(&camera_orient,&orient_vec,&up_vector,NULL); + + // normalize the vector from the player to the current target, and scale by a factor to calculate + // the objects position + vm_vec_copy_scale(&obj_pos,&orient_vec,factor); + + hud_render_target_setup(&camera_eye, &camera_orient, 0.5f); + model_clear_instance(Asteroid_info[asteroidp->type].model_num[subtype]); + model_render(Asteroid_info[asteroidp->type].model_num[subtype], &target_objp->orient, &obj_pos, MR_NO_LIGHTING | MR_LOCK_DETAIL ); + hud_render_target_close(); + } + + HUD_reset_clip(); + hud_blit_target_foreground(); + hud_blit_target_integrity(1); + // hud_set_default_color(); + hud_set_gauge_color(HUD_TARGET_MONITOR); + + // hud print type of Asteroid (debris) + char hud_name[64]; + switch (asteroidp->type) { + case ASTEROID_TYPE_SMALL: + case ASTEROID_TYPE_MEDIUM: + case ASTEROID_TYPE_BIG: + strcpy(hud_name, NOX("asteroid")); + break; + + case DEBRIS_TERRAN_SMALL: + case DEBRIS_TERRAN_MEDIUM: + case DEBRIS_TERRAN_LARGE: + strcpy(hud_name, NOX("terran debris")); + break; + + case DEBRIS_VASUDAN_SMALL: + case DEBRIS_VASUDAN_MEDIUM: + case DEBRIS_VASUDAN_LARGE: + strcpy(hud_name, NOX("vasudan debris")); + break; + + case DEBRIS_SHIVAN_SMALL: + case DEBRIS_SHIVAN_MEDIUM: + case DEBRIS_SHIVAN_LARGE: + strcpy(hud_name, NOX("shivan debris")); + break; + + default: + Int3(); + } + + emp_hud_printf(Targetbox_coords[gr_screen.res][TBOX_NAME][0], Targetbox_coords[gr_screen.res][TBOX_NAME][1], EG_TBOX_NAME, hud_name); + + time_to_impact = asteroid_time_to_impact(target_objp); + if ( time_to_impact >= 0 ) { + emp_hud_printf(Targetbox_coords[gr_screen.res][TBOX_CLASS][0], Targetbox_coords[gr_screen.res][TBOX_CLASS][1], EG_TBOX_CLASS, NOX("impact: %.1f sec"), time_to_impact); + } +#endif +} + +void get_turret_subsys_name(model_subsystem *system_info, char *outstr) +{ + Assert(system_info->type == SUBSYSTEM_TURRET); + + if (system_info->turret_weapon_type >= 0) { + // check if beam or flak using weapon flags + if (Weapon_info[system_info->turret_weapon_type].wi_flags & WIF_FLAK) { + sprintf(outstr, "%s", XSTR("Flak turret", 1566)); + } else if (Weapon_info[system_info->turret_weapon_type].wi_flags & WIF_BEAM) { + sprintf(outstr, "%s", XSTR("Beam turret", 1567)); + } else { + + if (Weapon_info[system_info->turret_weapon_type].subtype == WP_LASER) { + sprintf(outstr, "%s", XSTR("Laser turret", 1568)); + } else if (Weapon_info[system_info->turret_weapon_type].subtype == WP_MISSILE) { + sprintf(outstr, "%s", XSTR("Missile lnchr", 1569)); + } else { + // Illegal subtype + Int3(); + sprintf(outstr, "%s", NOX("Turret")); + } + } + } else { + // This should not happen + sprintf(outstr, "%s", NOX("Unused")); + } +} + +// ------------------------------------------------------------------------------------- +// hud_render_target_ship_info() +// +// Render the data for a ship on the target monitor. Called by hud_render_target_ship(). +// +void hud_render_target_ship_info(object *target_objp) +{ + ship *target_shipp; + ship_info *target_sip; + ai_info *target_aip; + int w,h,screen_integrity=1, base_index; + char outstr[256]; + float ship_integrity, shield_strength; + + Assert(target_objp->type == OBJ_SHIP); + target_shipp = &Ships[target_objp->instance]; + target_sip = &Ship_info[target_shipp->ship_info_index]; + target_aip = &Ai_info[target_shipp->ai_index]; + + strcpy( outstr, target_shipp->ship_name ); + + if ( hud_gauge_maybe_flash(HUD_TARGET_MONITOR) == 1 ) { + hud_set_iff_color(target_objp, 1); + } else { + // Print out ship name, with wing name if it exists + if ( hud_targetbox_maybe_flash(TBOX_FLASH_NAME) ) { + hud_set_iff_color(target_objp, 1); + } else { + hud_set_iff_color(target_objp); + } + } + + // take ship "copies" into account before printing ship class name. + base_index = target_shipp->ship_info_index; + if ( target_sip->flags & SIF_SHIP_COPY ) + base_index = ship_info_base_lookup( target_shipp->ship_info_index ); + + // maybe do some translation + if (Lcl_gr) { + lcl_translate_targetbox_name(outstr); + } + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_NAME][0], Targetbox_coords[gr_screen.res][TBOX_NAME][1], EG_TBOX_NAME, outstr); + + // print out ship class + char temp_name[NAME_LENGTH+2] = ""; + + // if this ship has an alternate type name + if(target_shipp->alt_type_index >= 0){ + mission_parse_lookup_alt_index(target_shipp->alt_type_index, temp_name); + } else { + strcpy(temp_name, Ship_info[base_index].name); + if ( strstr(Ship_info[base_index].name, NOX("#")) ) { + strcpy(temp_name, Ship_info[base_index].name); + hud_end_string_at_first_hash_symbol(temp_name); + } + } + + if (Lcl_gr) { + lcl_translate_targetbox_name(temp_name); + } + emp_hud_printf(Targetbox_coords[gr_screen.res][TBOX_CLASS][0], Targetbox_coords[gr_screen.res][TBOX_CLASS][1], EG_TBOX_CLASS, temp_name); + + ship_integrity = 1.0f; + hud_get_target_strength(target_objp, &shield_strength, &ship_integrity); + + // convert to values of 0->100 + shield_strength *= 100.0f; + ship_integrity *= 100.0f; + + screen_integrity = fl2i(ship_integrity+0.5f); + if ( screen_integrity == 0 ) { + if ( ship_integrity > 0 ) { + screen_integrity = 1; + } + } + // Print out right-justified integrity + sprintf(outstr,XSTR( "%d%%", 341), screen_integrity); + gr_get_string_size(&w,&h,outstr); + + if ( hud_gauge_maybe_flash(HUD_TARGET_MONITOR) == 1 ) { + // hud_set_bright_color(); + hud_set_gauge_color(HUD_TARGET_MONITOR, HUD_C_BRIGHT); + } else { + hud_targetbox_maybe_flash(TBOX_FLASH_HULL); + } + + emp_hud_printf(Targetbox_coords[gr_screen.res][TBOX_HULL][0]-w, Targetbox_coords[gr_screen.res][TBOX_HULL][1], EG_TBOX_HULL, "%s", outstr); + hud_set_gauge_color(HUD_TARGET_MONITOR); + + // print out the targeted sub-system and % integrity + if (Player_ai->targeted_subsys != NULL) { + shield_strength = Player_ai->targeted_subsys->current_hits/Player_ai->targeted_subsys->system_info->max_hits *100.0f; + screen_integrity = fl2i(shield_strength+0.5f); + + if ( screen_integrity < 0 ) { + screen_integrity = 0; + } + + if ( screen_integrity == 0 ) { + if ( shield_strength > 0 ) { + screen_integrity = 1; + } + } + + if ( screen_integrity <= 0 ){ + hud_targetbox_start_flash(TBOX_FLASH_SUBSYS); // need to flash 0% continuously + hud_targetbox_maybe_flash(TBOX_FLASH_SUBSYS); + } + + // PRINT SUBSYS NAME + // hud_set_default_color(); + // get turret subsys name + if (Player_ai->targeted_subsys->system_info->type == SUBSYSTEM_TURRET) { + get_turret_subsys_name(Player_ai->targeted_subsys->system_info, outstr); + } else { + sprintf(outstr, "%s", Player_ai->targeted_subsys->system_info->name); + } + hud_targetbox_truncate_subsys_name(outstr); + gr_printf(Target_window_coords[gr_screen.res][0]+2, Target_window_coords[gr_screen.res][1]+Target_window_coords[gr_screen.res][3]-h, outstr); + + // AL 23-3-98: Fighter bays are a special case. Player cannot destroy them, so don't + // show the subsystem strength + if ( strnicmp(NOX("fighter"), Player_ai->targeted_subsys->system_info->name, 7) ) { + sprintf(outstr,XSTR( "%d%%", 341),screen_integrity); + gr_get_string_size(&w,&h,outstr); + gr_printf(Target_window_coords[gr_screen.res][0]+Target_window_coords[gr_screen.res][2]-w-1, Target_window_coords[gr_screen.res][1]+Target_window_coords[gr_screen.res][3] - h, "%s", outstr); + } + + hud_set_gauge_color(HUD_TARGET_MONITOR); + } + + // print out 'disabled' on the monitor if the target is disabled + if ( (target_shipp->flags & SF_DISABLED) || (ship_subsys_disrupted(target_shipp, SUBSYSTEM_ENGINE)) ) { + if ( target_shipp->flags & SF_DISABLED ) { + sprintf(outstr, XSTR( "DISABLED", 342)); + } else { + sprintf(outstr, XSTR( "DISRUPTED", 343)); + } + gr_get_string_size(&w,&h,outstr); + gr_printf(Target_window_coords[gr_screen.res][0]+Target_window_coords[gr_screen.res][2]/2 - w/2 - 1, Target_window_coords[gr_screen.res][1]+Target_window_coords[gr_screen.res][3] - 2*h, "%s", outstr); + } + + hud_targetbox_show_extra_ship_info(target_shipp, target_aip); +} + +// call to draw the integrity bar that is on the right of the target monitor +void hud_blit_target_integrity(int disabled,int force_obj_num) +{ + object *objp; + int clip_h,w,h; + char buf[16]; + int current_ts; + + if ( Target_view_integrity_gauge.first_frame == -1 ) + return; + + if ( disabled ) { + GR_AABITMAP(Target_view_integrity_gauge.first_frame, Integrity_bar_coords[gr_screen.res][0], Integrity_bar_coords[gr_screen.res][1]); + return; + } + + if(force_obj_num == -1){ + Assert(Player_ai->target_objnum >= 0 ); + objp = &Objects[Player_ai->target_objnum]; + } else { + objp = &Objects[Player_ai->target_objnum]; + } + + clip_h = fl2i( (1 - Pl_target_integrity) * Integrity_bar_coords[gr_screen.res][3] ); + + // print out status of ship + if ( (Ships[objp->instance].flags & SF_DISABLED) || (ship_subsys_disrupted(&Ships[objp->instance], SUBSYSTEM_ENGINE)) ) { + sprintf(buf,XSTR( "dis", 344)); + current_ts = TS_DIS; + } else { + if ( Pl_target_integrity > 0.9 ) { + sprintf(buf,XSTR( "ok", 345)); + current_ts = TS_OK; + } else if ( Pl_target_integrity > 0.2 ) { + sprintf(buf,XSTR( "dmg", 346)); + current_ts = TS_DMG; + } else { + sprintf(buf,XSTR( "crt", 347)); + current_ts = TS_CRT; + } + } + + if ( Last_ts != -1 && current_ts != Last_ts ) { + hud_targetbox_start_flash(TBOX_FLASH_STATUS); + } + Last_ts = current_ts; + + hud_targetbox_maybe_flash(TBOX_FLASH_STATUS); + + emp_hud_string(Integrity_string_coords[gr_screen.res][0], Integrity_string_coords[gr_screen.res][1], EG_TBOX_INTEG, buf); + + hud_set_gauge_color(HUD_TARGET_MONITOR); + + bm_get_info(Target_view_integrity_gauge.first_frame,&w,&h); + + if ( clip_h > 0 ) { + // draw the dark portion + GR_AABITMAP_EX(Target_view_integrity_gauge.first_frame, Integrity_bar_coords[gr_screen.res][0], Integrity_bar_coords[gr_screen.res][1], w, clip_h,0,0); + } + + if ( clip_h <= Integrity_bar_coords[gr_screen.res][3] ) { + // draw the bright portion + GR_AABITMAP_EX(Target_view_integrity_gauge.first_frame+1, Integrity_bar_coords[gr_screen.res][0], Integrity_bar_coords[gr_screen.res][1]+clip_h,w,h-clip_h,0,clip_h); + } +} + +// determine if the subsystem is in line-of sight, without taking into accout whether the player ship is +// facing the subsystem +int hud_targetbox_subsystem_in_view(object *target_objp, int *sx, int *sy) +{ + ship_subsys *subsys; + vector subobj_pos; + vertex subobj_vertex; + int rval = -1; + polymodel *pm; + + subsys = Player_ai->targeted_subsys; + if (subsys != NULL ) { + vm_vec_unrotate(&subobj_pos, &subsys->system_info->pnt, &target_objp->orient); + vm_vec_add2(&subobj_pos, &target_objp->pos); + + // is it subsystem in view + if ( Player->subsys_in_view == -1 ) { + rval = ship_subsystem_in_sight(target_objp, subsys, &View_position, &subobj_pos, 0); + } else { + rval = Player->subsys_in_view; + } + + // get screen coords, adjusting for autocenter + Assert(target_objp->type == OBJ_SHIP); + if (target_objp->type == OBJ_SHIP) { + pm = model_get(Ships[target_objp->instance].modelnum); + if (pm->flags & PM_FLAG_AUTOCEN) { + vector temp, delta; + vm_vec_copy_scale(&temp, &pm->autocenter, -1.0f); + vm_vec_unrotate(&delta, &temp, &target_objp->orient); + vm_vec_add2(&subobj_pos, &delta); + } + } + + g3_rotate_vertex(&subobj_vertex, &subobj_pos); + g3_project_vertex(&subobj_vertex); + *sx = (int) subobj_vertex.sx; + *sy = (int) subobj_vertex.sy; + } + + return rval; +} + +void hud_update_cargo_scan_sound() +{ + if ( Player->cargo_inspect_time <= 0 ) { + player_stop_cargo_scan_sound(); + return; + } + player_maybe_start_cargo_scan_sound(); + +} + +// If the player is scanning for cargo, draw some cool scanning lines on the target monitor +void hud_maybe_render_cargo_scan(ship_info *target_sip) +{ + int x1, y1, x2, y2; + int scan_time; // time required to scan ship + + if ( Player->cargo_inspect_time <= 0 ) { + return; + } + + scan_time = target_sip->scan_time; + // hud_set_default_color(); + hud_set_gauge_color(HUD_TARGET_MONITOR, HUD_C_BRIGHT); + + // draw horizontal scan line + x1 = Cargo_scan_coords[gr_screen.res][0]; + y1 = fl2i(0.5f + Cargo_scan_coords[gr_screen.res][1] + ( (i2fl(Player->cargo_inspect_time) / scan_time) * Cargo_scan_coords[gr_screen.res][3] )); + x2 = x1 + Cargo_scan_coords[gr_screen.res][2]; + + gr_line(x1, y1, x2, y1); + + // draw vertical scan line + x1 = fl2i(0.5f + Cargo_scan_coords[gr_screen.res][0] + ( (i2fl(Player->cargo_inspect_time) / scan_time) * Cargo_scan_coords[gr_screen.res][2] )); + y1 = Cargo_scan_coords[gr_screen.res][1]; + y2 = y1 + Cargo_scan_coords[gr_screen.res][3]; + + gr_line(x1, y1-3, x1, y2-1); +} + +// Get the eye position for an object at the origin, called from hud_render_target_ship() +// input: eye_pos => Global pos for eye (output parameter) +// orient => Orientation of object at the origin +void hud_targetbox_get_eye(vector *eye_pos, matrix *orient, int ship_num) +{ + ship *shipp; + polymodel *pm; + eye *ep; + vector origin = {0.0f, 0.0f, 0.0f}; + + shipp = &Ships[ship_num]; + pm = model_get( shipp->modelnum ); + + // If there is no eye, don't do anything + if ( pm->n_view_positions == 0 ) { + return; + } + + ep = &(pm->view_positions[0] ); + + model_find_world_point( eye_pos, &ep->pnt, shipp->modelnum, ep->parent, orient, &origin ); +} + +// ------------------------------------------------------------------------------------- +// hud_render_target_ship() +// +// Render a ship to the target monitor +// +void hud_render_target_ship(object *target_objp) +{ + vector obj_pos = {0.0f,0.0f,0.0f}; + vector camera_eye = {0.0f,0.0f,0.0f}; + matrix camera_orient = IDENTITY_MATRIX; + ship *target_shipp; + ship_info *target_sip; + vector orient_vec, up_vector; + int sx, sy; + int subsys_in_view; + float factor; + + target_shipp = &Ships[target_objp->instance]; + target_sip = &Ship_info[target_shipp->ship_info_index]; + + if ( Detail.targetview_model ) { + // take the forward orientation to be the vector from the player to the current target + vm_vec_sub(&orient_vec, &target_objp->pos, &Player_obj->pos); + vm_vec_normalize(&orient_vec); + + factor = -target_sip->closeup_pos.z; + + // use the player's up vector, and construct the viewers orientation matrix + up_vector = Player_obj->orient.uvec; + vm_vector_2_matrix(&camera_orient,&orient_vec,&up_vector,NULL); + + // normalize the vector from the player to the current target, and scale by a factor to calculate + // the objects position + vm_vec_copy_scale(&obj_pos,&orient_vec,factor); + + // set camera eye to eye of ship relative to origin + // hud_targetbox_get_eye(&camera_eye, &camera_orient, Player_obj->instance); + + hud_render_target_setup(&camera_eye, &camera_orient, target_sip->closeup_zoom); + // model_clear_instance(target_sip->modelnum); + ship_model_start( target_objp ); + + // maybe render a special hud-target-only model + if(target_sip->modelnum_hud >= 0){ + model_render( target_sip->modelnum_hud, &target_objp->orient, &obj_pos, MR_NO_LIGHTING | MR_LOCK_DETAIL | MR_AUTOCENTER); + } else { + model_render( target_sip->modelnum, &target_objp->orient, &obj_pos, MR_NO_LIGHTING | MR_LOCK_DETAIL | MR_AUTOCENTER); + } + ship_model_stop( target_objp ); + + sx = 0; + sy = 0; + // check if subsystem target has changed + if ( Player_ai->targeted_subsys == Player_ai->last_subsys_target ) { + vector save_pos; + save_pos = target_objp->pos; + target_objp->pos = obj_pos; + subsys_in_view = hud_targetbox_subsystem_in_view(target_objp, &sx, &sy); + target_objp->pos = save_pos; + + if ( subsys_in_view != -1 ) { + + // AL 29-3-98: If subsystem is destroyed, draw gray brackets + if ( (Player_ai->targeted_subsys->current_hits <= 0) && (strnicmp(NOX("fighter"), Player_ai->targeted_subsys->system_info->name, 7)) ) { + gr_set_color_fast(&IFF_colors[IFF_COLOR_MESSAGE][1]); + } else { + hud_set_iff_color( target_objp, 1 ); + } + + if ( subsys_in_view ) { + draw_brackets_square_quick(sx - 10, sy - 10, sx + 10, sy + 10); + } else { + draw_brackets_diamond_quick(sx - 10, sy - 10, sx + 10, sy + 10); + } + } + } + hud_render_target_close(); + } + HUD_reset_clip(); + hud_blit_target_foreground(); + hud_blit_target_integrity(0,OBJ_INDEX(target_objp)); + + hud_render_target_ship_info(target_objp); + hud_maybe_render_cargo_scan(target_sip); +} + +// ------------------------------------------------------------------------------------- +// hud_render_target_debris() +// +// Render a piece of debris on the target monitor +// +void hud_render_target_debris(object *target_objp) +{ + vector obj_pos = {0.0f,0.0f,0.0f}; + vector camera_eye = {0.0f,0.0f,0.0f}; + matrix camera_orient = IDENTITY_MATRIX; + debris *debrisp; + vector orient_vec, up_vector; + int target_team, base_index; + float factor; + + debrisp = &Debris[target_objp->instance]; + + //target_sip = &Ship_info[debrisp->ship_info_index]; + target_team = obj_team(target_objp); + + + if ( Detail.targetview_model ) { + // take the forward orientation to be the vector from the player to the current target + vm_vec_sub(&orient_vec, &target_objp->pos, &Player_obj->pos); + vm_vec_normalize(&orient_vec); + + factor = 2*target_objp->radius; + + // use the player's up vector, and construct the viewers orientation matrix + up_vector = Player_obj->orient.uvec; + vm_vector_2_matrix(&camera_orient,&orient_vec,&up_vector,NULL); + + // normalize the vector from the player to the current target, and scale by a factor to calculate + // the objects position + vm_vec_copy_scale(&obj_pos,&orient_vec,factor); + + hud_render_target_setup(&camera_eye, &camera_orient, 0.5f); + model_clear_instance(debrisp->model_num); + submodel_render( debrisp->model_num, debrisp->submodel_num, &target_objp->orient, &obj_pos, MR_NO_LIGHTING | MR_LOCK_DETAIL ); + hud_render_target_close(); + } + + HUD_reset_clip(); + hud_blit_target_foreground(); + hud_blit_target_integrity(1); + // hud_set_default_color(); + hud_set_gauge_color(HUD_TARGET_MONITOR); + + // take ship "copies" into account before printing out ship class information + base_index = debrisp->ship_info_index; + if ( Ship_info[base_index].flags & SIF_SHIP_COPY ) + base_index = ship_info_base_lookup( debrisp->ship_info_index ); + + // print out ship class that debris came from + char *printable_ship_class = Ship_info[base_index].name; + if ( strstr(Ship_info[base_index].name, NOX("#")) ) { + char temp_name[NAME_LENGTH]; + strcpy(temp_name, Ship_info[base_index].name); + hud_end_string_at_first_hash_symbol(temp_name); + printable_ship_class = temp_name; + } + + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_CLASS][0], Targetbox_coords[gr_screen.res][TBOX_CLASS][1], EG_TBOX_CLASS, printable_ship_class); + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_NAME][0], Targetbox_coords[gr_screen.res][TBOX_NAME][1], EG_TBOX_NAME, XSTR( "debris", 348)); +} + +// ------------------------------------------------------------------------------------- +// hud_render_target_weapon() +// +// Render a missile or a missile view to the target monitor +// +void hud_render_target_weapon(object *target_objp) +{ + vector obj_pos = {0.0f,0.0f,0.0f}; + vector camera_eye = {0.0f,0.0f,0.0f}; + matrix camera_orient = IDENTITY_MATRIX; + vector orient_vec, up_vector; + weapon_info *target_wip = NULL; + weapon *wp = NULL; + object *viewer_obj, *viewed_obj; + int target_team, is_homing, is_player_missile, missile_view, viewed_model_num, w, h; + float factor; + char outstr[100]; // temp buffer + + target_team = obj_team(target_objp); + + wp = &Weapons[target_objp->instance]; + target_wip = &Weapon_info[wp->weapon_info_index]; + + is_homing = FALSE; + if ( target_wip->wi_flags & WIF_HOMING && wp->homing_object != &obj_used_list ) + is_homing = TRUE; + + is_player_missile = FALSE; + if ( target_objp->parent_sig == Player_obj->signature ) { + is_player_missile = TRUE; + } + + if ( Detail.targetview_model ) { + + viewer_obj = Player_obj; + viewed_obj = target_objp; + missile_view = FALSE; + viewed_model_num = target_wip->model_num; + if ( is_homing && is_player_missile ) { + viewer_obj = target_objp; + viewed_obj = wp->homing_object; + missile_view = TRUE; + viewed_model_num = Ships[wp->homing_object->instance].modelnum; + } + + // take the forward orientation to be the vector from the player to the current target + vm_vec_sub(&orient_vec, &viewed_obj->pos, &viewer_obj->pos); + vm_vec_normalize(&orient_vec); + + if ( missile_view == FALSE ) + factor = 2*target_objp->radius; + else + factor = vm_vec_dist_quick(&viewer_obj->pos, &viewed_obj->pos); + + // use the viewer's up vector, and construct the viewers orientation matrix + up_vector = viewer_obj->orient.uvec; + vm_vector_2_matrix(&camera_orient,&orient_vec,&up_vector,NULL); + + // normalize the vector from the viewer to the viwed target, and scale by a factor to calculate + // the objects position + vm_vec_copy_scale(&obj_pos,&orient_vec,factor); + + hud_render_target_setup(&camera_eye, &camera_orient, View_zoom/3); + model_clear_instance(viewed_model_num); + model_render( viewed_model_num, &viewed_obj->orient, &obj_pos, MR_NO_LIGHTING | MR_LOCK_DETAIL ); + hud_render_target_close(); + } + + HUD_reset_clip(); + if ( is_homing == TRUE ) { + hud_blit_target_foreground(); + } else { + hud_blit_target_foreground(); + } + + hud_blit_target_integrity(1); + // hud_set_default_color(); + hud_set_gauge_color(HUD_TARGET_MONITOR); + + // print out the weapon class name + sprintf( outstr,"%s", target_wip->name ); + gr_get_string_size(&w,&h,outstr); + + // drop name past the # sign + if ( strstr(outstr, NOX("#")) ) { + hud_end_string_at_first_hash_symbol(outstr); + } + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_NAME][0], Targetbox_coords[gr_screen.res][TBOX_NAME][1], EG_TBOX_NAME, outstr); + + // If a homing weapon, show time to impact + if ( is_homing ) { + float dist, speed; + + dist = vm_vec_dist(&target_objp->pos, &wp->homing_object->pos); + speed = vm_vec_mag(&target_objp->phys_info.vel); + if ( speed > 0 ) { + sprintf(outstr, NOX("impact: %.1f sec"), dist/speed); + } else { + sprintf(outstr, XSTR( "unknown", 349)); + } + + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_CLASS][0], Targetbox_coords[gr_screen.res][TBOX_CLASS][1], EG_TBOX_CLASS, outstr); + } +} + +// ------------------------------------------------------------------------------------- +// hud_render_target_model() will render the target in the small targetting box. The box +// is then shaded to give a monochrome effect +// +void hud_render_target_model() +{ + object *target_objp; + + if ( !hud_gauge_active(HUD_TARGET_MONITOR) ) + return; + + if ( Player_ai->target_objnum == -1) + return; + + if ( Target_static_playing ) + return; + + target_objp = &Objects[Player_ai->target_objnum]; + + // Draw the background frame + hud_render_target_background(); + + switch ( target_objp->type ) { + case OBJ_SHIP: + hud_render_target_ship(target_objp); + break; + + case OBJ_DEBRIS: + hud_render_target_debris(target_objp); + break; + + case OBJ_WEAPON: + hud_render_target_weapon(target_objp); + break; + + case OBJ_ASTEROID: + hud_render_target_asteroid(target_objp); + break; + + case OBJ_JUMP_NODE: + hud_render_target_jump_node(target_objp); + break; + + default: + // Error(LOCATION, "Trying to show object type %d on target monitor\n", target_objp->type); + hud_cease_targeting(); + break; + } // end switch +} + +void hud_cargo_scan_update(object *targetp, float frametime) +{ + char outstr[256]; // temp buffer for sprintf'ing hud output + int hx, hy; + + // Account for HUD shaking + hx = fl2i(HUD_offset_x); + hy = fl2i(HUD_offset_y); + + // display cargo inspection status + if ( targetp->type == OBJ_SHIP ) { + if ( player_inspect_cargo(frametime, outstr) ) { + if ( hud_gauge_active(HUD_TARGET_MONITOR) ) { + if ( Player->cargo_inspect_time > 0 ) { + hud_targetbox_start_flash(TBOX_FLASH_CARGO); + } + + // Print out what the cargo is + if ( hud_gauge_maybe_flash(HUD_TARGET_MONITOR) == 1 ) { + // hud_set_bright_color(); + hud_set_gauge_color(HUD_TARGET_MONITOR, HUD_C_BRIGHT); + } else { + hud_targetbox_maybe_flash(TBOX_FLASH_CARGO); + } + + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_CARGO][0]+hx, Targetbox_coords[gr_screen.res][TBOX_CARGO][1]+hy, EG_TBOX_CARGO, outstr); + hud_set_gauge_color(HUD_TARGET_MONITOR); + } + } + } // end if (is_ship) + +} + +// ----------------------------------------------------------------------------------- +// hud_show_target_data() will display the data about the target in and +// around the targetting window +// +void hud_show_target_data(float frametime) +{ + char outstr[256]; // temp buffer for sprintf'ing hud output + int w,h; // width and height of string about to print + object *target_objp; + ship *shipp = NULL; + debris *debrisp = NULL; + ship_info *sip = NULL; + int is_ship = 0; + + hud_set_gauge_color(HUD_TARGET_MONITOR); + + target_objp = &Objects[Player_ai->target_objnum]; + + switch( Objects[Player_ai->target_objnum].type ) { + case OBJ_SHIP: + shipp = &Ships[target_objp->instance]; + sip = &Ship_info[shipp->ship_info_index]; + is_ship = 1; + break; + + case OBJ_DEBRIS: + debrisp = &Debris[target_objp->instance]; + sip = &Ship_info[debrisp->ship_info_index]; + break; + + case OBJ_WEAPON: + sip = NULL; + break; + + case OBJ_ASTEROID: + sip = NULL; + break; + + case OBJ_JUMP_NODE: + return; + + default: + Int3(); // can't happen + break; + } + + int hx, hy; + + // Account for HUD shaking + hx = fl2i(HUD_offset_x); + hy = fl2i(HUD_offset_y); + + // print out the target distance and speed + sprintf(outstr,XSTR( "d: %.0f%s", 350), Player_ai->current_target_distance, modifiers[Player_ai->current_target_dist_trend]); + + hud_num_make_mono(outstr); + gr_get_string_size(&w,&h,outstr); + + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_DIST][0]+hx, Targetbox_coords[gr_screen.res][TBOX_DIST][1]+hy, EG_TBOX_DIST, outstr); + + float spd; +#if 0 + spd = vm_vec_dist(&target_objp->pos, &target_objp->last_pos) / frametime; +#endif + // 7/28/99 DKA: Do not use vec_mag_quick -- the error is too big + spd = vm_vec_mag(&target_objp->phys_info.vel); +// spd = target_objp->phys_info.fspeed; + if ( spd < 0.1 ) { + spd = 0.0f; + } + // if the speed is 0, determine if we are docked with something -- if so, get the velocity from + // our docked object instead + if ( (spd == 0.0f) && is_ship ) { + ai_info *aip; + object *other_objp; + + aip = &Ai_info[shipp->ai_index]; + if ( aip->ai_flags & AIF_DOCKED ) { + Assert( aip->dock_objnum != -1 ); + other_objp = &Objects[aip->dock_objnum]; + spd = other_objp->phys_info.fspeed; + if ( spd < 0.1 ) + spd = 0.0f; + } + } + + sprintf(outstr, XSTR( "s: %.0f%s", 351), spd, (spd>1)?modifiers[Player_ai->current_target_speed_trend]:""); + hud_num_make_mono(outstr); + + emp_hud_string(Targetbox_coords[gr_screen.res][TBOX_SPEED][0]+hx, Targetbox_coords[gr_screen.res][TBOX_SPEED][1]+hy, EG_TBOX_SPEED, outstr); + + // + // output target info for debug purposes only, this will be removed later + // + +#ifndef NDEBUG + //XSTR:OFF + char outstr2[256]; + if ( Show_target_debug_info && (is_ship == 1) ) { + int sx, sy, dy; + sx = 5; + dy = gr_get_font_height() + 1; + sy = 300 - 7*dy; + + gr_set_color_fast(&HUD_color_debug); + + if ( shipp->ai_index >= 0 ) { + ai_info *aip = &Ai_info[shipp->ai_index]; + + sprintf(outstr,"AI: %s",Ai_behavior_names[aip->mode]); + + switch (aip->mode) { + case AIM_CHASE: + Assert(aip->submode <= SM_BIG_PARALLEL); // Must be <= largest chase submode value. +// sprintf(outstr,"AI: %s",Submode_text[aip->submode]); + sprintf(outstr2," / %s",Submode_text[aip->submode]); + strcat(outstr,outstr2); + break; + case AIM_STRAFE: + Assert(aip->submode <= AIS_STRAFE_POSITION); // Must be <= largest chase submode value. +// sprintf(outstr,"AI: %s",Strafe_submode_text[aip->submode-AIS_STRAFE_ATTACK]); + sprintf(outstr2," / %s",Strafe_submode_text[aip->submode-AIS_STRAFE_ATTACK]); + strcat(outstr,outstr2); + break; + case AIM_WAYPOINTS: +// gr_printf(sx, sy, "Wpnum: %i",aip->wp_index); + sprintf(outstr2," / Wpnum: %i",aip->wp_index); + strcat(outstr,outstr2); + break; + default: + break; + } + + gr_printf(sx, sy, outstr); + sy += dy; + + gr_printf(sx, sy, "Max speed = %d, (%d%%)", (int) shipp->current_max_speed, (int) (100.0f * vm_vec_mag(&target_objp->phys_info.vel)/shipp->current_max_speed)); + sy += dy; + + // data can be found in target montior + // gr_printf(TARGET_WINDOW_X1+TARGET_WINDOW_WIDTH+3, TARGET_WINDOW_Y1+5*h, "Shields: %d", (int) Players[Player_num].current_target->shields); + if (aip->target_objnum != -1) { + char target_str[32]; + float dot, dist; + vector v2t; + + if (aip->target_objnum == Player_obj-Objects) + strcpy(target_str, "Player!"); + else + sprintf(target_str, "%s", Ships[Objects[aip->target_objnum].instance].ship_name); + +// gr_printf(TARGET_WINDOW_X1+TARGET_WINDOW_WIDTH+2, TARGET_WINDOW_Y1+4*h, "Target: %s", target_str); + gr_printf(sx, sy, "Targ: %s", target_str); + sy += dy; + + dist = vm_vec_dist_quick(&Objects[Player_ai->target_objnum].pos, &Objects[aip->target_objnum].pos); + vm_vec_normalized_dir(&v2t,&Objects[aip->target_objnum].pos, &Objects[Player_ai->target_objnum].pos); + + dot = vm_vec_dot(&v2t, &Objects[Player_ai->target_objnum].orient.fvec); + + // data can be found in target montior + // gr_printf(TARGET_WINDOW_X1+TARGET_WINDOW_WIDTH+3, TARGET_WINDOW_Y1+6*h, "Targ dist: %5.1f", dist); +// gr_printf(TARGET_WINDOW_X1+TARGET_WINDOW_WIDTH+2, TARGET_WINDOW_Y1+5*h, "Targ dot: %3.2f", dot); + gr_printf(sx, sy, "Targ dot: %3.2f", dot); + sy += dy; +// gr_printf(TARGET_WINDOW_X1+TARGET_WINDOW_WIDTH+2, TARGET_WINDOW_Y1+6*h, "Targ dst: %3.2f", dist); + gr_printf(sx, sy, "Targ dst: %3.2f", dist); + sy += dy; + + if ( aip->targeted_subsys != NULL ) { + sprintf(outstr, "Subsys: %s", aip->targeted_subsys->system_info->name); + gr_printf(sx, sy, outstr); + } + sy += dy; + } + + // print out energy transfer information on the ship + sy = 70; + + sprintf(outstr,"MAX G/E: %.0f/%.0f",shipp->weapon_energy,shipp->current_max_speed); + gr_printf(sx, sy, outstr); + sy += dy; + + sprintf(outstr,"G/S/E: %.2f/%.2f/%.2f",Energy_levels[shipp->weapon_recharge_index],Energy_levels[shipp->shield_recharge_index],Energy_levels[shipp->engine_recharge_index]); + gr_printf(sx, sy, outstr); + sy += dy; + + // Show information about attacker. + { + int found = 0; + + if (Enemy_attacker != NULL) + if (Enemy_attacker->type == OBJ_SHIP) { + ship *eshipp; + ai_info *eaip; + float dot, dist; + vector v2t; + + eshipp = &Ships[Enemy_attacker->instance]; + eaip = &Ai_info[eshipp->ai_index]; + + if (eaip->target_objnum == Player_obj-Objects) { + found = 1; + dist = vm_vec_dist_quick(&Enemy_attacker->pos, &Player_obj->pos); + vm_vec_normalized_dir(&v2t,&Objects[eaip->target_objnum].pos, &Enemy_attacker->pos); + + dot = vm_vec_dot(&v2t, &Enemy_attacker->orient.fvec); + + gr_printf(sx, sy, "#%i: %s", Enemy_attacker-Objects, Ships[Enemy_attacker->instance].ship_name); + sy += dy; + gr_printf(sx, sy, "Targ dist: %5.1f", dist); + sy += dy; + gr_printf(sx, sy, "Targ dot: %3.2f", dot); + sy += dy; + } + } + + if (Player_ai->target_objnum == Enemy_attacker - Objects) + found = 0; + + if (!found) { + int i; + + Enemy_attacker = NULL; + for (i=0; itarget_objnum) { + enemy = Ai_info[Ships[Objects[i].instance].ai_index].target_objnum; + + if (enemy == Player_obj-Objects) { + Enemy_attacker = &Objects[i]; + break; + } + } + } + } + } + + // Show target size + // hud_target_w + gr_printf(sx, sy, "Targ size: %dx%d", Hud_target_w, Hud_target_h ); + sy += dy; + + polymodel *pm = model_get( shipp->modelnum ); + gr_printf(sx, sy, "POF:%s", pm->filename ); + sy += dy; + + gr_printf(sx, sy, "Mass: %.2f\n", pm->mass); + sy += dy; + } + } + + // display the weapons for the target on the HUD. Include ammo counts. + if ( Show_target_weapons && (is_ship == 1) ) { + int sx, sy, dy, i; + ship_weapon *swp; + + swp = &shipp->weapons; + sx = 400; + sy = 100; + dy = gr_get_font_height(); + + sprintf(outstr,"Num primaries: %d", swp->num_primary_banks); + gr_printf(sx,sy,outstr); + sy += dy; + for ( i = 0; i < swp->num_primary_banks; i++ ) { + sprintf(outstr,"%d. %s", i+1, Weapon_info[swp->primary_bank_weapons[i]].name); + gr_printf(sx,sy,outstr); + sy += dy; + } + + sy += dy; + sprintf(outstr,"Num secondaries: %d", swp->num_secondary_banks); + gr_printf(sx,sy,outstr); + sy += dy; + for ( i = 0; i < swp->num_secondary_banks; i++ ) { + sprintf(outstr,"%d. %s", i+1, Weapon_info[swp->secondary_bank_weapons[i]].name); + gr_printf(sx,sy,outstr); + sy += dy; + } + } + //XSTR:ON + +#endif +} + +// called at the start of each level +void hud_targetbox_static_init() +{ + Target_static_next = 0;; + Target_static_playing = 0; +} + +// determine if we should draw static on top of the target box +int hud_targetbox_static_maybe_blit(float frametime) +{ + float sensors_str; + + // on lowest skill level, don't show static on target monitor + if ( Game_skill_level == 0 ) + return 0; + + // if multiplayer observer, don't show static + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){ + return 0; + } + + sensors_str = ship_get_subsystem_strength( Player_ship, SUBSYSTEM_SENSORS ); + + if ( ship_subsys_disrupted(Player_ship, SUBSYSTEM_SENSORS) ) { + sensors_str = SENSOR_STR_TARGET_NO_EFFECTS-1; + } + + if ( sensors_str > SENSOR_STR_TARGET_NO_EFFECTS ) { + Target_static_playing = 0; + Target_static_next = 0; + } else { + if ( Target_static_next == 0 ) + Target_static_next = 1; + } + + if ( timestamp_elapsed(Target_static_next) ) { + Target_static_playing ^= 1; + Target_static_next = timestamp_rand(50, 750); + } + + if ( Target_static_playing ) { + // hud_set_default_color(); + hud_set_gauge_color(HUD_TARGET_MONITOR); + hud_anim_render(&Target_static, frametime, 1); + if ( Target_static_looping == -1 ) { + Target_static_looping = snd_play_looping(&Snds[SND_STATIC]); + } + } else { + if ( Target_static_looping != -1 ) { + snd_stop(Target_static_looping); + Target_static_looping = -1; + } + } + + return Target_static_playing; +} + +// start the targetbox item flashing for duration ms +// input: index => TBOX_FLASH_ #define +// duration => optional param (default value TBOX_FLASH_DURATION), how long to flash in ms +void hud_targetbox_start_flash(int index, int duration) +{ + Targetbox_flash_timers[index][0] = timestamp(duration); +} + +// stop flashing a specific targetbox item +void hud_targetbox_end_flash(int index) +{ + Targetbox_flash_timers[index][0] = 1; +} + +// determine if a given flashing index is bright or not +int hud_targetbox_is_bright(int index) +{ + return (Targetbox_flash_flags & (1< 1.0 + int status[HUD_WINGMAN_MAX_SHIPS_PER_WINGS]; // HUD_WINGMAN_STATUS_* +} wingman_status; + +wingman_status HUD_wingman_status[HUD_WINGMAN_MAX_WINGS]; + +#define HUD_WINGMAN_UPDATE_STATUS_INTERVAL 200 +static int HUD_wingman_update_timer; + +static int HUD_wingman_flash_duration[HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS]; +static int HUD_wingman_flash_next[HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS]; +static int HUD_wingman_flash_is_bright; + +// coords to draw wingman status icons, for 1-5 wings (0-4) +int HUD_wingman_left_coords[GR_NUM_RESOLUTIONS][5][2] = { + { // GR_640 + {550, 144}, // where to draw the left part of gauge if we have 1 wing + {550, 144}, // "" 2 wings + {515, 144}, // "" 3 wings + {480, 144}, // "" 4 wings + {445, 144} // "" 5 wings + }, + { // GR_1024 + {932, 144}, + {932, 144}, + {897, 144}, + {862, 144}, + {827, 144} + }, +}; +int HUD_wingman_middle_coords[GR_NUM_RESOLUTIONS][5][2] = { + { // GR_640 + {0, 0}, // we never draw this for 1 wing + {0, 0}, // we never draw this for 2 wings + {586, 144}, // where to draw the _first_ middle gauge for 3 wings + {551, 144}, // "" 4 wings + {516, 144} // "" 5 wings + }, + { // GR_1024 + {0, 0}, + {0, 0}, + {968, 144}, + {933, 144}, + {898, 144} + } +}; +int HUD_wingman_right_coords[GR_NUM_RESOLUTIONS][5][2] = { + { // GR_640 + {621, 144}, // always drawn in the same spot + {621, 144}, + {621, 144}, + {621, 144}, + {621, 144}, + }, + { // GR_1024 + {1003, 144}, + {1003, 144}, + {1003, 144}, + {1003, 144}, + {1003, 144}, + } +}; + +// special coordinates if only one wing is present +int HUD_wingman_status_single_coords[GR_NUM_RESOLUTIONS][4][2] = +{ + { // GR_640 + {589,159}, // where to draw dots 1 - 4 if we have only one wing present (special case) + {582,167}, + {596,167}, + {589,175}, + }, + { // GR_1024 + {971,159}, + {964,167}, + {978,167}, + {971,175}, + } +}; + +int HUD_wingman_status_name_coords[GR_NUM_RESOLUTIONS][HUD_WINGMAN_MAX_WINGS][2] = +{ + { // GR_640 + {459,185}, // duplicated the first item because we only ever display 5 items + {459,185}, + {494,185}, + {529,185}, + {564,185}, + {599,185}, + }, + { // GR_1024 + {841,185}, // duplicated the first item because we only ever display 5 items + {841,185}, + {876,185}, + {911,185}, + {946,185}, + {981,185}, + } +}; + +int HUD_wingman_status_coords[GR_NUM_RESOLUTIONS][HUD_WINGMAN_MAX_WINGS][HUD_WINGMAN_MAX_SHIPS_PER_WINGS][2] = +{ + // duplicated first set of data because we will only ever display up to 5 wings + { // GR_640 + // 1 wing present + {{467,159}, // ship 1 + {460,167}, // ship 2 + {474,167}, // ship 3 + {467,175}, // ship 4 + {456,175}, // ship 5 + {478,175}}, // ship 6 + + // 1 wing present + {{467,159}, + {460,167}, + {474,167}, + {467,175}, + {456,175}, + {478,175}}, + + // 2 wings present + {{502,159}, + {495,167}, + {509,167}, + {502,175}, + {491,175}, + {513,175}}, + + // 3 wings present + {{537,159}, + {530,167}, + {544,167}, + {537,175}, + {526,175}, + {548,175}}, + + // 4 wings present + {{572,159}, + {565,167}, + {579,167}, + {572,175}, + {561,175}, + {583,175}}, + + // 5 wings present + {{607,159}, + {600,167}, + {614,167}, + {607,175}, + {596,175}, + {618,175}}, + }, + { // GR_1024 + {{849,159}, + {842,167}, + {856,167}, + {849,175}, + {838,175}, + {860,175}}, + + {{849,159}, + {842,167}, + {856,167}, + {849,175}, + {838,175}, + {860,175}}, + + {{884,159}, + {877,167}, + {891,167}, + {884,175}, + {873,175}, + {895,175}}, + + {{919,159}, + {912,167}, + {926,167}, + {919,175}, + {908,175}, + {930,175}}, + + {{954,159}, + {947,167}, + {961,167}, + {954,175}, + {943,175}, + {965,175}}, + + {{989,159}, + {982,167}, + {996,167}, + {989,175}, + {978,175}, + {1000,175}}, + } +}; + +int hud_wingman_status_wing_index(char *wing_name) +{ +//XSTR:OFF + if ( !stricmp("alpha", wing_name) ) { + return 0; + } else if ( !stricmp("beta", wing_name) ) { + return 1; + } else if ( !stricmp("gamma", wing_name) ) { + return 2; + } else if ( !stricmp("delta", wing_name) ) { + return 3; + } else if ( !stricmp("epsilon", wing_name) ) { + return 4; + } else if ( (Game_mode & GM_MULTIPLAYER) && IS_MISSION_MULTI_TEAMS && !stricmp("zeta", wing_name) ) { + return 5; + } else { + return -1; + } +//XSTR:ON +} + +// flag a player wing ship as destroyed +void hud_set_wingman_status_dead(int wing_index, int wing_pos) +{ + Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS); + Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS); + + HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_DEAD; +} + +// flags a given player wing ship as departed +void hud_set_wingman_status_departed(int wing_index, int wing_pos) +{ + Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS); + Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS); + + HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_NOT_HERE; +} + +// flags a given player wing ship as not existing +void hud_set_wingman_status_none( int wing_index, int wing_pos) +{ + int i; + + Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS); + Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS); + + HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_NONE; + + int used = 0; + for ( i = 0; i < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; i++ ) { + if ( HUD_wingman_status[wing_index].status[i] != HUD_WINGMAN_STATUS_NONE ) { + used = 1; + break; + } + } + + HUD_wingman_status[wing_index].used = used; +} + +// flags a given player wing ship as "alive" (for multiplayer respawns ) +void hud_set_wingman_status_alive( int wing_index, int wing_pos) +{ + Assert(wing_index >= 0 && wing_index < HUD_WINGMAN_MAX_WINGS); + Assert(wing_pos >= 0 && wing_index < HUD_WINGMAN_MAX_SHIPS_PER_WINGS); + + HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_ALIVE; +} + +// get the hull percent for a specific ship, return value 0.0 -> 1.0 +float hud_get_ship_hull_percent(int ship_index) +{ + ship_info *sip; + object *ship_objp; + + ship_objp = &Objects[Ships[ship_index].objnum]; + sip = &Ship_info[Ships[ship_index].ship_info_index]; + + return (ship_objp->hull_strength / sip->initial_hull_strength); +} + +void hud_wingman_status_init_late_wings() +{ +/* + int i, j, wing_index; + + for ( i = 0; i < num_wings; i++ ) { + wing_index = hud_wingman_status_wing_index(Wings[i].name); + + if ( (wing_index >= 0) && (Wings[i].total_arrived_count == 0) ) { + HUD_wingman_status[wing_index].used = 1; + for (j = 0; j < Wings[i].wave_count; j++) { + HUD_wingman_status[wing_index].status[j] = HUD_WINGMAN_STATUS_NOT_HERE; + } + } + } +*/ +} + +// function which marks the other team wing as not used for the wingman status gauge +void hud_wingman_kill_multi_teams() +{ + int wing_index; + + // do nothing in single player or non team v. team games + if ( Game_mode & GM_NORMAL ) + return; + + if ( !IS_MISSION_MULTI_TEAMS ) + return; + + wing_index = -1; + if ( Net_player->p_info.team == 0 ) + wing_index = hud_wingman_status_wing_index(NOX("zeta")); + else if ( Net_player->p_info.team == 1 ) + wing_index = hud_wingman_status_wing_index(NOX("alpha")); + + if ( wing_index == -1 ) + return; + + HUD_wingman_status[wing_index].ignore = 1; +} + + +// called once per level to init the wingman status gauge. Loads in the frames the first time +void hud_init_wingman_status_gauge() +{ + int i, j; + + if ( !Wingman_status_gauge_loaded ) { + + for ( i = 0; i < HUD_WINGMAN_STATUS_NUM_FRAMES; i++ ) { + Wingman_status_frames[i].first_frame = bm_load_animation(Wingman_status_filenames[gr_screen.res][i], &Wingman_status_frames[i].num_frames); + if ( Wingman_status_frames[i].first_frame == -1 ) { + Warning(LOCATION, NOX("Error loading Wingman_status_filenames[gr_screen.res][i]'\n")); + return; + } + } + Wingman_status_gauge_loaded = 1; + } + + hud_wingman_status_init_flash(); + + HUD_wingman_update_timer=timestamp(0); // update status right away + + for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) { + HUD_wingman_status[i].ignore = 0; + HUD_wingman_status[i].used = 0; + for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) { + HUD_wingman_status[i].status[j] = HUD_WINGMAN_STATUS_NONE; + } + } + + hud_wingman_status_init_late_wings(); + hud_wingman_kill_multi_teams(); + hud_wingman_status_update(); +} + +// Update the status of the wingman status +void hud_wingman_status_update() +{ + if ( timestamp_elapsed(HUD_wingman_update_timer) ) { + int wing_index,wing_pos; + ship_obj *so; + object *ship_objp; + ship *shipp; + + HUD_wingman_update_timer=timestamp(HUD_WINGMAN_UPDATE_STATUS_INTERVAL); + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + ship_objp = &Objects[so->objnum]; + shipp = &Ships[ship_objp->instance]; + + wing_index = shipp->wing_status_wing_index; + wing_pos = shipp->wing_status_wing_pos; + + if ( (wing_index >= 0) && (wing_pos >= 0) ) { + + HUD_wingman_status[wing_index].used = 1; + if (!(shipp->flags & SF_DEPARTING) ) { + HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_ALIVE; + } + HUD_wingman_status[wing_index].hull[wing_pos] = hud_get_ship_hull_percent(ship_objp->instance); + if ( HUD_wingman_status[wing_index].hull[wing_pos] <= 0 ) { + HUD_wingman_status[wing_index].status[wing_pos] = HUD_WINGMAN_STATUS_DEAD; + } + } + } + } +} + +void hud_wingman_status_blit_left_frame(int num_wings_to_draw) +{ + int sx, sy, bitmap; + + // draw left side of frame + if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){ + Int3(); + return; + } + sx = HUD_wingman_left_coords[gr_screen.res][num_wings_to_draw - 1][0]; + sy = HUD_wingman_left_coords[gr_screen.res][num_wings_to_draw - 1][1]; + bitmap = Wingman_status_frames[BACKGROUND_LEFT].first_frame; + + if ( bitmap > -1 ) { + GR_AABITMAP(bitmap, sx, sy); + // gr_set_bitmap(bitmap); + // gr_aabitmap(sx, sy); + } + + // write "wingmen" on gauge + gr_string(sx+2, sy+2, XSTR( "wingmen", 352)); +} + +void hud_wingman_status_blit_middle_frame(int num_wings_to_draw) +{ + int sx, sy, bitmap; + int idx; + + bitmap = Wingman_status_frames[BACKGROUND_MIDDLE].first_frame; + if ( bitmap < 0 ) { + return; + } + + // don't draw for 1 or 2 wings + if((num_wings == 1) || (num_wings == 2)){ + return; + } + + // draw left side of frame + if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){ + Int3(); + return; + } + sx = -1; + sy = -1; + for(idx=num_wings_to_draw; idx>=3; idx--){ + sx = HUD_wingman_middle_coords[gr_screen.res][idx - 1][0]; + sy = HUD_wingman_middle_coords[gr_screen.res][idx - 1][1]; + GR_AABITMAP(bitmap, sx, sy); + } +} + +void hud_wingman_status_blit_right_frame(int num_wings_to_draw) +{ + int sx, sy, bitmap; + + // draw left side of frame + if((num_wings_to_draw < 1) || (num_wings_to_draw > 5)){ + Int3(); + return; + } + + sx = HUD_wingman_right_coords[gr_screen.res][num_wings_to_draw - 1][0]; + sy = HUD_wingman_right_coords[gr_screen.res][num_wings_to_draw - 1][1]; + bitmap = Wingman_status_frames[BACKGROUND_RIGHT].first_frame; + + if ( bitmap > -1 ) { + GR_AABITMAP(bitmap, sx, sy); + } +} + +void hud_wingman_status_blit_dots(int wing_index, int screen_index, int num_wings_to_draw) +{ + int i, sx, sy, is_bright, bitmap = -1, screen_pos; + + Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame; + + if ( Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame < 0 ) { + return; + } + + if ( Wingman_status_frames[WINGMAN_STATUS_NAMES].first_frame < 0 ) { + return; + } + + screen_pos = screen_index + (HUD_WINGMAN_MAX_WINGS - num_wings_to_draw); + + // draw wingman dots + for ( i = 0; i < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; i++ ) { + + if ( hud_wingman_status_maybe_flash(wing_index, i) ) { + is_bright=1; + } else { + is_bright=0; + } + + switch( HUD_wingman_status[wing_index].status[i] ) { + + case HUD_WINGMAN_STATUS_ALIVE: + bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame; + if ( HUD_wingman_status[wing_index].hull[i] > 0.5f ) { + // gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][is_bright]); + // use gauge color + hud_set_gauge_color(HUD_WINGMEN_STATUS, is_bright ? HUD_C_BRIGHT : HUD_C_NORMAL); + } else { + gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][is_bright]); + } + break; + + case HUD_WINGMAN_STATUS_DEAD: + gr_set_color_fast(&IFF_colors[IFF_COLOR_HOSTILE][0]); + bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1; + break; + + case HUD_WINGMAN_STATUS_NOT_HERE: + // gr_set_color_fast(&IFF_colors[IFF_COLOR_FRIENDLY][0]); + hud_set_gauge_color(HUD_WINGMEN_STATUS, is_bright ? HUD_C_BRIGHT : HUD_C_NORMAL); + bitmap = Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1; + break; + + default: + bitmap=-1; + break; + + } // end swtich + + if ( num_wings_to_draw == 1 ) { + sx = HUD_wingman_status_single_coords[gr_screen.res][i][0]; + sy = HUD_wingman_status_single_coords[gr_screen.res][i][1]; + } else { + sx = HUD_wingman_status_coords[gr_screen.res][screen_pos][i][0]; + sy = HUD_wingman_status_coords[gr_screen.res][screen_pos][i][1]; + } + + if ( bitmap > -1 ) { + GR_AABITMAP(bitmap, sx, sy); + } + } + + // draw wing name + bitmap = Wingman_status_frames[WINGMAN_STATUS_NAMES].first_frame + wing_index; + + if ( num_wings_to_draw == 1 ) { + sx = HUD_wingman_status_single_coords[gr_screen.res][0][0] - 8; + sy = HUD_wingman_status_single_coords[gr_screen.res][0][1] + 26; + } else { + sx = HUD_wingman_status_name_coords[gr_screen.res][screen_pos][0]; + sy = HUD_wingman_status_name_coords[gr_screen.res][screen_pos][1]; + } + + // hud_set_default_color(); + hud_set_gauge_color(HUD_WINGMEN_STATUS); + if ( bitmap > -1 ) { + GR_AABITMAP(bitmap, sx, sy); + // gr_set_bitmap(bitmap); + // gr_aabitmap(sx, sy); + } + +} + +int hud_wingman_status_wingmen_exist(int num_wings_to_draw) +{ + int i, j, count = 0; + + switch ( num_wings_to_draw ) { + case 0: + count = 0; + break; + case 1: + for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) { + if ( HUD_wingman_status[i].used > 0 ) { + for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) { + if ( HUD_wingman_status[i].status[j] != HUD_WINGMAN_STATUS_NONE ) { + count++; + } + } + } + } + break; + default: + count = 2; + break; + + } + + if ( count > 1 ) { + return 1; + } + + return 0; +} + +// Draw the wingman status gauge +void hud_wingman_status_render() +{ + int i, count, num_wings_to_draw = 0; + + for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) { + if ( (HUD_wingman_status[i].used > 0) && (HUD_wingman_status[i].ignore == 0) ) { + num_wings_to_draw++; + } + } + + if ( !hud_wingman_status_wingmen_exist(num_wings_to_draw) ) { + return; + } + + // hud_set_default_color(); + hud_set_gauge_color(HUD_WINGMEN_STATUS); + + // blit the background frames + hud_wingman_status_blit_left_frame(num_wings_to_draw); + hud_wingman_status_blit_middle_frame(num_wings_to_draw); + hud_wingman_status_blit_right_frame(num_wings_to_draw); + + count = 0; + for (i = 0; i < HUD_WINGMAN_MAX_WINGS; i++) { + if ( (HUD_wingman_status[i].used <= 0) || (HUD_wingman_status[i].ignore == 1) ) { + continue; + } + + hud_wingman_status_blit_dots(i, count, num_wings_to_draw); + count++; + } +} + +// init the flashing timers for the wingman status gauge +void hud_wingman_status_init_flash() +{ + int i, j; + + for ( i = 0; i < HUD_WINGMAN_MAX_WINGS; i++ ) { + for ( j = 0; j < HUD_WINGMAN_MAX_SHIPS_PER_WINGS; j++ ) { + HUD_wingman_flash_duration[i][j] = timestamp(0); + HUD_wingman_flash_next[i][j] = timestamp(0); + } + } + + HUD_wingman_flash_is_bright = 0; +} + +// start the targetbox item flashing for TBOX_FLASH_DURATION +void hud_wingman_status_start_flash(int wing_index, int wing_pos) +{ + HUD_wingman_flash_duration[wing_index][wing_pos] = timestamp(TBOX_FLASH_DURATION); +} + +// set the color for flashing dot +// exit: 1 => set bright color +// 0 => set default color +int hud_wingman_status_maybe_flash(int wing_index, int wing_pos) +{ + int index, draw_bright=0; + + index = wing_index*HUD_WINGMAN_MAX_SHIPS_PER_WINGS + wing_pos; + + if ( !timestamp_elapsed(HUD_wingman_flash_duration[wing_index][wing_pos]) ) { + if ( timestamp_elapsed(HUD_wingman_flash_next[wing_index][wing_pos]) ) { + HUD_wingman_flash_next[wing_index][wing_pos] = timestamp(TBOX_FLASH_INTERVAL); + HUD_wingman_flash_is_bright ^= (1<wave_count; i++) { + if ( wingp->ship_index[i] == shipnum ) { + wing_pos = i; + break; + } + } + + return wing_pos; +} + +void hud_wingman_status_set_index(int shipnum) +{ + int wing_index, wing_pos; + ship *shipp; + wing *wingp; + + if ( shipnum < 0 ) { + return; + } + + shipp = &Ships[shipnum]; + + if ( shipp->wingnum < 0 ) { + return; + } + + wingp = &Wings[shipp->wingnum]; + + // Check for Alpha, Beta, Gamma, Delta or Epsilon wings + wing_index = hud_wingman_status_wing_index(wingp->name); + if ( wing_index < 0 ) { + return; + } + + shipp->wing_status_wing_index = (char)wing_index; + + wing_pos = hud_wingman_status_wing_pos(shipnum, wing_index, wingp); + + shipp->wing_status_wing_pos = (char)wing_pos; +} + +void hudwingmanstatus_page_in() +{ + int i; + for ( i = 0; i < HUD_WINGMAN_STATUS_NUM_FRAMES; i++ ) { + bm_page_in_aabitmap( Wingman_status_frames[i].first_frame, Wingman_status_frames[i].num_frames ); + } +} + +int get_blip_bitmap() +{ + return Wingman_status_frames[WINGMAN_STATUS_DOTS].first_frame+1; +} + diff --git a/src/inetfile/cftp.cpp b/src/inetfile/cftp.cpp new file mode 100644 index 0000000..93e2ffa --- /dev/null +++ b/src/inetfile/cftp.cpp @@ -0,0 +1,559 @@ + /* + * $Logfile: /Freespace2/code/Inetfile/CFtp.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * FTP Client class (get only) + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 5/04/99 7:34p Dave + * Fixed slow HTTP get problem. + * + * 2 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 1 4/20/99 4:37p Dave + * + * Initial version + * + * $NoKeywords: $ + */ +#include +#include +#include + +#include "cftp.h" + +void FTPObjThread( void * obj ) +{ + ((CFtpGet *)obj)->WorkerThread(); +} + +void CFtpGet::AbortGet() +{ + m_Aborting = true; + while(!m_Aborted) ; //Wait for the thread to end + fclose(LOCALFILE); +} + +CFtpGet::CFtpGet(char *URL,char *localfile,char *Username,char *Password) +{ + SOCKADDR_IN listensockaddr; + m_State = FTP_STATE_STARTUP; + + m_ListenSock = INVALID_SOCKET; + m_DataSock = INVALID_SOCKET; + m_ControlSock = INVALID_SOCKET; + m_iBytesIn = 0; + m_iBytesTotal = 0; + m_Aborting = false; + m_Aborted = false; + + LOCALFILE = fopen(localfile,"wb"); + if(NULL == LOCALFILE) + { + m_State = FTP_STATE_CANT_WRITE_FILE; + return; + } + + if(Username) + { + strcpy(m_szUserName,Username); + } + else + { + strcpy(m_szUserName,"anonymous"); + } + if(Password) + { + strcpy(m_szPassword,Password); + } + else + { + strcpy(m_szPassword,"pxouser@pxo.net"); + } + m_ListenSock = socket(AF_INET, SOCK_STREAM, 0); + if(INVALID_SOCKET == m_ListenSock) + { + // vint iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return; + } + else + { + listensockaddr.sin_family = AF_INET; + listensockaddr.sin_port = 0; + listensockaddr.sin_addr.s_addr = INADDR_ANY; + + // Bind the listen socket + if (bind(m_ListenSock, (SOCKADDR *)&listensockaddr, sizeof(SOCKADDR))) + { + //Couldn't bind the socket + // int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return; + } + + // Listen for the server connection + if (listen(m_ListenSock, 1)) + { + //Couldn't listen on the socket + // int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return; + } + } + m_ControlSock = socket(AF_INET, SOCK_STREAM, 0); + if(INVALID_SOCKET == m_ControlSock) + { + m_State = FTP_STATE_SOCKET_ERROR; + return; + } + //Parse the URL + //Get rid of any extra ftp:// stuff + char *pURL = URL; + if(_strnicmp(URL,"ftp:",4)==0) + { + pURL +=4; + while(*pURL == '/') + { + pURL++; + } + } + //There shouldn't be any : in this string + if(strchr(pURL,':')) + { + m_State = FTP_STATE_URL_PARSING_ERROR; + return; + } + //read the filename by searching backwards for a / + //then keep reading until you find the first / + //when you found it, you have the host and dir + char *filestart = NULL; + char *dirstart = NULL; + for(int i = strlen(pURL);i>=0;i--) + { + if(pURL[i]== '/') + { + if(!filestart) + { + filestart = pURL+i+1; + dirstart = pURL+i+1; + strcpy(m_szFilename,filestart); + } + else + { + dirstart = pURL+i+1; + } + } + } + if((dirstart==NULL) || (filestart==NULL)) + { + m_State = FTP_STATE_URL_PARSING_ERROR; + return; + } + else + { + strncpy(m_szDir,dirstart,(filestart-dirstart)); + m_szDir[(filestart-dirstart)] = NULL; + strncpy(m_szHost,pURL,(dirstart-pURL)); + m_szHost[(dirstart-pURL)-1] = NULL; + } + //At this point we should have a nice host,dir and filename + + //if(NULL==CreateThread(NULL,0,ObjThread,this,0,&m_dwThreadId)) + if(NULL==_beginthread(FTPObjThread,0,this)) + { + m_State = FTP_STATE_INTERNAL_ERROR; + return; + } + m_State = FTP_STATE_CONNECTING; +} + + + +CFtpGet::~CFtpGet() +{ + if(m_ListenSock != INVALID_SOCKET) + { + shutdown(m_ListenSock,2); + closesocket(m_ListenSock); + } + if(m_DataSock != INVALID_SOCKET) + { + shutdown(m_DataSock,2); + closesocket(m_DataSock); + } + if(m_ControlSock != INVALID_SOCKET) + { + shutdown(m_ControlSock,2); + closesocket(m_ControlSock); + } + + +} + +//Returns a value to specify the status (ie. connecting/connected/transferring/done) +int CFtpGet::GetStatus() +{ + return m_State; +} + +unsigned int CFtpGet::GetBytesIn() +{ + return m_iBytesIn; +} + +unsigned int CFtpGet::GetTotalBytes() +{ + + return m_iBytesTotal; +} + +//This function does all the work -- connects on a blocking socket +//then sends the appropriate user and password commands +//and then the cwd command, the port command then get and finally the quit +void CFtpGet::WorkerThread() +{ + ConnectControlSocket(); + if(m_State != FTP_STATE_LOGGING_IN) + { + return; + } + LoginHost(); + if(m_State != FTP_STATE_LOGGED_IN) + { + return; + } + GetFile(); + + //We are all done now, and state has the current state. + m_Aborted = true; + + +} + +unsigned int CFtpGet::GetFile() +{ + //Start off by changing into the proper dir. + char szCommandString[200]; + int rcode; + + sprintf(szCommandString,"TYPE I\r\n"); + rcode = SendFTPCommand(szCommandString); + if(rcode >=400) + { + m_State = FTP_STATE_UNKNOWN_ERROR; + return 0; + } + if(m_Aborting) + return 0; + if(m_szDir[0]) + { + sprintf(szCommandString,"CWD %s\r\n",m_szDir); + rcode = SendFTPCommand(szCommandString); + if(rcode >=400) + { + m_State = FTP_STATE_DIRECTORY_INVALID; + return 0; + } + } + if(m_Aborting) + return 0; + if(!IssuePort()) + { + m_State = FTP_STATE_UNKNOWN_ERROR; + return 0; + } + if(m_Aborting) + return 0; + sprintf(szCommandString,"RETR %s\r\n",m_szFilename); + rcode = SendFTPCommand(szCommandString); + if(rcode >=400) + { + m_State = FTP_STATE_FILE_NOT_FOUND; + return 0; + } + if(m_Aborting) + return 0; + //Now we will try to determine the file size... + char *p,*s; + p = strchr(recv_buffer,'('); + p++; + if(p) + { + s = strchr(p,' '); + *s = NULL; + m_iBytesTotal = atoi(p); + } + if(m_Aborting) + return 0; + + m_DataSock = accept(m_ListenSock, NULL,NULL);//(SOCKADDR *)&sockaddr,&iAddrLength); + // Close the listen socket + closesocket(m_ListenSock); + if (m_DataSock == INVALID_SOCKET) + { + // int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return 0; + } + if(m_Aborting) + return 0; + ReadDataChannel(); + + m_State = FTP_STATE_FILE_RECEIVED; + return 1; +} + +unsigned int CFtpGet::IssuePort() +{ + + char szCommandString[200]; + SOCKADDR_IN listenaddr; // Socket address structure + int iLength; // Length of the address structure + UINT nLocalPort; // Local port for listening + UINT nReplyCode; // FTP server reply code + + + // Get the address for the hListenSocket + iLength = sizeof(listenaddr); + if (getsockname(m_ListenSock, (LPSOCKADDR)&listenaddr,&iLength) == SOCKET_ERROR) + { + // int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return 0; + } + + // Extract the local port from the hListenSocket + nLocalPort = listenaddr.sin_port; + + // Now, reuse the socket address structure to + // get the IP address from the control socket. + if (getsockname(m_ControlSock, (LPSOCKADDR)&listenaddr,&iLength) == SOCKET_ERROR) + { + // int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return 0; + } + + // Format the PORT command with the correct numbers. + sprintf(szCommandString, "PORT %d,%d,%d,%d,%d,%d\r\n", + listenaddr.sin_addr.S_un.S_un_b.s_b1, + listenaddr.sin_addr.S_un.S_un_b.s_b2, + listenaddr.sin_addr.S_un.S_un_b.s_b3, + listenaddr.sin_addr.S_un.S_un_b.s_b4, + nLocalPort & 0xFF, + nLocalPort >> 8); + + // Tell the server which port to use for data. + nReplyCode = SendFTPCommand(szCommandString); + if (nReplyCode != 200) + { + // int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_SOCKET_ERROR; + return 0; + } + return 1; +} + +int CFtpGet::ConnectControlSocket() +{ + HOSTENT *he; + SERVENT *se; + SOCKADDR_IN hostaddr; + he = gethostbyname(m_szHost); + if(he == NULL) + { + m_State = FTP_STATE_HOST_NOT_FOUND; + return 0; + } + //m_ControlSock + if(m_Aborting) + return 0; + se = getservbyname("ftp", NULL); + + if(se == NULL) + { + hostaddr.sin_port = htons(21); + } + else + { + hostaddr.sin_port = se->s_port; + } + hostaddr.sin_family = AF_INET; + memcpy(&hostaddr.sin_addr,he->h_addr_list[0],4); + if(m_Aborting) + return 0; + //Now we will connect to the host + if(connect(m_ControlSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR))) + { + // int iWinsockErr = WSAGetLastError(); + m_State = FTP_STATE_CANT_CONNECT; + return 0; + } + m_State = FTP_STATE_LOGGING_IN; + return 1; +} + + +int CFtpGet::LoginHost() +{ + char szLoginString[200]; + int rcode; + + sprintf(szLoginString,"USER %s\r\n",m_szUserName); + rcode = SendFTPCommand(szLoginString); + if(rcode >=400) + { + m_State = FTP_STATE_LOGIN_ERROR; + return 0; + } + sprintf(szLoginString,"PASS %s\r\n",m_szPassword); + rcode = SendFTPCommand(szLoginString); + if(rcode >=400) + { + m_State = FTP_STATE_LOGIN_ERROR; + return 0; + } + + m_State = FTP_STATE_LOGGED_IN; + return 1; +} + + +unsigned int CFtpGet::SendFTPCommand(char *command) +{ + + FlushControlChannel(); + // Send the FTP command + if (SOCKET_ERROR ==(send(m_ControlSock,command,strlen(command), 0))) + { + // int iWinsockErr = WSAGetLastError(); + // Return 999 to indicate an error has occurred + return(999); + } + + // Read the server's reply and return the reply code as an integer + return(ReadFTPServerReply()); +} + + + +unsigned int CFtpGet::ReadFTPServerReply() +{ + unsigned int rcode; + unsigned int iBytesRead; + char chunk[2]; + char szcode[5]; + unsigned int igotcrlf = 0; + memset(recv_buffer,0,1000); + do + { + chunk[0]=NULL; + iBytesRead = recv(m_ControlSock,chunk,1,0); + + if (iBytesRead == SOCKET_ERROR) + { + // int iWinsockErr = WSAGetLastError(); + // Return 999 to indicate an error has occurred + return(999); + } + + if((chunk[0]==0x0a) || (chunk[0]==0x0d)) + { + if(recv_buffer[0]!=NULL) + { + igotcrlf = 1; + } + } + else + { chunk[1] = NULL; + strcat(recv_buffer,chunk); + } + + Sleep(1); + }while(igotcrlf==0); + + if(recv_buffer[3] == '-') + { + //Hack -- must be a MOTD + return ReadFTPServerReply(); + } + if(recv_buffer[3] != ' ') + { + //We should have 3 numbers then a space + return 999; + } + memcpy(szcode,recv_buffer,3); + szcode[3] = NULL; + rcode = atoi(szcode); + // Extract the reply code from the server reply and return as an integer + return(rcode); +} + + +unsigned int CFtpGet::ReadDataChannel() +{ + char sDataBuffer[4096]; // Data-storage buffer for the data channel + int nBytesRecv; // Bytes received from the data channel + m_State = FTP_STATE_RECEIVING; + if(m_Aborting) + return 0; + do + { + if(m_Aborting) + return 0; + nBytesRecv = recv(m_DataSock, (LPSTR)&sDataBuffer,sizeof(sDataBuffer), 0); + + m_iBytesIn += nBytesRecv; + if (nBytesRecv > 0 ) + { + fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE); + //Write sDataBuffer, nBytesRecv + } + + Sleep(1); + }while (nBytesRecv > 0); + fclose(LOCALFILE); + // Close the file and check for error returns. + if (nBytesRecv == SOCKET_ERROR) + { + //Ok, we got a socket error -- xfer aborted? + m_State = FTP_STATE_RECV_FAILED; + return 0; + } + else + { + //done! + m_State = FTP_STATE_FILE_RECEIVED; + return 1; + } +} + + +void CFtpGet::FlushControlChannel() +{ + fd_set read_fds; + TIMEVAL timeout; + char flushbuff[3]; + + timeout.tv_sec=0; + timeout.tv_usec=0; + + FD_ZERO(&read_fds); + FD_SET(m_ControlSock,&read_fds); + + while(select(0,&read_fds,NULL,NULL,&timeout)) + { + recv(m_ControlSock,flushbuff,1,0); + + Sleep(1); + } +} \ No newline at end of file diff --git a/src/inetfile/chttpget.cpp b/src/inetfile/chttpget.cpp new file mode 100644 index 0000000..520f920 --- /dev/null +++ b/src/inetfile/chttpget.cpp @@ -0,0 +1,774 @@ +/* +* $Logfile: /Freespace2/code/Inetfile/Chttpget.cpp $ +* $Revision$ +* $Date$ +* $Author$ +* +* HTTP Client class (get only) +* +* $Log$ +* Revision 1.1 2002/05/03 03:28:09 root +* Initial revision +* + * + * 5 8/24/99 1:49a Dave + * Fixed client-side afterburner stuttering. Added checkbox for no version + * checking on PXO join. Made button info passing more friendly between + * client and server. + * + * 4 8/22/99 1:19p Dave + * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in + * which d3d cards are detected. + * + * 21 8/21/99 6:33p Kevin + * Fixed Proxy Stuff + * + * 20 8/21/99 6:48a Jeff + * Linux port + * + * 19 8/20/99 3:01p Kevin + * Added support for Proxies (I hope!) + * + * 18 8/15/99 6:38p Jeff + * fixed compile error + * + * 17 8/15/99 6:26p Kevin + * + * 16 4/14/99 1:20a Jeff + * fixed case mismatched #includes + * + * 15 3/03/99 12:28a Nate + * sped up something or other when the connection is done + * + * 14 2/03/99 4:20p Kevin + * Got multiplayer working with .mn3 files, and setup autodownloading + * + * 13 1/27/99 5:49p Kevin + * + * 12 1/27/99 5:38p Kevin + * + * 11 12/30/98 12:15p Kevin + * Auto Mission Download system + * + * 10 10/12/98 4:59p Kevin + * Added delay to thread when cancelled... + * + * 9 10/12/98 4:49p Nate + * More fixes + * + * 8 10/12/98 1:54p Nate + * Fixed bug + * + * 7 10/12/98 11:30a Kevin + * More memory stuff + * + * 6 10/08/98 12:59p Nate + * fixed cancel + * + * 5 10/08/98 9:57a Kevin + * made transfer cancellable + * + * 4 7/31/98 12:19p Nate + * Fixed http abort problem. + * + * 3 7/31/98 11:57a Kevin + * Added new functions for getting state + * + * 2 6/01/98 10:10a Kevin + * Added DLL connection interface and auto update DLL + * + * 1 5/27/98 9:52a Kevin + * + * 1 5/25/98 5:31p Kevin + * Initial version +* +* $NoKeywords: $ +*/ + +// #define WIN32 + +// #ifdef WIN32 +#include +#include +// #endif + +#include +#include +#include + +#include "inetgetfile.h" +#include "chttpget.h" + +#ifdef __LINUX__ + +inline void Sleep(int millis) +{ + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = millis*1000; + select(0,NULL,NULL,NULL,&tv); +} +#endif + +#define NW_AGHBN_CANCEL 1 +#define NW_AGHBN_LOOKUP 2 +#define NW_AGHBN_READ 3 + +#ifndef __LINUX__ +void __cdecl http_gethostbynameworker(void *parm); +#else +void *http_gethostbynameworker(void *parm); +#endif + +int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname); + +#ifndef __LINUX__ +void HTTPObjThread( void * obj ) +#else +void *HTTPObjThread( void * obj ) +#endif +{ + ((ChttpGet *)obj)->WorkerThread(); + ((ChttpGet *)obj)->m_Aborted = true; + //OutputDebugString("http transfer exiting....\n"); + + #ifdef __LINUX__ + return NULL; + #endif +} + +void ChttpGet::AbortGet() +{ +// #ifdef WIN32 + OutputDebugString("Aborting....\n"); +// #endif + m_Aborting = true; + while(!m_Aborted) Sleep(50); //Wait for the thread to end +// #ifdef WIN32 + OutputDebugString("Aborted....\n"); +// #endif +} + +ChttpGet::ChttpGet(char *URL,char *localfile,char *proxyip,unsigned short proxyport) +{ + m_ProxyEnabled = true; + m_ProxyIP = proxyip; + m_ProxyPort = proxyport; + GetFile(URL,localfile); +} + +ChttpGet::ChttpGet(char *URL,char *localfile) +{ + m_ProxyEnabled = false; + GetFile(URL,localfile); +} + + +void ChttpGet::GetFile(char *URL,char *localfile) +{ + m_DataSock = INVALID_SOCKET; + m_iBytesIn = 0; + m_iBytesTotal = 0; + m_State = HTTP_STATE_STARTUP;; + m_Aborting = false; + m_Aborted = false; + + strncpy(m_URL,URL,MAX_URL_LEN-1); + m_URL[MAX_URL_LEN-1] = 0; + + LOCALFILE = fopen(localfile,"wb"); + if(NULL == LOCALFILE) + { + m_State = HTTP_STATE_CANT_WRITE_FILE; + return; + } + m_DataSock = socket(AF_INET, SOCK_STREAM, 0); + if(INVALID_SOCKET == m_DataSock) + { + m_State = HTTP_STATE_SOCKET_ERROR; + return; + } + unsigned long arg; + + arg = true; +#ifndef __LINUX__ + ioctlsocket( m_DataSock, FIONBIO, &arg ); +#else + ioctl( m_DataSock, FIONBIO, &arg ); +#endif + + char *pURL = URL; + if(strnicmp(URL,"http:",5)==0) + { + pURL +=5; + while(*pURL == '/') + { + pURL++; + } + } + //There shouldn't be any : in this string + if(strchr(pURL,':')) + { + m_State = HTTP_STATE_URL_PARSING_ERROR; + return; + } + //read the filename by searching backwards for a / + //then keep reading until you find the first / + //when you found it, you have the host and dir + char *filestart = NULL; + char *dirstart = NULL; + for(int i = strlen(pURL);i>=0;i--) + { + if(pURL[i]== '/') + { + if(!filestart) + { + filestart = pURL+i+1; + dirstart = pURL+i+1; + strcpy(m_szFilename,filestart); + } + else + { + dirstart = pURL+i+1; + } + } + } + if((dirstart==NULL) || (filestart==NULL)) + { + m_State = HTTP_STATE_URL_PARSING_ERROR; + return; + } + else + { + strcpy(m_szDir,dirstart);//,(filestart-dirstart)); + //m_szDir[(filestart-dirstart)] = NULL; + strncpy(m_szHost,pURL,(dirstart-pURL)); + m_szHost[(dirstart-pURL)-1] = '\0'; + } +// #ifdef WIN32 + if(NULL==_beginthread(HTTPObjThread,0,this)) + { + m_State = HTTP_STATE_INTERNAL_ERROR; + return; + } + /* +#elif defined(__LINUX__) + pthread_t thread; + if(!inet_LoadThreadLib()) + { + m_State = HTTP_STATE_INTERNAL_ERROR; + return; + } + if(df_pthread_create(&thread,NULL,HTTPObjThread,this)!=0) + { + m_State = HTTP_STATE_INTERNAL_ERROR; + return; + } +#endif + */ +} + + +ChttpGet::~ChttpGet() +{ + if(m_DataSock != INVALID_SOCKET) + { + shutdown(m_DataSock,2); +#ifndef __LINUX__ + closesocket(m_DataSock); +#else + close(m_DataSock); +#endif + } +} + +int ChttpGet::GetStatus() +{ + return m_State; +} + +unsigned int ChttpGet::GetBytesIn() +{ + return m_iBytesIn; +} + +unsigned int ChttpGet::GetTotalBytes() +{ + return m_iBytesTotal; +} + + +void ChttpGet::WorkerThread() +{ + char szCommand[1000]; + char *p; + int irsp = 0; + ConnectSocket(); + if(m_Aborting) + { + fclose(LOCALFILE); + return; + } + if(m_State != HTTP_STATE_CONNECTED) + { + fclose(LOCALFILE); + return; + } + sprintf(szCommand,"GET %s%s HTTP/1.1\nAccept: */*\nAccept-Encoding: deflate\nHost: %s\n\n\n",m_ProxyEnabled?"":"/",m_ProxyEnabled?m_URL:m_szDir,m_szHost); + send(m_DataSock,szCommand,strlen(szCommand),0); + p = GetHTTPLine(); + if(strnicmp("HTTP/",p,5)==0) + { + char *pcode; + pcode = strchr(p,' ')+1; + if(!pcode) + { + m_State = HTTP_STATE_UNKNOWN_ERROR; + fclose(LOCALFILE); + return; + + } + pcode[3] = '\0'; + irsp = atoi(pcode); + + if(irsp == 0) + { + m_State = HTTP_STATE_UNKNOWN_ERROR; + fclose(LOCALFILE); + return; + } + if(irsp==200) + { + int idataready=0; + do + { + p = GetHTTPLine(); + if(p==NULL) + { + m_State = HTTP_STATE_UNKNOWN_ERROR; + fclose(LOCALFILE); + return; + } + if(*p=='\0') + { + idataready = 1; + break; + } + if(strnicmp(p,"Content-Length:",strlen("Content-Length:"))==0) + { + char *s = strchr(p,' ')+1; + p = s; + if(s) + { + while(*s) + { + if(!isdigit(*s)) + { + *s='\0'; + } + s++; + }; + m_iBytesTotal = atoi(p); + } + + } + + Sleep(1); + }while(!idataready); + ReadDataChannel(); + return; + } + m_State = HTTP_STATE_FILE_NOT_FOUND; + fclose(LOCALFILE); + return; + } + else + { + m_State = HTTP_STATE_UNKNOWN_ERROR; + fclose(LOCALFILE); + return; + } +} + +int ChttpGet::ConnectSocket() +{ + //HOSTENT *he; + unsigned int ip; + SERVENT *se; + SOCKADDR_IN hostaddr; + if(m_Aborting){ + return 0; + } + + ip = inet_addr((const char *)m_szHost); + + int rcode = 0; + if(ip==INADDR_NONE) + { + http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_szHost); + do + { + if(m_Aborting) + { + http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_szHost); + return 0; + } + rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_szHost); + + Sleep(1); + }while(rcode==0); + } + + if(rcode == -1) + { + m_State = HTTP_STATE_HOST_NOT_FOUND; + return 0; + } + //m_ControlSock + if(m_Aborting) + return 0; + se = getservbyname("http", NULL); + if(m_Aborting) + return 0; + if(se == NULL) + { + hostaddr.sin_port = htons(80); + } + else + { + hostaddr.sin_port = se->s_port; + } + hostaddr.sin_family = AF_INET; + //ip = htonl(ip); + memcpy(&hostaddr.sin_addr,&ip,4); + + if(m_ProxyEnabled) + { + //This is on a proxy, so we need to make sure to connect to the proxy machine + ip = inet_addr((const char *)m_ProxyIP); + + if(ip==INADDR_NONE) + { + http_Asyncgethostbyname(&ip,NW_AGHBN_LOOKUP,m_ProxyIP); + rcode = 0; + do + { + if(m_Aborting) + { + http_Asyncgethostbyname(&ip,NW_AGHBN_CANCEL,m_ProxyIP); + return 0; + } + rcode = http_Asyncgethostbyname(&ip,NW_AGHBN_READ,m_ProxyIP); + + Sleep(1); + }while(rcode==0); + + + if(rcode == -1) + { + m_State = HTTP_STATE_HOST_NOT_FOUND; + return 0; + } + + } + //Use either the proxy port or 80 if none specified + hostaddr.sin_port = htons((ushort)(m_ProxyPort ? m_ProxyPort : 80)); + //Copy the proxy address... + memcpy(&hostaddr.sin_addr,&ip,4); + + } + //Now we will connect to the host + fd_set wfds; + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + int serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR)); + int cerr = WSAGetLastError(); + if(serr) + { + while((cerr==WSAEALREADY)||(cerr==WSAEINVAL)||(cerr==WSAEWOULDBLOCK)) + { + FD_ZERO(&wfds); + FD_SET( m_DataSock, &wfds ); + if(select(0,NULL,&wfds,NULL,&timeout)) + { + serr = 0; + break; + } + if(m_Aborting) + return 0; + serr = connect(m_DataSock, (SOCKADDR *)&hostaddr, sizeof(SOCKADDR)); + if(serr == 0) + break; + cerr = WSAGetLastError(); + if(cerr==WSAEISCONN) + { + serr = 0; + break; + } + + Sleep(1); + }; + } + if(serr) + { + m_State = HTTP_STATE_CANT_CONNECT; + return 0; + } + m_State = HTTP_STATE_CONNECTED; + return 1; +} + +char *ChttpGet::GetHTTPLine() +{ + unsigned int iBytesRead; + char chunk[2]; + unsigned int igotcrlf = 0; + memset(recv_buffer,0,1000); + do + { + chunk[0]='\0'; + bool gotdata = false; + do + { + iBytesRead = recv(m_DataSock,chunk,1,0); + + if(SOCKET_ERROR == iBytesRead) + { + int error = WSAGetLastError(); + if(WSAEWOULDBLOCK==error) + { + gotdata = false; + continue; + } + else + return NULL; + } + else + { + gotdata = true; + } + + Sleep(1); + }while(!gotdata); + + if(chunk[0]==0x0d) + { + //This should always read a 0x0a + do + { + iBytesRead = recv(m_DataSock,chunk,1,0); + + if(SOCKET_ERROR == iBytesRead) + { + int error = WSAGetLastError(); + if(WSAEWOULDBLOCK==error) + { + gotdata = false; + continue; + } + else + return NULL; + } + else + { + gotdata = true; + } + + Sleep(1); + }while(!gotdata); + igotcrlf = 1; + } + else + { chunk[1] = '\0'; + strcat(recv_buffer,chunk); + } + + Sleep(1); + }while(igotcrlf==0); + return recv_buffer; +} + +unsigned int ChttpGet::ReadDataChannel() +{ + char sDataBuffer[4096]; // Data-storage buffer for the data channel + int nBytesRecv = 0; // Bytes received from the data channel + + fd_set wfds; + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 500; + + m_State = HTTP_STATE_RECEIVING; + do + { + FD_ZERO(&wfds); + FD_SET( m_DataSock, &wfds ); + + if((m_iBytesTotal)&&(m_iBytesIn==m_iBytesTotal)) + { + break; + } + select(0,&wfds,NULL,NULL,&timeout); + if(m_Aborting) + { + fclose(LOCALFILE); + return 0; + } + nBytesRecv = recv(m_DataSock, (char *)&sDataBuffer,sizeof(sDataBuffer), 0); + if(m_Aborting) + { + fclose(LOCALFILE); + return 0; + } + if(SOCKET_ERROR == nBytesRecv) + { + int error = WSAGetLastError(); + if(WSAEWOULDBLOCK==error) + { + nBytesRecv = 1; + continue; + } + } + m_iBytesIn += nBytesRecv; + if (nBytesRecv > 0 ) + { + fwrite(sDataBuffer,nBytesRecv,1,LOCALFILE); + //Write sDataBuffer, nBytesRecv + } + + Sleep(1); + }while (nBytesRecv > 0); + fclose(LOCALFILE); + // Close the file and check for error returns. + if (nBytesRecv == SOCKET_ERROR) + { + //Ok, we got a socket error -- xfer aborted? + m_State = HTTP_STATE_RECV_FAILED; + return 0; + } + else + { + //OutputDebugString("HTTP File complete!\n"); + //done! + m_State = HTTP_STATE_FILE_RECEIVED; + return 1; + } +} + + +typedef struct _async_dns_lookup +{ + unsigned int ip; //resolved host. Write only to worker thread. + char * host;//host name to resolve. read only to worker thread + bool done; //write only to the worker thread. Signals that the operation is complete + bool error; //write only to worker thread. Thread sets this if the name doesn't resolve + bool abort; //read only to worker thread. If this is set, don't fill in the struct. +}async_dns_lookup; + +async_dns_lookup httpaslu; +async_dns_lookup *http_lastaslu = NULL; + +#ifndef __LINUX__ +void __cdecl http_gethostbynameworker(void *parm); +#else +void *http_gethostbynameworker(void *parm); +#endif + +int http_Asyncgethostbyname(unsigned int *ip,int command, char *hostname) +{ + + if(command==NW_AGHBN_LOOKUP) + { + if(http_lastaslu) + http_lastaslu->abort = true; + + async_dns_lookup *newaslu; + newaslu = (async_dns_lookup *)malloc(sizeof(async_dns_lookup)); + memset(&newaslu->ip,0,sizeof(unsigned int)); + newaslu->host = hostname; + newaslu->done = false; + newaslu->error = false; + newaslu->abort = false; + http_lastaslu = newaslu; + httpaslu.done = false; + +// #ifdef WIN32 + _beginthread(http_gethostbynameworker,0,newaslu); + /* +#elif defined(__LINUX__) + pthread_t thread; + if(!inet_LoadThreadLib()) + { + return 0; + } + + df_pthread_create(&thread,NULL,http_gethostbynameworker,newaslu); +#endif + */ + return 1; + } + else if(command==NW_AGHBN_CANCEL) + { + if(http_lastaslu) + http_lastaslu->abort = true; + http_lastaslu = NULL; + } + else if(command==NW_AGHBN_READ) + { + if(!http_lastaslu) + return -1; + if(httpaslu.done) + { + //free(http_lastaslu); + http_lastaslu = NULL; + memcpy(ip,&httpaslu.ip,sizeof(unsigned int)); + return 1; + } + else if(httpaslu.error) + { + free(http_lastaslu); + http_lastaslu = NULL; + return -1; + } + else return 0; + } + return -2; + +} + +// This is the worker thread which does the lookup. +#ifndef __LINUX__ +void __cdecl http_gethostbynameworker(void *parm) +#else +void *http_gethostbynameworker(void *parm) +#endif +{ +#ifdef __LINUX__ + df_pthread_detach(df_pthread_self()); +#endif + async_dns_lookup *lookup = (async_dns_lookup *)parm; + HOSTENT *he = gethostbyname(lookup->host); + if(he==NULL) + { + lookup->error = true; + #ifdef __LINUX__ + return NULL; + #else + return; + #endif + } + else if(!lookup->abort) + { + memcpy(&lookup->ip,he->h_addr_list[0],sizeof(unsigned int)); + lookup->done = true; + memcpy(&httpaslu,lookup,sizeof(async_dns_lookup)); + } + free(lookup); + +#ifdef __LINUX__ + return NULL; +#endif +} diff --git a/src/inetfile/inetgetfile.cpp b/src/inetfile/inetgetfile.cpp new file mode 100644 index 0000000..96cfd47 --- /dev/null +++ b/src/inetfile/inetgetfile.cpp @@ -0,0 +1,281 @@ + /* + * $Logfile: /Freespace2/code/Inetfile/inetgetfile.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * InternetGetFile Class + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 8/22/99 1:19p Dave + * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in + * which d3d cards are detected. + * + * 3 6/16/99 10:27a Andsager + * Make directory if it does not already exist. + * + * 2 4/20/99 6:39p Dave + * Almost done with artillery targeting. Added support for downloading + * images on the PXO screen. + * + * 1 4/20/99 4:37p Dave + * + * Initial version + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include "cftp.h" +#include "chttpget.h" + +#include "inetgetfile.h" + +#define INET_STATE_CONNECTING 1 +#define INET_STATE_ERROR 2 +#define INET_STATE_RECEIVING 3 +#define INET_STATE_GOT_FILE 4 + +void InetGetFile::AbortGet() +{ + if(m_bUseHTTP) + { + http->AbortGet(); + } + else + { + ftp->AbortGet(); + } +} + +InetGetFile::InetGetFile(char *URL,char *localfile) +{ + m_HardError = 0; + http=NULL; + ftp=NULL; + if ((URL==NULL)||(localfile==NULL)) { + m_HardError = INET_ERROR_BADPARMS; + } + + // create directory if not already there. + char dir_name[256], *end; + + // make sure localfile has \ in it or we'll be here a long time. + if (strstr(localfile, "\\")) { + strcpy(dir_name, localfile); + int len = strlen(localfile); + end = dir_name + len; + + // start from end of localfile and go to first \ to get dirname + while ( *end != '\\' ) { + end--; + } + *end = '\0'; + + if ( _mkdir(dir_name)==0 ) { + mprintf(( "CFILE: Created new directory '%s'\n", dir_name )); + } + } + + if (strstr(URL,"http:")) { + m_bUseHTTP = TRUE; + + // using http proxy? + extern char Multi_options_proxy[512]; + extern ushort Multi_options_proxy_port; + if(strlen(Multi_options_proxy) > 0){ + http = new ChttpGet(URL, localfile, Multi_options_proxy, Multi_options_proxy_port); + } else { + http = new ChttpGet(URL, localfile); + } + + if (http==NULL) { + m_HardError = INET_ERROR_NO_MEMORY; + } + } else if (strstr(URL,"ftp:")) { + m_bUseHTTP = FALSE; + ftp = new CFtpGet(URL,localfile); + if (ftp==NULL) { + m_HardError = INET_ERROR_NO_MEMORY; + } + } else { + m_HardError = INET_ERROR_CANT_PARSE_URL; + } + Sleep(1000); +} + +InetGetFile::~InetGetFile() +{ + if(http!=NULL) delete http; + if(ftp!=NULL) delete ftp; +} + +BOOL InetGetFile::IsConnecting() +{ + int state; + if(m_bUseHTTP) + { + state = http->GetStatus(); + } + else + { + state = ftp->GetStatus(); + } + if(state == FTP_STATE_CONNECTING) + { + return TRUE; + } + else + { + return FALSE; + } + +} + +BOOL InetGetFile::IsReceiving() +{ + int state; + if(m_bUseHTTP) + { + state = http->GetStatus(); + } + else + { + state = ftp->GetStatus(); + } + if(state == FTP_STATE_RECEIVING) + { + return TRUE; + } + else + { + return FALSE; + } +} + +BOOL InetGetFile::IsFileReceived() +{ + int state; + if(m_bUseHTTP) + { + state = http->GetStatus(); + } + else + { + state = ftp->GetStatus(); + } + if(state == FTP_STATE_FILE_RECEIVED) + { + return TRUE; + } + else + { + return FALSE; + } +} + +BOOL InetGetFile::IsFileError() +{ + int state; + if(m_HardError) return TRUE; + if(m_bUseHTTP) + { + state = http->GetStatus(); + } + else + { + state = ftp->GetStatus(); + } + switch(state) + { + + case FTP_STATE_URL_PARSING_ERROR: + case FTP_STATE_HOST_NOT_FOUND: + case FTP_STATE_DIRECTORY_INVALID: + case FTP_STATE_FILE_NOT_FOUND: + case FTP_STATE_CANT_CONNECT: + case FTP_STATE_LOGIN_ERROR: + case FTP_STATE_INTERNAL_ERROR: + case FTP_STATE_SOCKET_ERROR: + case FTP_STATE_UNKNOWN_ERROR: + case FTP_STATE_RECV_FAILED: + case FTP_STATE_CANT_WRITE_FILE: + return TRUE; + case FTP_STATE_CONNECTING: + return FALSE; + default: + return FALSE; + } +} + +int InetGetFile::GetErrorCode() +{ + int state; + if(m_HardError) return m_HardError; + if(m_bUseHTTP) + { + state = http->GetStatus(); + } + else + { + state = ftp->GetStatus(); + } + switch(state) + { + + case FTP_STATE_URL_PARSING_ERROR: + return INET_ERROR_CANT_PARSE_URL; + + case FTP_STATE_HOST_NOT_FOUND: + return INET_ERROR_HOST_NOT_FOUND; + + + case FTP_STATE_DIRECTORY_INVALID: + case FTP_STATE_FILE_NOT_FOUND: + return INET_ERROR_BAD_FILE_OR_DIR; + + case FTP_STATE_CANT_CONNECT: + case FTP_STATE_LOGIN_ERROR: + case FTP_STATE_INTERNAL_ERROR: + case FTP_STATE_SOCKET_ERROR: + case FTP_STATE_UNKNOWN_ERROR: + case FTP_STATE_RECV_FAILED: + + return INET_ERROR_UNKNOWN_ERROR; + + case FTP_STATE_CANT_WRITE_FILE: + return INET_ERROR_CANT_WRITE_FILE; + default: + return INET_ERROR_NO_ERROR; + } +} + +int InetGetFile::GetTotalBytes() +{ + if(m_bUseHTTP) + { + return http->GetTotalBytes(); + } + else + { + return ftp->GetTotalBytes(); + } +} + +int InetGetFile::GetBytesIn() +{ + if(m_bUseHTTP) + { + return http->GetBytesIn(); + } + else + { + return ftp->GetBytesIn(); + } +} \ No newline at end of file diff --git a/src/io/joy.cpp b/src/io/joy.cpp new file mode 100644 index 0000000..2e0ef60 --- /dev/null +++ b/src/io/joy.cpp @@ -0,0 +1,1455 @@ +/* + * $Logfile: /Freespace2/code/Io/Joy.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to read the joystick + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 5 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 4 6/02/99 6:18p Dave + * Fixed TNT lockup problems! Wheeeee! + * + * 3 10/09/98 2:57p Dave + * Starting splitting up OS stuff. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 61 5/24/98 12:56a Mike + * Put in debug code, but comment out, to check joystick sensitivity. + * + * 60 5/19/98 6:54p Lawrance + * Set default joystick sensitivity to max + * + * 59 5/13/98 7:14p Hoffoss + * Made invalid axis return center position value. + * + * 58 5/13/98 1:17a Hoffoss + * Added joystick axes configurability. + * + * 57 5/07/98 3:15p Hoffoss + * Fixed typo. + * + * 56 5/07/98 12:41p Hoffoss + * Changed code to make joystick sensitivity default to center of range. + * + * 55 5/06/98 12:02a Hoffoss + * Fixed throttle problems with joysticks. + * + * 54 5/05/98 8:38p Hoffoss + * Added sensitivity adjustment to options menu and made it save to pilot + * file. + * + * 53 5/04/98 11:08p Hoffoss + * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp. + * Updated references everywhere to it. + * + * 52 5/03/98 6:02p Hoffoss + * Added DirectInput support for joystick (only works with v5.0, so only + * Win95). + * + * 51 5/01/98 5:59p Hoffoss + * Checking in code so I can switch back to NT to work on this instead + * (Win95, bah!) + * + * 50 4/30/98 12:12p Hoffoss + * Changed code to explicitly use calibrated values from Windows for + * joystick axis. + * + * 49 4/29/98 12:13a Lawrance + * Add function for reading down count without resetting internal count. + * Add hook to reset demo trailer timer. + * + * 48 4/25/98 12:02p Lawrance + * take out crit sec code around joystick read in joy_process + * + * 47 4/22/98 9:03p John + * Put critical section around all the joygetposex functions. Had to set + * up events to signal the polling thread to end at the end of the + * program. + * + * 46 4/17/98 3:07p Jim + * added debug code to test joystick + * + * 45 4/13/98 10:16a John + * Switched gettime back to timer_get_milliseconds, which is now thread + * safe. + * + * 44 4/12/98 11:08p Lawrance + * switch back to using gettime() in separate threads + * + * 43 4/12/98 5:31p Lawrance + * use timer_get_milliseconds() instead of gettime() + * + * 42 3/21/98 11:29a John + * Made joy_flush work when a button is held down + * + * 41 3/12/98 10:44a Hoffoss + * Whoops, should probably use the actual define rather than 0. + * + * 40 3/12/98 10:38a Hoffoss + * Changed joystick default to first slot in never set up in FSLaunch + * + * 39 3/11/98 5:27p Hoffoss + * Added to FreeSpace usage of joystick specified through FSLaunch. + * + * 38 3/09/98 4:44p Sandeep + * + * 37 3/07/98 5:23p Sandeep + * + * 35 3/06/98 11:12a Hoffoss + * Fixed joystick deadzone bug. + * + * 33 3/06/98 10:02a Hoffoss + * Made dead zone adjustable, and defaulted it to 10% instead of 5%. + * + * 36 3/07/98 4:50p John + * Added code to allow toggling force feedback on/off in setup + * + * 35 3/06/98 11:12a Hoffoss + * Fixed joystick deadzone bug. + * + * 33 3/06/98 10:02a Hoffoss + * Made dead zone adjustable, and defaulted it to 10% instead of 5%. + * + * 32 2/11/98 9:56p Jim + * allender: from sandeep on Jim's machine -- some force feedback stuff + * + * 31 1/29/98 11:04a Sandeep + * + * 30 1/27/98 8:40p Sandeep + * + * 29 1/19/98 6:15p John + * Fixed all my Optimized Build compiler warnings + * + * 28 1/08/98 6:35p Hoffoss + * Fixed joystick undefined detection. + * + * 27 1/08/98 3:48p Dan + * Fixed bug with joystick axis reading thinking it's undefined at + * extremes. + * + * 26 10/16/97 5:37p Lawrance + * change thread priority from THREAD_PRIORITY_TIME_CRITICAL to + * THREAD_PRIORITY_HIGHEST + * + * 25 10/09/97 10:15a Johnson + * try to init several times if joystick init fails + * + * 24 10/07/97 10:42a Johnson + * zero out JOYINFOEX struct before setting dwSize + * + * 23 10/06/97 5:54p Johnson + * ALAN: fix nasty bug where dwSize member of JOYINFOEX was not being set, + * resulting in random failure + * + * 22 9/15/97 11:42p Lawrance + * change button_info to joy_button_info to avoid name conflict + * + * 21 8/07/97 11:26p Lawrance + * add support for 4th axis (rudder) + * + * 20 7/29/97 5:30p Lawrance + * move gettime() from keyboard module to timer module + * + * 19 7/11/97 11:43a Lawrance + * fix bug with joy_up_count + * + * 18 7/10/97 12:29a Lawrance + * fix problem with NT not recognizing an axis that was set under 95 + * + * 17 7/09/97 11:41p Lawrance + * added throttle and hat support + * + * 16 6/19/97 9:50a John + * fixed bug where joy_close getting called out of order doesn't matter. + * + * 15 5/18/97 2:40p Lawrance + * added joy_get_caps() + * + * 14 4/22/97 10:56a John + * fixed some resource leaks. + * + * 13 2/27/97 2:23p Lawrance + * took out debug stmts + * + * 12 2/27/97 10:04a Lawrance + * fixed bug that was causing all joy buttons but 0 to be read incorrectly + * + * 11 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#include +#include + +#include "pstypes.h" +#include "joy.h" +#include "fix.h" +#include "key.h" +#include "timer.h" +#include "osregistry.h" +#include "joy_ff.h" +#include "vdinput.h" +#include "osapi.h" + +#define PRECALIBRATED 1 + +static int Joy_inited = 0; +int joy_num_sticks = 0; +int Dead_zone_size = 10; +int Cur_joystick = -1; // joystick used for input or -1 +int Joy_sensitivity = 9; + +CRITICAL_SECTION joy_lock; + +HANDLE joy_thread = NULL; +DWORD joy_thread_id; +int joy_pollrate = 1000 / 18; // poll at 18Hz + +HANDLE Joy_tell_thread_to_end_event = NULL; +HANDLE Joy_thread_says_its_done_event = NULL; + +static int Joy_last_x_reading = 0; +static int Joy_last_y_reading = 0; + +int Joy_di_inited = 0; +static LPDIRECTINPUT Di_joystick_obj = NULL; +static LPDIRECTINPUTDEVICE2 Di_joystick = NULL; + +typedef struct joy_button_info { + int actual_state; // Set if the button is physically down + int state; // Set when the button goes from up to down, cleared on down to up. Different than actual_state after a flush. + int down_count; + int up_count; + int down_time; + uint last_down_check; // timestamp in milliseconds of last +} joy_button_info; + +Joy_info joystick; + +joy_button_info joy_buttons[JOY_TOTAL_BUTTONS]; + +int joy_di_init(); +int joy_di_shutdown(); +int joystick_read_raw_axis_di(int num_axes, int *axis); + +// -------------------------------------------------------------- +// joy_flush() +// +// Clear the state of the joystick. +// +void joy_flush() +{ + int i; + joy_button_info *bi; + + if ( joy_num_sticks < 1 ) return; + + EnterCriticalSection(&joy_lock); + for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) { + bi = &joy_buttons[i]; + bi->state = 0; + bi->down_count = 0; + bi->up_count = 0; + bi->down_time = 0; + bi->last_down_check = timer_get_milliseconds(); + } + + LeaveCriticalSection(&joy_lock); +} + +// -------------------------------------------------------------- +// joy_process() +// +// Runs as a separate thread, and updates the state of the joystick +// +DWORD joy_process(DWORD lparam) +{ + MMRESULT rs; + JOYINFOEX ji; + int i,state; + joy_button_info *bi; + + for ( i = 0; i < JOY_TOTAL_BUTTONS; i++) { + bi = &joy_buttons[i]; + bi->actual_state = 0; // Don't set in flush code! + bi->state = 0; + bi->down_count = 0; + bi->up_count = 0; + bi->down_time = 0; + bi->last_down_check = timer_get_milliseconds(); + } + + while (1) { + // Wait for the thread to be signaled to end or 1/18th of a second to pass... + if ( WaitForSingleObject( Joy_tell_thread_to_end_event, joy_pollrate )==WAIT_OBJECT_0) { + break; + } + + memset(&ji, 0, sizeof(ji)); + ji.dwSize = sizeof(ji); +// ji.dwFlags = JOY_RETURNBUTTONS | JOY_RETURNRAWDATA; + ji.dwFlags = JOY_RETURNALL; + + EnterCriticalSection(&joy_lock); + + uint joy_state = 0; + if (Cur_joystick >= 0) { + rs = joyGetPosEx(Cur_joystick, &ji); + // If there's an error, assume all buttons down. + if (rs == JOYERR_NOERROR) { + joy_state = ji.dwButtons; + } + } + + // Process ji.dwButtons + for (i=0; i= 0) { + // AL: test code to try and find out why this call fails the first time + rs = 0; + for (count=0; count<20; count++) { + rs = joyGetPosEx(Cur_joystick, &ji); + if (rs == JOYERR_NOERROR) + break; + } + + if (rs == JOYERR_NOERROR) { + joy_num_sticks++; + + Joy_tell_thread_to_end_event = CreateEvent( NULL, FALSE, FALSE, NULL ); + Joy_thread_says_its_done_event = CreateEvent( NULL, FALSE, FALSE, NULL ); + + joy_thread = CreateThread(NULL, + 1024 * 32, + (LPTHREAD_START_ROUTINE) joy_process, + NULL, + 0, + &joy_thread_id); + + // SetThreadPriority(joy_thread, THREAD_PRIORITY_TIME_CRITICAL - 1); + SetThreadPriority(joy_thread, THREAD_PRIORITY_HIGHEST); + } + } + + mprintf(("Windoze reported %d joysticks, we found %d\n", n, joy_num_sticks)); + +#ifdef PRECALIBRATED + // Fake a calibration + if (joy_num_sticks > 0) { + for (i=0; i<4; i++) { + joystick.axis_min[i] = 0; + joystick.axis_center[i] = 32768; + joystick.axis_max[i] = 65536; + } + } +#else + // Fake a calibration + if (joy_num_sticks > 0) { + joy_set_cen(); + for (i=0; i<4; i++) { + joystick.axis_min[i] = 0; + joystick.axis_max[i] = joystick.axis_center[i]*2; + } + } +#endif + + joy_ff_init(); + // joy_di_init(); + + return joy_num_sticks; +} + +// -------------------------------------------------------------- +// joy_cheap_cal() +// +// Manual calibrate joystick routine +// +void joy_cheap_cal() +{ + if ( joy_num_sticks < 1 ) return; + + while(1) { + Sleep(50); + if ( key_inkey()) break; + if (joy_down_count(0)) break; + mprintf(( "Move stick to upper-left and hit button\n" )); + } + joy_set_ul(); + + while(1) { + Sleep(50); + if ( key_inkey()) break; + if (joy_down_count(0)) break; + mprintf(( "Move stick to lower-right and hit button\n" )); + } + joy_set_lr(); + + while(1) { + Sleep(50); + if ( key_inkey()) break; + if (joy_down_count(0)) break; + mprintf(( "Move stick to center and hit button\n" )); + } + joy_set_cen(); +} + +// -------------------------------------------------------------- +// joy_get_pos_old() +// +// Get the position of the joystick axes +// +int joy_get_pos_old(int * x, int * y ) +{ + MMRESULT rs; + JOYINFOEX ji; + + if ( joy_num_sticks < 1 ) { + if (x) *x = 0; + if (y) *y = 0; + return 0; + } + + memset(&ji, 0, sizeof(ji)); + ji.dwSize = sizeof(ji); + +#ifdef PRECALIBRATED +//JOY_USEDEADZONE| +//JOY_RETURNCENTERED| +//JOY_RETURNX|JOY_RETURNY|JOY_RETURNRAWDATA; + ji.dwFlags = JOY_RETURNALL; +#else + ji.dwFlags = JOY_CAL_READXYONLY; +#endif + + if (Cur_joystick >= 0) { + EnterCriticalSection(&joy_lock); + rs = joyGetPosEx(Cur_joystick, &ji); + LeaveCriticalSection(&joy_lock); + + if (rs == JOYERR_NOERROR) { +#if 1 + if (x) + *x = ji.dwXpos; + if (y) + *y = ji.dwYpos; +#else + if (x) { + *x = (ji.dwXpos - 32768) * 2; + if (*x < -65536) + *x = -65536; + else if (*x > 65536) + *x = 65536; + } + + if (y) { + *y = (ji.dwYpos - 32768) * 2; + if (*y < -65536) + *y = -65536; + else if (*y > 65536) + *y = 65536; + } +#endif + return 1; + } + } + + if (x) + *x = 0; + if (y) + *y = 0; + + return 0; +} + + +// -------------------------------------------------------------- +// joy_down_count() +// +// Return the number of times the joystick button has gone down since +// joy_down_count() was last called +// +// input: btn => button number to check +// reset_count => (default 1): if true reset down_count +// +// returns: number of times button 'btn' has gone down since last call +// +int joy_down_count(int btn, int reset_count) +{ + int tmp; + + if ( joy_num_sticks < 1 ) return 0; + if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0; + + EnterCriticalSection(&joy_lock); + tmp = joy_buttons[btn].down_count; + if ( reset_count ) { + joy_buttons[btn].down_count = 0; + } + LeaveCriticalSection(&joy_lock); + + return tmp; +} + + +// -------------------------------------------------------------- +// joy_down() +// +// Return the state of button number 'btn' +// +// input: btn => button number to check +// +// returns: 0 => not pressed +// 1 => pressed +// +int joy_down(int btn) +{ + int tmp; + + if ( joy_num_sticks < 1 ) return 0; + if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS )) return 0; + + EnterCriticalSection(&joy_lock); + tmp = joy_buttons[btn].state; + LeaveCriticalSection(&joy_lock); + + return tmp; +} + +// -------------------------------------------------------------- +// joy_up_count() +// +// Return the number of times the joystick button has gone up since +// joy_up_count() was last called +// +// input: btn => button number to check +// +// returns: number of times button 'btn' has gone up since last call +// +int joy_up_count(int btn) +{ + int tmp; + + if ( joy_num_sticks < 1 ) return 0; + if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0; + + EnterCriticalSection(&joy_lock); + tmp = joy_buttons[btn].up_count; + joy_buttons[btn].up_count = 0; + LeaveCriticalSection(&joy_lock); + + return tmp; +} + +// -------------------------------------------------------------- +// joy_down_time() +// +// Return a number between 0 and 1. This number represents the percentage +// time that the joystick button has been down since it was last checked +// +// input: btn => button number to check +// returns: value between 0 and 1 +// +float joy_down_time(int btn) +{ + float rval; + unsigned int now; + joy_button_info *bi; + + if ( joy_num_sticks < 1 ) return 0.0f; + if ( (btn < 0) || (btn >= JOY_TOTAL_BUTTONS)) return 0.0f; + bi = &joy_buttons[btn]; + EnterCriticalSection(&joy_lock); + + now = timer_get_milliseconds(); + + if ( bi->down_time == 0 && joy_down(btn) ) { + bi->down_time += joy_pollrate; + } + + if ( (now - bi->last_down_check) > 0) + rval = i2fl(bi->down_time) / (now - bi->last_down_check); + else + rval = 0.0f; + + bi->down_time = 0; + bi->last_down_check = now; + + LeaveCriticalSection(&joy_lock); + + if (rval < 0) + rval = 0.0f; + if (rval > 1) + rval = 1.0f; + + return rval; +} + +// -------------------------------------------------------------- +// joy_get_cal_vals() +// +// Get the calibrated min, center, and max for all axes +// +// input: axis_min => OUTPUT PARAMETER: array of at least 4 ints to hold min axis values +// axis_center => OUTPUT PARAMETER: array of at least 4 ints to hold center axis values +// axis_min => OUTPUT PARAMETER: array of at least 4 ints to hold max axis values +// +void joy_get_cal_vals(int *axis_min, int *axis_center, int *axis_max) +{ + int i; + + for ( i = 0; i < 4; i++) { + axis_min[i] = joystick.axis_min[i]; + axis_center[i] = joystick.axis_center[i]; + axis_max[i] = joystick.axis_max[i]; + } +} + +// -------------------------------------------------------------- +// joy_set_cal_vals() +// +// Get the calibrated min, center, and max for all axes +// +// input: axis_min => array of at 4 ints that hold min axis values +// axis_center => array of at 4 ints that hold center axis values +// axis_min => array of at 4 ints that hold max axis values +// +void joy_set_cal_vals(int *axis_min, int *axis_center, int *axis_max) +{ + int i; + + for (i=0; i<4; i++) { + joystick.axis_min[i] = axis_min[i]; + joystick.axis_center[i] = axis_center[i]; + joystick.axis_max[i] = axis_max[i]; + } +} + +// -------------------------------------------------------------- +// joystick_read_raw_axis() +// +// Read the raw axis information for a specified number of axes. +// +// input: num_axes => number of axes to read. Note the axes go in the following order: +// dwXpos +// dwYpos +// dwZpos (throttle) +// dwRpos (rudder) +// dwUpos (5th axis) +// dwVpos (6th axis) +// +// axis => an array of at least 4 ints to hold axis data +// +int joystick_read_raw_axis(int num_axes, int *axis) +{ + MMRESULT rs; + JOYINFOEX ji; + int i; + + Assert(num_axes <= JOY_NUM_AXES); + for (i=0; i= 0) { + EnterCriticalSection(&joy_lock); + rs = joyGetPosEx(Cur_joystick, &ji); + LeaveCriticalSection(&joy_lock); + + } else + return 0; + + if (rs != JOYERR_NOERROR) + return 0; + + switch (num_axes) { + case 6: + if (joystick.axis_valid[5]) + axis[5] = ji.dwVpos; + + case 5: + if (joystick.axis_valid[4]) + axis[4] = ji.dwUpos; + + case 4: + if (joystick.axis_valid[3]) + axis[3] = ji.dwRpos; + + case 3: + if (joystick.axis_valid[2]) + axis[2] = ji.dwZpos; + + case 2: + if (joystick.axis_valid[1]) + axis[1] = ji.dwYpos; + + case 1: + if (joystick.axis_valid[0]) + axis[0] = ji.dwXpos; + + break; + + default: + Int3(); + break; + } + + return 1; +} + +// -------------------------------------------------------------- +// joy_set_ul() +// +// Get the minimum axis information (namely, joystick in upper left). +// This is called by a manual calibration routine. +// +// NOTE: sets the values in joystick.axis_min[] +// +void joy_set_ul() +{ + joystick_read_raw_axis( 2, joystick.axis_min ); +} + +// -------------------------------------------------------------- +// joy_set_lr() +// +// Get the maximum axis information (namely, joystick in lower right). +// This is called by a manual calibration routine. +// +// NOTE: sets the values in joystick.axis_max[] +// +void joy_set_lr() +{ + joystick_read_raw_axis( 2, joystick.axis_max ); +} + +// -------------------------------------------------------------- +// joy_set_cen() +// +// Get the center axis information (namely, joystick in dead zone). +// This is called by a manual calibration routine. +// +// NOTE: sets the values in joystick.axis_center[] +// +void joy_set_cen() +{ + joystick_read_raw_axis( 2, joystick.axis_center ); +} + +int joy_get_unscaled_reading(int raw, int axn) +{ + int rng; + + // Make sure it's calibrated properly. + if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5) + return 0; + + if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5) + return 0; + + rng = joystick.axis_max[axn] - joystick.axis_min[axn]; + raw -= joystick.axis_min[axn]; // adjust for linear range starting at 0 + + // cap at limits + if (raw < 0) + raw = 0; + if (raw > rng) + raw = rng; + + return (int) ((unsigned int) raw * (unsigned int) F1_0 / (unsigned int) rng); // convert to 0 - F1_0 range. +} + +// -------------------------------------------------------------- +// joy_get_scaled_reading() +// +// input: raw => the raw value for an axis position +// axn => axis number, numbered starting at 0 +// +// return: joy_get_scaled_reading will return a value that represents +// the joystick pos from -1 to +1 for the specified axis number 'axn', and +// the raw value 'raw' +// +int joy_get_scaled_reading(int raw, int axn) +{ + int x, d, dead_zone, rng; + float percent, sensitivity_percent, non_sensitivity_percent; + + // Make sure it's calibrated properly. + if (joystick.axis_center[axn] - joystick.axis_min[axn] < 5) + return 0; + + if (joystick.axis_max[axn] - joystick.axis_center[axn] < 5) + return 0; + + raw -= joystick.axis_center[axn]; + + dead_zone = (joystick.axis_max[axn] - joystick.axis_min[axn]) * Dead_zone_size / 100; + + if (raw < -dead_zone) { + rng = joystick.axis_center[axn] - joystick.axis_min[axn] - dead_zone; + d = -raw - dead_zone; + + } else if (raw > dead_zone) { + rng = joystick.axis_max[axn] - joystick.axis_center[axn] - dead_zone; + d = raw - dead_zone; + + } else + return 0; + + if (d > rng) + d = rng; + + Assert(Joy_sensitivity >= 0 && Joy_sensitivity <= 9); + + // compute percentages as a range between 0 and 1 + sensitivity_percent = (float) Joy_sensitivity / 9.0f; + non_sensitivity_percent = (float) (9 - Joy_sensitivity) / 9.0f; + + // find percent of max axis is at + percent = (float) d / (float) rng; + + // work sensitivity on axis value + percent = (percent * sensitivity_percent + percent * percent * percent * percent * percent * non_sensitivity_percent); + + x = (int) ((float) F1_0 * percent); + + //nprintf(("AI", "d=%6i, sens=%3i, percent=%6.3f, val=%6i, ratio=%6.3f\n", d, Joy_sensitivity, percent, (raw<0) ? -x : x, (float) d/x)); + + if (raw < 0) + return -x; + + return x; +} + +// -------------------------------------------------------------- +// joy_get_pos() +// +// input: x => OUTPUT PARAMETER: x-axis position of stick (-1 to 1) +// y => OUTPUT PARAMETER: y-axis position of stick (-1 to 1) +// z => OUTPUT PARAMETER: z-axis (throttle) position of stick (-1 to 1) +// r => OUTPUT PARAMETER: rudder position of stick (-1 to 1) +// +// return: success => 1 +// failure => 0 +// +int joy_get_pos(int *x, int *y, int *z, int *rx) +{ + int axis[JOY_NUM_AXES]; + + if (x) *x = 0; + if (y) *y = 0; + if (z) *z = 0; + if (rx) *rx = 0; + + if (joy_num_sticks < 1) return 0; + + joystick_read_raw_axis( 6, axis ); + + // joy_get_scaled_reading will return a value represents the joystick pos from -1 to +1 + if (x && joystick.axis_valid[0]) + *x = joy_get_scaled_reading(axis[0], 0); + if (y && joystick.axis_valid[1]) + *y = joy_get_scaled_reading(axis[1], 1); + if (z && joystick.axis_valid[2]) + *z = joy_get_unscaled_reading(axis[2], 2); + if (rx && joystick.axis_valid[3]) + *rx = joy_get_scaled_reading(axis[3], 3); + + if (x) + Joy_last_x_reading = *x; + + if (y) + Joy_last_x_reading = *y; + + return 1; +} + +// change in joy position since last call +void joy_get_delta(int *dx, int *dy) +{ + static int old_joy_x = 0; + static int old_joy_y = 0; + + if ( !Joy_inited ) { + *dx = *dy = 0; + return; + } + + *dx = Joy_last_x_reading - old_joy_x; + *dy = Joy_last_y_reading - old_joy_y; + + old_joy_x = Joy_last_x_reading; + old_joy_y = Joy_last_y_reading; +} + +//// This is the DirectInput joystick stuff + +GUID Di_joy_guid; +int Di_joy_guid_valid = 0; + +BOOL CALLBACK joy_di_enum(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) +{ + char buf[64]; + + nprintf(("Joystick", "Joystick detected: %s (%s)\n", lpddi->tszInstanceName, lpddi->tszProductName)); + sprintf(buf, "Joystick %d", Cur_joystick + 1); + if (!stricmp(buf, lpddi->tszInstanceName)) { + Di_joy_guid = lpddi->guidInstance; + Di_joy_guid_valid = 1; + nprintf(("Joystick", " (Selected joystick)\n")); + } + + return DIENUM_CONTINUE; +} +/* +BOOL FAR PASCAL InitJoystickInput(LPCDIDEVICEINSTANCE pdinst, LPVOID pvRef) +{ + LPDIRECTINPUT pdi = pvRef; + LPDIRECTINPUTDEVICE pdev; + DIPROPRANGE diprg; + + // create the DirectInput joystick device + if(pdi->lpVtbl->CreateDevice(pdi, &pdinst->guidInstance, &pdev, NULL) != DI_OK) + { + OutputDebugString("IDirectInput::CreateDevice FAILED\n"); + return DIENUM_CONTINUE; + } + + // set joystick data format + if (pdev->lpVtbl->SetDataFormat(pdev, &c_dfDIJoystick) != DI_OK) + { + OutputDebugString("IDirectInputDevice::SetDataFormat FAILED\n"); + pdev->lpVtbl->Release(pdev); + return DIENUM_CONTINUE; + } + + // set the cooperative level + if (pdev->lpVtbl->SetCooperativeLevel(pdev, hWndMain, + DISCL_NONEXCLUSIVE | DISCL_FOREGROUND) != DI_OK) + { + OutputDebugString("IDirectInputDevice::SetCooperativeLevel FAILED\n"); + pdev->lpVtbl->Release(pdev); + return DIENUM_CONTINUE; + } + + // set X-axis range to (-1000 ... +1000) + // This lets us test against 0 to see which way the stick is pointed. + + diprg.diph.dwSize = sizeof(diprg); + diprg.diph.dwHeaderSize = sizeof(diprg.diph); + diprg.diph.dwObj = DIJOFS_X; + diprg.diph.dwHow = DIPH_BYOFFSET; + diprg.lMin = -1000; + diprg.lMax = +1000; + + if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK) + { + OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n"); + pdev->lpVtbl->Release(pdev); + return FALSE; + } + + // + // And again for Y-axis range + // + diprg.diph.dwObj = DIJOFS_Y; + + if (pdev->lpVtbl->SetProperty(pdev, DIPROP_RANGE, &diprg.diph) != DI_OK) + { + OutputDebugString("IDirectInputDevice::SetProperty(DIPH_RANGE) FAILED\n"); + pdev->lpVtbl->Release(pdev); + return FALSE; + } + + // set X axis dead zone to 50% (to avoid accidental turning) + // Units are ten thousandths, so 50% = 5000/10000. + if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_X, DIPH_BYOFFSET, 5000) != DI_OK) + { + OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n"); + pdev->lpVtbl->Release(pdev); + return FALSE; + } + + + // set Y axis dead zone to 50% (to avoid accidental thrust) + // Units are ten thousandths, so 50% = 5000/10000. + if (SetDIDwordProperty(pdev, DIPROP_DEADZONE, DIJOFS_Y, DIPH_BYOFFSET, 5000) != DI_OK) + { + OutputDebugString("IDirectInputDevice::SetProperty(DIPH_DEADZONE) FAILED\n"); + pdev->lpVtbl->Release(pdev); + return FALSE; + } + + + // Add it to our list of devices. If AddInputDevice succeeds, + // he will do an AddRef. + AddInputDevice(pdev, pdinst); + hRes = pdev->lpVtbl->QueryInterface( + pdev, &IID_IDirectInputDevice2, + (LPVOID *)&g_rgpdevFound[g_cpdevFound]); + + pdev->lpVtbl->Release(pdev); + + return DIENUM_CONTINUE; +} +*/ +int joy_di_init() +{ + HRESULT hr; + LPDIRECTINPUTDEVICE pdev; + + Joy_di_inited = 0; + hr = DirectInputCreate(GetModuleHandle(NULL), 0x500, &Di_joystick_obj, NULL); + if (FAILED(hr)) { + mprintf(( "DirectInputCreate() failed!\n" )); + return -1; + } + + Di_joy_guid_valid = 0; + hr = Di_joystick_obj->EnumDevices(DIDEVTYPE_JOYSTICK, joy_di_enum, Di_joystick_obj, DIEDFL_ATTACHEDONLY); + if (FAILED(hr)) { + mprintf(( "EnumDevice() failed!\n" )); + return -1; + } + + if (!Di_joy_guid_valid) { + mprintf(( "Correct joystick not found.\n" )); + return -1; + } + + hr = Di_joystick_obj->CreateDevice(Di_joy_guid, &pdev, NULL); + if (FAILED(hr)) { + mprintf(( "CreateDevice() failed!\n" )); + return -1; + } + + hr = pdev->SetDataFormat(&c_dfDIJoystick); + if (FAILED(hr)) { + mprintf(( "SetDataFormat() failed!\n" )); + if (hr == DIERR_ACQUIRED) + mprintf(( " (reason: DIERR_ACQUIRED)\n" )); + + if (hr == DIERR_INVALIDPARAM) + mprintf(( " (reason: DIERR_INVALIDPARAM)\n" )); + + if (hr == DIERR_NOTINITIALIZED) + mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" )); + + pdev->Release(); + return -1; + } + + hr = pdev->SetCooperativeLevel((HWND) os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); + if (FAILED(hr)) { + mprintf(( "SetCooperativeLevel() failed!\n" )); + if (hr == DIERR_ACQUIRED) + mprintf(( " (reason: DIERR_ACQUIRED)\n" )); + + if (hr == DIERR_INVALIDPARAM) + mprintf(( " (reason: DIERR_INVALIDPARAM)\n" )); + + if (hr == DIERR_NOTINITIALIZED) + mprintf(( " (reason: DIERR_NOTINITIALIZED)\n" )); + + pdev->Release(); + return -1; + } + + hr = pdev->QueryInterface(IID_IDirectInputDevice2, (LPVOID *) &Di_joystick); + if (FAILED(hr)) { + pdev->Release(); + return -1; + } + + Di_joystick->Acquire(); + + pdev->Release(); + Joy_di_inited = 1; + nprintf(("Joystick", "DirectInput initialization of joystick succeeded\n")); + return 0; +} + +int joy_di_shutdown() +{ + // Destroy any lingering IDirectInputDevice object. + if (Di_joystick) { + // Unacquire the device one last time just in case we got really confused + // and tried to exit while the device is still acquired. + Di_joystick->Unacquire(); + + Di_joystick->Release(); + Di_joystick = NULL; + } + + // Destroy any lingering IDirectInput object. + if (Di_joystick_obj) { + Di_joystick_obj->Release(); + Di_joystick_obj = NULL; + } + + Joy_di_inited = 0; + return 0; +} + +int joystick_read_raw_axis_di(int num_axes, int *axis) +{ + int repeat = 1; + HRESULT hr = 0; + DIJOYSTATE joy_state; + + if (!Joy_di_inited) + return 0; + + repeat = 1; + while (repeat) { + repeat = 0; + + hr = Di_joystick->Poll(); + if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) { + // DirectInput is telling us that the input stream has + // been interrupted. We aren't tracking any state + // between polls, so we don't have any special reset + // that needs to be done. We just re-acquire and + // try again. + Sleep(1000); // Pause a second... + hr = Di_joystick->Acquire(); + if (SUCCEEDED(hr)) + repeat = 1; + } + } + + repeat = 1; + memset(&joy_state, 0, sizeof(joy_state)); + while (repeat) { + repeat = 0; + + hr = Di_joystick->GetDeviceState(sizeof(joy_state), &joy_state); + if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) { + // DirectInput is telling us that the input stream has + // been interrupted. We aren't tracking any state + // between polls, so we don't have any special reset + // that needs to be done. We just re-acquire and + // try again. + Sleep(1000); // Pause a second... + hr = Di_joystick->Acquire(); + if (SUCCEEDED(hr)) + repeat = 1; + } + } + + if (SUCCEEDED(hr)) { + switch (num_axes) { + case 6: + if (joystick.axis_valid[5]) + axis[5] = joy_state.lRy; + + case 5: + if (joystick.axis_valid[4]) + axis[4] = joy_state.lRx; + + case 4: + if (joystick.axis_valid[3]) + axis[3] = joy_state.lRz; + + case 3: + if (joystick.axis_valid[2]) + axis[2] = joy_state.lZ; + + case 2: + if (joystick.axis_valid[1]) + axis[1] = joy_state.lY; + + case 1: + if (joystick.axis_valid[0]) + axis[0] = joy_state.lX; + + break; + } + } + + return 1; +} diff --git a/src/io/joy_ff.cpp b/src/io/joy_ff.cpp new file mode 100644 index 0000000..f7b84bd --- /dev/null +++ b/src/io/joy_ff.cpp @@ -0,0 +1,809 @@ +/* + * $Logfile: /Freespace2/code/Io/Joy_ff.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code for joystick Force Feedback. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 3 10/09/98 2:57p Dave + * Starting splitting up OS stuff. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 13 5/20/98 5:47p Sandeep + * + * 12 5/20/98 3:52p Allender + * fixed compiler warnings + * + * 11 5/20/98 11:06a Hoffoss + * Fixed typo. + * + * 10 5/20/98 10:57a Hoffoss + * Made directional hit effect FF a toggle in launcher, and made calibrate + * not freeze launcher until process ends. + * + * 9 5/18/98 4:53p Hoffoss + * Some force feedback tweaks and pilot initializations there should have + * been happening, but weren't, and not are! + * + * 8 5/17/98 6:28p Hoffoss + * Added some stuff. + * + * 7 5/17/98 5:45p Hoffoss + * Adjusted ship handling Force Feedback to not be as strong. Poor + * Sandeep's wrist can't handle it. :) + * + * 6 5/08/98 5:31p Hoffoss + * Isolated the joystick force feedback code more from dependence on other + * libraries. + * + * 5 5/08/98 12:27p Hoffoss + * Polished up the error handling and such a bit. + * + * 4 5/08/98 9:54a Hoffoss + * Generalized some of the effect stuff to try some swapping of effects. + * (going to start next, but checking this in in case I want to revert + * back to this state). + * + * 3 5/07/98 12:24a Hoffoss + * Finished up sidewinder force feedback support. + * + * 2 5/04/98 11:08p Hoffoss + * Expanded on Force Feedback code, and moved it all into Joy_ff.cpp. + * Updated references everywhere to it. + * + * $NoKeywords: $ + */ + +#include "vecmat.h" +#include "sw_force.h" +#include "osregistry.h" +#include "joy_ff.h" +#include "osapi.h" + +typedef struct { + DIEFFECT effect; + DIPERIODIC periodic_struct; + DIENVELOPE envelope_struct; + DWORD axes[2]; + LONG direction[2]; + GUID guid; +} di_periodic_effect_struct; + +int joy_ff_handling_scaler; +int Joy_ff_enabled = 0; +int Joy_ff_directional_hit_effect_enabled = 1; + +LPDIRECTINPUT pDi; +LPDIRECTINPUTDEVICE2 pDiDevice; + +int joy_ff_create_effects(); +void joy_ff_stop_effects(); + +LPDIRECTINPUTEFFECT pHitEffect1; +LPDIRECTINPUTEFFECT pHitEffect2; +LPDIRECTINPUTEFFECT pAfterburn1; +LPDIRECTINPUTEFFECT pAfterburn2; +LPDIRECTINPUTEFFECT pShootEffect; +LPDIRECTINPUTEFFECT pSecShootEffect; +LPDIRECTINPUTEFFECT pSpring; +LPDIRECTINPUTEFFECT pDock; +LPDIRECTINPUTEFFECT pDeathroll1; +LPDIRECTINPUTEFFECT pDeathroll2; +LPDIRECTINPUTEFFECT pExplode; + +di_periodic_effect_struct Struct_deathroll1; +di_periodic_effect_struct Struct_deathroll2; +di_periodic_effect_struct Struct_explode; +di_periodic_effect_struct Struct_afterburn1; +di_periodic_effect_struct Struct_afterburn2; +di_periodic_effect_struct Struct_dock; + +di_condition_effect_struct Spring_cond_effect; + +void joy_ff_afterburn_off(); +void init_periodic_effect_struct(di_periodic_effect_struct *effect, int type, int dur, int per, int ang = 0, int mag = 10000, int att = 0, int fade = 0); + +int joy_ff_init() +{ + int ff_enabled; + + Joy_ff_enabled = 0; // Assume no force feedback + ff_enabled = os_config_read_uint(NULL, "EnableJoystickFF", 0); + + if (ff_enabled) { + HRESULT hr; + TCHAR g_szOutput[256]; + TCHAR szCodeString[256]; + + Joy_ff_directional_hit_effect_enabled = os_config_read_uint(NULL, "EnableHitEffect", 1); + + hr = SWFF_OpenDefaultFFJoystick((HWND) os_get_window(), &pDi, &pDiDevice); + if (FAILED(hr)) { + nprintf(("Sandeep", "No FF On Joystick, not using FF\n")); + SWFF_ErrorCodeToString(hr, &szCodeString[0]); + wsprintf(g_szOutput, "Make sure JOYSTICKID1 has Force Feedback\n" + "Code = %lx: %s\n", hr, szCodeString); + + nprintf(("Sandeep", g_szOutput)); + return -1; + + } + + nprintf(("Sandeep", "There is FF on this joystick! (The peasants cheer)\n")); + SWFF_DestroyAllEffects(pDiDevice); + if (joy_ff_create_effects()) + return -1; + Joy_ff_enabled = 1; + } + + return 0; +} + +void joy_ff_shutdown() +{ + if (Joy_ff_enabled) { + pSpring->Stop(); + joy_ff_stop_effects(); + SWFF_DestroyAllEffects(pDiDevice); + pDiDevice->Unacquire(); + pDiDevice->Release(); + pDi->Release(); + } +} + +HRESULT joy_ff_handle_error(HRESULT hr, char *eff_name = NULL) +{ + if (FAILED(hr)) { + TCHAR szCodeString[256]; + + SWFF_ErrorCodeToString(hr, szCodeString); + if (eff_name) + nprintf(("Joystick", "FF: Error for %s: %s\n", eff_name, szCodeString)); + else + nprintf(("Joystick", "FF: Error: %s\n", szCodeString)); + } + + return hr; +} + +int joy_ff_create_std_periodic(LPDIRECTINPUTEFFECT *eff, int type, int dur, int per, int ang = 0, int mag = 10000, int att = 0, int fade = 0) +{ + joy_ff_handle_error(SWFF_CreatePeriodicEffect(pDiDevice, eff, type, dur, per, ang, mag, 0, att, 0, fade, 0, -1)); + if (!*eff) + return -1; + + return 0; +} + +void joy_ff_start_effect(LPDIRECTINPUTEFFECT eff, char *name) +{ + HRESULT hr; + + nprintf(("Joystick", "FF: Starting effect %s\n", name)); + hr = joy_ff_handle_error(eff->Start(1, 0)); + if (hr == DIERR_INPUTLOST) { + joy_reacquire_ff(); + joy_ff_handle_error(eff->Start(1, 0)); + } +} + +int joy_ff_create_effects() +{ + joy_ff_handle_error(SWFF_CreateConstantForceEffect( + pDiDevice, + &pHitEffect1, + 300000, // Duration + 0, // Angle + 10000, // Magnitude + 0, // Attack time + 10000, // Attack level + 120000, // Fade time + 1, // Fade level + -1), "HitEffect1"); + + if (pHitEffect1) + nprintf(("Joystick", "FF: Hit effect 1 loaded\n")); + else { // bail out early if we can't even load this (because rest probably won't either and failing is slow. + nprintf(("Joystick", "FF: Hit effect 1 failed to load\n")); + return -1; + } + + joy_ff_create_std_periodic( + &pHitEffect2, + SINE, + 300000, // Duration + 100000, // Period + 9000, // Angle + 10000, // Magnitude + 100000, // Attack time + 100000); // Fade time + + if (pHitEffect2) + nprintf(("Joystick", "FF: Hit effect 2 loaded\n")); + else + nprintf(("Joystick", "FF: Hit effect 2 failed to load\n")); + + joy_ff_handle_error(SWFF_CreatePeriodicEffect( + pDiDevice, + &pShootEffect, + SAWTOOTH_DOWN, + 160000, // Duration + 20000, // Period + 0, // Angle + 10000, // Magnitude + 0, + 0, + 0, + 120000, + 0, -1), "ShootEffect"); + + if (pShootEffect) + nprintf(("Joystick", "FF: Fire primary effect loaded\n")); + else + nprintf(("Joystick", "FF: Fire primary effect failed to load\n")); + + joy_ff_handle_error(SWFF_CreateConstantForceEffect( + pDiDevice, + &pSecShootEffect, + 200000, // Duration + 0, // Angle + 10000, // Magnitude + 50000, // Attack time + 10000, // Attack level + 100000, // Fade time + 1, // Fade level + -1), "SecShootEffect"); + + if (pSecShootEffect) + nprintf(("Joystick", "FF: Fire Secondary effect loaded\n")); + else + nprintf(("Joystick", "FF: Fire Secondary effect failed to load\n")); + + joy_ff_handle_error(SWFF_CreateConditionEffectStruct(&Spring_cond_effect, + pDiDevice, + &pSpring, + SPRING, + INFINITE, // uS + 100, // X Coefficient + 0, // X Offset + 100, // Y Coefficient + 0, // Y Offset + -1), // button play mask + "Spring"); + + if (pSpring) { + nprintf(("Joystick", "FF: Spring effect loaded\n")); + joy_ff_start_effect(pSpring, "Spring"); + + } else + nprintf(("Joystick", "FF: Spring effect failed to load\n")); + + init_periodic_effect_struct( + &Struct_afterburn1, + SINE, + INFINITE, // Duration + 20000, // Period + 0, // Angle + 8000); // Magnitude + + pDiDevice->CreateEffect(Struct_afterburn1.guid, &Struct_afterburn1.effect, &pAfterburn1, NULL); + if (pAfterburn1) + nprintf(("Joystick", "FF: Afterburner effect 1 loaded\n")); + else + nprintf(("Joystick", "FF: Afterburner effect 1 failed to load\n")); + + init_periodic_effect_struct( + &Struct_afterburn2, + SINE, + INFINITE, // Duration + 120000, // Period + 9000, // Angle + 4400); // Magnitude + + pDiDevice->CreateEffect(Struct_afterburn2.guid, &Struct_afterburn2.effect, &pAfterburn2, NULL); + if (pAfterburn2) + nprintf(("Joystick", "FF: Afterburner effect 2 loaded\n")); + else + nprintf(("Joystick", "FF: Afterburner effect 2 failed to load\n")); + + init_periodic_effect_struct( + &Struct_dock, + SQUARE_HIGH, + 125000, // Duration + 100000, // Period + 9000, // Angle + 4000); // Magnitude + + pDiDevice->CreateEffect(Struct_dock.guid, &Struct_dock.effect, &pDock, NULL); + if (pDock) + nprintf(("Joystick", "FF: Dock effect loaded\n")); + else + nprintf(("Joystick", "FF: Dock effect failed to load\n")); + + init_periodic_effect_struct( + &Struct_explode, + SAWTOOTH_DOWN, + 500000, // Duration + 20000, // Period + 9000, // Angle + 10000, // Magnitude + 0, // Attack time + 500000); // Fade time + + pDiDevice->CreateEffect(Struct_explode.guid, &Struct_explode.effect, &pExplode, NULL); + if (pExplode) + nprintf(("Joystick", "FF: Explosion effect loaded\n")); + else + nprintf(("Joystick", "FF: Explosion effect failed to load\n")); + + init_periodic_effect_struct( + &Struct_deathroll1, + SINE, + INFINITE, // Duration + 200000, // Period + 0, // Angle + 10000, // Magnitude + 2000000, // Attack time + 0); // Fade time + + pDiDevice->CreateEffect(Struct_deathroll1.guid, &Struct_deathroll1.effect, &pDeathroll1, NULL); + if (pDeathroll1) + nprintf(("Joystick", "FF: Deathroll effect 1 loaded\n")); + else + nprintf(("Joystick", "FF: Deathroll effect 1 failed to load\n")); + + init_periodic_effect_struct( + &Struct_deathroll2, + SINE, + INFINITE, // Duration + 200000, // Period + 9000, // Angle + 10000, // Magnitude + 2000000, // Attack time + 0); // Fade time + + pDiDevice->CreateEffect(Struct_deathroll2.guid, &Struct_deathroll2.effect, &pDeathroll2, NULL); + if (pDeathroll2) + nprintf(("Joystick", "FF: Deathroll effect 2 loaded\n")); + else + nprintf(("Joystick", "FF: Deathroll effect 2 failed to load\n")); + + return 0; +} + +void joy_ff_stop_effects() +{ + joy_ff_afterburn_off(); +} + +void joy_ff_mission_init(vector v) +{ + v.z = 0.0f; +// joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) - 1.3f) * 10.5f); + joy_ff_handling_scaler = (int) ((vm_vec_mag(&v) + 1.3f) * 5.0f); +// joy_ff_handling_scaler = (int) (vm_vec_mag(&v) * 7.5f); +} + +void joy_ff_adjust_handling(int speed) +{ + int v; + + v = speed * joy_ff_handling_scaler * 2 / 3; +// v += joy_ff_handling_scaler * joy_ff_handling_scaler * 6 / 7 + 250; + v += joy_ff_handling_scaler * 45 - 500; + if (v > 10000) + v = 10000; + + if (pSpring) { + if (Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient != v) { + HRESULT hr; + + Spring_cond_effect.DIConditionStruct[0].lPositiveCoefficient = v; + Spring_cond_effect.DIConditionStruct[0].lNegativeCoefficient = v; + Spring_cond_effect.DIConditionStruct[1].lPositiveCoefficient = v; + Spring_cond_effect.DIConditionStruct[1].lNegativeCoefficient = v; + nprintf(("Joystick", "FF: New handling force = %d\n", v)); + + hr = joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring"); + if (hr == DIERR_INPUTLOST) { + joy_reacquire_ff(); + joy_ff_handle_error(pSpring->SetParameters(&Spring_cond_effect.DIEffectStruct, DIEP_TYPESPECIFICPARAMS), "Spring"); + } + } + } +} + +void joy_ff_change_effect(di_periodic_effect_struct *s, LPDIRECTINPUTEFFECT eff, int gain = -1, int dur = 0, int flags = -1) +{ + int reload = 0; + + if ((gain >= 0) && ((int) s->effect.dwGain != gain)) { + s->effect.dwGain = gain; + nprintf(("Joystick", "FF: Gain reset to %d\n", gain)); + reload = 1; + } + + if (dur && ((int) s->effect.dwDuration != dur)) { + s->effect.dwDuration = dur; + nprintf(("Joystick", "FF: Duration reset to %d\n", dur)); + reload = 1; + } + + if (flags == -1) { + flags = DIEP_DURATION | DIEP_SAMPLEPERIOD | DIEP_GAIN | DIEP_DIRECTION | DIEP_ENVELOPE | DIEP_TYPESPECIFICPARAMS; + nprintf(("Joystick", "FF: Doing full reload of effect\n")); + } + + if (flags != (DIEP_DURATION | DIEP_GAIN)) + reload = 1; + + if (reload) { + HRESULT hr; + + nprintf(("Joystick", "FF: Swapping in a new effect\n")); + hr = joy_ff_handle_error(eff->SetParameters(&s->effect, flags)); + if (hr == DIERR_INPUTLOST) { + joy_reacquire_ff(); + joy_ff_handle_error(eff->SetParameters(&s->effect, flags)); + } + + } else + nprintf(("Joystick", "FF: Swap effect requested, but nothing changed\n")); +} + +int joy_ff_effect_playing(LPDIRECTINPUTEFFECT eff) +{ + DWORD flags; + + eff->GetEffectStatus(&flags); + return (flags & DIEGES_PLAYING); +} + +void joy_ff_docked() +{ + if (pDock) { + pDock->Stop(); + if (joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock") == DIERR_INPUTLOST) { + joy_reacquire_ff(); + joy_ff_handle_error(SWFF_SetGain(pDock, 10000), "Dock"); + } + + joy_ff_start_effect(pDock, "Dock"); + } +} + +void joy_ff_play_reload_effect() +{ + if (pDock) { + pDock->Stop(); + if (joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)") == DIERR_INPUTLOST) { + joy_reacquire_ff(); + joy_ff_handle_error(SWFF_SetGain(pDock, 5000), "Dock (Reload)"); + } + + joy_ff_start_effect(pDock, "Dock (Reload)"); + } +} + +int Joy_ff_afterburning = 0; + +void joy_ff_afterburn_on() +{ + if (pAfterburn1) { + pAfterburn1->Stop(); + joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN); + joy_ff_start_effect(pAfterburn1, "Afterburn1"); + } + + if (pAfterburn2) { + pAfterburn2->Stop(); + joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, 5000, INFINITE, DIEP_DURATION | DIEP_GAIN); + joy_ff_start_effect(pAfterburn2, "Afterburn2"); + } + + nprintf(("Joystick", "FF: Afterburn started\n")); + Joy_ff_afterburning = 1; +} + +void joy_ff_afterburn_off() +{ + if (!Joy_ff_afterburning) + return; + + if (pAfterburn1) { + pAfterburn1->Stop(); + } + + if (pAfterburn2) { + pAfterburn2->Stop(); + } + + Joy_ff_afterburning = 0; + nprintf(("Joystick", "FF: Afterburn stopped\n")); +} + +void joy_ff_deathroll() +{ + if (pDeathroll1) { + pDeathroll1->Stop(); + joy_ff_start_effect(pDeathroll1, "Deathroll1"); + } + + if (pDeathroll2) { + pDeathroll2->Stop(); + joy_ff_start_effect(pDeathroll2, "Deathroll2"); + } +} + +void joy_ff_explode() +{ + if (pDeathroll1) + pDeathroll1->Stop(); + + if (pDeathroll2) + pDeathroll2->Stop(); + + if (pExplode) { + pExplode->Stop(); + joy_ff_start_effect(pExplode, "Explode"); + } +} + +void joy_ff_fly_by(int mag) +{ + int gain; + + if (Joy_ff_afterburning) + return; + + gain = mag * 120 + 4000; + if (gain > 10000) + gain = 10000; + + if (pAfterburn1) { + pAfterburn1->Stop(); + joy_ff_change_effect(&Struct_afterburn1, pAfterburn1, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN); + joy_ff_start_effect(pAfterburn1, "Afterburn1 (Fly by)"); + } + + if (pAfterburn2) { + pAfterburn2->Stop(); + joy_ff_change_effect(&Struct_afterburn2, pAfterburn2, gain, 6000 * mag + 400000, DIEP_DURATION | DIEP_GAIN); + joy_ff_start_effect(pAfterburn2, "Afterburn2 (Fly by)"); + } +} + +void joy_reacquire_ff() +{ + if (!Joy_ff_enabled) + return; + + nprintf(("Joystick", "FF: Reacquiring\n")); + pDiDevice->Acquire(); + joy_ff_start_effect(pSpring, "Spring"); +} + +void joy_unacquire_ff() +{ +} + +void joy_ff_play_dir_effect(float x, float y) +{ + int idegs, imag; + float degs; + + if (!Joy_ff_enabled) + return; + + if (!pHitEffect1 || !pHitEffect2) + return; + + if (joy_ff_effect_playing(pHitEffect1) || joy_ff_effect_playing(pHitEffect2)) { + nprintf(("Joystick", "FF: HitEffect already playing. Skipping\n")); + return; + } + + if (Joy_ff_directional_hit_effect_enabled) { + if (x > 8000.0f) + x = 8000.0f; + else if (x < -8000.0f) + x = -8000.0f; + + if (y > 8000.0f) + y = 8000.0f; + else if (y < -8000.0f) + y = -8000.0f; + + imag = (int) fl_sqrt(x * x + y * y); + if (imag > 10000) + imag = 10000; + + degs = (float)atan2(x, y); + idegs = (int) (degs * 18000.0f / PI) + 90; + while (idegs < 0) + idegs += 36000; + + while (idegs >= 36000) + idegs -= 36000; + + if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1") == DIERR_INPUTLOST) { + joy_reacquire_ff(); + joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect1, idegs, imag), "HitEffect1"); + } + + idegs += 9000; + if (idegs >= 36000) + idegs -= 36000; + + if (joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2") == DIERR_INPUTLOST) { + joy_reacquire_ff(); + joy_ff_handle_error(SWFF_SetDirectionGain(pHitEffect2, idegs, imag), "HitEffect2"); + } + } + + joy_ff_start_effect(pHitEffect1, "HitEffect1"); + joy_ff_start_effect(pHitEffect2, "HitEffect2"); + //nprintf(("Joystick", "FF: Dir: %d, Mag = %d\n", idegs, imag)); +} + +void joy_ff_play_vector_effect(vector *v, float scaler) +{ + vector vf; + float x, y; + + nprintf(("Joystick", "FF: vec = { %f, %f, %f } s = %f\n", v->x, v->y, v->z, scaler)); + vm_vec_copy_scale(&vf, v, scaler); + x = vf.x; + vf.x = 0.0f; + + if (vf.y + vf.z < 0) + y = -vm_vec_mag(&vf); + else + y = vm_vec_mag(&vf); + + joy_ff_play_dir_effect(-x, -y); +} + +static int secondary_ff_level = 0; + +void joy_ff_play_secondary_shoot(int gain) +{ + if (!Joy_ff_enabled) + return; + + if (!pSecShootEffect) + return; + + gain = gain * 100 + 2500; + if (gain > 10000) + gain = 10000; + + if (gain != secondary_ff_level) { + if (joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect") == DIERR_INPUTLOST) { + joy_reacquire_ff(); + joy_ff_handle_error(SWFF_SetGain(pSecShootEffect, gain), "SecShootEffect"); + } + + if (joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect") == DIERR_INPUTLOST) { + joy_reacquire_ff(); + joy_ff_handle_error(SWFF_SetDuration(pSecShootEffect, 150000 + gain * 25), "SecShootEffect"); + } + + secondary_ff_level = gain; + nprintf(("Joystick", "FF: Secondary force = %d\n", gain)); + } + + pSecShootEffect->Stop(); + joy_ff_start_effect(pSecShootEffect, "SecShootEffect"); +} + +static int primary_ff_level = 0; + +void joy_ff_play_primary_shoot(int gain) +{ + if (!Joy_ff_enabled) + return; + + if (!pShootEffect) + return; + + if (gain > 10000) + gain = 10000; + + if (gain != primary_ff_level) { + if (joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect") == DIERR_INPUTLOST) { + joy_reacquire_ff(); + joy_ff_handle_error(SWFF_SetGain(pShootEffect, gain), "ShootEffect"); + } + + primary_ff_level = gain; + } + + pShootEffect->Stop(); + joy_ff_start_effect(pShootEffect, "ShootEffect"); +} + +void init_periodic_effect_struct(di_periodic_effect_struct *effect, int type, int dur, int per, int ang, int mag, int att, int fade) +{ + // type-specific stuff + DWORD dwPhase = 0; + GUID guid = GUID_Square; + + switch (type) { + case SINE: + guid = GUID_Sine; + break; + case COSINE: + guid = GUID_Sine; + dwPhase = 9000; + break; + case SQUARE_HIGH: + guid = GUID_Square; + break; + case SQUARE_LOW: + guid = GUID_Square; + dwPhase = 18000; + break; + case TRIANGLE_UP: + guid = GUID_Triangle; + break; + case TRIANGLE_DOWN: + guid = GUID_Triangle; + dwPhase = 18000; + break; + case SAWTOOTH_UP: + guid = GUID_SawtoothUp; + break; + case SAWTOOTH_DOWN: + guid = GUID_SawtoothDown; + break; + default: + Int3(); // illegal + break; + } + + effect->guid = guid; + effect->periodic_struct.dwMagnitude = mag; + effect->periodic_struct.lOffset = 0; + effect->periodic_struct.dwPhase = dwPhase; + effect->periodic_struct.dwPeriod = per; + + effect->envelope_struct.dwSize = sizeof(DIENVELOPE); + effect->envelope_struct.dwAttackTime = att; + effect->envelope_struct.dwAttackLevel = 0; + effect->envelope_struct.dwFadeTime = fade; + effect->envelope_struct.dwFadeLevel = 0; + + effect->axes[0] = DIJOFS_X; + effect->axes[1] = DIJOFS_Y; + + effect->direction[0] = ang; + effect->direction[1] = 0; + + effect->effect.dwSize = sizeof(DIEFFECT); + effect->effect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + effect->effect.dwDuration = dur; + effect->effect.dwSamplePeriod = HZ_TO_uS(100); + effect->effect.dwGain = 10000; + effect->effect.dwTriggerButton = DIEB_NOTRIGGER; + effect->effect.dwTriggerRepeatInterval = 0; + effect->effect.cAxes = 2; + effect->effect.rgdwAxes = effect->axes; + effect->effect.rglDirection = effect->direction; + effect->effect.lpEnvelope = &effect->envelope_struct; + effect->effect.cbTypeSpecificParams = sizeof(effect->periodic_struct); + effect->effect.lpvTypeSpecificParams = &effect->periodic_struct; +} diff --git a/src/io/key.cpp b/src/io/key.cpp new file mode 100644 index 0000000..178d384 --- /dev/null +++ b/src/io/key.cpp @@ -0,0 +1,1190 @@ +/* + * $Logfile: /Freespace2/code/Io/Key.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 6 10/29/99 6:10p Jefff + * squashed the damned y/z german issues once and for all + * + * 5 6/07/99 1:21p Dave + * Fixed debug console scrolling problem. Thread related. + * + * 4 6/02/99 6:18p Dave + * Fixed TNT lockup problems! Wheeeee! + * + * 3 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 37 6/19/98 3:50p Lawrance + * change GERMAN to GR_BUILD + * + * 36 6/17/98 11:05a Lawrance + * translate french and german keys + * + * 35 6/12/98 4:49p Hoffoss + * Added code to remap scancodes for german and french keyboards. + * + * 34 5/20/98 12:10a Mike + * Remove mprintfs. + * + * 33 5/19/98 12:19p Mike + * Cheat codes! + * + * 32 5/19/98 12:28a Mike + * Cheat stuff. + * + * 31 5/18/98 11:01p Mike + * Adding support for cheat system. + * + * 30 5/11/98 12:09a Lawrance + * Put in code to turn on/off NumLock key when running under 95 + * + * 29 5/01/98 4:23p Lawrance + * Remap the scancode for the UK "\" key + * + * 28 4/18/98 12:42p John + * Added code to use DirectInput to read keyboard. Took out because it + * didn't differentiate btwn Pause and Numlock and sometimes Ctrl. + * + * 27 4/13/98 10:16a John + * Switched gettime back to timer_get_milliseconds, which is now thread + * safe. + * + * 26 4/12/98 11:08p Lawrance + * switch back to using gettime() in separate threads + * + * 25 4/12/98 5:31p Lawrance + * use timer_get_milliseconds() instead of gettime() + * + * 24 3/25/98 8:08p John + * Restructured software rendering into two modules; One for windowed + * debug mode and one for DirectX fullscreen. + * + * 23 1/23/98 3:49p Mike + * Fix bug in negative time-down due to latency. + * + * 22 1/07/98 6:41p Lawrance + * Pass message latency to the keyboard lib. + * + * 21 11/17/97 10:42a John + * On Debug+Backsp, cleared out keys so that it looks like nothing ever + * happened, so they're not stuck down. + * + * 20 11/14/97 4:33p Mike + * Change Debug key to backquote (from F11). + * Balance a ton of subsystems in ships.tbl. + * Change "Heavy Laser" to "Disruptor". + * + * 19 9/13/97 9:30a Lawrance + * added ability to block certain keys from the keyboard + * + * 18 9/10/97 6:02p Hoffoss + * Added code to check for key-pressed sexp operator in FreeSpace as part + * of training mission stuff. + * + * 17 9/09/97 11:08a Sandeep + * fixed warning level 4 + * + * 16 7/29/97 5:30p Lawrance + * move gettime() to timer module + * + * 15 4/22/97 10:56a John + * fixed some resource leaks. + * + * 14 2/03/97 4:23p Allender + * use F11 as debug key now + * + * 13 1/10/97 5:15p Mike + * Moved ship-specific parameters from obj_subsystem to ship_subsys. + * + * Added turret code to AI system. + * + * $NoKeywords: $ + */ + +//#define USE_DIRECTINPUT + +#ifndef PLAT_UNIX +#include +#include +#endif + +#include "pstypes.h" +#include "key.h" +#include "fix.h" +#include "timer.h" +#include "osapi.h" +#include "localize.h" + +#define KEY_BUFFER_SIZE 16 + +//-------- Variable accessed by outside functions --------- +ubyte keyd_buffer_type; // 0=No buffer, 1=buffer ASCII, 2=buffer scans +ubyte keyd_repeat; +uint keyd_last_pressed; +uint keyd_last_released; +ubyte keyd_pressed[NUM_KEYS]; +int keyd_time_when_last_pressed; + +typedef struct keyboard { + ushort keybuffer[KEY_BUFFER_SIZE]; + uint time_pressed[KEY_BUFFER_SIZE]; + uint TimeKeyWentDown[NUM_KEYS]; + uint TimeKeyHeldDown[NUM_KEYS]; + uint TimeKeyDownChecked[NUM_KEYS]; + uint NumDowns[NUM_KEYS]; + uint NumUps[NUM_KEYS]; + int down_check[NUM_KEYS]; // nonzero if has been pressed yet this mission + uint keyhead, keytail; +} keyboard; + +keyboard key_data; + +int key_inited = 0; + +CRITICAL_SECTION key_lock; + +//int Backspace_debug=1; // global flag that will enable/disable the backspace key from stopping execution + // This flag was created since the backspace key is also used to correct mistakes + // when typing in your pilots callsign. This global flag is checked before execution + // is stopped. + +int ascii_table[128] = +{ 255, 255, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=',255,255, + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 255, 255, + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 39, '`', + 255, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 255,'*', + 255, ' ', 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,255,255, + 255, 255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255 }; + +int shifted_ascii_table[128] = +{ 255, 255, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+',255,255, + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 255, 255, + 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', + 255, '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 255,255, + 255, ' ', 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,255,255, + 255, 255, 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255 }; + +// used to limit the keypresses that are accepted from the keyboard +#define MAX_FILTER_KEYS 64 +int Num_filter_keys; +int Key_filter[MAX_FILTER_KEYS]; + +static int Key_numlock_was_on = 0; // Flag to indicate whether NumLock is on at start +static int Key_running_NT = 0; // NT is the OS + +int Cheats_enabled = 0; +int Key_normal_game = 0; + +int key_numlock_is_on() +{ +#ifdef PLAT_UNIX + int keys[SDLK_LAST]; + SDL_GetKeyState(keys); + if ( keys[SDLK_NUMLOCK] ) { + return 1; + } +#else + unsigned char keys[256]; + GetKeyboardState(keys); + if ( keys[VK_NUMLOCK] ) { + return 1; + } +#endif + return 0; +} + +void key_turn_off_numlock() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + unsigned char keys[256]; + GetKeyboardState(keys); + keys[VK_NUMLOCK] = 0; + SetKeyboardState(keys); +#endif +} + +void key_turn_on_numlock() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + unsigned char keys[256]; + GetKeyboardState(keys); + keys[VK_NUMLOCK] = 1; + SetKeyboardState(keys); +#endif +} + +// Convert a BIOS scancode to ASCII. +// If scancode >= 127, returns 255, meaning there is no corresponding ASCII code. +// Uses ascii_table and shifted_ascii_table to translate scancode to ASCII. +int key_to_ascii(int keycode ) +{ + int shifted; + + if ( !key_inited ) return 255; + + shifted = keycode & KEY_SHIFTED; + keycode &= 0xFF; + + if ( keycode>=127 ) + return 255; + + if (shifted) + return shifted_ascii_table[keycode]; + else + return ascii_table[keycode]; +} + +// Flush the keyboard buffer. +// Clear the keyboard array (keyd_pressed). +void key_flush() +{ + int i; + uint CurTime; + + if ( !key_inited ) return; + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + ENTER_CRITICAL_SECTION(&key_lock); + + key_data.keyhead = key_data.keytail = 0; + + //Clear the keyboard buffer + for (i=0; i= KEY_BUFFER_SIZE ) n=0; + return n; +} + +// Returns 1 if character waiting... 0 otherwise +int key_checkch() +{ + int is_one_waiting = 0; + + if ( !key_inited ) return 0; + + ENTER_CRITICAL_SECTION(&key_lock); + + if (key_data.keytail != key_data.keyhead){ + is_one_waiting = 1; + } + + LEAVE_CRITICAL_SECTION(&key_lock); + + return is_one_waiting; +} + +// Return key scancode if a key has been pressed, +// else return 0. +// Reads keys out of the key buffer and updates keyhead. +int key_inkey() +{ + int key = 0; + + if ( !key_inited ) return 0; + + ENTER_CRITICAL_SECTION(&key_lock); + + if (key_data.keytail!=key_data.keyhead) { + key = key_data.keybuffer[key_data.keyhead]; + key_data.keyhead = add_one(key_data.keyhead); + } + + LEAVE_CRITICAL_SECTION(&key_lock); + + return key; +} + +// Unget a key. Puts it back in the input queue. +void key_outkey(int key) +{ + int bufp; + + if ( !key_inited ) return; + + ENTER_CRITICAL_SECTION(&key_lock); + + bufp = key_data.keytail+1; + + if (bufp >= KEY_BUFFER_SIZE){ + bufp = 0; + } + + key_data.keybuffer[key_data.keytail] = (unsigned short)key; + + key_data.keytail = bufp; + + LEAVE_CRITICAL_SECTION(&key_lock); +} + + + +// Return amount of time last key was held down. +// This is currently (July 17, 1996) bogus because our timing is +// not accurate. +int key_inkey_time(uint * time) +{ + int key = 0; + + if ( !key_inited ) { + *time = 0; + return 0; + } + + ENTER_CRITICAL_SECTION(&key_lock); + + if (key_data.keytail!=key_data.keyhead) { + key = key_data.keybuffer[key_data.keyhead]; + *time = key_data.time_pressed[key_data.keyhead]; + key_data.keyhead = add_one(key_data.keyhead); + } + + LEAVE_CRITICAL_SECTION(&key_lock); + + return key; +} + + +// Returns scancode of last key pressed, if any (returns 0 if no key pressed) +// but does not update keyhead pointer. +int key_peekkey() +{ + int key = 0; + + if ( !key_inited ) return 0; + + ENTER_CRITICAL_SECTION(&key_lock); + + if (key_data.keytail!=key_data.keyhead) { + key = key_data.keybuffer[key_data.keyhead]; + } + LEAVE_CRITICAL_SECTION(&key_lock); + + return key; +} + +// If not installed, uses BIOS and returns getch(); +// Else returns pending key (or waits for one if none waiting). +int key_getch() +{ + int dummy=0; + int in; + + if ( !key_inited ) return 0; + + while (!key_checkch()){ + os_poll(); + + dummy++; + } + in = key_inkey(); + + return in; +} + +// Set global shift_status with modifier results (shift, ctrl, alt). +uint key_get_shift_status() +{ + unsigned int shift_status = 0; + + if ( !key_inited ) return 0; + + ENTER_CRITICAL_SECTION(&key_lock); + + if ( keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT] ) + shift_status |= KEY_SHIFTED; + + if ( keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT] ) + shift_status |= KEY_ALTED; + + if ( keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL] ) + shift_status |= KEY_CTRLED; + +#ifndef NDEBUG + if (keyd_pressed[KEY_DEBUG_KEY]) + shift_status |= KEY_DEBUGGED; +#else + if (keyd_pressed[KEY_DEBUG_KEY]) { + mprintf(("Cheats_enabled = %i, Key_normal_game = %i\n", Cheats_enabled, Key_normal_game)); + if ((Cheats_enabled) && Key_normal_game) { + mprintf(("Debug key\n")); + shift_status |= KEY_DEBUGGED1; + } + } +#endif + LEAVE_CRITICAL_SECTION(&key_lock); + + return shift_status; +} + +// Returns amount of time key (specified by "code") has been down since last call. +// Returns float, unlike key_down_time() which returns a fix. +float key_down_timef(uint scancode) +{ + uint time_down, time; + uint delta_time; + + if ( !key_inited ) return 0.0f; + + if ((scancode<0)|| (scancode>=NUM_KEYS)) return 0.0f; + + ENTER_CRITICAL_SECTION(&key_lock); + + time = timer_get_milliseconds(); + delta_time = time - key_data.TimeKeyDownChecked[scancode]; + key_data.TimeKeyDownChecked[scancode] = time; + + if ( delta_time <= 1 ) { + key_data.TimeKeyWentDown[scancode] = time; + if (keyd_pressed[scancode]) { + LEAVE_CRITICAL_SECTION(&key_lock); + return 1.0f; + } else { + LEAVE_CRITICAL_SECTION(&key_lock); + return 0.0f; + } + } + + if ( !keyd_pressed[scancode] ) { + time_down = key_data.TimeKeyHeldDown[scancode]; + key_data.TimeKeyHeldDown[scancode] = 0; + } else { + time_down = time - key_data.TimeKeyWentDown[scancode]; + key_data.TimeKeyWentDown[scancode] = time; + } + + LEAVE_CRITICAL_SECTION(&key_lock); + + return i2fl(time_down) / i2fl(delta_time); +} + +/* +// Returns amount of time key (specified by "code") has been down since last call. +// Returns float, unlike key_down_time() which returns a fix. +fix key_down_time( uint code ) +{ + uint time_down, time; + uint delta_time; + + if ( !key_inited ) return 0.0f; + + if ((scancode<0)|| (scancode>=NUM_KEYS)) return 0.0f; + + EnterCriticalSection( &key_lock ); + + time = timer_get_milliseconds(); + delta_time = time - TimeKeyDownChecked[scancode]; + TimeKeyDownChecked[scancode] = time; + + if ( delta_time <= 1 ) { + LeaveCriticalSection( &key_lock ); + if (keyd_pressed[scancode]) + return F1_0; + else + return 0; + } + + if ( !keyd_pressed[scancode] ) { + time_down = key_data.TimeKeyHeldDown[scancode]; + key_data.TimeKeyHeldDown[scancode] = 0; + } else { + time_down = time - key_data.TimeKeyWentDown[scancode]; + key_data.TimeKeyWentDown[scancode] = time; + } + + LeaveCriticalSection( &key_lock ); + + return fixmuldiv( time_down, F1_0, delta_time ); +} +*/ + + +// Returns number of times key has went from up to down since last call. +int key_down_count(int scancode) +{ + int n; + + if ( !key_inited ) return 0; + if ((scancode<0)|| (scancode>=NUM_KEYS)) return 0; + + ENTER_CRITICAL_SECTION(&key_lock); + + n = key_data.NumDowns[scancode]; + key_data.NumDowns[scancode] = 0; + + LEAVE_CRITICAL_SECTION(&key_lock); + + return n; +} + + +// Returns number of times key has went from down to up since last call. +int key_up_count(int scancode) +{ + int n; + + if ( !key_inited ) return 0; + if ((scancode<0)|| (scancode>=NUM_KEYS)) return 0; + + ENTER_CRITICAL_SECTION(&key_lock); + + n = key_data.NumUps[scancode]; + key_data.NumUps[scancode] = 0; + + LEAVE_CRITICAL_SECTION(&key_lock); + + return n; +} + +int key_check(int key) +{ + return key_data.down_check[key]; +} + +// Add a key up or down code to the key buffer. state=1 -> down, state=0 -> up +// latency => time difference in ms between when key was actually pressed and now +//void key_mark( uint code, int state ) +void key_mark( uint code, int state, uint latency ) +{ + uint scancode, breakbit, temp, event_time; + ushort keycode; + + if ( !key_inited ) return; + + ENTER_CRITICAL_SECTION(&key_lock); + + // If running in the UK, need to translate their wacky slash scancode to ours + if ( code == KEY_SLASH_UK ) { + code = KEY_SLASH; + } + + if(Lcl_fr){ + switch (code) { + case KEY_A: + code = KEY_Q; + break; + + case KEY_M: + code = KEY_COMMA; + break; + + case KEY_Q: + code = KEY_A; + break; + + case KEY_W: + code = KEY_Z; + break; + + case KEY_Z: + code = KEY_W; + break; + + case KEY_SEMICOL: + code = KEY_M; + break; + + case KEY_COMMA: + code = KEY_SEMICOL; + break; + } + } else if(Lcl_gr){ + switch (code) { + case KEY_Y: + code = KEY_Z; + break; + + case KEY_Z: + code = KEY_Y; + break; + } + + } + + if ( (code == 0xc5) && !Key_running_NT ) { + key_turn_off_numlock(); + } + + Assert( code < NUM_KEYS ); + + event_time = timer_get_milliseconds() - latency; + // event_time = timeGetTime() - latency; + + // Read in scancode + scancode = code & (NUM_KEYS-1); + breakbit = !state; + + if (breakbit) { + // Key going up + keyd_last_released = scancode; + keyd_pressed[scancode] = 0; + key_data.NumUps[scancode]++; + + // What is the point of this code? "temp" is never used! + temp = 0; + temp |= keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT]; + temp |= keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT]; + temp |= keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL]; +//#ifndef NDEBUG + temp |= keyd_pressed[KEY_DEBUG_KEY]; +//#endif + if (event_time < key_data.TimeKeyWentDown[scancode]) + key_data.TimeKeyHeldDown[scancode] = 0; + else + key_data.TimeKeyHeldDown[scancode] += event_time - key_data.TimeKeyWentDown[scancode]; + } else { + // Key going down + keyd_last_pressed = scancode; + keyd_time_when_last_pressed = event_time; + if (!keyd_pressed[scancode]) { + // First time down + key_data.TimeKeyWentDown[scancode] = event_time; + keyd_pressed[scancode] = 1; + key_data.NumDowns[scancode]++; + key_data.down_check[scancode]++; + +// mprintf(( "Scancode = %x\n", scancode )); + +// if ( scancode == KEY_BREAK ) +// Int3(); + + + } else if (!keyd_repeat) { + // Don't buffer repeating key if repeat mode is off + scancode = 0xAA; + } + + if ( scancode!=0xAA ) { + keycode = (unsigned short)scancode; + + if ( keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT] ) + keycode |= KEY_SHIFTED; + + if ( keyd_pressed[KEY_LALT] || keyd_pressed[KEY_RALT] ) + keycode |= KEY_ALTED; + + if ( keyd_pressed[KEY_LCTRL] || keyd_pressed[KEY_RCTRL] ) + keycode |= KEY_CTRLED; + +#ifndef NDEBUG + if ( keyd_pressed[KEY_DEBUG_KEY] ) + keycode |= KEY_DEBUGGED; +// if ( keycode == (KEY_BACKSP + KEY_DEBUGGED) ) { +// keycode = 0; +// keyd_pressed[KEY_DEBUG_KEY] = 0; +// keyd_pressed[KEY_BACKSP] = 0; +// Int3(); +// } +#else + if ( keyd_pressed[KEY_DEBUG_KEY] ) { + mprintf(("Cheats_enabled = %i, Key_normal_game = %i\n", Cheats_enabled, Key_normal_game)); + if (Cheats_enabled && Key_normal_game) { + keycode |= KEY_DEBUGGED1; + } + } + +#endif + + if ( keycode ) { + temp = key_data.keytail+1; + if ( temp >= KEY_BUFFER_SIZE ) temp=0; + + if (temp!=key_data.keyhead) { + int i, accept_key = 1; + // Num_filter_keys will only be non-zero when a key filter has + // been explicity set up via key_set_filter() + for ( i = 0; i < Num_filter_keys; i++ ) { + accept_key = 0; + if ( Key_filter[i] == keycode ) { + accept_key = 1; + break; + } + } + + if ( accept_key ) { + key_data.keybuffer[key_data.keytail] = keycode; + key_data.time_pressed[key_data.keytail] = keyd_time_when_last_pressed; + key_data.keytail = temp; + } + } + } + } + } + + LEAVE_CRITICAL_SECTION(&key_lock); +} + +#ifdef USE_DIRECTINPUT +void di_cleanup(); +int di_init(); +#endif + + +void key_close() +{ + if ( !key_inited ) return; + + #ifdef USE_DIRECTINPUT + di_cleanup(); + #endif + + if ( Key_numlock_was_on ) { + key_turn_on_numlock(); + Key_numlock_was_on = 0; + } + + key_inited = 0; +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + DeleteCriticalSection( &key_lock ); +#endif +} + +void key_init() +{ + // Initialize queue + if ( key_inited ) return; + key_inited = 1; + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + InitializeCriticalSection( &key_lock ); + + ENTER_CRITICAL_SECTION(&key_lock); +#endif + + keyd_time_when_last_pressed = timer_get_milliseconds(); + keyd_buffer_type = 1; + keyd_repeat = 1; + + // Clear the keyboard array + key_flush(); + + // Clear key filter + key_clear_filter(); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + LEAVE_CRITICAL_SECTION(&key_lock); + + #ifdef USE_DIRECTINPUT + di_init(); + #endif + + OSVERSIONINFO ver; + ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&ver); + if ( ver.dwPlatformId == VER_PLATFORM_WIN32_NT ) { + Key_running_NT = 1; + } else { + Key_running_NT = 0; + if ( key_numlock_is_on() ) { + Key_numlock_was_on = 1; + key_turn_off_numlock(); + } + } +#endif + + atexit(key_close); +} + +void key_level_init() +{ + int i; + + for (i=0; i array of keys to act as a filter +// num => number of keys in filter_array +// +void key_set_filter(int *filter_array, int num) +{ + int i; + + if ( num >= MAX_FILTER_KEYS ) { + Int3(); + num = MAX_FILTER_KEYS; + } + + Num_filter_keys = num; + + for ( i = 0; i < num; i++ ) { + Key_filter[i] = filter_array[i]; + } +} + +// Clear the key filter, so all keypresses are accepted from keyboard +// +void key_clear_filter() +{ + int i; + + Num_filter_keys = 0; + for ( i = 0; i < MAX_FILTER_KEYS; i++ ) { + Key_filter[i] = -1; + } +} + + +#ifdef USE_DIRECTINPUT + +// JAS - April 18, 1998 +// Not using because DI has the following problems: (Everything else works ok) +// Under NT, Pause and Numlock report as identical keys. +// Under 95, Pause is the same as pressing Ctrl then Numlock. So the game fires each +// time you hit it. +// + +//============================================================================ +// Direct Input code +// For the keyboard, this basically replaces our old functionallity of: +// WM_KEYDOWN: +// key_mark(...); +// WM_KEYUP: +// key_mark(...); +//============================================================================ + + +#include "vdinput.h" + +#define MAX_BUFFERED_KEYBOARD_EVENTS 10 + +static LPDIRECTINPUT Di_object = NULL; +static LPDIRECTINPUTDEVICE Di_keyboard = NULL; +static HANDLE Di_thread = NULL; +static DWORD Di_thread_id = NULL; +static HANDLE Di_event = NULL; + +DWORD di_process(DWORD lparam) +{ + while (1) { + if ( WaitForSingleObject( Di_event, INFINITE )==WAIT_OBJECT_0 ) { + + //mprintf(( "Got event!\n" )); + + HRESULT hr; + + DIDEVICEOBJECTDATA rgdod[10]; + DWORD dwItems = MAX_BUFFERED_KEYBOARD_EVENTS; + +again:; + hr = Di_keyboard->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), rgdod, &dwItems, 0); + + if (hr == DIERR_INPUTLOST) { + /* + * DirectInput is telling us that the input stream has + * been interrupted. We aren't tracking any state + * between polls, so we don't have any special reset + * that needs to be done. We just re-acquire and + * try again. + */ + Sleep(1000); // Pause a second... + hr = Di_keyboard->Acquire(); + if (SUCCEEDED(hr)) { + goto again; + } + } + + if (SUCCEEDED(hr)) { + // dwItems = number of elements read (could be zero) + if (hr == DI_BUFFEROVERFLOW) { + // Buffer had overflowed. + mprintf(( "Buffer overflowed!\n" )); + } + int i; + + //mprintf(( "Got %d events\n", dwItems )); + + for (i=0; i<(int)dwItems; i++ ) { + int key = rgdod[i].dwOfs; + int state = rgdod[i].dwData; + int stamp = rgdod[i].dwTimeStamp; + + int latency; + latency = timeGetTime() - stamp; + if ( latency < 0 ) + latency=0; + +// if ( key == KEY_PRINT_SCRN ) { +// key_mark( key, 1, latency ); +// } +// key_mark( key, (state&0x80?1:0), latency ); + mprintf(( "Key=%x, State=%x, Time=%d, Latency=%d\n", key, state, stamp, latency )); + } + + } + } + + } + + return 0; +} + + +int di_init() +{ + HRESULT hr; + + return 0; + + + /* + * Register with the DirectInput subsystem and get a pointer + * to a IDirectInput interface we can use. + * + * Parameters: + * + * g_hinst + * + * Instance handle to our application or DLL. + * + * DIRECTINPUT_VERSION + * + * The version of DirectInput we were designed for. + * We take the value from the header file. + * + * &g_pdi + * + * Receives pointer to the IDirectInput interface + * that was created. + * + * NULL + * + * We do not use OLE aggregation, so this parameter + * must be NULL. + * + */ + hr = DirectInputCreate(GetModuleHandle(NULL), 0x300, &Di_object, NULL); + + if (FAILED(hr)) { + mprintf(( "DirectInputCreate failed!\n" )); + return FALSE; + } + + /* + * Obtain an interface to the system keyboard device. + * + * Parameters: + * + * GUID_SysKeyboard + * + * The instance GUID for the device we wish to access. + * GUID_SysKeyboard is a predefined instance GUID that + * always refers to the system keyboard device. + * + * &g_pKeyboard + * + * Receives pointer to the IDirectInputDevice interface + * that was created. + * + * NULL + * + * We do not use OLE aggregation, so this parameter + * must be NULL. + * + */ + hr = Di_object->CreateDevice(GUID_SysKeyboard, &Di_keyboard, NULL); + + if (FAILED(hr)) { + mprintf(( "CreateDevice failed!\n" )); + return FALSE; + } + + /* + * Set the data format to "keyboard format". + * + * A data format specifies which controls on a device we + * are interested in, and how they should be reported. + * + * This tells DirectInput that we will be passing an array + * of 256 bytes to IDirectInputDevice::GetDeviceState. + * + * Parameters: + * + * c_dfDIKeyboard + * + * Predefined data format which describes + * an array of 256 bytes, one per scancode. + */ + hr = Di_keyboard->SetDataFormat(&c_dfDIKeyboard); + + if (FAILED(hr)) { + mprintf(( "SetDataFormat failed!\n" )); + return FALSE; + } + + + /* + * Set the cooperativity level to let DirectInput know how + * this device should interact with the system and with other + * DirectInput applications. + * + * Parameters: + * + * DISCL_NONEXCLUSIVE + * + * Retrieve keyboard data when acquired, not interfering + * with any other applications which are reading keyboard + * data. + * + * DISCL_FOREGROUND + * + * If the user switches away from our application, + * automatically release the keyboard back to the system. + * + */ + hr = Di_keyboard->SetCooperativeLevel((HWND)os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); + + if (FAILED(hr)) { + mprintf(( "SetCooperativeLevel failed!\n" )); + return FALSE; + } + + DIPROPDWORD hdr; + + // Turn on buffering + hdr.diph.dwSize = sizeof(DIPROPDWORD); + hdr.diph.dwHeaderSize = sizeof(DIPROPHEADER); + hdr.diph.dwObj = 0; + hdr.diph.dwHow = DIPH_DEVICE; // Apply to entire device + hdr.dwData = 16; //MAX_BUFFERED_KEYBOARD_EVENTS; + + hr = Di_keyboard->SetProperty( DIPROP_BUFFERSIZE, &hdr.diph ); + if (FAILED(hr)) { + mprintf(( "SetProperty DIPROP_BUFFERSIZE failed\n" )); + return FALSE; + } + + + Di_event = CreateEvent( NULL, FALSE, FALSE, NULL ); + Assert(Di_event != NULL); + + Di_thread = CreateThread(NULL, 1024, (LPTHREAD_START_ROUTINE)di_process, NULL, 0, &Di_thread_id); + Assert( Di_thread != NULL ); + + SetThreadPriority(Di_thread, THREAD_PRIORITY_HIGHEST); + + hr = Di_keyboard->SetEventNotification(Di_event); + if (FAILED(hr)) { + mprintf(( "SetEventNotification failed\n" )); + return FALSE; + } + + Di_keyboard->Acquire(); + + return TRUE; +} + +void di_cleanup() +{ + /* + * Destroy any lingering IDirectInputDevice object. + */ + if (Di_keyboard) { + + /* + * Cleanliness is next to godliness. Unacquire the device + * one last time just in case we got really confused and tried + * to exit while the device is still acquired. + */ + Di_keyboard->Unacquire(); + + Di_keyboard->Release(); + Di_keyboard = NULL; + } + + /* + * Destroy any lingering IDirectInput object. + */ + if (Di_object) { + Di_object->Release(); + Di_object = NULL; + } + + if ( Di_event ) { + CloseHandle(Di_event); + Di_event = NULL; + } + +} + +#endif + + + + diff --git a/src/io/keycontrol.cpp b/src/io/keycontrol.cpp new file mode 100644 index 0000000..9af4289 --- /dev/null +++ b/src/io/keycontrol.cpp @@ -0,0 +1,2856 @@ +/* + * $Logfile: /Freespace2/code/Io/KeyControl.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to read and deal with keyboard input. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 47 9/09/99 11:40p Dave + * Handle an Assert() in beam code. Added supernova sounds. Play the right + * 2 end movies properly, based upon what the player did in the mission. + * + * 46 9/07/99 11:26p Andsager + * Fix "r" targeting key, making evaluate_ship_as_closest_target() and + * hud_target_live_turret() consider if turret is targeting player + * + * 45 9/03/99 1:31a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 44 9/01/99 10:09a Dave + * Pirate bob. + * + * 43 8/27/99 10:36a Dave + * Impose a 2% penalty for hitting the shield balance key. + * + * 42 8/27/99 9:57a Dave + * Enabled standard cheat codes. Allow player to continue in a campaing + * after using cheat codes. + * + * 41 8/26/99 9:45a Dave + * First pass at easter eggs and cheats. + * + * 40 8/24/99 1:49a Dave + * Fixed client-side afterburner stuttering. Added checkbox for no version + * checking on PXO join. Made button info passing more friendly between + * client and server. + * + * 39 8/22/99 5:53p Dave + * Scoring fixes. Added self destruct key. Put callsigns in the logfile + * instead of ship designations for multiplayer players. + * + * 38 8/19/99 10:59a Dave + * Packet loss detection. + * + * 37 8/18/99 12:09p Andsager + * Add debug if message has no anim for message. Make messages come from + * wing leader. + * + * 36 8/05/99 2:05a Dave + * Whee. + * + * 35 8/01/99 12:39p Dave + * Added HUD contrast control key (for nebula). + * + * 34 7/31/99 2:30p Dave + * Added nifty mission message debug viewing keys. + * + * 33 7/21/99 8:10p Dave + * First run of supernova effect. + * + * 32 7/15/99 4:09p Andsager + * Disable cheats for FS2_DEMO + * + * 31 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 30 7/07/99 3:32p Dave + * Oops. Forgot to remove this. + * + * 29 7/02/99 4:31p Dave + * Much more sophisticated lightning support. + * + * 28 6/10/99 3:43p Dave + * Do a better job of syncing text colors to HUD gauges. + * + * 27 6/09/99 2:55p Andsager + * Allow multiple asteroid subtypes (of large, medium, small) and follow + * family. + * + * 26 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * 25 5/08/99 8:25p Dave + * Upped object pairs. First run of nebula lightning. + * + * 24 5/05/99 9:02p Dave + * Fixed D3D aabitmap rendering. Spiffed up nebula effect a bit (added + * rotations, tweaked values, made bitmap selection more random). Fixed + * D3D beam weapon clipping problem. Added D3d frame dumping. + * + * 23 5/03/99 9:07a Dave + * Pirate Bob. Changed beam test code a bit. + * + * 22 4/21/99 6:15p Dave + * Did some serious housecleaning in the beam code. Made it ready to go + * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added + * a handy macro for recalculating collision pairs for a given object. + * + * 21 4/16/99 5:54p Dave + * Support for on/off style "stream" weapons. Real early support for + * target-painting lasers. + * + * 20 3/31/99 8:24p Dave + * Beefed up all kinds of stuff, incluging beam weapons, nebula effects + * and background nebulae. Added per-ship non-dimming pixel colors. + * + * 19 3/29/99 6:17p Dave + * More work on demo system. Got just about everything in except for + * blowing ships up, secondary weapons and player death/warpout. + * + * 18 3/28/99 5:58p Dave + * Added early demo code. Make objects move. Nice and framerate + * independant, but not much else. Don't use yet unless you're me :) + * + * 17 3/09/99 6:24p Dave + * More work on object update revamping. Identified several sources of + * unnecessary bandwidth. + * + * 16 2/21/99 6:01p Dave + * Fixed standalone WSS packets. + * + * 15 2/21/99 1:48p Dave + * Some code for monitoring datarate for multiplayer in detail. + * + * 14 1/21/99 2:06p Dave + * Final checkin for multiplayer testing. + * + * 13 1/19/99 3:57p Andsager + * Round 2 of variables + * + * 12 1/12/99 12:53a Dave + * More work on beam weapons - made collision detection very efficient - + * collide against all object types properly - made 3 movement types + * smooth. Put in test code to check for possible non-darkening pixels on + * object textures. + * + * 11 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * 10 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 9 12/04/98 3:37p Andsager + * Added comment out asteroid launcher + * + * 8 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 7 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 6 10/26/98 9:42a Dave + * Early flak gun support. + * + * 5 10/20/98 1:39p Andsager + * Make so sparks follow animated ship submodels. Modify + * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add + * submodel_num. Add submodel_num to multiplayer hit packet. + * + * 4 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 3 10/09/98 2:57p Dave + * Starting splitting up OS stuff. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 387 6/17/98 11:02a Lawrance + * show what cheat code is in the comment + * + * 386 6/09/98 5:15p Lawrance + * French/German localization + * + * 385 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 384 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 383 5/24/98 1:46p Mike + * Final cheat codes. + * + * 382 5/19/98 2:20p Mike + * Comment out nprintf(). + * + * 381 5/19/98 12:19p Mike + * Cheat codes! + * + * 380 5/19/98 11:11a Lawrance + * Make 'G' only target hostiles + * + * 379 5/18/98 11:00p Mike + * Adding support for cheat system. + * + * 378 5/18/98 12:41a Allender + * fixed subsystem problems on clients (i.e. not reporting properly on + * damage indicator). Fixed ingame join problem with respawns. minor + * comm menu stuff + * + * 377 5/17/98 1:43a Dave + * Eradicated chatbox problems. Remove speed match for observers. Put in + * help screens for PXO. Fix messaging and end mission privelges. Fixed + * team select screen bugs. Misc UI fixes. + * + * 376 5/15/98 8:36p Lawrance + * Add 'target ship that last sent transmission' target key + * + * 375 5/14/98 11:07a Lawrance + * Ensure looped sounds get stopped before stopping all channels + * + * 374 5/12/98 11:59p Dave + * Put in some more functionality for Parallax Online. + * + * 373 5/11/98 5:29p Hoffoss + * Added mouse button mapped to joystick button support. + * + * 372 5/08/98 10:14a Lawrance + * Play sound when auto-targeting gets toggled. + * + * 371 5/04/98 9:25a Allender + * don't allow time compression in multiplayer + * + * 370 4/30/98 4:43p Allender + * trap player obj when changing dual fire status on ship + * + * 369 4/30/98 4:16p Peter + * fixes for critical button functions when a player (client) ship is dead + * or dying + * + * 368 4/27/98 9:03a Dave + * Fixed a multiplayer sequencing bug where paused players who were in the + * options screen got an Assert when unpausing. Removed an optimiized + * build warning in keycontrol. + * + * 367 4/26/98 4:29p Lawrance + * Put back time compression keys... somehow they were trashed by a later + * checkin. + * + * 366 4/25/98 5:36p Mike + * Prevent player warpout if engine < 10%. + * + * $NoKeywords: $ + */ + +#include "pstypes.h" +#include "linklist.h" +#include "key.h" +#include "joy.h" +#include "timer.h" +#include "ship.h" +#include "player.h" +#include "weapon.h" +#include "hud.h" +#include "contexthelp.h" +#include "gamesequence.h" +#include "osapi.h" +#include "missiongoals.h" +#include "hud.h" +#include "hudtarget.h" +#include "optionsmenu.h" +#include "freespace.h" +#include "controlsconfig.h" +#include "mouse.h" +#include "hudets.h" +#include "multi.h" +#include "multiutil.h" +#include "sound.h" +#include "gamesnd.h" +#include "bmpman.h" +#include "rbaudio.h" +#include "hudsquadmsg.h" +#include "eventmusic.h" +#include "animplay.h" +#include "freespace.h" +#include "cmeasure.h" +#include "missionhotkey.h" +#include "afterburner.h" +#include "missionparse.h" +#include "hudescort.h" +#include "hudshield.h" +#include "multiutil.h" +#include "multimsgs.h" +#include "keycontrol.h" +#include "shiphit.h" +#include "shipfx.h" +#include "hud.h" +#include "hudobserver.h" +#include "missionlog.h" +#include "hudtargetbox.h" +#include "popup.h" +#include "objcollide.h" +#include "hudconfig.h" +#include "missioncampaign.h" +#include "rtvoice.h" +#include "multi_respawn.h" +#include "multi_pmsg.h" +#include "crypt.h" +#include "ui.h" +#include "multi_pause.h" +#include "multi_observer.h" +#include "multi_endgame.h" +#include "beam.h" +#include "neblightning.h" +#include "supernova.h" +#include "missionmessage.h" +#include "mainhallmenu.h" +#include "aigoals.h" + +// -------------------------------------------------------------- +// Global to file +// -------------------------------------------------------------- + +// -------------------------------------------------------------- +// Externals +// -------------------------------------------------------------- +typedef struct asteroid_field { + vector min_bound; // Minimum range of field. + vector max_bound; // Maximum range of field. + vector vel; // Average asteroid moves at this velocity. + float speed; // Average speed of field + int num_initial_asteroids; // Number of asteroids at creation. +} asteroid_field; + +#define CHEAT_BUFFER_LEN 20 +#define CHEATSPOT (CHEAT_BUFFER_LEN - 1) + +char CheatBuffer[CHEAT_BUFFER_LEN+1]; + +#ifdef FS2_DEMO + char *Cheat_code_demo = NOX("33BE^(8]C01(:=BHt"); +#else + char *Cheat_code = NOX("33BE^(8]C01(:=BHt"); // www.freespace2.com + char *Cheat_code_fish = NOX("bDc9y+$;#AIDRoouM"); // vasudanswuvfishes + char *Cheat_code_headz = NOX("!;:::@>F7L?@@2:@A"); // humanheadsinside. + char *Cheat_code_tooled = NOX("sipp-^rM@L!U^usjX"); // tooledworkedowned + char *Cheat_code_pirate = NOX("MAP4YP[4=-2uC(yJ^"); // arrrrwalktheplank + char *Cheat_code_skip = NOX("7!ICkSI\"(8n3JesBP"); // skipmemymissionyo +#endif + // 666)6=N79+Z45=BE0e +int Tool_enabled = 0; + + /* +#else + // list of the cheat codes + //#ifdef INTERPLAYQA + // "DavidPerry" NOX("0!XZQ*K.pu"); + // NOX("&BvWJe=a?$VP*=@2W,2Y"); // Super-secret 20 character string! + //NOX("STs`nHqW\\lv#KD_aCSWN"); // solveditonceandforall (note double \\ as string contains \. + //XSTR:OFF + char *Cheat_code_in_game = NOX("///FES)P\\+_(Qs#+h-8o"); // arrwalktheplankmatey + //XSTR:ON +#endif + */ + +int All_movies_enabled = 0; + +//int Debug_allowed = 0; + +//#endif + +extern int AI_watch_object; +extern int Countermeasures_enabled; + +extern fix Game_time_compression; + +extern float do_subobj_hit_stuff(object *ship_obj, object *other_obj, vector *hitpos, float damage); + +extern void mission_goal_mark_all_true( int type ); + +int Normal_key_set[] = { + TARGET_NEXT, + TARGET_PREV, + TARGET_NEXT_CLOSEST_HOSTILE, + TARGET_PREV_CLOSEST_HOSTILE, + TARGET_NEXT_CLOSEST_FRIENDLY, + TARGET_PREV_CLOSEST_FRIENDLY, + TARGET_TARGETS_TARGET, + TARGET_SHIP_IN_RETICLE, + TARGET_LAST_TRANMISSION_SENDER, + TARGET_CLOSEST_SHIP_ATTACKING_TARGET, + TARGET_CLOSEST_SHIP_ATTACKING_SELF, + STOP_TARGETING_SHIP, + TOGGLE_AUTO_TARGETING, + TARGET_SUBOBJECT_IN_RETICLE, + TARGET_PREV_SUBOBJECT, + TARGET_NEXT_SUBOBJECT, + STOP_TARGETING_SUBSYSTEM, + + TARGET_NEXT_UNINSPECTED_CARGO, + TARGET_PREV_UNINSPECTED_CARGO, + TARGET_NEWEST_SHIP, + TARGET_NEXT_LIVE_TURRET, + TARGET_PREV_LIVE_TURRET, + TARGET_NEXT_BOMB, + TARGET_PREV_BOMB, + + ATTACK_MESSAGE, + DISARM_MESSAGE, + DISABLE_MESSAGE, + ATTACK_SUBSYSTEM_MESSAGE, + CAPTURE_MESSAGE, + ENGAGE_MESSAGE, + FORM_MESSAGE, + PROTECT_MESSAGE, + COVER_MESSAGE, + WARP_MESSAGE, + REARM_MESSAGE, + IGNORE_MESSAGE, + SQUADMSG_MENU, + + VIEW_CHASE, + VIEW_OTHER_SHIP, + + SHOW_GOALS, + END_MISSION, + + ADD_REMOVE_ESCORT, + ESCORT_CLEAR, + TARGET_NEXT_ESCORT_SHIP, + + XFER_SHIELD, + XFER_LASER, + INCREASE_SHIELD, + INCREASE_WEAPON, + INCREASE_ENGINE, + DECREASE_SHIELD, + DECREASE_WEAPON, + DECREASE_ENGINE, + ETS_EQUALIZE, + SHIELD_EQUALIZE, + SHIELD_XFER_TOP, + SHIELD_XFER_BOTTOM, + SHIELD_XFER_RIGHT, + SHIELD_XFER_LEFT, + + CYCLE_NEXT_PRIMARY, + CYCLE_PREV_PRIMARY, + CYCLE_SECONDARY, + CYCLE_NUM_MISSLES, + RADAR_RANGE_CYCLE, + + MATCH_TARGET_SPEED, + TOGGLE_AUTO_MATCH_TARGET_SPEED, + + VIEW_EXTERNAL, + VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK, + LAUNCH_COUNTERMEASURE, + ONE_THIRD_THROTTLE, + TWO_THIRDS_THROTTLE, + PLUS_5_PERCENT_THROTTLE, + MINUS_5_PERCENT_THROTTLE, + ZERO_THROTTLE, + MAX_THROTTLE, + + TARGET_CLOSEST_REPAIR_SHIP, + + MULTI_MESSAGE_ALL, + MULTI_MESSAGE_FRIENDLY, + MULTI_MESSAGE_HOSTILE, + MULTI_MESSAGE_TARGET, + MULTI_OBSERVER_ZOOM_TO, + + TIME_SPEED_UP, + TIME_SLOW_DOWN, + + TOGGLE_HUD_CONTRAST, + + MULTI_TOGGLE_NETINFO, + MULTI_SELF_DESTRUCT +}; + +int Dead_key_set[] = { + TARGET_NEXT, + TARGET_PREV, + TARGET_NEXT_CLOSEST_HOSTILE, + TARGET_PREV_CLOSEST_HOSTILE, + TARGET_NEXT_CLOSEST_FRIENDLY, + TARGET_PREV_CLOSEST_FRIENDLY, + TARGET_TARGETS_TARGET, + TARGET_CLOSEST_SHIP_ATTACKING_TARGET, + STOP_TARGETING_SHIP, + TOGGLE_AUTO_TARGETING, + TARGET_SUBOBJECT_IN_RETICLE, + TARGET_PREV_SUBOBJECT, + TARGET_NEXT_SUBOBJECT, + STOP_TARGETING_SUBSYSTEM, + TARGET_NEWEST_SHIP, + TARGET_NEXT_LIVE_TURRET, + TARGET_PREV_LIVE_TURRET, + TARGET_NEXT_BOMB, + TARGET_PREV_BOMB, + + VIEW_CHASE, + VIEW_OTHER_SHIP, + + SHOW_GOALS, + + ADD_REMOVE_ESCORT, + ESCORT_CLEAR, + TARGET_NEXT_ESCORT_SHIP, + TARGET_CLOSEST_REPAIR_SHIP, + + MULTI_MESSAGE_ALL, + MULTI_MESSAGE_FRIENDLY, + MULTI_MESSAGE_HOSTILE, + MULTI_MESSAGE_TARGET, + MULTI_OBSERVER_ZOOM_TO, + + TIME_SPEED_UP, + TIME_SLOW_DOWN +}; + +int Critical_key_set[] = { + CYCLE_SECONDARY, + CYCLE_NUM_MISSLES, + INCREASE_WEAPON, + DECREASE_WEAPON, + INCREASE_SHIELD, + DECREASE_SHIELD, + INCREASE_ENGINE, + DECREASE_ENGINE, + ETS_EQUALIZE, + SHIELD_EQUALIZE, + SHIELD_XFER_TOP, + SHIELD_XFER_BOTTOM, + SHIELD_XFER_LEFT, + SHIELD_XFER_RIGHT, + XFER_SHIELD, + XFER_LASER, +}; + +int Non_critical_key_set[] = { + CYCLE_NEXT_PRIMARY, + CYCLE_PREV_PRIMARY, + MATCH_TARGET_SPEED, + TOGGLE_AUTO_MATCH_TARGET_SPEED, + TARGET_NEXT, + TARGET_PREV, + TARGET_NEXT_CLOSEST_HOSTILE, + TARGET_PREV_CLOSEST_HOSTILE, + TOGGLE_AUTO_TARGETING, + TARGET_NEXT_CLOSEST_FRIENDLY, + TARGET_PREV_CLOSEST_FRIENDLY, + TARGET_SHIP_IN_RETICLE, + TARGET_LAST_TRANMISSION_SENDER, + TARGET_CLOSEST_REPAIR_SHIP, + TARGET_CLOSEST_SHIP_ATTACKING_TARGET, + STOP_TARGETING_SHIP, + TARGET_CLOSEST_SHIP_ATTACKING_SELF, + TARGET_TARGETS_TARGET, + TARGET_SUBOBJECT_IN_RETICLE, + TARGET_PREV_SUBOBJECT, + TARGET_NEXT_SUBOBJECT, + STOP_TARGETING_SUBSYSTEM, + TARGET_NEXT_BOMB, + TARGET_PREV_BOMB, + TARGET_NEXT_UNINSPECTED_CARGO, + TARGET_PREV_UNINSPECTED_CARGO, + TARGET_NEWEST_SHIP, + TARGET_NEXT_LIVE_TURRET, + TARGET_PREV_LIVE_TURRET, + ATTACK_MESSAGE, + DISARM_MESSAGE, + DISABLE_MESSAGE, + ATTACK_SUBSYSTEM_MESSAGE, + CAPTURE_MESSAGE, + ENGAGE_MESSAGE, + FORM_MESSAGE, + PROTECT_MESSAGE, + COVER_MESSAGE, + WARP_MESSAGE, + IGNORE_MESSAGE, + REARM_MESSAGE, + VIEW_CHASE, + VIEW_EXTERNAL, + VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK, + VIEW_OTHER_SHIP, + RADAR_RANGE_CYCLE, + SQUADMSG_MENU, + SHOW_GOALS, + END_MISSION, + ADD_REMOVE_ESCORT, + ESCORT_CLEAR, + TARGET_NEXT_ESCORT_SHIP, + MULTI_MESSAGE_ALL, + MULTI_MESSAGE_FRIENDLY, + MULTI_MESSAGE_HOSTILE, + MULTI_MESSAGE_TARGET, + MULTI_OBSERVER_ZOOM_TO, + TOGGLE_HUD_CONTRAST, + + MULTI_TOGGLE_NETINFO, + MULTI_SELF_DESTRUCT +}; + + +// set sizes of the key sets automatically +int Normal_key_set_size = sizeof(Normal_key_set) / sizeof(int); +int Dead_key_set_size = sizeof(Dead_key_set) / sizeof(int); +int Critical_key_set_size = sizeof(Critical_key_set) / sizeof(int); +int Non_critical_key_set_size = sizeof(Non_critical_key_set) / sizeof(int); + +// -------------------------------------------------------------- +// routine to process keys used only for debugging +// -------------------------------------------------------------- +//#ifndef NDEBUG + +void debug_cycle_player_ship(int delta) +{ + if ( Player_obj == NULL ) + return; + + int si_index = Ships[Player_obj->instance].ship_info_index; + int sanity = 0; + ship_info *sip; + while ( TRUE ) { + si_index += delta; + if ( si_index > MAX_SHIP_TYPES ){ + si_index = 0; + } + if ( si_index < 0 ){ + si_index = MAX_SHIP_TYPES - 1; + } + sip = &Ship_info[si_index]; + if ( sip->flags & SIF_PLAYER_SHIP ){ + break; + } + + // just in case + sanity++; + if ( sanity > MAX_SHIP_TYPES ){ + break; + } + } + + change_ship_type(Player_obj->instance, si_index); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Player ship changed to %s", 0), Ship_info[si_index].name); +} + +// cycle targeted ship to next ship in that species +void debug_cycle_targeted_ship(int delta) +{ + object *objp; + ship_info *sip; + int si_index, species; + char name[NAME_LENGTH]; + + if ( Player_ai->target_objnum == -1 ) + return; + + objp = &Objects[Player_ai->target_objnum]; + if ( objp->type != OBJ_SHIP ) + return; + + si_index = Ships[objp->instance].ship_info_index; + Assert(si_index != -1 ); + species = Ship_info[si_index].species; + + int sanity = 0; + + while ( TRUE ) { + si_index += delta; + if ( si_index > MAX_SHIP_TYPES ) + si_index = 0; + if ( si_index < 0 ) + si_index = MAX_SHIP_TYPES-1; + + + sip = &Ship_info[si_index]; + + // if it has test in the name, jump over it + strcpy(name, sip->name); + _strlwr(name); + if ( strstr(name,NOX("test")) != NULL ) + continue; + + if ( sip->species == species && (sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_TRANSPORT) ) ) + break; + + // just in case + sanity++; + if ( sanity > MAX_SHIP_TYPES ) + break; + } + + change_ship_type(objp->instance, si_index); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Changed player target to %s", 1), Ship_info[si_index].name); +} + +void debug_max_secondary_weapons(object *objp) +{ + int index; + ship *shipp = &Ships[objp->instance]; + ship_info *sip = &Ship_info[shipp->ship_info_index]; + + for ( index = 0; index < MAX_SECONDARY_BANKS; index++ ) { + shipp->weapons.secondary_bank_ammo[index] = sip->secondary_bank_ammo_capacity[index]; + } +} + +void debug_change_song(int delta) +{ + char buf[256]; + if ( event_music_next_soundtrack(delta) != -1 ) { + event_music_get_soundtrack_name(buf); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Soundtrack changed to: %s", 2), buf); + + } else { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Event music is not playing", 3)); + } +} + +//extern void set_global_ignore_object(int objnum); + +extern void hud_target_asteroid(); +extern int Framerate_delay; + +extern void snd_stop_any_sound(); + +void process_debug_keys(int k) +{ +#ifdef INTERPLAYQA + if ( !Debug_allowed ) + return; +#endif + + // if ( (k & KEY_DEBUGGED) && (Game_mode & GM_RECORDING_DEMO) ) + // return; + + switch (k) { + case KEY_DEBUGGED + KEY_H: + hud_target_toggle_hidden_from_sensors(); + break; + + case KEY_DEBUGGED + KEY_F: + /* + int i; + for (i=0; iflags & OF_COLLIDES){ + obj_set_flags(Player_obj, Player_obj->flags & ~(OF_COLLIDES)); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Player no longer collides"); + } else { + obj_set_flags(Player_obj, Player_obj->flags | OF_COLLIDES); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Player collides"); + } + break; + + case KEY_DEBUGGED + KEY_SHIFTED + KEY_C: + case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_C: + Countermeasures_enabled = !Countermeasures_enabled; + HUD_printf(XSTR( "Countermeasure firing: %s", 6), Countermeasures_enabled ? XSTR( "ENABLED", 7) : XSTR( "DISABLED", 8)); + break; + + case KEY_DEBUGGED + KEY_E: + gameseq_post_event(GS_EVENT_EVENT_DEBUG); + break; + + case KEY_DEBUGGED + KEY_COMMA: + if ( Game_time_compression > (F1_0/4) ){ // can't compress below 0.25 + Game_time_compression /= 2; + } + break; + case KEY_DEBUGGED + KEY_PERIOD: + if ( Game_time_compression < (F1_0*8) ){ + Game_time_compression *= 2; + } + break; + + // Kill! the currently targeted ship. + case KEY_DEBUGGED + KEY_K: + case KEY_DEBUGGED1 + KEY_K: + if (Player_ai->target_objnum != -1) { + object *objp = &Objects[Player_ai->target_objnum]; + + switch (objp->type) { + case OBJ_SHIP: + ship_apply_local_damage( objp, Player_obj, &objp->pos, 100000.0f, MISS_SHIELDS, CREATE_SPARKS); + ship_apply_local_damage( objp, Player_obj, &objp->pos, 1.0f, MISS_SHIELDS, CREATE_SPARKS); + break; + case OBJ_WEAPON: + Weapons[objp->instance].lifeleft = 0.01f; + break; + } + } + + break; + + // play the next mission message + case KEY_DEBUGGED + KEY_V: + extern int Message_debug_index; + extern int Num_messages_playing; + // stop any other messages + if(Num_messages_playing){ + message_kill_all(1); + } + + // next message + if(Message_debug_index >= Num_messages - 1){ + Message_debug_index = Num_builtin_messages; + } else { + Message_debug_index++; + } + + // play the message + message_send_unique_to_player( Messages[Message_debug_index].name, Message_waves[Messages[Message_debug_index].wave_info.index].name, MESSAGE_SOURCE_SPECIAL, MESSAGE_PRIORITY_HIGH, 0, 0 ); + if (Messages[Message_debug_index].avi_info.index == -1) { + HUD_printf("No anim set for message \"%s\"; None will play!", Messages[Message_debug_index].name); + } + break; + + // play the previous mission message + case KEY_DEBUGGED + KEY_SHIFTED + KEY_V: + extern int Message_debug_index; + extern int Num_messages_playing; + // stop any other messages + if(Num_messages_playing){ + message_kill_all(1); + } + + // go maybe go down one + if(Message_debug_index == Num_builtin_messages - 1){ + Message_debug_index = Num_builtin_messages; + } else if(Message_debug_index > Num_builtin_messages){ + Message_debug_index--; + } + + // play the message + message_send_unique_to_player( Messages[Message_debug_index].name, Message_waves[Messages[Message_debug_index].wave_info.index].name, MESSAGE_SOURCE_SPECIAL, MESSAGE_PRIORITY_HIGH, 0, 0 ); + if (Messages[Message_debug_index].avi_info.index == -1) { + HUD_printf("No avi associated with this message; None will play!"); + } + break; + + // reset to the beginning of mission messages + case KEY_DEBUGGED + KEY_ALTED + KEY_V: + extern int Message_debug_index; + Message_debug_index = Num_builtin_messages - 1; + HUD_printf("Resetting to first mission message"); + break; + + // Kill! the currently targeted ship. + case KEY_DEBUGGED + KEY_ALTED + KEY_SHIFTED + KEY_K: + case KEY_DEBUGGED1 + KEY_ALTED + KEY_SHIFTED + KEY_K: + if (Player_ai->target_objnum != -1) { + object *objp = &Objects[Player_ai->target_objnum]; + + if (objp->type == OBJ_SHIP) { + ship_apply_local_damage( objp, Player_obj, &objp->pos, Ship_info[Ships[objp->instance].ship_info_index].initial_hull_strength * 0.1f + 10.0f, MISS_SHIELDS, CREATE_SPARKS); + } + } + break; + + // Kill the currently targeted subsystem. + case KEY_DEBUGGED + KEY_SHIFTED + KEY_K: + case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_K: + if ((Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL)) { + object *objp = &Objects[Player_ai->target_objnum]; + if ( objp->type == OBJ_SHIP ) { + ship *sp = &Ships[objp->instance]; + vector g_subobj_pos; + + get_subsystem_world_pos(objp, Player_ai->targeted_subsys, &g_subobj_pos); + + do_subobj_hit_stuff(objp, Player_obj, &g_subobj_pos, (float) -Player_ai->targeted_subsys->system_info->type); //100.0f); + + if ( sp->subsys_info[SUBSYSTEM_ENGINE].current_hits <= 0.0f ) { + mission_log_add_entry(LOG_SHIP_DISABLED, sp->ship_name, NULL ); + sp->flags |= SF_DISABLED; // add the disabled flag + } + + if ( sp->subsys_info[SUBSYSTEM_TURRET].current_hits <= 0.0f ) { + mission_log_add_entry(LOG_SHIP_DISARMED, sp->ship_name, NULL ); + // sp->flags |= SF_DISARMED; // add the disarmed flag + } + } + } + break; + + case KEY_DEBUGGED + KEY_ALTED + KEY_K: + case KEY_DEBUGGED1 + KEY_ALTED + KEY_K: + { + float shield, integrity; + vector pos, randvec; + + vm_vec_rand_vec_quick(&randvec); + vm_vec_scale_add(&pos, &Player_obj->pos, &randvec, Player_obj->radius); + ship_apply_local_damage(Player_obj, Player_obj, &pos, 25.0f, MISS_SHIELDS, CREATE_SPARKS); + hud_get_target_strength(Player_obj, &shield, &integrity); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "You knocked yourself down to %7.3f percent hull.\n", 9), 100.0f * integrity); + break; + } + + // Whack down the player's shield and hull by a little more than 50% + // Select next object to be viewed by AI. + case KEY_DEBUGGED + KEY_I: + case KEY_DEBUGGED1 + KEY_I: + Player_obj->flags ^= OF_INVULNERABLE; + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "You are %s", 10), Player_obj->flags & OF_INVULNERABLE ? XSTR( "now INVULNERABLE!", 11) : XSTR( "no longer invulnerable...", 12)); + break; + + case KEY_DEBUGGED + KEY_SHIFTED + KEY_I: + case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_I: + if (Player_ai->target_objnum != -1) { + object *objp = &Objects[Player_ai->target_objnum]; + + objp->flags ^= OF_INVULNERABLE; + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Player's target [%s] is %s", 13), Ships[objp->instance].ship_name, objp->flags & OF_INVULNERABLE ? XSTR( "now INVULNERABLE!", 11) : XSTR( "no longer invulnerable...", 12)); + } + break; +/* + case KEY_DEBUGGED + KEY_ALTED + KEY_I: + if (Player_ai->target_objnum != -1) + set_global_ignore_object(Player_ai->target_objnum); + break; +*/ + + case KEY_DEBUGGED + KEY_N: + AI_watch_object++; + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Spewing debug info about object #%d", 14), AI_watch_object); + break; + + case KEY_DEBUGGED + KEY_O: + // case KEY_DEBUGGED1 + KEY_O: + toggle_player_object(); + break; + + case KEY_DEBUGGED + KEY_SHIFTED + KEY_O: + extern int Debug_octant; + if(Debug_octant == 7){ + Debug_octant = -1; + } else { + Debug_octant++; + } + nprintf(("General", "Debug_octant == %d\n", Debug_octant)); + break; + + case KEY_DEBUGGED + KEY_P: + supernova_start(20); + break; + + case KEY_DEBUGGED + KEY_W: + case KEY_DEBUGGED1 + KEY_W: + case KEY_DEBUGGED + KEY_SHIFTED + KEY_W: + case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_W: + // temp code for testing purposes, toggles weapon energy cheat + Weapon_energy_cheat = !Weapon_energy_cheat; + if (Weapon_energy_cheat) { + if (k & KEY_SHIFTED) + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Weapon energy and missile count will always be at full ALL SHIPS!", 15)); + else + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Weapon energy and missile count will always be at full for player", 16)); + + debug_max_secondary_weapons(Player_obj); + if (k & KEY_SHIFTED) { + object *objp; + + for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) + if (objp->type == OBJ_SHIP) + debug_max_secondary_weapons(objp); + } + + } else + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Normal weapon energy system / missile count restored", 17)); + + break; + + case KEY_DEBUGGED + KEY_G: + // case KEY_DEBUGGED1 + KEY_G: + mission_goal_mark_all_true( PRIMARY_GOAL ); + break; + + case KEY_DEBUGGED + KEY_G + KEY_SHIFTED: + // case KEY_DEBUGGED1 + KEY_G + KEY_SHIFTED: + mission_goal_mark_all_true( SECONDARY_GOAL ); + break; + + case KEY_DEBUGGED + KEY_G + KEY_ALTED: + // case KEY_DEBUGGED1 + KEY_G + KEY_ALTED: + mission_goal_mark_all_true( BONUS_GOAL ); + break; + + case KEY_DEBUGGED + KEY_9: { + case KEY_DEBUGGED1 + KEY_9: + ship* shipp; + + shipp = &Ships[Player_obj->instance]; + shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]++; + if ( shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank] >= Num_weapon_types ) + shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank] = First_secondary_index; + + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Secondary Weapon forced to %s", 18), Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name); + break; + } + + /* + case KEY_DEBUGGED + KEY_SHIFTED + KEY_9: { + case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_9: + ship* shipp; + + shipp = &Ships[Player_obj->instance]; + shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]--; + if ( shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank] < 0) + shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank] = Num_weapon_types - 1; + + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Secondary Weapon forced to %s", 18), Weapon_info[shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank]].name); + break; + } + */ + +#ifndef FS2_DEMO + case KEY_DEBUGGED + KEY_U: { + case KEY_DEBUGGED1 + KEY_U: + // launch asteroid + extern asteroid_field Asteroid_field; + object *asteroid_create(asteroid_field *asfieldp, int asteroid_type, int subtype); + object *objp = asteroid_create(&Asteroid_field, 0, 0); + vector vel; + vm_vec_copy_scale(&vel, &Player_obj->orient.fvec, 50.0f); + objp->phys_info.vel = vel; + objp->phys_info.desired_vel = vel; + objp->pos = Player_obj->pos; + //mission_goal_mark_all_true( PRIMARY_GOAL ); + break; + } +#endif + + case KEY_DEBUGGED + KEY_0: { + case KEY_DEBUGGED1 + KEY_0: + ship* shipp; + + shipp = &Ships[Player_obj->instance]; + shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]++; + if ( shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank] >= First_secondary_index ) + shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank] = 0; + + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Primary Weapon forced to %s", 19), Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name); + break; + } + + case KEY_DEBUGGED + KEY_SHIFTED + KEY_0: { + case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_0: + ship* shipp; + + shipp = &Ships[Player_obj->instance]; + shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]--; + if ( shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank] < 0) + shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank] = First_secondary_index-1 ; + + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Primary Weapon forced to %s", 19), Weapon_info[shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank]].name); + break; + } + + case KEY_DEBUGGED + KEY_J: { + int new_pattern = event_music_return_current_pattern(); + + new_pattern++; + if ( new_pattern >= MAX_PATTERNS ) + new_pattern = 0; + + event_music_change_pattern(new_pattern); + break; + } + + case KEY_DEBUGGED + KEY_M: { + if ( Event_music_enabled ) { + event_music_disable(); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Event music disabled", 20)); + + } else { + event_music_enable(); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Event music enabled", 21)); + } + + break; + } + + case KEY_DEBUGGED + KEY_R: { + // case KEY_DEBUGGED1 + KEY_R: + if (Player_ai->target_objnum != -1) + ai_issue_rearm_request(&Objects[Player_ai->target_objnum]); + else + ai_issue_rearm_request(Player_obj); + + break; + } + + case KEY_DEBUGGED + KEY_SHIFTED + KEY_UP: + Game_detail_level++; + HUD_printf( XSTR( "Detail level set to %+d\n", 22), Game_detail_level ); + break; + + case KEY_DEBUGGED + KEY_SHIFTED + KEY_DOWN: + Game_detail_level--; + HUD_printf( XSTR( "Detail level set to %+d\n", 22), Game_detail_level ); + break; + +#ifndef NDEBUG + case KEY_DEBUGGED + KEY_SHIFTED + KEY_T: { + extern int Test_begin; + + if ( Test_begin == 1 ) + break; + + Test_begin = 1; + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Frame Rate test started", 23)); + + break; + } +#endif + case KEY_DEBUGGED + KEY_D: + extern int OO_update_index; + + if(MULTIPLAYER_MASTER){ + do { + OO_update_index++; + } while((OO_update_index < (MAX_PLAYERS-1)) && !MULTI_CONNECTED(Net_players[OO_update_index])); + if(OO_update_index >= MAX_PLAYERS-1){ + OO_update_index = -1; + } + } else { + if(OO_update_index < 0){ + OO_update_index = MY_NET_PLAYER_NUM; + } else { + OO_update_index = -1; + } + } + break; + + // change player ship to next flyable type + case KEY_DEBUGGED + KEY_RIGHT: + debug_cycle_player_ship(1); + break; + + // change player ship to previous flyable ship + case KEY_DEBUGGED + KEY_LEFT: + debug_cycle_player_ship(-1); + break; + + // cycle target to ship + case KEY_DEBUGGED + KEY_SHIFTED + KEY_RIGHT: + debug_cycle_targeted_ship(1); + break; + + // cycle target to previous ship + case KEY_DEBUGGED + KEY_SHIFTED + KEY_LEFT: + debug_cycle_targeted_ship(-1); + break; + + // change species of the targeted ship + case KEY_DEBUGGED + KEY_S: { + if ( Player_ai->target_objnum < 0 ) + break; + + object *objp; + ship_info *sip; + + objp = &Objects[Player_ai->target_objnum]; + if ( objp->type != OBJ_SHIP ) + return; + + sip = &Ship_info[Ships[objp->instance].ship_info_index]; + sip->species++; + if ( sip->species > SPECIES_SHIVAN ) + sip->species = 0; + + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Species of target changed to: %s", 24), Species_names[sip->species]); + break; + } + + case KEY_DEBUGGED + KEY_SHIFTED + KEY_S: + game_increase_skill_level(); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Skill level set to %s.", 25), Skill_level_names(Game_skill_level)); + break; + + // kill all missiles + case KEY_DEBUGGED + KEY_SHIFTED + KEY_1: + beam_test(1); + break; + case KEY_DEBUGGED + KEY_SHIFTED + KEY_2: + beam_test(2); + break; + case KEY_DEBUGGED + KEY_SHIFTED + KEY_3: + beam_test(3); + break; + case KEY_DEBUGGED + KEY_SHIFTED + KEY_4: + beam_test(4); + break; + case KEY_DEBUGGED + KEY_SHIFTED + KEY_5: + beam_test(5); + break; + case KEY_DEBUGGED + KEY_SHIFTED + KEY_6: + beam_test(6); + break; + case KEY_DEBUGGED + KEY_SHIFTED + KEY_7: + beam_test(7); + break; + case KEY_DEBUGGED + KEY_SHIFTED + KEY_8: + beam_test(8); + break; + case KEY_DEBUGGED + KEY_SHIFTED + KEY_9: + beam_test(9); + break; + + case KEY_DEBUGGED + KEY_CTRLED + KEY_1: + beam_test_new(1); + break; + case KEY_DEBUGGED + KEY_CTRLED + KEY_2: + beam_test_new(2); + break; + case KEY_DEBUGGED + KEY_CTRLED + KEY_3: + beam_test_new(3); + break; + + case KEY_DEBUGGED + KEY_T: { + char buf[256]; + event_music_get_info(buf); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, buf); + break; + } + + case KEY_DEBUGGED + KEY_UP: + debug_change_song(1); + break; + + case KEY_DEBUGGED + KEY_DOWN: + debug_change_song(-1); + break; + + case KEY_PADMINUS: { + int init_flag = 0; + + if ( keyd_pressed[KEY_1] ) { + init_flag = 1; + HUD_color_red -= 4; + } + + if ( keyd_pressed[KEY_2] ) { + init_flag = 1; + HUD_color_green -= 4; + } + + if ( keyd_pressed[KEY_3] ) { + init_flag = 1; + HUD_color_blue -= 4; + } + + if (init_flag) + HUD_init_colors(); + + break; + } + + case KEY_DEBUGGED + KEY_Y: + /* + // blast a debug lightning bolt in front of the player + vector start, strike; + + vm_vec_scale_add(&start, &Player_obj->pos, &Player_obj->orient.fvec, 300.0f); + vm_vec_scale_add2(&start, &Player_obj->orient.rvec, -300.0f); + vm_vec_scale_add(&strike, &start, &Player_obj->orient.rvec, 600.0f); + nebl_bolt(DEBUG_BOLT, &start, &strike); + */ + extern int tst; + tst = 2; + break; + + case KEY_PADPLUS: { + int init_flag = 0; + + if ( keyd_pressed[KEY_1] ) { + init_flag = 1; + HUD_color_red += 4; + } + + if ( keyd_pressed[KEY_2] ) { + init_flag = 1; + HUD_color_green += 4; + } + + if ( keyd_pressed[KEY_3] ) { + init_flag = 1; + HUD_color_blue += 4; + } + + if (init_flag) + HUD_init_colors(); + + break; + } + } // end switch + +} +//#endif + +void ppsk_hotkeys(int k) +{ + +#ifndef FS2_DEMO + + // use k to check for keys that can have Shift,Ctrl,Alt,Del status + int hotkey_set; + +#ifndef NDEBUG + k &= ~KEY_DEBUGGED; // since hitting F11 will set this bit +#endif + + switch (k) { + case KEY_F5: + case KEY_F6: + case KEY_F7: + case KEY_F8: + case KEY_F9: + case KEY_F10: + case KEY_F11: + case KEY_F12: + hotkey_set = mission_hotkey_get_set_num(k); + if ( !(Players[Player_num].flags & PLAYER_FLAGS_MSG_MODE) ) + hud_target_hotkey_select( hotkey_set ); + else + hud_squadmsg_hotkey_select( hotkey_set ); + + break; + + case KEY_F5 + KEY_SHIFTED: + case KEY_F6 + KEY_SHIFTED: + case KEY_F7 + KEY_SHIFTED: + case KEY_F8 + KEY_SHIFTED: + case KEY_F9 + KEY_SHIFTED: + case KEY_F10 + KEY_SHIFTED: + case KEY_F11 + KEY_SHIFTED: + case KEY_F12 + KEY_SHIFTED: + hotkey_set = mission_hotkey_get_set_num(k&(~KEY_SHIFTED)); + mprintf(("Adding to set %d\n", hotkey_set+1)); + if ( Player_ai->target_objnum == -1) + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No target to add/remove from set %d.", 26), hotkey_set+1); + else { + hud_target_hotkey_add_remove( hotkey_set, &Objects[Player_ai->target_objnum], HOTKEY_USER_ADDED); + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "%s added to set %d. (F%d)", 27), Ships[Objects[Player_ai->target_objnum].instance].ship_name, hotkey_set, 4+hotkey_set+1); + } + + break; + + case KEY_F5 + KEY_SHIFTED + KEY_ALTED: + case KEY_F6 + KEY_SHIFTED + KEY_ALTED: + case KEY_F7 + KEY_SHIFTED + KEY_ALTED: + case KEY_F8 + KEY_SHIFTED + KEY_ALTED: + case KEY_F9 + KEY_SHIFTED + KEY_ALTED: + case KEY_F10 + KEY_SHIFTED + KEY_ALTED: + case KEY_F11 + KEY_SHIFTED + KEY_ALTED: + case KEY_F12 + KEY_SHIFTED + KEY_ALTED: + hotkey_set = mission_hotkey_get_set_num(k & ~KEY_SHIFTED+KEY_ALTED); + hud_target_hotkey_clear( hotkey_set ); + break; + + case KEY_SHIFTED + KEY_MINUS: + if ( HUD_color_alpha > HUD_COLOR_ALPHA_USER_MIN ) { + HUD_color_alpha--; + HUD_init_colors(); + } + break; +/* case KEY_SHIFTED + KEY_U: + { + object *debris_create(object *source_obj, int model_num, int submodel_num, vector *pos, vector *exp_center, int hull_flag, float exp_force); + + object *temp = debris_create(Player_obj, Ships[0].modelnum, model_get(Ships[0].modelnum)->debris_objects[0], &Player_obj->pos, &Player_obj->pos, 1, 1.0f); + if (temp) { + temp->hull_strength = 5000.0f; + int objnum = temp - Objects; + vm_vec_copy_scale(&Objects[objnum].phys_info.vel, &Player_obj->orient.fvec, 30.0f); + } + } + break; +*/ + + case KEY_SHIFTED + KEY_EQUAL: + if ( HUD_color_alpha < HUD_COLOR_ALPHA_USER_MAX ) { + HUD_color_alpha++; + HUD_init_colors(); + } + break; + } // end switch + +#endif + +} + +// check keypress 'key' against a set of valid controls and mark the match in the +// player's button info bitfield. Also checks joystick controls in the set. +// +// key = scancode (plus modifiers). +// count = total size of the list +// list = list of Control_config struct action indices to check for +void process_set_of_keys(int key, int count, int *list) +{ + int i; + + for (i=0; ibi, list[i]); +} + +// routine to process keys used for player ship stuff (*not* ship movement). +void process_player_ship_keys(int k) +{ + int masked_k; + + masked_k = k & ~KEY_CTRLED; // take out CTRL modifier only + + // moved this line to beginning of function since hotkeys now encompass + // F5 - F12. We can return after using F11 as a hotkey. + ppsk_hotkeys(masked_k); + if (keyd_pressed[KEY_DEBUG_KEY]){ + return; + } + + // if we're in supernova mode. do nothing + if(Player->control_mode == PCM_SUPERNOVA){ + return; + } + + // pass the key to the squadmate messaging code. If the messaging code took the key, then return + // from here immediately since we don't want to do further key processing. + if ( hud_squadmsg_read_key(k) ) + return; + + if ( Player->control_mode == PCM_NORMAL ) { + // The following things are not legal to do while dead. + if ( !(Game_mode & GM_DEAD) ) { + process_set_of_keys(masked_k, Normal_key_set_size, Normal_key_set); + } else { + process_set_of_keys(masked_k, Dead_key_set_size, Dead_key_set); + } + } else { + + } +} + +// Handler for when player hits 'ESC' during the game +void game_do_end_mission_popup() +{ + int pf_flags, choice; +// char savegame_filename[_MAX_FNAME]; + + // do the multiplayer version of this + if(Game_mode & GM_MULTIPLAYER){ + multi_quit_game(PROMPT_ALL); + } else { + + // single player version.... + // do housekeeping things. + game_stop_time(); + game_stop_looped_sounds(); + snd_stop_all(); + + pf_flags = PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON; + choice = popup(pf_flags, 3, POPUP_NO, XSTR( "&Yes, Quit", 28), XSTR( "Yes, &Restart", 29), XSTR( "Do you really want to end the mission?", 30)); + + switch (choice) { + case 1: + // save the game before quitting if in campaign mode + // MWA -- 3/26/98 -- no more save/restore!!!! +/* + if ( Game_mode & GM_CAMPAIGN_MODE ) { + memset(savegame_filename, 0, _MAX_FNAME); + mission_campaign_savefile_generate_root(savegame_filename); + strcat(savegame_filename, NOX("svg")); + if ( state_save_all(savegame_filename) ) { + Int3(); // could not save this game + } + } +*/ + gameseq_post_event(GS_EVENT_END_GAME); + break; + + case 2: + gameseq_post_event(GS_EVENT_ENTER_GAME); + break; + + default: + break; // do nothing + } + + game_start_time(); + game_flush(); + } +} + +// handle pause keypress +void game_process_pause_key() +{ + // special processing for multiplayer + if (Game_mode & GM_MULTIPLAYER) { + if(Multi_pause_status){ + multi_pause_request(0); + } else { + multi_pause_request(1); + } + } else { + gameseq_post_event( GS_EVENT_PAUSE_GAME ); + } +} + +#define WITHIN_BBOX() do { \ + float scale = 2.0f; \ + polymodel *pm = model_get(s_check->modelnum); \ + collided = 0; \ + if(pm != NULL){ \ + vector temp = new_obj->pos; \ + vector gpos; \ + vm_vec_sub2(&temp, &hit_check->pos); \ + vm_vec_rotate(&gpos, &temp, &hit_check->orient); \ + if((gpos.x >= pm->mins.x * scale) && (gpos.y >= pm->mins.y * scale) && (gpos.z >= pm->mins.z * scale) && (gpos.x <= pm->maxs.x * scale) && (gpos.y <= pm->maxs.y * scale) && (gpos.z <= pm->maxs.z * scale)) { \ + collided = 1; \ + } \ + } \ +} while(0) + +#define MOVE_AWAY_BBOX() do { \ + polymodel *pm = model_get(s_check->modelnum); \ + if(pm != NULL){ \ + switch((int)frand_range(0.0f, 3.9f)){ \ + case 0: \ + new_obj->pos.x += 200.0f; \ + break; \ + case 1: \ + new_obj->pos.x -= 200.0f; \ + break; \ + case 2: \ + new_obj->pos.y += 200.0f; \ + break; \ + case 3: \ + new_obj->pos.y -= 200.0f; \ + break; \ + default : \ + new_obj->pos.z -= 200.0f; \ + break; \ + } \ + } \ +} while(0) + +// process cheat codes +void game_process_cheats(int k) +{ + int i; + char *cryptstring; + + if ( k == 0 ){ + return; + } + + // no cheats in multiplayer, ever + if(Game_mode & GM_MULTIPLAYER){ + Cheats_enabled = 0; + return; + } + + k = key_to_ascii(k); + + for (i = 0; i < CHEAT_BUFFER_LEN; i++){ + CheatBuffer[i]=CheatBuffer[i+1]; + } + + CheatBuffer[CHEATSPOT]=(char)k; + + cryptstring=jcrypt(&CheatBuffer[CHEAT_BUFFER_LEN - CRYPT_STRING_LENGTH]); + +#ifdef FS2_DEMO + if ( !strcmp(Cheat_code_demo, cryptstring) ) { + HUD_printf(XSTR( "Cheats enabled.", 31)); + Cheats_enabled = 1; + if (Player->flags & PLAYER_FLAGS_MSG_MODE){ + hud_squadmsg_toggle(); + } + } + +#else + if( !strcmp(Cheat_code, cryptstring) && !(Game_mode & GM_MULTIPLAYER)){ + Cheats_enabled = 1; + HUD_printf("Cheats enabled"); + } + if( !strcmp(Cheat_code_fish, cryptstring) ){ + // only enable in the main hall + if((gameseq_get_state() == GS_STATE_MAIN_MENU) && (main_hall_id() == 1)){ + extern void fishtank_start(); + fishtank_start(); + } + } + if( !strcmp(Cheat_code_headz, cryptstring) ){ + main_hall_vasudan_funny(); + } + if( !strcmp(Cheat_code_skip, cryptstring) && (gameseq_get_state() == GS_STATE_MAIN_MENU)){ + extern void main_hall_campaign_cheat(); + main_hall_campaign_cheat(); + } + if( !strcmp(Cheat_code_tooled, cryptstring) && (Game_mode & GM_IN_MISSION)){ + Tool_enabled = 1; + HUD_printf("Prepare to be taken to school"); + } + if( !strcmp(Cheat_code_pirate, cryptstring) && (Game_mode & GM_IN_MISSION) && (Player_obj != NULL)){ + HUD_printf(NOX("Walk the plank")); + + for(int idx=0; idx<1; idx++){ + vector add = Player_obj->pos; + add.x += frand_range(-700.0f, 700.0f); + add.y += frand_range(-700.0f, 700.0f); + add.z += frand_range(-700.0f, 700.0f); + + int objnum = ship_create(&vmd_identity_matrix, &add, Num_ship_types - 1); + + if(objnum >= 0){ + int collided; + ship_obj *moveup; + object *hit_check; + ship *s_check; + object *new_obj = &Objects[objnum]; + + // place him + // now make sure we're not colliding with anyone + do { + collided = 0; + moveup = GET_FIRST(&Ship_obj_list); + while(moveup!=END_OF_LIST(&Ship_obj_list)){ + // don't check the new_obj itself!! + if(moveup->objnum != objnum){ + hit_check = &Objects[moveup->objnum]; + Assert(hit_check->type == OBJ_SHIP); + Assert(hit_check->instance >= 0); + if((hit_check->type != OBJ_SHIP) || (hit_check->instance < 0)){ + continue; + } + s_check = &Ships[hit_check->instance]; + + // just to make sure we don't get any strange magnitude errors + if(vm_vec_same(&hit_check->pos, &Objects[objnum].pos)){ + Objects[objnum].pos.x += 1.0f; + } + + WITHIN_BBOX(); + if(collided){ + MOVE_AWAY_BBOX(); + break; + } + collided = 0; + } + moveup = GET_NEXT(moveup); + } + } while(collided); + + // warpin + shipfx_warpin_start(&Objects[objnum]); + + // tell him to attack + // ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_CHASE_ANY, SM_ATTACK, NULL, &Ai_info[Ships[Objects[objnum].instance].ai_index] ); + } + } + } +#endif + /* +//#ifdef INTERPLAYQA + if ( !strcmp(Cheat_code_in_game, cryptstring) ) { + HUD_printf(XSTR( "Cheats enabled.", 31)); + Cheats_enabled = 1; + if (Player->flags & PLAYER_FLAGS_MSG_MODE){ + hud_squadmsg_toggle(); + } + } else if ( !strcmp(Cheat_code_movies, cryptstring) ) { + HUD_printf(XSTR( "All movies available in Tech Room", 32)); + All_movies_enabled = 1; + if (Player->flags & PLAYER_FLAGS_MSG_MODE){ + hud_squadmsg_toggle(); + } + } else if( !strcmp(Cheat_code_pirate, cryptstring) ){ + HUD_printf(NOX("Walk the plank")); + + for(int idx=0; idx<1; idx++){ + vector add; + add.x = frand_range(-1000.0f, 1000.0f); + add.y = frand_range(-1000.0f, 1000.0f); + add.z = frand_range(-1000.0f, 1000.0f); + + int objnum = ship_create(&vmd_identity_matrix, &add, Num_ship_types - 1); + + if(objnum >= 0){ + shipfx_warpin_start(&Objects[objnum]); + } + } + } +#endif + */ +} + +void game_process_keys() +{ + int k; + + button_info_clear(&Player->bi); // clear out the button info struct for the player + do { + k = game_poll(); + + // AL 12-10-97: Scan for keys used to leave the dead state (don't process any) + // DB 1-13-98 : New popup code will run the game do state, so we must skip + // all key processing in this function, since everything should be run through the popup dialog + if ( Game_mode & GM_DEAD_BLEW_UP ) { + continue; + } + + game_process_cheats( k ); + + // mwa -- 4/5/97 Moved these two function calls before the switch statement. I don't think + // that this has adverse affect on anything and is acutally desireable because of the + // ESC key being used to quit any HUD message/input mode that might be currently in use + process_player_ship_keys(k); + +// #ifndef NDEBUG + process_debug_keys(k); // Note, also processed for cheats. +// #endif + + switch (k) { + case 0: + // No key + break; + + case KEY_ESC: + if ( Player->control_mode != PCM_NORMAL ) { + if ( Player->control_mode == PCM_WARPOUT_STAGE1 ) { + gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP ); + } else { + // too late to abort warp out! + } + } else { + // let the ESC key break out of messaging mode + if ( Players[Player_num].flags & PLAYER_FLAGS_MSG_MODE ) { + hud_squadmsg_toggle(); + break; + } + + // if in external view or chase view, go back to cockpit view + if ( Viewer_mode & (VM_EXTERNAL|VM_CHASE|VM_OTHER_SHIP) ) { + Viewer_mode &= ~(VM_EXTERNAL|VM_CHASE|VM_OTHER_SHIP); + break; + } + + if (!(Game_mode & GM_DEAD_DIED)) + game_do_end_mission_popup(); + + } + break; + + case KEY_Y: + break; + + case KEY_N: + break; + + case KEY_ALTED + KEY_SHIFTED+KEY_J: + // treat the current joystick position as the center position + joy_set_cen(); + break; + + case KEY_DEBUGGED | KEY_PAUSE: + gameseq_post_event( GS_EVENT_DEBUG_PAUSE_GAME ); + break; + + case KEY_PAUSE: + game_process_pause_key(); + break; + } // end switch + } while (k); + + button_info_do(&Player->bi); // call functions based on status of button_info bit vectors +} + +int button_function_critical(int n, net_player *p = NULL) +{ + object *objp; + player *pl; + net_player *npl; + int at_self; // flag indicating the object is local (for hud messages, etc) + + Assert(n >= 0); + + // multiplayer clients should leave critical button bits alone and pass them to the server instead + if ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)) { + // if this flag is set, we should apply the button itself (came from the server) + if (!Multi_button_info_ok){ + return 0; + } + } + + // in single player mode make sure we're using the player object and the player himself, otherwise use the object and + // player pertaining to the passed net_player + npl = NULL; + if (p == NULL) { + objp = Player_obj; + pl = Player; + if(Game_mode & GM_MULTIPLAYER){ + npl = Net_player; + + // if we're the server in multiplayer and we're an observer, don't process our own critical button functions + if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){ + return 0; + } + } + at_self = 1; + } else { + objp = &Objects[p->player->objnum]; + pl = p->player; + npl = p; + at_self = 0; + + if ( NETPLAYER_IS_DEAD(npl) || (Ships[Objects[pl->objnum].instance].flags & SF_DYING) ) + return 0; + } + + switch (n) { + // cycle to next secondary weapon + case CYCLE_SECONDARY: + if(at_self) + control_used(CYCLE_SECONDARY); + + hud_gauge_popup_start(HUD_WEAPONS_GAUGE); + if (ship_select_next_secondary(objp)) { + ship* shipp = &Ships[objp->instance]; + if ( timestamp_elapsed(shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank]) ) { + shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(250); // 1/4 second delay until can fire + } + + // multiplayer server should maintain bank/link status here + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Assert(npl != NULL); + multi_server_update_player_weapons(npl,shipp); + } + } + break; + + // cycle number of missiles + case CYCLE_NUM_MISSLES: + if(at_self) + control_used(CYCLE_NUM_MISSLES); + + if ( objp == Player_obj ) { + if ( Player_ship->weapons.num_secondary_banks <= 0 ) { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "This ship has no secondary weapons", 33)); + gamesnd_play_iface(SND_GENERAL_FAIL); + break; + } + } + + if ( Ships[objp->instance].flags & SF_SECONDARY_DUAL_FIRE ) { + Ships[objp->instance].flags &= ~SF_SECONDARY_DUAL_FIRE; + if(at_self) { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Secondary weapon set to normal fire mode", 34)); + snd_play( &Snds[SND_SECONDARY_CYCLE] ); + hud_gauge_popup_start(HUD_WEAPONS_GAUGE); + } + } else { + Ships[objp->instance].flags |= SF_SECONDARY_DUAL_FIRE; + if(at_self) { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Secondary weapon set to dual fire mode", 35)); + snd_play( &Snds[SND_SECONDARY_CYCLE] ); + hud_gauge_popup_start(HUD_WEAPONS_GAUGE); + } + } + + // multiplayer server should maintain bank/link status here + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Assert(npl != NULL); + multi_server_update_player_weapons(npl,&Ships[objp->instance]); + } + break; + + // increase weapon recharge rate + case INCREASE_WEAPON: + if(at_self) + control_used(INCREASE_WEAPON); + increase_recharge_rate(objp, WEAPONS); + + // multiplayer server should maintain bank/link status here + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Assert(npl != NULL); + multi_server_update_player_weapons(npl,&Ships[objp->instance]); + } + break; + + // decrease weapon recharge rate + case DECREASE_WEAPON: + if(at_self) + control_used(DECREASE_WEAPON); + decrease_recharge_rate(objp, WEAPONS); + + // multiplayer server should maintain bank/link status here + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Assert(npl != NULL); + multi_server_update_player_weapons(npl,&Ships[objp->instance]); + } + break; + + // increase shield recharge rate + case INCREASE_SHIELD: + if(at_self) + control_used(INCREASE_SHIELD); + increase_recharge_rate(objp, SHIELDS); + + // multiplayer server should maintain bank/link status here + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Assert(npl != NULL); + multi_server_update_player_weapons(npl,&Ships[objp->instance]); + } + break; + + // decrease shield recharge rate + case DECREASE_SHIELD: + if(at_self) + control_used(DECREASE_SHIELD); + decrease_recharge_rate(objp, SHIELDS); + + // multiplayer server should maintain bank/link status here + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Assert(npl != NULL); + multi_server_update_player_weapons(npl,&Ships[objp->instance]); + } + break; + + // increase energy to engines + case INCREASE_ENGINE: + if(at_self) + control_used(INCREASE_ENGINE); + increase_recharge_rate(objp, ENGINES); + + // multiplayer server should maintain bank/link status here + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Assert(npl != NULL); + multi_server_update_player_weapons(npl,&Ships[objp->instance]); + } + break; + + // decrease energy to engines + case DECREASE_ENGINE: + if(at_self) + control_used(DECREASE_ENGINE); + decrease_recharge_rate(objp, ENGINES); + + // multiplayer server should maintain bank/link status here + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Assert(npl != NULL); + multi_server_update_player_weapons(npl,&Ships[objp->instance]); + } + break; + + // equalize recharge rates + case ETS_EQUALIZE: + if (at_self) { + control_used(ETS_EQUALIZE); + } + + set_default_recharge_rates(objp); + snd_play( &Snds[SND_ENERGY_TRANS] ); + // multiplayer server should maintain bank/link status here + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Assert(npl != NULL); + multi_server_update_player_weapons(npl,&Ships[objp->instance]); + } + break; + + // equalize shield energy to all quadrants + case SHIELD_EQUALIZE: + if(at_self){ + control_used(SHIELD_EQUALIZE); + } + hud_shield_equalize(objp, pl); + break; + + // transfer shield energy to front + case SHIELD_XFER_TOP: + if(at_self){ + control_used(SHIELD_XFER_TOP); + } + hud_augment_shield_quadrant(objp, 1); + break; + + // transfer shield energy to rear + case SHIELD_XFER_BOTTOM: + if(at_self) + control_used(SHIELD_XFER_BOTTOM); + hud_augment_shield_quadrant(objp, 2); + break; + + // transfer shield energy to left + case SHIELD_XFER_LEFT: + if(at_self) + control_used(SHIELD_XFER_LEFT); + hud_augment_shield_quadrant(objp, 3); + break; + + // transfer shield energy to right + case SHIELD_XFER_RIGHT: + if(at_self) + control_used(SHIELD_XFER_RIGHT); + hud_augment_shield_quadrant(objp, 0); + break; + + // transfer energy to shield from weapons + case XFER_SHIELD: + if(at_self) + control_used(XFER_SHIELD); + transfer_energy_to_shields(objp); + break; + + // transfer energy to weapons from shield + case XFER_LASER: + if(at_self) + control_used(XFER_LASER); + transfer_energy_to_weapons(objp); + break; + + // following are not handled here, but we need to bypass the Int3() + case LAUNCH_COUNTERMEASURE: + case VIEW_SLEW: + case VIEW_EXTERNAL: + case VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK: + case ONE_THIRD_THROTTLE: + case TWO_THIRDS_THROTTLE: + case MINUS_5_PERCENT_THROTTLE: + case PLUS_5_PERCENT_THROTTLE: + case ZERO_THROTTLE: + case MAX_THROTTLE: + return 0; + + default : + Int3(); // bad bad bad + break; + } + + return 1; +} + +// return !0 if the action is allowed, otherwise return 0 +int button_allowed(int n) +{ + if ( hud_disabled() ) { + switch (n) { + case SHOW_GOALS: + case END_MISSION: + case CYCLE_NEXT_PRIMARY: + case CYCLE_PREV_PRIMARY: + case CYCLE_SECONDARY: + case ONE_THIRD_THROTTLE: + case TWO_THIRDS_THROTTLE: + case PLUS_5_PERCENT_THROTTLE: + case MINUS_5_PERCENT_THROTTLE: + case ZERO_THROTTLE: + case MAX_THROTTLE: + return 1; + default: + return 0; + } + } + + return 1; +} + +// execute function corresponding to action n +// basically, these are actions which don't affect demo playback at all +int button_function_demo_valid(int n) +{ + // by default, we'll return "not processed". ret will get set to 1, if this is one of the keys which is always allowed, even in demo + // playback. + int ret = 0; + + // No keys, not even targeting keys, when player in death roll. He can press keys after he blows up. + if (Game_mode & GM_DEAD_DIED){ + return 0; + } + + // any of these buttons are valid + switch(n){ + case VIEW_CHASE: + control_used(VIEW_CHASE); + Viewer_mode ^= VM_CHASE; + if ( Viewer_mode & VM_CHASE ) { + Viewer_mode &= ~VM_EXTERNAL; + } + ret = 1; + break; + + case VIEW_EXTERNAL: + control_used(VIEW_EXTERNAL); + Viewer_mode ^= VM_EXTERNAL; + Viewer_mode &= ~VM_EXTERNAL_CAMERA_LOCKED; // reset camera lock when leave/entering external view + if ( Viewer_mode & VM_EXTERNAL ) { + Viewer_mode &= ~VM_CHASE; + } + ret = 1; + break; + + case VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK: + control_used(VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK); + if ( Viewer_mode & VM_EXTERNAL ) { + Viewer_mode ^= VM_EXTERNAL_CAMERA_LOCKED; + if ( Viewer_mode & VM_EXTERNAL_CAMERA_LOCKED ) { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "External camera is locked, controls will move ship", 36)); + } else { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "External camera is free, controls will move the camera, not the ship", 37)); + } + } + ret = 1; + break; + + case VIEW_OTHER_SHIP: + control_used(VIEW_OTHER_SHIP); + if ( Player_ai->target_objnum < 0 ) { + snd_play( &Snds[SND_TARGET_FAIL] ); + } else { + if ( Objects[Player_ai->target_objnum].type != OBJ_SHIP ) { + snd_play( &Snds[SND_TARGET_FAIL] ); + } else { + Viewer_mode ^= VM_OTHER_SHIP; + } + } + ret = 1; + break; + + case TIME_SLOW_DOWN: + if ( Game_mode & GM_NORMAL ) { + if ( Game_time_compression > F1_0) { + Game_time_compression /= 2; + } else { + gamesnd_play_error_beep(); + } + } else { + gamesnd_play_error_beep(); + } + ret = 1; + break; + + case TIME_SPEED_UP: + if ( Game_mode & GM_NORMAL ) { + if ( Game_time_compression < (F1_0*4) ) { + Game_time_compression *= 2; + } else { + gamesnd_play_error_beep(); + } + } else { + gamesnd_play_error_beep(); + } + ret = 1; + break; + } + + // done + return ret; +} + +// execute function corresponding to action n (BUTTON_ #define from KeyControl.h) +int button_function(int n) +{ + Assert(n >= 0); + + if ( !button_allowed(n) ) { + return 0; + } + + // No keys, not even targeting keys, when player in death roll. He can press keys after he blows up. + if (Game_mode & GM_DEAD_DIED){ + return 0; + } + + switch (n) { + // cycle to next primary weapon + case CYCLE_NEXT_PRIMARY: + // bogus? + if((Player_obj == NULL) || (Player_ship == NULL)){ + break; + } + + hud_gauge_popup_start(HUD_WEAPONS_GAUGE); + if (ship_select_next_primary(Player_obj, CYCLE_PRIMARY_NEXT)) { + ship* shipp = Player_ship; + shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank] = timestamp(250); // 1/4 second delay until can fire + // multiplayer server should maintain bank/link status here + // if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ +// Assert(npl != NULL); +// multi_server_update_player_weapons(npl,shipp); +// } + } + break; + + // cycle to previous primary weapon + case CYCLE_PREV_PRIMARY: + // bogus? + if((Player_obj == NULL) || (Player_ship == NULL)){ + break; + } + + hud_gauge_popup_start(HUD_WEAPONS_GAUGE); + if (ship_select_next_primary(Player_obj, CYCLE_PRIMARY_PREV)) { + ship* shipp = Player_ship; + shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank] = timestamp(250); // 1/4 second delay until can fire + + // multiplayer server should maintain bank/link status here + // if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + // Assert(npl != NULL); + // multi_server_update_player_weapons(npl,shipp); + // } + } + break; + + // cycle to next secondary weapon + case CYCLE_SECONDARY: + return button_function_critical(CYCLE_SECONDARY); + break; + + // cycle number of missiles fired from secondary bank + case CYCLE_NUM_MISSLES: + return button_function_critical(CYCLE_NUM_MISSLES); + break; + + // undefined in multiplayer for clients right now + // match target speed + case MATCH_TARGET_SPEED: + control_used(MATCH_TARGET_SPEED); + // If player is auto-matching, break auto-match speed + if ( Player->flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) { + Player->flags &= ~PLAYER_FLAGS_AUTO_MATCH_SPEED; + } + player_match_target_speed(); + break; + + // undefined in multiplayer for clients right now + // toggle auto-match target speed + case TOGGLE_AUTO_MATCH_TARGET_SPEED: + // multiplayer observers can't match target speed + if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){ + break; + } + + Player->flags ^= PLAYER_FLAGS_AUTO_MATCH_SPEED; + control_used(TOGGLE_AUTO_MATCH_TARGET_SPEED); + hud_gauge_popup_start(HUD_AUTO_SPEED); + if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) { + snd_play(&Snds[SND_SHIELD_XFER_OK], 1.0f); +// HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Auto match target speed activated", -1)); + if ( !Player->flags & PLAYER_FLAGS_MATCH_TARGET ) { + player_match_target_speed(); + } + } + else { +// HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Auto match target deactivated", -1)); + snd_play(&Snds[SND_SHIELD_XFER_OK], 1.0f); + player_match_target_speed(); + } + break; + + // target next + case TARGET_NEXT: + control_used(TARGET_NEXT); + if ( hud_sensors_ok(Player_ship) ) { + hud_target_next(); + } + break; + + // target previous + case TARGET_PREV: + control_used(TARGET_PREV); + if ( hud_sensors_ok(Player_ship) ) { + hud_target_prev(); + } + break; + + // target the next hostile target + case TARGET_NEXT_CLOSEST_HOSTILE: + control_used(TARGET_NEXT_CLOSEST_HOSTILE); + if (hud_sensors_ok(Player_ship)){ + hud_target_next_list(); + } + break; + + // target the previous closest hostile + case TARGET_PREV_CLOSEST_HOSTILE: + control_used(TARGET_PREV_CLOSEST_HOSTILE); + if (hud_sensors_ok(Player_ship)){ + hud_target_next_list(1,0); + } + break; + + // toggle auto-targeting + case TOGGLE_AUTO_TARGETING: + control_used(TOGGLE_AUTO_TARGETING); + hud_gauge_popup_start(HUD_AUTO_TARGET); + Players[Player_num].flags ^= PLAYER_FLAGS_AUTO_TARGETING; + if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) { + if (hud_sensors_ok(Player_ship)) { + hud_target_closest(opposing_team_mask(Player_ship->team), -1, FALSE, TRUE ); + snd_play(&Snds[SND_SHIELD_XFER_OK], 1.0f); +// HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Auto targeting activated", -1)); + } else { + Players[Player_num].flags ^= PLAYER_FLAGS_AUTO_TARGETING; + } + } else { + snd_play(&Snds[SND_SHIELD_XFER_OK], 1.0f); +// HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Auto targeting deactivated", -1)); + } + break; + + // target the next friendly ship + case TARGET_NEXT_CLOSEST_FRIENDLY: + control_used(TARGET_NEXT_CLOSEST_FRIENDLY); + if (hud_sensors_ok(Player_ship)){ + hud_target_next_list(0); + } + break; + + // target the closest friendly ship + case TARGET_PREV_CLOSEST_FRIENDLY: + control_used(TARGET_PREV_CLOSEST_FRIENDLY); + if (hud_sensors_ok(Player_ship)) { + hud_target_next_list(0,0); + } + break; + + // target ship closest to center of reticle + case TARGET_SHIP_IN_RETICLE: + control_used(TARGET_SHIP_IN_RETICLE); + if (hud_sensors_ok(Player_ship)){ + hud_target_in_reticle_new(); + } + break; + + case TARGET_LAST_TRANMISSION_SENDER: + control_used(TARGET_LAST_TRANMISSION_SENDER); + if ( hud_sensors_ok(Player_ship)) { + hud_target_last_transmit(); + } + break; + + // target the closest repair ship + case TARGET_CLOSEST_REPAIR_SHIP: + control_used(TARGET_CLOSEST_REPAIR_SHIP); + // AL: Try to find the closest repair ship coming to repair the player... if no support + // ships are coming to rearm the player, just try for the closest repair ship + if ( hud_target_closest_repair_ship(OBJ_INDEX(Player_obj)) == 0 ) { + if ( hud_target_closest_repair_ship() == 0 ) { + snd_play(&Snds[SND_TARGET_FAIL]); + } + } + break; + + // target the closest ship attacking current target + case TARGET_CLOSEST_SHIP_ATTACKING_TARGET: + control_used(TARGET_CLOSEST_SHIP_ATTACKING_TARGET); + if (hud_sensors_ok(Player_ship)){ + hud_target_closest(opposing_team_mask(Player_ship->team), Player_ai->target_objnum); + } + break; + + // stop targeting ship + case STOP_TARGETING_SHIP: + control_used(STOP_TARGETING_SHIP); + hud_cease_targeting(); + break; + + // target closest ship that is attacking player + case TARGET_CLOSEST_SHIP_ATTACKING_SELF: + control_used(TARGET_CLOSEST_SHIP_ATTACKING_SELF); + if (hud_sensors_ok(Player_ship)){ + hud_target_closest(opposing_team_mask(Player_ship->team), OBJ_INDEX(Player_obj), TRUE, 0, 1); + } + break; + + // target your target's target + case TARGET_TARGETS_TARGET: + control_used(TARGET_TARGETS_TARGET); + if (hud_sensors_ok(Player_ship)){ + hud_target_targets_target(); + } + break; + + // target ships subsystem in reticle + case TARGET_SUBOBJECT_IN_RETICLE: + control_used(TARGET_SUBOBJECT_IN_RETICLE); + if (hud_sensors_ok(Player_ship)){ + hud_target_subsystem_in_reticle(); + } + break; + + case TARGET_PREV_SUBOBJECT: + control_used(TARGET_PREV_SUBOBJECT); + if (hud_sensors_ok(Player_ship)){ + hud_target_prev_subobject(); + } + break; + + // target next subsystem on current target + case TARGET_NEXT_SUBOBJECT: + control_used(TARGET_NEXT_SUBOBJECT); + if (hud_sensors_ok(Player_ship)){ + hud_target_next_subobject(); + } + break; + + // stop targeting subsystems on ship + case STOP_TARGETING_SUBSYSTEM: + control_used(STOP_TARGETING_SUBSYSTEM); + hud_cease_subsystem_targeting(); + break; + + case TARGET_NEXT_BOMB: + control_used(TARGET_NEXT_BOMB); + hud_target_missile(Player_obj, 1); + break; + + case TARGET_PREV_BOMB: + control_used(TARGET_PREV_BOMB); + hud_target_missile(Player_obj, 0); + break; + + case TARGET_NEXT_UNINSPECTED_CARGO: + hud_target_uninspected_object(1); + break; + + case TARGET_PREV_UNINSPECTED_CARGO: + hud_target_uninspected_object(0); + break; + + case TARGET_NEWEST_SHIP: + hud_target_newest_ship(); + break; + + case TARGET_NEXT_LIVE_TURRET: + hud_target_live_turret(1); + break; + + case TARGET_PREV_LIVE_TURRET: + hud_target_live_turret(0); + break; + + // wingman message: attack current target + case ATTACK_MESSAGE: + control_used(ATTACK_MESSAGE); + hud_squadmsg_shortcut( ATTACK_TARGET_ITEM ); + break; + + // wingman message: disarm current target + case DISARM_MESSAGE: + control_used(DISARM_MESSAGE); + hud_squadmsg_shortcut( DISARM_TARGET_ITEM ); + break; + + // wingman message: disable current target + case DISABLE_MESSAGE: + control_used(DISABLE_MESSAGE); + hud_squadmsg_shortcut( DISABLE_TARGET_ITEM ); + break; + + // wingman message: disable current target + case ATTACK_SUBSYSTEM_MESSAGE: + control_used(ATTACK_SUBSYSTEM_MESSAGE); + hud_squadmsg_shortcut( DISABLE_SUBSYSTEM_ITEM ); + break; + + // wingman message: capture current target + case CAPTURE_MESSAGE: + control_used(CAPTURE_MESSAGE); + hud_squadmsg_shortcut( CAPTURE_TARGET_ITEM ); + break; + + // wingman message: engage enemy + case ENGAGE_MESSAGE: + control_used(ENGAGE_MESSAGE); + hud_squadmsg_shortcut( ENGAGE_ENEMY_ITEM ); + break; + + // wingman message: form on my wing + case FORM_MESSAGE: + control_used(FORM_MESSAGE); + hud_squadmsg_shortcut( FORMATION_ITEM ); + break; + + // wingman message: protect current target + case PROTECT_MESSAGE: + control_used(PROTECT_MESSAGE); + hud_squadmsg_shortcut( PROTECT_TARGET_ITEM ); + break; + + // wingman message: cover me + case COVER_MESSAGE: + control_used(COVER_MESSAGE); + hud_squadmsg_shortcut( COVER_ME_ITEM ); + break; + + // wingman message: warp out + case WARP_MESSAGE: + control_used(WARP_MESSAGE); + hud_squadmsg_shortcut( DEPART_ITEM ); + break; + + case IGNORE_MESSAGE: + control_used(IGNORE_MESSAGE); + hud_squadmsg_shortcut( IGNORE_TARGET_ITEM ); + break; + + // rearm message + case REARM_MESSAGE: + control_used(REARM_MESSAGE); + hud_squadmsg_rearm_shortcut(); + break; + + // cycle to next radar range + case RADAR_RANGE_CYCLE: + control_used(RADAR_RANGE_CYCLE); + HUD_config.rp_dist++; + if ( HUD_config.rp_dist >= RR_MAX_RANGES ) + HUD_config.rp_dist = 0; + + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Radar range set to %s", 38), Radar_range_text(HUD_config.rp_dist)); + break; + + // toggle the squadmate messaging menu + case SQUADMSG_MENU: + control_used(SQUADMSG_MENU); + hud_squadmsg_toggle(); // leave the details to the messaging code!!! + break; + + // show the mission goals screen + case SHOW_GOALS: + control_used(SHOW_GOALS); + gameseq_post_event( GS_EVENT_SHOW_GOALS ); + break; + + // end the mission + case END_MISSION: + // in multiplayer, all end mission requests should go through the server + if(Game_mode & GM_MULTIPLAYER){ + multi_handle_end_mission_request(); + break; + } + + control_used(END_MISSION); + + if (collide_predict_large_ship(Player_obj, 200.0f)) { + gamesnd_play_iface(SND_GENERAL_FAIL); + HUD_printf(XSTR( "** WARNING ** Collision danger. Warpout not activated.", 39)); + } else if (ship_get_subsystem_strength( Player_ship, SUBSYSTEM_ENGINE ) < 0.1f) { + gamesnd_play_iface(SND_GENERAL_FAIL); + HUD_printf(XSTR( "Engine failure. Cannot engage warp drive.", 40)); + } else { + gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START ); + } + break; + + case ADD_REMOVE_ESCORT: + if ( Player_ai->target_objnum >= 0 ) { + control_used(ADD_REMOVE_ESCORT); + hud_add_remove_ship_escort(Player_ai->target_objnum); + } + break; + + case ESCORT_CLEAR: + control_used(ESCORT_CLEAR); + hud_escort_clear_all(); + break; + + case TARGET_NEXT_ESCORT_SHIP: + control_used(TARGET_NEXT_ESCORT_SHIP); + hud_escort_target_next(); + break; + + // increase weapon recharge rate + case INCREASE_WEAPON: + hud_gauge_popup_start(HUD_ETS_GAUGE); + return button_function_critical(INCREASE_WEAPON); + break; + + // decrease weapon recharge rate + case DECREASE_WEAPON: + hud_gauge_popup_start(HUD_ETS_GAUGE); + return button_function_critical(DECREASE_WEAPON); + break; + + // increase shield recharge rate + case INCREASE_SHIELD: + hud_gauge_popup_start(HUD_ETS_GAUGE); + return button_function_critical(INCREASE_SHIELD); + break; + + // decrease shield recharge rate + case DECREASE_SHIELD: + hud_gauge_popup_start(HUD_ETS_GAUGE); + return button_function_critical(DECREASE_SHIELD); + break; + + // increase energy to engines + case INCREASE_ENGINE: + hud_gauge_popup_start(HUD_ETS_GAUGE); + return button_function_critical(INCREASE_ENGINE); + break; + + // decrease energy to engines + case DECREASE_ENGINE: + hud_gauge_popup_start(HUD_ETS_GAUGE); + return button_function_critical(DECREASE_ENGINE); + break; + + case ETS_EQUALIZE: + hud_gauge_popup_start(HUD_ETS_GAUGE); + return button_function_critical(ETS_EQUALIZE); + break; + + // equalize shield energy to all quadrants + case SHIELD_EQUALIZE: + return button_function_critical(SHIELD_EQUALIZE); + break; + + // transfer shield energy to front + case SHIELD_XFER_TOP: + return button_function_critical(SHIELD_XFER_TOP); + break; + + // transfer shield energy to rear + case SHIELD_XFER_BOTTOM: + return button_function_critical(SHIELD_XFER_BOTTOM); + break; + + // transfer shield energy to left + case SHIELD_XFER_LEFT: + return button_function_critical(SHIELD_XFER_LEFT); + break; + + // transfer shield energy to right + case SHIELD_XFER_RIGHT: + return button_function_critical(SHIELD_XFER_RIGHT); + break; + + // transfer energy to shield from weapons + case XFER_SHIELD: + return button_function_critical(XFER_SHIELD); + break; + + // transfer energy to weapons from shield + case XFER_LASER: + return button_function_critical(XFER_LASER); + break; + + // message all netplayers button + case MULTI_MESSAGE_ALL: + multi_msg_key_down(MULTI_MSG_ALL); + break; + + // message all friendlies button + case MULTI_MESSAGE_FRIENDLY: + multi_msg_key_down(MULTI_MSG_FRIENDLY); + break; + + // message all hostiles button + case MULTI_MESSAGE_HOSTILE: + multi_msg_key_down(MULTI_MSG_HOSTILE); + break; + + // message targeted ship (if player) + case MULTI_MESSAGE_TARGET: + multi_msg_key_down(MULTI_MSG_TARGET); + break; + + // if i'm an observer, zoom to my targeted object + case MULTI_OBSERVER_ZOOM_TO: + multi_obs_zoom_to_target(); + break; + + // toggle between high and low HUD contrast + case TOGGLE_HUD_CONTRAST: + gamesnd_play_iface(SND_USER_SELECT); + hud_toggle_contrast(); + break; + + // toggle network info + case MULTI_TOGGLE_NETINFO: + extern int Multi_display_netinfo; + Multi_display_netinfo = !Multi_display_netinfo; + break; + + // self destruct (multiplayer only) + case MULTI_SELF_DESTRUCT: + if(!(Game_mode & GM_MULTIPLAYER)){ + break; + } + + // bogus netplayer + if((Net_player == NULL) || (Net_player->player == NULL)){ + break; + } + + // blow myself up, if I'm the server + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + if((Net_player->player->objnum >= 0) && (Net_player->player->objnum < MAX_OBJECTS) && + (Objects[Net_player->player->objnum].type == OBJ_SHIP) && (Objects[Net_player->player->objnum].instance >= 0) && (Objects[Net_player->player->objnum].instance < MAX_SHIPS)){ + + ship_self_destruct(&Objects[Net_player->player->objnum]); + } + } + // otherwise send a packet to the server + else { + send_self_destruct_packet(); + } + break; + + // following are not handled here, but we need to bypass the Int3() + case LAUNCH_COUNTERMEASURE: + case VIEW_SLEW: + case ONE_THIRD_THROTTLE: + case TWO_THIRDS_THROTTLE: + case MINUS_5_PERCENT_THROTTLE: + case PLUS_5_PERCENT_THROTTLE: + case ZERO_THROTTLE: + case MAX_THROTTLE: + return 0; + + default: + Int3(); + break; + } // end switch + + return 1; +} + +// Call functions for when buttons are pressed +void button_info_do(button_info *bi) +{ + int i, j; + + for (i=0; istatus[i] == 0 ){ + continue; + } + + // at least one bit is set in the status integer + for (j=0; j<32; j++) { + + // check if the bit is set. If button_function returns 1 (implying the action was taken), then unset the bit + if ( bi->status[i] & (1 << j) ) { + // always process buttons which are valid for demo playback + if(button_function_demo_valid(32 * i + j)){ + bi->status[i] &= ~(1 << j); + } + // other buttons + else { + // if we're in demo playback, always clear the bits + if(Game_mode & GM_DEMO_PLAYBACK){ + bi->status[i] &= ~(1 << j); + } + // otherwise check as normal + else if (button_function(32 * i + j)) { + bi->status[i] &= ~(1 << j); + } + } + } + } + } +} + + +// set the bit for the corresponding action n (BUTTON_ #define from KeyControl.h) +void button_info_set(button_info *bi, int n) +{ + int field_num, bit_num; + + field_num = n / 32; + bit_num = n % 32; + + bi->status[field_num] |= (1 << bit_num); +} + +// unset the bit for the corresponding action n (BUTTON_ #define from KeyControl.h) +void button_info_unset(button_info *bi, int n) +{ + int field_num, bit_num; + + field_num = n / 32; + bit_num = n % 32; + + bi->status[field_num] &= ~(1 << bit_num); +} + +int button_info_query(button_info *bi, int n) +{ + return bi->status[n / 32] & (1 << (n % 32)); +} + +// clear out the button_info struct +void button_info_clear(button_info *bi) +{ + int i; + + for (i=0; istatus[i] = 0; + } +} + +// strip out all noncritical keys from the button info struct +void button_strip_noncritical_keys(button_info *bi) +{ + int idx; + + // clear out all noncritical keys + for(idx=0;idx +#include +#endif + +#include "mouse.h" +#include "2d.h" +#include "osapi.h" + +#define MOUSE_MODE_DI 0 +#define MOUSE_MODE_WIN 1 + +#ifdef NDEBUG +LOCAL int Mouse_mode = MOUSE_MODE_DI; +#else +LOCAL int Mouse_mode = MOUSE_MODE_WIN; +#endif + +LOCAL int mouse_inited = 0; +LOCAL int Di_mouse_inited = 0; +LOCAL int Mouse_x; +LOCAL int Mouse_y; + +CRITICAL_SECTION mouse_lock; + +// #define USE_DIRECTINPUT + +int mouse_flags; +int mouse_left_pressed = 0; +int mouse_right_pressed = 0; +int mouse_middle_pressed = 0; +int mouse_left_up = 0; +int mouse_right_up = 0; +int mouse_middle_up = 0; +int Mouse_dx = 0; +int Mouse_dy = 0; +int Mouse_dz = 0; + +int Mouse_sensitivity = 4; +int Use_mouse_to_fly = 0; +int Mouse_hidden = 0; +int Keep_mouse_centered = 0;; + +int di_init(); +void di_cleanup(); +void mouse_force_pos(int x, int y); +void mouse_eval_deltas_di(); + +int mouse_is_visible() +{ + return !Mouse_hidden; +} + +void mouse_close() +{ + if (!mouse_inited) + return; + +#ifdef USE_DIRECTINPUT + di_cleanup(); +#endif + mouse_inited = 0; +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + DeleteCriticalSection( &mouse_lock ); +#endif +} + +void mouse_init() +{ + // Initialize queue + if ( mouse_inited ) return; + mouse_inited = 1; + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + InitializeCriticalSection( &mouse_lock ); +#endif + + ENTER_CRITICAL_SECTION(&mouse_lock); + + mouse_flags = 0; + Mouse_x = gr_screen.max_w / 2; + Mouse_y = gr_screen.max_h / 2; + +#ifdef USE_DIRECTINPUT + if (!di_init()) + Mouse_mode = MOUSE_MODE_WIN; +#else + Mouse_mode = MOUSE_MODE_WIN; +#endif + + LEAVE_CRITICAL_SECTION(&mouse_lock); + + atexit( mouse_close ); +} + + +// ---------------------------------------------------------------------------- +// mouse_mark_button() is called asynchronously by the OS when a mouse button +// goes up or down. The mouse button that is affected is passed via the +// flags parameter. +// +// parameters: flags ==> mouse button pressed/released +// set ==> 1 - button is pressed +// 0 - button is released + +void mouse_mark_button( uint flags, int set) +{ + if ( !mouse_inited ) return; + + ENTER_CRITICAL_SECTION(&mouse_lock); + + if ( !(mouse_flags & MOUSE_LEFT_BUTTON) ) { + + if ( (flags & MOUSE_LEFT_BUTTON) && (set == 1) ) { + mouse_left_pressed++; + +//////////////////////////// +/// SOMETHING TERRIBLE IS ABOUT TO HAPPEN. I FEEL THIS IS NECESSARY FOR THE DEMO, SINCE +/// I DON'T WANT TO CALL CRITICAL SECTION CODE EACH FRAME TO CHECK THE LEFT MOUSE BUTTON. +/// PLEASE SEE ALAN FOR MORE INFORMATION. +//////////////////////////// +#ifdef FS2_DEMO + { + extern void demo_reset_trailer_timer(); + demo_reset_trailer_timer(); + } +#endif +//////////////////////////// +/// IT'S OVER. SEE, IT WASN'T SO BAD RIGHT? IT'S IS VERY UGLY LOOKING, I KNOW. +//////////////////////////// + + } + } + else { + if ( (flags & MOUSE_LEFT_BUTTON) && (set == 0) ){ + mouse_left_up++; + } + } + + if ( !(mouse_flags & MOUSE_RIGHT_BUTTON) ) { + + if ( (flags & MOUSE_RIGHT_BUTTON) && (set == 1) ){ + mouse_right_pressed++; + } + } + else { + if ( (flags & MOUSE_RIGHT_BUTTON) && (set == 0) ){ + mouse_right_up++; + } + } + + if ( !(mouse_flags & MOUSE_MIDDLE_BUTTON) ) { + + if ( (flags & MOUSE_MIDDLE_BUTTON) && (set == 1) ){ + mouse_middle_pressed++; + } + } + else { + if ( (flags & MOUSE_MIDDLE_BUTTON) && (set == 0) ){ + mouse_middle_up++; + } + } + + if ( set ){ + mouse_flags |= flags; + } else { + mouse_flags &= ~flags; + } + + LEAVE_CRITICAL_SECTION(&mouse_lock); +} + +void mouse_flush() +{ + if (!mouse_inited) + return; + + mouse_eval_deltas(); + Mouse_dx = Mouse_dy = Mouse_dz = 0; + ENTER_CRITICAL_SECTION(&mouse_lock); + mouse_left_pressed = 0; + mouse_right_pressed = 0; + mouse_middle_pressed = 0; + mouse_flags = 0; + LEAVE_CRITICAL_SECTION(&mouse_lock); +} + +int mouse_down_count(int n, int reset_count) +{ + int tmp = 0; + if ( !mouse_inited ) return 0; + + if ( (n < LOWEST_MOUSE_BUTTON) || (n > HIGHEST_MOUSE_BUTTON)) return 0; + + ENTER_CRITICAL_SECTION(&mouse_lock); + + switch (n) { + case MOUSE_LEFT_BUTTON: + tmp = mouse_left_pressed; + if ( reset_count ) { + mouse_left_pressed = 0; + } + break; + + case MOUSE_RIGHT_BUTTON: + tmp = mouse_right_pressed; + if ( reset_count ) { + mouse_right_pressed = 0; + } + break; + + case MOUSE_MIDDLE_BUTTON: + tmp = mouse_middle_pressed; + if ( reset_count ) { + mouse_middle_pressed = 0; + } + break; + } // end switch + + LEAVE_CRITICAL_SECTION(&mouse_lock); + + return tmp; +} + +// mouse_up_count() returns the number of times button n has gone from down to up +// since the last call +// +// parameters: n - button of mouse (see #define's in mouse.h) +// +int mouse_up_count(int n) +{ + int tmp = 0; + if ( !mouse_inited ) return 0; + + if ( (n < LOWEST_MOUSE_BUTTON) || (n > HIGHEST_MOUSE_BUTTON)) return 0; + + ENTER_CRITICAL_SECTION(&mouse_lock); + + switch (n) { + case MOUSE_LEFT_BUTTON: + tmp = mouse_left_up; + mouse_left_up = 0; + break; + + case MOUSE_RIGHT_BUTTON: + tmp = mouse_right_up; + mouse_right_up = 0; + break; + + case MOUSE_MIDDLE_BUTTON: + tmp = mouse_middle_up; + mouse_middle_up = 0; + break; + + default: + Assert(0); // can't happen + break; + } // end switch + + LEAVE_CRITICAL_SECTION(&mouse_lock); + + return tmp; +} + +// returns 1 if mouse button btn is down, 0 otherwise + +int mouse_down(int btn) +{ + int tmp; + if ( !mouse_inited ) return 0; + + if ( (btn < LOWEST_MOUSE_BUTTON) || (btn > HIGHEST_MOUSE_BUTTON)) return 0; + + + ENTER_CRITICAL_SECTION(&mouse_lock); + + + if ( mouse_flags & btn ) + tmp = 1; + else + tmp = 0; + + LEAVE_CRITICAL_SECTION(&mouse_lock); + + return tmp; +} + +// returns the fraction of time btn has been down since last call +// (currently returns 1 if buttons is down, 0 otherwise) +// +float mouse_down_time(int btn) +{ + float tmp; + if ( !mouse_inited ) return 0.0f; + + if ( (btn < LOWEST_MOUSE_BUTTON) || (btn > HIGHEST_MOUSE_BUTTON)) return 0.0f; + + ENTER_CRITICAL_SECTION(&mouse_lock); + + if ( mouse_flags & btn ) + tmp = 1.0f; + else + tmp = 0.0f; + + LEAVE_CRITICAL_SECTION(&mouse_lock); + + return tmp; +} + +void mouse_get_delta(int *dx, int *dy, int *dz) +{ + if (dx) + *dx = Mouse_dx; + if (dy) + *dy = Mouse_dy; + if (dz) + *dz = Mouse_dz; +} + +// Forces the actual windows cursor to be at (x,y). This may be independent of our tracked (x,y) mouse pos. +void mouse_force_pos(int x, int y) +{ + if (os_foreground()) { // only mess with windows's mouse if we are in control of it +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + POINT pnt; + + pnt.x = x; + pnt.y = y; + ClientToScreen((HWND) os_get_window(), &pnt); + SetCursorPos(pnt.x, pnt.y); +#endif + } +} + +#include "gamesequence.h" + +// change in mouse position since last call +void mouse_eval_deltas() +{ + static int old_x = 0; + static int old_y = 0; + int tmp_x, tmp_y, cx, cy; + + Mouse_dx = Mouse_dy = Mouse_dz = 0; + if (!mouse_inited) + return; + + if (Mouse_mode == MOUSE_MODE_DI) { + mouse_eval_deltas_di(); + return; + } + + cx = gr_screen.max_w / 2; + cy = gr_screen.max_h / 2; + + ENTER_CRITICAL_SECTION(&mouse_lock); + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + POINT pnt; + GetCursorPos(&pnt); + ScreenToClient((HWND)os_get_window(), &pnt); + tmp_x = pnt.x; + tmp_y = pnt.y; +#endif + + Mouse_dx = tmp_x - old_x; + Mouse_dy = tmp_y - old_y; + Mouse_dz = 0; + + if (Keep_mouse_centered && Mouse_hidden) { + if (Mouse_dx || Mouse_dy) + mouse_force_pos(cx, cy); + + old_x = cx; + old_y = cy; + + } else { + old_x = tmp_x; + old_y = tmp_y; + } + + LEAVE_CRITICAL_SECTION(&mouse_lock); +} + +#ifndef PLAT_UNIX +#include "vdinput.h" + +static LPDIRECTINPUT Di_mouse_obj = NULL; +static LPDIRECTINPUTDEVICE Di_mouse = NULL; +#endif + +void mouse_eval_deltas_di() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + int repeat = 1; + HRESULT hr = 0; + DIMOUSESTATE mouse_state; + + Mouse_dx = Mouse_dy = Mouse_dz = 0; + if (!Di_mouse_inited) + return; + + repeat = 1; + memset(&mouse_state, 0, sizeof(mouse_state)); + while (repeat) { + repeat = 0; + + hr = Di_mouse->GetDeviceState(sizeof(mouse_state), &mouse_state); + if ((hr == DIERR_INPUTLOST) || (hr == DIERR_NOTACQUIRED)) { + // DirectInput is telling us that the input stream has + // been interrupted. We aren't tracking any state + // between polls, so we don't have any special reset + // that needs to be done. We just re-acquire and + // try again. + Sleep(500); // Pause a half second... + hr = Di_mouse->Acquire(); + if (SUCCEEDED(hr)) + repeat = 1; + } + } + + if (SUCCEEDED(hr)) { + Mouse_dx = (int) mouse_state.lX; + Mouse_dy = (int) mouse_state.lY; + Mouse_dz = (int) mouse_state.lZ; + + } else { + Mouse_dx = Mouse_dy = Mouse_dz = 0; + } + + Mouse_x += Mouse_dx; + Mouse_y += Mouse_dy; + + if (Mouse_x < 0) + Mouse_x = 0; + + if (Mouse_y < 0) + Mouse_y = 0; + + if (Mouse_x >= gr_screen.max_w) + Mouse_x = gr_screen.max_w - 1; + + if (Mouse_y >= gr_screen.max_h) + Mouse_y = gr_screen.max_h - 1; + + // keep the mouse inside our window so we don't switch applications or anything (debug bug people reported?) + // JH: Dang! This makes the mouse readings in DirectInput act screwy! +// mouse_force_pos(gr_screen.max_w / 2, gr_screen.max_h / 2); +#endif +} + +int mouse_get_pos(int *xpos, int *ypos) +{ + int flags; + + if (Mouse_mode == MOUSE_MODE_DI) { + if (xpos) + *xpos = Mouse_x; + + if (ypos) + *ypos = Mouse_y; + + return mouse_flags; + } + + if (!mouse_inited) { + *xpos = *ypos = 0; + return 0; + } + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + POINT pnt; + GetCursorPos(&pnt); + ScreenToClient((HWND)os_get_window(), &pnt); + +// EnterCriticalSection(&mouse_lock); + + flags = mouse_flags; + Mouse_x = pnt.x; + Mouse_y = pnt.y; +#endif + +// LeaveCriticalSection(&mouse_lock); + + if (Mouse_x < 0){ + Mouse_x = 0; + } + + if (Mouse_y < 0){ + Mouse_y = 0; + } + + if (Mouse_x >= gr_screen.max_w){ + Mouse_x = gr_screen.max_w - 1; + } + + if (Mouse_y >= gr_screen.max_h){ + Mouse_y = gr_screen.max_h - 1; + } + + if (xpos){ + *xpos = Mouse_x; + } + + if (ypos){ + *ypos = Mouse_y; + } + + return flags; +} + +void mouse_get_real_pos(int *mx, int *my) +{ + if (Mouse_mode == MOUSE_MODE_DI) { + *mx = Mouse_x; + *my = Mouse_y; + return; + } + +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + POINT pnt; + GetCursorPos(&pnt); + ScreenToClient((HWND)os_get_window(), &pnt); + + *mx = pnt.x; + *my = pnt.y; +#endif +} + +void mouse_set_pos(int xpos, int ypos) +{ + if (Mouse_mode == MOUSE_MODE_DI) { + Mouse_x = xpos; + Mouse_y = ypos; + return; + } + + if ((xpos != Mouse_x) || (ypos != Mouse_y)){ + mouse_force_pos(xpos, ypos); + } +} + +int di_init() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; + return 0; +#else + HRESULT hr; + + if (Mouse_mode == MOUSE_MODE_WIN){ + return 0; + } + + Di_mouse_inited = 0; + hr = DirectInputCreate(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &Di_mouse_obj, NULL); + if (FAILED(hr)) { + hr = DirectInputCreate(GetModuleHandle(NULL), 0x300, &Di_mouse_obj, NULL); + if (FAILED(hr)) { + mprintf(( "DirectInputCreate() failed!\n" )); + return FALSE; + } + } + + hr = Di_mouse_obj->CreateDevice(GUID_SysMouse, &Di_mouse, NULL); + if (FAILED(hr)) { + mprintf(( "CreateDevice() failed!\n" )); + return FALSE; + } + + hr = Di_mouse->SetDataFormat(&c_dfDIMouse); + if (FAILED(hr)) { + mprintf(( "SetDataFormat() failed!\n" )); + return FALSE; + } + + hr = Di_mouse->SetCooperativeLevel((HWND)os_get_window(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); + if (FAILED(hr)) { + mprintf(( "SetCooperativeLevel() failed!\n" )); + return FALSE; + } +/* + DIPROPDWORD hdr; + + // Turn on buffering + hdr.diph.dwSize = sizeof(DIPROPDWORD); + hdr.diph.dwHeaderSize = sizeof(DIPROPHEADER); + hdr.diph.dwObj = 0; + hdr.diph.dwHow = DIPH_DEVICE; // Apply to entire device + hdr.dwData = 16; //MAX_BUFFERED_KEYBOARD_EVENTS; + + hr = Di_mouse->SetProperty( DIPROP_BUFFERSIZE, &hdr.diph ); + if (FAILED(hr)) { + mprintf(( "SetProperty DIPROP_BUFFERSIZE failed\n" )); + return FALSE; + } + + Di_event = CreateEvent( NULL, FALSE, FALSE, NULL ); + Assert(Di_event != NULL); + + hr = Di_mouse->SetEventNotification(Di_event); + if (FAILED(hr)) { + mprintf(( "SetEventNotification failed\n" )); + return FALSE; + } +*/ + Di_mouse->Acquire(); + + Di_mouse_inited = 1; + return TRUE; +#endif +} + +void di_cleanup() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + // Destroy any lingering IDirectInputDevice object. + if (Di_mouse) { + // Unacquire the device one last time just in case we got really confused + // and tried to exit while the device is still acquired. + Di_mouse->Unacquire(); + + Di_mouse->Release(); + Di_mouse = NULL; + } + + // Destroy any lingering IDirectInput object. + if (Di_mouse_obj) { + Di_mouse_obj->Release(); + Di_mouse_obj = NULL; + } + + Di_mouse_inited = 0; +#endif +} diff --git a/src/io/sw_error.hpp b/src/io/sw_error.hpp new file mode 100644 index 0000000..7581888 --- /dev/null +++ b/src/io/sw_error.hpp @@ -0,0 +1,144 @@ +/**************************************************************************** + + MODULE: SW_Error.HPP + Tab settings: 5 9 + Copyright 1995, 1996, Microsoft Corporation, All Rights Reserved. + + PURPOSE: Header for Error Codes + + FUNCTIONS: + + Author(s): Name: + ---------- ---------------- + MEA Manolito E. Adan + + Revision History: + ----------------- + Version Date Author Comments + ------- ------ ----- ------------------------------------------- + 1.0 22-Jan-96 MEA original + +****************************************************************************/ +#ifndef SW_Error_SEEN +#define SW_Error_SEEN + +#include +#include +#include + +/* +typedef struct _SF_ERROR { + HRESULT HCode; // HRESULT code + ULONG ulDriverCode; // Error code from device driver +} SF_ERROR, *PSF_ERROR; +*/ + + +//--------------------------------------------------------------------------- +// Error Status Codes +//--------------------------------------------------------------------------- +/* + * On Windows NT 3.5 and Windows 95, scodes are 32-bit values + * laid out as follows: + * + * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+---------------------+-------------------------------+ + * |S|R|C|N|r| Facility | Code | + * +-+-+-+-+-+---------------------+-------------------------------+ + * + * where + * + * S - Severity - indicates success/fail + * + * 0 - Success + * 1 - Fail (COERROR) + * + * R - reserved portion of the facility code, corresponds to NT's + * second severity bit. + * + * C - reserved portion of the facility code, corresponds to NT's + * C field. + * + * N - reserved portion of the facility code. Used to indicate a + * mapped NT status value. + * + * r - reserved portion of the facility code. Reserved for internal + * use. Used to indicate HRESULT values that are not status + * values, but are instead message ids for display strings. + * + * Facility - is the facility code + * FACILITY_NULL 0x0 + * FACILITY_RPC 0x1 + * FACILITY_DISPATCH 0x2 + * FACILITY_STORAGE 0x3 + * FACILITY_ITF 0x4 + * FACILITY_WIN32 0x7 + * FACILITY_WINDOWS 0x8 + * + * Code - is the facility's status code + * + */ + +// SWForce Errors +#define MAKE_FF_SCODE(sev,fac,code) \ + ((SCODE) (((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))) ) + +#define MAKE_FF_E( err ) (MAKE_FF_SCODE( 1, FACILITY_ITF, err )) +#define MAKE_FF_S( warn ) (MAKE_FF_SCODE( 0, FACILITY_ITF, warn )) + +#define DINPUT_DRIVER_ERR_BASE 0x500 + +#define SUCCESS 0x00 // successful +#define SFERR_INVALID_OBJECT MAKE_FF_E(DINPUT_DRIVER_ERR_BASE + 1) // Invalid object ID +#define SFERR_INVALID_PARAM DIERR_INVALIDPARAM // Invalid parameters +#define SFERR_NO_SUPPORT DIERR_UNSUPPORTED // Function not supported +#define SFERR_INVALID_DEVICE DIERR_DEVICENOTREG // Device not found +#define SFERR_FFDEVICE_MEMORY DIERR_DEVICEFULL // Out of download RAM +#define SFERR_END_OF_LIST MAKE_FF_S(DINPUT_DRIVER_ERR_BASE + 6) // End of the list +#define SFERR_DEVICE_NACK MAKE_FF_E(DINPUT_DRIVER_ERR_BASE + 7) // Device NACK received +#define SFERR_INVALID_STRUCT_SIZE DIERR_INVALIDPARAM // Invalid structure passed +#define SFERR_EFFECT_NOT_IN_DEVICE DIERR_NOTDOWNLOADED // Effect was not downloaded, so + // cannot unload. +#define SFERR_RAW_OUT_DATAEVENT_CREATION MAKE_FF_E(DINPUT_DRIVER_ERR_BASE + 10) // Could not create Event +#define SFERR_RAW_OUT_THREAD_CREATION MAKE_FF_E(DINPUT_DRIVER_ERR_BASE + 11) // Could not create a thread +#define SFERR_EFFECT_STATUS_BUSY DIERR_EFFECTPLAYING // Device busy playing Effect +#define SFERR_OUT_OF_FF_MEMORY DIERR_OUTOFMEMORY // FF system has run out of memory + // cannot create new Effect +#define SFERR_SYSTEM_INIT MAKE_FF_E(DINPUT_DRIVER_ERR_BASE + 14) // Could not create SWForce +#define SFERR_DRIVER_ERROR MAKE_FF_E(DINPUT_DRIVER_ERR_BASE + 15) // Driver error detected +#define SFERR_NON_FF_DEVICE MAKE_FF_E(DINPUT_DRIVER_ERR_BASE + 16) // This is a non-FF device, driver not found +#define SFERR_INVALID_HAL_OBJECT MAKE_FF_E(DINPUT_DRIVER_ERR_BASE + 17) // HAL cannot emulate this object +#define SFERR_INVALID_MEMBER_VALUE DIERR_INVALIDPARAM // Data structure has invalid member value + +// VFX_ error codes +#define VFX_ERR_BASE DINPUT_DRIVER_ERR_BASE + 100 +#define VFX_ERR_FILE_NOT_FOUND HRESULT_FROM_WIN32(MMIOERR_FILENOTFOUND) +#define VFX_ERR_FILE_OUT_OF_MEMORY DIERR_OUTOFMEMORY +#define VFX_ERR_FILE_CANNOT_OPEN HRESULT_FROM_WIN32(MMIOERR_CANNOTOPEN) +#define VFX_ERR_FILE_CANNOT_CLOSE HRESULT_FROM_WIN32(MMIOERR_CANNOTCLOSE) +#define VFX_ERR_FILE_CANNOT_READ HRESULT_FROM_WIN32(MMIOERR_CANNOTREAD) +#define VFX_ERR_FILE_CANNOT_WRITE HRESULT_FROM_WIN32(MMIOERR_CANNOTWRITE) +#define VFX_ERR_FILE_CANNOT_SEEK HRESULT_FROM_WIN32(MMIOERR_CANNOTSEEK) +#define VFX_ERR_FILE_UNKNOWN_ERROR MAKE_FF_E(VFX_ERR_BASE + 8) +#define VFX_ERR_FILE_BAD_FORMAT MAKE_FF_E(VFX_ERR_BASE + 9) +#define VFX_ERR_FILE_ACCESS_DENIED HRESULT_FROM_WIN32(MMIOERR_ACCESSDENIED) +#define VFX_ERR_FILE_SHARING_VIOLATION HRESULT_FROM_WIN32(MMIOERR_SHARINGVIOLATION) +#define VFX_ERR_FILE_NETWORK_ERROR HRESULT_FROM_WIN32(MMIOERR_NETWORKERROR) +#define VFX_ERR_FILE_TOO_MANY_OPEN_FILES HRESULT_FROM_WIN32(MMIOERR_TOOMANYOPENFILES) +#define VFX_ERR_FILE_INVALID HRESULT_FROM_WIN32(MMIOERR_INVALIDFILE) +#define VFX_ERR_FILE_END_OF_FILE MAKE_FF_E(VFX_ERR_BASE + 15) + +// SideWinder Driver Error codes +#define SWDEV_ERR_BASE DINPUT_DRIVER_ERR_BASE + 200 +#define SWDEV_ERR_INVALID_ID MAKE_FF_E(SWDEV_ERR_BASE + 1) // Invalid Download ID +#define SWDEV_ERR_INVALID_PARAM MAKE_FF_E(SWDEV_ERR_BASE + 2) // Invalid Download Parameter +#define SWDEV_ERR_CHECKSUM MAKE_FF_E(SWDEV_ERR_BASE + 3) // Invalid Checksum in COMM Packet +#define SWDEV_ERR_TYPE_FULL MAKE_FF_E(SWDEV_ERR_BASE + 4) // No More RAM space for Effect Type +#define SWDEV_ERR_UNKNOWN_CMD MAKE_FF_E(SWDEV_ERR_BASE + 5) // Unrecognized Device command +#define SWDEV_ERR_PLAYLIST_FULL MAKE_FF_E(SWDEV_ERR_BASE + 6) // Play List is full, cannot play any more Effects +#define SWDEV_ERR_PROCESSLIST_FULL MAKE_FF_E(SWDEV_ERR_BASE + 7) // Process List is full, cannot download + + +#endif // of ifndef SW_Error_SEEN + diff --git a/src/io/sw_guid.hpp b/src/io/sw_guid.hpp new file mode 100644 index 0000000..d76fb92 --- /dev/null +++ b/src/io/sw_guid.hpp @@ -0,0 +1,296 @@ +/**************************************************************************** + + MODULE: SWD_GUID.HPP + Tab Settings: 5 9 + Copyright 1995, 1996, Microsoft Corporation, All Rights Reserved. + + PURPOSE: CLSIDs and IIDs defined for DirectInputForce + + FUNCTIONS: + + Author(s): Name: + ---------- ---------------- + + Revision History: + ----------------- + Version Date Author Comments + ------- ------ ----- ------------------------------------------- + 1.0 06-Feb-97 MEA original, Based on SWForce + 23-Feb-97 MEA Modified for DirectInput FF Device Driver + 1.1 14-Apr-97 MEA Added GUID_RTCSpring + +****************************************************************************/ +#ifndef _SWD_GUID_SEEN +#define _SWD_GUID_SEEN + +#ifdef INITGUIDS +#include +#endif //INITGUIDS + + +/* + * GUIDs + * + */ + + +// +// --- VFX Class ID +// +DEFINE_GUID(CLSID_VFX, /* 04ace0a7-1fa8-11d0-aa22-00a0c911f471 */ + 0x04ace0a7, + 0x1fa8, + 0x11d0, + 0xaa, 0x22, 0x00, 0xa0, 0xc9, 0x11, 0xf4, 0x71); + +// +// --- VFX Interface +// +DEFINE_GUID(IID_IVFX, /* 04ace0a6-1fa8-11d0-aa22-00a0c911f471 */ + 0x04ace0a6, + 0x1fa8, + 0x11d0, + 0xaa, 0x22, 0x00, 0xa0, 0xc9, 0x11, 0xf4, 0x71); + +// +// --- Effect GUIDs +// +DEFINE_GUID(GUID_Wall, /* e84cd1a1-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1a1, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +#if 0 +// Built in ROM Effects +DEFINE_GUID(GUID_RandomNoise, /* e84cd1a3-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1a3, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_AircraftCarrierTakeOff, /* e84cd1a4-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1a4, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_BasketballDribble, /* e84cd1a5-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1a5, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_CarEngineIdle, /* e84cd1a6-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1a6, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_ChainsawIdle, /* e84cd1a7-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1a7, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_ChainsawInAction, /* e84cd1a8-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1a8, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_DieselEngineIdle, /* e84cd1a9-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1a9, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_Jump, /* e84cd1aa-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1aa, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_Land, /* e84cd1ab-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1ab, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_MachineGun, /* e84cd1ac-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1ac, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_Punched, /* e84cd1ad-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1ad, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_RocketLaunch, /* e84cd1ae-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1ae, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_SecretDoor, /* e84cd1af-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1af, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +DEFINE_GUID(GUID_SwitchClick, /* e84cd1b0-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1b0, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_WindGust, /* e84cd1b1-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1b1, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_WindShear, /* e84cd1b2-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1b2, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_Pistol, /* e84cd1b3-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1b3, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_Shotgun, /* e84cd1b4-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1b4, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_Laser1, /* e84cd1b5-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1b5, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_Laser2, /* e84cd1b6-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1b6, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_Laser3, /* e84cd1b7-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1b7, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_Laser4, /* e84cd1b8-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1b8, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_Laser5, /* e84cd1b9-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1b9, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_Laser6, /* e84cd1ba-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1ba, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_OutOfAmmo, /* e84cd1bb-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1bb, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_LightningGun, /* e84cd1bc-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1bc, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_Missile, /* e84cd1bd-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1bd, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_GatlingGun, /* e84cd1be-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1be, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_ShortPlasma, /* e84cd1bf-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1bf, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_PlasmaCannon1, /* e84cd1c0-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1c0, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_PlasmaCannon2, /* e84cd1c1-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1c1, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_Cannon, /* e84cd1c2-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1c2, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); +#endif + +DEFINE_GUID(GUID_RawForce, /* e84cd1c6-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1c6, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +DEFINE_GUID(GUID_VFXEffect, /* e84cd1c7-81fa-11d0-94ab-0080c74c7e95 */ + 0xe84cd1c7, + 0x81fa, + 0x11d0, + 0x94, 0xab, 0x00, 0x80, 0xc7, 0x4c, 0x7e, 0x95 + ); + +#endif //_SWD_GUID_SEEN + diff --git a/src/io/swff_lib.cpp b/src/io/swff_lib.cpp new file mode 100644 index 0000000..6eaa8b6 --- /dev/null +++ b/src/io/swff_lib.cpp @@ -0,0 +1,2485 @@ +/**************************************************************************** + +Module name: SWFF_LIB.cpp + + (C) Copyright Microsoft Corp. 1993. All rights reserved. + + You have a royalty-free right to use, modify, reproduce and + distribute the Library Files (and/or any modified version) in + any way you find useful, provided that you agree that + Microsoft has no warranty obligations or liability for any + Sample Application Files which are modified. + + +Purpose: This module provides routines to simplify creating Effects + using the DirectInput Force Feedback subsystem. + +Algorithm: + +Version Date Author Comments +------- --------- ------ -------- + 1.1 01-Apr-97 MEA/DJS + 15-Apr-97 MEA Moved prototype SWFF_SetDuration to sw_force.h + 16-Apr-97 DMS Added SWFF_CreateEffectFromVFXEx + 14-May-97 DMS Added SWFF_PutRawAxisForce + and SWFF_CreateRawAxisForceEffect + 22-May-97 DMS Added SWFF_CreateEffectFromVFX2 + and SWFF_CreateEffectFromVFX2Ex + and SWFF_CreateEffectFromVFXBuffer + + +****************************************************************************/ + +#include +#include +#include +#include "mmsystem.h" +#include "sw_force.h" // SideWinder Force Feedback Header File + +#define INITGUIDS // Make GUID available +#include "sw_guid.hpp" +#include "vdinput.h" +#undef INITGUIDS + + +BOOL CALLBACK DIEnumAndDestroyCreatedEffectsProc(LPDIRECTINPUTEFFECT pDIEffect, LPVOID lpvRef); +BOOL CALLBACK DIEnumDevicesProc(LPCDIDEVICEINSTANCE lpddi, LPVOID lpvContext); + + +// ---------------------------------------------------------------------------- +// +// ***** FUNCTIONS ************************************************************ +// +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +// Function: SWFF_OpenDefaultFFJoystick +// Parameters: HWND hWnd - Client Window Handle +// LPDIRECTINPUT* ppDI - Pointer to DIRECTINPUT +// LPDIRECTINPUTDEVICE2* ppDIDevice) - Pointer to IDIRECTINPUTDEVICE2 +// +// Returns: +// Algorithm: +// Comments: +// ---------------------------------------------------------------------------- +HRESULT SWFF_OpenDefaultFFJoystick( + IN HWND hWnd, + LPDIRECTINPUT* ppDI, + LPDIRECTINPUTDEVICE2* ppDIDevice) +{ + HRESULT hResult; + if(hWnd == NULL || ppDI == NULL || ppDIDevice == NULL) + { + return SFERR_INVALID_PARAM; + } + + // create the DirectInput object + hResult = DirectInputCreate(GetModuleHandle(NULL), DIRECTINPUT_VERSION, ppDI, NULL); + if(FAILED(hResult)) + return hResult; + + // enumerate the first attached joystick + // instance goes in pDIDeviceInstance + DIDEVICEINSTANCE DIDeviceInstance; + DIDeviceInstance.dwDevType = 0; + hResult = (*ppDI)->EnumDevices(DIDEVTYPE_JOYSTICK, DIEnumDevicesProc, &DIDeviceInstance, DIEDFL_FORCEFEEDBACK); + if(FAILED(hResult)) + { + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + if(DIDeviceInstance.dwDevType == 0) + { + (*ppDI)->Release(); + *ppDI = NULL; + return DIERR_DEVICENOTREG; + } + + // create the DirectInput Device object + LPDIRECTINPUTDEVICE pDIDevice = NULL; + hResult = (*ppDI)->CreateDevice(DIDeviceInstance.guidInstance, &pDIDevice, NULL); + if(FAILED(hResult)) + { + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + + // get a pointer to its DirectInputDevice2 interface + hResult = pDIDevice->QueryInterface(IID_IDirectInputDevice2, (void**)ppDIDevice); + if(FAILED(hResult)) + { + pDIDevice->Release(); + pDIDevice = NULL; + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + pDIDevice->Release(); + pDIDevice = NULL; + + // set the data format to the pre-defined DirectInput joystick format + hResult = (*ppDIDevice)->SetDataFormat(&c_dfDIJoystick); + if(FAILED(hResult)) + { + (*ppDIDevice)->Release(); + *ppDIDevice = NULL; + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + + // set the cooperative level + hResult = (*ppDIDevice)->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND); + if(FAILED(hResult)) + { + (*ppDIDevice)->Release(); + *ppDIDevice = NULL; + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + + // turn auto-center off +/* DIPROPDWORD DIPropAutoCenter; + DIPropAutoCenter.diph.dwSize = sizeof(DIPropAutoCenter); + DIPropAutoCenter.diph.dwHeaderSize = sizeof(DIPROPHEADER); + DIPropAutoCenter.diph.dwObj = 0; + DIPropAutoCenter.diph.dwHow = DIPH_DEVICE; + DIPropAutoCenter.dwData = 0; + + hResult = (*ppDIDevice)->SetProperty(DIPROP_AUTOCENTER, &DIPropAutoCenter.diph); + if(FAILED(hResult)) + { + (*ppDIDevice)->Release(); + *ppDIDevice = NULL; + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } +*/ + // acquire the joystick + hResult = (*ppDIDevice)->Acquire(); + if(FAILED(hResult)) + { + (*ppDIDevice)->Release(); + *ppDIDevice = NULL; + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + return SUCCESS; +} + + +// ---------------------------------------------------------------------------- +// Function: SWFF_OpenDefaultFFJoystickEx +// Parameters: HWND hWnd - Client Window Handle +// LPDIRECTINPUT* ppDI - Pointer to IDIRECTINPUT +// HINSTANCE hInstance - object instance handle +// LPDIRECTINPUTDEVICE2* ppDIDevice) - Pointer to IDIRECTINPUTDEVICE2 +// DWORD dwFlags - DISCL_EXCLUSIVE | DISCL_FOREGROUND +// +// Returns: +// Algorithm: +// Comments: +// ---------------------------------------------------------------------------- +HRESULT SWFF_OpenDefaultFFJoystickEx( + IN HWND hWnd, + IN HINSTANCE hInstance, + OUT LPDIRECTINPUT* ppDI, + OUT LPDIRECTINPUTDEVICE2* ppDIDevice, + IN DWORD dwFlags) +{ + HRESULT hResult; + if(hWnd == NULL || hInstance == NULL || ppDI == NULL || ppDIDevice == NULL) + { + return SFERR_INVALID_PARAM; + } + + // create the DirectInput object + hResult = DirectInputCreate(hInstance, DIRECTINPUT_VERSION, ppDI, NULL); + if(FAILED(hResult)) + return hResult; + + // enumerate the first attached joystick + // instance goes in pDIDeviceInstance + DIDEVICEINSTANCE DIDeviceInstance; + DIDeviceInstance.dwDevType = 0; + hResult = (*ppDI)->EnumDevices(DIDEVTYPE_JOYSTICK, DIEnumDevicesProc, &DIDeviceInstance, DIEDFL_FORCEFEEDBACK); + if(FAILED(hResult)) + { + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + if(DIDeviceInstance.dwDevType == 0) + { + (*ppDI)->Release(); + *ppDI = NULL; + return DIERR_DEVICENOTREG; + } + + // create the DirectInput Device object + LPDIRECTINPUTDEVICE pDIDevice = NULL; + hResult = (*ppDI)->CreateDevice(DIDeviceInstance.guidInstance, &pDIDevice, NULL); + if(FAILED(hResult)) + { + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + + // get a pointer to its DirectInputDevice2 interface + hResult = pDIDevice->QueryInterface(IID_IDirectInputDevice2, (void**)ppDIDevice); + if(FAILED(hResult)) + { + pDIDevice->Release(); + pDIDevice = NULL; + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + pDIDevice->Release(); + pDIDevice = NULL; + + // set the data format to the pre-defined DirectInput joystick format + hResult = (*ppDIDevice)->SetDataFormat(&c_dfDIJoystick); + if(FAILED(hResult)) + { + (*ppDIDevice)->Release(); + *ppDIDevice = NULL; + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + + // set the cooperative level + hResult = (*ppDIDevice)->SetCooperativeLevel(hWnd, dwFlags); + if(FAILED(hResult)) + { + (*ppDIDevice)->Release(); + *ppDIDevice = NULL; + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + + // turn auto-center off + DIPROPDWORD DIPropAutoCenter; + DIPropAutoCenter.diph.dwSize = sizeof(DIPropAutoCenter); + DIPropAutoCenter.diph.dwHeaderSize = sizeof(DIPROPHEADER); + DIPropAutoCenter.diph.dwObj = 0; + DIPropAutoCenter.diph.dwHow = DIPH_DEVICE; + DIPropAutoCenter.dwData = 0; + + hResult = (*ppDIDevice)->SetProperty(DIPROP_AUTOCENTER, &DIPropAutoCenter.diph); + if(FAILED(hResult)) + { + (*ppDIDevice)->Release(); + *ppDIDevice = NULL; + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + + // acquire the joystick + hResult = (*ppDIDevice)->Acquire(); + if(FAILED(hResult)) + { + (*ppDIDevice)->Release(); + *ppDIDevice = NULL; + (*ppDI)->Release(); + *ppDI = NULL; + return hResult; + } + return SUCCESS; +} + + +BOOL CALLBACK DIEnumDevicesProc(LPCDIDEVICEINSTANCE lpddi, LPVOID lpvContext) +{ + LPDIDEVICEINSTANCE pDIDeviceInstance = (LPDIDEVICEINSTANCE)lpvContext; + if(pDIDeviceInstance == NULL) + { + return DIENUM_STOP; + } + + if(GET_DIDEVICE_TYPE(lpddi->dwDevType) == DIDEVTYPE_JOYSTICK) + { + memcpy((LPVOID)pDIDeviceInstance, (LPVOID)lpddi, sizeof(*pDIDeviceInstance)); + + return DIENUM_STOP; + } + + return DIENUM_CONTINUE; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_DestroyEffect +// Purpose: Destroys one effect or all effects +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT pDIEffect - Effect to destroy +// - NULL destroys all effects +// Returns: +// Algorithm: +// Comments: +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_DestroyEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN LPDIRECTINPUTEFFECT pDIEffect) +{ + HRESULT hResult = SUCCESS; + if(pDIEffect != NULL) + { + pDIEffect->Release(); + } + else + hResult = SWFF_DestroyAllEffects(pDIDevice); + + return hResult; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_DestroyAllEffects +// Purpose: Destroys all created effects +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// Returns: +// Algorithm: +// Comments: +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_DestroyAllEffects( + IN LPDIRECTINPUTDEVICE2 pDIDevice) +{ + HRESULT hResult; + + if(pDIDevice == NULL) + return SFERR_INVALID_PARAM; + + hResult = pDIDevice->EnumCreatedEffectObjects(DIEnumAndDestroyCreatedEffectsProc, NULL, 0); + + return hResult; +} + +BOOL CALLBACK DIEnumAndDestroyCreatedEffectsProc(LPDIRECTINPUTEFFECT pDIEffect, LPVOID lpvRef) +{ + pDIEffect->Release(); + + return DIENUM_CONTINUE; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_SetGain +// Purpose: Sets Gain for the Effect +// Parameters: LPDIRECTINPUTEFFECT pDIEffect - Pointer to effect to set the gain for +// DWORD dwGain - Gain in 1 to 10000 +// Returns: +// Algorithm: +// Comments: +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_SetGain( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN DWORD dwGain) +{ + if(pDIEffect == NULL) + return SFERR_INVALID_PARAM; + + DIEFFECT thisEffect = { sizeof(DIEFFECT) }; + thisEffect.dwGain = dwGain; + return pDIEffect->SetParameters(&thisEffect, DIEP_GAIN); +} + + +// ---------------------------------------------------------------------------- +// Function: SWFF_SetDirection +// Purpose: Sets 2D Angle Direction for the Effect +// Parameters: LPDIRECTINPUTEFFECT pDIEffect - Pointer to effect to set the direction for +// DWORD dwAngle - Direction in 0 to 35999 +// Returns: +// Algorithm: +// Comments: +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_SetDirection( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN DWORD dwAngle) +{ + if(pDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // set up a DIEFFECT structure so we can change direction + LONG rglDirection[2]; + DIEFFECT thisEffect = {sizeof(DIEFFECT)}; + thisEffect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + thisEffect.cAxes = 2; + thisEffect.rgdwAxes = NULL; + thisEffect.rglDirection = rglDirection; + thisEffect.rglDirection[0] = dwAngle; + return pDIEffect->SetParameters(&thisEffect, DIEP_DIRECTION); +} + +HRESULT SWFF_SetDirectionGain( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN DWORD dwAngle, + IN DWORD dwGain) +{ + if(pDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // set up a DIEFFECT structure so we can change direction + LONG rglDirection[2]; + DIEFFECT thisEffect = {sizeof(DIEFFECT)}; + + thisEffect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + thisEffect.cAxes = 2; + thisEffect.rgdwAxes = NULL; + thisEffect.rglDirection = rglDirection; + thisEffect.rglDirection[0] = dwAngle; + thisEffect.dwGain = dwGain; + return pDIEffect->SetParameters(&thisEffect, DIEP_DIRECTION | DIEP_GAIN); +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_SetDuration +// Purpose: Sets Duration for the Effect +// Parameters: LPDIRECTINPUTEFFECT pDIEffect - Pointer to effect to set the duration for +// DWORD dwDuration - In uSecs, INFINITE is FOREVER +// Returns: +// Algorithm: +// Comments: +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_SetDuration( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN DWORD dwDuration) +{ + if(pDIEffect == NULL) + return SFERR_INVALID_PARAM; + + DIEFFECT thisEffect = { sizeof(DIEFFECT) }; + thisEffect.dwDuration = dwDuration; + return pDIEffect->SetParameters(&thisEffect, DIEP_DURATION); +} + + +// ---------------------------------------------------------------------------- +// Function: SWFF_PutRawForce +// Purpose: Sends Force Value,Direction to ff Device +// Parameters: LPDIRECTINPUTEFFECT pDIEffect - Pointer to a two axis raw force object +// LONG lMagnitude - -10000 to +10000 force value +// DWORD dwDirection - 0 to 35999 +// Returns: +// Algorithm: +// Comments: To use this, you need to create the effect using +// SWFF_CreateRawForceEffect() first +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_PutRawForce( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN LONG lMagnitude, + IN DWORD dwDirection) +{ + if(pDIEffect == NULL) + return SFERR_INVALID_PARAM; + + DICONSTANTFORCE DIConstantForceStruct; + DIConstantForceStruct.lMagnitude = lMagnitude; + + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = dwDirection; + rglDirection[1] = 0; + + DIEFFECT thisEffect = { sizeof(DIEFFECT) }; + thisEffect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + thisEffect.cAxes = 2; + thisEffect.rgdwAxes = rgdwAxes; + thisEffect.rglDirection = rglDirection; + + thisEffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + thisEffect.lpvTypeSpecificParams = &DIConstantForceStruct; + return pDIEffect->SetParameters(&thisEffect, DIEP_DIRECTION|DIEP_TYPESPECIFICPARAMS); +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_PutRawAxisForce +// Purpose: Sends Force Value,Direction to ff Device +// Parameters: LPDIRECTINPUTEFFECT pDIEffect - Pointer to a one axis raw force object +// LONG lMagnitude - -10000 to +10000 force value +// Returns: +// Algorithm: +// Comments: To use this, you need to create the effect using +// SWFF_CreateRawAxisForceEffect() first +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_PutRawAxisForce( + IN LPDIRECTINPUTEFFECT pDIEffect, + IN LONG lMagnitude) +{ + if(pDIEffect == NULL) + return SFERR_INVALID_PARAM; + + DICONSTANTFORCE DIConstantForceStruct; + DIConstantForceStruct.lMagnitude = lMagnitude; + + DIEFFECT thisEffect = { sizeof(DIEFFECT) }; + + thisEffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + thisEffect.lpvTypeSpecificParams = &DIConstantForceStruct; + return pDIEffect->SetParameters(&thisEffect, DIEP_TYPESPECIFICPARAMS); +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateRawForceEffect +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// LONG lMagnitude - -10000 to 10000 +// DWORD dwDirection - 0 to 35999 +// +// +// Returns: +// Algorithm: +// Comments: Create this Effect once, then use SetParameter(...) to play the +// force value +// +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateRawForceEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT * ppDIEffect, + IN LONG lMagnitude, + IN DWORD dwDirection) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + // Always clear return IPtr + *ppDIEffect = NULL; + + LPDIRECTINPUTEFFECT pRawForce; + + DICONSTANTFORCE DIConstantForceStruct; + DIConstantForceStruct.lMagnitude = lMagnitude; + + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = dwDirection; + rglDirection[1] = 0; + + DIEFFECT DIEffect; + DIEffect.dwSize = sizeof(DIEFFECT); + DIEffect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + DIEffect.dwDuration = INFINITE; + DIEffect.dwSamplePeriod = HZ_TO_uS(100); + DIEffect.dwGain = 10000; + DIEffect.dwTriggerButton = DIEB_NOTRIGGER; + DIEffect.dwTriggerRepeatInterval= 0; + DIEffect.cAxes = 2; + DIEffect.rgdwAxes = rgdwAxes; + DIEffect.rglDirection = rglDirection; + DIEffect.lpEnvelope = NULL; + DIEffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + DIEffect.lpvTypeSpecificParams = &DIConstantForceStruct; + + HRESULT hRet; + hRet = pDIDevice->CreateEffect(GUID_RawForce, &DIEffect, &pRawForce, NULL); + if(FAILED(hRet)) return hRet; + + *ppDIEffect = pRawForce; + return SUCCESS; +} +#endif + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateRawAxisForceEffect +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// LONG lMagnitude - -10000 to 10000 +// DWORD dwAxis - Either X_AXIS or Y_AXIS +// +// +// Returns: +// Algorithm: +// Comments: Create this Effect once, then use SetParameter(...) to play the +// force value +// +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateRawAxisForceEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT * ppDIEffect, + IN LONG lMagnitude, + IN DWORD dwAxis) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + if(!(dwAxis == X_AXIS || dwAxis == Y_AXIS)) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + LPDIRECTINPUTEFFECT pRawForce; + + DICONSTANTFORCE DIConstantForceStruct; + DIConstantForceStruct.lMagnitude = lMagnitude; + + DWORD rgdwAxes[1]; + if(dwAxis == X_AXIS) + rgdwAxes[0] = DIJOFS_X; + else + rgdwAxes[0] = DIJOFS_Y; + + LONG rglDirection[1]; + rglDirection[0] = 0; + + DIEFFECT DIEffect; + DIEffect.dwSize = sizeof(DIEFFECT); + DIEffect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_CARTESIAN; + DIEffect.dwDuration = INFINITE; + DIEffect.dwSamplePeriod = HZ_TO_uS(100); + DIEffect.dwGain = 10000; + DIEffect.dwTriggerButton = DIEB_NOTRIGGER; + DIEffect.dwTriggerRepeatInterval= 0; + DIEffect.cAxes = 1; + DIEffect.rgdwAxes = rgdwAxes; + DIEffect.rglDirection = rglDirection; + DIEffect.lpEnvelope = NULL; + DIEffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + DIEffect.lpvTypeSpecificParams = &DIConstantForceStruct; + + HRESULT hRet; + hRet = pDIDevice->CreateEffect(GUID_RawForce, &DIEffect, &pRawForce, NULL); + if(FAILED(hRet)) return hRet; + + *ppDIEffect = pRawForce; + return SUCCESS; +} +#endif + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateROMEffect +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// REFGUID refGUID - GUID for ROM Effect +// DWORD dwDuration - uS +// DWORD dwGain - 1 to 10000 +// DWORD dwDirection - 0 to 35999 +// LONG lButton - Index of playback button, -1 for none +// +// Returns: +// Algorithm: +// Comments: Assumes valid GUID for the ROM Effect +// Note: If unmodified ROM Effect, user has to pass +// DEFAULT_ROM_EFFECT_DURATION, DEFAULT_ROM_EFFECT_GAIN +// ---------------------------------------------------------------------------- +HRESULT SWFF_CreateROMEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT * ppDIEffect, + IN REFGUID refGUID, + IN DWORD dwDuration, + IN DWORD dwGain, + IN DWORD dwDirection, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + // Always clear return IPtr + *ppDIEffect = NULL; + + LPDIRECTINPUTEFFECT pROMEffect = NULL; + + // Default NO Envelope + DIENVELOPE DIEnvelopeStruct; + DIEnvelopeStruct.dwSize = sizeof(DIENVELOPE); + DIEnvelopeStruct.dwAttackTime = 0; + DIEnvelopeStruct.dwAttackLevel = 10000; + DIEnvelopeStruct.dwFadeTime = 0; + DIEnvelopeStruct.dwFadeLevel = 10000; + + // 2DOF + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = dwDirection; + rglDirection[1] = 0; + + DIEFFECT DIEffect; + DIEffect.dwSize = sizeof(DIEFFECT); + DIEffect.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + // Set Duration and Gain to use Default ROM Effect params unless overridden + DIEffect.dwDuration = dwDuration; // can be DEFAULT_ROM_EFFECT_DURATION + DIEffect.dwSamplePeriod = DEFAULT_ROM_EFFECT_OUTPUTRATE; + DIEffect.dwGain = dwGain; // can be DEFAULT_ROM_EFFECT_GAIN; + // + DIEffect.dwTriggerButton = lButton == -1 ? DIEB_NOTRIGGER : FIELD_OFFSET(DIJOYSTATE, rgbButtons) + lButton; + DIEffect.dwTriggerRepeatInterval= 0; + DIEffect.cAxes = 2; + DIEffect.rgdwAxes = rgdwAxes; + DIEffect.rglDirection = rglDirection; + DIEffect.lpEnvelope = &DIEnvelopeStruct; + DIEffect.cbTypeSpecificParams = 0; + DIEffect.lpvTypeSpecificParams = NULL; + + HRESULT hRet = pDIDevice->CreateEffect(refGUID, &DIEffect, &pROMEffect, NULL); + if(FAILED(hRet)) return hRet; + + *ppDIEffect = pROMEffect; + return SUCCESS; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_WriteRegString +// Parameters: LPCTSTR pszKey - The key under HKCR to place the value +// LPCTSTR pszValue - The string value for pszKey +// +// Returns: TRUE if the registry entry was successfully made +// Algorithm: +// Comments: Helper function for SWFF_RegisterVFXObject to write registry entries +// Note: +// ---------------------------------------------------------------------------- +BOOL SWFF_WriteRegString( + IN LPCTSTR pszKey, + IN LPCTSTR pszValue) +{ + HKEY hKey; + LONG lRet; + int nLen; + + if(pszKey == NULL || pszValue == NULL) + return FALSE; + + // create it + hKey = HKEY_CLASSES_ROOT; + lRet = RegCreateKey(hKey, pszKey, &hKey); + if(lRet != ERROR_SUCCESS) + return FALSE; + + // save the value into the key + nLen = strlen(pszValue); + lRet = RegSetValueEx(hKey, NULL, 0, REG_SZ, (PBYTE)pszValue, nLen + 1); + if(lRet != ERROR_SUCCESS) + { + RegCloseKey(hKey); + return FALSE; + } + + // close the key + RegCloseKey(hKey); + if(lRet != ERROR_SUCCESS) + return FALSE; + + // if we have reached this point, then it was a success + return TRUE; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_RegisterVFXObject +// Parameters: LPCTSTR - Pointer to the fully-qualified path name of VFX.DLL +// +// Returns: TRUE if the object was successfully registered +// Algorithm: +// Comments: Example of code to register the VFX com object. +// You supply lpszVFXPath depending on where you install VFX.DLL +// Note: +// ---------------------------------------------------------------------------- +#define GUID_VFX_Object "{04ace0a7-1fa8-11d0-aa22-00a0c911f471}" +BOOL SWFF_RegisterVFXObject(IN LPCTSTR pszVFXPath) +{ + if(pszVFXPath == NULL) + return FALSE; + + return SWFF_WriteRegString("\\VFX1.0", "VFX Object") + && SWFF_WriteRegString("\\VFX1.0\\CLSID", GUID_VFX_Object) + && SWFF_WriteRegString("\\VFX", "VFX Object") + && SWFF_WriteRegString("\\VFX\\CurVer", "VFX1.0") + && SWFF_WriteRegString("\\VFX\\CLSID", GUID_VFX_Object) + && SWFF_WriteRegString("\\CLSID\\"GUID_VFX_Object, "VFX Object") + && SWFF_WriteRegString("\\CLSID\\"GUID_VFX_Object"\\VersionIndependentProgID", "VFX") + && SWFF_WriteRegString("\\CLSID\\"GUID_VFX_Object"\\InprocServer32", pszVFXPath) + && SWFF_WriteRegString("\\CLSID\\"GUID_VFX_Object"\\NotInsertable", ""); +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateVFXEffectFromFile +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// TCHAR *pszFileName - Pointer to VFX File name +// +// Returns: +// Algorithm: +// Comments: +// Note: +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateVFXEffectFromFile( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const TCHAR *pszFileName) +{ + if(pDIDevice == NULL || ppDIEffect == NULL || pszFileName == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + LPDIRECTINPUTEFFECT pVFXEffect; + + VFX_PARAM VFXParam; + VFXParam.m_Bytes = sizeof(VFX_PARAM); + VFXParam.m_PointerType = VFX_FILENAME; + VFXParam.m_BufferSize = 0; + VFXParam.m_pFileNameOrBuffer = (PVOID) pszFileName; + + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = DEFAULT_VFX_EFFECT_DIRECTION; + rglDirection[1] = 0; + + DIEFFECT DIEffectStruct; + DIEffectStruct.dwSize = sizeof(DIEFFECT); + DIEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + DIEffectStruct.dwDuration = DEFAULT_VFX_EFFECT_DURATION; + DIEffectStruct.dwSamplePeriod = HZ_TO_uS(100); + DIEffectStruct.dwGain = DEFAULT_VFX_EFFECT_GAIN; + DIEffectStruct.dwTriggerButton = DIEB_NOTRIGGER; + DIEffectStruct.dwTriggerRepeatInterval = 0; + DIEffectStruct.cAxes = 2; + DIEffectStruct.rgdwAxes = rgdwAxes; + DIEffectStruct.rglDirection = rglDirection; + DIEffectStruct.lpEnvelope = NULL; + DIEffectStruct.cbTypeSpecificParams = sizeof(VFX_PARAM); + DIEffectStruct.lpvTypeSpecificParams = &VFXParam; + + HRESULT hResult; + hResult = pDIDevice->CreateEffect(GUID_VFXEffect, &DIEffectStruct, &pVFXEffect, NULL); + if(FAILED(hResult)) return hResult; + + *ppDIEffect = pVFXEffect; + return hResult; +} +#endif + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateVFXEffectFromFileEx +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// TCHAR *pszFileName - Pointer to VFX File +// DWORD dwDuration - INFINITE or default +// DWORD dwGain - 1 to 10000 +// DWORD dwDirection - 0 to 35999 +// +// Returns: +// Algorithm: +// Comments: +// Note: +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateVFXEffectFromFileEx( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const TCHAR *pszFileName, + IN DWORD dwDuration, + IN DWORD dwGain, + IN DWORD dwDirection) +{ + if(pDIDevice == NULL || ppDIEffect == NULL || pszFileName == NULL) + return SFERR_INVALID_PARAM; + // Always clear return IPtr + *ppDIEffect = NULL; + + LPDIRECTINPUTEFFECT pVFXEffect; + + VFX_PARAM VFXParam; + VFXParam.m_Bytes = sizeof(VFX_PARAM); + VFXParam.m_PointerType = VFX_FILENAME; + VFXParam.m_BufferSize = 0; + VFXParam.m_pFileNameOrBuffer = (PVOID) pszFileName; + + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = dwDirection; + rglDirection[1] = 0; + + DIEFFECT DIEffectStruct; + DIEffectStruct.dwSize = sizeof(DIEFFECT); + DIEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + DIEffectStruct.dwDuration = dwDuration; + DIEffectStruct.dwSamplePeriod = HZ_TO_uS(100); + DIEffectStruct.dwGain = dwGain; + DIEffectStruct.dwTriggerButton = DIEB_NOTRIGGER; + DIEffectStruct.dwTriggerRepeatInterval = 0; + DIEffectStruct.cAxes = 2; + DIEffectStruct.rgdwAxes = rgdwAxes; + DIEffectStruct.rglDirection = rglDirection; + DIEffectStruct.lpEnvelope = NULL; + DIEffectStruct.cbTypeSpecificParams = sizeof(VFX_PARAM); + DIEffectStruct.lpvTypeSpecificParams = &VFXParam; + + HRESULT hResult; + hResult = pDIDevice->CreateEffect(GUID_VFXEffect, &DIEffectStruct, &pVFXEffect, NULL); + if(FAILED(hResult)) return hResult; + + *ppDIEffect = pVFXEffect; + return hResult; +} +#endif + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateVFXEffectFromBuffer +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// LPVOID pBuffer - Pointer to VFX Buffer +// DWORD dwBufferSize - Buffer size in bytes +// +// Returns: +// Algorithm: +// Comments: +// Note: +// If you are compiling the FRC files as resources in your executable +// by putting #include "script.vfx" in your .rc file, you would use +// code similar to the following to create the effect (no error checking). +// +// HRSRC hResInfo = FindResource(NULL, "IDF_FOO", "FORCE"); +// DWORD dwBytes = SizeofResource(NULL, hResInfo); +// HGLOBAL hRsrc = LoadResource(NULL, hResInfo); +// PVOID pBuffer = LockResource(hRsrc); +// SWFF_CreateEffectFromVFXBuffer(pDIDevice, pBuffer, dwBytes, &pDIEffect); +// +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateVFXEffectFromBuffer( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const LPVOID pBuffer, + IN DWORD dwBufferSize) +{ + if(pDIDevice == NULL || ppDIEffect == NULL || pBuffer == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + LPDIRECTINPUTEFFECT pVFXEffect; + + VFX_PARAM VFXParam; + VFXParam.m_Bytes = sizeof(VFX_PARAM); + VFXParam.m_PointerType = VFX_BUFFER; + VFXParam.m_BufferSize = dwBufferSize; + VFXParam.m_pFileNameOrBuffer = pBuffer; + + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = DEFAULT_VFX_EFFECT_DIRECTION; + rglDirection[1] = 0; + + DIEFFECT DIEffectStruct; + DIEffectStruct.dwSize = sizeof(DIEFFECT); + DIEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + DIEffectStruct.dwDuration = DEFAULT_VFX_EFFECT_DURATION; + DIEffectStruct.dwSamplePeriod = HZ_TO_uS(100); + DIEffectStruct.dwGain = DEFAULT_VFX_EFFECT_GAIN; + DIEffectStruct.dwTriggerButton = DIEB_NOTRIGGER; + DIEffectStruct.dwTriggerRepeatInterval = 0; + DIEffectStruct.cAxes = 2; + DIEffectStruct.rgdwAxes = rgdwAxes; + DIEffectStruct.rglDirection = rglDirection; + DIEffectStruct.lpEnvelope = NULL; + DIEffectStruct.cbTypeSpecificParams = sizeof(VFX_PARAM); + DIEffectStruct.lpvTypeSpecificParams = &VFXParam; + + HRESULT hResult; + hResult = pDIDevice->CreateEffect(GUID_VFXEffect, &DIEffectStruct, &pVFXEffect, NULL); + if(FAILED(hResult)) return hResult; + + *ppDIEffect = pVFXEffect; + return hResult; +} +#endif + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateVFXEffectFromBufferEx +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// LPVOID pBuffer - Pointer to VFX Buffer +// DWORD dwBufferSize - Buffer size in bytes +// DWORD dwDuration - INFINITE or default +// DWORD dwGain - 1 to 10000 +// DWORD dwDirection - 0 to 35999 +// +// Returns: +// Algorithm: +// Comments: +// Note: +// See note for SWFF_CreateVFXEffectFromBuffer(...) +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateVFXEffectFromBufferEx( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const LPVOID pBuffer, + IN DWORD dwBufferSize, + IN DWORD dwDuration, + IN DWORD dwGain, + IN DWORD dwDirection) +{ + if(pDIDevice == NULL || ppDIEffect == NULL || pBuffer == NULL) + return SFERR_INVALID_PARAM; + // Always clear return IPtr + *ppDIEffect = NULL; + + LPDIRECTINPUTEFFECT pVFXEffect; + + VFX_PARAM VFXParam; + VFXParam.m_Bytes = sizeof(VFX_PARAM); + VFXParam.m_PointerType = VFX_BUFFER; + VFXParam.m_BufferSize = dwBufferSize; + VFXParam.m_pFileNameOrBuffer = pBuffer; + + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = dwDirection; + rglDirection[1] = 0; + + DIEFFECT DIEffectStruct; + DIEffectStruct.dwSize = sizeof(DIEFFECT); + DIEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + DIEffectStruct.dwDuration = dwDuration; + DIEffectStruct.dwSamplePeriod = HZ_TO_uS(100); + DIEffectStruct.dwGain = dwGain; + DIEffectStruct.dwTriggerButton = DIEB_NOTRIGGER; + DIEffectStruct.dwTriggerRepeatInterval = 0; + DIEffectStruct.cAxes = 2; + DIEffectStruct.rgdwAxes = rgdwAxes; + DIEffectStruct.rglDirection = rglDirection; + DIEffectStruct.lpEnvelope = NULL; + DIEffectStruct.cbTypeSpecificParams = sizeof(VFX_PARAM); + DIEffectStruct.lpvTypeSpecificParams = &VFXParam; + + HRESULT hResult; + hResult = pDIDevice->CreateEffect(GUID_VFXEffect, &DIEffectStruct, &pVFXEffect, NULL); + if(FAILED(hResult)) return hResult; + + *ppDIEffect = pVFXEffect; + return hResult; +} +#endif + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateDIEffectFromFile +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// TCHAR *pszFileName - Pointer to VFX File name +// +// Returns: +// Algorithm: +// Comments: +// Note: If the file contains multiple effects or a custom effect this +// function will fail. Use SWFF_CreateDIEffectFromFileEx. +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateDIEffectFromFile( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const TCHAR *pszFileName) +{ + HRESULT hResult; + PVFX pIVFX; + + if(pDIDevice == NULL || pszFileName == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + *ppDIEffect = NULL; + + hResult = CoInitialize(NULL); + if(FAILED(hResult)) + return hResult; + + hResult = CoCreateInstance(CLSID_VFX, + NULL, + CLSCTX_INPROC_SERVER, + IID_IVFX, + (void**)&pIVFX); + if(pIVFX == NULL) + { + CoUninitialize(); + return SFERR_SYSTEM_INIT; + } + + if(FAILED(hResult)) + { + CoUninitialize(); + return hResult; + } + + // Create the Effect from a *.frc file + DWORD dwInFlags = VFXCE_CREATE_SINGLE; + hResult = pIVFX->CreateEffectFromFile(pDIDevice, ppDIEffect, + 0, pszFileName, NULL, NULL, dwInFlags, NULL); + + // clean up + pIVFX->Release(); + CoUninitialize(); + + if(FAILED(hResult)) return hResult; + + return hResult; +} +#endif + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateDIEffectFromFileEx +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT** pppDIEffect - Pointer to an array of +// LPDIRECTINPUTEFFECT's. +// This array is allocated +// by the function. Caller is +// responsible for deleting. +// PDWORD pdwEffectCount - Gets the number of effects in array +// TCHAR *pszFileName - Pointer to VFX File name +// PPVOID ppUDBuffer - Gets an array containing custom force +// samples. This array is allocated by +// the function. Caller is +// responsible for deleting. +// PDWORD pdwOutFlags - Receives 0 if the file contains +// a single effect. Otherwise +// it receives VFXCE_CONCATENATE +// or VFXCE_SUPERIMPOSE. +// +// Returns: +// Algorithm: +// Comments: +// Note: call delete [] pppDIEffect and delete [] ppUDBuffer after +// releasing the effects +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateDIEffectFromFileEx( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT** pppDIEffect, + IN OUT PDWORD pdwEffectCount, + IN const TCHAR *pszFileName, + IN OUT void** ppUDBuffer, + IN OUT PDWORD pdwOutFlags) +{ + // parameter check + if(pDIDevice == NULL || pszFileName == NULL || pppDIEffect == NULL || + pdwEffectCount == NULL || ppUDBuffer == NULL || pdwOutFlags == NULL) + { + return SFERR_INVALID_PARAM; + } + + // zero out the return values + *pppDIEffect = NULL; + *pdwEffectCount = 0; + *ppUDBuffer = NULL; + *pdwOutFlags = 0; + + HRESULT hResult; + PVFX pIVFX; + + hResult = CoInitialize(NULL); + if(FAILED(hResult)) + return hResult; + + hResult = CoCreateInstance(CLSID_VFX, + NULL, + CLSCTX_INPROC_SERVER, + IID_IVFX, + (void**)&pIVFX); + if(pIVFX == NULL) + { + CoUninitialize(); + return SFERR_SYSTEM_INIT; + } + + if(FAILED(hResult)) + { + CoUninitialize(); + return hResult; + } + + // see how big a DIEffect array we have to allocate, and see how much memory, if any, + // we need to allocate for UD sample caching + DWORD dwInFlags = VFXCE_CALC_BUFFER_SIZE | VFXCE_CALC_EFFECT_COUNT; + DWORD dwEffectCount; + DWORD dwBufferSize; + hResult = pIVFX->CreateEffectFromFile(NULL, NULL, + &dwEffectCount, pszFileName, NULL, &dwBufferSize, dwInFlags, NULL); + if(FAILED(hResult)) + { + pIVFX->Release(); + CoUninitialize(); + return hResult; + } + + // allocate memory for the effects + LPDIRECTINPUTEFFECT* ppDIEffect = new LPDIRECTINPUTEFFECT[dwEffectCount]; + if(ppDIEffect == NULL) + { + pIVFX->Release(); + CoUninitialize(); + return DIERR_OUTOFMEMORY; + } + + // allocate memory for the custom force samples + PVOID pUDBuffer = NULL; + if(dwBufferSize > 0) + { + pUDBuffer = new BYTE[dwBufferSize]; + if(pUDBuffer == NULL) + { + delete [] ppDIEffect; + ppDIEffect = NULL; + pIVFX->Release(); + CoUninitialize(); + return DIERR_OUTOFMEMORY; + } + } + + // Create the Effect from a *.frc file + DWORD dwOutFlags; + dwInFlags = VFXCE_CREATE_MULTIPLE; + hResult = pIVFX->CreateEffectFromFile(pDIDevice, ppDIEffect, + &dwEffectCount, pszFileName, pUDBuffer, &dwBufferSize, dwInFlags, &dwOutFlags); + + if(FAILED(hResult)) + { + delete [] ppDIEffect; + ppDIEffect = NULL; + delete [] pUDBuffer; + pUDBuffer = NULL; + pIVFX->Release(); + CoUninitialize(); + return hResult; + } + + // clean up + pIVFX->Release(); + CoUninitialize(); + + if(FAILED(hResult)) return hResult; + + // assign the results + *pppDIEffect = ppDIEffect; + *pdwEffectCount = dwEffectCount; + *ppUDBuffer = pUDBuffer; + *pdwOutFlags = dwOutFlags; + + return hResult; +} +#endif + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateDIEffectFromBuffer +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// LPVOID pBuffer - Pointer to VFX Buffer +// DWORD dwBufferSize - Buffer size in bytes +// +// Returns: +// Algorithm: +// Comments: +// Note: If the file contains multiple effects or a custom effect this +// function will fail. Use SWFF_CreateDIEffectFromBufferEx. +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateDIEffectFromBuffer( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN const LPVOID pBuffer, + IN DWORD dwBufferSize) +{ + HRESULT hResult; + PVFX pIVFX; + + if(pDIDevice == NULL || pBuffer == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + *ppDIEffect = NULL; + + hResult = CoInitialize(NULL); + if(FAILED(hResult)) + return hResult; + + hResult = CoCreateInstance(CLSID_VFX, + NULL, + CLSCTX_INPROC_SERVER, + IID_IVFX, + (void**)&pIVFX); + if(pIVFX == NULL) + { + CoUninitialize(); + return SFERR_SYSTEM_INIT; + } + + if(FAILED(hResult)) + { + CoUninitialize(); + return hResult; + } + + // Create the Effect from a *.frc file + DWORD dwInFlags = VFXCE_CREATE_SINGLE; + hResult = pIVFX->CreateEffectFromBuffer(pDIDevice, ppDIEffect, + 0, pBuffer, dwBufferSize, NULL, NULL, dwInFlags, NULL); + + // clean up + pIVFX->Release(); + CoUninitialize(); + + if(FAILED(hResult)) return hResult; + + return hResult; +} +#endif + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateDIEffectFromBufferEx +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - Pointer to DirectInputDevice +// LPDIRECTINPUTEFFECT** pppDIEffect - Pointer to an array of +// LPDIRECTINPUTEFFECT's. +// This array is allocated +// by the function. Caller is +// responsible for deleting. +// PDWORD pdwEffectCount - Gets the number of effects in array +// LPVOID pBuffer - Pointer to VFX Buffer +// DWORD dwBufferSize - Buffer size in bytes +// PPVOID ppUDBuffer - Gets an array containing custom force +// samples. This array is allocated by +// the function. Caller is +// responsible for deleting. +// PDWORD pdwOutFlags - Receives 0 if the file contains +// a single effect. Otherwise +// it receives VFXCE_CONCATENATE +// or VFXCE_SUPERIMPOSE. +// +// Returns: +// Algorithm: +// Comments: +// Note: call delete [] pppDIEffect and delete [] ppUDBuffer after +// releasing the effects +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateDIEffectFromBufferEx( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT** pppDIEffect, + IN OUT PDWORD pdwEffectCount, + IN const LPVOID pBuffer, + IN DWORD dwBufferSize, + IN OUT void** ppUDBuffer, + IN OUT PDWORD pdwOutFlags) +{ + // parameter check + if(pDIDevice == NULL || pBuffer == NULL || pppDIEffect == NULL|| + pdwEffectCount == NULL || ppUDBuffer == NULL || pdwOutFlags == NULL) + { + return SFERR_INVALID_PARAM; + } + + // zero out the return values + *pppDIEffect = NULL; + *pdwEffectCount = 0; + *ppUDBuffer = NULL; + *pdwOutFlags = 0; + + HRESULT hResult; + PVFX pIVFX; + + + + hResult = CoInitialize(NULL); + if(FAILED(hResult)) + return hResult; + + hResult = CoCreateInstance(CLSID_VFX, + NULL, + CLSCTX_INPROC_SERVER, + IID_IVFX, + (void**)&pIVFX); + if(pIVFX == NULL) + { + CoUninitialize(); + return SFERR_SYSTEM_INIT; + } + + if(FAILED(hResult)) + { + CoUninitialize(); + return hResult; + } + + // see how big a DIEffect array we have to allocate, and see how much memory, if any, + // we need to allocate for UD sample caching + DWORD dwInFlags = VFXCE_CALC_BUFFER_SIZE | VFXCE_CALC_EFFECT_COUNT; + DWORD dwEffectCount; + DWORD dwUDBufferSize; + hResult = pIVFX->CreateEffectFromBuffer(NULL, NULL, + &dwEffectCount, pBuffer, dwBufferSize, NULL, &dwUDBufferSize, dwInFlags, NULL); + if(FAILED(hResult)) + { + pIVFX->Release(); + CoUninitialize(); + return hResult; + } + + // allocate memory for the effects + LPDIRECTINPUTEFFECT* ppDIEffect = new LPDIRECTINPUTEFFECT[dwEffectCount]; + if(ppDIEffect == NULL) + { + pIVFX->Release(); + CoUninitialize(); + return DIERR_OUTOFMEMORY; + } + + // allocate memory for the custom force samples + PVOID pUDBuffer = NULL; + if(dwUDBufferSize > 0) + { + pUDBuffer = new BYTE[dwUDBufferSize]; + if(pUDBuffer == NULL) + { + delete [] ppDIEffect; + ppDIEffect = NULL; + pIVFX->Release(); + CoUninitialize(); + return DIERR_OUTOFMEMORY; + } + } + + // Create the Effect from a *.frc file + DWORD dwOutFlags; + dwInFlags = VFXCE_CREATE_MULTIPLE; + hResult = pIVFX->CreateEffectFromBuffer(pDIDevice, ppDIEffect, + &dwEffectCount, pBuffer, dwBufferSize, pUDBuffer, &dwUDBufferSize, dwInFlags, &dwOutFlags); + + if(FAILED(hResult)) + { + delete [] ppDIEffect; + ppDIEffect = NULL; + delete [] pUDBuffer; + pUDBuffer = NULL; + pIVFX->Release(); + CoUninitialize(); + return hResult; + } + + // clean up + pIVFX->Release(); + CoUninitialize(); + + if(FAILED(hResult)) return hResult; + + // assign the results + *pppDIEffect = ppDIEffect; + *pdwEffectCount = dwEffectCount; + *ppUDBuffer = pUDBuffer; + *pdwOutFlags = dwOutFlags; + + return hResult; +} +#endif + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreatePeriodicEffect +// Purpose: Creates a Periodic type Effect with specified params +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// DWORD dwType - Type of PERIODIC Effect (SINE | COSINE | ...) +// DWORD dwDuration - uS +// DWORD dwPeriod - uS +// DWORD dwDirection - 0 to 35999 +// DWORD dwMagnitude - 0 to 10000 +// LONG lOffset - Offset in -10000 to 10000 +// DWORD dwAttackTime - Envelope Attack Time in uS +// DWORD dwAttackLevel - Envelope Attack Level in 0 to 10000 +// DWORD dwFadeTime - Envelope Fade time in uS +// DWORD dwFadeLevel - Envelope Fade Level +// LONG lButton - Index of playback button, -1 for none +// +// Returns: +// Algorithm: +// Comments: +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_CreatePeriodicEffect( IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwType, + IN DWORD dwDuration, + IN DWORD dwPeriod, + IN DWORD dwDirection, + IN DWORD dwMagnitude, + IN LONG lOffset, + IN DWORD dwAttackTime, + IN DWORD dwAttackLevel, + IN DWORD dwFadeTime, + IN DWORD dwFadeLevel, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + // Always clear return IPtr + *ppDIEffect = NULL; + + // type-specific stuff + DWORD dwPhase = 0; + GUID guid; + switch(dwType) + { + case SINE: + guid = GUID_Sine; + break; + case COSINE: + guid = GUID_Sine; + dwPhase = 9000; + break; + case SQUARE_HIGH: + guid = GUID_Square; + break; + case SQUARE_LOW: + guid = GUID_Square; + dwPhase = 18000; + break; + case TRIANGLE_UP: + guid = GUID_Triangle; + break; + case TRIANGLE_DOWN: + guid = GUID_Triangle; + dwPhase = 18000; + break; + case SAWTOOTH_UP: + guid = GUID_SawtoothUp; + break; + case SAWTOOTH_DOWN: + guid = GUID_SawtoothDown; + break; + default: + // illegal + break; + } + + DIPERIODIC DIPeriodicStruct; + DIPeriodicStruct.dwMagnitude = dwMagnitude; + DIPeriodicStruct.lOffset = lOffset; + DIPeriodicStruct.dwPhase = dwPhase; + DIPeriodicStruct.dwPeriod = dwPeriod; + + DIENVELOPE DIEnvelopeStruct; + DIEnvelopeStruct.dwSize = sizeof(DIENVELOPE); + DIEnvelopeStruct.dwAttackTime = dwAttackTime; + DIEnvelopeStruct.dwAttackLevel = dwAttackLevel; + DIEnvelopeStruct.dwFadeTime = dwFadeTime; + DIEnvelopeStruct.dwFadeLevel = dwFadeLevel; + + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = dwDirection; + rglDirection[1] = 0; + + DIEFFECT DIEffectStruct; + DIEffectStruct.dwSize = sizeof(DIEFFECT); + DIEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + DIEffectStruct.dwDuration = dwDuration; + DIEffectStruct.dwSamplePeriod = HZ_TO_uS(100); + DIEffectStruct.dwGain = 10000; + DIEffectStruct.dwTriggerButton = lButton == -1 ? DIEB_NOTRIGGER : FIELD_OFFSET(DIJOYSTATE, rgbButtons) + lButton; + DIEffectStruct.dwTriggerRepeatInterval = 0; + DIEffectStruct.cAxes = 2; + DIEffectStruct.rgdwAxes = rgdwAxes; + DIEffectStruct.rglDirection = rglDirection; + DIEffectStruct.lpEnvelope = &DIEnvelopeStruct; + DIEffectStruct.cbTypeSpecificParams = sizeof(DIPeriodicStruct); + DIEffectStruct.lpvTypeSpecificParams = &DIPeriodicStruct; + + HRESULT hResult; + hResult = pDIDevice->CreateEffect(guid, &DIEffectStruct, ppDIEffect, NULL); + + return hResult; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateSpringEffect +// Purpose: Creates a Spring type Effect with specified params +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// DWORD dwDuration - Duration in uS +// LONG lKx - X-Axis K Coefficient in -10000 to 10000 +// LONG lCenterx - X-Axis Center in -10000 to 10000 +// LONG lKy - Y-Axis K Coefficient in -10000 to 10000 +// LONG lCentery - Y-Axis Center in -10000 to 10000 +// LONG lButton - Index of playback button, -1 for none +// +// Returns: +// Algorithm: +// Comments: +// To create a 1D spring, set the lKx or lKy parameter to 0 +// To create a 2D spring, set both lKx and lKy parameter to non-zero +// or set both lFx and lFy to zero +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_CreateSpringEffect( IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN LONG lKx, + IN LONG lCenterx, + IN LONG lKy, + IN LONG lCentery, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + HRESULT hResult = SWFF_CreateConditionEffect(pDIDevice, + ppDIEffect, + SPRING, + dwDuration, + lKx, lCenterx, + lKy, lCentery, + lButton); + + return hResult; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateDamperEffect +// Purpose: Creates a Damper type Effect with specified params +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// DWORD dwDuration - Duration in uS +// LONG lBx - X-Axis B Coefficient +/-10000 +// LONG lV0x - X-Axis Initial Velocity +/-10000 +// LONG lBy - Y-Axis B Coefficient +/-10000 +// LONG lV0y - Y-Axis Initial Velocity +/-10000 +// LONG lButton - Index of playback button, -1 for none +// Returns: +// Algorithm: +// Comments: +// To create a 1D Damper, set the lBx or lBy parameter to 0 +// To create a 2D Damper, set both lBx and lBy parameter to non-zero +// or set both lFx and lFy to zero +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_CreateDamperEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN LONG lBx, + IN LONG lV0x, + IN LONG lBy, + IN LONG lV0y, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + HRESULT hResult = SWFF_CreateConditionEffect(pDIDevice, + ppDIEffect, + DAMPER, + dwDuration, + lBx, lV0x, + lBy, lV0y, + lButton); + + return hResult; +} + + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateInertiaEffect +// Purpose: Creates an Inertia type Effect with specified params +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// DWORD dwDuration - Duration in uS +// LONG lMx - X-Axis M Coefficient +/-10000 +// LONG lA0x - X-Axis Initial Acceleration +/-10000 +// LONG lMy - Y-Axis N Coefficient +/-10000 +// LONG lA0y - Y-Axis Initial Acceleration +/-10000 +// LONG lButton - Index of playback button, -1 for none +// Returns: +// Algorithm: +// Comments: +// To create a 1D Inertia, set the lMx or lMy parameter to 0 +// To create a 2D Inertia, set both lMx and lMy parameter to non-zero +// or set both lFx and lFy to zero +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_CreateInertiaEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN LONG lMx, + IN LONG lA0x, + IN LONG lMy, + IN LONG lA0y, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + HRESULT hResult = SWFF_CreateConditionEffect(pDIDevice, + ppDIEffect, + INERTIA, + dwDuration, + lMx, lA0x, + lMy, lA0y, + lButton); + + return hResult; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateFrictionEffect +// Purpose: Creates a Friction type Effect with specified params +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// DWORD dwDuration - Duration in uS +// LONG lFx - X-Axis F Coefficient +/-10000 +// LONG lFy - Y-Axis F Coefficient +/-10000 +// LONG lButton - Index of playback button, -1 for none +// Returns: +// Algorithm: +// Comments: +// To create a 1D Friction, set the lFx or lFy parameter to 0 +// To create a 2D Friction, set both lFx and lFy parameter to non-zero +// or set both lFx and lFy to zero +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_CreateFrictionEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN LONG lFx, + IN LONG lFy, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + HRESULT hResult = SWFF_CreateConditionEffect(pDIDevice, + ppDIEffect, + FRICTION, + dwDuration, + lFx, 0, + lFy, 0, + lButton); + + return hResult; +} + +HRESULT SWFF_CreateConditionEffectStruct( + di_condition_effect_struct *ptr, + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwType, + IN DWORD dwDuration, + IN LONG lXCoefficient, + IN LONG lXOffset, + IN LONG lYCoefficient, + IN LONG lYOffset, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + GUID guid; + switch(dwType) + { + case SPRING: + guid = GUID_Spring; + break; + case INERTIA: + guid = GUID_Inertia; + break; + case DAMPER: + guid = GUID_Damper; + break; + case FRICTION: + guid = GUID_Friction; + break; + default: + break; + } + + ptr->DIConditionStruct[0].lOffset = lXOffset; + ptr->DIConditionStruct[0].lPositiveCoefficient = lXCoefficient; + ptr->DIConditionStruct[0].lNegativeCoefficient = lXCoefficient; + ptr->DIConditionStruct[0].dwPositiveSaturation = 10000; + ptr->DIConditionStruct[0].dwNegativeSaturation = 10000; + ptr->DIConditionStruct[0].lDeadBand = 0; + ptr->DIConditionStruct[1].lOffset = lYOffset; + ptr->DIConditionStruct[1].lPositiveCoefficient = lYCoefficient; + ptr->DIConditionStruct[1].lNegativeCoefficient = lYCoefficient; + ptr->DIConditionStruct[1].dwPositiveSaturation = 10000; + ptr->DIConditionStruct[1].dwNegativeSaturation = 10000; + ptr->DIConditionStruct[1].lDeadBand = 0; + + DWORD rgdwAxes[2]; + int nAxisCount = 0; + if(lXCoefficient != 0) + { + rgdwAxes[nAxisCount] = DIJOFS_X; + nAxisCount++; + } + + if(lYCoefficient != 0) + { + rgdwAxes[nAxisCount] = DIJOFS_Y; + nAxisCount++; + } + + if(lXCoefficient == 0 && lYCoefficient == 0) + { + nAxisCount = 2; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + } + + DWORD cbTypeSpecificParams; + PVOID pvTypeSpecificParams; + + if (nAxisCount == 1) { + cbTypeSpecificParams = sizeof(DICONDITION[1]); + if (lXCoefficient) + pvTypeSpecificParams = &ptr->DIConditionStruct[0]; + else + pvTypeSpecificParams = &ptr->DIConditionStruct[1]; + + } else { + cbTypeSpecificParams = sizeof(DICONDITION[2]); + pvTypeSpecificParams = &ptr->DIConditionStruct[0]; + } + + ptr->rglDirection[0] = 0; + ptr->rglDirection[1] = 0; + + ptr->DIEffectStruct.dwSize = sizeof(DIEFFECT); + ptr->DIEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_CARTESIAN; + ptr->DIEffectStruct.dwDuration = dwDuration; + ptr->DIEffectStruct.dwSamplePeriod = HZ_TO_uS(100); + ptr->DIEffectStruct.dwGain = 10000; + ptr->DIEffectStruct.dwTriggerButton = lButton == -1 ? DIEB_NOTRIGGER : FIELD_OFFSET(DIJOYSTATE, rgbButtons) + lButton; + ptr->DIEffectStruct.dwTriggerRepeatInterval = 0; + ptr->DIEffectStruct.cAxes = nAxisCount; + ptr->DIEffectStruct.rgdwAxes = rgdwAxes; + ptr->DIEffectStruct.rglDirection = ptr->rglDirection; + ptr->DIEffectStruct.lpEnvelope = NULL; + ptr->DIEffectStruct.cbTypeSpecificParams = cbTypeSpecificParams; + ptr->DIEffectStruct.lpvTypeSpecificParams = pvTypeSpecificParams; + + HRESULT hResult; + hResult = pDIDevice->CreateEffect(guid, &ptr->DIEffectStruct, ppDIEffect, NULL); + + return hResult; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateConditionEffect +// Purpose: Creates a Condition type Effect with specified params +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// DWORD dwType - SPRING | INERTIA | DAMPER | FRICTION +// DWORD dwDuration - Duration in uS +// LONG lXCoefficient - Coefficient in -10000 to 10000 +// LONG lXOffset - Offset in -10000 to 10000 +// LONG lYCoefficient - Coefficient in -10000 to 10000 +// LONG lYOffset - Offset in -10000 to 10000 +// LONG lButton - Index of playback button, -1 for none +// Returns: +// Algorithm: +// Comments: +// To create a 1D Friction, set the lFx or lFy parameter to 0 +// To create a 2D Friction, set both lFx and lFy parameter to non-zero +// or set both lFx and lFy to zero +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_CreateConditionEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwType, + IN DWORD dwDuration, + IN LONG lXCoefficient, + IN LONG lXOffset, + IN LONG lYCoefficient, + IN LONG lYOffset, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + GUID guid; + switch(dwType) + { + case SPRING: + guid = GUID_Spring; + break; + case INERTIA: + guid = GUID_Inertia; + break; + case DAMPER: + guid = GUID_Damper; + break; + case FRICTION: + guid = GUID_Friction; + break; + default: + break; + } + + DICONDITION DIConditionStruct[2]; + DIConditionStruct[0].lOffset = lXOffset; + DIConditionStruct[0].lPositiveCoefficient = lXCoefficient; + DIConditionStruct[0].lNegativeCoefficient = lXCoefficient; + DIConditionStruct[0].dwPositiveSaturation = 10000; + DIConditionStruct[0].dwNegativeSaturation = 10000; + DIConditionStruct[0].lDeadBand = 0; + DIConditionStruct[1].lOffset = lYOffset; + DIConditionStruct[1].lPositiveCoefficient = lYCoefficient; + DIConditionStruct[1].lNegativeCoefficient = lYCoefficient; + DIConditionStruct[1].dwPositiveSaturation = 10000; + DIConditionStruct[1].dwNegativeSaturation = 10000; + DIConditionStruct[1].lDeadBand = 0; + + DWORD rgdwAxes[2]; + int nAxisCount = 0; + if(lXCoefficient != 0) + { + rgdwAxes[nAxisCount] = DIJOFS_X; + nAxisCount++; + } + if(lYCoefficient != 0) + { + rgdwAxes[nAxisCount] = DIJOFS_Y; + nAxisCount++; + } + if(lXCoefficient == 0 && lYCoefficient == 0) + { + nAxisCount = 2; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + } + + DWORD cbTypeSpecificParams; + PVOID pvTypeSpecificParams; + if(nAxisCount == 1) + { + cbTypeSpecificParams = sizeof(DICONDITION[1]); + if(lXCoefficient != 0) + pvTypeSpecificParams = &DIConditionStruct[0]; + else + pvTypeSpecificParams = &DIConditionStruct[1]; + } + else + { + cbTypeSpecificParams = sizeof(DICONDITION[2]); + pvTypeSpecificParams = &DIConditionStruct[0]; + } + + LONG rglDirection[2]; + rglDirection[0] = 0; + rglDirection[1] = 0; + + DIEFFECT DIEffectStruct; + DIEffectStruct.dwSize = sizeof(DIEFFECT); + DIEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_CARTESIAN; + DIEffectStruct.dwDuration = dwDuration; + DIEffectStruct.dwSamplePeriod = HZ_TO_uS(100); + DIEffectStruct.dwGain = 10000; + DIEffectStruct.dwTriggerButton = lButton == -1 ? DIEB_NOTRIGGER : FIELD_OFFSET(DIJOYSTATE, rgbButtons) + lButton; + DIEffectStruct.dwTriggerRepeatInterval = 0; + DIEffectStruct.cAxes = nAxisCount; + DIEffectStruct.rgdwAxes = rgdwAxes; + DIEffectStruct.rglDirection = rglDirection; + DIEffectStruct.lpEnvelope = NULL; + DIEffectStruct.cbTypeSpecificParams = cbTypeSpecificParams; + DIEffectStruct.lpvTypeSpecificParams = pvTypeSpecificParams; + + HRESULT hResult; + hResult = pDIDevice->CreateEffect(guid, &DIEffectStruct, ppDIEffect, NULL); + + return hResult; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateRampEffect +// Purpose: Creates a Ramp type Effect with specified params +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// DWORD dwDuration - uS +// DWORD dwDirection - 0 to 35999 +// LONG lStart - -10000 to 10000 +// LONG lEnd - -10000 to 10000 +// DWORD dwAttackTime - Envelope Attack Time in uS +// DWORD dwAttackLevel - Envelope Attack Level in 0 to 10000 +// DWORD dwFadeTime - Envelope Fade time in uS +// DWORD dwFadeLevel - Envelope Fade Level +// LONG lButton - Index of playback button, -1 for none +// Returns: +// Algorithm: +// Comments: +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_CreateRampEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN DWORD dwDirection, + IN LONG lStart, + IN LONG lEnd, + IN DWORD dwAttackTime, + IN DWORD dwAttackLevel, + IN DWORD dwFadeTime, + IN DWORD dwFadeLevel, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + DIRAMPFORCE DIRampStruct; + DIRampStruct.lStart = lStart; + DIRampStruct.lEnd = lEnd; + + DIENVELOPE DIEnvelopeStruct; + DIEnvelopeStruct.dwSize = sizeof(DIENVELOPE); + DIEnvelopeStruct.dwAttackTime = dwAttackTime; + DIEnvelopeStruct.dwAttackLevel = dwAttackLevel; + DIEnvelopeStruct.dwFadeTime = dwFadeTime; + DIEnvelopeStruct.dwFadeLevel = dwFadeLevel; + + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = dwDirection; + rglDirection[1] = 0; + + DIEFFECT DIEffectStruct; + DIEffectStruct.dwSize = sizeof(DIEFFECT); + DIEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + DIEffectStruct.dwDuration = dwDuration; + DIEffectStruct.dwSamplePeriod = HZ_TO_uS(100); + DIEffectStruct.dwGain = 10000; + DIEffectStruct.dwTriggerButton = lButton == -1 ? DIEB_NOTRIGGER : FIELD_OFFSET(DIJOYSTATE, rgbButtons) + lButton; + DIEffectStruct.dwTriggerRepeatInterval = 0; + DIEffectStruct.cAxes = 2; + DIEffectStruct.rgdwAxes = rgdwAxes; + DIEffectStruct.rglDirection = rglDirection; + DIEffectStruct.lpEnvelope = &DIEnvelopeStruct; + DIEffectStruct.cbTypeSpecificParams = sizeof(DIRampStruct); + DIEffectStruct.lpvTypeSpecificParams = &DIRampStruct; + + HRESULT hResult; + hResult = pDIDevice->CreateEffect(GUID_RampForce, &DIEffectStruct, ppDIEffect, NULL); + + return hResult; +} + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateConstantForceEffect +// Purpose: Creates a ConstantForce type Effect with specified params +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// DWORD dwDuration - in uS +// DWORD dwDirection - in 0 to 35999 +// LONG lMagnitude - in -10000 to 10000 +// DWORD dwAttackTime - Envelope Attack Time in uS +// DWORD dwAttackLevel - Envelope Attack Level in 0 to 10000 +// DWORD dwFadeTime - Envelope Fade time in uS +// DWORD dwFadeLevel - Envelope Fade Level +// LONG lButton - Index of playback button, -1 for none +// Returns: +// Algorithm: +// Comments: +// +// ---------------------------------------------------------------------------- +HRESULT SWFF_CreateConstantForceEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN DWORD dwDirection, + IN LONG lMagnitude, + IN DWORD dwAttackTime, + IN DWORD dwAttackLevel, + IN DWORD dwFadeTime, + IN DWORD dwFadeLevel, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + DICONSTANTFORCE DIConstantForceStruct; + DIConstantForceStruct.lMagnitude = lMagnitude; + + DIENVELOPE DIEnvelopeStruct; + DIEnvelopeStruct.dwSize = sizeof(DIENVELOPE); + DIEnvelopeStruct.dwAttackTime = dwAttackTime; + DIEnvelopeStruct.dwAttackLevel = dwAttackLevel; + DIEnvelopeStruct.dwFadeTime = dwFadeTime; + DIEnvelopeStruct.dwFadeLevel = dwFadeLevel; + + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = dwDirection; + rglDirection[1] = 0; + + DIEFFECT DIEffectStruct; + DIEffectStruct.dwSize = sizeof(DIEFFECT); + DIEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + DIEffectStruct.dwDuration = dwDuration; + DIEffectStruct.dwSamplePeriod = HZ_TO_uS(100); + DIEffectStruct.dwGain = 10000; + DIEffectStruct.dwTriggerButton = lButton == -1 ? DIEB_NOTRIGGER : FIELD_OFFSET(DIJOYSTATE, rgbButtons) + lButton; + DIEffectStruct.dwTriggerRepeatInterval = 0; + DIEffectStruct.cAxes = 2; + DIEffectStruct.rgdwAxes = rgdwAxes; + DIEffectStruct.rglDirection = rglDirection; + DIEffectStruct.lpEnvelope = &DIEnvelopeStruct; + DIEffectStruct.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + DIEffectStruct.lpvTypeSpecificParams = &DIConstantForceStruct; + + HRESULT hResult; + hResult = pDIDevice->CreateEffect(GUID_ConstantForce, &DIEffectStruct, ppDIEffect, NULL); + + return hResult; +} + + +// ---------------------------------------------------------------------------- +// Function: SWFF_CreateWallEffect +// Purpose: Creates a Wall Effect +// Parameters: LPDIRECTINPUTDEVICE2 pDIDevice - IDIRECTINPUTDEVICE2 interface +// LPDIRECTINPUTEFFECT* ppDIEffect - Receives pointer to created effect +// DWORD dwDuration - in uS +// DWORD dwDirection - 0 | 9000 | 18000 | 27000 +// DWORD dwDistance - Distance from centerin 0 to 10000 +// BOOL bInner - T/F = Inner/Outer +// LONG lCoefficient - Wall Constant in 0 to 10000 +// LONG lButton - Index of playback button, -1 for none +// Returns: +// Algorithm: +// Comments: +// +// ---------------------------------------------------------------------------- +#if 0 +HRESULT SWFF_CreateWallEffect( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIRECTINPUTEFFECT* ppDIEffect, + IN DWORD dwDuration, + IN DWORD dwDirection, + IN DWORD dwDistance, + IN BOOL bInner, + IN LONG lWallCoefficient, + IN LONG lButton) +{ + if(pDIDevice == NULL || ppDIEffect == NULL) + return SFERR_INVALID_PARAM; + + // Always clear return IPtr + *ppDIEffect = NULL; + + BE_WALL_PARAM WallStruct; + WallStruct.m_Bytes = sizeof(WallStruct); + WallStruct.m_WallType = bInner ? WALL_INNER : WALL_OUTER; + WallStruct.m_WallConstant = lWallCoefficient; + WallStruct.m_WallAngle = dwDirection; + WallStruct.m_WallDistance = dwDistance; + + + DWORD rgdwAxes[2]; + rgdwAxes[0] = DIJOFS_X; + rgdwAxes[1] = DIJOFS_Y; + + LONG rglDirection[2]; + rglDirection[0] = 0; + rglDirection[1] = 0; + + DIEFFECT DIEffectStruct; + DIEffectStruct.dwSize = sizeof(DIEFFECT); + DIEffectStruct.dwFlags = DIEFF_OBJECTOFFSETS | DIEFF_POLAR; + DIEffectStruct.dwDuration = dwDuration; + DIEffectStruct.dwSamplePeriod = HZ_TO_uS(100); + DIEffectStruct.dwGain = 10000; + DIEffectStruct.dwTriggerButton = lButton == -1 ? DIEB_NOTRIGGER : FIELD_OFFSET(DIJOYSTATE, rgbButtons) + lButton; + DIEffectStruct.dwTriggerRepeatInterval = 0; + DIEffectStruct.cAxes = 2; + DIEffectStruct.rgdwAxes = rgdwAxes; + DIEffectStruct.rglDirection = rglDirection; + DIEffectStruct.lpEnvelope = NULL; + DIEffectStruct.cbTypeSpecificParams = sizeof(WallStruct); + DIEffectStruct.lpvTypeSpecificParams = &WallStruct; + + HRESULT hResult; + hResult = pDIDevice->CreateEffect(GUID_Wall, &DIEffectStruct, ppDIEffect, NULL); + + return hResult; +} +#endif + + +// ---------------------------------------------------------------------------- +// FUNCTION: SWFF_GetJoyData +// PURPOSE: Retrieves Joystick data +// PARAMETERS: int nJoyID - JOYSTICKID1-16 +// JOYINFOEX *pjix - PTR to a JOYYINFOEX structure +// char *pszErr - Ptr to Error code string +// RETURNS: JOYINFOEX filled in +// +// The axis and buttons info: +// All axis are in the range 0 to 65535. +// jix.dwXpos - X position. +// jix.dwYpos - Y position. +// jix.dwZpos - Throttle slider control +// jix.dwRpos - Z Rotation position. +// +// To see if button 1 is pressed: +// (jix.dwButtons & JOY_BUTTON1) ? PRESSED : NOT_PRESSED; +// likewise for the other buttons JOY_BUTTON2, JOY_BUTTON3 Â… +// JOY_BUTTON8 +// +// Hat Switch (POV) is in jix.dwPOV +// The range is 0 to 35900 and the value is -1 if the +// Hat Switch is not pressed. +// +// TRUE if successful, else FALSE +// COMMENTS: +// ---------------------------------------------------------------------------- +BOOL SWFF_GetJoyData(int nJoyID, JOYINFOEX * pjix, char *pszErr) +{ + if(pjix == NULL || pszErr == NULL) + return FALSE; + + memset(pjix, 0x00, sizeof(JOYINFOEX)); // for good measure + pjix->dwSize = sizeof(JOYINFOEX); + +// NOTE: With SideWinder Digital OverDrive, it takes no more time to return all +// information from the joystick than it does to just get +// the button states or axis. +// + pjix->dwFlags = JOY_RETURNALL; + + +// joyGetPoxEx will fill in the joyinfoex struct with all the +// joystick information +// + + switch(joyGetPosEx(nJoyID, pjix)) + { + case JOYERR_NOERROR: // no problem + strcpy(pszErr,"SUCCESS"); + break; + + case MMSYSERR_NODRIVER: + strcpy(pszErr,"The joystick driver is not present."); + return FALSE; + + case MMSYSERR_INVALPARAM: + strcpy(pszErr,"An invalid parameter was passed."); + return FALSE; + + case MMSYSERR_BADDEVICEID: + strcpy(pszErr,"The specified joystick identifier is invalid."); + return FALSE; + + case JOYERR_UNPLUGGED: + strcpy(pszErr,"Your joystick is unplugged."); + return FALSE; + + default: + strcpy(pszErr,"Unknown joystick error."); + return FALSE; + + } // end of switch + return TRUE; + } // GetJoyData() + + +// ---------------------------------------------------------------------------- +// FUNCTION: SWFF_GetJoyData2 +// PURPOSE: Retrieves Joystick data using DInput calls +// PARAMETERS: LPDIRECTINPUTDEVICE2 pDIDevice - Pointer to DirectInputDevice +// LPDIJOYSTATE pjs - PTR to a DIJOYSTATE structure +// RETURNS: DIJOYSTATE filled in +// +// The axis info: +// all axes have range from 0 to 65535 +// pjs->lX - X position +// pjs->lY - Y position +// pjs->lZ - Throttle position +// +// To see if button 0 is pressed: +// (pjs->rgbButtons[0] & 0x80) ? PRESSED : NOT_PRESSED; +// likewise for the other buttons pjs->rgbButtons[1], +// pjs->rgbButtons[2] +// +// Hat Switch (POV) is in pjs->rgdwPov[0] +// The range is 0 to 35999 and the value is -1 if the +// Hat Switch is not pressed. +// +// COMMENTS: +// ---------------------------------------------------------------------------- +/*HRESULT SWFF_GetJoyData2( + IN LPDIRECTINPUTDEVICE2 pDIDevice, + IN OUT LPDIJOYSTATE pjs) +{ + HRESULT hResult; + + if(pDIDevice == NULL || pjs == NULL) + return SFERR_INVALID_PARAM; + + memset(pjs, 0x00, sizeof(DIJOYSTATE)); // for good measure + + // NOTE: With SideWinder Digital OverDrive, it takes no more time to return all + // information from the joystick than it does to just get + // the button states or axis. + // + + // must poll before using GetDeviceState(...) + hResult = pDIDevice->Poll(); + if(FAILED(hResult)) + return hResult; + + // retrieve the values cached during Poll() + hResult = pDIDevice->GetDeviceState(sizeof(DIJOYSTATE), pjs); + + return hResult; + } // GetJoyData2() +*/ + +// ---------------------------------------------------------------------------- +// Function: SWFF_ErrorCodeToString +// Parameters: HRESULT hResult - Error Code +// TCHAR * pszString - Ptr to string to fill with code +// Returns: +// Algorithm: +// Comments: +// +// ---------------------------------------------------------------------------- +void SWFF_ErrorCodeToString(HRESULT hResult, TCHAR * pszCodeString) +{ + if(pszCodeString == NULL) + return; + +//XSTR:OFF + + switch(hResult) + { + case S_FALSE: strcpy(pszCodeString, "S_FALSE"); break; + case DI_POLLEDDEVICE: strcpy(pszCodeString, "DI_POLLEDDEVICE"); break; +// case DI_DOWNLOADSKIPPED: strcpy(pszCodeString, "DI_DOWNLOADSKIPPED"); break; +// case DI_EFFECTRESTARTED: strcpy(pszCodeString, "DI_EFFECTRESTARTED"); break; + case DIERR_OLDDIRECTINPUTVERSION: strcpy(pszCodeString, "DIERR_OLDDIRECTINPUTVERSION" ); break; + case DIERR_BETADIRECTINPUTVERSION: strcpy(pszCodeString, "DIERR_BETADIRECTINPUTVERSION" ); break; + case DIERR_BADDRIVERVER: strcpy(pszCodeString, "DIERR_BADDRIVERVER" ); break; + case DIERR_DEVICENOTREG: strcpy(pszCodeString, "DIERR_DEVICENOTREG" ); break; + case DIERR_NOTFOUND: strcpy(pszCodeString, "DIERR_NOTFOUND" ); break; + case DIERR_INVALIDPARAM: strcpy(pszCodeString, "DIERR_INVALIDPARAM" ); break; + case DIERR_NOINTERFACE: strcpy(pszCodeString, "DIERR_NOINTERFACE" ); break; + case DIERR_GENERIC: strcpy(pszCodeString, "DIERR_GENERIC" ); break; + case DIERR_OUTOFMEMORY: strcpy(pszCodeString, "DIERR_OUTOFMEMORY" ); break; + case DIERR_UNSUPPORTED: strcpy(pszCodeString, "DIERR_UNSUPPORTED" ); break; + case DIERR_NOTINITIALIZED: strcpy(pszCodeString, "DIERR_NOTINITIALIZED" ); break; + case DIERR_ALREADYINITIALIZED: strcpy(pszCodeString, "DIERR_ALREADYINITIALIZED" ); break; + case DIERR_NOAGGREGATION: strcpy(pszCodeString, "DIERR_NOAGGREGATION" ); break; + case DIERR_INPUTLOST: strcpy(pszCodeString, "DIERR_INPUTLOST" ); break; + case DIERR_ACQUIRED: strcpy(pszCodeString, "DIERR_ACQUIRED" ); break; + case DIERR_NOTACQUIRED: strcpy(pszCodeString, "DIERR_NOTACQUIRED" ); break; + case E_ACCESSDENIED: strcpy(pszCodeString, "E_ACCESSDENIED: DIERR_OTHERAPPHASPRIO, DIERR_READONLY, DIERR_HANDLEEXISTS"); break; + case E_PENDING: strcpy(pszCodeString, "E_PENDING" ); break; + case DIERR_INSUFFICIENTPRIVS: strcpy(pszCodeString, "DIERR_INSUFFICIENTPRIVS" ); break; + case DIERR_DEVICEFULL: strcpy(pszCodeString, "DIERR_DEVICEFULL" ); break; + case DIERR_MOREDATA: strcpy(pszCodeString, "DIERR_MOREDATA" ); break; + case DIERR_NOTDOWNLOADED: strcpy(pszCodeString, "DIERR_NOTDOWNLOADED" ); break; + case DIERR_HASEFFECTS: strcpy(pszCodeString, "DIERR_HASEFFECTS" ); break; + case DIERR_NOTEXCLUSIVEACQUIRED: strcpy(pszCodeString, "DIERR_NOTEXCLUSIVEACQUIRED"); break; + case DIERR_INCOMPLETEEFFECT: strcpy(pszCodeString, "DIERR_INCOMPLETEEFFECT" ); break; + case DIERR_NOTBUFFERED: strcpy(pszCodeString, "DIERR_NOTBUFFERED" ); break; + case DIERR_EFFECTPLAYING: strcpy(pszCodeString, "DIERR_EFFECTPLAYING"); break; + case SFERR_INVALID_OBJECT: strcpy(pszCodeString, "SFERR_INVALID_OBJECT" ); break; + case SFERR_END_OF_LIST: strcpy(pszCodeString, "SFERR_END_OF_LIST" ); break; + case SFERR_DEVICE_NACK: strcpy(pszCodeString, "SFERR_DEVICE_NACK" ); break; + case SFERR_RAW_OUT_DATAEVENT_CREATION: strcpy(pszCodeString, "SFERR_RAW_OUT_DATAEVENT_CREATION" ); break; + case SFERR_RAW_OUT_THREAD_CREATION: strcpy(pszCodeString, "SFERR_RAW_OUT_THREAD_CREATION" ); break; + case SFERR_SYSTEM_INIT: strcpy(pszCodeString, "SFERR_SYSTEM_INIT" ); break; + case SFERR_DRIVER_ERROR: strcpy(pszCodeString, "SFERR_DRIVER_ERROR" ); break; + case SFERR_NON_FF_DEVICE: strcpy(pszCodeString, "SFERR_NON_FF_DEVICE" ); break; + case SFERR_INVALID_HAL_OBJECT: strcpy(pszCodeString, "SFERR_INVALID_HAL_OBJECT" ); break; + case VFX_ERR_FILE_NOT_FOUND: strcpy(pszCodeString, "VFX_ERR_FILE_NOT_FOUND" ); break; + case VFX_ERR_FILE_CANNOT_OPEN: strcpy(pszCodeString, "VFX_ERR_FILE_CANNOT_OPEN" ); break; + case VFX_ERR_FILE_CANNOT_CLOSE: strcpy(pszCodeString, "VFX_ERR_FILE_CANNOT_CLOSE" ); break; + case VFX_ERR_FILE_CANNOT_READ: strcpy(pszCodeString, "VFX_ERR_FILE_CANNOT_READ" ); break; + case VFX_ERR_FILE_CANNOT_WRITE: strcpy(pszCodeString, "VFX_ERR_FILE_CANNOT_WRITE" ); break; + case VFX_ERR_FILE_CANNOT_SEEK: strcpy(pszCodeString, "VFX_ERR_FILE_CANNOT_SEEK" ); break; + case VFX_ERR_FILE_UNKNOWN_ERROR: strcpy(pszCodeString, "VFX_ERR_FILE_UNKNOWN_ERROR" ); break; + case VFX_ERR_FILE_BAD_FORMAT: strcpy(pszCodeString, "VFX_ERR_FILE_BAD_FORMAT" ); break; + case VFX_ERR_FILE_ACCESS_DENIED: strcpy(pszCodeString, "VFX_ERR_FILE_ACCESS_DENIED" ); break; + case VFX_ERR_FILE_SHARING_VIOLATION: strcpy(pszCodeString, "VFX_ERR_FILE_SHARING_VIOLATION" ); break; + case VFX_ERR_FILE_NETWORK_ERROR: strcpy(pszCodeString, "VFX_ERR_FILE_NETWORK_ERROR" ); break; + case VFX_ERR_FILE_TOO_MANY_OPEN_FILES: strcpy(pszCodeString, "VFX_ERR_FILE_TOO_MANY_OPEN_FILES" ); break; + case VFX_ERR_FILE_INVALID: strcpy(pszCodeString, "VFX_ERR_FILE_INVALID" ); break; + case VFX_ERR_FILE_END_OF_FILE: strcpy(pszCodeString, "VFX_ERR_FILE_END_OF_FILE" ); break; + case SWDEV_ERR_INVALID_ID : strcpy(pszCodeString, "SWDEV_ERR_INVALID_ID" ); break; + case SWDEV_ERR_INVALID_PARAM : strcpy(pszCodeString, "SWDEV_ERR_INVALID_PARAM" ); break; + case SWDEV_ERR_CHECKSUM : strcpy(pszCodeString, "SWDEV_ERR_CHECKSUM" ); break; + case SWDEV_ERR_TYPE_FULL : strcpy(pszCodeString, "SWDEV_ERR_TYPE_FULL" ); break; + case SWDEV_ERR_UNKNOWN_CMD : strcpy(pszCodeString, "SWDEV_ERR_UNKNOWN_CMD" ); break; + case SWDEV_ERR_PLAYLIST_FULL : strcpy(pszCodeString, "SWDEV_ERR_PLAYLIST_FULL" ); break; + case SWDEV_ERR_PROCESSLIST_FULL : strcpy(pszCodeString, "SWDEV_ERR_PROCESSLIST_FULL" ); break; + default: sprintf(pszCodeString, "%x", hResult); break; + } + +//XSTR:ON + +} diff --git a/src/io/timer.cpp b/src/io/timer.cpp new file mode 100644 index 0000000..f52c503 --- /dev/null +++ b/src/io/timer.cpp @@ -0,0 +1,583 @@ +/* + * $Logfile: /Freespace2/code/Io/Timer.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Include file for timer stuff + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 4 5/26/99 3:19p Dave + * Fixed release build errors. + * + * 3 5/17/99 6:03p Dave + * Added new timing code. Put in code to allow camera speed zooming. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 14 4/13/98 10:16a John + * Switched gettime back to timer_get_milliseconds, which is now thread + * safe. + * + * 13 4/13/98 10:11a John + * Made timer functions thread safe. Made timer_init be called in all + * projects. + * + * 12 4/13/98 8:09a John + * Made timer_get functions thread safe. + * + * 11 12/02/97 3:56p John + * fixed a bug with timestamp_until, created one with it rolling over. + * + * 10 9/11/97 7:12p Hoffoss + * Added more functionality to training sexp handling code. + * + * 9 8/29/97 4:49p Allender + * added mprintf to check for wrap problems with timestamp ticker + * + * 8 8/28/97 1:38p Allender + * from John: changes to timer func to detect early rollever + * + * 7 7/29/97 5:30p Lawrance + * move gettime() from keyboard module to timer module + * + * 6 7/16/97 4:42p Mike + * Make afterburner shake viewer, not ship. + * Shake for limited time. + * Add timestamp_until() function to timer library. + * + * 5 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#ifndef PLAT_UNIX +#include +#endif + +#include "limits.h" +#include "pstypes.h" +#include "timer.h" +#include "2d.h" +#include "alphacolors.h" + +#ifndef NDEBUG + #define USE_TIMING +#endif + +static longlong Timer_last_value, Timer_base; +static uint Timer_freq=0; + +static int Timer_inited = 0; + +static CRITICAL_SECTION Timer_lock; + +void timer_close() +{ + if ( Timer_inited ) { + Timer_inited = 0; +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + DeleteCriticalSection( &Timer_lock ); +#endif + } +} + +void timer_init() +{ + if ( !Timer_inited ) { +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + LARGE_INTEGER tmp; + QueryPerformanceFrequency(&tmp); + Assert( tmp.HighPart == 0 ); + Timer_freq = tmp.LowPart; + + QueryPerformanceCounter((LARGE_INTEGER *)&Timer_base); + QueryPerformanceCounter((LARGE_INTEGER *)&Timer_last_value); + + InitializeCriticalSection(&Timer_lock); +#endif + + Timer_inited = 1; + + atexit(timer_close); + } +} + +// Fills Time_now with the ticks since program start +static void timer_get(LARGE_INTEGER * out) +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + EnterCriticalSection(&Timer_lock); + + longlong time_tmp; + longlong Time_now; + + QueryPerformanceCounter((LARGE_INTEGER *)&time_tmp); + if ( time_tmp < Timer_last_value ) { + // The clock has rolled! + Timer_base = time_tmp; + mprintf(( "TIMER ROLLED!\n" )); + // Hack: I'm not accounting for the time before roll occured, + // since I'm not sure at what value this timer is going to roll at. + Time_now = time_tmp; + } + Time_now = time_tmp - Timer_base; + Timer_last_value = time_tmp; + + out->QuadPart = Time_now; + + LeaveCriticalSection(&Timer_lock); +#endif +} + +fix timer_get_fixed_seconds() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; + return 0; +#else + int tmp; + LARGE_INTEGER temp_large; + + if (!Timer_inited) { + Int3(); // Make sure you call timer_init before anything that uses timer functions! + return 0; + } + + timer_get(&temp_large); + + // Timing in fixed point (16.16) seconds. + // Can be used for up to 1000 hours + _asm mov edx, temp_large.HighPart + _asm mov eax, temp_large.LowPart + + _asm shld edx, eax, 16 ; Keep 32+11 bits + _asm shl eax, 16 + // edx:eax = number of 1.19Mhz pulses elapsed. + _asm mov ebx, Timer_freq + + // Make sure we won't divide overflow. Make time wrap at about 9 hours +sub_again: + _asm sub edx, ebx ; subtract until negative... + _asm jns sub_again ; ...to prevent divide overflow... + _asm add edx, ebx ; ...then add in to get correct value. + _asm div ebx + //eax = fixed point seconds elapsed... + + _asm mov tmp, eax + + return tmp; +#endif +} + +fix timer_get_fixed_secondsX() +{ + return timer_get_fixed_seconds(); +} + +fix timer_get_approx_seconds() +{ + return timer_get_fixed_seconds(); +} + +int timer_get_milliseconds() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; + return 0; +#else + int tmp; + LARGE_INTEGER temp_large; + + if (!Timer_inited) { + Int3(); // Make sure you call timer_init before anything that uses timer functions! + return 0; + } + + timer_get(&temp_large); + + temp_large.QuadPart *= (longlong)1000; + + // Timing in milliseconds. + _asm mov edx, temp_large.HighPart + _asm mov eax, temp_large.LowPart + + //_asm shld edx, eax, 16 ; Keep 32+11 bits + //_asm shl eax, 16 + // edx:eax = number of 1.19Mhz pulses elapsed. + _asm mov ebx, Timer_freq + + // Make sure we won't divide overflow. Make time wrap at about 9 hours +sub_again: + _asm sub edx, ebx ; subtract until negative... + _asm jns sub_again ; ...to prevent divide overflow... + _asm add edx, ebx ; ...then add in to get correct value. + _asm div ebx + //eax = milliseconds elapsed... + + _asm mov tmp, eax + + return tmp; +#endif +} + +int timer_get_microseconds() +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; + return 0; +#else + int tmp; + LARGE_INTEGER temp_large; + + if (!Timer_inited) { + Int3(); // Make sure you call timer_init before anything that uses timer functions! + return 0; + } + + timer_get(&temp_large); + + temp_large.QuadPart *= (longlong)1000000; + + // Timing in milliseconds. + _asm mov edx, temp_large.HighPart + _asm mov eax, temp_large.LowPart + + //_asm shld edx, eax, 16 ; Keep 32+11 bits + //_asm shl eax, 16 + // edx:eax = number of 1.19Mhz pulses elapsed. + _asm mov ebx, Timer_freq + + // Make sure we won't divide overflow. Make time wrap at about 9 hours +sub_again: + _asm sub edx, ebx ; subtract until negative... + _asm jns sub_again ; ...to prevent divide overflow... + _asm add edx, ebx ; ...then add in to get correct value. + _asm div ebx + //eax = milliseconds elapsed... + + _asm mov tmp, eax + + return tmp; +#endif +} + +// 0 means invalid, +// 1 means always return true +// 2 and above actually check the time +int timestamp_ticker = 2; + +void timestamp_reset() +{ + timestamp_ticker = 2; +} + +// Restrict all time values between 0 and MAX_TIME +// so we don't have to use UINTs to calculate rollover. +// For debugging & testing, you could set this to +// something like 1 minute (6000). +#define MAX_TIME (INT_MAX/2) + +void timestamp_inc(float frametime) +{ + timestamp_ticker += (int)(frametime*TIMESTAMP_FREQUENCY); + + if ( timestamp_ticker > MAX_TIME ) { + timestamp_ticker = 2; // Roll! + } + + if (timestamp_ticker < 2 ) { + mprintf(("Whoa!!! timestamp_ticker < 2 -- resetting to 2!!!\n")); + timestamp_ticker = 2; + } +} + +int timestamp(int delta_ms ) +{ + int t2; + if (delta_ms < 0 ) return 0; + if (delta_ms == 0 ) return 1; + t2 = timestamp_ticker + delta_ms; + if ( t2 > MAX_TIME ) { + // wrap!!! + t2 = delta_ms - (MAX_TIME-timestamp_ticker); + } + if (t2 < 2 ) t2 = 2; // hack?? + return t2; +} + +// Returns milliseconds until timestamp will elapse. +// Negative value gives milliseconds ago that timestamp elapsed. +int timestamp_until(int stamp) +{ + // JAS: FIX + // HACK!! This doesn't handle rollover! + // (Will it ever happen?) + + return stamp - timestamp_ticker; + +/* + uint delta; + + delta = stamp - timestamp_ticker; + + + if (delta > UINT_MAX/2) + delta = UINT_MAX - delta + 1; + else if (delta < - ( (int) (UINT_MAX/2))) + delta = UINT_MAX + delta + 1; + + return delta; +*/ +} + +// alternate timestamp functions. The way these work is you call xtimestamp() to get the +// current counter value, and then call +int timestamp() +{ + return timestamp_ticker; +} + +int timestamp_has_time_elapsed(int stamp, int time) +{ + int t; + + if (time <= 0) + return 1; + + t = stamp + time; + if (t <= timestamp_ticker) + return 1; // if we are unlucky enough to have it wrap on us, this will assume time has elapsed. + + return 0; +} + +// timing functions ------------------------------------------------------------------------------- + +#define MAX_TIMING_EVENTS 15 + +// timing struct +#ifdef USE_TIMING + +typedef struct timing { + char name[64]; + int microseconds_total; + int start; + int ref_count; +} timing; + +timing Timing_frame; +timing Timing_events[MAX_TIMING_EVENTS]; +int Timing_event_count = 0; + +#endif + +// lookup a timing event +int timing_event_lookup(char *event_name) +{ +#ifndef USE_TIMING + return -1; +#else + int idx; + + // sanity + if(event_name == NULL){ + return -1; + } + + // look through all events + for(idx=0; idxinstance]; + + if ( Fred_running ) { + model_set_outline_color(0, 255, 0); + model_render(node->modelnum, &node_orient, pos, MR_NO_LIGHTING | MR_LOCK_DETAIL | MR_NO_POLYS | MR_SHOW_OUTLINE ); + } else { + if ( view_pos ) { + int alpha_index = HUD_color_alpha; + + // generate alpha index based on distance to jump node + float dist; + + dist = vm_vec_dist_quick(view_pos, pos); + + // linearly interpolate alpha. At 1000m or less, full intensity. At 10000m or more 1/2 intensity. + if ( dist < 1000 ) { + alpha_index = HUD_COLOR_ALPHA_USER_MAX - 2; + } else if ( dist > 10000 ) { + alpha_index = HUD_COLOR_ALPHA_USER_MIN; + } else { + alpha_index = fl2i( HUD_COLOR_ALPHA_USER_MAX - 2 + (dist-1000) * (HUD_COLOR_ALPHA_USER_MIN-HUD_COLOR_ALPHA_USER_MAX-2) / (9000) + 0.5f); + if ( alpha_index < HUD_COLOR_ALPHA_USER_MIN ) { + alpha_index = HUD_COLOR_ALPHA_USER_MIN; + } + } + + // nprintf(("Alan","alpha index is: %d\n", alpha_index)); + gr_set_color_fast(&HUD_color_defaults[alpha_index]); +// model_set_outline_color(HUD_color_red, HUD_color_green, HUD_color_blue); + + } else { + gr_set_color(HUD_color_red, HUD_color_green, HUD_color_blue); + } + model_render(node->modelnum, &node_orient, pos, MR_NO_LIGHTING | MR_LOCK_DETAIL | MR_NO_POLYS | MR_SHOW_OUTLINE_PRESET ); + } + +} + +// create a jump node object and return index to it. +int jumpnode_create(vector *pos) +{ + int obj; + + Assert(Num_jump_nodes < MAX_JUMP_NODES); + + Jump_nodes[Num_jump_nodes].modelnum = model_load(NOX("subspacenode.pof"), NULL, NULL); + if ( Jump_nodes[Num_jump_nodes].modelnum < 0 ) { + Int3(); + return -1; + } + + obj = obj_create(OBJ_JUMP_NODE, -1, Num_jump_nodes, NULL, pos, model_get_radius(Jump_nodes[Num_jump_nodes].modelnum), OF_RENDERS); + sprintf(Jump_nodes[Num_jump_nodes].name, XSTR( "Jump Node %d", 632), Num_jump_nodes); + if (obj >= 0) { + Jump_nodes[Num_jump_nodes].objnum = obj; + Num_jump_nodes++; + } + return obj; +} + +// only called by FRED +void jumpnode_render_all() +{ + int i; + object *jumpnode_objp; + + for ( i = 0; i < Num_jump_nodes; i++ ) { + jumpnode_objp = &Objects[Jump_nodes[i].objnum]; + jumpnode_render(jumpnode_objp, &jumpnode_objp->pos); + } +} diff --git a/src/lighting/lighting.cpp b/src/lighting/lighting.cpp new file mode 100644 index 0000000..e8e9493 --- /dev/null +++ b/src/lighting/lighting.cpp @@ -0,0 +1,1006 @@ +/* + * $Logfile: /Freespace2/code/Lighting/Lighting.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to calculate dynamic lighting on a vertex. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 5 6/22/99 2:22p Dave + * Doh. Fixed a type bug. + * + * 4 6/18/99 5:16p Dave + * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD + * dialog to PXO screen. + * + * 3 5/09/99 6:00p Dave + * Lots of cool new effects. E3 build tweaks. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 37 4/12/98 9:56a John + * Made lighting detail flags work. Made explosions cast light on + * highest. + * + * 36 4/10/98 5:20p John + * Changed RGB in lighting structure to be ubytes. Removed old + * not-necessary 24 bpp software stuff. + * + * 35 4/04/98 5:17p John + * Added sun stuff. Made Glide optionally use 8-bpp textures. (looks + * bad) + * + * 34 3/14/98 3:07p Adam + * re-added some lighting changes + * + * 33 3/13/98 4:10p John + * Put back in Adam's old lighting values. + * + * 32 3/12/98 8:42a John + * Checked in Adam's new lighting values. + * Checked in changes to timing models code + * + * 31 2/26/98 5:42p John + * + * 30 2/26/98 3:25p John + * Added code to turn on/off lighting. Made lighting used dist*dist + * instead of dist + * + * 29 2/19/98 10:51p John + * Enabled colored lighting for hardware (Glide) + * + * 28 2/13/98 5:00p John + * Made lighting push functions return number of releveent lights. + * + * 27 1/29/98 3:36p Johnson + * JAS: Fixed some problems with pre_player_entry. + * + * 26 1/29/98 3:16p Allender + * fix compiler warnings + * + * 25 1/29/98 8:14a John + * Added support for RGB lighting + * + * 24 1/23/98 5:08p John + * Took L out of vertex structure used B (blue) instead. Took all small + * fireballs out of fireball types and used particles instead. Fixed some + * debris explosion things. Restructured fireball code. Restructured + * some lighting code. Made dynamic lighting on by default. Made groups + * of lasers only cast one light. Made fireballs not cast light. + * + * 23 1/10/98 1:14p John + * Added explanation to debug console commands + * + * 22 1/02/98 11:53a Adam + * Changed lighting mode to darken. Changed ambient and reflect to .75 + * and .50 to compensate. + * + * 21 1/02/98 11:28a Adam + * Changed default ambient to 0.55f from 0.45f. + * + * 20 12/21/97 4:33p John + * Made debug console functions a class that registers itself + * automatically, so you don't need to add the function to + * debugfunctions.cpp. + * + * 19 12/17/97 7:53p John + * Fixed a bug where gunpoint for flashes were in world coordinates, + * should have been object. + * + * 18 12/17/97 5:11p John + * Added brightening back into fade table. Added code for doing the fast + * dynamic gun flashes and thruster flashes. + * + * 17 12/12/97 3:02p John + * First Rev of Ship Shadows + * + * 16 11/07/97 7:24p John + * changed lighting to take two ranges. + * In textest, added test code to draw nebulas + * + * 15 11/04/97 9:19p John + * Optimized dynamic lighting more. + * + * 14 10/29/97 5:05p John + * Changed dynamic lighting to only rotate and calculate lighting for + * point lights that are close to an object. Changed lower framerate cap + * from 4 to .5. + * + * 13 9/24/97 12:37p Mike + * Crank ambient lighting from 0.2 to 0.6 + * + * 12 9/17/97 9:44a John + * fixed bug with lighting set to default + * + * 11 9/11/97 5:36p Jasen + * Changed ambient and reflective lighting values to give a bit more + * realism to the game. Yeah, yeah, I know I am not a programmer. + * + * 10 4/24/97 2:58p John + * + * 9 4/24/97 11:49a John + * added new lighting commands to console. + * + * 8 4/23/97 5:26p John + * First rev of new debug console stuff. + * + * 7 4/22/97 3:14p John + * upped the ambient light + * + * 6 4/17/97 6:06p John + * New code/data for v19 of BSPGEN with smoothing and zbuffer + * optimizations. + * + * 5 4/08/97 5:18p John + * First rev of decent (dynamic, correct) lighting in FreeSpace. + * + * 4 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * 3 1/30/97 9:35a Hoffoss + * Added header for files. + * + * $NoKeywords: $ + */ + +#include "pstypes.h" +#include "vecmat.h" +#include "3d.h" +#include "fvi.h" +#include "key.h" +#include "lighting.h" +#include "timer.h" +#include "systemvars.h" + +#define MAX_LIGHTS 256 +#define MAX_LIGHT_LEVELS 16 + +#define LT_DIRECTIONAL 0 // A light like a sun +#define LT_POINT 1 // A point light, like an explosion +#define LT_TUBE 2 // A tube light, like a fluorescent light + +typedef struct light { + int type; // What type of light this is + vector vec; // location in world space of a point light or the direction of a directional light or the first point on the tube for a tube light + vector vec2; // second point on a tube light + vector local_vec; // rotated light vector + vector local_vec2; // rotated 2nd light vector for a tube light + float intensity; // How bright the light is. + float rad1, rad1_squared; // How big of an area a point light affect. Is equal to l->intensity / MIN_LIGHT; + float rad2, rad2_squared; // How big of an area a point light affect. Is equal to l->intensity / MIN_LIGHT; + float r,g,b; // The color components of the light + int ignore_objnum; // Don't light this object. Used to optimize weapons casting light on parents. + int affected_objnum; // for "unique lights". ie, lights which only affect one object (trust me, its useful) +} light; + +light Lights[MAX_LIGHTS]; +int Num_lights=0; + +light *Relevent_lights[MAX_LIGHTS][MAX_LIGHT_LEVELS]; +int Num_relevent_lights[MAX_LIGHT_LEVELS]; +int Num_light_levels = 0; + +#define MAX_STATIC_LIGHTS 10 +light * Static_light[MAX_STATIC_LIGHTS]; +int Static_light_count = 0; + +static int Light_in_shadow = 0; // If true, this means we're in a shadow + +#define LM_BRIGHTEN 0 +#define LM_DARKEN 1 + +#define MIN_LIGHT 0.03f // When light drops below this level, ignore it. Must be non-zero! (1/32) + + +int Lighting_off = 0; + +// For lighting values, 0.75 is full intensity + +#if 1 // ADAM'S new stuff + int Lighting_mode = LM_BRIGHTEN; + #define AMBIENT_LIGHT_DEFAULT 0.15f //0.10f + #define REFLECTIVE_LIGHT_DEFAULT 0.75f //0.90f +#else + int Lighting_mode = LM_DARKEN; + #define AMBIENT_LIGHT_DEFAULT 0.75f //0.10f + #define REFLECTIVE_LIGHT_DEFAULT 0.50f //0.90f +#endif + +float Ambient_light = AMBIENT_LIGHT_DEFAULT; +float Reflective_light = REFLECTIVE_LIGHT_DEFAULT; + +int Lighting_flag = 1; + +DCF(light,"Changes lighting parameters") +{ + if ( Dc_command ) { + dc_get_arg(ARG_STRING); + if ( !strcmp( Dc_arg, "ambient" )) { + dc_get_arg(ARG_FLOAT); + if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) ) { + Dc_help = 1; + } else { + Ambient_light = Dc_arg_float; + } + } else if ( !strcmp( Dc_arg, "reflect" )) { + dc_get_arg(ARG_FLOAT); + if ( (Dc_arg_float < 0.0f) || (Dc_arg_float > 1.0f) ) { + Dc_help = 1; + } else { + Reflective_light = Dc_arg_float; + } + } else if ( !strcmp( Dc_arg, "default" )) { + Lighting_mode = LM_BRIGHTEN; + Ambient_light = AMBIENT_LIGHT_DEFAULT; + Reflective_light = REFLECTIVE_LIGHT_DEFAULT; + Lighting_flag = 0; + } else if ( !strcmp( Dc_arg, "mode" )) { + dc_get_arg(ARG_STRING); + if ( !strcmp(Dc_arg, "light") ) { + Lighting_mode = LM_BRIGHTEN; + } else if ( !strcmp(Dc_arg, "darken")) { + Lighting_mode = LM_DARKEN; + } else { + Dc_help = 1; + } + } else if ( !strcmp( Dc_arg, "dynamic" )) { + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_NONE); + if ( Dc_arg_type & ARG_TRUE ) Lighting_flag = 1; + else if ( Dc_arg_type & ARG_FALSE ) Lighting_flag = 0; + else if ( Dc_arg_type & ARG_NONE ) Lighting_flag ^= 1; + } else if ( !strcmp( Dc_arg, "on" ) ) { + Lighting_off = 0; + } else if ( !strcmp( Dc_arg, "off" ) ) { + Lighting_off = 1; + } else { + // print usage, not stats + Dc_help = 1; + } + } + + if ( Dc_help ) { + dc_printf( "Usage: light keyword\nWhere keyword can be in the following forms:\n" ); + dc_printf( "light on|off Turns all lighting on/off\n" ); + dc_printf( "light default Resets lighting to all default values\n" ); + dc_printf( "light ambient X Where X is the ambient light between 0 and 1.0\n" ); + dc_printf( "light reflect X Where X is the material reflectiveness between 0 and 1.0\n" ); + dc_printf( "light dynamic [bool] Toggles dynamic lighting on/off\n" ); + dc_printf( "light mode [light|darken] Changes the lighting mode.\n" ); + dc_printf( " Where 'light' means the global light adds light.\n"); + dc_printf( " and 'darken' means the global light subtracts light.\n"); + Dc_status = 0; // don't print status if help is printed. Too messy. + } + + if ( Dc_status ) { + dc_printf( "Ambient light is set to %.2f\n", Ambient_light ); + dc_printf( "Reflective light is set to %.2f\n", Reflective_light ); + dc_printf( "Dynamic lighting is: %s\n", (Lighting_flag?"on":"off") ); + switch( Lighting_mode ) { + case LM_BRIGHTEN: dc_printf( "Lighting mode is: light\n" ); break; + case LM_DARKEN: dc_printf( "Lighting mode is: darken\n" ); break; + default: dc_printf( "Lighting mode is: UNKNOWN\n" ); break; + } + } +} + +void light_reset() +{ + int idx; + + // reset static (sun) lights + for(idx=0; idxtype ) { + case LT_DIRECTIONAL: + // Rotate the light direction into local coodinates + vm_vec_rotate(&l->local_vec, &l->vec, &Light_matrix ); + break; + + case LT_POINT: { + vector tempv; + // Rotate the point into local coordinates + vm_vec_sub(&tempv, &l->vec, &Light_base ); + vm_vec_rotate(&l->local_vec, &tempv, &Light_matrix ); + } + break; + + case LT_TUBE:{ + vector tempv; + // Rotate the point into local coordinates + vm_vec_sub(&tempv, &l->vec, &Light_base ); + vm_vec_rotate(&l->local_vec, &tempv, &Light_matrix ); + + // Rotate the point into local coordinates + vm_vec_sub(&tempv, &l->vec2, &Light_base ); + vm_vec_rotate(&l->local_vec2, &tempv, &Light_matrix ); + } + break; + + default: + Int3(); // Invalid light type + } +} + +// Sets the ambient lighting level. +// Ignored for now. +void light_set_ambient(float ambient_light) +{ +} + +void light_add_directional( vector *dir, float intensity, float r, float g, float b ) +{ + light * l; + + if ( Lighting_off ) return; + + if ( Num_lights >= MAX_LIGHTS ) return; + + l = &Lights[Num_lights++]; + + l->type = LT_DIRECTIONAL; + + if ( Lighting_mode == LM_BRIGHTEN ) { + vm_vec_copy_scale( &l->vec, dir, -1.0f ); + } else { + vm_vec_copy_scale( &l->vec, dir, 1.0f ); + } + + l->r = r; + l->g = g; + l->b = b; + l->intensity = intensity; + l->rad1 = 0.0f; + l->rad2 = 0.0f; + l->rad1_squared = l->rad1*l->rad1; + l->rad2_squared = l->rad2*l->rad2; + l->ignore_objnum = -1; + l->affected_objnum = -1; + + Assert( Num_light_levels <= 1 ); +// Relevent_lights[Num_relevent_lights[Num_light_levels-1]++][Num_light_levels-1] = l; + + if(Static_light_count < MAX_STATIC_LIGHTS){ + Static_light[Static_light_count++] = l; + } +} + + +void light_add_point( vector * pos, float rad1, float rad2, float intensity, float r, float g, float b, int ignore_objnum ) +{ + light * l; + + if ( Lighting_off ) return; + + if (!Lighting_flag) return; + +// if ( keyd_pressed[KEY_LSHIFT] ) return; + + if ( Num_lights >= MAX_LIGHTS ) { + mprintf(( "Out of lights!\n" )); + return; + } + + l = &Lights[Num_lights++]; + + l->type = LT_POINT; + l->vec = *pos; + l->r = r; + l->g = g; + l->b = b; + l->intensity = intensity; + l->rad1 = rad1; + l->rad2 = rad2; + l->rad1_squared = l->rad1*l->rad1; + l->rad2_squared = l->rad2*l->rad2; + l->ignore_objnum = ignore_objnum; + l->affected_objnum = -1; + + Assert( Num_light_levels <= 1 ); +// Relevent_lights[Num_relevent_lights[Num_light_levels-1]++][Num_light_levels-1] = l; +} + +void light_add_point_unique( vector * pos, float rad1, float rad2, float intensity, float r, float g, float b, int affected_objnum) +{ + light * l; + + if ( Lighting_off ) return; + + if (!Lighting_flag) return; + +// if ( keyd_pressed[KEY_LSHIFT] ) return; + + if ( Num_lights >= MAX_LIGHTS ) { + mprintf(( "Out of lights!\n" )); + return; + } + + l = &Lights[Num_lights++]; + + l->type = LT_POINT; + l->vec = *pos; + l->r = r; + l->g = g; + l->b = b; + l->intensity = intensity; + l->rad1 = rad1; + l->rad2 = rad2; + l->rad1_squared = l->rad1*l->rad1; + l->rad2_squared = l->rad2*l->rad2; + l->ignore_objnum = -1; + l->affected_objnum = affected_objnum; + + Assert( Num_light_levels <= 1 ); +} + +// for now, tube lights only affect one ship (to keep the filter stuff simple) +void light_add_tube(vector *p0, vector *p1, float r1, float r2, float intensity, float r, float g, float b, int affected_objnum) +{ + light * l; + + if ( Lighting_off ) return; + + if (!Lighting_flag) return; + +// if ( keyd_pressed[KEY_LSHIFT] ) return; + + if ( Num_lights >= MAX_LIGHTS ) { + mprintf(( "Out of lights!\n" )); + return; + } + + l = &Lights[Num_lights++]; + + l->type = LT_TUBE; + l->vec = *p0; + l->vec2 = *p1; + l->r = r; + l->g = g; + l->b = b; + l->intensity = intensity; + l->rad1 = r1; + l->rad2 = r2; + l->rad1_squared = l->rad1*l->rad1; + l->rad2_squared = l->rad2*l->rad2; + l->ignore_objnum = -1; + l->affected_objnum = affected_objnum; + + Assert( Num_light_levels <= 1 ); +} + +// Reset the list of lights to point to all lights. +void light_filter_reset() +{ + int i; + light *l; + + if ( Lighting_off ) return; + + Num_light_levels = 1; + + int n = Num_light_levels-1; + Num_relevent_lights[n] = 0; + + l = Lights; + for (i=0; itype ) { + case LT_DIRECTIONAL: + //Relevent_lights[Num_relevent_lights[n2]++][n2] = l; + break; + + case LT_POINT: { + // if this is a "unique" light source, it only affects one guy + if(l->affected_objnum >= 0){ + if(objnum == l->affected_objnum){ + vector to_light; + float dist_squared, max_dist_squared; + vm_vec_sub( &to_light, &l->vec, pos ); + dist_squared = vm_vec_mag_squared(&to_light); + + max_dist_squared = l->rad2+rad; + max_dist_squared *= max_dist_squared; + + if ( dist_squared < max_dist_squared ) { + Relevent_lights[Num_relevent_lights[n2]++][n2] = l; + } + } + } + // otherwise check all relevant objects + else { + // if ( (l->ignore_objnum<0) || (l->ignore_objnum != objnum) ) { + vector to_light; + float dist_squared, max_dist_squared; + vm_vec_sub( &to_light, &l->vec, pos ); + dist_squared = vm_vec_mag_squared(&to_light); + + max_dist_squared = l->rad2+rad; + max_dist_squared *= max_dist_squared; + + if ( dist_squared < max_dist_squared ) { + Relevent_lights[Num_relevent_lights[n2]++][n2] = l; + } + // } + } + } + break; + + // hmm. this could probably be more optimal + case LT_TUBE: + // all tubes are "unique" light sources for now + if((l->affected_objnum >= 0) && (objnum == l->affected_objnum)){ + Relevent_lights[Num_relevent_lights[n2]++][n2] = l; + } + break; + + default: + Int3(); // Invalid light type + } + } + + return Num_relevent_lights[n2]; +} + +int is_inside( vector *min, vector *max, vector * p0, float rad ) +{ + float *origin = (float *)&p0->x; + float *minB = (float *)min; + float *maxB = (float *)max; + int i; + + for (i=0; i<3; i++ ) { + if ( origin[i] < minB[i] - rad ) { + return 0; + } else if (origin[i] > maxB[i] + rad ) { + return 0; + } + } + return 1; +} + + +int light_filter_push_box( vector *min, vector *max ) +{ + int i; + light *l; + + if ( Lighting_off ) return 0; + + int n1,n2; + n1 = Num_light_levels-1; + n2 = Num_light_levels; + Num_light_levels++; + +// static int mll = -1; +// if ( Num_light_levels > mll ) { +// mll = Num_light_levels; +// mprintf(( "Max level = %d\n", mll )); +// } + + Assert( Num_light_levels < MAX_LIGHT_LEVELS ); + + Num_relevent_lights[n2] = 0; + + for (i=0; itype ) { + case LT_DIRECTIONAL: + Int3(); //Relevent_lights[Num_relevent_lights[n2]++][n2] = l; + break; + + case LT_POINT: { + // l->local_vec + if ( is_inside( min, max, &l->local_vec, l->rad2 ) ) { + Relevent_lights[Num_relevent_lights[n2]++][n2] = l; + } + } + break; + + case LT_TUBE: + if ( is_inside(min, max, &l->local_vec, l->rad2) || is_inside(min, max, &l->local_vec2, l->rad2) ) { + Relevent_lights[Num_relevent_lights[n2]++][n2] = l; + } + break; + + default: + Int3(); // Invalid light type + } + } + + return Num_relevent_lights[n2]; +} + +void light_filter_pop() +{ + if ( Lighting_off ) return; + + Num_light_levels--; + Assert( Num_light_levels > 0 ); +} + +int l_num_points=0, l_num_lights=0; + + +void light_rotate_all() +{ + int i; + light *l; + + if ( Lighting_off ) return; + + int n = Num_light_levels-1; + + l = Lights; + for (i=0; i MAX_STATIC_LIGHTS) || (n > Static_light_count-1)){ + return 0; + } + + if ( Static_light[n] == NULL ) { + return 0; + } + + if (pos) { + *pos = Static_light[n]->vec; + + if ( Lighting_mode != LM_DARKEN ) { + vm_vec_scale( pos, -1.0f ); + } + } + return 1; +} + + +void light_set_shadow( int state ) +{ + Light_in_shadow = state; +} + + +ubyte light_apply( vector *pos, vector * norm, float static_light_level ) +{ + int i, idx; + float lval; + light *l; + + if (Detail.lighting==0) { + // No static light + ubyte l = ubyte(fl2i(static_light_level*255.0f)); + return l; + } + + if ( Lighting_off ) return 191; + + // Factor in ambient light + lval = Ambient_light; + + // Factor in light from suns if there are any + if ( !Light_in_shadow ){ + // apply all sun lights + for(idx=0; idxlocal_vec, norm )*Static_light[idx]->intensity*Reflective_light; // reflective light + + switch(Lighting_mode) { + case LM_BRIGHTEN: + if ( ltmp > 0.0f ) + lval += ltmp; + break; + case LM_DARKEN: + if ( ltmp > 0.0f ) + lval -= ltmp; + + if ( lval < 0.0f ) + lval = 0.0f; + break; + } + } + } + + // At this point, l must be between 0 and 0.75 (0.75-1.0 is for dynamic light only) + if ( lval < 0.0f ) { + lval = 0.0f; + } else if ( lval > 0.75f ) { + lval = 0.75f; + } + + lval *= static_light_level; + + int n = Num_light_levels-1; + + l_num_lights += Num_relevent_lights[n]; + l_num_points++; + + for (i=0; ilocal_vec, pos ); + dot = vm_vec_dot(&to_light, norm ); + if ( dot > 0.0f ) { + dist = vm_vec_mag_squared(&to_light); + if ( dist < l->rad1_squared ) { + lval += l->intensity*dot; + } else if ( dist < l->rad2_squared ) { + // dist from 0 to + float n = dist - l->rad1_squared; + float d = l->rad2_squared - l->rad1_squared; + float ltmp = (1.0f - n / d )*dot*l->intensity; + lval += ltmp; + } + if ( lval > 1.0f ) { + return 255; + } + } + } + + return ubyte(fl2i(lval*255.0f)); +} + + +void light_apply_rgb( ubyte *param_r, ubyte *param_g, ubyte *param_b, vector *pos, vector * norm, float static_light_level ) +{ + int i, idx; + float rval, gval, bval; + light *l; + + if (Detail.lighting==0) { + // No static light + ubyte l = ubyte(fl2i(static_light_level*255.0f)); + *param_r = l; + *param_g = l; + *param_b = l; + return; + } + + if ( Lighting_off ) { + *param_r = 255; + *param_g = 255; + *param_b = 255; + return; + } + + // Factor in ambient light + rval = Ambient_light; + gval = Ambient_light; + bval = Ambient_light; + + // Factor in light from sun if there is one + if ( !Light_in_shadow ){ + // apply all sun lights + for(idx=0; idxlocal_vec, norm )*Static_light[idx]->intensity*Reflective_light; // reflective light + + switch(Lighting_mode) { + case LM_BRIGHTEN: + if ( ltmp > 0.0f ) { + rval += Static_light[idx]->r * ltmp; + gval += Static_light[idx]->g * ltmp; + bval += Static_light[idx]->b * ltmp; + } + break; + case LM_DARKEN: + if ( ltmp > 0.0f ) { + rval -= ltmp; if ( rval < 0.0f ) rval = 0.0f; + gval -= ltmp; if ( gval < 0.0f ) gval = 0.0f; + bval -= ltmp; if ( bval < 0.0f ) bval = 0.0f; + } + break; + } + } + } + + // At this point, l must be between 0 and 0.75 (0.75-1.0 is for dynamic light only) + if ( rval < 0.0f ) { + rval = 0.0f; + } else if ( rval > 0.75f ) { + rval = 0.75f; + } + if ( gval < 0.0f ) { + gval = 0.0f; + } else if ( gval > 0.75f ) { + gval = 0.75f; + } + if ( bval < 0.0f ) { + bval = 0.0f; + } else if ( bval > 0.75f ) { + bval = 0.75f; + } + + rval *= static_light_level; + gval *= static_light_level; + bval *= static_light_level; + + int n = Num_light_levels-1; + + l_num_lights += Num_relevent_lights[n]; + l_num_points++; + + vector to_light; + float dot, dist; + vector temp; + for (i=0; itype){ + // point lights + case LT_POINT: + vm_vec_sub( &to_light, &l->local_vec, pos ); + break; + + // tube lights + case LT_TUBE: + if(vm_vec_dist_to_line(pos, &l->local_vec, &l->local_vec2, &temp, &dist) != 0){ + continue; + } + vm_vec_sub(&to_light, &temp, pos); + dist *= dist; // since we use radius squared + break; + + // others. BAD + default: + Int3(); + } + + dot = vm_vec_dot(&to_light, norm); + // dot = 1.0f; + if ( dot > 0.0f ) { + // indicating that we already calculated the distance (vm_vec_dist_to_line(...) does this for us) + if(dist < 0.0f){ + dist = vm_vec_mag_squared(&to_light); + } + if ( dist < l->rad1_squared ) { + float ratio; + ratio = l->intensity*dot; + ratio *= 0.25f; + rval += l->r*ratio; + gval += l->g*ratio; + bval += l->b*ratio; + } else if ( dist < l->rad2_squared ) { + float ratio; + // dist from 0 to + float n = dist - l->rad1_squared; + float d = l->rad2_squared - l->rad1_squared; + ratio = (1.0f - n / d)*dot*l->intensity; + ratio *= 0.25f; + rval += l->r*ratio; + gval += l->g*ratio; + bval += l->b*ratio; + } + } + } + + float m = rval; + if ( gval > m ) m = gval; + if ( bval > m ) m = bval; + + if ( m > 1.0f ) { + float im = 1.0f / m; + + rval *= im; + gval *= im; + bval *= im; + } + + if ( rval < 0.0f ) { + rval = 0.0f; + } else if ( rval > 1.0f ) { + rval = 1.0f; + } + if ( gval < 0.0f ) { + gval = 0.0f; + } else if ( gval > 1.0f ) { + gval = 1.0f; + } + if ( bval < 0.0f ) { + bval = 0.0f; + } else if ( bval > 1.0f ) { + bval = 1.0f; + } + + *param_r = ubyte(fl2i(rval*255.0f)); + *param_g = ubyte(fl2i(gval*255.0f)); + *param_b = ubyte(fl2i(bval*255.0f)); +} + + +/* +float light_apply( vector *pos, vector * norm ) +{ +#if 1 + float r,g,b; + light_apply_rgb( &r, &g, &b, pos, norm ); + return (r+g+b) / 3.0f; +#else + return light_apply_ramp( pos, norm ); +#endif + +} +*/ + + + diff --git a/src/localization/fhash.cpp b/src/localization/fhash.cpp new file mode 100644 index 0000000..7c788e0 --- /dev/null +++ b/src/localization/fhash.cpp @@ -0,0 +1,227 @@ +/* + * $Logfile: /Freespace2/code/localization/fhash.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 5 2/23/99 9:25a Dave + * Stubbed out a bunch of stuff to get cfile and lcl functions in. + * + * 4 12/01/98 4:46p Dave + * Put in targa bitmap support (16 bit). + * + * $NoKeywords: $ + */ + +#include +#include +#include +#include "pstypes.h" +#include "fhash.h" + +// ----------------------------------------------------------------------------------------------- +// HASH DEFINES/VARS +// + +// hash node +typedef struct fhash_node { + char *str; // allocated dynamically + int id; // can be -1 + fhash_node *next, *prev; // for chaining in an individual hash table entry (non-circular, doubly linked list) +} fhash_node; + +// hash table itself (with chained nodes) +#define HASH_TABLE_SIZE 253 // works better when not a power of 2, and is prime +fhash_node *Hash_table_fred[HASH_TABLE_SIZE]; + +// if the hash table is active +int Fhash_active = 0; + + +// ----------------------------------------------------------------------------------------------- +// HASH FORWARD DECLARATIONS +// + +// hash a string. return an index into the hash table where it should be inserted +int fhash_get_hash_index(char *str); + +// insert a string into hash table index N, will take care of allocating/chaining everything +void fhash_insert(char *str, int id, int n); + + +// ----------------------------------------------------------------------------------------------- +// HASH FUNCTIONS +// + +// initialize the hash table +void fhash_init() +{ + memset(Hash_table_fred, 0, sizeof(fhash_node) * HASH_TABLE_SIZE); +} + +// set the hash table to be active for parsing +void fhash_activate() +{ + Fhash_active = 1; +} + +// set the hash table to be inactive for parsing +void fhash_deactivate() +{ + Fhash_active = 0; +} + +// if the hash table is active +int fhash_active() +{ + return Fhash_active; +} + +// flush out the hash table, freeing up everything +void fhash_flush() +{ + int idx; + fhash_node *moveup, *backup; + + // go through each element + for(idx=0; idxnext; + + // free up this element + if(backup->str != NULL){ + free(backup->str); + } + free(backup); + } + + // null this element + Hash_table_fred[idx] = NULL; + } + } +} + +// add a string with the given id# to the has table +void fhash_add_str(char *str, int id) +{ + int hash_index; + + // if the hash table isn't active, don't bother + Assert(Fhash_active); + if(!Fhash_active){ + return; + } + + // determine where the string goes in the has table + Assert(str != NULL); + if(str == NULL){ + return; + } + hash_index = fhash_get_hash_index(str); + + // insert into the hash table + fhash_insert(str, id, hash_index); +} + +// determine if the passed string exists in the table +// returns : -2 if the string doesn't exit, or >= -1 as the string id # otherwise +int fhash_string_exists(char *str) +{ + int hash_index; + fhash_node *moveup; + + Assert(str != NULL); + if(str == NULL){ + return -2; + } + + // get the hash index for this string + hash_index = fhash_get_hash_index(str); + + // if there are no entries, it doesn't exist + if(Hash_table_fred[hash_index] == NULL){ + return -2; + } + + // otherwise compare against all strings in this code + moveup = Hash_table_fred[hash_index]; + while(moveup != NULL){ + // do a string compare on this item + Assert(moveup->str != NULL); + if(moveup->str != NULL){ + if(!strcmp(moveup->str, str)){ + return moveup->id; + } + } + + // next item + moveup = moveup->next; + } + + // didn't find it + return -2; +} + + +// ----------------------------------------------------------------------------------------------- +// HASH FORWARD DEFINITIONS +// + +// hash a string. return an index into the hash table where it should be inserted +int fhash_get_hash_index(char *str) +{ + int accum = 0; + int idx, str_len; + int ret; + + // add up the string + str_len = strlen(str); + for(idx=0; idxstr = strdup(str); + new_node->id = id; + new_node->next = NULL; + new_node->prev = NULL; + + // if this hash index is NULL, just assign it + if(Hash_table_fred[n] == NULL){ + Hash_table_fred[n] = new_node; + } else { + moveup = Hash_table_fred[n]; + while(moveup->next != NULL){ + moveup = moveup->next; + } + new_node->prev = moveup; + moveup->next = new_node; + } +} \ No newline at end of file diff --git a/src/localization/localize.cpp b/src/localization/localize.cpp new file mode 100644 index 0000000..c0c5ee2 --- /dev/null +++ b/src/localization/localize.cpp @@ -0,0 +1,1614 @@ +/* + * $Logfile: /Freespace2/code/Localization/localize.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 91 6/16/00 3:16p Jefff + * sim of the year dvd version changes, a few german soty localization + * fixes + * + * 90 11/02/99 8:20p Jefff + * more translation changes/additions + * + * 89 11/02/99 3:24p Jefff + * added translation functions for a few key instances where english was + * showing up + * + * 88 10/29/99 10:40p Jefff + * mo stirngs + * + * 87 10/28/99 2:05a Jefff + * safety fix in localization stuff + * + * 86 10/26/99 10:53a Jefff + * German builds now correctly default to German on first run + * + * 85 10/25/99 11:22p Jefff + * mo' strings (launcher) + * + * 84 10/25/99 5:46p Jefff + * Many localization fixes/changes for German builds + * + * 83 10/14/99 3:25p Jefff + * soo...many....xstrs.... + * + * 82 10/14/99 2:52p Jefff + * localization fixes. added support for hi-res specific xstr offsets + * + * 81 10/13/99 3:58p Jefff + * added a bunch of missed XSTRs + * + * 80 9/13/99 11:30a Dave + * Added checkboxes and functionality for disabling PXO banners as well as + * disabling d3d zbuffer biasing. + * + * 79 9/08/99 9:59a Jefff + * + * 78 9/07/99 4:01p Dave + * Fixed up a string.tbl paroblem (self destruct message). Make sure IPX + * does everything properly (setting up address when binding). Remove + * black rectangle background from UI_INPUTBOX. + * + * 77 9/07/99 1:54p Jefff + * skip mission strings added + * + * 76 9/03/99 10:55a Jefff + * xstr_size++ + * + * 75 9/03/99 1:31a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 74 8/27/99 12:04a Dave + * Campaign loop screen. + * + * 73 8/26/99 12:48p Jefff + * + * 72 8/22/99 5:53p Dave + * Scoring fixes. Added self destruct key. Put callsigns in the logfile + * instead of ship designations for multiplayer players. + * + * 71 8/22/99 1:55p Dave + * Cleaned up host/team-captain leaving code. + * + * 70 8/17/99 7:32p Jefff + * mo' strings + * + * 69 8/16/99 5:15p Dave + * Upped string count. + * + * 68 8/16/99 4:04p Dave + * Big honking checkin. + * + * 67 8/11/99 2:09p Jefff + * ill give you three guesses. + * + * 66 8/10/99 7:57p Jefff + * even more strings + * + * 65 8/05/99 2:40p Jefff + * added string + * + * 64 8/02/99 9:13p Dave + * Added popup tips. + * + * 63 8/02/99 12:01p Jefff + * added a string + * + * 62 7/29/99 10:47p Dave + * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs. + * + * 61 7/29/99 9:44a Jefff + * added some strings for ingame objectives screen + * + * 60 7/27/99 7:15p Jefff + * 3 new interface strings + * + * 59 7/27/99 6:53p Dave + * Hi-res main hall support. + * + * 58 7/24/99 1:54p Dave + * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert + * missions. + * + * 57 7/22/99 4:08p Jefff + * some new strings + * + * 56 7/19/99 7:20p Dave + * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula + * pre-rendering. + * + * 55 7/19/99 2:13p Dave + * Added some new strings for Heiko. + * + * 54 7/15/99 2:37p Jefff + * + * 53 7/13/99 6:07p Jefff + * Added support for localization string offsets. + * + * 52 7/09/99 10:32p Dave + * Command brief and red alert screens. + * + * 51 6/25/99 2:51p Jasons + * New string. + * + * 50 6/25/99 11:59a Dave + * Multi options screen. + * + * 49 6/24/99 12:34a Dave + * Main options screen. + * + * 48 6/19/99 3:58p Dave + * More new strings. + * + * 47 6/14/99 5:19p Dave + * + * 46 6/04/99 11:32a Dave + * Added reset text to ship select screen. Fixed minor xstr bug in ui + * window + * + * 45 6/01/99 6:07p Dave + * New loading/pause/please wait bar. + * + * 44 6/01/99 4:03p Dave + * Changed some popup flag #defines. Upped string count to 1336 + * + * 43 5/26/99 11:46a Dave + * Added ship-blasting lighting and made the randomization of lighting + * much more customizable. + * + * 42 5/22/99 6:05p Dave + * Fixed a few localization # problems. + * + * 41 5/21/99 6:45p Dave + * Sped up ui loading a bit. Sped up localization disk access stuff. Multi + * start game screen, multi password, and multi pxo-help screen. + * + * 40 5/17/99 2:52p Dave + * Fixed dumb localization problem. + * + * 39 5/17/99 10:07a Dave + * Updated string count. + * + * 38 5/04/99 6:38p Dave + * Finished multi join-wait screen. + * + * 37 5/04/99 5:20p Dave + * Fixed up multiplayer join screen and host options screen. Should both + * be at 100% now. + * + * 36 5/03/99 11:04p Dave + * Most of the way done with the multi join screen. + * + * 35 5/03/99 8:32p Dave + * New version of multi host options screen. + * + * 34 4/09/99 2:28p Dave + * Upped Xstring count. + * + * 33 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 32 4/08/99 12:50p Neilk + * changed number of xstr constant + * + * 31 4/07/99 5:54p Dave + * Fixes for encryption in updatelauncher. + * + * 30 3/24/99 4:05p Dave + * Put in support for assigning the player to a specific squadron with a + * specific logo. Preliminary work for doing pos/orient checksumming in + * multiplayer to reduce bandwidth. + * + * 29 3/23/99 3:35p Andsager + * Add error check for duplicate string indices + * + * 28 3/22/99 2:56p Andsager + * Localize Launcher Updater + * + * 27 2/23/99 11:18a Andsager + * Localize launcher using strings.tbl + * + * 26 2/22/99 9:35p Andsager + * Add lcl_get_language_name() returns string with current lang. Added + * localization for launcher. + * + * 25 2/21/99 6:02p Dave + * Fixed standalone WSS packets. + * + * 24 2/17/99 2:11p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 23 2/12/99 6:15p Dave + * Pre-mission Squad War code is 95% done. + * + * 22 2/02/99 7:27p Neilk + * implemented lc_close() to free up some string memory when shutting down + * + * 21 1/30/99 1:29a Dave + * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot + * screen. Fixed beam weapon death messages. + * + * 20 1/29/99 7:56p Neilk + * Added new mission log interface + * + * 19 1/29/99 4:17p Dave + * New interface screens. + * + * 18 1/29/99 12:47a Dave + * Put in sounds for beam weapon. A bunch of interface screens (tech + * database stuff). + * + * 17 1/13/99 2:10p Andsager + * added string + * + * 16 12/14/98 12:13p Dave + * Spiffed up xfer system a bit. Put in support for squad logo file xfer. + * Need to test now. + * + * 15 12/03/98 5:22p Dave + * Ported over Freespace 1 multiplayer ships.tbl and weapons.tbl + * checksumming. + * + * 14 12/02/98 5:47p Dave + * Put in interface xstr code. Converted barracks screen to new format. + * + * 13 12/01/98 4:46p Dave + * Put in targa bitmap support (16 bit). + * + * $NoKeywords: $ + */ + + +#include +#include "cfile.h" +#include "localize.h" +#include "parselo.h" +#include "osregistry.h" +#include "encrypt.h" +#include "2d.h" + +// ------------------------------------------------------------------------------------------------------------ +// LOCALIZE DEFINES/VARS +// + +// general language/localization data --------------------- + +// current language +int Lcl_current_lang = LCL_ENGLISH; + +// language info table +typedef struct lang_info { + char lang_name[LCL_LANG_NAME_LEN + 1]; // literal name of the language + char lang_ext[LCL_LANG_NAME_LEN + 1]; // for adding to names on disk access +} lang_info; + +lang_info Lcl_languages[LCL_NUM_LANGUAGES] = { + { "English", "" }, // english + { "German", "gr" }, // german + { "French", "fr" }, // french +}; + +//#if defined(GERMAN_BUILD) +//#define DEFAULT_LANGUAGE "German" +//#else +#define DEFAULT_LANGUAGE "English" +//#endif + +// following is the offset where special characters start in our font +#define LCL_SPECIAL_CHARS_FR 164 +#define LCL_SPECIAL_CHARS_GR 164 +#define LCL_SPECIAL_CHARS 127 +int Lcl_special_chars; + +// use these to replace *_BUILD, values before +// only 1 will be active at a time +int Lcl_fr = 0; +int Lcl_gr = 0 ; +int Lcl_english = 1; + + +// executable string localization data -------------------- + +// XSTR_SIZE is the total count of unique XSTR index numbers. An index is used to +// reference an entry in strings.tbl. This is used for translation of strings from +// the english version (in the code) to a foreign version (in the table). Thus, if you +// add a new string to the code, you must assign it a new index. Use the number below for +// that index and increase the number below by one. +#define XSTR_SIZE 1570 + + +// struct to allow for strings.tbl-determined x offset +// offset is 0 for english, by default +typedef struct { + char *str; + int offset_x; // string offset in 640 + int offset_x_hi; // string offset in 1024 +} lcl_xstr; + +//char *Xstr_table[XSTR_SIZE]; +lcl_xstr Xstr_table[XSTR_SIZE]; +int Xstr_inited = 0; + + +// table/mission externalization stuff -------------------- + +#define TABLE_STRING_FILENAME "tstrings.tbl" +// filename of the file to use when localizing table strings +char *Lcl_ext_filename = NULL; +CFILE *Lcl_ext_file = NULL; + +// for scanning/parsing tstrings.tbl (from ExStr) +#define PARSE_TEXT_STRING_LEN 2048 +#define PARSE_ID_STRING_LEN 128 +#define TS_SCANNING 0 // scanning for a line of text +#define TS_ID_STRING 1 // reading in an id string +#define TS_OPEN_QUOTE 2 // looking for an open quote +#define TS_STRING 3 // reading in the text string itself +int Ts_current_state = 0; +char Ts_text[PARSE_TEXT_STRING_LEN]; // string we're currently working with +char Ts_id_text[PARSE_ID_STRING_LEN]; // id string we're currently working with +int Ts_text_size; +int Ts_id_text_size; + +// file pointers for optimized string lookups +// some example times for Freespace2 startup with granularities (mostly .tbl files, ~500 strings in the table file, many looked up more than once) +// granularity 20 : 13 secs +// granularity 10 : 11 secs +// granularity 5 : 9 secs +// granularity 2 : 7-8 secs +#define LCL_GRANULARITY 1 // how many strings between each pointer (lower granularities should give faster lookup times) +#define LCL_MAX_POINTERS 4500 // max # of pointers +#define LCL_MAX_STRINGS (LCL_GRANULARITY * LCL_MAX_POINTERS) +int Lcl_pointers[LCL_MAX_POINTERS]; +int Lcl_pointer_count = 0; + + +// ------------------------------------------------------------------------------------------------------------ +// LOCALIZE FORWARD DECLARATIONS +// + +// associate table file externalization with the specified input file +void lcl_ext_associate(char *filename); + +// given a valid XSTR() tag piece of text, extract the string portion, return it in out, nonzero on success +int lcl_ext_get_text(char *xstr, char *out); + +// given a valid XSTR() tag piece of text, extract the id# portion, return the value in out, nonzero on success +int lcl_ext_get_id(char *xstr, int *out); + +// given a valid XSTR() id#, lookup the string in tstrings.tbl, filling in out if found, nonzero on success +int lcl_ext_lookup(char *out, int id); + +// if the char is a valid char for a signed integer value string +int lcl_is_valid_numeric_char(char c); + +// sub-parse function for individual lines of tstrings.tbl (from Exstr) +// returns : integer with the low bits having the following values : +// 0 on fail, 1 on success, 2 if found a matching id/string pair, 3 if end of language has been found +// for cases 1 and 2 : the high bit (1<<31) will be set if the parser detected the beginning of a new string id on this line +// so be sure to mask this value out to get the low portion of the return value +// +int lcl_ext_lookup_sub(char *text, char *out, int id); + +// initialize the pointer array into tstrings.tbl (call from lcl_ext_open() ONLY) +void lcl_ext_setup_pointers(); + + +// ------------------------------------------------------------------------------------------------------------ +// LOCALIZE FUNCTIONS +// + +// initialize localization, if no language is passed - use the language specified in the registry +void lcl_init(int lang_init) +{ + char lang_string[128]; + char *ret; + int lang, idx; + + // initialize encryption + encrypt_init(); + + // read the language from the registry + if(lang_init < 0){ + memset(lang_string, 0, 128); +#if defined(GERMAN_BUILD) + // defualt lang to German, mein herr + ret = os_config_read_string(NULL, "Language", "German"); +#else + // default to DEFAULT_LANGUAGE (which should be english so we dont have to put German text + // in tstrings in the #default section + ret = os_config_read_string(NULL, "Language", DEFAULT_LANGUAGE); +#endif + if(ret == NULL){ + Int3(); + strcpy(lang_string, DEFAULT_LANGUAGE); + } else { + strcpy(lang_string, ret); + } + + // look it up + lang = -1; + for(idx=0; idx= 0) && (lang_init < LCL_NUM_LANGUAGES)); + lang = lang_init; + } + + // language markers + Lcl_pointer_count = 0; + + // associate the table string file + lcl_ext_associate(TABLE_STRING_FILENAME); + + // set the language (this function takes care of setting up file pointers) + lcl_set_language(lang); +} + +// added 2.2.99 by NeilK to take care of fs2 launcher memory leaks +// shutdown localization +void lcl_close() +{ + // if the filename exists, free it up + if(Lcl_ext_filename != NULL){ + free(Lcl_ext_filename); + } +}; + +// determine what language we're running in, see LCL_* defines above +int lcl_get_language() +{ + return Lcl_current_lang; +} + +// initialize the xstr table +void lcl_xstr_init() +{ + char chr, buf[4096]; + char language_tag[512]; + int i, z, index, rval; + char *p_offset = NULL; + int offset_lo = 0, offset_hi = 0; + + for (i=0; i= 0) { + // Assert(buf[i] == '"'); + if (buf[i] != '"') { + // probably an offset on this entry + buf[i+1] = 0; // drop down a null terminator (prolly unnecessary) + while(!is_white_space(buf[i])) i--; // back up over the potential offset + while(is_white_space(buf[i])) i--; // now back up over intervening spaces + num_offsets_on_this_line = 1; + if (buf[i] != '"') { + // could have a 2nd offset value (one for 640, one for 1024) + // so back up again + while(!is_white_space(buf[i])) i--; // back up over the 2nd offset + while(is_white_space(buf[i])) i--; // now back up over intervening spaces + num_offsets_on_this_line = 2; + } + + p_offset = &buf[i+1]; // get ptr to string section with offset in it + if (buf[i] != '"') { + Error(LOCATION, NOX("Strings.tbl is corrupt")); // now its an error + } + } + + buf[i] = 0; + } + + // copy string into buf + z = 0; + for (i=1; buf[i]; i++) { + chr = buf[i]; + if (chr == '\\') { + chr = buf[++i]; + if (chr == 'n') { + chr = '\n'; + } else if (chr == 'r') { + chr = '\r'; + } + + } + + buf[z++] = chr; + } + + // null terminator on buf + buf[z] = 0; + + // write into Xstr_table + if (index >= 0 && index < XSTR_SIZE) { + if (Xstr_table[index].str != NULL) { + Warning(LOCATION, "Strings table index %d used more than once", index); + } + Xstr_table[index].str = strdup(buf); + } + + // read offset information, assume 0 if nonexistant + if (p_offset != NULL) { + if (sscanf(p_offset, "%d%d", &offset_lo, &offset_hi) < num_offsets_on_this_line) { + // whatever is in the file ain't a proper offset + Error(LOCATION, NOX("Strings.tbl is corrupt")); + } + } + Xstr_table[index].offset_x = offset_lo; + if (num_offsets_on_this_line == 2) { + Xstr_table[index].offset_x_hi = offset_hi; + } else { + Xstr_table[index].offset_x_hi = offset_lo; + } + + // clear out our vars + p_offset = NULL; + offset_lo = 0; + offset_hi = 0; + num_offsets_on_this_line = 0; + } + } + + Xstr_inited = 1; +} + + +// free Xstr table +void lcl_xstr_close() +{ + for (int i=0; i max_len){ + error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", in, str_len, max_len); + return; + } + strcpy(out, in); + if(id != NULL){ + *id = -2; + } + return; + } + + // otherwise, check to see if it's an XSTR() tag + memset(first_four, 0, 5); + strncpy(first_four, in, 4); + if(stricmp(first_four, "XSTR")){ + // NOT an XSTR() tag + if(str_len > max_len){ + error_display(0, "Token too long: [%s]. Length = %i. Max is %i.\n", in, str_len, max_len); + return; + } + strcpy(out, in); + if(id != NULL){ + *id = -2; + } + return; + } + + // at this point we _know_ its an XSTR() tag, so split off the strings and id sections + if(!lcl_ext_get_text(in, text_str)){ + Int3(); + strcpy(out, in); + if(id != NULL){ + *id = -1; + } + return; + } + if(!lcl_ext_get_id(in, &str_id)){ + strcpy(out, in); + if(id != NULL){ + *id = -1; + } + return; + } + + // if the localization file is not open, or we're running in the default language, return the original string + if((Lcl_ext_file == NULL) || (str_id < 0) || (Lcl_current_lang == LCL_DEFAULT_LANGUAGE)){ + strcpy(out, text_str); + if(id != NULL){ + *id = str_id; + } + return; + } + + // attempt to find the string + if(lcl_ext_lookup(lookup_str, str_id)){ + // copy to the outgoing string + Assert(strlen(lookup_str) <= (unsigned int)(max_len - 1)); + + if (strlen(lookup_str) > (unsigned int)(max_len-1)) { + // be safe and truncate string to fit + strncpy(out, lookup_str, (size_t) (max_len-1)); + out[max_len-1] = '\0'; // ensure null terminator, since strncpy(...) doesnt. + } else { + strcpy(out, lookup_str); + } + + } + // otherwise use what we have - probably should Int3() or assert here + else { + strcpy(out, text_str); + } + + // set the id # + if(id != NULL){ + *id = str_id; + } +} + +// translate the specified string based upon the current language +char *XSTR(char *str, int index) +{ + if(!Xstr_inited){ + return str; + } + + // perform a lookup + if (index >= 0 && index < XSTR_SIZE) { + if (Xstr_table[index].str){ + return Xstr_table[index].str; // return translation of string + } + } + + // can't translate, return original english string + return str; +} + +// retrieve the offset for a localized string +int lcl_get_xstr_offset(int index, int res) +{ + if (res == GR_640) { + return Xstr_table[index].offset_x; + } else { + return Xstr_table[index].offset_x_hi; + } +} + + +// ------------------------------------------------------------------------------------------------------------ +// LOCALIZE FORWARD DEFINITIONS +// + +// associate table file externalization with the specified input file +void lcl_ext_associate(char *filename) +{ + // if the filename already exists, free it up + if(Lcl_ext_filename != NULL){ + free(Lcl_ext_filename); + } + + // set the new filename + Lcl_ext_filename = strdup(filename); +} + +// given a valid XSTR() tag piece of text, extract the string portion, return it in out, nonzero on success +int lcl_ext_get_text(char *xstr, char *out) +{ + int str_start, str_end; + int str_len; + char *p, *p2; + + Assert(xstr != NULL); + Assert(out != NULL); + str_len = strlen(xstr); + + // this is some crazy wack-ass code. + // look for the open quote + str_start = str_end = 0; + p = strstr(xstr, "\""); + if(p == NULL){ + error_display(0, "Error parsing XSTR() tag %s\n", xstr); + return 0; + } else { + str_start = p - xstr + 1; + } + // make sure we're not about to walk past the end of the string + if((p - xstr) >= str_len){ + error_display(0, "Error parsing XSTR() tag %s\n", xstr); + return 0; + } + + // look for the close quote + p2 = strstr(p+1, "\""); + if(p2 == NULL){ + error_display(0, "Error parsing XSTR() tag %s\n", xstr); + return 0; + } else { + str_end = p2 - xstr; + } + + // now that we know the boundaries of the actual string in the XSTR() tag. copy it + memcpy(out, xstr + str_start, str_end - str_start); + + // success + return 1; +} + +// given a valid XSTR() tag piece of text, extract the id# portion, return the value in out, nonzero on success +int lcl_ext_get_id(char *xstr, int *out) +{ + char *p, *pnext; + int str_len; + + Assert(xstr != NULL); + Assert(out != NULL); + + str_len = strlen(xstr); + + // find the first quote + p = strstr(xstr, "\""); + if(p == NULL){ + error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr); + return 0; + } + // make sure we're not about to walk off the end of the string + if((p - xstr) >= str_len){ + error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr); + return 0; + } + p++; + + // continue searching until we find the close quote + while(1){ + pnext = strstr(p, "\""); + if(pnext == NULL){ + error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr); + return 0; + } + + // if the previous char is a \, we know its not the "end-of-string" quote + if(*(pnext - 1) != '\\'){ + p = pnext; + break; + } + + // continue + p = pnext; + } + + // search until we find a , + pnext = strstr(p, ","); + if(pnext == NULL){ + error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr); + return 0; + } + // make sure we're not about to walk off the end of the string + if((pnext - xstr) >= str_len){ + error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr); + return 0; + } + pnext++; + + // now get the id string + p = pnext; + pnext = strtok(p, ")"); + if(pnext == NULL){ + error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr); + return 0; + } + + // get the value and we're done + *out = atoi(pnext); + + // success + return 1; +} + +// given a valid XSTR() id#, lookup the string in tstrings.tbl, filling in out if found, nonzero on success +int lcl_ext_lookup(char *out, int id) +{ + char text[1024]; + int ret; + int pointer; + + Assert(Lcl_pointer_count >= 0); + Assert(Lcl_pointers[0] >= 0); + Assert(Lcl_pointers[Lcl_pointer_count - 1] >= 0); + Assert(Lcl_ext_file != NULL); + Assert(id >= 0); + + // seek to the closest pointer <= the id# we're looking for + pointer = id / LCL_GRANULARITY; + cfseek(Lcl_ext_file, Lcl_pointers[pointer], CF_SEEK_SET); + + // reset parsing vars and go to town + Ts_current_state = TS_SCANNING; + Ts_id_text_size = 0; + Ts_text_size; + memset(Ts_text, 0, PARSE_TEXT_STRING_LEN); + memset(Ts_id_text, 0, PARSE_ID_STRING_LEN); + while((cftell(Lcl_ext_file) < Lcl_pointers[Lcl_pointer_count - 1]) && cfgets(text, 1024, Lcl_ext_file)){ + ret = lcl_ext_lookup_sub(text, out, id); + + // run the line parse function + switch(ret & 0x0fffffff){ + // error + case 0 : + Int3(); // should never get here - it means the string doens't exist in the table!! + return 0; + + // success parsing the line - please continue + case 1 : + break; + + // found a matching string/id pair + case 2 : + // success +#if defined(GERMAN_BUILD) + // this is because tstrings.tbl reads in as ANSI for some reason + // opening tstrings with "rb" mode didnt seem to help, so its now still "rt" like before + lcl_fix_umlauts(out, LCL_TO_ASCII); +#endif + return 1; + + // end of language found + case 3 : + Int3(); // should never get here - it means the string doens't exist in the table!! + return 0; + } + } + + Int3(); // should never get here - it means the string doens't exist in the table!! + return 0; +} + +// sub-parse function for individual lines of tstrings.tbl (from Exstr) +// returns : integer with the low bits having the following values : +// 0 on fail, 1 on success, 2 if found a matching id/string pair, 3 if end of language has been found +// for cases 1 and 2 : the high bit (1<<31) will be set if the parser detected the beginning of a new string id on this line +// +int lcl_ext_lookup_sub(char *text, char *out, int id) +{ + char *front; // front of the line + char *p; // current ptr + int len = strlen(text); + int count; + char text_copy[1024]; + char *tok; + int found_new_string_id = 0; + + front = text; + p = text; + count = 0; + while(count < len){ + // do something useful + switch(Ts_current_state){ + // scanning for a line of text + case TS_SCANNING: + // if the first word is #end, we're done with the file altogether + strcpy(text_copy, text); + tok = strtok(text_copy, " \n"); + if((tok != NULL) && !stricmp(tok, "#end")){ + return 3; + } + // if its a commented line, skip it + else if((text[0] == ';') || (text[0] == ' ') || (text[0] == '\n')){ + return 1; + } + // otherwise we should have an ID #, so stuff it and move to the proper state + else { + if(lcl_is_valid_numeric_char(*p)){ + memset(Ts_id_text, 0, PARSE_ID_STRING_LEN); + Ts_id_text_size = 0; + Ts_id_text[Ts_id_text_size++] = *p; + Ts_current_state = TS_ID_STRING; + + found_new_string_id = 1; + } + // error + else { + Int3(); + return 0; + } + } + break; + + // scanning in an id string + case TS_ID_STRING: + // if we have another valid char + if(lcl_is_valid_numeric_char(*p)){ + Ts_id_text[Ts_id_text_size++] = *p; + } + // if we found a comma, our id# is finished, look for the open quote + else if(*p == ','){ + Ts_current_state = TS_OPEN_QUOTE; + } else { + Int3(); + return 0; + } + break; + + case TS_OPEN_QUOTE: + // valid space or an open quote + if((*p == ' ') || (*p == '\"')){ + if(*p == '\"'){ + memset(Ts_text, 0, PARSE_TEXT_STRING_LEN); + Ts_text_size = 0; + Ts_current_state = TS_STRING; + } + } else { + Int3(); + return 0; + } + break; + + case TS_STRING: + // if we have an end quote, we need to look for a comma + if((*p == '\"') /*&& (Ts_text_size > 0)*/ && (Ts_text[Ts_text_size - 1] != '\\')){ + // we're now done - we have a string + Ts_current_state = TS_SCANNING; + + // if the id#'s match, copy the string and return "string found" + if((atoi(Ts_id_text) == id) && (out != NULL)){ + strcpy(out, Ts_text); + + return found_new_string_id ? (1<<1) | (1<<31) : (1<<1); + } + + // otherwise, just continue parsing + return found_new_string_id ? (1<<0) | (1<<31) : (1<<0); + } + // otherwise add to the string + else { + Ts_text[Ts_text_size++] = *p; + } + break; + } + + // if we have a newline, return success, we're done with this line + if(*p == '\n'){ + return found_new_string_id ? (1<<0) | (1<<31) : (1<<0); + } + + // next char in the line + p++; + count++; + } + + // success + return found_new_string_id ? (1<<0) | (1<<31) : (1<<0); +} + +// if the char is a valid char for a signed integer value +int lcl_is_valid_numeric_char(char c) +{ + return ( (c == '-') || (c == '0') || (c == '1') || (c == '2') || (c == '3') || (c == '4') || + (c == '5') || (c == '6') || (c == '7') || (c == '8') || (c == '9') ) ? 1 : 0; +} + +// initialize the pointer array into tstrings.tbl (call from lcl_ext_open() ONLY) +void lcl_ext_setup_pointers() +{ + char language_string[128]; + char line[1024]; + char *tok; + int string_count; + int ret; + int found_start = 0; + + // open the localization file + lcl_ext_open(); + if(Lcl_ext_file == NULL){ + error_display(0, "Error opening externalization file! File likely does not exist or could not be found"); + return; + } + + // seek to the currently active language + memset(language_string, 0, 128); + strcpy(language_string, "#"); + if(!stricmp(DEFAULT_LANGUAGE, Lcl_languages[Lcl_current_lang].lang_name)){ + strcat(language_string, "default"); + } else { + strcat(language_string, Lcl_languages[Lcl_current_lang].lang_name); + } + memset(line, 0, 1024); + + // reset seek variables and begin + Lcl_pointer_count = 0; + while(cfgets(line, 1024, Lcl_ext_file)){ + tok = strtok(line, " \n"); + if(tok == NULL){ + continue; + } + + // if the language matches, we're good to start parsing strings + if(!stricmp(language_string, tok)){ + found_start = 1; + break; + } + } + + // if we didn't find the language specified, error + if(found_start <= 0){ + error_display(0, "Could not find specified langauge in tstrings.tbl!\n"); + lcl_ext_close(); + return; + } + + string_count = 0; + while(cfgets(line, 1024, Lcl_ext_file)){ + ret = lcl_ext_lookup_sub(line, NULL, -1); + + // do stuff + switch(ret & 0x0fffffff){ + // error + case 0 : + lcl_ext_close(); + return; + + // end of language found + case 3 : + // mark one final pointer + Lcl_pointers[Lcl_pointer_count++] = cftell(Lcl_ext_file) - strlen(line) - 1; + lcl_ext_close(); + return; + } + + // the only other case we care about is the beginning of a new id# + if(ret & (1<<31)){ + if((string_count % LCL_GRANULARITY) == 0){ + // mark the pointer down + Lcl_pointers[Lcl_pointer_count++] = cftell(Lcl_ext_file) - strlen(line) - 1; + + // if we're out of pointer slots + if(Lcl_pointer_count >= LCL_MAX_POINTERS){ + error_display(0, "Out of pointer for tstrings.tbl lookup. Please increment LCL_MAX_POINTERS in localize.cpp"); + lcl_ext_close(); + return; + } + } + // increment string count + string_count++; + } + } + + // should never get here. we should always be exiting through case 3 (end of language section) of the above switch + // statement + Int3(); + lcl_ext_close(); +} + +void lcl_get_language_name(char *lang_name) +{ + Assert(LCL_NUM_LANGUAGES == 3); + + strcpy(lang_name, Lcl_languages[Lcl_current_lang].lang_name); +} + +// converts german umlauted chars from ASCII to ANSI +// so they appear in the launcher +// how friggin lame is this +// pass in a null terminated string, foo! +// returns ptr to string you sent in +char* lcl_fix_umlauts(char *str, int which_way) +{ + int i=0; + + if (which_way == LCL_TO_ANSI) { + // moving to ANSI charset + // run thru string and perform appropriate conversions + while (str[i] != '\0') { + switch (str[i]) { + case '\x81': + // lower umlaut u + str[i] = '\xFC'; + break; + case '\x84': + // lower umlaut a + str[i] = '\xE4'; + break; + case '\x94': + // lower umlaut o + str[i] = '\xF6'; + break; + case '\x9A': + // upper umlaut u + str[i] = '\xDC'; + break; + case '\x8E': + // upper umlaut a + str[i] = '\xC4'; + break; + case '\x99': + // upper umlaut o + str[i] = '\xD6'; + break; + case '\xE1': + // beta-lookin thing that means "ss" + str[i] = '\xDF'; + break; + } + + i++; + } + } else { + // moving to ASCII charset + // run thru string and perform appropriate conversions + while (str[i] != '\0') { + switch (str[i]) { + case '\xFC': + // lower umlaut u + str[i] = '\x81'; + break; + case '\xE4': + // lower umlaut a + str[i] = '\x84'; + break; + case '\xF6': + // lower umlaut o + str[i] = '\x94'; + break; + case '\xDC': + // upper umlaut u + str[i] = '\x9A'; + break; + case '\xC4': + // upper umlaut a + str[i] = '\x8E'; + break; + case '\xD6': + // upper umlaut o + str[i] = '\x99'; + break; + case '\xDF': + // beta-lookin thing that means "ss" + str[i] = '\xE1'; + break; + } + + i++; + } + } + + return str; +} + +// ------------------------------------------------------------------ +// lcl_translate_wep_name() +// +// For displaying weapon names in german version +// since we cant actually just change them outright. +// +void lcl_translate_wep_name(char *name) +{ + if (!strcmp(name, "Morning Star")) { + strcpy(name, "Morgenstern"); + } else if (!strcmp(name, "MorningStar")) { + strcpy(name, "Morgenstern D"); + } else if (!strcmp(name, "UD-8 Kayser")) { + strcpy(name, "Kayserstrahl"); + } else if (!strcmp(name, "UD-D Kayser")) { + strcpy(name, "Kayserstrahl"); + } +} + +// ------------------------------------------------------------------ +// lcl_translate_brief_icon_name() +// +// For displaying ship names in german version +// since we cant actually just change them outright. +// +void lcl_translate_brief_icon_name(char *name) +{ + char *pos; + char buf[128]; + + if (!stricmp(name, "Subspace Portal")) { + strcpy(name, "Subraum Portal"); + + } else if (!stricmp(name, "Alpha Wing")) { + strcpy(name, "Alpha"); + + } else if (!stricmp(name, "Beta Wing")) { + strcpy(name, "Beta"); + + } else if (!stricmp(name, "Zeta Wing")) { + strcpy(name, "Zeta"); + + } else if (!stricmp(name, "Capella Node")) { + strcpy(name, "Capella"); + + } else if (!stricmp(name, "Hostile")) { + strcpy(name, "Gegner"); + + } else if (!stricmp(name, "Hostile Craft")) { + strcpy(name, "Gegner"); + + } else if (!stricmp(name, "Rebel Wing")) { + strcpy(name, "Rebellen"); + + } else if (!stricmp(name, "Rebel Fleet")) { + strcpy(name, "Rebellenflotte"); + + } else if (!stricmp(name, "Sentry Gun")) { + strcpy(name, "Gesch\x81tz"); + + } else if (!stricmp(name, "Cargo")) { + strcpy(name, "Fracht"); + + } else if (!stricmp(name, "Knossos Device")) { + strcpy(name, "Knossosger\x84t"); + + } else if (!stricmp(name, "Support")) { + strcpy(name, "Versorger"); + + } else if (!stricmp(name, "Unknown")) { + strcpy(name, "Unbekannt"); + + } else if (!stricmp(name, "Instructor")) { + strcpy(name, "Ausbilder"); + + } else if (!stricmp(name, "Jump Node")) { + strcpy(name, "Sprungknoten"); + + } else if (!stricmp(name, "Escort")) { + strcpy(name, "Geleitschutz"); + + } else if (!stricmp(name, "Asteroid Field")) { + strcpy(name, "Asteroidenfeld"); + + } else if (!stricmp(name, "Enif Station")) { + strcpy(name, "Station Enif"); + + } else if (!stricmp(name, "Rally Point")) { + strcpy(name, "Sammelpunkt"); + + } else if ((pos = strstr(name, "Transport")) != NULL) { + pos += 9; // strlen of "transport" + strcpy(buf, "Transporter"); + strcat(buf, pos); + strcpy(name, buf); + + } else if ((pos = strstr(name, "Jump Node")) != NULL) { + pos += 9; // strlen of "jump node" + strcpy(buf, "Sprungknoten"); + strcat(buf, pos); + strcpy(name, buf); + + } else if (!stricmp(name, "Orion under repair")) { + strcpy(name, "Orion wird repariert"); + + // SOTY-specific ones below! + + } else if (!stricmp(name, "Wayfarer Station")) { + strcpy(name, "Station Wayfarer"); + } else if (!stricmp(name, "Enemy")) { + strcpy(name, "Gegner"); + } else if (!stricmp(name, "Supply Depot")) { + strcpy(name, "Nachschubdepot"); + } else if (!stricmp(name, "Fighter Escort")) { + strcpy(name, "Jagdschutz"); + } else if (!stricmp(name, "Shivans")) { + strcpy(name, "Shivaner"); + } else if (!stricmp(name, "NTF Base of Operations")) { + strcpy(name, "NTF-Operationsbasis"); + } else if (!stricmp(name, "NTF Bombers")) { + strcpy(name, "NTF-Bomber"); + } else if (!stricmp(name, "NTF Fighters")) { + strcpy(name, "NTF-J\x84ger"); + } else if (!stricmp(name, "Sentry")) { + strcpy(name, "Sperrgesch\x81tz"); + } else if (!stricmp(name, "Cargo Containers")) { + strcpy(name, "Frachtbeh\x84lter"); + } else if (!stricmp(name, "NTF Reinforcements")) { + strcpy(name, "NTF-Verst\x84rkungen"); + } else if (!stricmp(name, "NTF Base")) { + strcpy(name, "NTF-St\x81tzpunkt"); + } else if (!stricmp(name, "Refugee Convoy")) { + strcpy(name, "Fl\x81""chtlingskonvoi"); + } else if (!stricmp(name, "Food Convoy")) { + strcpy(name, "Nachschubkonvoi"); + } else if (!stricmp(name, "Governor's Shuttle")) { + strcpy(name, "F\x84hre des Gouverneurs"); + } else if (!stricmp(name, "GTVA Patrol")) { + strcpy(name, "GTVA-Patrouille"); + } else if (!stricmp(name, "Escort fighters")) { + strcpy(name, "Geleitschutz"); + } else if (!stricmp(name, "Nagada Outpost")) { + strcpy(name, "Nagada-Aussenposten"); + } else if (!stricmp(name, "Fighters")) { + strcpy(name, "J\x84ger"); + } else if (!stricmp(name, "Bombers")) { + strcpy(name, "Bomber"); + } else if (!stricmp(name, "Enemy Destroyers")) { + strcpy(name, "Feindliche Zerst\x94rer"); + } else if (!stricmp(name, "Ross 128 System")) { + strcpy(name, "System Ross 128"); + } else if (!stricmp(name, "Knossos Station")) { + strcpy(name, "Knossos-Station"); + } else if (!stricmp(name, "Transporters")) { + strcpy(name, "Transporter"); + } else if (!stricmp(name, "Pirates?")) { + strcpy(name, "Piraten?"); + } else if (!stricmp(name, "Escorts")) { + strcpy(name, "Geleitschutz"); + } else if (!stricmp(name, "Shivan Fighters")) { + strcpy(name, "J\x84ger"); + } else if (!stricmp(name, "Shivan Territory")) { + strcpy(name, "Shivaner"); + } +} + + +// ------------------------------------------------------------------ +// lcl_translate_ship_name() +// +// For displaying ship names in german version in the briefing +// since we cant actually just change them outright. +// +void lcl_translate_ship_name(char *name) +{ + if (!strcmp(name, "GTDR Amazon Advanced")) { + strcpy(name, "GTDR Amazon VII"); + } +} + +// ------------------------------------------------------------------ +// lcl_translate_targetbox_name() +// +// For displaying ship names in german version in the targetbox +// since we cant actually just change them outright. +// +void lcl_translate_targetbox_name(char *name) +{ + char *pos; + char buf[128]; + + if ((pos = strstr(name, "Sentry")) != NULL) { + pos += 6; // strlen of "sentry" + strcpy(buf, "Sperrgesch\x81tz"); + strcat(buf, pos); + strcpy(name, buf); + + } else if ((pos = strstr(name, "Support")) != NULL) { + pos += 7; // strlen of "support" + strcpy(buf, "Versorger"); + strcat(buf, pos); + strcpy(name, buf); + + } else if ((pos = strstr(name, "Unknown")) != NULL) { + pos += 7; // strlen of "unknown" + strcpy(buf, "Unbekannt"); + strcat(buf, pos); + strcpy(name, buf); + + } else if ((pos = strstr(name, "Drone")) != NULL) { + pos += 5; // strlen of "drone" + strcpy(buf, "Drohne"); + strcat(buf, pos); + strcpy(name, buf); + + } else if ((pos = strstr(name, "Jump Node")) != NULL) { + pos += 9; // strlen of "jump node" + strcpy(buf, "Sprungknoten"); + strcat(buf, pos); + strcpy(name, buf); + + } else if (!stricmp(name, "Instructor")) { + strcpy(name, "Ausbilder"); + + } else if (!stricmp(name, "NTF Vessel")) { + strcpy(name, "NTF-Schiff"); + + } else if (!stricmp(name, "Enif Station")) { + strcpy(name, "Station Enif"); + } +} diff --git a/src/math/fix.cpp b/src/math/fix.cpp new file mode 100644 index 0000000..4ac3255 --- /dev/null +++ b/src/math/fix.cpp @@ -0,0 +1,49 @@ +/* + * $Logfile: /Freespace2/code/Math/Fix.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to deal with 16.16 fixed point numbers. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 2 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + + +#ifndef PLAT_UNIX +#include +#endif + +#include "pstypes.h" +#include "fix.h" + +fix fixmul(fix a, fix b) +{ + longlong tmp; + tmp = (longlong)a * (longlong)b; + return (fix)(tmp>>16); +} + +fix fixdiv(fix a, fix b) +{ + return MulDiv(a,65536,b); +} + +fix fixmuldiv(fix a, fix b,fix c) +{ + return MulDiv(a,b,c); +} diff --git a/src/math/floating.cpp b/src/math/floating.cpp new file mode 100644 index 0000000..5cd5796 --- /dev/null +++ b/src/math/floating.cpp @@ -0,0 +1,193 @@ +/* + * $Logfile: /Freespace2/code/Math/Floating.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Low-level floating point math routines + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 13 2/26/98 3:28p John + * Changed all sqrt's to use fl_sqrt. Took out isqrt function + * + * 12 1/30/98 12:25p Mike + * Make frand() not return 1.0, which can cause overflow when indexing + * into arrays. + * + * 11 1/26/98 10:43p Mike + * Make ships not all zoom away from an impending shockwave at the same + * time. Based on ai class and randomness + * + * 10 1/20/98 9:47a Mike + * Suppress optimized compiler warnings. + * Some secondary weapon work. + * + * 9 1/17/98 3:32p Mike + * Add rand_range(), returns random float in min..max. + * + * 8 9/09/97 11:07a Sandeep + * fixed warning level 4 + * + * 7 8/05/97 10:18a Lawrance + * my_rand() being used temporarily instead of rand() + * + * 6 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#include +#include + +#include "pstypes.h" +#include "floating.h" +#include "timer.h" + +#define LOOKUP_BITS 6 +#define EXP_POS 23 +#define EXP_BIAS 127 +typedef float FLOAT; + +#define LOOKUP_POS (EXP_POS-LOOKUP_BITS) +#define SEED_POS (EXP_POS-8) +#define TABLE_SIZE (2<> EXP_POS) & 0xFF ) +#define SET_EXP(a) ((a) << EXP_POS ) +#define GET_EMANT(a) (((a) >> LOOKUP_POS) & LOOKUP_MASK ) +#define SET_MANTSEED(a) (((unsigned long)(a)) << SEED_POS ) + +static unsigned char iSqrt[TABLE_SIZE]; +static int iSqrt_inited = 0; + +int fl_magic = 0x59C00000; //representation of 2^51 + 2^52 +const float *p_fl_magic = (const float *)&fl_magic; + +union _flint { + unsigned long i; + float f; +} fi, fo; + +/* +static void MakeInverseSqrtLookupTable() +{ + long f; + unsigned char *h; + union _flint fi, fo; + + iSqrt_inited = 1; + for ( f=0, h=iSqrt; f < TABLE_SIZE; f++ ) { + fi.i = ((EXP_BIAS-1)<>SEED_POS ) & 0xFF); + } + iSqrt[ TABLE_SIZE / 2 ] = 0xFF; +} +*/ + +// HACK! +float fl_isqrt_c( float x ) +{ +// unsigned long a = ((union _flint *)(&x))->i; +// float arg = x; +// union _flint seed; +// FLOAT r; + + int t1, t2, t3; + t1 = timer_get_microseconds(); + float r1 = 1.0f / (float)sqrt((double)x); + t2 = timer_get_microseconds(); +// float r2 = fl_isqrt_asm(x); + t3 = timer_get_microseconds(); + + return r1; + + +/* if ( !iSqrt_inited ) + MakeInverseSqrtLookupTable(); + + seed.i = SET_EXP(((3*EXP_BIAS-1) - GET_EXP(a)) >> 1 ) | SET_MANTSEED(iSqrt[GET_EMANT(a)]); + r = seed.f; + r = (3.0f - r * r * arg ) * r * 0.5f; + r = (3.0f - r * r * arg ) * r * 0.5f; + return r; +*/ +} + +// rounds off a floating point number to a multiple of some number +float fl_roundoff(float x, int multiple) +{ + float half = (float) multiple / 2.0f; + + if (x < 0) + half = -half; + + x += half; + return (float) (((int) x / multiple) * multiple); +} + + +// Return random value in range 0.0..1.0- (1.0- means the closest number less than 1.0) +float frand() +{ + float rval; + rval = ((float) myrand()) / (RAND_MAX + 1); + return rval; +} + +// Return a floating point number in the range min..max. +float frand_range(float min, float max) +{ + float rval; + + rval = frand(); + rval = rval * (max - min) + min; + + return rval; +} + +// Call this in the frame interval to get TRUE chance times per second. +// If you want it to return TRUE 3 times per second, call it in the frame interval like so: +// rand_chance(flFrametime, 3.0f); +int rand_chance(float frametime, float chance) // default value for chance = 1.0f. +{ + while (--chance > 0.0f) + if (frand() < frametime) + return 1; + + return frand() < (frametime * (chance + 1.0f)); +} + +/*fix fl2f( float x ) +{ + float nf; + nf = x*65536.0f + 8390656.0f; + return ((*((int *)&nf)) & 0x7FFFFF)-2048; +} +*/ + + +/* +>#define S 65536.0 +>#define MAGIC (((S * S * 16) + (S*.5)) * S) +> +>#pragma inline float2int; +> +>ulong float2int( float d ) +>{ +> double dtemp = MAGIC + d; +> return (*(ulong *)&dtemp) - 0x80000000; +>} + +*/ \ No newline at end of file diff --git a/src/math/fvi.cpp b/src/math/fvi.cpp new file mode 100644 index 0000000..d9be87a --- /dev/null +++ b/src/math/fvi.cpp @@ -0,0 +1,1549 @@ +/* + * $Logfile: /Freespace2/code/Math/Fvi.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines to find intersections of various 3d things. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 5 8/16/99 8:19a Andsager + * Add project_point_onto_bbox() to fvi and include in aicode + * + * 4 5/17/99 5:38p Mattf + * Removed bogus assert. + * + * 3 11/13/98 11:10a Andsager + * Add fvi_two_lines_in_3space() - returns closest point of intersection + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 37 3/27/98 2:03p Andsager + * Removed rarely hit warning message. + * + * 36 3/18/98 3:04p Andsager + * Increase collision error warning distance + * + * 35 3/09/98 12:11a Andsager + * Allow spher-model collisions at slightly negative times. Increase + * error tolerance between estimated radius and true radius. + * + * 34 1/20/98 9:47a Mike + * Suppress optimized compiler warnings. + * Some secondary weapon work. + * + * 33 1/19/98 9:30a Andsager + * Changed warning to mprintf. Increased error tolerance. + * + * 32 1/15/98 5:55p Andsager + * increase error tolerance in polyedge_sphereline + * + * 31 1/15/98 10:47a Andsager + * Reduced tolerance in polyedge_sphereline to 0.01m absolute distance. + * Changed asserts to warning + * + * 30 1/12/98 9:20p Andsager + * Modify calling procedure to fvi_sphere_plane. Modify + * fvi_polyedge_sphereline to find the first instance when the edge is + * hit. + * + * 29 1/09/98 10:08a Mike + * Comment out warning for QA rev. + * + * 28 1/05/98 2:59p Andsager + * Relaxed error tolerance in fvi_polyedge_sphereline(). Improved control + * flow. + * + * 27 12/23/97 9:39a Andsager + * Slight optimization and improved error checking in polyedge_sphereline + * + * 26 12/22/97 9:58p Andsager + * Work around numerical precision problem in polyedge_sphereline + * + * 25 12/15/97 5:54p Andsager + * Yet another version of fvi_polyedge_sphereline + * + * 24 12/05/97 5:17p Andsager + * Fixed stupid bug missing some poly:sphere edge collisions. + * + * 23 11/14/97 5:30p Andsager + * Include modified version of polyedge_sphereline + * + * 22 11/05/97 5:36p Andsager + * Fixed bug in fvi_polyedge_sphereline causing incorrect hit points to be + * returned, + * + * 21 11/03/97 11:16p Andsager + * Implemented third (and hopefully last) sphere-edge collision detection + * + * 20 10/19/97 9:42p Andsager + * add fvi_sphere_plane to header + * + * 19 10/19/97 9:27p Andsager + * fixed bug in sphere_plane. Improved control flow and comments in + * polyedge_sphereline + * + * 18 10/17/97 1:21a Andsager + * add new function to check sphere-polgon edge collision + * + * 17 10/11/97 1:42p Mike + * Remove warning message by deleting definition of plane_normal. + * + * 16 10/03/97 5:06p Andsager + * added sphere polygon intersection code + * + * 15 9/28/97 2:16p Andsager + * added fvi_moving_sphere_intersect_plane + * + * 14 8/22/97 11:42a John + * fixed bug in fvi_point_face that was failing near some vertices. + * + * 13 7/21/97 2:39p John + * fixed same thing as previous comment for fvi_ray_sphere. + * + * 12 7/21/97 2:24p John + * fixed bug with fvi_segment_sphere in case where p0 is in sphere and p1 + * is out, but p0 is past center, where the code would fail. + * + * 11 6/23/97 11:11a John + * made fvi_segment_sphere not get 0 length vector in normalize error. + * + * 10 4/01/97 1:03p John + * Changed fvi_ray_plane to take a dir, not two points. + * + * 9 3/27/97 11:36a Mike + * Fix fvi_ray_plane code. Enables using fvi_ray_plane to place objects + * arbitrarily far away from the viewer (in Fred). + * + * 8 3/26/97 10:48a Hoffoss + * JAS: Added fvi_ray_sphere + * + * 7 3/24/97 3:55p John + * renamed fvi functions so rays and segments aren't confusing. + * + * 6 3/24/97 3:26p John + * Cleaned up and restructured model_collide code and fvi code. In fvi + * made code that finds uvs work.. Added bm_get_pixel to BmpMan. + * + * 5 2/17/97 5:18p John + * Added a bunch of RCS headers to a bunch of old files that don't have + * them. + * + * $NoKeywords: $ + */ + +#include // For FLT_MAX + +#include "pstypes.h" +#include "vecmat.h" +#include "floating.h" +#include "fvi.h" + +#define SMALL_NUM 1E-6 +void accurate_square_root( float A, float B, float C, float discriminant, float *root1, float *root2 ); + + +float matrix_determinant_from_vectors(vector *v1,vector *v2,vector *v3) +{ + float ans; + ans=v1->x*v2->y*v3->z; + ans+=v2->x*v3->y*v1->z; + ans+=v3->x*v1->y*v2->z; + ans-=v1->z*v2->y*v3->x; + ans-=v2->z*v3->y*v1->x; + ans-=v3->z*v1->y*v2->x; + + return ans; +} + +// lines: L1 = P1 + V1s and L2 = P2 + V2t +// finds the point on each line of closest approach (s and t) (lines need not intersect to get closest point) +// taken from graphic gems I, p. 304 +// +void fvi_two_lines_in_3space(vector *p1, vector *v1, vector *p2, vector *v2, float *s, float *t) +{ + vector cross,delta; + vm_vec_crossprod(&cross, v1, v2); + vm_vec_sub(&delta, p2, p1); + + float denominator = vm_vec_mag_squared(&cross); + float num_t, num_s; + + if (denominator > 1e-10) { + num_s = matrix_determinant_from_vectors(&delta, v2, &cross); + *s = num_s / denominator; + + num_t = matrix_determinant_from_vectors(&delta, v1, &cross); + *t = num_t / denominator; + } else { + // two lines are parallel + *s = FLT_MAX; + *t = FLT_MAX; + } +} + + +//-------------------------------------------------------------------- +// fvi_ray_plane - Finds the point on the specified plane where the +// infinite ray intersects. +// +// Returns scaled-distance plane is from the ray_origin (t), so +// P = O + t*D, where P is the point of intersection, O is the ray origin, +// and D is the ray's direction. So 0.0 would mean the intersection is +// exactly on the ray origin, 1.0 would be on the ray origin plus the ray +// direction vector, anything negative would be behind the ray's origin. +// If you pass a pointer to the new_pnt, this routine will perform the P= +// calculation to calculate the point of intersection and put the result +// in *new_pnt. +// +// If radius is anything other than 0.0, it assumes you want the intersection +// point to be that radius from the plane. +// +// Note that ray_direction doesn't have to be normalized unless you want +// the return value to be in units from the ray origin. +// +// Also note that new_pnt will always be filled in to some valid value, +// even if it is a point at infinity. +// +// If the plane and line are parallel, this will return the largest +// negative float number possible. +// +// So if you want to check a line segment from p0 to p1, you would pass +// p0 as ray_origin, p1-p0 as the ray_direction, and there would be an +// intersection if the return value is between 0 and 1. + +float fvi_ray_plane(vector *new_pnt, + vector *plane_pnt,vector *plane_norm, // Plane description, a point and a normal + vector *ray_origin,vector *ray_direction, // Ray description, a point and a direction + float rad) +{ + vector w; + float num,den,t; + + vm_vec_sub(&w,ray_origin,plane_pnt); + + den = -vm_vec_dot(plane_norm,ray_direction); + if ( den == 0.0f ) { // Ray & plane are parallel, so there is no intersection + if ( new_pnt ) { + new_pnt->x = -FLT_MAX; + new_pnt->y = -FLT_MAX; + new_pnt->z = -FLT_MAX; + } + return -FLT_MAX; + } + + num = vm_vec_dot(plane_norm,&w); + num -= rad; //move point out by rad + + t = num / den; + + if ( new_pnt ) + vm_vec_scale_add(new_pnt,ray_origin,ray_direction,t); + + return t; +} + + +//find the point on the specified plane where the line intersects +//returns true if point found, false if line parallel to plane +//new_pnt is the found point on the plane +//plane_pnt & plane_norm describe the plane +//p0 & p1 are the ends of the line. +int fvi_segment_plane(vector *new_pnt, + vector *plane_pnt,vector *plane_norm, + vector *p0,vector *p1,float rad) +{ + float t; + vector d; + + vm_vec_sub( &d, p1, p0 ); + + t = fvi_ray_plane(new_pnt, + plane_pnt,plane_norm, // Plane description, a point and a normal + p0,&d, // Ray description, a point and a direction + rad); + + if ( t < 0.0f ) return 0; // intersection lies behind p0 + if ( t > 1.0f ) return 0; // intersection lies past p1 + + return 1; // They intersect! +} + + + + +//maybe this routine should just return the distance and let the caller +//decide it it's close enough to hit +//determine if and where a vector intersects with a sphere +//vector defined by p0,p1 +//returns 1 if intersects, and fills in intp +//else returns 0 +int fvi_segment_sphere(vector *intp,vector *p0,vector *p1,vector *sphere_pos,float sphere_rad) +{ + vector d,dn,w,closest_point; + float mag_d,dist,w_dist,int_dist; + + //this routine could be optimized if it's taking too much time! + + vm_vec_sub(&d,p1,p0); + vm_vec_sub(&w,sphere_pos,p0); + + mag_d = vm_vec_mag(&d); + + if (mag_d <= 0.0f) { + int_dist = vm_vec_mag(&w); + *intp = *p0; + return (int_dist mag_d+sphere_rad) + return 0; //cannot hit + + vm_vec_scale_add(&closest_point,p0,&dn,w_dist); + + dist = vm_vec_dist(&closest_point,sphere_pos); + + if (dist < sphere_rad) { + float dist2,rad2,shorten; + + dist2 = dist*dist; + rad2 = sphere_rad*sphere_rad; + + shorten = fl_sqrt(rad2 - dist2); + + int_dist = w_dist-shorten; + + if (int_dist > mag_d || int_dist < 0.0f) { + //past one or the other end of vector, which means we're inside + + *intp = *p0; //don't move at all + return 1; + } + + vm_vec_scale_add(intp,p0,&dn,int_dist); //calc intersection point + +// { +// fix dd = vm_vec_dist(intp,sphere_pos); +// Assert(dd == sphere_rad); +// mprintf(0,"dd=%x, rad=%x, delta=%x\n",dd,sphere_rad,dd-sphere_rad); +// } + + return 1; + } + else + return 0; +} + + +//determine if and where a ray intersects with a sphere +//vector defined by p0,p1 +//returns 1 if intersects, and fills in intp +//else returns 0 +int fvi_ray_sphere(vector *intp,vector *p0,vector *p1,vector *sphere_pos,float sphere_rad) +{ + vector d,dn,w,closest_point; + float mag_d,dist,w_dist,int_dist; + + //this routine could be optimized if it's taking too much time! + + vm_vec_sub(&d,p1,p0); + vm_vec_sub(&w,sphere_pos,p0); + + mag_d = vm_vec_mag(&d); + + if (mag_d <= 0.0f) { + int_dist = vm_vec_mag(&w); + *intp = *p0; + return (int_dist mag_d+sphere_rad) +// return 0; //cannot hit + + vm_vec_scale_add(&closest_point,p0,&dn,w_dist); + + dist = vm_vec_dist(&closest_point,sphere_pos); + + if (dist < sphere_rad) { + float dist2,rad2,shorten; + + dist2 = dist*dist; + rad2 = sphere_rad*sphere_rad; + + shorten = fl_sqrt(rad2 - dist2); + + int_dist = w_dist-shorten; + +// if (int_dist > mag_d || int_dist < 0.0f) { + if (int_dist < 0.0f) { + //past one or the begining of vector, which means we're inside + + *intp = *p0; //don't move at all + return 1; + } + + vm_vec_scale_add(intp,p0,&dn,int_dist); //calc intersection point + +// { +// fix dd = vm_vec_dist(intp,sphere_pos); +// Assert(dd == sphere_rad); +// mprintf(0,"dd=%x, rad=%x, delta=%x\n",dd,sphere_rad,dd-sphere_rad); +// } + + return 1; + } + else + return 0; +} + + +//============================================================== +// Finds intersection of a ray and an axis-aligned bounding box +// Given a ray with origin at p0, and direction pdir, this function +// returns non-zero if that ray intersects an axis-aligned bounding box +// from min to max. If there was an intersection, then hitpt will contain +// the point where the ray begins inside the box. +// Fast ray-box intersection taken from Graphics Gems I, pages 395,736. +int fvi_ray_boundingbox( vector *min, vector *max, vector * p0, vector *pdir, vector *hitpt ) +{ + float *origin = (float *)&p0->x; + float *dir = (float *)&pdir->x; + float *minB = (float *)min; + float *maxB = (float *)max; + float *coord = (float *)&hitpt->x; + int inside = 1; + int middle[3]; + int i; + int which_plane; + float maxt[3]; + float candidate_plane[3]; + + for (i=0; i<3; i++ ) { + if ( origin[i] < minB[i] ) { + middle[i] = 0; + candidate_plane[i] = minB[i]; + inside = 0; + } else if (origin[i] > maxB[i] ) { + middle[i] = 0; + candidate_plane[i] = maxB[i]; + inside = 0; + } else { + middle[i] = 1; + } + } + + // ray origin inside bounding box + if ( inside ) { + *hitpt = *p0; + return 1; + } + + // calculate T distances to canditate plane + for (i=0; i<3; i++ ) { + if ( (!middle[i]) && (dir[i] != 0.0f )) + maxt[i] = (candidate_plane[i]-origin[i]) / dir[i]; + else + maxt[i] = -1.0f; + } + + // Get largest of the maxt's for final choice of intersection + which_plane = 0; + for (i=1; i<3; i++ ) + if (maxt[which_plane] < maxt[i] ) + which_plane = i; + + // check final candidate actually inside box + if ( maxt[which_plane] < 0.0f ) return 0; + + for (i=0; i<3; i++ ) { + if (which_plane != i ) { + coord[i] = origin[i] + maxt[which_plane]*dir[i]; + if ( coord[i] < minB[i] || coord[i] > maxB[i] ) + return 0; + } else { + coord[i] = candidate_plane[i]; + } + } + return 1; +} + + + +//given largest componant of normal, return i & j +//if largest componant is negative, swap i & j +int ij_table[3][2] = { + {2,1}, //pos x biggest + {0,2}, //pos y biggest + {1,0}, //pos z biggest + }; + +// fvi_point_face +// see if a point in inside a face by projecting into 2d. Also +// Finds uv's if uvls is not NULL. Returns 0 if point isn't on +// face, non-zero otherwise. +// From Graphics Gems I, "An efficient Ray-Polygon intersection", p390 +// checkp - The point to check +// nv - how many verts in the poly +// verts - the vertives for the polygon +// norm1 - the polygon's normal +// u_out,vout - if not null and v_out not null and uvls not_null and point is on face, the uv's of where it hit +// uvls - a list of uv pairs for each vertex +// This replaces the old check_point_to_face & find_hitpoint_uv +// WARNING!! In Gems, they use the code "if (u1==0)" in this function. +// I have found several cases where this will not detect collisions it should. +// I found two solutions: +// 1. Declare the 'beta' variable to be a double. +// 2. Instead of using 'if (u1==0)', compare it to a small value. +// I chose #2 because I would rather have our code work with all floats +// and never need doubles. -JAS Aug22,1997 +#define delta 0.0001f +#define UNINITIALIZED_VALUE -1234567.8f + +int fvi_point_face(vector *checkp, int nv, vector **verts, vector * norm1, float *u_out,float *v_out, uv_pair * uvls ) +{ + float *norm, *P; + vector t; + int i0, i1,i2; + + norm = (float *)norm1; + + //project polygon onto plane by finding largest component of normal + t.x = fl_abs(norm[0]); + t.y = fl_abs(norm[1]); + t.z = fl_abs(norm[2]); + + if (t.x > t.y) if (t.x > t.z) i0=0; else i0=2; + else if (t.y > t.z) i0=1; else i0=2; + + if (norm[i0] > 0.0f) { + i1 = ij_table[i0][0]; + i2 = ij_table[i0][1]; + } + else { + i1 = ij_table[i0][1]; + i2 = ij_table[i0][0]; + } + + // Have i0, i1, i2 + P = (float *)checkp; + vectora **V = (vectora **)verts; + + float u0, u1, u2, v0, v1, v2, alpha = UNINITIALIZED_VALUE, gamma; + float beta; + + int inter=0, i = 2; + + u0 = P[i1] - V[0]->xyz[i1]; + v0 = P[i2] - V[0]->xyz[i2]; + + do { + + u1 = V[i-1]->xyz[i1] - V[0]->xyz[i1]; + u2 = V[i ]->xyz[i1] - V[0]->xyz[i1]; + + v1 = V[i-1]->xyz[i2] - V[0]->xyz[i2]; + v2 = V[i ]->xyz[i2] - V[0]->xyz[i2]; + + + if ( (u1 >-delta) && (u1=0.0f) && (beta<=1.0f)) { + alpha = (v0 - beta*v2)/v1; + inter = ((alpha>=0.0f)&&(alpha+beta<=1.0f)); + } + } else { + + beta = (v0*u1 - u0*v1) / (v2*u1 - u2*v1); + if ((beta >=0.0f) && (beta<=1.0f)) { + Assert(beta != UNINITIALIZED_VALUE); + alpha = (u0 - beta*u2)/u1; + inter = ((alpha>=0.0f)&&(alpha+beta<=1.0f)); + } + + + } + +/* + // This is test code that I used to detect failures. See the + // comments above this function for details. + double betad; + float alphad; + int interd=0; + + betad = (v0*u1 - u0*v1) / (v2*u1 - u2*v1); + if ((betad >=0.0f) && (betad<=1.0f)) { + alphad = (u0 - betad*u2)/u1; + interd = ((alphad>=0.0f)&&(alphad+betad<=1.0f)); + } + + if ( interd != inter ) { + mprintf(( "u0=%.4f, u1=%.16f, u2=%.4f\n", u0, u1, u2 )); + mprintf(( "v0=%.4f, v1=%.16f, v2=%.4f\n", v0, v1, v2 )); + } +*/ + + } while ((!inter) && (++i < nv) ); + + if ( inter && uvls && u_out && v_out ) { + // Assert(alpha != 1.0f); + gamma = 1.0f - (alpha+beta); + *u_out = gamma * uvls[0].u + alpha*uvls[i-1].u + beta*uvls[i].u; + *v_out = gamma * uvls[0].v + alpha*uvls[i-1].v + beta*uvls[i].v; + } + + return inter; +} + +// **************************************************************************** +// +// SPHERE FACE INTERSECTION CODE +// +// **************************************************************************** + +int check_sphere_point( vector *point, vector *sphere_start, vector *sphere_vel, float radius, float *collide_time ); + +// ---------------------------------------------------------------------------- +// fvi_sphere_plane() +// returns whether a sphere hits a given plane in the time [0,1] +// if two collisions occur, returns earliest legal time +// returns the intersection point on the plane +// +// inputs: intersect_point => position on plane where sphere makes first contact [if hit_time in range 0-1] +// sphere_center_start => initial sphere center +// sphere_velocity => initial sphere velocity +// sphere_radius => radius of sphere +// plane_normal => normal to the colliding plane +// plane_point => point in the colliding plane +// hit_time => time surface of sphere first hits plane +// delta_t => time for sphere to cross plane (first to last contact) +// +// return: 1 if sphere may be in contact with plane in time range [0-1], 0 otherwise +// + +int fvi_sphere_plane(vector *intersect_point, vector *sphere_center_start, vector *sphere_velocity, float sphere_radius, + vector *plane_normal, vector *plane_point, float *hit_time, float *crossing_time) +{ + float D, xs0_dot_norm, vs_dot_norm; + float t1, t2; + + // find the time and position of the ray-plane intersection + D = -vm_vec_dotprod( plane_normal, plane_point ); + xs0_dot_norm = vm_vec_dotprod( plane_normal, sphere_center_start ); + vs_dot_norm = vm_vec_dotprod( plane_normal, sphere_velocity); + + // protect against divide by zero + if (fl_abs(vs_dot_norm) > 1e-30) { + t1 = (-D - xs0_dot_norm + sphere_radius) / vs_dot_norm; + t2 = (-D - xs0_dot_norm - sphere_radius) / vs_dot_norm; + } else { + return 0; + } + + // sort t1 < t2 + if (t2 < t1) { + float temp = t1; + t1 = t2; + t2 = temp; + } + + *hit_time = t1; + + // find hit pos if t1 in range 0-1 + if (t1 > 0 && t1 < 1) { + vector v_temp; + vm_vec_scale_add( &v_temp, sphere_center_start, sphere_velocity, t1 ); + vm_project_point_onto_plane( intersect_point, &v_temp, plane_normal, plane_point ); + } + + // get time to cross + *crossing_time = t2 - t1; + + return ( (t1 < 1) && (t2 > 0) ); +} + +// ---------------------------------------------------------------------------- +// fvi_sphere_perp_edge() +// returns whether a sphere hits and edge for the case the edge is perpendicular to sphere_velocity +// if two collisions occur, returns the earliest legal time +// returns the intersection point on the edge +// +// inputs: intersect_point => position on plane where sphere makes first contact [RESULT] +// sphere_center_start => initial sphere center +// sphere_velocity => initial sphere velocity +// sphere_radius => radius of sphere +// edge_point1 => first edge point +// edge_point2 => second edge point +// max_time => maximum legal time at which collision can occur +// collide_time => actual time of the collision +// +int fvi_sphere_perp_edge(vector *intersect_point, vector *sphere_center_start, vector *sphere_velocity, + float sphere_radius, vector *edge_point1, vector *edge_point2, float *collide_time) +{ + // find the intersection in the plane normal to sphere velocity and edge velocity + // choose a plane point V0 (first vertex of the edge) + // project vectors and points into the plane + // find the projection of the intersection and see if it lies on the edge + + vector edge_velocity; + vector V0, V1; + vector Xe_proj, Xs_proj; + + V0 = *edge_point1; + V1 = *edge_point2; + vm_vec_sub(&edge_velocity, &V1, &V0); + + // define a set of local unit vectors + vector x_hat, y_hat, z_hat; + float max_edge_parameter; + + vm_vec_copy_normalize( &x_hat, &edge_velocity ); + vm_vec_copy_normalize( &y_hat, sphere_velocity ); + vm_vec_crossprod( &z_hat, &x_hat, &y_hat ); + max_edge_parameter = vm_vec_mag( &edge_velocity ); + + vector temp; + // next two temp should be same as starting velocities + vm_vec_projection_onto_plane(&temp, sphere_velocity, &z_hat); + Assert ( !vm_vec_cmp(&temp, sphere_velocity) ); + vm_vec_projection_onto_plane(&temp, &edge_velocity, &z_hat); + Assert ( !vm_vec_cmp(&temp, &edge_velocity) ); + + // should return V0 + vm_project_point_onto_plane(&Xe_proj, &V0, &z_hat, &V0); + Assert ( !vm_vec_cmp(&Xe_proj, &V0) ); + + vm_project_point_onto_plane(&Xs_proj, sphere_center_start, &z_hat, &V0); + + vector plane_coord; + plane_coord.x = vm_vec_dotprod(&Xs_proj, &x_hat); + plane_coord.y = vm_vec_dotprod(&Xe_proj, &y_hat); + plane_coord.z = vm_vec_dotprod(&Xe_proj, &z_hat); + + // determime the position on the edge line + vm_vec_copy_scale( intersect_point, &x_hat, plane_coord.x ); + vm_vec_scale_add2( intersect_point, &y_hat, plane_coord.y ); + vm_vec_scale_add2( intersect_point, &z_hat, plane_coord.z ); + + // check if point is actually on edge + float edge_parameter; + vector temp_vec; + + vm_vec_sub( &temp_vec, intersect_point, &V0 ); + edge_parameter = vm_vec_dotprod( &temp_vec, &x_hat ); + + if ( edge_parameter < 0 || edge_parameter > max_edge_parameter ) { + return 0; + } + + return ( check_sphere_point(intersect_point, sphere_center_start, sphere_velocity, sphere_radius, collide_time) ); +} + + +// ---------------------------------------------------------------------------- +// check_sphere_point() +// determines whether and where a moving sphere hits a point +// +// inputs: point => point sphere collides with +// sphere_start => initial sphere center +// sphere_vel => velocity of sphere +// radius => radius of sphere +// collide_time => time of first collision with t >= 0 +// +int check_sphere_point( vector *point, vector *sphere_start, vector *sphere_vel, float radius, float *collide_time ) +{ + vector delta_x; + float delta_x_sqr, vs_sqr, delta_x_dot_vs; + + vm_vec_sub( &delta_x, sphere_start, point ); + delta_x_sqr = vm_vec_mag_squared( &delta_x ); + vs_sqr = vm_vec_mag_squared( sphere_vel ); + delta_x_dot_vs = vm_vec_dotprod( &delta_x, sphere_vel ); + +// a = vs_sqr; +// b = 2.0f*delta_x_dot_vs; +// c = delta_x_sqr - radius*radius; + + float discriminant = delta_x_dot_vs*delta_x_dot_vs - vs_sqr*(delta_x_sqr - radius*radius); + if (discriminant < 0) { + return 0; + } + + float radical, time1, time2; + radical = fl_sqrt(discriminant); + time1 = (-delta_x_dot_vs + radical) / vs_sqr; + time2 = (-delta_x_dot_vs - radical) / vs_sqr; + + if (time1 > time2) { + float temp = time1; + time1 = time2; + time2 = temp; + } + + if (time1 >= 0 && time1 <= 1.0) { + *collide_time = time1; + return 1; + } + + if (time2 >= 0 && time2 <= 1.0) { + *collide_time = time2; + return 1; + } + + return 0; +} + + + +// ---------------------------------------------------------------------------- +// +// fvi_polyedge_sphereline() +// +// Given a polygon vertex list and a moving sphere, find the first contact the sphere makes with the edge, if any +// +// Inputs: hit_point => point on edge +// xs0 => starting point for sphere +// vs => sphere velocity +// Rs => sphere radius +// nv => number of vertices +// verts => vertices making up polygon edges +// hit_time => time the sphere hits an edge +// +// Return: 1 if sphere hits polyedge, 0 if sphere misses +/* +#define TOL 1E-3 + +int fvi_polyedge_sphereline(vector *hit_point, vector *xs0, vector *vs, float Rs, int nv, vector **verts, float *hit_time) +{ + int i; + vector v0, v1; + vector ve; // edge velocity + float best_sphere_time; // earliest time sphere hits edge + vector delta_x; + float delta_x_dot_ve, delta_x_dot_vs, ve_dot_vs, ve_sqr, vs_sqr; + float denominator; + float time_el, time_sl; // times for edge_line and sphere_line at closest approach + vector temp_edge_hit, temp_sphere_hit; + vector best_edge_hit; // edge position for earliest edge hit + + best_sphere_time = FLT_MAX; + + for (i=0; i Rs*Rs) { + continue; + } + + // Starting from the positions of closest approach, back up the sphere until it is just touching the edge_line. + // Check this edge_line point against the range of the edge. If not in range, check if the sphere hits the + // extreme of the edge. + + // time_e1 - the edge_line position of closest approach + // time_e - the edge position where sphere makes first contact + float dist_e_sqr, dist_s_sqr, delta_te, delta_ts, cos_sqr; + float time_s; + float time_em, time_ep, time_sm, time_sp; + float te_per_ts; + cos_sqr = (ve_dot_vs*ve_dot_vs) / (ve_sqr*vs_sqr); + dist_s_sqr = (Rs*Rs - d0_sqr) / (1.0f - cos_sqr); + delta_ts = fl_sqrt(dist_s_sqr / vs_sqr); + time_sm = time_sl - delta_ts; + time_sp = time_sl + delta_ts; + + if (time_sm > 1 || time_sp < 0) { + continue; + } + + dist_e_sqr = (Rs*Rs - d0_sqr) * cos_sqr / (1.0f - cos_sqr); + delta_te = fl_sqrt(dist_e_sqr / ve_sqr); + time_em = time_el - delta_te; + time_ep = time_el + delta_te; + + if (cos_sqr > 0.5f) { + if (time_em > 1 || time_ep < 0) { + continue; + } + } else { + delta_te = fl_sqrt( (Rs*Rs - d0_sqr) / ve_sqr ); + if ((time_el - delta_te) > 1 || (time_el + delta_te) < 0) { + continue; + } + } + + // Check if we already have a hit + // Move sphere back to time_sm. If ve_dot_vs > 0 edge to time_em, < 0 time_ep. + + if (time_sm >= 0 && time_sm <= 1) { + if (ve_dot_vs > 0) { + if (time_em >= 0 && time_em <= 1) { + vm_vec_scale_add( &temp_edge_hit, &v0, &ve, time_em ); + time_s = time_sm; + goto Hit; + } + } else { + if (time_ep >= 0 && time_ep <= 1) { + vm_vec_scale_add( &temp_edge_hit, &v0, &ve, time_ep ); + time_s = time_sm; + goto Hit; + } + } + } + + // Find the ratio of times between sphere_line and edge_line. + te_per_ts = fl_sqrt( vs_sqr / ve_sqr ); + + // Check the location of the closest point on the edge line corresponding to the first valid point on sphere_line. + // First valid sphere_line point is the greater of (1) t = 0 or (2) t = time_sm. + // If the corresponding edge interval is left, we check against the rightmost edgepoint. + // If the corresponding edge interval is right, we check against the leftmost edgepoint. + // If the corresponding edge interval contains this point, then we are already penetrating. + float first_valid_sphere_time; + float closest_edge_time; + + // Find first valid sphere time + if (time_sm < 0) { + first_valid_sphere_time = 0.0f; + } else { + first_valid_sphere_time = time_sm; + Assert( time_sm <= 1.0f ); + } + + if (ve_dot_vs > 0) { + + // Find corresponding edge time + closest_edge_time = time_el - te_per_ts * (time_sl - first_valid_sphere_time) * fl_sqrt(cos_sqr); + if (time_em < 0) { + time_em = 0.0f; + } + if (time_ep > 1) { + time_ep = 1.0f; + } + + if (time_em > closest_edge_time - SMALL_NUM) { + // edge interval is right so test against left edge + vm_vec_scale_add( &temp_edge_hit, &v0, &ve, time_em ); + if ( !check_sphere_point( &temp_edge_hit, xs0, vs, Rs, &time_s ) ) { + continue; + } + + } else if (time_ep < closest_edge_time + SMALL_NUM) { + // edge interval is left so test against right edge + vm_vec_scale_add( &temp_edge_hit, &v0, &ve, time_ep ); + if ( !check_sphere_point( &temp_edge_hit, xs0, vs, Rs, &time_s ) ) { + continue; + } + + } else { + // edge interval contains point, so already penetrating + continue; + } + } else { + // Sphere and edge have opposite velocities + + // Find corresponding edge time + closest_edge_time = time_el + te_per_ts * (time_sl - first_valid_sphere_time) * fl_sqrt(cos_sqr); + if (time_em < 0) { + time_em = 0.0f; + } + if (time_ep > 1) { + time_ep = 1.0f; + } + + if (closest_edge_time > time_ep - SMALL_NUM) { + // edge interval is right so test against left edge + vm_vec_scale_add( &temp_edge_hit, &v0, &ve, time_ep ); + if ( !check_sphere_point( &temp_edge_hit, xs0, vs, Rs, &time_s ) ) { + continue; + } + + } else if (closest_edge_time < time_em + SMALL_NUM) { + // edge interval is left so test against right edge + vm_vec_scale_add( &temp_edge_hit, &v0, &ve, time_em ); + if ( !check_sphere_point( &temp_edge_hit, xs0, vs, Rs, &time_s ) ) { + continue; + } + + } else { + // edge interval contains point, so already penetrating + continue; + } + } + +Hit: +// vector temp; +// vm_vec_scale_add( &temp, xs0, vs, time_s); +// float q = vm_vec_dist( &temp, &temp_edge_hit ); +// if (q > Rs + .003 || q < Rs - .003) { +// Int3(); +// } + if (time_s < best_sphere_time) { + best_sphere_time = time_s; + best_edge_hit = temp_edge_hit; + } + } // end edge loop + + if (best_sphere_time <= 1.0f) { + *hit_time = best_sphere_time; + *hit_point = best_edge_hit; + return 1; + } else { + return 0; + } +} +*/ +// ---------------------------------------------------------------------------- +// +// fvi_polyedge_sphereline() +// +// Given a polygon vertex list and a moving sphere, find the first contact the sphere makes with the edge, if any +// +// Inputs: hit_point => point on edge +// xs0 => starting point for sphere +// vs => sphere velocity +// Rs => sphere radius +// nv => number of vertices +// verts => vertices making up polygon edges +// hit_time => time the sphere hits an edge +// +// Return: 1 if sphere hits polyedge, 0 if sphere misses + +#define WARN_DIST 1.0 + +int fvi_polyedge_sphereline(vector *hit_point, vector *xs0, vector *vs, float Rs, int nv, vector **verts, float *hit_time) +{ + int i; + vector v0, v1; + vector ve; // edge velocity + float best_sphere_time; // earliest time sphere hits edge + vector delta_x; + float delta_x_dot_ve, delta_x_dot_vs, ve_dot_vs, ve_sqr, vs_sqr, delta_x_sqr; + vector temp_edge_hit, temp_sphere_hit; + + best_sphere_time = FLT_MAX; + + for (i=0; i 0) { + root = fl_sqrt(discriminant); + root1 = (float) ((-B + root)/(2*A)); + root2 = (float) ((-B - root)/(2*A)); + + + // sort root1 and root2 + if (root2 < root1) { + temp = root1; + root1 = root2; + root2 = temp; + } + + if (root1 >= -0.05f && root1 < 0.0f) { + root1 = 0.000001f; + } + + // look only at first hit + if ( (root1 >= 0) && (root1 <= 1) ) { + t_sphere_hit = root1; + } else { + goto TryVertex; + } + } else { + // discriminant negative, so no hit possible + continue; + } + + // check if best time with this edge is less than best so far + if (t_sphere_hit >= best_sphere_time) + continue; + + vm_vec_scale_add( &temp_sphere_hit, xs0, vs, t_sphere_hit ); + float q; + // solve for edge time + A = ve_sqr * (ve_dot_vs*ve_dot_vs - ve_sqr*vs_sqr); + B = 2*ve_sqr * (delta_x_dot_ve*vs_sqr - delta_x_dot_vs*ve_dot_vs); + C = 2*delta_x_dot_ve*delta_x_dot_vs*ve_dot_vs + Rs*Rs*ve_dot_vs*ve_dot_vs + - delta_x_sqr*ve_dot_vs*ve_dot_vs - delta_x_dot_ve*delta_x_dot_ve*vs_sqr; + + discriminant = B*B - 4*A*C; + + // guard against nearly perpendicular sphere edge velocities + if ( (discriminant < 0) ) { + discriminant = 0; + } + + root = fl_sqrt(discriminant); + root1 = (float) ((-B + root)/(2*A)); + root2 = (float) ((-B - root)/(2*A)); + + // given sphere position, find which edge time (position) allows a valid solution + if ( (root1 >= 0) && (root1 <= 1) ) { + // try edge root1 + vm_vec_scale_add( &temp_edge_hit, &v0, &ve, root1 ); + q = vm_vec_dist_squared(&temp_edge_hit, &temp_sphere_hit); + if ( fl_abs(q - Rs*Rs) < 0.2*Rs ) { // error less than 0.1m absolute (2*delta*Radius) + goto Hit; + } + } + + if ( (root2 >= 0) && (root2 <= 1) ) { + // try edge root2 + vm_vec_scale_add( &temp_edge_hit, &v0, &ve, root2 ); + q = vm_vec_dist_squared(&temp_edge_hit, &temp_sphere_hit); + if ( fl_abs(q - Rs*Rs) < 0.2*Rs ) { // error less than 0.1m absolute + goto Hit; + } + } else { + // both root1 and root2 out of range so we have to check vertices + goto TryVertex; + } + + // Misses EDGE, so try ENDPOINTS + // Not exactly sure about this part (ie, which endpoint to check) + + // CHECK V0, we don't need to recalculate delta_x + // CHECK V1, we *need* to recalculate delat_x + + // check end points + +TryVertex: + // try V0 + A = vs_sqr; + B = 2*delta_x_dot_vs; + C = delta_x_sqr - Rs*Rs; + int v0_hit; + float sphere_v0, sphere_v1; + + sphere_v0 = UNINITIALIZED_VALUE; + sphere_v1 = UNINITIALIZED_VALUE; + + v0_hit = 0; + discriminant = B*B - 4*A*C; + if (discriminant > 0) { + root = fl_sqrt(discriminant); + root1 = (float) ((-B + root)/(2*A)); + root2 = (float) ((-B - root)/(2*A)); + + if (root1 > root2) { + temp = root1; + root1 = root2; + root2 = temp; + } + + // look only at the fist hit (ignore negative first hit) + if ( (root1 > 0) && (root1 < 1) ) { + v0_hit = 1; + sphere_v0 = root1; + vm_vec_scale_add( &temp_sphere_hit, xs0, vs, root1 ); + // q = vm_vec_dist_squared( &v0, &temp_sphere_hit ); // debug + // if ( fl_abs(q - Rs*Rs) > 2*WARN_DIST*Rs ) + // mprintf(("Estimated radius error: Estimate %f, actual %f Get Dave A.\n", fl_sqrt(q), Rs)); + } + } + + // try V1 + vm_vec_sub( &delta_x, xs0, &v1 ); + delta_x_sqr = vm_vec_mag_squared( &delta_x ); + delta_x_dot_vs = vm_vec_dotprod( &delta_x, vs ); + int v1_hit; + + B = 2*delta_x_dot_vs; + C = delta_x_sqr - Rs*Rs; + + v1_hit = 0; + discriminant = B*B - 4*A*C; + if (discriminant > 0) { + root = fl_sqrt(discriminant); + root1 = (float) ((-B + root)/(2*A)); + root2 = (float) ((-B - root)/(2*A)); + + if (root1 > root2) { + temp = root1; + root1 = root2; + root2 = temp; + } + + // look only at the first hit (ignore negative first hit) + if ( (root1 > 0) && (root1 < 1) ) { + v1_hit = 1; + sphere_v1 = root1; + vm_vec_scale_add( &temp_sphere_hit, xs0, vs, root1 ); + // q = vm_vec_dist_squared( &v1, &temp_sphere_hit ); + // if ( fl_abs(q - Rs*Rs) > 2*WARN_DIST*Rs ) + // mprintf(("Estimated radius error: Estimate %f, actual %f Get Dave A.\n", fl_sqrt(q), Rs)); + } + } + + // set hitpoint to closest vetex hit, if any + if ( v0_hit ) { + Assert(sphere_v0 != UNINITIALIZED_VALUE); + t_sphere_hit = sphere_v0; + temp_edge_hit = v0; + + if (v1_hit) { + Assert(sphere_v1 != UNINITIALIZED_VALUE); + if (sphere_v1 < sphere_v0) { + t_sphere_hit = sphere_v1; + temp_edge_hit = v1; + } + } + } else if ( v1_hit ) { + Assert(sphere_v1 != UNINITIALIZED_VALUE); + t_sphere_hit = sphere_v1; + temp_edge_hit = v1; + } else { + continue; + } + + vm_vec_scale_add( &temp_sphere_hit, xs0, vs, t_sphere_hit ); + q = vm_vec_dist_squared(&temp_edge_hit, &temp_sphere_hit); + // if ( fl_abs(q - Rs*Rs) > 2*WARN_DIST*Rs ) { + // mprintf(("Estimated radius error: Estimate %f, actual %f Get Dave A.\n", fl_sqrt(q), Rs)); + // } + +Hit: +// vector temp; +// vm_vec_scale_add( &temp, xs0, vs, time_s); +// float q = vm_vec_dist( &temp, &temp_edge_hit ); +// if (q > Rs + .003 || q < Rs - .003) { +// Int3(); +// } + if (t_sphere_hit < best_sphere_time) { + best_sphere_time = t_sphere_hit; + *hit_point = temp_edge_hit; + } + } // end edge loop + + if (best_sphere_time <= 1.0f) { + *hit_time = best_sphere_time; + return 1; + } else { + return 0; + } +} + + +// ---------------------------------------------------------------------------- +// +// fvi_closest_point_on_line_segment() +// +// Finds the closest point on a line to a given fixed point +// +// Inputs: closest_point => the closest point on the line +// fixed_point => the fixed point +// line_point1 => first point on the line +// line_point2 => second point on the line +// +void fvi_closest_point_on_line_segment(vector *closest_point, vector *fixed_point, vector *line_point1, vector *line_point2) +{ + vector delta_x, line_velocity; + float t; + + vm_vec_sub(&line_velocity, line_point2, line_point1); + vm_vec_sub(&delta_x, line_point1, fixed_point); + t = -vm_vec_dotprod(&delta_x, &line_velocity) / vm_vec_mag_squared(&line_velocity); + + // Constrain t to be in range [0,1] + if (t < 0) { + t = 0.0f; + } else if (t > 1) { + t = 1.0f; + } + + vm_vec_scale_add(closest_point, line_point1, &line_velocity, t); +} + + +// ---------------------------------------------------------------------------- +// +// fvi_check_sphere_sphere() +// +// checks whether two spheres hit given initial and starting positions and radii +// does not check whether sphere are already touching. +// +// Inputs x_p0 => polymodel sphere, start point +// x_p1 => polymodel sphere, end point +// x_s0 => other sphere, start point +// x_s1 => other sphere, end point +// radius_p => radius of polymodel sphere +// radius_s => radius of other sphere +// +// returns 1 if spheres overlap, 0 otherwise +// +int fvi_check_sphere_sphere(vector *x_p0, vector *x_p1, vector *x_s0, vector *x_s1, float radius_p, float radius_s, float *t1, float *t2) +{ + vector delta_x, delta_v; + float discriminant, separation, delta_x_dot_delta_v, delta_v_sqr, delta_x_sqr; + float time1, time2; + + // Check that there are either 0 or 2 pointers to time + Assert( (!(t1) && !(t2)) || (t1 && t2) ); + + vm_vec_sub(&delta_x, x_s0, x_p0); + delta_x_sqr = vm_vec_mag_squared(&delta_x); + separation = radius_p + radius_s; + + // Check if already touching + if (delta_x_sqr < separation*separation) { + if ( !t1 ) { + return 1; + } + } + + // Find delta_v (in polymodel sphere frame of ref) + // Note: x_p0 and x_p1 will be same for fixed polymodel + vm_vec_sub(&delta_v, x_s1, x_s0); + vm_vec_add2(&delta_v, x_p0); + vm_vec_sub2(&delta_v, x_p1); + + delta_x_dot_delta_v = vm_vec_dotprod(&delta_x, &delta_v); + delta_v_sqr = vm_vec_mag_squared(&delta_v); + + discriminant = delta_x_dot_delta_v*delta_x_dot_delta_v - delta_v_sqr*(delta_x_sqr - separation*separation); + + if (discriminant < 0) { + return 0; + } + + float radical = fl_sqrt(discriminant); + + time1 = (-delta_x_dot_delta_v + radical) / delta_v_sqr; + time2 = (-delta_x_dot_delta_v - radical) / delta_v_sqr; + + // sort t1 < t2 + float temp; + if (time1 > time2) { + temp = time1; + time1 = time2; + time2 = temp; + } + + if ( t1 ) { + *t1 = time1; + *t2 = time2; + } + + // check whether the range from t1 to t2 intersects [0,1] + if (time1 > 1 || time2 < 0) { + return 0; + } else { + return 1; + } +} + +// ---------------------------------------------------------------------------- +// +// fvi_cull_polyface_sphere() +// +// Culls polyfaces which moving sphere can not intersect +// +// Inputs: poly_center => center of polygon face to test +// poly_r => radius of polygon face in question +// sphere_start => start point of moving sphere +// sphere_end => end point of moving sphere +// sphere_r => radius of moving sphere +// +// Output: returns 0 if no collision is possible, 1 if collision may be possible +// +// Polygon face is characterized by a center and a radius. This routine checks whether it is +// *impossible* for a moving sphere to intersect a fixed polygon face. +int fvi_cull_polyface_sphere(vector *poly_center, float poly_r, vector *sphere_start, vector *sphere_end, float sphere_r) +{ + vector closest_point, closest_separation; + float max_sep; + + fvi_closest_point_on_line_segment(&closest_point, poly_center, sphere_start, sphere_end); + vm_vec_sub(&closest_separation, &closest_point, poly_center); + + max_sep = vm_vec_mag(&closest_separation) + poly_r; + + if ( max_sep > sphere_r ) { + return 0; + } else { + return 1; + } +} + +// --------------------------------------------------------------------------------------------------------------------- +// fvi_closest_line_line +// finds the closest points between two lines +// +void fvi_closest_line_line( vector *x0, vector *vx, vector *y0, vector *vy, float *x_time, float *y_time ) +{ + vector vx_cross_vy, delta_l, delta_l_cross_vx, delta_l_cross_vy; + float denominator; + + vm_vec_sub(&delta_l, y0, x0); + + vm_vec_crossprod(&vx_cross_vy, vx, vy); + vm_vec_crossprod(&delta_l_cross_vx, &delta_l, vx); + vm_vec_crossprod(&delta_l_cross_vy, &delta_l, vy); + + denominator = vm_vec_mag_squared(&vx_cross_vy); + + *x_time = vm_vec_dotprod(&delta_l_cross_vy, &vx_cross_vy) / denominator; + *y_time = vm_vec_dotprod(&delta_l_cross_vx, &vx_cross_vy) / denominator; + +// vector x_result, y_result; +// vm_vec_scale_add(&x_result, x0, vx, *x_time); +// vm_vec_scale_add(&y_result, y0, vy, *y_time); +// *dist_sqr = vm_vec_dist_squared(&x_result, &y_result); + +} + +// -------------------------------------------------------------------------------------------------------------------- +void accurate_square_root( float A, float B, float C, float discriminant, float *root1, float *root2 ) +{ + float root = fl_sqrt(discriminant); + + if (B > 0) { + *root1 = 2.0f*C / (-B - root); + *root2 = (-B - root) / (2.0f*A); + } else { // B < 0 + *root1 = (-B + root) / (2.0f*A); + *root2 = 2.0f*C / (-B + root); + } +} + +// vector mins - minimum extents of bbox +// vector maxs - maximum extents of bbox +// vector start - point in bbox reference frame +// vector box_pt - point in bbox reference frame. +// NOTE: if a coordinate of start is *inside* the bbox, it is *not* moved to surface of bbox +// return: 1 if inside, 0 otherwise. +int project_point_onto_bbox(vector *mins, vector *maxs, vector *start, vector *box_pt) +{ + int inside = TRUE; + + if (start->x > maxs->x) { + box_pt->x = maxs->x; + inside = FALSE; + } else if (start->x < mins->x) { + box_pt->x = mins->x; + inside = FALSE; + } else { + box_pt->x = start->x; + } + + if (start->y > maxs->y) { + box_pt->y = maxs->y; + inside = FALSE; + } else if (start->y < mins->y) { + box_pt->y = mins->y; + inside = FALSE; + } else { + box_pt->y = start->y; + } + + if (start->z > maxs->z) { + box_pt->z = maxs->z; + inside = FALSE; + } else if (start->z < mins->z) { + box_pt->z = mins->z; + inside = FALSE; + } else { + box_pt->z = start->z; + } + + return inside; +} diff --git a/src/math/spline.cpp b/src/math/spline.cpp new file mode 100644 index 0000000..5dc6ab9 --- /dev/null +++ b/src/math/spline.cpp @@ -0,0 +1,310 @@ +/* + * $Logfile: /Freespace2/code/Math/spline.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 7/08/99 10:53a Dave + * New multiplayer interpolation scheme. Not 100% done yet, but still + * better than the old way. + * + * 2 7/06/99 4:24p Dave + * Mid-level checkin. Starting on some potentially cool multiplayer + * smoothness crap. + * + * + * $NoKeywords: $ + */ + +#include "vecmat.h" +#include "2d.h" +#include "3d.h" +#include "alphacolors.h" +#include "spline.h" + +// ------------------------------------------------------------------------------------------------- +// SPLINE DEFINES/VARS +// + + +// ------------------------------------------------------------------------------------------------- +// SPLINE FUNCTIONS +// + +// integer factorial. =o +int bez_fact(int n) +{ + int idx; + int product = 1; + + // do eet + for(idx=1; idx<=n; idx++){ + product *= idx; + } + + return product; +} + +// bez constructor +bez_spline::bez_spline() +{ + int idx; + + // zero all points + for(idx=0; idxx = 0.0f; + out->y = 0.0f; + out->z = 0.0f; + for(idx=0; idxx += pts[idx].x * bez_val; + + // y component + out->y += pts[idx].y * bez_val; + + // z component + out->z += pts[idx].z * bez_val; + } +} + +// render a bezier +void bez_spline::bez_render(int divs, color *c) +{ + float inc; + int idx; + vertex a, b; + vector pt; + + // bleh + if(divs <= 0){ + return; + } + inc = 1.0f / (float)divs; + + // draw in red + gr_set_color_fast(c); + + // draw that many divisions + bez_get_point(&pt, 0.0f); + g3_rotate_vertex(&a, &pt); + for(idx=1; idx<=divs; idx++){ + // second point + bez_get_point(&pt, (float)idx * inc); + g3_rotate_vertex(&b, &pt); + + // draw the line + g3_draw_line(&a, &b); + + // store b + a = b; + } + + // draw the control points + gr_set_color_fast(&Color_bright_green); + for(idx=0; idx> SEMIRAND_MAX_LOG) & (SEMIRAND_MAX - 1); + c = (num >> (2 * SEMIRAND_MAX_LOG)) & (SEMIRAND_MAX - 1); + + return Semirand[a] ^ Semirand[b] ^ Semirand[c]; +} + +// Return a random value in 0.0f .. 1.0f- (ie, it will never return 1.0f). +float static_randf(int num) +{ + int a; + + a = static_rand(num); + + return (a & 0xffff) / 65536.0f; +} + +float static_randf_range(int num, float min, float max) +{ + float rval; + + rval = static_randf(num); + rval = rval * (max - min) + min; + + return rval; +} + + +void static_randvec(int num, vector *vp) +{ + vp->x = static_randf(num) - 0.5f; + vp->y = static_randf(num+1) - 0.5f; + vp->z = static_randf(num+2) - 0.5f; + + vm_vec_normalize_quick(vp); +} + +///////////////////////////////////////////////////////////////////// +// Alternate random number generator, that doesn't affect rand() sequence +///////////////////////////////////////////////////////////////////// +#define RND_MASK 0x6000 +#define RND_MAX 0x7fff +int Rnd_seed = 1; + +// Seed the random number generator. Doesn't have to be called. +void srand_alt(int seed) +{ + Rnd_seed = seed; +} + +// Get a random integer between 1 and RND_MAX +int rand_alt() +{ + static int x=Rnd_seed; + int old_x; + old_x = x; + x >>= 1; + if ( old_x & 1 ) { + x ^= RND_MASK; + } + return x; +} + +// Get a random float between 0 and 1.0 +float frand_alt() +{ + int r = rand_alt(); + return i2fl(r)/RND_MAX; +} + diff --git a/src/math/vecmat.cpp b/src/math/vecmat.cpp new file mode 100644 index 0000000..3dce290 --- /dev/null +++ b/src/math/vecmat.cpp @@ -0,0 +1,2827 @@ +/* + * $Logfile: /Freespace2/code/Math/VecMat.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module containg functions for manipulating vectors and matricies + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 10 9/08/99 3:36p Jefff + * Make sure argument of sqrt is positive in approach. + * + * 9 6/22/99 1:51p Danw + * Some sanity for vm_vec_dist_to_line(...) + * + * 8 6/18/99 5:16p Dave + * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD + * dialog to PXO screen. + * + * 7 4/28/99 11:13p Dave + * Temporary checkin of artillery code. + * + * 6 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 5 1/12/99 12:53a Dave + * More work on beam weapons - made collision detection very efficient - + * collide against all object types properly - made 3 movement types + * smooth. Put in test code to check for possible non-darkening pixels on + * object textures. + * + * 4 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 3 11/18/98 4:10p Johnson + * Add assert in vm_interpolate_matrix + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 72 9/11/98 10:10a Andsager + * Optimize and rename matrix_decomp to vm_matrix_to_rot_axis_and_angle, + * rename quatern_rot to vm_quaternion_rotate + * + * 71 5/01/98 2:25p Andsager + * Fix bug in matrix interpolate (approach) when in rotvel is above limit. + * + * 70 4/07/98 3:10p Andsager + * Make vm_test_parallel based on absolute difference. Optimize matrix + * decomp. Fix potential bug in get_camera_limits with time = 0. + * Optimize vm_forward_interpolate. + * + * 69 4/06/98 8:54a Andsager + * Fix bug where matrix interpolate gets accel of 0 + * + * 68 4/03/98 5:34p Andsager + * Optimized approach and away (used in matrix interpolation) + * + * 67 4/01/98 9:21p John + * Made NDEBUG, optimized build with no warnings or errors. + * + * 66 3/23/98 1:12p Andsager + * Reformat matrix inerpolation code + * + * 65 3/23/98 12:53p Andsager + * + * 63 3/09/98 3:51p Mike + * More error checking. + * + * 62 2/26/98 3:28p John + * Changed all sqrt's to use fl_sqrt. Took out isqrt function + * + * 61 2/02/98 5:12p Mike + * Make vm_vec_rand_vec_quick() detect potential null vector condition and + * recover. + * + * 60 1/20/98 9:47a Mike + * Suppress optimized compiler warnings. + * Some secondary weapon work. + * + * 59 12/17/97 5:44p Andsager + * Change vm_matrix_interpolate so that it does not overshoot if optional + * last parameter is 1 + * + * 58 9/30/97 5:04p Andsager + * add vm_estimate_next_orientation + * + * 57 9/28/97 2:17p Andsager + * added vm_project_point_onto_plane + * + * 56 9/09/97 10:15p Andsager + * added vm_rotate_vec_to_body() and vm_rotate_vec_to_world() + * + * 55 8/20/97 5:33p Andsager + * added vm_vec_projection_parallel and vm_vec_projection_onto_surface + * + * 54 8/20/97 9:51a Lawrance + * swap x and y parameters in atan2_safe() to be consistent with library + * atan2() + * + * 53 8/20/97 9:40a Lawrance + * modified special case values in atan2_safe() + * + * 52 8/19/97 11:41p Lawrance + * use atan2_safe() instead of atan2() + * + * 51 8/18/97 4:46p Hoffoss + * Added global default axis vector constants. + * + * 50 8/03/97 3:54p Lawrance + * added vm_find_bounding_sphere() + * + * 49 7/28/97 3:40p Andsager + * remove duplicate vm_forwarad_interpolate + * + * 48 7/28/97 2:21p John + * changed vecmat functions to not return src. Started putting in code + * for inline vector math. Fixed some bugs with optimizer. + * + * 47 7/28/97 3:24p Andsager + * + * 46 7/28/97 2:41p Mike + * Replace vm_forward_interpolate(). + * + * 45 7/28/97 1:18p Andsager + * implement vm_fvec_matrix_interpolate(), which interpolates matrices on + * xy and then z + * + * 44 7/28/97 10:28a Mike + * Use forward_interpolate() to prevent weird banking behavior. + * + * Suppress a couple annoying mprints and clarify another. + * + * 43 7/24/97 5:24p Andsager + * implement forward vector interpolation + * + * 42 7/10/97 8:52a Andsager + * optimization and clarification of matrix_decomp() + * + * 41 7/09/97 2:54p Mike + * More matrix_decomp optimization. + * + * 40 7/09/97 2:52p Mike + * Optimize and error-prevent matrix_decomp(). + * + * 39 7/09/97 12:05a Mike + * Error prevention in matrix_interpolate(). + * + * 38 7/07/97 11:58p Lawrance + * add get_camera_limits() + * + * 37 7/03/97 11:22a Mike + * Fix bug in matrix_interpolate. Was doing result = goal instead of + * *result = *goal. + * + * 36 7/03/97 9:27a Mike + * Hook in Dave's latest version of matrix_interpolate which doesn't jerk. + * + * 35 7/02/97 4:25p Mike + * Add matrix_interpolate(), but don't call it. + * + * 34 7/01/97 3:27p Mike + * Improve skill level support. + * + * 33 6/25/97 12:27p Hoffoss + * Added some functions I needed for Fred. + * + * 32 5/21/97 8:49a Lawrance + * added vm_vec_same() + * + * 31 4/15/97 4:00p Mike + * Intermediate checkin caused by getting other files. Working on camera + * slewing system. + * + * 30 4/10/97 3:20p Mike + * Change hull damage to be like shields. + * + * 29 3/17/97 1:55p Hoffoss + * Added function for error checking matrices. + * + * 28 3/11/97 10:46p Mike + * Fix make_rand_vec_quick. Was generating values in -0.5..1.5 instead of + * -1.0..1.0. + * + * 27 3/06/97 5:36p Mike + * Change vec_normalize_safe() back to vec_normalize(). + * Spruce up docking a bit. + * + * 26 3/06/97 10:56a Mike + * Write error checking version of vm_vec_normalize(). + * Fix resultant problems. + * + * 25 3/04/97 3:30p John + * added function to interpolate an angle. + * + * 24 2/26/97 10:32a John + * changed debris collision to use vm_vec_dist_squared. Changed + * vm_vec_dist_squared to not int3 on bad values. + * + * 23 2/25/97 5:54p Hoffoss + * Improved vector and matrix compare functions. + * + * 22 2/25/97 5:28p Hoffoss + * added some commented out test code. + * + * 21 2/25/97 5:12p John + * Added functions to see if two matrices or vectors are close. + * + * $NoKeywords: $ + * +*/ + +#include +#include + +#include "vecmat.h" +#include "floating.h" + +#define SMALL_NUM 1e-7 +#define SMALLER_NUM 1e-20 +#define CONVERT_RADIANS 0.017453 // conversion factor from degrees to radians +int index_largest (float a, float b, float c); // returns index of largest, NO_LARGEST if all less than SMALL_NUM + + +vector vmd_zero_vector = ZERO_VECTOR; +vector vmd_x_vector = { 1.0f, 0.0f, 0.0f }; +vector vmd_y_vector = { 0.0f, 1.0f, 0.0f }; +vector vmd_z_vector = { 0.0f, 0.0f, 1.0f }; +matrix vmd_identity_matrix = IDENTITY_MATRIX; + +#define UNINITIALIZED_VALUE -12345678.9f + +// ----------------------------------------------------------- +// atan2_safe() +// +// Wrapper around atan2() that used atan() to calculate angle. Safe +// for optimized builds. Handles special cases when x == 0. +// +float atan2_safe(float y, float x) +{ + float ang; + + // special case, x == 0 + if ( x == 0 ) { + if ( y == 0 ) + ang = 0.0f; + else if ( y > 0 ) + ang = PI/2; + else + ang = -PI/2; + + return ang; + } + + ang = (float)atan(y/x); + if ( x < 0 ){ + ang += PI; + } + + return ang; +} + +// --------------------------------------------------------------------- +// vm_vec_component() +// +// finds projection of a vector along a unit (normalized) vector +// +float vm_vec_projection_parallel(vector *component, vector *src, vector *unit_vec) +{ + float mag; + Assert( vm_vec_mag(unit_vec) > 0.999f && vm_vec_mag(unit_vec) < 1.001f ); + + mag = vm_vec_dotprod(src, unit_vec); + vm_vec_copy_scale(component, unit_vec, mag); + return mag; +} + +// --------------------------------------------------------------------- +// vm_vec_projection_onto_plane() +// +// finds projection of a vector onto a plane specified by a unit normal vector +// +void vm_vec_projection_onto_plane(vector *projection, vector *src, vector *unit_normal) +{ + float mag; + Assert( vm_vec_mag(unit_normal) > 0.999f && vm_vec_mag(unit_normal) < 1.001f ); + + mag = vm_vec_dotprod(src, unit_normal); + *projection = *src; + vm_vec_scale_add2(projection, unit_normal, -mag); +} + +// --------------------------------------------------------------------- +// vm_vec_project_point_onto_plane() +// +// finds the point on a plane closest to a given point +// moves the point in the direction of the plane normal until it is on the plane +// +void vm_project_point_onto_plane(vector *new_point, vector *point, vector *plane_normal, vector *plane_point) +{ + float D; // plane constant in Ax+By+Cz+D = 0 or dot(X,n) - dot(Xp,n) = 0, so D = -dot(Xp,n) + float dist; + Assert( vm_vec_mag(plane_normal) > 0.999f && vm_vec_mag(plane_normal) < 1.001f ); + + D = -vm_vec_dotprod(plane_point, plane_normal); + dist = vm_vec_dotprod(point, plane_normal) + D; + + *new_point = *point; + vm_vec_scale_add2(new_point, plane_normal, -dist); +} + +// Take abs(x), then sqrt. Could insert warning message if desired. +float asqrt(float x) +{ + if (x < 0.0f) + return fl_sqrt(-x); + else + return fl_sqrt(x); +} + +void vm_set_identity(matrix *m) +{ + m->rvec.x = 1.0f; m->rvec.y = 0.0f; m->rvec.z = 0.0f; + m->uvec.x = 0.0f; m->uvec.y = 1.0f; m->uvec.z = 0.0f; + m->fvec.x = 0.0f; m->fvec.y = 0.0f; m->fvec.z = 1.0f; +} + +//adds two vectors, fills in dest, returns ptr to dest +//ok for dest to equal either source, but should use vm_vec_add2() if so +#ifndef _INLINE_VECMAT +void vm_vec_add(vector *dest,vector *src0,vector *src1) +{ + dest->x = src0->x + src1->x; + dest->y = src0->y + src1->y; + dest->z = src0->z + src1->z; +} +#endif + +//subs two vectors, fills in dest, returns ptr to dest +//ok for dest to equal either source, but should use vm_vec_sub2() if so +#ifndef _INLINE_VECMAT +void vm_vec_sub(vector *dest,vector *src0,vector *src1) +{ + dest->x = src0->x - src1->x; + dest->y = src0->y - src1->y; + dest->z = src0->z - src1->z; +} +#endif + + +//adds one vector to another. returns ptr to dest +//dest can equal source +#ifndef _INLINE_VECMAT +void vm_vec_add2(vector *dest,vector *src) +{ + dest->x += src->x; + dest->y += src->y; + dest->z += src->z; +} +#endif + +//subs one vector from another, returns ptr to dest +//dest can equal source +#ifndef _INLINE_VECMAT +void vm_vec_sub2(vector *dest,vector *src) +{ + dest->x -= src->x; + dest->y -= src->y; + dest->z -= src->z; +} +#endif + +//averages two vectors. returns ptr to dest +//dest can equal either source +vector *vm_vec_avg(vector *dest,vector *src0,vector *src1) +{ + dest->x = (src0->x + src1->x) * 0.5f; + dest->y = (src0->y + src1->y) * 0.5f; + dest->z = (src0->z + src1->z) * 0.5f; + + return dest; +} + + +//averages four vectors. returns ptr to dest +//dest can equal any source +vector *vm_vec_avg4(vector *dest,vector *src0,vector *src1,vector *src2,vector *src3) +{ + dest->x = (src0->x + src1->x + src2->x + src3->x) * 0.25f; + dest->y = (src0->y + src1->y + src2->y + src3->y) * 0.25f; + dest->z = (src0->z + src1->z + src2->z + src3->z) * 0.25f; + return dest; +} + + +//scales a vector in place. returns ptr to vector +#ifndef _INLINE_VECMAT +void vm_vec_scale(vector *dest,float s) +{ + dest->x = dest->x * s; + dest->y = dest->y * s; + dest->z = dest->z * s; +} +#endif + + +//scales and copies a vector. returns ptr to dest +#ifndef _INLINE_VECMAT +void vm_vec_copy_scale(vector *dest,vector *src,float s) +{ + dest->x = src->x*s; + dest->y = src->y*s; + dest->z = src->z*s; +} +#endif + +//scales a vector, adds it to another, and stores in a 3rd vector +//dest = src1 + k * src2 +#ifndef _INLINE_VECMAT +void vm_vec_scale_add(vector *dest,vector *src1,vector *src2,float k) +{ + dest->x = src1->x + src2->x*k; + dest->y = src1->y + src2->y*k; + dest->z = src1->z + src2->z*k; +} +#endif + +//scales a vector and adds it to another +//dest += k * src +#ifndef _INLINE_VECMAT +void vm_vec_scale_add2(vector *dest,vector *src,float k) +{ + dest->x += src->x*k; + dest->y += src->y*k; + dest->z += src->z*k; +} +#endif + +//scales a vector and adds it to another +//dest += k * src +#ifndef _INLINE_VECMAT +void vm_vec_scale_sub2(vector *dest,vector *src,float k) +{ + dest->x -= src->x*k; + dest->y -= src->y*k; + dest->z -= src->z*k; +} +#endif + +//scales a vector in place, taking n/d for scale. returns ptr to vector +//dest *= n/d +#ifndef _INLINE_VECMAT +void vm_vec_scale2(vector *dest,float n,float d) +{ + d = 1.0f/d; + + dest->x = dest->x* n * d; + dest->y = dest->y* n * d; + dest->z = dest->z* n * d; +} +#endif + +//returns dot product of 2 vectors +#ifndef _INLINE_VECMAT +float vm_vec_dotprod(vector *v0,vector *v1) +{ + return (v1->x*v0->x)+(v1->y*v0->y)+(v1->z*v0->z); +} +#endif + + +//returns dot product of and vector +#ifndef _INLINE_VECMAT +float vm_vec_dot3(float x,float y,float z,vector *v) +{ + return (x*v->x)+(y*v->y)+(z*v->z); +} +#endif + +//returns magnitude of a vector +float vm_vec_mag(vector *v) +{ + float x,y,z,mag1, mag2; + x = v->x*v->x; + y = v->y*v->y; + z = v->z*v->z; + mag1 = x+y+z; + if ( mag1 < 0.0 ) + Int3(); + mag2 = fl_sqrt(mag1); + if ( mag2 < 0.0 ) + Int3(); + return mag2; +} + +//returns squared magnitude of a vector, useful if you want to compare distances +float vm_vec_mag_squared(vector *v) +{ + float x,y,z,mag1; + x = v->x*v->x; + y = v->y*v->y; + z = v->z*v->z; + mag1 = x+y+z; + return mag1; +} + +float vm_vec_dist_squared(vector *v0, vector *v1) +{ + float dx, dy, dz; + + dx = v0->x - v1->x; + dy = v0->y - v1->y; + dz = v0->z - v1->z; + return dx*dx + dy*dy + dz*dz; +} + +//computes the distance between two points. (does sub and mag) +float vm_vec_dist(vector *v0,vector *v1) +{ + float t1; + vector t; + + vm_vec_sub(&t,v0,v1); + + t1 = vm_vec_mag(&t); + + return t1; +} + + + +//computes an approximation of the magnitude of the vector +//uses dist = largest + next_largest*3/8 + smallest*3/16 +float vm_vec_mag_quick(vector *v) +{ + float a,b,c,bc, t; + + if ( v->x < 0.0 ) + a = -v->x; + else + a = v->x; + + if ( v->y < 0.0 ) + b = -v->y; + else + b = v->y; + + if ( v->z < 0.0 ) + c = -v->z; + else + c = v->z; + + if (a < b) { + float t=a; a=b; b=t; + } + + if (b < c) { + float t=b; b=c; c=t; + + if (a < b) { + float t=a; a=b; b=t; + } + } + + bc = (b * 0.25f) + (c * 0.125f); + + t = a + bc + (bc * 0.5f); + + return t; +} + +//computes an approximation of the distance between two points. +//uses dist = largest + next_largest*3/8 + smallest*3/16 +float vm_vec_dist_quick(vector *v0,vector *v1) +{ + vector t; + + vm_vec_sub(&t,v0,v1); + + return vm_vec_mag_quick(&t); +} + +//normalize a vector. returns mag of source vec +float vm_vec_copy_normalize(vector *dest,vector *src) +{ + float m; + + m = vm_vec_mag(src); + + // Mainly here to trap attempts to normalize a null vector. + if (m <= 0.0f) { + Warning(LOCATION, "Null vector in vector normalize.\n" + "Trace out of vecmat.cpp and find offending code.\n"); + dest->x = 1.0f; + dest->y = 0.0f; + dest->z = 0.0f; + + return 1.0f; + } + + float im = 1.0f / m; + + dest->x = src->x * im; + dest->y = src->y * im; + dest->z = src->z * im; + + return m; +} + +//normalize a vector. returns mag of source vec +float vm_vec_normalize(vector *v) +{ + float t; + t = vm_vec_copy_normalize(v,v); + return t; +} + +// Normalize a vector. +// If vector is 0,0,0, return 1,0,0. +// Don't generate a Warning(). +// returns mag of source vec +float vm_vec_normalize_safe(vector *v) +{ + float m; + + m = vm_vec_mag(v); + + // Mainly here to trap attempts to normalize a null vector. + if (m <= 0.0f) { + v->x = 1.0f; + v->y = 0.0f; + v->z = 0.0f; + return 1.0f; + } + + float im = 1.0f / m; + + v->x *= im; + v->y *= im; + v->z *= im; + + return m; + +} + + +//returns approximation of 1/magnitude of a vector +float vm_vec_imag(vector *v) +{ +// return 1.0f / sqrt( (v->x*v->x)+(v->y*v->y)+(v->z*v->z) ); + return fl_isqrt( (v->x*v->x)+(v->y*v->y)+(v->z*v->z) ); +} + +//normalize a vector. returns 1/mag of source vec. uses approx 1/mag +float vm_vec_copy_normalize_quick(vector *dest,vector *src) +{ +// return vm_vec_copy_normalize(dest, src); + float im; + + im = vm_vec_imag(src); + + Assert(im > 0.0f); + + dest->x = src->x*im; + dest->y = src->y*im; + dest->z = src->z*im; + + return 1.0f/im; +} + +//normalize a vector. returns mag of source vec. uses approx mag +float vm_vec_normalize_quick(vector *src) +{ +// return vm_vec_normalize(src); + + float im; + + im = vm_vec_imag(src); + + Assert(im > 0.0f); + + src->x = src->x*im; + src->y = src->y*im; + src->z = src->z*im; + + return 1.0f/im; + +} + +//normalize a vector. returns mag of source vec. uses approx mag +float vm_vec_copy_normalize_quick_mag(vector *dest,vector *src) +{ +// return vm_vec_copy_normalize(dest, src); + + float m; + + m = vm_vec_mag_quick(src); + + Assert(m > 0.0f); + + float im = 1.0f / m; + + dest->x = src->x * im; + dest->y = src->y * im; + dest->z = src->z * im; + + return m; + +} + +//normalize a vector. returns mag of source vec. uses approx mag +float vm_vec_normalize_quick_mag(vector *v) +{ +// return vm_vec_normalize(v); + float m; + + m = vm_vec_mag_quick(v); + + Assert(m > 0.0f); + + v->x = v->x*m; + v->y = v->y*m; + v->z = v->z*m; + + return m; + +} + + + +//return the normalized direction vector between two points +//dest = normalized(end - start). Returns mag of direction vector +//NOTE: the order of the parameters matches the vector subtraction +float vm_vec_normalized_dir(vector *dest,vector *end,vector *start) +{ + float t; + + vm_vec_sub(dest,end,start); + t = vm_vec_normalize(dest); + return t; +} + +//return the normalized direction vector between two points +//dest = normalized(end - start). Returns mag of direction vector +//NOTE: the order of the parameters matches the vector subtraction +float vm_vec_normalized_dir_quick(vector *dest,vector *end,vector *start) +{ + vm_vec_sub(dest,end,start); + + return vm_vec_normalize_quick(dest); +} + +//return the normalized direction vector between two points +//dest = normalized(end - start). Returns mag of direction vector +//NOTE: the order of the parameters matches the vector subtraction +float vm_vec_normalized_dir_quick_mag(vector *dest,vector *end,vector *start) +{ + float t; + vm_vec_sub(dest,end,start); + + t = vm_vec_normalize_quick_mag(dest); + return t; +} + +//computes surface normal from three points. result is normalized +//returns ptr to dest +//dest CANNOT equal either source +vector *vm_vec_normal(vector *dest,vector *p0,vector *p1,vector *p2) +{ + vm_vec_perp(dest,p0,p1,p2); + + vm_vec_normalize(dest); + + return dest; +} + + +//computes cross product of two vectors. +//Note: this magnitude of the resultant vector is the +//product of the magnitudes of the two source vectors. This means it is +//quite easy for this routine to overflow and underflow. Be careful that +//your inputs are ok. +vector *vm_vec_crossprod(vector *dest,vector *src0,vector *src1) +{ + dest->x = (src0->y * src1->z) - (src0->z * src1->y); + dest->y = (src0->z * src1->x) - (src0->x * src1->z); + dest->z = (src0->x * src1->y) - (src0->y * src1->x); + + return dest; +} + +// test if 2 vectors are parallel or not. +int vm_test_parallel(vector *src0, vector *src1) +{ + if ( (fl_abs(src0->x - src1->x) < 1e-4) && (fl_abs(src0->y - src1->y) < 1e-4) && (fl_abs(src0->z - src1->z) < 1e-4) ) { + return 1; + } else { + return 0; + } +} + +//computes non-normalized surface normal from three points. +//returns ptr to dest +//dest CANNOT equal either source +vector *vm_vec_perp(vector *dest,vector *p0,vector *p1,vector *p2) +{ + vector t0,t1; + + vm_vec_sub(&t0,p1,p0); + vm_vec_sub(&t1,p2,p1); + + return vm_vec_crossprod(dest,&t0,&t1); +} + + +//computes the delta angle between two vectors. +//vectors need not be normalized. if they are, call vm_vec_delta_ang_norm() +//the forward vector (third parameter) can be NULL, in which case the absolute +//value of the angle in returned. Otherwise the angle around that vector is +//returned. +float vm_vec_delta_ang(vector *v0,vector *v1,vector *fvec) +{ + float t; + vector t0,t1,t2; + + vm_vec_copy_normalize(&t0,v0); + vm_vec_copy_normalize(&t1,v1); + vm_vec_copy_normalize(&t2,fvec); + + t = vm_vec_delta_ang_norm(&t0,&t1,&t2); + + return t; +} + +//computes the delta angle between two normalized vectors. +float vm_vec_delta_ang_norm(vector *v0,vector *v1,vector *fvec) +{ + float a; + vector t; + + a = (float)acos(vm_vec_dot(v0,v1)); + + if (fvec) { + vm_vec_cross(&t,v0,v1); + if ( vm_vec_dotprod(&t,fvec) < 0.0 ) { + a = -a; + } + } + + return a; +} + +matrix *sincos_2_matrix(matrix *m,float sinp,float cosp,float sinb,float cosb,float sinh,float cosh) +{ + float sbsh,cbch,cbsh,sbch; + + + sbsh = sinb*sinh; + cbch = cosb*cosh; + cbsh = cosb*sinh; + sbch = sinb*cosh; + + m->rvec.x = cbch + sinp*sbsh; //m1 + m->uvec.z = sbsh + sinp*cbch; //m8 + + m->uvec.x = sinp*cbsh - sbch; //m2 + m->rvec.z = sinp*sbch - cbsh; //m7 + + m->fvec.x = sinh*cosp; //m3 + m->rvec.y = sinb*cosp; //m4 + m->uvec.y = cosb*cosp; //m5 + m->fvec.z = cosh*cosp; //m9 + + m->fvec.y = -sinp; //m6 + + + return m; + +} + +//computes a matrix from a set of three angles. returns ptr to matrix +matrix *vm_angles_2_matrix(matrix *m,angles *a) +{ + matrix * t; + float sinp,cosp,sinb,cosb,sinh,cosh; + + sinp = (float)sin(a->p); cosp = (float)cos(a->p); + sinb = (float)sin(a->b); cosb = (float)cos(a->b); + sinh = (float)sin(a->h); cosh = (float)cos(a->h); + + t = sincos_2_matrix(m,sinp,cosp,sinb,cosb,sinh,cosh); + + return t; +} + +//computes a matrix from one angle. +// angle_index = 0,1,2 for p,b,h +matrix *vm_angle_2_matrix(matrix *m, float a, int angle_index) +{ + matrix * t; + float sinp,cosp,sinb,cosb,sinh,cosh; + + sinp = (float)sin(0.0f); cosp = (float)cos(0.0f); + sinb = (float)sin(0.0f); cosb = (float)cos(0.0f); + sinh = (float)sin(0.0f); cosh = (float)cos(0.0f); + + switch (angle_index) { + case 0: + sinp = (float)sin(a); cosp = (float)cos(a); + break; + case 1: + sinb = (float)sin(a); cosb = (float)cos(a); + break; + case 2: + sinh = (float)sin(a); cosh = (float)cos(a); + break; + } + + t = sincos_2_matrix(m,sinp,cosp,sinb,cosb,sinh,cosh); + + return t; +} + + +//computes a matrix from a forward vector and an angle +matrix *vm_vec_ang_2_matrix(matrix *m,vector *v,float a) +{ + matrix * t; + float sinb,cosb,sinp,cosp,sinh,cosh; + + sinb = (float)sin(a); cosb = (float)cos(a); + + sinp = -v->y; + cosp = fl_sqrt(1.0 - sinp*sinp); + + sinh = v->x / cosp; + cosh = v->z / cosp; + + t = sincos_2_matrix(m,sinp,cosp,sinb,cosb,sinh,cosh); + + return t; +} + + +//computes a matrix from one or more vectors. The forward vector is required, +//with the other two being optional. If both up & right vectors are passed, +//the up vector is used. If only the forward vector is passed, a bank of +//zero is assumed +//returns ptr to matrix +matrix *vm_vector_2_matrix(matrix *m,vector *fvec,vector *uvec,vector *rvec) +{ + vector *xvec=&m->rvec,*yvec=&m->uvec,*zvec=&m->fvec; + + + Assert(fvec != NULL); + + // This had been commented out, but that's bogus. Code below relies on a valid zvec. + if (vm_vec_copy_normalize(zvec,fvec) == 0.0) { + Assert(0); + return m; + } + + if (uvec == NULL) { + + if (rvec == NULL) { //just forward vec + +bad_vector2: + ; + + if ((zvec->x==0.0) && (zvec->z==0.0)) { //forward vec is straight up or down + + m->rvec.x = (float)1.0; + m->uvec.z = (zvec->y<0.0)?(float)1.0:(float)-1.0; + + m->rvec.y = m->rvec.z = m->uvec.x = m->uvec.y = (float)0.0; + } + else { //not straight up or down + + xvec->x = zvec->z; + xvec->y = (float)0.0; + xvec->z = -zvec->x; + + vm_vec_normalize(xvec); + + vm_vec_crossprod(yvec,zvec,xvec); + + } + + } + else { //use right vec + + if (vm_vec_copy_normalize(xvec,rvec) == 0.0) + goto bad_vector2; + + vm_vec_crossprod(yvec,zvec,xvec); + + //normalize new perpendicular vector + if (vm_vec_normalize(yvec) == 0.0) + goto bad_vector2; + + //now recompute right vector, in case it wasn't entirely perpendiclar + vm_vec_crossprod(xvec,yvec,zvec); + + } + } + else { //use up vec + + if (vm_vec_copy_normalize(yvec,uvec) == 0.0f) + goto bad_vector2; + + vm_vec_crossprod(xvec,yvec,zvec); + + //normalize new perpendicular vector + if (vm_vec_normalize(xvec) == 0.0) + goto bad_vector2; + + //now recompute up vector, in case it wasn't entirely perpendiclar + vm_vec_crossprod(yvec,zvec,xvec); + + } + return m; +} + +//quicker version of vm_vector_2_matrix() that takes normalized vectors +matrix *vm_vector_2_matrix_norm(matrix *m,vector *fvec,vector *uvec,vector *rvec) +{ + vector *xvec=&m->rvec,*yvec=&m->uvec,*zvec=&m->fvec; + + + Assert(fvec != NULL); + + *zvec = *fvec; + + if (uvec == NULL) { + + if (rvec == NULL) { //just forward vec + +bad_vector2: + ; + + if ((zvec->x==0.0) && (zvec->z==0.0)) { //forward vec is straight up or down + + m->rvec.x = (float)1.0; + m->uvec.z = (zvec->y<0.0)?(float)1.0:(float)-1.0; + + m->rvec.y = m->rvec.z = m->uvec.x = m->uvec.y = (float)0.0; + } + else { //not straight up or down + + xvec->x = zvec->z; + xvec->y = (float)0.0; + xvec->z = -zvec->x; + + vm_vec_normalize(xvec); + + vm_vec_crossprod(yvec,zvec,xvec); + + } + + } + else { //use right vec + + vm_vec_crossprod(yvec,zvec,xvec); + + //normalize new perpendicular vector + if (vm_vec_normalize(yvec) == 0.0) + goto bad_vector2; + + //now recompute right vector, in case it wasn't entirely perpendiclar + vm_vec_crossprod(xvec,yvec,zvec); + + } + } + else { //use up vec + + vm_vec_crossprod(xvec,yvec,zvec); + + //normalize new perpendicular vector + if (vm_vec_normalize(xvec) == 0.0) + goto bad_vector2; + + //now recompute up vector, in case it wasn't entirely perpendiclar + vm_vec_crossprod(yvec,zvec,xvec); + + } + + + return m; +} + + +//rotates a vector through a matrix. returns ptr to dest vector +//dest CANNOT equal source +vector *vm_vec_rotate(vector *dest,vector *src,matrix *m) +{ + dest->x = (src->x*m->rvec.x)+(src->y*m->rvec.y)+(src->z*m->rvec.z); + dest->y = (src->x*m->uvec.x)+(src->y*m->uvec.y)+(src->z*m->uvec.z); + dest->z = (src->x*m->fvec.x)+(src->y*m->fvec.y)+(src->z*m->fvec.z); + + return dest; +} + +//rotates a vector through the transpose of the given matrix. +//returns ptr to dest vector +//dest CANNOT equal source +// This is a faster replacement for this common code sequence: +// vm_copy_transpose_matrix(&tempm,src_matrix); +// vm_vec_rotate(dst_vec,src_vect,&tempm); +// Replace with: +// vm_vec_unrotate(dst_vec,src_vect, src_matrix) +// +// THIS DOES NOT ACTUALLY TRANSPOSE THE SOURCE MATRIX!!! So if +// you need it transposed later on, you should use the +// vm_vec_transpose() / vm_vec_rotate() technique. + +vector *vm_vec_unrotate(vector *dest,vector *src,matrix *m) +{ + dest->x = (src->x*m->rvec.x)+(src->y*m->uvec.x)+(src->z*m->fvec.x); + dest->y = (src->x*m->rvec.y)+(src->y*m->uvec.y)+(src->z*m->fvec.y); + dest->z = (src->x*m->rvec.z)+(src->y*m->uvec.z)+(src->z*m->fvec.z); + + return dest; +} + +//transpose a matrix in place. returns ptr to matrix +matrix *vm_transpose_matrix(matrix *m) +{ + float t; + + t = m->uvec.x; m->uvec.x = m->rvec.y; m->rvec.y = t; + t = m->fvec.x; m->fvec.x = m->rvec.z; m->rvec.z = t; + t = m->fvec.y; m->fvec.y = m->uvec.z; m->uvec.z = t; + + return m; +} + +//copy and transpose a matrix. returns ptr to matrix +//dest CANNOT equal source. use vm_transpose_matrix() if this is the case +matrix *vm_copy_transpose_matrix(matrix *dest,matrix *src) +{ + + Assert(dest != src); + + dest->rvec.x = src->rvec.x; + dest->rvec.y = src->uvec.x; + dest->rvec.z = src->fvec.x; + + dest->uvec.x = src->rvec.y; + dest->uvec.y = src->uvec.y; + dest->uvec.z = src->fvec.y; + + dest->fvec.x = src->rvec.z; + dest->fvec.y = src->uvec.z; + dest->fvec.z = src->fvec.z; + + + return dest; +} + +//mulitply 2 matrices, fill in dest. returns ptr to dest +//dest CANNOT equal either source +matrix *vm_matrix_x_matrix(matrix *dest,matrix *src0,matrix *src1) +{ + + Assert(dest!=src0 && dest!=src1); + + dest->rvec.x = vm_vec_dot3(src0->rvec.x,src0->uvec.x,src0->fvec.x, &src1->rvec); + dest->uvec.x = vm_vec_dot3(src0->rvec.x,src0->uvec.x,src0->fvec.x, &src1->uvec); + dest->fvec.x = vm_vec_dot3(src0->rvec.x,src0->uvec.x,src0->fvec.x, &src1->fvec); + + dest->rvec.y = vm_vec_dot3(src0->rvec.y,src0->uvec.y,src0->fvec.y, &src1->rvec); + dest->uvec.y = vm_vec_dot3(src0->rvec.y,src0->uvec.y,src0->fvec.y, &src1->uvec); + dest->fvec.y = vm_vec_dot3(src0->rvec.y,src0->uvec.y,src0->fvec.y, &src1->fvec); + + dest->rvec.z = vm_vec_dot3(src0->rvec.z,src0->uvec.z,src0->fvec.z, &src1->rvec); + dest->uvec.z = vm_vec_dot3(src0->rvec.z,src0->uvec.z,src0->fvec.z, &src1->uvec); + dest->fvec.z = vm_vec_dot3(src0->rvec.z,src0->uvec.z,src0->fvec.z, &src1->fvec); + + + return dest; +} + + +//extract angles from a matrix +angles *vm_extract_angles_matrix(angles *a,matrix *m) +{ + float sinh,cosh,cosp; + + if (m->fvec.x==0.0 && m->fvec.z==0.0) //zero head + a->h = (float)0.0; + else + // a->h = (float)atan2(m->fvec.z,m->fvec.x); + a->h = (float)atan2_safe(m->fvec.x,m->fvec.z); + + sinh = (float)sin(a->h); cosh = (float)cos(a->h); + + if (fl_abs(sinh) > fl_abs(cosh)) //sine is larger, so use it + cosp = m->fvec.x*sinh; + else //cosine is larger, so use it + cosp = m->fvec.z*cosh; + + if (cosp==0.0 && m->fvec.y==0.0) + a->p = (float)0.0; + else + // a->p = (float)atan2(cosp,-m->fvec.y); + a->p = (float)atan2_safe(-m->fvec.y, cosp); + + + if (cosp == 0.0) //the cosine of pitch is zero. we're pitched straight up. say no bank + + a->b = (float)0.0; + + else { + float sinb,cosb; + + sinb = m->rvec.y/cosp; + cosb = m->uvec.y/cosp; + + if (sinb==0.0 && cosb==0.0) + a->b = (float)0.0; + else + // a->b = (float)atan2(cosb,sinb); + a->b = (float)atan2_safe(sinb,cosb); + } + + + return a; +} + + +//extract heading and pitch from a vector, assuming bank==0 +angles *vm_extract_angles_vector_normalized(angles *a,vector *v) +{ + + a->b = 0.0f; //always zero bank + + a->p = (float)asin(-v->y); + + if (v->x==0.0f && v->z==0.0f) + a->h = (float)0.0; + else + a->h = (float)atan2_safe(v->z,v->x); + + return a; +} + +//extract heading and pitch from a vector, assuming bank==0 +angles *vm_extract_angles_vector(angles *a,vector *v) +{ + vector t; + + if (vm_vec_copy_normalize(&t,v) != 0.0) + vm_extract_angles_vector_normalized(a,&t); + + return a; +} + +//compute the distance from a point to a plane. takes the normalized normal +//of the plane (ebx), a point on the plane (edi), and the point to check (esi). +//returns distance in eax +//distance is signed, so negative dist is on the back of the plane +float vm_dist_to_plane(vector *checkp,vector *norm,vector *planep) +{ + float t1; + vector t; + + vm_vec_sub(&t,checkp,planep); + + t1 = vm_vec_dot(&t,norm); + + return t1; + +} + +// Given mouse movement in dx, dy, returns a 3x3 rotation matrix in RotMat. +// Taken from Graphics Gems III, page 51, "The Rolling Ball" +// Example: +//if ( (Mouse.dx!=0) || (Mouse.dy!=0) ) { +// GetMouseRotation( Mouse.dx, Mouse.dy, &MouseRotMat ); +// vm_matrix_x_matrix(&tempm,&LargeView.ev_matrix,&MouseRotMat); +// LargeView.ev_matrix = tempm; +//} + + +void vm_trackball( int idx, int idy, matrix * RotMat ) +{ + float dr, cos_theta, sin_theta, denom, cos_theta1; + float Radius = 100.0f; + float dx,dy; + float dxdr,dydr; + + idy *= -1; + + dx = (float)idx; dy = (float)idy; + + dr = fl_sqrt(dx*dx+dy*dy); + + denom = fl_sqrt(Radius*Radius+dr*dr); + + cos_theta = Radius/denom; + sin_theta = dr/denom; + + cos_theta1 = 1.0f - cos_theta; + + dxdr = dx/dr; + dydr = dy/dr; + + RotMat->rvec.x = cos_theta + (dydr*dydr)*cos_theta1; + RotMat->uvec.x = - ((dxdr*dydr)*cos_theta1); + RotMat->fvec.x = (dxdr*sin_theta); + + RotMat->rvec.y = RotMat->uvec.x; + RotMat->uvec.y = cos_theta + ((dxdr*dxdr)*cos_theta1); + RotMat->fvec.y = (dydr*sin_theta); + + RotMat->rvec.z = -RotMat->fvec.x; + RotMat->uvec.z = -RotMat->fvec.y; + RotMat->fvec.z = cos_theta; +} + +// Compute the outer product of A = A * transpose(A). 1x3 vector becomes 3x3 matrix. +void vm_vec_outer_product(matrix *mat, vector *vec) +{ + mat->rvec.x = vec->x * vec->x; + mat->rvec.y = vec->x * vec->y; + mat->rvec.z = vec->x * vec->z; + + mat->uvec.x = vec->y * vec->x; + mat->uvec.y = vec->y * vec->y; + mat->uvec.z = vec->y * vec->z; + + mat->fvec.x = vec->z * vec->x; + mat->fvec.y = vec->z * vec->y; + mat->fvec.z = vec->z * vec->z; +} + +// Find the point on the line between p0 and p1 that is nearest to int_pnt. +// Stuff result in nearest_point. +// Uses algorithm from page 148 of Strang, Linear Algebra and Its Applications. +// Returns value indicating whether *nearest_point is between *p0 and *p1. +// 0.0f means *nearest_point is *p0, 1.0f means it's *p1. 2.0f means it's beyond p1 by 2x. +// -1.0f means it's "before" *p0 by 1x. +float find_nearest_point_on_line(vector *nearest_point, vector *p0, vector *p1, vector *int_pnt) +{ + vector norm, xlated_int_pnt, projected_point; + matrix mat; + float mag, dot; + + vm_vec_sub(&norm, p1, p0); + vm_vec_sub(&xlated_int_pnt, int_pnt, p0); + + if (IS_VEC_NULL(&norm)) { + *nearest_point = *int_pnt; + return 9999.9f; + } + + mag = vm_vec_normalize(&norm); // Normalize vector so we don't have to divide by dot product. + + if (mag < 0.01f) { + *nearest_point = *int_pnt; + return 9999.9f; + // Warning(LOCATION, "Very small magnitude in find_nearest_point_on_line.\n"); + } + + vm_vec_outer_product(&mat, &norm); + + vm_vec_rotate(&projected_point, &xlated_int_pnt, &mat); + vm_vec_add(nearest_point, &projected_point, p0); + + dot = vm_vec_dot(&norm, &projected_point); + + return dot/mag; +} + +//make sure matrix is orthogonal +//computes a matrix from one or more vectors. The forward vector is required, +//with the other two being optional. If both up & right vectors are passed, +//the up vector is used. If only the forward vector is passed, a bank of +//zero is assumed +//returns ptr to matrix +void vm_orthogonalize_matrix(matrix *m_src) +{ + float umag, rmag; + matrix tempm; + matrix * m = &tempm; + + if (vm_vec_copy_normalize(&m->fvec,&m_src->fvec) == 0.0f) { + Error( LOCATION, "forward vec should not be zero-length" ); + } + + umag = vm_vec_mag(&m_src->uvec); + rmag = vm_vec_mag(&m_src->rvec); + if (umag <= 0.0f) { // no up vector to use.. + if (rmag <= 0.0f) { // no right vector either, so make something up + if (!m->fvec.x && !m->fvec.z && m->fvec.y) // vertical vector + vm_vec_make(&m->uvec, 0.0f, 0.0f, 1.0f); + else + vm_vec_make(&m->uvec, 0.0f, 1.0f, 0.0f); + + } else { // use the right vector to figure up vector + vm_vec_crossprod(&m->uvec, &m->fvec, &m_src->rvec); + if (vm_vec_normalize(&m->uvec) == 0.0f) + Error( LOCATION, "Bad vector!" ); + } + + } else { // use source up vector + vm_vec_copy_normalize(&m->uvec, &m_src->uvec); + } + + // use forward and up vectors as good vectors to calculate right vector + vm_vec_crossprod(&m->rvec, &m->uvec, &m->fvec); + + //normalize new perpendicular vector + if (vm_vec_normalize(&m->rvec) == 0.0f) + Error( LOCATION, "Bad vector!" ); + + //now recompute up vector, in case it wasn't entirely perpendiclar + vm_vec_crossprod(&m->uvec, &m->fvec, &m->rvec); + *m_src = tempm; +} + +// like vm_orthogonalize_matrix(), except that zero vectors can exist within the +// matrix without causing problems. Valid vectors will be created where needed. +void vm_fix_matrix(matrix *m) +{ + float fmag, umag, rmag; + + fmag = vm_vec_mag(&m->fvec); + umag = vm_vec_mag(&m->uvec); + rmag = vm_vec_mag(&m->rvec); + if (fmag <= 0.0f) { + if ((umag > 0.0f) && (rmag > 0.0f) && !vm_test_parallel(&m->uvec, &m->rvec)) { + vm_vec_crossprod(&m->fvec, &m->uvec, &m->rvec); + vm_vec_normalize(&m->fvec); + + } else if (umag > 0.0f) { + if (!m->uvec.x && !m->uvec.y && m->uvec.z) // z vector + vm_vec_make(&m->fvec, 1.0f, 0.0f, 0.0f); + else + vm_vec_make(&m->fvec, 0.0f, 0.0f, 1.0f); + } + + } else + vm_vec_normalize(&m->fvec); + + // we now have a valid and normalized forward vector + + if ((umag <= 0.0f) || vm_test_parallel(&m->fvec, &m->uvec)) { // no up vector to use.. + if ((rmag <= 0.0f) || vm_test_parallel(&m->fvec, &m->rvec)) { // no right vector either, so make something up + if (!m->fvec.x && m->fvec.y && !m->fvec.z) // vertical vector + vm_vec_make(&m->uvec, 0.0f, 0.0f, -1.0f); + else + vm_vec_make(&m->uvec, 0.0f, 1.0f, 0.0f); + + } else { // use the right vector to figure up vector + vm_vec_crossprod(&m->uvec, &m->fvec, &m->rvec); + vm_vec_normalize(&m->uvec); + } + + } else + vm_vec_normalize(&m->uvec); + + // we now have both valid and normalized forward and up vectors + + vm_vec_crossprod(&m->rvec, &m->uvec, &m->fvec); + + //normalize new perpendicular vector + vm_vec_normalize(&m->rvec); + + //now recompute up vector, in case it wasn't entirely perpendiclar + vm_vec_crossprod(&m->uvec, &m->fvec, &m->rvec); +} + +//Rotates the orient matrix by the angles in tangles and then +//makes sure that the matrix is orthogonal. +void vm_rotate_matrix_by_angles( matrix *orient, angles *tangles ) +{ + matrix rotmat,new_orient; + vm_angles_2_matrix(&rotmat,tangles); + vm_matrix_x_matrix(&new_orient,orient,&rotmat); + *orient = new_orient; + vm_orthogonalize_matrix(orient); +} + +// dir must be normalized! +float vm_vec_dot_to_point(vector *dir, vector *p1, vector *p2) +{ + vector tvec; + + vm_vec_sub(&tvec, p2, p1); + vm_vec_normalize(&tvec); + + return vm_vec_dot(dir, &tvec); + +} + +///////////////////////////////////////////////////////// +// Given a plane and a point, return the point on the plane closest the the point. +// Result returned in q. +void compute_point_on_plane(vector *q, plane *planep, vector *p) +{ + float k, tv; + vector normal; + + normal.x = planep->A; + normal.y = planep->B; + normal.z = planep->C; + + k = (planep->D + vm_vec_dot(&normal, p)) / vm_vec_dot(&normal, &normal); + + vm_vec_scale_add(q, p, &normal, -k); + + tv = planep->A * q->x + planep->B * q->y + planep->C * q->z + planep->D; +} + + +// Generate a fairly random vector that's fairly near normalized. +void vm_vec_rand_vec_quick(vector *rvec) +{ + rvec->x = (frand() - 0.5f) * 2; + rvec->y = (frand() - 0.5f) * 2; + rvec->z = (frand() - 0.5f) * 2; + + if (IS_VEC_NULL(rvec)) + rvec->x = 1.0f; + + vm_vec_normalize_quick(rvec); +} + +// Given an point "in" rotate it by "angle" around an +// arbritary line defined by a point on the line "line_point" +// and the normalized line direction, "line_dir" +// Returns the rotated point in "out". +void vm_rot_point_around_line(vector *out, vector *in, float angle, vector *line_point, vector *line_dir) +{ + vector tmp, tmp1; + matrix m, r, im; + angles ta; + + vm_vector_2_matrix_norm(&m, line_dir, NULL, NULL ); + vm_copy_transpose_matrix(&im,&m); + + ta.p = ta.h = 0.0f; + ta.b = angle; + vm_angles_2_matrix(&r,&ta); + + vm_vec_sub( &tmp, in, line_point ); // move relative to a point on line + vm_vec_rotate( &tmp1, &tmp, &m); // rotate into line's base + vm_vec_rotate( &tmp, &tmp1, &r); // rotate around Z + vm_vec_rotate( &tmp1, &tmp, &im); // unrotate out of line's base + vm_vec_add( out, &tmp1, line_point ); // move back to world coordinates +} + +// Given two position vectors, return 0 if the same, else non-zero. +int vm_vec_cmp( vector * a, vector * b ) +{ + float diff = vm_vec_dist(a,b); +//mprintf(( "Diff=%.32f\n", diff )); + if ( diff > 0.005f ) + return 1; + else + return 0; +} + +// Given two orientation matrices, return 0 if the same, else non-zero. +int vm_matrix_cmp( matrix * a, matrix * b ) +{ + float tmp1,tmp2,tmp3; + tmp1 = (float)fl_abs(vm_vec_dot( &a->uvec, &b->uvec ) - 1.0f); + tmp2 = (float)fl_abs(vm_vec_dot( &a->fvec, &b->fvec ) - 1.0f); + tmp3 = (float)fl_abs(vm_vec_dot( &a->rvec, &b->rvec ) - 1.0f); +// mprintf(( "Mat=%.16f, %.16f, %.16f\n", tmp1, tmp2, tmp3 )); + + if ( tmp1 > 0.0000005f ) return 1; + if ( tmp2 > 0.0000005f ) return 1; + if ( tmp3 > 0.0000005f ) return 1; + return 0; +} + + +// Moves angle 'h' towards 'desired_angle', taking the shortest +// route possible. It will move a maximum of 'step_size' radians +// each call. All angles in radians. +void vm_interp_angle( float *h, float desired_angle, float step_size ) +{ + float delta; + + if ( desired_angle < 0.0f ) desired_angle += PI2; + if ( desired_angle > PI2 ) desired_angle -= PI2; + + delta = desired_angle - *h; + + if ( fl_abs(delta) > PI ) { + // Go the other way, since it will be shorter. + if ( delta > 0.0f ) { + delta = delta - PI2; + } else { + delta = PI2 - delta; + } + } + + if ( delta > step_size ) + *h += step_size; + else if ( delta < -step_size ) + *h -= step_size; + else + *h = desired_angle; + + // If we wrap outside of 0 to 2*PI, then put the + // angle back in the range 0 to 2*PI. + if ( *h > PI2 ) *h -= PI2; + if ( *h < 0.0f ) *h += PI2; +} + +// check a matrix for zero rows and columns +int vm_check_matrix_for_zeros(matrix *m) +{ + if (!m->fvec.x && !m->fvec.y && !m->fvec.z) + return 1; + if (!m->rvec.x && !m->rvec.y && !m->rvec.z) + return 1; + if (!m->uvec.x && !m->uvec.y && !m->uvec.z) + return 1; + + if (!m->fvec.x && !m->rvec.x && !m->uvec.x) + return 1; + if (!m->fvec.y && !m->rvec.y && !m->uvec.y) + return 1; + if (!m->fvec.z && !m->rvec.z && !m->uvec.z) + return 1; + + return 0; +} + +// see if two vectors are the same +int vm_vec_same(vector *v1, vector *v2) +{ + if ( v1->x == v2->x && v1->y == v2->y && v1->z == v2->z ) + return 1; + + return 0; +} + + +// -------------------------------------------------------------------------------------- + +void vm_quaternion_rotate(matrix *M, float theta, vector *u) +// given an arbitrary rotation axis and rotation angle, function generates the +// corresponding rotation matrix +// +// M is the return rotation matrix theta is the angle of rotation +// u is the direction of the axis. +// this is adapted from Computer Graphics (Hearn and Bker 2nd ed.) p. 420 +// +{ + + float a,b,c, s; + + a = (float) (u->x * sin(theta * 0.5f)); + b = (float) (u->y * sin(theta * 0.5f)); + c = (float) (u->z * sin(theta * 0.5f)); + s = (float) cos(theta/2.0); + +// 1st ROW vector + M->rvec.x = 1.0f - 2.0f*b*b - 2.0f*c*c; + M->rvec.y = 2.0f*a*b + 2.0f*s*c; + M->rvec.z = 2.0f*a*c - 2.0f*s*b; +// 2nd ROW vector + M->uvec.x = 2.0f*a*b - 2.0f*s*c; + M->uvec.y = 1.0f - 2.0f*a*a - 2.0f*c*c; + M->uvec.z = 2.0f*b*c + 2.0f*s*a; +// 3rd ROW vector + M->fvec.x = 2.0f*a*c + 2.0f*s*b; + M->fvec.y = 2.0f*b*c - 2.0f*s*a; + M->fvec.z = 1.0f - 2.0f*a*a - 2.0f*b*b; +} + +// -------------------------------------------------------------------------------------- +// function finds the rotation matrix about the z axis for a given rotation angle (in radians) +// this is an optimized version vm_quaternion_rotate +// +// inputs: m => point to resultant rotation matrix +// angle => rotation angle about z axis (in radians) +// +void rotate_z ( matrix *m, float theta ) +{ + m->rvec.x = (float) cos (theta); + m->rvec.y = (float) sin (theta); + m->rvec.z = 0.0f; + + m->uvec.x = -m->rvec.y; + m->uvec.y = m->rvec.x; + m->uvec.z = 0.0f; + + m->fvec.x = 0.0f; + m->fvec.y = 0.0f; + m->fvec.z = 1.0f; +} + + +// -------------------------------------------------------------------------------------- + +//void vm_matrix_to_rot_axis_and_angle(matrix *m, float *theta, vector *rot_axis) +// Converts a matrix into a rotation axis and an angle around that axis +// Note for angle is very near 0, returns 0 with axis of (1,0,0) +// For angles near PI, returns PI with correct axis +// +// rot_axis - the resultant axis of rotation +// theta - the resultatn rotation around the axis +// m - the initial matrix +void vm_matrix_to_rot_axis_and_angle(matrix *m, float *theta, vector *rot_axis) +{ + float trace = m->a2d[0][0] + m->a2d[1][1] + m->a2d[2][2]; + float cos_theta = 0.5f * (trace - 1.0f); + + if (cos_theta > 0.999999875f) { // angle is less than 1 milirad (0.057 degrees) + *theta = 0.0f; + + vm_vec_make(rot_axis, 1.0f, 0.0f, 0.0f); + } else if (cos_theta > -0.999999875f) { // angle is within limits between 0 and PI + *theta = float(acos(cos_theta)); + Assert(!_isnan(*theta)); + + rot_axis->x = (m->uvec.z - m->fvec.y); + rot_axis->y = (m->fvec.x - m->rvec.z); + rot_axis->z = (m->rvec.y - m->uvec.x); + vm_vec_normalize(rot_axis); + } else { // angle is PI within limits + *theta = PI; + + // find index of largest diagonal term + int largest_diagonal_index = 0; + + if (m->a2d[1][1] > m->a2d[0][0]) { + largest_diagonal_index = 1; + } + if (m->a2d[2][2] > m->a2d[largest_diagonal_index][largest_diagonal_index]) { + largest_diagonal_index = 2; + } + + switch (largest_diagonal_index) { + case 0: + float ix; + ix = 1.0f / rot_axis->x; + + rot_axis->x = fl_sqrt(m->a2d[0][0] + 1.0f); + rot_axis->y = m->a2d[0][1] * ix; + rot_axis->z = m->a2d[0][2] * ix; + vm_vec_normalize(rot_axis); + break; + + case 1: + float iy; + iy = 1.0f / rot_axis->y; + + rot_axis->y = fl_sqrt(m->a2d[1][1] + 1.0f); + rot_axis->x = m->a2d[1][0] * iy; + rot_axis->z = m->a2d[1][2] * iy; + vm_vec_normalize(rot_axis); + break; + + case 2: + float iz; + iz = 1.0f / rot_axis->z; + + rot_axis->z = fl_sqrt(m->a2d[2][2] + 1.0f); + rot_axis->x = m->a2d[2][0] * iz; + rot_axis->y = m->a2d[2][1] * iz; + break; + + default: + Int3(); // this should never happen + break; + } + + // normalize rotation axis + vm_vec_normalize(rot_axis); + } +} + + +// -------------------------------------------------------------------------------------- +// This routine determines the resultant angular displacement and angular velocity in trying to reach a goal +// given an angular velocity APPROACHing a goal. It uses maximal acceleration to a point (called peak), then maximal +// deceleration to arrive at the goal with zero angular velocity. This can occasionally cause overshoot. +// w_in > 0 +// w_max > 0 +// theta_goal > 0 +// aa > 0 +// returns delta_theta +float away(float w_in, float w_max, float theta_goal, float aa, float delta_t, float *w_out, int no_overshoot); +float approach(float w_in, float w_max, float theta_goal, float aa, float delta_t, float *w_out, int no_overshoot) +{ + float delta_theta; // amount rotated during time delta_t + Assert(w_in >= 0); + Assert(theta_goal > 0); + float effective_aa; + + if (aa == 0) { + *w_out = w_in; + delta_theta = w_in*delta_t; + return delta_theta; + } + + if (no_overshoot && (w_in*w_in > 2.0f*1.05f*aa*theta_goal)) { + w_in = fl_sqrt(2.0f*aa*theta_goal); + } + + if (w_in*w_in > 2.0f*1.05f*aa*theta_goal) { // overshoot condition + effective_aa = 1.05f*aa; + delta_theta = w_in*delta_t - 0.5f*effective_aa*delta_t*delta_t; + + if (delta_theta > theta_goal) { // pass goal during this frame + float t_goal = (-w_in + fl_sqrt(w_in*w_in +2.0f*effective_aa*theta_goal)) / effective_aa; + // get time to theta_goal and away + Assert(t_goal < delta_t); + w_in -= effective_aa*t_goal; + delta_theta = w_in*t_goal + 0.5f*effective_aa*t_goal*t_goal; + delta_theta -= away(-w_in, w_max, 0.0f, aa, delta_t - t_goal, w_out, no_overshoot); + *w_out = -*w_out; + return delta_theta; + } else { + if (delta_theta < 0) { + // pass goal and return this frame + *w_out = 0.0f; + return theta_goal; + } else { + // do not pass goal this frame + *w_out = w_in - effective_aa*delta_t; + return delta_theta; + } + } + } else if (w_in*w_in < 2.0f*0.95f*aa*theta_goal) { // undershoot condition + // find peak angular velocity + float wp_sqr = fl_abs(aa*theta_goal + 0.5f*w_in*w_in); + Assert(wp_sqr >= 0); + + if (wp_sqr > w_max*w_max) { + float time_to_w_max = (w_max - w_in) / aa; + if (time_to_w_max < 0) { + // speed already too high + // TODO: consider possible ramp down to below w_max + *w_out = w_in - aa*delta_t; + if (*w_out < 0) { + *w_out = 0.0f; + } + + delta_theta = 0.5f*(w_in + *w_out)*delta_t; + return delta_theta; + } else if (time_to_w_max > delta_t) { + // does not reach w_max this frame + *w_out = w_in + aa*delta_t; + delta_theta = 0.5f*(w_in + *w_out)*delta_t; + return delta_theta; + } else { + // reaches w_max this frame + // TODO: consider when to ramp down from w_max + *w_out = w_max; + delta_theta = 0.5f*(w_in + *w_out)*delta_t; + return delta_theta; + } + } else { // wp < w_max + if (wp_sqr > (w_in + aa*delta_t)*(w_in + aa*delta_t)) { + // does not reach wp this frame + *w_out = w_in + aa*delta_t; + delta_theta = 0.5f*(w_in + *w_out)*delta_t; + return delta_theta; + } else { + // reaches wp this frame + float wp = fl_sqrt(wp_sqr); + float time_to_wp = (wp - w_in) / aa; + Assert(time_to_wp > 0); + + // accel + *w_out = wp; + delta_theta = 0.5f*(w_in + *w_out)*time_to_wp; + + // decel + float time_remaining = delta_t - time_to_wp; + *w_out -= aa*time_remaining; + if (*w_out < 0) { // reached goal + *w_out = 0.0f; + delta_theta = theta_goal; + return delta_theta; + } + delta_theta += 0.5f*(wp + *w_out)*time_remaining; + return delta_theta; + } + } + } else { // on target + // reach goal this frame + if (w_in - aa*delta_t < 0) { + // reach goal this frame + *w_out = 0.0f; + return theta_goal; + } else { + // move toward goal + *w_out = w_in - aa*delta_t; + Assert(*w_out >= 0); + delta_theta = 0.5f*(w_in + *w_out)*delta_t; + return delta_theta; + } + } +} + + +// -------------------------------------------------------------------------------------- + +// This routine determines the resultant angular displacement and angular velocity in trying to reach a goal +// given an angular velocity AWAY from a goal. It uses maximal acceleration to a point (called peak), then maximal +// deceleration to arrive at the goal with zero angular acceleration. +// w_in < 0 +// w_max > 0 +// theta_goal > 0 +// aa > 0 +// returns angle rotated this frame +float away(float w_in, float w_max, float theta_goal, float aa, float delta_t, float *w_out, int no_overshoot) + +{ + float delta_theta;// amount rotated during time + float t0; // time to velocity is 0 + float t_excess; // time remaining in interval after velocity is 0 + + Assert(theta_goal >=0); + Assert(w_in <= 0); + + if ((-w_in < 1e-5) && (theta_goal < 1e-5)) { + *w_out = 0.0f; + return theta_goal; + } + + if (aa == 0) { + *w_out = w_in; + delta_theta = w_in*delta_t; + return delta_theta; + } + + t0 = -w_in / aa; + + if (t0 > delta_t) { // no reversal in this time interval + *w_out = w_in + aa * delta_t; + delta_theta = (w_in + *w_out) / 2.0f * delta_t; + return delta_theta; + } + + // use time remaining after v = 0 + delta_theta = 0.5f*w_in*t0; + theta_goal -= delta_theta; // delta_theta is *negative* + t_excess = delta_t - t0; + delta_theta += approach(0.0f, w_max, theta_goal, aa, t_excess, w_out, no_overshoot); + return delta_theta; +} + +// -------------------------------------------------------------------------------------- + +void vm_matrix_interpolate(matrix *goal_orient, matrix *curr_orient, vector *w_in, float delta_t, + matrix *next_orient, vector *w_out, vector *vel_limit, vector *acc_limit, int no_overshoot) +{ + matrix rot_matrix; // rotation matrix from curr_orient to goal_orient + matrix Mtemp1; // temp matrix + vector rot_axis; // vector indicating direction of rotation axis + vector theta_goal; // desired angular position at the end of the time interval + vector theta_end; // actual angular position at the end of the time interval + float theta; // magnitude of rotation about the rotation axis + + // FIND ROTATION NEEDED FOR GOAL + // goal_orient = R curr_orient, so R = goal_orient curr_orient^-1 + vm_copy_transpose_matrix(&Mtemp1, curr_orient); // Mtemp1 = curr ^-1 + vm_matrix_x_matrix(&rot_matrix, &Mtemp1, goal_orient); // R = goal * Mtemp1 + vm_orthogonalize_matrix(&rot_matrix); + vm_matrix_to_rot_axis_and_angle(&rot_matrix, &theta, &rot_axis); // determines angle and rotation axis from curr to goal + + // find theta to goal + vm_vec_copy_scale(&theta_goal, &rot_axis, theta); + + if (theta < SMALL_NUM) { + *next_orient = *goal_orient; + vm_vec_zero(w_out); + return; + } + + theta_end = vmd_zero_vector; + float delta_theta; + + // find rotation about x + if (theta_goal.x > 0) { + if (w_in->x >= 0) { + delta_theta = approach(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = delta_theta; + } else { // w_in->x < 0 + delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = delta_theta; + } + } else if (theta_goal.x < 0) { + if (w_in->x <= 0) { + delta_theta = approach(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = -delta_theta; + w_out->x = -w_out->x; + } else { // w_in->x > 0 + delta_theta = away(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = -delta_theta; + w_out->x = -w_out->x; + } + } else { // theta_goal == 0 + if (w_in->x < 0) { + delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = delta_theta; + } else { + delta_theta = away(-w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = -delta_theta; + w_out->x = -w_out->x; + } + } + + + // find rotation about y + if (theta_goal.y > 0) { + if (w_in->y >= 0) { + delta_theta = approach(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = delta_theta; + } else { // w_in->y < 0 + delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = delta_theta; + } + } else if (theta_goal.y < 0) { + if (w_in->y <= 0) { + delta_theta = approach(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = -delta_theta; + w_out->y = -w_out->y; + } else { // w_in->y > 0 + delta_theta = away(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = -delta_theta; + w_out->y = -w_out->y; + } + } else { // theta_goal == 0 + if (w_in->y < 0) { + delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = delta_theta; + } else { + delta_theta = away(-w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = -delta_theta; + w_out->y = -w_out->y; + } + } + + // find rotation about z + if (theta_goal.z > 0) { + if (w_in->z >= 0) { + delta_theta = approach(w_in->z, vel_limit->z, theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot); + theta_end.z = delta_theta; + } else { // w_in->z < 0 + delta_theta = away(w_in->z, vel_limit->z, theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot); + theta_end.z = delta_theta; + } + } else if (theta_goal.z < 0) { + if (w_in->z <= 0) { + delta_theta = approach(-w_in->z, vel_limit->z, -theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot); + theta_end.z = -delta_theta; + w_out->z = -w_out->z; + } else { // w_in->z > 0 + delta_theta = away(-w_in->z, vel_limit->z, -theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot); + theta_end.z = -delta_theta; + w_out->z = -w_out->z; + } + } else { // theta_goal == 0 + if (w_in->z < 0) { + delta_theta = away(w_in->z, vel_limit->z, theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot); + theta_end.z = delta_theta; + } else { + delta_theta = away(-w_in->z, vel_limit->z, theta_goal.z, acc_limit->z, delta_t, &w_out->z, no_overshoot); + theta_end.z = -delta_theta; + w_out->z = -w_out->z; + } + } + + // the amount of rotation about each axis is determined in + // functions approach and away. first find the magnitude + // of the rotation and then normalize the axis + rot_axis = theta_end; + Assert(is_valid_vec(&rot_axis)); + Assert(vm_vec_mag(&rot_axis) > 0); + + // normalize rotation axis and determine total rotation angle + theta = vm_vec_normalize(&rot_axis); + + // arrived at goal? + if (theta_end.x == theta_goal.x && theta_end.y == theta_goal.y && theta_end.z == theta_goal.z) { + *next_orient = *goal_orient; + } else { + // otherwise rotate to better position + vm_quaternion_rotate(&Mtemp1, theta, &rot_axis); + Assert(is_valid_matrix(&Mtemp1)); + vm_matrix_x_matrix(next_orient, curr_orient, &Mtemp1); + vm_orthogonalize_matrix(next_orient); + } +} // end matrix_interpolate + + +// -------------------------------------------------------------------------------------- + + +void get_camera_limits(matrix *start_camera, matrix *end_camera, float time, vector *acc_max, vector *w_max) +{ + matrix temp, rot_matrix; + float theta; + vector rot_axis; + vector angle; + + // determine the necessary rotation matrix + vm_copy_transpose(&temp, start_camera); + vm_matrix_x_matrix(&rot_matrix, &temp, end_camera); + vm_orthogonalize_matrix(&rot_matrix); + + // determine the rotation axis and angle + vm_matrix_to_rot_axis_and_angle(&rot_matrix, &theta, &rot_axis); + + // find the rotation about each axis + angle.x = theta * rot_axis.x; + angle.y = theta * rot_axis.y; + angle.z = theta * rot_axis.z; + + // allow for 0 time input + if (time <= 1e-5f) { + vm_vec_make(acc_max, 0.0f, 0.0f, 0.0f); + vm_vec_make(w_max, 0.0f, 0.0f, 0.0f); + } else { + + // find acceleration limit using (theta/2) takes (time/2) + // and using const accel theta = 1/2 acc * time^2 + acc_max->x = 4.0f * (float)fl_abs(angle.x) / (time * time); + acc_max->y = 4.0f * (float)fl_abs(angle.y) / (time * time); + acc_max->z = 4.0f * (float)fl_abs(angle.z) / (time * time); + + // find angular velocity limits + // w_max = acc_max * time / 2 + w_max->x = acc_max->x * time / 2.0f; + w_max->y = acc_max->y * time / 2.0f; + w_max->z = acc_max->z * time / 2.0f; + } +} + +// --------------------------------------------------------------------------------------------- +// +// inputs: goal_orient => goal orientation matrix +// orient => current orientation matrix (with current forward vector) +// w_in => current input angular velocity +// delta_t => time to move toward goal +// next_orient => the orientation matrix at time delta_t (with current forward vector) +// NOTE: this does not include any rotation about z (bank) +// w_out => the angular velocity of the ship at delta_t +// vel_limit => maximum rotational speed +// acc_limit => maximum rotational speed +// +// function moves the forward vector toward the goal forward vector taking account of anglular +// momentum (velocity) Attempt to try to move bank by goal delta_bank. Rotational velocity +// on x/y is rotated with bank, giving smoother motion. +void vm_fvec_matrix_interpolate(matrix *goal_orient, matrix *orient, vector *w_in, float delta_t, matrix *next_orient, + vector *w_out, vector *vel_limit, vector *acc_limit, int no_overshoot) +{ + matrix Mtemp1; // temporary matrix + matrix M_intermed; // intermediate matrix after xy rotation + vector local_rot_axis; // vector indicating direction of rotation axis (local coords) + vector rot_axis; // vector indicating direction of rotation axis (world coords) + vector theta_goal; // desired angular position at the end of the time interval + vector theta_end; // actual angular position at the end of the time interval + float theta; // magnitude of rotation about the rotation axis + float bank; // magnitude of rotation about the forward axis + int no_bank; // flag set if there is no bank for the object + vector vtemp; // temp angular velocity before rotation about z + float z_dotprod; // dotprod of orient->fvec and goal_orient->fvec + float r_dotprod; // dotprod of orient->rvec and goal_orient->rvec + float delta_bank; + + // FIND XY ROTATION NEEDED FOR GOAL + // rotation vector is (current fvec) orient->fvec x goal_f + // magnitude = asin ( magnitude of crossprod ) + vm_vec_crossprod ( &rot_axis, &orient->fvec, &goal_orient->fvec ); + + float t = vm_vec_mag(&rot_axis); + if (t > 1.0f) + t = 1.0f; + + z_dotprod = vm_vec_dotprod ( &orient->fvec, &goal_orient->fvec ); + + if ( t < SMALLER_NUM ) { + if ( z_dotprod > 0.0f ) + theta = 0.0f; + else { // the forward vector is pointing exactly opposite of goal + // arbitrarily choose the x axis to rotate around until t becomes large enough + theta = PI; + rot_axis = orient->rvec; + } + } else { + theta = (float) asin ( t ); + vm_vec_scale ( &rot_axis, 1/t ); + if ( z_dotprod < 0.0f ) + theta = PI - theta; + } + + // rotate rot_axis into ship reference frame + vm_vec_rotate ( &local_rot_axis, &rot_axis, orient ); + + // find theta to goal + vm_vec_copy_scale(&theta_goal, &local_rot_axis, theta); + Assert ( fl_abs (theta_goal.z) < 0.001f ); // check for proper rotation + + theta_end = vmd_zero_vector; + float delta_theta; + + // find rotation about x + if (theta_goal.x > 0) { + if (w_in->x >= 0) { + delta_theta = approach(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = delta_theta; + } else { // w_in->x < 0 + delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = delta_theta; + } + } else if (theta_goal.x < 0) { + if (w_in->x <= 0) { + delta_theta = approach(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = -delta_theta; + w_out->x = -w_out->x; + } else { // w_in->x > 0 + delta_theta = away(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = -delta_theta; + w_out->x = -w_out->x; + } + } else { // theta_goal == 0 + if (w_in->x < 0) { + delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = delta_theta; + } else { + delta_theta = away(-w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = -delta_theta; + w_out->x = -w_out->x; + } + } + + // find rotation about y + if (theta_goal.y > 0) { + if (w_in->y >= 0) { + delta_theta = approach(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = delta_theta; + } else { // w_in->y < 0 + delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = delta_theta; + } + } else if (theta_goal.y < 0) { + if (w_in->y <= 0) { + delta_theta = approach(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = -delta_theta; + w_out->y = -w_out->y; + } else { // w_in->y > 0 + delta_theta = away(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = -delta_theta; + w_out->y = -w_out->y; + } + } else { // theta_goal == 0 + if (w_in->y < 0) { + delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = delta_theta; + } else { + delta_theta = away(-w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = -delta_theta; + w_out->y = -w_out->y; + } + } + + // FIND Z ROTATON MATRIX + theta_end.z = 0.0f; + rot_axis = theta_end; + Assert(is_valid_vec(&rot_axis)); + + // normalize rotation axis and determine total rotation angle + theta = vm_vec_mag(&rot_axis); + if (theta < SMALL_NUM) { + theta = 0.0f; + M_intermed = *orient; + } else { + vm_vec_scale ( &rot_axis, 1/theta ); + vm_quaternion_rotate ( &Mtemp1, theta, &rot_axis ); + Assert(is_valid_matrix(&Mtemp1)); + vm_matrix_x_matrix ( &M_intermed, orient, &Mtemp1 ); + Assert(is_valid_matrix(&M_intermed)); + } + + + // FIND ROTATION ABOUT Z (IF ANY) + // no rotation if delta_bank and w_in both 0 or rotational acc in forward is 0 + no_bank = ( acc_limit->z == 0.0f && vel_limit->z == 0.0f ); + + if ( no_bank ) { // no rotation on z, so we're done (no rotation of w) + *next_orient = M_intermed; + vm_orthogonalize_matrix ( next_orient ); + return; + } else { + // calculate delta_bank using orient->rvec, goal_orient->rvec + // + vm_vec_crossprod ( &rot_axis, &orient->rvec, &goal_orient->rvec ); + + t = vm_vec_mag(&rot_axis); + if (t > 1.0f) + t = 1.0f; + + r_dotprod = vm_vec_dotprod ( &orient->rvec, &goal_orient->rvec ); + + if ( t < SMALLER_NUM ) { + if ( r_dotprod > 0.0f ) + theta = 0.0f; + else { // the right vector is pointing exactly opposite of goal, so rotate 180 on z + theta = PI; + rot_axis = orient->fvec; + } + } else { + theta = (float) asin ( t ); + vm_vec_scale ( &rot_axis, 1/t ); + if ( z_dotprod < 0.0f ) + theta = PI - theta; + } + + // rotate rot_axis into ship reference frame + vm_vec_rotate ( &local_rot_axis, &rot_axis, orient ); + + // find theta.z to goal + delta_bank = local_rot_axis.z * theta; + Assert( fl_abs (local_rot_axis.x) < 0.001f ); // check for proper rotation + bank = 0.0f; + + // end calculate delta_bank + // find rotation about z + if (delta_bank > 0) { + if (w_in->z >= 0) { + delta_theta = approach(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = delta_theta; + } else { // w_in->z < 0 + delta_theta = away(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = delta_theta; + } + } else if (delta_bank < 0) { + if (w_in->z <= 0) { + delta_theta = approach(-w_in->z, vel_limit->z, -delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = -delta_theta; + w_out->z = -w_out->z; + } else { // w_in->z > 0 + delta_theta = away(-w_in->z, vel_limit->z, -delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = -delta_theta; + w_out->z = -w_out->z; + } + } else { // theta_goal == 0 + if (w_in->z < 0) { + delta_theta = away(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = delta_theta; + } else { + delta_theta = away(-w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = -delta_theta; + w_out->z = -w_out->z; + } + } + + if ( fl_abs (bank) < SMALL_NUM ) + { + *next_orient = M_intermed; + vm_orthogonalize_matrix ( next_orient ); + } else { + + rotate_z ( &Mtemp1, bank ); + vtemp = *w_out; + vm_vec_rotate ( w_out, &vtemp, &Mtemp1 ); + vm_matrix_x_matrix ( next_orient, &M_intermed, &Mtemp1 ); + Assert(is_valid_matrix(next_orient)); + vm_orthogonalize_matrix ( next_orient ); + } + } +} // end vm_fvec_matrix_interpolate + + +// --------------------------------------------------------------------------------------------- +// +// inputs: goal_f => goal forward vector +// orient => current orientation matrix (with current forward vector) +// w_in => current input angular velocity +// delta_t => time to move toward goal +// delta_bank => desired change in bank in degrees +// next_orient => the orientation matrix at time delta_t (with current forward vector) +// NOTE: this does not include any rotation about z (bank) +// w_out => the angular velocity of the ship at delta_t +// vel_limit => maximum rotational speed +// acc_limit => maximum rotational speed +// +// function moves the forward vector toward the goal forward vector taking account of anglular +// momentum (velocity) Attempt to try to move bank by goal delta_bank. Rotational velocity +// on x/y is rotated with bank, giving smoother motion. +void vm_forward_interpolate(vector *goal_f, matrix *orient, vector *w_in, float delta_t, float delta_bank, + matrix *next_orient, vector *w_out, vector *vel_limit, vector *acc_limit, int no_overshoot) +{ + matrix Mtemp1; // temporary matrix + vector local_rot_axis; // vector indicating direction of rotation axis (local coords) + vector rot_axis; // vector indicating direction of rotation axis (world coords) + vector theta_goal; // desired angular position at the end of the time interval + vector theta_end; // actual angular position at the end of the time interval + float theta; // magnitude of rotation about the rotation axis + float bank; // magnitude of rotation about the forward axis + int no_bank; // flag set if there is no bank for the object + vector vtemp; // + float z_dotprod; + + // FIND ROTATION NEEDED FOR GOAL + // rotation vector is (current fvec) orient->fvec x goal_f + // magnitude = asin ( magnitude of crossprod ) + vm_vec_crossprod( &rot_axis, &orient->fvec, goal_f ); + + float t = vm_vec_mag(&rot_axis); + if (t > 1.0f) + t = 1.0f; + + z_dotprod = vm_vec_dotprod( &orient->fvec, goal_f ); + + if ( t < SMALLER_NUM ) { + if ( z_dotprod > 0.0f ) + theta = 0.0f; + else { // the forward vector is pointing exactly opposite of goal + // arbitrarily choose the x axis to rotate around until t becomes large enough + theta = PI; + rot_axis = orient->rvec; + } + } else { + theta = (float) asin( t ); + vm_vec_scale ( &rot_axis, 1/t ); + if ( z_dotprod < 0.0f ) + theta = PI - theta; + } + + // rotate rot_axis into ship reference frame + vm_vec_rotate( &local_rot_axis, &rot_axis, orient ); + + // find theta to goal + vm_vec_copy_scale(&theta_goal, &local_rot_axis, theta); + Assert(fl_abs(theta_goal.z) < 0.001f); // check for proper rotation + + theta_end = vmd_zero_vector; + float delta_theta; + + // find rotation about x + if (theta_goal.x > 0) { + if (w_in->x >= 0) { + delta_theta = approach(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = delta_theta; + } else { // w_in->x < 0 + delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = delta_theta; + } + } else if (theta_goal.x < 0) { + if (w_in->x <= 0) { + delta_theta = approach(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = -delta_theta; + w_out->x = -w_out->x; + } else { // w_in->x > 0 + delta_theta = away(-w_in->x, vel_limit->x, -theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = -delta_theta; + w_out->x = -w_out->x; + } + } else { // theta_goal == 0 + if (w_in->x < 0) { + delta_theta = away(w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = delta_theta; + } else { + delta_theta = away(-w_in->x, vel_limit->x, theta_goal.x, acc_limit->x, delta_t, &w_out->x, no_overshoot); + theta_end.x = -delta_theta; + w_out->x = -w_out->x; + } + } + + // find rotation about y + if (theta_goal.y > 0) { + if (w_in->y >= 0) { + delta_theta = approach(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = delta_theta; + } else { // w_in->y < 0 + delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = delta_theta; + } + } else if (theta_goal.y < 0) { + if (w_in->y <= 0) { + delta_theta = approach(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = -delta_theta; + w_out->y = -w_out->y; + } else { // w_in->y > 0 + delta_theta = away(-w_in->y, vel_limit->y, -theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = -delta_theta; + w_out->y = -w_out->y; + } + } else { // theta_goal == 0 + if (w_in->y < 0) { + delta_theta = away(w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = delta_theta; + } else { + delta_theta = away(-w_in->y, vel_limit->y, theta_goal.y, acc_limit->y, delta_t, &w_out->y, no_overshoot); + theta_end.y = -delta_theta; + w_out->y = -w_out->y; + } + } + + // no rotation if delta_bank and w_in both 0 or rotational acc in forward is 0 + no_bank = ( delta_bank == 0.0f && vel_limit->z == 0.0f && acc_limit->z == 0.0f ); + + // do rotation about z + bank = 0.0f; + if ( !no_bank ) { + // convert delta_bank to radians + delta_bank *= (float) CONVERT_RADIANS; + + // find rotation about z + if (delta_bank > 0) { + if (w_in->z >= 0) { + delta_theta = approach(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = delta_theta; + } else { // w_in->z < 0 + delta_theta = away(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = delta_theta; + } + } else if (delta_bank < 0) { + if (w_in->z <= 0) { + delta_theta = approach(-w_in->z, vel_limit->z, -delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = -delta_theta; + w_out->z = -w_out->z; + } else { // w_in->z > 0 + delta_theta = away(-w_in->z, vel_limit->z, -delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = -delta_theta; + w_out->z = -w_out->z; + } + } else { // theta_goal == 0 + if (w_in->z < 0) { + delta_theta = away(w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = delta_theta; + } else { + delta_theta = away(-w_in->z, vel_limit->z, delta_bank, acc_limit->z, delta_t, &w_out->z, no_overshoot); + bank = -delta_theta; + w_out->z = -w_out->z; + } + } + } + + // the amount of rotation about each axis is determined in + // functions approach and away. first find the magnitude + // of the rotation and then normalize the axis (ship coords) + theta_end.z = bank; + rot_axis = theta_end; + + // normalize rotation axis and determine total rotation angle + theta = vm_vec_mag(&rot_axis); + if ( theta > SMALL_NUM ) + vm_vec_scale( &rot_axis, 1/theta ); + + if ( theta < SMALL_NUM ) { + *next_orient = *orient; + return; + } else { + vm_quaternion_rotate( &Mtemp1, theta, &rot_axis ); + vm_matrix_x_matrix( next_orient, orient, &Mtemp1 ); + Assert(is_valid_matrix(next_orient)); + vtemp = *w_out; + vm_vec_rotate( w_out, &vtemp, &Mtemp1 ); + } +} // end vm_forward_interpolate + +// ------------------------------------------------------------------------------------ +// vm_find_bounding_sphere() +// +// Calculate a bounding sphere for a set of points. +// +// input: pnts => array of world positions +// num_pnts => number of points inside pnts array +// center => OUTPUT PARAMETER: contains world pos of bounding sphere center +// radius => OUTPUT PARAMETER: continas radius of bounding sphere +// +#define BIGNUMBER 100000000.0f +void vm_find_bounding_sphere(vector *pnts, int num_pnts, vector *center, float *radius) +{ + int i; + float rad, rad_sq, xspan, yspan, zspan, maxspan; + float old_to_p, old_to_p_sq, old_to_new; + vector diff, xmin, xmax, ymin, ymax, zmin, zmax, dia1, dia2, *p; + + xmin = vmd_zero_vector; + ymin = vmd_zero_vector; + zmin = vmd_zero_vector; + xmax = vmd_zero_vector; + ymax = vmd_zero_vector; + zmax = vmd_zero_vector; + xmin.x = ymin.y = zmin.z = BIGNUMBER; + xmax.x = ymax.y = zmax.z = -BIGNUMBER; + + for ( i = 0; i < num_pnts; i++ ) { + p = &pnts[i]; + if ( p->x < xmin.x ) + xmin = *p; + if ( p->x > xmax.x ) + xmax = *p; + if ( p->y < ymin.y ) + ymin = *p; + if ( p->y > ymax.y ) + ymax = *p; + if ( p->z < zmin.z ) + zmin = *p; + if ( p->z > zmax.z ) + zmax = *p; + } + + // find distance between two min and max points (squared) + vm_vec_sub(&diff, &xmax, &xmin); + xspan = vm_vec_mag_squared(&diff); + + vm_vec_sub(&diff, &ymax, &ymin); + yspan = vm_vec_mag_squared(&diff); + + vm_vec_sub(&diff, &zmax, &zmin); + zspan = vm_vec_mag_squared(&diff); + + dia1 = xmin; + dia2 = xmax; + maxspan = xspan; + if ( yspan > maxspan ) { + maxspan = yspan; + dia1 = ymin; + dia2 = ymax; + } + if ( zspan > maxspan ) { + maxspan = yspan; + dia1 = zmin; + dia2 = zmax; + } + + // calc inital center + vm_vec_add(center, &dia1, &dia2); + vm_vec_scale(center, 0.5f); + + vm_vec_sub(&diff, &dia2, center); + rad_sq = vm_vec_mag_squared(&diff); + rad = fl_sqrt(rad_sq); + Assert( !_isnan(rad) ); + + // second pass + for ( i = 0; i < num_pnts; i++ ) { + p = &pnts[i]; + vm_vec_sub(&diff, p, center); + old_to_p_sq = vm_vec_mag_squared(&diff); + if ( old_to_p_sq > rad_sq ) { + old_to_p = fl_sqrt(old_to_p_sq); + // calc radius of new sphere + rad = (rad + old_to_p) / 2.0f; + rad_sq = rad * rad; + old_to_new = old_to_p - rad; + // calc new center of sphere + center->x = (rad*center->x + old_to_new*p->x) / old_to_p; + center->y = (rad*center->y + old_to_new*p->y) / old_to_p; + center->z = (rad*center->z + old_to_new*p->z) / old_to_p; + nprintf(("Alan", "New sphere: cen,rad = %f %f %f %f\n", center->x, center->y, center->z, rad)); + } + } + + *radius = rad; +} + +// ---------------------------------------------------------------------------- +// vm_rotate_vec_to_body() +// +// rotates a vector from world coordinates to body coordinates +// +// inputs: body_vec => vector in body coordinates +// world_vec => vector in world coordinates +// orient => orientation matrix +// +vector* vm_rotate_vec_to_body(vector *body_vec, vector *world_vec, matrix *orient) +{ + return vm_vec_unrotate(body_vec, world_vec, orient); +} + + +// ---------------------------------------------------------------------------- +// vm_rotate_vec_to_world() +// +// rotates a vector from body coordinates to world coordinates +// +// inputs world_vec => vector in world coordinates +// body_vec => vector in body coordinates +// orient => orientation matrix +// +vector* vm_rotate_vec_to_world(vector *world_vec, vector *body_vec, matrix *orient) +{ + return vm_vec_rotate(world_vec, body_vec, orient); +} + + +// ---------------------------------------------------------------------------- +// vm_estimate_next_orientation() +// +// given a last orientation and current orientation, estimate the next orientation +// +// inputs: last_orient => last orientation matrix +// current_orient => current orientation matrix +// next_orient => next orientation matrix [the result] +// +void vm_estimate_next_orientation(matrix *last_orient, matrix *current_orient, matrix *next_orient) +{ + // R L = C => R = C (L)^-1 + // N = R C => N = C (L)^-1 C + + matrix Mtemp; + matrix Rot_matrix; + vm_copy_transpose_matrix(&Mtemp, last_orient); // Mtemp = (L)^-1 + vm_matrix_x_matrix(&Rot_matrix, &Mtemp, current_orient); // R = C Mtemp1 + vm_matrix_x_matrix(next_orient, current_orient, &Rot_matrix); +} + +// Return true if all elements of *vec are legal, that is, not a NAN. +int is_valid_vec(vector *vec) +{ + return !_isnan(vec->x) && !_isnan(vec->y) && !_isnan(vec->z); +} + +// Return true if all elements of *m are legal, that is, not a NAN. +int is_valid_matrix(matrix *m) +{ + return is_valid_vec(&m->fvec) && is_valid_vec(&m->uvec) && is_valid_vec(&m->rvec); +} + +// interpolate between 2 vectors. t goes from 0.0 to 1.0. at +void vm_vec_interp_constant(vector *out, vector *v0, vector *v1, float t) +{ + vector cross; + float total_ang; + + // get the cross-product of the 2 vectors + vm_vec_crossprod(&cross, v0, v1); + vm_vec_normalize(&cross); + + // get the total angle between the 2 vectors + total_ang = -(float)acos(vm_vec_dot(v0, v1)); + + // rotate around the cross product vector by the appropriate angle + vm_rot_point_around_line(out, v0, t * total_ang, &vmd_zero_vector, &cross); +} + +// randomly perturb a vector around a given (normalized vector) or optional orientation matrix +void vm_vec_random_cone(vector *out, vector *in, float max_angle, matrix *orient) +{ + vector t1, t2; + matrix *rot; + matrix m; + + // get an orientation matrix + if(orient != NULL){ + rot = orient; + } else { + vm_vector_2_matrix(&m, in, NULL, NULL); + rot = &m; + } + + // axis 1 + vm_rot_point_around_line(&t1, in, fl_radian(frand_range(-max_angle, max_angle)), &vmd_zero_vector, &rot->fvec); + + // axis 2 + vm_rot_point_around_line(&t2, &t1, fl_radian(frand_range(-max_angle, max_angle)), &vmd_zero_vector, &rot->rvec); + + // axis 3 + vm_rot_point_around_line(out, &t2, fl_radian(frand_range(-max_angle, max_angle)), &vmd_zero_vector, &rot->uvec); +} + +// given a start vector, an orientation and a radius, give a point on the plane of the circle +// if on_edge is 1, the point is on the very edge of the circle +void vm_vec_random_in_circle(vector *out, vector *in, matrix *orient, float radius, int on_edge) +{ + vector temp; + + // point somewhere in the plane + vm_vec_scale_add(&temp, in, &orient->rvec, on_edge ? radius : frand_range(0.0f, radius)); + + // rotate to a random point on the circle + vm_rot_point_around_line(out, &temp, fl_radian(frand_range(0.0f, 359.0f)), in, &orient->fvec); +} + +// find the nearest point on the line to p. if dist is non-NULL, it is filled in +// returns 0 if the point is inside the line segment, -1 if "before" the line segment and 1 ir "after" the line segment +int vm_vec_dist_to_line(vector *p, vector *l0, vector *l1, vector *nearest, float *dist) +{ + vector a, b, c; + float b_mag, comp; + +#ifndef NDEBUG + if(vm_vec_same(l0, l1)){ + *nearest = vmd_zero_vector; + return -1; + } +#endif + + // compb_a == a dot b / len(b) + vm_vec_sub(&a, p, l0); + vm_vec_sub(&b, l1, l0); + b_mag = vm_vec_copy_normalize(&c, &b); + + // calculate component + comp = vm_vec_dotprod(&a, &b) / b_mag; + + // stuff nearest + vm_vec_scale_add(nearest, l0, &c, comp); + + // maybe get the distance + if(dist != NULL){ + *dist = vm_vec_dist(nearest, p); + } + + // return the proper value + if(comp < 0.0f){ + return -1; // before the line + } else if(comp > b_mag){ + return 1; // after the line + } + return 0; // on the line +} \ No newline at end of file diff --git a/src/menuui/barracks.cpp b/src/menuui/barracks.cpp new file mode 100644 index 0000000..160ae0a --- /dev/null +++ b/src/menuui/barracks.cpp @@ -0,0 +1,1663 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/Barracks.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C file for implementing barracks section + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 42 10/25/99 5:47p Jefff + * reassigned some xstr ids + * + * 41 9/14/99 11:01p Jefff + * stats area coord fix + * + * 40 9/09/99 11:24a Jefff + * + * 39 9/02/99 11:26a Jefff + * fixed incorrect pilot list dimensions + * + * 38 8/17/99 2:24p Dave + * Fixed wacky squad color stuff. + * + * 37 8/16/99 4:27p Jefff + * fix button position on squad change error popup + * + * 36 8/11/99 3:58p Jefff + * fixed player list box coord bug + * + * 35 8/05/99 2:46p Jefff + * added popup to the disabled squad selection buttons in single player + * + * 34 8/02/99 11:02p Dave + * Fixed up squad filename and squad name copying when converting a pilot + * between single and multiplayer. + * + * 33 7/27/99 7:17p Jefff + * Replaced some art text with XSTR() text. + * + * 32 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 31 6/29/99 7:39p Dave + * Lots of small bug fixes. + * + * 30 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 29 6/11/99 11:13a Dave + * last minute changes before press tour build. + * + * 28 4/25/99 7:43p Dave + * Misc small bug fixes. Made sun draw properly. + * + * 27 4/25/99 3:02p Dave + * Build defines for the E3 build. + * + * 26 3/24/99 4:05p Dave + * Put in support for assigning the player to a specific squadron with a + * specific logo. Preliminary work for doing pos/orient checksumming in + * multiplayer to reduce bandwidth. + * + * 25 2/25/99 4:19p Dave + * Added multiplayer_beta defines. Added cd_check define. Fixed a few + * release build warnings. Added more data to the squad war request and + * response packets. + * + * 24 2/02/99 12:09p Neilk + * Fixed text coords, centered pilot and squad pics in 1024x768, added vss + * comment template + * + * $NoKeywords: $ + */ + +#include "barracks.h" +#include "managepilot.h" +#include "ui.h" +#include "font.h" +#include "key.h" +#include "gamesnd.h" +#include "osapi.h" +#include "popup.h" +#include "playermenu.h" +#include "gamesequence.h" +#include "contexthelp.h" +#include "2d.h" +#include "ctype.h" +#include "freespace.h" +#include "systemvars.h" +#include "bmpman.h" +#include "mouse.h" +#include "osregistry.h" +#include "alphacolors.h" + +void delete_pilot_file( char *pilot_name, int single ); // manage_pilot.cpp + +// stats defines +#define NUM_STAT_LINES 85 +#define STAT_COLUMN1_W 40 +#define STAT_COLUMN2_W 10 + +static int Stat_column1_w[GR_NUM_RESOLUTIONS] = +{ + 40, // GR_640 + 40 // GR_1024 +}; + +static int Stat_column2_w[GR_NUM_RESOLUTIONS] = +{ + 10, // GR_640 + 10 // GR_1024 +}; + +// constants for coordinate lookup +#define BARRACKS_X_COORD 0 +#define BARRACKS_Y_COORD 1 +#define BARRACKS_W_COORD 2 +#define BARRACKS_H_COORD 3 + +// area ints + +// pilot selection field +static int Barracks_list_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 42, 34, 400, 90 + }, + { // GR_1024 + 45, 51, 646, 144 + } +}; + +// pilot stats field +static int Barracks_stats_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 32, 212, 240, 250 + }, + { // GR_1024 + 42, 351, 240, 400 + } +}; + +static int Barracks_stats2_coords[GR_NUM_RESOLUTIONS][3] = { + { // GR_640 + 276, 212, 81 // X2, , W2 + }, + { // GR_1024 + 286, 351, 81 // X2, , W2 + } +}; + +// pilot picture field +static int Barracks_image_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 461, 23, 160, 120 + }, + { // GR_1024 + 782, 58, 160, 120 + } +}; + +// pilot picture # of # location +static int Barracks_image_number_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 461, 145 + }, + { // GR_1024 + 732, 239 + } +}; + +// pilot squad logo field +int Barracks_squad_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 495, 177, 128, 128 + }, + { // GR_1024 + 829, 323, 128, 128 + } +}; + +// pilot squad # of # location +int Barracks_squad_number_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 492, 307 + }, + { // GR_1024 + 794, 500 + } +}; + +// button defines +#define BARRACKS_NUM_BUTTONS 19 + +// pilot selection buttons +#define B_PILOT_CREATE_BOTTON 0 // B_PILOT_CREATE_BOTTON +#define B_PILOT_SCROLL_UP_BUTTON 1 // B_PILOT_SCROLL_UP_BUTTON +#define B_PILOT_SCROLL_DOWN_BUTTON 2 // B_PILOT_SCROLL_DOWN_BUTTON +#define B_PILOT_DELETE_BUTTON 11 // B_PILOT_B_PILOT_DELETE_BUTTON +#define B_PILOT_SET_ACTIVE_BUTTON 12 // B_PILOT_B_PILOT_SET_ACTIVE_BUTTON +#define B_PILOT_CLONE_BUTTON 13 // B_PILOT_B_PILOT_CLONE_BUTTON +#define B_PILOT_SINGLE_MODE_BUTTON 14 // B_PILOT_SINGLE_MODE_BUTTON +#define B_PILOT_MULTI_MODE_BUTTON 15 // B_PILOT_MULTI_MODE_BUTTON +#define B_PILOT_CONVERT_BUTTON 16 // B_PILOT_B_PILOT_CONVERT_BUTTON + +// squad logo picture buttons +#define B_SQUAD_PREV_BUTTON 17 +#define B_SQUAD_NEXT_BUTTON 18 + +// pilot picture buttons +#define B_PIC_PREV_PILOT_BUTTON 3 // B_PILOT_B_PIC_PREV_PILOT_BUTTON +#define B_PIC_NEXT_PILOT_BUTTON 4 // B_PILOT_B_PIC_NEXT_PILOT_BUTTON + +// pilot stat buttons +#define B_STATS_MEDAL_BUTTON 8 // B_STATS_MEDAL_BUTTON +#define B_STATS_SCROLL_DOWN_BUTTON 9 // B_STATS_SCROLL_DOWN_BUTTON +#define B_STATS_SCROLL_UP_BUTTON 10 // B_STATS_SCROLL_UP_BUTTON + +// general buttons +#define B_ACCEPT_BUTTON 5 // B_B_ACCEPT_BUTTON +#define B_HELP_BUTTON 6 // B_B_HELP_BUTTON +#define B_OPTION_BUTTON 7 // B_OPTION_BUTTON + +//XSTR:OFF +// bitmaps defs +static char *Barracks_bitmap_fname[GR_NUM_RESOLUTIONS] = { + "Barracks", // GR_640 + "2_Barracks" // GR_1024 +}; + +static char *Barracks_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = { + "Barracks-M", // GR_640 + "2_Barracks-M" // GR_1024 +}; + +//XSTR:ON + +#define BARRACKS_IMAGE_NOT_LOADED -2 + +struct barracks_bitmaps { + char *filename; + int x, y; + int b; +}; + +struct barracks_buttons { + char *filename; + int x, y; + int text_x, text_y; // this is where the text label is + int hotspot; + int repeat; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + barracks_buttons(char *name, int x1, int y1, int x2, int y2, int h, int r = 0) : filename(name), x(x1), y(y1), text_x(x2), text_y(y2), hotspot(h), repeat(r) {} +}; + +static int Background_bitmap = -1; +static UI_WINDOW Ui_window; +static UI_BUTTON List_region; +static UI_INPUTBOX Inputbox; + +static barracks_buttons Buttons[GR_NUM_RESOLUTIONS][BARRACKS_NUM_BUTTONS] = { +//XSTR:OFF + { // GR_640 + barracks_buttons("BAB_00", 8, 122, 11, 157, 0), + barracks_buttons("BAB_01", 323, 130, 0, 0, 1, 1), + barracks_buttons("BAB_02", 350, 130, 0, 0, 2, 1), + barracks_buttons("BAB_03", 559, 143, 0, 0, 3, 1), + barracks_buttons("BAB_04", 598, 143, 0, 0, 4, 1), + barracks_buttons("BAB_05", 571, 425, 578, 413, 5), + barracks_buttons("BAB_06", 533, 425, 500, 438, 6), + barracks_buttons("BAB_07", 533, 453, 481, 465, 7), + barracks_buttons("BAB_08", 361, 425, 401, 465, 8), + barracks_buttons("BAB_09", 0, 267, 0, 0, 9, 1), + barracks_buttons("BAB_10", 0, 224, 0, 0, 10,1), + barracks_buttons("BAB_11", 120, 122, 123, 157, 11), + barracks_buttons("BAB_12", 376, 124, 378, 163, 12), + barracks_buttons("BAB_13", 66, 122, 69, 157, 13), + barracks_buttons("BAB_14", 323, 0, 324, 25, 14), + barracks_buttons("BAB_15", 372, 0, 374, 25, 15), + barracks_buttons("BAB_16", 180, 122, 182, 157, 16), + barracks_buttons("BAB_17", 559, 306, 0, 0, 17), + barracks_buttons("BAB_18", 598, 306, 0, 0, 18) + }, + { // GR_1024 + barracks_buttons("2_BAB_00", 14, 196, 35, 252, 0), + barracks_buttons("2_BAB_01", 518, 209, 0, 0, 1, 1), + barracks_buttons("2_BAB_02", 561, 209, 0, 0, 2, 1), + barracks_buttons("2_BAB_03", 896, 229, 0, 0, 3, 1), + barracks_buttons("2_BAB_04", 958, 229, 0, 0, 4, 1), + barracks_buttons("2_BAB_05", 914, 681, 932, 665, 5), + barracks_buttons("2_BAB_06", 854, 681, 800, 704, 6), + barracks_buttons("2_BAB_07", 854, 724, 778, 743, 7), + barracks_buttons("2_BAB_08", 579, 681, 641, 743, 8), + barracks_buttons("2_BAB_09", 0, 428, 0, 0, 9, 1), + barracks_buttons("2_BAB_10", 0, 360, 0, 0, 10,1), + barracks_buttons("2_BAB_11", 193, 196, 214, 252, 11), + barracks_buttons("2_BAB_12", 602, 200, 617, 262, 12), + barracks_buttons("2_BAB_13", 107, 196, 128, 252, 13), + barracks_buttons("2_BAB_14", 517, 0, 532, 40, 14), + barracks_buttons("2_BAB_15", 596, 0, 614, 40, 15), + barracks_buttons("2_BAB_16", 289, 196, 309, 252, 16), + barracks_buttons("2_BAB_17", 896, 491, 0, 0, 17), + barracks_buttons("2_BAB_18", 958, 491, 0, 0, 18) + } +//XSTR:ON +}; + + +// FIXME add to strings.tbl, set correct coords +#define BARRACKS_NUM_TEXT 2 +UI_XSTR Barracks_text[GR_NUM_RESOLUTIONS][BARRACKS_NUM_TEXT] = { + { + // GR_640 + { "Barracks", 1434, 17, 7, UI_XSTR_COLOR_GREEN, -1, NULL }, + { "Pilot Stats", 1435, 17, 180, UI_XSTR_COLOR_GREEN, -1, NULL } + }, + { + // GR_1024 + { "Barracks", 1434, 27, 11, UI_XSTR_COLOR_GREEN, -1, NULL }, + { "Pilot Stats", 1435, 27, 288, UI_XSTR_COLOR_GREEN, -1, NULL } + } +}; + + +static int Num_stat_lines; +static char Stat_labels[NUM_STAT_LINES][STAT_COLUMN1_W]; +static char Stats[NUM_STAT_LINES][STAT_COLUMN2_W]; + +extern int Player_sel_mode; + +static player *Cur_pilot; +static int Num_pilots; +static int Selected_line; +static char Pilots_arr[MAX_PILOTS][MAX_FILENAME_LEN]; +static char *Pilots[MAX_PILOTS]; +static int Pic_number; +static int Pic_squad_number; +static bool Barracks_callsign_enter_mode; +static int Pilot_ranks[MAX_PILOTS]; +static int List_scroll_offset; +static int Stats_scroll_offset; +static int Clone_flag; +static int Pilot_images[MAX_PILOT_IMAGES]; +static int Pilot_squad_images[MAX_PILOT_IMAGES]; +static int Rank_pips_bitmaps; +static int Rank_pips_count; + +void barracks_squad_change_popup(); + + +// ------------------------------------------------------------------------------------------------- +// +// BARRACKS screen +// + +#define STRCPY1(a, b) do { \ + Assert(strlen(b) < STAT_COLUMN1_W); \ + strcpy(a, b); \ +} while (0) + +void barracks_init_stats(scoring_struct *stats) +{ + int i; + float f; + + Num_stat_lines = 0; + + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "*All Time Stats", 50)); + Stats[Num_stat_lines][0] = 0; + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + Stat_labels[Num_stat_lines][0] = 0; + Stats[Num_stat_lines][0] = 0; + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Primary weapon shots:", 51)); + sprintf(Stats[Num_stat_lines], "%d", stats->p_shots_fired); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Primary weapon hits:", 52)); + sprintf(Stats[Num_stat_lines], "%d", stats->p_shots_hit); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Primary friendly hits:", 53)); + sprintf(Stats[Num_stat_lines], "%d", stats->p_bonehead_hits); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Primary hit %:", 54)); + if (stats->p_shots_fired > 0) { + f = (float) stats->p_shots_hit * 100.0f / (float) stats->p_shots_fired; + } else { + f = 0.0f; + } + sprintf(Stats[Num_stat_lines], XSTR( "%.1f%%", 55), f); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Primary friendly hit %:", 56)); + if (stats->p_bonehead_hits > 0) { + f = (float) stats->p_bonehead_hits * 100.0f / (float) stats->p_shots_fired; + } else { + f = 0.0f; + } + sprintf(Stats[Num_stat_lines], XSTR( "%.1f%%", 55), f); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + Stat_labels[Num_stat_lines][0] = 0; + Stats[Num_stat_lines][0] = 0; + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Secondary weapon shots:", 57)); + sprintf(Stats[Num_stat_lines], "%d", stats->s_shots_fired); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Secondary weapon hits:", 58)); + sprintf(Stats[Num_stat_lines], "%d", stats->s_shots_hit); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Secondary friendly hits:", 59)); + sprintf(Stats[Num_stat_lines], "%d", stats->s_bonehead_hits); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Secondary hit %:", 60)); + if (stats->s_shots_fired > 0) { + f = (float) stats->s_shots_hit * 100.0f / (float) stats->s_shots_fired; + } else { + f = 0.0f; + } + sprintf(Stats[Num_stat_lines], XSTR( "%.1f%%", 55), f); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Secondary friendly hit %:", 61)); + if (stats->s_bonehead_hits > 0) { + f = (float) stats->s_bonehead_hits * 100.0f / (float) stats->s_shots_fired; + } else { + f = 0.0f; + } + sprintf(Stats[Num_stat_lines], XSTR( "%.1f%%", 55), f); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + Stat_labels[Num_stat_lines][0] = 0; + Stats[Num_stat_lines][0] = 0; + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Total kills:", 62)); + sprintf(Stats[Num_stat_lines], "%d", stats->kill_count_ok); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "Assists:", 63)); + sprintf(Stats[Num_stat_lines], "%d", stats->assists); + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + Stat_labels[Num_stat_lines][0] = 0; + Stats[Num_stat_lines][0] = 0; + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + Stat_labels[Num_stat_lines][0] = 0; + Stats[Num_stat_lines][0] = 0; + Num_stat_lines++; + + STRCPY1(Stat_labels[Num_stat_lines], XSTR( "*Kills by Ship Type", 64)); + Stats[Num_stat_lines][0] = 0; + Num_stat_lines++; + + Assert(Num_stat_lines < NUM_STAT_LINES); + Stat_labels[Num_stat_lines][0] = 0; + Stats[Num_stat_lines][0] = 0; + Num_stat_lines++; + + for (i=0; ikills[i]) { + Assert(Num_stat_lines < NUM_STAT_LINES); + Assert(strlen(Ship_info[i].name) + 1 < STAT_COLUMN1_W); + sprintf(Stat_labels[Num_stat_lines], NOX("%s:"), Ship_info[i].name); + sprintf(Stats[Num_stat_lines], "%d", stats->kills[i]); + Num_stat_lines++; + } + } + + for (i=0; i 4) && !stricmp(str + flen - elen, ".pcx")) { + str[flen - elen] = '\0'; + } +} + +// new pilot name has focus, so update stats/pic to that pilot +int barracks_new_pilot_selected() +{ + char stripped[MAX_FILENAME_LEN+1] = ""; + + // save the previous pilot first, so changes to it are kept + if (strlen(Cur_pilot->callsign)) { + write_pilot_file(); + } + + // check if we have a valid pilot hilighted. If so, attempt to active it + if ((Num_pilots < 1) || (Selected_line < 0) || (Selected_line >= Num_pilots)) { + Cur_pilot->callsign[0] = 0; // this indicates no pilot active + return -1; + } + + if (read_pilot_file(Pilots[Selected_line], !Player_sel_mode, Cur_pilot)) { + Cur_pilot->callsign[0] = 0; // this indicates no pilot active + return -1; + } + + // init stuff to reflect new pilot + int i; + barracks_init_stats(&Cur_pilot->stats); + for (i=0; iimage_filename); + barracks_strip_pcx(stripped); + if (!stricmp(stripped, Pilot_image_names[i])) { + break; + } + } + Pic_number = i; + for ( i=0; isquad_filename); + barracks_strip_pcx(stripped); + if (!stricmp(stripped, Pilot_squad_image_names[i])) { + break; + } + } + Pic_squad_number = i; + + return 0; +} + +void barracks_set_callsign_enter_mode(bool set_callsign_enter_mode) +{ + // set global mode variable + Barracks_callsign_enter_mode = set_callsign_enter_mode; + + // disable/enable all buttons + for (int idx=0; idx= MAX_PILOTS) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // play sound for pilot creation + gamesnd_play_iface(SND_SCROLL); + + // only write pilot file if there is an active pilot + if (strlen(Player->callsign)) { + write_pilot_file(); + } + + // move other pilot names and ranks down to make room for the new one + int idx = Num_pilots; + Assert(Num_pilots >= 0); + while (idx--) { + strcpy(Pilots[idx + 1], Pilots[idx]); + Pilot_ranks[idx + 1] = Pilot_ranks[idx]; + } + + Selected_line = 0; + Num_pilots++; + Pilots[Selected_line][0] = 0; + Pilot_ranks[Selected_line] = 0; + List_scroll_offset = 0; + + // set mode to accept pilot name text + barracks_set_callsign_enter_mode(true); + // set focus to input box + Inputbox.set_focus(); + // set initial pilot name to "" + Inputbox.set_text(""); + // reset size of input box to only 1 line + Inputbox.update_dimensions(Barracks_list_coords[gr_screen.res][BARRACKS_X_COORD], Barracks_list_coords[gr_screen.res][BARRACKS_Y_COORD], Barracks_list_coords[gr_screen.res][BARRACKS_W_COORD], gr_get_font_height()); +} + +// exiting screen without canceling, so load in the new pilot selected here +int barracks_pilot_accepted() +{ + char str[CALLSIGN_LEN + 1]; + + // check if pilot active. If not, don't allow accept. + if (!Cur_pilot->callsign[0]){ + return -1; + } + + // set his image + player_set_squad_bitmap(Cur_pilot, Cur_pilot->squad_filename); + +// Skill_level = get_default_skill_level(); + + // MWA -- I think that we should be writing Cur_pilot here. + //write_pilot_file(!is_pilot_multi(Cur_pilot)); + write_pilot_file( Cur_pilot ); + + // when we store the LastPlayer key, we have to mark it as being single or multiplayer, so we know where to look for him + // (since we could have a single and a multiplayer pilot with the same callsign) + // we'll distinguish them by putting an M and the end of the multiplayer callsign and a P at the end of a single player + strcpy(str, Cur_pilot->callsign); + strcat(str, is_pilot_multi(Cur_pilot) ? NOX("M") : NOX("S")); + os_config_write_string( NULL, "LastPlayer", str ); + return 0; +} + +// scroll up barracks pilot list one line +void barracks_scroll_callsign_up() +{ + if (Selected_line > 0) { + Selected_line--; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + + if ((Selected_line >= 0) && (Selected_line < List_scroll_offset)) { + List_scroll_offset = Selected_line; + } +} + +// scroll down barracks pilot list one line +void barracks_scroll_callsign_down() +{ + if (Selected_line < Num_pilots - 1) { + Selected_line++; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + + // num_pilots_to_fill_height is the number of pilots that can fit in given height + int num_pilots_to_fill_height = Barracks_list_coords[gr_screen.res][BARRACKS_H_COORD] / gr_get_font_height(); + if (Selected_line >= List_scroll_offset + num_pilots_to_fill_height) { + List_scroll_offset++; + } +} + +// scroll up barracks stats list one line +void barracks_scroll_stats_up() +{ + if (Stats_scroll_offset > 0) { + Stats_scroll_offset--; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// scroll down barracks stats list one line +void barracks_scroll_stats_down() +{ + int font_height = gr_get_font_height(); + + if (Stats_scroll_offset + Barracks_stats_coords[gr_screen.res][BARRACKS_H_COORD] / font_height < Num_stat_lines) { + Stats_scroll_offset++; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// show previous pilot pic +void barracks_prev_pic() +{ + // check if no pilot images or no pilot selected + if ((Num_pilot_images == 0) || (Cur_pilot->callsign[0] == '\0')) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // reset pilot pic number + Pic_number--; + if (Pic_number < 0) { + Pic_number = Num_pilot_images - 1; + } + + // copy pilot pic filename into pilot struct + if ((Pic_number >= 0) && (Pic_number < Num_pilot_images)) { + strcpy(Cur_pilot->image_filename, Pilot_image_names[Pic_number]); + } + + // play scroll sound + gamesnd_play_iface(SND_SCROLL); +} + +// show next pilot pic +void barracks_next_pic() +{ + // check if no pilot images or no pilot selected + if ((Num_pilot_images == 0) || (Cur_pilot->callsign[0] == '\0')) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // reset pilot pic number + Pic_number++; + if (Pic_number >= Num_pilot_images){ + Pic_number = 0; + } + + // copy pilot pic filename into pilot struct + if ((Pic_number >= 0) && (Pic_number < Num_pilot_images)){ + strcpy(Cur_pilot->image_filename, Pilot_image_names[Pic_number]); + } + + // play scroll sound + gamesnd_play_iface(SND_SCROLL); +} + +// show previous squad pic +void barracks_prev_squad_pic() +{ + // check if no pilot images or no pilot selected + if ((Num_pilot_squad_images == 0) || (Cur_pilot->callsign[0] == '\0')) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // reset pilot pic number + Pic_squad_number--; + if (Pic_squad_number < 0) { + Pic_squad_number = Num_pilot_squad_images - 1; + } + + // copy pilot pic filename into pilot struct + if ((Pic_squad_number >= 0) && (Pic_squad_number < Num_pilot_squad_images)) { + strcpy(Cur_pilot->squad_filename, Pilot_squad_image_names[Pic_squad_number]); + } + + // play scroll sound + gamesnd_play_iface(SND_SCROLL); +} + +// show next pilot pic +void barracks_next_squad_pic() +{ + // check if no pilot images or no pilot selected + if ((Num_pilot_squad_images == 0) || (Cur_pilot->callsign[0] == '\0')) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // reset pilot pic number + Pic_squad_number++; + if (Pic_squad_number >= Num_pilot_squad_images){ + Pic_squad_number = 0; + } + + // copy pilot pic filename into pilot struct + if ((Pic_squad_number >= 0) && (Pic_squad_number < Num_pilot_squad_images)){ + strcpy(Cur_pilot->squad_filename, Pilot_squad_image_names[Pic_squad_number]); + } + + // play scroll sound + gamesnd_play_iface(SND_SCROLL); +} + +void barracks_delete_pilot() +{ + char buf[MAX_FILENAME_LEN]; + int active = 0; + + if (!Num_pilots) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + int popup_rval = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, XSTR( "Warning!\n\nAre you sure you wish to delete this pilot?", 65)); + if (popup_rval != 1) { + return; + } + + if (!stricmp(Pilots[Selected_line], Cur_pilot->callsign)) { + active = 1; + } + + strcpy(buf, Pilots[Selected_line]); + for (int i=Selected_line; i= Num_pilots) { + Selected_line = Num_pilots - 1; + } + + if (active) { + if (Selected_line >= 0) { + barracks_new_pilot_selected(); + } else { + Cur_pilot->callsign[0] = 0; + } + } + + delete_pilot_file(buf, Player_sel_mode == PLAYER_SELECT_MODE_SINGLE ? 1 : 0); + gamesnd_play_iface(SND_USER_SELECT); +} + +// Filter out pilots of wrong type (which shouldn't be in the directory we are checking, but just to be safe..) +int barracks_pilot_filter(char *filename) +{ + int r, rank; + + r = verify_pilot_file(filename, Player_sel_mode == PLAYER_SELECT_MODE_SINGLE, &rank); + if (rank >= Rank_pips_count) + rank = Rank_pips_count - 1; + + if (!r) { + Pilot_ranks[Num_pilots++] = rank; + } + + return !r; +} + +// callback handler for the squadon selection buttons when they are disabled (in single player) +void barracks_squad_change_popup() +{ + // show a popup + popup( PF_USE_AFFIRMATIVE_ICON | PF_NO_NETWORKING, 1, POPUP_OK, XSTR("You cannot change your squadron in Single Player mode.", 1445)); +} + + +void barracks_init_player_stuff(int mode) +{ + // determine if we should be looking for single or multiplayers at the outset + Player_sel_mode = mode; + + // get the list of pilots based upon whether we're in single or multiplayer mode + Num_pilots = 0; + Get_file_list_filter = barracks_pilot_filter; + + // single player specific stuff + if (mode == PLAYER_SELECT_MODE_SINGLE) { + Num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_SINGLE_PLAYERS, NOX("*.plr"), CF_SORT_TIME); + + // disable squad logo switching + Buttons[gr_screen.res][B_SQUAD_PREV_BUTTON].button.hide(); + Buttons[gr_screen.res][B_SQUAD_PREV_BUTTON].button.disable(); + Buttons[gr_screen.res][B_SQUAD_PREV_BUTTON].button.set_disabled_action(barracks_squad_change_popup); + Buttons[gr_screen.res][B_SQUAD_NEXT_BUTTON].button.hide(); + Buttons[gr_screen.res][B_SQUAD_NEXT_BUTTON].button.disable(); + Buttons[gr_screen.res][B_SQUAD_NEXT_BUTTON].button.set_disabled_action(barracks_squad_change_popup); + } + // multiplayer specific stuff + else { + Num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_MULTI_PLAYERS, NOX("*.plr"), CF_SORT_TIME); + + // enable squad logo switching + Buttons[gr_screen.res][B_SQUAD_PREV_BUTTON].button.enable(); + Buttons[gr_screen.res][B_SQUAD_PREV_BUTTON].button.unhide(); + Buttons[gr_screen.res][B_SQUAD_NEXT_BUTTON].button.enable(); + Buttons[gr_screen.res][B_SQUAD_NEXT_BUTTON].button.unhide(); + } + + int ranks[MAX_PILOTS]; + + for (int i=0; icallsign, Player_sel_mode == PLAYER_SELECT_MODE_MULTI)) { + z = popup(0, 2, POPUP_CANCEL, POPUP_OK, temp); + if (z != 1) + break; + } + + strcpy(old_pic, Cur_pilot->image_filename); + strcpy(old_squad_pic, Cur_pilot->squad_filename); + strcpy(old_squad, Cur_pilot->squad_name); + init_new_pilot(Cur_pilot, 0); + strcpy(Cur_pilot->image_filename, old_pic); + strcpy(Cur_pilot->squad_filename, old_squad_pic); + strcpy(Cur_pilot->squad_name, old_squad); + if (Player_sel_mode == PLAYER_SELECT_MODE_SINGLE) { + Cur_pilot->flags |= PLAYER_FLAGS_IS_MULTI; + write_pilot_file(); + barracks_init_player_stuff(PLAYER_SELECT_MODE_MULTI); + + } else { + write_pilot_file(); + barracks_init_player_stuff(PLAYER_SELECT_MODE_SINGLE); + } + + gamesnd_play_iface(SND_USER_SELECT); + + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +#endif + break; + } + + case B_PILOT_CREATE_BOTTON: + Clone_flag = 0; + barracks_create_new_pilot(); + break; + + case B_HELP_BUTTON: + launch_context_help(); + gamesnd_play_iface(SND_HELP_PRESSED); + break; + + case B_OPTION_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + break; + + case B_STATS_MEDAL_BUTTON: +#ifdef FS2_DEMO + game_feature_not_in_demo_popup(); +#else + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_VIEW_MEDALS); +#endif + break; + + case B_PILOT_DELETE_BUTTON: + barracks_delete_pilot(); + break; + + case B_PILOT_SINGLE_MODE_BUTTON: + if (Player_sel_mode != PLAYER_SELECT_MODE_SINGLE) { + gamesnd_play_iface(SND_USER_SELECT); + barracks_init_player_stuff(PLAYER_SELECT_MODE_SINGLE); + } + break; + + case B_PILOT_MULTI_MODE_BUTTON: +#if defined(DEMO) || defined(OEM_BUILD) // not for FS2_DEMO + game_feature_not_in_demo_popup(); +#else + if (Player_sel_mode != PLAYER_SELECT_MODE_MULTI) { + gamesnd_play_iface(SND_USER_SELECT); + barracks_init_player_stuff(PLAYER_SELECT_MODE_MULTI); + } +#endif + break; + } +} + + +void barracks_display_pilot_callsigns(int prospective_pilot) +{ + int y = 0; + int cur_pilot_idx = List_scroll_offset; + + int multi = 0; + if (Player_sel_mode == PLAYER_SELECT_MODE_MULTI) { + multi = 1; + } + + int font_height = gr_get_font_height(); + while (y + font_height <= Barracks_list_coords[gr_screen.res][BARRACKS_H_COORD]) { + if (cur_pilot_idx >= Num_pilots) + break; + + if (!stricmp(Cur_pilot->callsign, Pilots[cur_pilot_idx]) && (is_pilot_multi(Cur_pilot) == multi)) { + if ((cur_pilot_idx == Selected_line) || (cur_pilot_idx == prospective_pilot)) { + gr_set_color_fast(&Color_text_active_hi); + } else { + gr_set_color_fast(&Color_text_active); + } + } else { + if (cur_pilot_idx == Selected_line) { + gr_set_color_fast(&Color_text_selected); + } else if (cur_pilot_idx == prospective_pilot) { + gr_set_color_fast(&Color_text_subselected); + } else { + gr_set_color_fast(&Color_text_normal); + } + } + + gr_printf(Barracks_list_coords[gr_screen.res][BARRACKS_X_COORD], Barracks_list_coords[gr_screen.res][BARRACKS_Y_COORD] + y, Pilots[cur_pilot_idx]); + gr_set_bitmap(Rank_pips_bitmaps + Pilot_ranks[cur_pilot_idx]); + gr_bitmap(Barracks_list_coords[gr_screen.res][BARRACKS_X_COORD] - 34, Barracks_list_coords[gr_screen.res][BARRACKS_Y_COORD] + y); + + y += font_height; + cur_pilot_idx++; + } +} + + + +void barracks_display_pilot_stats() +{ + int y = 0; + int z = Stats_scroll_offset; + int font_height = gr_get_font_height(); + char *str; + int i, w, h; + while (y + font_height <= Barracks_stats_coords[gr_screen.res][BARRACKS_H_COORD]) { + if (z >= Num_stat_lines) { + break; + } + + str = Stat_labels[z]; + if (*str == '*') { + gr_set_color_fast(&Color_text_heading); + str++; + + gr_get_string_size(&w, &h, str); + i = Barracks_stats_coords[gr_screen.res][BARRACKS_Y_COORD] + y + h / 2 - 1; + gr_line(Barracks_stats_coords[gr_screen.res][BARRACKS_X_COORD], i, Barracks_stats_coords[gr_screen.res][BARRACKS_X_COORD] + Barracks_stats_coords[gr_screen.res][BARRACKS_W_COORD] - w - 2, i); + gr_line(Barracks_stats_coords[gr_screen.res][BARRACKS_X_COORD] + Barracks_stats_coords[gr_screen.res][BARRACKS_W_COORD] + 1, i, Barracks_stats2_coords[gr_screen.res][BARRACKS_X_COORD] + Barracks_stats2_coords[gr_screen.res][BARRACKS_W_COORD], i); + + } else { + gr_set_color_fast(&Color_text_normal); + } + + gr_get_string_size(&w, NULL, str); + gr_printf(Barracks_stats_coords[gr_screen.res][BARRACKS_X_COORD] + Barracks_stats_coords[gr_screen.res][BARRACKS_W_COORD] - w, Barracks_stats_coords[gr_screen.res][BARRACKS_Y_COORD] + y, "%s", str); + str = Stats[z]; + if (*str) { + gr_printf(Barracks_stats2_coords[gr_screen.res][BARRACKS_X_COORD], Barracks_stats_coords[gr_screen.res][BARRACKS_Y_COORD] + y, "%s", str); + } + + y += font_height; + z++; + } +} + + +// process pilot callsign +void barracks_accept_new_pilot_callsign() +{ + char buf[CALLSIGN_LEN + 1]; + char name[MAX_FILENAME_LEN]; + int i; + + int z = 0; + Inputbox.get_text(buf); + drop_white_space(buf); + + if (!isalpha(*buf)) { + z = 1; + } else { + for (i=1; buf[i]; i++) { + if (!isalpha(buf[i]) && !isdigit(buf[i]) && !strchr(VALID_PILOT_CHARS, buf[i])) { + z = 1; + return; + } + } + } + + for (i=1; icallsign, buf); + init_new_pilot(Cur_pilot, !Clone_flag); + + // again, make sure we set his flags correctly to ensure that he gets saved to the proper directory and gets + // displayed correctly + if (Player_sel_mode == PLAYER_SELECT_MODE_SINGLE) { + Cur_pilot->flags &= ~(PLAYER_FLAGS_IS_MULTI); + } else { + Cur_pilot->flags |= PLAYER_FLAGS_IS_MULTI; + Cur_pilot->stats.flags |= STATS_FLAG_MULTIPLAYER; + } + + if ( !(Game_mode & GM_STANDALONE_SERVER) ) { + write_pilot_file(Cur_pilot); + } + + Selected_line = 0; + barracks_new_pilot_selected(); + barracks_set_callsign_enter_mode(false); +} + + +// draw pilot image and clean up afterwards +void barracks_draw_pilot_pic() +{ + // draw pilot pic + if (Cur_pilot->callsign[0] && (Pic_number >= 0) && (Pic_number < Num_pilot_images)) { + if (Pilot_images[Pic_number] >= 0) { + // JAS: This code is hacked to allow the animation to use all 256 colors + extern int Palman_allow_any_color; + Palman_allow_any_color = 1; + gr_set_bitmap(Pilot_images[Pic_number]); + gr_bitmap(Barracks_image_coords[gr_screen.res][BARRACKS_X_COORD], Barracks_image_coords[gr_screen.res][BARRACKS_Y_COORD]); + Palman_allow_any_color = 0; + + // print number of the current pic + char buf[40]; + sprintf(buf, XSTR( "%d of %d", 71), Pic_number + 1, Num_pilot_images); + gr_printf(Barracks_image_number_coords[gr_screen.res][BARRACKS_X_COORD], Barracks_image_number_coords[gr_screen.res][BARRACKS_Y_COORD], buf); + } + } else { + Pic_number = -1; + } +} + +// draw squad image and clean up afterwards +void barracks_draw_squad_pic() +{ + char buf[40]; + + // draw pilot pic + if (Cur_pilot->callsign[0] && (Pic_squad_number >= 0) && (Pic_squad_number < Num_pilot_squad_images)) { + if (Pilot_squad_images[Pic_squad_number] >= 0) { + // JAS: This code is hacked to allow the animation to use all 256 colors + extern int Palman_allow_any_color; + Palman_allow_any_color = 1; + gr_set_bitmap(Pilot_squad_images[Pic_squad_number]); + gr_bitmap(Barracks_squad_coords[gr_screen.res][BARRACKS_X_COORD], Barracks_squad_coords[gr_screen.res][BARRACKS_Y_COORD]); + Palman_allow_any_color = 0; + + // print number of current squad pic + if(Player_sel_mode != PLAYER_SELECT_MODE_SINGLE){ + sprintf(buf,XSTR( "%d of %d", 71), Pic_squad_number+1, Num_pilot_squad_images); + gr_printf(Barracks_squad_number_coords[gr_screen.res][BARRACKS_X_COORD], Barracks_squad_number_coords[gr_screen.res][BARRACKS_Y_COORD], buf); + } + } + } else { + Pic_squad_number = -1; + } +} + +// ----------------------------------------------------------------------------- +void barracks_init() +{ + UI_WINDOW *w = &Ui_window; + + // save current pilot file, so we don't possibly loose it. + write_pilot_file(); + + // create interface + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Ui_window.set_mask_bmap(Barracks_bitmap_mask_fname[gr_screen.res]); + + // load background bitmap + Background_bitmap = bm_load(Barracks_bitmap_fname[gr_screen.res]); + if(Background_bitmap < 0){ + // we failed to load the bitmap - this is very bad + Int3(); + } + + // create buttons + for (int i=0; iadd_XSTR("Create", 1034, Buttons[gr_screen.res][0].text_x, Buttons[gr_screen.res][0].text_y, &Buttons[gr_screen.res][0].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Accept", 1035, Buttons[gr_screen.res][5].text_x, Buttons[gr_screen.res][5].text_y, &Buttons[gr_screen.res][5].button, UI_XSTR_COLOR_PINK); + w->add_XSTR("Help", 928, Buttons[gr_screen.res][6].text_x, Buttons[gr_screen.res][6].text_y, &Buttons[gr_screen.res][6].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Options",1036, Buttons[gr_screen.res][7].text_x, Buttons[gr_screen.res][7].text_y, &Buttons[gr_screen.res][7].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Medals", 1037, Buttons[gr_screen.res][8].text_x, Buttons[gr_screen.res][8].text_y, &Buttons[gr_screen.res][8].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Remove", 1038, Buttons[gr_screen.res][11].text_x, Buttons[gr_screen.res][11].text_y, &Buttons[gr_screen.res][11].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Select", 1552, Buttons[gr_screen.res][12].text_x, Buttons[gr_screen.res][12].text_y, &Buttons[gr_screen.res][12].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Clone", 1040, Buttons[gr_screen.res][13].text_x, Buttons[gr_screen.res][13].text_y, &Buttons[gr_screen.res][13].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Single", 1041, Buttons[gr_screen.res][14].text_x, Buttons[gr_screen.res][14].text_y, &Buttons[gr_screen.res][14].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Multi", 1042, Buttons[gr_screen.res][15].text_x, Buttons[gr_screen.res][15].text_y, &Buttons[gr_screen.res][15].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Convert",1043, Buttons[gr_screen.res][16].text_x, Buttons[gr_screen.res][16].text_y, &Buttons[gr_screen.res][16].button, UI_XSTR_COLOR_GREEN); + for(int i=0; iadd_XSTR(&Barracks_text[gr_screen.res][i]); + } + + // button for selecting pilot + List_region.create(&Ui_window, "", Barracks_list_coords[gr_screen.res][BARRACKS_X_COORD], Barracks_list_coords[gr_screen.res][BARRACKS_Y_COORD], Barracks_list_coords[gr_screen.res][BARRACKS_W_COORD], Barracks_list_coords[gr_screen.res][BARRACKS_H_COORD], 0, 1); + List_region.hide(); + + // create input box (for new pilot) + Inputbox.create(&Ui_window, Barracks_list_coords[gr_screen.res][BARRACKS_X_COORD], Barracks_list_coords[gr_screen.res][BARRACKS_Y_COORD], Barracks_list_coords[gr_screen.res][BARRACKS_W_COORD], CALLSIGN_LEN - 1, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_LETTER_FIRST); + Inputbox.set_valid_chars(VALID_PILOT_CHARS); + Inputbox.disable(); + Inputbox.hide(); + + // load in help overlay bitmap + help_overlay_load(BARRACKS_OVERLAY); + help_overlay_set_state(BARRACKS_OVERLAY,0); + + // other init stuff + Barracks_callsign_enter_mode = 0; + List_scroll_offset = Stats_scroll_offset = Pic_number = Pic_squad_number = Selected_line = 0; + Cur_pilot = &Players[Player_num]; + + // disable squad logo selection buttons in single player + if(!(Cur_pilot->flags & PLAYER_FLAGS_IS_MULTI)){ + // squad logo picture buttons + Buttons[gr_screen.res][B_SQUAD_PREV_BUTTON].button.hide(); + Buttons[gr_screen.res][B_SQUAD_PREV_BUTTON].button.disable(); + Buttons[gr_screen.res][B_SQUAD_NEXT_BUTTON].button.hide(); + Buttons[gr_screen.res][B_SQUAD_NEXT_BUTTON].button.disable(); + } else { + // squad logo picture buttons + Buttons[gr_screen.res][B_SQUAD_PREV_BUTTON].button.enable(); + Buttons[gr_screen.res][B_SQUAD_PREV_BUTTON].button.unhide(); + Buttons[gr_screen.res][B_SQUAD_NEXT_BUTTON].button.enable(); + Buttons[gr_screen.res][B_SQUAD_NEXT_BUTTON].button.unhide(); + } + + // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed + barracks_set_hotkeys(1); + + // load ramp pips + Rank_pips_bitmaps = bm_load_animation("IconRankMini.ani", &Rank_pips_count); + + // load up the pilot pic list + pilot_load_pic_list(); + pilot_load_squad_pic_list(); + + // don't load pilot images yet + for (int i=0; istats); + + // disable some buttons for the multiplayer beta and e3 build +#if defined(MULTIPLAYER_BETA_BUILD) || defined(E3_BUILD) || defined(PRESS_TOUR_BUILD) + Buttons[gr_screen.res][B_PILOT_CLONE_BUTTON].button.hide(); + Buttons[gr_screen.res][B_PILOT_CONVERT_BUTTON].button.hide(); + Buttons[gr_screen.res][B_PILOT_CLONE_BUTTON].button.disable(); + Buttons[gr_screen.res][B_PILOT_CONVERT_BUTTON].button.disable(); +#endif + + // multiplayer beta build +#ifdef MULTIPLAYER_BETA_BUILD + Buttons[gr_screen.res][B_PILOT_SINGLE_MODE_BUTTON].button.hide(); + Buttons[gr_screen.res][B_PILOT_SINGLE_MODE_BUTTON].button.disable(); +#endif + + // e3 build +#if defined(E3_BUILD) || defined(PRESS_TOUR_BUILD) + Buttons[gr_screen.res][B_PILOT_MULTI_MODE_BUTTON].button.hide(); + Buttons[gr_screen.res][B_PILOT_MULTI_MODE_BUTTON].button.disable(); +#endif + + // base the mode we're in (single or multi) on the status of the currently selected pilot +#ifdef MULTIPLAYER_BETA_BUILD + barracks_init_player_stuff(1); +#elif defined(E3_BUILD) || defined(PRESS_TOUR_BUILD) + barracks_init_player_stuff(0); +#else + barracks_init_player_stuff(is_pilot_multi(Player)); +#endif +} + +// ----------------------------------------------------------------------------- +void barracks_do_frame(float frametime) +{ + int k = Ui_window.process(); + + if ( k > 0 ) { + if ( help_overlay_active(BARRACKS_OVERLAY) ) { + help_overlay_set_state(BARRACKS_OVERLAY,0); + k = 0; + } + } + + // pilot that mouse is over + int prospective_pilot = -1; + int i; + + // Entering pilot callsign + if (Barracks_callsign_enter_mode) { + // set focus to inputbox + Inputbox.set_focus(); + + switch (k) { + case KEY_ESC: + // cancel create pilot + Num_pilots--; + for (i=0; i= 0) && (pilot_index < Num_pilots)) { + prospective_pilot = pilot_index; + } + } + + // if mouse clicked in list region, find index into Pilots array + if (List_region.pressed()) { + if (prospective_pilot != -1) { + Selected_line = prospective_pilot; + gamesnd_play_iface(SND_USER_SELECT); + } + } + } + + // check mouse over help + if (mouse_down(MOUSE_LEFT_BUTTON)) { + help_overlay_set_state(BARRACKS_OVERLAY, 0); + } + + // do pilot pic stuff + if ((Pic_number >= 0) && (Pic_number < Num_pilot_images)) { + if (Pilot_images[Pic_number] == BARRACKS_IMAGE_NOT_LOADED) { // haven't tried loading it yet + Pilot_images[Pic_number] = bm_load(Pilot_image_names[Pic_number]); + if (Pilot_images[Pic_number] >= 0) { + int w, h; + + bm_get_info(Pilot_images[Pic_number], &w, &h, NULL); + // check for invalid pilot pic file + if ((w != PLAYER_PILOT_PIC_W) || (h != PLAYER_PILOT_PIC_H)) { + bm_unload(Pilot_images[Pic_number]); + Pilot_images[Pic_number] = -1; + } + } + } + } else { + Pic_number = -1; + } + + // do squad pic stuff + if ((Pic_squad_number >= 0) && (Pic_squad_number < Num_pilot_squad_images)) { + if (Pilot_squad_images[Pic_squad_number] == BARRACKS_IMAGE_NOT_LOADED) { // haven't tried loading it yet + Pilot_squad_images[Pic_squad_number] = bm_load_duplicate(Pilot_squad_image_names[Pic_squad_number]); + if (Pilot_squad_images[Pic_squad_number] >= 0) { + int w, h; + + bm_get_info(Pilot_squad_images[Pic_squad_number], &w, &h, NULL); + // check for invalid pilot pic file + if ((w != PLAYER_SQUAD_PIC_W) || (h != PLAYER_SQUAD_PIC_H)) { + bm_unload(Pilot_squad_images[Pic_squad_number]); + Pilot_squad_images[Pic_squad_number] = -1; + } + } + } + } else { + Pic_squad_number = -1; + } + + // draw the background, etc + gr_reset_clip(); + GR_MAYBE_CLEAR_RES(Background_bitmap); + if (Background_bitmap >= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + // draw pilot image and clean up afterwards + barracks_draw_pilot_pic(); + barracks_draw_squad_pic(); + + // draw the window + Ui_window.draw(); + + // light up the correct mode button (single or multi) + if (Player_sel_mode == PLAYER_SELECT_MODE_SINGLE) { + Buttons[gr_screen.res][B_PILOT_SINGLE_MODE_BUTTON].button.draw_forced(2); + } else { + Buttons[gr_screen.res][B_PILOT_MULTI_MODE_BUTTON].button.draw_forced(2); + } + + // write out pilot call signs + barracks_display_pilot_callsigns(prospective_pilot); + + // write out current pilot stats + barracks_display_pilot_stats(); + + // blit help overlay if active + help_overlay_maybe_blit(BARRACKS_OVERLAY); + + // flip the page + gr_flip(); +} + +// ----------------------------------------------------------------------------- +void barracks_close() +{ + // destroy window + Ui_window.destroy(); + + // release background bitmap + if (Background_bitmap >= 0) { + bm_unload(Background_bitmap); + } + + // release rank pip bitmaps + for (int i=0; i= 0) { + bm_unload(Pilot_images[i]); + } + } + + // unload the overlay bitmap + help_overlay_unload(BARRACKS_OVERLAY); + + game_flush(); +} diff --git a/src/menuui/credits.cpp b/src/menuui/credits.cpp new file mode 100644 index 0000000..f193f8e --- /dev/null +++ b/src/menuui/credits.cpp @@ -0,0 +1,753 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/Credits.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C source file for displaying game credits + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 20 9/14/99 5:14a Dave + * Fixed credits drawing in Glide. + * + * 19 9/13/99 1:53p Dave + * Fixed completely brain dead code in credits_init(). + * + * 18 9/09/99 10:55a Jefff + * + * 17 9/03/99 11:45a Jefff + * + * 16 9/01/99 5:28p Jefff + * hi res art shows up now + * + * 15 9/01/99 4:20p Jefff + * mo' pictures + * + * 14 9/01/99 12:19p Jefff + * text splitting for long lines + * + * 13 7/19/99 2:13p Dave + * Added some new strings for Heiko. + * + * 12 2/03/99 6:06p Dave + * Groundwork for FS2 PXO usertracker support. Gametracker support next. + * + * 11 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 10 2/01/99 5:55p Dave + * Removed the idea of explicit bitmaps for buttons. Fixed text + * highlighting for disabled gadgets. + * + * 9 1/30/99 9:01p Dave + * Coord fixes. + * + * 8 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 7 1/29/99 12:47a Dave + * Put in sounds for beam weapon. A bunch of interface screens (tech + * database stuff). + * + * 6 1/28/99 1:46a Dave + * Updated coords and bitmaps. + * + * 5 1/14/99 5:15p Neilk + * changed credits, command debrief interfaces to high resolution support + * + * 4 11/20/98 4:08p Dave + * Fixed flak effect in multiplayer. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 18 6/19/98 3:51p Lawrance + * deal with foreign chars in the credits + * + * 17 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 16 5/24/98 9:01p Lawrance + * Add commit sounds when accept is pressed + * + * 15 5/20/98 1:04p Hoffoss + * Made credits screen use new artwork and removed rating field usage from + * Fred (a goal struct member). + * + * 14 5/12/98 4:17p Hoffoss + * Make ctrl-arrows (up/down) switch between tech room screens. + * + * 13 5/12/98 11:21a Hoffoss + * Disabled cutscene screen and simulator room. + * + * 12 5/11/98 8:04p Hoffoss + * Fixed minor bugs. + * + * 11 4/22/98 3:35p John + * String externalization marking + * + * 10 4/22/98 10:46a Hoffoss + * Added images to credits screen. + * + * 9 4/21/98 7:07p Hoffoss + * Fixed problem where when switching screens flashes old tab hilight once + * before switching to new state. + * + * 8 4/17/98 3:28p Hoffoss + * Added new credits screen code. + * + * 7 3/05/98 11:15p Hoffoss + * Changed non-game key checking to use game_check_key() instead of + * game_poll(). + * + * 6 2/22/98 12:19p John + * Externalized some strings + * + * 5 1/05/98 2:30p John + * made credits.tbl display + * + * 4 9/19/97 5:14p Lawrance + * use new naming convention for spooled music + * + * 3 8/31/97 6:38p Lawrance + * pass in frametime to do_frame loop + * + * 2 4/22/97 11:06a Lawrance + * credits music playing, credits screen is a separate state + * + * $NoKeywords: $ + */ + +#include + +#include "gamesequence.h" +#include "font.h" +#include "key.h" +#include "bmpman.h" +#include "2d.h" +#include "timer.h" +#include "gamesnd.h" +#include "audiostr.h" +#include "eventmusic.h" /* for Master_event_music_volume */ +#include "cfile.h" +#include "ui.h" +#include "missionscreencommon.h" +#include "player.h" +#include "freespace.h" +#include "alphacolors.h" +#include "localize.h" + +#define CREDITS_MUSIC_DELAY 2000 +#define CREDITS_SCROLL_RATE 15.0f +#define CREDITS_ARTWORK_DISPLAY_TIME 9.0f +#define CREDITS_ARTWORK_FADE_TIME 1.0f + +#define NUM_BUTTONS 5 +#define NUM_IMAGES 46 + +#define TECH_DATABASE_BUTTON 0 +#define SIMULATOR_BUTTON 1 +#define CUTSCENES_BUTTON 2 +#define CREDITS_BUTTON 3 +#define EXIT_BUTTON 4 + +// inidicies for coordinates +#define CREDITS_X_COORD 0 +#define CREDITS_Y_COORD 1 +#define CREDITS_W_COORD 2 +#define CREDITS_H_COORD 3 + +static char* Credits_bitmap_fname[GR_NUM_RESOLUTIONS] = { + "Credits", // GR_640 + "2_Credits" +}; + +static char* Credits_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = { + "Credits-M", // GR_640 + "2_Credits-M" +}; + +int Credits_image_coords[GR_NUM_RESOLUTIONS][4] = { + { + 219, 15, 394, 286 // GR_640 + }, + { + 351, 25, 629, 455 // GR_1024 + } +}; + +// x, y, w, h +int Credits_text_coords[GR_NUM_RESOLUTIONS][4] = { + { + 26, 316, 482, 157 // GR_640 + }, + { + 144, 507, 568, 249 // GR_640 + } +}; + +struct credits_screen_buttons { + char *filename; + int x, y, xt, yt; + int hotspot; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + credits_screen_buttons(char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {} +}; + +static int Background_bitmap; +/* +static int CreditsWin01 = -1; +static int CreditsWin02 = -1; +static int CreditsWin03 = -1; +static int CreditsWin04 = -1; +*/ +static UI_WINDOW Ui_window; + +static credits_screen_buttons Buttons[NUM_BUTTONS][GR_NUM_RESOLUTIONS] = { +//XSTR:OFF + { + credits_screen_buttons("TDB_00", 7, 3, 37, 7, 0), // GR_640 + credits_screen_buttons("2_TDB_00", 12, 5, 59, 12, 0) // GR_1024 + }, + { + credits_screen_buttons("TDB_01", 7, 18, 37, 23, 1), // GR_640 + credits_screen_buttons("2_TDB_01", 12, 31, 59, 37, 1) // GR_1024 + }, + { + credits_screen_buttons("TDB_02", 7, 34, 37, 38, 2), // GR_640 + credits_screen_buttons("2_TDB_02", 12, 56, 59, 62, 2) // GR_1024 + }, + { + credits_screen_buttons("TDB_03", 7, 49, 37, 54, 3), // GR_640 + credits_screen_buttons("2_TDB_03", 12, 81, 59, 88, 3) // GR_1024 + }, + { + credits_screen_buttons("CRB_04", 571, 425, 588, 413, 4), // GR_640 + credits_screen_buttons("2_CRB_04", 914, 681, 953, 668, 4) // GR_1024 + } +//XSTR:ON +}; + +static int Credits_music_handle = -1; +static int Credits_music_begin_timestamp; + +static int Credits_frametime; // frametime of credits_do_frame() loop in ms +static int Credits_last_time; // timestamp used to calc frametime (in ms) +static float Credits_counter; +static int Credits_artwork_index; +static int Credits_bmps[NUM_IMAGES]; + +char *Credit_text = NULL; +int Credit_text_malloced = 0; // TRUE if credit_text was malloced + +// Positions for credits... +float Credit_start_pos, Credit_stop_pos, Credit_position = 0.0f; + +void credits_stop_music() +{ + if ( Credits_music_handle != -1 ) { + audiostream_close_file(Credits_music_handle); + Credits_music_handle = -1; + } +} + +void credits_load_music(char* fname) +{ + if ( Credits_music_handle != -1 ){ + return; + } + + if ( fname ){ + Credits_music_handle = audiostream_open( fname, ASF_EVENTMUSIC ); + } +} + +void credits_start_music() +{ + if (Credits_music_handle != -1) { + if ( !audiostream_is_playing(Credits_music_handle) ){ + audiostream_play(Credits_music_handle, Master_event_music_volume); + } + } else { + nprintf(("Warning", "Cannot play credits music\n")); + } +} + +int credits_screen_button_pressed(int n) +{ + switch (n) { + case TECH_DATABASE_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_TECH_MENU); + return 1; + + case SIMULATOR_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_SIMULATOR_ROOM); + return 1; + + case CUTSCENES_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN); + return 1; + + case EXIT_BUTTON: + gamesnd_play_iface(SND_COMMIT_PRESSED); + gameseq_post_event(GS_EVENT_MAIN_MENU); + game_flush(); + break; + } + + return 0; +} + +void credits_init() +{ + int i, w, h; + credits_screen_buttons *b; + char line[512] = ""; + char *linep1, *linep2; + + int credits_spooled_music_index = event_music_get_spooled_music_index("Cinema"); + if(credits_spooled_music_index != -1){ + char *credits_wavfile_name = Spooled_music[credits_spooled_music_index].filename; + if(credits_wavfile_name != NULL){ + credits_load_music(credits_wavfile_name); + } + } + + // Use this id to trigger the start of music playing on the briefing screen + Credits_music_begin_timestamp = timestamp(CREDITS_MUSIC_DELAY); + + Credits_frametime = 0; + Credits_last_time = timer_get_milliseconds(); + + Credit_text = NULL; + Credit_text_malloced = 0; + + // allocate enough space for credits text + CFILE *fp = cfopen( NOX("credits.tbl"), "rb" ); + if(fp != NULL){ + int size; + size = cfilelength(fp); + Credit_text = (char *) malloc(size + 200); + cfclose(fp); + + // open localization and parse + lcl_ext_open(); + read_file_text("credits.tbl"); + reset_parse(); + + // keep reading everything in + strcpy(Credit_text,""); + while(!check_for_string_raw("#end")){ + stuff_string_line(line, 511); + linep1 = line; + + do { + linep2 = split_str_once(linep1, Credits_text_coords[gr_screen.res][2]); + strcat(Credit_text, linep1); + strcat(Credit_text, "\n"); + linep1 = linep2; + } while (linep2 != NULL); + } + + // close localization + lcl_ext_close(); + } else { + Credit_text = NOX("No credits available.\n"); + } + + int ch; + for ( i = 0; Credit_text[i]; i++ ) { + ch = Credit_text[i]; + switch (ch) { + case -4: + ch = 129; + break; + + case -28: + ch = 132; + break; + + case -10: + ch = 148; + break; + + case -23: + ch = 130; + break; + + case -30: + ch = 131; + break; + + case -25: + ch = 135; + break; + + case -21: + ch = 137; + break; + + case -24: + ch = 138; + break; + + case -17: + ch = 139; + break; + + case -18: + ch = 140; + break; + + case -60: + ch = 142; + break; + + case -55: + ch = 144; + break; + + case -12: + ch = 147; + break; + + case -14: + ch = 149; + break; + + case -5: + ch = 150; + break; + + case -7: + ch = 151; + break; + + case -42: + ch = 153; + break; + + case -36: + ch = 154; + break; + + case -31: + ch = 160; + break; + + case -19: + ch = 161; + break; + + case -13: + ch = 162; + break; + + case -6: + ch = 163; + break; + + case -32: + ch = 133; + break; + + case -22: + ch = 136; + break; + + case -20: + ch = 141; + break; + } + Credit_text[i] = (char)ch; + } + + gr_get_string_size(&w, &h, Credit_text); + + Credit_start_pos = i2fl(Credits_text_coords[gr_screen.res][CREDITS_H_COORD]); + Credit_stop_pos = -i2fl(h); + Credit_position = Credit_start_pos; + + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Ui_window.set_mask_bmap(Credits_bitmap_mask_fname[gr_screen.res]); + common_set_interface_palette("InterfacePalette"); // set the interface palette + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // add some text + Ui_window.add_XSTR("Technical Database", 1055, Buttons[TECH_DATABASE_BUTTON][gr_screen.res].xt, Buttons[TECH_DATABASE_BUTTON][gr_screen.res].yt, &Buttons[TECH_DATABASE_BUTTON][gr_screen.res].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Mission Simulator", 1056, Buttons[SIMULATOR_BUTTON][gr_screen.res].xt, Buttons[SIMULATOR_BUTTON][gr_screen.res].yt, &Buttons[SIMULATOR_BUTTON][gr_screen.res].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Cutscenes", 1057, Buttons[CUTSCENES_BUTTON][gr_screen.res].xt, Buttons[CUTSCENES_BUTTON][gr_screen.res].yt, &Buttons[CUTSCENES_BUTTON][gr_screen.res].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Credits", 1058, Buttons[CREDITS_BUTTON][gr_screen.res].xt, Buttons[CREDITS_BUTTON][gr_screen.res].yt, &Buttons[CREDITS_BUTTON][gr_screen.res].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Exit", 1420, Buttons[EXIT_BUTTON][gr_screen.res].xt, Buttons[EXIT_BUTTON][gr_screen.res].yt, &Buttons[EXIT_BUTTON][gr_screen.res].button, UI_XSTR_COLOR_PINK); + + if (Player->flags & PLAYER_FLAGS_IS_MULTI) { + Buttons[SIMULATOR_BUTTON][gr_screen.res].button.disable(); + Buttons[CUTSCENES_BUTTON][gr_screen.res].button.disable(); + } + + Buttons[EXIT_BUTTON][gr_screen.res].button.set_hotkey(KEY_CTRLED | KEY_ENTER); + + Background_bitmap = bm_load(Credits_bitmap_fname[gr_screen.res]); + Credits_artwork_index = rand() % NUM_IMAGES; + for (i=0; i= 0){ + bm_unload(Credits_bmps[i]); + Credits_bmps[i] = -1; + } + } + + credits_stop_music(); + + if (Credit_text) { + if (Credit_text_malloced){ + free(Credit_text); + } + + Credit_text = NULL; + } + + if (Background_bitmap){ + bm_unload(Background_bitmap); + } + + Ui_window.destroy(); + common_free_interface_palette(); // restore game palette +} + +void credits_do_frame(float frametime) +{ + int i, k, next, percent, bm1, bm2; + int bx1, by1, bw1, bh1; + int bx2, by2, bw2, bh2; + + // Use this id to trigger the start of music playing on the credits screen + if ( timestamp_elapsed(Credits_music_begin_timestamp) ) { + Credits_music_begin_timestamp = 0; + credits_start_music(); + } + + k = Ui_window.process(); + switch (k) { + case KEY_ESC: + gameseq_post_event(GS_EVENT_MAIN_MENU); + key_flush(); + break; + + case KEY_CTRLED | KEY_UP: + case KEY_SHIFTED | KEY_TAB: + if ( !(Player->flags & PLAYER_FLAGS_IS_MULTI) ) { + credits_screen_button_pressed(CUTSCENES_BUTTON); + break; + } + // else, react like tab key. + + case KEY_CTRLED | KEY_DOWN: + case KEY_TAB: + credits_screen_button_pressed(TECH_DATABASE_BUTTON); + break; + + default: + break; + } // end switch + + for (i=0; i= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + percent = (int) (100.0f - (CREDITS_ARTWORK_DISPLAY_TIME - Credits_counter) * 100.0f / CREDITS_ARTWORK_FADE_TIME); + if (percent < 0){ + percent = 0; + } + + next = Credits_artwork_index + 1; + if (next >= NUM_IMAGES){ + next = 0; + } + + if (Credits_bmps[Credits_artwork_index] < 0) { + char buf[40]; + + if (gr_screen.res == GR_1024) { + sprintf(buf, NOX("2_CrIm%0.2d"), Credits_artwork_index); + } else { + sprintf(buf, NOX("CrIm%0.2d"), Credits_artwork_index); + } + Credits_bmps[Credits_artwork_index] = bm_load(buf); + } + + if (Credits_bmps[next] < 0) { + char buf[40]; + + if (gr_screen.res == GR_1024) { + sprintf(buf, NOX("2_CrIm%0.2d"), Credits_artwork_index); + } else { + sprintf(buf, NOX("CrIm%0.2d"), next); + } + Credits_bmps[next] = bm_load(buf); + } + + bm1 = Credits_bmps[Credits_artwork_index]; + bm2 = Credits_bmps[next]; + + if((bm1 != -1) && (bm2 != -1)){ + Assert(percent >= 0 && percent <= 100); + + // get width and height + bm_get_info(bm1, &bw1, &bh1, NULL, NULL, NULL); + bm_get_info(bm2, &bw2, &bh2, NULL, NULL, NULL); + + // determine where to draw the coords + bx1 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw1)/2); + by1 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh1)/2); + bx2 = Credits_image_coords[gr_screen.res][CREDITS_X_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_W_COORD] - bw2)/2); + by2 = Credits_image_coords[gr_screen.res][CREDITS_Y_COORD] + ((Credits_image_coords[gr_screen.res][CREDITS_H_COORD] - bh2)/2); + + gr_cross_fade(bm1, bm2, bx1, by1, bx2, by2, (float)percent / 100.0f); + } + + /* + if (CreditsWin01 != -1) { + gr_set_bitmap(CreditsWin01); + gr_bitmap(233, 5); + } + + if (CreditsWin02 != -1) { + gr_set_bitmap(CreditsWin02); + gr_bitmap(616, 8); + } + + if (CreditsWin03 != -1) { + gr_set_bitmap(CreditsWin03); + gr_bitmap(233, 299); + } + + if (CreditsWin04 != -1) { + gr_set_bitmap(CreditsWin04); + gr_bitmap(215, 8); + } + */ + + Ui_window.draw(); + + for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){ + if (Buttons[i][gr_screen.res].button.button_down()){ + break; + } + } + + if (i > CREDITS_BUTTON){ + Buttons[CREDITS_BUTTON][gr_screen.res].button.draw_forced(2); + } + + gr_set_clip(Credits_text_coords[gr_screen.res][CREDITS_X_COORD], Credits_text_coords[gr_screen.res][CREDITS_Y_COORD], Credits_text_coords[gr_screen.res][CREDITS_W_COORD], Credits_text_coords[gr_screen.res][CREDITS_H_COORD]); + gr_set_font(FONT1); + gr_set_color_fast(&Color_normal); + + int sy; + if ( Credit_position > 0 ) { + sy = fl2i(Credit_position+0.5f); + } else { + sy = fl2i(Credit_position-0.5f); + } + + // HACK - I don't want to change the string code, so we'll just use a special version here + if(gr_screen.mode == GR_GLIDE){ + extern void gr_glide_string_hack(int sx, int sy, char *s); + gr_glide_string_hack(0x8000, sy, Credit_text); + } else { + gr_string(0x8000, sy, Credit_text); + } + + int temp_time; + temp_time = timer_get_milliseconds(); + + Credits_frametime = temp_time - Credits_last_time; + Credits_last_time = temp_time; + timestamp_inc(Credits_frametime / 1000.0f); + + float fl_frametime = i2fl(Credits_frametime) / 1000.f; + if (keyd_pressed[KEY_LSHIFT]) { + Credit_position -= fl_frametime * CREDITS_SCROLL_RATE * 4.0f; + } else { + Credit_position -= fl_frametime * CREDITS_SCROLL_RATE; + } + + if (Credit_position < Credit_stop_pos){ + Credit_position = Credit_start_pos; + } + + Credits_counter += fl_frametime; + while (Credits_counter >= CREDITS_ARTWORK_DISPLAY_TIME) { + Credits_counter -= CREDITS_ARTWORK_DISPLAY_TIME; + Credits_artwork_index = next; + } + + gr_flip(); +} diff --git a/src/menuui/fishtank.cpp b/src/menuui/fishtank.cpp new file mode 100644 index 0000000..84d74c7 --- /dev/null +++ b/src/menuui/fishtank.cpp @@ -0,0 +1,265 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/fishtank.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * bloop + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 9/01/99 10:09a Dave + * Pirate bob. + * + * 2 8/26/99 9:45a Dave + * First pass at easter eggs and cheats. + * + * + * $NoKeywords: $ + */ + +#include "animplay.h" +#include "packunpack.h" +#include "2d.h" +#include "fishtank.h" +#include "freespace.h" +#include "gamesequence.h" + +// fish +typedef struct fish { + float x, y; // x and y coords + float x_speed, y_speed; // x and y speed + int left; // left or right + anim_instance *a; // tha animation + int onscreen; + int swimming; // whee +} fish; +#define MAX_FISH 12 +fish Fish[MAX_FISH]; + +// fish anim name +#define FISH_LEFT_ANIM_NAME "f_left.ani" +#define FISH_RIGHT_ANIM_NAME "f_right.ani" + +#define FISH_ANIM_WIDTH 100 +#define FISH_ANIM_HEIGHT 30 + +anim *Fish_left_anim = NULL; +anim *Fish_right_anim = NULL; + +int Fish_inited = 0; + +void fish_generate() +{ + fish *f; + int idx; + + if(!Fish_inited){ + return; + } + + // bogus anims + if((Fish_left_anim == NULL) || (Fish_right_anim == NULL)){ + return; + } + + // find a free fish + f = NULL; + for(idx=0; idxleft = frand_range(0.0f, 1.0f) < 0.5f ? 0 : 1; + + // start location + if(f->left){ + f->x = gr_screen.max_w + frand_range(0.0f, 50.0f); + } else { + f->x = frand_range(0.0f, -50.0f) - FISH_ANIM_WIDTH; + } + f->y = frand_range(-40.0f, (float)gr_screen.max_h + 40.0f); + + // speed + if(f->left){ + f->x_speed = frand_range(-1.0f, -15.0f); + } else { + f->x_speed = frand_range(1.0f, 15.0f); + } + f->y_speed = frand_range(0.0f, 1.0f) < 0.5f ? frand_range(1.0f, 4.0f) : frand_range(-1.0f, -4.0f); + + // all fish start out offscreen + f->onscreen = 0; + + // he's swimming + f->swimming = 1; + + // anim instance + anim_play_struct aps; + + if(f->left){ + anim_play_init(&aps, Fish_left_anim, (int)f->x, (int)f->y); + f->a = anim_play(&aps); + + // doh. cancel him + if(f->a == NULL){ + f->swimming = 0; + } else { + f->a->screen_id = GS_STATE_MAIN_MENU; + f->a->looped = 1; + f->a->framerate_independent = 1; + } + } else { + anim_play_init(&aps, Fish_right_anim, (int)f->x, (int)f->y); + f->a = anim_play(&aps); + + // doh. cancel him + if(f->a == NULL){ + f->swimming = 0; + } else { + f->a->screen_id = GS_STATE_MAIN_MENU; + f->a->looped = 1; + f->a->framerate_independent = 1; + } + } +} + +void fish_flush(fish *f) +{ + // bogus + if(f == NULL){ + return; + } + + // release his render instance + if(f->a != NULL){ + anim_release_render_instance(f->a); + f->a = NULL; + } + + // no longer swimming + f->swimming = 0; +} + +void fishtank_start() +{ + int idx; + + if(Fish_inited){ + return; + } + + // try and load the fish anim + Fish_left_anim = anim_load(FISH_LEFT_ANIM_NAME); + if(Fish_left_anim == NULL){ + return; + } + Fish_right_anim = anim_load(FISH_RIGHT_ANIM_NAME); + if(Fish_right_anim == NULL){ + return; + } + + // no anim instances + for(idx=0; idxswimming){ + continue; + } + + // move him along + f->x += f->x_speed * flFrametime; + f->y += f->y_speed * flFrametime; + + // is he currently onscreen ? + onscreen = 0; + if( (f->x < (float)gr_screen.max_w) && ((f->x + FISH_ANIM_WIDTH) >= 0.0f) && + (f->y < (float)gr_screen.max_h) && ((f->y + FISH_ANIM_HEIGHT) >= 0.0f) ){ + onscreen = 1; + } + + // if he was onscreen before, but is no longer, flush him and make a new fish + if(f->onscreen && !onscreen){ + fish_flush(f); + + fish_generate(); + continue; + } + + // otherwise just mark his current status + f->onscreen = onscreen; + + // render + if(f->onscreen){ + // set coords + f->a->x = (int)f->x; + f->a->y = (int)f->y; + + anim_render_one(GS_STATE_MAIN_MENU, f->a, flFrametime); + } + } +} + diff --git a/src/menuui/mainhallmenu.cpp b/src/menuui/mainhallmenu.cpp new file mode 100644 index 0000000..e8b3d5a --- /dev/null +++ b/src/menuui/mainhallmenu.cpp @@ -0,0 +1,2151 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/MainHallMenu.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for main-hall menu code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 62 10/28/99 2:04a Jefff + * fixed a string + * + * 61 9/15/99 6:28p Dave + * No load mission button in RELEASE_REAL + * + * 60 9/13/99 4:52p Dave + * RESPAWN FIX + * + * 59 9/13/99 11:30a Dave + * Added checkboxes and functionality for disabling PXO banners as well as + * disabling d3d zbuffer biasing. + * + * 58 9/07/99 6:55p Jefff + * added jump-to-campaign-mission cheat + * + * 57 9/07/99 4:01p Dave + * Fixed up a string.tbl paroblem (self destruct message). Make sure IPX + * does everything properly (setting up address when binding). Remove + * black rectangle background from UI_INPUTBOX. + * + * 56 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 55 9/06/99 1:30a Dave + * Intermediate checkin. Started on enforcing CD-in-drive to play the + * game. + * + * 54 9/03/99 1:31a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 53 9/01/99 11:02p Dave + * Made head guy look right when not animating. + * + * 52 8/27/99 12:04a Dave + * Campaign loop screen. + * + * 51 8/26/99 9:45a Dave + * First pass at easter eggs and cheats. + * + * 50 8/25/99 11:57a Jefff + * freespace -> freespace 2 in tooltip strings + * + * 49 8/25/99 10:50a Dave + * Added music to the mainhall.tbl + * + * 48 8/24/99 8:55p Dave + * Make sure nondimming pixels work properly in tech menu. + * + * 47 8/18/99 11:44a Jefff + * minor string fixes + * + * 46 8/10/99 4:45p Jefff + * changed a string + * + * 45 8/06/99 1:01p Andsager + * disable quick start (g) for demo + * + * 44 8/05/99 10:34a Jefff + * more mouseover sound fixes + * + * 43 8/04/99 9:12p Andsager + * Add campaign room popup allowing restart of campaign in demo. + * + * 42 8/04/99 5:36p Dave + * Make glide and D3D switch out properly. + * + * 41 8/04/99 4:35p Jefff + * fixed tech room & options sounds playing only once + * + * 40 8/03/99 5:41p Jefff + * made "f1 for help" draw after animations, and stop if help overlay + * active + * + * 39 8/03/99 4:42p Jefff + * small f1 text fix + * + * 38 8/03/99 3:48p Jefff + * + * 37 8/02/99 9:13p Dave + * Added popup tips. + * + * 36 8/02/99 2:26p Jefff + * "press f1.." text reworked + * + * 35 8/02/99 12:19p Andsager + * disable "L" load screen + * + * 34 8/02/99 11:12a Jefff + * adjusted tooltip shader stuff for more a more pleasing effect. + * + * 33 7/30/99 6:05p Jefff + * added shader behind tooltip text + * + * 32 7/30/99 4:20p Andsager + * Added user click sounds to main hall + * + * 31 7/29/99 10:47p Dave + * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs. + * + * 30 7/27/99 6:53p Dave + * Hi-res main hall support. + * + * 29 7/16/99 1:49p Dave + * 8 bit aabitmaps. yay. + * + * 28 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 27 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 26 6/25/99 2:51p Jasons + * Changed wording of network error message. + * + * 25 6/21/99 1:30p Alanl + * changed main menu music tag + * + * 24 6/19/99 3:56p Dave + * Moved main hall definitions into a table file. Whee! + * + * 23 6/11/99 11:13a Dave + * last minute changes before press tour build. + * + * 22 5/09/99 8:57p Dave + * Final E3 build preparations. + * + * 21 4/25/99 3:02p Dave + * Build defines for the E3 build. + * + * 20 4/12/99 10:07p Dave + * Made network startup more forgiving. Added checkmarks to dogfight + * screen for players who hit commit. + * + * 19 3/28/99 5:58p Dave + * Added early demo code. Make objects move. Nice and framerate + * independant, but not much else. Don't use yet unless you're me :) + * + * 18 3/25/99 5:47p Dave + * Removed the whee + * + * 17 3/19/99 9:51a Dave + * Checkin to repair massive source safe crash. Also added support for + * pof-style nebulae, and some new weapons code. + * + * 16 2/25/99 4:19p Dave + * Added multiplayer_beta defines. Added cd_check define. Fixed a few + * release build warnings. Added more data to the squad war request and + * response packets. + * + * 15 2/03/99 11:44a Dave + * Fixed d3d transparent textures. + * + * 14 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 13 1/27/99 9:56a Dave + * Temporary checkin of beam weapons for Dan to make cool sounds. + * + * 12 12/31/98 11:23a Dave + * Put mini-indicators for status of ships.tbl and weapons.tbl in the + * lower left corner of the main hall. + * + * 11 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 10 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 9 11/20/98 4:08p Dave + * Fixed flak effect in multiplayer. + * + * 8 11/20/98 11:16a Dave + * Fixed up IPX support a bit. Making sure that switching modes and + * loading/saving pilot files maintains proper state. + * + * 7 11/19/98 4:57p Dave + * Ignore PXO option if IPX is selected. + * + * 6 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 5 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 4 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 103 10/02/98 3:22p Allender + * fix up the -connect option and fix the -port option + * + * 102 8/20/98 5:31p Dave + * Put in handy multiplayer logfile system. Now need to put in useful + * applications of it all over the code. + * + * 101 8/07/98 10:40a Allender + * new command line flags for starting netgames. Only starting currently + * works, and PXO isn't implemented yet + * + * 100 7/13/98 10:46a Lawrance + * Index another localized string + * + * 99 7/10/98 1:13a Allender + * lots of small multiplayer update changes. Code in launcher to specify + * connection speed. A couple of small fixes regarding empty mission + * files. Time out players after 10 second when they don't connect on + * their reliable socket. + * + * 98 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 97 6/05/98 9:50a Lawrance + * OEM changes + * + * 96 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 95 5/22/98 9:02p Allender + * remove G key from main hall + * + * 94 5/22/98 10:54a Allender + * new dialog information for networking to tell user if connection type + * doesn't match the setup specified type + * + * 93 5/15/98 5:15p Dave + * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy + * status for team vs. team. Put in asserts to check for invalid team vs. + * team situations. + * + * 92 5/13/98 12:23a Lawrance + * Don't init main hall if already inited... not needed now, but may be + * useful if we don't always want to call main_hall_close() when switching + * states + * + * 91 5/12/98 2:46a Dave + * Rudimentary communication between Parallax Online and freespace. Can + * get and store channel lists. + * + * 90 5/08/98 5:33p Lawrance + * check for CD when trying to enter ready room in single player + * + * + * $NoKeywords: $ + * + */ + +#include "mainhallmenu.h" +#include "palman.h" +#include "bmpman.h" +#include "2d.h" +#include "gamesequence.h" +#include "animplay.h" +#include "key.h" +#include "timer.h" +#include "ui.h" +#include "snazzyui.h" +#include "player.h" +#include "audiostr.h" +#include "gamesnd.h" +#include "eventmusic.h" +#include "mouse.h" +#include "contexthelp.h" +#include "cmdline.h" +#include "psnet.h" +#include "multiui.h" +#include "multiutil.h" +#include "popup.h" +#include "rtvoice.h" +#include "osapi.h" +#include "playermenu.h" +#include "freespace.h" +#include "multi_voice.h" +#include "multi.h" +#include "alphacolors.h" +#include "demo.h" +#include "fishtank.h" + +// #include "movie.h" + +// ---------------------------------------------------------------------------- +// MAIN HALL DATA DEFINES +// +#define MAX_RANDOM_INTERCOM_SOUNDS 10 +#define NUM_RANDOM_INTERCOM_SOUNDS_0 3 +#define NUM_RANDOM_INTERCOM_SOUNDS_1 3 + +#define MAX_MISC_ANIMATIONS 10 +#define NUM_MISC_ANIMATIONS_0 2 +#define NUM_MISC_ANIMATIONS_1 4 + +#define MAX_DOOR_ANIMATIONS 10 +#define NUM_DOOR_ANIMATIONS_0 6 +#define NUM_DOOR_ANIMATIONS_1 6 + +#define MAX_DOOR_SOUNDS 10 +#define NUM_DOOR_SOUNDS_0 6 +#define NUM_DOOR_SOUNDS_1 6 + +#define MISC_ANIM_MODE_LOOP 0 // loop the animation +#define MISC_ANIM_MODE_HOLD 1 // play to the end and hold the animation +#define MISC_ANIM_MODE_TIMED 2 // uses timestamps to determine when a finished anim should be checked again + +#define NUM_REGIONS 7 // (6 + 1 for multiplayer equivalent of campaign room) +typedef struct main_hall_defines { + // bitmap and mask + char bitmap[MAX_FILENAME_LEN+1]; + char mask[MAX_FILENAME_LEN+1]; + + // music + char music[MAX_FILENAME_LEN+1]; + + // intercom defines ------------------- + + // # of intercom sounds + int num_random_intercom_sounds; + + // random (min/max) delays between playing intercom sounds + int intercom_delay[MAX_RANDOM_INTERCOM_SOUNDS][2]; + + // intercom sounds themselves + int intercom_sounds[MAX_RANDOM_INTERCOM_SOUNDS]; + + // intercom sound pan values + float intercom_sound_pan[MAX_RANDOM_INTERCOM_SOUNDS]; + + + // misc animations -------------------- + + // # of misc animations + int num_misc_animations; + + // filenames of the misc animations + char misc_anim_name[MAX_MISC_ANIMATIONS][MAX_FILENAME_LEN+1]; + + // Time until we will next play a given misc animation, min delay, and max delay + int misc_anim_delay[MAX_MISC_ANIMATIONS][3]; + + // coords of where to play the misc anim + int misc_anim_coords[MAX_MISC_ANIMATIONS][2]; + + // misc anim play modes (see MISC_ANIM_MODE_* above) + int misc_anim_modes[MAX_MISC_ANIMATIONS]; + + // panning values for each of the misc anims + float misc_anim_sound_pan[MAX_MISC_ANIMATIONS]; + + // [N][0] == # of sounds, [N][1-9] sound index + int misc_anim_special_sounds[MAX_MISC_ANIMATIONS][10]; + + // [N][0] == # of triggers, [N][1-9] >= frame num + int misc_anim_special_trigger[MAX_MISC_ANIMATIONS][10]; + + // [N][0] == # of handles, [N][1-9] == sound handle num + int misc_anim_sound_handles[MAX_MISC_ANIMATIONS][10]; + + // [N][0] == # of handles, [N][1-9] == sound "should" be playing + int misc_anim_sound_flag[MAX_MISC_ANIMATIONS][10]; + + + // door animations -------------------- + + // # of door animations + int num_door_animations; + + // filenames of the door animations + char door_anim_name[MAX_DOOR_ANIMATIONS][MAX_FILENAME_LEN+1]; + + // first pair : coords of where to play a given door anim + // second pair : center of a given door anim in windowed mode + int door_anim_coords[MAX_DOOR_ANIMATIONS][4]; + + + // door sounds ------------------------ + + // # of door sounds + int num_door_sounds; + + // sounds for each region (open/close) + int door_sounds[MAX_DOOR_SOUNDS][2]; + + // pan values for the door sounds + float door_sound_pan[MAX_DOOR_SOUNDS]; + + + // region descriptions ---------------- + + // text (tooltip) description + char *region_descript[NUM_REGIONS]; + + // y coord of where to draw tooltip text + int region_yval; + +} main_hall_defines; + + +// use main hall 0 by default +main_hall_defines Main_hall_defines[GR_NUM_RESOLUTIONS][NUM_MAIN_HALLS]; +main_hall_defines *Main_hall = &Main_hall_defines[0][0]; + +int Vasudan_funny = 0; +int Vasudan_funny_plate = -1; + +char Main_hall_campaign_cheat[512] = ""; + +// ---------------------------------------------------------------------------- +// MISC interface data +// +// is the main hall inited (for reentrancy) +int Main_hall_inited = 0; + +// handle to the playing music +int Main_hall_music_handle = -1; + +// background bitmap handle +int Main_hall_bitmap; + +// background bitmap mask handle +int Main_hall_mask; + +// variable used for automatic netgame starting/joining +int Main_hall_netgame_started = 0; + +// bitmap struct for th background mask bitmap +bitmap *Main_hall_mask_bitmap; + +// actual data for the background mask bitmap +ubyte *Main_hall_mask_data; + +int Main_hall_mask_w, Main_hall_mask_h; + + +// ---------------------------------------------------------------------------- +// MOUSE clicking stuff +// +// indicates whether a right click occured +int Main_hall_right_click; + +// use this to cycle through the selectable regions instead of the mouse's current region +int Main_hall_last_clicked_region; + +// use this to determine how long the cursor has to linger on a region before it starts playing +#define MAIN_HALL_REGION_LINGER 175 // in ms +int Main_hall_region_linger_stamp = -1; + +// handle any right clicks which may have occured +void main_hall_handle_right_clicks(); + + +// ---------------------------------------------------------------------------- +// RANDOM intercom sounds +// + +// next random intercom sound to play +int Main_hall_next_intercom_sound = 0; + +// delay for the next intercom sound +int Main_hall_next_intercom_sound_stamp = -1; + +// handle to any playing instance of a random intercom sound +int Main_hall_intercom_sound_handle = -1; + +// handle any details related to random intercom sounds +void main_hall_handle_random_intercom_sounds(); + + +// ---------------------------------------------------------------------------- +// MISC animations +// + +// the misc animations themselves +anim *Main_hall_misc_anim[MAX_MISC_ANIMATIONS]; + +// the instance of a given misc animation +anim_instance *Main_hall_misc_anim_instance[MAX_MISC_ANIMATIONS]; + +// handle starting, stopping and randomizing misc animations +void main_hall_handle_misc_anims(); + +// cull any finished misc animation instances +void main_hall_cull_misc_anim_instances(); + +// render all playing misc animations +void main_hall_render_misc_anims(float frametime); + + +// ---------------------------------------------------------------------------- +// DOOR animations (not all of these are doors anymore, but they're doorlike _regions_) +// +#define DOOR_TEXT_X 100 +#define DOOR_TEXT_Y 450 + +// the door animations themselves +anim *Main_hall_door_anim[MAX_DOOR_ANIMATIONS]; + +// the instance of a given door animation +anim_instance *Main_hall_door_anim_instance[MAX_DOOR_ANIMATIONS]; + +// render all playing door animations +void main_hall_render_door_anims(float frametime); + + +// ---------------------------------------------------------------------------- +// SNAZZY MENU stuff +// +#define NUM_MAIN_HALL_REGIONS 10 +#define NUM_MAIN_HALL_MOUSE_REGIONS 6 + +// region mask #'s (identifiers) +#define EXIT_REGION 0 +#define BARRACKS_REGION 1 +#define READY_ROOM_REGION 2 +#define TECH_ROOM_REGION 3 +#define OPTIONS_REGION 4 +#define CAMPAIGN_ROOM_REGION 5 +#define MULTIPLAYER_REGION 10 +#define LOAD_MISSION_REGION 11 +#define QUICK_START_REGION 12 +#define SKILL_LEVEL_REGION 13 + +// all the menu regions in the main hall +MENU_REGION Main_hall_region[NUM_MAIN_HALL_REGIONS]; + +// # of regions (options) on this screen. parsed from a table +int Main_hall_num_options; + +// region over which the mouse is currently residing, or -1 if over no region +// NOTE : you should nevery change this directly. Always use main_hall_handle_mouse_location(int) +// to do this. Otherwise, the door opening and closing animations will get screwed up +int Main_hall_mouse_region; + +// set this to skip a frame +int Main_hall_frame_skip; + +// do any necessary processing based upon the mouse location +void main_hall_handle_mouse_location(int cur_region); + +// if the mouse has moved off of the currently active region, handle the anim accordingly +void main_hall_mouse_release_region(int region); + +// if the mouse has moved on this region, handle it accordingly +void main_hall_mouse_grab_region(int region); + + +// ---------------------------------------------------------------------------- +// SOUND data / handlers +// - + +// toaster oven room sound idex +#define TOASTER_REGION 3 + +// everyone's favorite desk guardian +#define ALLENDER_REGION 4 + +// handles to the sound instances of the doors opening/closing +int Main_hall_door_sound_handles[MAX_DOOR_SOUNDS] = { + -1,-1,-1,-1,-1,-1 +}; + +// sound handle for looping ambient sound +int Main_hall_ambient_loop = -1; + +// cull any door sounds that have finished playing +void main_hall_cull_door_sounds(); + +// handle starting, stopping and reversing "door" animations +void main_hall_handle_region_anims(); + + +// ---------------------------------------------------------------------------- +// warning/notification messages +// +#define MAIN_HALL_NOTIFY_TIME 3500 + +// timestamp for the notification messages +int Main_hall_notify_stamp = -1; + +// text to display as the current notification message +char Main_hall_notify_text[300]=""; + +// set the current notification string and the associated timestamp +void main_hall_set_notify_string(char *str); + +// handle any drawing, culling, etc of notification messages +void main_hall_notify_do(); + + +// ---------------------------------------------------------------------------- +// MISC functions +// + +// upper _RIGHT_ corner for the version text +#define MAIN_HALL_VERSION_X 630 +#define MAIN_HALL_VERSION_Y 467 + +// main hall help overlay ID +int Main_hall_overlay_id; + +// blit the freespace version # +void main_hall_blit_version(); + +// blit any necessary tooltips +void main_hall_maybe_blit_tooltips(); + +// shader for behind tooltips +shader Main_hall_tooltip_shader; + +// num pixels shader is above/below tooltip text +static int Main_hall_tooltip_padding[GR_NUM_RESOLUTIONS] = { + 4, // GR_640 + 7, // GR_1024 +}; +static int Main_hall_f1_text_frame = 0; +static int F1_text_done = 0; + +// read in main hall table +void main_hall_read_table(); + +// "press f1" for help stuff +#define MAIN_HALL_HELP_TIME 5000 +int Main_hall_help_stamp = -1; +void main_hall_process_help_stuff(); + + +// ---------------------------------------------------------------------------- +// VOICE RECORDING STUFF +// + +// are we currently recording voice? +int Recording = 0; + + +// called when multiplayer clicks on the ready room door. May pop up dialog depending on network +// connection status and errors +void main_hall_do_multi_ready() +{ + int error; + + error = psnet_get_network_status(); + switch( error ) { + case NETWORK_ERROR_NO_TYPE: + popup( PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "You have not defined your type of Internet connection. Please run the Launcher, hit the setup button, and go to the Network tab and choose your connection type.", 360)); + break; + case NETWORK_ERROR_NO_WINSOCK: + popup( PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "Winsock is not installed. You must have TCP/IP and Winsock installed to play multiplayer FreeSpace.", 361)); + break; + case NETWORK_ERROR_NO_PROTOCOL: + if(Multi_options_g.protocol == NET_TCP){ + popup( PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "TCP/IP protocol not found. This protocol is required for multiplayer FreeSpace.", 362)); + } else { + Assert(Multi_options_g.protocol == NET_IPX); + popup( PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "IPX protocol not found. This protocol is required for multiplayer FreeSpace.", 362)); + } + break; + case NETWORK_ERROR_CONNECT_TO_ISP: + popup( PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "You have selected Dial Up Networking as your type of connection to the Internet. You are not currently connected. You must connect to your ISP before continuing on past this point.", 363)); + break; + case NETWORK_ERROR_LAN_AND_RAS: + popup( PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "You have indicated that you use a LAN for networking. You also appear to be dialed into your ISP. Please disconnect from your service provider, or choose Dial Up Networking.", 364)); + break; + + case NETWORK_ERROR_NONE: + default: + break; + } + + // if our selected protocol is not active + if((Multi_options_g.protocol == NET_TCP) && !Tcp_active){ + popup( PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "You have selected TCP/IP for multiplayer Freespace, but the TCP/IP protocol was not detected on your machine.", 362)); + return; + } + if((Multi_options_g.protocol == NET_IPX) && !Ipx_active){ + popup( PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "You have selected IPX for multiplayer Freespace, but the IPX protocol was not detected on your machine.", 1402)); + return; + } + + if ( error != NETWORK_ERROR_NONE ){ + return; + } + + // 7/9/98 -- MWA. Deal with the connection speed issue. make a call to the multiplayer code to + // determine is a valid connection setting exists + if ( Multi_connection_speed == CONNECTION_SPEED_NONE ) { + popup( PF_NO_NETWORKING, 1, POPUP_OK, XSTR( "You must define your connection speed. Please run the Launcher, hit the setup button, and go to the Network tab and choose your connection speed.", 986) ); + return; + } + + // go to parallax online +#ifdef MULTIPLAYER_BETA_BUILD // do we want this for FS2_DEMO + Multi_options_g.pxo = 1; + Multi_options_g.protocol = NET_TCP; + gameseq_post_event( GS_EVENT_PXO ); +#else + + // go to the regular join game screen + gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME ); +#endif + + // select protocol + psnet_use_protocol(Multi_options_g.protocol); +} + +// blit some small color indicators to show whether ships.tbl and weapons.tbl are valid +// green == valid, red == invalid. +// ships.tbl will be on the left, weapons.tbl on the right +int Mh_ship_table_status[GR_NUM_RESOLUTIONS][2] = { + { 1, 479 }, + { 1, 767 } +}; +int Mh_weapon_table_status[GR_NUM_RESOLUTIONS][2] = { + { 3, 479 }, + { 3, 767 } +}; +void main_hall_blit_table_status() +{ + // blit ship table status + gr_set_color_fast(Game_ships_tbl_valid ? &Color_bright_green : &Color_bright_red); + gr_line(Mh_ship_table_status[gr_screen.res][0], Mh_ship_table_status[gr_screen.res][1], Mh_ship_table_status[gr_screen.res][0], Mh_ship_table_status[gr_screen.res][1]); + + // blit weapon table status + gr_set_color_fast(Game_weapons_tbl_valid ? &Color_bright_green : &Color_bright_red); + gr_line(Mh_weapon_table_status[gr_screen.res][0], Mh_weapon_table_status[gr_screen.res][1], Mh_weapon_table_status[gr_screen.res][0], Mh_ship_table_status[gr_screen.res][1]); +} + +// bash the player to a specific mission in a campaign +void main_hall_campaign_cheat() +{ + char *ret = popup_input(0, XSTR("Enter mission name.\n\n* This will destroy all legitimate progress in this campaign. *", -1)); + + // yay + if(ret != NULL) { + // strcpy(Main_hall_campaign_cheat, ret); + mission_campaign_jump_to_mission(ret); + } +} + +// ------------------------------------------------------------------------------------------------------------------- +// FUNCTION DEFINITIONS BEGIN +// + +// initialize the main hall proper +void main_hall_init(int main_hall_num) +{ + if ( Main_hall_inited ) { + return; + } + + int idx,s_idx; + char temp[100], whee[100]; + + // read in the main hall table + main_hall_read_table(); + + // create the snazzy interface and load up the info from the table + snazzy_menu_init(); + read_menu_tbl(NOX("MAIN HALL"), temp, whee, Main_hall_region, &Main_hall_num_options, 0); + + // assign the proper main hall data + Assert((main_hall_num >= 0) && (main_hall_num < NUM_MAIN_HALLS)); + Main_hall = &Main_hall_defines[gr_screen.res][main_hall_num]; + + // tooltip strings + Main_hall->region_descript[0] = XSTR( "Exit FreeSpace 2", 353); + Main_hall->region_descript[1] = XSTR( "Barracks - Manage your FreeSpace 2 pilots", 354); + Main_hall->region_descript[2] = XSTR( "Ready room - Start or continue a campaign", 355); + Main_hall->region_descript[3] = XSTR( "Tech room - View specifications of FreeSpace 2 ships and weaponry", 356); + Main_hall->region_descript[4] = XSTR( "Options - Change your FreeSpace 2 options", 357); + Main_hall->region_descript[5] = XSTR( "Campaign Room - View all available campaigns", 358); + Main_hall->region_descript[6] = XSTR( "Multiplayer - Start or join a multiplayer game", 359); + + // init tooltip shader + float gray_intensity = 0.02f; // nearly black + float c = (gr_screen.mode == GR_DIRECT3D) ? 0.11f : 0.07f; // adjust for renderer differences + gr_create_shader(&Main_hall_tooltip_shader, gray_intensity, gray_intensity, gray_intensity, c); + + // load the background bitmap + Main_hall_bitmap = bm_load(Main_hall->bitmap); + if(Main_hall_bitmap < 0){ + nprintf(("General","WARNING! Couldn't load main hall background bitmap %s\n", Main_hall->bitmap)); + } + + // remove any multiplayer flags from the game mode + Game_mode &= ~(GM_MULTIPLAYER); + + // set the interface palette +#ifndef HARDWARE_ONLY + palette_use_bm_palette(Main_hall_bitmap); +#endif + + Main_hall_mask_w = -1; + Main_hall_mask_h = -1; + + // load the mask + Main_hall_mask = bm_load(Main_hall->mask); + if (Main_hall_mask < 0) { + Error(LOCATION,"Could not load in %s!", Main_hall->mask); + } else { + // get a pointer to bitmap by using bm_lock(), so we can feed it to he snazzy menu system + Main_hall_mask_bitmap = bm_lock(Main_hall_mask, 8, BMP_AABITMAP); + Main_hall_mask_data = (ubyte*)Main_hall_mask_bitmap->data; + bm_get_info(Main_hall_mask, &Main_hall_mask_w, &Main_hall_mask_h); + } + + // load up the misc animations, and nullify all the delay timestamps for the misc animations + for(idx=0;idxnum_misc_animations;idx++) { + Main_hall_misc_anim[idx] = NULL; + Main_hall_misc_anim[idx] = anim_load(Main_hall->misc_anim_name[idx]); + if(Main_hall_misc_anim[idx] == NULL) { + nprintf(("General","WARNING!, Could not load misc %s anim in main hall\n",Main_hall->misc_anim_name)); + } + + // null out the animation instances + Main_hall_misc_anim_instance[idx] = NULL; + + // null out the delay timestamps + Main_hall->misc_anim_delay[idx][0] = -1; + } + + // load up the door animations + for(idx=0;idxnum_door_animations;idx++) { + Main_hall_door_anim[idx] = NULL; + Main_hall_door_anim[idx] = anim_load(Main_hall->door_anim_name[idx]); + if(Main_hall_door_anim[idx] == NULL){ + nprintf(("General","WARNING!, Could not load door anim %s in main hall\n",Main_hall->door_anim_name[idx])); + } + + // null out the animation instances + Main_hall_door_anim_instance[idx] = NULL; + } + + // load in help overlay bitmap + if(Main_hall == &Main_hall_defines[gr_screen.res][0]) { + Main_hall_overlay_id = MH_OVERLAY; + } else { + Assert(Main_hall == &Main_hall_defines[gr_screen.res][1]); + Main_hall_overlay_id = MH2_OVERLAY; + } + help_overlay_load(Main_hall_overlay_id); + help_overlay_set_state(Main_hall_overlay_id,0); + + // check to see if the "very first pilot" flag is set, and load the overlay if so + if (!F1_text_done) { + if (Main_hall_f1_text_frame == 0) { + Main_hall_help_stamp = timestamp(MAIN_HALL_HELP_TIME); + } else { + F1_text_done = 1; + } + } + +/* + if(Player_select_very_first_pilot) { + Main_hall_help_stamp = timestamp(MAIN_HALL_HELP_TIME); + + // don't display the "press f1" message more than once + Player_select_very_first_pilot = 0; + } else { + Main_hall_help_stamp = -1; + } +*/ + Main_hall_region_linger_stamp = -1; + + strcpy(Main_hall_campaign_cheat, ""); + + // zero out the door sounds + for(idx=0;idxnum_door_sounds;idx++){ + Main_hall_door_sound_handles[idx] = -1; + } + + // zero out the misc anim sounds + for(idx=0;idxnum_misc_animations;idx++){ + for(s_idx = 1;s_idx < 10;s_idx++){ + Main_hall->misc_anim_sound_handles[idx][s_idx] = -1; + Main_hall->misc_anim_sound_flag[idx][s_idx] = 0; + } + } + + // skip the first frame + Main_hall_frame_skip = 1; + + // initialize the music + main_hall_start_music(); + + // initialize the main hall notify text + Main_hall_notify_stamp = 1; + + // initialize the random intercom sound stuff + Main_hall_next_intercom_sound = 0; + Main_hall_next_intercom_sound_stamp = -1; + Main_hall_intercom_sound_handle = -1; + + // set the placement of the mouse cursor (start at the ready room) + Main_hall_mouse_region = -1; + Main_hall_last_clicked_region = READY_ROOM_REGION; + mouse_set_pos(Main_hall->door_anim_coords[READY_ROOM_REGION][2],Main_hall->door_anim_coords[READY_ROOM_REGION][3]); + + Main_hall_inited = 1; + + // determine if we have a right click + Main_hall_right_click = mouse_down(MOUSE_RIGHT_BUTTON); + + // set the game_mode based on the type of player + Assert( Player != NULL ); + if ( Player->flags & PLAYER_FLAGS_IS_MULTI ){ + Game_mode = GM_MULTIPLAYER; + } else { + Game_mode = GM_NORMAL; + } + + if ( (Cmdline_start_netgame || (Cmdline_connect_addr != NULL)) && !Main_hall_netgame_started ) { + Main_hall_netgame_started = 1; + main_hall_do_multi_ready(); + } +} + +void main_hall_exit_game() +{ +#if defined(NDEBUG) || defined(INTERPLAYQA) + int choice; + + // stop music first + main_hall_stop_music(); + main_hall_stop_ambient(); + choice = popup( PF_NO_NETWORKING | PF_BODY_BIG, 2, POPUP_NO, POPUP_YES, XSTR( "Exit Game?", 365)); + if ( choice == 1 ) { + gameseq_post_event(GS_EVENT_QUIT_GAME); + } else { + main_hall_start_music(); + main_hall_start_ambient(); + } +#else + gameseq_post_event(GS_EVENT_QUIT_GAME); +#endif +} + + +// do a frame for the main hall +void main_hall_do(float frametime) +{ + int code, key, snazzy_action; + + // need to ensure ambient is playing, since it may be stopped by a playing movie + main_hall_start_ambient(); + + // handle any animation details + main_hall_handle_misc_anims(); + main_hall_handle_region_anims(); + + // handle any random intercom sound details + main_hall_handle_random_intercom_sounds(); + + // handle any mouse clicks + main_hall_handle_right_clicks(); + + // handle any sound details + main_hall_cull_door_sounds(); + + // process any keypresses/mouse events + snazzy_action = -1; + code = snazzy_menu_do(Main_hall_mask_data, Main_hall_mask_w, Main_hall_mask_h, Main_hall_num_options, Main_hall_region, &snazzy_action, 1, &key); + + if(key){ + extern void game_process_cheats(int k); + game_process_cheats(key); + } + switch(key){ + case KEY_ENTER: + snazzy_action = SNAZZY_CLICKED; + break; + +#ifndef NDEBUG + case KEY_1: + // no soup for you! + // movie_play("endprt2b.mve", 0); + break; + case KEY_2: + // no soup for you! + // movie_play_two("endprt2a.mve", "endprt2b.mve", 0); + break; + case KEY_3: + main_hall_campaign_cheat(); + break; + case KEY_DEBUGGED + KEY_D: + demo_start_playback("test.fsd"); + break; + } +#else + } +#endif + + // do any processing based upon what happened to the snazzy menu + switch (snazzy_action) { + case SNAZZY_OVER: + main_hall_handle_mouse_location(code); + break; + + case SNAZZY_CLICKED: + switch (code) { + // clicked on the exit region + case EXIT_REGION: + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + main_hall_exit_game(); + break; + + // clicked on the readyroom region + case READY_ROOM_REGION: +#ifdef MULTIPLAYER_BETA_BUILD + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + Player->flags |= PLAYER_FLAGS_IS_MULTI; + main_hall_do_multi_ready(); +#elif defined(E3_BUILD) || defined(PRESS_TOUR_BUILD) + gameseq_post_event(GS_EVENT_NEW_CAMPAIGN); +#else + if (Player->flags & PLAYER_FLAGS_IS_MULTI){ + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + main_hall_do_multi_ready(); + } else { + if(strlen(Main_hall_campaign_cheat)){ + gameseq_post_event(GS_EVENT_CAMPAIGN_CHEAT); + } else { + gameseq_post_event(GS_EVENT_NEW_CAMPAIGN); + } + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + } +#endif + break; + + // clicked on the tech room region + case TECH_ROOM_REGION: +#if defined(FS2_DEMO) + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + game_feature_not_in_demo_popup(); +#else + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + gameseq_post_event( GS_EVENT_TECH_MENU ); +#endif + break; + + // clicked on the options region + case OPTIONS_REGION: + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + break; + + // clicked on the campaign toom region + case CAMPAIGN_ROOM_REGION: +#if !defined(MULTIPLAYER_BETA_BUILD) && !defined(E3_BUILD) && !defined(PRESS_TOUR_BUILD) + +#ifdef FS2_DEMO + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + { + //game_feature_not_in_demo_popup(); + int reset_campaign = popup(PF_USE_AFFIRMATIVE_ICON|PF_BODY_BIG, 2, "Exit", "Restart Campaign", "Campaign Room only available in full version. However, you may restart the campaign."); + if (reset_campaign == 1) { + mission_campaign_savefile_delete(Campaign.filename); + mission_campaign_load(Campaign.filename); + mission_campaign_next_mission(); + } + } + +#else + if(Player->flags & PLAYER_FLAGS_IS_MULTI){ + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + main_hall_set_notify_string(XSTR( "Campaign Room not valid for multiplayer pilots", 366)); + } else { + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + gameseq_post_event(GS_EVENT_CAMPAIGN_ROOM); + } +#endif + +#endif + break; + + // clicked on the multiplayer region + case MULTIPLAYER_REGION: +#if defined(DEMO) || defined(OEM_BUILD) // not for FS2_DEMO + game_feature_not_in_demo_popup(); +#else + if (Player->flags & PLAYER_FLAGS_IS_MULTI){ + // NOTE : this isn't a great thing to be calling this anymore. But we'll leave it for now + gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME ); + } else { + main_hall_set_notify_string(XSTR( "Not a valid multiplayer pilot!!", 367)); + } +#endif + break; + + // load mission key was pressed + case LOAD_MISSION_REGION: +#ifdef RELEASE_REAL +#else + #if !(defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)) + //#if !defined(NDEBUG) || defined(INTERPLAYQA) + if (Player->flags & PLAYER_FLAGS_IS_MULTI){ + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + main_hall_set_notify_string(XSTR( "Load Mission not valid for multiplayer pilots", 368)); + } else { + #ifdef GAME_CD_CHECK + // if ( !game_do_cd_check() ) { + // break; + // } + #endif + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + gameseq_post_event( GS_EVENT_LOAD_MISSION_MENU ); + } + //#endif + #endif +#endif + break; + + // quick start a game region + case QUICK_START_REGION: +#if !defined(NDEBUG) && !defined(FS2_DEMO) + if (Player->flags & PLAYER_FLAGS_IS_MULTI){ + main_hall_set_notify_string(XSTR( "Quick Start not valid for multiplayer pilots", 369)); + } else { + + if (Num_recent_missions > 0) { + strncpy( Game_current_mission_filename, Recent_missions[0], MAX_FILENAME_LEN ); + } else { + mission_load_up_campaign(); + strncpy( Game_current_mission_filename, Campaign.missions[0].name, MAX_FILENAME_LEN ); + } + + Campaign.current_mission = -1; + gameseq_post_event(GS_EVENT_START_GAME_QUICK); + } +#endif + break; + + // clicked on the barracks region + case BARRACKS_REGION: + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + gameseq_post_event( GS_EVENT_BARRACKS_MENU ); + break; + + // increate the skill level + case SKILL_LEVEL_REGION: + char temp[100]; + + game_increase_skill_level(); + sprintf(temp, XSTR( "Skill level set to %s.", 370), Skill_level_names(Game_skill_level)); + main_hall_set_notify_string(temp); + break; + + // escape was pressed + case ESC_PRESSED: + // if there is a help overlay active, then don't quit the game - just kill the overlay + if(!help_overlay_active(Main_hall_overlay_id)){ + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + main_hall_exit_game(); + } + // kill the overlay + else { + help_overlay_set_state(Main_hall_overlay_id,0); + } + break; + } + + // if the escape key wasn't pressed handle any mouse position related events + if (code != ESC_PRESSED){ + main_hall_handle_mouse_location(code); + } + break; + + default: + main_hall_handle_mouse_location(-1); + break; + } + + if ( mouse_down(MOUSE_LEFT_BUTTON) ) { + help_overlay_set_state(Main_hall_overlay_id, 0); + } + + // draw the background bitmap + gr_reset_clip(); + GR_MAYBE_CLEAR_RES(Main_hall_bitmap); + if(Main_hall_bitmap >= 0){ + gr_set_bitmap(Main_hall_bitmap); + gr_bitmap(0, 0); + } + + // draw any pending notification messages + main_hall_notify_do(); + + // render misc animations + main_hall_render_misc_anims(frametime); + + // render door animtions + main_hall_render_door_anims(frametime); + + // blit any appropriate tooltips + main_hall_maybe_blit_tooltips(); + + // fishtank + fishtank_process(); + + // process any help "hit f1" timestamps and display any messages if necessary + if (!F1_text_done) { + main_hall_process_help_stuff(); + } + + // blit help overlay if active + help_overlay_maybe_blit(Main_hall_overlay_id); + + // blit the freespace version # + main_hall_blit_version(); + + // blit ship and weapon table status + main_hall_blit_table_status(); + + // if we're in nice D3D texture format +#ifndef NDEBUG + gr_set_color_fast(&Color_white); + + // d3d + if(gr_screen.mode == GR_DIRECT3D){ + if(Bm_pixel_format == BM_PIXEL_FORMAT_ARGB_D3D){ + gr_string(320, gr_screen.max_h - 10, "D3D ARGB"); + } + extern int D3d_rendition_uvs; + extern int D3D_32bit; + extern int D3D_fog_mode; + extern int D3D_zbias; + if(D3d_rendition_uvs){ + gr_string(320, gr_screen.max_h - 20, "D3D rendition"); + } + if(D3D_32bit){ + gr_string(320, gr_screen.max_h - 30, "D3D 32bit"); + } + gr_printf(320, gr_screen.max_h - 40, "Fog : %d", D3D_fog_mode); + gr_printf(320, gr_screen.max_h - 50, "Zbias : %d", D3D_zbias); + // extern void d3d_test(); + // d3d_test(); + } else if(gr_screen.mode == GR_GLIDE){ + extern int Glide_voodoo3; + if(Glide_voodoo3){ + gr_string(320, gr_screen.max_h - 20, "VOODOO 3"); + } + } +#endif + + gr_flip(); + + // maybe run the player tips popup +// #if defined(FS2_DEMO) && defined(NDEBUG) + player_tips_popup(); +// #endif + + // if we were supposed to skip a frame, then stop doing it after 1 frame + if(Main_hall_frame_skip){ + Main_hall_frame_skip = 0; + } +} + +// close the main hall proper +void main_hall_close() +{ + int idx,s_idx; + + if(!Main_hall_inited){ + return; + } + + // unload the main hall bitmap + if(Main_hall_bitmap != -1){ + bm_unload(Main_hall_bitmap); + } + + // unload any bitmaps + if(Main_hall_mask >= 0){ + // make sure we unlock the mask bitmap so it can be unloaded + bm_unlock(Main_hall_mask); + if(!bm_unload(Main_hall_mask)){ + nprintf(("General","WARNING! Couldn't unload main hall mask bitmap!\n")); + } + } + + // free up any (possibly) playing misc animation handles + for(idx=0;idxnum_misc_animations;idx++){ + if(Main_hall_misc_anim_instance[idx]!=NULL){ + anim_stop_playing(Main_hall_misc_anim_instance[idx]); + Main_hall_misc_anim_instance[idx] = NULL; + } + } + + // free up any (possibly) playing door animation handles + for(idx=0;idxnum_door_animations;idx++){ + if(Main_hall_door_anim_instance[idx]!=NULL){ + anim_stop_playing(Main_hall_door_anim_instance[idx]); + Main_hall_door_anim_instance[idx] = NULL; + } + } + + + // free up any misc animations/instances + for(idx=0;idxnum_misc_animations;idx++){ + if((Main_hall_misc_anim_instance[idx]!=NULL) && (anim_playing(Main_hall_misc_anim_instance[idx]))){ + Main_hall_misc_anim_instance[idx] = NULL; + } + if(Main_hall_misc_anim[idx]!=NULL){ + if(anim_free(Main_hall_misc_anim[idx]) == -1){ + nprintf(("General","WARNING!, Could not free up misc anim %s in main hall\n",Main_hall->misc_anim_name[idx])); + } + } + } + + // free up any door animations/instances + for(idx=0;idxnum_door_animations;idx++){ + if((Main_hall_door_anim_instance[idx]!=NULL) && (anim_playing(Main_hall_door_anim_instance[idx]))){ + Main_hall_door_anim_instance[idx] = NULL; + } + if(Main_hall_door_anim[idx]!=NULL){ + if(anim_free(Main_hall_door_anim[idx]) == -1){ + nprintf(("General","WARNING!, Could not free up door anim %s in main hall\n",Main_hall->door_anim_name[idx])); + } + } + } + + // stop any playing door sounds + for(idx=0;idxnum_door_sounds-2;idx++){ // don't cut off the glow sounds (requested by Dan) + if((Main_hall_door_sound_handles[idx] != -1) && snd_is_playing(Main_hall_door_sound_handles[idx])){ + snd_stop(Main_hall_door_sound_handles[idx]); + Main_hall_door_sound_handles[idx] = -1; + } + } + + // stop any playing misc animation sounds + for(idx=0;idxnum_misc_animations;idx++){ + for(s_idx=1;s_idx<10;s_idx++){ + if(snd_is_playing(Main_hall->misc_anim_sound_handles[idx][s_idx])){ + snd_stop(Main_hall->misc_anim_sound_handles[idx][s_idx]); + Main_hall->misc_anim_sound_handles[idx][s_idx] = -1; + } + } + } + + // unload the overlay bitmap + help_overlay_unload(Main_hall_overlay_id); + + // close any snazzy menu details + snazzy_menu_close(); + + // restore + palette_restore_palette(); + + // no fish + fishtank_stop(); + + // not inited anymore + Main_hall_inited = 0; +} + +// start the main hall music playing +void main_hall_start_music() +{ + // start a looping ambient sound + main_hall_start_ambient(); + + // if we have selected no music, then don't do this + if ( Cmdline_freespace_no_music ) { + return; + } + + int main_hall_spooled_music_index = event_music_get_spooled_music_index(Main_hall->music); + + if ((Main_hall_music_handle == -1) && (main_hall_spooled_music_index != -1)) { + char *music_wavfile_name = Spooled_music[main_hall_spooled_music_index].filename; + if (music_wavfile_name != NULL) { + Main_hall_music_handle = audiostream_open( music_wavfile_name, ASF_EVENTMUSIC ); + if ( Main_hall_music_handle != -1 ) + audiostream_play(Main_hall_music_handle, Master_event_music_volume); + } + else { + nprintf(("Warning", "No music file exists to play music at the main menu!\n")); + } + } +} + +// stop the main hall music +void main_hall_stop_music() +{ + if ( Main_hall_music_handle != -1 ) { + audiostream_close_file(Main_hall_music_handle); + Main_hall_music_handle = -1; + } +} + +// do any necessary instantiation of misc animations +void main_hall_handle_misc_anims() +{ + int idx,s_idx; + + if(Main_hall_frame_skip) + return; + + for(idx=0;idxnum_misc_animations;idx++){ + // if the anim isn't playing + if(Main_hall_misc_anim_instance[idx] == NULL){ + // if the timestamp is -1, then reset it to some random value (based on MIN and MAX) and continue + if(Main_hall->misc_anim_delay[idx][0] == -1){ + Main_hall->misc_anim_delay[idx][0] = timestamp(Main_hall->misc_anim_delay[idx][1] + + (int)(((float)rand()/(float)RAND_MAX) * (float)(Main_hall->misc_anim_delay[idx][2] - Main_hall->misc_anim_delay[idx][1]))); + + // if the timestamp is not -1 and has popped, play the anim and make the timestap -1 + } else if (timestamp_elapsed(Main_hall->misc_anim_delay[idx][0]) && Main_hall_misc_anim[idx]) { + anim_play_struct aps; + + anim_play_init(&aps, Main_hall_misc_anim[idx], Main_hall->misc_anim_coords[idx][0], Main_hall->misc_anim_coords[idx][1]); + aps.screen_id = GS_STATE_MAIN_MENU; + aps.framerate_independent = 1; + + Main_hall_misc_anim_instance[idx] = anim_play(&aps); + + // kill the timestamp + Main_hall->misc_anim_delay[idx][0] = -1; + + // reset the "should be playing" flags + for(s_idx=1;s_idx<10;s_idx++){ + Main_hall->misc_anim_sound_flag[idx][s_idx] = 0; + } + } + } + // if the anim is playing + else { + // check to see if any special trigger points have been reached by the animation + // since the frame triggers must be in ascending order, we will count down so that we don't trigger too many sounds + for(s_idx=Main_hall->misc_anim_special_sounds[idx][0]; s_idx > 0; s_idx--){ + // if we've passed the trigger point, then play the sound and break out of the loop + if((Main_hall_misc_anim_instance[idx]->frame_num >= Main_hall->misc_anim_special_trigger[idx][s_idx]) && !Main_hall->misc_anim_sound_flag[idx][s_idx]){ + Main_hall->misc_anim_sound_flag[idx][s_idx] = 1; + + // if the sound is already playing, then kill it. This is a pretty safe thing to do since we can assume that + // by the time we get to this point again, the sound will have been long finished + if(snd_is_playing(Main_hall->misc_anim_sound_handles[idx][s_idx])){ + snd_stop(Main_hall->misc_anim_sound_handles[idx][s_idx]); + Main_hall->misc_anim_sound_handles[idx][s_idx] = -1; + } + // play the sound + Main_hall->misc_anim_sound_handles[idx][s_idx] = snd_play(&Snds_iface[Main_hall->misc_anim_special_sounds[idx][s_idx]],Main_hall->misc_anim_sound_pan[idx]); + break; + } + } + + // if the animation mode is MISC_ANIM_MODE_HOLD, pause it at the last frame + if((Main_hall->misc_anim_modes[idx] == MISC_ANIM_MODE_HOLD) && (Main_hall_misc_anim_instance[idx]->frame_num == Main_hall_misc_anim_instance[idx]->stop_at)){ + anim_pause(Main_hall_misc_anim_instance[idx]); + Main_hall_misc_anim_instance[idx]->stop_now = FALSE; + } + + // if the animation mode is MISC_ANIM_MODE_LOOP, check to see if it should be looped + if((Main_hall->misc_anim_modes[idx] == MISC_ANIM_MODE_LOOP) && (Main_hall_misc_anim_instance[idx]->frame_num == Main_hall_misc_anim_instance[idx]->stop_at)){ + anim_release_render_instance(Main_hall_misc_anim_instance[idx]); + + // start it playing again + anim_play_struct aps; + + anim_play_init(&aps, Main_hall_misc_anim[idx], Main_hall->misc_anim_coords[idx][0], Main_hall->misc_anim_coords[idx][1]); + aps.screen_id = GS_STATE_MAIN_MENU; + aps.framerate_independent = 1; + + Main_hall_misc_anim_instance[idx] = anim_play(&aps); + + // kill the timestamp + Main_hall->misc_anim_delay[idx][0] = -1; + + // reset the "should be playing" flags + for(s_idx=1;s_idx<10;s_idx++){ + Main_hall->misc_anim_sound_flag[idx][s_idx] = 0; + } + } + + // cull any misc animations which are marked as done (!is_playing) + if(!anim_playing(Main_hall_misc_anim_instance[idx])){ + Main_hall_misc_anim_instance[idx] = NULL; + } + } + } +} + +// render all playing misc animations +void main_hall_render_misc_anims(float frametime) +{ + int idx; + + // HACKETY HACK HACK - always render misc anim 3 first, if it is playing + if(Main_hall_misc_anim_instance[2] != NULL){ + anim_render_one(GS_STATE_MAIN_MENU,Main_hall_misc_anim_instance[2],frametime); + } + + // render all other animations + for(idx=0;idxnum_door_animations;idx++){ + if((Main_hall_door_anim_instance[idx] != NULL) && !anim_playing(Main_hall_door_anim_instance[idx])){ + Main_hall_door_anim_instance[idx] = NULL; + } + } + + // go through each region animation + for(idx=0;idxnum_door_animations;idx++){ + // if the instance is not null and the animation is playing + if((Main_hall_door_anim_instance[idx] != NULL) && anim_playing(Main_hall_door_anim_instance[idx])){ + // check to see if we should hold a given door "open" + if((Main_hall_mouse_region == idx) && (Main_hall_door_anim_instance[idx]->frame_num == Main_hall_door_anim_instance[idx]->stop_at)){ + anim_pause(Main_hall_door_anim_instance[idx]); + Main_hall_door_anim_instance[idx]->stop_now = FALSE; + } + // check to see if we should close a door being held open + if((Main_hall_mouse_region != idx) && (Main_hall_door_anim_instance[idx]->paused)){ + anim_unpause(Main_hall_door_anim_instance[idx]); + } + } + } +} + +// do any necessary processing based upon the mouse location +void main_hall_handle_mouse_location(int cur_region) +{ + if(Main_hall_frame_skip) + return; + + if(cur_region > NUM_MAIN_HALL_MOUSE_REGIONS) { + // MWA -- inserted return since Int3() was tripped when hitting L from main + // menu. + return; + } + + // if the mouse is now over a resgion + if (cur_region != -1) { + // if we're still over the same region we were last frame, check stuff + if (cur_region == Main_hall_mouse_region) { + // if we have a linger timestamp set and it has expired, then get moving + if ((Main_hall_region_linger_stamp != -1) && timestamp_elapsed(Main_hall_region_linger_stamp)) { + main_hall_mouse_grab_region(cur_region); + + // release the region linger stamp + Main_hall_region_linger_stamp = -1; + } + } else { + // if we're currently on another region, release it + if ((Main_hall_mouse_region != -1) && (cur_region != Main_hall_mouse_region)) { + main_hall_mouse_release_region(Main_hall_mouse_region); + } + + // set the linger time + if (Main_hall_region_linger_stamp == -1) { + Main_hall_mouse_region = cur_region; + Main_hall_region_linger_stamp = timestamp(MAIN_HALL_REGION_LINGER); + } + } + } + // if it was over a region but isn't anymore, release that region + else { + if (Main_hall_mouse_region != -1) { + main_hall_mouse_release_region(Main_hall_mouse_region); + Main_hall_mouse_region = -1; + + // release the region linger timestamp + Main_hall_region_linger_stamp = -1; + } + } +} + +// if the mouse has moved off of the currently active region, handle the anim accordingly +void main_hall_mouse_release_region(int region) +{ + if(Main_hall_frame_skip){ + return; + } + + // if the animation is currently playing in the forward direction, change direction and be done, otherwise don't do a thing + if ( (Main_hall_door_anim_instance[region] != NULL) && anim_playing(Main_hall_door_anim_instance[region]) && (Main_hall_door_anim_instance[region]->direction == ANIM_DIRECT_FORWARD)){ + anim_reverse_direction(Main_hall_door_anim_instance[region]); + } + + // check for door sounds, ignoring the OPTIONS_REGION (which isn't a door) + if ((Main_hall_door_anim_instance[region] != NULL)) { + // don't stop the toaster oven or microwave regions from playing all the way through + if (Main_hall_door_sound_handles[region] != -1) { + snd_stop(Main_hall_door_sound_handles[region]); + } + Main_hall_door_sound_handles[region] = snd_play(&Snds_iface[Main_hall->door_sounds[region][1]], Main_hall->door_sound_pan[region]); + + // make sure to set the sound to play from the right spot + snd_set_pos(Main_hall_door_sound_handles[region], &Snds_iface[SND_MAIN_HALL_DOOR_CLOSE], + (float)(Main_hall_door_anim_instance[region]->start_at - Main_hall_door_anim_instance[region]->frame_num) / (float)Main_hall_door_anim_instance[region]->parent->total_frames, 1); + } +} + +// if the mouse has moved on this region, handle it accordingly +void main_hall_mouse_grab_region(int region) +{ + if (Main_hall_frame_skip) { + return; + } + + // if the animation is not playing, start it playing + if ( !Main_hall_door_anim_instance[region] ) { + if ( Main_hall_door_anim[region] ) { + anim_play_struct aps; + + anim_play_init(&aps, Main_hall_door_anim[region], Main_hall->door_anim_coords[region][0], Main_hall->door_anim_coords[region][1]); + aps.screen_id = GS_STATE_MAIN_MENU; + aps.framerate_independent = 1; + + Main_hall_door_anim_instance[region] = anim_play(&aps); + } + } + // otherwise if its playing in the reverse direction, change it to the forward direction + else if (Main_hall_door_anim_instance[region]->direction == ANIM_DIRECT_REVERSE) { + anim_reverse_direction(Main_hall_door_anim_instance[region]); + } + + // check for opening/starting sounds + // kill the currently playing sounds if necessary + if(Main_hall_door_sound_handles[region] != -1){ + snd_stop(Main_hall_door_sound_handles[region]); + } + Main_hall_door_sound_handles[region] = snd_play(&Snds_iface[Main_hall->door_sounds[region][0]],Main_hall->door_sound_pan[region]); + + // start the sound playing at the right spot relative to the completion of the animation + if(Main_hall_door_anim_instance[region]->frame_num != -1){ + snd_set_pos(Main_hall_door_sound_handles[region],&Snds_iface[SND_MAIN_HALL_DOOR_OPEN], + (float)Main_hall_door_anim_instance[region]->frame_num / (float)Main_hall_door_anim_instance[region]->parent->total_frames,1); + } +} + +// handle any right clicks which may have occured +void main_hall_handle_right_clicks() +{ + int new_region; + + if(Main_hall_frame_skip) + return; + + // check to see if the button has been clicked + if(!Main_hall_right_click){ + if(mouse_down(MOUSE_RIGHT_BUTTON)){ + // cycle through the available regions + if(Main_hall_last_clicked_region == NUM_MAIN_HALL_MOUSE_REGIONS - 1){ + new_region = 0; + } else + new_region = Main_hall_last_clicked_region + 1; + + // set the position of the mouse cursor and the newly clicked region + mouse_set_pos(Main_hall->door_anim_coords[new_region][2],Main_hall->door_anim_coords[new_region][3]); + + main_hall_handle_mouse_location(new_region); + Main_hall_last_clicked_region = new_region; + + // set the mouse as being clicked + Main_hall_right_click = 1; + } + } + // set the mouse as being unclicked + else if(Main_hall_right_click && !(mouse_down(MOUSE_RIGHT_BUTTON))){ + Main_hall_right_click = 0; + } +} + +// cull any door sounds that have finished playing +void main_hall_cull_door_sounds() +{ + int idx; + // basically just set the handle of any finished sound to be -1, so that we know its free any where else in the code we may need it + for(idx=0;idxnum_door_sounds;idx++){ + if((Main_hall_door_sound_handles[idx] != -1) && !snd_is_playing(Main_hall_door_sound_handles[idx])){ + Main_hall_door_sound_handles[idx] = -1; + } + } +} + +void main_hall_handle_random_intercom_sounds() +{ + // if we have no timestamp for the next random sound, then set on + if((Main_hall_next_intercom_sound_stamp == -1) && (Main_hall_intercom_sound_handle == -1)){ + Main_hall_next_intercom_sound_stamp = timestamp((int)(((float)rand()/(float)RAND_MAX) * + (float)(Main_hall->intercom_delay[Main_hall_next_intercom_sound][1] + - Main_hall->intercom_delay[Main_hall_intercom_sound_handle][0])) ); + } + + // if the there is no sound playing + if(Main_hall_intercom_sound_handle == -1){ + // if the timestamp has popped, play a sound + if((Main_hall_next_intercom_sound_stamp != -1) && (timestamp_elapsed(Main_hall_next_intercom_sound_stamp))){ + // play the sound + Main_hall_intercom_sound_handle = snd_play(&Snds_iface[Main_hall->intercom_sounds[Main_hall_next_intercom_sound]]); + + // unset the timestamp + Main_hall_next_intercom_sound_stamp = -1; + } + } + // if the sound is playing + else { + // if the sound has finished, set the timestamp and continue + if(!snd_is_playing(Main_hall_intercom_sound_handle)){ + // increment the next sound + if(Main_hall_next_intercom_sound >= (Main_hall->num_random_intercom_sounds-1)){ + Main_hall_next_intercom_sound = 0; + } else { + Main_hall_next_intercom_sound++; + } + + // set the timestamp + Main_hall_next_intercom_sound_stamp = timestamp((int)(((float)rand()/(float)RAND_MAX) * + (float)(Main_hall->intercom_delay[Main_hall_next_intercom_sound][1] + - Main_hall->intercom_delay[Main_hall_next_intercom_sound][0])) ); + + // release the sound handle + Main_hall_intercom_sound_handle = -1; + } + } +} + +// set the notification string with its decay timeout +void main_hall_set_notify_string(char *str) +{ + strcpy(Main_hall_notify_text,str); + Main_hall_notify_stamp = timestamp(MAIN_HALL_NOTIFY_TIME); +} + +void main_hall_notify_do() +{ + // check to see if we should try and do something + if(Main_hall_notify_stamp != -1){ + // if the text time has expired + if(timestamp_elapsed(Main_hall_notify_stamp)){ + strcpy(Main_hall_notify_text,""); + Main_hall_notify_stamp = -1; + } else { + int w,h; + gr_set_color_fast(&Color_bright); + + gr_get_string_size(&w,&h,Main_hall_notify_text); + gr_printf((gr_screen.max_w - w)/2, gr_screen.max_h - 40, Main_hall_notify_text); + } + } +} + +// start a looping ambient sound for main hall +void main_hall_start_ambient() +{ + int play_ambient_loop = 0; + + if ( Main_hall_ambient_loop == -1 ) { + play_ambient_loop = 1; + } else { + if ( !snd_is_playing(Main_hall_ambient_loop) ) { + play_ambient_loop = 1; + } + } + + if ( play_ambient_loop ) { + Main_hall_ambient_loop = snd_play_looping(&Snds_iface[SND_MAIN_HALL_AMBIENT]); + } +} + +// stop a looping ambient sound for the main hall +void main_hall_stop_ambient() +{ + if ( Main_hall_ambient_loop != -1 ) { + snd_stop(Main_hall_ambient_loop); + Main_hall_ambient_loop = -1; + } +} + +// Reset the volume of the looping ambient sound. This is called from the options +// screen when the looping ambient sound might be playing. +void main_hall_reset_ambient_vol() +{ + if ( Main_hall_ambient_loop >= 0 ) { + snd_set_volume(Main_hall_ambient_loop, Snds_iface[SND_MAIN_HALL_AMBIENT].default_volume); + } +} + +// blit the freespace version # +void main_hall_blit_version() +{ + char version_string[100]; + int w; + + // format the version string + get_version_string(version_string); + + // get the length of the string + gr_get_string_size(&w,NULL,version_string); + + // print the string out in the lower right corner + gr_set_color_fast(&Color_white); + gr_string(gr_screen.max_w - 55, gr_screen.max_h - 12, version_string); +} + +// blit any necessary tooltips +void main_hall_maybe_blit_tooltips() +{ + int w; + int text_index; + + // if we're over no region - don't blit anything + if(Main_hall_mouse_region < 0) { + return; + } + + // get the index of the proper text to be using + if(Main_hall_mouse_region == READY_ROOM_REGION) { + // if this is a multiplayer pilot, the ready room region becomes the multiplayer region + if(Player->flags & PLAYER_FLAGS_IS_MULTI){ + text_index = NUM_REGIONS - 1; + } else { + text_index = READY_ROOM_REGION; + } + } else { + text_index = Main_hall_mouse_region; + } + + // set the color and blit the string + if(!help_overlay_active(Main_hall_overlay_id)) { + int shader_y = (Main_hall->region_yval) - Main_hall_tooltip_padding[gr_screen.res]; // subtract more to pull higher + // get the width of the string + gr_get_string_size(&w, NULL, Main_hall->region_descript[text_index]); + + gr_set_shader(&Main_hall_tooltip_shader); + gr_shade(0, shader_y, gr_screen.clip_width, (gr_screen.clip_height - shader_y)); + + gr_set_color_fast(&Color_bright_white); + gr_string((gr_screen.max_w - w)/2, Main_hall->region_yval, Main_hall->region_descript[text_index]); + } +} + + +void main_hall_process_help_stuff() +{ + int w, h; + char str[255]; + + // if the timestamp has popped, don't do anything + if(Main_hall_help_stamp == -1) { + return; + } + + // if the timestamp has popped, advance frame + if(timestamp_elapsed(Main_hall_help_stamp)) { + Main_hall_f1_text_frame++; + } + + // otherwise print out the message + strcpy(str, XSTR( "Press F1 for help", 371)); + gr_get_string_size(&w, &h, str); + + int y_anim_offset = Main_hall_f1_text_frame; + + // if anim is off the screen finally, stop altogether + if ( (y_anim_offset >= (2*Main_hall_tooltip_padding[gr_screen.res]) + h) || (help_overlay_active(Main_hall_overlay_id)) ) { + Main_hall_f1_text_frame = -1; + Main_hall_help_stamp = -1; + F1_text_done = 1; + return; + } + + // set the color and print out text and shader + gr_set_color_fast(&Color_bright_white); + gr_shade(0, 0, gr_screen.max_w, (2*Main_hall_tooltip_padding[gr_screen.res]) + h - y_anim_offset); + gr_string((gr_screen.max_w - w)/2, Main_hall_tooltip_padding[gr_screen.res] - y_anim_offset, str); +} + +// what main hall we're on (should be 0 or 1) +int main_hall_id() +{ + // only 1 of 2 main halls + if(Main_hall == &Main_hall_defines[gr_screen.res][0]){ + return 0; + } + + return 1; +} + +// read in main hall table +void main_hall_read_table() +{ + main_hall_defines *m, temp; + int count, idx, s_idx, m_idx; + + // read the file in + read_file_text("mainhall.tbl"); + reset_parse(); + + // go for it + count = 0; + while(!optional_string("#end")){ + + // read in 2 resolutions + for(m_idx=0; m_idx= NUM_MAIN_HALLS){ + m = &temp; + } else { + m = &Main_hall_defines[m_idx][count]; + } + + // ready + required_string("$Main Hall"); + + // bitmap and mask + required_string("+Bitmap:"); + stuff_string(m->bitmap, F_NAME, NULL, MAX_FILENAME_LEN); + required_string("+Mask:"); + stuff_string(m->mask, F_NAME, NULL, MAX_FILENAME_LEN); + required_string("+Music:"); + stuff_string(m->music, F_NAME, NULL, MAX_FILENAME_LEN); + + // intercom sounds + required_string("+Num Intercom Sounds:"); + stuff_int(&m->num_random_intercom_sounds); + for(idx=0; idxnum_random_intercom_sounds; idx++){ + // intercom delay + required_string("+Intercom delay:"); + stuff_int(&m->intercom_delay[idx][0]); + stuff_int(&m->intercom_delay[idx][1]); + } + for(idx=0; idxnum_random_intercom_sounds; idx++){ + // intercom sound id + required_string("+Intercom sound:"); + stuff_int(&m->intercom_sounds[idx]); + } + for(idx=0; idxnum_random_intercom_sounds; idx++){ + // intercom pan + required_string("+Intercom pan:"); + stuff_float(&m->intercom_sound_pan[idx]); + } + + // misc animations + required_string("+Num Misc Animations:"); + stuff_int(&m->num_misc_animations); + for(idx=0; idxnum_misc_animations; idx++){ + // anim names + required_string("+Misc anim:"); + stuff_string(m->misc_anim_name[idx], F_NAME, NULL); + } + for(idx=0; idxnum_misc_animations; idx++){ + // anim delay + required_string("+Misc anim delay:"); + stuff_int(&m->misc_anim_delay[idx][0]); + stuff_int(&m->misc_anim_delay[idx][1]); + stuff_int(&m->misc_anim_delay[idx][2]); + } + for(idx=0; idxnum_misc_animations; idx++){ + // anim coords + required_string("+Misc anim coords:"); + stuff_int(&m->misc_anim_coords[idx][0]); + stuff_int(&m->misc_anim_coords[idx][1]); + } + for(idx=0; idxnum_misc_animations; idx++){ + // anim mode + required_string("+Misc anim mode:"); + stuff_int(&m->misc_anim_modes[idx]); + } + for(idx=0; idxnum_misc_animations; idx++){ + // anim pan + required_string("+Misc anim pan:"); + stuff_float(&m->misc_anim_sound_pan[idx]); + } + for(idx=0; idxnum_misc_animations; idx++){ + // anim sound id + required_string("+Misc anim sounds:"); + stuff_int(&m->misc_anim_special_sounds[idx][0]); + for(s_idx=0; s_idxmisc_anim_special_sounds[idx][0]; s_idx++){ + stuff_int(&m->misc_anim_special_sounds[idx][s_idx + 1]); + } + } + for(idx=0; idxnum_misc_animations; idx++){ + // anim sound triggers + required_string("+Misc anim trigger:"); + stuff_int(&m->misc_anim_special_trigger[idx][0]); + for(s_idx=0; s_idxmisc_anim_special_trigger[idx][0]; s_idx++){ + stuff_int(&m->misc_anim_special_trigger[idx][s_idx + 1]); + } + } + for(idx=0; idxnum_misc_animations; idx++){ + // anim sound handles + required_string("+Misc anim handles:"); + stuff_int(&m->misc_anim_sound_handles[idx][0]); + } + for(idx=0; idxnum_misc_animations; idx++){ + // anim sound flags + required_string("+Misc anim flags:"); + stuff_int(&m->misc_anim_sound_flag[idx][0]); + } + + // door animations + required_string("+Num Door Animations:"); + stuff_int(&m->num_door_animations); + for(idx=0; idxnum_door_animations; idx++){ + // door name + required_string("+Door anim:"); + stuff_string(m->door_anim_name[idx], F_NAME, NULL); + } + for(idx=0; idxnum_door_animations; idx++){ + // door coords + required_string("+Door coords:"); + stuff_int(&m->door_anim_coords[idx][0]); + stuff_int(&m->door_anim_coords[idx][1]); + stuff_int(&m->door_anim_coords[idx][2]); + stuff_int(&m->door_anim_coords[idx][3]); + } + for(idx=0; idxnum_door_animations; idx++){ + // door open and close sounds + required_string("+Door sounds:"); + stuff_int(&m->door_sounds[idx][0]); + stuff_int(&m->door_sounds[idx][1]); + } + for(idx=0; idxnum_door_animations; idx++){ + // door pan value + required_string("+Door pan:"); + stuff_float(&m->door_sound_pan[idx]); + } + + // tooltip y location + required_string("+Tooltip Y:"); + stuff_int(&m->region_yval); + for(idx=0; idxregion_descript[idx] = NULL; + } + } + + if(count < NUM_MAIN_HALLS){ + count++; + } + } + + // are we funny? + if(Vasudan_funny){ + Main_hall_defines[GR_640][1].door_sounds[OPTIONS_REGION][0] = SND_VASUDAN_BUP; + Main_hall_defines[GR_640][1].door_sounds[OPTIONS_REGION][1] = SND_VASUDAN_BUP; + Main_hall_defines[GR_1024][1].door_sounds[OPTIONS_REGION][0] = SND_VASUDAN_BUP; + Main_hall_defines[GR_1024][1].door_sounds[OPTIONS_REGION][1] = SND_VASUDAN_BUP; + + // set head anim. hehe + strcpy(Main_hall_defines[GR_640][1].door_anim_name[OPTIONS_REGION], "vhallheads"); + strcpy(Main_hall_defines[GR_1024][1].door_anim_name[OPTIONS_REGION], "2_vhallheads"); + + // set the background + strcpy(Main_hall_defines[GR_640][1].bitmap, "vhallhead"); + strcpy(Main_hall_defines[GR_1024][1].bitmap, "2_vhallhead"); + } +} + +// make the vasudan main hall funny +void main_hall_vasudan_funny() +{ + Vasudan_funny = 1; +} + + +/* +#include "3d.h" +int argh = -1; +matrix view = { + 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f +}; +*/ +void d3d_test() +{ + /* + vertex p1; + vector sun_pos = vmd_zero_vector; + sun_pos.z = 1.0f; + + if(argh == -1){ + argh = bm_load("sun01"); + bm_lock(argh, 16, BMP_TEX_XPARENT); + bm_unlock(argh); + } + + g3_start_frame(1); + g3_set_view_matrix(&vmd_zero_vector, &view, 0.5f); + g3_rotate_vertex(&p1, &sun_pos); + g3_project_vertex(&p1); + gr_zbuffer_set(GR_ZBUFF_NONE); + gr_set_bitmap( argh ); + g3_draw_bitmap(&p1, 0, 0.05f, TMAP_FLAG_TEXTURED | TMAP_FLAG_XPARENT); + g3_end_frame(); + */ +} + diff --git a/src/menuui/mainhalltemp.cpp b/src/menuui/mainhalltemp.cpp new file mode 100644 index 0000000..710e563 --- /dev/null +++ b/src/menuui/mainhalltemp.cpp @@ -0,0 +1,272 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/MainHallTemp.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file for main-hall menu code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 3 6/04/99 9:52a Dave + * Fixed some rendering problems. + * + * 2 6/03/99 10:15p Dave + * Put in temporary main hall screen. + * + * $NoKeywords: $ + * + */ + +#include "mainhallmenu.h" +#include "mainhalltemp.h" +#include "2d.h" +#include "ui.h" +#include "key.h" +#include "bmpman.h" +#include "gamesnd.h" +#include "gamesequence.h" +#include "missionparse.h" +#include "missioncampaign.h" +#include "player.h" +#include "freespace.h" + +// ------------------------------------------------------------------------------------------------------------------------ +// TEMP MAIN HALL DEFINES/VARS +// + +#define MHT_NUM_BUTTONS 6 + +char *Mht_bitmap_fname[GR_NUM_RESOLUTIONS] = { + "mht_background", // GR_640 + "2_mht_background" // GR_1024 +}; + +char *Mht_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = { + "mht_mask", // GR_640 + "2_mht_mask" // GR_1024 +}; + +// button defs +#define MHT_READY_ROOM 0 +#define MHT_CAMPAIGN_ROOM 1 +#define MHT_OPTIONS 2 +#define MHT_TECH_ROOM 3 +#define MHT_BARRACKS 4 +#define MHT_EXIT 5 + +UI_WINDOW Mht_window; // the window object for the join screen +int Mht_bitmap; // the background bitmap + +ui_button_info Mht_buttons[GR_NUM_RESOLUTIONS][MHT_NUM_BUTTONS] = { + { // GR_640 + ui_button_info( "MHT_00", 15, 194, -1, -1, 2 ), // ready room + ui_button_info( "MHT_01", 15, 222, -1, -1, 5 ), // campaigns + ui_button_info( "MHT_02", 14, 251, -1, -1, 4 ), // options + ui_button_info( "MHT_03", 14, 280, -1, -1, 3 ), // tech room + ui_button_info( "MHT_04", 14, 309, -1, -1, 1 ), // barracks + ui_button_info( "MHT_05", 16, 339, -1, -1, 0 ), // exit + }, + { // GR_1024 + ui_button_info( "2_MHT_00", 25, 312, -1, -1, 2 ), // ready room + ui_button_info( "2_MHT_01", 25, 357, -1, -1, 5 ), // campaigns + ui_button_info( "2_MHT_02", 24, 403, -1, -1, 4 ), // options + ui_button_info( "2_MHT_03", 24, 450, -1, -1, 3 ), // tech room + ui_button_info( "2_MHT_04", 25, 497, -1, -1, 1 ), // barracks + ui_button_info( "2_MHT_05", 27, 544, -1, -1, 0 ), // exit + } +}; + + +// ------------------------------------------------------------------------------------------------------------------------ +// TEMP MAIN HALL FUNCTIONS +// + +void mht_check_buttons(); +void mht_button_pressed(int n); +void mht_exit_game(); + +void mht_init() +{ + int idx; + + // create the interface window + Mht_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0); + Mht_window.set_mask_bmap(Mht_bitmap_mask_fname[gr_screen.res]); + + // load the background bitmap + Mht_bitmap = bm_load(Mht_bitmap_fname[gr_screen.res]); + if(Mht_bitmap < 0){ + // we failed to load the bitmap - this is very bad + Int3(); + } + + // create the interface buttons + for(idx=0; idxflags & PLAYER_FLAGS_IS_MULTI ){ + Game_mode = GM_MULTIPLAYER; + } else { + Game_mode = GM_NORMAL; + } +} + +void mht_do() +{ + int k = Mht_window.process(); + + // need to ensure ambient is playing, since it may be stopped by a playing movie + main_hall_start_ambient(); + + // process any keypresses + switch(k){ + case KEY_ESC : + mht_exit_game(); + break; + + case KEY_B: + gameseq_post_event( GS_EVENT_BARRACKS_MENU ); + break; + + case KEY_G: + if(Player->flags & PLAYER_FLAGS_IS_MULTI){ + break; + } + + if (Num_recent_missions > 0) { + strncpy( Game_current_mission_filename, Recent_missions[0], MAX_FILENAME_LEN ); + } else { + mission_load_up_campaign(); + strncpy( Game_current_mission_filename, Campaign.missions[0].name, MAX_FILENAME_LEN ); + } + + Campaign.current_mission = -1; + gameseq_post_event(GS_EVENT_START_GAME_QUICK); + break; + + case KEY_L: + gameseq_post_event( GS_EVENT_LOAD_MISSION_MENU ); + break; + + case KEY_F2: + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + break; + + case KEY_M: + if (Player->flags & PLAYER_FLAGS_IS_MULTI){ + main_hall_do_multi_ready(); + } + break; + } + + // process button presses + mht_check_buttons(); + + // draw the background, etc + gr_reset_clip(); + GR_MAYBE_CLEAR_RES(Mht_bitmap); + if(Mht_bitmap != -1){ + gr_set_bitmap(Mht_bitmap); + gr_bitmap(0,0); + } + Mht_window.draw(); + + // flip the buffer + gr_flip(); +} + +void mht_close() +{ + // unload any bitmaps + if(!bm_unload(Mht_bitmap)){ + nprintf(("General","WARNING : could not unload background bitmap %s\n", Mht_bitmap_fname[gr_screen.res])); + } + + // destroy the UI_WINDOW + Mht_window.destroy(); +} + +void mht_check_buttons() +{ + int idx; + for(idx=0; idxflags & PLAYER_FLAGS_IS_MULTI){ + main_hall_do_multi_ready(); + } else { + gameseq_post_event(GS_EVENT_NEW_CAMPAIGN); + + gamesnd_play_iface(SND_USER_SELECT); + } + break; + + case MHT_CAMPAIGN_ROOM: + gameseq_post_event(GS_EVENT_CAMPAIGN_ROOM); + gamesnd_play_iface(SND_USER_SELECT); + break; + + case MHT_OPTIONS: + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + gamesnd_play_iface(SND_USER_SELECT); + break; + + case MHT_TECH_ROOM: + gameseq_post_event( GS_EVENT_TECH_MENU ); + gamesnd_play_iface(SND_USER_SELECT); + break; + + case MHT_BARRACKS: + gameseq_post_event( GS_EVENT_BARRACKS_MENU ); + gamesnd_play_iface(SND_USER_SELECT); + break; + + case MHT_EXIT: + mht_exit_game(); + gamesnd_play_iface(SND_USER_SELECT); + break; + } +} + +void mht_exit_game() +{ + // stop music first + main_hall_stop_music(); + main_hall_stop_ambient(); + gameseq_post_event(GS_EVENT_QUIT_GAME); +} + diff --git a/src/menuui/optionsmenu.cpp b/src/menuui/optionsmenu.cpp new file mode 100644 index 0000000..31bcce0 --- /dev/null +++ b/src/menuui/optionsmenu.cpp @@ -0,0 +1,1727 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/OptionsMenu.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module that contains functions to drive the Options user interface + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 31 10/25/99 5:47p Jefff + * reassigned some xstr ids + * + * 30 10/14/99 2:50p Jefff + * localization fixes + * + * 29 9/13/99 6:07p Jefff + * took out audiostream pausing to allow for correct volume adjustments + * + * 28 9/09/99 2:28p Jefff + * some detail slider #defines were switched around + * + * 27 9/08/99 2:38p Jefff + * sound pausing going to menu from game + * + * 26 8/05/99 3:40p Jefff + * hi-res text adjustments + * + * 25 8/04/99 4:06p Jefff + * fixed volume sliders blacking out at master volume of 1.0 + * + * 24 8/02/99 6:05p Jefff + * failure sound on skill slider when disabled in game + * + * 23 7/19/99 3:29p Dave + * Fixed gamma bitmap in the options screen. + * + * 22 7/19/99 2:13p Dave + * Added some new strings for Heiko. + * + * 21 7/18/99 9:54p Andsager + * Demo build clean up + * + * 20 7/15/99 7:15p Jefff + * Added sounds for skill select + * + * 19 7/15/99 4:10p Andsager + * Disable control_config hud_config in FS2_DEMO + * + * 18 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 17 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 16 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 15 6/25/99 11:59a Dave + * Multi options screen. + * + * 14 6/24/99 12:34a Dave + * Main options screen. + * + * 13 6/22/99 7:03p Dave + * New detail options screen. + * + * 12 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 11 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * 10 4/02/99 4:44p Jasenw + * + * 9 2/05/99 7:22p Neilk + * Fixed gamma bitmap and converted coordinates for multiple resolutions + * + * 8 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 7 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 6 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 5 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 4 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 3 10/09/98 2:57p Dave + * Starting splitting up OS stuff. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 123 6/12/98 7:13p Hoffoss + * Fixed options screen problem where it wasn't showing tooltips. + * + * 122 6/12/98 4:52p Hoffoss + * Added support for special characters in in forgeign languages. + * + * 121 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 120 6/05/98 9:50a Lawrance + * OEM changes + * + * 119 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 118 5/24/98 4:42p Dan + * AL: Fix several bugs related to pausing and enabling/disabling event + * music + * + * 117 5/20/98 5:18p John + * Made the gamma box draw as a bitmap, not a bunch of pixels. Speeds up + * the menu dramatically. + * + * 116 5/19/98 1:21p Duncan + * Use 'InterfacePalette' instad of 'OptionsMainPalette' + * + * 115 5/18/98 9:14p Dave + * Put in network config files support. + * + * 114 5/18/98 5:16p Hoffoss + * Changed code to use new palette Jasen told me to use. + * + * 113 5/16/98 3:40p Lawrance + * Don't restore the original details unless they've been stored! + * + * 112 5/13/98 10:03a Mitri + * AL: Fix some bugs as a result of recent rearranging of code + * + * 111 5/13/98 12:24a Lawrance + * Only load required bitmaps and masks for the options screen + * + * 110 5/08/98 3:52p Hoffoss + * Fixed placement of gamma graphic. + * + * 109 5/07/98 12:41p Hoffoss + * Added joystick deadzone slider support and mouse on/off support. + * + * 108 5/06/98 8:06p Dave + * Made standalone reset properly under weird conditions. Tweak + * optionsmulti screen. Upped MAX_WEAPONS to 350. Put in new launch + * countdown anim. Minro ui fixes/tweaks. + * + * 107 5/05/98 8:38p Hoffoss + * Added sensitivity adjustment to options menu and made it save to pilot + * file. + * + * 106 4/27/98 9:00a Jasen + * Updated coords for button change. + * + * 105 4/25/98 3:33p Allender + * do exit confirmation + * + * 104 4/22/98 12:34a Dave + * Make sure hud config and control config buttons draw properly in all + * tab modes. Make small tab buttons light up correctly in multi options + * screen. + * + * 103 4/21/98 12:14a Allender + * disable skill level buttons for multiplayer games + * + * 102 4/20/98 6:04p Dave + * Implement multidata cache flushing and xferring mission files to + * multidata. Make sure observers can't change hud config. Fix pilot image + * viewing in popup. Put in game status field. Tweaked multi options. + * + * 101 4/18/98 12:45p Dave + * Aesthetic changes to multi options screen. Put in missing exit button. + * oops. + * + * 100 4/17/98 5:27p Dave + * More work on the multi options screen. Fixed many minor ui todo bugs. + * + * + * $NoKeywords: $ + * +*/ + +#include "grinternal.h" +#include "ui.h" +#include "missionscreencommon.h" +#include "bmpman.h" +#include "gamesequence.h" +#include "key.h" +#include "managepilot.h" +#include "freespace.h" +#include "gamesnd.h" +#include "sound.h" +#include "eventmusic.h" +#include "mainhallmenu.h" +#include "audiostr.h" +#include "multi.h" +#include "psnet.h" +#include "popup.h" +#include "popupdead.h" +#include "missionbriefcommon.h" +#include "optionsmenu.h" +#include "optionsmenumulti.h" +#include "joy.h" +#include "mouse.h" +#include "osregistry.h" +#include "alphacolors.h" +#include "timer.h" +#include "neb.h" +#include "beam.h" + + +// will display a notification warning message +#define OPTIONS_NOTIFY_TIME 3500 +#define OPTIONS_NOTIFY_Y 450 + +#define NUM_BUTTONS 24 +#define NUM_ANIS 4 +#define NUM_TABS 3 +#define NUM_COMMONS 10 + +#define TABLESS 999 + +#define OPTIONS_TAB 0 +#define MULTIPLAYER_TAB 1 +#define DETAIL_LEVELS_TAB 2 +#define ABORT_GAME_BUTTON 3 +#define CONTROL_CONFIG_BUTTON 4 +#define HUD_CONFIG_BUTTON 5 +#define ACCEPT_BUTTON 6 + +#define BRIEF_VOICE_OFF 7 +#define BRIEF_VOICE_ON 8 +#define MOUSE_OFF 9 +#define MOUSE_ON 10 +#define GAMMA_DOWN 11 +#define GAMMA_UP 12 + +// detail level screen buttons +#define PLANETS_ON 13 +#define PLANETS_OFF 14 +#define HUD_TARGETVIEW_RENDER_ON 15 +#define HUD_TARGETVIEW_RENDER_OFF 16 +#define WEAPON_EXTRAS_ON 17 +#define WEAPON_EXTRAS_OFF 18 + +#define LOW_DETAIL_N 19 +#define MEDIUM_DETAIL_N 20 +#define HIGH_DETAIL_N 21 +#define VERY_HIGH_DETAIL_N 22 +#define CUSTOM_DETAIL_N 23 + +#define REPEAT (1<<0) +#define NO_MOUSE_OVER_SOUND (1<<1) + +// indicies for options coordinates +#define OPTIONS_X_COORD 0 +#define OPTIONS_Y_COORD 1 +#define OPTIONS_W_COORD 2 +#define OPTIONS_H_COORD 3 + +struct options_buttons { + char *filename; + int x, y; + int hotspot; + int tab; + int flags; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + options_buttons(char *name, int x1, int y1, int h, int t, int f = 0) : filename(name), x(x1), y(y1), hotspot(h), tab(t), flags(f) {} +}; + +options_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { + { // GR_640 + options_buttons("OPT_00", 17, 2, 0, -1), // options tab + options_buttons("OPT_01", 102, 2, 1, -1), // multiplayer tab + options_buttons("OPT_02", 170, 2, 2, -1), // detail levels tab + options_buttons("OPT_03", 10, 444, 3, -1), // abort game button + options_buttons("OPT_04", 411, 444, 4, -1), // control config button + options_buttons("OPT_05", 506, 444, 5, -1), // hud config + options_buttons("OPT_06", 576, 434, 6, -1), // accept button + + options_buttons("OMB_07", 51, 74, 7, OPTIONS_TAB, 2), // Briefing / debriefing voice toggle off + options_buttons("OMB_08", 106, 74, 8, OPTIONS_TAB, 2), // Briefing / debriefing voice toggle on + options_buttons("OMB_18", 51, 266, 18, OPTIONS_TAB, 2), // Mouse off + options_buttons("OMB_19", 106, 266, 19, OPTIONS_TAB, 2), // Mouse on + options_buttons("OMB_26", 578, 149, 26, OPTIONS_TAB, 1), // Gamma Down + options_buttons("OMB_27", 607, 149, 27, OPTIONS_TAB, 1), // Gamma Up + + options_buttons("ODB_21", 597, 261, 21, DETAIL_LEVELS_TAB, 2), // Planets On + options_buttons("ODB_20", 539, 261, 20, DETAIL_LEVELS_TAB, 2), // Planets Off + options_buttons("ODB_23", 597, 307, 23, DETAIL_LEVELS_TAB, 2), // Target View Rendering On + options_buttons("ODB_22", 539, 307, 22, DETAIL_LEVELS_TAB, 2), // Target View Rendering Off + options_buttons("ODB_25", 597, 354, 25, DETAIL_LEVELS_TAB, 2), // Weapon Extras On + options_buttons("ODB_24", 539, 354, 24, DETAIL_LEVELS_TAB, 2), // Weapon Extras Off + + options_buttons("ODB_14", 614, 76, 14, DETAIL_LEVELS_TAB, 2), // Low Preset Detail + options_buttons("ODB_15", 614, 96, 15, DETAIL_LEVELS_TAB, 2), // Medium Preset Detail + options_buttons("ODB_16", 614, 114, 16, DETAIL_LEVELS_TAB, 2), // High Preset Detail + options_buttons("ODB_17", 614, 133, 17, DETAIL_LEVELS_TAB, 2), // Highest Preset Detail + options_buttons("ODB_18", 614, 152, 18, DETAIL_LEVELS_TAB, 2), // Custom Detail + }, + { // GR_1024 + options_buttons("2_OPT_00", 27, 4, 0, -1), // options tab + options_buttons("2_OPT_01", 164, 4, 1, -1), // multiplayer tab + options_buttons("2_OPT_02", 272, 4, 2, -1), // detail levels tab + options_buttons("2_OPT_03", 16, 711, 3, -1), // abort game + options_buttons("2_OPT_04", 657, 711, 4, -1), // control config button + options_buttons("2_OPT_05", 809, 711, 5, -1), // hud config button + options_buttons("2_OPT_06", 922, 694, 6, -1), // accept button + + options_buttons("2_OMB_07", 81, 118, 7, OPTIONS_TAB, 2), // Briefing / debriefing voice toggle off + options_buttons("2_OMB_08", 170, 118, 8, OPTIONS_TAB, 2), // Briefing / debriefing voice toggle on + options_buttons("2_OMB_18", 81, 425, 18, OPTIONS_TAB, 2), // Mouse off + options_buttons("2_OMB_19", 170, 425, 19, OPTIONS_TAB, 2), // Mouse on + options_buttons("2_OMB_26", 925, 238, 26, OPTIONS_TAB, 1), // Gamma Down + options_buttons("2_OMB_27", 971, 238, 27, OPTIONS_TAB, 1), // Gamma Up + + options_buttons("2_ODB_21", 956, 417, 21, DETAIL_LEVELS_TAB, 2), // Planets On + options_buttons("2_ODB_20", 863, 417, 20, DETAIL_LEVELS_TAB, 2), // Planets Off + options_buttons("2_ODB_23", 956, 492, 23, DETAIL_LEVELS_TAB, 2), // Target View Rendering On + options_buttons("2_ODB_22", 863, 492, 22, DETAIL_LEVELS_TAB, 2), // Target View Rendering Off + options_buttons("2_ODB_25", 956, 567, 25, DETAIL_LEVELS_TAB, 2), // Weapon Extras On + options_buttons("2_ODB_24", 863, 567, 24, DETAIL_LEVELS_TAB, 2), // Weapon Extras Off + + options_buttons("2_ODB_14", 983, 122, 14, DETAIL_LEVELS_TAB, 2), // Low Preset Detail + options_buttons("2_ODB_15", 983, 153, 15, DETAIL_LEVELS_TAB, 2), // Medium Preset Detail + options_buttons("2_ODB_16", 983, 183, 16, DETAIL_LEVELS_TAB, 2), // High Preset Detail + options_buttons("2_ODB_17", 983, 213, 17, DETAIL_LEVELS_TAB, 2), // Highest Preset Detail + options_buttons("2_ODB_18", 983, 243, 18, DETAIL_LEVELS_TAB, 2), // Custom Detail + } +}; + +#define NUM_OPTIONS_SLIDERS 7 +#define OPT_SOUND_VOLUME_SLIDER 0 +#define OPT_MUSIC_VOLUME_SLIDER 1 +#define OPT_VOICE_VOLUME_SLIDER 2 +#define OPT_MOUSE_SENS_SLIDER 3 +#define OPT_JOY_SENS_SLIDER 4 +#define OPT_JOY_DEADZONE_SLIDER 5 +#define OPT_SKILL_SLIDER 6 + +op_sliders Options_sliders[GR_NUM_RESOLUTIONS][NUM_OPTIONS_SLIDERS] = { + { // GR_640 + op_sliders("OMB_10", 31, 139, -1, -1, 10, 20, 10, + "OMB_11", 11, 226, 137, + "OMB_09", 9, 4, 137 ), // sound fx volume slider + op_sliders("OMB_13", 31, 174, -1, -1, 13, 20, 10, + "OMB_14", 14, 226, 172, + "OMB_12", 12, 4, 172 ), // music volume slider + op_sliders("OMB_16", 31, 209, -1, -1, 16, 20, 10, + "OMB_17", 17, 226, 206, + "OMB_15", 15, 4, 206 ), // voice volume slider + op_sliders("OMB_20", 6, 316, -1, -1, 20, 20, 10, NULL, -1, -1, -1, NULL, -1, -1, -1), // mouse sensitivity + op_sliders("OMB_28", 440, 259, -1, -1, 28, 20, 10, NULL, -1, -1, -1, NULL, -1, -1, -1), // joystick sensitivity + op_sliders("OMB_29", 440, 290, -1, -1, 29, 20, 10, NULL, -1, -1, -1, NULL, -1, -1, -1), // joystick deadzone + op_sliders("OMB_21", 440, 75, -1, -1, 21, 36, 5, NULL, -1, -1, -1, NULL, -1, -1, -1) + }, + { // GR_1024 + op_sliders("2_OMB_10", 50, 223, -1, -1, 10, 32, 10, + "2_OMB_11", 11, 361, 219, + "2_OMB_09", 9, 7, 219 ), // sound fx volume slider + op_sliders("2_OMB_13", 50, 281, -1, -1, 13, 32, 10, + "2_OMB_14", 14, 361, 275, + "2_OMB_12", 12, 7, 275 ), // music volume slider + op_sliders("2_OMB_16", 50, 335, -1, -1, 16, 32, 10, + "2_OMB_17", 17, 361, 330, + "2_OMB_15", 15, 7, 330 ), // voice volume slider + op_sliders("2_OMB_20", 9, 505, -1, -1, 20, 32, 10, NULL, -1, -1, -1, NULL, -1, -1, -1), // mouse sensitivity + op_sliders("2_OMB_28", 704, 414, -1, -1, 28, 32, 10, NULL, -1, -1, -1, NULL, -1, -1, -1), // joystick sensitivity + op_sliders("2_OMB_29", 704, 464, -1, -1, 29, 32, 10, NULL, -1, -1, -1, NULL, -1, -1, -1), // joystick deadzone + op_sliders("2_OMB_21", 704, 120, -1, -1, 21, 60, 5, NULL, -1, -1, -1, NULL, -1, -1, -1) + } +}; + +static struct { + char *filename; + char *mask_filename; + int bitmap; + int mask; + +} Backgrounds[GR_NUM_RESOLUTIONS][NUM_TABS] = { +//XSTR:OFF + { // GR_640 + { "OptionsMain", "OptionsMain-M"}, + { "OptionsMulti", "OptionsMulti-M"}, + { "OptionsDetail", "OptionsDetail-M"}, + }, + { // GR_1024 + { "2_OptionsMain", "2_OptionsMain-M"}, + { "2_OptionsMulti", "2_OptionsMulti-M"}, + { "2_OptionsDetail", "2_OptionsDetail-M"}, + } +//XSTR:ON +}; + +static int Tab = 0; +static int Options_menu_inited = 0; +static int Options_multi_inited = 0; +static int Options_detail_inited = 0; +static int Button_bms[NUM_COMMONS][MAX_BMAPS_PER_GADGET]; + +static UI_WINDOW Ui_window; +UI_GADGET Options_bogus; + +static int Backup_skill_level; +static float Backup_sound_volume; +static float Backup_music_volume; +static float Backup_voice_volume; + +static int Backup_briefing_voice_enabled; +static int Backup_use_mouse_to_fly; + +static int Sound_volume_int; +static int Music_volume_int; +static int Voice_volume_int; + +static int Voice_vol_handle = -1; +int Options_notify_stamp = -1; +char Options_notify_string[200]; + +// called whenever accept is hit +// do any processing, etc in here. +void options_accept(); +void options_force_button_frame(int n, int frame_num); + +extern float Freespace_gamma; + +void options_add_notify(char *str); +void options_notify_do_frame(); + +int Options_gamma_coords[GR_NUM_RESOLUTIONS][4] = { + { + 435, 179, 195, 28 // GR_640 + }, + { + 692, 287, 308, 44 // GR_1024 + } +}; + +#define MAX_GAMMA_BITMAP_SIZE 17500 + +int Options_gamma_num_coords[GR_NUM_RESOLUTIONS][4] = { + { + 489, 159, 65, 17 // GR_640 + }, + { + 779, 254, 65, 17 // GR_1024 + } +}; + +int Options_skills_text_coords[GR_NUM_RESOLUTIONS][4] = { + { + 468, 104, 155, 10 // GR_640 + }, + { + 750, 169, 246, 21 // GR_1024 + } +}; + + +// --------------------------------------------------------------------------------------------------------- +// DETAIL LEVEL OPTIONS definitions BEGIN +// + +#define NUM_DETAIL_SLIDERS 8 + +/* +#define DETAIL_DISTANCE_SLIDER 0 +#define NEBULA_DETAIL_SLIDER 1 +#define HARDWARE_TEXTURES_SLIDER 2 +#define NUM_PARTICLES_SLIDER 6 +#define SHARD_CULLING_SLIDER 3 +#define SHIELD_DETAIL_SLIDER 4 +#define NUM_STARS_SLIDER 5 +#define LIGHTING_SLIDER 7 +*/ +#define DETAIL_DISTANCE_SLIDER 0 +#define NEBULA_DETAIL_SLIDER 1 +#define HARDWARE_TEXTURES_SLIDER 2 +#define NUM_PARTICLES_SLIDER 3 +#define SHARD_CULLING_SLIDER 4 +#define SHIELD_DETAIL_SLIDER 5 +#define NUM_STARS_SLIDER 6 +#define LIGHTING_SLIDER 7 +op_sliders Detail_sliders[GR_NUM_RESOLUTIONS][NUM_DETAIL_SLIDERS] = { + { // GR_640 + op_sliders("ODB_07", 21, 71, -1, -1, 7, 20, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // model detail + op_sliders("ODB_08", 21, 119, -1, -1, 8, 20, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // nebula detail + op_sliders("ODB_09", 21, 166, -1, -1, 9, 20, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // textures + op_sliders("ODB_10", 21, 212, -1, -1, 10, 20, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // particles + op_sliders("ODB_11", 21, 260, -1, -1, 11, 20, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // debris + op_sliders("ODB_12", 21, 307, -1, -1, 12, 20, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // shield hit + op_sliders("ODB_13", 21, 354, -1, -1, 13, 20, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // stars + op_sliders("ODB_19", 518, 212, -1, -1, 19, 20, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // lighting + }, + { // GR_1024 + op_sliders("2_ODB_07", 34, 114, -1, -1, 7, 32, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // model detail + op_sliders("2_ODB_08", 34, 190, -1, -1, 8, 32, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // nebula detail + op_sliders("2_ODB_09", 34, 265, -1, -1, 9, 32, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // textures + op_sliders("2_ODB_10", 34, 340, -1, -1, 10, 32, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // particles + op_sliders("2_ODB_11", 34, 416, -1, -1, 11, 32, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // debris + op_sliders("2_ODB_12", 34, 492, -1, -1, 12, 32, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // shield hit + op_sliders("2_ODB_13", 34, 567, -1, -1, 13, 32, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // stars + op_sliders("2_ODB_19", 829, 340, -1, -1, 19, 32, 5, NULL, -1, -1, -1, NULL, -1, -1, -1), // lighting + } +}; +int Detail_slider_pos[NUM_DETAIL_SLIDERS]; +detail_levels Detail_original; // backup of Detail settings when screen is first entered +UI_GADGET Detail_bogus; +void options_detail_init(); +void options_detail_hide_stuff(); +void options_detail_unhide_stuff(); +void options_detail_do_frame(); +void options_detail_set_level(int level); + +// text +#define OPTIONS_NUM_TEXT 49 +UI_XSTR Options_text[GR_NUM_RESOLUTIONS][OPTIONS_NUM_TEXT] = { + { // GR_640 + // common text + { "Options", 1036, 10, 35, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][OPTIONS_TAB].button }, + { "Multi", 1042, 97, 35, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][MULTIPLAYER_TAB].button }, + { "Detail", 1351, 166, 35, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][DETAIL_LEVELS_TAB].button }, + { "Exit", 1059, 8, 417, UI_XSTR_COLOR_PINK, -1, &Buttons[0][ABORT_GAME_BUTTON].button }, + { "Game", 1412, 8, 430, UI_XSTR_COLOR_PINK, -1, &Buttons[0][ABORT_GAME_BUTTON].button }, + { "Control", 1352, 409, 418, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][CONTROL_CONFIG_BUTTON].button }, + { "Config", 1353, 409, 430, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][CONTROL_CONFIG_BUTTON].button }, + { "HUD", 1354, 504, 418, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][HUD_CONFIG_BUTTON].button }, + { "Config", 1415, 504, 430, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][HUD_CONFIG_BUTTON].button }, + { "Accept", 1035, 573, 412, UI_XSTR_COLOR_PINK, -1, &Buttons[0][ACCEPT_BUTTON].button }, + + // text for the detail level screen + { "Preset Detail Levels", 1355, 455, 56, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Low", 1160, 570, 82, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][LOW_DETAIL_N].button }, + { "Medium", 1161, 550, 100, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][MEDIUM_DETAIL_N].button }, + { "High", 1162, 568, 120, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][HIGH_DETAIL_N].button }, + { "Very High", 1163, 530, 139, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][VERY_HIGH_DETAIL_N].button }, + { "Custom", 1356, 546, 158, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][CUSTOM_DETAIL_N].button }, + { "Off", 1286, 509, 267, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][PLANETS_OFF].button }, + { "On", 1285, 573, 267, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][PLANETS_ON].button }, + { "Off", 1286, 509, 314, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][HUD_TARGETVIEW_RENDER_OFF].button }, + { "On", 1285, 573, 314, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][HUD_TARGETVIEW_RENDER_ON].button }, + { "Off", 1286, 509, 361, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][WEAPON_EXTRAS_OFF].button }, + { "On", 1285, 573, 361, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][WEAPON_EXTRAS_ON].button }, + { "Planets/Backgrounds", 1357, 455, 244, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Target View Rendering", 1358, 446, 291, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Weapon Extras", 1359, 497, 338, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Model Detail", 1360, 27, 56, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Nebula Detail", 1361, 27, 103, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "3D Hardware Textures", 1362, 27, 150, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Particles", 1363, 27, 197, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Impact Effects", 1364, 27, 244, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Shield Hit Effects", 1365, 27, 291, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Stars", 1366, 27, 338, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Lighting", 1367, 549, 197, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + + // main options screen text + { "Briefing Voice", 1368, 14, 58, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Off", 1286, 20, 81, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][BRIEF_VOICE_OFF].button }, + { "On", 1285, 83, 81, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][BRIEF_VOICE_ON].button }, + { "Volume", 1369, 14, 111, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Effects", 1370, 20, 130, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Music", 1371, 20, 165, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Voice", 1372, 20, 199, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Mouse", 1373, 14, 249, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Off", 1286, 20, 273, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][MOUSE_OFF].button }, + { "On", 1285, 83, 273, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][MOUSE_ON].button }, + { "Sensitivity", 1529, 20, 297, UI_XSTR_COLOR_GREEN, -1, &Options_sliders[0][OPT_MOUSE_SENS_SLIDER].slider }, + { "Skill Level", 1509, 533, 58, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Brightness", 1375, 532, 133, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Joystick", 1376, 556, 231, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Sensitivity", 1374, 538, 250, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Deadzone", 1377, 538, 281, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + }, + { // GR_1024 + // common text + { "Options", 1036, 16, 57, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][OPTIONS_TAB].button }, + { "Multi", 1042, 172, 57, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][MULTIPLAYER_TAB].button }, + { "Detail", 1351, 283, 57, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][DETAIL_LEVELS_TAB].button }, + { "Exit", 1059, 13, 685, UI_XSTR_COLOR_PINK, -1, &Buttons[1][ABORT_GAME_BUTTON].button }, + { "Game", 1412, 13, 696, UI_XSTR_COLOR_PINK, -1, &Buttons[1][ABORT_GAME_BUTTON].button }, + { "Control", 1352, 655, 685, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][CONTROL_CONFIG_BUTTON].button }, + { "Config", 1353, 655, 696, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][CONTROL_CONFIG_BUTTON].button }, + { "HUD", 1354, 806, 685, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][HUD_CONFIG_BUTTON].button }, + { "Config", 1415, 806, 696, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][HUD_CONFIG_BUTTON].button }, + { "Accept", 1035, 927, 672, UI_XSTR_COLOR_PINK, -1, &Buttons[1][ACCEPT_BUTTON].button }, + + // text for the detail level screen + { "Preset Detail Levels", 1355, 809, 90, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Low", 1160, 944, 131, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][LOW_DETAIL_N].button }, + { "Medium", 1161, 924, 161, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][MEDIUM_DETAIL_N].button }, + { "High", 1162, 942, 192, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][HIGH_DETAIL_N].button }, + { "Very High", 1163, 903, 222, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][VERY_HIGH_DETAIL_N].button }, + { "Custom", 1356, 922, 252, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][CUSTOM_DETAIL_N].button }, + { "Off", 1286, 835, 427, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][PLANETS_OFF].button }, + { "On", 1285, 936, 427, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][PLANETS_ON].button }, + { "Off", 1286, 835, 503, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][HUD_TARGETVIEW_RENDER_OFF].button }, + { "On", 1285, 936, 503, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][HUD_TARGETVIEW_RENDER_ON].button }, + { "Off", 1286, 835, 578, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][WEAPON_EXTRAS_OFF].button }, + { "On", 1285, 936, 578, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][WEAPON_EXTRAS_ON].button }, + { "Planets/Backgrounds", 1357, 808, 391, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Target View Rendering", 1358, 799, 466, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Weapon Extras", 1359, 850, 542, UI_XSTR_COLOR_GREEN, -1, &Detail_bogus }, + { "Model Detail", 1360, 44, 99, UI_XSTR_COLOR_GREEN, -1, &Detail_sliders[1][DETAIL_DISTANCE_SLIDER].slider }, + { "Nebula Detail", 1361, 44, 175, UI_XSTR_COLOR_GREEN, -1, &Detail_sliders[1][NEBULA_DETAIL_SLIDER].slider }, + { "3D Hardware Textures", 1362, 44, 250, UI_XSTR_COLOR_GREEN, -1, &Detail_sliders[1][HARDWARE_TEXTURES_SLIDER].slider }, + { "Particles", 1363, 44, 325, UI_XSTR_COLOR_GREEN, -1, &Detail_sliders[1][NUM_PARTICLES_SLIDER].slider }, + { "Impact Effects", 1364, 44, 401, UI_XSTR_COLOR_GREEN, -1, &Detail_sliders[1][SHARD_CULLING_SLIDER].slider }, + { "Shield Hit Effects", 1365, 44, 476, UI_XSTR_COLOR_GREEN, -1, &Detail_sliders[1][SHIELD_DETAIL_SLIDER].slider }, + { "Stars", 1366, 44, 552, UI_XSTR_COLOR_GREEN, -1, &Detail_sliders[1][NUM_STARS_SLIDER].slider }, + { "Lighting", 1367, 903, 326, UI_XSTR_COLOR_GREEN, -1, &Detail_sliders[1][LIGHTING_SLIDER].slider }, + + // main options screen text + { "Briefing Voice", 1368, 23, 93, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Off", 1286, 32, 130, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][BRIEF_VOICE_OFF].button }, + { "On", 1285, 134, 130, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][BRIEF_VOICE_ON].button }, + { "Volume", 1369, 23, 178, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Effects", 1370, 33, 209, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Music", 1371, 33, 264, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Voice", 1372, 33, 319, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Mouse", 1373, 23, 399, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Off", 1286, 32, 437, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][MOUSE_OFF].button }, + { "On", 1285, 134, 437, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][MOUSE_ON].button }, + { "Sensitivity", 1529, 34, 477, UI_XSTR_COLOR_GREEN, -1, &Options_sliders[1][OPT_MOUSE_SENS_SLIDER].slider }, + { "Skill Level", 1509, 854, 93, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Brightness", 1375, 852, 214, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Joystick", 1376, 891, 370, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Sensitivity", 1374, 861, 400, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + { "Deadzone", 1377, 861, 451, UI_XSTR_COLOR_GREEN, -1, &Options_bogus }, + } +}; + + +// +// DETAIL LEVEL tab definitions END +// --------------------------------------------------------------------------------------------------------- + +void options_play_voice_clip() +{ + int snd_id; + + if ( snd_is_playing(Voice_vol_handle) ) { + snd_stop(Voice_vol_handle); + Voice_vol_handle=-1; + } + + snd_id = snd_load(&Snds_iface[SND_VOICE_SLIDER_CLIP]); + Voice_vol_handle = snd_play_raw( snd_id, 0.0f, 1.0f, SND_PRIORITY_SINGLE_INSTANCE ); +} + +void options_add_notify(char *str) +{ + strcpy(Options_notify_string, str); + Options_notify_stamp = timestamp(OPTIONS_NOTIFY_TIME); +} + +void options_notify_do_frame() +{ + int w,h; + + if (Options_notify_stamp != -1) { + if (timestamp_elapsed(Options_notify_stamp)) { + Options_notify_stamp = -1; + + } else { + gr_get_string_size(&w, &h, Options_notify_string); + gr_printf((gr_screen.max_w - w) / 2, OPTIONS_NOTIFY_Y, Options_notify_string); + } + } +} + +/* +void options_set_bmaps(int btn, int bm_index) +{ + int j; + + for (j=0; j= 0); + Ui_window.set_mask_bmap(Backgrounds[gr_screen.res][Tab].mask, Backgrounds[gr_screen.res][Tab].mask_filename); + } + + for (i=0; i<256; i++){ + flags[i] = 0; + } + + // activate, deactivate any necessary controls + for (i=0; i 5.0f) { + Freespace_gamma = 5.0f; + gamesnd_play_iface(SND_GENERAL_FAIL); + + } else { + gamesnd_play_iface(SND_USER_SELECT); + } + + gr_set_gamma(Freespace_gamma); + sprintf(tmp_gamma_string, NOX("%.2f"), Freespace_gamma); + os_config_write_string(NULL, NOX("Gamma"), tmp_gamma_string); +} + +void options_button_pressed(int n) +{ + int choice; + + switch (n) { + case OPTIONS_TAB: + case MULTIPLAYER_TAB: + case DETAIL_LEVELS_TAB: + if (Tab != n) + options_change_tab(n); + + break; + + case ABORT_GAME_BUTTON: + gamesnd_play_iface(SND_USER_SELECT); + choice = popup( PF_NO_NETWORKING | PF_BODY_BIG, 2, POPUP_NO, POPUP_YES, XSTR( "Exit Game?", 374)); + if ( choice == 1 ) + gameseq_post_event(GS_EVENT_QUIT_GAME); + break; + + case CONTROL_CONFIG_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_CONTROL_CONFIG); + break; + + case HUD_CONFIG_BUTTON: +#ifdef FS2_DEMO + game_feature_not_in_demo_popup(); +#else + // can't go to the hud config screen when a multiplayer observer + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){ + gamesnd_play_iface(SND_GENERAL_FAIL); + options_add_notify(XSTR( "Cannot use HUD config when an observer!", 375)); + break; + } + + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_HUD_CONFIG); +#endif + break; + + case ACCEPT_BUTTON: + options_accept(); + break; + + // BEGIN - detail level tab buttons + + case HUD_TARGETVIEW_RENDER_ON: + Detail.targetview_model = 1; + gamesnd_play_iface(SND_USER_SELECT); + break; + + case HUD_TARGETVIEW_RENDER_OFF: + Detail.targetview_model = 0; + gamesnd_play_iface(SND_USER_SELECT); + break; + + case PLANETS_ON: + Detail.planets_suns = 1; + gamesnd_play_iface(SND_USER_SELECT); + break; + + case PLANETS_OFF: + Detail.planets_suns = 0; + gamesnd_play_iface(SND_USER_SELECT); + break; + + case WEAPON_EXTRAS_ON: + Detail.weapon_extras = 1; + gamesnd_play_iface(SND_USER_SELECT); + break; + + case WEAPON_EXTRAS_OFF: + Detail.weapon_extras = 0; + gamesnd_play_iface(SND_USER_SELECT); + break; + + case LOW_DETAIL_N: + options_detail_set_level(0); + gamesnd_play_iface(SND_USER_SELECT); + break; + + case MEDIUM_DETAIL_N: + options_detail_set_level(1); + gamesnd_play_iface(SND_USER_SELECT); + break; + + case HIGH_DETAIL_N: + options_detail_set_level(2); + gamesnd_play_iface(SND_USER_SELECT); + break; + + case VERY_HIGH_DETAIL_N: + options_detail_set_level(3); + gamesnd_play_iface(SND_USER_SELECT); + break; + + case CUSTOM_DETAIL_N: + options_detail_set_level(-1); + gamesnd_play_iface(SND_USER_SELECT); + break; + // END - detail level tab buttons + + case GAMMA_DOWN: + options_change_gamma(-0.05f); + break; + + case GAMMA_UP: + options_change_gamma(0.05f); + break; + + case BRIEF_VOICE_ON: + Briefing_voice_enabled = 1; + gamesnd_play_iface(SND_USER_SELECT); + break; + + case BRIEF_VOICE_OFF: + Briefing_voice_enabled = 0; + gamesnd_play_iface(SND_USER_SELECT); + break; + + case MOUSE_ON: + Use_mouse_to_fly = 1; + gamesnd_play_iface(SND_USER_SELECT); + break; + + case MOUSE_OFF: + Use_mouse_to_fly = 0; + gamesnd_play_iface(SND_USER_SELECT); + break; + } +} + +void options_sliders_update() +{ + // sound slider + if (Options_sliders[gr_screen.res][OPT_SOUND_VOLUME_SLIDER].slider.pos != Sound_volume_int) { + Sound_volume_int = Options_sliders[gr_screen.res][OPT_SOUND_VOLUME_SLIDER].slider.pos; + Master_sound_volume = ((float) (Sound_volume_int) / 9.0f); + set_sound_volume(); + gamesnd_play_iface(SND_USER_SELECT); + } + + // music slider + if (Options_sliders[gr_screen.res][OPT_MUSIC_VOLUME_SLIDER].slider.pos != Music_volume_int) { + Music_volume_int = Options_sliders[gr_screen.res][OPT_MUSIC_VOLUME_SLIDER].slider.pos; + Master_event_music_volume = ((float) (Music_volume_int) / 9.0f); + if (Master_event_music_volume > 0.0f) { + event_music_enable(); + } + + set_music_volume(); + gamesnd_play_iface(SND_USER_SELECT); + } + + // voice slider + if (Options_sliders[gr_screen.res][OPT_VOICE_VOLUME_SLIDER].slider.pos != Voice_volume_int) { + Voice_volume_int = Options_sliders[gr_screen.res][OPT_VOICE_VOLUME_SLIDER].slider.pos; + Master_voice_volume = ((float) (Voice_volume_int) / 9.0f); + set_voice_volume(); + options_play_voice_clip(); + } + + if (Mouse_sensitivity != Options_sliders[gr_screen.res][OPT_MOUSE_SENS_SLIDER].slider.pos) { + Mouse_sensitivity = Options_sliders[gr_screen.res][OPT_MOUSE_SENS_SLIDER].slider.pos; + gamesnd_play_iface(SND_USER_SELECT); + } + + if (Joy_sensitivity != Options_sliders[gr_screen.res][OPT_JOY_SENS_SLIDER].slider.pos) { + Joy_sensitivity = Options_sliders[gr_screen.res][OPT_JOY_SENS_SLIDER].slider.pos; + gamesnd_play_iface(SND_USER_SELECT); + } + + if (Dead_zone_size != Options_sliders[gr_screen.res][OPT_JOY_DEADZONE_SLIDER].slider.pos * 5) { + Dead_zone_size = Options_sliders[gr_screen.res][OPT_JOY_DEADZONE_SLIDER].slider.pos * 5; + gamesnd_play_iface(SND_USER_SELECT); + } + + if (Game_skill_level != Options_sliders[gr_screen.res][OPT_SKILL_SLIDER].slider.pos) { + Game_skill_level = Options_sliders[gr_screen.res][OPT_SKILL_SLIDER].slider.pos; + gamesnd_play_iface(SND_USER_SELECT); + } +} + +void options_accept() +{ + // apply the selected multiplayer options + if ( Options_multi_inited ) { + #if !defined(DEMO) && !defined(OEM_BUILD) // not for FS2_DEMO + options_multi_accept(); + #endif + } + + // If music is zero volume, disable + if ( Master_event_music_volume <= 0.0f ) { +// event_music_disable(); + event_music_level_close(); + } + + // apply other options (display options, etc) + // note: return in here (and play failed sound) if they can't accept yet for some reason + + gamesnd_play_iface(SND_COMMIT_PRESSED); + gameseq_post_event(GS_EVENT_PREVIOUS_STATE); +} + +void options_load_background_and_mask(int tab) +{ + Assert(tab == OPTIONS_TAB || tab == DETAIL_LEVELS_TAB ); + Backgrounds[gr_screen.res][tab].bitmap = bm_load(Backgrounds[gr_screen.res][tab].filename); + Backgrounds[gr_screen.res][tab].mask = bm_load(Backgrounds[gr_screen.res][tab].mask_filename); +} + +int Gamma_last_set = -1; +int Gamma_colors_inited = 0; + +void options_menu_init() +{ + int i, j; + options_buttons *b; + + Assert(!Options_menu_inited); + + // pause all sounds, since we could get here through the game + beam_pause_sounds(); + //audiostream_pause_all(); + + Tab = 0; + Gamma_last_set = -1; + + common_set_interface_palette("InterfacePalette"); // set the interface palette + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, b->flags & REPEAT, 1); + // set up callback for when a mouse first goes over a button + if (b->filename) { + b->button.set_bmaps(b->filename); + if ( !(b->flags & NO_MOUSE_OVER_SOUND) ) { + b->button.set_highlight_action(common_play_highlight_sound); + } + + } else { + b->button.hide(); + } + + b->button.link_hotspot(b->hotspot); + if (i < NUM_COMMONS) { + for (j=0; jbutton.bmap_ids[j]; + } + } + } + + // add all xstr text + for(i=0; i= 0){ + bm_unload(Backgrounds[gr_screen.res][i].bitmap); + } + if ((Backgrounds[gr_screen.res][i].mask >= 0) && (i != Tab)){ // Ui_window.destroy() expects to release current tab's mask. + bm_unload(Backgrounds[gr_screen.res][i].mask); + } + } + + if ( Voice_vol_handle >= 0 ) { + snd_stop(Voice_vol_handle); + Voice_vol_handle = -1; + } + +#if !defined(DEMO) && !defined(OEM_BUILD) // not for FS2_DEMO + options_multi_close(); +#endif + + Ui_window.destroy(); + common_free_interface_palette(); // restore game palette + write_pilot_file(); + game_flush(); + + // unpause all sounds, since we could be headed back to the game + beam_unpause_sounds(); + //audiostream_unpause_all(); + + Options_menu_inited = 0; + Options_multi_inited = 0; + Options_detail_inited = 0; + + +} + + +void draw_gamma_box() +{ + int x, y, v; + +// NEILK: i had to change this declaration because the size is determined dynamically. I just picked an arbitrary large number to data size (although we should always be using less) +// TODO: change MAX size to maximum size for a 1024x768 bitmap +// ushort Gamma_data[Options_gamma_coords[gr_screen.res][OPTIONS_W_COORD]*Options_gamma_coords[gr_screen.res][OPTIONS_H_COORD]*2]; + ushort Gamma_data[MAX_GAMMA_BITMAP_SIZE]; + + v = fl2i( pow(0.5f, 1.0f / Freespace_gamma) * 255.0f ); + if (v > 255){ + v = 255; + } else if (v < 0){ + v = 0; + } + + int Gamma_changed = 0; + if ( v != Gamma_last_set ) { + Gamma_changed = 1; + } else { + Gamma_changed = 0; + } + Gamma_last_set = v; + + { + ushort clr_full_white = 0; + ushort clr_half_white = 0; + ubyte r, g, b, a; + + // if we're in bitmap poly mode + if(Gr_bitmap_poly){ + BM_SELECT_TEX_FORMAT(); + } else { + BM_SELECT_SCREEN_FORMAT(); + } + + // set full white + r = g = b = a = 255; + bm_set_components((ubyte*)&clr_full_white, &r, &g, &b, &a); + + // set half white + r = g = b = (ubyte)v; + bm_set_components((ubyte*)&clr_half_white, &r, &g, &b, &a); + + ushort *dptr = Gamma_data; + for (y=0; y= NUM_TABS) + i = 0; + + options_change_tab(i); + break; + + case KEY_C: + if (Tab == OPTIONS_TAB) { + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_CONTROL_CONFIG); + } + + break; + + case KEY_H: + if (Tab == OPTIONS_TAB) { + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_HUD_CONFIG); + } + + break; + + case KEY_ESC: + // if(Tab != MULTIPLAYER_TAB){ + options_cancel_exit(); + // } + break; + + case KEY_CTRLED | KEY_ENTER: + options_accept(); + break; + + case KEY_DELETE: + break; + + case KEY_ENTER: + break; + } + + for (i=0; i= 0) { + gr_set_bitmap(i); + gr_bitmap(0, 0); + } + + Ui_window.draw(); + + // NOTE : this must be done here so that any special drawing crap we do is not overwritten by the UI_WINDOW::draw() call + // do specific processing for the multiplayer tab + switch (Tab) { + case MULTIPLAYER_TAB: +#if !defined(DEMO) && !defined(OEM_BUILD) // not for FS2_DEMO + options_multi_do(k); +#endif + break; + + case DETAIL_LEVELS_TAB: + options_detail_do_frame(); + break; + + default: + Game_skill_level = Options_sliders[gr_screen.res][OPT_SKILL_SLIDER].slider.pos; + break; + } + + // handle the displaying of any notification messages + options_notify_do_frame(); + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, b->flags & REPEAT, 1); + // set up callback for when a mouse first goes over a button + if (b->filename) { + b->button.set_bmaps(b->filename); + if ( !(b->flags & NO_MOUSE_OVER_SOUND) ) { + b->button.set_highlight_action(common_play_highlight_sound); + } + + } else { + b->button.hide(); + } + + b->button.link_hotspot(b->hotspot); + } + + // create detail level sliders + for ( i = 0; i < NUM_DETAIL_SLIDERS; i++ ) { + Detail_sliders[gr_screen.res][i].slider.create(&Ui_window, Detail_sliders[gr_screen.res][i].x, Detail_sliders[gr_screen.res][i].y, + Detail_sliders[gr_screen.res][i].dots, Detail_sliders[gr_screen.res][i].filename, + Detail_sliders[gr_screen.res][i].hotspot, Detail_sliders[gr_screen.res][i].right_filename, Detail_sliders[gr_screen.res][i].right_mask, Detail_sliders[gr_screen.res][i].right_x, Detail_sliders[gr_screen.res][i].right_y, + Detail_sliders[gr_screen.res][i].left_filename, Detail_sliders[gr_screen.res][i].left_mask, Detail_sliders[gr_screen.res][i].left_x, Detail_sliders[gr_screen.res][i].left_y, + Detail_sliders[gr_screen.res][i].dot_w); + } + + // init the actual slider positions and our internal positions + options_detail_synch_sliders(); +} + +void options_detail_sliders_update() +{ + int i; + + for ( i = 0; i < NUM_DETAIL_SLIDERS; i++ ) { + if ( Detail_sliders[gr_screen.res][i].slider.pos != Detail_slider_pos[i] ) { + Detail_slider_pos[i] = Detail_sliders[gr_screen.res][i].slider.pos; + gamesnd_play_iface(SND_USER_SELECT); + } + } + + // set Detail based on slider positions + Detail.detail_distance = Detail_sliders[gr_screen.res][DETAIL_DISTANCE_SLIDER].slider.pos; + + // modify nebula stuff + Detail.nebula_detail = Detail_sliders[gr_screen.res][NEBULA_DETAIL_SLIDER].slider.pos; + neb2_set_detail_level(Detail.nebula_detail); + + Detail.hardware_textures = Detail_sliders[gr_screen.res][HARDWARE_TEXTURES_SLIDER].slider.pos; + Detail.num_small_debris = Detail_sliders[gr_screen.res][SHARD_CULLING_SLIDER].slider.pos; + Detail.shield_effects = Detail_sliders[gr_screen.res][SHIELD_DETAIL_SLIDER].slider.pos; + Detail.num_stars = Detail_sliders[gr_screen.res][NUM_STARS_SLIDER].slider.pos; + Detail.num_particles = Detail_sliders[gr_screen.res][NUM_PARTICLES_SLIDER].slider.pos; + Detail.lighting = Detail_sliders[gr_screen.res][LIGHTING_SLIDER].slider.pos; +} + +void options_detail_hide_stuff() +{ + int i; + + for ( i = 0; i < NUM_DETAIL_SLIDERS; i++ ) { + Detail_sliders[gr_screen.res][i].slider.disable(); + Detail_sliders[gr_screen.res][i].slider.hide(); + } + + // this will hide text unassociated with any real control + Detail_bogus.hide(); +} + +void options_detail_unhide_stuff() +{ + int i; + + for ( i = 0; i < NUM_DETAIL_SLIDERS; i++ ) { + Detail_sliders[gr_screen.res][i].slider.enable(); + Detail_sliders[gr_screen.res][i].slider.unhide(); + } + + // this will hide text unassociated with any real control + Detail_bogus.unhide(); +} + +void options_force_button_frame(int n, int frame_num) +{ + if ( !Buttons[gr_screen.res][n].button.button_down() ) { + Buttons[gr_screen.res][n].button.draw_forced(frame_num); + } +} + +// called once per frame to set lit buttons +void options_detail_do_frame() +{ + options_detail_sliders_update(); + + // force on/off buttons to draw their correct setting + + if ( Detail.targetview_model ) { + options_force_button_frame(HUD_TARGETVIEW_RENDER_ON, 2); + options_force_button_frame(HUD_TARGETVIEW_RENDER_OFF, 0); + } else { + options_force_button_frame(HUD_TARGETVIEW_RENDER_OFF, 2); + options_force_button_frame(HUD_TARGETVIEW_RENDER_ON, 0); + } + + if ( Detail.planets_suns == 1 ) { + options_force_button_frame(PLANETS_ON, 2); + options_force_button_frame(PLANETS_OFF, 0); + } else { + options_force_button_frame(PLANETS_OFF, 2); + options_force_button_frame(PLANETS_ON, 0); + } + + if ( Detail.weapon_extras) { + options_force_button_frame(WEAPON_EXTRAS_ON, 2); + options_force_button_frame(WEAPON_EXTRAS_OFF, 0); + } else { + options_force_button_frame(WEAPON_EXTRAS_OFF, 2); + options_force_button_frame(WEAPON_EXTRAS_ON, 0); + } + + int current_detail; + + if ( Detail.setting >= 0 ) { + current_detail = current_detail_level(); + Detail.setting = current_detail; + } else { + current_detail = -1; + } + + options_force_button_frame(LOW_DETAIL_N, 0); + options_force_button_frame(MEDIUM_DETAIL_N, 0); + options_force_button_frame(HIGH_DETAIL_N, 0); + options_force_button_frame(VERY_HIGH_DETAIL_N, 0); + options_force_button_frame(CUSTOM_DETAIL_N, 0); + + switch ( current_detail ) { + case -1: + options_force_button_frame(CUSTOM_DETAIL_N, 2); + break; + case 0: + options_force_button_frame(LOW_DETAIL_N, 2); + break; + case 1: + options_force_button_frame(MEDIUM_DETAIL_N, 2); + break; + case 2: + options_force_button_frame(HIGH_DETAIL_N, 2); + break; + case 3: + options_force_button_frame(VERY_HIGH_DETAIL_N, 2); + break; + } +} + +// Set all the detail settings to a predefined level +void options_detail_set_level(int level) +{ + detail_level_set(level); + options_detail_synch_sliders(); +} + +// +// DETAIL LEVEL tab definitions END +// --------------------------------------------------------------------------------------------------------- + + diff --git a/src/menuui/optionsmenumulti.cpp b/src/menuui/optionsmenumulti.cpp new file mode 100644 index 0000000..483d138 --- /dev/null +++ b/src/menuui/optionsmenumulti.cpp @@ -0,0 +1,2538 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/OptionsMenuMulti.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 21 10/25/99 5:47p Jefff + * reassigned some xstr ids + * + * 20 8/02/99 2:44p Dave + * Disable IPX for demo build. + * + * 19 7/15/99 7:15p Jefff + * Added various sound FX + * + * 18 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 17 6/25/99 11:59a Dave + * Multi options screen. + * + * 16 6/22/99 7:03p Dave + * New detail options screen. + * + * 15 4/25/99 3:02p Dave + * Build defines for the E3 build. + * + * 14 2/25/99 4:19p Dave + * Added multiplayer_beta defines. Added cd_check define. Fixed a few + * release build warnings. Added more data to the squad war request and + * response packets. + * + * 13 2/19/99 2:55p Dave + * Temporary checking to report the winner of a squad war match. + * + * 12 2/17/99 2:10p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 11 2/12/99 6:16p Dave + * Pre-mission Squad War code is 95% done. + * + * 10 2/05/99 7:22p Neilk + * Fixed gamma bitmap and converted coordinates for multiple resolutions + * + * 9 2/02/99 11:36a Dave + * Removed obsolete data reference. + * + * 8 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 7 11/20/98 11:16a Dave + * Fixed up IPX support a bit. Making sure that switching modes and + * loading/saving pilot files maintains proper state. + * + * 6 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 5 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 4 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 3 10/09/98 2:57p Dave + * Starting splitting up OS stuff. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 27 7/08/98 11:56a Dave + * Changed mic problem error message. + * + * 26 6/12/98 7:13p Hoffoss + * Fixed options screen problem where it wasn't showing tooltips. + * + * 25 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 24 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 23 5/21/98 9:45p Dave + * Lengthened tracker polling times. Put in initial support for PXO + * servers with channel filters. Fixed several small UI bugs. + * + * 22 5/20/98 2:24a Dave + * Fixed server side voice muting. Tweaked multi debrief/endgame + * sequencing a bit. Much friendlier for stats tossing/accepting now. + * + * 21 5/19/98 1:35a Dave + * Tweaked pxo interface. Added rankings url to pxo.cfg. Make netplayer + * local options update dynamically in netgames. + * + * 20 5/18/98 12:03p Frank + * Make sure network changes made in options screen are loaded into any + * current Net_player as well as the Player. + * + * 19 5/11/98 11:39p Dave + * Stuff. + * + * 18 5/10/98 7:05p Dave + * Fix endgame sequencing ESC key. Changed how host options warning popups + * are done. Fixed pause/message scrollback/options screen problems in mp. + * Make sure observer HUD doesn't try to lock weapons. + * + * 17 5/08/98 7:08p Dave + * Lots of UI tweaking. + * + * 16 5/08/98 5:04p Dave + * Go to the join game screen when quitting multiplayer. Fixed mission + * text chat bugs. Put mission type symbols on the create game list. + * Started updating standalone gui controls. + * + * 15 5/07/98 12:57a Dave + * Fixed incorrect calls to free() from stats code. Put in new artwork for + * debrief and host options screens. Another modification to scoring + * system for secondary weapons. + * + * 14 5/06/98 8:06p Dave + * Made standalone reset properly under weird conditions. Tweak + * optionsmulti screen. Upped MAX_WEAPONS to 350. Put in new launch + * countdown anim. Minro ui fixes/tweaks. + * + * 13 5/02/98 5:38p Dave + * Put in new tracker API code. Put in ship information on mp team select + * screen. Make standalone server name permanent. Fixed standalone server + * text messages. + * + * 12 4/30/98 5:24p Adam + * JAS: Made multi config specify path. + * + * 11 4/28/98 5:13p Dave + * Remove references to old MT API + * + * 10 4/25/98 2:00p Dave + * Installed a bunch of multiplayer context help screens. Reworked ingame + * join ship select screen. Fix places where network timestamps get hosed. + * + * 9 4/22/98 4:09p John + * String externalization + * + * 8 4/22/98 12:34a Dave + * Make sure hud config and control config buttons draw properly in all + * tab modes. Make small tab buttons light up correctly in multi options + * screen. + * + * 7 4/21/98 4:44p Dave + * Implement Vasudan ships in multiplayer. Added a debug function to bash + * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui + * problem in options screen. + * + * 6 4/20/98 6:04p Dave + * Implement multidata cache flushing and xferring mission files to + * multidata. Make sure observers can't change hud config. Fix pilot image + * viewing in popup. Put in game status field. Tweaked multi options. + * + * 5 4/18/98 12:45p Dave + * Aesthetic changes to multi options screen. Put in missing exit button. + * oops. + * + * 4 4/17/98 6:33p Dave + * Finished first run of the screen. About time. + * + * 3 4/17/98 5:27p Dave + * More work on the multi options screen. Fixed many minor ui todo bugs. + * + * 2 4/17/98 12:42a Dave + * Worked on voice tab. Need to implement mic testing and player muting + * list thingie. + * + * 1 4/16/98 11:39p Dave + * + * + * $NoKeywords: $ + */ + +#include "ui.h" +#include "bmpman.h" +#include "cfile.h" +#include "key.h" +#ifndef PLAT_UNIX +#include "ds.h" +#endif +#include "font.h" +#include "gamesnd.h" +#include "freespace.h" +#include "player.h" +#include "multi.h" +#include "multi_voice.h" +#include "rtvoice.h" +#include "mouse.h" +#include "optionsmenu.h" +#include "optionsmenumulti.h" +#include "popup.h" +#include "osregistry.h" +#include "alphacolors.h" +#include "timer.h" + +// general data section ------------------------------------------------ +UI_WINDOW *Om_window = NULL; + +static char* Om_background_0_fname[GR_NUM_RESOLUTIONS] = { + "OptionsMultiGen", // GR_640 + "2_OptionsMultiGen" // GR_1024 +}; + +static char* Om_background_0_mask_fname[GR_NUM_RESOLUTIONS] = { + "OptionsMultiGen-M", // GR_640 + "2_OptionsMultiGen-M" // GR_1024 +}; + +static char* Om_background_1_fname[GR_NUM_RESOLUTIONS] = { + "OptionsMultiVox", // GR_640 + "2_OptionsMultiVox" // GR_1024 +}; + +static char* Om_background_1_mask_fname[GR_NUM_RESOLUTIONS] = { + "OptionsMultiVox-M", // GR_640 + "2_OptionsMultiVox-M" // GR_1024 +}; + +int Om_background_0 = -1; +int Om_mask_0 = -1; + +int Om_background_1 = -1; +int Om_mask_1 = -1; + +// screen modes +#define OM_MODE_NONE -1 // no mode (unintialized) +#define OM_MODE_GENERAL 0 // general tab +#define OM_MODE_VOX 1 // voice tab +int Om_mode = OM_MODE_NONE; + +// notification stuff +#define OM_NOTIFY_TIME 8000 +#define OM_NOTIFY_Y 430 +#define OM_NOTIFY_Y2 440 +int Om_notify_stamp = -1; +char Om_notify_string[255]; + +// load all background bitmaps +void options_multi_load_bmaps(); + +// unload all the background bitmaps +void options_multi_unload_bmaps(); + +// add a notification message +void options_multi_add_notify(char *str); + +// process and blit any notification messages +void options_multi_notify_process(); + + +// protocol options section ------------------------------------------- +#define OM_PRO_NUM_BUTTONS 10 + +#define OM_PRO_TCP 0 +#define OM_PRO_IPX 1 +#define OM_PRO_SCROLL_IP_UP 2 +#define OM_PRO_SCROLL_IP_DOWN 3 +#define OM_PRO_ADD_IP 4 +#define OM_PRO_DELETE_IP 5 +#define OM_PRO_LOCAL_BROADCAST 6 +#define OM_PRO_VMT 7 +#define OM_PRO_VOX_TAB 8 +#define OM_PRO_GEN_TAB 9 + +ui_button_info Om_pro_buttons[GR_NUM_RESOLUTIONS][OM_PRO_NUM_BUTTONS] = { + { // GR_640 + ui_button_info("OMuB_07", 7, 66, -1, -1, 7), + ui_button_info("OMuB_08", 7, 84, -1, -1, 8), + ui_button_info("OMuB_09", 1, 124, -1, -1, 9), + ui_button_info("OMuB_10", 1, 157, -1, -1, 10), + ui_button_info("OMuB_11", 20, 207, -1, -1, 11), + ui_button_info("OMuB_12", 64, 207, -1, -1, 12), + ui_button_info("OMuB_13", 9, 251, -1, -1, 13), + ui_button_info("OMuB_14", 9, 282, -1, -1, 14), + ui_button_info("OMuB_15", 610, 53, -1, -1, 15), + ui_button_info("OMuB_16", 610, 72, -1, -1, 16), + }, + { // GR_1024 + ui_button_info("2_OMuB_07", 12, 105, -1, -1, 7), + ui_button_info("2_OMuB_08", 12, 134, -1, -1, 8), + ui_button_info("2_OMuB_09", 2, 198, -1, -1, 9), + ui_button_info("2_OMuB_10", 2, 252, -1, -1, 10), + ui_button_info("2_OMuB_11", 32, 332, -1, -1, 11), + ui_button_info("2_OMuB_12", 103, 332, -1, -1, 12), + ui_button_info("2_OMuB_13", 14, 402, -1, -1, 13), + ui_button_info("2_OMuB_14", 14, 452, -1, -1, 14), + ui_button_info("2_OMuB_15", 976, 85, -1, -1, 15), + ui_button_info("2_OMuB_16", 976, 114, -1, -1, 16), + } +}; + +UI_GADGET Om_pro_bogus; + +// test +#define OM_PRO_NUM_TEXT 12 +UI_XSTR Om_pro_text[GR_NUM_RESOLUTIONS][OM_PRO_NUM_TEXT] = { + { // GR_640 + { "TCP", 1378, 38, 70, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_TCP].button }, + { "IPX", 1379, 38, 88, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_IPX].button }, + { "IP Address", 1380, 30, 128, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus }, + { "add", 1381, 22, 235, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_ADD_IP].button }, + { "rem.", 1382, 68, 235, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_DELETE_IP].button }, + { "Broadcast Locally", 1397, 42, 260, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_LOCAL_BROADCAST].button }, + { "PXO", 1383, 42, 291, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_VMT].button }, + { "Login", 1384, 14, 309, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus }, + { "Password", 1385, 14, 336, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus }, + { "Squadron", 1386, 14, 363, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus }, + { "Voice", 1528, 557, 60, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_VOX_TAB].button }, + { "General", 1388, 542, 77, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_GEN_TAB].button }, + }, + { // GR_1024 + { "TCP", 1378, 61, 113, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_TCP].button }, + { "IPX", 1379, 61, 141, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_IPX].button }, + { "IP Address", 1380, 47, 206, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus }, + { "add", 1381, 36, 375, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_ADD_IP].button }, + { "rem.", 1382, 109, 375, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_DELETE_IP].button }, + { "Broadcast Locally", 1397, 68, 417, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_LOCAL_BROADCAST].button }, + { "PXO", 1383, 68, 467, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_VMT].button }, + { "Login", 1384, 23, 495, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus }, + { "Password", 1385, 23, 538, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus }, + { "Squadron", 1386, 23, 582, UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus }, + { "Voice", 1528, 921, 96, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_VOX_TAB].button }, + { "General", 1388, 902, 123, UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_GEN_TAB].button }, + } +}; + +// defines for the tracker input boxes +int Om_tracker_login_coords[GR_NUM_RESOLUTIONS][4] = { + { + 19, 322, 226, -1 // GR_640 + }, + { + 31, 518, 361, -1 // GR_1024 + } +}; +int Om_tracker_passwd_coords[GR_NUM_RESOLUTIONS][4] = { + { + 19, 350, 226, -1 // GR_640 + }, + { + 31, 562, 361, -1 // GR_1024 + } +}; +int Om_tracker_squad_name_coords[GR_NUM_RESOLUTIONS][4] = { + { + 19, 378, 226, -1 // GR_640 + }, + { + 31, 607, 361, -1 // GR_1024 + } +}; + +// protocol section tracker login input box +UI_INPUTBOX Om_tracker_login; + +// protocol section tracker passwd input box +UI_INPUTBOX Om_tracker_passwd; + +// pxo squad name/password +UI_INPUTBOX Om_tracker_squad_name; + +#define TRACKER_FOCUS_NONE 0 +#define TRACKER_FOCUS_LOGIN 1 +#define TRACKER_FOCUS_PASSWORD 2 +#define TRACKER_FOCUS_SQUADRON 3 +static int Om_tracker_focus = 0; + +// ip address list vars +#define IP_STRING_LEN 255 +#define MAX_IP_ADDRS 100 + +#define IP_CONFIG_FNAME NOX("Tcp.cfg") + +#define IP_EMPTY_STRING "" + +int Ip_list_coords[GR_NUM_RESOLUTIONS][4] = { + { + 29, 137, 227, 67 // GR_640 + }, + { + 46, 220, 364, 106 // GR_1024 + } +}; + + +int Ip_list_max_display[GR_NUM_RESOLUTIONS] = { + 5, + 5 +}; + +static int Ip_input_coords[GR_NUM_RESOLUTIONS][4] = { + { + 109, 128, 140, -1 // GR_640 + }, + { + 132, 206, 261, -1 // GR_640 + } +}; + +int Om_input_mode = 0; +int Om_ip_start; // index of the first element to be displayed in the list box +int Om_ip_selected; // the selected default IP address +int Om_ip_disp_count; // how many items are currently being displayed +int Om_num_ips; // # of ip addresses we have +char Om_ip_addrs[MAX_IP_ADDRS][IP_STRING_LEN]; // the ip addresses themselves +UI_BUTTON Om_ip_button; // button for detecting clicks on the ip address list +UI_INPUTBOX Om_ip_input; // input box for adding new ip addresses + +// setting vars +int Om_local_broadcast; // whether the player has local broadcast selected or not +int Om_tracker_flag; // if the guy has the tracker selected +int Om_protocol; // protocol in use + +// load all the controls for the protocol section +void options_multi_load_protocol_controls(); + +// disable/hide all the controls for the protocol section +void options_multi_disable_protocol_controls(); + +// enable/unhide all the controls for the protocol section +void options_multi_enable_protocol_controls(); + +// intialize the protocol section vars +void options_multi_init_protocol_vars(); + +// do frame for the protocol section +void options_multi_protocol_do(int key); + +// if the accept button was hit +void options_multi_protocol_accept(); + +// check for button presses +void options_multi_protocol_check_buttons(); + +// if a button was pressed +void options_multi_protocol_button_pressed(int n); + +// load the ip address file +void options_multi_protocol_load_ip_file(); + +// save the ip address file +void options_multi_protocol_save_ip_file(); + +// draw the list of ip addresses +void options_multi_protocol_display_ips(); + +// scroll the list of ip addresses down +void options_multi_protocol_scroll_ip_down(); + +// scroll the list of ip addresses up +void options_multi_protocol_scroll_ip_up(); + +// check the ip list to see if the user has selected a new item +void options_multi_protocol_check_ip_list(); + +// delete the currently selected ip if any +void options_multi_protocol_delete_ip(); + +// attempt to add the currently entered ip address +void options_multi_protocol_add_current_ip(); + + +// general options tab section ------------------------------------------- +#define OM_GEN_NUM_BUTTONS 10 + +#define OM_GEN_OBJ_LOW 0 +#define OM_GEN_OBJ_MED 1 +#define OM_GEN_OBJ_HIGH 2 +#define OM_GEN_OBJ_LAN 3 +#define OM_GEN_PIX_YES 4 +#define OM_GEN_PIX_NO 5 +#define OM_GEN_XFER_MULTIDATA_YES 6 +#define OM_GEN_XFER_MULTIDATA_NO 7 +#define OM_GEN_FLUSH_NO 8 +#define OM_GEN_FLUSH_YES 9 + +ui_button_info Om_gen_buttons[GR_NUM_RESOLUTIONS][OM_GEN_NUM_BUTTONS] = { + { // GR_640 + ui_button_info("OGB_17", 598, 117, -1, -1, 17), + ui_button_info("OGB_18", 598, 139, -1, -1, 18), + ui_button_info("OGB_19", 598, 161, -1, -1, 19), + ui_button_info("OGB_20", 598, 183, -1, -1, 20), + ui_button_info("OGB_21", 549, 229, -1, -1, 21), + ui_button_info("OGB_22", 598, 229, -1, -1, 22), + ui_button_info("OGB_23", 598, 286, -1, -1, 23), + ui_button_info("OGB_24", 598, 307, -1, -1, 24), + ui_button_info("OGB_25", 598, 347, -1, -1, 25), + ui_button_info("OGB_26", 598, 368, -1, -1, 26), + }, + { // GR_1024 + ui_button_info("2_OGB_17", 957, 188, -1, -1, 17), + ui_button_info("2_OGB_18", 957, 223, -1, -1, 18), + ui_button_info("2_OGB_19", 957, 258, -1, -1, 19), + ui_button_info("2_OGB_20", 957, 293, -1, -1, 20), + ui_button_info("2_OGB_21", 879, 366, -1, -1, 21), + ui_button_info("2_OGB_22", 957, 366, -1, -1, 22), + ui_button_info("2_OGB_23", 957, 457, -1, -1, 23), + ui_button_info("2_OGB_24", 957, 491, -1, -1, 24), + ui_button_info("2_OGB_25", 957, 555, -1, -1, 25), + ui_button_info("2_OGB_26", 957, 589, -1, -1, 26), + } +}; + +UI_GADGET Om_gen_bogus; + +// text +#define OM_GEN_NUM_TEXT 14 +UI_XSTR Om_gen_text[GR_NUM_RESOLUTIONS][OM_GEN_NUM_TEXT] = { + { // GR_640 + { "Object Update", 1391, 511, 104, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus }, + { "Low", 1160, 558, 127, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_OBJ_LOW].button }, + { "Medium", 1161, 538, 149, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_OBJ_MED].button }, + { "High", 1162, 556, 171, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_OBJ_HIGH].button }, + { "Lan", 1392, 561, 193, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_OBJ_LAN].button }, + { "Pilot / Squad Images", 1393, 463, 214, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus }, + { "Yes", 1394, 555, 257, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_PIX_YES].button }, + { "No", 1395, 604, 257, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_PIX_NO].button }, + { "Transfer Missions", 1396, 478, 271, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus }, + { "/multidata", 1397, 519, 292, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_XFER_MULTIDATA_YES].button }, + { "/missions", 1398, 527, 314, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_XFER_MULTIDATA_NO].button }, + { "Flush Cache", 1399, 529, 334, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus }, + { "Never", 1400, 548, 355, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_FLUSH_NO].button }, + { "Before Game", 1401, 502, 377, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[0][OM_GEN_FLUSH_YES].button }, + }, + { // GR_1024 + { "Object Update", 1391, 818, 166, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus }, + { "Low", 1160, 913, 204, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_OBJ_LOW].button }, + { "Medium", 1161, 892, 239, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_OBJ_MED].button }, + { "High", 1162, 909, 274, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_OBJ_HIGH].button }, + { "Lan", 1392, 916, 310, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_OBJ_LAN].button }, + { "Pilot / Squad Images", 1393, 821, 345, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus }, + { "Yes", 1394, 887, 411, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_PIX_YES].button }, + { "No", 1395, 966, 411, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_PIX_NO].button }, + { "Transfer Missions", 1396, 844, 435, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus }, + { "/multidata", 1397, 858, 468, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_XFER_MULTIDATA_YES].button }, + { "/missions", 1398, 870, 503, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_XFER_MULTIDATA_NO].button }, + { "Flush Cache", 1399, 886, 533, UI_XSTR_COLOR_GREEN, -1, &Om_gen_bogus }, + { "Never", 1400, 897, 568, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_FLUSH_NO].button }, + { "Before Game", 1401, 849, 603, UI_XSTR_COLOR_GREEN, -1, &Om_gen_buttons[1][OM_GEN_FLUSH_YES].button }, + } +}; + +// setting vars +int Om_gen_obj_update; // object update level +int Om_gen_pix; // accept pilot pix or not +int Om_gen_xfer_multidata; // xfer missions to multidata or not +int Om_gen_flush_cache; // flush multidata directory before every game + +// load all the general tab controls +void options_multi_load_gen_controls(); + +// disable/hide all the general tab controls +void options_multi_disable_gen_controls(); + +// enable/unhide all the general tab controls +void options_multi_enable_gen_controls(); + +// initialize the general tab vars +void options_multi_init_gen_vars(); + +// accept function for the general tab +void options_multi_gen_accept(); + +// do frame for the general tab +void options_multi_gen_do(); + +// check for button presses +void options_multi_gen_check_buttons(); + +// a button was pressed +void options_multi_gen_button_pressed(int n); + + +// voice options tab section ------------------------------------------- +#define OM_VOX_NUM_BUTTONS 6 + +#define OM_VOX_VOICE_TEST 0 +#define OM_VOX_VOICE_YES 1 +#define OM_VOX_VOICE_NO 2 +#define OM_VOX_PLIST_UP 3 +#define OM_VOX_PLIST_DOWN 4 +#define OM_VOX_VOICE_MUTE 5 + +UI_GADGET Om_vox_bogus; + +ui_button_info Om_vox_buttons[GR_NUM_RESOLUTIONS][OM_VOX_NUM_BUTTONS] = { + { // GR_640 + ui_button_info("OVB_17", 562, 118, -1, -1, 17), + ui_button_info("OVB_19", 551, 208, -1, -1, 19), + ui_button_info("OVB_20", 599, 208, -1, -1, 20), + ui_button_info("OVB_21", 614, 256, -1, -1, 21), + ui_button_info("OVB_22", 614, 290, -1, -1, 22), + ui_button_info("OVB_23", 599, 354, -1, -1, 23), + }, + { // GR_640 + ui_button_info("2_OVB_17", 900, 189, -1, -1, 17), + ui_button_info("2_OVB_19", 882, 333, -1, -1, 19), + ui_button_info("2_OVB_20", 959, 333, -1, -1, 20), + ui_button_info("2_OVB_21", 983, 410, -1, -1, 21), + ui_button_info("2_OVB_22", 983, 464, -1, -1, 22), + ui_button_info("2_OVB_23", 959, 566, -1, -1, 23), + } +}; + +// text +#define OM_VOX_NUM_TEXT 6 +UI_XSTR Om_vox_text[GR_NUM_RESOLUTIONS][OM_VOX_NUM_TEXT] = { + { // GR_640 + { "Mic test", 1389, 567, 104, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[0][OM_VOX_VOICE_TEST].button }, + { "Voice Quality", 1531, 439, 149, UI_XSTR_COLOR_GREEN, -1, &Om_vox_bogus }, + { "Voice Transmission", 1530, 439, 193, UI_XSTR_COLOR_GREEN, -1, &Om_vox_bogus }, + { "On", 1285, 556, 233, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[0][OM_VOX_VOICE_YES].button }, + { "Off", 1286, 604, 233, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[0][OM_VOX_VOICE_NO].button }, + { "Mute", 1390, 594, 381, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[0][OM_VOX_VOICE_MUTE].button }, + }, + { // GR_1024 + { "mic test", 1389, 908, 166, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[1][OM_VOX_VOICE_TEST].button }, + { "Voice Quality", 1531, 703, 239, UI_XSTR_COLOR_GREEN, -1, &Om_vox_bogus }, + { "Voice Transmission", 1530, 783, 310, UI_XSTR_COLOR_GREEN, -1, &Om_vox_bogus }, + { "On", 1285, 890, 373, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[1][OM_VOX_VOICE_YES].button }, + { "Off", 1286, 967, 373, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[1][OM_VOX_VOICE_NO].button }, + { "Mute", 1390, 950, 609, UI_XSTR_COLOR_GREEN, -1, &Om_vox_buttons[1][OM_VOX_VOICE_MUTE].button }, + } +}; + +#define NUM_OM_VOX_SLIDERS 1 +#define OM_VOX_QOS_SLIDER 0 + +op_sliders Om_vox_sliders[GR_NUM_RESOLUTIONS][NUM_OM_VOX_SLIDERS] = { + { // GR_640 + op_sliders("OVB_18", 429, 162, -1, -1, 18, 20, 10, NULL, -1, -1, -1, NULL, -1, -1, -1), // voice QOS + }, + { // GR_1024 + op_sliders("2_OVB_18", 686, 259, -1, -1, 18, 20, 10, NULL, -1, -1, -1, NULL, -1, -1, -1), // voice QOS + } +}; + +// player list area +int Om_vox_plist_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 377, 270, 232, 79 + }, + { // GR_1024 + 604, 432, 371, 127 + } +}; +int Om_vox_plist_max_display[GR_NUM_RESOLUTIONS] = { + 6, + 6 +}; + +int Om_vox_plist_start; +UI_BUTTON Om_vox_plist_button; + +// voice test buffer +#define OM_VOX_BUF_SIZE (1<<12) +#define OM_VOX_COMP_SIZE ((1<<15) + (1<<14)) + +#define OM_VOX_WAVE_Y 240 +#define OM_VOX_WAVE_WIDTH 300 + +#define OM_VOX_DROP_ICON_X 100 +#define OM_VOX_DROP_ICON_Y 100 + +#define OM_VOX_RECORD_INT 175 + +unsigned char Om_vox_voice_buffer[OM_VOX_BUF_SIZE]; +int Om_vox_voice_buffer_size = -1; + +unsigned char Om_vox_comp_buffer[OM_VOX_COMP_SIZE]; +int Om_vox_voice_comp_size = -1; + +int Om_vox_playback_handle; + +// status of any test voice recording +#define OM_VOX_TEST_NONE -1 +#define OM_VOX_TEST_RECORDING 0 +#define OM_VOX_TEST_PLAYBACK 1 +int Om_vox_test_status = OM_VOX_TEST_NONE; + +// setting vars +int Om_vox_accept_voice; + +// simple list of players we are looking through +net_player *Om_vox_players[MAX_PLAYERS]; + +// selected player +net_player *Om_vox_player_select; + +// mute or don't mute for each player +int Om_vox_player_flags[MAX_PLAYERS]; + +// the # of players +int Om_vox_num_players; + +// load all the voice tab controls +void options_multi_load_vox_controls(); + +// disable/hide all the voice tab controls +void options_multi_disable_vox_controls(); + +// enable/unhide all the voice tab controls +void options_multi_enable_vox_controls(); + +// initialize the voice tab vars +void options_multi_init_vox_vars(); + +// accept function for the voice tab +void options_multi_vox_accept(); + +// do frame for the voice tab +void options_multi_vox_do(); + +// check for button presses +void options_multi_vox_check_buttons(); + +// a button was pressed +void options_multi_vox_button_pressed(int n); + +// process/display the player list +void options_multi_vox_process_player_list(); + +// scroll the player list down +void options_multi_vox_plist_scroll_down(); + +// scroll the player list up +void options_multi_vox_plist_scroll_up(); + +// get the index into the player list of the passed netplayer +int options_multi_vox_plist_get(net_player *pl); + + +// general data section ------------------------------------------------ + +// load all background bitmaps +void options_multi_load_bmaps() +{ + // load both background bitmaps + Om_background_0 = bm_load(Om_background_0_fname[gr_screen.res]); + if(Om_background_0 == -1){ + nprintf(("Network","Error loading options background %s\n",Om_background_0_fname[gr_screen.res])); + } + + Om_background_1 = bm_load(Om_background_1_fname[gr_screen.res]); + if(Om_background_1 == -1){ + nprintf(("Network","Error loading options background %s\n",Om_background_1_fname[gr_screen.res])); + } + + // load in both mask bitmaps + Om_mask_0 = bm_load(Om_background_0_mask_fname[gr_screen.res]); + if(Om_mask_0 == -1){ + nprintf(("Network","Error loading options background mask %s\n",Om_background_0_mask_fname[gr_screen.res])); + } + + Om_mask_1 = bm_load(Om_background_1_mask_fname[gr_screen.res]); + if(Om_mask_1 == -1){ + nprintf(("Network","Error loading options background mask %s\n",Om_background_1_mask_fname[gr_screen.res])); + } +} + +// unload all the background bitmaps +void options_multi_unload_bmaps() +{ + // unload all background bitmaps + if(Om_background_0 != -1){ + bm_release(Om_background_0); + Om_background_0 = -1; + } + if(Om_background_1 != -1){ + bm_release(Om_background_1); + Om_background_1 = -1; + } + + // unload all mask bitmaps + if(Om_mask_0 != -1){ + bm_release(Om_mask_0); + Om_mask_0 = -1; + } + if(Om_mask_1 != -1){ + bm_release(Om_mask_1); + Om_mask_1 = -1; + } +} + +// add a notification message +void options_multi_add_notify(char *str) +{ + // copy the string + memset(Om_notify_string,0,255); + if(str != NULL){ + strcpy(Om_notify_string,str); + } + + // set the timestamp + Om_notify_stamp = timestamp(OM_NOTIFY_TIME); +} + +// process and blit any notification messages +void options_multi_notify_process() +{ + int w; + char *p_str[3]; + int n_chars[3]; + char line[255]; + int line_count; + int y_start; + int idx; + + // if there is no timestamp, do nothing + if(Om_notify_stamp == -1){ + return; + } + + // otherwise, if it has elapsed, unset it + if(timestamp_elapsed(Om_notify_stamp)){ + Om_notify_stamp = -1; + return; + } + + // otherwise display the string + line_count = split_str(Om_notify_string, 600, n_chars, p_str, 3); + y_start = OM_NOTIFY_Y; + gr_set_color_fast(&Color_bright); + for(idx=0;idxadd_XSTR(&Om_pro_text[gr_screen.res][idx]); + } + + // create the tracker input boxes + Om_tracker_login.create(Om_window, Om_tracker_login_coords[gr_screen.res][0], Om_tracker_login_coords[gr_screen.res][1], Om_tracker_login_coords[gr_screen.res][2], LOGIN_LEN - 1, Multi_tracker_login, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_NO_BACK); + Om_tracker_passwd.create(Om_window, Om_tracker_passwd_coords[gr_screen.res][0], Om_tracker_passwd_coords[gr_screen.res][1], Om_tracker_passwd_coords[gr_screen.res][2], 1, Multi_tracker_passwd, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_PASSWD | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_NO_BACK); + Om_tracker_squad_name.create(Om_window, Om_tracker_squad_name_coords[gr_screen.res][0], Om_tracker_squad_name_coords[gr_screen.res][1], Om_tracker_squad_name_coords[gr_screen.res][2], LOGIN_LEN - 1, Multi_tracker_squad_name, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_NO_BACK); + + // create the invisible button for checking for clicks on the ip address list + Om_ip_button.create(Om_window, "", Ip_list_coords[gr_screen.res][0], Ip_list_coords[gr_screen.res][1], Ip_list_coords[gr_screen.res][2], Ip_list_coords[gr_screen.res][3], 0, 1); + Om_ip_button.hide(); + + // create the new ip address input box + Om_ip_input.create(Om_window, Ip_input_coords[gr_screen.res][0], Ip_input_coords[gr_screen.res][1], Ip_input_coords[gr_screen.res][2], IP_STRING_LEN, IP_EMPTY_STRING, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU); + Om_ip_input.hide(); + Om_ip_input.disable(); + + // disable IPX button in demo +#ifdef FS2_DEMO + Om_pro_buttons[gr_screen.res][OM_PRO_IPX].button.disable(); + Om_pro_buttons[gr_screen.res][OM_PRO_IPX].button.hide(); +#endif + + // bogus control + Om_pro_bogus.base_create(Om_window, UI_KIND_ICON, 0, 0, 0, 0); +} + +// disable/hide all the controls for the protocol section +void options_multi_disable_protocol_controls() +{ + int idx; + + // hide and disable all the protocol buttons + for(idx=0;idxm_local_options.flags & MLO_FLAG_LOCAL_BROADCAST) ? 1 : 0; + + // whether or not we're playing on the tracker + Om_tracker_flag = 0; // (Multi_options_g.protocol == NET_TCP) && Multi_options_g.pxo ? 1 : 0; + + // load the ip address list + Om_ip_disp_count = 0; + options_multi_protocol_load_ip_file(); + Om_ip_selected = Om_num_ips - 1; + Om_ip_start = Om_num_ips - 1; +} + +// do frame for the protocol section +void options_multi_protocol_do(int key) +{ + // check for button presses + options_multi_protocol_check_buttons(); + + // force draw the correct "local broadcast" button + if(Om_local_broadcast){ + Om_pro_buttons[gr_screen.res][OM_PRO_LOCAL_BROADCAST].button.draw_forced(2); + } + + // draw the "vmt" button if it is selected + if(Om_tracker_flag){ + Om_pro_buttons[gr_screen.res][OM_PRO_VMT].button.draw_forced(2); + } + + // see if he hit any interesting key presses + switch(key){ + case KEY_ENTER: + // add a new ip string if we're in "input" mode + if(Om_input_mode){ + options_multi_protocol_add_current_ip(); + + // clear the text control and input mode + Om_ip_input.set_text(""); + Om_ip_input.clear_focus(); + Om_ip_input.disable(); + Om_input_mode = 0; + } + + // if the tracker login inputbox has focus, lose it + if(Om_tracker_login.has_focus()){ + Om_tracker_login.clear_focus(); + gamesnd_play_iface(SND_COMMIT_PRESSED); + } + // if the tracker password inputbox has focus, lose it + if(Om_tracker_passwd.has_focus()){ + Om_tracker_passwd.clear_focus(); + gamesnd_play_iface(SND_COMMIT_PRESSED); + } + // if the tracker squad name inputbox has focus, lose it + if(Om_tracker_squad_name.has_focus()){ + Om_tracker_squad_name.clear_focus(); + gamesnd_play_iface(SND_COMMIT_PRESSED); + } + break; + + case KEY_ESC: + // if we're in input mode, cancel out + if(Om_input_mode){ + // clear the text control and input mode + Om_ip_input.set_text(""); + Om_ip_input.clear_focus(); + Om_ip_input.disable(); + Om_input_mode = 0; + } + // otherwise quit the options screen altogether + else { + options_cancel_exit(); + } + break; + + case KEY_TAB: + // tab through the tracker input controls + if(Om_tracker_login.has_focus()){ + Om_tracker_passwd.set_focus(); + } else if(Om_tracker_passwd.has_focus()){ + Om_tracker_squad_name.set_focus(); + } else if(Om_tracker_squad_name.has_focus()){ + Om_tracker_login.set_focus(); + } + break; + } + + // force draw the proper protocol + if (Om_protocol == NET_IPX) { + Om_pro_buttons[gr_screen.res][OM_PRO_IPX].button.draw_forced(2); + } else { + Om_pro_buttons[gr_screen.res][OM_PRO_TCP].button.draw_forced(2); + } + + // force draw the proper tab button + switch (Om_mode) { + case OM_MODE_GENERAL: + Om_pro_buttons[gr_screen.res][OM_PRO_GEN_TAB].button.draw_forced(2); + break; + + case OM_MODE_VOX: + Om_pro_buttons[gr_screen.res][OM_PRO_VOX_TAB].button.draw_forced(2); + break; + } + + // check to see if the user has clicked on the ip list and selected a new item + options_multi_protocol_check_ip_list(); + + // draw the list of ip addresses + options_multi_protocol_display_ips(); + + // hack to play sound when input boxes gain focus + if (Om_tracker_login.has_focus()) { + if (Om_tracker_focus != TRACKER_FOCUS_LOGIN) { + Om_tracker_focus = TRACKER_FOCUS_LOGIN; + gamesnd_play_iface(SND_USER_SELECT); + } + } else if (Om_tracker_passwd.has_focus()) { + if (Om_tracker_focus != TRACKER_FOCUS_PASSWORD) { + Om_tracker_focus = TRACKER_FOCUS_PASSWORD; + gamesnd_play_iface(SND_USER_SELECT); + } + } else if (Om_tracker_squad_name.has_focus()) { + if (Om_tracker_focus != TRACKER_FOCUS_SQUADRON) { + Om_tracker_focus = TRACKER_FOCUS_SQUADRON; + gamesnd_play_iface(SND_USER_SELECT); + } + } else { + Om_tracker_focus = TRACKER_FOCUS_NONE; + } +} + +// if the accept button was hit +void options_multi_protocol_accept() +{ + // if the user has selected local broadcast write it into his options struct + Player->m_local_options.flags &= ~(MLO_FLAG_LOCAL_BROADCAST); + if(Om_local_broadcast){ + Player->m_local_options.flags |= MLO_FLAG_LOCAL_BROADCAST; + } + + // active protocol + Multi_options_g.protocol = Om_protocol; + + // copy the VMT login and password data + Om_tracker_login.get_text(Multi_tracker_login); + Om_tracker_passwd.get_text(Multi_tracker_passwd); + Om_tracker_squad_name.get_text(Multi_tracker_squad_name); + + // write out the tracker login and passwd values to the registry + os_config_write_string( "PXO", "Login", Multi_tracker_login ); + os_config_write_string( "PXO", "Password", Multi_tracker_passwd ); + + // write out the PXO squad name and passwd values to the registry + os_config_write_string( "PXO", "SquadName", Multi_tracker_squad_name ); + + // save the ip address list + options_multi_protocol_save_ip_file(); +} + +// check for button presses +void options_multi_protocol_check_buttons() +{ + int idx; + + // go through each button + for(idx=0;idx= 0); + Om_window->set_mask_bmap(Om_mask_0, Om_background_0_mask_fname[gr_screen.res]); + } + + // play a sound + gamesnd_play_iface(SND_USER_SELECT); + + break; + + // voice tab button + case OM_PRO_VOX_TAB: + if(Om_mode != OM_MODE_VOX){ + // set the voice tab + Om_mode = OM_MODE_VOX; + + // disable the general controls + options_multi_disable_gen_controls(); + + // enable the voice controls + options_multi_enable_vox_controls(); + + // set the voice screen mask + Assert(Om_mask_1 >= 0); + Om_window->set_mask_bmap(Om_mask_1, Om_background_1_mask_fname[gr_screen.res]); + } + // play a sound + gamesnd_play_iface(SND_USER_SELECT); + + break; + + // tcp mode + case OM_PRO_TCP: + Om_protocol = NET_TCP; + gamesnd_play_iface(SND_USER_SELECT); + break; + + // ipx mode + case OM_PRO_IPX: +#ifndef FS2_DEMO + Om_protocol = NET_IPX; + gamesnd_play_iface(SND_USER_SELECT); +#endif + break; + } +} + +// load the ip address file +void options_multi_protocol_load_ip_file() +{ + char line[IP_STRING_LEN]; + CFILE *file = NULL; + + // reset the ip address count + Om_num_ips = 0; + + // attempt to open the ip list file + file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA); + if(file == NULL){ + nprintf(("Network","Error loading tcp.cfg file!\n")); + return; + } + + // read in all the strings in the file + while(!cfeof(file)){ + line[0] = '\0'; + cfgets(line,IP_STRING_LEN,file); + + // strip off any newline character + if(line[strlen(line) - 1] == '\n'){ + line[strlen(line) - 1] = '\0'; + } + + // 0 length lines don't get processed + if((line[0] == '\0') || (line[0] == '\n') ) + continue; + + if ( !psnet_is_valid_ip_string(line) ) { + nprintf(("Network","Invalid ip string (%s)\n",line)); + } else { + if(Om_num_ips < MAX_IP_ADDRS-1){ + strcpy(Om_ip_addrs[Om_num_ips++],line); + } + } + } + + cfclose(file); +} + +// save the ip address file +void options_multi_protocol_save_ip_file() +{ + int idx; + CFILE *file = NULL; + + // attempt to open the ip list file for writing + file = cfopen(IP_CONFIG_FNAME,"wt",CFILE_NORMAL,CF_TYPE_DATA ); + if(file == NULL){ + nprintf(("Network","Error loading tcp.cfg file\n")); + return; + } + + // write out all the string we have + for(idx=0;idx= Ip_list_max_display[gr_screen.res]){ + Om_ip_disp_count = Ip_list_max_display[gr_screen.res]; + } else { + Om_ip_disp_count = Om_ip_start + 1; + } + + // display the addresses + for(idx=Om_ip_start; idx >= Om_ip_start - Om_ip_disp_count + 1 ; idx--){ + if(idx == Om_ip_selected){ + gr_set_color_fast(&Color_bright); + } else { + gr_set_color_fast(&Color_white); + } + + gr_printf(Ip_list_coords[gr_screen.res][0], y_start, Om_ip_addrs[idx]); + y_start += 10; + } +} + +// scroll the list of ip addresses down +void options_multi_protocol_scroll_ip_down() +{ + if(Om_ip_start >= Ip_list_max_display[gr_screen.res]){ + gamesnd_play_iface(SND_SCROLL); + Om_ip_start--; + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// scroll the list of ip addresses up +void options_multi_protocol_scroll_ip_up() +{ + if(Om_ip_start < Om_num_ips-1){ + gamesnd_play_iface(SND_SCROLL); + Om_ip_start++; + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// check the ip list to see if the user has selected a new item +void options_multi_protocol_check_ip_list() +{ + int click_y; + int item; + + if(Om_ip_button.pressed()){ + // determine which item he clicked on + Om_ip_button.get_mouse_pos(NULL, &click_y); + item = click_y / 10; + + // determine if there is an item in this location, and select it if so + if(item < Om_ip_disp_count){ + Om_ip_selected = Om_ip_start - item; + } + } +} + +// delete the currently selected ip if any +void options_multi_protocol_delete_ip() +{ + int idx; + + // attempt to delete the currently highlighted item + if(Om_ip_selected != -1){ + + // move down all the other items + for(idx=Om_ip_selected; idx < Om_num_ips; idx++){ + strcpy(Om_ip_addrs[idx],Om_ip_addrs[idx+1]); + } + + // make sure to decrement the starting index + Om_ip_start--; + + // check to make sure that the selected item is valid + Om_num_ips--; + if(Om_num_ips <= 0){ + Om_ip_selected = -1; + } else { + if(Om_ip_selected > 0){ + Om_ip_selected--; + } + } + } +} + +// return 10, if successflu +char Ip_str[IP_STRING_LEN+1]; +int Ip_validated_already = 0; +int options_multi_verify_ip() +{ + int result; + + if(!Ip_validated_already){ + // see if its a valid ip address + result = psnet_is_valid_ip_string(Ip_str); + + // if the result is a valid ip string, return immediately + if(result){ + return 10; + } + + // otherwise, change the popup text to indicate that it is invalid and wait for the user to click ok + popup_change_text(XSTR( "Ip string is invalid!", 386)); + } + + Ip_validated_already = 1; + + // always wait for the user to hit the "cancel" button + return 0; +} + +// attempt to add the currently entered ip address +void options_multi_protocol_add_current_ip() +{ + // get the entered string + Om_ip_input.get_text(Ip_str); + + // this popup wil do several things. + // 1.) It will display a popup so the user isn't left scratching his head + // 2.) If the address + Ip_validated_already = 0; + if(popup_till_condition(options_multi_verify_ip, XSTR( "Cancel", 387), XSTR( "Verifying ip address", 388)) == 10){ + if(Om_num_ips < MAX_IP_ADDRS){ + strcpy(Om_ip_addrs[Om_num_ips],Ip_str); + Om_ip_start = Om_num_ips; + Om_num_ips++; + + // if this is the first item on the list, select it + if(Om_num_ips == 1){ + Om_ip_selected = 0; + } + } else { + options_multi_add_notify(XSTR( "Max # of IP addresses reached!", 389)); + } + } +} + +// general options tab section ------------------------------------------- + +// load all the general tab controls +void options_multi_load_gen_controls() +{ + int idx; + + Assert(Om_window != NULL); + + // instantiate all the buttons + for(idx=0; idxadd_XSTR(&Om_gen_text[gr_screen.res][idx]); + } + + // bogus control + Om_gen_bogus.base_create(Om_window, UI_KIND_ICON, 0, 0, 0, 0); +} + +// disable/hide all the general tab controls +void options_multi_disable_gen_controls() +{ + int idx; + + // go through all the controls + for(idx=0;idxm_local_options.obj_update_level; + + // initialize the accept pix var + if(Player->m_local_options.flags & MLO_FLAG_ACCEPT_PIX){ + Om_gen_pix = 1; + } else { + Om_gen_pix = 0; + } + + // initialize the xfer_multidata var + if(Player->m_local_options.flags & MLO_FLAG_XFER_MULTIDATA){ + Om_gen_xfer_multidata = 1; + } else { + Om_gen_xfer_multidata = 0; + } + + // initialize the flush cache var + if(Player->m_local_options.flags & MLO_FLAG_FLUSH_CACHE){ + Om_gen_flush_cache = 1; + } else { + Om_gen_flush_cache = 0; + } +} + +// accept function for the general tab +void options_multi_gen_accept() +{ + // apply the object update level + Player->m_local_options.obj_update_level = Om_gen_obj_update; + + // apply the accept pix var + Player->m_local_options.flags &= ~(MLO_FLAG_ACCEPT_PIX); + if(Om_gen_pix){ + Player->m_local_options.flags |= MLO_FLAG_ACCEPT_PIX; + } + + // apply the xfer multidata var + Player->m_local_options.flags &= ~(MLO_FLAG_XFER_MULTIDATA); + if(Om_gen_xfer_multidata){ + Player->m_local_options.flags |= MLO_FLAG_XFER_MULTIDATA; + } + + // apply the flush cache var + Player->m_local_options.flags &= ~(MLO_FLAG_FLUSH_CACHE); + if(Om_gen_flush_cache){ + Player->m_local_options.flags |= MLO_FLAG_FLUSH_CACHE; + } +} + +// do frame for the general tab +void options_multi_gen_do() +{ + // check for button presses + options_multi_gen_check_buttons(); + + // draw the proper object update button + switch(Om_gen_obj_update){ + case OBJ_UPDATE_LOW: + Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_LOW].button.draw_forced(2); + break; + case OBJ_UPDATE_MEDIUM: + Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_MED].button.draw_forced(2); + break; + case OBJ_UPDATE_HIGH: + Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_HIGH].button.draw_forced(2); + break; + case OBJ_UPDATE_LAN: + Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_LAN].button.draw_forced(2); + break; + default : + Int3(); + } + + // draw the proper pix button + if(Om_gen_pix){ + Om_gen_buttons[gr_screen.res][OM_GEN_PIX_YES].button.draw_forced(2); + } else { + Om_gen_buttons[gr_screen.res][OM_GEN_PIX_NO].button.draw_forced(2); + } + + // draw the proper xfer multidata button + if(Om_gen_xfer_multidata){ + Om_gen_buttons[gr_screen.res][OM_GEN_XFER_MULTIDATA_YES].button.draw_forced(2); + } else { + Om_gen_buttons[gr_screen.res][OM_GEN_XFER_MULTIDATA_NO].button.draw_forced(2); + } + + // draw the proper flush cache button + if(Om_gen_flush_cache){ + Om_gen_buttons[gr_screen.res][OM_GEN_FLUSH_YES].button.draw_forced(2); + } else { + Om_gen_buttons[gr_screen.res][OM_GEN_FLUSH_NO].button.draw_forced(2); + } +} + +// check for button presses +void options_multi_gen_check_buttons() +{ + int idx; + + // go through all the buttons + for(idx=0;idxadd_XSTR(&Om_vox_text[gr_screen.res][idx]); + } + + // sliders + for ( idx = 0; idx < NUM_OM_VOX_SLIDERS; idx++ ) { + Om_vox_sliders[gr_screen.res][idx].slider.create(Om_window, Om_vox_sliders[gr_screen.res][idx].x, Om_vox_sliders[gr_screen.res][idx].y, + Om_vox_sliders[gr_screen.res][idx].dots, Om_vox_sliders[gr_screen.res][idx].filename, + Om_vox_sliders[gr_screen.res][idx].hotspot, Om_vox_sliders[gr_screen.res][idx].right_filename, Om_vox_sliders[gr_screen.res][idx].right_mask, Om_vox_sliders[gr_screen.res][idx].right_x, Om_vox_sliders[gr_screen.res][idx].right_y, + Om_vox_sliders[gr_screen.res][idx].left_filename, Om_vox_sliders[gr_screen.res][idx].left_mask, Om_vox_sliders[gr_screen.res][idx].left_x, Om_vox_sliders[gr_screen.res][idx].left_y, + Om_vox_sliders[gr_screen.res][idx].dot_w); + } + + // create the player list select button + Om_vox_plist_button.create(Om_window, "", Om_vox_plist_coords[gr_screen.res][0], Om_vox_plist_coords[gr_screen.res][1], Om_vox_plist_coords[gr_screen.res][2], Om_vox_plist_coords[gr_screen.res][3], 0, 1); + Om_vox_plist_button.hide(); + + // build a list of net players + Om_vox_num_players = 0; + for(idx=0;idxflags & NETINFO_FLAG_CONNECTED)){ + continue; + } + + // add all players I know about + if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){ + // set the netplayer pointer + Om_vox_players[Om_vox_num_players] = &Net_players[idx]; + + // set his mute flag + Om_vox_player_flags[Om_vox_num_players] = (Multi_voice_local_prefs & (1<m_local_options.flags & MLO_FLAG_NO_VOICE){ + Om_vox_accept_voice = 0; + } else { + Om_vox_accept_voice = 1; + } +} + +// accept function for the voice tab +void options_multi_vox_accept() +{ + int idx; + int voice_pref_flags; + + // set the accept voice flag + Player->m_local_options.flags &= ~(MLO_FLAG_NO_VOICE); + if(!Om_vox_accept_voice){ + Player->m_local_options.flags |= MLO_FLAG_NO_VOICE; + } + + // build the voice preferences stuff + voice_pref_flags = 0xffffffff; + for(idx=0;idx= (DWORD)Om_vox_voice_comp_size)){ + // flush all playing sounds safely + rtvoice_stop_playback_all(); + + // null the sound handle + Om_vox_playback_handle = -1; + + // set this so we know not to display any more waveforms + Om_vox_voice_buffer_size = -1; + Om_vox_voice_comp_size = -1; + + // free the status up + Om_vox_test_status = OM_VOX_TEST_NONE; + } +#endif + break; + } +} + +// check for button presses +void options_multi_vox_check_buttons() +{ + int idx; + + // go through all the buttons + for(idx=0; idxflags & NETINFO_FLAG_CONNECTED)){ + options_multi_add_notify(XSTR( "Cannot test mic while in a multiplayer game!", 391)); + break; + } + + // if this machine is not capable of playing + if(!Multi_voice_can_record){ + options_multi_add_notify(XSTR( "DirectSoundCapture could not be initialized. To initialize DirectSoundCapture your sound card must be full duplex and your sound card drivers must support DirectSoundCapture", 392)); + } else { + // if we're not already doing a record test + if(Om_vox_test_status == OM_VOX_TEST_NONE){ + // set the quality of sound + rtvoice_set_qos(Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.pos + 1); + + // clear the comp buffer + memset(Om_vox_comp_buffer,128,OM_VOX_COMP_SIZE); + + Om_vox_test_status = OM_VOX_TEST_RECORDING; + multi_voice_test_record_start(); + } + } + break; + } +} + +// screen shader +extern shader Grey_shader; + +// process and blit any voice waveform if necessary +void options_multi_vox_process_waveform() +{ + int c_width = OM_VOX_WAVE_WIDTH; + int avg_len; + int buf_offset; + int idx,a_idx,running_avg; + + // if we're not in recording or playback mode + if(Om_vox_test_status == OM_VOX_TEST_NONE){ + return; + } + + // grey the screen + gr_set_shader(&Grey_shader); + gr_shade(0,0,gr_screen.clip_width, gr_screen.clip_height); + + switch(Om_vox_test_status){ + case OM_VOX_TEST_RECORDING: + // if we have no sound buffer size, do nothing + if(Om_vox_voice_buffer_size <= 0){ + return; + } + + // if we are not recording, do nothing + if(Om_vox_test_status != OM_VOX_TEST_RECORDING){ + return; + } + + // get the # of samples we'll average for one line + avg_len = Om_vox_voice_buffer_size / c_width; + + // blit the waveform + gr_set_color_fast(&Color_green); + buf_offset = 0; + for(idx=0; idx < c_width; idx++){ + // reset the running average + running_avg = 0; + for(a_idx = 0; a_idx < avg_len; a_idx++){ + running_avg += (int)Om_vox_voice_buffer[buf_offset] - 128; + + // increment the buffer offset + buf_offset++; + } + + running_avg /= avg_len; + gr_line((gr_screen.max_w - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y, (gr_screen.max_w - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y + running_avg); + } + + // if this packet would have been dropped, notify the user + if(multi_voice_test_packet_tossed()){ + gr_set_color_fast(&Color_bright); + gr_string(OM_VOX_DROP_ICON_X,OM_VOX_DROP_ICON_Y, XSTR( "Packet Overflow", 393)); + } + break; + + case OM_VOX_TEST_PLAYBACK: + // get the offset into the playing direct sound buffer +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + buf_offset = ds_get_play_position(ds_get_channel(Om_vox_playback_handle)); + + // get the # of samples we'll average for one line + avg_len = (int)((float)OM_VOX_RECORD_INT * ((1024.0f * 11.0f) / 1000.0f)) / c_width; + + // blit the waveform + gr_set_color_fast(&Color_red); + for(idx=0; idx < c_width; idx++){ + // reset the running average + running_avg = 0; + for(a_idx = 0; a_idx < avg_len; a_idx++){ + if(buf_offset < (OM_VOX_COMP_SIZE - 2)){ + running_avg += (int)Om_vox_comp_buffer[buf_offset] - 128; + + // increment the buffer offset + buf_offset++; + } + } + + running_avg /= avg_len; + gr_line((gr_screen.max_w - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y, (gr_screen.max_w - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y + running_avg); + } +#endif + break; + } +} + +// process/display the player list +void options_multi_vox_process_player_list() +{ + int idx; + int y_start,p_count; + int selected_index,click_y; + char str[CALLSIGN_LEN+2]; + + // check for mouse clicks + if(Om_vox_plist_button.pressed()){ + Om_vox_plist_button.get_mouse_pos(NULL,&click_y); + selected_index = (click_y / 10) + Om_vox_plist_start; + + // if he clicked on a valid player, select him + if(Om_vox_players[selected_index] != NULL){ + Om_vox_player_select = Om_vox_players[selected_index]; + + nprintf(("Network","Selecting player %s\n",Om_vox_player_select->player->callsign)); + } + } + + // draw the list of players + p_count = 0; + y_start = Om_vox_plist_coords[gr_screen.res][1]; + for(idx = Om_vox_plist_start; idx < Om_vox_num_players; idx++){ + if(Om_vox_players[idx] != NULL){ + // if he's the selected player, highlight him + if(Om_vox_players[idx] == Om_vox_player_select){ + gr_set_color_fast(&Color_bright); + } else { + gr_set_color_fast(&Color_normal); + } + + // force fit his callsign + strcpy(str,Om_vox_players[idx]->player->callsign); + gr_force_fit_string(str, CALLSIGN_LEN+1, Om_vox_plist_coords[gr_screen.res][2]); + + // blit the callsign + gr_string(Om_vox_plist_coords[gr_screen.res][0], y_start, str); + + // increment the y index + y_start += 10; + + // increment the player count + p_count++; + } + + // if we've reached max display, break out + if(p_count >= Om_vox_plist_max_display[gr_screen.res]){ + break; + } + } +} + +// get the index into the player list of the passed netplayer +int options_multi_vox_plist_get(net_player *pl) +{ + int idx; + + for(idx=0;idx= Om_vox_plist_max_display[gr_screen.res]){ + Om_vox_plist_start++; + gamesnd_play_iface(SND_USER_SELECT); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// scroll the player list up +void options_multi_vox_plist_scroll_up() +{ + if(Om_vox_plist_start > 0){ + Om_vox_plist_start--; + gamesnd_play_iface(SND_USER_SELECT); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + + +// extern functions ----------------------------------------------------- + +// called when the options screen is initialized, pass in the UI window +void options_multi_init(UI_WINDOW *options_window) +{ + // assign the options window + Om_window = options_window; + + // load the background bitmaps + options_multi_load_bmaps(); + + // load the controls for the protocol area + options_multi_load_protocol_controls(); + + // load the controls for the general tab + options_multi_load_gen_controls(); + + // load the controls for the voice tab + options_multi_load_vox_controls(); + + // disable all the protocol controls + options_multi_disable_protocol_controls(); + + // disable all the general tab controls + options_multi_disable_gen_controls(); + + // disable all the voice tab controls + options_multi_disable_vox_controls(); + + // intialize the protocol section vars + options_multi_init_protocol_vars(); + + // initialize the general tab vars + options_multi_init_gen_vars(); + + // initialize the voice tab vars + options_multi_init_vox_vars(); + + // intialize the multiplayer voice recording system + if( !((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_CONNECTED)) ){ + multi_voice_init(); + } + + // set the default screen mode + Om_mode = OM_MODE_NONE; +} + +// do frame for the multi options screen +void options_multi_do(int key) +{ + // do frame for the protocol section + options_multi_protocol_do(key); + + // process and blit any notification messages + options_multi_notify_process(); + + // process the proper tab control + switch(Om_mode){ + case OM_MODE_GENERAL: + options_multi_gen_do(); + break; + case OM_MODE_VOX: + options_multi_vox_do(); + break; + default : + Int3(); + } +} + +// called when the entire options screen is closed +void options_multi_close() +{ + // null out the window handle + Om_window = NULL; + + // unload all background bitmaps + options_multi_unload_bmaps(); + + // stop any playing voice + rtvoice_stop_playback_all(); + + // unset the screen mode + Om_mode = OM_MODE_NONE; +} + +// called if the accept button on the main options screen was hit +void options_multi_accept() +{ + // accept function for the protocol section + options_multi_protocol_accept(); + + // accept function for the general tab + options_multi_gen_accept(); + + // accept function for the voice tab + options_multi_vox_accept(); + + // if Net_player is not null, copy these new settings to him + if(Net_player != NULL){ + multi_options_local_load(&Net_player->p_info.options, NULL); + } + multi_options_local_load(&Player->m_local_options, NULL); + + // if we're connected to a game server, update our options on the server now + if((Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && MULTI_CONNECTED(Net_players[MY_NET_PLAYER_NUM]) ){ + multi_options_update_local(); + } +} + +// called when the multiplayer tab is hit - initializes/switches all necessary data. +// NOTE : this is different from the initialization function, which is called only when the options menu is started +void options_multi_select() +{ + // set the windows mask bitmap + Assert(Om_mask_0 >= 0); + Om_window->set_mask_bmap(Om_mask_0, Om_background_0_mask_fname[gr_screen.res]); + + // set the default screen mode + Om_mode = OM_MODE_GENERAL; + + // clear any notification messages + Om_notify_stamp = -1; + + // enable all the protocol controls + options_multi_enable_protocol_controls(); + + // enable the general tab controls + options_multi_enable_gen_controls(); +} + +// return the bitmap handle of the current background bitmap, or -1 if the multiplayer tab is not active +int options_multi_background_bitmap() +{ + // return the background bitmap mode based upon the current mode + switch(Om_mode){ + case OM_MODE_GENERAL: + return Om_background_0; + + case OM_MODE_VOX: + return Om_background_1; + } + + // unknown mode of some kind + return -1; +} + +// called when the multiplayer tab has been switched from +void options_multi_unselect() +{ + // unset the mode + Om_mode = OM_MODE_NONE; + + // disable all the protocol controls + options_multi_disable_protocol_controls(); + + // disable all the general tab controls + options_multi_disable_gen_controls(); + + // disable all the vox tab controls + options_multi_disable_vox_controls(); + + // stop any test voice recording + multi_voice_test_record_stop(); +} + +// set voice sound buffer for display +void options_multi_set_voice_data(unsigned char *sound_buf,int buf_size,unsigned char *comp_buf, int comp_size, int uncomp_size, double gain) +{ + // copy the buffer to the vox tab data + if(buf_size > OM_VOX_BUF_SIZE){ + memcpy(Om_vox_voice_buffer,sound_buf,OM_VOX_BUF_SIZE); + Om_vox_voice_buffer_size = OM_VOX_BUF_SIZE; + } else { + memcpy(Om_vox_voice_buffer,sound_buf,buf_size); + Om_vox_voice_buffer_size = buf_size; + } + + // copy and uncompress the compressed buffer + if(Om_vox_voice_comp_size == -1){ + Om_vox_voice_comp_size = 0; + } + // if we can fit it, decompress this data + if((Om_vox_voice_comp_size + uncomp_size) < OM_VOX_COMP_SIZE){ + // uncompress the data + rtvoice_uncompress(comp_buf, comp_size, gain, Om_vox_comp_buffer + Om_vox_voice_comp_size, uncomp_size); + + Om_vox_voice_comp_size += uncomp_size; + } +} + +// return whether we want to eat a tabbed keypress +int options_multi_eat_tab() +{ + // do we want to eat the tab key or not + if(Om_tracker_passwd.has_focus() || Om_tracker_login.has_focus() || Om_tracker_squad_name.has_focus()){ + return 1; + } + + return 0; +} diff --git a/src/menuui/playermenu.cpp b/src/menuui/playermenu.cpp new file mode 100644 index 0000000..9459db3 --- /dev/null +++ b/src/menuui/playermenu.cpp @@ -0,0 +1,1517 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/PlayerMenu.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to drive the Player Select initial screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 43 11/02/99 11:42a Jefff + * fixed copyright symbol in german fonts + * + * 42 10/27/99 12:27a Jefff + * localized tips correctly + * + * 41 9/13/99 4:52p Dave + * RESPAWN FIX + * + * 40 9/02/99 11:10a Jefff + * fixed 1024 list display bug - was only showing 8 pilots at a time + * + * 39 8/26/99 8:51p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 38 8/26/99 9:45a Dave + * First pass at easter eggs and cheats. + * + * 37 8/16/99 6:39p Jefff + * + * 36 8/16/99 6:37p Jefff + * minor string alterations + * + * 35 8/05/99 4:17p Dave + * Tweaks to client interpolation. + * + * 34 8/05/99 11:29a Mikek + * Jacked up number of comments from 20 to 40, thereby doubling the + * quality of our game. + * + * 33 8/04/99 10:53a Dave + * Added title to the user tips popup. + * + * 32 8/03/99 3:21p Jefff + * + * 31 8/03/99 10:32a Jefff + * raised location of bottom_text to not interfere w/ logo. changed + * "please enter callsign" to "type callsign and press enter" + * + * 30 8/02/99 9:13p Dave + * Added popup tips. + * + * 29 7/30/99 10:29a Jefff + * fixed colors of bottom display texts + * + * 28 7/27/99 7:17p Jefff + * Replaced some art text with XSTR() text. + * + * 27 7/19/99 2:06p Jasons + * Remove all palette stuff from player select menu. + * + * 26 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 25 7/09/99 9:51a Dave + * Added thick polyline code. + * + * 24 6/11/99 11:13a Dave + * last minute changes before press tour build. + * + * 23 5/21/99 6:45p Dave + * Sped up ui loading a bit. Sped up localization disk access stuff. Multi + * start game screen, multi password, and multi pxo-help screen. + * + * 22 4/25/99 3:02p Dave + * Build defines for the E3 build. + * + * 21 3/25/99 2:31p Neilk + * Coordinate changes to handle new artwork + * + * 20 2/25/99 4:19p Dave + * Added multiplayer_beta defines. Added cd_check define. Fixed a few + * release build warnings. Added more data to the squad war request and + * response packets. + * + * 19 2/01/99 5:55p Dave + * Removed the idea of explicit bitmaps for buttons. Fixed text + * highlighting for disabled gadgets. + * + * 18 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 17 1/30/99 1:53a Dave + * Fix some harcoded coords. + * + * 16 1/30/99 1:28a Dave + * 1024x768 full support. + * + * 15 1/29/99 1:25p Dave + * New code for choose pilot screen. + * + * 14 1/29/99 12:47a Dave + * Put in sounds for beam weapon. A bunch of interface screens (tech + * database stuff). + * + * 13 1/12/99 12:53a Dave + * More work on beam weapons - made collision detection very efficient - + * collide against all object types properly - made 3 movement types + * smooth. Put in test code to check for possible non-darkening pixels on + * object textures. + * + * 12 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 11 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 10 12/01/98 6:20p Dave + * Removed tga test bitmap code. + * + * 9 12/01/98 4:46p Dave + * Put in targa bitmap support (16 bit). + * + * 8 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 7 11/20/98 11:16a Dave + * Fixed up IPX support a bit. Making sure that switching modes and + * loading/saving pilot files maintains proper state. + * + * 6 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 5 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 4 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 3 10/09/98 2:57p Dave + * Starting splitting up OS stuff. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * + * $NoKeywords: $ + * + */ + +#include + +#include "playermenu.h" +#include "2d.h" +#include "ui.h" +#include "gamesnd.h" +#include "player.h" +#include "cfile.h" +#include "key.h" +#include "managepilot.h" +#include "missionscreencommon.h" +#include "bmpman.h" +#include "freespace.h" +#include "parselo.h" +#include "gamesequence.h" +#include "timer.h" +#include "cmdline.h" +#include "osregistry.h" +#include "palman.h" +#include "mainhallmenu.h" +#include "multi.h" +#include "popup.h" +#include "mouse.h" +#include "alphacolors.h" +#include "localize.h" + +// -------------------------------------------------------------------------------------------------------- +// Demo title screen +#ifdef FS2_DEMO +static int Demo_title_active = 0; +static int Demo_title_bitmap = -1; +static int Demo_title_expire_timestamp = 0; +static int Demo_title_need_fade_in = 1; +static char *Demo_title_bitmap_filename = NOX("DemoTitle1"); +#endif + +// -------------------------------------------------------------------------------------------------------- +// PLAYER SELECT defines +// + +//#define MAX_PLAYER_SELECT_LINES 8 // max # of pilots displayed at once +int Player_select_max_lines[GR_NUM_RESOLUTIONS] = { // max # of pilots displayed at once + 8, // GR_640 + 15 // GR_1024 +}; + +// button control defines +#define NUM_PLAYER_SELECT_BUTTONS 8 // button control defines + +#define CREATE_PILOT_BUTTON 0 // +#define CLONE_BUTTON 1 // +#define DELETE_BUTTON 2 // +#define SCROLL_LIST_UP_BUTTON 3 // +#define SCROLL_LIST_DOWN_BUTTON 4 // +#define ACCEPT_BUTTON 5 // +#define SINGLE_BUTTON 6 // +#define MULTI_BUTTON 7 // + +// list text display area +int Choose_list_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 114, 117, 400, 87 + }, + { // GR_1024 + 183, 186, 640, 139 + } +}; + +char *Player_select_background_bitmap_name[GR_NUM_RESOLUTIONS] = { + "ChoosePilot", + "2_ChoosePilot" +}; +char *Player_select_background_mask_bitmap[GR_NUM_RESOLUTIONS] = { + "ChoosePilot-m", + "2_ChoosePilot-m" +}; +// #define PLAYER_SELECT_PALETTE NOX("ChoosePilotPalette") // palette for the screen + +#define PLAYER_SELECT_MAIN_HALL_OVERLAY NOX("MainHall1") // main hall help overlay + +// convenient struct for handling all button controls +struct barracks_buttons { + char *filename; + int x, y, xt, yt; + int hotspot; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + barracks_buttons(char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {} +}; + +static barracks_buttons Player_select_buttons[GR_NUM_RESOLUTIONS][NUM_PLAYER_SELECT_BUTTONS] = { + { // GR_640 + // create, clone and delete (respectively) + barracks_buttons("CPB_00", 114, 205, 117, 240, 0), + barracks_buttons("CPB_01", 172, 205, 175, 240, 1), + barracks_buttons("CPB_02", 226, 205, 229, 240, 2), + + // scroll up, scroll down, and accept (respectively) + barracks_buttons("CPB_03", 429, 213, -1, -1, 3), + barracks_buttons("CPB_04", 456, 213, -1, -1, 4), + barracks_buttons("CPB_05", 481, 207, 484, 246, 5), + + // single player select and multiplayer select, respectively + barracks_buttons("CPB_06", 428, 82, 430, 108, 6), + barracks_buttons("CPB_07", 477, 82, 481, 108, 7) + }, + { // GR_1024 + // create, clone and delete (respectively) + barracks_buttons("2_CPB_00", 182, 328, 199, 384, 0), + barracks_buttons("2_CPB_01", 275, 328, 292, 384, 1), + barracks_buttons("2_CPB_02", 361, 328, 379, 384, 2), + + // scroll up, scroll down, and accept (respectively) + barracks_buttons("2_CPB_03", 686, 341, -1, -1, 3), + barracks_buttons("2_CPB_04", 729, 341, -1, -1, 4), + barracks_buttons("2_CPB_05", 770, 332, 787, 394, 5), + + // single player select and multiplayer select, respectively + barracks_buttons("2_CPB_06", 685, 132, 700, 173, 6), + barracks_buttons("2_CPB_07", 764, 132, 782, 173, 7) + } +}; + +// FIXME add to strings.tbl +#define PLAYER_SELECT_NUM_TEXT 1 +UI_XSTR Player_select_text[GR_NUM_RESOLUTIONS][PLAYER_SELECT_NUM_TEXT] = { + { // GR_640 + { "Choose Pilot", 1436, 122, 90, UI_XSTR_COLOR_GREEN, -1, NULL } + }, + { // GR_1024 + { "Choose Pilot", 1436, 195, 143, UI_XSTR_COLOR_GREEN, -1, NULL } + } +}; + +UI_WINDOW Player_select_window; // ui window for this screen +UI_BUTTON Player_select_list_region; // button for detecting mouse clicks on this screen +UI_INPUTBOX Player_select_input_box; // input box for adding new pilot names + +// #define PLAYER_SELECT_PALETTE_FNAME NOX("InterfacePalette") +int Player_select_background_bitmap; // bitmap for this screen +// int Player_select_palette; // palette bitmap for this screen +int Player_select_autoaccept = 0; +// int Player_select_palette_set = 0; + +// flag indicating if this is the absolute first pilot created and selected. Used to determine +// if the main hall should display the help overlay screen +int Player_select_very_first_pilot = 0; +int Player_select_initial_count = 0; +char Player_select_very_first_pilot_callsign[CALLSIGN_LEN + 2]; + +extern int Main_hall_bitmap; // bitmap handle to the main hall bitmap + +int Player_select_mode; // single or multiplayer - never set directly. use player_select_init_player_stuff() +int Player_select_num_pilots; // # of pilots on the list +int Player_select_list_start; // index of first list item to start displaying in the box +int Player_select_pilot; // index into the Pilot array of which is selected as the active pilot +int Player_select_input_mode; // 0 if the player _isn't_ typing a callsign, 1 if he is +char Pilots_arr[MAX_PILOTS][MAX_FILENAME_LEN]; +char *Pilots[MAX_PILOTS]; +int Player_select_clone_flag; // clone the currently selected pilot +char Player_select_last_pilot[CALLSIGN_LEN + 10]; // callsign of the last used pilot, or none if there wasn't one +int Player_select_last_is_multi; + +int Player_select_force_bastion = 0; + +// notification text areas + +static int Player_select_bottom_text_y[GR_NUM_RESOLUTIONS] = { + 314, // GR_640 + 502 // GR_1024 +}; + +static int Player_select_middle_text_y[GR_NUM_RESOLUTIONS] = { + 253, // GR_640 + 404 // GR_1024 +}; + +char Player_select_bottom_text[150] = ""; +char Player_select_middle_text[150] = ""; +void player_select_set_bottom_text(char *txt); +void player_select_set_middle_text(char *txt); + + +// FORWARD DECLARATIONS +void player_select_init_player_stuff(int mode); // switch between single and multiplayer modes +void player_select_set_input_mode(int n); +void player_select_button_pressed(int n); +void player_select_scroll_list_up(); +void player_select_scroll_list_down(); +int player_select_create_new_pilot(); +void player_select_delete_pilot(); +void player_select_display_all_text(); +void player_select_display_copyright(); +void player_select_set_bottom_text(char *txt); +void player_select_set_controls(int gray); +void player_select_draw_list(); +void player_select_process_noninput(int k); +void player_select_process_input(int k); +int player_select_pilot_file_filter(char *filename); +int player_select_get_last_pilot_info(); +void player_select_eval_very_first_pilot(); +void player_select_commit(); +void player_select_cancel_create(); + + +// basically, gray out all controls (gray == 1), or ungray the controls (gray == 0) +void player_select_set_controls(int gray) +{ + int idx; + + for(idx=0;idx= 0 ) { +#ifndef HARDWARE_ONLY + palette_use_bm_palette(Demo_title_bitmap); +#endif + Demo_title_active = 1; + Demo_title_expire_timestamp = timestamp(5000); + } else { + Demo_title_active = 0; + } + */ + Demo_title_active = 0; +#endif + + // create the UI window + Player_select_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Player_select_window.set_mask_bmap(Player_select_background_mask_bitmap[gr_screen.res]); + + // initialize the control buttons + for (i=0; ibutton.create(&Player_select_window, NULL, b->x, b->y, 60, 30, 1, 1); + else + b->button.create(&Player_select_window, NULL, b->x, b->y, 60, 30, 1, 1); + + // set its highlight action + b->button.set_highlight_action(common_play_highlight_sound); + + // set its animation bitmaps + b->button.set_bmaps(b->filename); + + // link the mask hotspot + b->button.link_hotspot(b->hotspot); + } + + // add some text + w = &Player_select_window; + w->add_XSTR("Create", 1034, Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].xt, Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].yt, &Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Clone", 1040, Player_select_buttons[gr_screen.res][CLONE_BUTTON].xt, Player_select_buttons[gr_screen.res][CLONE_BUTTON].yt, &Player_select_buttons[gr_screen.res][CLONE_BUTTON].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Remove", 1038, Player_select_buttons[gr_screen.res][DELETE_BUTTON].xt, Player_select_buttons[gr_screen.res][DELETE_BUTTON].yt, &Player_select_buttons[gr_screen.res][DELETE_BUTTON].button, UI_XSTR_COLOR_GREEN); + + w->add_XSTR("Select", 1039, Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].xt, Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].yt, &Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button, UI_XSTR_COLOR_PINK); + w->add_XSTR("Single", 1041, Player_select_buttons[gr_screen.res][SINGLE_BUTTON].xt, Player_select_buttons[gr_screen.res][SINGLE_BUTTON].yt, &Player_select_buttons[gr_screen.res][SINGLE_BUTTON].button, UI_XSTR_COLOR_GREEN); + w->add_XSTR("Multi", 1042, Player_select_buttons[gr_screen.res][MULTI_BUTTON].xt, Player_select_buttons[gr_screen.res][MULTI_BUTTON].yt, &Player_select_buttons[gr_screen.res][MULTI_BUTTON].button, UI_XSTR_COLOR_GREEN); + for(i=0; iadd_XSTR(&Player_select_text[gr_screen.res][i]); + } + + + // create the list button text select region + Player_select_list_region.create(&Player_select_window, "", Choose_list_coords[gr_screen.res][0], Choose_list_coords[gr_screen.res][1], Choose_list_coords[gr_screen.res][2], Choose_list_coords[gr_screen.res][3], 0, 1); + Player_select_list_region.hide(); + + // create the pilot callsign input box + Player_select_input_box.create(&Player_select_window, Choose_list_coords[gr_screen.res][0], Choose_list_coords[gr_screen.res][1], Choose_list_coords[gr_screen.res][2] , CALLSIGN_LEN - 1, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_LETTER_FIRST); + Player_select_input_box.set_valid_chars(VALID_PILOT_CHARS); + Player_select_input_box.hide(); + Player_select_input_box.disable(); + + // not currently entering any text + Player_select_input_mode = 0; + + // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed + Player_select_buttons[gr_screen.res][SCROLL_LIST_UP_BUTTON].button.set_hotkey(KEY_UP); + Player_select_buttons[gr_screen.res][SCROLL_LIST_DOWN_BUTTON].button.set_hotkey(KEY_DOWN); + Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button.set_hotkey(KEY_ENTER); + Player_select_buttons[gr_screen.res][CREATE_PILOT_BUTTON].button.set_hotkey(KEY_C); + + // disable the single player button in the multiplayer beta +#ifdef MULTIPLAYER_BETA_BUILD + Player_select_buttons[gr_screen.res][SINGLE_BUTTON].button.hide(); + Player_select_buttons[gr_screen.res][SINGLE_BUTTON].button.disable(); +#elif defined(E3_BUILD) || defined(PRESS_TOUR_BUILD) + Player_select_buttons[gr_screen.res][MULTI_BUTTON].button.hide(); + Player_select_buttons[gr_screen.res][MULTI_BUTTON].button.disable(); +#endif + + + // attempt to load in the background bitmap + Player_select_background_bitmap = bm_load(Player_select_background_bitmap_name[gr_screen.res]); + Assert(Player_select_background_bitmap >= 0); + + // load in the palette for the screen + // Player_select_palette = bm_load(PLAYER_SELECT_PALETTE); + // Player_select_palette_set = 0; + + // unset the very first pilot data + Player_select_very_first_pilot = 0; + Player_select_initial_count = -1; + memset(Player_select_very_first_pilot_callsign, 0, CALLSIGN_LEN + 2); + +// if(Player_select_num_pilots == 0){ +// Player_select_autoaccept = 1; +// } + + // if we found a pilot +#if defined(DEMO) || defined(OEM_BUILD) || defined(E3_BUILD) || defined(PRESS_TOUR_BUILD) // not for FS2_DEMO + player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE); +#elif defined(MULTIPLAYER_BETA_BUILD) + player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI); +#else + if (player_select_get_last_pilot_info()) { + if (Player_select_last_is_multi) { + player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI); + } else { + player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE); + } + } + // otherwise go to the single player mode by default + else { + player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE); + } +#endif + + if((Player_select_num_pilots == 1) && Player_select_input_mode){ + Player_select_autoaccept = 1; + } +} + +#ifdef FS2_DEMO +// Display the demo title screen +void demo_title_blit() +{ + int k; + + Mouse_hidden = 1; + + if ( timestamp_elapsed(Demo_title_expire_timestamp) ) { + Demo_title_active = 0; + } + + k = game_poll(); + if ( k > 0 ) { + Demo_title_active = 0; + } + + if ( Demo_title_need_fade_in ) { + gr_fade_out(0); + } + + gr_set_bitmap(Demo_title_bitmap); + gr_bitmap(0,0); + + gr_flip(); + + if ( Demo_title_need_fade_in ) { + gr_fade_in(0); + Demo_title_need_fade_in = 0; + } + + if ( !Demo_title_active ) { + gr_fade_out(0); + Mouse_hidden = 0; + } +} + +#endif + +void player_select_do() +{ + int k; + +#ifdef FS2_DEMO + if ( Demo_title_active ) { + // demo_title_blit(); + return; + } +#endif + + //if ( !Player_select_palette_set ) { + // Assert(Player_select_palette >= 0); +//#ifndef HARDWARE_ONLY +// palette_use_bm_palette(Player_select_palette); +//#endif +// Player_select_palette_set = 1; +// } + + // set the input box at the "virtual" line 0 to be active so the player can enter a callsign + if (Player_select_input_mode){ + Player_select_input_box.set_focus(); + } + + // process any ui window stuff + k = Player_select_window.process(); + if(k){ + extern void game_process_cheats(int k); + game_process_cheats(k); + } + switch(k){ + // switch between single and multiplayer modes + case KEY_TAB : +#if defined(DEMO) || defined(OEM_BUILD) // not for FS2_DEMO + break; +#else + + if(Player_select_input_mode){ + gamesnd_play_iface(SND_GENERAL_FAIL); + break; + } + // play a little sound + gamesnd_play_iface(SND_USER_SELECT); + if(Player_select_mode == PLAYER_SELECT_MODE_MULTI){ + player_select_set_bottom_text(XSTR( "Single-Player Mode", 376)); + + // reinitialize as single player mode + player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE); + } else if(Player_select_mode == PLAYER_SELECT_MODE_SINGLE){ + player_select_set_bottom_text(XSTR( "Multiplayer Mode", 377)); + + // reinitialize as multiplayer mode + player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI); + } + break; +#endif + } + + // draw the player select pseudo-dialog over it + gr_set_bitmap(Player_select_background_bitmap); + gr_bitmap(0,0); + + // press the accept button + if (Player_select_autoaccept) { + Player_select_buttons[gr_screen.res][ACCEPT_BUTTON].button.press_button(); + } + + // draw any ui window stuf + Player_select_window.draw(); + + // light up the correct mode button (single or multi) + if (Player_select_mode == PLAYER_SELECT_MODE_SINGLE){ + Player_select_buttons[gr_screen.res][SINGLE_BUTTON].button.draw_forced(2); + } else { + Player_select_buttons[gr_screen.res][MULTI_BUTTON].button.draw_forced(2); + } + + // draw the pilot list text + player_select_draw_list(); + + // draw copyright message on the bottom on the screen + player_select_display_copyright(); + + if (!Player_select_input_mode) { + player_select_process_noninput(k); + } else { + player_select_process_input(k); + } + + // draw any pending messages on the bottom or middle of the screen + player_select_display_all_text(); + +#ifndef RELEASE_REAL + // gr_set_color_fast(&Color_bright_green); + // gr_string(0x8000, 10, "Development version - DO NOT RELEASE"); +#endif + + /* + gr_set_color(255, 0, 0); + vector whee[5]; + vector *arr[5] = {&whee[0], &whee[1], &whee[2], &whee[3], &whee[4]}; + whee[0].x = 10; whee[0].y = 10; whee[0].z = 0.0f; + whee[1].x = 50; whee[1].y = 50; whee[1].z = 0.0f; + whee[2].x = 50; whee[2].y = 90; whee[2].z = 0.0f; + whee[3].x = 90; whee[3].y = 130; whee[3].z = 0.0f; + whee[4].x = 180; whee[4].y = 130; whee[4].z = 0.0f; + gr_pline_special(arr, 5, 2); + */ + + + gr_flip(); +} + +void player_select_close() +{ + // destroy the player select window + Player_select_window.destroy(); + + // if we're in input mode - we should undo the pilot create reqeust + if(Player_select_input_mode){ + player_select_cancel_create(); + } + + // actually set up the Player struct here + if((Player_select_pilot == -1) || (Player_select_num_pilots == 0)){ + nprintf(("General","WARNING! No pilot selected! We should be exiting the game now!\n")); + return; + } + + // unload all bitmaps + if(Player_select_background_bitmap >= 0){ + bm_release(Player_select_background_bitmap); + Player_select_background_bitmap = -1; + } + // if(Player_select_palette >= 0){ + // bm_release(Player_select_palette); + //Player_select_palette = -1; + // } + + // setup the player struct + Player_num = 0; + Player = &Players[0]; + Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE; + + // now read in a the pilot data + if (read_pilot_file(Pilots[Player_select_pilot], !Player_select_mode, Player) != 0) { + Error(LOCATION,"Couldn't load pilot file, bailing"); + Player = NULL; + } + + if (Player_select_force_bastion) { + Player->on_bastion = 1; + } +} + +void player_select_set_input_mode(int n) +{ + int i; + + // set the input mode + Player_select_input_mode = n; + + // enable all the player select buttons + for (i=0; i= MAX_PILOTS) { + player_select_set_bottom_text(XSTR( "You already have the maximum # of pilots!", 379)); + + gamesnd_play_iface(SND_GENERAL_FAIL); + break; + } + + if (Player_select_pilot >= 0) { + // first we have to make sure this guy is actually loaded for when we create the clone + if (Player == NULL) { + Player = &Players[0]; + Player->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE; + } + + // attempt to read in the pilot file of the guy to be cloned + if (read_pilot_file(Pilots[Player_select_pilot], !Player_select_mode, Player) != 0) { + Error(LOCATION,"Couldn't load pilot file, bailing"); + Player = NULL; + Int3(); + } + + // set the clone flag + Player_select_clone_flag = 1; + + // create the new pilot (will be cloned with Player_select_clone_flag_set) + if (!player_select_create_new_pilot()) { + player_select_set_bottom_text(XSTR( "Error creating new pilot file!", 380)); + Player_select_clone_flag = 0; + memset(Player,0,sizeof(player)); + Player = NULL; + break; + } + + // clear the player out + // JH: What the hell? How do you clone a pilot if you clear out the source you are copying + // from? These next 2 lines are pure stupidity, so I commented them out! +// memset(Player,0,sizeof(player)); +// Player = NULL; + + // display some text on the bottom of the dialog + player_select_set_bottom_text(XSTR( "Type Callsign and Press Enter", 381)); + + // gray out all controls in the dialog + player_select_set_controls(1); + } + break; + + case CREATE_PILOT_BUTTON: + // if we're at max-pilots, don't allow another to be added + if(Player_select_num_pilots >= MAX_PILOTS){ + player_select_set_bottom_text(XSTR( "You already have the maximum # of pilots!", 379)); + + gamesnd_play_iface(SND_GENERAL_FAIL); + break; + } + + // create a new pilot + if (!player_select_create_new_pilot()) { + player_select_set_bottom_text(XSTR( "Type Callsign and Press Enter", 381)); + } + + // don't clone anyone + Player_select_clone_flag = 0; + + // display some text on the bottom of the dialog + player_select_set_bottom_text(XSTR( "Type Callsign and Press Enter", 381)); + + // gray out all controls + player_select_set_controls(1); + break; + + case DELETE_BUTTON: + player_select_set_bottom_text(""); + + if (Player_select_pilot >= 0) { + // display a popup requesting confirmation + ret = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, XSTR( "Warning!\n\nAre you sure you wish to delete this pilot?", 382)); + + // delete the pilot + if(ret == 1){ + player_select_delete_pilot(); + } + } + break; + + case SINGLE_BUTTON: + player_select_set_bottom_text(""); + + Player_select_autoaccept = 0; + // switch to single player mode + if (Player_select_mode != PLAYER_SELECT_MODE_SINGLE) { + // play a little sound + gamesnd_play_iface(SND_USER_SELECT); + + player_select_set_bottom_text(XSTR( "Single Player Mode", 376)); + + // reinitialize as single player mode + player_select_init_player_stuff(PLAYER_SELECT_MODE_SINGLE); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + + case MULTI_BUTTON: + player_select_set_bottom_text(""); + + Player_select_autoaccept = 0; +#if defined(DEMO) || defined(OEM_BUILD) // not for FS2_DEMO + game_feature_not_in_demo_popup(); +#else + // switch to multiplayer mode + if (Player_select_mode != PLAYER_SELECT_MODE_MULTI) { + // play a little sound + gamesnd_play_iface(SND_USER_SELECT); + + player_select_set_bottom_text(XSTR( "Multiplayer Mode", 377)); + + // reinitialize as multiplayer mode + player_select_init_player_stuff(PLAYER_SELECT_MODE_MULTI); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +#endif + break; + } +} + +int player_select_create_new_pilot() +{ + int idx; + + // make sure we haven't reached the max + if (Player_select_num_pilots >= MAX_PILOTS) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return 0; + } + + int play_scroll_sound = 1; + +#ifdef FS2_DEMO + if ( Demo_title_active ) { + play_scroll_sound = 0; + } +#endif + + if ( play_scroll_sound ) { + gamesnd_play_iface(SND_SCROLL); + } + + idx = Player_select_num_pilots; + + // move all the pilots in the list up + while (idx--) { + strcpy(Pilots[idx + 1], Pilots[idx]); + } + + // by default, set the default netgame protocol to be VMT + Multi_options_g.protocol = NET_TCP; + + // select the beginning of the list + Player_select_pilot = 0; + Player_select_num_pilots++; + Pilots[Player_select_pilot][0] = 0; + Player_select_list_start= 0; + + // set us to be in input mode + player_select_set_input_mode(1); + + // set the input box to have focus + Player_select_input_box.set_focus(); + Player_select_input_box.set_text(""); + Player_select_input_box.update_dimensions(Choose_list_coords[gr_screen.res][0], Choose_list_coords[gr_screen.res][1], Choose_list_coords[gr_screen.res][2], gr_get_font_height()); + + return 1; +} + +void player_select_delete_pilot() +{ + char filename[MAX_PATH_LEN + 1]; + int i, deleted_cur_pilot; + + deleted_cur_pilot = 0; + + // tack on the full path and the pilot file extension + // build up the path name length + // make sure we do this based upon whether we're in single or multiplayer mode + strcpy( filename, Pilots[Player_select_pilot] ); + strcat( filename, NOX(".plr") ); + + // attempt to delete the pilot + if (Player_select_mode == PLAYER_SELECT_MODE_SINGLE) { + cf_delete( filename, CF_TYPE_SINGLE_PLAYERS ); + } else { + cf_delete( filename, CF_TYPE_MULTI_PLAYERS ); + } + + // delete all the campaign save files for this pilot. + mission_campaign_delete_all_savefiles( Pilots[Player_select_pilot], (Player_select_mode != PLAYER_SELECT_MODE_SINGLE) ); + + // move all the players down + for (i=Player_select_pilot; i= Player_select_num_pilots) { + Player_select_pilot = Player_select_num_pilots - 1; + } + +} + +// scroll the list of players up +void player_select_scroll_list_up() +{ + if (Player_select_pilot == -1) + return; + + // change the pilot selected index and play the appropriate sound + if (Player_select_pilot) { + Player_select_pilot--; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + + if (Player_select_pilot < Player_select_list_start){ + Player_select_list_start = Player_select_pilot; + } +} + +// scroll the list of players down +void player_select_scroll_list_down() +{ + // change the pilot selected index and play the appropriate sound + if (Player_select_pilot < Player_select_num_pilots - 1) { + Player_select_pilot++; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + + if (Player_select_pilot >= (Player_select_list_start + Player_select_max_lines[gr_screen.res])){ + Player_select_list_start++; + } +} + +// fill in the data on the last played pilot (callsign and is_multi or not) +int player_select_get_last_pilot_info() +{ + char *last_player; + + last_player = os_config_read_string( NULL, "LastPlayer", NULL); + + if(last_player == NULL){ + return 0; + } else { + strcpy(Player_select_last_pilot,last_player); + } + + // determine if he was a single or multi-player based upon the last character in his callsign + Player_select_last_is_multi = Player_select_last_pilot[strlen(Player_select_last_pilot)-1] == 'M' ? 1 : 0; + Player_select_last_pilot[strlen(Player_select_last_pilot)-1]='\0'; + + return 1; +} + +int player_select_get_last_pilot() +{ + // if the player has the Cmdline_use_last_pilot command line option set, try and drop out quickly + if(Cmdline_use_last_pilot){ + int idx; + + if(!player_select_get_last_pilot_info()){ + return 0; + } + + if(Player_select_last_is_multi){ + Player_select_num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_MULTI_PLAYERS, NOX("*.plr"), CF_SORT_TIME); + } else { + Player_select_num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_SINGLE_PLAYERS, NOX("*.plr"), CF_SORT_TIME); + } + + Player_select_pilot = -1; + idx = 0; + // pick the last player + for(idx=0;idxflags |= PLAYER_FLAGS_STRUCTURE_IN_USE; + return 1; + } + } + + return 0; +} + +void player_select_init_player_stuff(int mode) +{ + Player_select_list_start = 0; + + // set the select mode to single player for default + Player_select_mode = mode; + + // load up the list of players based upon the Player_select_mode (single or multiplayer) + Get_file_list_filter = player_select_pilot_file_filter; + if (mode == PLAYER_SELECT_MODE_SINGLE){ + Player_select_num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_SINGLE_PLAYERS, NOX("*.plr"), CF_SORT_TIME); + } else { + Player_select_num_pilots = cf_get_file_list_preallocated(MAX_PILOTS, Pilots_arr, Pilots, CF_TYPE_MULTI_PLAYERS, NOX("*.plr"), CF_SORT_TIME); + } + + Player = NULL; + + // if this value is -1, it means we should set it to the num pilots count + if(Player_select_initial_count == -1){ + Player_select_initial_count = Player_select_num_pilots; + } + + // select the first pilot if any exist, otherwise set to -1 + if (Player_select_num_pilots == 0) { + Player_select_pilot = -1; + player_select_set_middle_text(XSTR( "Type Callsign and Press Enter", 381)); + player_select_set_controls(1); // gray out the controls + player_select_create_new_pilot(); + } else { + Player_select_pilot = 0; + } +} + +void player_select_draw_list() +{ + int idx; + + for (idx=0; idx= 0) { + int ret; + + // display a popup requesting confirmation + ret = popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON,2,POPUP_NO,POPUP_YES,XSTR( "Are you sure you want to delete this pilot?", 383)); + + // delete the pilot + if(ret == 1){ + player_select_delete_pilot(); + } + } + break; + } + + // check to see if the user has clicked on the "list region" button + // and change the selected pilot appropriately + if (Player_select_list_region.pressed()) { + int click_y; + // get the mouse position + Player_select_list_region.get_mouse_pos(NULL, &click_y); + + // determine what index to select + //idx = (click_y+5) / 10; + idx = click_y / gr_get_font_height(); + + + // if he selected a valid item + if(((idx + Player_select_list_start) < Player_select_num_pilots) && (idx >= 0)){ + Player_select_pilot = idx + Player_select_list_start; + } + } + + // if the player has double clicked on a valid pilot, choose it and hit the accept button + if (Player_select_list_region.double_clicked()) { + if ((Player_select_pilot >= 0) && (Player_select_pilot < Player_select_num_pilots)) { + player_select_button_pressed(ACCEPT_BUTTON); + } + } +} + +void player_select_process_input(int k) +{ + char buf[CALLSIGN_LEN + 1]; + int idx,z; + + // if the player is in the process of typing in a new pilot name... + switch (k) { + // cancel create pilot + case KEY_ESC: + player_select_cancel_create(); + break; + + // accept a new pilot name + case KEY_ENTER: + Player_select_input_box.get_text(buf); + drop_white_space(buf); + z = 0; + if (!isalpha(*buf)) { + z = 1; + } else { + for (idx=1; buf[idx]; idx++) { + if (!isalpha(buf[idx]) && !isdigit(buf[idx]) && !strchr(VALID_PILOT_CHARS, buf[idx])) { + z = 1; + break; + } + } + } + + for (idx=1; idxflags |= PLAYER_FLAGS_STRUCTURE_IN_USE; + } + + strcpy(Player->callsign, buf); + init_new_pilot(Player, !Player_select_clone_flag); + + // set him as being a multiplayer pilot if we're in the correct mode + if (Player_select_mode == PLAYER_SELECT_MODE_MULTI) { + Player->flags |= PLAYER_FLAGS_IS_MULTI; + Player->stats.flags |= STATS_FLAG_MULTIPLAYER; + } + + // create his pilot file + write_pilot_file(Player); + + // unset the player + memset(Player, 0, sizeof(player)); + Player = NULL; + + // make this guy the selected pilot and put him first on the list + Player_select_pilot = 0; + + // unset the input mode + player_select_set_input_mode(0); + + // clear any pending bottom text + player_select_set_bottom_text(""); + + // clear any pending middle text + player_select_set_middle_text(""); + + // ungray all the controls + player_select_set_controls(0); + + // evaluate whether or not this is the very first pilot + player_select_eval_very_first_pilot(); + break; + + case 0: + break; + + // always kill middle text when a char is pressed in input mode + default: + player_select_set_middle_text(""); + break; + } +} + +// draw copyright message on the bottom on the screen +void player_select_display_copyright() +{ + int sx, sy, w; + char Copyright_msg1[256], Copyright_msg2[256]; + +// strcpy(Copyright_msg1, XSTR("Descent: FreeSpace - The Great War, Copyright c 1998, Volition, Inc.", -1)); + gr_set_color_fast(&Color_white); + + sprintf(Copyright_msg1, NOX("FreeSpace 2")); +#if defined(GERMAN_BUILD) + sprintf(Copyright_msg2, XSTR("Copyright %c 1999, Volition, Inc. All rights reserved.", 385), '\xA8'); +#else + sprintf(Copyright_msg2, XSTR("Copyright %c 1999, Volition, Inc. All rights reserved.", 385), '\x83'); +#endif + + gr_get_string_size(&w, NULL, Copyright_msg1); + sx = fl2i((gr_screen.max_w / 2) - w/2.0f + 0.5f); + sy = (gr_screen.max_h - 2) - 2*gr_get_font_height(); + gr_string(sx, sy, Copyright_msg1); + + gr_get_string_size(&w, NULL, Copyright_msg2); + sx = fl2i((gr_screen.max_w / 2) - w/2.0f + 0.5f); + sy = (gr_screen.max_h - 2) - gr_get_font_height(); + gr_string(sx, sy, Copyright_msg2); +} + +void player_select_display_all_text() +{ + int w, h; + + // only draw if we actually have a valid string + if (strlen(Player_select_bottom_text)) { + gr_get_string_size(&w, &h, Player_select_bottom_text); + + w = (gr_screen.max_w - w) / 2; + gr_set_color_fast(&Color_bright_white); + gr_printf(w, Player_select_bottom_text_y[gr_screen.res], Player_select_bottom_text); + } + + // only draw if we actually have a valid string + if (strlen(Player_select_middle_text)) { + gr_get_string_size(&w, &h, Player_select_middle_text); + + w = (gr_screen.max_w - w) / 2; + gr_set_color_fast(&Color_bright_white); + gr_printf(w, Player_select_middle_text_y[gr_screen.res], Player_select_middle_text); + } +} + +int player_select_pilot_file_filter(char *filename) +{ + return !verify_pilot_file(filename, Player_select_mode == PLAYER_SELECT_MODE_SINGLE); +} + +void player_select_set_bottom_text(char *txt) +{ + if (txt) { + strncpy(Player_select_bottom_text, txt, 149); + } +} + +void player_select_set_middle_text(char *txt) +{ + if (txt) { + strncpy(Player_select_middle_text, txt, 149); + } +} + +void player_select_eval_very_first_pilot() +{ + // never bring up the initial main hall help overlay + // Player_select_very_first_pilot = 0; + + // if we already have this flag set, check to see if our callsigns match + if(Player_select_very_first_pilot){ + // if the callsign has changed, unset the flag + if(strcmp(Player_select_very_first_pilot_callsign,Pilots[Player_select_pilot])){ + Player_select_very_first_pilot = 0; + } + } + // otherwise check to see if there is only 1 pilot + else { + if((Player_select_num_pilots == 1) && (Player_select_initial_count == 0)){ + // set up the data + Player_select_very_first_pilot = 1; + strcpy(Player_select_very_first_pilot_callsign,Pilots[Player_select_pilot]); + } + } +} + +void player_select_commit() +{ + // if we've gotten to this point, we should have ensured this was the case + Assert(Player_select_num_pilots > 0); + + gameseq_post_event(GS_EVENT_MAIN_MENU); + gamesnd_play_iface(SND_COMMIT_PRESSED); + + // evaluate if this is the _very_ first pilot + player_select_eval_very_first_pilot(); +} + +void player_select_cancel_create() +{ + int idx; + + Player_select_num_pilots--; + + // make sure we correct the Selected_pilot index to account for the cancelled action + if (Player_select_num_pilots == 0) { + Player_select_pilot = -1; + } + + // move all pilots down + for (idx=0; idx= MAX_PLAYER_TIPS){ + break; + } + Player_tips[Num_player_tips++] = stuff_and_malloc_string(F_NAME, NULL, 1024); + } + + // stop externalizing, homey + lcl_ext_close(); +} +void player_tips_popup() +{ + int tip, ret; + + // player has disabled tips + if((Player != NULL) && !Player->tips){ + return; + } + // only show tips once per instance of Freespace + if(Player_tips_shown == 1){ + return; + } + Player_tips_shown = 1; + + // randomly pick one + tip = (int)frand_range(0.0f, (float)Num_player_tips - 1.0f); + + char all_txt[2048]; + + do { + sprintf(all_txt, XSTR("NEW USER TIP\n\n%s", 1565), Player_tips[tip]); + ret = popup(PF_NO_SPECIAL_BUTTONS | PF_TITLE | PF_TITLE_WHITE, 3, XSTR("&Ok", 669), XSTR("&Next", 1444), XSTR("Don't show me this again", 1443), all_txt); + + // now what? + switch(ret){ + // next + case 1: + if(tip >= Num_player_tips - 1){ + tip = 0; + } else { + tip++; + } + break; + + // don't show me this again + case 2: + ret = 0; + Player->tips = 0; + write_pilot_file(Player); + break; + } + } while(ret > 0); +} \ No newline at end of file diff --git a/src/menuui/readyroom.cpp b/src/menuui/readyroom.cpp new file mode 100644 index 0000000..a2f6f48 --- /dev/null +++ b/src/menuui/readyroom.cpp @@ -0,0 +1,1900 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/ReadyRoom.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Ready Room code, which is the UI screen for selecting Campaign/mission to play next mainly. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 20 9/30/99 5:59p Jefff + * fixed compile error in OEM ver + * + * 19 9/08/99 12:28p Jefff + * + * 18 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 17 9/02/99 2:33p Jefff + * column heading coordinate changes + * + * 16 8/22/99 4:16p Jefff + * scroll button coord fixes + * + * 15 7/20/99 1:49p Dave + * Peter Drake build. Fixed some release build warnings. + * + * 14 7/19/99 2:13p Dave + * Added some new strings for Heiko. + * + * 13 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 12 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 11 2/01/99 5:55p Dave + * Removed the idea of explicit bitmaps for buttons. Fixed text + * highlighting for disabled gadgets. + * + * 10 1/30/99 9:01p Dave + * Coord fixes. + * + * 9 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 8 1/29/99 12:47a Dave + * Put in sounds for beam weapon. A bunch of interface screens (tech + * database stuff). + * + * 7 12/11/98 4:35p Andsager + * Fix sim room bug when no standalone missions + * + * 6 12/07/98 5:02p Dan + * Removed improper use of bitmap filename with extension, + * + * 5 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * + * $NoKeywords: $ + */ + +#include + +#include "2d.h" +#include "font.h" +#include "ui.h" +#include "uidefs.h" +#include "key.h" +#include "bmpman.h" +#include "gamesequence.h" +#include "missioncampaign.h" +#include "sound.h" +#include "gamesnd.h" +#include "missionscreencommon.h" +#include "freespace.h" +#include "missionparse.h" +#include "player.h" +#include "managepilot.h" +#include "popup.h" +#include "contexthelp.h" +#include "cfilesystem.h" +#include "freespace.h" +#include "alphacolors.h" + +#define MAX_MISSIONS 1024 + +int Mission_list_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 33, 108, 402, 279 + }, + { // GR_1024 + 43, 175, 402, 279 + } +}; + +int Campaign_list_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 491, 108, 115, 279 + }, + { // GR_1024 + 491, 175, 115, 279 + } +}; + + +// x coordinate offsets for data when campaign tab active +#define C_TEXT_X 0 +#define C_SUBTEXT_X 19 + +// x coordinate offsets for data when mission tab active +#define M_TEXT_X 0 + +#define MODE_CAMPAIGNS 0 +#define MODE_MISSIONS 1 + +#define MAX_LINES 200 +#define MAX_DESC_LINES 200 +#define NUM_BUTTONS 11 +#define LIST_BUTTONS_MAX 40 + +#define SCROLL_UP_BUTTON 0 +#define SCROLL_DOWN_BUTTON 1 +#define MISSION_TAB 2 +#define CAMPAIGN_TAB 3 +#define HELP_BUTTON 4 +#define COMMIT_BUTTON 5 +#define OPTIONS_BUTTON 6 +#define TECH_DATABASE_BUTTON 7 +#define SIMULATOR_BUTTON 8 +#define CUTSCENES_BUTTON 9 +#define CREDITS_BUTTON 10 + +#define CAMPAIGN_MISSION_HASH_SIZE 307 + +struct sim_room_buttons { + char *filename; + int x, y, xt, yt; + int hotspot; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + sim_room_buttons(char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {} +}; + +static sim_room_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { +//XSTR:OFF + { // GR_640 + sim_room_buttons("LMB_04", 1, 99, -1, -1, 4), + sim_room_buttons("LMB_05", 1, 381, -1, -1, 5), + sim_room_buttons("LMB_06", 6, 438, 40, 445, 6), + sim_room_buttons("LMB_07", 6, 457, 40, 462, 7), + sim_room_buttons("LMB_08", 534, 426, 500, 440, 8), + sim_room_buttons("LMB_09", 571, 426, 572, 413, 9), + sim_room_buttons("LMB_10", 534, 455, 480, 462, 10), + + sim_room_buttons("TDB_00", 7, 3, 37, 7, 0), + sim_room_buttons("TDB_01", 7, 18, 37, 23, 1), + sim_room_buttons("TDB_02", 7, 34, 37, 38, 2), + sim_room_buttons("TDB_03", 7, 49, 37, 54, 3), + }, + { // GR_1024 + sim_room_buttons("2_LMB_04", 2, 159, -1, -1, 4), + sim_room_buttons("2_LMB_05", 2, 609, -1, -1, 5), + sim_room_buttons("2_LMB_06", 10, 701, 64, 712, 6), + sim_room_buttons("2_LMB_07", 10, 732, 64, 739, 7), + sim_room_buttons("2_LMB_08", 854, 681, 800, 704, 8), + sim_room_buttons("2_LMB_09", 914, 681, 915, 660, 9), + sim_room_buttons("2_LMB_10", 854, 728, 800, 728, 10), + + sim_room_buttons("2_TDB_00", 12, 5, 59, 12, 0), + sim_room_buttons("2_TDB_01", 12, 31, 59, 37, 1), + sim_room_buttons("2_TDB_02", 12, 56, 59, 62, 2), + sim_room_buttons("2_TDB_03", 12, 81, 59, 88, 3), + } +//XSTR:ON +}; + +char *Sim_filename[GR_NUM_RESOLUTIONS] = { + "LoadMission", + "2_LoadMission" +}; +char *Sim_mask_filename[GR_NUM_RESOLUTIONS] = { + "LoadMission-m", + "2_LoadMission-m" +}; + +char *Campaign_filename[GR_NUM_RESOLUTIONS] = { + "Campaign", + "2_Campaign" +}; +char *Campaign_mask_filename[GR_NUM_RESOLUTIONS] = { + "Campaign-m", + "2_Campaign-m" +}; + +// misc text. ("Mission" and "Filename" +#define NUM_SIM_MISC_TEXT 2 +#define SIM_MISC_TEXT_MISSION 0 +#define SIM_MISC_TEXT_FILENAME 1 +int Sim_misc_text_coords[GR_NUM_RESOLUTIONS][NUM_SIM_MISC_TEXT][2] = { + { // GR_640 + {33, 95}, + {491, 95} + }, + { // GR_1024 + {43, 155}, + {491, 155} + } +}; + +// readyroom text line stuff +#define READYROOM_LINE_CAMPAIGN 1 +#define READYROOM_LINE_CMISSION 2 +#define READYROOM_LINE_MISSION 3 + +#define READYROOM_FLAG_FROM_VOLITION (1<<0) // volition made +static struct { + int type; // see READYROOM_LINE_* defines above + char *name; + char *filename; + int x; // X coordinate of line + int y; // Y coordinate of line + int flags; // special flags, see READYROOM_FLAG_* defines above +} sim_room_lines[MAX_LINES]; + +static char Cur_campaign[MAX_FILENAME_LEN]; +static char *Mission_filenames[MAX_MISSIONS]; +static char *Standalone_mission_names[MAX_MISSIONS]; +static int Standalone_mission_flags[MAX_MISSIONS]; +static char *Campaign_missions[MAX_MISSIONS]; +static char *Campaign_mission_names[MAX_CAMPAIGN_MISSIONS]; +static int Campaign_mission_flags[MAX_MISSIONS]; +static char *Campaign_descs[MAX_CAMPAIGNS]; +static char *Campaign_descs_temp[MAX_CAMPAIGNS]; +static char *Campaign_file_names_temp[MAX_CAMPAIGNS]; +static int Standalone_mission_names_inited = 0; +static int Campaign_names_inited = 0; +static int Campaign_mission_names_inited = 0; +static int Num_standalone_missions; +static int Num_campaign_missions; +static int Num_player_missions; +static int Scroll_offset; +static int Selected_line; +static int Num_lines; +static int Num_campaign_missions_with_info = 0; +static int Num_standalone_missions_with_info = 0; +static int list_x1; +static int list_x2; +static int list_y; +static int list_w1; +static int list_w2; +static int list_h; +static int Background_bitmap; +static UI_WINDOW Ui_window; +static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list + +typedef struct hash_node { + hash_node *next; + char *filename; +} hash_node; + +static hash_node *Campaign_mission_hash_table[CAMPAIGN_MISSION_HASH_SIZE]; +static int Hash_table_inited; + +// special icons (1.04 + stuff) +#define NUM_MISSION_ICONS 1 +#define MISSION_ICON_VOLITION 0 // mini volition death's head :) + +// icon offsets (see LIST_ defines above +//#define MISSION_ICON_VOLITION_X (46) +#define MISSION_ICON_VOLITION_Y_OFFSET (-1) + +// icon offsets +static int Sim_volition_icon_x[GR_NUM_RESOLUTIONS] = { + 38, + 49 +}; + +// special icons themselves +int Mission_icon_bitmaps[NUM_MISSION_ICONS]; +//XSTR:OFF +char *Mission_icon_bitmap_filenames[NUM_MISSION_ICONS] = { + "icon-volition" +}; +//XSTR:ON +void sim_room_load_mission_icons(); +void sim_room_unload_mission_icons(); +void sim_room_blit_icons(int line_index, int y_start, fs_builtin_mission *fb = NULL, int is_md = 0); + +// Finds a hash value for mission filename +// +// returns hash value +int hash_filename(char *filename) { + unsigned __int64 hash_val = 0; + char *ptr = filename; + + // Dont hash .fsm extension, convert all to upper case + for (int i=0; i < ((signed int)(strlen(filename)) - 4); i++) { + hash_val = (hash_val << 4) + toupper(*ptr++); + } + + return int(hash_val % CAMPAIGN_MISSION_HASH_SIZE); +} + +// insert filename into Campaign_mission_hash_table +// +// returns 1 if successful, 0 if could not allocate memory +int hash_insert(char *filename) { + int hash_val = hash_filename(filename); + hash_node *cur_node; + + // Check if table empty + if (Campaign_mission_hash_table[hash_val] == NULL) { + Campaign_mission_hash_table[hash_val] = new hash_node; + + cur_node = Campaign_mission_hash_table[hash_val]; + + if (cur_node == NULL) { + // Unable to allocate memory + return 0; + } + } else { + // Walk down list to first empty node + cur_node = Campaign_mission_hash_table[hash_val]; + while (cur_node->next != NULL) { + cur_node = cur_node->next; + } + + // Create new node + cur_node->next = new hash_node; + + if (cur_node->next == NULL) { + // unable to allocate memory + return 0; + } else { + cur_node = cur_node->next; + } + } + + // Initialize data + cur_node->next = NULL; + cur_node->filename = filename; + + // Return successs + return 1; +} + +// Checks if a filename already exitst in the hash table +// +// returns 1 if found (collision), 0 if no collision +int campaign_mission_hash_collision(char *filename) +{ + int hash_val = hash_filename(filename); + hash_node *cur_node = Campaign_mission_hash_table[hash_val]; + + if (cur_node == NULL) { + return 0; + } + + do { + if (!stricmp(filename, cur_node->filename)) { + return 1; + } + + cur_node = cur_node->next; + } while (cur_node != NULL); + + // Ran out of stuff to check + return 0; +} + +// builds hash table of campaign mission filenames +// +// returns 1 if successful, 0 if not successful +int build_campaign_mission_filename_hash_table() +{ + int rval; + // Go through all campaign missions + for (int i=0; inext != NULL) { + hash_node *temp = cur_node->next; + delete cur_node; + cur_node = temp; + } + + // Delete last node + delete cur_node; + Campaign_mission_hash_table[i] = NULL; + } + } +} + + +// add a line of sim_room smuck to end of list +int sim_room_line_add(int type, char *name, char *filename, int x, int y, int flags) +{ + if (Num_lines >= MAX_LINES) + return 0; + + sim_room_lines[Num_lines].type = type; + sim_room_lines[Num_lines].name = name; + sim_room_lines[Num_lines].filename = filename; + sim_room_lines[Num_lines].x = x; + sim_room_lines[Num_lines].y = y; + sim_room_lines[Num_lines].flags = flags; + return Num_lines++; +} + +// filter out all multiplayer campaigns +int campaign_room_campaign_filter(char *filename) +{ + int type, max_players; + char name[NAME_LENGTH], *desc = NULL; + + #ifdef OEM_BUILD + // also need to check if this is the builtin campaign + if ( game_find_builtin_mission(filename) && mission_campaign_get_info(filename, name, &type, &max_players, &desc) ) { + #else + if ( mission_campaign_get_info(filename, name, &type, &max_players, &desc) ) { + #endif + if ( type == CAMPAIGN_TYPE_SINGLE ) { + Campaign_file_names_temp[Num_campaigns] = strdup(filename); + Campaign_descs_temp[Num_campaigns++] = desc; + return 1; + } + } + + if (desc){ + free(desc); + } + + return 0; +} + +// build up a list of all missions in all campaigns. +int sim_room_campaign_mission_filter(char *filename) +{ + int num; + + num = mission_campaign_get_mission_list(filename, &Campaign_missions[Num_campaign_missions], MAX_CAMPAIGN_MISSIONS - Num_campaign_missions); + if (num < 0) + return 0; + + Num_campaign_missions += num; + return 1; +} + +// filter out all missions already used in existing campaigns +int sim_room_standalone_mission_filter(char *filename) +{ + int type; + char mission_name[255]; + + // Check if a campaign mission (single and multi) + if (campaign_mission_hash_collision(filename)) { + return 0; + } + + // Check if a standalone multi mission OR Mdisk mission with data + type = mission_parse_is_multi(filename, mission_name); + if (type && !(type & MISSION_TYPE_SINGLE)) + return 0; + + return 1; +} + +// builds up list of standalone missions and adds them to missions simulator +// processes one mission per frame +// +// returns 1 if finished with all missions, 0 otherwise +// +int build_standalone_mission_list_do_frame() +{ + int font_height = gr_get_font_height(); + char filename[MAX_FILENAME_LEN]; + char str[256]; + + // When no standalone missions in data directory + if (Num_standalone_missions == 0) { + Standalone_mission_names_inited = 1; + return 1; + } + + // Set global variable so we we'll have list available next time + Standalone_mission_names[Num_standalone_missions_with_info] = NULL; + Standalone_mission_flags[Num_standalone_missions_with_info] = 0; + + if (Num_standalone_missions > 0) { // sanity check + if (strlen(Mission_filenames[Num_standalone_missions_with_info]) < MAX_FILENAME_LEN - 4) { // sanity check? + strcpy(filename, Mission_filenames[Num_standalone_missions_with_info]); + + // update popup + memset(str, 0, 256); + sprintf(str, XSTR("Single Mission\n\n%s",989), filename); + popup_change_text(str); + + // tack on an extension + strcat(filename, FS_MISSION_FILE_EXT); + if (!get_mission_info(filename)) { + Standalone_mission_names[Num_standalone_missions_with_info] = strdup(The_mission.name); + Standalone_mission_flags[Num_standalone_missions_with_info] = The_mission.game_type; + int y = Num_lines * (font_height + 2); + + // determine some extra information + int flags = 0; + fs_builtin_mission *fb = game_find_builtin_mission(filename); + if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){ + flags |= READYROOM_FLAG_FROM_VOLITION; + } + + // add the line + sim_room_line_add(READYROOM_LINE_MISSION, Standalone_mission_names[Num_standalone_missions_with_info], Mission_filenames[Num_standalone_missions_with_info], list_x1 + M_TEXT_X, y, flags); + } + } + + Num_standalone_missions_with_info++; + } + + if (Num_standalone_missions_with_info == Num_standalone_missions) { + Standalone_mission_names_inited = 1; + return 1; + } else { + return 0; + } +} + +// builds up list of already played missions in a campaign and adds them to missions simulator +// processes one mission per frame +// +// returns 1 if finished with all missions, 0 otherwise +// +int build_campaign_mission_list_do_frame() +{ + int font_height = gr_get_font_height(); + char str[256]; + + // When no campaign files in data directory + if (Campaign.num_missions == 0) { + Campaign_mission_names_inited = 1; + return 1; + } + + // change popup + memset(str, 0, 256); + sprintf(str, XSTR("Campaign Mission\n\n%s",990), Campaign.missions[Num_campaign_missions_with_info].name); + popup_change_text(str); + + // Set global variable so we we'll have list available next time + Campaign_mission_names[Num_campaign_missions_with_info] = NULL; + Campaign_mission_flags[Num_campaign_missions_with_info] = 0; + + // Only allow missions already completed + if (Campaign.missions[Num_campaign_missions_with_info].completed) { + if (!get_mission_info(Campaign.missions[Num_campaign_missions_with_info].name)) { + // add to list + Campaign_mission_names[Num_campaign_missions_with_info] = strdup(The_mission.name); + Campaign_mission_flags[Num_campaign_missions_with_info] = The_mission.game_type; + int y = Num_campaign_missions_with_info * (font_height + 2); + + // determine some extra information + int flags = 0; + fs_builtin_mission *fb = game_find_builtin_mission(Campaign.missions[Num_campaign_missions_with_info].name); + if((fb != NULL) && (fb->flags & FSB_FROM_VOLITION)){ + flags |= READYROOM_FLAG_FROM_VOLITION; + } + + sim_room_line_add(READYROOM_LINE_CMISSION, Campaign_mission_names[Num_campaign_missions_with_info], Campaign.missions[Num_campaign_missions_with_info].name, list_x1 + C_SUBTEXT_X, y, flags); + } + } + + Num_campaign_missions_with_info++; + + if (Num_campaign_missions_with_info == Campaign.num_missions) { + Campaign_mission_names_inited = 1; + return 1; + } else { + return 0; + } +} + +// Builds list of either (1) standalone missions or (2) already played missions in current campaign +// First time through, it builds list, next time it uses precompiled list +void sim_room_build_listing() +{ + int i, y; + int font_height = gr_get_font_height(); + char full_filename[256]; + + Num_lines = y = 0; + list_y = Mission_list_coords[gr_screen.res][1]; + list_h = Mission_list_coords[gr_screen.res][3]; + + // Stand alone single player missions. + if (Player->readyroom_listing_mode == MODE_MISSIONS) { + if (Hash_table_inited) { + if (!Standalone_mission_names_inited) { // Is this the first time through + // build_list_do_frame builds list and adds sim room line and sets Standalone_mission_names_inited + popup_till_condition(build_standalone_mission_list_do_frame, POPUP_CANCEL, XSTR("Loading missions", 991) ); + } else { + for (i=0; iflags & FSB_FROM_VOLITION)){ + flags |= READYROOM_FLAG_FROM_VOLITION; + } + + sim_room_line_add(READYROOM_LINE_MISSION, Standalone_mission_names[i], Mission_filenames[i], list_x1 + M_TEXT_X, y, flags); + y += font_height + 2; + } + } + } + } + } else { + // Campaign missions + list_y += font_height + 2; + list_h -= font_height - 2; + + if (!Campaign_mission_names_inited) { // Is this the first time through + popup_till_condition(build_campaign_mission_list_do_frame, POPUP_CANCEL, XSTR("Loading campaign missions",992)); + // builds list, adds sim room line and sets Campaign_mission_names_inited + } else { + for (i=0; iflags & FSB_FROM_VOLITION)){ + flags |= READYROOM_FLAG_FROM_VOLITION; + } + + sim_room_line_add(READYROOM_LINE_CMISSION, Campaign_mission_names[i], Campaign.missions[i].name, list_x1 + C_SUBTEXT_X, y, flags); + y += font_height; + } + } + } + } +} + +int sim_room_line_query_visible(int n) +{ + int y; + + if ((n < 0) || (n >= Num_lines)) + return 0; + + y = sim_room_lines[n].y - sim_room_lines[Scroll_offset].y; + if ((y < 0) || (y + gr_get_font_height() > list_h)) + return 0; + + return 1; +} + +void sim_room_scroll_screen_up() +{ + if (Player->readyroom_listing_mode != MODE_MISSIONS) { + if (Scroll_offset) { + Scroll_offset--; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); + + return; + } + + if (Scroll_offset) { + Scroll_offset--; + Assert(Selected_line > Scroll_offset); + while (!sim_room_line_query_visible(Selected_line)) + Selected_line--; + + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void sim_room_scroll_line_up() +{ + if (Selected_line) { + Selected_line--; + if (Selected_line < Scroll_offset) + Scroll_offset = Selected_line; + + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void sim_room_scroll_screen_down() +{ + if (Player->readyroom_listing_mode != MODE_MISSIONS) { + if (sim_room_lines[Num_lines - 1].y + gr_get_font_height() > sim_room_lines[Scroll_offset].y + list_h) { + Scroll_offset++; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); + + return; + } + + if (sim_room_lines[Num_lines - 1].y + gr_get_font_height() > sim_room_lines[Scroll_offset].y + list_h) { + Scroll_offset++; + while (!sim_room_line_query_visible(Selected_line)) { + Selected_line++; + Assert(Selected_line < Num_lines); + } + + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void sim_room_scroll_line_down() +{ + if (Selected_line < Num_lines - 1) { + Selected_line++; + + Assert(Selected_line > Scroll_offset); + while (!sim_room_line_query_visible(Selected_line)) + Scroll_offset++; + + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +// returns: 0 = success, !0 = aborted or failed +int ready_room_reset_campaign() +{ + int z, rval = 1; + + z = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_CANCEL, POPUP_OK, XSTR( "Warning\nThis will cause all progress in your\ncurrent campaign to be lost", 110) ); + + if (z) { + mission_campaign_savefile_delete(Campaign.filename); + mission_campaign_load(Campaign.filename); + rval = 0; + } + + return rval; +} + +// Decide if we should offer choice to resume this savegame +int sim_room_can_resume_savegame(char *savegame_filename) +{ + #ifdef FREESPACE_SAVERESTORE_SYSTEM + char savegame_mission[MAX_FILENAME_LEN]; + + if (state_read_description(savegame_filename, NULL, savegame_mission)) { + return 0; + } + + if (stricmp(Game_current_mission_filename, savegame_mission)) { + return 0; + } + + return 1; + #else + return 0; + #endif +} + +// Decide wether to resume a save game or not +// exit: 1 => savegame has been restored +// 0 => no restore, proceed to briefing +// -1 => don't start mission at all +int sim_room_maybe_resume_savegame() +{ + // MWA -- 3/26/98 -- removed all savegame references in game + return 0; + + /* + char savegame_filename[_MAX_FNAME]; + int popup_rval = -1, resume_savegame = 0; + + // Generate the save-game filename for this campaign + memset(savegame_filename, 0, _MAX_FNAME); + mission_campaign_savefile_generate_root(savegame_filename); + strcat(savegame_filename, NOX("svg")); + + // Decide if we should offer choice to resume this savegame + if ( sim_room_can_resume_savegame(savegame_filename) ) { + popup_rval = popup(0, 3, XSTR("&Cancel",-1), XSTR("&Overwrite",-1), XSTR("&Resume",-1), XSTR("A save game for this mission exists.", -1)); + switch ( popup_rval ) { + case 0: + case -1: + resume_savegame = -1; + break; + case 1: + resume_savegame = 0; + break; + case 2: + resume_savegame = 1; + break; + default: + Int3(); + resume_savegame = -1; + break; + } + } else { + resume_savegame = 0; + } + + if (resume_savegame == 1) { + if ( state_restore_all(savegame_filename) == -1 ) { + popup_rval = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_NO, POPUP_YES, XSTR("Error\nSaved misison could not be loaded.\nDo you wish to start this mission from the beginning?", -1)); + if (popup_rval == 1) { + resume_savegame = 0; + } else { + resume_savegame = -1; + } + + } else { + resume_savegame = 1; + } + } + + // If we are resuming this savegame, then delete the file + if (resume_savegame == 1) { + cf_delete(savegame_filename); + } + + return resume_savegame; + */ +} + +int readyroom_continue_campaign() +{ + if (mission_campaign_next_mission()) { // is campaign and next mission valid? + +#ifdef FS2_DEMO + int reset_campaign = 0; + reset_campaign = popup(PF_BODY_BIG, 2, POPUP_NO, POPUP_YES, XSTR( "Demo Campaign Is Over. Would you like to play the campaign again?", 111) ); + if ( reset_campaign == 1 ) { + mission_campaign_savefile_delete(Campaign.filename); + mission_campaign_load(Campaign.filename); + mission_campaign_next_mission(); + } else { + return -1; + } +#else + gamesnd_play_iface(SND_GENERAL_FAIL); + popup(0, 1, POPUP_OK, XSTR( "The campaign is over. To replay the campaign, either create a new pilot or restart the campaign in the campaign room.", 112) ); + return -1; +#endif + } + + // CD CHECK + if(!game_do_cd_mission_check(Game_current_mission_filename)){ + return -1; + } + + // set the bit for campaign mode + Game_mode |= GM_CAMPAIGN_MODE; + gameseq_post_event( GS_EVENT_START_GAME ); + + return 0; +} + +void sim_room_commit() +{ + if ((Selected_line >= Num_lines) || !sim_room_lines[Selected_line].filename) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + strncpy(Game_current_mission_filename, sim_room_lines[Selected_line].filename, MAX_FILENAME_LEN); + + Game_mode &= ~(GM_CAMPAIGN_MODE); // be sure this bit is clear + + // CD CHECK + if(game_do_cd_mission_check(Game_current_mission_filename)){ + // don't resume savegame, proceed to briefing + gameseq_post_event(GS_EVENT_START_GAME); + gamesnd_play_iface(SND_COMMIT_PRESSED); + } +} + +int sim_room_button_pressed(int n) +{ + switch (n) { + case SCROLL_UP_BUTTON: + sim_room_scroll_screen_up(); + break; + + case SCROLL_DOWN_BUTTON: + sim_room_scroll_screen_down(); + break; + + case MISSION_TAB: +#ifdef OEM_BUILD + game_feature_not_in_demo_popup(); +// gamesnd_play_iface(SND_GENERAL_FAIL); + break; +#else + Player->readyroom_listing_mode = MODE_MISSIONS; + Selected_line = Scroll_offset = 0; + gamesnd_play_iface(SND_USER_SELECT); + sim_room_build_listing(); + break; +#endif + + case CAMPAIGN_TAB: + Player->readyroom_listing_mode = MODE_CAMPAIGNS; + Scroll_offset = 0; + gamesnd_play_iface(SND_USER_SELECT); + sim_room_build_listing(); + break; + + case COMMIT_BUTTON: + sim_room_commit(); + break; + + case HELP_BUTTON: + launch_context_help(); + gamesnd_play_iface(SND_HELP_PRESSED); + break; + + case OPTIONS_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + return 1; + + case TECH_DATABASE_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_TECH_MENU); + return 1; + + case CUTSCENES_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN); + return 1; + + case CREDITS_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_CREDITS); + return 1; + } + + return 0; +} + +// --------------------------------------------------------------------- +// mission_sim_room_init() +// +// Initialize the sim_room assignment screen system. Called when GS_STATE_sim_room_SCREEN +// is entered. +// +void sim_room_init() +{ + int i; + sim_room_buttons *b; + char wild_card[256]; + + list_x1 = Mission_list_coords[gr_screen.res][0]; + list_x2 = Campaign_list_coords[gr_screen.res][0]; + list_y = Mission_list_coords[gr_screen.res][1]; + list_w1 = Mission_list_coords[gr_screen.res][2]; + list_w2 = Campaign_list_coords[gr_screen.res][2]; + list_h = Mission_list_coords[gr_screen.res][3]; + + // force mode to valid range. I once had a bogus value here. Don't know how that happened, though. + if (Player->readyroom_listing_mode != MODE_MISSIONS) + Player->readyroom_listing_mode = MODE_CAMPAIGNS; + + *Game_current_mission_filename = 0; + common_set_interface_palette("InterfacePalette"); // set the interface palette + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Ui_window.set_mask_bmap(Sim_mask_filename[gr_screen.res]); + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // screen/button specific text + Ui_window.add_XSTR("Single Missions", 1060, Buttons[gr_screen.res][MISSION_TAB].xt, Buttons[gr_screen.res][MISSION_TAB].yt, &Buttons[gr_screen.res][MISSION_TAB].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Campaign Missions", 1061, Buttons[gr_screen.res][CAMPAIGN_TAB].xt, Buttons[gr_screen.res][CAMPAIGN_TAB].yt, &Buttons[gr_screen.res][CAMPAIGN_TAB].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Help", 928, Buttons[gr_screen.res][HELP_BUTTON].xt, Buttons[gr_screen.res][HELP_BUTTON].yt, &Buttons[gr_screen.res][HELP_BUTTON].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Commit", 1062, Buttons[gr_screen.res][COMMIT_BUTTON].xt, Buttons[gr_screen.res][COMMIT_BUTTON].yt, &Buttons[gr_screen.res][COMMIT_BUTTON].button, UI_XSTR_COLOR_PINK); + Ui_window.add_XSTR("Options", 1036, Buttons[gr_screen.res][OPTIONS_BUTTON].xt, Buttons[gr_screen.res][OPTIONS_BUTTON].yt, &Buttons[gr_screen.res][OPTIONS_BUTTON].button, UI_XSTR_COLOR_GREEN); + + // common tab button text + Ui_window.add_XSTR("Technical Database", 1055, Buttons[gr_screen.res][TECH_DATABASE_BUTTON].xt, Buttons[gr_screen.res][TECH_DATABASE_BUTTON].yt, &Buttons[gr_screen.res][TECH_DATABASE_BUTTON].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Mission Simulator", 1056, Buttons[gr_screen.res][SIMULATOR_BUTTON].xt, Buttons[gr_screen.res][SIMULATOR_BUTTON].yt, &Buttons[gr_screen.res][SIMULATOR_BUTTON].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Cutscenes", 1057, Buttons[gr_screen.res][CUTSCENES_BUTTON].xt, Buttons[gr_screen.res][CUTSCENES_BUTTON].yt, &Buttons[gr_screen.res][CUTSCENES_BUTTON].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Credits", 1058, Buttons[gr_screen.res][CREDITS_BUTTON].xt, Buttons[gr_screen.res][CREDITS_BUTTON].yt, &Buttons[gr_screen.res][CREDITS_BUTTON].button, UI_XSTR_COLOR_GREEN); + + // misc text - not associated with any buttons + Ui_window.add_XSTR("Mission", 1063, Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_MISSION][0], Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_MISSION][1], NULL, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Filename", 1064, Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_FILENAME][0], Sim_misc_text_coords[gr_screen.res][SIM_MISC_TEXT_FILENAME][1], NULL, UI_XSTR_COLOR_GREEN); + + for (i=0; icurrent_campaign); + mission_load_up_campaign(); + mission_campaign_next_mission(); + + Num_campaigns = Num_campaign_missions = 0; + Get_file_list_filter = sim_room_campaign_mission_filter; + memset(wild_card, 0, 256); + strcpy(wild_card, NOX("*")); + strcat(wild_card, FS_CAMPAIGN_FILE_EXT); + Num_campaigns = cf_get_file_list(MAX_CAMPAIGNS, Campaign_file_names, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME); + + Hash_table_inited = 0; + if (build_campaign_mission_filename_hash_table()) { + Hash_table_inited = 1; + } + + // HACK + GR_MAYBE_CLEAR_RES(Background_bitmap); + if(Background_bitmap != -1){ + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + Ui_window.draw(); + gr_flip(); + + Get_file_list_filter = sim_room_standalone_mission_filter; + memset(wild_card, 0, 256); + strcpy(wild_card, NOX("*")); + strcat(wild_card, FS_MISSION_FILE_EXT); + Num_standalone_missions = cf_get_file_list(MAX_MISSIONS, Mission_filenames, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME); + + Num_campaign_missions_with_info = Num_standalone_missions_with_info = Standalone_mission_names_inited = Campaign_names_inited = Campaign_mission_names_inited = 0; + sim_room_build_listing(); + + // load special mission icons + sim_room_load_mission_icons(); +} + +// --------------------------------------------------------------------- +// sim_room_close() +// +// Cleanup the sim_room assignment screen system. Called when GS_STATE_sim_room_SCREEN +// is left. +// +void sim_room_close() +{ + int i; + + for (i=0; i= 0) + bm_unload(Background_bitmap); + + if (Standalone_mission_names_inited){ + for (i=0; i 0) || B1_JUST_RELEASED ) { + if ( help_overlay_active(SIM_ROOM_OVERLAY) ) { + help_overlay_set_state(SIM_ROOM_OVERLAY, 0); + Ui_window.set_ignore_gadgets(0); + k = 0; + } + } + + if ( !help_overlay_active(SIM_ROOM_OVERLAY) ) { + Ui_window.set_ignore_gadgets(0); + } + + switch (k) { + case KEY_DOWN: // scroll list down + sim_room_scroll_line_down(); + break; + + case KEY_UP: // scroll list up + sim_room_scroll_line_up(); + break; + + case KEY_ESC: + gameseq_post_event(GS_EVENT_MAIN_MENU); + break; + + case KEY_CTRLED | KEY_UP: + sim_room_button_pressed(TECH_DATABASE_BUTTON); + break; + + case KEY_CTRLED | KEY_DOWN: + sim_room_button_pressed(CUTSCENES_BUTTON); + break; + + case KEY_TAB: + if (Player->readyroom_listing_mode == MODE_CAMPAIGNS) + Player->readyroom_listing_mode = MODE_MISSIONS; + else + Player->readyroom_listing_mode = MODE_CAMPAIGNS; + + Selected_line = Scroll_offset = 0; + gamesnd_play_iface(SND_USER_SELECT); + sim_room_build_listing(); + break; + + case KEY_F2: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + break; + } // end switch + + for (i=0; i= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + Ui_window.draw(); + + for (i=TECH_DATABASE_BUTTON; i<=CREDITS_BUTTON; i++){ + if (Buttons[gr_screen.res][i].button.button_down()){ + break; + } + } + + if (i > CREDITS_BUTTON){ + Buttons[gr_screen.res][SIMULATOR_BUTTON].button.draw_forced(2); + } + + if (!Buttons[gr_screen.res][CAMPAIGN_TAB].button.button_down() && !Buttons[gr_screen.res][MISSION_TAB].button.button_down()) { + if (Player->readyroom_listing_mode == MODE_CAMPAIGNS){ + Buttons[gr_screen.res][CAMPAIGN_TAB].button.draw_forced(2); + } else if (Player->readyroom_listing_mode == MODE_MISSIONS){ + Buttons[gr_screen.res][MISSION_TAB].button.draw_forced(2); + } + } + + gr_set_font(FONT1); + if (Player->readyroom_listing_mode == MODE_CAMPAIGNS) { + gr_set_color_fast(&Color_text_heading); + strcpy(buf, Campaign.name); + gr_force_fit_string(buf, 255, list_w1); + gr_printf(list_x1, Mission_list_coords[gr_screen.res][1], buf); + + if (Campaign.filename) { + sprintf(buf, NOX("%s%s"), Campaign.filename, FS_CAMPAIGN_FILE_EXT); + gr_force_fit_string(buf, 255, list_w2); + gr_printf(list_x2, Mission_list_coords[gr_screen.res][1], buf); + + // blit the proper icons if necessary + char full_name[256]; + memset(full_name, 0, 256); + strcpy(full_name, cf_add_ext(Campaign.filename,FS_CAMPAIGN_FILE_EXT)); + fs_builtin_mission *fb = game_find_builtin_mission(full_name); + if(fb != NULL){ + // sim_room_blit_icons(0, Mission_list_coords[gr_screen.res][1], fb, 0); + } + } + } + + line = Scroll_offset; + while (sim_room_line_query_visible(line)) { + y = list_y + sim_room_lines[line].y - sim_room_lines[Scroll_offset].y; + + if (sim_room_lines[line].type != READYROOM_LINE_CAMPAIGN) { + List_buttons[line - Scroll_offset].update_dimensions(list_x1, y, list_x2 + list_w2 - list_x1, font_height); + List_buttons[line - Scroll_offset].enable(); + + } else + List_buttons[line - Scroll_offset].disable(); + + if (line == Selected_line) + gr_set_color_fast(&Color_text_selected); + else if (line == select_tease_line) + gr_set_color_fast(&Color_text_subselected); + else + gr_set_color_fast(&Color_text_normal); + + strcpy(buf, sim_room_lines[line].name); + gr_force_fit_string(buf, 255, list_x1 + list_w1 - sim_room_lines[line].x); + gr_printf(sim_room_lines[line].x, y, buf); + + if (sim_room_lines[line].filename) { + strcpy(buf, sim_room_lines[line].filename); + gr_force_fit_string(buf, 255, list_w2); + gr_printf(list_x2, y, buf); + } + + // blit additional icon information + sim_room_blit_icons(line, y); + + line++; + } + + i = line - Scroll_offset; + while (i < LIST_BUTTONS_MAX) + List_buttons[i++].disable(); + + // blit help overlay if active + help_overlay_maybe_blit(SIM_ROOM_OVERLAY); + + gr_flip(); +} + +void sim_room_blit_icons(int line_index, int y_start, fs_builtin_mission *fb, int is_md) +{ + int is_from_volition = 0; + + // determine icon status + if(fb == NULL){ + is_from_volition = (sim_room_lines[line_index].flags & READYROOM_FLAG_FROM_VOLITION) ? 1 : 0; + } else { + is_from_volition = (fb->flags & FSB_FROM_VOLITION) ? 1 : 0; + } + + // if the line is flagged as a volition file + if(is_from_volition && (Mission_icon_bitmaps[MISSION_ICON_VOLITION] >= 0)){ + gr_set_bitmap(Mission_icon_bitmaps[MISSION_ICON_VOLITION]); + gr_bitmap(Sim_volition_icon_x[gr_screen.res], y_start + MISSION_ICON_VOLITION_Y_OFFSET); + } +} + +/// Campaign room stuff below +int Cr_list_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 47, 21, 565, 233 + }, + { // GR_1024 + 64, 34, 916, 373 + } +}; + +int Cr_info_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 28, 267, 476, 103 + }, + { // GR_1024 + 45, 427, 761, 165 + }, +}; + +#define CR_NUM_BUTTONS 6 + +#define CR_SCROLL_UP_BUTTON 0 +#define CR_SCROLL_DOWN_BUTTON 1 +#define CR_SCROLL_INFO_UP_BUTTON 2 +#define CR_SCROLL_INFO_DOWN_BUTTON 3 +#define CR_RESET_BUTTON 4 +#define CR_COMMIT_BUTTON 5 + +#define MAX_INFO_LINES 20 + +#define MAX_INFO_LINE_LEN 256 + +ui_button_info Cr_buttons[GR_NUM_RESOLUTIONS][CR_NUM_BUTTONS] = { + { // GR_640 + ui_button_info("CAB_00", 2, 42, -1, -1, 0), + ui_button_info("CAB_01", 2, 89, -1, -1, 1), + ui_button_info("CAB_02", 2, 279, -1, -1, 2), + ui_button_info("CAB_03", 2, 325, -1, -1, 3), + ui_button_info("CAB_04", 579, 353, -1, -1, 4), + ui_button_info("CAB_05", 575, 434, -1, -1, 5), + }, + { // GR_1024 + ui_button_info("2_CAB_00", 3, 68, -1, -1, 0), + ui_button_info("2_CAB_01", 3, 142, -1, -1, 1), + ui_button_info("2_CAB_02", 3, 446, -1, -1, 2), + ui_button_info("2_CAB_03", 3, 520, -1, -1, 3), + ui_button_info("2_CAB_04", 927, 565, -1, -1, 4), + ui_button_info("2_CAB_05", 920, 694, -1, -1, 5), + } +}; + +// text +#define CR_NUM_TEXT 3 +UI_XSTR Cr_text[GR_NUM_RESOLUTIONS][CR_NUM_TEXT] = { + { // GR_640 + { "Restart", 1403, 569, 326, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[0][CR_RESET_BUTTON].button }, + { "Campaign", 1404, 569, 337, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[0][CR_RESET_BUTTON].button }, + { "Select", 1409, 568, 413, UI_XSTR_COLOR_PINK, -1, &Cr_buttons[0][CR_COMMIT_BUTTON].button }, + }, + { // GR_1024 + { "Restart", 1403, 922, 523, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[1][CR_RESET_BUTTON].button }, + { "Campaign", 1404, 922, 538, UI_XSTR_COLOR_GREEN, -1, &Cr_buttons[1][CR_RESET_BUTTON].button }, + { "Select", 1409, 921, 665, UI_XSTR_COLOR_PINK, -1, &Cr_buttons[1][CR_COMMIT_BUTTON].button }, + } +}; + +static struct { + char *text; + int len; +} campaign_desc_lines[MAX_DESC_LINES]; + +static int Num_desc_lines; +static int Desc_scroll_offset; +static int Selected_campaign_index; +static int Active_campaign_index; + +char *Info_text_ptrs[MAX_INFO_LINES]; +int Num_info_lines, Info_text_line_size[MAX_INFO_LINES]; + +void campaign_room_build_listing() +{ + int c, i, y, type, max_players; + int font_height = gr_get_font_height(); + char name[NAME_LENGTH]; + + Num_lines = y = 0; + if (!Campaign_names_inited) { + for (i=0; iflags & FSB_FROM_VOLITION){ + flags |= READYROOM_FLAG_FROM_VOLITION; + } + } + + sim_room_line_add(READYROOM_LINE_CAMPAIGN, Campaign_names[c], Campaign_file_names[c], Cr_list_coords[gr_screen.res][0], y, flags); + y += font_height + 2; + } + } +} + +void set_new_campaign_line(int n) +{ + char *str; + + Selected_campaign_index = n; + str = Campaign_descs[Selected_campaign_index]; + Num_info_lines = 0; + if (str) { + Num_info_lines = split_str(str, Cr_info_coords[gr_screen.res][2], Info_text_line_size, Info_text_ptrs, MAX_INFO_LINES); + Assert(Num_info_lines >= 0); + } + + Desc_scroll_offset = 0; +} + +void campaign_room_scroll_info_up() +{ + if (Desc_scroll_offset) { + Desc_scroll_offset--; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void campaign_room_scroll_info_down() +{ + if ( (Num_info_lines - Desc_scroll_offset) * gr_get_font_height() > Cr_info_coords[gr_screen.res][3]) { + Desc_scroll_offset++; + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +// returns: 0 = success, !0 = aborted or failed +int campaign_room_reset_campaign(int n) +{ + char *filename; + int z; + + // z = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_CANCEL, POPUP_OK, XSTR( "Warning\nThis will cause all progress in your\ncurrent campaign to be lost", 110), Campaign_names[n]); + z = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, POPUP_CANCEL, POPUP_OK, XSTR( "Warning\nThis will cause all progress in your\ncurrent campaign to be lost", 110)); + if (z == 1) { + filename = (char *) malloc(strlen(Campaign_file_names[n]) + 5); + strcpy(filename, Campaign_file_names[n]); + strcat(filename, FS_CAMPAIGN_FILE_EXT); + + mission_campaign_savefile_delete(filename); + mission_campaign_load(filename); + mission_campaign_next_mission(); + return 0; + } + + return 1; +} + +void campaign_room_commit() +{ + if (Selected_campaign_index < 0) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + if (stricmp(Campaign_file_names[Selected_campaign_index], Campaign.filename)) { // new campaign selected + if ((Active_campaign_index >= 0) && campaign_room_reset_campaign(Active_campaign_index)) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + mission_campaign_savefile_delete(Campaign_file_names[Selected_campaign_index]); + mission_campaign_load(Campaign_file_names[Selected_campaign_index]); + strcpy(Player->current_campaign, Campaign.filename); // track new campaign for player + } + + if (mission_campaign_next_mission()) { // is campaign and next mission valid? + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + gameseq_post_event(GS_EVENT_MAIN_MENU); + gamesnd_play_iface(SND_COMMIT_PRESSED); +} + +int campaign_room_button_pressed(int n) +{ + switch (n) { + case CR_SCROLL_UP_BUTTON: + sim_room_scroll_screen_up(); + break; + + case CR_SCROLL_DOWN_BUTTON: + sim_room_scroll_screen_down(); + break; + + case CR_SCROLL_INFO_UP_BUTTON: + campaign_room_scroll_info_up(); + break; + + case CR_SCROLL_INFO_DOWN_BUTTON: + campaign_room_scroll_info_down(); + break; + + case CR_COMMIT_BUTTON: + campaign_room_commit(); + break; + + /* + case CR_HELP_BUTTON: + launch_context_help(); + gamesnd_play_iface(SND_HELP_PRESSED); + break; + + case CR_OPTIONS_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + return 1; + */ + + case CR_RESET_BUTTON: + if ( (Active_campaign_index < 0) || (Active_campaign_index >= Num_campaigns) ) + gamesnd_play_iface(SND_GENERAL_FAIL); + else if (campaign_room_reset_campaign(Active_campaign_index)) + gamesnd_play_iface(SND_GENERAL_FAIL); + else + gamesnd_play_iface(SND_USER_SELECT); + + break; + } + + return 0; +} + +void campaign_room_init() +{ + int i, j, load_failed; + ui_button_info *b; + char wild_card[256]; + + list_h = Mission_list_coords[gr_screen.res][3]; + + // common_set_interface_palette("InterfacePalette"); // set the interface palette + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Ui_window.set_mask_bmap(Campaign_mask_filename[gr_screen.res]); + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, (i < 2), 1); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + for (i=0; i= 0) + bm_unload(Background_bitmap); + + if (Campaign_names_inited) + for (i=0; i 0) || B1_JUST_RELEASED ) { + if ( help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) { + help_overlay_set_state(CAMPAIGN_ROOM_OVERLAY, 0); + Ui_window.set_ignore_gadgets(0); + k = 0; + } + } + + if ( !help_overlay_active(CAMPAIGN_ROOM_OVERLAY) ) { + Ui_window.set_ignore_gadgets(0); + } + + switch (k) { + case KEY_DOWN: // scroll list down + if (Selected_campaign_index < Num_campaigns - 1) { + set_new_campaign_line(Selected_campaign_index + 1); + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); + + break; + + case KEY_UP: // scroll list up + if (Selected_campaign_index < 0) + Selected_campaign_index = 1; + + if (Selected_campaign_index) { + set_new_campaign_line(Selected_campaign_index - 1); + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); + + break; + + case KEY_ESC: + gameseq_post_event(GS_EVENT_MAIN_MENU); + break; + } // end switch + + for (i=0; i= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + Ui_window.draw(); + + gr_set_font(FONT1); + line = Scroll_offset; + while (sim_room_line_query_visible(line)) { + y = Cr_list_coords[gr_screen.res][1] + sim_room_lines[line].y - sim_room_lines[Scroll_offset].y; + + List_buttons[line - Scroll_offset].update_dimensions(Cr_list_coords[gr_screen.res][0], y, Cr_list_coords[gr_screen.res][2], font_height); + List_buttons[line - Scroll_offset].enable(); + + if (!stricmp(sim_room_lines[line].filename, Campaign.filename)) { + gr_set_color_fast(&Color_white); + i = y + font_height / 2 - 1; + gr_circle(Cr_list_coords[gr_screen.res][0] - 6, i, 5); + + gr_set_color_fast(&Color_bright_white); + gr_line(Cr_list_coords[gr_screen.res][0] - 10, i, Cr_list_coords[gr_screen.res][0] - 8, i); + gr_line(Cr_list_coords[gr_screen.res][0] - 6, i - 4, Cr_list_coords[gr_screen.res][0] - 6, i - 2); + gr_line(Cr_list_coords[gr_screen.res][0] - 4, i, Cr_list_coords[gr_screen.res][0] - 2, i); + gr_line(Cr_list_coords[gr_screen.res][0] - 6, i + 2, Cr_list_coords[gr_screen.res][0] - 6, i + 4); + } + + if (line == Selected_campaign_index) + gr_set_color_fast(&Color_text_selected); + else if (line == select_tease_line) + gr_set_color_fast(&Color_text_subselected); + else + gr_set_color_fast(&Color_text_normal); + + strcpy(buf, sim_room_lines[line].name); + gr_force_fit_string(buf, 255, Cr_list_coords[gr_screen.res][0] + Cr_list_coords[gr_screen.res][2] - sim_room_lines[line].x); + gr_printf(sim_room_lines[line].x, y, buf); + line++; + } + + i = line - Scroll_offset; + while (i < LIST_BUTTONS_MAX) + List_buttons[i++].disable(); + + y = 0; + i = Desc_scroll_offset; + gr_set_color_fast(&Color_text_normal); + + while (y + font_height <= Cr_info_coords[gr_screen.res][3]) { + if (i >= Num_info_lines) + break; + + Assert(Info_text_line_size[i] < MAX_INFO_LINE_LEN); + strncpy(line_text, Info_text_ptrs[i], Info_text_line_size[i]); + line_text[Info_text_line_size[i]] = 0; + drop_white_space(line_text); + gr_string(Cr_info_coords[gr_screen.res][0], Cr_info_coords[gr_screen.res][1] + y, line_text); + y += font_height; + i++; + } + + // blit help overlay if active + help_overlay_maybe_blit(CAMPAIGN_ROOM_OVERLAY); + + gr_flip(); +} + +void sim_room_load_mission_icons() +{ + int idx; + + // load all bitmaps + for(idx=0; idx= 0){ + bm_unload(Mission_icon_bitmaps[idx]); + Mission_icon_bitmaps[idx] = -1; + } + } +} + diff --git a/src/menuui/snazzyui.cpp b/src/menuui/snazzyui.cpp new file mode 100644 index 0000000..7a70870 --- /dev/null +++ b/src/menuui/snazzyui.cpp @@ -0,0 +1,471 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/SnazzyUI.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Code to drive the Snazzy User Interface + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 7 7/16/99 1:49p Dave + * 8 bit aabitmaps. yay. + * + * 6 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 5 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 4 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 57 4/09/98 5:51p Lawrance + * Be able to disable sounds for certain menus + * + * 56 4/08/98 10:18a John + * Made version and snazzy info all render as white in all screen modes. + * + * 55 3/18/98 12:11p John + * redid some string externalization + * + * 54 3/05/98 11:15p Hoffoss + * Changed non-game key checking to use game_check_key() instead of + * game_poll(). + * + * 53 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 52 2/03/98 11:52p Lawrance + * call snazzy_flush() from game_flush() + * + * 51 1/20/98 2:23p Dave + * Removed optimized build warnings. 99% done with ingame join fixes. + * + * 50 1/10/98 12:47a Lawrance + * rip out hud config text drawing + * + * 49 12/23/97 5:28p Hoffoss + * Made enter key act the same as clicking the mouse button in main hall + * screen. + * + * 48 11/19/97 8:36p Dave + * Removed references to MainMenu.h + * + * 47 11/11/97 4:57p Dave + * Put in support for single vs. multiplayer pilots. Began work on + * multiplayer campaign saving. Put in initial player select screen + * + * 46 9/07/97 10:06p Lawrance + * let snazzy code keep track of mouse status + * + * 45 8/11/97 9:48p Lawrance + * don't poll keyboard if not requested + * + * 44 6/11/97 1:13p John + * Started fixing all the text colors in the game. + * + * 43 6/05/97 10:19a Lawrance + * before playing a sound, ensure it is valid + * + * 42 6/05/97 1:07a Lawrance + * changes to support sound interface + * + * 41 4/23/97 5:19p Lawrance + * split up misc sounds into: gamewide, ingame, and interface + * + * 40 4/18/97 2:54p Lawrance + * sounds now have a default volume, when playing, pass a scaling factor + * not the actual volume + * + * 39 3/31/97 5:45p Lawrance + * supporting changes to allow multiple streamed audio files + * + * 38 3/19/97 5:53p Lawrance + * integrating new Misc_sounds[] array (replaces old Game_sounds + * structure) + * + * 37 3/01/97 2:13p Lawrance + * made to work with new cfile changes + * + * 36 2/25/97 11:11a Lawrance + * adding more functionality needed for ship selection screen + * + * 35 2/05/97 10:35a Lawrance + * supporting spooled music at menus, briefings, credits etc. + * + * 34 1/22/97 11:01a John + * Added code to stream wav files in during menus. + * + * 33 1/20/97 7:58p John + * Fixed some link errors with testcode. + * + * 32 1/13/97 5:36p Lawrance + * integrating new Game_sounds structure for general game sounds + * + * 31 1/07/97 6:56p Lawrance + * adding sound hooks + * + * 30 12/10/96 4:18p Lawrance + * added snazzy_menu_close() call and integrated with existing menus + * + * 29 12/09/96 2:53p Lawrance + * fixed bug where both the snazzy code and ui code were reading the + * keyboard, and the keypress from the snazzy code was being lost + * + * 28 12/08/96 1:57a Lawrance + * integrating hud configuration + * + * 27 11/26/96 10:13a Allender + * fixed code to properly get text from the region + * + * 26 11/21/96 7:14p Lawrance + * converted menu code to use a file (menu.tbl) to get the data for the + * menu + * + * 25 11/15/96 2:14p Lawrance + * improved comments, removed some unnecssary #includes and code + * + * 24 11/15/96 12:09p John + * Added new UI code. Made mouse not return Enter when you click it. + * Changed the doSnazzyUI function and names to be snazzy_menu_xxx. + * + * 23 11/13/96 4:02p Lawrance + * complete over-haul of the menu system and the states associated with + * them + * + * 22 11/13/96 10:30a John + * Added code to call game_poll instead of key_inkey. + * + * 21 11/13/96 8:32a Lawrance + * streamlined menu code + * + * 20 11/12/96 12:20p John + * + * 19 11/12/96 12:20p John + * added game pol + * + * 18 11/11/96 4:03p Lawrance + * + * 17 11/08/96 10:00a Lawrance + * + * 16 11/06/96 8:54a Lawrance + * added revision templates, made more efficient + * + * $NoKeywords: $ + * +*/ + + +#include + +#include "pstypes.h" +#include "2d.h" +#include "pcxutils.h" +#include "key.h" +#include "optionsmenu.h" // need the #defines for the menu choices +#include "techmenu.h" // need the #defines for the menu choices +#include "trainingmenu.h" // need the #defines for the menu choices +#include "font.h" +#include "mouse.h" +#include "snazzyui.h" +#include "cfile.h" +#include "hudconfig.h" +#include "gamesequence.h" +#include "hud.h" +#include "sound.h" +#include "gamesnd.h" +#include "freespace.h" +#include "alphacolors.h" +#include "localize.h" + + +extern int ascii_table[]; +extern int shifted_ascii_table[]; + +static int Snazzy_mouse_left_was_down; + +void snazzy_flush() +{ + Snazzy_mouse_left_was_down = 0; +} + +void snazzy_menu_init() +{ + game_flush(); +} + +// snazzy_menu_do() +// +// This function will return an identifier that matches the region of the +// image the mouse pointer is currently over. The function works by working +// with two images +// +// 1. An image that is displayed to the player +// 2. A second image, not seen, which has masks for certain areas of image 1 +// +// The idea is to read the mouse, and determine if the mouse pointer is currently +// over one of these regions. Since these regions may be many different colors, +// the mask is checked instead, since the regions are always a uniform color +// +// The action parameter is used to return whether the region is clicked on or simply +// has the mouse over it. The #defines SNAZZY_OVER and SNAZZY_CLICKED are used. +// +// The purpose of the key_in parameter is to allow the caller to determine if any +// keys are pressed while within the snazzy_menu_do(). It is an optional parameter. +// + +int snazzy_menu_do(ubyte *data, int mask_w, int mask_h, int num_regions, MENU_REGION *regions, int *action, int poll_key, int *key) +{ + int i, k, x, y, offset; + int choice = -1, mouse_on_choice = -1; + ubyte pixel_value; + + Assert(data != NULL); + Assert(num_regions > 0); + Assert(regions != NULL); + + gr_reset_clip(); // don't remove + mouse_get_pos( &x, &y ); + + // boundary conditions + if((y > mask_h - 1) || (x > mask_w - 1)){ + pixel_value = 0; + } else { + // check the pixel value under the mouse + offset = y * mask_w + x; + pixel_value = *(data + (offset)); + } + + *action = -1; + + k = 0; + if ( poll_key ) { + k = game_check_key(); + if (key) + *key = k; // pass keypress back to caller + } + +// if (mouse_down_count(MOUSE_LEFT_BUTTON) ) { + if ( !mouse_down(MOUSE_LEFT_BUTTON) && Snazzy_mouse_left_was_down ) { + if (pixel_value >= 0) + //nprintf(("Alan", "pixel val: %d\n", pixel_value)); + for (i=0; i < num_regions; i++) { + if (pixel_value == regions[i].mask) { + choice = regions[i].mask; + if ( regions[i].click_sound != -1 ) { + snd_play( &Snds_iface[regions[i].click_sound], 0.0f ); + } + } + } // end for + } + + switch ( k ) { + case KEY_ESC: + choice = ESC_PRESSED; + break; + + default: + if ( k ) + for (i=0; i= 0) { + for (i=0; i= 0) && (mouse_on_choice <= (num_regions)) && (i >=0)) { + gr_printf( 0x8000, 450, regions[i].text ); + } + + if ( mouse_down(MOUSE_LEFT_BUTTON) ){ + Snazzy_mouse_left_was_down = 1; + } else { + Snazzy_mouse_left_was_down = 0; + } + + if ( choice > -1 || choice == ESC_PRESSED ) { + *action = SNAZZY_CLICKED; + return choice; + } + + if ( mouse_on_choice > -1 ) { + *action = SNAZZY_OVER; + return mouse_on_choice; + } + + return -1; +} + +// add_region() will set up a menu region +// +// +// + +void snazzy_menu_add_region(MENU_REGION* region, char* text, int mask, int key, int click_sound) +{ + region->mask = mask; + region->key = key; + strcpy(region->text, text); + region->click_sound = click_sound; +} + + + +// read_menu_tbl() will parse through menu.tbl and store the different menu region data +// +// +// + +void read_menu_tbl(char* menu_name, char* bkg_filename, char* mask_filename, MENU_REGION* regions, int* num_regions, int play_sound) +{ + CFILE* fp; + int state=0; + char* p1, *p2, *p3; + //char music_filename[128]; + + char seps[] = NOX(" ,\t"); + char *token; + char tmp_line[132]; + + *num_regions=0; + + // open localization + lcl_ext_open(); + + fp = cfopen( NOX("menu.tbl"), "rt" ); + if (fp == NULL) { + Error(LOCATION, "menu.tbl could not be opened\n"); + + // close localization + lcl_ext_close(); + + return; + } + + + while (cfgets(tmp_line, 132, fp)) { + p1 = strchr(tmp_line,'\n'); if (p1) *p1 = '\0'; + p1 = strchr(tmp_line,';'); if (p1) *p1 = '\0'; + p1 = p3 = strchr( tmp_line, '[' ); + + if (p3 && state == 1) { + // close localization + lcl_ext_close(); + + cfclose(fp); + return; + } + + if ( p1 || p3) { + if (!state) { + p2 = strchr( tmp_line, ']' ); + if (p2) *p2 = 0; + if (!stricmp( ++p1, menu_name )) state = 1; + } else { + cfclose(fp); + break; + } + } else if (state) { + + + // parse a region line + p1 = strchr( tmp_line, '\"' ); + if (p1) { + p2 = strchr( tmp_line+1, '\"' ); + if (!p2) { + nprintf(("Warning","Error parsing menu file\n")); + + // close localization + lcl_ext_close(); + + return; + } + *p2 = 0; + strcpy(regions[*num_regions].text,++p1); + p2++; + + // get the tokens mask number + token = strtok( p2, seps ); + regions[*num_regions].mask = atoi(token); + + // get the hot key character + token = strtok( NULL, seps ); + regions[*num_regions].key = token[0]; + + // stuff default click sound (not in menu.tbl) + if ( play_sound ) { + regions[*num_regions].click_sound = SND_IFACE_MOUSE_CLICK; + } else { + regions[*num_regions].click_sound = -1; + } + + *num_regions = *num_regions + 1; + + } + else { + // get the menu filenames + + // Establish string and get the first token + token = strtok( tmp_line, seps ); + if ( token != NULL ) + { + // store the background filename + strcpy(bkg_filename, token); + + // get the mask filename + token = strtok( NULL, seps ); + strcpy(mask_filename, token); + } + } + } + } + cfclose(fp); + + // close localization + lcl_ext_close(); +} + +// snazzy_menu_close() is called when the menu using a snazzy interface is exited +// +// + +void snazzy_menu_close() +{ + game_flush(); +} diff --git a/src/menuui/techmenu.cpp b/src/menuui/techmenu.cpp new file mode 100644 index 0000000..cc6ab8e --- /dev/null +++ b/src/menuui/techmenu.cpp @@ -0,0 +1,1664 @@ +/* + * $Logfile: /Freespace2/code/MenuUI/TechMenu.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module that contains functions to drive the Tech Menu user interface + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 43 11/02/99 3:22p Jefff + * translate ship names in tech room + * + * 42 10/25/99 5:47p Jefff + * reassigned some xstr ids + * + * 41 10/12/99 4:50p Jefff + * + * 40 9/08/99 11:09a Dave + * Use stills for intel and weapon stuff in the techroom if animations + * aren't avaliable. + * + * 39 9/05/99 11:19p Dave + * Made d3d texture cache much more safe. Fixed training scoring bug where + * it would backout scores without ever having applied them in the first + * place. + * + * 38 9/03/99 11:19a Jefff + * fixed double render of intel descriptions + * + * 37 9/03/99 1:32a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 36 9/01/99 5:12p Jefff + * Fixed bad scroll bug + * + * 35 8/25/99 3:03p Jefff + * 'more' indicator, lotsa coord changes for new background, disabling of + * buttons in trackball mode, list follows selected item when prev/next + * buttons used. + * + * 34 8/24/99 8:55p Dave + * Make sure nondimming pixels work properly in tech menu. + * + * 33 8/24/99 10:47a Jefff + * tech room weapon anims. added tech anim field to weapons.tbl + * + * 32 8/10/99 5:29p Jefff + * use new tech_title field in weapons array + * + * 31 8/10/99 3:45p Jefff + * Put the smack down on the tech room. Its all new, but tastefully done. + * + * 30 8/09/99 5:53p Jefff + * + * 29 8/02/99 10:27a Jefff + * removed a warning for the time being + * + * 28 8/02/99 10:13a Jefff + * started scrubbing, waiting till after demo to continue + * + * 27 7/28/99 1:02p Jefff + * Changed "Species" to "Intelligence" -- also changed in strings.tbl. + * + * 26 7/26/99 4:33p Jordonr + * Don't need techroom palette + * + * 25 7/20/99 1:49p Dave + * Peter Drake build. Fixed some release build warnings. + * + * 24 7/19/99 2:13p Dave + * Added some new strings for Heiko. + * + * 23 7/14/99 9:42a Dave + * Put in clear_color debug function. Put in base for 3dnow stuff / P3 + * stuff + * + * 22 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 21 5/09/99 8:57p Dave + * Final E3 build preparations. + * + * 20 5/09/99 6:00p Dave + * Lots of cool new effects. E3 build tweaks. + * + * 19 4/29/99 2:16p Neilk + * added a 2nd callback for slider so its model is not loaded on mouselock + * until mouse button is released + * + * 18 4/26/99 5:05p Neilk + * updated to new artwork, added slider support + * + * 17 4/25/99 3:02p Dave + * Build defines for the E3 build. + * + * 16 4/23/99 12:01p Johnson + * Added SIF_HUGE_SHIP + * + * 15 4/12/99 10:07p Dave + * Made network startup more forgiving. Added checkmarks to dogfight + * screen for players who hit commit. + * + * 14 4/08/99 2:10a Dave + * Numerous bug fixes for the beta. Added builtin mission info for the + * beta. + * + * 13 2/19/99 11:42a Dave + * Put in model rendering autocentering. + * + * 12 2/01/99 5:55p Dave + * Removed the idea of explicit bitmaps for buttons. Fixed text + * highlighting for disabled gadgets. + * + * 11 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 10 1/29/99 3:56p Neilk + * Converted tech_infos screens to multiresolution + * + * 9 1/29/99 3:54p Neilk + * + * 8 1/29/99 12:47a Dave + * Put in sounds for beam weapon. A bunch of interface screens (tech + * database stuff). + * + * 7 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 6 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 5 10/16/98 9:40a Andsager + * Remove ".h" files from model.h + * + * 4 10/13/98 2:47p Andsager + * Remove reference to Tech_shivan_species_avail + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 67 9/21/98 10:02p Dave + * Last minute changes to techroom weapon/ship/species stuff. + * + * 66 9/07/98 2:49p Dan + * Removed spurious Assert + * + * 65 7/06/98 2:42p Hoffoss + * Fixed bug with weapons shown in tech database. + * + * 64 6/10/98 11:48a Lawrance + * fix bug with trying to free NULL anims + * + * 63 6/05/98 9:54a Lawrance + * OEM changes + * + * 62 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 61 5/26/98 11:10a Lawrance + * Fix bug where window controls get disabled when F1 pressed twice + * + * 60 5/23/98 10:38p Lawrance + * Avoid doing a cfile refresh when running debug + * + * 59 5/23/98 6:49p Lawrance + * Fix problems with refreshing the file list when a CD is inserted + * + * 58 5/22/98 11:15a Lawrance + * Tweak how CD gets asked for + * + * 57 5/22/98 1:06a Hoffoss + * Made Fred not use OLE. + * + * 56 5/21/98 6:57p Lawrance + * Only ask for the CD once + * + * 55 5/21/98 12:46a Hoffoss + * Made text offset reset when new description is loaded. + * + * 54 5/20/98 9:46p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 53 5/20/98 3:28p Sandeep + * Fixed bug where in multi only discovered weapons show up, and in single + * all weapons showed up (backwards). + * + * 52 5/20/98 2:11p Hoffoss + * Fixed some species description problems. + * + * 51 5/18/98 6:45p Hoffoss + * Added species descriptions to species database in techroom. + * + * 50 5/17/98 6:13p Hoffoss + * Fixed scrolling of info text area. + * + * $NoKeywords: $ + * + */ + +#include "gamesequence.h" +#include "techmenu.h" +#include "2d.h" +#include "3d.h" +#include "snazzyui.h" +#include "managepilot.h" +#include "key.h" +#include "bmpman.h" +#include "timer.h" +#include "missioncampaign.h" +#include "missionshipchoice.h" +#include "freespace.h" +#include "mainhallmenu.h" +#include "missionscreencommon.h" +#include "gamesnd.h" +#include "font.h" +#include "vecmat.h" +#include "mouse.h" +#include "uidefs.h" +#include "contexthelp.h" +#include "alphacolors.h" +#include "animplay.h" +#include "localize.h" +#include "lighting.h" + +#define REVOLUTION_RATE 5.2f + +#define NUM_BUTTONS 16 +#define NUM_TABS 3 +#define LIST_BUTTONS_MAX 41 + +#define SHIPS_DATA_MODE (1<<0) +#define WEAPONS_DATA_MODE (1<<1) +#define SPECIES_DATA_MODE (1<<2) +#define WEAPONS_SPECIES_DATA_MODE (WEAPONS_DATA_MODE | SPECIES_DATA_MODE) + +#define SHIPS_DATA_TAB 0 +#define WEAPONS_DATA_TAB 1 +#define INTEL_DATA_TAB 2 +#define TECH_DATABASE_TAB 3 +#define SIMULATOR_TAB 4 +#define CUTSCENES_TAB 5 +#define CREDITS_TAB 6 + +#define SCROLL_LIST_UP 7 +#define SCROLL_LIST_DOWN 8 +#define SCROLL_INFO_UP 9 +#define SCROLL_INFO_DOWN 10 + +#define PREV_ENTRY_BUTTON 11 +#define NEXT_ENTRY_BUTTON 12 + +#define HELP_BUTTON 13 +#define OPTIONS_BUTTON 14 +#define EXIT_BUTTON 15 + +//#define PREV_ENTRY_BUTTON2 16 +//#define NEXT_ENTRY_BUTTON2 17 + + +#define REPEAT (1<<0) +#define NO_MOUSE_OVER_SOUND (1<<1) + +// indicies for coords +#define SHIP_X_COORD 0 +#define SHIP_Y_COORD 1 +#define SHIP_W_COORD 2 +#define SHIP_H_COORD 3 + +// background filename for species +// note weapon filename is now same as ship filename +char *Tech_background_filename[GR_NUM_RESOLUTIONS] = { + "TechShipData", + "2_TechShipData" +}; +char *Tech_mask_filename[GR_NUM_RESOLUTIONS] = { + "TechShipData-M", + "2_TechShipData-M" +}; +char *Tech_slider_filename[GR_NUM_RESOLUTIONS] = { + "slider", + "2_slider" +}; + +int Tech_list_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 27, 98, 161, 234 + }, + { // GR_1024 + 43, 157, 253, 374 + } +}; + +int Tech_ship_display_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 239, 98, 393, 222 + }, + { // GR_1024 + 382, 158, 629, 355 + } +}; + +int Tech_desc_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 29, 347, 365, 125 + }, + { // GR_1024 + 47, 555, 584, 200 + } +}; + +int Tech_ani_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 196, 115 + }, + { // GR_1024 + 449, 245 + } +}; + +/*int Tech_desc_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 24, 139, 376, 281 + }, + { // GR_1024 + 24, 182, 638, 528 + } +};*/ + +int Tech_slider_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 2, 118, 20, 194 + }, + { // GR_1024 + 3, 190, 32, 310 + } +}; + +// detail backup +int Tech_detail_backup; +int Tech_texture_backup; + +#define MAX_TEXT_LINES 100 +#define MAX_TEXT_LINE_LEN 256 + +struct techroom_buttons { + char *filename; + int x, y, xt, yt; + int hotspot; + int tab; + int flags; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + techroom_buttons(char *name, int x1, int y1, int xt1, int yt1, int h, int t, int f = 0) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h), tab(t), flags(f) {} +}; + +static techroom_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { + { // GR_640 + techroom_buttons("TDB_04", 406, 384, 447, 393, 4, -1), // ship data tab + techroom_buttons("TDB_05", 404, 418, 447, 429, 5, -1), // weapons data tab + techroom_buttons("TDB_06", 404, 447, 447, 461, 6, -1), // species data tab + techroom_buttons("TDB_00", 7, 3, 37, 7, 0, -1), // technical database tab + techroom_buttons("TDB_01", 7, 18, 37, 23, 1, -1), // mission simulator tab + techroom_buttons("TDB_02", 7, 34, 37, 38, 2, -1), // cutscenes tab + techroom_buttons("TDB_03", 7, 49, 37, 54, 3, -1), // credits tab + techroom_buttons("TDB_07", 1, 86, -1, -1, 7, SHIPS_DATA_MODE, REPEAT), // prev data entry + techroom_buttons("TDB_08", 1, 317, -1, -1, 8, SHIPS_DATA_MODE, REPEAT), // next data entry + techroom_buttons("TDB_09", 1, 406, -1, -1, 9, SHIPS_DATA_MODE, REPEAT), // prev data entry + techroom_buttons("TDB_10", 1, 447, -1, -1, 10, SHIPS_DATA_MODE, REPEAT), // next data entry + //techroom_buttons("TDB_11", 558, 272, -1, -1, 11, WEAPONS_SPECIES_DATA_MODE), // prev data entry + //techroom_buttons("TDB_12", 606, 272, -1, -1, 12, WEAPONS_SPECIES_DATA_MODE), // next data entry + techroom_buttons("TDB_11a",559, 323, -1, -1, 11, SHIPS_DATA_MODE, REPEAT), // prev data entry + techroom_buttons("TDB_12a",609, 323, -1, -1, 12, SHIPS_DATA_MODE, REPEAT), // next data entry + techroom_buttons("TDB_13", 533, 425, 500, 440, 13, -1), // help + techroom_buttons("TDB_14", 533, 455, 479, 464, 14, -1), // options + techroom_buttons("TDB_15a",571, 425, 588, 413, 15, -1), // exit + }, + { // GR_1024 + techroom_buttons("2_TDB_04", 649, 614, 717, 630, 4, -1), // ship data tab + techroom_buttons("2_TDB_05", 646, 669, 717, 687, 5, -1), // weapons data tab + techroom_buttons("2_TDB_06", 646, 716, 717, 739, 6, -1), // species data tab + techroom_buttons("2_TDB_00", 12, 5, 59, 12, 0, -1), // technical database tab + techroom_buttons("2_TDB_01", 12, 31, 59, 37, 1, -1), // mission simulator tab + techroom_buttons("2_TDB_02", 12, 56, 59, 62, 2, -1), // cutscenes tab + techroom_buttons("2_TDB_03", 12, 81, 59, 88, 3, -1), // credits tab + techroom_buttons("2_TDB_07", 1, 138, -1, -1, 7, SHIPS_DATA_MODE, REPEAT), // prev data entry + techroom_buttons("2_TDB_08", 1, 507, -1, -1, 8, SHIPS_DATA_MODE, REPEAT), // next data entry + techroom_buttons("2_TDB_09", 1, 649, -1, -1, 9, SHIPS_DATA_MODE, REPEAT), // prev data entry + techroom_buttons("2_TDB_10", 1, 716, -1, -1, 10, SHIPS_DATA_MODE, REPEAT), // next data entry + //techroom_buttons("2_TDB_11", 893, 436, -1, -1, 11, WEAPONS_SPECIES_DATA_MODE), // prev data entry + //techroom_buttons("2_TDB_12", 970, 436, -1, -1, 12, WEAPONS_SPECIES_DATA_MODE), // next data entry + techroom_buttons("2_TDB_11a", 895, 518, -1, -1, 11, SHIPS_DATA_MODE, REPEAT), // prev data entry + techroom_buttons("2_TDB_12a", 974, 518, -1, -1, 12, SHIPS_DATA_MODE, REPEAT), // next data entry + techroom_buttons("2_TDB_13", 854, 681, 800, 704, 13, -1), // help + techroom_buttons("2_TDB_14", 854, 728, 780, 743, 14, -1), // options + techroom_buttons("2_TDB_15a", 914, 681, 930, 660, 15, -1), // exit + }, +}; + +static UI_WINDOW Ui_window; +static UI_BUTTON View_window; +//static int Background_bitmap; +static int Tech_background_bitmap; +static int Intel_bg_bitmap; +static int Tab = 0; +// static int List_size; +static int List_offset; +static int Select_tease_line; +static int Limit; +static int Trackball_mode = 1; +static int Trackball_active = 0; +static matrix Techroom_ship_orient = IDENTITY_MATRIX; +// static int Tech_room_ask_for_cd; + +static int Text_size; +static int Text_offset; +static int Text_line_size[MAX_TEXT_LINES]; +static char *Text_lines[MAX_TEXT_LINES]; + +static int Cur_entry; // this is the current entry selected, using entry indexing +static int Cur_entry_index; // this is the current entry selected, using master list indexing +static int Techroom_ship_modelnum; +static float Techroom_ship_rot; +static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list +static int Anim_playing_id = -1; +static anim_instance *Cur_anim_instance = NULL; +static int Palette_bmp; +//static int ShipWin01; +//static int ShipWin02; +//static int ShipWin03; +//static int ShipWin04; +static ubyte Palette[768]; +static char Palette_name[128]; + +static int Ships_loaded = 0; +static int Weapons_loaded = 0; +static int Intel_loaded = 0; + +// out entry data struct & vars +typedef struct { + int index; // index into the master table that its in (ie Ship_info[]) + char* name; // ptr to name string + char* desc; // ptr to description string + anim* animation; // ptr to the animation + int bitmap; // bitmap handle + int has_anim; // flag to indicate the presence of an animation for this item +} tech_list_entry; + +static tech_list_entry Ship_list[MAX_SHIP_TYPES]; +static int Ship_list_size = 0; +static tech_list_entry Weapon_list[MAX_WEAPON_TYPES]; +static int Weapon_list_size = 0; +static tech_list_entry Intel_list[MAX_INTEL_ENTRIES]; +static int Intel_list_size = 0; +static tech_list_entry *Current_list; // points to currently valid display list +static int Current_list_size = 0; + + +// slider stuff +static UI_SLIDER2 Tech_slider; + +//XSTR:OFF +/* +static char *Intel_anim_filenames[MAX_INTEL_ENTRIES] = { + "tech_tpilot.ani", + "tech_vasudan.ani", + "tech_shivan.ani", +}; +*/ +//XSTR:ON + +// Intelligence master data structs (these get inited @ game startup from species.tbl) +intel_data Intel_info[MAX_INTEL_ENTRIES]; +int Intel_info_size = 0; + +// some prototypes to make you happy +int techroom_load_ani(anim **animpp, char *name); +void tech_common_render(); +void techroom_start_anim(); +void tech_scroll_list_up(); +void tech_scroll_list_down(); + + + + +//////////////////////////////////////////////////// +// like, functions and stuff + +void techroom_init_desc(char *src, int w) +{ + Text_size = Text_offset = 0; + if (!src) { + return; + } + + Text_size = split_str(src, w, Text_line_size, Text_lines, MAX_TEXT_LINES); + Assert(Text_size >= 0 && Text_size < MAX_TEXT_LINES); +} + +void techroom_select_new_entry() +{ + Assert(Current_list != NULL); + if (Current_list == NULL) return; + + Cur_entry_index = Current_list[Cur_entry].index; + + // if we are in the ships tab, load the ship model + if (Tab == SHIPS_DATA_TAB) { + ship_info *sip = &Ship_info[Cur_entry_index]; + +#ifdef MULTIPLAYER_BETA_BUILD + // don't load supercaps in the beta + if((sip->flags & SIF_SUPERCAP) || (sip->flags & SIF_DRYDOCK)){ + Techroom_ship_modelnum = -1; + } else { + Techroom_ship_modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]); + } + + // page in ship textures properly (takes care of nondimming pixels) + model_page_in_textures(Techroom_ship_modelnum, Cur_entry_index); +#else + Techroom_ship_modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]); + + // page in ship textures properly (takes care of nondimming pixels) + model_page_in_textures(Techroom_ship_modelnum, Cur_entry_index); +#endif + } else { + Techroom_ship_modelnum = -1; + Trackball_mode = 0; + } + +// Techroom_ship_rot = PI; + + techroom_init_desc(Current_list[Cur_entry].desc, Tech_desc_coords[gr_screen.res][SHIP_W_COORD]); + techroom_start_anim(); +} + +// write out the current description in the bottom window +void techroom_render_desc(int xo, int yo, int h) +{ + int y, z, len, font_height; + char line[MAX_TEXT_LINE_LEN + 1]; + + font_height = gr_get_font_height(); + + y = 0; + z = Text_offset; + while (y + font_height <= h) { + if (z >= Text_size){ + break; + } + + len = Text_line_size[z]; + if (len > MAX_TEXT_LINE_LEN){ + len = MAX_TEXT_LINE_LEN; + } + + strncpy(line, Text_lines[z], len); + line[len] = 0; + gr_string(xo, yo + y, line); + + y += font_height; + z++; + } + + // maybe output 'more' indicator + if ( z < Text_size ) { + // can be scrolled down + int more_txt_x = Tech_desc_coords[gr_screen.res][0] + (Tech_desc_coords[gr_screen.res][2]/2) - 10; // FIXME should move these to constants since they dont move + int more_txt_y = Tech_desc_coords[gr_screen.res][1] + Tech_desc_coords[gr_screen.res][3]; // located below brief text, centered + int w, h; + gr_get_string_size(&w, &h, XSTR("more", 1469), strlen(XSTR("more", 1469))); + gr_set_color_fast(&Color_black); + gr_rect(more_txt_x-2, more_txt_y, w+3, h); + gr_set_color_fast(&Color_red); + gr_string(more_txt_x, more_txt_y, XSTR("more", 1469)); // base location on the input x and y? + } + +} + +// new version of weapons +void techroom_weapons_render2(float frametime) +{ + // render common stuff + tech_common_render(); + + // render the animation + { + // JAS: This code is hacked to allow the animation to use all 256 colors + extern int Palman_allow_any_color; + Palman_allow_any_color = 1; + anim_render_all(0, frametime); + Palman_allow_any_color = 0; + } + + // if our active item has a bitmap instead of an animation, draw it + if((Cur_entry > 0) && (Current_list[Cur_entry].animation == NULL) && (Current_list[Cur_entry].bitmap >= 0)){ + gr_set_bitmap(Current_list[Cur_entry].bitmap); + gr_bitmap(Tech_ani_coords[gr_screen.res][0], Tech_ani_coords[gr_screen.res][1]); + } +} + +// renders the stuff common to all 3 tech room tabs +void tech_common_render() +{ + char buf[256]; + int y, z, font_height; + + // render description in its box + gr_set_color_fast(&Color_text_normal); + techroom_render_desc(Tech_desc_coords[gr_screen.res][SHIP_X_COORD], Tech_desc_coords[gr_screen.res][SHIP_Y_COORD], Tech_desc_coords[gr_screen.res][SHIP_H_COORD]); + + font_height = gr_get_font_height(); + + // draw the list of entries + y = 0; + z = List_offset; + while (y + font_height <= Tech_list_coords[gr_screen.res][SHIP_H_COORD]) { + if (z >= Current_list_size) { + break; + } + + if (z == Cur_entry) { + gr_set_color_fast(&Color_text_selected); + } else if (z == Select_tease_line) { + gr_set_color_fast(&Color_text_subselected); + } else { + gr_set_color_fast(&Color_text_normal); + } + + strcpy(buf, Current_list[z].name); + if (Lcl_gr) { + lcl_translate_ship_name(buf); + } + + gr_force_fit_string(buf, 255, Tech_list_coords[gr_screen.res][SHIP_W_COORD]); + gr_string(Tech_list_coords[gr_screen.res][SHIP_X_COORD], Tech_list_coords[gr_screen.res][SHIP_Y_COORD] + y, buf); + + List_buttons[z - List_offset].update_dimensions(Tech_list_coords[gr_screen.res][SHIP_X_COORD], Tech_list_coords[gr_screen.res][SHIP_Y_COORD] + y, Tech_list_coords[gr_screen.res][SHIP_W_COORD], font_height); + List_buttons[z - List_offset].enable(1); + + y += font_height; + z++; + } + + // disable the rest of the list buttons + z -= List_offset; + while (z < LIST_BUTTONS_MAX) { + List_buttons[z++].disable(); + } +} + +void techroom_ships_render(float frametime) +{ + // render all the common stuff + tech_common_render(); + + // now render the trackball ship, which is unique to the ships tab + float rev_rate; + angles rot_angles, view_angles; + int z; + ship_info *sip = &Ship_info[Cur_entry_index]; + + // get correct revolution rate + rev_rate = REVOLUTION_RATE; + z = sip->flags; + if (z & SIF_BIG_SHIP) { + rev_rate *= 1.7f; + } + if (z & SIF_HUGE_SHIP) { + rev_rate *= 3.0f; + } + + // rotate the ship as much as required for this frame + Techroom_ship_rot += PI2 * frametime / rev_rate; + while (Techroom_ship_rot > PI2){ + Techroom_ship_rot -= PI2; + } + + // turn off fogging + gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0); + + // reorient ship + if (Trackball_active) { + int dx, dy; + matrix mat1, mat2; + + if (Trackball_active) { + mouse_get_delta(&dx, &dy); + if (dx || dy) { + vm_trackball(-dx, -dy, &mat1); + vm_matrix_x_matrix(&mat2, &mat1, &Techroom_ship_orient); + Techroom_ship_orient = mat2; + } + } + + } else { + // setup stuff needed to render the ship + view_angles.p = -0.6f; + view_angles.b = 0.0f; + view_angles.h = 0.0f; + vm_angles_2_matrix(&Techroom_ship_orient, &view_angles); + + rot_angles.p = 0.0f; + rot_angles.b = 0.0f; + rot_angles.h = Techroom_ship_rot; + vm_rotate_matrix_by_angles(&Techroom_ship_orient, &rot_angles); + } + + gr_set_clip(Tech_ship_display_coords[gr_screen.res][SHIP_X_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_Y_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_W_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_H_COORD]); + + // render the ship +#ifdef MULTIPLAYER_BETA_BUILD + if((sip->flags & SIF_SUPERCAP) || (sip->flags & SIF_DRYDOCK)) { + gr_set_color_fast(&Color_bright); + gr_string(Tech_ship_display_coords[gr_screen.res][SHIP_X_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_Y_COORD] + 50, NOX("No soup for you!")); + } else { + g3_start_frame(1); + + g3_set_view_matrix(&sip->closeup_pos, &vmd_identity_matrix, sip->closeup_zoom * 1.3f); + + // lighting for techroom + light_reset(); + vector light_dir = vmd_zero_vector; + light_dir.y = 1.0f; + light_add_directional(&light_dir, 0.85f, 1.0f, 1.0f, 1.0f); + // light_filter_reset(); + light_rotate_all(); + // lighting for techroom + + model_clear_instance(Techroom_ship_modelnum); + model_set_detail_level(0); + model_render(Techroom_ship_modelnum, &Techroom_ship_orient, &vmd_zero_vector, MR_NO_LIGHTING | MR_LOCK_DETAIL | MR_AUTOCENTER); + + g3_end_frame(); + } +#else + g3_start_frame(1); + + g3_set_view_matrix(&sip->closeup_pos, &vmd_identity_matrix, sip->closeup_zoom * 1.3f); + + // lighting for techroom + light_reset(); + vector light_dir = vmd_zero_vector; + light_dir.y = 1.0f; + light_add_directional(&light_dir, 0.85f, 1.0f, 1.0f, 1.0f); + // light_filter_reset(); + light_rotate_all(); + // lighting for techroom + + model_clear_instance(Techroom_ship_modelnum); + model_set_detail_level(0); + model_render(Techroom_ship_modelnum, &Techroom_ship_orient, &vmd_zero_vector, MR_LOCK_DETAIL | MR_AUTOCENTER); + + g3_end_frame(); +#endif + + gr_reset_clip(); +} + +// select previous entry in current list +void tech_prev_entry() +{ + Cur_entry--; + if (Cur_entry < 0) { + Cur_entry = Current_list_size - 1; + + // scroll to end of list + List_offset = Cur_entry - Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() + 1; + if (List_offset < 0) { + // this happens when there are not enough items to scroll + List_offset = 0; + } + Tech_slider.force_currentItem(Tech_slider.get_numberItems()); + } else { + // maybe adjust list position by 1 + if (List_offset > Cur_entry) { + tech_scroll_list_up(); + Tech_slider.forceUp(); + } + } + + techroom_select_new_entry(); + gamesnd_play_iface(SND_SCROLL); +} + +// select next entry in current list +void tech_next_entry() +{ + Cur_entry++; + if (Cur_entry >= Current_list_size) { + Cur_entry = 0; + + // scroll to beginning of list + List_offset = 0; + Tech_slider.force_currentItem(Cur_entry); + } else { + // maybe adjust list position by 1 + if (List_offset + Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() <= Cur_entry) { + tech_scroll_list_down(); + Tech_slider.forceDown(); + } + } + + techroom_select_new_entry(); + gamesnd_play_iface(SND_SCROLL); +} + +void tech_scroll_info_up() +{ + if (Text_offset) { + Text_offset--; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +void tech_scroll_info_down() +{ + int h; + + if (Tab == SHIPS_DATA_TAB){ + h = Tech_desc_coords[gr_screen.res][SHIP_H_COORD]; + } else { + h = Tech_desc_coords[gr_screen.res][3]; + } + + if (Text_offset + h / gr_get_font_height() < Text_size) { + Text_offset++; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +void tech_scroll_list_up() +{ + //int last; + + if (List_offset > 0) { + List_offset--; + //last = List_offset + Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() - 1; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +void tech_scroll_list_down() +{ + if (List_offset + Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() < Current_list_size) { + List_offset++; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// this doesnt do a damn thing... +void tech_ship_scroll_capture() +{ + techroom_select_new_entry(); +} + + +// this is obsolete - see techroom_weapons_render2(...) +void techroom_weapons_render(float frametime) +{ + gr_set_color_fast(&Color_text_normal); + techroom_render_desc(Tech_desc_coords[gr_screen.res][0], Tech_desc_coords[gr_screen.res][1], Tech_desc_coords[gr_screen.res][3]); + + { + // JAS: This code is hacked to allow the animation to use all 256 colors + extern int Palman_allow_any_color; + Palman_allow_any_color = 1; + anim_render_all(0, frametime); + Palman_allow_any_color = 0; + } + + // if our active item has a bitmap instead of an animation, draw it + if((Cur_entry >= 0) && (Current_list[Cur_entry].animation == NULL) && (Current_list[Cur_entry].bitmap >= 0)){ + gr_set_bitmap(Current_list[Cur_entry].bitmap); + gr_bitmap(Tech_ani_coords[gr_screen.res][0], Tech_ani_coords[gr_screen.res][1]); + } +} + +void techroom_intel_render(float frametime) +{ + tech_common_render(); + + { + // JAS: This code is hacked to allow the animation to use all 256 colors + extern int Palman_allow_any_color; + Palman_allow_any_color = 1; + anim_render_all(0, frametime); + Palman_allow_any_color = 0; + } + + // if our active item has a bitmap instead of an animation, draw it + if((Cur_entry >= 0) && (Current_list[Cur_entry].animation == NULL) && (Current_list[Cur_entry].bitmap >= 0)){ + gr_set_bitmap(Current_list[Cur_entry].bitmap); + gr_bitmap(Tech_ani_coords[gr_screen.res][0], Tech_ani_coords[gr_screen.res][1]); + } +} + +void techroom_stop_anim(int id) +{ + if (Cur_anim_instance && (id != Anim_playing_id)) { + anim_stop_playing(Cur_anim_instance); + Cur_anim_instance = NULL; + } +} + +void techroom_start_anim() +{ + int id; + anim *animp; + anim_play_struct aps; + + if (Cur_entry < 0) { + techroom_stop_anim(-1); + Anim_playing_id = -1; + } + + if (Tab == WEAPONS_DATA_TAB) { + id = Cur_entry; + } else { + id = Cur_entry + 2000; // this offset is arbitrary? + } + + techroom_stop_anim(id); + + // if we actually have an animation + if(Current_list[Cur_entry].animation != NULL){ + animp = Current_list[Cur_entry].animation; + + if (id != Anim_playing_id) { + Anim_playing_id = -1; + if (animp) { + anim_play_init(&aps, animp, Tech_ani_coords[gr_screen.res][0], Tech_ani_coords[gr_screen.res][1]); + aps.looped = 1; + Cur_anim_instance = anim_play(&aps); + Anim_playing_id = id; + } + + if (animp) { + memcpy(Palette, animp->palette, 384); + gr_set_palette(animp->name, Palette, 1); + } + } + } +} + + +void techroom_change_tab(int num) +{ + int i, multi = 0, mask, font_height, max_num_entries_viewable; + + Tab = num; + // Assert(Current_list_size >= 0); + List_offset = 0; + Cur_entry = 0; + multi = Player->flags & PLAYER_FLAGS_IS_MULTI; + + for (i=0; iflags & PLAYER_FLAGS_IS_MULTI) { + Buttons[gr_screen.res][SIMULATOR_TAB].button.disable(); + Buttons[gr_screen.res][CUTSCENES_TAB].button.disable(); + } + + switch (Tab) { + case SHIPS_DATA_TAB: + mask = multi ? SIF_IN_TECH_DATABASE_M : SIF_IN_TECH_DATABASE; + + // load ship info if necessary + if (Ships_loaded == 0) { + Ship_list_size = 0; + for (i=0; i max_num_entries_viewable ? Current_list_size-max_num_entries_viewable : 0); + + // no anim to start here + break; + + case WEAPONS_DATA_TAB: + + // load weapon info & anims if necessary + if (Weapons_loaded == 0) { + Weapon_list_size = 0; + mask = multi ? WIF_PLAYER_ALLOWED : WIF_IN_TECH_DATABASE; + + for (i=0; i 0) && (!strcmp(Weapon_info[i].tech_desc, Weapon_list[Weapon_list_size-1].desc))) { + continue; + } + + // we have a weapon that should be in the tech db, so fill out the entry struct + Weapon_list[Weapon_list_size].index = i; + Weapon_list[Weapon_list_size].desc = Weapon_info[i].tech_desc; + Weapon_list[Weapon_list_size].has_anim = 1; + Weapon_list[Weapon_list_size].name = Weapon_info[i].tech_title; + Weapon_list[Weapon_list_size].bitmap = -1; + if (Weapon_list[Weapon_list_size].name[0] == 0) { + Weapon_list[Weapon_list_size].name = Weapon_info[i].name; + } + + // load the weapon animation + if(!techroom_load_ani(&Weapon_list[Weapon_list_size].animation, Weapon_info[i].tech_anim_filename)){ + Weapon_list[Weapon_list_size].has_anim = 0; + Weapon_list[Weapon_list_size].animation = NULL; + + // hmm. try a bitmap instead + Weapon_list[Weapon_list_size].bitmap = bm_load(Weapon_info[i].tech_anim_filename); + } + + Weapon_list_size++; + } + } + + Weapons_loaded = 1; + } + + Current_list = Weapon_list; + Current_list_size = Weapon_list_size; + + font_height = gr_get_font_height(); + max_num_entries_viewable = Tech_list_coords[gr_screen.res][SHIP_H_COORD] / font_height; + Tech_slider.set_numberItems(Current_list_size > max_num_entries_viewable ? Current_list_size-max_num_entries_viewable : 0); + + techroom_start_anim(); + break; + + case INTEL_DATA_TAB: + + // load intel if necessary + if ( Intel_loaded == 0 ) { + // now populate the entry structs + Intel_list_size = 0; + for (i=0; i max_num_entries_viewable ? Current_list_size-max_num_entries_viewable : 0); + + techroom_start_anim(); + break; + } + + // reset the entry + Cur_entry = 0; + techroom_select_new_entry(); + +} + +int techroom_button_pressed(int num) +{ + switch (num) { + case SHIPS_DATA_TAB: + case WEAPONS_DATA_TAB: + case INTEL_DATA_TAB: + techroom_change_tab(num); + break; + + case SIMULATOR_TAB: +#if !defined(E3_BUILD) && !defined(PD_BUILD) + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_SIMULATOR_ROOM); + return 1; +#else + return 0; +#endif + + case CUTSCENES_TAB: +#if !defined(E3_BUILD) && !defined(PD_BUILD) + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN); + return 1; +#else + return 0; +#endif + + case CREDITS_TAB: +#if !defined(E3_BUILD) && !defined(PD_BUILD) + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_CREDITS); + return 1; +#else + return 0; +#endif + + case PREV_ENTRY_BUTTON: + tech_prev_entry(); + break; + + case NEXT_ENTRY_BUTTON: + tech_next_entry(); + break; + + case SCROLL_LIST_UP: + tech_scroll_list_up(); + Tech_slider.forceUp(); + break; + + case SCROLL_LIST_DOWN: + tech_scroll_list_down(); + Tech_slider.forceDown(); + break; + + case SCROLL_INFO_UP: + tech_scroll_info_up(); + break; + + case SCROLL_INFO_DOWN: + tech_scroll_info_down(); + break; + + case HELP_BUTTON: + launch_context_help(); + gamesnd_play_iface(SND_HELP_PRESSED); + break; + + case OPTIONS_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + break; + + case EXIT_BUTTON: + gamesnd_play_iface(SND_COMMIT_PRESSED); + gameseq_post_event(GS_EVENT_MAIN_MENU); + break; + } + + return 0; +} + +int techroom_load_ani(anim **animpp, char *name) +{ + int load_attempts = 0; + char anim_filename[64] = "2_"; + + // hi-res support + // (i dont think there are any hi-res anims for these tho) + if (gr_screen.res == GR_1024) { + strcat(anim_filename, name); + } else { + strcpy(anim_filename, name); + } + + while(1) { + if ( load_attempts++ > 5 ) { + // Tech_room_ask_for_cd = 0; + return 0; + } + + *animpp = anim_load(anim_filename, 1); + if ( *animpp ) { + return 1; + } else if (gr_screen.res == GR_1024) { + // try to load low-res version if hi-res failed + *animpp = anim_load(name, 1); + if (*animpp) { + return 1; + } + } + + // couldn't load animation, ask user to insert CD (if necessary) + // if ( Tech_room_ask_for_cd ) { + // if ( game_do_cd_check() == 0 ) { + // Tech_room_ask_for_cd = 0; + // break; + // } + // } + } + + // bogus + return 0; +} + + +void techroom_intel_init() +{ + int rval; + static int inited = 0; + + // open localization + lcl_ext_open(); + + if (!inited) { + if ((rval = setjmp(parse_abort)) != 0) { + // close localization + lcl_ext_close(); + + return; + } else { + read_file_text("species.tbl"); + reset_parse(); + + Intel_info_size = 0; + + while (optional_string("$Entry:")) { + Assert(Intel_info_size < MAX_INTEL_ENTRIES); + if (Intel_info_size >= MAX_INTEL_ENTRIES) break; + + required_string("$Name:"); + stuff_string(Intel_info[Intel_info_size].name, F_NAME, NULL, 32); + required_string("$Anim:"); + stuff_string(Intel_info[Intel_info_size].anim_filename, F_NAME, NULL, 32); + required_string("$AlwaysInTechRoom:"); + stuff_int(&Intel_info[Intel_info_size].in_tech_db); + required_string("$Description:"); + stuff_string(Intel_info[Intel_info_size].desc, F_MULTITEXT, NULL, TECH_INTEL_DESC_LEN); + + + Intel_info_size++; + } + inited = 1; + } + } + + // close localization + lcl_ext_close(); +} + +void techroom_init() +{ + int i, idx; + techroom_buttons *b; + + gr_reset_clip(); + gr_clear(); + Mouse_hidden++; + gr_flip(); + Mouse_hidden--; + + Ships_loaded = 0; + Weapons_loaded = 0; + Intel_loaded = 0; + + // Tech_room_ask_for_cd = 1; + + // backup and bash detail level stuff + Tech_detail_backup = Detail.detail_distance; + Detail.detail_distance = MAX_DETAIL_LEVEL; + Tech_texture_backup = Detail.hardware_textures; + Detail.hardware_textures = MAX_DETAIL_LEVEL; + + /* + Palette_bmp = bm_load("TechDataPalette"); + Assert(Palette_bmp); + bm_get_palette(Palette_bmp, Palette, Palette_name); // get the palette for this bitmap + gr_set_palette(Palette_name, Palette, 1); + */ + + // unflag fullneb + The_mission.flags &= ~MISSION_FLAG_FULLNEB; + + // set up UI stuff + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Ui_window.set_mask_bmap(Tech_mask_filename[gr_screen.res]); + + Tech_background_bitmap = bm_load(Tech_background_filename[gr_screen.res]); + if (Tech_background_bitmap < 0) { + // failed to load bitmap, not a good thing + Int3(); + } + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, b->flags & REPEAT, 1); + // set up callback for when a mouse first goes over a button + if (b->filename) { + b->button.set_bmaps(b->filename); +// if ( !(b->flags & NO_MOUSE_OVER_SOUND) ) { + b->button.set_highlight_action(common_play_highlight_sound); +// } + + } else { + b->button.hide(); + } + + b->button.link_hotspot(b->hotspot); + } + + // common tab button text + Ui_window.add_XSTR("Technical Database", 1055, Buttons[gr_screen.res][TECH_DATABASE_TAB].xt, Buttons[gr_screen.res][TECH_DATABASE_TAB].yt, &Buttons[gr_screen.res][TECH_DATABASE_TAB].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Mission Simulator", 1056, Buttons[gr_screen.res][SIMULATOR_TAB].xt, Buttons[gr_screen.res][SIMULATOR_TAB].yt, &Buttons[gr_screen.res][SIMULATOR_TAB].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Cutscenes", 1057, Buttons[gr_screen.res][CUTSCENES_TAB].xt, Buttons[gr_screen.res][CUTSCENES_TAB].yt, &Buttons[gr_screen.res][CUTSCENES_TAB].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Credits", 1058, Buttons[gr_screen.res][CREDITS_TAB].xt, Buttons[gr_screen.res][CREDITS_TAB].yt, &Buttons[gr_screen.res][CREDITS_TAB].button, UI_XSTR_COLOR_GREEN); + + // common ship/weapon/intel text + Ui_window.add_XSTR("Ships", 293, Buttons[gr_screen.res][SHIPS_DATA_TAB].xt, Buttons[gr_screen.res][SHIPS_DATA_TAB].yt, &Buttons[gr_screen.res][SHIPS_DATA_TAB].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Weapons", 1553, Buttons[gr_screen.res][WEAPONS_DATA_TAB].xt, Buttons[gr_screen.res][WEAPONS_DATA_TAB].yt, &Buttons[gr_screen.res][WEAPONS_DATA_TAB].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Intelligence", 1066, Buttons[gr_screen.res][INTEL_DATA_TAB].xt, Buttons[gr_screen.res][INTEL_DATA_TAB].yt, &Buttons[gr_screen.res][INTEL_DATA_TAB].button, UI_XSTR_COLOR_GREEN); + + // common help/options/commit text + + // NK: removed these two text labels on Tech screen update 4/26/99 +// Ui_window.add_XSTR("Help", 928, Buttons[gr_screen.res][HELP_BUTTON].xt, Buttons[gr_screen.res][HELP_BUTTON].yt, &Buttons[gr_screen.res][HELP_BUTTON].button, UI_XSTR_COLOR_GREEN); +// Ui_window.add_XSTR("Options", 1036, Buttons[gr_screen.res][OPTIONS_BUTTON].xt, Buttons[gr_screen.res][OPTIONS_BUTTON].yt, &Buttons[gr_screen.res][OPTIONS_BUTTON].button, UI_XSTR_COLOR_GREEN); + Ui_window.add_XSTR("Exit", 1418, Buttons[gr_screen.res][EXIT_BUTTON].xt, Buttons[gr_screen.res][EXIT_BUTTON].yt, &Buttons[gr_screen.res][EXIT_BUTTON].button, UI_XSTR_COLOR_PINK); + + if (Player->flags & PLAYER_FLAGS_IS_MULTI) { + Buttons[gr_screen.res][SIMULATOR_TAB].button.disable(); + Buttons[gr_screen.res][CUTSCENES_TAB].button.disable(); + } + + // set some hotkeys + Buttons[gr_screen.res][PREV_ENTRY_BUTTON].button.set_hotkey(KEY_LEFT); + Buttons[gr_screen.res][NEXT_ENTRY_BUTTON].button.set_hotkey(KEY_RIGHT); + Buttons[gr_screen.res][SCROLL_INFO_UP].button.set_hotkey(KEY_UP); + Buttons[gr_screen.res][SCROLL_INFO_DOWN].button.set_hotkey(KEY_DOWN); + + + for (i=0; i= 0 ){ + bm_unload(Weapon_list[i].bitmap); + Weapon_list[i].bitmap = -1; + } + } + + for (i=0; i= 0 ){ + bm_unload(Intel_list[i].bitmap); + Intel_list[i].bitmap = -1; + } + } + + Ships_loaded = 0; + Weapons_loaded = 0; + Intel_loaded = 0; + + /* + if (ShipWin01){ + bm_unload(ShipWin01); + } + if (ShipWin02){ + bm_unload(ShipWin02); + } + if (ShipWin03){ + bm_unload(ShipWin03); + } + if (ShipWin04){ + bm_unload(ShipWin04); + } + */ + + if (Tech_background_bitmap) { + bm_unload(Tech_background_bitmap); + } + + Ui_window.destroy(); + common_free_interface_palette(); // restore game palette + if (Palette_bmp){ + bm_unload(Palette_bmp); + } + + // restore detail settings + Detail.detail_distance = Tech_detail_backup; + Detail.hardware_textures = Tech_texture_backup; +} + +void techroom_do_frame(float frametime) +{ + int i, k; + + // turn off controls when overlay is on + if ( help_overlay_active(TECH_ROOM_OVERLAY) ) { + Buttons[gr_screen.res][HELP_BUTTON].button.reset_status(); + Ui_window.set_ignore_gadgets(1); + } + + // turn off controls in trackball mode + if (Trackball_active) { + Ui_window.set_ignore_gadgets(1); + } else { + Ui_window.set_ignore_gadgets(0); + } + + k = Ui_window.process() & ~KEY_DEBUGGED; + + if ( (k > 0) || B1_JUST_RELEASED ) { + if ( help_overlay_active(TECH_ROOM_OVERLAY) ) { + help_overlay_set_state(TECH_ROOM_OVERLAY, 0); + Ui_window.set_ignore_gadgets(0); + k = 0; + } + } + + if ( !help_overlay_active(TECH_ROOM_OVERLAY) ) { + Ui_window.set_ignore_gadgets(0); + } + + switch (k) { + case KEY_SHIFTED | KEY_TAB: // activate previous tab + i = Tab - 1; + if (i < 0) { + i = NUM_TABS - 1; + } + + techroom_change_tab(i); + break; + + case KEY_TAB: // activate next tab + i = Tab + 1; + if (i >= NUM_TABS) { + i = 0; + } + + techroom_change_tab(i); + break; + + case KEY_CTRLED | KEY_DOWN: + if ( !(Player->flags & PLAYER_FLAGS_IS_MULTI) ) { + techroom_button_pressed(SIMULATOR_TAB); + break; + } + // fall through + + case KEY_CTRLED | KEY_UP: + techroom_button_pressed(CREDITS_TAB); + break; +/* + case KEY_UP: + tech_prev_entry(); + break; + + case KEY_DOWN: + tech_next_entry(); + break; +*/ + case KEY_CTRLED | KEY_ENTER: + case KEY_ESC: + gameseq_post_event(GS_EVENT_MAIN_MENU); + break; + } + + // check ship model window for activity + if (View_window.pressed()) { + Trackball_active = 1; + Trackball_mode = 1; + } + if (B1_RELEASED) { + Trackball_active = 0; + } + + // check all da buttons + for (i=0; i= 0) { + gr_set_bitmap(Tech_background_bitmap); + gr_bitmap(0, 0); + } + + // render + switch (Tab) { + case SHIPS_DATA_TAB: + techroom_ships_render(frametime); + + /* + if (ShipWin01) { + gr_set_bitmap(ShipWin01); + gr_bitmap(223, 104); + } + + if (ShipWin02) { + gr_set_bitmap(ShipWin02); + gr_bitmap(621, 124); + } + + if (ShipWin03) { + gr_set_bitmap(ShipWin03); + gr_bitmap(223, 338); + } + + if (ShipWin04) { + gr_set_bitmap(ShipWin04); + gr_bitmap(218, 124); + } + */ + + break; + + case WEAPONS_DATA_TAB: + techroom_weapons_render2(frametime); + break; + + case INTEL_DATA_TAB: + techroom_intel_render(frametime); + break; + } + + Ui_window.draw(); + + for (i=TECH_DATABASE_TAB; i<=CREDITS_TAB; i++) { + if (Buttons[gr_screen.res][i].button.button_down()) { + break; + } + } + if (i > CREDITS_TAB) { + Buttons[gr_screen.res][TECH_DATABASE_TAB].button.draw_forced(2); + } + + for (i=0; idata; + bm_get_info(trainingMenuMask, &Training_mask_w, &Training_mask_h); + } +} + +void training_menu_close() +{ + if (training_menu_inited) { + // done with the bitmap, so unlock it + bm_unlock(trainingMenuMask); + + // unload the bitmaps + bm_unload(trainingMenuBitmap); + bm_unload(trainingMenuMask); + + training_menu_inited = 0; + snazzy_menu_close(); + } +} + +void training_menu_do_frame(float frametime) +{ + int training_menu_choice; + + if (!training_menu_inited) { + training_menu_init(); + training_menu_inited=1; + } + + gr_reset_clip(); + gr_set_color(0,0,0); + GR_MAYBE_CLEAR_RES(trainingMenuBitmap); + // set the background + if(trainingMenuBitmap != -1){ + gr_set_bitmap(trainingMenuBitmap); + gr_bitmap(0,0); + } + + int snazzy_action = -1; + training_menu_choice = snazzy_menu_do(mask_data, Training_mask_w, Training_mask_h, num_training, region, &snazzy_action); + if ( snazzy_action != SNAZZY_CLICKED ){ + training_menu_choice = -1; + } + + switch (training_menu_choice) { + + case TRAINING_MENU_TRAINING_MISSIONS_MASK: + break; + case TRAINING_MENU_REPLAY_MISSIONS_MASK: + // TODO: load the mission and start the briefing + break; + case TRAINING_MENU_RETURN_MASK: + case ESC_PRESSED: + gameseq_post_event(GS_EVENT_MAIN_MENU); + break; + case -1: + // nothing selected + break; + default: + Error(LOCATION, "Unknown option %d in training menu screen", training_menu_choice ); + break; + + } // end switch + + gr_flip(); +} + diff --git a/src/mission/missionbriefcommon.cpp b/src/mission/missionbriefcommon.cpp new file mode 100644 index 0000000..664914f --- /dev/null +++ b/src/mission/missionbriefcommon.cpp @@ -0,0 +1,2584 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionBriefCommon.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for briefing code common to FreeSpace and FRED + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 19 11/02/99 3:23p Jefff + * translate briefing icon names + * + * 18 9/09/99 9:44a Jefff + * doh, fixed reversed brief text color thingy. i am stoopid. + * + * 17 9/08/99 11:14a Jefff + * toned down hostile/friendly colors in briefing text + * + * 16 9/07/99 12:20p Mikeb + * return pos of briefing icon even it does not fit on screen. + * + * 15 9/03/99 1:32a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 14 8/10/99 7:28p Jefff + * shuffled some text around + * + * 13 7/30/99 3:05p Jefff + * Fixed briefing icon fades -- in and out were reversed. + * + * 12 7/26/99 1:52p Mikeb + * Fixed strange briefing bug where a NULL wasn't being checked for when + * copying briefing stage text. Odd. + * + * 11 7/24/99 6:15p Jefff + * moved "stage x of y" text in multiplayer mode so its not covered by the + * chatbox + * + * 10 7/20/99 7:09p Jefff + * briefing text occupies full window in 1024x768 + * + * 9 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 8 6/29/99 7:39p Dave + * Lots of small bug fixes. + * + * 7 2/05/99 7:19p Neilk + * Removed black part from mission screen, fixed info text coords + * + * 6 1/29/99 4:17p Dave + * New interface screens. + * + * 5 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 4 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 122 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 121 6/05/98 9:54a Lawrance + * OEM changes + * + * 120 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 119 5/23/98 10:38p Lawrance + * Avoid doing a cfile refresh when running debug + * + * 118 5/23/98 6:49p Lawrance + * Fix problems with refreshing the file list when a CD is inserted + * + * 117 5/21/98 6:57p Lawrance + * Don't prompt for the CD if voice not found + * + * 116 5/21/98 12:35a Lawrance + * Tweak how CD is checked for + * + * 115 5/12/98 11:46a John + * Changed the way the "glowing movement" type text draw. Use Hoffoss's + * gr_get_string_size optional length parameter to determine length of + * string which accounts for kerning on the last character and then I only + * draw each character only once. + * + * 114 5/08/98 5:32p Lawrance + * prompt for CD if can't load animations or voice + * + * 113 5/06/98 5:30p John + * Removed unused cfilearchiver. Removed/replaced some unused/little used + * graphics functions, namely gradient_h and _v and pixel_sp. Put in new + * DirectX header files and libs that fixed the Direct3D alpha blending + * problems. + * + * 112 4/27/98 9:08p Allender + * fix the debriefing stage problems when clients get to screen long after + * server + * + * 111 4/25/98 3:49p Lawrance + * Save briefing auto-advance pref + * + * 110 4/20/98 3:53p Lawrance + * Fix various bugs with auto-advancing through briefings. + * + * $NoKeywords: $ + */ + +#include "freespace.h" +#include "ship.h" +#include "key.h" +#include "2d.h" +#include "3d.h" +#include "line.h" +#include "timer.h" +#include "math.h" +#include "linklist.h" +#include "mouse.h" +#include "hud.h" +#include "osapi.h" +#include "object.h" +#include "multi.h" +#include "bmpman.h" +#include "missionbrief.h" +#include "missiongrid.h" +#include "missionbriefcommon.h" +#include "animplay.h" +#include "fvi.h" +#include "float.h" +#include "gamesnd.h" +#include "cmdline.h" +#include "parselo.h" +#include "audiostr.h" +#include "missioncmdbrief.h" +#include "missiondebrief.h" +#include "alphacolors.h" +#include "localize.h" + +// -------------------------------------------------------------------------------------- +// briefing icons +// -------------------------------------------------------------------------------------- +hud_frames Icon_bitmaps[MAX_BRIEF_ICONS][MAX_SPECIES_NAMES]; +hud_anim Icon_highlight_anims[MAX_BRIEF_ICONS][MAX_SPECIES_NAMES]; +hud_anim Icon_fade_anims[MAX_BRIEF_ICONS][MAX_SPECIES_NAMES]; + + +// -------------------------------------------------------------------------------------- +// briefing screen +// -------------------------------------------------------------------------------------- + +brief_screen bscreen; + +// briefing screen sections +#define BRIEF_CUP_X1 400 +#define BRIEF_CUP_Y1 70 +#define BRIEF_CUP_X2 639 +#define BRIEF_CUP_Y2 245 +#define BRIEF_CUPINFO_X1 445 +#define BRIEF_CUPINFO_Y1 247 +#define BRIEF_CUPINFO_X2 639 +#define BRIEF_CUPINFO_Y2 438 + +char *Brief_static_name[GR_NUM_RESOLUTIONS] = { + "BriefMap", + "2_BriefMap" +}; + +int Brief_static_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 10, 130 + }, + { // GR_1024 + 15, 208 + } +}; + +int Brief_bmap_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 0, 115 + }, + { // GR_1024 + 0, 184 + } +}; + +int Brief_grid_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 19, 147, 555, 232 + }, + { // GR_1024 + 30, 235, 888, 371 + } +}; + +int Brief_text_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 28, 399, 395, 74 + }, + { // GR_1024 + 46, 637, 630, 120 + } +}; + +int Brief_stage_text_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 138, 117 + }, + { // GR_1024 + 227, 194 + } +}; + +int Brief_stage_text_coords_multi[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 479, 385 + }, + { // GR_1024 + 821, 616 + } +}; + +int Brief_text_max_lines[GR_NUM_RESOLUTIONS] = { + 6, 6 +}; + +#define LOOKAT_DIST 500.0f + +// -------------------------------------------------------------------------------------- +// Game-wide global data +// -------------------------------------------------------------------------------------- +briefing Briefings[MAX_TEAMS]; // there is exactly one briefing per mission +debriefing Debriefings[MAX_TEAMS]; // there can be multiple debriefings per mission +briefing *Briefing; // pointer used in code -- points to correct briefing +debriefing *Debriefing; // pointer to correct debriefing + +int Briefing_voice_enabled=1; // flag which turn on/off voice playback of briefings/debriefings + +// -------------------------------------------------------------------------------------- +// Module global data +// -------------------------------------------------------------------------------------- + +static int Last_new_stage; +int Cur_brief_id; + +const char BRIEF_META_CHAR = '$'; + +// static int Brief_voice_ask_for_cd; + +// camera related +static vector Current_cam_pos; // current camera position +static vector Target_cam_pos; // desired camera position +static matrix Current_cam_orient; // current camera orientation +static matrix Target_cam_orient; // desired camera orientation +static matrix Start_cam_orient; // start camera orientation +static vector Start_cam_pos; // position of camera at the start of a translation +static vector Cam_vel; // camera velocity +static vector Current_lookat_pos; // lookat point +static vector Target_lookat_pos; // lookat point +static vector Start_lookat_pos; +static vector Lookat_vel; // lookat point velocity + +static float Start_cam_move; // time at which camera started moving (seconds) +static float Total_move_time; // time in which camera should move from current pos to target pos (seconds) +static float Elapsed_time; + +static float Start_dist; +static float End_dist; +static float Dist_change_rate; + +static vector Acc_limit; +static vector Vel_limit; + +static float Total_dist; +static float Peak_speed; +static float Cam_accel; +static float Last_dist; +static vector W_init; + +// flag to indicate that the sound for a spinning highlight animation has played +static int Brief_stage_highlight_sound_handle = -1; + +// used for scrolling briefing text ( if necessary ) +int Num_brief_text_lines[MAX_TEXT_STREAMS]; +int Top_brief_text_line; +static char Brief_text[MAX_BRIEF_LINES][MAX_BRIEF_LINE_LEN]; + +// Used to support drawing colored text for the briefing. Gets complicates since we +// need to be able to draw one character at a time as well when the briefing text +// first appears. +typedef struct colored_char +{ + char letter; + ubyte color; // index into Brief_text_colors[] +} colored_char; + +static colored_char Colored_text[MAX_TEXT_STREAMS][MAX_BRIEF_LINES][MAX_BRIEF_LINE_LEN]; +static int Colored_text_len[MAX_TEXT_STREAMS][MAX_BRIEF_LINES]; + +#define MAX_BRIEF_TEXT_COLORS 9 +#define BRIEF_TEXT_WHITE 0 +#define BRIEF_TEXT_BRIGHT_WHITE 1 +#define BRIEF_TEXT_RED 2 +#define BRIEF_TEXT_GREEN 3 +#define BRIEF_TEXT_YELLOW 4 +#define BRIEF_TEXT_BLUE 5 +#define BRIEF_IFF_FRIENDLY 6 +#define BRIEF_IFF_HOSTILE 7 +#define BRIEF_IFF_NEUTRAL 8 + +color Brief_color_red, Brief_color_green; + +color *Brief_text_colors[MAX_BRIEF_TEXT_COLORS] = +{ + &Color_white, + &Color_bright_white, + &Color_red, + &Color_green, + &Color_yellow, + &Color_blue, + &Brief_color_green, + &Brief_color_red, + &IFF_colors[IFF_COLOR_NEUTRAL][0], +}; + +#define BRIGHTEN_LEAD 2 + +float Brief_text_wipe_time_elapsed; +static int Max_briefing_line_len; + +static int Brief_voice_ended; +static int Brief_textdraw_finished; +static int Brief_voice_started; +static int Brief_stage_time; + +const float BRIEF_TEXT_WIPE_TIME = 1.5f; // time in seconds for wipe to occur +static int Brief_text_wipe_snd; // sound handle of sound effect for text wipe +static int Play_brief_voice; + +// animation stuff +static int Play_highlight_flag; +static int Cam_target_reached; +static int Cam_movement_done; + +// moving icons +typedef struct icon_move_info +{ + icon_move_info *next, *prev; + int used; + int id; + vector start; + vector finish; + vector current; + + // used to move icons smoothly + vector direction; + float total_dist; + float accel; + float total_move_time; + float peak_speed; + int reached_dest; + float last_dist; +} icon_move_info; + +#define MAX_MOVE_ICONS 10 +icon_move_info Icon_movers[MAX_MOVE_ICONS]; +icon_move_info Icon_move_list; // head of linked list + +// fading out icons +typedef struct fade_icon +{ + hud_anim fade_anim; // anim info + vector pos; + int team; +} fade_icon; + +#define MAX_FADE_ICONS 30 +fade_icon Fading_icons[MAX_FADE_ICONS]; +int Num_fade_icons; + +// voice id's for briefing text +int Brief_voices[MAX_BRIEF_STAGES]; + +cmd_brief *Cur_cmd_brief; +cmd_brief Cmd_briefs[MAX_TEAMS]; + +// -------------------------------------------------------------------------------------- +// forward declarations +// -------------------------------------------------------------------------------------- +void brief_render_elements(vector *pos, grid *gridp); +void brief_render_icons(int stage_num, float frametime); +void brief_parse_icon_tbl(); +void brief_grid_read_camera_controls( control_info * ci, float frametime ); +void brief_maybe_create_new_grid(grid *gridp, vector *pos, matrix *orient, int force = 0); +grid *brief_create_grid(grid *gridp, vector *forward, vector *right, vector *center, int nrows, int ncols, float square_size); +grid *brief_create_default_grid(void); +void brief_render_grid(grid *gridp); +void brief_modify_grid(grid *gridp); +void brief_rpd_line(vector *v0, vector *v1); +void brief_set_text_color(int color_index); +extern void get_camera_limits(matrix *start_camera, matrix *end_camera, float time, vector *acc_max, vector *w_max); +int brief_text_wipe_finished(); + +void brief_set_icon_color(int team) +{ + switch (team) { + case TEAM_FRIENDLY: SET_COLOR_FRIENDLY; break; + case TEAM_HOSTILE: SET_COLOR_HOSTILE; break; + case TEAM_NEUTRAL: SET_COLOR_NEUTRAL; break; + case TEAM_TRAITOR: SET_COLOR_HOSTILE; break; + default: + SET_COLOR_HOSTILE; break; + } // end switch +} + +// -------------------------------------------------------------------------------------- +// brief_move_icon_reset() +// +// +void brief_move_icon_reset() +{ + int i; + + list_init(&Icon_move_list); + for ( i = 0; i < MAX_MOVE_ICONS; i++ ) + Icon_movers[i].used = 0; +} + + +// -------------------------------------------------------------------------------------- +// Does one time initialization of the briefing and debriefing structures. +// Namely setting all malloc'ble pointers to NULL. Called once at game startup. +void mission_brief_common_init() +{ + int i,j; + + // setup brief text colors + gr_init_alphacolor( &Brief_color_green, 50, 100, 50, 255 ); + gr_init_alphacolor( &Brief_color_red, 140, 20, 20, 255 ); + + if ( Fred_running ) { + // If Fred is running malloc out max space + for (i=0; inum_frames; j++ ) { + bm_unload(ib->first_frame+j); + } + } + } +} + +// determine if icon type is used in the current briefing +int brief_icon_used_in_briefing(int icon_type) +{ + int num_icons, num_stages, i, j; + + num_stages = Briefing->num_stages; + + for ( i = 0; i < num_stages; i++ ) { + num_icons = Briefing->stages[i].num_icons; + for ( j = 0; j < num_icons; j++ ) { + if ( Briefing->stages[i].icons[j].type == icon_type ) { + return 1; + } + } + } + + return 0; +} + +// -------------------------------------------------------------------------------------- +// brief_parse_icon_tbl() +// +// +void brief_parse_icon_tbl() +{ + int num_icons, rval; + char name[NAME_LENGTH]; + hud_frames *hf; + hud_anim *ha; + int idx; + + // open localization + lcl_ext_open(); + + if ((rval = setjmp(parse_abort)) != 0) { + Error(LOCATION, "Unable to parse icons.tbl! Code = %i.\n", rval); + } + else { + read_file_text("icons.tbl"); + reset_parse(); + } + + num_icons = 0; + required_string("#Start"); + + + int load_this_icon = 0; + + while (required_string_either("#End","$Name:")) { + for(idx=0; idxfirst_frame = bm_load_animation(name, &hf->num_frames); + if ( hf->first_frame == -1 ) { + Int3(); // missing briefing icon + } + } + + // load in fade frames + required_string("$Name:"); + stuff_string(name, F_NAME, NULL); + ha = &Icon_fade_anims[num_icons][idx]; + hud_anim_init(ha, 0, 0, name); + + // load in highlighting frames + required_string("$Name:"); + stuff_string(name, F_NAME, NULL); + ha = &Icon_highlight_anims[num_icons][idx]; + hud_anim_init(ha, 0, 0, name); + } + + // next icon _type_ + num_icons++; + } + required_string("#End"); + + // close localization + lcl_ext_close(); +} + +// -------------------------------------------------------------------------------------- +// brief_close_map() +// +// +void brief_close_map() +{ + brief_unload_icons(); +} + +void brief_preload_highlight_anim(brief_icon *bi) +{ + hud_anim *ha; + int species = ship_get_species_by_type(bi->ship_class); + + if(species < 0){ + return; + } + + ha = &Icon_highlight_anims[bi->type][species]; + if ( !stricmp(NOX("none"), ha->name) ) { + return; + } + + // force read of data from disk, so we don't glitch on initial playback + if ( ha->first_frame == -1 ) { + hud_anim_load(ha); + Assert(ha->first_frame >= 0); + } + + bi->highlight_anim = *ha; + + gr_set_bitmap(ha->first_frame); + gr_aabitmap(0, 0); +} + +void brief_preload_fade_anim(brief_icon *bi) +{ + hud_anim *ha; + int species = ship_get_species_by_type(bi->ship_class); + + if(species < 0){ + return; + } + + ha = &Icon_fade_anims[bi->type][species]; + if ( !stricmp(NOX("none"), ha->name) ) { + return; + } + + // force read of data from disk, so we don't glitch on initial playback + if ( ha->first_frame == -1 ) { + hud_anim_load(ha); + Assert(ha->first_frame >= 0); + } + + gr_set_bitmap(ha->first_frame); + gr_aabitmap(0, 0); +} + +// preload highlight, fadein and fadeout animations that are used in this stage +void brief_preload_anims() +{ + int num_icons, num_stages, i, j; + brief_icon *bi; + + num_stages = Briefing->num_stages; + + for ( i = 0; i < num_stages; i++ ) { + num_icons = Briefing->stages[i].num_icons; + for ( j = 0; j < num_icons; j++ ) { + bi = &Briefing->stages[i].icons[j]; + if ( bi->flags & BI_HIGHLIGHT ) { + brief_preload_highlight_anim(bi); + } + brief_preload_fade_anim(bi); + } + } +} + +// -------------------------------------------------------------------------------------- +// brief_init_map() +// +// +void brief_init_map() +{ + vector *pos; + matrix *orient; + + Assert( Briefing != NULL ); + + pos = &Briefing->stages[0].camera_pos; + orient = &Briefing->stages[0].camera_orient; + vm_vec_zero(&Current_lookat_pos); + vm_vec_zero(&Target_lookat_pos); + Elapsed_time = 0.0f; + Total_move_time = 0.0f; + + The_grid = brief_create_default_grid(); + brief_maybe_create_new_grid(The_grid, pos, orient, 1); + + brief_init_anims(); + brief_init_icons(); + brief_move_icon_reset(); + brief_preload_anims(); + + Brief_text_wipe_snd = -1; + Last_new_stage = -1; + Num_fade_icons=0; +} + + +#pragma optimize("", off) + +// render fade-out anim frame +static int Fade_frame_count[128]; // for debug + +void brief_render_fade_outs(float frametime) +{ + int i,bx,by,w,h; + float bxf,byf; + vertex tv; // temp vertex used to find screen position for text + fade_icon *fi; + + + for (i=0; ipos); + + if (!(tv.flags & PF_PROJECTED)) + g3_project_vertex(&tv); + + if (!(tv.flags & PF_OVERFLOW) ) { // make sure point projected before drawing text + + brief_set_icon_color(fi->team); + + if ( fi->fade_anim.first_frame < 0 ) { + continue; + } + + bm_get_info( fi->fade_anim.first_frame, &w, &h, NULL); + + bxf = tv.sx - w / 2.0f + 0.5f; + byf = tv.sy - h / 2.0f + 0.5f; + bx = fl2i(bxf); + by = fl2i(byf); + + if ( fi->fade_anim.first_frame >= 0 ) { + fi->fade_anim.sx = bx; + fi->fade_anim.sy = by; + hud_anim_render(&fi->fade_anim, frametime, 1, 0, 0, 0); + } + } + } +} + +// figure out how far an icon should move based on the elapsed time +float brief_icon_get_dist_moved(icon_move_info *mi, float elapsed_time) +{ + float time, dist_moved=0.0f; + + // first half of movement + if ( elapsed_time < mi->total_move_time/2.0f ) { + dist_moved=0.5f*mi->accel*elapsed_time*elapsed_time; // d = 1/2at^2 + return dist_moved; + } + + // second half of movement + time=elapsed_time - mi->total_move_time/2.0f; + dist_moved=(mi->total_dist/2.0f)+(mi->peak_speed*time) - 0.5f*mi->accel*time*time; + return dist_moved; +} + +// Draw a line between two icons on the briefing screen +void brief_render_icon_line(int stage_num, int line_num) +{ + brief_line *bl; + brief_icon *icon[2]; + vertex icon_vertex[2]; + int icon_status[2] = {0,0}; + int icon_w, icon_h, i; + float icon_x[2], icon_y[2]; + + bl = &Briefing->stages[stage_num].lines[line_num]; + icon[0] = &Briefing->stages[stage_num].icons[bl->start_icon]; + icon[1] = &Briefing->stages[stage_num].icons[bl->end_icon]; + + // project icons + for (i=0; i<2; i++) { + g3_rotate_vertex(&icon_vertex[i],&icon[i]->pos); + if (!(icon_vertex[i].flags&PF_PROJECTED)) + g3_project_vertex(&icon_vertex[i]); + + if (!(icon_vertex[i].flags & PF_OVERFLOW) ) { // make sure point projected before drawing text + icon_status[i]=1; + } + } + + if ( !icon_status[0] || !icon_status[1] ) { + return; + } + + // get screen (x,y) for icons + for (i=0; i<2; i++) { + brief_common_get_icon_dimensions(&icon_w, &icon_h, icon[i]->type, icon[i]->ship_class); + icon_x[i] = icon_vertex[i].sx; + icon_y[i] = icon_vertex[i].sy; + } + + brief_set_icon_color(icon[0]->team); + + gr_line(fl2i(icon_x[0]), fl2i(icon_y[0]), fl2i(icon_x[1]), fl2i(icon_y[1])); +} + +// ------------------------------------------------------------------------------------- +// Draw a briefing icon +// +// parameters: stage_num => briefing stage number (start at 0) +// icon_num => icon number in stage +// frametime => time elapsed in seconds +// selected => FRED only (will be 0 or non-zero) +// w_scale_factor => scale icon in width by this amount (default 1.0f) +// h_scale_factor => scale icon in height by this amount (default 1.0f) +void brief_render_icon(int stage_num, int icon_num, float frametime, int selected, float w_scale_factor, float h_scale_factor) +{ + brief_icon *bi, *closeup_icon; + hud_frames *ib; + vertex tv; // temp vertex used to find screen position for text + vector *pos = NULL; + int bx,by,bc,w,h,icon_w,icon_h,icon_bitmap=-1; + float bxf, byf, dist=0.0f; + + Assert( Briefing != NULL ); + + bi = &Briefing->stages[stage_num].icons[icon_num]; + + icon_move_info *mi, *next; + int interp_pos_found = 0; + + mi = GET_FIRST(&Icon_move_list); + if (mi) + while ( mi != &Icon_move_list ) { + next = mi->next; + if ( ( mi->id != 0 ) && ( mi->id == bi->id ) ) { + + if ( !mi->reached_dest ) { + dist = brief_icon_get_dist_moved(mi, Elapsed_time); + if ( dist < mi->last_dist ) { + mi->reached_dest=1; + mi->last_dist=0.0f; + } + mi->last_dist=dist; + } + + if ( !mi->reached_dest ) { + vector dist_moved; + vm_vec_copy_scale(&dist_moved, &mi->direction, dist); + vm_vec_add(&mi->current, &mi->start, &dist_moved); + } else { + mi->current = mi->finish; + } + + pos = &mi->current; + interp_pos_found = 1; + break; + } + mi = next; + } + + if ( !interp_pos_found ) + pos = &bi->pos; + + brief_render_elements(pos, The_grid); + g3_rotate_vertex(&tv,pos); + + if (!(tv.flags&PF_PROJECTED)) + g3_project_vertex(&tv); + + if (!(tv.flags & PF_OVERFLOW) ) { // make sure point projected before drawing text + + brief_set_icon_color(bi->team); + + int species = ship_get_species_by_type(bi->ship_class); + + if(species < 0){ + return; + } + + ib = &Icon_bitmaps[bi->type][species]; + if ( ib->first_frame < 0 ) { + Int3(); + return; + } + + brief_common_get_icon_dimensions(&icon_w, &icon_h, bi->type, bi->ship_class); + + closeup_icon = (brief_icon*)brief_get_closeup_icon(); + if ( bi == closeup_icon || selected ) { + icon_bitmap=ib->first_frame+1; +// gr_set_bitmap(ib->first_frame+1); + } + else { + icon_bitmap=ib->first_frame; +// gr_set_bitmap(ib->first_frame); + } + + // draw icon centered at draw position +// bx = fl2i(tv.sx - ib->icon_w/2.0f); +// by = fl2i(tv.sy - ib->icon_h/2.0f); +// bc = bx + fl2i(ib->icon_w/2.0f); + //gr_aabitmap(bx, by); + + float scaled_w, scaled_h; + + scaled_w = icon_w * w_scale_factor; + scaled_h = icon_h * h_scale_factor; + bxf = tv.sx - scaled_w / 2.0f + 0.5f; + byf = tv.sy - scaled_h / 2.0f + 0.5f; + bx = fl2i(bxf); + by = fl2i(byf); + bc = fl2i(tv.sx); + + if ( (bx < 0) || (bx > gr_screen.max_w) || (by < 0) || (by > gr_screen.max_h) && !Fred_running ) { + bi->x = bx; + bi->y = by; + return; + } + + vertex va, vb; + va.sx = bxf; + va.sy = byf; + va.u = 0.0f; + va.v = 0.0f; + + vb.sx = bxf+scaled_w-1; + vb.sy = byf+scaled_h-1; + vb.u = 1.0f; + vb.v = 1.0f; + + // render highlight anim frame + if ( (bi->flags&BI_SHOWHIGHLIGHT) && (bi->flags&BI_HIGHLIGHT) ) { + hud_anim *ha = &bi->highlight_anim; + if ( ha->first_frame >= 0 ) { + ha->sx = bi->hold_x; + if ( strlen(bi->label) > 0 ) { + ha->sy = bi->hold_y - fl2i(gr_get_font_height()/2.0f +0.5) - 2; + } else { + ha->sy = bi->hold_y; + } + + //hud_set_iff_color(bi->team); + brief_set_icon_color(bi->team); + + hud_anim_render(ha, frametime, 1, 0, 1); + + if ( Brief_stage_highlight_sound_handle < 0 ) { + if ( !Fred_running) { + Brief_stage_highlight_sound_handle = snd_play(&Snds_iface[SND_ICON_HIGHLIGHT]); + } + } + } + } + + // render fade-in anim frame + if ( bi->flags & BI_FADEIN ) { + hud_anim *ha = &bi->fadein_anim; + if ( ha->first_frame >= 0 ) { + ha->sx = bx; + ha->sy = by; +// hud_set_iff_color(bi->team); + brief_set_icon_color(bi->team); + + if ( hud_anim_render(ha, frametime, 1, 0, 0, 1) == 0 ) { + bi->flags &= ~BI_FADEIN; + } + } else { + bi->flags &= ~BI_FADEIN; + } + } + + if ( !(bi->flags & BI_FADEIN) ) { + gr_set_bitmap(icon_bitmap); + + if ( Fred_running ) { + gr_aascaler(&va, &vb); + } else { + // Don't bother scaling for the game + gr_aabitmap(bx, by); + } + + // draw text centered over the icon (make text darker) + if ( bi->type == ICON_FIGHTER_PLAYER || bi->type == ICON_BOMBER_PLAYER ) { + gr_get_string_size(&w,&h,Players[Player_num].callsign); + gr_printf(bc - fl2i(w/2.0f), by - h, Players[Player_num].callsign); + } + else { + if (Lcl_gr) { + char buf[128]; + strcpy(buf, bi->label); + lcl_translate_brief_icon_name(buf); + gr_get_string_size(&w, &h, buf); + gr_printf(bc - fl2i(w/2.0f), by - h, buf); + } else { + gr_get_string_size(&w,&h,bi->label); + gr_printf(bc - fl2i(w/2.0f), by - h, bi->label); + } + } + + // show icon as selected (FRED only) + if ( selected ) { + gr_get_string_size(&w,&h,NOX("(S)")); + gr_printf(bc - fl2i(w/2.0f), by - h*2, NOX("(S)")); + } + } + + // store screen x,y,w,h + bi->x = bx; + bi->y = by; + bi->w = fl2i(scaled_w); + bi->h = fl2i(scaled_h); + + } // end if vertex is projected +} + +#pragma optimize("", on) + +// ------------------------------------------------------------------------------------- +// brief_render_icons() +// +void brief_render_icons(int stage_num, float frametime) +{ + int i, num_icons, num_lines; + + Assert( Briefing != NULL ); + + num_icons = Briefing->stages[stage_num].num_icons; + num_lines = Briefing->stages[stage_num].num_lines; + + if ( Cam_target_reached ) { + for ( i = 0; i < num_lines; i++ ) { + brief_render_icon_line(stage_num, i); + } + } + + for ( i = 0; i < num_icons; i++ ) { + brief_render_icon(stage_num, i, frametime, 0); + } +} + +// ------------------------------------------------------------------------------------ +// brief_start_highlight_anims() +// +// see if there are any highlight animations to play +// +void brief_start_highlight_anims(int stage_num) +{ + brief_stage *bs; + brief_icon *bi; + int x,y,i,anim_w,anim_h; + + Assert( Briefing != NULL ); + bs = &Briefing->stages[stage_num]; + + for ( i = 0; i < bs->num_icons; i++ ) { + bi = &bs->icons[i]; + if ( bi->flags & BI_HIGHLIGHT ) { + bi->flags &= ~BI_SHOWHIGHLIGHT; + if ( bi->highlight_anim.first_frame < 0 ) { + continue; + } + + bi->highlight_anim.time_elapsed=0.0f; + + bm_get_info( bi->highlight_anim.first_frame, &anim_w, &anim_h, NULL); + x = fl2i( i2fl(bi->x) + bi->w/2.0f - anim_w/2.0f ); + y = fl2i( i2fl(bi->y) + bi->h/2.0f - anim_h/2.0f ); + bi->hold_x = x; + bi->hold_y = y; + bi->flags |= BI_SHOWHIGHLIGHT; + bi->highlight_anim.time_elapsed=0.0f; + } + } +} + +// ------------------------------------------------------------------------------------- +// brief_render_map() +// +// +void brief_render_map(int stage_num, float frametime) +{ + brief_stage *bs; + + gr_set_clip(bscreen.map_x1 + 1, bscreen.map_y1 + 1, bscreen.map_x2 - bscreen.map_x1 - 1, bscreen.map_y2 - bscreen.map_y1 - 2); + + // REMOVED by neilk: removed gr_clear for FS2 because interface no longer calls for black background on grid + // gr_clear(); + + if (stage_num >= Briefing->num_stages) { + gr_reset_clip(); + return; + } + + Assert(Briefing); + bs = &Briefing->stages[stage_num]; + + g3_start_frame(0); + g3_set_view_matrix(&Current_cam_pos, &Current_cam_orient, 0.5f); + + brief_maybe_create_new_grid(The_grid, &Current_cam_pos, &Current_cam_orient); + brief_render_grid(The_grid); + + brief_render_fade_outs(frametime); + + // go ahead and render everything that is in the active objects list + brief_render_icons(stage_num, frametime); + + if ( Cam_target_reached && brief_text_wipe_finished() ) { + + if ( Brief_textdraw_finished == 0 ) { + Brief_textdraw_finished = 1; + Brief_stage_time = 0; + } + + if ( Play_highlight_flag ) { + brief_start_highlight_anims(stage_num); + Play_highlight_flag = 0; + } + } + + anim_render_all(ON_BRIEFING_SELECT, frametime); + + gr_reset_clip(); + g3_end_frame(); +} + + +// ------------------------------------------------------------------------------------- +// brief_text_set_color() +// +void brief_text_set_color(char c) { + switch ( c ) { + case 'f': + SET_COLOR_FRIENDLY; + break; + case 'h': + SET_COLOR_HOSTILE; + break; + case 'n': + SET_COLOR_NEUTRAL; + break; + case 'r': + gr_set_color_fast(&Color_red); + break; + case 'g': + gr_set_color_fast(&Color_green); + break; + case 'b': + gr_set_color_fast(&Color_blue); + break; + default: + Int3(); // unsupported meta-code + break; + } // end switch +} + +// Display what stage of the briefing is active +void brief_blit_stage_num(int stage_num, int stage_max) +{ + char buf[64]; + // int w; + + Assert( Briefing != NULL ); + gr_set_color_fast(&Color_text_heading); + sprintf(buf, XSTR( "Stage %d of %d", 394), stage_num + 1, stage_max); + if (Game_mode & GM_MULTIPLAYER) { + gr_printf(Brief_stage_text_coords_multi[gr_screen.res][0], Brief_stage_text_coords_multi[gr_screen.res][1], buf); + } else { + gr_printf(Brief_stage_text_coords[gr_screen.res][0], Brief_stage_text_coords[gr_screen.res][1], buf); + } + + // draw the title of the mission + // if this goes above briefing text, it will need to be raised 10 pixels in multiplayer to make + // room for stage num, which makes toom for chat box + /* + if (Game_mode & GM_MULTIPLAYER) { + gr_get_string_size(&w,NULL,The_mission.name); + gr_string(bscreen.map_x2 - w, bscreen.map_y2 + 5, The_mission.name); + } else { + gr_get_string_size(&w,NULL,The_mission.name); + gr_string(bscreen.map_x2 - w, bscreen.map_y2 + 5, The_mission.name); + } + */ +} + +// Render a line of text for the briefings. Lines are drawn in as a wipe, with leading bright +// white characters. Have to jump through some hoops since we support colored words. This means +// that we need to process the line one character at a time. +void brief_render_line(int line_num, int x, int y, int instance) +{ + int len, count, next, truncate_len, last_color, offset, w, h, bright_len, i; + colored_char *src; + char line[MAX_BRIEF_LINE_LEN]; + + src = &Colored_text[instance][line_num][0]; + len = Colored_text_len[instance][line_num]; + + if (len <= 0){ + return; + } + + truncate_len = fl2i(Brief_text_wipe_time_elapsed / BRIEF_TEXT_WIPE_TIME * Max_briefing_line_len); + if (truncate_len > len){ + truncate_len = len; + } + + bright_len = 0; + if (truncate_len < len) { + if (truncate_len <= BRIGHTEN_LEAD) { + bright_len = truncate_len; + truncate_len = 0; + + } else { + bright_len = BRIGHTEN_LEAD; + truncate_len -= BRIGHTEN_LEAD; + } + } + + offset = 0; + count = 0; + next = 0; + + gr_set_color_fast(&Color_white); + last_color = BRIEF_TEXT_WHITE; + for (i=0; i= truncate_len){ + break; + } + + line[next] = src[count].letter; + + if (is_white_space(line[next])) { + // end of word reached, blit it + line[next + 1] = 0; + gr_string(x + offset, y, line); + gr_get_string_size(&w, &h, line); + offset += w; + next = 0; + + // reset color + if (last_color != BRIEF_TEXT_WHITE) { + brief_set_text_color(BRIEF_TEXT_WHITE); + last_color = BRIEF_TEXT_WHITE; + } + + count++; + continue; + } + + if (src[count].color != last_color) { + brief_set_text_color(src[count].color); + last_color = src[count].color; + } + + count++; + next++; + } // end for + + line[next] = 0; + gr_string(x + offset, y, line); + + + // draw leading portion of the line bright white + if (bright_len) { + + gr_set_color_fast(&Color_bright_white); + for (i=0; i 0 ) { + int width_dim, height_dim; + gr_get_string_size(&width_dim, &height_dim, line, truncate_len ); + gr_string(x+width_dim, y, &line[truncate_len]); + } else { + gr_string(x, y, line); + } + + // JAS: Not needed? + // // now erase the part we don't want to be bright white + // gr_set_color_fast(&Color_black); + // if (i > BRIGHTEN_LEAD) { + // line[i - BRIGHTEN_LEAD] = 0; + // gr_get_string_size(&w, &h, line); + // gr_set_clip(x, y, w, gr_get_font_height()); + // gr_clear(); + // gr_reset_clip(); + // } + } +} + +int brief_text_wipe_finished() +{ + if ( Brief_text_wipe_time_elapsed > (BRIEF_TEXT_WIPE_TIME+0.5f) ) { + return 1; + } + + return 0; +} + +// ------------------------------------------------------------------------------------- +// brief_render_text() +// +// input: frametime => Time in seconds of previous frame +// instance => Optional parameter. Used to indicate which text stream is used. +// This value is 0 unless multiple text streams are required +int brief_render_text(int line_offset, int x, int y, int h, float frametime, int instance, int line_spacing) +{ + int fh, line, yy; + + fh = gr_get_font_height(); + if (Brief_text_wipe_time_elapsed == 0) { + if (snd_is_playing(Brief_text_wipe_snd)) { + snd_stop(Brief_text_wipe_snd); + } + gamesnd_play_iface(SND_BRIEF_TEXT_WIPE); + Play_brief_voice = 1; + } + + Brief_text_wipe_time_elapsed += frametime; + + line = line_offset; + yy = 0; + while (yy + fh <= h) { + if (line >= Num_brief_text_lines[instance]) + break; + + brief_render_line(line, x, y + yy, instance); + + line++; + yy += fh + line_spacing; + } + + if ( brief_text_wipe_finished() && (Play_brief_voice) ) { + Play_brief_voice = 0; + return 1; + } + + return 0; +} + +// ------------------------------------------------------------------------------------ +// brief_render_elements() +// +// Draw the lines that show objects positions on the grid +// +void brief_render_elements(vector *pos, grid* gridp) +{ + vector gpos; // Location of point on grid. +// vector tpos; + float dxz; + plane tplane; + vector *gv; + + if ( pos->y < 1 && pos->y > -1 ) + return; + + tplane.A = gridp->gmatrix.uvec.x; + tplane.B = gridp->gmatrix.uvec.y; + tplane.C = gridp->gmatrix.uvec.z; + tplane.D = gridp->planeD; + + compute_point_on_plane(&gpos, &tplane, pos); + + dxz = vm_vec_dist(pos, &gpos)/8.0f; + + gv = &gridp->gmatrix.uvec; + if (gv->x * pos->x + gv->y * pos->y + gv->z * pos->z < -gridp->planeD) + gr_set_color(127, 127, 127); + else + gr_set_color(255, 255, 255); // white + +// AL 11-20-97: don't draw elevation lines.. they are confusing +/* + brief_rpd_line(&gpos, pos); // Line from grid to object center. + + tpos = gpos; + + vm_vec_scale_add2(&gpos, &gridp->gmatrix.rvec, -dxz/2); + vm_vec_scale_add2(&gpos, &gridp->gmatrix.fvec, -dxz/2); + + vm_vec_scale_add2(&tpos, &gridp->gmatrix.rvec, dxz/2); + vm_vec_scale_add2(&tpos, &gridp->gmatrix.fvec, dxz/2); + + brief_rpd_line(&gpos, &tpos); + + vm_vec_scale_add2(&gpos, &gridp->gmatrix.rvec, dxz); + vm_vec_scale_add2(&tpos, &gridp->gmatrix.rvec, -dxz); + + brief_rpd_line(&gpos, &tpos); +*/ +} + + +// ------------------------------------------------------------------------------------ +// brief_reset_icons() +// +void brief_reset_icons(int stage_num) +{ + brief_stage *bs; + brief_icon *bi; + int i; + + Assert( Briefing != NULL ); + bs = &Briefing->stages[stage_num]; + + for ( i = 0; i < bs->num_icons; i++ ) { + bi = &bs->icons[i]; + bi->flags &= ~BI_SHOWHIGHLIGHT; + } +} + +// ------------------------------------------------------------------------------------ +// brief_set_camera_target() +// +// input: pos => target position for the camera +// orient => target orientation for the camera +// time => time in ms to reach target +// +void brief_set_camera_target(vector *pos, matrix *orient, int time) +{ + float time_in_seconds; + + time_in_seconds = time / 1000.0f; + + if ( time == 0 ) { + Current_cam_pos = *pos; + Current_cam_orient = *orient; + } + + Target_cam_pos = *pos; + Target_cam_orient = *orient; + Start_cam_orient = Current_cam_orient; + Start_cam_pos = Current_cam_pos; // we need this when checking if camera movement complete + Start_cam_move = timer_get_milliseconds()*1000.0f; // start time, convert to seconds + Total_move_time = time_in_seconds; + Elapsed_time = 0.0f; + + vm_vec_scale_add(&Start_lookat_pos, &Start_cam_pos, &Start_cam_orient.fvec, LOOKAT_DIST); + vm_vec_scale_add(&Target_lookat_pos, &Target_cam_pos, &Target_cam_orient.fvec, LOOKAT_DIST); + + Play_highlight_flag = 1; // once target reached, play highlight anims + Cam_target_reached = 0; + Cam_movement_done=0; + anim_release_all_instances(ON_BRIEFING_SELECT); // stop any briefing-specific anims + + // calculate camera velocity + vm_vec_sub(&Cam_vel, pos, &Current_cam_pos); +// vm_vec_scale(&Cam_vel, 1.0f/time_in_seconds); + if ( !IS_VEC_NULL(&Cam_vel) ) { + vm_vec_normalize(&Cam_vel); + } + + // calculate lookat point velocity + vm_vec_sub(&Lookat_vel, &Target_lookat_pos, &Current_lookat_pos); + vm_vec_scale(&Lookat_vel, 1.0f/time_in_seconds); + + Start_dist = vm_vec_dist(&Start_cam_pos, &Start_lookat_pos); + End_dist = vm_vec_dist(&Target_cam_pos, &Target_lookat_pos); + Dist_change_rate = (End_dist - Start_dist) / time_in_seconds; + + Total_dist=vm_vec_dist(&Start_cam_pos, &Target_cam_pos); + +// Peak_speed=Total_dist/Total_move_time*1.5f; +// Cam_accel = Peak_speed/Total_move_time*3.0f; + + Peak_speed=Total_dist/Total_move_time*2.0f; + Cam_accel = 4*Total_dist/(Total_move_time*Total_move_time); + Last_dist=0.0f; + + vm_vec_zero(&W_init); + + get_camera_limits(&Start_cam_orient, &Target_cam_orient, Total_move_time, &Acc_limit, &Vel_limit); +} + + +ubyte brief_return_color_index(char c) +{ + switch (c) { + case 'f': + return BRIEF_IFF_FRIENDLY; + + case 'h': + return BRIEF_IFF_HOSTILE; + + case 'n': + return BRIEF_IFF_NEUTRAL; + + case 'r': + return BRIEF_TEXT_RED; + + case 'g': + return BRIEF_TEXT_GREEN; + + case 'b': + return BRIEF_TEXT_BLUE; + + default: + Int3(); // unsupported meta-code + break; + } // end switch + + return BRIEF_TEXT_WHITE; +} + +void brief_set_text_color(int color_index) +{ + Assert(color_index < MAX_BRIEF_TEXT_COLORS); + gr_set_color_fast(Brief_text_colors[color_index]); +} + +// Set up the Colored_text array. +// input: index => Index into Brief_text[] for source text. +// instance => Which instance of Colored_text[] to use. +// Value is 0 unless multiple text streams are required. +int brief_text_colorize(int index, int instance) +{ + char *src; + int len, i, skip_to_next_word, dest_len; + colored_char *dest; + ubyte active_color_index; + + src = Brief_text[index]; + dest = &Colored_text[instance][index][0]; + len = strlen(src); + + skip_to_next_word = 0; + dest_len = 0; + active_color_index = BRIEF_TEXT_WHITE; + for (i=0; i paragraph of text to process +// w => max width of line in pixels +// instance => optional parameter, used when multiple text streams are required +// (default value is 0) +int brief_color_text_init(char *src, int w, int instance) +{ + int i, n_lines, len; + int n_chars[MAX_BRIEF_LINES]; + char *p_str[MAX_BRIEF_LINES]; + + Assert(src); + n_lines = split_str(src, w, n_chars, p_str, MAX_BRIEF_LINES, BRIEF_META_CHAR); + Assert(n_lines >= 0); + + Max_briefing_line_len = 1; + for (i=0; i Max_briefing_line_len) + Max_briefing_line_len = len; + } + + Brief_text_wipe_time_elapsed = 0.0f; + Play_brief_voice = 0; + + Num_brief_text_lines[instance] = n_lines; + return n_lines; +} + +// ------------------------------------------------------------------------------------ +// brief_get_free_move_icon() +// +// returns: failure => -1 +// success => handle to a free move icon struct +// +int brief_get_free_move_icon() +{ + int i; + + for ( i = 0; i < MAX_MOVE_ICONS; i++ ) { + if ( Icon_movers[i].used == 0 ) + break; + } + + if ( i == MAX_MOVE_ICONS ) + return -1; + + Icon_movers[i].used = 1; + return i; +} + + +// ------------------------------------------------------------------------------------ +// brief_set_move_list() +// +// input: new_stage => new stage number that briefing is now moving to +// current_stage => current stage that the briefing is on +// time => time in seconds +// +int brief_set_move_list(int new_stage, int current_stage, float time) +{ + brief_stage *newb, *cb; + icon_move_info *imi; + int i,j,k,num_movers,is_gone=0; + + Assert(new_stage != current_stage); + + Assert( Briefing != NULL ); + newb = &Briefing->stages[new_stage]; + cb = &Briefing->stages[current_stage]; + num_movers = 0; + + for ( i = 0; i < cb->num_icons; i++ ) { + is_gone=1; + for ( j = 0; j < newb->num_icons; j++ ) { + if ( ( cb->icons[i].id != 0 ) && ( cb->icons[i].id == newb->icons[j].id ) ) { + is_gone=0; + if ( vm_vec_cmp(&cb->icons[i].pos, &newb->icons[j].pos) ) { + //nprintf(("Alan","We found a match in icon %s\n", cb->icons[i].label)); + k = brief_get_free_move_icon(); + if ( k == -1 ) { + Int3(); // should never happen, get Alan + return 0; + } + imi = &Icon_movers[k]; + imi->id = cb->icons[i].id; + imi->start = cb->icons[i].pos; + imi->finish = newb->icons[j].pos; + imi->current = imi->start; + list_append(&Icon_move_list, imi); + + imi->total_dist = vm_vec_dist(&imi->start, &imi->finish); + imi->total_move_time = time; + imi->peak_speed = imi->total_dist/imi->total_move_time*2.0f; + imi->accel = 4*imi->total_dist/(time*time); + imi->last_dist=0.0f; + imi->reached_dest=0; + imi->direction; + + vm_vec_sub(&imi->direction, &imi->finish, &imi->start); + if ( !IS_VEC_NULL(&imi->direction) ) { + vm_vec_normalize(&imi->direction); + } + + num_movers++; + } + } + } + + // Set up fading icon (to fade out) + if (is_gone == 1) { + if ( Num_fade_icons >= MAX_FADE_ICONS ) { + Int3(); + Num_fade_icons=0; + } + + int species = ship_get_species_by_type(cb->icons[i].ship_class); + if(species < 0) { + return 0; + } + + Fading_icons[Num_fade_icons].fade_anim = Icon_fade_anims[cb->icons[i].type][species]; + Fading_icons[Num_fade_icons].pos = cb->icons[i].pos; + Fading_icons[Num_fade_icons].team = cb->icons[i].team; + Num_fade_icons++; + } + } + + // flag new icons for fading in + for ( i=0; inum_icons; i++ ) { + int is_new = 1; + newb->icons[i].flags &= ~BI_FADEIN; + for ( j=0; jnum_icons; j++ ) { + if ( ( cb->icons[j].id != 0 ) && ( cb->icons[j].id == newb->icons[i].id ) ) { + is_new=0; + } + } + if ( is_new ) { + int species = ship_get_species_by_type(newb->icons[i].ship_class); + if(species < 0) { + return 0; + } + + newb->icons[i].flags |= BI_FADEIN; + newb->icons[i].fadein_anim = Icon_fade_anims[newb->icons[i].type][species]; + newb->icons[i].fadein_anim.time_elapsed = 0.0f; + } + } + + return num_movers; +} + +void brief_clear_fade_out_icons() +{ + Num_fade_icons = 0; +} + + +// ------------------------------------------------------------------------------------ +// brief_set_new_stage() +// +// input: pos => target position for the camera +// orient => target orientation for the camera +// time => time in ms to reach target +// stage_num => stage number of briefing (start numbering at 0) +// + +void brief_set_new_stage(vector *pos, matrix *orient, int time, int stage_num) +{ + char msg[MAX_BRIEF_LEN]; + int num_movers, new_time, not_objv = 1; + + Assert( Briefing != NULL ); + new_time = time; + + if (stage_num >= Briefing->num_stages) { + not_objv = 0; // turns out this is an objectives stage + new_time = 0; + } + + if ( stage_num == Last_new_stage ) { + return; + } + + num_movers = 0; + brief_move_icon_reset(); + brief_clear_fade_out_icons(); + if ( (Last_new_stage != -1) && not_objv ) { + num_movers = brief_set_move_list(stage_num, Last_new_stage, new_time / 1000.0f); + } + + if ( (Last_new_stage != -1) && (num_movers == 0) && not_objv ) { + if ( !vm_vec_cmp( &Briefing->stages[stage_num].camera_pos, &Briefing->stages[Last_new_stage].camera_pos) ) { + if ( !vm_vec_cmp( &Briefing->stages[stage_num].camera_orient.fvec, &Briefing->stages[Last_new_stage].camera_orient.fvec) ){ + new_time = 0; + } + } + } + + if (not_objv) { + if(Briefing->stages[stage_num].new_text == NULL){ + strcpy(msg, ""); + } else { + strcpy(msg, Briefing->stages[stage_num].new_text); + } + } else { + strcpy(msg, XSTR( "Please review your objectives for this mission.", 395)); + } + + if (gr_screen.res == GR_640) { + // GR_640 + Num_brief_text_lines[0] = brief_color_text_init(msg, MAX_BRIEF_LINE_W_640); + } else { + // GR_1024 + Num_brief_text_lines[0] = brief_color_text_init(msg, MAX_BRIEF_LINE_W_1024); + } + Top_brief_text_line = 0; + + if (not_objv){ + brief_set_camera_target(pos, orient, new_time); + } + + if ( snd_is_playing(Brief_stage_highlight_sound_handle) ) { + snd_stop(Brief_stage_highlight_sound_handle); + } + + Brief_voice_ended = 0; + Brief_textdraw_finished = 0; + Brief_voice_started = 0; + Brief_stage_time = 0; + + + Brief_stage_highlight_sound_handle = -1; + Last_new_stage = stage_num; +} + +// ------------------------------------------------------------------------------------ +// camera_pos_past_target() +// +// +int camera_pos_past_target(vector *start, vector *current, vector *dest) +{ + vector num, den; + float ratio; + + vm_vec_sub(&num, current, start); + vm_vec_sub(&den, start, dest); + + ratio = vm_vec_mag_quick(&num) / vm_vec_mag_quick(&den); + if (ratio >= 1.0f) + return TRUE; + + return FALSE; +} + +// ------------------------------------------------------------------------------------ +// Interpolate between matrices. +// elapsed_time/total_time gives percentage of interpolation between cur +// and goal. +void interpolate_matrix(matrix *result, matrix *goal, matrix *start, float elapsed_time, float total_time) +{ + vector fvec, rvec; + float time0, time1; + + if ( !vm_matrix_cmp( goal, start ) ) { + return; + } + + time0 = elapsed_time / total_time; + time1 = (total_time - elapsed_time) / total_time; + + vm_vec_copy_scale(&fvec, &start->fvec, time1); + vm_vec_scale_add2(&fvec, &goal->fvec, time0); + + vm_vec_copy_scale(&rvec, &start->rvec, time1); + vm_vec_scale_add2(&rvec, &goal->rvec, time0); + + vm_vector_2_matrix(result, &fvec, NULL, &rvec); + } + +// calculate how far the camera should have moved +float brief_camera_get_dist_moved(float elapsed_time) +{ + float time, dist_moved=0.0f; + + // first half of movement + if ( elapsed_time < Total_move_time/2.0f ) { + dist_moved=0.5f*Cam_accel*elapsed_time*elapsed_time; // d = 1/2at^2 + return dist_moved; + } + + // second half of movement + time=elapsed_time - Total_move_time/2.0f; + dist_moved=(Total_dist/2.0f)+(Peak_speed*time) - 0.5f*Cam_accel*time*time; + return dist_moved; + +} + +// ------------------------------------------------------------------------------------ +// Update the camera position +void brief_camera_move(float frametime, int stage_num) +{ + vector dist_moved; + float dist; + vector w_out; + matrix result; + + Elapsed_time += frametime; + + if ( Cam_target_reached ) { +// Current_cam_pos = Target_cam_pos; +// Current_lookat_pos = Target_lookat_pos; +// Current_cam_orient = Target_cam_orient; + return; + } + + // Update orientation + if ( (Elapsed_time < Total_move_time) ) { +// interpolate_matrix(&Current_cam_orient, &Target_cam_orient, &Start_cam_orient, Elapsed_time, Total_move_time ); + vm_matrix_interpolate(&Target_cam_orient, &Current_cam_orient, &W_init, frametime, &result, &w_out, &Vel_limit, &Acc_limit); + Current_cam_orient = result; + W_init = w_out; + } + + /* + // interpolate lookat position + if ( vm_vec_cmp( &Current_lookat_pos, &Target_lookat_pos ) ) { + vm_vec_copy_scale(&dist_moved, &Lookat_vel, Elapsed_time); + vm_vec_add(&Current_lookat_pos, &Start_lookat_pos, &dist_moved); + + if ( camera_pos_past_target(&Start_lookat_pos, &Current_lookat_pos, &Target_lookat_pos) ) { + Current_lookat_pos = Target_lookat_pos; + } + } + + cur_dist = Start_dist + Dist_change_rate * Elapsed_time; + vm_vec_copy_scale(&dist_moved, &Current_cam_orient.fvec, -cur_dist); + vm_vec_add(&Current_cam_pos, &Current_lookat_pos, &dist_moved); + */ + + // use absolute pos to update position + if ( vm_vec_cmp( &Current_cam_pos, &Target_cam_pos ) ) { + dist = brief_camera_get_dist_moved(Elapsed_time); + if ( dist < Last_dist ) { + Cam_movement_done=1; + Last_dist=0.0f; + } + Last_dist=dist; + + if ( Cam_movement_done == 0 ) { + vm_vec_copy_scale(&dist_moved, &Cam_vel, dist); + vm_vec_add(&Current_cam_pos, &Start_cam_pos, &dist_moved); + } else { + Current_cam_pos=Target_cam_pos; + } + } + else { + Cam_movement_done=1; + Current_cam_pos=Target_cam_pos; + } + + if ( Cam_movement_done && (Elapsed_time >= Total_move_time) ) { + Cam_target_reached=1; + } +} + +// Project the viewer's position onto the grid plane. If more than threshold distance +// from grid center, move grid center. +void brief_maybe_create_new_grid(grid* gridp, vector *pos, matrix *orient, int force) +{ + int roundoff; + plane tplane; + vector gpos, tmp, c; + float dist_to_plane; + float square_size, ux, uy, uz; + + ux = tplane.A = gridp->gmatrix.uvec.x; + uy = tplane.B = gridp->gmatrix.uvec.y; + uz = tplane.C = gridp->gmatrix.uvec.z; + tplane.D = gridp->planeD; + + compute_point_on_plane(&c, &tplane, pos); + dist_to_plane = fl_abs(vm_dist_to_plane(pos, &gridp->gmatrix.uvec, &c)); + square_size = 1.0f; + + while (dist_to_plane >= 25.0f) + { + square_size *= 10.0f; + dist_to_plane /= 10.0f; + } + + if (fvi_ray_plane(&gpos, &gridp->center, &gridp->gmatrix.uvec, pos, &orient->fvec, 0.0f)<0.0f) { + vector p; + vm_vec_scale_add(&p,pos,&orient->fvec, 100.0f ); + compute_point_on_plane(&gpos, &tplane, &p ); + } + + if (vm_vec_dist(&gpos, &c) > 50.0f * square_size) + { + vm_vec_sub(&tmp, &gpos, &c); + vm_vec_normalize(&tmp); + vm_vec_scale_add(&gpos, &c, &tmp, 50.0f * square_size); + } + + roundoff = (int) square_size * 10; + if (!ux) + gpos.x = fl_roundoff(gpos.x, roundoff); + if (!uy) + gpos.y = fl_roundoff(gpos.y, roundoff); + if (!uz) + gpos.z = fl_roundoff(gpos.z, roundoff); + + if ((square_size != gridp->square_size) || + (gpos.x != gridp->center.x) || + (gpos.y != gridp->center.y) || + (gpos.z != gridp->center.z) || force) + { + gridp->square_size = square_size; + gridp->center = gpos; + brief_modify_grid(gridp); + } +} + +// Create a grid +// *forward is vector pointing forward +// *right is vector pointing right +// *center is center point of grid +// length is length of grid +// width is width of grid +// square_size is size of a grid square +// For example: +// *forward = (0.0, 0.0, 1.0) +// *right = (1.0, 0.0, 0.0) +// *center = (0.0, 0.0, 0.0) +// nrows = 10 +// ncols = 50.0 +// square_size = 10.0 +// will generate a grid of squares 10 long by 5 wide. +// Each grid square will be 10.0 x 10.0 units. +// The center of the grid will be at the global origin. +// The grid will be parallel to the xz plane (because the normal is 0,1,0). +// (In fact, it will be the xz plane because it is centered on the origin.) +// +// Stuffs grid in *gridp. If gridp == NULL, mallocs and returns a grid. +grid *brief_create_grid(grid *gridp, vector *forward, vector *right, vector *center, int nrows, int ncols, float square_size) +{ + int i, ncols2, nrows2, d = 1; + vector dfvec, drvec, cur, cur2, tvec, uvec, save, save2; + + Assert(square_size > 0.0); + if (double_fine_gridlines) + d = 2; + + if (gridp == NULL) + gridp = (grid *) malloc(sizeof(grid)); + + Assert(gridp); + + gridp->center = *center; + gridp->square_size = square_size; + + // Create the plane equation. + Assert(!IS_VEC_NULL(forward)); + Assert(!IS_VEC_NULL(right)); + + vm_vec_copy_normalize(&dfvec, forward); + vm_vec_copy_normalize(&drvec, right); + + vm_vec_cross(&uvec, &dfvec, &drvec); + + Assert(!IS_VEC_NULL(&uvec)); + + gridp->gmatrix.uvec = uvec; + + gridp->planeD = -(center->x * uvec.x + center->y * uvec.y + center->z * uvec.z); + Assert(!_isnan(gridp->planeD)); + + gridp->gmatrix.fvec = dfvec; + gridp->gmatrix.rvec = drvec; + + vm_vec_scale(&dfvec, square_size); + vm_vec_scale(&drvec, square_size); + + vm_vec_scale_add(&cur, center, &dfvec, (float) -nrows * d / 2); + vm_vec_scale_add2(&cur, &drvec, (float) -ncols * d / 2); + vm_vec_scale_add(&cur2, center, &dfvec, (float) -nrows * 5 / 2); + vm_vec_scale_add2(&cur2, &drvec, (float) -ncols * 5 / 2); + save = cur; + save2 = cur2; + + gridp->ncols = ncols; + gridp->nrows = nrows; + ncols2 = ncols / 2; + nrows2 = nrows / 2; + Assert(ncols < MAX_GRIDLINE_POINTS && nrows < MAX_GRIDLINE_POINTS); + + // Create the points along the edges of the grid, so we can just draw lines + // between them to form the grid. + for (i=0; i<=ncols*d; i++) { + gridp->gpoints1[i] = cur; // small, dark gridline points + vm_vec_scale_add(&tvec, &cur, &dfvec, (float) nrows * d); + gridp->gpoints2[i] = tvec; + vm_vec_add2(&cur, &drvec); + } + + for (i=0; i<=ncols2; i++) { + gridp->gpoints5[i] = cur2; // large, brighter gridline points + vm_vec_scale_add(&tvec, &cur2, &dfvec, (float) nrows2 * 10); + gridp->gpoints6[i] = tvec; + vm_vec_scale_add2(&cur2, &drvec, 10.0f); + } + + cur = save; + cur2 = save2; + for (i=0; i<=nrows*d; i++) { + gridp->gpoints3[i] = cur; // small, dark gridline points + vm_vec_scale_add(&tvec, &cur, &drvec, (float) ncols * d); + gridp->gpoints4[i] = tvec; + vm_vec_add2(&cur, &dfvec); + } + + for (i=0; i<=nrows2; i++) { + gridp->gpoints7[i] = cur2; // large, brighter gridline points + vm_vec_scale_add(&tvec, &cur2, &drvec, (float) ncols2 * 10); + gridp->gpoints8[i] = tvec; + vm_vec_scale_add2(&cur2, &dfvec, 10.0f); + } + + return gridp; +} + +// Create a nice grid -- centered at origin, 10x10, 10.0 size squares, in xz plane. +grid *brief_create_default_grid(void) +{ + grid *rgrid; + vector fvec, rvec, cvec; + + rgrid = brief_create_grid(&Global_grid, vm_vec_make(&fvec, 0.0f, 0.0f, 1.0f), + vm_vec_make(&rvec, 1.0f, 0.0f, 0.0f), + vm_vec_make(&cvec, 0.0f, -10.0f, 0.0f), 100, 100, 5.0f); + + physics_init(&rgrid->physics); + rgrid->physics.flags |= (PF_ACCELERATES | PF_SLIDE_ENABLED); + return rgrid; +} + +// Rotate and project points and draw a line. +void brief_rpd_line(vector *v0, vector *v1) +{ + vertex tv0, tv1; + g3_rotate_vertex(&tv0, v0); + g3_rotate_vertex(&tv1, v1); + +/* + g3_project_vertex(&tv0); + g3_project_vertex(&tv1); + + if ( (tv0.flags & PF_OVERFLOW) || (tv1.flags & PF_OVERFLOW) ) + return; +*/ + + gr_set_color_fast(&Color_grey); + g3_draw_line(&tv0, &tv1); +} + +// Renders a grid defined in a grid struct +void brief_render_grid(grid *gridp) +{ + int i, ncols, nrows; + + ncols = gridp->ncols; + nrows = gridp->nrows; + if (double_fine_gridlines) + { + ncols *= 2; + nrows *= 2; + } + + gr_set_color(30,30,30); +// SET_DARK; + + // Draw the column lines. + for (i=0; i<=ncols; i++) + brief_rpd_line(&gridp->gpoints1[i], &gridp->gpoints2[i]); + + // Draw the row lines. + for (i=0; i<=nrows; i++) + brief_rpd_line(&gridp->gpoints3[i], &gridp->gpoints4[i]); + + ncols = gridp->ncols / 2; + nrows = gridp->nrows / 2; + + // now draw the larger, brighter gridlines that is x10 the scale of smaller one. +// SET_MEDIUM; +/* + for (i=0; i<=ncols; i++) + brief_rpd_line(&gridp->gpoints5[i], &gridp->gpoints6[i]); + + for (i=0; i<=nrows; i++) + brief_rpd_line(&gridp->gpoints7[i], &gridp->gpoints8[i]); +*/ +} + +void brief_modify_grid(grid *gridp) +{ + brief_create_grid(gridp, &gridp->gmatrix.fvec, &gridp->gmatrix.rvec, &gridp->center, + gridp->nrows, gridp->ncols, gridp->square_size); +} + +void brief_unload_anims() +{ + int i, idx; + + for (i=0; i 5 ) { + break; + } + + Brief_voices[voice_num] = audiostream_open( name, ASF_VOICE ); + if ( Brief_voices[voice_num] >= 0 ) { + break; + } + + // Don't bother to ask for the CD in multiplayer + if ( Game_mode & GM_MULTIPLAYER ) { + break; + } + + // couldn't load animation, ask user to insert CD (if necessary) + // if ( Brief_voice_ask_for_cd ) { + // if ( game_do_cd_check() == 0 ) { + // Brief_voice_ask_for_cd = 0; + // break; + // } + // } + } +} + +// open and pre-load the stream buffers for the different voice streams +void brief_voice_load_all() +{ + int i; + brief_stage *bs; + + // Brief_voice_ask_for_cd = 1; + + Assert( Briefing != NULL ); + for ( i = 0; i < Briefing->num_stages; i++ ) { + bs = &Briefing->stages[i]; + if ( strnicmp(bs->voice, NOX("none"), 4) ) { + brief_load_voice_file(i, bs->voice); +// Brief_voices[i] = audiostream_open( bs->voice, ASF_VOICE ); + } + } +} + +// close all the briefing voice streams +void brief_voice_unload_all() +{ + int i; + + for ( i = 0; i < MAX_BRIEF_STAGES; i++ ) { + if ( Brief_voices[i] != -1 ) { + audiostream_close_file(Brief_voices[i], 0); + Brief_voices[i] = -1; + } + } +} + +// start playback of the voice for a particular briefing stage +void brief_voice_play(int stage_num) +{ + if ( Brief_voices[stage_num] == -1 ) + return; // voice file doesn't exist + + if ( !Briefing_voice_enabled ) { + return; + } + + if ( audiostream_is_playing( Brief_voices[stage_num]) ) + return; + + audiostream_play(Brief_voices[stage_num], Master_voice_volume, 0); + Brief_voice_started = 1; +} + +// stop playback of the voice for a particular briefing stage +void brief_voice_stop(int stage_num) +{ + if ( Brief_voices[stage_num] == -1 ) + return; + + audiostream_stop(Brief_voices[stage_num]); // stream is automatically rewound +} + +// pause playback of the voice for a particular briefing stage, to resume just +// call brief_voice_unpause() again +void brief_voice_pause(int stage_num) +{ + if ( Brief_voices[stage_num] == -1 ) + return; + + audiostream_pause(Brief_voices[stage_num]); +} + +void brief_voice_unpause(int stage_num) +{ + if ( Brief_voices[stage_num] == -1 ) + return; + + audiostream_unpause(Brief_voices[stage_num]); +} + +void brief_reset_last_new_stage() +{ + Last_new_stage = -1; +} + +// get the dimensions for a briefing icon +void brief_common_get_icon_dimensions(int *w, int *h, int type, int ship_class) +{ + Assert(type >= 0 && type < MAX_BRIEF_ICONS); + + // in case anything goes wrong + *w=0; + *h=0; + + int species = ship_get_species_by_type(ship_class); + if(species < 0){ + return; + } + + if ( Icon_bitmaps[type][species].first_frame >= 0 ) { + bm_get_info( Icon_bitmaps[type][species].first_frame, w, h, NULL); + } else { + *w=0; + *h=0; + } +} + +void cmd_brief_reset() +{ + int i, j; + static int inited = 0; + + if (inited) { + for (i=0; iauto_advance ) { + return 0; + } + + Brief_stage_time += fl2i(frametime*1000 + 0.5f); + + if ( (Brief_voices[stage_num] >= 0) && Briefing_voice_enabled ) { + voice_active = 1; + } else { + voice_active = 0; + } + + if ( voice_active && (Brief_voice_ended == 0) && Brief_voice_started) { + if ( !audiostream_is_playing( Brief_voices[stage_num]) ) { + Brief_voice_ended = 1; + Brief_stage_time = 0; + } + } + + if ( Brief_voice_ended ) { + if ( Brief_stage_time > STAGE_ADVANCE_DELAY ) { + advance = 1; + } + } + + if ( !voice_active && (Brief_textdraw_finished > 0) ) { + if ( Brief_stage_time > max(5000, Num_brief_text_lines[0] * 3500) ) { + advance = 1; + } + } + + return advance; +} diff --git a/src/mission/missioncampaign.cpp b/src/mission/missioncampaign.cpp new file mode 100644 index 0000000..4e82539 --- /dev/null +++ b/src/mission/missioncampaign.cpp @@ -0,0 +1,1888 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionCampaign.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * source for dealing with campaigns + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 23 9/14/99 4:35a Dave + * Argh. Added all kinds of code to handle potential crashes in debriefing + * code. + * + * 22 9/09/99 11:40p Dave + * Handle an Assert() in beam code. Added supernova sounds. Play the right + * 2 end movies properly, based upon what the player did in the mission. + * + * 21 9/09/99 9:34a Jefff + * fixed a potential exit-loop bug + * + * 20 9/07/99 6:55p Jefff + * functionality to break out of a loop. hacked functionality to jump to + * a specific mission in a campaign -- doesnt grant ships/weapons from + * skipped missions tho. + * + * 19 9/07/99 2:19p Jefff + * clear skip mission player vars on mission skip + * + * 18 9/06/99 9:45p Jefff + * break out of loop and skip mission support + * + * 17 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 16 9/03/99 1:32a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 15 8/27/99 12:04a Dave + * Campaign loop screen. + * + * 14 8/04/99 5:36p Andsager + * Show upsell screens at end of demo campaign before returning to main + * hall. + * + * 13 2/05/99 3:50p Anoop + * Removed dumb campaign mission stats saving from multiplayer. + * + * 12 12/17/98 2:43p Andsager + * Modify fred campaign save file to include optional mission loops + * + * 11 12/12/98 3:17p Andsager + * Clean up mission eval, goal, event and mission scoring. + * + * 10 12/10/98 9:59a Andsager + * Fix some bugs with mission loops + * + * 9 12/09/98 1:56p Andsager + * Initial checkin of mission loop + * + * 8 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 7 11/03/98 4:48p Johnson + * Fixed campaign file versioning bug left over from Silent Threat port. + * + * 6 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 5 10/13/98 2:47p Andsager + * Remove reference to Tech_shivan_species_avail + * + * 4 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 3 10/07/98 6:27p Dave + * Globalized mission and campaign file extensions. Removed Silent Threat + * special code. Moved \cache \players and \multidata into the \data + * directory. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 95 9/10/98 1:17p Dave + * Put in code to flag missions and campaigns as being MD or not in Fred + * and Freespace. Put in multiplayer support for filtering out MD + * missions. Put in multiplayer popups for warning of non-valid missions. + * + * 94 9/01/98 4:25p Dave + * Put in total (I think) backwards compatibility between mission disk + * freespace and non mission disk freespace, including pilot files and + * campaign savefiles. + * + * 93 7/06/98 4:10p Hoffoss + * Fixed some bugs that presented themselves when trying to use a pilot + * that has a no longer existent campaign active. Also expanded the + * campaign load code to actually return a proper error code, instead of + * always trapping errors internally and crashing, and always returning 0. + * + * 92 6/17/98 9:30a Allender + * fixed red alert replay stats clearing problem + * + * 91 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 90 5/25/98 1:29p Allender + * end mission sequencing + * + * 89 5/21/98 9:25p Allender + * endgame movie always viewable at end of campaign + * + * 88 5/13/98 5:14p Allender + * red alert support to go back to previous mission + * + * 87 5/12/98 4:16p Hoffoss + * Fixed bug where not all missions in all campaigns were being filtered + * out of stand alone mission listing in simulator room. + * + * 86 5/05/98 3:29p Hoffoss + * Changed code so description is BEFORE num players in campaign file. + * Other code is relying on this ordering. + * + * 85 5/05/98 12:19p Dave + * campaign description goes *after* num players + * + * 84 5/04/98 5:52p Comet + * Fixed bug with Galatea/Bastion selection when finishing missions. + * + * 83 5/01/98 2:46p Duncan + * fix a cfile problem with campaigns related to the new cfile stuff + * + * 82 5/01/98 12:34p John + * Added code to force FreeSpace to run in the same dir as exe and made + * all the parse error messages a little nicer. + * + * 81 4/30/98 7:01p Duncan + * AL: don't allow deletion of campaign files in multiplayer + * + * 80 4/30/98 4:53p John + * Restructured and cleaned up cfile code. Added capability to read off + * of CD-ROM drive and out of multiple pack files. + * + * $NoKeywords: $ + */ + +#include +#ifndef PLAT_UNIX +#include +#include +#endif +#include +#include +#include + +#include "key.h" +#include "ui.h" +#include "missioncampaign.h" +#include "gamesequence.h" +#include "2d.h" +#include "parselo.h" +#include "missionload.h" +#include "freespace.h" +#include "sexp.h" +#include "cfile.h" +#include "player.h" +#include "missiongoals.h" +// #include "movie.h" +#include "multi.h" +#include "techmenu.h" +#include "eventmusic.h" +#include "alphacolors.h" +#include "localize.h" +#include "supernova.h" + +// mission disk stuff +#define CAMPAIGN_SAVEFILE_MAX_SHIPS_OLD 75 +#define CAMPAIGN_SAVEFILE_MAX_WEAPONS_OLD 44 + +#define CAMPAIGN_INITIAL_RELEASE_FILE_VERSION 6 + +// campaign wasn't ended +int Campaign_ended_in_mission = 0; + +// stuff for selecting campaigns. We need to keep both arrays around since we display the +// list of campaigns by name, but must load campaigns by filename +char *Campaign_names[MAX_CAMPAIGNS]; +char *Campaign_file_names[MAX_CAMPAIGNS]; +int Num_campaigns; + +char *campaign_types[MAX_CAMPAIGN_TYPES] = +{ +//XSTR:OFF + "single", + "multi coop", + "multi teams" +//XSTR:ON +}; + +// modules local variables to deal with getting new ships/weapons available to the player +int Num_granted_ships, Num_granted_weapons; // per mission counts of new ships and weapons +int Granted_ships[MAX_SHIP_TYPES]; +int Granted_weapons[MAX_WEAPON_TYPES]; + +// variables to control the UI stuff for loading campaigns +LOCAL int Campaign_ui_active = 0; +LOCAL UI_WINDOW Campaign_window; +LOCAL UI_LISTBOX Campaign_listbox; +LOCAL UI_BUTTON Campaign_okb, Campaign_cancelb; + +// the campaign!!!!! +campaign Campaign; + +// variables with deal with the campaign save file +#define CAMPAIGN_FILE_VERSION 12 +//#define CAMPAIGN_FILE_COMPATIBLE_VERSION CAMPAIGN_INITIAL_RELEASE_FILE_VERSION +#define CAMPAIGN_FILE_COMPATIBLE_VERSION CAMPAIGN_FILE_VERSION +#define CAMPAIGN_FILE_ID 0xbeefcafe + +// variables with deal with the campaign stats save file +#define CAMPAIGN_STATS_FILE_VERSION 1 +#define CAMPAIGN_STATS_FILE_COMPATIBLE_VERSION 1 +#define CAMPAIGN_STATS_FILE_ID 0xabbadaad + +// mission_campaign_get_name returns a string (which is malloced in this routine) of the name +// of the given freespace campaign file. In the type field, we return if the campaign is a single +// player or multiplayer campaign. The type field will only be valid if the name returned is non-NULL +int mission_campaign_get_info(char *filename, char *name, int *type, int *max_players, char **desc) +{ + int rval, i; + char campaign_type[NAME_LENGTH], fname[MAX_FILENAME_LEN]; + + Assert( name != NULL ); + Assert( type != NULL ); + + // open localization + lcl_ext_open(); + + strcpy(fname, filename); + if ((strlen(fname) < 4) || stricmp(fname + strlen(fname) - 4, FS_CAMPAIGN_FILE_EXT)){ + strcat(fname, FS_CAMPAIGN_FILE_EXT); + } + + Assert(strlen(fname) < MAX_FILENAME_LEN); + + if ((rval = setjmp(parse_abort)) != 0) { + if (rval == 5){ + // close localization + lcl_ext_close(); + + return 0; + } + + Error(LOCATION, "Error parsing '%s'\r\nError code = %i.\r\n", fname, rval); + + } else { + read_file_text( fname ); + reset_parse(); + required_string("$Name:"); + + stuff_string( name, F_NAME, NULL ); + if ( name == NULL ) { + Int3(); + nprintf(("Warning", "No name found for campaign file %s\n", filename)); + + // close localization + lcl_ext_close(); + + return 0; + } + + required_string( "$Type:" ); + stuff_string( campaign_type, F_NAME, NULL ); + + *type = -1; + for (i=0; i 0) { + skip_to_string("+Num Players:"); + stuff_int(max_players); + } + + // if we found a valid campaign type + if ((*type) >= 0) { + // close localization + lcl_ext_close(); + + return 1; + } + } + + Int3(); // get Allender -- incorrect type found + + // close localization + lcl_ext_close(); + + return 0; +} + +// parses campaign and returns a list of missions in it. Returns number of missions added to +// the 'list', and up to 'max' missions may be added to 'list'. Returns negative on error. +// +int mission_campaign_get_mission_list(char *filename, char **list, int max) +{ + int rval, i, num = 0; + char name[NAME_LENGTH]; + + filename = cf_add_ext(filename, FS_CAMPAIGN_FILE_EXT); + + // read the mission file and get the list of mission filenames + if ((rval = setjmp(parse_abort)) != 0) { + // since we can't return count of allocated elements, free them instead + for (i=0; i 0) { + stuff_string(name, F_NAME, NULL); + if (num < max) + list[num++] = strdup(name); + } + } + + return num; +} + +void mission_campaign_maybe_add( char *filename, int multiplayer ) +{ + char name[NAME_LENGTH]; + int type,max_players; + + if ( mission_campaign_get_info( filename, name, &type, &max_players) ) { + if ( !multiplayer && ( type == CAMPAIGN_TYPE_SINGLE) ) { + Campaign_names[Num_campaigns] = strdup(name); + Campaign_file_names[Num_campaigns] = strdup(filename); + Num_campaigns++; + } + } +} + +// mission_campaign_build_list() builds up the list of campaigns that the user might +// be able to pick from. It uses the multiplayer flag to tell if we should display a list +// of single or multiplayer campaigns. This routine sets the Num_campaigns and Campaign_names +// global variables +void mission_campaign_build_list( int multiplayer ) +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + int find_handle; + _finddata_t find; + char wild_card[256]; + + Num_campaigns = 0; + mission_campaign_maybe_add( BUILTIN_CAMPAIGN, multiplayer); + + memset(wild_card, 0, 256); + strcpy(wild_card, NOX("data\\missions\\*")); + strcat(wild_card, FS_CAMPAIGN_FILE_EXT); + find_handle = _findfirst( wild_card, &find ); + if( find_handle != -1 ) { + if ( !(find.attrib & _A_SUBDIR) && stricmp(find.name, BUILTIN_CAMPAIGN) ){ + mission_campaign_maybe_add( find.name, multiplayer); + } + + while( !_findnext( find_handle, &find ) ) { + if ( !(find.attrib & _A_SUBDIR) && stricmp(find.name, BUILTIN_CAMPAIGN) ) { + if ( Num_campaigns >= MAX_CAMPAIGNS ){ + //MessageBox( -2,-2, 1, "Only the first 300 files will be displayed.", "Ok" ); + break; + } else { + mission_campaign_maybe_add( find.name, multiplayer); + } + } + } + } +#endif +} + +// gets optional ship/weapon information +void mission_campaign_get_sw_info() +{ + int i, count, ship_list[MAX_SHIP_TYPES], weapon_list[MAX_WEAPON_TYPES]; + + // set allowable ships to the SIF_PLAYER_SHIPs + memset( Campaign.ships_allowed, 0, sizeof(Campaign.ships_allowed) ); + for (i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Ship_info[i].flags & SIF_PLAYER_SHIP ) + Campaign.ships_allowed[i] = 1; + } + + for (i = 0; i < MAX_WEAPON_TYPES; i++ ) + Campaign.weapons_allowed[i] = 1; + + if ( optional_string("+Starting Ships:") ) { + for (i = 0; i < MAX_SHIP_TYPES; i++ ) + Campaign.ships_allowed[i] = 0; + + count = stuff_int_list(ship_list, MAX_SHIP_TYPES, SHIP_INFO_TYPE); + + // now set the array elements stating which ships we are allowed + for (i = 0; i < count; i++ ) { + if ( Ship_info[ship_list[i]].flags & SIF_PLAYER_SHIP ) + Campaign.ships_allowed[ship_list[i]] = 1; + } + } + + if ( optional_string("+Starting Weapons:") ) { + for (i = 0; i < MAX_WEAPON_TYPES; i++ ) + Campaign.weapons_allowed[i] = 0; + + count = stuff_int_list(weapon_list, MAX_WEAPON_TYPES, WEAPON_POOL_TYPE); + + // now set the array elements stating which ships we are allowed + for (i = 0; i < count; i++ ) + Campaign.weapons_allowed[weapon_list[i]] = 1; + } +} + +// mission_campaign_load starts a new campaign. It reads in the mission information in the campaign file +// It also sets up all variables needed inside of the game to deal with starting mission numbers, etc +// +// Note: Due to difficulties in generalizing this function, parts of it are duplicated throughout +// this file. If you change the format of the campaign file, you should be sure these related +// functions work properly and update them if it breaks them. +int mission_campaign_load( char *filename, int load_savefile ) +{ + int len, rval, i; + char name[NAME_LENGTH], type[NAME_LENGTH]; + + filename = cf_add_ext(filename, FS_CAMPAIGN_FILE_EXT); + + // open localization + lcl_ext_open(); + + // read the mission file and get the list of mission filenames + if ((rval = setjmp(parse_abort)) != 0) { + mprintf(("Error parsing '%s'\r\nError code = %i.\r\n", filename, rval)); + + // close localization + lcl_ext_close(); + + return CAMPAIGN_ERROR_CORRUPT; + + } else { + // be sure to remove all old malloced strings of Mission_names + // we must also free any goal stuff that was from a previous campaign + // this also frees sexpressions so the next call to init_sexp will be able to reclaim + // nodes previously used by another campaign. + mission_campaign_close(); + + strcpy( Campaign.filename, filename ); + + // only initialize the sexpression stuff when Fred isn't running. It'll screw things up major + // if it does + if ( !Fred_running ){ + init_sexp(); // must initialize the sexpression stuff + } + + read_file_text( filename ); + reset_parse(); + memset( &Campaign, 0, sizeof(Campaign) ); + + // copy filename to campaign structure minus the extension + len = strlen(filename) - 4; + Assert(len < MAX_FILENAME_LEN); + strncpy(Campaign.filename, filename, len); + Campaign.filename[len] = 0; + + required_string("$Name:"); + stuff_string( name, F_NAME, NULL ); + + //Store campaign name in the global struct + strcpy( Campaign.name, name ); + + required_string( "$Type:" ); + stuff_string( type, F_NAME, NULL ); + + for (i = 0; i < MAX_CAMPAIGN_TYPES; i++ ) { + if ( !stricmp(type, campaign_types[i]) ) { + Campaign.type = i; + break; + } + } + + if ( i == MAX_CAMPAIGN_TYPES ) + Error(LOCATION, "Unknown campaign type %s!", type); + + Campaign.desc = NULL; + if (optional_string("+Description:")) + Campaign.desc = stuff_and_malloc_string(F_MULTITEXT, NULL, MISSION_DESC_LENGTH); + + // if the type is multiplayer -- get the number of players + Campaign.num_players = 0; + if ( Campaign.type != CAMPAIGN_TYPE_SINGLE) { + required_string("+Num players:"); + stuff_int( &(Campaign.num_players) ); + } + + // parse the optional ship/weapon information + mission_campaign_get_sw_info(); + + // parse the mission file and actually read in the mission stuff + Campaign.num_missions = 0; + while ( required_string_either("#End", "$Mission:") ) { + cmission *cm; + + required_string("$Mission:"); + stuff_string(name, F_NAME, NULL); + cm = &Campaign.missions[Campaign.num_missions]; + cm->name = strdup(name); + + cm->briefing_cutscene[0] = 0; + if ( optional_string("+Briefing Cutscene:") ) + stuff_string( cm->briefing_cutscene, F_NAME, NULL ); + + cm->flags = 0; + if (optional_string("+Flags:")) + stuff_int(&cm->flags); + + cm->formula = -1; + if ( optional_string("+Formula:") ) { + cm->formula = get_sexp_main(); + if ( !Fred_running ) { + Assert ( cm->formula != -1 ); + sexp_mark_persistent( cm->formula ); + + } else { + if ( cm->formula == -1 ){ + // close localization + lcl_ext_close(); + + return CAMPAIGN_ERROR_SEXP_EXHAUSTED; + } + } + } + + // Do misison looping stuff + cm->has_mission_loop = 0; + if ( optional_string("+Mission Loop:") ) { + cm->has_mission_loop = 1; + } + + cm->mission_loop_desc = NULL; + if ( optional_string("+Mission Loop Text:")) { + cm->mission_loop_desc = stuff_and_malloc_string(F_MULTITEXT, NULL, MISSION_DESC_LENGTH); + } + + cm->mission_loop_brief_anim = NULL; + if ( optional_string("+Mission Loop Brief Anim:")) { + cm->mission_loop_brief_anim = stuff_and_malloc_string(F_MULTITEXT, NULL, MAX_FILENAME_LEN); + } + + cm->mission_loop_brief_sound = NULL; + if ( optional_string("+Mission Loop Brief Sound:")) { + cm->mission_loop_brief_sound = stuff_and_malloc_string(F_MULTITEXT, NULL, MAX_FILENAME_LEN); + } + + cm->mission_loop_formula = -1; + if ( optional_string("+Formula:") ) { + cm->mission_loop_formula = get_sexp_main(); + if ( !Fred_running ) { + Assert ( cm->mission_loop_formula != -1 ); + sexp_mark_persistent( cm->mission_loop_formula ); + + } else { + if ( cm->mission_loop_formula == -1 ){ + // close localization + lcl_ext_close(); + + return CAMPAIGN_ERROR_SEXP_EXHAUSTED; + } + } + } + + if (optional_string("+Level:")) { + stuff_int( &cm->level ); + if ( cm->level == 0 ) // check if the top (root) of the whole tree + Campaign.next_mission = Campaign.num_missions; + + } else + Campaign.realign_required = 1; + + if (optional_string("+Position:")) + stuff_int( &cm->pos ); + else + Campaign.realign_required = 1; + + if (Fred_running) { + cm->num_goals = -1; + cm->num_events = -1; + cm->notes = NULL; + + } else { + cm->num_goals = 0; + cm->num_events = 0; + } + + cm->goals = NULL; + cm->events = NULL; + Campaign.num_missions++; + } + } + + // set up the other variables for the campaign stuff. After initializing, we must try and load + // the campaign save file for this player. Since all campaign loads go through this routine, I + // think this place should be the only necessary place to load the campaign save stuff. The campaign + // save file will get written when a mission has ended by player choice. + Campaign.next_mission = 0; + Campaign.prev_mission = -1; + Campaign.current_mission = -1; + + // loading the campaign will get us to the current and next mission that the player must fly + // plus load all of the old goals that future missions might rely on. + if (!Fred_running && load_savefile && (Campaign.type == CAMPAIGN_TYPE_SINGLE)) { + mission_campaign_savefile_load(Campaign.filename); + } + + // close localization + lcl_ext_close(); + + return 0; +} + +// mission_campaign_load_by_name() loads up a freespace campaign given the filename. This routine +// is used to load up campaigns when a pilot file is loaded. Generally, the +// filename will probably be the freespace campaign file, but not necessarily. +int mission_campaign_load_by_name( char *filename ) +{ + char name[NAME_LENGTH],test[5]; + int type,max_players; + + // make sure to tack on .fsc on the end if its not there already + if(strlen(filename) > 0){ + if(strlen(filename) > 4){ + strcpy(test,filename+(strlen(filename)-4)); + if(strcmp(test, FS_CAMPAIGN_FILE_EXT)!=0){ + strcat(filename, FS_CAMPAIGN_FILE_EXT); + } + } else { + strcat(filename, FS_CAMPAIGN_FILE_EXT); + } + } else { + Error(LOCATION,"Tried to load campaign file with illegal length/extension!"); + } + + if (!mission_campaign_get_info(filename, name, &type, &max_players)){ + return -1; + } + + Num_campaigns = 0; + Campaign_file_names[Num_campaigns] = filename; + Campaign_names[Num_campaigns] = name; + Num_campaigns++; + mission_campaign_load(filename); + return 0; +} + +int mission_campaign_load_by_name_csfe( char *filename, char *callsign ) +{ + Game_mode |= GM_NORMAL; + strcpy(Player->callsign, callsign); + return mission_campaign_load_by_name( filename); +} + + +// mission_campaign_init initializes some variables then loads the default Freespace single player campaign. +void mission_campaign_init() +{ + memset(&Campaign, 0, sizeof(Campaign) ); +} + +// Fill in the root of the campaign save filename +void mission_campaign_savefile_generate_root(char *filename) +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + char base[_MAX_FNAME]; + + Assert ( strlen(Campaign.filename) != 0 ); + + // build up the filename for the save file. There could be a problem with filename length, + // but this problem can get fixed in several ways -- ignore the problem for now though. + _splitpath( Campaign.filename, NULL, NULL, base, NULL ); + Assert ( (strlen(base) + strlen(Player->callsign) + 1) < _MAX_FNAME ); + + sprintf( filename, NOX("%s.%s."), Player->callsign, base ); +#endif +} + +// mission_campaign_savefile_save saves the state of the campaign. This function will probably always be called +// then the player is done flying a mission in the campaign path. It will save the missions played, the +// state of the goals, etc. + +int mission_campaign_savefile_save() +{ + char filename[_MAX_FNAME]; + CFILE *fp; + int i,j, mission_count; + + memset(filename, 0, _MAX_FNAME); + mission_campaign_savefile_generate_root(filename); + + // name the file differently depending on whether we're in single player or multiplayer mode + // single player : *.csg + strcat( filename, NOX("csg")); + + fp = cfopen(filename,"wb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS); + + if (!fp) + return errno; + + // Write out campaign file info + cfwrite_int( CAMPAIGN_FILE_ID,fp ); + cfwrite_int( CAMPAIGN_FILE_VERSION,fp ); + + // put in the file signature (single or multiplayer campaign) - see MissionCampaign.h for the #defines + cfwrite_int( CAMPAIGN_SINGLE_PLAYER_SIG, fp ); + + // do we need to write out the filename of the campaign? + cfwrite_string_len( Campaign.filename, fp ); + cfwrite_int( Campaign.prev_mission, fp ); + cfwrite_int( Campaign.next_mission, fp ); + cfwrite_int( Campaign.loop_reentry, fp ); + cfwrite_int( Campaign.loop_enabled, fp ); + + // write out the information for ships/weapons which this player is allowed to use + cfwrite_int(Num_ship_types, fp); + cfwrite_int(Num_weapon_types, fp); + for ( i = 0; i < Num_ship_types; i++ ){ + cfwrite_char( Campaign.ships_allowed[i], fp ); + } + + for ( i = 0; i < Num_weapon_types; i++ ){ + cfwrite_char( Campaign.weapons_allowed[i], fp ); + } + + // write out the completed mission matrix. Used to tell which missions the player + // can replay in the simulator. Also, each completed mission contains a list of the goals + // that were in the mission along with the goal completion status. + cfwrite_int( Campaign.num_missions_completed, fp ); + for (i = 0; i < MAX_CAMPAIGN_MISSIONS; i++ ) { + if ( Campaign.missions[i].completed ) { + cfwrite_int( i, fp ); + cfwrite_int( Campaign.missions[i].num_goals, fp ); + for ( j = 0; j < Campaign.missions[i].num_goals; j++ ) { + cfwrite_string_len( Campaign.missions[i].goals[j].name, fp ); + cfwrite_char( Campaign.missions[i].goals[j].status, fp ); + } + cfwrite_int( Campaign.missions[i].num_events, fp ); + for ( j = 0; j < Campaign.missions[i].num_events; j++ ) { + cfwrite_string_len( Campaign.missions[i].events[j].name, fp ); + cfwrite_char( Campaign.missions[i].events[j].status, fp ); + } + + // write flags + cfwrite_int(Campaign.missions[i].flags, fp); + } + } + + cfclose( fp ); + + // 6/17/98 + // ugh! due to horrible bug, the stats saved at the end of every level were not written + // out to disk. Write out a seperate file to do this. We will only read it in if we actually + // find the file. + memset(filename, 0, _MAX_FNAME); + mission_campaign_savefile_generate_root(filename); + + // name the file differently depending on whether we're in single player or multiplayer mode + // single player : *.csg + strcat( filename, NOX("css")); + + fp = cfopen(filename,"wb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS); + + if (!fp) + return errno; + + // Write out campaign file info + cfwrite_int( CAMPAIGN_STATS_FILE_ID,fp ); + cfwrite_int( CAMPAIGN_STATS_FILE_VERSION,fp ); + + // determine how many missions we are saving -- I think that this method is safer than the method + // I used for release + mission_count = 0; + for ( i = 0; i < Campaign.num_missions; i++ ) { + if ( Campaign.missions[i].completed ) { + mission_count++; + } + } + + // write out the stats information to disk. + cfwrite_int( mission_count, fp ); + for (i = 0; i < Campaign.num_missions; i++ ) { + if ( Campaign.missions[i].completed ) { + cfwrite_int( i, fp ); + cfwrite( &Campaign.missions[i].stats, sizeof(scoring_struct), 1, fp ); + } + } + + cfclose( fp ); + + return 0; +} + +// The following function always only ever ever ever called by CSFE!!!!! +int campaign_savefile_save(char *pname) +{ + if (Campaign.type == CAMPAIGN_TYPE_SINGLE) + Game_mode &= ~GM_MULTIPLAYER; + else + Game_mode |= GM_MULTIPLAYER; + + strcpy(Player->callsign, pname); + //memcpy(&Campaign, camp, sizeof(campaign)); + return mission_campaign_savefile_save(); +} + + +// the below two functions is internal to this module. It is here so that I can group the save/load +// functions together. +// + +// mission_campaign_savefile_delete deletes any save file in the players directory for the given +// campaign filename +void mission_campaign_savefile_delete( char *cfilename, int is_multi ) +{ +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + char filename[_MAX_FNAME], base[_MAX_FNAME]; + + _splitpath( cfilename, NULL, NULL, base, NULL ); + + if ( Player->flags & PLAYER_FLAGS_IS_MULTI ) { + return; // no such thing as a multiplayer campaign savefile + } + + sprintf( filename, NOX("%s.%s.csg"), Player->callsign, base ); + + cf_delete( filename, CF_TYPE_SINGLE_PLAYERS ); +#endif +} + +void campaign_delete_save( char *cfn, char *pname) +{ + strcpy(Player->callsign, pname); + mission_campaign_savefile_delete(cfn); +} + +// next function deletes all the save files for this particular pilot. Just call cfile function +// which will delete multiple files +// Player_select_mode tells us whether we are deleting single or multiplayer files +void mission_campaign_delete_all_savefiles( char *pilot_name, int is_multi ) +{ + int dir_type, num_files, i; + char *names[MAX_CAMPAIGNS], spec[MAX_FILENAME_LEN + 2], *ext; + char filename[1024]; + int (*filter_save)(char *filename); + + if ( is_multi ) { + return; // can't have multiplayer campaign save files + } + + ext = NOX(".csg"); + dir_type = CF_TYPE_SINGLE_PLAYERS; + + sprintf(spec, NOX("%s.*%s"), pilot_name, ext); + + // HACK HACK HACK HACK!!!! cf_get_file_list is not reentrant. Pretty dumb because it should + // be. I have to save any file filters + filter_save = Get_file_list_filter; + Get_file_list_filter = NULL; + num_files = cf_get_file_list(MAX_CAMPAIGNS, names, dir_type, spec); + Get_file_list_filter = filter_save; + + for (i=0; icallsign) + 1) < _MAX_FNAME ); + + if(Game_mode & GM_MULTIPLAYER) + sprintf( filename, NOX("%s.%s.msg"), Player->callsign, base ); + else + sprintf( filename, NOX("%s.%s.csg"), Player->callsign, base ); + + fp = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS ); + if ( !fp ) + return; + + id = cfread_int( fp ); + if ( id != CAMPAIGN_FILE_ID ) { + Warning(LOCATION, "Campaign save file has invalid signature"); + cfclose( fp ); + return; + } + + version = cfread_int( fp ); + if ( version < CAMPAIGN_FILE_COMPATIBLE_VERSION ) { + Warning(LOCATION, "Campaign save file too old -- not compatible. Deleting file.\nYou can continue from here without trouble\n\n"); + cfclose( fp ); + cf_delete( filename, CF_TYPE_SINGLE_PLAYERS ); + return; + } + + // verify that we are loading the correct type of campaign file for the mode that we are in. + if(version >= 3) + type_sig = cfread_int( fp ); + else + type_sig = CAMPAIGN_SINGLE_PLAYER_SIG; + // the actual check + Assert( ((Game_mode & GM_MULTIPLAYER) && (type_sig==CAMPAIGN_MULTI_PLAYER_SIG)) || (!(Game_mode & GM_MULTIPLAYER) && (type_sig==CAMPAIGN_SINGLE_PLAYER_SIG)) ); + + Campaign.type = type_sig == CAMPAIGN_SINGLE_PLAYER_SIG ? CAMPAIGN_TYPE_SINGLE : CAMPAIGN_TYPE_MULTI_COOP; + + // read in the filename of the campaign and compare the filenames to be sure that + // we are reading data that really belongs to this campaign. I think that this check + // is redundant. + cfread_string_len( filename, _MAX_FNAME, fp ); + /*if ( stricmp( filename, cfilename) ) { // Used to be !stricmp. How did this ever work? --MK, 11/9/97 + Warning(LOCATION, "Campaign save file appears corrupt because of mismatching filenames."); + cfclose(fp); + return; + }*/ + + Campaign.prev_mission = cfread_int( fp ); + Campaign.next_mission = cfread_int( fp ); + Campaign.loop_reentry = cfread_int( fp ); + Campaign.loop_enabled = cfread_int( fp ); + + // load information about ships/weapons allowed + int ship_count, weapon_count; + + // if earlier than mission disk version, use old MAX_SHIP_TYPES, otherwise read from the file + if(version <= CAMPAIGN_INITIAL_RELEASE_FILE_VERSION){ + ship_count = CAMPAIGN_SAVEFILE_MAX_SHIPS_OLD; + weapon_count = CAMPAIGN_SAVEFILE_MAX_WEAPONS_OLD; + } else { + ship_count = cfread_int(fp); + weapon_count = cfread_int(fp); + } + + for ( i = 0; i < ship_count; i++ ){ + Campaign.ships_allowed[i] = cfread_ubyte( fp ); + } + + for ( i = 0; i < weapon_count; i++ ){ + Campaign.weapons_allowed[i] = cfread_ubyte( fp ); + } + + // read in the completed mission matrix. Used to tell which missions the player + // can replay in the simulator. Also, each completed mission contains a list of the goals + // that were in the mission along with the goal completion status. + Campaign.num_missions_completed = cfread_int( fp ); + for (i = 0; i < Campaign.num_missions_completed; i++ ) { + num = cfread_int( fp ); + Campaign.missions[num].completed = 1; + Campaign.missions[num].num_goals = cfread_int( fp ); + + // be sure to malloc out space for the goals stuff, then zero the memory!!! Don't do malloc + // if there are no goals + Campaign.missions[num].goals = (mgoal *)malloc( Campaign.missions[num].num_goals * sizeof(mgoal) ); + if ( Campaign.missions[num].num_goals > 0 ) { + memset( Campaign.missions[num].goals, 0, sizeof(mgoal) * Campaign.missions[num].num_goals ); + Assert( Campaign.missions[num].goals != NULL ); + } + + // now read in the goal information for this mission + for ( j = 0; j < Campaign.missions[num].num_goals; j++ ) { + cfread_string_len( Campaign.missions[num].goals[j].name, NAME_LENGTH, fp ); + Campaign.missions[num].goals[j].status = cfread_char( fp ); + } + + // get the events from the savefile + Campaign.missions[num].num_events = cfread_int( fp ); + + // be sure to malloc out space for the events stuff, then zero the memory!!! Don't do malloc + // if there are no events +// if (Campaign.missions[num].events < 0) +// Campaign.missions[num].events = 0; + Campaign.missions[num].events = (mevent *)malloc( Campaign.missions[num].num_events * sizeof(mevent) ); + if ( Campaign.missions[num].num_events > 0 ) { + memset( Campaign.missions[num].events, 0, sizeof(mevent) * Campaign.missions[num].num_events ); + Assert( Campaign.missions[num].events != NULL ); + } + + // now read in the event information for this mission + for ( j = 0; j < Campaign.missions[num].num_events; j++ ) { + cfread_string_len( Campaign.missions[num].events[j].name, NAME_LENGTH, fp ); + Campaign.missions[num].events[j].status = cfread_char( fp ); + } + + // now read flags + Campaign.missions[num].flags = cfread_int(fp); + } + + cfclose( fp ); + + // 4/17/98 + // now, try and read in the campaign stats saved information. This code was added for the 1.03 patch + // since the stats data was never written out to disk. We try and open the file, and if we cannot find + // it, then simply return + sprintf( filename, NOX("%s.%s.css"), Player->callsign, base ); + + fp = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS ); + if ( !fp ) + return; + + id = cfread_int( fp ); + if ( id != CAMPAIGN_STATS_FILE_ID ) { + Warning(LOCATION, "Campaign stats save file has invalid signature"); + cfclose( fp ); + return; + } + + version = cfread_int( fp ); + if ( version < CAMPAIGN_STATS_FILE_COMPATIBLE_VERSION ) { + Warning(LOCATION, "Campaign save file too old -- not compatible. Deleting file.\nYou can continue from here without trouble\n\n"); + cfclose( fp ); + cf_delete( filename, CF_TYPE_SINGLE_PLAYERS ); + return; + } + + num_stats_blocks = cfread_int( fp ); + for (i = 0; i < num_stats_blocks; i++ ) { + num = cfread_int( fp ); + cfread( &Campaign.missions[num].stats, sizeof(scoring_struct), 1, fp ); + } + + cfclose(fp); + +#endif +} + +// the following code only ever called by CSFE!!!! +void campaign_savefile_load(char *fname, char *pname) +{ + if (Campaign.type==CAMPAIGN_TYPE_SINGLE) { + Game_mode &= ~GM_MULTIPLAYER; + Game_mode &= GM_NORMAL; + } + else + Game_mode |= GM_MULTIPLAYER; + strcpy(Player->callsign, pname); + mission_campaign_savefile_load(fname); +} + +// mission_campaign_next_mission sets up the internal veriables of the campaign +// structure so the player can play the next mission. If there are no more missions +// available in the campaign, this function returns -1, else 0 if the mission was +// set successfully +int mission_campaign_next_mission() +{ + if ( (Campaign.next_mission == -1) || (strlen(Campaign.name) == 0) ) // will be set to -1 when there is no next mission + return -1; + + Campaign.current_mission = Campaign.next_mission; + strncpy( Game_current_mission_filename, Campaign.missions[Campaign.current_mission].name, MAX_FILENAME_LEN ); + + // check for end of loop. + if (Campaign.current_mission == Campaign.loop_reentry) { + Campaign.loop_enabled = 0; + } + + // reset the number of persistent ships and weapons for the next campaign mission + Num_granted_ships = 0; + Num_granted_weapons = 0; + return 0; +} + +// mission_campaign_previous_mission() gets called to go to the previous mission in +// the campaign. Used only for Red Alert missions +int mission_campaign_previous_mission() +{ + if ( !(Game_mode & GM_CAMPAIGN_MODE) ) + return 0; + + if ( Campaign.prev_mission == -1 ) + return 0; + + Campaign.current_mission = Campaign.prev_mission; + Campaign.next_mission = Campaign.current_mission; + Campaign.num_missions_completed--; + Campaign.missions[Campaign.next_mission].completed = 0; + mission_campaign_savefile_save(); + + // reset the player stats to be the stats from this level + memcpy( &Player->stats, &Campaign.missions[Campaign.current_mission].stats, sizeof(Player->stats) ); + + strncpy( Game_current_mission_filename, Campaign.missions[Campaign.current_mission].name, MAX_FILENAME_LEN ); + Num_granted_ships = 0; + Num_granted_weapons = 0; + + return 1; +} + +/* +// determine what the next mission is after the current one. Because this evaluates an sexp, +// and that could check just about anything, the results are only going to be valid in +// certain places. +// DA 12/09/98 -- To allow for mission loops, need to maintain call with store stats +int mission_campaign_eval_next_mission( int store_stats ) +{ + char *name; + int cur, i; + cmission *mission; + + Campaign.next_mission = -1; + cur = Campaign.current_mission; + name = Campaign.missions[cur].name; + + mission = &Campaign.missions[cur]; + + // first we must save the status of the current missions goals in the campaign mission structure. + // After that, we can determine which mission is tagged as the next mission. Finally, we + // can save the campaign save file + // we might have goal and event status if the player replayed a mission + if ( mission->num_goals > 0 ) { + free( mission->goals ); + } + + mission->num_goals = Num_goals; + if ( mission->num_goals > 0 ) { + mission->goals = (mgoal *)malloc( sizeof(mgoal) * Num_goals ); + Assert( mission->goals != NULL ); + } + + // copy the needed info from the Mission_goal struct to our internal structure + for (i = 0; i < Num_goals; i++ ) { + if ( strlen(Mission_goals[i].name) == 0 ) { + char name[NAME_LENGTH]; + + sprintf(name, NOX("Goal #%d"), i); + //Warning(LOCATION, "Mission goal in mission %s must have a +Name field! using %s for campaign save file\n", mission->name, name); + strcpy( mission->goals[i].name, name); + } else + strcpy( mission->goals[i].name, Mission_goals[i].name ); + Assert ( Mission_goals[i].satisfied != GOAL_INCOMPLETE ); // should be true or false at this point!!! + mission->goals[i].status = (char)Mission_goals[i].satisfied; + } + + // do the same thing for events as we did for goals + // we might have goal and event status if the player replayed a mission + if ( mission->num_events > 0 ) { + free( mission->events ); + } + + mission->num_events = Num_mission_events; + if ( mission->num_events > 0 ) { + mission->events = (mevent *)malloc( sizeof(mevent) * Num_mission_events ); + Assert( mission->events != NULL ); + } + + // copy the needed info from the Mission_goal struct to our internal structure + for (i = 0; i < Num_mission_events; i++ ) { + if ( strlen(Mission_events[i].name) == 0 ) { + char name[NAME_LENGTH]; + + sprintf(name, NOX("Event #%d"), i); + nprintf(("Warning", "Mission goal in mission %s must have a +Name field! using %s for campaign save file\n", mission->name, name)); + strcpy( mission->events[i].name, name); + } else + strcpy( mission->events[i].name, Mission_events[i].name ); + + // getting status for the events is a little different. If the formula value for the event entry + // is -1, then we know the value of the result field will never change. If the formula is + // not -1 (i.e. still being evaluated at mission end time), we will write "incomplete" for the + // event evaluation + if ( Mission_events[i].formula == -1 ) { + if ( Mission_events[i].result ) + mission->events[i].status = EVENT_SATISFIED; + else + mission->events[i].status = EVENT_FAILED; + } else + Int3(); + } + + // maybe store the alltime stats which would be current at the end of this mission + if ( store_stats ) { + memcpy( &mission->stats, &Player->stats, sizeof(Player->stats) ); + scoring_backout_accept( &mission->stats ); + } + + if ( store_stats ) { // second (last) time through, so use choose loop_mission if chosen + if ( Campaign.loop_enabled ) { + Campaign.next_mission = Campaign.loop_mission; + } else { + // evaluate next mission (straight path) + if (Campaign.missions[cur].formula != -1) { + flush_sexp_tree(Campaign.missions[cur].formula); // force formula to be re-evaluated + eval_sexp(Campaign.missions[cur].formula); // this should reset Campaign.next_mission to proper value + } + } + } else { + + // evaluate next mission (straight path) + if (Campaign.missions[cur].formula != -1) { + flush_sexp_tree(Campaign.missions[cur].formula); // force formula to be re-evaluated + eval_sexp(Campaign.missions[cur].formula); // this should reset Campaign.next_mission to proper value + } + + // evaluate mission loop mission (if any) so it can be used if chosen + if ( Campaign.missions[cur].has_mission_loop ) { + int copy_next_mission = Campaign.next_mission; + // Set temporarily to -1 so we know if loop formula fails to assign + Campaign.next_mission = -1; // Cannot exit campaign from loop + if (Campaign.missions[cur].mission_loop_formula != -1) { + flush_sexp_tree(Campaign.missions[cur].mission_loop_formula); // force formula to be re-evaluated + eval_sexp(Campaign.missions[cur].mission_loop_formula); // this should reset Campaign.next_mission to proper value + } + + Campaign.loop_mission = Campaign.next_mission; + Campaign.next_mission = copy_next_mission; + } + } + + if (Campaign.next_mission == -1) + nprintf(("allender", "No next mission to proceed to.\n")); + else + nprintf(("allender", "Next mission is number %d [%s]\n", Campaign.next_mission, Campaign.missions[Campaign.next_mission].name)); + + return Campaign.next_mission; +} */ + +// Evaluate next campaign mission - set as Campaign.next_mission. Also set Campaign.loop_mission +void mission_campaign_eval_next_mission() +{ + Campaign.next_mission = -1; + int cur = Campaign.current_mission; + + // evaluate next mission (straight path) + if (Campaign.missions[cur].formula != -1) { + flush_sexp_tree(Campaign.missions[cur].formula); // force formula to be re-evaluated + eval_sexp(Campaign.missions[cur].formula); // this should reset Campaign.next_mission to proper value + } + + // evaluate mission loop mission (if any) so it can be used if chosen + if ( Campaign.missions[cur].has_mission_loop ) { + int copy_next_mission = Campaign.next_mission; + // Set temporarily to -1 so we know if loop formula fails to assign + Campaign.next_mission = -1; + if (Campaign.missions[cur].mission_loop_formula != -1) { + flush_sexp_tree(Campaign.missions[cur].mission_loop_formula); // force formula to be re-evaluated + eval_sexp(Campaign.missions[cur].mission_loop_formula); // this should reset Campaign.next_mission to proper value + } + + Campaign.loop_mission = Campaign.next_mission; + Campaign.next_mission = copy_next_mission; + } + + if (Campaign.next_mission == -1) { + nprintf(("allender", "No next mission to proceed to.\n")); + } else { + nprintf(("allender", "Next mission is number %d [%s]\n", Campaign.next_mission, Campaign.missions[Campaign.next_mission].name)); + } + +} + +// Store mission's goals and events in Campaign struct +void mission_campaign_store_goals_and_events() +{ + char *name; + int cur, i; + cmission *mission; + + cur = Campaign.current_mission; + name = Campaign.missions[cur].name; + + mission = &Campaign.missions[cur]; + + // first we must save the status of the current missions goals in the campaign mission structure. + // After that, we can determine which mission is tagged as the next mission. Finally, we + // can save the campaign save file + // we might have goal and event status if the player replayed a mission + if ( mission->num_goals > 0 ) { + free( mission->goals ); + } + + mission->num_goals = Num_goals; + if ( mission->num_goals > 0 ) { + mission->goals = (mgoal *)malloc( sizeof(mgoal) * Num_goals ); + Assert( mission->goals != NULL ); + } + + // copy the needed info from the Mission_goal struct to our internal structure + for (i = 0; i < Num_goals; i++ ) { + if ( strlen(Mission_goals[i].name) == 0 ) { + char name[NAME_LENGTH]; + + sprintf(name, NOX("Goal #%d"), i); + //Warning(LOCATION, "Mission goal in mission %s must have a +Name field! using %s for campaign save file\n", mission->name, name); + strcpy( mission->goals[i].name, name); + } else + strcpy( mission->goals[i].name, Mission_goals[i].name ); + Assert ( Mission_goals[i].satisfied != GOAL_INCOMPLETE ); // should be true or false at this point!!! + mission->goals[i].status = (char)Mission_goals[i].satisfied; + } + + // do the same thing for events as we did for goals + // we might have goal and event status if the player replayed a mission + if ( mission->num_events > 0 ) { + free( mission->events ); + } + + mission->num_events = Num_mission_events; + if ( mission->num_events > 0 ) { + mission->events = (mevent *)malloc( sizeof(mevent) * Num_mission_events ); + Assert( mission->events != NULL ); + } + + // copy the needed info from the Mission_goal struct to our internal structure + for (i = 0; i < Num_mission_events; i++ ) { + if ( strlen(Mission_events[i].name) == 0 ) { + char name[NAME_LENGTH]; + + sprintf(name, NOX("Event #%d"), i); + nprintf(("Warning", "Mission goal in mission %s must have a +Name field! using %s for campaign save file\n", mission->name, name)); + strcpy( mission->events[i].name, name); + } else + strcpy( mission->events[i].name, Mission_events[i].name ); + + // getting status for the events is a little different. If the formula value for the event entry + // is -1, then we know the value of the result field will never change. If the formula is + // not -1 (i.e. still being evaluated at mission end time), we will write "incomplete" for the + // event evaluation + if ( Mission_events[i].formula == -1 ) { + if ( Mission_events[i].result ) + mission->events[i].status = EVENT_SATISFIED; + else + mission->events[i].status = EVENT_FAILED; + } else + Int3(); + } +} + +// this function is called when the player's mission is over. It updates the internal store of goals +// and their status then saves the state of the campaign in the campaign file. This gets called +// after player accepts mission results in debriefing. +void mission_campaign_mission_over() +{ + int mission_num, i; + cmission *mission; + + // I don't think that we should have a record for these -- maybe we might?????? If we do, + // then we should free them + if ( !(Game_mode & GM_CAMPAIGN_MODE) ){ + return; + } + + mission_num = Campaign.current_mission; + Assert( mission_num != -1 ); + mission = &Campaign.missions[mission_num]; + + // determine if any ships/weapons were granted this mission + for ( i=0; istats, &Player->stats, sizeof(Player->stats) ); + if(!(Game_mode & GM_MULTIPLAYER)){ + scoring_backout_accept( &mission->stats ); + } + + // if we are moving to a new mission, then change our data. If we are staying on the same mission, + // then we don't want to do anything. Remove information about goals/events + if ( Campaign.next_mission != mission_num ) { + Campaign.prev_mission = mission_num; + Campaign.current_mission = -1; + Campaign.num_missions_completed++; + Campaign.missions[mission_num].completed = 1; + + // save the scoring values from the previous mission at the start of this mission -- for red alert + + // save the state of the campaign in the campaign save file and move to the end_game state + if (Campaign.type == CAMPAIGN_TYPE_SINGLE) { + mission_campaign_savefile_save(); + } + + } else { + // free up the goals and events which were just malloced. It's kind of like erasing any fact + // that the player played this mission in the campaign. + free( mission->goals ); + mission->num_goals = 0; + + free( mission->events ); + mission->num_events = 0; + + Sexp_nodes[mission->formula].value = SEXP_UNKNOWN; + } + + Assert(Player); + if (Campaign.missions[Campaign.next_mission].flags & CMISSION_FLAG_BASTION){ + Player->on_bastion = 1; + } else { + Player->on_bastion = 0; + } + + mission_campaign_next_mission(); // sets up whatever needs to be set to actually play next mission +} + +// called when the game closes -- to get rid of memory errors for Bounds checker +void mission_campaign_close() +{ + int i; + + if (Campaign.desc) + free(Campaign.desc); + + // be sure to remove all old malloced strings of Mission_names + // we must also free any goal stuff that was from a previous campaign + for ( i=0; i 0 ){ + free ( Campaign.missions[i].goals ); + } + + if ( Campaign.missions[i].num_events > 0 ){ + free ( Campaign.missions[i].events ); + } + + if ( !Fred_running ){ + sexp_unmark_persistent(Campaign.missions[i].formula); // free any sexpression nodes used by campaign. + } + + Campaign.missions[i].num_goals = 0; + Campaign.missions[i].num_events = 0; + } +} + +// extract the mission filenames for a campaign. +// +// filename => name of campaign file +// dest => storage for the mission filename, must be already allocated +// num => output parameter for the number of mission filenames in the campaign +// +// note that dest should allocate at least dest[MAX_CAMPAIGN_MISSIONS][NAME_LENGTH] +int mission_campaign_get_filenames(char *filename, char dest[][NAME_LENGTH], int *num) +{ + int rval; + + // read the mission file and get the list of mission filenames + if ((rval = setjmp(parse_abort)) != 0) { + return rval; + + } else { + read_file_text(filename); + Assert(strlen(filename) < MAX_FILENAME_LEN - 1); // make sure no overflow + + reset_parse(); + required_string("$Name:"); + advance_to_eoln(NULL); + + required_string( "$Type:" ); + advance_to_eoln(NULL); + + // parse the mission file and actually read in the mission stuff + *num = 0; + while ( skip_to_string("$Mission:") == 1 ) { + stuff_string(dest[*num], F_NAME, NULL); + (*num)++; + } + } + + return 0; +} + +// function to read the goals and events from a mission in a campaign file and store that information +// in the campaign structure for use in the campaign editor, error checking, etc +void read_mission_goal_list(int num) +{ + char *filename, notes[NOTES_LENGTH], goals[MAX_GOALS][NAME_LENGTH]; + char events[MAX_MISSION_EVENTS][NAME_LENGTH]; + int i, z, r, event_count, count = 0; + + filename = Campaign.missions[num].name; + if ((r = setjmp(parse_abort))>0) { + Warning(LOCATION, "Error reading \"%s\" (code = %d)", filename, r); + return; + } + + // open localization + lcl_ext_open(); + + read_file_text(filename); + init_parse(); + + // first, read the mission notes for this mission. Used in campaign editor + if (skip_to_string("#Mission Info")) { + if (skip_to_string("$Notes:")) { + stuff_string(notes, F_NOTES, NULL); + if (Campaign.missions[num].notes){ + free(Campaign.missions[num].notes); + } + + Campaign.missions[num].notes = (char *) malloc(strlen(notes) + 1); + strcpy(Campaign.missions[num].notes, notes); + } + } + + event_count = 0; + // skip to events section in the mission file. Events come before goals, so we process them first + if ( skip_to_string("#Events") ) { + while (1) { + if (skip_to_string("$Formula:", "#Goals") != 1){ + break; + } + + z = skip_to_string("+Name:", "$Formula:"); + if (!z){ + break; + } + + if (z == 1){ + stuff_string(events[event_count], F_NAME, NULL); + } else { + sprintf(events[event_count], NOX("Event #%d"), event_count + 1); + } + + event_count++; + Assert(event_count < MAX_MISSION_EVENTS); + } + } + + count = 0; + if (skip_to_string("#Goals")) { + while (1) { + if (skip_to_string("$Type:", "#End") != 1){ + break; + } + + z = skip_to_string("+Name:", "$Type:"); + if (!z){ + break; + } + + if (z == 1){ + stuff_string(goals[count], F_NAME, NULL); + } else { + sprintf(goals[count], NOX("Goal #%d"), count + 1); + } + + count++; + Assert(count < MAX_GOALS); + } + } + + Campaign.missions[num].num_goals = count; + if (count) { + Campaign.missions[num].goals = (mgoal *) malloc(count * sizeof(mgoal)); + Assert(Campaign.missions[num].goals); // make sure we got the memory + memset(Campaign.missions[num].goals, 0, count * sizeof(mgoal)); + + for (i=0; icurrent_campaign)) + return mission_campaign_load(Player->current_campaign); + else + return mission_campaign_load(BUILTIN_CAMPAIGN); +} + +// for end of campaign in the single player game. Called when the end of campaign state is +// entered, which is triggered when the end-campaign sexpression is hit + +void mission_campaign_end_init() +{ + // no need to do any initialization. +} + +void mission_campaign_end_do() +{ + // play the movies + event_music_level_close(); + mission_goal_fail_incomplete(); + scoring_level_close(); + mission_campaign_mission_over(); + + // eventually we'll want to play one of two options (good ending or bad ending) + // did the supernova blow? + if(Supernova_status == SUPERNOVA_HIT){ + // no soup for you! + // movie_play_two("endpart1.mve", "endprt2b.mve"); // good ending + } else { + // no soup for you! + // movie_play_two("endpart1.mve", "endprt2a.mve"); // good ending + } + +#ifdef FS2_DEMO + gameseq_post_event( GS_EVENT_END_DEMO ); +#else + gameseq_post_event( GS_EVENT_MAIN_MENU ); +#endif +} + +void mission_campaign_end_close() +{ + // nothing to do here. +} + + +// skip to the next mission in the campaign +// this also posts the state change by default. pass 0 to override that +void mission_campaign_skip_to_next(int start_game) +{ + // mark all goals/events complete + // these do not really matter, since is-previous-event-* and is-previous-goal-* sexps check + // to see if the mission was skipped, and use defaults accordingly. + mission_goal_mark_objectives_complete(); + mission_goal_mark_events_complete(); + + // mark mission as skipped + Campaign.missions[Campaign.current_mission].flags |= CMISSION_FLAG_SKIPPED; + + // store + mission_campaign_store_goals_and_events(); + + // now set the next mission + mission_campaign_eval_next_mission(); + + // clear out relevant player vars + Player->failures_this_session = 0; + Player->show_skip_popup = 1; + + if (start_game) { + // proceed to next mission or main hall + if ((Campaign.missions[Campaign.current_mission].has_mission_loop) && (Campaign.loop_mission != -1)) { + // go to loop solicitation + gameseq_post_event(GS_EVENT_LOOP_BRIEF); + } else { + // closes out mission stuff, sets up next one + mission_campaign_mission_over(); + + if ( Campaign.next_mission == -1 ) { + // go to main hall, tha campaign is over! + gameseq_post_event(GS_EVENT_MAIN_MENU); + } else { + // go to next mission + gameseq_post_event(GS_EVENT_START_GAME); + } + } + } +} + + +// breaks your ass out of the loop +// this also posts the state change +void mission_campaign_exit_loop() +{ + // set campaign to loop reentry point + Campaign.next_mission = Campaign.loop_reentry; + Campaign.current_mission = -1; + Campaign.loop_enabled = 0; + + // set things up for next mission + mission_campaign_next_mission(); + gameseq_post_event(GS_EVENT_START_GAME); +} + + +// used for jumping to a particular campaign mission +// all pvs missions marked skipped +// this relies on correct mission ordering in the campaign file +void mission_campaign_jump_to_mission(char *name) +{ + int i = 0; + char dest_name[64]; + + // load in the campaign junk + mission_load_up_campaign(); + + // tack the .fs2 onto the input name + strcpy(dest_name, name); + strcat(name, ".fs2"); + + // search for our mission + for (i=0; i from headers which included it. Made psnet_socket + * type which is defined just as SOCKET type is. + * + * 90 1/10/98 1:14p John + * Added explanation to debug console commands + * + * 89 1/08/98 10:26a Lawrance + * Delay directive success sound effect about 1/2 second. + * + * 88 1/07/98 6:46p Lawrance + * If a goal is invalid, ignore it when evaluating mission success. + * + * 87 1/07/98 11:09a Lawrance + * Add sound hook for when directive gets completed. + * + * 86 1/02/98 3:07p Hoffoss + * Changed sexp evaluation to every half second. + * + * 85 12/27/97 8:08p Lawrance + * get savegames working again + * + * 84 12/26/97 10:02p Lawrance + * Add event music for when goals fail. + * + * 83 12/22/97 6:07p Hoffoss + * Made directives flash when completed, fixed but with is-destroyed + * operator. + * + * 82 12/21/97 4:33p John + * Made debug console functions a class that registers itself + * automatically, so you don't need to add the function to + * debugfunctions.cpp. + * + * 81 12/19/97 12:43p Hoffoss + * Changed code to allow counts in directives. + * + * 80 12/15/97 5:26p Allender + * temporary code to display for 5 second completion status of objectives + * + * 79 12/03/97 4:16p Hoffoss + * Changed sound stuff used in interface screens for interface purposes. + * + * 78 12/03/97 11:35a Hoffoss + * Made changes to HUD messages send throughout the game. + * + * 77 12/02/97 10:47a Hoffoss + * Changed mention of goals to objectives. + * + * 76 12/01/97 12:26a Lawrance + * Add flag MGF_NO_MUSIC to mission_goal struct, to avoid playing music + * for certain goals + * + * 75 11/21/97 2:16p Allender + * debug keys to mark all goals (p/s/b) as satisfied + * + * 74 11/17/97 11:45a Johnson + * when marking goals false, add log entry. Be sure that "delay" function + * check for equal to delay as well + * + * 73 11/13/97 10:16p Hoffoss + * Added icons to mission log scrollback. + * + * 72 11/13/97 4:05p Hoffoss + * Added hiding code for mission log entries. + * + * 71 11/05/97 7:11p Hoffoss + * Made changed to the hud message system. Hud messages can now have + * sources so they can be color coded. + * + * 70 11/02/97 10:09p Lawrance + * add missing fields from mission_event to save/restore + * + * 69 10/31/97 4:28p Allender + * fail all incomplete mission goals when mission is over + * + * 68 10/29/97 11:06a Jasen + * eval goals/events every 1.75 seconds instead of every 2.5 seconds for + * slightly more accurate timing of events + * + * 67 10/28/97 10:05a Allender + * added function to mark all goals as failed + * + * 66 10/28/97 9:33a Lawrance + * change 'Alt-Q' in HUD message to use text for bound key + * + * 65 10/27/97 5:03p Hoffoss + * Fixed comment. + * + * 64 10/23/97 2:16p Johnson + * make pointers point to NULl after freeing to prevent problems when + * tried to free multiple times + * + * 63 10/16/97 2:35p Hoffoss + * Enhanced the mission goals screen a little. + * + * 62 10/16/97 1:28p Hoffoss + * New mission goals screen implemented. + * + * 61 10/10/97 6:15p Hoffoss + * Implemented a training objective list display. + * + * 60 10/09/97 4:44p Hoffoss + * Dimmed training window glass and made it less transparent, added flags + * to events, set he stage for detecting current events. + * + * 59 10/06/97 4:11p Lawrance + * add missing field from mission_events to save/restore + * + * 58 10/03/97 4:14p Hoffoss + * Augmented event debug view code. + * + * 57 9/30/97 10:01a Hoffoss + * Added event chaining support to Fred and FreeSpace. + * + * 56 9/29/97 1:58p Hoffoss + * Need to check in changes so I can get merged code to continue working + * on event chaining code. + * +*/ + +#include "freespace.h" +#include "object.h" +#include "missiongoals.h" +#include "missionparse.h" +#include "missionlog.h" +#include "missiontraining.h" +#include "missionscreencommon.h" +#include "gamesequence.h" +#include "hud.h" +#include "key.h" +#include "2d.h" +#include "timer.h" +#include "linklist.h" +#include "ship.h" +#include "ai.h" +#include "parselo.h" +#include "sexp.h" +#include "eventmusic.h" +#include "multi.h" +#include "multimsgs.h" +#include "stand_gui.h" +#include "ui.h" +#include "bmpman.h" +#include "sound.h" +#include "gamesnd.h" +#include "alphacolors.h" +#include "multi_team.h" + +// timestamp stuff for evaluating mission goals +#define GOAL_TIMESTAMP 0 // make immediately eval +#define GOAL_TIMESTAMP_TRAINING 500 // every half second + +#define MAX_GOALS_PER_LIST 15 +#define MAX_GOAL_LINES 200 + +// indicies for coordinates +#define GOAL_SCREEN_X_COORD 0 +#define GOAL_SCREEN_Y_COORD 1 +#define GOAL_SCREEN_W_COORD 2 +#define GOAL_SCREEN_H_COORD 3 + +/* +#define GOAL_SCREEN_TEXT_X 81 +#define GOAL_SCREEN_TEXT_Y 95 +#define GOAL_SCREEN_TEXT_W 385 +#define GOAL_SCREEN_TEXT_H 299 +#define GOAL_SCREEN_ICON_X 45 +*/ + +static int Goal_screen_text_coords[GR_NUM_RESOLUTIONS][4] = { + { + 81,95,385,299 // GR_640 + }, + { + 130,152,385,299 // GR_1024 + } +}; + +static int Goal_screen_icon_xcoord[GR_NUM_RESOLUTIONS] = { + 45, // GR_640 + 72 // GR_1024 +}; + + +#if defined(GERMAN_BUILD) + // german version gets slightly diff coords + static int Objective_key_text_coords[GR_NUM_RESOLUTIONS][3][2] = { + { + // GR_640 + {175, 344}, + {316, 344}, + {432, 344} + }, + { + // GR_1024 + {310, 546}, + {536, 546}, + {688, 546} + } + }; + static int Objective_key_icon_coords[GR_NUM_RESOLUTIONS][3][2] = { + { + // GR_640 + {150, 339}, + {290, 339}, + {406, 339} + }, + { + // GR_1024 + {272, 542}, + {498, 542}, + {650, 542} + } +}; +#else + static int Objective_key_text_coords[GR_NUM_RESOLUTIONS][3][2] = { + { + // GR_640 + {195, 344}, + {306, 344}, + {432, 344} + }, + { + // GR_1024 + {310, 546}, + {486, 546}, + {688, 546} + } + }; + static int Objective_key_icon_coords[GR_NUM_RESOLUTIONS][3][2] = { + { + // GR_640 + {170, 339}, + {280, 339}, + {406, 339} + }, + { + // GR_1024 + {272, 542}, + {448, 542}, + {650, 542} + } +}; +#endif + + +#define NUM_GOAL_SCREEN_BUTTONS 3 // total number of buttons +#define GOAL_SCREEN_BUTTON_SCROLL_UP 0 +#define GOAL_SCREEN_BUTTON_SCROLL_DOWN 1 +#define GOAL_SCREEN_BUTTON_RETURN 2 + +struct goal_list { + int count; + mission_goal *list[MAX_GOALS_PER_LIST]; + int line_offsets[MAX_GOALS_PER_LIST]; + int line_spans[MAX_GOALS_PER_LIST]; + + goal_list() : count(0) {} + void add(mission_goal *m); + void set(); + void icons_display(int y); +}; + +struct goal_buttons { + char *filename; + int x, y; + int hotspot; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + goal_buttons(char *name, int x1, int y1, int h) : filename(name), x(x1), y(y1), hotspot(h) {} +}; + +struct goal_text { + int m_num_lines; + int m_line_sizes[MAX_GOAL_LINES]; + char *m_lines[MAX_GOAL_LINES]; + + void init(); + int add(char *text = NULL); + void display(int n, int y); +}; + +int Num_mission_events; +int Num_goals = 0; // number of goals for this mission +int Event_index; // used by sexp code to tell what event it came from +int Mission_goal_timestamp; + +mission_event Mission_events[MAX_MISSION_EVENTS]; +mission_goal Mission_goals[MAX_GOALS]; // structure for the goals of this mission +goal_text Goal_text; + +#define DIRECTIVE_SOUND_DELAY 500 // time directive success sound effect is delayed +#define DIRECTIVE_SPECIAL_DELAY 7000 // mark special directives as true after 7 seconds + +static int Mission_directive_sound_timestamp; // timestamp to control when directive succcess sound gets played +static int Mission_directive_special_timestamp; // used to specially mark a directive as true even though it's not + +char *Goal_type_text(int n) +{ + switch (n) { + case 0: + return XSTR( "Primary", 396); + + case 1: + return XSTR( "Secondary", 397); + + case 2: + return XSTR( "Bonus", 398); + } + + return NULL; +}; + +static int Goal_screen_text_x; +static int Goal_screen_text_y; +static int Goal_screen_text_w; +static int Goal_screen_text_h; +static int Goal_screen_icon_x; + +static goal_list Primary_goal_list; +static goal_list Secondary_goal_list; +static goal_list Bonus_goal_list; + +static int Scroll_offset; +static int Goals_screen_bg_bitmap; +static int Goal_complete_bitmap; +static int Goal_incomplete_bitmap; +static int Goal_failed_bitmap; +static UI_WINDOW Goals_screen_ui_window; + +goal_buttons Goal_buttons[NUM_GOAL_SCREEN_BUTTONS] = { +//XSTR:OFF + goal_buttons("MOB_00", 475, 288, 0), + goal_buttons("MOB_01", 475, 336, 1), + goal_buttons("MOB_02", 553, 409, 2), +//XSTR:ON +}; + +void goal_screen_button_pressed(int num); +void goal_screen_scroll_up(); +void goal_screen_scroll_down(); + +// +/// goal_list structure functions +// + +void goal_list::add(mission_goal *m) +{ + Assert(count < MAX_GOALS_PER_LIST); + list[count++] = m; +} + +void goal_list::set() +{ + int i; + + for (i=0; imessage); + + if (i < count - 1) + Goal_text.add(); + } +} + +void goal_list::icons_display(int yoff) +{ + int i, y, ys, bmp, font_height; + + font_height = gr_get_font_height(); + for (i=0; isatisfied) { + case GOAL_COMPLETE: + bmp = Goal_complete_bitmap; + break; + + case GOAL_INCOMPLETE: + bmp = Goal_incomplete_bitmap; + break; + + case GOAL_FAILED: + bmp = Goal_failed_bitmap; + break; + } + + if (bmp >= 0) { + bm_get_info(bmp, NULL, &ys, NULL); + y = Goal_screen_text_y // offset of text window on screen + + y * font_height // relative line position offset + + line_spans[i] * font_height / 2 // center of text offset + - ys / 2; // center of icon offest + + if ((y >= Goal_screen_text_y - ys / 2) && (y + ys <= Goal_screen_text_y + Goal_screen_text_h + ys / 2)) { + gr_set_bitmap(bmp); + gr_bitmap(Goal_screen_icon_x, y); + } + } + } +} + +// +/// goal_text structure functions +// + +// initializes the goal text struct (empties it out) +void goal_text::init() +{ + m_num_lines = 0; +} + +// Adds lines of goal text. If passed NULL (or nothing passed) a blank line is added. If +// the text is too long, it is automatically split into more than one line. +// Returns the number of lines added. +int goal_text::add(char *text) +{ + int max, count; + + max = MAX_GOAL_LINES - m_num_lines; + if (max < 1) { + Error(LOCATION, "Goal text space exhausted"); + return 0; + } + + if (!text) { + m_lines[m_num_lines] = NULL; + m_line_sizes[m_num_lines++] = 0; + return 1; + } + + count = split_str(text, Goal_screen_text_w - Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_X_COORD] + Goal_screen_icon_xcoord[gr_screen.res], m_line_sizes + m_num_lines, m_lines + m_num_lines, max); + m_num_lines += count; + return count; +} + +// Display a line of goal text +// n = goal text line number +// y = y offset to draw relative to goal text area top +void goal_text::display(int n, int y) +{ + int y1, w, h; + char buf[MAX_GOAL_TEXT]; + + if ((n < 0) || (n >= m_num_lines) || (m_line_sizes[n] < 1)) + return; // out of range, don't draw anything + + Assert(m_line_sizes[n] < MAX_GOAL_TEXT); + y += Goal_screen_text_y; + if (*m_lines[n] == '*') { // header line + gr_set_color_fast(&Color_text_heading); + strncpy(buf, m_lines[n] + 1, m_line_sizes[n] - 1); + buf[m_line_sizes[n] - 1] = 0; + + gr_get_string_size(&w, &h, buf); + y1 = y + h / 2 - 1; + gr_line(Goal_screen_icon_x, y1, Goal_screen_text_x - 2, y1); + gr_line(Goal_screen_text_x + w + 1, y1, Goal_screen_icon_x + Goal_screen_text_w, y1); + + } else { + gr_set_color_fast(&Color_text_normal); + strncpy(buf, m_lines[n], m_line_sizes[n]); + buf[m_line_sizes[n]] = 0; + } + + gr_printf(Goal_screen_text_x, y, buf); +} + +// mission_init_goals: initializes info for goals. Called as part of mission initialization. +void mission_init_goals() +{ + int i; + + Num_goals = 0; + for (i=0; ibutton.create(&Goals_screen_ui_window, "", b->x, b->y, 60, 30, (i < 2), 1); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed + Goal_buttons[GOAL_SCREEN_BUTTON_SCROLL_UP].button.set_hotkey(KEY_UP); + Goal_buttons[GOAL_SCREEN_BUTTON_SCROLL_DOWN].button.set_hotkey(KEY_DOWN); + + Goals_screen_bg_bitmap = bm_load("ObjectivesBG"); + Goal_complete_bitmap = bm_load("ObjComp"); + Goal_incomplete_bitmap = bm_load("ObjIncomp"); + Goal_failed_bitmap = bm_load("ObjFail"); + + + // if (Goal_incomplete_bitmap < 0) Int3(); + + if (Goals_screen_bg_bitmap < 0) { + Warning(LOCATION, "Could not load the background bitmap: ObjectivesBG.pcx"); + } +} + +// cleanup called when exiting the show goals screen +void mission_show_goals_close() +{ + if (Goals_screen_bg_bitmap >= 0) + bm_unload(Goals_screen_bg_bitmap); + + if (Goal_complete_bitmap) + bm_unload(Goal_complete_bitmap); + + if (Goal_incomplete_bitmap) + bm_unload(Goal_incomplete_bitmap); + + if (Goal_failed_bitmap) + bm_unload(Goal_failed_bitmap); + + Goals_screen_ui_window.destroy(); + common_free_interface_palette(); // restore game palette + game_flush(); +} + +// called once a frame during show goals state to process events and render the screen +void mission_show_goals_do_frame(float frametime) +{ + int k, i, y, z; + int font_height = gr_get_font_height(); + + k = Goals_screen_ui_window.process(); + switch (k) { + case KEY_ESC: + mission_goal_exit(); + break; + + case KEY_DOWN: + goal_screen_scroll_down(); + break; + + case KEY_UP: + goal_screen_scroll_up(); + break; + + default: + // do nothing + break; + } // end switch + + for (i=0; i= 0) { + gr_set_bitmap(Goals_screen_bg_bitmap); + gr_bitmap(0, 0); + } + Goals_screen_ui_window.draw(); + + y = 0; + z = Scroll_offset; + while (y + font_height <= Goal_screen_text_h) { + Goal_text.display(z, y); + y += font_height; + z++; + } + + Primary_goal_list.icons_display(Scroll_offset); + Secondary_goal_list.icons_display(Scroll_offset); + Bonus_goal_list.icons_display(Scroll_offset); + + gr_flip(); +} + +// Mission Log Objectives subscreen init. +// Called once right before entering the Mission Log screen to do initializations. +int ML_objectives_init(int x, int y, int w, int h) +{ + int i, type, team_num; + + Primary_goal_list.count = 0; + Secondary_goal_list.count = 0; + Bonus_goal_list.count = 0; + + Goal_screen_text_x = x - Goal_screen_icon_xcoord[gr_screen.res] + Goal_screen_text_coords[gr_screen.res][GOAL_SCREEN_X_COORD]; + Goal_screen_text_y = y; + Goal_screen_text_w = w; + Goal_screen_text_h = h; + Goal_screen_icon_x = x; + + team_num = 0; // this is the default team -- we will change it if in a multiplayer team v. team game + if ( (Game_mode & GM_MULTIPLAYER) && (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS) ){ + team_num = Net_player->p_info.team; + } + + // fill up the lists so we can display the goals appropriately + for (i=0; i= 0) { + bm_unload(Goal_complete_bitmap); + } + + if (Goal_incomplete_bitmap >= 0) { + bm_unload(Goal_incomplete_bitmap); + } + + if (Goal_failed_bitmap >= 0) { + bm_unload(Goal_failed_bitmap); + } +} + +void ML_objectives_do_frame(int scroll_offset) +{ + int y, z; + int font_height = gr_get_font_height(); + + y = 0; + z = scroll_offset; + while (y + font_height <= Goal_screen_text_h) { + Goal_text.display(z, y); + y += font_height; + z++; + } + + Primary_goal_list.icons_display(scroll_offset); + Secondary_goal_list.icons_display(scroll_offset); + Bonus_goal_list.icons_display(scroll_offset); +} + +void ML_render_objectives_key() +{ + // display icon key at the bottom + gr_set_bitmap(Goal_complete_bitmap); + gr_bitmap(Objective_key_icon_coords[gr_screen.res][0][0], Objective_key_icon_coords[gr_screen.res][0][1]); + gr_set_bitmap(Goal_incomplete_bitmap); + gr_bitmap(Objective_key_icon_coords[gr_screen.res][1][0], Objective_key_icon_coords[gr_screen.res][1][1]); + gr_set_bitmap(Goal_failed_bitmap); + gr_bitmap(Objective_key_icon_coords[gr_screen.res][2][0], Objective_key_icon_coords[gr_screen.res][2][1]); + + gr_string(Objective_key_text_coords[gr_screen.res][0][0], Objective_key_text_coords[gr_screen.res][0][1] , XSTR("Complete", 1437)); + gr_string(Objective_key_text_coords[gr_screen.res][1][0], Objective_key_text_coords[gr_screen.res][1][1] , XSTR("Incomplete", 1438)); + gr_string(Objective_key_text_coords[gr_screen.res][2][0], Objective_key_text_coords[gr_screen.res][2][1] , XSTR("Failed", 1439)); +} + + +// temporary hook for temporarily displaying objective completion/failure +// extern void message_training_add_simple( char *text ); + +void mission_goal_status_change( int goal_num, int new_status) +{ + int type; + + Assert(goal_num < Num_goals); + Assert((new_status == GOAL_FAILED) || (new_status == GOAL_COMPLETE)); + + // if in a multiplayer game, send a status change to clients + if ( MULTIPLAYER_MASTER ){ + send_mission_goal_info_packet( goal_num, new_status, -1 ); + } + + type = Mission_goals[goal_num].type & GOAL_TYPE_MASK; + Mission_goals[goal_num].satisfied = new_status; + if ( new_status == GOAL_FAILED ) { + // don't display bonus goal failure + if ( type != BONUS_GOAL ) { + + // only do HUD and music is goals are my teams goals. + if ( (Game_mode & GM_NORMAL) || ((Net_player != NULL) && (Net_player->p_info.team == Mission_goals[goal_num].team)) ) { + hud_add_objective_messsage(type, new_status); + if ( !Mission_goals[goal_num].flags & MGF_NO_MUSIC ) { // maybe play event music + event_music_primary_goal_failed(); + } + //HUD_sourced_printf(HUD_SOURCE_FAILED, "%s goal failed at time %6.1f!", Goal_type_text(type), f2fl(Missiontime) ); + } + } + mission_log_add_entry( LOG_GOAL_FAILED, Mission_goals[goal_num].name, NULL, goal_num ); + } else if ( new_status == GOAL_COMPLETE ) { + if ( (Game_mode & GM_NORMAL) || ((Net_player != NULL) && (Net_player->p_info.team == Mission_goals[goal_num].team))) { + hud_add_objective_messsage(type, new_status); + // cue for Event Music + if ( !(Mission_goals[goal_num].flags & MGF_NO_MUSIC) ) { + event_music_primary_goals_met(); + } + mission_log_add_entry( LOG_GOAL_SATISFIED, Mission_goals[goal_num].name, NULL, goal_num ); + } + + if(Game_mode & GM_MULTIPLAYER){ + // squad war + multi_team_maybe_add_score((int)(Mission_goals[goal_num].score * scoring_get_scale_factor()), Mission_goals[goal_num].team); + } else { + // deal with the score + Player->stats.m_score += (int)(Mission_goals[goal_num].score * scoring_get_scale_factor()); + } + } +} + +// return value: +// EVENT_UNBORN = event has yet to be available (not yet evaluatable) +// EVENT_CURRENT = current (evaluatable), but not yet true +// EVENT_SATISFIED = event has occured (true) +// EVENT_FAILED = event failed, can't possibly become true anymore +int mission_get_event_status(int event) +{ + // check for directive special events first. We will always return from this part of the if statement + if ( Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL ) { + + // if this event is temporarily true, return as such + if ( Mission_events[event].flags & MEF_DIRECTIVE_TEMP_TRUE ){ + return EVENT_SATISFIED; + } + + // if the timestamp has elapsed, we can "mark" this directive as true although it's really not. + if ( timestamp_elapsed(Mission_directive_special_timestamp) ) { + Mission_events[event].satisfied_time = Missiontime; + Mission_events[event].flags |= MEF_DIRECTIVE_TEMP_TRUE; + } + + return EVENT_CURRENT; + } else if (Mission_events[event].flags & MEF_CURRENT) { + if (!Mission_events[event].born_on_date){ + Mission_events[event].born_on_date = timestamp(); + } + + if (Mission_events[event].result) { + return EVENT_SATISFIED; + } + + if (Mission_events[event].formula < 0) { + return EVENT_FAILED; + } + + return EVENT_CURRENT; + } + + return EVENT_UNBORN; +} + +void mission_event_set_directive_special(int event) +{ + // bogus + if((event < 0) || (event >= Num_mission_events)){ + return; + } + + Mission_events[event].flags |= MEF_DIRECTIVE_SPECIAL; + + // start from a known state + Mission_events[event].flags &= ~MEF_DIRECTIVE_TEMP_TRUE; + Mission_directive_special_timestamp = timestamp(DIRECTIVE_SPECIAL_DELAY); +} + +void mission_event_unset_directive_special(int event) +{ + // bogus + if((event < 0) || (event >= Num_mission_events)){ + return; + } + + Mission_events[event].flags &= ~(MEF_DIRECTIVE_SPECIAL); + + // this event may be marked temporarily true -- if so, then unmark this value!!! + if ( Mission_events[event].flags & MEF_DIRECTIVE_TEMP_TRUE ){ + Mission_events[event].flags &= ~MEF_DIRECTIVE_TEMP_TRUE; + } + Mission_events[event].satisfied_time = 0; + Mission_directive_special_timestamp = timestamp(-1); +} + +// function which evaluates and processes the given event +void mission_process_event( int event ) +{ + int store_flags = Mission_events[event].flags; + int store_formula = Mission_events[event].formula; + int store_result = Mission_events[event].result; + int store_count = Mission_events[event].count; + + int result, sindex; + + Directive_count = 0; + Event_index = event; + sindex = Mission_events[event].formula; + result = Mission_events[event].result; + + // if chained, insure that previous event is true and next event is false + if (Mission_events[event].chain_delay >= 0) { // this indicates it's chained + if (event > 0){ + if (!Mission_events[event - 1].result || ((fix) Mission_events[event - 1].timestamp + i2f(Mission_events[event].chain_delay) > Missiontime)){ + sindex = -1; // bypass evaluation + } + } + + if ((event < Num_mission_events - 1) && Mission_events[event + 1].result && (Mission_events[event + 1].chain_delay >= 0)){ + sindex = -1; // bypass evaluation + } + } + + if (sindex >= 0) { + Sexp_useful_number = 1; + result = eval_sexp(sindex); + + // if the directive count is a special value, deal with that first. Mark the event as a special + // event, and unmark it when the directive is true again. + if ( (Directive_count == DIRECTIVE_WING_ZERO) && !(Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL) ) { + // make it special - which basically just means that its true until the next wave arrives + mission_event_set_directive_special(event); + + Directive_count = 0; + } else if ( (Mission_events[event].flags & MEF_DIRECTIVE_SPECIAL) && Directive_count > 1 ) { + // make it non special + mission_event_unset_directive_special(event); + } + + if (Mission_events[event].count || (Directive_count > 1)){ + Mission_events[event].count = Directive_count; + } + + if (Sexp_useful_number){ + Mission_events[event].flags |= MEF_CURRENT; + } + } + + Event_index = 0; + Mission_events[event].result = result; + + // if the sexpression is known false, then no need to evaluate anymore + if ((sindex >= 0) && (Sexp_nodes[sindex].value == SEXP_KNOWN_FALSE)) { + Mission_events[event].timestamp = (int) Missiontime; + Mission_events[event].satisfied_time = Missiontime; + Mission_events[event].repeat_count = -1; + Mission_events[event].formula = -1; + return; + } + + if (result && !Mission_events[event].satisfied_time) { + Mission_events[event].satisfied_time = Missiontime; + if ( Mission_events[event].objective_text ) { + Mission_directive_sound_timestamp = timestamp(DIRECTIVE_SOUND_DELAY); + } + } + + // decrement the repeat count. When at 0, don't eval this function anymore + if ( result || timestamp_valid(Mission_events[event].timestamp) ) { + Mission_events[event].repeat_count--; + if ( Mission_events[event].repeat_count <= 0 ) { + Mission_events[event].timestamp = (int)Missiontime; + Mission_events[event].formula = -1; + + if(Game_mode & GM_MULTIPLAYER){ + // squad war + multi_team_maybe_add_score((int)(Mission_events[event].score * scoring_get_scale_factor()), Mission_events[event].team); + } else { + // deal with the player's score + Player->stats.m_score += (int)(Mission_events[event].score * scoring_get_scale_factor()); + } + } else { + // set the timestamp to time out 'interval' seconds in the future. We must also reset the + // value at the sexpresion node to unknown so that it will get reevaled + Mission_events[event].timestamp = timestamp( Mission_events[event].interval * 1000 ); +// Sexp_nodes[Mission_events[event].formula].value = SEXP_UNKNOWN; + } + } + + // see if anything has changed + if(MULTIPLAYER_MASTER && ((store_flags != Mission_events[event].flags) || (store_formula != Mission_events[event].formula) || (store_result != Mission_events[event].result) || (store_count != Mission_events[event].count)) ){ + send_event_update_packet(event); + } +} + +// Maybe play a directive success sound... need to poll since the sound is delayed from when +// the directive is actually satisfied. +void mission_maybe_play_directive_success_sound() +{ + if ( timestamp_elapsed(Mission_directive_sound_timestamp) ) { + Mission_directive_sound_timestamp=0; + snd_play( &Snds[SND_DIRECTIVE_COMPLETE] ); + } +} + +void mission_eval_goals() +{ + int i, result, goal_changed = 0; + + // before checking whether or not we should evaluate goals, we should run through the events and + // process any whose timestamp is valid and has expired. This would catch repeating events only + for (i=0; i= Num_goals ) { + dc_printf ("First parameter must be a valid goal number.\n"); + return; + } + + num = Dc_arg_int; + dc_get_arg(ARG_TRUE|ARG_FALSE|ARG_STRING); + if ( Dc_arg_type & ARG_TRUE ) + Mission_goals[num].satisfied = GOAL_COMPLETE; + else if ( Dc_arg_type & ARG_FALSE ) + Mission_goals[num].satisfied = GOAL_FAILED; + else if ( Dc_arg_type & ARG_NONE ) + Mission_goals[num].satisfied = GOAL_INCOMPLETE; + else if ( Dc_arg_type & ARG_STRING) { + if ( !stricmp(Dc_arg, "satisfied") ) + Mission_goals[num].satisfied = GOAL_COMPLETE; + else if ( !stricmp( Dc_arg, "failed") ) + Mission_goals[num].satisfied = GOAL_FAILED; + else if ( !stricmp( Dc_arg, "unknown") ) + Mission_goals[num].satisfied = GOAL_INCOMPLETE; + else + dc_printf("Unknown status %s. Use 'satisfied', 'failed', or 'unknown'\n", Dc_arg); + } + } + + if ( Dc_help ) { + dc_printf("Usage: change_mission_goal \n"); + dc_printf(" -- Integer number of goal to change. See show_mission_goals\n"); + dc_printf(" -- [bool] where a true value makes the goal satisfied,\n"); + dc_printf(" a false value makes the goal failed.\n"); + dc_printf("The field may also be one of 'satisfied', 'failed', or 'unknown'\n"); + dc_printf("\nExamples:\n\n'change_mission_goal 1 true' makes goal 1 successful.\n"); + dc_printf("'change_mission_goal 2' marks goal 2 not complete\n"); + dc_printf("'change_mission_goal 0 satisfied' marks goal 0 as satisfied\n"); + Dc_status = 0; + } +} +//XSTR:ON + +// debug functions to mark all primary/secondary/bonus goals as true +#ifndef DEBUG + +void mission_goal_mark_all_true(int type) +{ + int i; + + for (i = 0; i < Num_goals; i++ ) { + if ( (Mission_goals[i].type & GOAL_TYPE_MASK) == type ) + Mission_goals[i].satisfied = GOAL_COMPLETE; + } + + HUD_sourced_printf(HUD_SOURCE_HIDDEN, NOX("All %s goals marked true"), Goal_type_text(type) ); +} + +#endif + +void goal_screen_button_pressed(int num) +{ + switch (num) { + case GOAL_SCREEN_BUTTON_SCROLL_UP: + goal_screen_scroll_up(); + break; + + case GOAL_SCREEN_BUTTON_SCROLL_DOWN: + goal_screen_scroll_down(); + break; + + case GOAL_SCREEN_BUTTON_RETURN: + mission_goal_exit(); + break; + } +} + +void goal_screen_scroll_up() +{ + if (Scroll_offset) { + Scroll_offset--; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +void goal_screen_scroll_down() +{ + int max_lines; + + max_lines = Goal_screen_text_h / gr_get_font_height(); + if (Scroll_offset + max_lines < Goal_text.m_num_lines) { + Scroll_offset++; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// Return the number of resolved goals in num_resolved, return the total +// number of valid goals in total +void mission_goal_fetch_num_resolved(int desired_type, int *num_resolved, int *total, int team) +{ + int i,type; + + *num_resolved=0; + *total=0; + + for (i=0; i= 0) && (Mission_goals[i].team != team)){ + continue; + } + + if (Mission_goals[i].type & INVALID_GOAL) { + continue; + } + + type = Mission_goals[i].type & GOAL_TYPE_MASK; + if ( type != desired_type ) { + continue; + } + + *total = *total + 1; + + if (Mission_goals[i].satisfied != GOAL_INCOMPLETE) { + *num_resolved = *num_resolved + 1; + } + } +} + +// Return whether there are any incomplete goals of the specified type +int mission_goals_incomplete(int desired_type, int team) +{ + int i, type; + + for (i=0; i= 0) && (Mission_goals[i].team != team)){ + continue; + } + + if (Mission_goals[i].type & INVALID_GOAL) { + continue; + } + + type = Mission_goals[i].type & GOAL_TYPE_MASK; + if ( type != desired_type ) { + continue; + } + + if (Mission_goals[i].satisfied == GOAL_INCOMPLETE) { + return 1; + } + } + + return 0; +} + +void mission_goal_exit() +{ + snd_play( &Snds_iface[SND_USER_SELECT] ); + gameseq_post_event(GS_EVENT_PREVIOUS_STATE); +} + diff --git a/src/mission/missiongrid.cpp b/src/mission/missiongrid.cpp new file mode 100644 index 0000000..5a7b84e --- /dev/null +++ b/src/mission/missiongrid.cpp @@ -0,0 +1,331 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionGrid.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for grid specific functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 7 10/03/97 8:55a John + * moved Fred's grid_render code out of MissionGrid and into Fred. Added + * code to turn background under overlays grey. + * + * 6 7/28/97 2:21p John + * changed vecmat functions to not return src. Started putting in code + * for inline vector math. Fixed some bugs with optimizer. + * + * 5 7/14/97 12:04a Lawrance + * added function that navmap calls to draw elevation lines + * + * 4 6/24/97 3:47p Hoffoss + * Changed the default grid elevation to 0 instead of -10. + * + * 3 6/18/97 11:36p Lawrance + * move grid rendering code to MissionGrid.cpp + * + * 2 6/12/97 11:25a Lawrance + * added grid_read_camera_controls() + * + * 1 6/12/97 10:17a Lawrance + * + * $NoKeywords: $ + */ + +#include "physics.h" +#include "key.h" +#include "missiongrid.h" +#include "fvi.h" +#include "float.h" +#include "3d.h" +#include "2d.h" + +grid Global_grid; +grid *The_grid; +int double_fine_gridlines = 0; + +void grid_read_camera_controls( control_info * ci, float frametime ) +{ + float kh; + + { + float temp = ci->heading; + float temp1 = ci->pitch; + memset( ci, 0, sizeof(control_info) ); + ci->heading = temp; + ci->pitch = temp1; + } + + // From keyboard... + kh = key_down_timef(KEY_PAD6) - key_down_timef(KEY_PAD4); + if (kh == 0.0f) + ci->heading = 0.0f; + else if (kh > 0.0f) { + if (ci->heading < 0.0f) + ci->heading = 0.0f; + } else // kh < 0 + if (ci->heading > 0.0f) + ci->heading = 0.0f; + ci->heading += kh; + + kh = key_down_timef(KEY_PAD8) - key_down_timef(KEY_PAD2); + if (kh == 0.0f) + ci->pitch = 0.0f; + else if (kh > 0.0f) { + if (ci->pitch < 0.0f) + ci->pitch = 0.0f; + } else // kh < 0 + if (ci->pitch > 0.0f) + ci->pitch = 0.0f; + ci->pitch += kh; + + ci->bank = (key_down_timef(KEY_PAD7) - key_down_timef(KEY_PAD9)); + ci->forward = (key_down_timef(KEY_A) - key_down_timef(KEY_Z)); + ci->sideways = (key_down_timef(KEY_PAD3) - key_down_timef(KEY_PAD1)); + ci->vertical = (key_down_timef(KEY_PADMINUS) - key_down_timef(KEY_PADPLUS)); +} + +// Project the viewer's position onto the grid plane. If more than threshold distance +// from grid center, move grid center. +void maybe_create_new_grid(grid* gridp, vector *pos, matrix *orient, int force) +{ + int roundoff; + plane tplane; + vector gpos, tmp, c; + float dist_to_plane; + float square_size, ux, uy, uz; + + ux = tplane.A = gridp->gmatrix.uvec.x; + uy = tplane.B = gridp->gmatrix.uvec.y; + uz = tplane.C = gridp->gmatrix.uvec.z; + tplane.D = gridp->planeD; + + compute_point_on_plane(&c, &tplane, pos); + dist_to_plane = fl_abs(vm_dist_to_plane(pos, &gridp->gmatrix.uvec, &c)); + square_size = 1.0f; + while (dist_to_plane >= 25.0f) + { + square_size *= 10.0f; + dist_to_plane /= 10.0f; + } + + if (fvi_ray_plane(&gpos, &gridp->center, &gridp->gmatrix.uvec, pos, &orient->fvec, 0.0f)<0.0f) { + vector p; + vm_vec_scale_add(&p,pos,&orient->fvec, 100.0f ); + compute_point_on_plane(&gpos, &tplane, &p ); + } + + if (vm_vec_dist(&gpos, &c) > 50.0f * square_size) + { + vm_vec_sub(&tmp, &gpos, &c); + vm_vec_normalize(&tmp); + vm_vec_scale_add(&gpos, &c, &tmp, 50.0f * square_size); + } + + roundoff = (int) square_size * 10; + if (!ux) + gpos.x = fl_roundoff(gpos.x, roundoff); + if (!uy) + gpos.y = fl_roundoff(gpos.y, roundoff); + if (!uz) + gpos.z = fl_roundoff(gpos.z, roundoff); + + if ((square_size != gridp->square_size) || + (gpos.x != gridp->center.x) || + (gpos.y != gridp->center.y) || + (gpos.z != gridp->center.z) || force) + { + gridp->square_size = square_size; + gridp->center = gpos; + modify_grid(gridp); + } +} + +// Create a grid +// *forward is vector pointing forward +// *right is vector pointing right +// *center is center point of grid +// length is length of grid +// width is width of grid +// square_size is size of a grid square +// For example: +// *forward = (0.0, 0.0, 1.0) +// *right = (1.0, 0.0, 0.0) +// *center = (0.0, 0.0, 0.0) +// nrows = 10 +// ncols = 50.0 +// square_size = 10.0 +// will generate a grid of squares 10 long by 5 wide. +// Each grid square will be 10.0 x 10.0 units. +// The center of the grid will be at the global origin. +// The grid will be parallel to the xz plane (because the normal is 0,1,0). +// (In fact, it will be the xz plane because it is centered on the origin.) +// +// Stuffs grid in *gridp. If gridp == NULL, mallocs and returns a grid. +grid *create_grid(grid *gridp, vector *forward, vector *right, vector *center, int nrows, int ncols, float square_size) +{ + int i, ncols2, nrows2, d = 1; + vector dfvec, drvec, cur, cur2, tvec, uvec, save, save2; + + Assert(square_size > 0.0); + if (double_fine_gridlines) + d = 2; + + if (gridp == NULL) + gridp = (grid *) malloc(sizeof(grid)); + + Assert(gridp); + + gridp->center = *center; + gridp->square_size = square_size; + + // Create the plane equation. + Assert(!IS_VEC_NULL(forward)); + Assert(!IS_VEC_NULL(right)); + + vm_vec_copy_normalize(&dfvec, forward); + vm_vec_copy_normalize(&drvec, right); + + vm_vec_cross(&uvec, &dfvec, &drvec); + + Assert(!IS_VEC_NULL(&uvec)); + + gridp->gmatrix.uvec = uvec; + + gridp->planeD = -(center->x * uvec.x + center->y * uvec.y + center->z * uvec.z); + Assert(!_isnan(gridp->planeD)); + + gridp->gmatrix.fvec = dfvec; + gridp->gmatrix.rvec = drvec; + + vm_vec_scale(&dfvec, square_size); + vm_vec_scale(&drvec, square_size); + + vm_vec_scale_add(&cur, center, &dfvec, (float) -nrows * d / 2); + vm_vec_scale_add2(&cur, &drvec, (float) -ncols * d / 2); + vm_vec_scale_add(&cur2, center, &dfvec, (float) -nrows * 5 / 2); + vm_vec_scale_add2(&cur2, &drvec, (float) -ncols * 5 / 2); + save = cur; + save2 = cur2; + + gridp->ncols = ncols; + gridp->nrows = nrows; + ncols2 = ncols / 2; + nrows2 = nrows / 2; + Assert(ncols < MAX_GRIDLINE_POINTS && nrows < MAX_GRIDLINE_POINTS); + + // Create the points along the edges of the grid, so we can just draw lines + // between them to form the grid. + for (i=0; i<=ncols*d; i++) { + gridp->gpoints1[i] = cur; // small, dark gridline points + vm_vec_scale_add(&tvec, &cur, &dfvec, (float) nrows * d); + gridp->gpoints2[i] = tvec; + vm_vec_add2(&cur, &drvec); + } + + for (i=0; i<=ncols2; i++) { + gridp->gpoints5[i] = cur2; // large, brighter gridline points + vm_vec_scale_add(&tvec, &cur2, &dfvec, (float) nrows2 * 10); + gridp->gpoints6[i] = tvec; + vm_vec_scale_add2(&cur2, &drvec, 10.0f); + } + + cur = save; + cur2 = save2; + for (i=0; i<=nrows*d; i++) { + gridp->gpoints3[i] = cur; // small, dark gridline points + vm_vec_scale_add(&tvec, &cur, &drvec, (float) ncols * d); + gridp->gpoints4[i] = tvec; + vm_vec_add2(&cur, &dfvec); + } + + for (i=0; i<=nrows2; i++) { + gridp->gpoints7[i] = cur2; // large, brighter gridline points + vm_vec_scale_add(&tvec, &cur2, &drvec, (float) ncols2 * 10); + gridp->gpoints8[i] = tvec; + vm_vec_scale_add2(&cur2, &dfvec, 10.0f); + } + + return gridp; +} + +// Create a nice grid -- centered at origin, 10x10, 10.0 size squares, in xz plane. +grid *create_default_grid(void) +{ + grid *rgrid; + vector fvec, rvec, cvec; + + rgrid = create_grid(&Global_grid, vm_vec_make(&fvec, 0.0f, 0.0f, 1.0f), + vm_vec_make(&rvec, 1.0f, 0.0f, 0.0f), + vm_vec_make(&cvec, 0.0f, 0.0f, 0.0f), 100, 100, 5.0f); + + physics_init(&rgrid->physics); + return rgrid; +} + +// Rotate and project points and draw a line. +void rpd_line(vector *v0, vector *v1) +{ + vertex tv0, tv1; + + g3_rotate_vertex(&tv0, v0); + g3_rotate_vertex(&tv1, v1); + g3_draw_line(&tv0, &tv1); +} + + +void modify_grid(grid *gridp) +{ + create_grid(gridp, &gridp->gmatrix.fvec, &gridp->gmatrix.rvec, &gridp->center, + gridp->nrows, gridp->ncols, gridp->square_size); +} + +void grid_render_elevation_line(vector *pos, grid* gridp) +{ + vector gpos; // Location of point on grid. + vector tpos; + float dxz; + plane tplane; + vector *gv; + + tplane.A = gridp->gmatrix.uvec.x; + tplane.B = gridp->gmatrix.uvec.y; + tplane.C = gridp->gmatrix.uvec.z; + tplane.D = gridp->planeD; + + compute_point_on_plane(&gpos, &tplane, pos); + + dxz = vm_vec_dist(pos, &gpos)/8.0f; + + gv = &gridp->gmatrix.uvec; + if (gv->x * pos->x + gv->y * pos->y + gv->z * pos->z < -gridp->planeD) + gr_set_color(127, 127, 127); + else + gr_set_color(255, 255, 255); // white + + rpd_line(&gpos, pos); // Line from grid to object center. + + tpos = gpos; + + vm_vec_scale_add2(&gpos, &gridp->gmatrix.rvec, -dxz/2); + vm_vec_scale_add2(&gpos, &gridp->gmatrix.fvec, -dxz/2); + + vm_vec_scale_add2(&tpos, &gridp->gmatrix.rvec, dxz/2); + vm_vec_scale_add2(&tpos, &gridp->gmatrix.fvec, dxz/2); + + rpd_line(&gpos, &tpos); + + vm_vec_scale_add2(&gpos, &gridp->gmatrix.rvec, dxz); + vm_vec_scale_add2(&tpos, &gridp->gmatrix.rvec, -dxz); + + rpd_line(&gpos, &tpos); +} \ No newline at end of file diff --git a/src/mission/missionhotkey.cpp b/src/mission/missionhotkey.cpp new file mode 100644 index 0000000..ae5e9a0 --- /dev/null +++ b/src/mission/missionhotkey.cpp @@ -0,0 +1,1434 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionHotKey.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for the Hotkey selection screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 5 10/14/99 2:50p Jefff + * localization fixes + * + * 4 8/17/99 3:00p Jefff + * updated for fs2 + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 54 6/09/98 5:15p Lawrance + * French/German localization + * + * 53 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 52 5/26/98 11:10a Lawrance + * Fix bug where window controls get disabled when F1 pressed twice + * + * 51 5/06/98 10:47a Allender + * allow escape pods to be shown on hotkey screen + * + * 50 5/05/98 1:49a Lawrance + * Add in missing help overlays + * + * 49 4/25/98 7:40p Allender + * fixd some small hotkey stuff. Worked on turret orientation being + * correct for multiplayer. new sexpression called end-campaign will will + * end the main campaign + * + * 48 4/20/98 12:36a Mike + * Make team vs. team work when player is hostile. Several targeting + * problems. + * + * 47 4/13/98 10:52a Jasen + * Updated coords for new Hotkey config screen. + * + * 46 3/11/98 10:33p Allender + * made a "hidden" hotkey which is the last hotkey in the set, which means + * to hide the ship/wing until the mission is entered. + * + * 45 3/11/98 11:20a Hoffoss + * Changed hotkey screen to only show friendly and hostile catagories + * (everything non-friendly is hostile). + * + * 44 3/10/98 9:51a Allender + * minor fixups to hotkey stuff. + * + * 43 3/07/98 8:09p Allender + * maybe restore hotkeys when setting defaults + * + * 42 2/27/98 4:31p Allender + * don't show ships on hotkey screen which are hidden to sensors + * + * 41 2/23/98 8:14a John + * + * 40 2/23/98 8:06a John + * Externalized some strings + * + * 39 2/22/98 12:19p John + * Externalized some strings + * + * 38 2/10/98 2:06p Hoffoss + * Eliminated cargo (and NavBouys) from hotkey list. + * + * 37 1/29/98 10:26a Hoffoss + * Made changes so arrow buttons repeat scrolling when held down. + * + * 36 1/28/98 6:22p Dave + * Made standalone use ~8 megs less memory. Fixed multiplayer submenu + * sequencing bug. + * + * 35 1/26/98 4:42p Allender + * fixed restoration of hotkeys when replaying mission. Change the + * meaning of "departed wing" to mean anytime a wing "departs" (with any + * number of remaining wingmen). + * + * 34 1/19/98 9:37p Allender + * Great Compiler Warning Purge of Jan, 1998. Used pragma's in a couple + * of places since I was unsure of what to do with code. + * + * 33 1/18/98 5:09p Lawrance + * Added support for TEAM_TRAITOR + * + * 32 1/14/98 5:22p Allender + * save/restore hotkey selections when replaying the same mission + * + * 31 12/15/97 12:13p Hoffoss + * Changed code to allow hotkey listing to repeat scroll when mouse held + * down on buttons. + * + * 30 12/10/97 2:30p Hoffoss + * Removed dead code that isn't being used anymore. + * + * 29 12/09/97 8:12a Allender + * changes to hotkey stuff. Don't allow mission defined hotkeys to + * override user defined ones once the mission starts + * + * 28 12/03/97 4:16p Hoffoss + * Changed sound stuff used in interface screens for interface purposes. + * + * 27 12/01/97 3:39p Hoffoss + * Changed naming of headings. + * + * 26 12/01/97 3:29p Jasen + * Fixed button coordinates. + * + * 25 12/01/97 2:50p Hoffoss + * Improved hotkey screen. F keys are in seperate columns now, and + * Shift-F key adds that to item. + * + * 24 11/24/97 10:14p Allender + * fixed a couple of problem with assignments in the hotkey screen. alpha + * wing problems, num_ships problems + * + * 23 11/19/97 3:40p Allender + * don't allow player to get assigned by himself to a hotkey. Don't allow + * navbuoys to be assigned either + * + * 22 11/10/97 5:36p Hoffoss + * Fixed bug in last fix. :) + * + * $NoKeywords: $ + */ + +#include "missionhotkey.h" +#include "gamesequence.h" +#include "freespace.h" +#include "key.h" +#include "bmpman.h" +#include "2d.h" +#include "timer.h" +#include "gamesnd.h" +#include "audiostr.h" +#include "ship.h" +#include "object.h" +#include "linklist.h" +#include "hudtarget.h" +#include "player.h" +#include "ui.h" +#include "uidefs.h" +#include "missionscreencommon.h" +#include "font.h" +#include "gamesnd.h" +#include "controlsconfig.h" +#include "contexthelp.h" +#include "alphacolors.h" +#include "beam.h" + +static int Key_sets[MAX_KEYED_TARGETS] = { + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12 +}; + +///////////////////////////// + +static int Hotkey_bits[MAX_SHIPS]; // bitfield indicating which hotkeys are used by each ship + +static int Hotkey_sets_saved; // have we saved the sets for this mission + +static int Mission_hotkey_save_timestamp; // timestamp used to tell us when we can save +#define HOTKEY_SAVE_TIME 15000 // save sets this number of milliseconds into the mission + +typedef struct { + int setnum; + char name[NAME_LENGTH]; +} HK_save_info; + +HK_save_info Hotkey_saved_info[MAX_HOTKEY_TARGET_ITEMS]; +int Num_hotkeys_saved; + + +static char *Hotkey_background_fname[GR_NUM_RESOLUTIONS] = { + "Hotkeys", // GR_640 + "2_Hotkeys" // GR_1024 +}; + +static char *Hotkey_mask_fname[GR_NUM_RESOLUTIONS] = { + "Hotkeys-M", // GR_640 + "2_Hotkeys-M" // GR_1024 +}; + +//#define GROUP_LIST_X 40 +//#define GROUP_LIST_W 160 + +// #define ICON_LIST_X 219 +// #define ICON_LIST_W 8 + +// #define ICON_LIST_X 280 +// #define ICON_LIST_W 8 + +//#define SHIP_LIST_X 242 +//#define SHIP_LIST_X2 259 +//#define SHIP_LIST_W 341 +//#define SHIP_LIST_W2 324 + +// #define SHIP_LIST_X 302 +// #define SHIP_LIST_X2 319 +// #define SHIP_LIST_W 281 +// #define SHIP_LIST_W2 264 + +// #define LIST_Y 70 +// #define LIST_H 280 + +/* +#define HOTKEY_X 575 +#define HOTKEY_Y 41 +*/ + +#define HOTKEY_LINE_HEADING 1 +#define HOTKEY_LINE_WING 2 +#define HOTKEY_LINE_SHIP 3 +#define HOTKEY_LINE_SUBSHIP 4 // ship that is in a wing + +#define WING_FLAG 0x80000 + +#define MAX_LINES 200 +#define NUM_BUTTONS 10 +#define LIST_BUTTONS_MAX 40 + +#define SCROLL_UP_BUTTON 0 +#define SCROLL_DOWN_BUTTON 1 +#define CANCEL_BUTTON 2 +#define CLEAR_BUTTON 3 +#define RESET_BUTTON 4 +#define ADD_HOTKEY_BUTTON 5 +#define REMOVE_HOTKEY_BUTTON 6 +#define HELP_BUTTON 7 +#define OPTIONS_BUTTON 8 +#define ACCEPT_BUTTON 9 + +// coords for entire ship box +static int Hotkey_list_coords[GR_NUM_RESOLUTIONS][4] = { + { + // GR_640 + 29, // x + 22, // y + 502, // w + 315 // h + }, + { + // GR_1024 + 47, // x + 35, // y + 802, // w + 505 // h + } +}; + +// coords for big "F9" thing in the corner +static int Hotkey_function_name_coords[GR_NUM_RESOLUTIONS][4] = { + { + // GR_640 + 570, // x + 14, // y + 59, // w + 22 // h + }, + { + // GR_1024 + 912, // x + 22, // y + 94, // w + 36 // h + } +}; + +/* +#define FIELD_LEFT_EDGE 0 +#define FIELD_F5 1 +#define FIELD_F6 2 +#define FIELD_F7 3 +#define FIELD_F8 4 +#define FIELD_F9 5 +#define FIELD_F10 6 +#define FIELD_F11 7 +#define FIELD_F12 8 +#define FIELD_ICON 9 +#define FIELD_RIGHT_EDGE 10 +// x coords of unseen field boundaries ( | field1 | field2 | ... | ) +// entried will all be centered in fields except FIELD_SHIP which will be left justified +// an edge is named by the field on its left +static int Hotkey_field_edge[GR_NUM_RESOLUTIONS][11] = { + { + 29, 56, 83, 110, 137, 164, 191, 218, 245, 280, 531 + }, + { + 47, 91, 135, 179, 223, 267, 311, 355, 399, 448, 849 + } +} +*/ + +static int Hotkey_function_field_width[GR_NUM_RESOLUTIONS] = { + 27, // GR_640 + 44 // GR_1024 +}; +static int Hotkey_wing_icon_x[GR_NUM_RESOLUTIONS] = { + 246, // GR_640 + 400 // GR_1024 +}; +static int Hotkey_ship_x[GR_NUM_RESOLUTIONS] = { + 280, // GR_640 + 448 // GR_1024 +}; + +// pragma pair put into place because of compiler warnings about being unable to inline +// the constructor function of the hotkey_buttons set. +#pragma warning(disable: 4710) + +struct hotkey_buttons { + char *filename; + int x, y; + int hotspot; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + hotkey_buttons(char *name, int x1, int y1, int h) : filename(name), x(x1), y(y1), hotspot(h) {} +}; + +// button definitions +static hotkey_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { +//XSTR:OFF + { + // GR_640 + hotkey_buttons("HKB_00", 1, 94, 0), + hotkey_buttons("HKB_01", 1, 133, 1), + hotkey_buttons("HKB_02", 15, 342, 2), + hotkey_buttons("HKB_03", 84, 342, 3), + hotkey_buttons("HKB_04", 161, 342, 4), + hotkey_buttons("HKB_05", 539, 5, 5), + hotkey_buttons("HKB_06", 539, 44, 6), + hotkey_buttons("HKB_07", 539, 431, 7), + hotkey_buttons("HKB_08", 539, 455, 8), + hotkey_buttons("HKB_09", 575, 432, 9) + }, + { + // GR_1024 + hotkey_buttons("2_HKB_00", 2, 150, 0), + hotkey_buttons("2_HKB_01", 2, 213, 1), + hotkey_buttons("2_HKB_02", 24, 548, 2), + hotkey_buttons("2_HKB_03", 135, 548, 3), + hotkey_buttons("2_HKB_04", 258, 548, 4), + hotkey_buttons("2_HKB_05", 862, 8, 5), + hotkey_buttons("2_HKB_06", 862, 71, 6), + hotkey_buttons("2_HKB_07", 863, 690, 7), + hotkey_buttons("2_HKB_08", 862, 728, 8), + hotkey_buttons("2_HKB_09", 920, 692, 9) + } +//XSTR:ON +}; +#pragma warning(default: 4710) + +#define HOTKEY_NUM_TEXT 6 +static UI_XSTR Hotkey_text[GR_NUM_RESOLUTIONS][HOTKEY_NUM_TEXT] = { + { + // GR_640 + { "Cancel", 1516, 7, 392, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][CANCEL_BUTTON].button }, + { "Clear", 1517, 85, 392, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][CLEAR_BUTTON].button }, + { "Reset", 1518, 159, 392, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][RESET_BUTTON].button }, + { "Help", 1519, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][HELP_BUTTON].button }, + { "Options", 1520, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][OPTIONS_BUTTON].button }, + { "Accept", 1521, 573, 413, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_640][ACCEPT_BUTTON].button } + }, + { + // GR_1024 + { "Cancel", 1516, 30, 629, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][CANCEL_BUTTON].button }, + { "Clear", 1517, 151, 629, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][CLEAR_BUTTON].button }, + { "Reset", 1518, 269, 629, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][RESET_BUTTON].button }, + { "Help", 1519, 800, 704, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][HELP_BUTTON].button }, + { "Options", 1520, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][OPTIONS_BUTTON].button }, + { "Accept", 1521, 902, 661, UI_XSTR_COLOR_GREEN, -1, &Buttons[GR_1024][ACCEPT_BUTTON].button } + } +}; + + + +static struct { + char *label; + int type; + int index; + int y; // Y coordinate of line +} Hotkey_lines[MAX_LINES]; + +static int Cur_hotkey = 0; +static int Scroll_offset; +static int Num_lines; +static int Selected_line; +static int Background_bitmap; +static int Wing_bmp; +static UI_WINDOW Ui_window; +static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list +//static UI_BUTTON List_region; + +////////////////////// + + +// function used in a couple of places to get the actual hotkey set number from a key value. +// not trivial since our current keysets (F5 - F12) do not have sequential keycodes +int mission_hotkey_get_set_num( int k ) +{ + int i; + + for (i = 0; i < MAX_KEYED_TARGETS; i++ ) { + if ( Key_sets[i] == k ) { + return i; + } + } + + Int3(); // get allender + return 0; +} + +// function to maybe restore some hotkeys during the first N seconds of the mission +void mission_hotkey_maybe_restore() +{ + int i, index; + + for ( i = 0; i < Num_hotkeys_saved; i++ ) { + // don't process something that has no set + if ( Hotkey_saved_info[i].setnum == -1 ) + continue; + + // the ship is present, add it to the given set. + index = ship_name_lookup(Hotkey_saved_info[i].name); + if ( index != -1 ) { + hud_target_hotkey_add_remove( Hotkey_saved_info[i].setnum, &Objects[Ships[index].objnum], HOTKEY_USER_ADDED ); + Hotkey_saved_info[i].setnum = -1; + } + } +} + +// --------------------------------------------------------------------- +// mission_hotkey_set_defaults() +// +// Set up the hotkey lists for the player based on the mission designer +// defaults. +// +void mission_hotkey_set_defaults() +{ + int i,j; + wing *wp; + ship *sp; + object *A; + + for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) { + hud_target_hotkey_clear(i); + } + + // set the variable letting us know that we should save the hotkey sets + Hotkey_sets_saved = 0; + Mission_hotkey_save_timestamp = timestamp(HOTKEY_SAVE_TIME); + + // if we have hotkeys saved from the previous run of this mission, then simply keep the cleared + // sets, and let the restore code take care of it! This works because this function is currently + // only called from one place -- after the mission loads. + if ( Num_hotkeys_saved > 0 ) { + mission_hotkey_maybe_restore(); + return; + } + + // Check for ships with a hotkey assigned + obj_merge_created_list(); + for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) { + + if ( (A == &obj_used_list) || (A->type != OBJ_SHIP) || ((Game_mode & GM_NORMAL) && (A == Player_obj)) ) { + continue; + } + + Assert(A->instance >= 0 && A->instance < MAX_SHIPS); + sp = &Ships[A->instance]; + + if ( sp->hotkey == -1 ) + continue; + + // if the hotkey is the last hotkey in the list, then don't add it either since this hotkey is a special + // marker to indicate that this ship should remain invisible in the hotkey screen until after mission + // starts + if ( sp->hotkey == MAX_KEYED_TARGETS ) + continue; + + Assert(sp->objnum >= 0); + hud_target_hotkey_add_remove( sp->hotkey, &Objects[sp->objnum], HOTKEY_MISSION_FILE_ADDED ); + } + + // Check for wings with a hotkey assigned + for ( i = 0; i < num_wings; i++ ) { + wp = &Wings[i]; + + if ( wp->hotkey == -1 ) + continue; + + // like ships, skip this wing if the hotkey is the last hotkey item + if ( wp->hotkey == MAX_KEYED_TARGETS ) + continue; + + for ( j = 0; j < wp->current_count; j++ ) { + if ( wp->ship_index[j] == -1 ) + continue; + + sp = &Ships[wp->ship_index[j]]; + hud_target_hotkey_add_remove( wp->hotkey, &Objects[sp->objnum], HOTKEY_MISSION_FILE_ADDED ); + } + } +} + +// function to reset the saved hotkeys -- called when a new mission is loaded +void mission_hotkey_reset_saved() +{ + Num_hotkeys_saved = 0; +} + +// next function called when we might want to save the hotkey sets for the player. We will save the hotkey +// sets N seconds into the mission +void mission_hotkey_maybe_save_sets() +{ + int i; + htarget_list *hitem, *plist; + HK_save_info *hkp; + + if ( !timestamp_elapsed(Mission_hotkey_save_timestamp) ) { + mission_hotkey_maybe_restore(); + return; + } + + // no processing if we have saved them. + if ( Hotkey_sets_saved ) + return; + + for ( i = 0; i < MAX_HOTKEY_TARGET_ITEMS; i++ ) + Hotkey_saved_info[i].setnum = -1; + + Num_hotkeys_saved = 0; + hkp = &(Hotkey_saved_info[0]); + + for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) { + + // get the list. do nothing if list is empty + plist = &(Player->keyed_targets[i]); + if ( EMPTY(plist) ) + continue; + + for ( hitem = GET_FIRST(plist); hitem != END_OF_LIST(plist); hitem = GET_NEXT(hitem) ) { + Assert( Num_hotkeys_saved < MAX_HOTKEY_TARGET_ITEMS ); + hkp->setnum = i; + strcpy( hkp->name, Ships[hitem->objp->instance].ship_name ); + hkp++; + Num_hotkeys_saved++; + } + } + + Hotkey_sets_saved = 1; +} + +// function which gets called from MissionParse to maybe add a ship or wing to a hotkey set. +// this intermediate function is needed so that we don't blast over possibly saved hotkey sets +void mission_hotkey_mf_add( int set, int objnum, int how_to_add ) +{ + // if we are restoring hotkeys, and the timer hasn't elapsed, then return and let the + // hotkey restoration code deal with it + if ( Num_hotkeys_saved && !timestamp_elapsed(Mission_hotkey_save_timestamp) ) + return; + + // we can add it to the set + hud_target_hotkey_add_remove( set, &Objects[objnum], how_to_add ); +} + +void mission_hotkey_validate() +{ + htarget_list *hitem, *plist; + object *A; + int obj_valid, i; + + for ( i = 0; i < MAX_KEYED_TARGETS; i++ ) { + plist = &(Players[Player_num].keyed_targets[i]); + if ( EMPTY( plist ) ) // no items in list, then do nothing + continue; + + hitem = GET_FIRST(plist); + while ( hitem != END_OF_LIST(plist) ) { + + // ensure this object is still valid and in the obj_used_list + obj_valid = FALSE; + for ( A = GET_FIRST(&obj_used_list); A !=END_OF_LIST(&obj_used_list); A = GET_NEXT(A) ) { + if ( A->signature == hitem->objp->signature ) { + obj_valid = TRUE; + break; + } + } + if ( obj_valid == FALSE ) { + htarget_list *temp; + + temp = GET_NEXT(hitem); + list_remove( plist, hitem ); + list_append( &htarget_free_list, hitem ); + hitem->objp = NULL; + hitem = temp; + continue; + } + hitem = GET_NEXT( hitem ); + } // end while + } // end for +} + + +// get the Hotkey_bits of a whole wing (bits must be set in all ships of wing for a hotkey bit to be set) +int get_wing_hotkeys(int n) +{ + int i, total = 0xffffffff; + + Assert((n >= 0) && (n < num_wings)); + for (i=0; i= MAX_LINES) + return 0; + + Hotkey_lines[Num_lines].label = text; + Hotkey_lines[Num_lines].type = type; + Hotkey_lines[Num_lines].index = index; + Hotkey_lines[Num_lines].y = y; + return Num_lines++; +} + +// insert a line of hotkey smuck before line 'n'. +int hotkey_line_insert(int n, char *text, int type, int index) +{ + int z; + + if (Num_lines >= MAX_LINES) + return 0; + + z = Num_lines++; + while (z > n) { + Hotkey_lines[z] = Hotkey_lines[z - 1]; + z--; + } + + Hotkey_lines[z].label = text; + Hotkey_lines[z].type = type; + Hotkey_lines[z].index = index; + return z; +} + +// insert a line of hotkey smuck somewhere between 'start' and end of list such that it is +// sorted by name +int hotkey_line_add_sorted(char *text, int type, int index, int start) +{ + int z; + + if (Num_lines >= MAX_LINES) + return -1; + + z = Num_lines - 1; + while ((z >= start) && ((Hotkey_lines[z].type == HOTKEY_LINE_SUBSHIP) || (stricmp(text, Hotkey_lines[z].label) < 0))) + z--; + + z++; + while ((z < Num_lines) && (Hotkey_lines[z].type == HOTKEY_LINE_SUBSHIP)) + z++; + + return hotkey_line_insert(z, text, type, index); +} + +int hotkey_get_team(int i) +{ + if (Ships[i].team == Player_ship->team) + return TEAM_FRIENDLY; + + return TEAM_HOSTILE; +} + +int hotkey_build_team_listing(int team, int y) +{ + ship_obj *so; + char *str = NULL; + int i, j, s, z, start; + int font_height = gr_get_font_height(); + + for (i=0; i= MAX_SHIPS) + return y; + + if (team == Player_ship->team) + str = XSTR( "Friendly ships", 402); + else { + str = XSTR( "Enemy ships", 403); + } + + hotkey_line_add(str, HOTKEY_LINE_HEADING, 0, y); + y += 2; + + start = Num_lines; + + // next loop used to loop through max ships, comparing team values. MWA changed this to iterate + // through object list. Seemed safer since it doesn't rely on the team value getting reset to + // a bogus value between missions + //for (i=0; iobjnum == OBJ_INDEX(Player_obj)) ) + continue; + + shipnum = Objects[so->objnum].instance; + + // filter out cargo containers, navbouys, etc + if ( (Ship_info[Ships[shipnum].ship_info_index].flags & SIF_HARMLESS) && !(Ship_info[Ships[shipnum].ship_info_index].flags & SIF_ESCAPEPOD) ) + continue; + + // don't process non-ships (dunno what that would be, though). + if (Ship_info[Ships[shipnum].ship_info_index].flags & SIF_NO_SHIP_TYPE) + continue; + + // don't process ships invisible to sensors, dying or departing + if ( Ships[shipnum].flags & (SF_HIDDEN_FROM_SENSORS|SF_DYING|SF_DEPARTING) ) + continue; + + // if a ship's hotkey is the last hotkey on the list, then maybe make the hotkey -1 if + // we are now in mission. Otherwise, skip this ship + if ( Ships[shipnum].hotkey == MAX_KEYED_TARGETS ) { + if ( !(Game_mode & GM_IN_MISSION) ) + continue; // skip to next ship + Ships[shipnum].hotkey = -1; + } + + // be sure this ship isn't in a wing, and that the teams match + if ( (hotkey_get_team(shipnum) == team) && (Ships[shipnum].wingnum < 0) ) { + hotkey_line_add_sorted(Ships[shipnum].ship_name, HOTKEY_LINE_SHIP, shipnum, start); + } + } + + for (i=0; iwingnum == i) && (Wings[i].current_count == 1) ) + continue; + + // if a ship's hotkey is the last hotkey on the list, then maybe make the hotkey -1 if + // we are now in mission. Otherwise, skip this ship + if ( Wings[i].hotkey == MAX_KEYED_TARGETS ) { + if ( !(Game_mode & GM_IN_MISSION) ) + continue; // skip to next ship + Wings[i].hotkey = -1; + } + + // don't add any wing data whose ships are hidden from sensors + for ( j = 0; j < Wings[i].current_count; j++ ) { + if ( Ships[Wings[i].ship_index[j]].flags & SF_HIDDEN_FROM_SENSORS ) + break; + } + // if we didn't reach the end of the list, don't display the wing + if ( j < Wings[i].current_count ) + continue; + + z = hotkey_line_add_sorted(Wings[i].name, HOTKEY_LINE_WING, i, start); + if (Wings[i].flags & WF_EXPANDED) { + for (j=0; j= Num_lines)) + return 0; + + y = Hotkey_lines[n].y - Hotkey_lines[Scroll_offset].y; + if ((y < 0) || (y + gr_get_font_height() > Hotkey_list_coords[gr_screen.res][3])) + return 0; + + return 1; +} + +void hotkey_scroll_screen_up() +{ + if (Scroll_offset) { + Scroll_offset--; + Assert(Selected_line > Scroll_offset); + while (!hotkey_line_query_visible(Selected_line) || (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)) + Selected_line--; + + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void hotkey_scroll_line_up() +{ + if (Selected_line > 1) { + Selected_line--; + while (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING) + Selected_line--; + + if (Selected_line < Scroll_offset) + Scroll_offset = Selected_line; + + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void hotkey_scroll_screen_down() +{ + if (Hotkey_lines[Num_lines - 1].y + gr_get_font_height() > Hotkey_lines[Scroll_offset].y + Hotkey_list_coords[gr_screen.res][3]) { + Scroll_offset++; + while (!hotkey_line_query_visible(Selected_line) || (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING)) { + Selected_line++; + Assert(Selected_line < Num_lines); + } + + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void hotkey_scroll_line_down() +{ + if (Selected_line < Num_lines - 1) { + Selected_line++; + while (Hotkey_lines[Selected_line].type == HOTKEY_LINE_HEADING) + Selected_line++; + + Assert(Selected_line > Scroll_offset); + while (!hotkey_line_query_visible(Selected_line)) + Scroll_offset++; + + gamesnd_play_iface(SND_SCROLL); + + } else + gamesnd_play_iface(SND_GENERAL_FAIL); +} + +void expand_wing() +{ + int i, z; + + if (Hotkey_lines[Selected_line].type == HOTKEY_LINE_WING) { + i = Hotkey_lines[Selected_line].index; + Wings[i].flags ^= WF_EXPANDED; + hotkey_build_listing(); + for (z=0; zobjp->type == OBJ_SHIP); + Hotkey_bits[hitem->objp->instance] |= (1 << i); + } + } +} + +void clear_hotkeys() +{ + int i, b, z; + + z = Hotkey_lines[Selected_line].type; + if (z == HOTKEY_LINE_WING) { + z = Hotkey_lines[Selected_line].index; + b = ~get_wing_hotkeys(z); + for (i=0; iobjnum].instance] & (1 << i) ) { + hud_target_hotkey_add_remove(i, &Objects[so->objnum], HOTKEY_USER_ADDED ); + } + } + } +} + +void add_hotkey(int hotkey) +{ + int i, z; + + z = Hotkey_lines[Selected_line].type; + if (z == HOTKEY_LINE_WING) { + z = Hotkey_lines[Selected_line].index; + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, i < 2 ? 1 : 0, 1); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // add all xstr text + for(i=0; i= 0) + bm_unload(Wing_bmp); + + // unload the overlay bitmap +// help_overlay_unload(HOTKEY_OVERLAY); + + // unpause all beam weapon sounds + beam_unpause_sounds(); + + // unpause all game music + audiostream_unpause_all(); + + Ui_window.destroy(); + common_free_interface_palette(); // restore game palette + game_flush(); +} + +// --------------------------------------------------------------------- +// mission_hotkey_do_frame() +// +// Called once per frame to process user input for the Hotkey Assignment Screen +// +void mission_hotkey_do_frame(float frametime) +{ + char buf[256]; + int i, k, w, h, y, z, line, hotkeys; + int font_height = gr_get_font_height(); + int select_tease_line = -1; // line mouse is down on, but won't be selected until button released + color circle_color; + + if ( help_overlay_active(HOTKEY_OVERLAY) ) { + Buttons[gr_screen.res][HELP_BUTTON].button.reset_status(); + Ui_window.set_ignore_gadgets(1); + } + + k = Ui_window.process() & ~KEY_DEBUGGED; + + if ( (k > 0) || B1_JUST_RELEASED ) { + if ( help_overlay_active(HOTKEY_OVERLAY) ) { + help_overlay_set_state(HOTKEY_OVERLAY, 0); + Ui_window.set_ignore_gadgets(0); + k = 0; + } + } + + if ( !help_overlay_active(HOTKEY_OVERLAY) ) { + Ui_window.set_ignore_gadgets(0); + } + + switch (k) { + case KEY_DOWN: // scroll list down + hotkey_scroll_line_down(); + break; + + case KEY_UP: // scroll list up + hotkey_scroll_line_up(); + break; + + case KEY_PAGEDOWN: // scroll list down + hotkey_scroll_screen_down(); + break; + + case KEY_PAGEUP: // scroll list up + hotkey_scroll_screen_up(); + break; + + case KEY_CTRLED | KEY_ENTER: + save_hotkeys(); + // fall through to next state -- allender changed this behavior since ESC should always cancel, no? + + case KEY_ESC: + mission_hotkey_exit(); + break; + + case KEY_TAB: + case KEY_ENTER: + case KEY_PADENTER: + expand_wing(); + break; + + case KEY_EQUAL: + case KEY_PADPLUS: + add_hotkey(Cur_hotkey); + break; + + case KEY_MINUS: + case KEY_PADMINUS: + remove_hotkey(); + break; + + case KEY_F2: + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + break; + + case KEY_CTRLED | KEY_R: + reset_hotkeys(); + break; + + case KEY_CTRLED | KEY_C: + clear_hotkeys(); + break; + } // end switch + + // ? + for (i=0; i= Hotkey_wing_icon_x[gr_screen.res]) && (z < (Hotkey_wing_icon_x[gr_screen.res]) + Hotkey_function_field_width[gr_screen.res])) { + expand_wing(); + } + } + + if (List_buttons[i].double_clicked()) { + Selected_line = i + Scroll_offset; + hotkeys = -1; + switch (Hotkey_lines[Selected_line].type) { + case HOTKEY_LINE_WING: + hotkeys = get_wing_hotkeys(Hotkey_lines[Selected_line].index); + break; + + case HOTKEY_LINE_SHIP: + case HOTKEY_LINE_SUBSHIP: + hotkeys = Hotkey_bits[Hotkey_lines[Selected_line].index]; + break; + } + + if (hotkeys != -1) { + if (hotkeys & (1 << Cur_hotkey)) + remove_hotkey(); + else + add_hotkey(Cur_hotkey); + } + } + } + + if (Background_bitmap >= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + + } else + gr_clear(); + + Ui_window.draw(); + gr_init_color(&circle_color, 160, 160, 0); + + // draw the big "F10" in the little box + gr_set_font(FONT2); + gr_set_color_fast(&Color_text_normal); + strcpy(buf, Scan_code_text[Key_sets[Cur_hotkey]]); + gr_get_string_size(&w, &h, buf); + gr_printf(Hotkey_function_name_coords[gr_screen.res][0] + (Hotkey_function_name_coords[gr_screen.res][2] - w) / 2, Hotkey_function_name_coords[gr_screen.res][1], buf); + + gr_set_font(FONT1); + line = Scroll_offset; + while (hotkey_line_query_visible(line)) { + z = Hotkey_lines[line].index; + y = Hotkey_list_coords[gr_screen.res][1] + Hotkey_lines[line].y - Hotkey_lines[Scroll_offset].y; + hotkeys = 0; + switch (Hotkey_lines[line].type) { + case HOTKEY_LINE_HEADING: + gr_set_color_fast(&Color_text_heading); + + gr_get_string_size(&w, &h, Hotkey_lines[line].label); + i = y + h / 2 - 1; + gr_line(Hotkey_list_coords[gr_screen.res][0], i, Hotkey_ship_x[gr_screen.res] - 2, i); + gr_line(Hotkey_ship_x[gr_screen.res] + w + 1, i, Hotkey_list_coords[gr_screen.res][0] + Hotkey_list_coords[gr_screen.res][2], i); + break; + + case HOTKEY_LINE_WING: + gr_set_bitmap(Wing_bmp); + bm_get_info(Wing_bmp, NULL, &h, NULL); + i = y + font_height / 2 - h / 2 - 1; + gr_bitmap(Hotkey_wing_icon_x[gr_screen.res], i); + +// i = y + font_height / 2 - 1; +// gr_set_color_fast(&circle_color); +// gr_circle(ICON_LIST_X + 4, i, 5); + +// gr_set_color_fast(&Color_bright); +// gr_line(ICON_LIST_X, i, ICON_LIST_X + 2, i); +// gr_line(ICON_LIST_X + 4, i - 4, ICON_LIST_X + 4, i - 2); +// gr_line(ICON_LIST_X + 6, i, ICON_LIST_X + 8, i); +// gr_line(ICON_LIST_X + 4, i + 2, ICON_LIST_X + 4, i + 4); + + hotkeys = get_wing_hotkeys(Hotkey_lines[line].index); + break; + + case HOTKEY_LINE_SHIP: + case HOTKEY_LINE_SUBSHIP: + hotkeys = Hotkey_bits[Hotkey_lines[line].index]; + break; + + default: + Int3(); + } + + if (Hotkey_lines[line].type != HOTKEY_LINE_HEADING) { + List_buttons[line - Scroll_offset].update_dimensions(Hotkey_list_coords[gr_screen.res][0], y, Hotkey_list_coords[gr_screen.res][0] + Hotkey_list_coords[gr_screen.res][2] - Hotkey_list_coords[gr_screen.res][0], font_height); + List_buttons[line - Scroll_offset].enable(); + if (hotkeys & (1 << Cur_hotkey)) { + if (line == Selected_line) + gr_set_color_fast(&Color_text_active_hi); + else + gr_set_color_fast(&Color_text_active); + + } else { + if (line == Selected_line) + gr_set_color_fast(&Color_text_selected); + else if (line == select_tease_line) + gr_set_color_fast(&Color_text_subselected); + else + gr_set_color_fast(&Color_text_normal); + } + + } else { + List_buttons[line - Scroll_offset].disable(); + } + + // print active hotkeys associated for this line + if (hotkeys) { + for (i=0; i 1); + buf[strlen(buf) - 2] = 0; // lose the ", " on the end + + gr_force_fit_string(buf, 255, GROUP_LIST_W); + gr_printf(GROUP_LIST_X, y, buf);*/ + } + + // draw ship/wing name + strcpy(buf, Hotkey_lines[line].label); + if (Hotkey_lines[line].type == HOTKEY_LINE_SUBSHIP) { + // indent + gr_force_fit_string(buf, 255, Hotkey_list_coords[gr_screen.res][0] + Hotkey_list_coords[gr_screen.res][2] - (Hotkey_ship_x[gr_screen.res]+20)); + gr_printf(Hotkey_ship_x[gr_screen.res]+20, y, buf); + } else { + gr_force_fit_string(buf, 255, Hotkey_list_coords[gr_screen.res][0] + Hotkey_list_coords[gr_screen.res][2] - Hotkey_ship_x[gr_screen.res]); + gr_printf(Hotkey_ship_x[gr_screen.res], y, buf); + } + + line++; + } + + i = line - Scroll_offset; + while (i < LIST_BUTTONS_MAX) + List_buttons[i++].disable(); + + // blit help overlay if active + help_overlay_maybe_blit(HOTKEY_OVERLAY); + + gr_flip(); +} + +void mission_hotkey_exit() +{ + gameseq_post_event(GS_EVENT_PREVIOUS_STATE); +} \ No newline at end of file diff --git a/src/mission/missionload.cpp b/src/mission/missionload.cpp new file mode 100644 index 0000000..884c620 --- /dev/null +++ b/src/mission/missionload.cpp @@ -0,0 +1,557 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionLoad.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C source module for mission loading + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:09 root + * Initial revision + * + * + * 5 7/20/99 1:49p Dave + * Peter Drake build. Fixed some release build warnings. + * + * 4 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 3 10/07/98 6:27p Dave + * Globalized mission and campaign file extensions. Removed Silent Threat + * special code. Moved \cache \players and \multidata into the \data + * directory. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 102 5/19/98 1:19p Allender + * new low level reliable socket reading code. Make all missions/campaign + * load/save to data missions folder (i.e. we are rid of the player + * missions folder) + * + * 101 5/10/98 10:05p Allender + * only show cutscenes which have been seen before. Made Fred able to + * write missions anywhere, defaulting to player misison folder, not data + * mission folder. Fix FreeSpace code to properly read missions from + * correct locations + * + * 100 4/30/98 4:53p John + * Restructured and cleaned up cfile code. Added capability to read off + * of CD-ROM drive and out of multiple pack files. + * + * 99 2/23/98 6:55p Lawrance + * Rip out obsolete code. + * + * 98 2/23/98 8:53a John + * String externalization + * + * 97 1/19/98 9:37p Allender + * Great Compiler Warning Purge of Jan, 1998. Used pragma's in a couple + * of places since I was unsure of what to do with code. + * + * 96 1/17/98 8:49p Hoffoss + * Fixed mission_load() calls to handle failure correctly. + * + * 95 12/28/97 1:34p John + * Fixed yet another mission filename bug + * + * 94 12/28/97 12:42p John + * Put in support for reading archive files; Made missionload use the + * cf_get_file_list function. Moved demos directory out of data tree. + * + * 93 12/27/97 2:39p John + * Took out the outdated ui_getfilelist functions. Made the mission load + * screen use cf_get_filelist instead. Fixed a bug in mission load that + * crashed the program if there are no missions available. + * + * 92 12/23/97 12:00p Allender + * change write_pilot_file to *not* take is_single as a default parameter. + * causing multiplayer pilots to get written to the single player folder + * + * 91 11/11/97 4:57p Dave + * Put in support for single vs. multiplayer pilots. Began work on + * multiplayer campaign saving. Put in initial player select screen + * + * 90 10/31/97 11:27a John + * appended path for j:\tmp missions + * + * 89 10/31/97 11:19a John + * added filter catagory for j:\tmp\*.fsm + * + * 88 10/12/97 5:22p Lawrance + * have ESC back out of mission load screen + * + * 87 9/18/97 10:19p Lawrance + * Add a mission campaign filter to the load screen + * + * 86 9/16/97 2:41p Allender + * beginning of code to change way player starts are handled. Reordered + * some code when missions loading since player ship is now created at + * mission load time instead of before misison load + * + * 85 8/25/97 5:47p Mike + * Increase number of missions supported in mission load list (outside + * campaign) to 256 and Assert() if there are more than 256. + * + * 84 8/20/97 5:19p Hoffoss + * Fixed bug where creating a new pilot causes the mission load mission + * list box to be empty. + * + * 83 7/28/97 10:53a Lawrance + * initialize a timestamp + * + * 82 7/17/97 4:25p John + * First, broken, stage of changing config stuff + * + * 81 7/05/97 1:47p Lawrance + * write pilot file when a mission is loaded + * + * 80 6/26/97 5:53p Lawrance + * save recently played missions, allow player to choose from list + * + * 79 6/12/97 12:39p John + * made ui use freespace colors + * + * 78 6/12/97 11:35a John + * more menu backgrounds + * + * 77 5/14/97 1:33p Allender + * remmoved extern declaration + * + * 76 5/12/97 4:59p Allender + * move the rest of the mission initialization functions into + * game_level_init(). All mission loading now going through this + * fucntion. + * + * 75 5/12/97 3:21p Allender + * re-ordered mission load code into single function in Freespace. + * Simulation part now runs as seperate thread + * + * 74 5/12/97 12:27p John + * Restructured Graphics Library to add support for multiple renderers. + * + * 73 4/28/97 5:43p Lawrance + * allow hotkey assignment screen to work from ship selection + * + * 72 4/25/97 11:31a Allender + * Campaign state now saved in campaign save file in player directory. + * Made some global variables follow naming convention. Solidified + * continuing campaigns based on new structure + * + * 71 4/23/97 4:46p Allender + * remove unused code + * + * 70 4/23/97 3:21p Allender + * more campaign stuff -- mission branching through campaign file now + * works!!!! + * + * 69 4/22/97 10:44a Allender + * more campaign stuff. Info about multiple campaigns now stored in + * player file -- not saving some player information in save games. + * + * 68 4/18/97 9:59a Allender + * more campaign stuff. All campaign related varaibles now stored in + * campaign structure + * + * 67 4/17/97 9:02p Allender + * new campaign stuff. all campaign related material stored in external + * file. Continuing campaign won't work at this time + * + * 66 4/15/97 4:37p Lawrance + * removed unused variables + * +*/ + +#include "missionload.h" +#include "missiongoals.h" +#include "missionparse.h" +#include "missionshipchoice.h" +#include "missionlog.h" +#include "missionmessage.h" +#include "cfile.h" +#include "osapi.h" +#include "vecmat.h" +#include "player.h" +#include "object.h" +#include "ship.h" +#include "ailocal.h" +#include "managepilot.h" +#include "hud.h" +#include "freespace.h" +#include "key.h" +#include "2d.h" +#include "line.h" +#include "timer.h" +#include "math.h" +#include "linklist.h" +#include "mouse.h" +#include "weapon.h" +#include "gamesequence.h" +#include "ui.h" +#include "sexp.h" +#include "missionhotkey.h" +#include "missioncampaign.h" +#include "cfilesystem.h" +#include "alphacolors.h" + + +extern mission The_mission; // need to send this info to the briefing +extern int shifted_ascii_table[]; +extern int ascii_table[]; + +// ----------------------------------------------- +// For recording most recent missions played +// ----------------------------------------------- +char Recent_missions[MAX_RECENT_MISSIONS][MAX_FILENAME_LEN]; +int Num_recent_missions; + + +// ----------------------------------------------------- +// ml_update_recent_missions() +// +// Update the Recent_missions[][] array +// +void ml_update_recent_missions(char *filename) +{ + char tmp[MAX_RECENT_MISSIONS][MAX_FILENAME_LEN], *p; + int i,j; + + + for ( i = 0; i < Num_recent_missions; i++ ) { + strcpy( tmp[i], Recent_missions[i] ); + } + + // get a pointer to just the basename of the filename (including extension) + p = strrchr(filename, '\\'); + if ( p == NULL ) { + p = filename; + } else { + p++; + } + + Assert(strlen(p) < MAX_FILENAME_LEN); + strcpy( Recent_missions[0], p ); + + j = 1; + for ( i = 0; i < Num_recent_missions; i++ ) { + if ( stricmp(Recent_missions[0], tmp[i]) ) { + strcpy(Recent_missions[j++], tmp[i]); + if ( j >= MAX_RECENT_MISSIONS ) { + break; + } + } + } + + Num_recent_missions = j; + Assert(Num_recent_missions <= MAX_RECENT_MISSIONS); +} + +// Mission_load takes no parameters. +// It expects the following global variables to be set correctly: +// Game_current_mission_filename + +// returns -1 if failed, 0 if successful +int mission_load() +{ + char filename[128], *ext; + + mprintf(("MISSION LOAD: '%s'\n", Game_current_mission_filename)); + + strncpy(filename, Game_current_mission_filename, 127); + ext = strchr(filename, '.'); + if (ext) { + mprintf(( "Hmmm... Extension passed to mission_load...\n" )); + *ext = 0; // remove any extension! + } + + strcat(filename, FS_MISSION_FILE_EXT); + + // does the magical mission parsing + // creates all objects, except for the player object + // save the player object later since the player may get + // to choose the type of ship that he is to fly + // return value of 0 indicates success, other is failure. + + if ( parse_main(filename) ) + return -1; + + if (Select_default_ship) { + int ret; + ret = create_default_player_ship(); + Assert(!ret); + } + + ml_update_recent_missions(Game_current_mission_filename); // update recently played missions list + write_pilot_file(); + return 0; +} + +//==================================== +// Mission Load Menu stuff +#define MLM_MAX_MISSIONS 256 +int mlm_active=0; +UI_WINDOW mlm_window; +UI_LISTBOX mlm_mission_list; +UI_LISTBOX recent_mission_list; +UI_LISTBOX campaign_filter; + +UI_BUTTON mlm_ok, mlm_cancel; +char * mlm_missions[MLM_MAX_MISSIONS]; +char * recent_missions[MAX_RECENT_MISSIONS]; +char * campaign_names[MAX_CAMPAIGNS+2]; +char * campaign_missions[MAX_CAMPAIGN_MISSIONS]; +int mlm_nfiles = 0; +static int last_recent_current = -1; +static int last_mlm_current = -1; +static int Campaign_filter_index; + +char * jtmp_missions[MLM_MAX_MISSIONS]; +int jtmp_nfiles = 0; + + +void ml_change_listbox() +{ + if ( !Num_recent_missions || !mlm_nfiles ) + return; + + if ( mlm_mission_list.current() != -1 ) { + mlm_mission_list.set_current(-1); + last_mlm_current = -1; + recent_mission_list.set_focus(); + recent_mission_list.set_current(0); + return; + } + + if ( recent_mission_list.current() != -1 ) { + recent_mission_list.set_current(-1); + last_recent_current = -1; + mlm_mission_list.set_focus(); + mlm_mission_list.set_current(0); + return; + } +} + +static char Campaign_missions[MAX_CAMPAIGN_MISSIONS][NAME_LENGTH]; +static char Campaign_name_list[MAX_CAMPAIGNS+2][NAME_LENGTH]; +static int Num_campaign_missions; + +// get the mission filenames that make up a campaign +extern int mission_campaign_get_filenames(char *filename, char dest[][NAME_LENGTH], int *num); + +void mission_load_menu_init() +{ + int i; + char wild_card[256]; + Assert( mlm_active == 0 ); + mlm_active = 1; + + memset(wild_card, 0, 256); + strcpy(wild_card, NOX("*")); + strcat(wild_card, FS_MISSION_FILE_EXT); + mlm_nfiles = cf_get_file_list( MLM_MAX_MISSIONS, mlm_missions, CF_TYPE_MISSIONS, wild_card, CF_SORT_NAME ); + jtmp_nfiles = 0; + + Assert(mlm_nfiles <= MLM_MAX_MISSIONS); + + mlm_window.create( 100,100,500,300, 0 ); //WIN_DIALOG + + mlm_ok.create( &mlm_window, NOX("Ok"), 125, 420, 80, 40 ); + mlm_cancel.create( &mlm_window, NOX("Cancel"), 250, 420, 80, 40 ); + mlm_cancel.set_hotkey( KEY_ESC ); + + mlm_mission_list.create( &mlm_window, 450, 150, 150, 200, mlm_nfiles, mlm_missions ); + + for ( i = 0; i < Num_recent_missions; i++ ) { + recent_missions[i] = Recent_missions[i]; + } + recent_mission_list.create( &mlm_window, 250, 150, 150, 200, Num_recent_missions, recent_missions ); + + mlm_mission_list.set_focus(); + mlm_mission_list.set_current(0); + + + mission_campaign_build_list(0); + for ( i = 0; i < Num_campaigns; i++ ) { + strcpy(Campaign_name_list[i+1], Campaign_names[i]); + } + strcpy(Campaign_name_list[0], NOX("All campaigns")); + strcpy(Campaign_name_list[1], NOX("Player Missions")); + + for ( i = 0; i < Num_campaigns+2; i++ ) { + campaign_names[i] = Campaign_name_list[i]; + } + + campaign_filter.create( &mlm_window, 50, 150, 150, 200, Num_campaigns+2, campaign_names ); + Campaign_filter_index = 0; + campaign_filter.set_current(Campaign_filter_index); +} + +void mission_load_menu_do() +{ + int selected, key_in, recent_current, mlm_current, use_recent_flag, i; + + + Assert( mlm_active == 1 ); + + key_in = mlm_window.process(); + + if ( key_in ) { + + switch ( key_in & KEY_MASK ) { + + case KEY_UP: + case KEY_DOWN: + case KEY_HOME: + case KEY_END: + case KEY_PAGEUP: + case KEY_PAGEDOWN: + case KEY_ENTER: + break; + + case KEY_RIGHT: + case KEY_LEFT: + ml_change_listbox(); + break; + + case KEY_ESC: + gameseq_post_event(GS_EVENT_MAIN_MENU); + break; + + default: + break; + + } // end switch + + } + + if ( campaign_filter.current() != Campaign_filter_index ) { + Campaign_filter_index = campaign_filter.current(); + + if ( Campaign_filter_index > 1 ) { + mission_campaign_get_filenames(Campaign_file_names[Campaign_filter_index-2], Campaign_missions, &Num_campaign_missions); + + for ( i = 0; i < Num_campaign_missions; i++ ) { + campaign_missions[i] = Campaign_missions[i]; + } + mlm_mission_list.set_new_list(Num_campaign_missions, campaign_missions); + } else if ( Campaign_filter_index == 0 ) { + mlm_mission_list.set_new_list(mlm_nfiles, mlm_missions); + } else if ( Campaign_filter_index == 1 ) { + mlm_mission_list.set_new_list(jtmp_nfiles, jtmp_missions); + } + mlm_current = 0; + } + + mlm_current = mlm_mission_list.current(); + recent_current = recent_mission_list.current(); + + if ( mlm_current != last_mlm_current ) { + recent_mission_list.set_current(-1); + last_recent_current = -1; + } + last_mlm_current = mlm_current; + + if ( recent_current != last_recent_current ) { + mlm_mission_list.set_current(-1); + last_mlm_current = -1; + } + last_recent_current = recent_current; + + if (mlm_cancel.pressed()) + gameseq_post_event(GS_EVENT_MAIN_MENU); + + // Check if they hit OK, if so, use the current listbox + // selection. + selected = -1; + use_recent_flag = 0; + if (mlm_ok.pressed()) { + selected = mlm_mission_list.current(); + if ( selected == -1 ) { + selected = recent_mission_list.current(); + use_recent_flag = 1; + } + } else { + // If they didn't hit OK, then check for a double-click on + // a list box item. + selected = mlm_mission_list.selected(); + if ( selected == -1 ) { + selected = recent_mission_list.selected(); + use_recent_flag = 1; + } + } + + char mission_name_final[512] = ""; + + if ( selected > -1 ) { + Campaign.current_mission = -1; + if ( use_recent_flag ) { + strncpy( mission_name_final, recent_missions[selected], MAX_FILENAME_LEN ); + } else { + char mission_name[NAME_LENGTH]; + if ( Campaign_filter_index == 0 ) { + strcpy(mission_name, mlm_missions[selected]); + } else if (Campaign_filter_index == 1 ) { + strcpy( mission_name, jtmp_missions[selected]); + } else { + strcpy(mission_name, Campaign_missions[selected]); + } + strncpy( mission_name_final, mission_name, MAX_FILENAME_LEN ); + } + + // go +#ifdef PD_BUILD + // if this valid + if((game_find_builtin_mission(mission_name_final) != NULL) || strstr(mission_name_final, "peterdrake")){ + strcpy(Game_current_mission_filename, mission_name_final); + mprintf(( "Selected '%s'\n", Game_current_mission_filename )); + gameseq_post_event(GS_EVENT_START_GAME); + } +#else + strcpy(Game_current_mission_filename, mission_name_final); + mprintf(( "Selected '%s'\n", Game_current_mission_filename )); + gameseq_post_event(GS_EVENT_START_GAME); +#endif + } + + gr_clear(); + gr_set_color_fast( &Color_bright ); + gr_printf( 0x8000, 10, NOX("Select Mission") ); + + gr_printf( 50, 135, NOX("Campaign Filter")); + gr_printf( 250, 135, NOX("Recently Played")); + gr_printf( 450, 135, NOX("Mission List")); + mlm_window.draw(); + + gr_flip(); +} + +void mission_load_menu_close() +{ + int i; + + Assert( mlm_active == 1 ); + mlm_active = 0; + + for (i=0; ipname ) ) { + if ( (entry->type == LOG_SHIP_SUBSYS_DESTROYED) || (entry->type == LOG_SHIP_DISARMED) || (entry->type == LOG_SHIP_DISABLED) ) + entry->flags |= MLF_OBSOLETE; + } + } + } + + // check to see if we are getting to about 80% of our log capacity. If so, cull the log. + if ( last_entry > LOG_CULL_MARK ) { + mission_log_cull_obsolete_entries(); + + // if we culled the entries, and we are still low on space, we need to take more drastic measures. + // these include removing all non-essential entries from the log. These entries are entries + // which has not been asked for by mission_log_get_time + if ( last_entry > LOG_CULL_MARK ) { + nprintf(("missionlog", "marking the first %d non-essential log entries as obsolete\n", LOG_LAST_DITCH_CULL_NUM)); + for (i = 0; i < LOG_LAST_DITCH_CULL_NUM; i++ ) { + entry = &log_entries[i]; + if ( !(entry->flags & MLF_ESSENTIAL) ){ + entry->flags |= MLF_OBSOLETE; + } + } + + // cull the obsolete entries again + mission_log_cull_obsolete_entries(); + + // if we get to this point, and there are no entries left -- we are in big trouble. We will simply + // mark the first 20% of the log as obsolete and compress. Don't do this unless we are *really* + // in trouble + if ( last_entry > LOG_CULL_DOORDIE_MARK ) { + nprintf(("missionlog", "removing the first %d entries in the mission log!!!!\n", LOG_LAST_DITCH_CULL_NUM)); + for (i = 0; i < LOG_LAST_DITCH_CULL_NUM; i++ ){ + entry->flags |= MLF_OBSOLETE; + } + + mission_log_cull_obsolete_entries(); + } + } + } +} + +// assigns flag values to an entry based on the team value passed in +void mission_log_flag_team( log_entry *entry, int which_entry, int team ) +{ + if ( which_entry == ML_FLAG_PRIMARY ) { + if ( team == TEAM_FRIENDLY ) + entry->flags |= MLF_PRIMARY_FRIENDLY; + else + entry->flags |= MLF_PRIMARY_HOSTILE; + + } else if ( which_entry == ML_FLAG_SECONDARY ) { + if ( team == TEAM_FRIENDLY ) + entry->flags |= MLF_SECONDARY_FRIENDLY; + else + entry->flags |= MLF_SECONDARY_HOSTILE; + + } else + Int3(); // get allender -- impossible type +} + +// following function adds an entry into the mission log. +// pass a type and a string which indicates the object +// that this event is for. Don't add entries with this function for multiplayer +void mission_log_add_entry(int type, char *pname, char *sname, int info_index) +{ + int last_entry_save; + log_entry *entry; + + // multiplayer clients don't use this function to add log entries -- they will get + // all their info from the host + if ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){ + return; + } + + last_entry_save = last_entry; + + // mark any entries as obsolete. Part of the pruning is done based on the type (and name) passed + // for a new entry + mission_log_obsolete_entries(type, pname); + + entry = &log_entries[last_entry]; + + if ( last_entry == MAX_LOG_ENTRIES ){ + return; + } + + entry->type = type; + if ( pname ) { + Assert (strlen(pname) < NAME_LENGTH); + strcpy(entry->pname, pname); + } else + strcpy( entry->pname, EMPTY_LOG_NAME ); + + if ( sname ) { + Assert (strlen(sname) < NAME_LENGTH); + strcpy(entry->sname, sname); + } else + strcpy( entry->sname, EMPTY_LOG_NAME ); + + entry->index = info_index; + entry->flags = 0; + + // determine the contents of the flags member based on the type of entry we added. We need to store things + // like team for the primary and (possibly) secondary object for this entry. + switch ( type ) { + int index, si; + + case LOG_SHIP_DESTROYED: + case LOG_SHIP_ARRIVE: + case LOG_SHIP_DEPART: + case LOG_SHIP_DOCK: + case LOG_SHIP_SUBSYS_DESTROYED: + case LOG_SHIP_UNDOCK: + case LOG_SHIP_DISABLED: + case LOG_SHIP_DISARMED: + case LOG_SELF_DESTRUCT: + // multiplayer. callsign is passed in for ship destroyed and self destruct + if((Game_mode & GM_MULTIPLAYER) && (multi_find_player_by_callsign(pname) >= 0)){ + int np_index = multi_find_player_by_callsign(pname); + index = multi_get_player_ship( np_index ); + } else { + index = ship_name_lookup( pname ); + } + + Assert ( index != -1 ); + if(index < 0){ + mission_log_flag_team( entry, ML_FLAG_PRIMARY, TEAM_FRIENDLY ); + } else { + mission_log_flag_team( entry, ML_FLAG_PRIMARY, Ships[index].team ); + } + + // some of the entries have a secondary component. Figure out what is up with them. + if ( (type == LOG_SHIP_DOCK) || (type == LOG_SHIP_UNDOCK)) { + if ( sname ) { + index = ship_name_lookup( sname ); + Assert( index != -1 ); + mission_log_flag_team( entry, ML_FLAG_SECONDARY, Ships[index].team ); + } + } else if ( type == LOG_SHIP_DESTROYED ) { + if ( sname ) { + int team; + + // multiplayer, player name will possibly be sent in + if((Game_mode & GM_MULTIPLAYER) && (multi_find_player_by_callsign(sname) >= 0)){ + // get the player's ship + int np_index = multi_find_player_by_callsign(sname); + int np_ship = multi_get_player_ship(np_index); + + if(np_ship != -1){ + team = Ships[Objects[Net_players[np_index].player->objnum].instance].team; + } + // argh. badness + else { + team = TEAM_FRIENDLY; + } + } else { + index = ship_name_lookup( sname ); + // no ship, then it probably exited -- check the exited + if ( index == -1 ) { + index = ship_find_exited_ship_by_name( sname ); + if ( index == -1 ) { + // Int3(); // get allender. name of object who killed ship appears to be bogus!!! + break; + } + team = Ships_exited[index].team; + } else { + team = Ships[index].team; + } + } + + mission_log_flag_team( entry, ML_FLAG_SECONDARY, team ); + } else { + nprintf(("missionlog", "No secondary name for ship destroyed log entry!\n")); + } + } else if ( (type == LOG_SHIP_SUBSYS_DESTROYED) && (Ship_info[Ships[index].ship_info_index].flags & SIF_SMALL_SHIP) ) { + // make subsystem destroyed entries for small ships hidden + entry->flags |= MLF_HIDDEN; + } else if ( (type == LOG_SHIP_ARRIVE) && (Ships[index].wingnum != -1 ) ) { + // arrival of ships in wings don't display + entry->flags |= MLF_HIDDEN; + } + break; + + case LOG_WING_DESTROYED: + case LOG_WING_DEPART: + case LOG_WING_ARRIVE: + index = wing_name_lookup( pname, 1 ); + Assert( index != -1 ); + Assert( info_index != -1 ); // this is the team value + + // get the team value for this wing. Departed or destroyed wings will pass the team + // value in info_index parameter. For arriving wings, get the team value from the + // first ship in the list + if ( type == LOG_WING_ARRIVE ) { + si = Wings[index].ship_index[0]; + Assert( si != -1 ); + mission_log_flag_team( entry, ML_FLAG_PRIMARY, Ships[si].team ); + } else { + mission_log_flag_team( entry, ML_FLAG_PRIMARY, info_index ); + } + +#ifndef NDEBUG + // MWA 2/25/98. debug code to try to find any ships in this wing that have departed. + // scan through all log entries and find at least one ship_depart entry for a ship + // that was in this wing. + if ( type == LOG_WING_DEPART ) { + int i; + + // if all were destroyed, then don't do this debug code. + if ( Wings[index].total_destroyed == Wings[index].total_arrived_count ){ + break; + } + + for ( i = 0; i < last_entry; i++ ) { + if ( log_entries[i].type != LOG_SHIP_DEPART ){ + continue; + } + if( log_entries[i].index == index ){ + break; + } + } + if ( i == last_entry ){ + Int3(); // get Allender -- cannot find any departed ships from wing that supposedly departed. + } + } +#endif + + break; + + // don't display waypoint done entries + case LOG_WAYPOINTS_DONE: + entry->flags |= MLF_HIDDEN; + break; + + default: + break; + } + + entry->timestamp = Missiontime; + + // if in multiplayer and I am the master, send this log entry to everyone + if ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) ){ + send_mission_log_packet( last_entry ); + } + + last_entry++; + +#ifndef NDEBUG + if ( !(last_entry % 10) ) { + if ( (last_entry > LOG_HALFWAY_REPORT_NUM) && (last_entry > last_entry_save) ){ + nprintf(("missionlog", "new highwater point reached for mission log (%d entries).\n", last_entry)); + } + } +#endif + +} + +// function, used in multiplayer only, which adds an entry sent by the host of the game, into +// the mission log. The index of the log entry is passed as one of the parameters in addition to +// the normal parameters used for adding an entry to the log +void mission_log_add_entry_multi( int type, char *pname, char *sname, int index, fix timestamp, int flags ) +{ + log_entry *entry; + + // we'd better be in multiplayer and not the master of the game + Assert ( Game_mode & GM_MULTIPLAYER ); + Assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ); + + // mark any entries as obsolete. Part of the pruning is done based on the type (and name) passed + // for a new entry + mission_log_obsolete_entries(type, pname); + + entry = &log_entries[last_entry]; + + if ( last_entry == MAX_LOG_ENTRIES ){ + return; + } + + last_entry++; + + entry->type = type; + if ( pname ) { + Assert (strlen(pname) < NAME_LENGTH); + strcpy(entry->pname, pname); + } + if ( sname ) { + Assert (strlen(sname) < NAME_LENGTH); + strcpy(entry->sname, sname); + } + entry->index = index; + + entry->flags = flags; + entry->timestamp = timestamp; +} + +// function to determine is the given event has taken place count number of times. + +int mission_log_get_time_indexed( int type, char *pname, char *sname, int count, fix *time) +{ + int i, found; + log_entry *entry; + + entry = &log_entries[0]; + for (i = 0; i < last_entry; i++) { + found = 0; + if ( entry->type == type ) { + // if we are looking for a dock/undock entry, then we don't care about the order in which the names + // were passed into this function. Count the entry as found if either name matches both in the other + // set. + if ( (type == LOG_SHIP_DOCK) || (type == LOG_SHIP_UNDOCK) ) { + Assert ( sname ); + if ( (!stricmp(entry->pname, pname) && !stricmp(entry->sname, sname)) || (!stricmp(entry->pname, sname) && !stricmp(entry->sname, pname)) ) + found = 1; + } else { + // for non dock/undock goals, then the names are important! + if ( stricmp(entry->pname, pname) ) + goto next_entry; + if ( !sname || !stricmp(sname, entry->sname) ) + found = 1; + } + + if ( found ) { + count--; + if ( !count ) { + entry->flags |= MLF_ESSENTIAL; // since the goal code asked for this entry, mark it as essential + if ( time ) + *time = entry->timestamp; + return 1; + } + } + } + +next_entry: + entry++; + } + + return 0; +} + +// this function determines if the given type of event on the specified +// object has taken place yet. If not, it returns 0. If it has, the +// timestamp that the event happened is returned in the time parameter +int mission_log_get_time( int type, char *pname, char *sname, fix *time ) +{ + return mission_log_get_time_indexed( type, pname, sname, 1, time ); +} + +void message_log_add_seg(int n, int x, int color, char *text, int flags = 0) +{ + log_text_seg *seg, **parent; + + if ((n < 0) || (n >= MAX_LOG_LINES)) + return; + + parent = &Log_lines[n]; + while (*parent) + parent = &((*parent)->next); + + seg = (log_text_seg *) malloc(sizeof(log_text_seg)); + Assert(seg); + seg->text = strdup(text); + seg->color = color; + seg->x = x; + seg->flags = flags; + seg->next = NULL; + *parent = seg; +} + +void message_log_add_segs(char *text, int color, int flags = 0) +{ + char *ptr; + int w; + + while (1) { + if (X == ACTION_X) { + while (is_white_space(*text)) + text++; + } + + if (!text) { + mprintf(("Why are you passing a NULL pointer to message_log_add_segs?\n")); + return; + } + + if ( !text[0] ) { + return; + } + + if (P_width - X < 1) + ptr = text; + else + ptr = split_str_once(text, P_width - X); + + if (ptr != text) + message_log_add_seg(Num_log_lines, X, color, text, flags); + + if (!ptr) { + gr_get_string_size(&w, NULL, text); + X += w; + return; + } + + Num_log_lines++; + X = ACTION_X; + text = ptr; + } +} + +void message_log_remove_segs(int n) +{ + log_text_seg *ptr, *ptr2; + + if ((n < 0) || (n >= MAX_LOG_LINES)) + return; + + ptr = Log_lines[n]; + while (ptr) { + ptr2 = ptr->next; + free(ptr); + ptr = ptr2; + } + + Log_lines[n] = NULL; +} + +// pw = total pixel width +void message_log_init_scrollback(int pw) +{ + char text[256]; + log_entry *entry; + int i, c, kill, type; + + P_width = pw; + mission_log_cull_obsolete_entries(); // compact array so we don't have gaps + + // initialize the log lines data + Num_log_lines = 0; + for (i=0; iflags & MLF_HIDDEN) + continue; + + // track time of event (normal timestamp milliseconds format) + Log_line_timestamps[Num_log_lines] = (int) ( f2fl(entry->timestamp) * 1000.0f ); + + // Generate subject ship text for entry + if ( entry->flags & MLF_PRIMARY_FRIENDLY ){ + c = LOG_COLOR_FRIENDLY; + } else if ( entry->flags & MLF_PRIMARY_HOSTILE ){ + c = LOG_COLOR_HOSTILE; + } else if ( (entry->type == LOG_GOAL_SATISFIED) || (entry->type == LOG_GOAL_FAILED) ){ + c = LOG_COLOR_BRIGHT; + } else { + c = LOG_COLOR_OTHER; + } + + if ( (Lcl_gr) && ((entry->type == LOG_GOAL_FAILED) || (entry->type == LOG_GOAL_SATISFIED)) ) { + // in german goal events, just say "objective" instead of objective name + // this is cuz we cant translate objective names + message_log_add_seg(Num_log_lines, OBJECT_X, c, "Einsatzziel"); + } else { + message_log_add_seg(Num_log_lines, OBJECT_X, c, entry->pname); + } + + // now on to the actual message itself + X = ACTION_X; + kill = 0; + if ( entry->flags & MLF_SECONDARY_FRIENDLY ){ + c = LOG_COLOR_FRIENDLY; + } else if ( entry->flags & MLF_SECONDARY_HOSTILE ){ + c = LOG_COLOR_HOSTILE; + } else { + c = LOG_COLOR_NORMAL; + } + + switch (entry->type) { + case LOG_SHIP_DESTROYED: + message_log_add_segs(XSTR( "Destroyed", 404), LOG_COLOR_NORMAL); + if (strlen(entry->sname)) { + message_log_add_segs(XSTR( " Kill: ", 405), LOG_COLOR_NORMAL); + message_log_add_segs(entry->sname, c); + if (entry->index >= 0) { + sprintf(text, NOX(" (%d%%)"), entry->index); + message_log_add_segs(text, LOG_COLOR_BRIGHT); + } + } + break; + + case LOG_SELF_DESTRUCT: + message_log_add_segs(XSTR( "Self Destructed", 1476), LOG_COLOR_NORMAL); + break; + + case LOG_WING_DESTROYED: + message_log_add_segs(XSTR( "Destroyed", 404), LOG_COLOR_NORMAL); + break; + + case LOG_SHIP_ARRIVE: + message_log_add_segs(XSTR( "Arrived", 406), LOG_COLOR_NORMAL); + break; + + case LOG_WING_ARRIVE: + if (entry->index > 1){ + sprintf(text, XSTR( "Arrived (wave %d)", 407), entry->index); + } else { + strcpy(text, XSTR( "Arrived", 406)); + } + message_log_add_segs(text, LOG_COLOR_NORMAL); + break; + + case LOG_SHIP_DEPART: + message_log_add_segs(XSTR( "Departed", 408), LOG_COLOR_NORMAL); + break; + + case LOG_WING_DEPART: + message_log_add_segs(XSTR( "Departed", 408), LOG_COLOR_NORMAL); + break; + + case LOG_SHIP_DOCK: + message_log_add_segs(XSTR( "docked with ", 409), LOG_COLOR_NORMAL); + message_log_add_segs(entry->sname, c); + break; + + case LOG_SHIP_SUBSYS_DESTROYED: { + int si_index, model_index; + + si_index = (int)((entry->index >> 16) & 0xffff); + model_index = (int)(entry->index & 0xffff); + + message_log_add_segs(XSTR( "Subsystem ", 410), LOG_COLOR_NORMAL); + //message_log_add_segs(entry->sname, LOG_COLOR_BRIGHT); + char *subsys_name = Ship_info[si_index].subsystems[model_index].name; + if (Ship_info[si_index].subsystems[model_index].type == SUBSYSTEM_TURRET) { + subsys_name = XSTR("Turret", 1487); + } + message_log_add_segs(subsys_name, LOG_COLOR_BRIGHT); + message_log_add_segs(XSTR( " destroyed", 411), LOG_COLOR_NORMAL); + break; + } + + case LOG_SHIP_UNDOCK: + message_log_add_segs(XSTR( "Undocked with ", 412), LOG_COLOR_NORMAL); + message_log_add_segs(entry->sname, c); + break; + + case LOG_SHIP_DISABLED: + message_log_add_segs(XSTR( "Disabled", 413), LOG_COLOR_NORMAL); + break; + + case LOG_SHIP_DISARMED: + message_log_add_segs(XSTR( "Disarmed", 414), LOG_COLOR_NORMAL); + break; + + case LOG_PLAYER_REARM: + message_log_add_segs(XSTR( " called for rearm", 415), LOG_COLOR_NORMAL); + break; + + case LOG_PLAYER_REARM_ABORT: + message_log_add_segs(XSTR( " aborted rearm", 416), LOG_COLOR_NORMAL); + break; + + case LOG_PLAYER_REINFORCEMENT: + message_log_add_segs(XSTR( "Called in as reinforcement", 417), LOG_COLOR_NORMAL); + break; + + case LOG_CARGO_REVEALED: + Assert( entry->index != -1 ); + message_log_add_segs(XSTR( "Cargo revealed: ", 418), LOG_COLOR_NORMAL); + message_log_add_segs( Cargo_names[entry->index], LOG_COLOR_BRIGHT ); + break; + + case LOG_CAP_SUBSYS_CARGO_REVEALED: + Assert( entry->index != -1 ); + message_log_add_segs(entry->sname, LOG_COLOR_NORMAL); + message_log_add_segs(XSTR( " subsystem cargo revealed: ", 1488), LOG_COLOR_NORMAL); + message_log_add_segs( Cargo_names[entry->index], LOG_COLOR_BRIGHT ); + break; + + + case LOG_GOAL_SATISFIED: + case LOG_GOAL_FAILED: { + type = Mission_goals[entry->index].type & GOAL_TYPE_MASK; + + // don't display failed bonus goals + if ( (type == BONUS_GOAL) && (entry->type == LOG_GOAL_FAILED) ) { + kill = 1; + break; // don't display this line + } + + sprintf( text, XSTR( "%s objective ", 419), Goal_type_text(type) ); + if ( entry->type == LOG_GOAL_SATISFIED ) + strcat(text, XSTR( "satisfied.", 420)); + else + strcat(text, XSTR( "failed.", 421)); + + message_log_add_segs(text, LOG_COLOR_BRIGHT, (entry->type == LOG_GOAL_SATISFIED?LOG_FLAG_GOAL_TRUE:LOG_FLAG_GOAL_FAILED) ); + break; + } // matches case statement! + } + + if (kill) { + message_log_remove_segs(Num_log_lines); + + } else { + if (Num_log_lines < MAX_LOG_LINES) + Num_log_lines++; + } + } +} + +void message_log_shutdown_scrollback() +{ + int i; + + for (i=0; i= Num_log_lines) + break; + + if (Log_line_timestamps[line]) { + gr_set_color_fast(&Color_text_normal); + gr_print_timestamp(list_x + TIME_X, list_y + y, Log_line_timestamps[line]); + } + + seg = Log_lines[line]; + while (seg) { + switch (seg->color) { + case LOG_COLOR_BRIGHT: + gr_set_color_fast(&Color_bright); + break; + + case LOG_COLOR_FRIENDLY: + gr_set_color_fast(&Color_bright_green); + break; + + case LOG_COLOR_HOSTILE: + gr_set_color_fast(&Color_bright_red); + break; + + case LOG_COLOR_OTHER: + gr_set_color_fast(&Color_normal); + break; + + default: + gr_set_color_fast(&Color_text_normal); + break; + } + + strcpy(buf, seg->text); + if (seg->x < ACTION_X) + gr_force_fit_string(buf, 256, ACTION_X - OBJECT_X - 8); + else + gr_force_fit_string(buf, 256, list_w - seg->x); + + gr_string(list_x + seg->x, list_y + y, buf); + + // possibly "print" some symbols for interesting log entries + if ( (seg->flags & LOG_FLAG_GOAL_TRUE) || (seg->flags & LOG_FLAG_GOAL_FAILED) ) { + int i; + + if ( seg->flags & LOG_FLAG_GOAL_FAILED ) + gr_set_color_fast(&Color_bright_red); + else + gr_set_color_fast(&Color_bright_green); + + i = list_y + y + font_h / 2 - 1; + gr_circle(list_x + TIME_X - 6, i, 5); + + gr_set_color_fast(&Color_bright); + gr_line(list_x + TIME_X - 10, i, list_x + TIME_X - 8, i); + gr_line(list_x + TIME_X - 6, i - 4, list_x + TIME_X - 6, i - 2); + gr_line(list_x + TIME_X - 4, i, list_x + TIME_X - 2, i); + gr_line(list_x + TIME_X - 6, i + 2, list_x + TIME_X - 6, i + 4); + } + + seg = seg->next; + } + + y += font_h; + line++; + } +} diff --git a/src/mission/missionmessage.cpp b/src/mission/missionmessage.cpp new file mode 100644 index 0000000..60f214e --- /dev/null +++ b/src/mission/missionmessage.cpp @@ -0,0 +1,2091 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionMessage.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Controls messaging to player during the mission + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 32 9/12/99 8:09p Dave + * Fixed problem where skip-training button would cause mission messages + * not to get paged out for the current mission. + * + * 31 9/01/99 2:52p Andsager + * Add new heads to FRED and some debug code for playing heads + * + * 30 8/28/99 7:29p Dave + * Fixed wingmen persona messaging. Make sure locked turrets don't count + * towards the # attacking a player. + * + * 29 8/26/99 8:51p Dave + * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes. + * + * 28 8/23/99 5:04p Jefff + * Added new mission flag to disable built-in messages from playing. + * Added fred support as well. + * + * 27 8/19/99 10:12a Alanl + * preload mission-specific messages on machines greater than 48MB + * + * 26 8/18/99 12:09p Andsager + * Add debug if message has no anim for message. Make messages come from + * wing leader. + * + * 25 7/31/99 2:30p Dave + * Added nifty mission message debug viewing keys. + * + * 24 7/24/99 2:19p Dave + * Fixed broken build. + * + * 23 7/23/99 5:44p Andsager + * make personas consistently choose same ship + * + * 22 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 21 7/14/99 4:27p Andsager + * Added multiple message debug check + * + * 20 7/06/99 10:41a Andsager + * Add AWACS need help messages + * + * 19 7/02/99 11:16a Andsager + * Removed mult message debug check. + * + * 18 7/02/99 11:13a Andsager + * max debug version + * + * 17 6/16/99 10:20a Dave + * Added send-message-list sexpression. + * + * 16 6/14/99 5:53p Dave + * Removed duplicate message check temporarily. + * + * 15 6/10/99 3:43p Dave + * Do a better job of syncing text colors to HUD gauges. + * + * 14 6/09/99 2:56p Andsager + * Check all messages for repeat. Allow multiple versions of same message + * if queued > 20 apart. + * + * 13 6/07/99 11:33a Anoop + * Get rid of erroneous Int3() in multiple message check. + * + * 12 6/07/99 10:31a Andsager + * Get rid of false multiplayer multiple messages catch. + * + * 11 6/03/99 2:56p Andsager + * DOH!! + * + * 10 6/03/99 2:44p Andsager + * Fix stupid bug in debug code. + * + * 9 6/03/99 2:08p Andsager + * Put in debug code to find multiple mission messages. + * + * 8 3/29/99 6:17p Dave + * More work on demo system. Got just about everything in except for + * blowing ships up, secondary weapons and player death/warpout. + * + * 7 1/28/99 12:19a Dave + * Fixed a dumb debug build unhandled exception. + * + * 6 1/07/99 10:08a Jasen + * coords + * + * 5 1/07/99 9:24a Dave + * Put in hi-res coord support for head anim. + * + * 4 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 3 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 127 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 126 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 125 5/24/98 12:55a Mike + * Fix bug with scream from Installation. Should also fix bug with double + * screams from some ships. + * + * 124 5/18/98 6:06p Lawrance + * Don't play messages or auto-target on first frame + * + * 123 5/15/98 8:36p Lawrance + * Add 'target ship that last sent transmission' target key + * + * 122 5/09/98 10:00p Allender + * make vasudan persona for support use terran support persona + * + * 121 5/08/98 11:21a Allender + * fix ingame join trouble. Small messaging fix. Enable collisions for + * friendlies again + * + * 120 5/06/98 12:19p Lawrance + * Fix typo for 'Stray Warning Final' + * + * 119 5/05/98 9:12p Allender + * fix large problem introduced last checkin when changiing Assert to if + * + * 118 5/05/98 4:12p Chad + * changed Assert info if statement when removing messages from queue when + * too old + * + * 117 5/01/98 12:34p John + * Added code to force FreeSpace to run in the same dir as exe and made + * all the parse error messages a little nicer. + * + * 116 4/27/98 9:00p Allender + * mission specific messages from # are now sourced to terran + * command + * + * 115 4/26/98 11:35a Allender + * make traitor message play by iteself in all cases + * + * 114 4/25/98 11:49p Lawrance + * Add Terran Command stray messages + * + * 113 4/22/98 9:17a Allender + * be sure that builtin command messages play with the correct hud source. + * Also be sure that messages which get converted to Terran command to the + * same + * + * 112 4/20/98 1:30a Lawrance + * Don't load head animations if talking head gauge is disabled. + * + * 111 4/17/98 11:03a Allender + * some rearm message being played too often and sent with incorrect + * 'who-from' + * + * 110 4/13/98 5:06p Lawrance + * Cut off talking head about 250ms before wave ends + * + * 109 4/10/98 9:14a Lawrance + * fix up persona code for the demo + * + * 108 4/09/98 2:15p Allender + * fixed compiler warnings + * + * 107 4/09/98 12:36p Allender + * don't allow the same ship to have messages overlapping. Put in code to + * check for ship's existence (wingman only) before actually playing + * message + * + * 106 4/09/98 12:32a Lawrance + * Fix bugs related to multiple screams from same ship, builtin messages + * playing after screams, or praising while severly damaged. + * + * 105 4/08/98 3:45p Allender + * mission message overhaul. Make message from any wingman mean any + * wingman with that persona. Terran command wave and ani's for dead + * ships now play correctly. + * + * 104 4/07/98 8:09p Lawrance + * don't play talking heads in the demo + * + * 103 4/07/98 5:30p Lawrance + * Player can't send/receive messages when comm is destroyed. Garble + * messages when comm is damaged. + * + * 102 4/07/98 5:26p Allender + * low priority mission specific messages won't interrupt anything. + * + * 101 4/07/98 10:51a Allender + * remove any allied from message senders. Make heads for mission + * specific messages play appropriately + * + * 100 4/07/98 12:04a Mike + * New system for instructor chastising player if he fires at instructor. + * + * 99 4/03/98 11:39a Lawrance + * only allow 1 wingman persona in demo + * + * 98 4/02/98 1:09p Allender + * don't process messages before player "enters" mission (i.e. due to + * player entry delay) + * + * 97 4/02/98 10:06a Allender + * wing arrival message for delta and epsilon wings + * + * 96 4/01/98 10:47p Lawrance + * Supporting builtin messages for rearm and repair requests + * + * 95 3/25/98 8:43p Hoffoss + * Changed anim_play() to not be so damn complex when you try and call it. + * + * 94 3/24/98 12:46p Allender + * save shipnum before killing currently playing message in preparation + * for playing death scream. + * + * 93 3/22/98 3:54p Andsager + * AL: Prevent -1 index into Ships[] array when playing a scream + * + * 92 3/18/98 10:20p Allender + * force wingman scream when he's talking and then dies + * + * 91 3/18/98 12:03p John + * Marked all the new strings as externalized or not. + * + * 90 3/17/98 4:01p Hoffoss + * Added HUD_SOURCE_TERRAN_CMD and changed code to utilize it when a + * message is being sent from Terran Command. + * + * 89 3/05/98 10:18p Lawrance + * Play voice cue sound when there is no voice file present + * + * 88 3/02/98 5:42p John + * Removed WinAVI stuff from Freespace. Made all HUD gauges wriggle from + * afterburner. Made gr_set_clip work good with negative x &y. Made + * model_caching be on by default. Made each cached model have it's own + * bitmap id. Made asteroids not rotate when model_caching is on. + * + * 87 3/02/98 9:34a Allender + * don't allow mission specific messages to timeout. Assert when trying + * to remove a mission specific messages from the queue. Print out in the + * log file if the voice didn't play. + * + * 86 2/23/98 8:45a John + * Externalized Strings + * + * 85 2/20/98 8:33p Lawrance + * Add the 'All Alone' message + * + * 84 2/16/98 2:20p Allender + * make death scream kill any other messages from that ship + * + * 83 2/12/98 4:58p Lawrance + * Add support for 'All Clear' radio message + * + * 82 2/11/98 9:44p Allender + * rearm repair code fixes. hud support view shows abort status. New + * support ship killed message. More network stats + * + * 81 2/04/98 10:44p Allender + * mark personas as not used between missions. Don't ever randomly + * choose a Vasudan persona + * + * 80 1/29/98 11:38a Allender + * support for Vasudan personas + * + * 79 1/25/98 10:04p Lawrance + * Fix nasty divide-by-zero bug in message_calc_anim_start_frame(). + * + * 78 1/24/98 4:46p Lawrance + * add in support for new voice messasges + * + * 77 1/22/98 5:13p Lawrance + * pick useful starting frame when playing animation + * + * 76 1/21/98 7:20p Lawrance + * Make subsystem locking only work with line-of-sight, cleaned up locking + * code, moved globals to player struct. + * + * 75 1/21/98 11:54a Duncan + * Commended out assert for the moment to allow Fred to run. Mark can fix + * this properly later, since he understands it. + * + * 74 1/21/98 10:33a Allender + * fixed up messaging code to play a random head when playing a builtin + * message + * + * 73 1/20/98 6:21p Lawrance + * Stop animation from playing when voice clip ends early. + * + * 72 1/20/98 3:43p Allender + * don't queue messages when player becomes traitor + * + * 71 1/20/98 12:52p Lawrance + * Draw talking head as alpha-color bitmap, black out region behind + * animation. + * + * 70 1/20/98 10:20a Lawrance + * Draw head animation as alpha-colored. + * + * 69 1/18/98 9:51p Lawrance + * Add support for 'Player Died' messages. + * + * 68 1/14/98 9:49p Allender + * removed 3'oclock and 9'oclock messages + * + * 67 1/13/98 3:11p Allender + * new messages for disable/disarm + * + * 66 1/12/98 11:16p Lawrance + * Wonderful HUD config. + * + * 65 1/07/98 4:41p Allender + * minor modification to special messages. Fixed cargo_revealed problem + * for multiplayer and problem with is-cargo-known sexpression + * + * 64 12/15/97 12:14p Allender + * implemented overlapping messages + * + * 63 12/12/97 4:58p Allender + * make messages interruptable. Put in code to support multiple messages + * at once, although this feature not fully implemented yet. + * + * 62 12/04/97 9:37p Dave + * Fixed a bunch of multiplayer messaging bugs. + * + * 61 12/02/97 2:37p Allender + * added asserts to be sure that an actual ship (i.e. not cargo or + * otherwise) is the ship sending a message to the player + * + * $NoKeywords: $ + */ + +#include "linklist.h" +#include "missionmessage.h" +#include "missiontraining.h" +#include "hudmessage.h" +#include "hudtarget.h" +#include "sexp.h" +#include "timer.h" +#include "parselo.h" +#include "gamesnd.h" +#include "sound.h" +#include "freespace.h" +#include "multi.h" +#include "multimsgs.h" +#include "gamesequence.h" +#include "animplay.h" +#include "controlsconfig.h" +#include "audiostr.h" +#include "hudsquadmsg.h" +#include "multiutil.h" +#include "hud.h" +#include "subsysdamage.h" +#include "emp.h" +#include "localize.h" +#include "demo.h" +#include "hudconfig.h" + +// here is a text list of the builtin message names. These names are used to match against +// names read in for builtin message radio bits to see what message to play. These are +// generic names, meaning that there will be the same message type for a number of different +// personas +char *Builtin_message_types[MAX_BUILTIN_MESSAGE_TYPES] = +{ +//XSTR:OFF + "Arrive Enemy", + "Attack Target", + "Beta Arrived", + "Check 6", + "Engage", + "Gamma Arrived", + "Help", + "Praise", + "Backup", + "Ignore Target", + "No", + "Oops 1", + "Permission", // AL: no code support yet + "Stray", // DA: no code support + "Depart", + "yes", + "Rearm on Way", + "On way", + "Rearm warping in", + "No Target", + "Docking Start", // AL: no message seems to exist for this + "Repair Done", + "Repair Aborted", + "Traitor", + "Rearm", + "Disable Target", + "Disarm Target", + "Player Dead", + "Death", + "Support Killed", + "All Clear", // DA: no code support + "All Alone", + "Repair", + "Delta Arrived", + "Epsilon Arrived", + "Instructor Hit", + "Instructor Attack", + "Stray Warning", + "Stray Warning Final", + "AWACS at 75", + "AWACS at 25" +//XSTR:ON +}; + +MMessage Messages[MAX_MISSION_MESSAGES]; +int Message_times[MAX_MISSION_MESSAGES]; + +int Num_messages, Num_message_avis, Num_message_waves; +int Num_builtin_messages, Num_builtin_avis, Num_builtin_waves; + +int Message_debug_index = -1; + +message_extra Message_avis[MAX_MESSAGE_AVIS]; +message_extra Message_waves[MAX_MESSAGE_WAVES]; + +#define MAX_PLAYING_MESSAGES 2 + +#ifdef FS2_DEMO + #define MAX_WINGMAN_HEADS 1 + #define MAX_COMMAND_HEADS 1 +#else +#define MAX_WINGMAN_HEADS 2 +#define MAX_COMMAND_HEADS 3 +#endif + +//XSTR:OFF +#define HEAD_PREFIX_STRING "head-" +#define COMMAND_HEAD_PREFIX "head-cm1" +#define COMMAND_WAVE_PREFIX "TC_" +#define SUPPORT_NAME "Support" +//XSTR:ON + +// variables to keep track of messages that are currently playing +int Num_messages_playing; // number of is a message currently playing? + +typedef struct pmessage { + anim_instance *anim; // handle of anim currently playing + int wave; // handle of wave currently playing + int id; // id of message currently playing + int priority; // priority of message currently playing + int shipnum; // shipnum of ship sending this message, -1 if from Terran command + int builtin_type; // if a builtin message, type of the message +} pmessage; + +LOCAL pmessage Playing_messages[MAX_PLAYING_MESSAGES]; + +int Message_shipnum; // ship number of who is sending message to player -- used outside this module + +// variables to control message queuing. All new messages to the player are queued. The array +// will be ordered by priority, then time submitted. + +#define MQF_CONVERT_TO_COMMAND (1<<0) // convert this queued message to terran command +#define MQF_CHECK_ALIVE (1<<1) // check for the existence of who_from before sending + +typedef struct message_q { + fix time_added; // time at which this entry was added + int window_timestamp; // timestamp which will tell us how long we have to play the message + int priority; // priority of the message + int message_num; // index into the Messages[] array + char who_from[NAME_LENGTH]; // who this message is from + int source; // who the source of the message is (HUD_SOURCE_* type) + int builtin_type; // type of builtin message (-1 if mission message) + int flags; // should this message entry be converted to Terran Command head/wave file + int min_delay_stamp; // minimum delay before this message will start playing + int group; // message is part of a group, don't time it out +} message_q; + +#define MAX_MESSAGE_Q 30 +#define MAX_MESSAGE_LIFE F1_0*30 // After being queued for 30 seconds, don't play it +#define DEFAULT_MESSAGE_LENGTH 3000 // default number of milliseconds to display message indicator on hud +message_q MessageQ[MAX_MESSAGE_Q]; +int MessageQ_num; // keeps track of number of entries on the queue. + +#define MESSAGE_IMMEDIATE_TIMESTAMP 1000 // immediate messages must play within 1 second +#define MESSAGE_SOON_TIMESTAMP 5000 // "soon" messages must play within 5 seconds +#define MESSAGE_ANYTIME_TIMESTAMP -1 // anytime timestamps are invalid + +// Persona information +int Num_personas; +Persona Personas[MAX_PERSONAS]; + +char *Persona_type_names[MAX_PERSONA_TYPES] = +{ +//XSTR:OFF + "wingman", + "support", + "large", + "command", +//XSTR:ON +}; + +int Command_persona; + +/////////////////////////////////////////////////////////////////// +// used to distort incoming messages when comms are damaged +/////////////////////////////////////////////////////////////////// +static int Message_wave_muted; +static int Message_wave_duration; +static int Next_mute_time; + +#define MAX_DISTORT_PATTERNS 2 +#define MAX_DISTORT_LEVELS 6 +static float Distort_patterns[MAX_DISTORT_PATTERNS][MAX_DISTORT_LEVELS] = +{ + {0.20f, 0.20f, 0.20f, 0.20f, 0.20f, 0.20f}, + {0.10f, 0.20f, 0.25f, 0.25f, 0.05f, 0.15f} +}; + +static int Distort_num; // which distort pattern is being used +static int Distort_next; // which section of distort pattern is next + +int Head_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 7, 45 + }, + { // GR_1024 + 7, 66 + } +}; + +// forward declaration +void message_maybe_distort_text(char *text); + +// following functions to parse messages.tbl -- code pretty much ripped from weapon/ship table parsing code + +// functions to deal with parsing personas. Personas are just a list of names that give someone +// sending a message an identity which spans the life of the mission +void persona_parse() +{ + int i; + char type[NAME_LENGTH]; + + Assert ( Num_personas < MAX_PERSONAS ); + + Personas[Num_personas].flags = 0; + required_string("$Persona:"); + stuff_string(Personas[Num_personas].name, F_NAME, NULL); + + // get the type name and set the appropriate flag + required_string("$Type:"); + stuff_string( type, F_NAME, NULL ); + for ( i = 0; i < MAX_PERSONA_TYPES; i++ ) { + if ( !stricmp( type, Persona_type_names[i]) ) { + + Personas[Num_personas].flags |= (1<name, F_NAME, NULL); + + // team + msgp->multi_team = -1; + if(optional_string("$Team:")){ + int mt; + stuff_int(&mt); + + // keep it real + if((mt < 0) || (mt >= 2)){ + mt = -1; + } + + // only bother with filters if multiplayer and TvT + if(Fred_running || ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ){ + msgp->multi_team = mt; + } + } + + // backwards compatibility for old fred missions - all new ones should use $MessageNew + if(optional_string("$Message:")){ + stuff_string(msgp->message, F_MESSAGE, NULL); + } else { + required_string("$MessageNew:"); + stuff_string(msgp->message, F_MULTITEXT, NULL); + } + + msgp->persona_index = -1; + if ( optional_string("+Persona:") ) { + stuff_string(persona_name, F_NAME, NULL); + msgp->persona_index = message_persona_name_lookup( persona_name ); + } + + if ( !Fred_running) + msgp->avi_info.index = -1; + else + msgp->avi_info.name = NULL; + + if ( optional_string("+AVI Name:") ) { + char avi_name[MAX_FILENAME_LEN]; + + stuff_string(avi_name, F_NAME, NULL); + if ( !Fred_running ) { + msgp->avi_info.index = add_avi(avi_name); + } else { + msgp->avi_info.name = strdup(avi_name); + } + } + + if ( !Fred_running ) + msgp->wave_info.index = -1; + else + msgp->wave_info.name = NULL; + + if ( optional_string("+Wave Name:") ) { + char wave_name[MAX_FILENAME_LEN]; + + stuff_string(wave_name, F_NAME, NULL); + if ( !Fred_running ) { + msgp->wave_info.index = add_wave(wave_name); + } else { + msgp->wave_info.name = strdup(wave_name); + } + } + + Num_messages++; +} + +void parse_msgtbl() +{ + // open localization + lcl_ext_open(); + + read_file_text("messages.tbl"); + reset_parse(); + Num_messages = 0; + Num_personas = 0; + + required_string("#Personas"); + while ( required_string_either("#Messages", "$Persona:")){ + persona_parse(); + } + + required_string("#Messages"); + while (required_string_either("#End", "$Name:")){ + message_parse(); + } + + required_string("#End"); + + // save the number of builting message things -- make initing between missions easier + Num_builtin_messages = Num_messages; + Num_builtin_avis = Num_message_avis; + Num_builtin_waves = Num_message_waves; + + // close localization + lcl_ext_close(); +} + +// this is called at the start of each level +void messages_init() +{ + int rval, i; + static int table_read = 0; + + if ( !table_read ) { + Command_persona = -1; + if ((rval = setjmp(parse_abort)) != 0) { + Error(LOCATION, "Error parsing '%s'\r\nError code = %i.\r\n", "messages.tbl", rval); + + } else { + parse_msgtbl(); + table_read = 1; + } + } + + // reset the number of messages that we have for this mission + Num_messages = Num_builtin_messages; + Message_debug_index = Num_builtin_messages - 1; + Num_message_avis = Num_builtin_avis; + Num_message_waves = Num_builtin_waves; + + // initialize the stuff for the linked lists of messages + MessageQ_num = 0; + for (i = 0; i < MAX_MESSAGE_Q; i++) { + MessageQ[i].priority = -1; + MessageQ[i].time_added = -1; + MessageQ[i].message_num = -1; + MessageQ[i].builtin_type = -1; + MessageQ[i].min_delay_stamp = -1; + MessageQ[i].group = 0; + } + + // this forces a reload of the AVI's and waves for builtin messages. Needed because the flic and + // sound system also get reset between missions! + for (i = 0; i < Num_builtin_avis; i++ ) { + Message_avis[i].anim_data = NULL; + } + + for (i = 0; i < Num_builtin_waves; i++ ){ + Message_waves[i].num = -1; + } + + Message_shipnum = -1; + Num_messages_playing = 0; + for ( i = 0; i < MAX_PLAYING_MESSAGES; i++ ) { + Playing_messages[i].anim = NULL; + Playing_messages[i].wave = -1; + Playing_messages[i].id = -1; + Playing_messages[i].priority = -1; + Playing_messages[i].shipnum = -1; + Playing_messages[i].builtin_type = -1; + } + + // reinitialize the personas. mark them all as not used + for ( i = 0; i < Num_personas; i++ ){ + Personas[i].flags &= ~PERSONA_FLAG_USED; + } + + Message_wave_muted = 0; + Next_mute_time = 1; + + memset(Message_times, 0, sizeof(int)*MAX_MISSION_MESSAGES); +} + +// called to do cleanup when leaving a mission +void message_mission_shutdown() +{ + int i; + + mprintf(("Unloading in mission messages\n")); + + training_mission_shutdown(); + + // remove the wave sounds from memory + for (i = 0; i < Num_message_waves; i++ ) { + if ( Message_waves[i].num != -1 ){ + snd_unload( Message_waves[i].num ); + } + } + +} + +// functions to deal with queuing messages to the message system. + +// Compare function for system qsort() for sorting message queue entries based on priority. +// Return values set to sort array in _decreasing_ order. If priorities equal, sort based +// on time added into queue +int message_queue_priority_compare(const void *a, const void *b) +{ + message_q *ma, *mb; + + ma = (message_q *) a; + mb = (message_q *) b; + + if (ma->priority > mb->priority) { + return -1; + } else if (ma->priority < mb->priority) { + return 1; + } else if (ma->time_added < mb->time_added) { + return -1; + } else if (ma->time_added > mb->time_added) { + return 1; + } else { + return 0; + } +} + +// function to kill all currently playing messages. kill_all parameter tells us to +// kill only the animations that are playing, or wave files too +void message_kill_all( int kill_all ) +{ + int i; + + Assert( Num_messages_playing ); + + // kill sounds for all voices currently playing + for ( i = 0; i < Num_messages_playing; i++ ) { + if ( (Playing_messages[i].anim != NULL) && anim_playing(Playing_messages[i].anim) ) { + anim_stop_playing( Playing_messages[i].anim ); + Playing_messages[i].anim=NULL; + } + + if ( kill_all ) { + if ( (Playing_messages[i].wave != -1 ) && snd_is_playing(Playing_messages[i].wave) ){ + snd_stop( Playing_messages[i].wave ); + } + + Playing_messages[i].shipnum = -1; + } + } + + if ( kill_all ) { + Num_messages_playing = 0; + } +} + +// function to kill nth playing message +void message_kill_playing( int message_num ) +{ + Assert( message_num < Num_messages_playing ); + + if ( (Playing_messages[message_num].anim != NULL) && anim_playing(Playing_messages[message_num].anim) ) { + anim_stop_playing( Playing_messages[message_num].anim ); + Playing_messages[message_num].anim=NULL; + } + if ( (Playing_messages[message_num].wave != -1 ) && snd_is_playing(Playing_messages[message_num].wave) ) + snd_stop( Playing_messages[message_num].wave ); + + Playing_messages[message_num].shipnum = -1; +} + + +// returns true if all messages currently playing are builtin messages +int message_playing_builtin() +{ + int i; + + for ( i = 0; i < Num_messages_playing; i++ ) { + if ( Playing_messages[i].id >= Num_builtin_messages ){ + break; + } + } + + // if we got through the list without breaking, all playing messages are builtin messages + if ( i == Num_messages_playing ){ + return 1; + } else { + return 0; + } +} + +// returns true in any playing message is of the specific builtin type +int message_playing_specific_builtin( int builtin_type ) +{ + int i; + + for (i = 0; i < Num_messages_playing; i++ ) { + if ( (Playing_messages[i].id < Num_builtin_messages) && (Playing_messages[i].builtin_type == builtin_type) ){ + return 1; + } + } + + return 0; +} + +// returns true if all messages current playing are unique messages +int message_playing_unique() +{ + int i; + + for ( i = 0; i < Num_messages_playing; i++ ) { + if ( Playing_messages[i].id < Num_builtin_messages ){ + break; + } + } + + // if we got through the list without breaking, all playing messages are builtin messages + if ( i == Num_messages_playing ){ + return 1; + } else { + return 0; + } +} + + +// returns the highest priority of the currently playing messages +#define MESSAGE_GET_HIGHEST 1 +#define MESSAGE_GET_LOWEST 2 +int message_get_priority(int which) +{ + int i; + int priority; + + if ( which == MESSAGE_GET_HIGHEST ){ + priority = MESSAGE_PRIORITY_LOW; + } else { + priority = MESSAGE_PRIORITY_HIGH; + } + + for ( i = 0; i < Num_messages_playing; i++ ) { + if ( (which == MESSAGE_GET_HIGHEST) && (Playing_messages[i].priority > priority) ){ + priority = Playing_messages[i].priority; + } else if ( (which == MESSAGE_GET_LOWEST) && (Playing_messages[i].priority < priority) ){ + priority = Playing_messages[i].priority; + } + } + + return priority; +} + + +// removes current message from the queue +void message_remove_from_queue(message_q *q) +{ + // quick out if nothing to do. + if ( MessageQ_num <= 0 ) { + return; + } + + MessageQ_num--; + q->priority = -1; + q->time_added = -1; + q->message_num = -1; + q->builtin_type = -1; + q->min_delay_stamp = -1; + q->group = 0; + + if ( MessageQ_num > 0 ) { + qsort(MessageQ, MAX_MESSAGE_Q, sizeof(message_q), message_queue_priority_compare); + } +} + +// Load in the sound data for a message. +// +// index - index into the Message_waves[] array +// +void message_load_wave(int index, const char *filename) +{ + if (index == -1) { + Int3(); + return; + } + + if ( Message_waves[index].num >= 0) { + return; + } + + game_snd tmp_gs; + memset(&tmp_gs, 0, sizeof(game_snd)); + strcpy( tmp_gs.filename, filename ); + Message_waves[index].num = snd_load( &tmp_gs ); + if ( Message_waves[index].num == -1 ) { + nprintf (("messaging", "Cannot load message wave: %s. Will not play\n", Message_waves[index].name )); + } +} + +// Play wave file associated with message +// input: m => pointer to message description +// +// note: changes Messave_wave_duration, Playing_messages[].wave, and Message_waves[].num +void message_play_wave( message_q *q ) +{ + int index; + MissionMessage *m; + char filename[MAX_FILENAME_LEN]; + + // check for multiple messages playing. don't check builtin messages. + if (q->message_num >= Num_builtin_messages) { + if ( (f2fl(Missiontime - Message_times[q->message_num]) < 10) && (f2fl(Missiontime) > 10) ) { + // Int3(); // Get Andsager + } + Message_times[q->message_num] = Missiontime; + } + + m = &Messages[q->message_num]; + + if ( m->wave_info.index != -1 ) { + index = m->wave_info.index; + + // sanity check + Assert( index != -1 ); + if ( index == -1 ){ + return; + } + + // if we need to bash the wave name because of "conversion" to terran command, do it here + strcpy( filename, Message_waves[index].name ); + if ( q->flags & MQF_CONVERT_TO_COMMAND ) { + char *p, new_filename[MAX_FILENAME_LEN]; + + Message_waves[index].num = -1; // forces us to reload the message + + // bash the filename here. Look for "[1-6]_" at the front of the message. If found, then + // convert to TC_* + p = strchr(filename, '_' ); + if ( p == NULL ) { + mprintf(("Cannot convert %s to terran command wave -- find Sandeep or Allender\n", Message_waves[index].name)); + return; + } + + // prepend the command name, and then the rest of the filename. + p++; + strcpy( new_filename, COMMAND_WAVE_PREFIX ); + strcat( new_filename, p ); + strcpy( filename, new_filename ); + } + + // load the sound file into memory + message_load_wave(index, filename); + if ( Message_waves[index].num == -1 ) { + m->wave_info.index = -1; + } + + if ( m->wave_info.index >= 0 ) { + // this call relies on the fact that snd_play returns -1 if the sound cannot be played + Message_wave_duration = snd_get_duration(Message_waves[index].num); + Playing_messages[Num_messages_playing].wave = snd_play_raw( Message_waves[index].num, 0.0f ); + } + } +} + +// Determine the starting frame for the animation +// input: time => time of voice clip, in ms +// ani => pointer to anim data +// reverse => flag to indicate that the start should be time ms from the end (used for death screams) +int message_calc_anim_start_frame(int time, anim *ani, int reverse) +{ + float wave_time, anim_time; + int start_frame; + + start_frame=0; + + // If no voice clip exists, start from beginning of anim + if ( time <= 0 ) { + return start_frame; + } + + // convert time to seconds + wave_time = time/1000.0f; + anim_time = ani->time; + + // If voice clip is longer than anim, start from beginning of anim + if ( wave_time >= (anim_time) ) { + return start_frame; + } + + if ( reverse ) { + start_frame = (ani->total_frames-1) - fl2i(ani->fps * wave_time + 0.5f); + } else { + int num_frames_extra; + num_frames_extra = fl2i(ani->fps * (anim_time - wave_time) + 0.5f); + if ( num_frames_extra > 0 ) { + start_frame=rand()%num_frames_extra; + } + } + + if ( start_frame < 0 ) { + Int3(); + start_frame=0; + } + + return start_frame; +} + +// Play animation associated with message +// input: m => pointer to message description +// q => message queue data +// +// note: changes Messave_wave_duration, Playing_messages[].wave, and Message_waves[].num +void message_play_anim( message_q *q ) +{ + message_extra *anim_info; + int is_death_scream=0, persona_index=-1, rand_index=-1; + char ani_name[MAX_FILENAME_LEN], *p; + MissionMessage *m; + + m = &Messages[q->message_num]; + + // check to see if the avi_index is valid -- try and load/play the avi if so. + if ( m->avi_info.index < 0 ) { + return; + } + + anim_info = &Message_avis[m->avi_info.index]; + + // get the filename. Strip off the extension since we won't need it anyway + strcpy(ani_name, anim_info->name); + p = strchr(ani_name, '.'); // gets us to the extension + if ( p ) { + *p = '\0'; + } + + // builtin messages are given a base ani which we should add a suffix on before trying + // to load the animation. See if this message is a builtin message which has a persona + // attached to it. Deal with munging the name + + // support ships use a wingman head. + // terran command uses it's own set of heads. + int subhead_selected = FALSE; + if ( (q->message_num < Num_builtin_messages) || !(_strnicmp(HEAD_PREFIX_STRING, ani_name, strlen(HEAD_PREFIX_STRING)-1)) ) { + persona_index = m->persona_index; + + // if this ani should be converted to a terran command, set the persona to the command persona + // so the correct head plays. + if ( q->flags & MQF_CONVERT_TO_COMMAND ) { + persona_index = Command_persona; + strcpy( ani_name, COMMAND_HEAD_PREFIX ); + } + + if ( Personas[persona_index].flags & (PERSONA_FLAG_WINGMAN | PERSONA_FLAG_SUPPORT) ) { + // get a random head -- it's one of two. + if ( q->builtin_type == MESSAGE_WINGMAN_SCREAM ) { + rand_index=2; // 3rd version is always death animation + is_death_scream=1; + } else { + rand_index = (Missiontime % MAX_WINGMAN_HEADS); + } + sprintf(ani_name, "%s%c", ani_name, 'a'+rand_index); + subhead_selected = TRUE; + } else if ( Personas[persona_index].flags & (PERSONA_FLAG_COMMAND | PERSONA_FLAG_LARGE) ) { + // get a random head -- it's one of two. + rand_index = (Missiontime % MAX_COMMAND_HEADS); + sprintf(ani_name, "%s%c", ani_name, 'a'+rand_index); + subhead_selected = TRUE; + } + if (!subhead_selected) { + // choose between a and b + rand_index = (Missiontime % MAX_WINGMAN_HEADS); + sprintf(ani_name, "%s%c", ani_name, 'a'+rand_index); + mprintf(("message '%s' with invalid head. Fix by assigning persona to the message.\n", m->name)); + } + nprintf(("Messaging", "playing head %s for %s\n", ani_name, q->who_from)); + } + + // check to see if the avi has been loaded. If not, then load the AVI. On an error loading + // the avi, set the top level index to -1 to avoid multiple tries at loading the flick. + if ( hud_gauge_active(HUD_TALKING_HEAD) ) { + anim_info->anim_data = anim_load( ani_name, 0 ); + } else { + return; + } + + if ( anim_info->anim_data == NULL ) { + nprintf (("messaging", "Cannot load message avi %s. Will not play.\n", ani_name)); + m->avi_info.index = -1; // if cannot load the avi -- set this index to -1 to avoid trying to load multiple times + } + + if ( m->avi_info.index >= 0 ) { + // This call relies on the fact that AVI_play will return -1 if the AVI cannot be played + // if any messages are already playing, kill off any head anims that are currently playing. We will + // only play a head anim of the newest messages being played + if ( Num_messages_playing > 0 ) { + nprintf(("messaging", "killing off any currently playing head animations\n")); + message_kill_all( 0 ); + } + + if ( hud_gauge_active(HUD_TALKING_HEAD) ) { + int anim_start_frame; + anim_play_struct aps; + + // figure out anim start frame + anim_start_frame = message_calc_anim_start_frame(Message_wave_duration, anim_info->anim_data, is_death_scream); + anim_play_init(&aps, anim_info->anim_data, Head_coords[gr_screen.res][0], Head_coords[gr_screen.res][1]); + aps.start_at = anim_start_frame; + + // aps.color = &HUD_color_defaults[HUD_color_alpha]; + aps.color = &HUD_config.clr[HUD_TALKING_HEAD]; + + Playing_messages[Num_messages_playing].anim = anim_play(&aps); + } + } +} + +// process the message queue -- called once a frame +void message_queue_process() +{ + char buf[4096]; + message_q *q; + int i; + MissionMessage *m; + + // Don't play messages until first frame has been rendered + if ( Framecount < 2 ) { + return; + } + + // determine if all playing messages (if any) are done playing. If any are done, remove their + // entries collapsing the Playing_messages array if necessary + if ( Num_messages_playing > 0 ) { + + // for each message playing, determine if it is done. + i = 0; + while ( i < Num_messages_playing ) { + int ani_done, wave_done, j; + + ani_done = 1; + if ( (Playing_messages[i].anim != NULL) && anim_playing(Playing_messages[i].anim) ) + ani_done = 0; + + wave_done = 1; + +// if ( (Playing_messages[i].wave != -1) && snd_is_playing(Playing_messages[i].wave) ) + if ( (Playing_messages[i].wave != -1) && (snd_time_remaining(Playing_messages[i].wave) > 250) ) + wave_done = 0; + + // AL 1-20-98: If voice message is done, kill the animation early + if ( (Playing_messages[i].wave != -1) && wave_done ) { + if ( !ani_done ) { + anim_stop_playing( Playing_messages[i].anim ); + } + } + + // see if the ship sending this message is dying. If do, kill wave and anim + if ( Playing_messages[i].shipnum != -1 ) { + if ( (Ships[Playing_messages[i].shipnum].flags & SF_DYING) && (Playing_messages[i].builtin_type != MESSAGE_WINGMAN_SCREAM) ) { + int shipnum; + + shipnum = Playing_messages[i].shipnum; + message_kill_playing( i ); + // force this guy to scream + // AL 22-2-98: Ensure don't use -1 to index into ships array. Mark, something is incorrect + // here, since message_kill_playing() seems to always set Playing_messages[i].shipnum to -1 + // MWA 3/24/98 -- save shipnum before killing message + // + Assert( shipnum >= 0 ); + if ( !(Ships[shipnum].flags & SF_SHIP_HAS_SCREAMED) ) { + ship_scream( &Ships[shipnum] ); + } + continue; // this should keep us in the while() loop with same value of i. + } // we should enter the next 'if' statement during next pass + } + + // if both ani and wave are done, mark internal variable so we can do next message on queue, and + // global variable to clear voice brackets on hud + if ( wave_done && ani_done ) { + nprintf(("messaging", "Message %d is done playing\n", i)); + Message_shipnum = -1; + Num_messages_playing--; + if ( Num_messages_playing == 0 ) + break; + + // there is still another message playing. Collapse the playing_message array + nprintf(("messaging", "Collapsing playing message stack\n")); + for ( j = i+1; j < Num_messages_playing + 1; j++ ) { + Playing_messages[j-1] = Playing_messages[j]; + } + } else { + // messages is not done playing -- move to next message + i++; + } + } + } + + // preprocess message queue and remove anything on the queue that is too old. If next message on + // the queue can be played, then break out of the loop. Otherwise, loop until nothing on the queue + while ( MessageQ_num > 0 ) { + q = &MessageQ[0]; + if ( timestamp_valid(q->window_timestamp) && timestamp_elapsed(q->window_timestamp) && !q->group) { + // remove message from queue and see if more to remove + nprintf(("messaging", "Message %s didn't play because it didn't fit into time window.\n", Messages[q->message_num].name)); + if ( q->message_num < Num_builtin_messages ){ // we should only ever remove builtin messages this way + message_remove_from_queue(q); + } else { + break; + } + } else { + break; + } + } + + // no need to process anything if there isn't anything on the queue + if ( MessageQ_num <= 0 ){ + return; + } + + // get a pointer to an item on the queue + int found = -1; + int idx = 0; + while((found == -1) && (idx < MessageQ_num)){ + // if this guy has no min delay timestamp, or it has expired, select him + if((MessageQ[idx].min_delay_stamp == -1) || timestamp_elapsed(MessageQ[idx].min_delay_stamp)){ + found = idx; + break; + } + + // next + idx++; + } + + // if we didn't find anything, bail + if(found == -1){ + return; + } + // if this is not the first item on the queue, make it the first item + if(found != 0){ + message_q temp; + + // store the entry + memcpy(&temp, &MessageQ[found], sizeof(message_q)); + + // move all other entries up + for(idx=found; idx>0; idx--){ + memcpy(&MessageQ[idx], &MessageQ[idx-1], sizeof(message_q)); + } + + // plop the entry down as being first + memcpy(&MessageQ[0], &temp, sizeof(message_q)); + } + + q = &MessageQ[0]; + Assert ( q->message_num != -1 ); + Assert ( q->priority != -1 ); + Assert ( q->time_added != -1 ); + + if ( Num_messages_playing ) { + // peek at the first message on the queue to see if it should interrupt, or overlap a currently + // playing message. Mission specific messages will always interrupt builtin messages. They + // will never interrupt other mission specific messages. + // + // Builtin message might interrupt other builtin messages, or overlap them, all depending on + // message priority. + + if ( q->builtin_type == MESSAGE_HAMMER_SWINE ) { + message_kill_all(1); + } else if ( message_playing_specific_builtin(MESSAGE_HAMMER_SWINE) ) { + MessageQ_num = 0; + return; + } else if ( message_playing_builtin() && ( q->message_num >= Num_builtin_messages) && (q->priority > MESSAGE_PRIORITY_LOW) ) { + // builtin is playing and we have a unique message to play. Kill currently playing message + // so unique can play uninterrupted. Only unique messages higher than low priority will interrupt + // other messages. + message_kill_all(1); + nprintf(("messaging", "Killing all currently playing messages to play unique message\n")); + } else if ( message_playing_builtin() && (q->message_num < Num_builtin_messages) ) { + // when a builtin message is queued, we might either overlap or interrupt the currently + // playing message. + // + // we have to check for num_messages_playing (again), since code for death scream might + // kill all messages. + if ( Num_messages_playing ) { + if ( message_get_priority(MESSAGE_GET_HIGHEST) < q->priority ) { + // lower priority message playing -- kill it. + message_kill_all(1); + nprintf(("messaging", "Killing all currently playing messages to play high priority builtin\n")); + } else if ( message_get_priority(MESSAGE_GET_LOWEST) > q->priority ) { + // queued message is a lower priority, so wait it out + return; + } else { + // if we get here, then queued messages is a builtin message with the same priority + // as the currently playing messages. This state will cause messages to overlap. + nprintf(("messaging", "playing builtin message (overlap) because priorities match\n")); + } + } + } else if ( message_playing_unique() && (q->message_num < Num_builtin_messages) ) { + // code messages can kill any low priority mission specific messages + if ( Num_messages_playing ) { + if ( message_get_priority(MESSAGE_GET_HIGHEST) == MESSAGE_PRIORITY_LOW ) { + message_kill_all(1); + nprintf(("messaging", "Killing low priority unique messages to play code message\n")); + } else { + return; // do nothing. + } + } + } else { + return; + } + } + + // if we are playing the maximum number of voices, then return. Make the check here since the above + // code might kill of currently playing messages + if ( Num_messages_playing == MAX_PLAYING_MESSAGES ) + return; + + Message_shipnum = ship_name_lookup( q->who_from ); + + // see if we need to check if sending ship is alive + if ( q->flags & MQF_CHECK_ALIVE ) { + if ( Message_shipnum == -1 ) { + goto all_done; + } + } + + // if this is a ship, then don't play anything if this ship is already talking + if ( Message_shipnum != -1 ) { + for ( i = 0; i < Num_messages_playing; i++ ) { + if ( (Playing_messages[i].shipnum != -1) && (Playing_messages[i].shipnum == Message_shipnum) ){ + return; + } + } + } + + // set up module globals for this message + m = &Messages[q->message_num]; + Playing_messages[Num_messages_playing].anim = NULL; + Playing_messages[Num_messages_playing].wave = -1; + Playing_messages[Num_messages_playing].id = q->message_num; + Playing_messages[Num_messages_playing].priority = q->priority; + Playing_messages[Num_messages_playing].shipnum = Message_shipnum; + Playing_messages[Num_messages_playing].builtin_type = q->builtin_type; + + Message_wave_duration = 0; + + // translate tokens in message to the real things + message_translate_tokens(buf, m->message); + + // AL: added 07/14/97.. only play avi/sound if in gameplay + if ( gameseq_get_state() != GS_STATE_GAME_PLAY ) + goto all_done; + + // AL 4-7-98: Can't receive messages if comm is destroyed + if ( hud_communications_state(Player_ship) == COMM_DESTROYED ) { + goto all_done; + } + + // Don't play death scream unless a small ship. + if ( q->builtin_type == MESSAGE_WINGMAN_SCREAM ) { + int t = Ship_info[Ships[Message_shipnum].ship_info_index].flags; + int t2 = SIF_SMALL_SHIP; + int t3 = t & t2; + if (!t3) { + goto all_done; + } + } + + // play wave first, since need to know duration for picking anim start frame + message_play_wave(q); + + // play animation for head + #ifndef DEMO // do we want this for FS2_DEMO + message_play_anim(q); + #endif + + // distort the message if comms system is damaged + message_maybe_distort_text(buf); + +#ifndef NDEBUG + // debug only -- if the message is a builtin message, put in parens whether or not the voice played + if ( Playing_messages[Num_messages_playing].wave == -1 ) { + strcat( buf, NOX("..(no wavefile for voice)")); + snd_play(&Snds[SND_CUE_VOICE]); + } +#endif + + HUD_sourced_printf( q->source, NOX("%s: %s"), q->who_from, buf ); + + if ( Message_shipnum >= 0 ) { + hud_target_last_transmit_add(Message_shipnum); + } + +all_done: + Num_messages_playing++; + message_remove_from_queue( q ); +} + +// queues up a message to display to the player +void message_queue_message( int message_num, int priority, int timing, char *who_from, int source, int group, int delay, int builtin_type ) +{ + int i, m_persona; + + if ( message_num < 0 ) return; + + // some messages can get queued quickly. Try to filter out certain types of messages before + // they get queued if there are other messages of the same type already queued + if ( (builtin_type == MESSAGE_REARM_ON_WAY) || (builtin_type == MESSAGE_OOPS) ) { + // if it is already playing, then don't play it + if ( message_playing_specific_builtin(builtin_type) ) + return; + + for ( i = 0; i < MessageQ_num; i++ ) { + // if one of these messages is already queued, the don't play + if ( (MessageQ[i].message_num == message_num) && (MessageQ[i].builtin_type == builtin_type) ) + return; + + } + } + + // check to be sure that we haven't reached our max limit on these messages yet. + if ( MessageQ_num == MAX_MESSAGE_Q ) { + Int3(); + return; + } + + // if player is a traitor, no messages for him!!! + if ( Player_ship->team == TEAM_TRAITOR ) { + return; + } + + m_persona = Messages[message_num].persona_index; + + // put the message into a slot + i = MessageQ_num; + MessageQ[i].time_added = Missiontime; + MessageQ[i].priority = priority; + MessageQ[i].message_num = message_num; + MessageQ[i].source = source; + MessageQ[i].builtin_type = builtin_type; + MessageQ[i].min_delay_stamp = timestamp(delay); + MessageQ[i].group = group; + strcpy(MessageQ[i].who_from, who_from); + + // SPECIAL HACK -- if the who_from is terran command, and there is a wingman persona attached + // to this message, then set a bit to tell the wave/anim playing code to play the command version + // of the wave and head + MessageQ[i].flags = 0; + if ( !stricmp(who_from, TERRAN_COMMAND) && (m_persona != -1) && (Personas[m_persona].flags & PERSONA_FLAG_WINGMAN) ) { + MessageQ[i].flags |= MQF_CONVERT_TO_COMMAND; + MessageQ[i].source = HUD_SOURCE_TERRAN_CMD; + } + + if ( (m_persona != -1) && (Personas[m_persona].flags & PERSONA_FLAG_WINGMAN) ) { + if ( !strstr(who_from, ".wav") ) { + MessageQ[i].flags |= MQF_CHECK_ALIVE; + } + } + + // set the timestamp of when to play this message based on the 'timing' value + if ( timing == MESSAGE_TIME_IMMEDIATE ) + MessageQ[i].window_timestamp = timestamp(MESSAGE_IMMEDIATE_TIMESTAMP); + else if ( timing == MESSAGE_TIME_SOON ) + MessageQ[i].window_timestamp = timestamp(MESSAGE_SOON_TIMESTAMP); + else + MessageQ[i].window_timestamp = timestamp(MESSAGE_ANYTIME_TIMESTAMP); // make invalid + + MessageQ_num++; + qsort(MessageQ, MAX_MESSAGE_Q, sizeof(message_q), message_queue_priority_compare); + + // Try to start it! + // MWA -- called every frame from game loop + //message_queue_process(); +} + +// function to return the persona index of the given ship. If it isn't assigned, it will be +// in this function. persona_type could be a wingman, Terran Command, or other generic ship +// type personas. ship is the ship we should assign a persona to +int message_get_persona( ship *shipp ) +{ + int i, ship_type, slist[MAX_PERSONAS], count; + + if ( shipp != NULL ) { + // see if this ship has a persona + if ( shipp->persona_index != -1 ) + return shipp->persona_index; + + // get the type of ship (i.e. support, fighter/bomber, etc) + ship_type = Ship_info[shipp->ship_info_index].flags; + + // shorcut for Vasudan personas. All vasudan fighters/bombers use the same persona. All Vasudan + // large ships will use the same persona + if ( Ship_info[shipp->ship_info_index].species == SPECIES_VASUDAN ) { + int persona_needed; + + if ( ship_type & (SIF_FIGHTER|SIF_BOMBER) ) { + persona_needed = PERSONA_FLAG_WINGMAN; + } else if ( ship_type & SIF_SUPPORT ) { + persona_needed = PERSONA_FLAG_SUPPORT; + } else { + persona_needed = PERSONA_FLAG_LARGE; + } + + // iternate through the persona list finding the one that we need + for ( i = 0; i < Num_personas; i++ ) { + if ( (Personas[i].flags & persona_needed) && (Personas[i].flags & PERSONA_FLAG_VASUDAN) ) { + nprintf(("messaging", "assigning vasudan persona %s to %s\n", Personas[i].name, shipp->ship_name)); + return i; + } + } + + // make support personas use the terran persona by not returning here when looking for + // Vasudan persona + if ( persona_needed != PERSONA_FLAG_SUPPORT ) + return -1; // shouldn't get here eventually, but return -1 for now to deal with missing persona + } + + // iterate through the persona list looking for one not used. Look at the type of persona + // and try to determine appropriate personas to use. + for ( i = 0; i < Num_personas; i++ ) { + + // if this is a vasudan persona -- skip it + if ( Personas[i].flags & PERSONA_FLAG_VASUDAN ) + continue; + + // check the ship types, and don't try to assign those which don't type match + if ( (ship_type & SIF_SUPPORT) && !(Personas[i].flags & PERSONA_FLAG_SUPPORT) ) + continue; + else if ( (ship_type & (SIF_FIGHTER|SIF_BOMBER)) && !(Personas[i].flags & PERSONA_FLAG_WINGMAN) ) + continue; + else if ( !(ship_type & (SIF_FIGHTER|SIF_BOMBER|SIF_SUPPORT)) && !(Personas[i].flags & PERSONA_FLAG_LARGE) ) + continue; + + if ( !(Personas[i].flags & PERSONA_FLAG_USED) ) { + nprintf(("messaging", "assigning persona %s to %s\n", Personas[i].name, shipp->ship_name)); + Personas[i].flags |= PERSONA_FLAG_USED; + return i; + } + } + + // grab a random one, and reuse it (staying within type specifications) + count = 0; + for ( i = 0; i < Num_personas; i++ ) { + + // see if ship meets our criterea + if ( (ship_type & SIF_SUPPORT) && !(Personas[i].flags & PERSONA_FLAG_SUPPORT) ) + continue; + else if ( (ship_type & (SIF_FIGHTER|SIF_BOMBER)) && !(Personas[i].flags & PERSONA_FLAG_WINGMAN) ) + continue; + else if ( !(ship_type & (SIF_FIGHTER|SIF_BOMBER|SIF_SUPPORT)) && !(Personas[i].flags & PERSONA_FLAG_LARGE) ) + continue; + else if ( Personas[i].flags & PERSONA_FLAG_VASUDAN ) // don't use any vasudan persona + continue; + + slist[count] = i; + count++; + } + + // couldn't find appropriate persona type + if ( count == 0 ) + return -1; + + // now get a random one from the list + i = (rand() % count); + i = slist[i]; + + nprintf(("messaging", "Couldn't find a new persona for ship %s, reusing persona %s\n", shipp->ship_name, Personas[i].name)); + + return i; + } + + // for now -- we don't support other types of personas (non-wingman personas) + Int3(); + return 0; +} + +// given a message id#, should it be filtered for me? +int message_filter_multi(int id) +{ + // not multiplayer + if(!(Game_mode & GM_MULTIPLAYER)){ + return 0; + } + + // bogus + if((id < 0) || (id >= Num_messages)){ + mprintf(("Filtering bogus mission message!\n")); + return 1; + } + + // builtin messages + if(id < Num_builtin_messages){ + } + // mission-specific messages + else { + // not team filtered + if(Messages[id].multi_team < 0){ + return 0; + } + + // not TvT + if(!(Netgame.type_flags & NG_TYPE_TEAM)){ + return 0; + } + + // is this for my team? + if((Net_player != NULL) && (Net_player->p_info.team != Messages[id].multi_team)){ + mprintf(("Filtering team-based mission message!\n")); + return 1; + } + } + + return 0; +} + +// send_unique_to_player sends a mission unique (specific) message to the player (possibly a multiplayer +// person). These messages are *not* the builtin messages +void message_send_unique_to_player( char *id, void *data, int m_source, int priority, int group, int delay ) +{ + int i, source; + char *who_from; + + source = 0; + who_from = NULL; + for (i=0; iship_name; + source = HUD_get_team_source(shipp->team); + + // be sure that this ship can actually send a message!!! (i.e. not-not-flyable -- get it!) + Assert( !(Ship_info[shipp->ship_info_index].flags & SIF_NOT_FLYABLE) ); // get allender or alan + } + + // not multiplayer or this message is for me, then queue it + // if ( !(Game_mode & GM_MULTIPLAYER) || ((multi_target == -1) || (multi_target == MY_NET_PLAYER_NUM)) ){ + + // maybe filter it out altogether + if(!message_filter_multi(i)){ + message_queue_message( i, priority, MESSAGE_TIME_ANYTIME, who_from, source, group, delay ); + } + + // record to the demo if necessary + if(Game_mode & GM_DEMO_RECORD){ + demo_POST_unique_message(id, who_from, m_source, priority); + } + // } + + // send a message packet to a player if destined for everyone or only a specific person + if ( MULTIPLAYER_MASTER ){ + send_mission_message_packet( i, who_from, priority, MESSAGE_TIME_SOON, source, -1, -1, -1); + } + + return; // all done with displaying + } + } + nprintf (("messaging", "Couldn't find message id %s to send to player!\n", id )); +} + +// send builtin_to_player sends a message (from messages.tbl) to the player. These messages are +// the generic infomrational type messages. The have priorities like misison specific messages, +// and use a timing to tell how long we should wait before playing this message +void message_send_builtin_to_player( int type, ship *shipp, int priority, int timing, int group, int delay, int multi_target, int multi_team_filter ) +{ + int i, persona_index; + int source; + + // if we aren't showing builtin msgs, bail + if (The_mission.flags & MISSION_FLAG_NO_BUILTIN_MSGS) { + return; + } + + // see if there is a persona assigned to this ship. If not, then try to assign one!!! + if ( shipp ) { + if ( shipp->persona_index == -1 ){ + shipp->persona_index = message_get_persona( shipp ); + } + + persona_index = shipp->persona_index; + if ( persona_index == -1 ) { + nprintf(("messaging", "Couldn't find persona for %s\n", shipp->ship_name )); + } + + // be sure that this ship can actually send a message!!! (i.e. not-not-flyable -- get it!) + Assert( !(Ship_info[shipp->ship_info_index].flags & SIF_NOT_FLYABLE) ); // get allender or alan + } else { + persona_index = Command_persona; // use the terran command persona + } + + // try to find a builtin message with the given type for the given persona + // make a loop out of this routne since we may try to play a message in the wrong + // persona if we can't find the right message for the given persona + do { + for ( i = 0; i < Num_builtin_messages; i++ ) { + char *name, *who_from; + + name = Builtin_message_types[type]; + + // see if the have the type of message + if ( stricmp(Messages[i].name, name) ){ + continue; + } + + // must have the correct persona. persona_index of -1 means find the first + // message possibly of the correct type + if ( (persona_index != -1 ) && (Messages[i].persona_index != persona_index) ){ + continue; + } + + // get who this message is from -- kind of a hack since we assume Terran Command in the + // absense of a ship. This will be fixed later + if ( shipp ) { + source = HUD_get_team_source( shipp->team ); + who_from = shipp->ship_name; + } else { + source = HUD_SOURCE_TERRAN_CMD; + who_from = TERRAN_COMMAND; + } + + // maybe change the who from here for special rearm cases (always seems like that is the case :-) ) + if ( !stricmp(who_from, TERRAN_COMMAND) && (type == MESSAGE_REARM_ON_WAY) ){ + who_from = SUPPORT_NAME; + } + + // determine what we should actually do with this dang message. In multiplayer, we must + // deal with the fact that this message might not get played on my machine if I am a server + + // not multiplayer or this message is for me, then queue it + if ( !(Game_mode & GM_MULTIPLAYER) || ((multi_target == -1) || (multi_target == MY_NET_PLAYER_NUM)) ){ + + // if this filter matches mine + if( (multi_team_filter < 0) || !(Netgame.type_flags & NG_TYPE_TEAM) || ((Net_player != NULL) && (Net_player->p_info.team == multi_team_filter)) ){ + message_queue_message( i, priority, timing, who_from, source, group, delay, type ); + + // post a builtin message + if(Game_mode & GM_DEMO_RECORD){ + demo_POST_builtin_message(type, shipp, priority, timing); + } + } + } + + // send a message packet to a player if destined for everyone or only a specific person + if ( MULTIPLAYER_MASTER ) { + // only send a message if it is of a particular type + if(multi_target == -1){ + if(multi_message_should_broadcast(type)){ + send_mission_message_packet( i, who_from, priority, timing, source, type, -1, multi_team_filter ); + } + } else { + send_mission_message_packet( i, who_from, priority, timing, source, type, multi_target, multi_team_filter ); + } + } + + return; // all done with displaying + } + + if ( persona_index >= 0 ) { + nprintf(("messaging", "Couldn't find builtin message %s for persona %d\n", Builtin_message_types[type], persona_index )); + nprintf(("messaging", "looking for message for any persona\n")); + persona_index = -1; + } else { + persona_index = -999; // used here and the next line only -- hard code bad, but I'm lazy + } + } while ( persona_index != -999 ); +} + +// message_is_playing() +// +// Return the Message_playing flag. Message_playing is local to MissionMessage.cpp, but +// this info is needed by code in HUDsquadmsg.cpp +// +int message_is_playing() +{ + return Num_messages_playing?1:0; +} + +// Functions below pertain only to personas!!!! + +// given a character string, try to find the persona index +int message_persona_name_lookup( char *name ) +{ + int i; + + for (i = 0; i < Num_personas; i++ ) { + if ( !stricmp(Personas[i].name, name) ) + return i; + } + + return -1; +} + + +// Blank out portions of the audio playback for the sound identified by Message_wave +// This works by using the same Distort_pattern[][] that was used to distort the associated text +void message_maybe_distort() +{ + int i; + int was_muted; + + if ( Num_messages_playing == 0 ) + return; + + for ( i = 0; i < Num_messages_playing; i++ ) { + if ( !snd_is_playing(Playing_messages[i].wave) ) + return; + } + + // distort the number of voices currently playing + for ( i = 0; i < Num_messages_playing; i++ ) { + Assert(Playing_messages[i].wave >= 0 ); + + was_muted = 0; + + // added check to see if EMP effect was active + // 8/24/98 - DB + if ( (hud_communications_state(Player_ship) != COMM_OK) || emp_active_local() ) { + was_muted = Message_wave_muted; + if ( timestamp_elapsed(Next_mute_time) ) { + Next_mute_time = fl2i(Distort_patterns[Distort_num][Distort_next++] * Message_wave_duration); + if ( Distort_next >= MAX_DISTORT_LEVELS ) + Distort_next = 0; + + Message_wave_muted ^= 1; + } + + if ( Message_wave_muted ) { + if ( !was_muted ) + snd_set_volume(Playing_messages[i].wave, 0.0f); + } else { + if ( was_muted ) + snd_set_volume(Playing_messages[i].wave, Master_sound_volume); + } + } + } +} + + +// if the player communications systems are heavily damaged, distort incoming messages. +// +// first case: Message_wave_duration == 0 (this occurs when there is no associated voice playback) +// Blank out random runs of characters in the message +// +// second case: Message_wave_duration > 0 (occurs when voice playback accompainies message) +// Blank out portions of the sound based on Distort_num, this this is that same +// data that will be used to blank out portions of the audio playback +// +void message_maybe_distort_text(char *text) +{ + int i, j, len, run, curr_offset, voice_duration, next_distort; + + if ( (hud_communications_state(Player_ship) == COMM_OK) && !emp_active_local() ) { + return; + } + + len = strlen(text); + if ( Message_wave_duration == 0 ) { + next_distort = 5+myrand()%5; + for ( i = 0; i < len; i++ ) { + if ( i == next_distort ) { + run = 3+myrand()%5; + if ( i+run > len ) + run = len-i; + for ( j = 0; j < run; j++) { + text[i++] = '-'; + if ( i >= len ) + break; + } + next_distort = i + (5+myrand()%5); + } + } + return; + } + + voice_duration = Message_wave_duration; + + // distort text + Distort_num = myrand()%MAX_DISTORT_PATTERNS; + Distort_next = 0; + curr_offset = 0; + while (voice_duration > 0) { + run = fl2i(Distort_patterns[Distort_num][Distort_next] * len); + if (Distort_next & 1) { + for ( i = curr_offset; i < min(len, curr_offset+run); i++ ) { + if ( text[i] != ' ' ) + text[i] = '-'; + } + curr_offset = i; + if ( i >= len ) + break; + } else { + curr_offset += run; + } + + voice_duration -= fl2i(Distort_patterns[Distort_num][Distort_next]*Message_wave_duration); + Distort_next++; + if ( Distort_next >= MAX_DISTORT_LEVELS ) + Distort_next = 0; + }; + + Distort_next = 0; +} + +// return 1 if a talking head animation is playing, otherwise return 0 +int message_anim_is_playing() +{ + int i; + + for (i = 0; i < Num_messages_playing; i++ ) { + if ( (Playing_messages[i].anim != NULL) && anim_playing(Playing_messages[i].anim) ) + return 1; + } + + return 0; +} + +// Load mission messages (this is called by the level paging code when running with low memory) +void message_pagein_mission_messages() +{ + int i; + + mprintf(("Paging in mission messages\n")); + + if (Num_messages <= Num_builtin_messages) { + return; + } + + char *sound_filename; + + for (i=Num_builtin_messages; i +#include +#include +#include +#include +#include + +#include "freespace.h" +#include "parselo.h" +#include "missionparse.h" +#include "missiongoals.h" +#include "missionlog.h" +#include "missionmessage.h" +#include "sexp.h" +#include "linklist.h" +#include "timer.h" +#include "ship.h" +#include "ai.h" +#include "aigoals.h" +#include "player.h" +#include "starfield.h" +#include "bmpman.h" +#include "lighting.h" +#include "eventmusic.h" +#include "missionbriefcommon.h" +#include "multi.h" +#include "multiutil.h" +#include "multimsgs.h" +#include "shipfx.h" +#include "debris.h" +#include "cfile.h" +#include "fireballs.h" +#include "gamesnd.h" +#include "gamesequence.h" +#include "medals.h" +#include "nebula.h" +#include "palman.h" +#include "hudets.h" +#include "missionhotkey.h" +#include "hudescort.h" +#include "asteroid.h" +#include "shiphit.h" +#include "staticrand.h" +#include "missioncmdbrief.h" +#include "redalert.h" +#include "multi_respawn.h" +#include "hudwingmanstatus.h" +#include "jumpnode.h" +#include "multi_endgame.h" +#include "localize.h" +#include "neb.h" +#include "demo.h" +#include "neblightning.h" +#include "fvi.h" + +LOCAL struct { + p_object *docker; + char dockee[NAME_LENGTH]; + char docker_point[NAME_LENGTH]; + char dockee_point[NAME_LENGTH]; +} Initially_docked[MAX_SHIPS]; + +int Total_initially_docked; + +mission The_mission; +char Mission_filename[80]; + +int Mission_palette; // index into Nebula_palette_filenames[] of palette file to use for mission +int Nebula_index; // index into Nebula_filenames[] of nebula to use in mission. +int Num_iff = MAX_IFF; +int Num_ai_behaviors = MAX_AI_BEHAVIORS; +int Num_cargo = 0; +int Num_status_names = MAX_STATUS_NAMES; +int Num_arrival_names = MAX_ARRIVAL_NAMES; +int Num_goal_type_names = MAX_GOAL_TYPE_NAMES; +int Num_team_names = MAX_TEAM_NAMES; +int Num_parse_goals; +int Player_starts = 1; +int Num_teams; +fix Entry_delay_time; + +ushort Current_file_checksum = 0; +ushort Last_file_checksum = 0; +int Current_file_length = 0; + +// alternate ship type names +char Mission_alt_types[MAX_ALT_TYPE_NAMES][NAME_LENGTH]; +int Mission_alt_type_count = 0; + +#define SHIP_WARP_TIME 5.0f // how many seconds it takes for ship to warp in + +// the ship arrival list will contain a list of ships that are yet to arrive. This +// list could also include ships that are part of wings! + +p_object ship_arrivals[MAX_SHIP_ARRIVALS], ship_arrival_list; // for linked list of ships to arrive later +int num_ship_arrivals; + +#define MAX_SHIP_ORIGINAL 100 +p_object ship_original[MAX_SHIP_ORIGINAL]; +int num_ship_original; + +// list for arriving support ship +p_object Support_ship_pobj; +p_object *Arriving_support_ship; +char Arriving_repair_targets[MAX_AI_GOALS][NAME_LENGTH]; +int Num_arriving_repair_targets; + +subsys_status Subsys_status[MAX_SUBSYS_STATUS]; +int Subsys_index; + +char Mission_parse_storm_name[NAME_LENGTH] = "none"; + +team_data Team_data[MAX_TEAMS]; + +// variables for player start in single player +char Player_start_shipname[NAME_LENGTH]; +int Player_start_shipnum; +p_object Player_start_pobject; + +// name of all ships to use while parsing a mission (since a ship might be referenced by +// something before that ship has even been loaded yet) +char Parse_names[MAX_SHIPS + MAX_WINGS][NAME_LENGTH]; +int Num_parse_names; + +//XSTR:OFF + +char *Nebula_filenames[NUM_NEBULAS] = { + "Nebula01", + "Nebula02", + "Nebula03" +}; + +char *Neb2_filenames[NUM_NEBULAS] = { + "Nebfull01", + "Nebfull02", + "Nebfull03" +}; + +// Note: Nebula_colors[] and Nebula_palette_filenames are linked via index numbers +char *Nebula_colors[NUM_NEBULA_COLORS] = { + "Red", + "Blue", + "Gold", + "Purple", + "Maroon", + "Green", + "Grey blue", + "Violet", + "Grey Green", +}; + +char *Iff_names[MAX_IFF] = { {"IFF 1"}, {"IFF 2"}, {"IFF 3"}, +}; + +char *Ai_behavior_names[MAX_AI_BEHAVIORS] = { + {"Chase"}, + {"Evade"}, + {"Get behind"}, + {"Stay Near"}, + {"Still"}, + {"Guard"}, + {"Avoid"}, + {"Waypoints"}, + {"Dock"}, + {"None"}, + {"Big Ship"}, + {"Path"}, + {"Be Rearmed"}, + {"Safety"}, + {"Evade Weapon"}, + {"Strafe"}, + {"Play Dead"}, + {"Bay Emerge"}, + {"Bay Depart"}, + {"Sentry Gun"}, + {"Warp Out"}, +}; + +char *Cargo_names[MAX_CARGO]; +char Cargo_names_buf[MAX_CARGO][NAME_LENGTH]; + +char *Ship_class_names[MAX_SHIP_TYPES]; // to be filled in from Ship_info array + +char *Icon_names[MAX_BRIEF_ICONS] = { + {"Fighter"}, {"Fighter Wing"}, {"Cargo"}, {"Cargo Wing"}, {"Largeship"}, + {"Largeship Wing"}, {"Capital"}, {"Planet"}, {"Asteroid Field"}, {"Waypoint"}, + {"Support Ship"}, {"Freighter(no cargo)"}, {"Freighter(has cargo)"}, + {"Freighter Wing(no cargo)"}, {"Freighter Wing(has cargo)"}, {"Installation"}, + {"Bomber"}, {"Bomber Wing"}, {"Cruiser"}, {"Cruiser Wing"}, {"Unknown"}, {"Unknown Wing"}, + {"Player Fighter"}, {"Player Fighter Wing"}, {"Player Bomber"}, {"Player Bomber Wing"}, + {"Knossos Device"}, {"Transport Wing"}, {"Corvette"}, {"Gas Miner"}, {"Awacs"}, {"Supercap"}, {"Sentry Gun"}, {"Jump Node"}, {"Transport"} +}; + +// Translate team mask values like TEAM_FRIENDLY to indices in Team_names array. +// -1 means an illegal value. +int Team_names_index_xlate[MAX_TEAM_NAMES_INDEX+1] = {-1, 0, 1, -1, 2, -1, -1, -1, 3}; + +char *Team_names[MAX_TEAM_NAMES] = { + {"Hostile"}, {"Friendly"}, {"Neutral"}, {"Unknown"}, +}; + +char *Status_desc_names[MAX_STATUS_NAMES] = { + {"Shields Critical"}, {"Engines Damaged"}, {"Fully Operational"}, +}; + +char *Status_type_names[MAX_STATUS_NAMES] = { + {"Damaged"}, {"Disabled"}, {"Corroded"}, +}; + +char *Status_target_names[MAX_STATUS_NAMES] = { + {"Weapons"}, {"Engines"}, {"Cable TV"}, +}; + +// definitions for arrival locations for ships/wings +char *Arrival_location_names[MAX_ARRIVAL_NAMES] = { + {"Hyperspace"}, {"Near Ship"}, {"In front of ship"}, {"Docking Bay"}, +}; + +char *Special_arrival_anchor_names[MAX_SPECIAL_ARRIVAL_ANCHORS] = +{ + "", + "", + "", + "", + "", + "", +}; + +char *Departure_location_names[MAX_ARRIVAL_NAMES] = { + {"Hyperspace"}, {"Docking Bay"}, +}; + +char *Goal_type_names[MAX_GOAL_TYPE_NAMES] = { + {"Primary"}, {"Secondary"}, {"Bonus"}, +}; + +char *Species_names[MAX_SPECIES_NAMES] = { + {"Terran"}, {"Vasudan"}, {"Shivan"}, +}; + +char *Reinforcement_type_names[] = { + "Attack/Protect", + "Repair/Rearm", +}; + +char *Old_game_types[OLD_MAX_GAME_TYPES] = { + "Single Player Only", + "Multiplayer Only", + "Single/Multi Player", + "Training mission" +}; + +char *Parse_object_flags[MAX_PARSE_OBJECT_FLAGS] = { + "cargo-known", + "ignore-count", + "protect-ship", + "reinforcement", + "no-shields", + "escort", + "player-start", + "no-arrival-music", + "no-arrival-warp", + "no-departure-warp", + "locked", + "invulnerable", + "hidden-from-sensors", + "scannable", + "kamikaze", + "no-dynamic", + "red-alert-carry", + "beam-protect-ship", + "guardian", + "special-warp" +}; + +char *Starting_wing_names[MAX_STARTING_WINGS+1] = { + "Alpha", + "Beta", + "Gamma", + "Zeta" +}; + +//XSTR:ON + +int Num_reinforcement_type_names = sizeof(Reinforcement_type_names) / sizeof(char *); + +vector Parse_viewer_pos; +matrix Parse_viewer_orient; + +// definitions for timestamps for eval'ing arrival/departure cues +int Mission_arrival_timestamp; +int Mission_departure_timestamp; +fix Mission_end_time; + +#define ARRIVAL_TIMESTAMP 2000 // every 2 seconds +#define DEPARTURE_TIMESTAMP 2200 // every 2.2 seconds -- just to be a little different + +// calculates a "unique" file signature as a ushort (checksum) and an int (file length) +// the amount of The_mission we're going to checksum +// WARNING : do NOT call this function on the server - it will overwrite goals, etc +#define MISSION_CHECKSUM_SIZE (NAME_LENGTH + NAME_LENGTH + 4 + DATE_TIME_LENGTH + DATE_TIME_LENGTH) + +// timers used to limit arrival messages and music +#define ARRIVAL_MUSIC_MIN_SEPARATION 60000 +#define ARRIVAL_MESSAGE_MIN_SEPARATION 30000 + +#define ARRIVAL_MESSAGE_DELAY_MIN 2000 +#define ARRIVAL_MESSAGE_DELAY_MAX 3000 + +static int Allow_arrival_music_timestamp; +static int Allow_arrival_message_timestamp; +static int Arrival_message_delay_timestamp; + +// multi TvT +static int Allow_arrival_music_timestamp_m[2]; +static int Allow_arrival_message_timestamp_m[2]; +static int Arrival_message_delay_timestamp_m[2]; + +// local prototypes +void parse_player_info2(mission *pm); +void post_process_mission(); +int allocate_subsys_status(); +void parse_common_object_data(p_object *objp); +void parse_asteroid_fields(mission *pm); +int mission_set_arrival_location(int anchor, int location, int distance, int objnum, vector *new_pos, matrix *new_orient); +int get_parse_name_index(char *name); +int get_anchor(char *name); +void mission_parse_do_initial_docks(); +void mission_parse_set_arrival_locations(); +void mission_set_wing_arrival_location( wing *wingp, int num_to_set ); +int parse_lookup_alt_name(char *name); + +void parse_mission_info(mission *pm) +{ + int i; + char game_string[NAME_LENGTH]; + + required_string("#Mission Info"); + + required_string("$Version:"); + stuff_float(&pm->version); + if (pm->version != MISSION_VERSION) + mprintf(("Older mission, should update it (%.2f<-->%.2f)", pm->version, MISSION_VERSION)); + + required_string("$Name:"); + stuff_string(pm->name, F_NAME, NULL); + + required_string("$Author:"); + stuff_string(pm->author, F_NAME, NULL); + + required_string("$Created:"); + stuff_string(pm->created, F_DATE, NULL); + + required_string("$Modified:"); + stuff_string(pm->modified, F_DATE, NULL); + + required_string("$Notes:"); + stuff_string(pm->notes, F_NOTES, NULL); + + if (optional_string("$Mission Desc:")) + stuff_string(pm->mission_desc, F_MULTITEXT, NULL, MISSION_DESC_LENGTH); + else + strcpy(pm->mission_desc, NOX("No description\n")); + + pm->game_type = MISSION_TYPE_SINGLE; // default to single player only + if ( optional_string("+Game Type:")) { + // HACK HACK HACK -- stuff_string was changed to *not* ignore carriage returns. Since the + // old style missions may have carriage returns, deal with it here. + ignore_white_space(); + stuff_string(game_string, F_NAME, NULL); + for ( i = 0; i < OLD_MAX_GAME_TYPES; i++ ) { + if ( !stricmp(game_string, Old_game_types[i]) ) { + + // this block of code is now old mission compatibility code. We specify game + // type in a different manner than before. + if ( i == OLD_GAME_TYPE_SINGLE_ONLY ) + pm->game_type = MISSION_TYPE_SINGLE; + else if ( i == OLD_GAME_TYPE_MULTI_ONLY ) + pm->game_type = MISSION_TYPE_MULTI; + else if ( i == OLD_GAME_TYPE_SINGLE_MULTI ) + pm->game_type = (MISSION_TYPE_SINGLE | MISSION_TYPE_MULTI ); + else if ( i == OLD_GAME_TYPE_TRAINING ) + pm->game_type = MISSION_TYPE_TRAINING; + else + Int3(); + + if ( pm->game_type & MISSION_TYPE_MULTI ) + pm->game_type |= MISSION_TYPE_MULTI_COOP; + + break; + } + } + } + + if ( optional_string("+Game Type Flags:") ) { + stuff_int(&pm->game_type); + } + + pm->flags = 0; + if (optional_string("+Flags:")){ + stuff_int(&pm->flags); + } + + // nebula mission stuff + Neb2_awacs = -1.0f; + if(optional_string("+NebAwacs:")){ + if(pm->flags & MISSION_FLAG_FULLNEB){ + stuff_float(&Neb2_awacs); + } else { + float temp; + stuff_float(&temp); + } + } + if(optional_string("+Storm:")){ + stuff_string(Mission_parse_storm_name, F_NAME, NULL); + nebl_set_storm(Mission_parse_storm_name); + } + + // get the number of players if in a multiplayer mission + pm->num_players = 1; + if ( pm->game_type & MISSION_TYPE_MULTI ) { + if ( optional_string("+Num Players:") ) { + stuff_int( &(pm->num_players) ); + } + } + + // get the number of respawns + pm->num_respawns = 0; + if ( pm->game_type & MISSION_TYPE_MULTI ) { + if ( optional_string("+Num Respawns:") ){ + stuff_int( (int*)&(pm->num_respawns) ); + } + } + + pm->red_alert = 0; + if ( optional_string("+Red Alert:")) { + stuff_int(&pm->red_alert); + } + red_alert_set_status(pm->red_alert); + + pm->scramble = 0; + if ( optional_string("+Scramble:")) { + stuff_int(&pm->scramble); + } + + pm->disallow_support = 0; + if ( optional_string("+Disallow Support:")) { + stuff_int(&pm->disallow_support); + } + + if (optional_string("+All Teams Attack")){ + Mission_all_attack = 1; + } else { + Mission_all_attack = 0; + } + + // Maybe delay the player's entry. + if (optional_string("+Player Entry Delay:")) { + float temp; + + stuff_float(&temp); + Assert(temp >= 0.0f); + Entry_delay_time = fl2f(temp); + } + + if (optional_string("+Viewer pos:")){ + stuff_vector(&Parse_viewer_pos); + } + + if (optional_string("+Viewer orient:")){ + stuff_matrix(&Parse_viewer_orient); + } + + // possible squadron reassignment + strcpy(The_mission.squad_name, ""); + strcpy(The_mission.squad_filename, ""); + if(optional_string("+SquadReassignName:")){ + stuff_string(The_mission.squad_name, F_NAME, NULL); + if(optional_string("+SquadReassignLogo:")){ + stuff_string(The_mission.squad_filename, F_NAME, NULL); + } + } + // always clear out squad reassignments if not single player + if(Game_mode & GM_MULTIPLAYER){ + strcpy(The_mission.squad_name, ""); + strcpy(The_mission.squad_filename, ""); + mprintf(("Ignoring squadron reassignment")); + } + // reassign the player + else { + if(!Fred_running && (Player != NULL) && (strlen(The_mission.squad_name) > 0) && (Game_mode & GM_CAMPAIGN_MODE)){ + mprintf(("Reassigning player to squadron %s\n", The_mission.squad_name)); + player_set_squad(Player, The_mission.squad_name); + player_set_squad_bitmap(Player, The_mission.squad_filename); + } + } + + // set up the Num_teams variable accoriding to the game_type variable' + Num_teams = 1; // assume 1 + + // multiplayer team v. team games have two teams. If we have three teams, we need to use + // a new mission mode! + if ( (pm->game_type & MISSION_TYPE_MULTI) && (pm->game_type & MISSION_TYPE_MULTI_TEAMS) ){ + Num_teams = 2; + } +} + +void parse_player_info(mission *pm) +{ + char alt[NAME_LENGTH + 2] = ""; + Assert(pm != NULL); + +// alternate type names begin here + mission_parse_reset_alt(); + if(optional_string("#Alternate Types:")){ + // read them all in + while(!optional_string("#end")){ + required_string("$Alt:"); + stuff_string(alt, F_NAME, NULL, NAME_LENGTH); + + // maybe store it + mission_parse_add_alt(alt); + } + } + + Player_starts = 0; + required_string("#Players"); + + while (required_string_either("#Objects", "$")){ + parse_player_info2(pm); + } +} + +void parse_player_info2(mission *pm) +{ + char str[NAME_LENGTH]; + int nt, i, total, list[MAX_SHIP_TYPES * 2], list2[MAX_WEAPON_TYPES * 2], num_starting_wings; + team_data *ptr; + char starting_wings[MAX_PLAYER_WINGS][NAME_LENGTH]; + + // read in a ship/weapon pool for each team. + for ( nt = 0; nt < Num_teams; nt++ ) { + int num_ship_choices; + + ptr = &Team_data[nt]; + // get the shipname for single player missions + // MWA -- make this required later!!!! + if ( optional_string("$Starting Shipname:") ) + stuff_string( Player_start_shipname, F_NAME, NULL ); + + required_string("$Ship Choices:"); + total = stuff_int_list(list, MAX_SHIP_TYPES * 2, SHIP_INFO_TYPE); + + Assert(!(total & 0x01)); // make sure we have an even count + + num_ship_choices = 0; + total /= 2; // there are only 1/2 the ships really on the list. + for (i=0; iflags & NETINFO_FLAG_AM_MASTER))) { + if ( !Campaign.ships_allowed[list[i*2]] ) + continue; + } + + ptr->ship_list[num_ship_choices] = list[i * 2]; + ptr->ship_count[num_ship_choices] = list[i * 2 + 1]; + num_ship_choices++; + } + ptr->number_choices = num_ship_choices; + + num_starting_wings = 0; + if (optional_string("+Starting Wings:")) + num_starting_wings = stuff_string_list(starting_wings, MAX_PLAYER_WINGS); + + ptr->default_ship = -1; + if (optional_string("+Default_ship:")) { + stuff_string(str, F_NAME, NULL); + ptr->default_ship = ship_info_lookup(str); + // see if the player's default ship is an allowable ship (campaign only). If not, then what + // do we do? choose the first allowable one? + if (Game_mode & GM_CAMPAIGN_MODE || ((Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER))) { + if ( !(Campaign.ships_allowed[ptr->default_ship]) ) { + for (i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Campaign.ships_allowed[ptr->default_ship] ) { + ptr->default_ship = i; + break; + } + } + Assert( i < MAX_SHIP_TYPES ); + } + } + } + + if (ptr->default_ship == -1) // invalid or not specified, make first in list + ptr->default_ship = ptr->ship_list[0]; + + for (i=0; iweaponry_pool[i] = 0; + + if (optional_string("+Weaponry Pool:")) { + total = stuff_int_list(list2, MAX_WEAPON_TYPES * 2, WEAPON_POOL_TYPE); + + Assert(!(total & 0x01)); // make sure we have an even count + total /= 2; + for (i=0; iflags & NETINFO_FLAG_AM_MASTER))) { + if ( !Campaign.weapons_allowed[list2[i*2]] ) + continue; + } + + if ((list2[i * 2] >= 0) && (list2[i * 2] < MAX_WEAPON_TYPES)) + ptr->weaponry_pool[list2[i * 2]] = list2[i * 2 + 1]; + } + } + } + + if ( nt != Num_teams ) + Error(LOCATION, "Not enough ship/weapon pools for mission. There are %d teams and only %d pools.", Num_teams, nt); +} + +void parse_plot_info(mission *pm) +{ + required_string("#Plot Info"); + + required_string("$Tour:"); + stuff_string(pm->tour_name, F_NAME, NULL); + + required_string("$Pre-Briefing Cutscene:"); + stuff_string(pm->pre_briefing_cutscene, F_FILESPEC, NULL); + + required_string("$Pre-Mission Cutscene:"); + stuff_string(pm->pre_mission_cutscene, F_FILESPEC, NULL); + + required_string("$Next Mission Success:"); + stuff_string(pm->next_mission_success, F_NAME, NULL); + + required_string("$Next Mission Partial:"); + stuff_string(pm->next_mission_partial, F_NAME, NULL); + + required_string("$Next Mission Failure:"); + stuff_string(pm->next_mission_failure, F_NAME, NULL); +} + +void parse_briefing_info(mission *pm) +{ + char junk[4096]; + + if ( !optional_string("#Briefing Info") ) + return; + + required_string("$Briefing Voice 1:"); + stuff_string(junk, F_FILESPEC, NULL); + + required_string("$Briefing Text 1:"); + stuff_string(junk, F_MULTITEXTOLD, NULL); + + required_string("$Briefing Voice 2:"); + stuff_string(junk, F_FILESPEC, NULL); + + required_string("$Briefing Text 2:"); + stuff_string(junk, F_MULTITEXTOLD, NULL); + + required_string("$Briefing Voice 3:"); + stuff_string(junk, F_FILESPEC, NULL); + + required_string("$Briefing Text 3:"); + stuff_string(junk, F_MULTITEXTOLD, NULL); + + required_string("$Debriefing Voice 1:"); + stuff_string(junk, F_FILESPEC, NULL); + + required_string("$Debriefing Text 1:"); + stuff_string(junk, F_MULTITEXTOLD, NULL); + + required_string("$Debriefing Voice 2:"); + stuff_string(junk, F_FILESPEC, NULL); + + required_string("$Debriefing Text 2:"); + stuff_string(junk, F_MULTITEXTOLD, NULL); + + required_string("$Debriefing Voice 3:"); + stuff_string(junk, F_FILESPEC, NULL); + + required_string("$Debriefing Text 3:"); + stuff_string(junk, F_MULTITEXTOLD, NULL); +} + +// parse the event music and briefing music for the mission +void parse_music(mission *pm) +{ + char name[NAME_LENGTH]; + + event_music_reset_choices(); + + if ( !optional_string("#Music") ) { + return; + } + + required_string("$Event Music:"); + stuff_string(name, F_NAME, NULL); + event_music_set_soundtrack(name); + + required_string("$Briefing Music:"); + stuff_string(name, F_NAME, NULL); + event_music_set_score(SCORE_BRIEFING, name); + + if ( optional_string("$Debriefing Success Music:") ) { + stuff_string(name, F_NAME, NULL); + event_music_set_score(SCORE_DEBRIEF_SUCCESS, name); + } + + if ( optional_string("$Debriefing Fail Music:") ) { + stuff_string(name, F_NAME, NULL); + event_music_set_score(SCORE_DEBRIEF_FAIL, name); + } +} + +void parse_cmd_brief(mission *pm) +{ + int stage; + + Assert(!Cur_cmd_brief->num_stages); + stage = 0; + + required_string("#Command Briefing"); + while (optional_string("$Stage Text:")) { + Assert(stage < CMD_BRIEF_STAGES_MAX); + Cur_cmd_brief->stage[stage].text = stuff_and_malloc_string(F_MULTITEXT, NULL, CMD_BRIEF_TEXT_MAX); + Assert(Cur_cmd_brief->stage[stage].text); + + required_string("$Ani Filename:"); + stuff_string(Cur_cmd_brief->stage[stage].ani_filename, F_FILESPEC, NULL); + if (optional_string("+Wave Filename:")) + stuff_string(Cur_cmd_brief->stage[stage].wave_filename, F_FILESPEC, NULL); + else + Cur_cmd_brief->stage[stage].wave_filename[0] = 0; + + stage++; + } + + Cur_cmd_brief->num_stages = stage; +} + +void parse_cmd_briefs(mission *pm) +{ + int i; + + cmd_brief_reset(); + // a hack follows because old missions don't have a command briefing + if (required_string_either("#Command Briefing", "#Briefing")) + return; + + for (i=0; inum_stages); + Assert(bp->num_stages <= MAX_BRIEF_STAGES); + + stage_num = 0; + while (required_string_either("$end_briefing", "$start_stage")) { + required_string("$start_stage"); + Assert(stage_num < MAX_BRIEF_STAGES); + bs = &bp->stages[stage_num++]; + required_string("$multi_text"); + if ( Fred_running ) { + stuff_string(bs->new_text, F_MULTITEXT, NULL, MAX_BRIEF_LEN); + } else { + bs->new_text = stuff_and_malloc_string(F_MULTITEXT, NULL, MAX_BRIEF_LEN); + } + required_string("$voice:"); + stuff_string(bs->voice, F_FILESPEC, NULL); + required_string("$camera_pos:"); + stuff_vector(&bs->camera_pos); + required_string("$camera_orient:"); + stuff_matrix(&bs->camera_orient); + required_string("$camera_time:"); + stuff_int(&bs->camera_time); + + if ( optional_string("$num_lines:") ) { + stuff_int(&bs->num_lines); + + if ( Fred_running ) { + Assert(bs->lines!=NULL); + } else { + if ( bs->num_lines > 0 ) { + bs->lines = (brief_line *)malloc(sizeof(brief_line)*bs->num_lines); + Assert(bs->lines!=NULL); + } + } + + for (i=0; inum_lines; i++) { + required_string("$line_start:"); + stuff_int(&bs->lines[i].start_icon); + required_string("$line_end:"); + stuff_int(&bs->lines[i].end_icon); + } + } + else { + bs->num_lines = 0; + } + + required_string("$num_icons:"); + stuff_int(&bs->num_icons); + + if ( Fred_running ) { + Assert(bs->lines!=NULL); + } else { + if ( bs->num_icons > 0 ) { + bs->icons = (brief_icon *)malloc(sizeof(brief_icon)*bs->num_icons); + Assert(bs->icons!=NULL); + } + } + + if ( optional_string("$flags:") ) + stuff_int(&bs->flags); + else + bs->flags = 0; + + if ( optional_string("$formula:") ) + bs->formula = get_sexp_main(); + else + bs->formula = Locked_sexp_true; + + Assert(bs->num_icons <= MAX_STAGE_ICONS ); + + while (required_string_either("$end_stage", "$start_icon")) { + required_string("$start_icon"); + Assert(icon_num < MAX_STAGE_ICONS); + bi = &bs->icons[icon_num++]; + + required_string("$type:"); + stuff_int(&bi->type); + + find_and_stuff("$team:", &team_index, F_NAME, Team_names, Num_team_names, "team name"); + Assert((team_index >= 0) && (team_index < MAX_TEAM_NAMES)); + bi->team = 1 << team_index; + + find_and_stuff("$class:", &bi->ship_class, F_NAME, Ship_class_names, Num_ship_types, "ship class"); + + required_string("$pos:"); + stuff_vector(&bi->pos); + + bi->label[0] = 0; + if (optional_string("$label:")) + stuff_string(bi->label, F_MESSAGE, NULL); + + if (optional_string("+id:")) { + stuff_int(&bi->id); + if (bi->id >= Cur_brief_id) + Cur_brief_id = bi->id + 1; + + } else { + bi->id = -1; + for (i=0; istages[i].num_icons; j++) + { + if (!stricmp(bp->stages[i].icons[j].label, bi->label)) + bi->id = bp->stages[i].icons[j].id; + } + + if (bi->id < 0) + bi->id = Cur_brief_id++; + } + + required_string("$hlight:"); + int val; + stuff_int(&val); + if ( val>0 ) { + bi->flags = BI_HIGHLIGHT; + } else { + bi->flags=0; + } + + required_string("$multi_text"); +// stuff_string(bi->text, F_MULTITEXT, NULL, MAX_ICON_TEXT_LEN); + stuff_string(not_used_text, F_MULTITEXT, NULL, MAX_ICON_TEXT_LEN); + required_string("$end_icon"); + } // end while + Assert(bs->num_icons == icon_num); + icon_num = 0; + required_string("$end_stage"); + } // end while + + Assert(bp->num_stages == stage_num); + required_string("$end_briefing"); + } + + if ( nt != Num_teams ) + Error(LOCATION, "Not enough briefings in mission file. There are %d teams and only %d briefings.", Num_teams, nt ); +} + +// ------------------------------------------------------------------------------------------------- +// parse_debriefing_old() +// +// Parse the data required for the mission debriefings +void parse_debriefing_old(mission *pm) +{ + int junk; + char waste[MAX_DEBRIEF_LEN]; + + if ( !optional_string("#Debriefing") ) + return; + + required_string("$num_debriefings:"); + stuff_int(&junk); + + while (required_string_either("#Players", "$start_debriefing")) { + required_string("$start_debriefing"); + required_string("$formula:"); + junk = get_sexp_main(); + required_string("$num_stages:"); + stuff_int(&junk); + while (required_string_either("$end_debriefing", "$start_stage")) { + required_string("$start_stage"); + required_string("$multi_text"); + stuff_string(waste, F_MULTITEXT, NULL, MAX_DEBRIEF_LEN); + required_string("$voice:"); + stuff_string(waste, F_FILESPEC, NULL); + required_string("$end_stage"); + } // end while + required_string("$end_debriefing"); + } // end while +} + +// ------------------------------------------------------------------------------------------------- +// parse_debriefing_new() +// +// Parse the data required for the mission debriefings +void parse_debriefing_new(mission *pm) +{ + int stage_num, nt; + debriefing *db; + debrief_stage *dbs; + + debrief_reset(); + + // next code should be old -- hopefully not called anymore + //if (!optional_string("#Debriefing_info")) { + // parse_debriefing_old(pm); + // return; + //} + + // 2/3/98 -- MWA. We can now have multiple briefings and debriefings on a team + for ( nt = 0; nt < Num_teams; nt++ ) { + + if ( !optional_string("#Debriefing_info") ) + break; + + stage_num = 0; + + db = &Debriefings[nt]; + + required_string("$Num stages:"); + stuff_int(&db->num_stages); + Assert(db->num_stages <= MAX_DEBRIEF_STAGES); + + while (required_string_either("#", "$Formula")) { + Assert(stage_num < MAX_DEBRIEF_STAGES); + dbs = &db->stages[stage_num++]; + required_string("$Formula:"); + dbs->formula = get_sexp_main(); + required_string("$multi text"); + if ( Fred_running ) { + stuff_string(dbs->new_text, F_MULTITEXT, NULL, MAX_DEBRIEF_LEN); + } else { + dbs->new_text = stuff_and_malloc_string(F_MULTITEXT, NULL, MAX_DEBRIEF_LEN); + } + required_string("$Voice:"); + stuff_string(dbs->voice, F_FILESPEC, NULL); + required_string("$Recommendation text:"); + if ( Fred_running ) { + stuff_string( dbs->new_recommendation_text, F_MULTITEXT, NULL, MAX_RECOMMENDATION_LEN); + } else { + dbs->new_recommendation_text = stuff_and_malloc_string( F_MULTITEXT, NULL, MAX_RECOMMENDATION_LEN); + } + } // end while + + Assert(db->num_stages == stage_num); + } + + if ( nt != Num_teams ) + Error(LOCATION, "Not enough debriefings for mission. There are %d teams and only %d debriefings;\n", Num_teams, nt ); +} + +void position_ship_for_knossos_warpin(p_object *objp, int shipnum, int objnum) +{ + // Assume no valid knossos device + Ships[shipnum].special_warp_objnum = -1; + + // find knossos device + int found = FALSE; + ship_obj *so; + int knossos_num = -1; + for (so=GET_FIRST(&Ship_obj_list); so!=END_OF_LIST(&Ship_obj_list); so=GET_NEXT(so)) { + knossos_num = Objects[so->objnum].instance; + if (Ship_info[Ships[knossos_num].ship_info_index].flags & SIF_KNOSSOS_DEVICE) { + // be close to the right device [allow multiple knossos's + if (vm_vec_dist_quick(&Objects[knossos_num].pos, &objp->pos) < 2.0f*(Objects[knossos_num].radius + Objects[objnum].radius) ) { + found = TRUE; + break; + } + } + } + + if (found) { + // set ship special_warp_objnum + Ships[shipnum].special_warp_objnum = knossos_num; + + // position self for warp on plane of device + vector new_point; + float dist = fvi_ray_plane(&new_point, &Objects[knossos_num].pos, &Objects[knossos_num].orient.fvec, &objp->pos, &objp->orient.fvec, 0.0f); + polymodel *pm = model_get(Ship_info[Ships[shipnum].ship_info_index].modelnum); + float desired_dist = -pm->mins.z; + vm_vec_scale_add2(&Objects[objnum].pos, &Objects[objnum].orient.fvec, (dist - desired_dist)); + // if ship is BIG or HUGE, make it go through the center of the knossos + if (Ship_info[Ships[shipnum].ship_info_index].flags & SIF_HUGE_SHIP) { + vector offset; + vm_vec_sub(&offset, &Objects[knossos_num].pos, &new_point); + vm_vec_add2(&Objects[objnum].pos, &offset); + } + } +} + +// Given a stuffed p_object struct, create an object and fill in the necessary fields. +// Return object number. +int parse_create_object(p_object *objp) +{ + int i, j, k, objnum, shipnum; + ai_info *aip; + ship_subsys *ptr; + ship_info *sip; + subsys_status *sssp; + ship_weapon *wp; + + // base level creation + objnum = ship_create(&objp->orient, &objp->pos, objp->ship_class); + Assert(objnum != -1); + shipnum = Objects[objnum].instance; + + // if arriving through knossos, adjust objpj->pos to plane of knossos and set flag + // special warp is single player only + if ((objp->flags & P_KNOSSOS_WARP_IN) && !(Game_mode & GM_MULTIPLAYER)) { + if (!Fred_running) { + position_ship_for_knossos_warpin(objp, shipnum, objnum); + } + } + + Ships[shipnum].group = objp->group; + Ships[shipnum].team = objp->team; + strcpy(Ships[shipnum].ship_name, objp->name); + Ships[shipnum].escort_priority = objp->escort_priority; + Ships[shipnum].special_exp_index = objp->special_exp_index; + Ships[shipnum].respawn_priority = objp->respawn_priority; + // if this is a multiplayer dogfight game, and its from a player wing, make it team traitor + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT) && (objp->wingnum >= 0)){ + for (i = 0; i < MAX_STARTING_WINGS; i++ ) { + if ( !stricmp(Starting_wing_names[i], Wings[objp->wingnum].name) ) { + Ships[shipnum].team = TEAM_TRAITOR; + } + } + } + + sip = &Ship_info[Ships[shipnum].ship_info_index]; + + if ( !Fred_running ) { + ship_assign_sound(&Ships[shipnum]); + } + + aip = &(Ai_info[Ships[shipnum].ai_index]); + aip->behavior = objp->behavior; + aip->mode = aip->behavior; + + // alternate type name + Ships[shipnum].alt_type_index = objp->alt_type_index; + + aip->ai_class = objp->ai_class; + Ships[shipnum].weapons.ai_class = objp->ai_class; // Fred uses this instead of above. + + // must reset the number of ai goals when the object is created + for (i = 0; i < MAX_AI_GOALS; i++ ){ + aip->goals[i].ai_mode = AI_GOAL_NONE; + } + + Ships[shipnum].cargo1 = objp->cargo1; + + Ships[shipnum].arrival_location = objp->arrival_location; + Ships[shipnum].arrival_distance = objp->arrival_distance; + Ships[shipnum].arrival_anchor = objp->arrival_anchor; + Ships[shipnum].arrival_cue = objp->arrival_cue; + Ships[shipnum].arrival_delay = objp->arrival_delay; + Ships[shipnum].departure_location = objp->departure_location; + Ships[shipnum].departure_anchor = objp->departure_anchor; + Ships[shipnum].departure_cue = objp->departure_cue; + Ships[shipnum].departure_delay = objp->departure_delay; + Ships[shipnum].determination = objp->determination; + Ships[shipnum].wingnum = objp->wingnum; + Ships[shipnum].hotkey = objp->hotkey; + Ships[shipnum].score = objp->score; + Ships[shipnum].persona_index = objp->persona_index; + + // set the orders that this ship will accept. It will have already been set to default from the + // ship create code, so only set them if the parse object flags say they are unique + if ( objp->flags & P_SF_USE_UNIQUE_ORDERS ) { + Ships[shipnum].orders_accepted = objp->orders_accepted; + + // MWA 5/15/98 -- Added the following debug code because some orders that ships + // will accept were apparently written out incorrectly with Fred. This Int3() should + // trap these instances. +#ifndef NDEBUG + if ( Fred_running ) { + int default_orders, remaining_orders; + + default_orders = ship_get_default_orders_accepted( &Ship_info[Ships[shipnum].ship_info_index] ); + remaining_orders = objp->orders_accepted & ~default_orders; + if ( remaining_orders ) { + Warning(LOCATION, "Ship %s has orders which it will accept that are\nnot part of default orders accepted.\n\nPlease reedit this ship and change the orders again\n", Ships[shipnum].ship_name); + } + } +#endif + } + + // check the parse object's flags for possible flags to set on this newly created ship + if ( objp->flags & P_OF_PROTECTED ) { + Objects[objnum].flags |= OF_PROTECTED; + } + + if ( objp->flags & P_OF_BEAM_PROTECTED ) { + Objects[objnum].flags |= OF_BEAM_PROTECTED; + } + + if (objp->flags & P_OF_CARGO_KNOWN) { + Ships[shipnum].flags |= SF_CARGO_REVEALED; + } + + if ( objp->flags & P_SF_IGNORE_COUNT ) + Ships[shipnum].flags |= SF_IGNORE_COUNT; + + if ( objp->flags & P_SF_REINFORCEMENT ) + Ships[shipnum].flags |= SF_REINFORCEMENT; + + if (objp->flags & P_OF_NO_SHIELDS || sip->shields == 0 ) + Objects[objnum].flags |= OF_NO_SHIELDS; + + if (objp->flags & P_SF_ESCORT) + Ships[shipnum].flags |= SF_ESCORT; + + if (objp->flags & P_KNOSSOS_WARP_IN) { + Objects[objnum].flags |= OF_SPECIAL_WARP; + } + + // don't set the flag if the mission is ongoing in a multiplayer situation. This will be set by the players in the + // game only before the game or during respawning. + // MWA -- changed the next line to remove the !(Game_mode & GM_MULTIPLAYER). We shouldn't be setting + // this flag in single player mode -- it gets set in post process mission. + //if ((objp->flags & P_OF_PLAYER_START) && (((Game_mode & GM_MULTIPLAYER) && !(Game_mode & GM_IN_MISSION)) || !(Game_mode & GM_MULTIPLAYER))) + if ( (objp->flags & P_OF_PLAYER_START) && (Fred_running || ((Game_mode & GM_MULTIPLAYER) && !(Game_mode & GM_IN_MISSION))) ) + Objects[objnum].flags |= OF_PLAYER_SHIP; + + if (objp->flags & P_SF_NO_ARRIVAL_MUSIC) + Ships[shipnum].flags |= SF_NO_ARRIVAL_MUSIC; + + if ( objp->flags & P_SF_NO_ARRIVAL_WARP ) + Ships[shipnum].flags |= SF_NO_ARRIVAL_WARP; + + if ( objp->flags & P_SF_NO_DEPARTURE_WARP ) + Ships[shipnum].flags |= SF_NO_DEPARTURE_WARP; + + if ( objp->flags & P_SF_INITIALLY_DOCKED ) + Ships[shipnum].flags |= SF_INITIALLY_DOCKED; + + if ( objp->flags & P_SF_LOCKED ) + Ships[shipnum].flags |= SF_LOCKED; + + if ( objp->flags & P_SF_WARP_BROKEN ) + Ships[shipnum].flags |= SF_WARP_BROKEN; + + if ( objp->flags & P_SF_WARP_NEVER ) + Ships[shipnum].flags |= SF_WARP_NEVER; + + if ( objp->flags & P_SF_HIDDEN_FROM_SENSORS ) + Ships[shipnum].flags |= SF_HIDDEN_FROM_SENSORS; + + // if ship is in a wing, and the wing's no_warp_effect flag is set, then set the equivalent + // flag for the ship + if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags & WF_NO_ARRIVAL_WARP) ) + Ships[shipnum].flags |= SF_NO_ARRIVAL_WARP; + + if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags & WF_NO_DEPARTURE_WARP) ) + Ships[shipnum].flags |= SF_NO_DEPARTURE_WARP; + + // mwa -- 1/30/98. Do both flags. Fred uses the ship flag, and FreeSpace will use the object + // flag. I'm to lazy at this point to deal with consolidating them. + if ( objp->flags & P_SF_INVULNERABLE ) { + Ships[shipnum].flags |= SF_INVULNERABLE; + Objects[objnum].flags |= OF_INVULNERABLE; + } + + if ( objp->flags & P_SF_GUARDIAN ) { + Objects[objnum].flags |= OF_GUARDIAN; + } + + if ( objp->flags & P_SF_SCANNABLE ) + Ships[shipnum].flags |= SF_SCANNABLE; + + if ( objp->flags & P_SF_RED_ALERT_STORE_STATUS ){ + Assert(!(Game_mode & GM_MULTIPLAYER)); + Ships[shipnum].flags |= SF_RED_ALERT_STORE_STATUS; + } + + // a couple of ai_info flags. Also, do a reasonable default for the kamikaze damage regardless of + // whether this flag is set or not + if ( objp->flags & P_AIF_KAMIKAZE ) { + Ai_info[Ships[shipnum].ai_index].ai_flags |= AIF_KAMIKAZE; + Ai_info[Ships[shipnum].ai_index].kamikaze_damage = objp->kamikaze_damage; + } + + if ( objp->flags & P_AIF_NO_DYNAMIC ) + Ai_info[Ships[shipnum].ai_index].ai_flags |= AIF_NO_DYNAMIC; + + // if the wing index and wing pos are set for this parse object, set them for the ship. This + // is useful in multiplayer when ships respawn + Ships[shipnum].wing_status_wing_index = objp->wing_status_wing_index; + Ships[shipnum].wing_status_wing_pos = objp->wing_status_wing_pos; + + // set up the ai_goals for this object -- all ships created here are AI controlled. + if ( objp->ai_goals != -1 ) { + int sexp; + + for ( sexp = CDR(objp->ai_goals); sexp != -1; sexp = CDR(sexp) ) + // make a call to the routine in MissionGoals.cpp to set up the ai goals for this object. + ai_add_ship_goal_sexp( sexp, AIG_TYPE_EVENT_SHIP, aip ); + + if ( objp->wingnum == -1 ) // free the sexpression nodes only for non-wing ships. wing code will handle it's own case + free_sexp2(objp->ai_goals); // free up sexp nodes for reused, since they aren't needed anymore. + } + + Assert(Ships[shipnum].modelnum != -1); + + // initialize subsystem statii here. The subsystems are given a percentage damaged. So a percent value + // of 20% means that the subsystem is 20% damaged (*not* 20% of max hits). This is opposite the way + // that the initial velocity/hull strength/shields work + i = objp->subsys_count; + while (i--) { + sssp = &Subsys_status[objp->subsys_index + i]; + if (!stricmp(sssp->name, NOX("Pilot"))) { + wp = &Ships[shipnum].weapons; + if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE) { + for (j=k=0; jprimary_banks[j] >= 0) || Fred_running ){ + wp->primary_bank_weapons[k] = sssp->primary_banks[j]; + + // next + k++; + } + } + + if (Fred_running){ + wp->num_primary_banks = sip->num_primary_banks; + } else { + wp->num_primary_banks = k; + } + } + + if (sssp->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE) { + for (j=k=0; jsecondary_banks[j] >= 0) || Fred_running ){ + wp->secondary_bank_weapons[k++] = sssp->secondary_banks[j]; + } + } + + if (Fred_running){ + wp->num_secondary_banks = sip->num_secondary_banks; + } else { + wp->num_secondary_banks = k; + } + } + + for (j=0; j < wp->num_secondary_banks; j++) + if (Fred_running){ + wp->secondary_bank_ammo[j] = sssp->secondary_ammo[j]; + } else { + int capacity = fl2i(sssp->secondary_ammo[j]/100.0f * sip->secondary_bank_ammo_capacity[j] + 0.5f ); + wp->secondary_bank_ammo[j] = fl2i(capacity / Weapon_info[wp->secondary_bank_weapons[j]].cargo_size + 0.5f); + } + continue; + } + + ptr = GET_FIRST(&Ships[shipnum].subsys_list); + while (ptr != END_OF_LIST(&Ships[shipnum].subsys_list)) { + if (!stricmp(ptr->system_info->subobj_name, sssp->name)) { + if (Fred_running) + ptr->current_hits = sssp->percent; + else { + float new_hits; + new_hits = ptr->system_info->max_hits * (100.0f - sssp->percent) / 100.f; + Ships[shipnum].subsys_info[ptr->system_info->type].current_hits -= (ptr->system_info->max_hits - new_hits); + if ( (100.0f - sssp->percent) < 0.5) { + ptr->current_hits = 0.0f; + ptr->submodel_info_1.blown_off = 1; + } else { + ptr->current_hits = new_hits; + } + } + + if (sssp->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE) + for (j=0; jweapons.primary_bank_weapons[j] = sssp->primary_banks[j]; + + if (sssp->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE) + for (j=0; jweapons.secondary_bank_weapons[j] = sssp->secondary_banks[j]; + + for (j=0; jweapons.secondary_bank_ammo[j] = sssp->secondary_ammo[j]; + } + + ptr->subsys_cargo_name = sssp->subsys_cargo_name; + + if (sssp->ai_class != SUBSYS_STATUS_NO_CHANGE) + ptr->weapons.ai_class = sssp->ai_class; + + ai_turret_select_default_weapon(ptr); + } + + ptr = GET_NEXT(ptr); + } + } + + // initial hull strength, shields, and velocity are all expressed as a percentage of the max value/ + // so a initial_hull value of 90% means 90% of max. This way is opposite of how subsystems are dealt + // with + if (Fred_running) { + Objects[objnum].phys_info.speed = (float) objp->initial_velocity; + // Ships[shipnum].hull_hit_points_taken = (float) objp->initial_hull; + Objects[objnum].hull_strength = (float) objp->initial_hull; + Objects[objnum].shields[0] = (float) objp->initial_shields; + + } else { + int max_allowed_sparks, num_sparks, i; + polymodel *pm; + + // Ships[shipnum].hull_hit_points_taken = (float)objp->initial_hull * sip->max_hull_hit_points / 100.0f; + Objects[objnum].hull_strength = objp->initial_hull * sip->initial_hull_strength / 100.0f; + for (i = 0; iinitial_shields * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS; + + // initial velocities now do not apply to ships which warp in after mission starts + if ( !(Game_mode & GM_IN_MISSION) ) { + Objects[objnum].phys_info.speed = (float)objp->initial_velocity * sip->max_speed / 100.0f; + Objects[objnum].phys_info.vel.z = Objects[objnum].phys_info.speed; + Objects[objnum].phys_info.prev_ramp_vel = Objects[objnum].phys_info.vel; + Objects[objnum].phys_info.desired_vel = Objects[objnum].phys_info.vel; + } + + // recalculate damage of subsystems + ship_recalc_subsys_strength( &Ships[shipnum] ); + + // create sparks on a ship whose hull is damaged. We will create two sparks for every 20% + // of hull damage done. 100 means no sparks. between 80 and 100 do two sparks. 60 and 80 is + // four, etc. + pm = model_get( sip->modelnum ); + max_allowed_sparks = get_max_sparks(&Objects[objnum]); + num_sparks = (int)((100.0f - objp->initial_hull) / 5.0f); + if (num_sparks > max_allowed_sparks) { + num_sparks = max_allowed_sparks; + } + + for (i = 0; i < num_sparks; i++ ) { + vector v1, v2; + + // DA 10/20/98 - sparks must be chosen on the hull and not any submodel + submodel_get_two_random_points(sip->modelnum, pm->detail[0], &v1, &v2); + ship_hit_sparks_no_rotate(&Objects[objnum], &v1); +// ship_hit_sparks_no_rotate(&Objects[objnum], &v2); + } + } + + // in mission, we add a log entry -- set ship positions for ships not in wings, and then do + // warpin effect + if ( (Game_mode & GM_IN_MISSION) && (!Fred_running) ) { + mission_log_add_entry( LOG_SHIP_ARRIVE, Ships[shipnum].ship_name, NULL ); + + // if this ship isn't in a wing, determine it's arrival location + if ( !Game_restoring ) { + if ( Ships[shipnum].wingnum == -1 ) { + int location; + // multiplayer clients set the arrival location of ships to be at location since their + // position has already been determined. Don't actually set the variable since we + // don't want the warp effect to show if coming from a dock bay. + location = objp->arrival_location; + if ( MULTIPLAYER_CLIENT ) + location = ARRIVE_AT_LOCATION; + mission_set_arrival_location(objp->arrival_anchor, location, objp->arrival_distance, objnum, NULL, NULL); + if ( objp->arrival_location != ARRIVE_FROM_DOCK_BAY ) + shipfx_warpin_start( &Objects[objnum] ); + } + } + + // possibly add this ship to a hotkey set + if ( (Ships[shipnum].wingnum == -1) && (Ships[shipnum].hotkey != -1 ) ) + mission_hotkey_mf_add( Ships[shipnum].hotkey, Ships[shipnum].objnum, HOTKEY_MISSION_FILE_ADDED ); + else if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].hotkey != -1 ) ) + mission_hotkey_mf_add( Wings[Ships[shipnum].wingnum].hotkey, Ships[shipnum].objnum, HOTKEY_MISSION_FILE_ADDED ); + + // possibly add this ship to the hud escort list + if ( Ships[shipnum].flags & SF_ESCORT ){ + hud_add_remove_ship_escort( objnum, 1 ); + } + } + + // for multiplayer games, make a call to the network code to assign the object signature + // of the newly created object. The network host of the netgame will always assign a signature + // to a newly created object. The network signature will get to the clients of the game in + // different manners depending on whether or not an individual ship or a wing was created. + if ( Game_mode & GM_MULTIPLAYER ) { + Objects[objnum].net_signature = objp->net_signature; + + if ( (Game_mode & GM_IN_MISSION) && MULTIPLAYER_MASTER && (objp->wingnum == -1) ){ + send_ship_create_packet( &Objects[objnum], (objp==Arriving_support_ship)?1:0 ); + } + } + + // if recording a demo, post the event + if(Game_mode & GM_DEMO_RECORD){ + demo_POST_obj_create(objp->name, Objects[objnum].signature); + } + + return objnum; +} + +// Mp points at the text of an object, which begins with the "$Name:" field. +// Snags all object information and calls parse_create_object to create a ship. +// Why create a ship? Why not an object? Stay tuned... +// +// flag is parameter that is used to tell what kind information we are retrieving from the mission. +// if we are just getting player starts, then don't create the objects +int parse_object(mission *pm, int flag, p_object *objp) +{ + // p_object temp_object; + // p_object *objp; + int i, j, count, shipnum, delay, destroy_before_mission_time; + char name[NAME_LENGTH], flag_strings[MAX_PARSE_OBJECT_FLAGS][NAME_LENGTH]; + + Assert(pm != NULL); + + // objp = &temp_object; + + required_string("$Name:"); + stuff_string(objp->name, F_NAME, NULL); + shipnum = ship_name_lookup(objp->name); + if (shipnum != -1) + error_display(0, NOX("Redundant ship name: %s\n"), objp->name); + + + find_and_stuff("$Class:", &objp->ship_class, F_NAME, Ship_class_names, Num_ship_types, "ship class"); + if (objp->ship_class < 0) { + Warning(LOCATION, "Ship \"%s\" has an invalid ship type (ships.tbl probably changed). Making it type 0", objp->name); + + // if fred is running, maybe notify the user that the mission contains MD content + if(Fred_running){ + Fred_found_unknown_ship_during_parsing = 1; + } + + objp->ship_class = 0; + } + + // if this is a multiplayer dogfight mission, skip support ships + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT) && (Ship_info[objp->ship_class].flags & SIF_SUPPORT)){ + return 0; + } + + // optional alternate name type + objp->alt_type_index = -1; + if(optional_string("$Alt:")){ + // alternate name + stuff_string(name, F_NAME, NULL, NAME_LENGTH); + + // try and find the alternate name + objp->alt_type_index = (char)mission_parse_lookup_alt(name); + Assert(objp->alt_type_index >= 0); + if(objp->alt_type_index < 0){ + mprintf(("Error looking up alternate ship type name!\n")); + } else { + mprintf(("Using alternate ship type name : %s\n", name)); + } + } + + int team_index; + find_and_stuff("$Team:", &team_index, F_NAME, Team_names, Num_team_names, "team name"); + Assert((team_index >= 0) && (team_index < MAX_TEAM_NAMES)); + objp->team = 1 << team_index; + + required_string("$Location:"); + stuff_vector(&objp->pos); + + required_string("$Orientation:"); + stuff_matrix(&objp->orient); + + find_and_stuff("$IFF:", &objp->iff, F_NAME, Iff_names, Num_iff, "IFF"); + find_and_stuff("$AI Behavior:", &objp->behavior, F_NAME, Ai_behavior_names, Num_ai_behaviors, "AI behavior"); + objp->ai_goals = -1; + + if ( optional_string("+AI Class:")) { + objp->ai_class = match_and_stuff(F_NAME, Ai_class_names, Num_ai_classes, "AI class"); + Assert(objp->ai_class > -1 ); + } else { + objp->ai_class = Ship_info[objp->ship_class].ai_class; + } + + if ( optional_string("$AI Goals:") ){ + objp->ai_goals = get_sexp_main(); + } + + if ( !required_string_either("$AI Goals:", "$Cargo 1:") ) { + required_string("$AI Goals:"); + objp->ai_goals = get_sexp_main(); + } + + objp->cargo1 = -1; + int temp; + find_and_stuff_or_add("$Cargo 1:", &temp, F_NAME, Cargo_names, &Num_cargo, MAX_CARGO, "cargo"); + objp->cargo1 = char(temp); + if ( optional_string("$Cargo 2:") ) { + char buf[NAME_LENGTH]; + stuff_string(buf, F_NAME, NULL); + } + + parse_common_object_data(objp); // get initial conditions and subsys status + count = 0; + while (required_string_either("$Arrival Location:", "$Status Description:")) { + Assert(count < MAX_OBJECT_STATUS); + + find_and_stuff("$Status Description:", &objp->status_type[count], F_NAME, Status_desc_names, Num_status_names, "Status Description"); + find_and_stuff("$Status:", &objp->status[count], F_NAME, Status_type_names, Num_status_names, "Status Type"); + find_and_stuff("$Target:", &objp->target[count], F_NAME, Status_target_names, Num_status_names, "Target"); + count++; + } + objp->status_count = count; + + objp->arrival_anchor = -1; + objp->arrival_distance = 0; + find_and_stuff("$Arrival Location:", &objp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location"); + if ( optional_string("+Arrival Distance:") ) { + stuff_int( &objp->arrival_distance ); + if ( objp->arrival_location != ARRIVE_AT_LOCATION ) { + required_string("$Arrival Anchor:"); + stuff_string(name, F_NAME, NULL); + objp->arrival_anchor = get_anchor(name); + } + } + + if (optional_string("+Arrival Delay:")) { + stuff_int(&delay); + if ( delay < 0 ) + Error(LOCATION, "Cannot have arrival delay < 0 (ship %s)", objp->name); + } else + delay = 0; + + if ( !Fred_running ){ + objp->arrival_delay = -delay; // use negative numbers to mean we haven't set up a timer yet + } else { + objp->arrival_delay = delay; + } + + required_string("$Arrival Cue:"); + objp->arrival_cue = get_sexp_main(); + if ( !Fred_running && (objp->arrival_cue >= 0) ) { + // eval the arrival cue. if the cue is true, set up the timestamp for the arrival delay + Assert ( objp->arrival_delay <= 0 ); + + // don't eval arrival_cues when just looking for player information. + if ( eval_sexp(objp->arrival_cue) ){ // evaluate to determine if sexp is always false. + objp->arrival_delay = timestamp( -objp->arrival_delay * 1000 ); + } + } + + find_and_stuff("$Departure Location:", &objp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location"); + objp->departure_anchor = -1; + if ( objp->departure_location == DEPART_AT_DOCK_BAY ) { + required_string("$Departure Anchor:"); + stuff_string(name, F_NAME, NULL); + objp->departure_anchor = get_anchor(name); + } + + if (optional_string("+Departure Delay:")) { + stuff_int(&delay); + if ( delay < 0 ){ + Error(LOCATION, "Cannot have departure delay < 0 (ship %s)", objp->name); + } + } else { + delay = 0; + } + + if ( !Fred_running ){ + objp->departure_delay = -delay; + } else { + objp->departure_delay = delay; + } + + required_string("$Departure Cue:"); + objp->departure_cue = get_sexp_main(); + + if (optional_string("$Misc Properties:")) + stuff_string(objp->misc, F_NAME, NULL); + + required_string("$Determination:"); + stuff_int(&objp->determination); + + objp->flags = 0; + if (optional_string("+Flags:")) { + count = stuff_string_list(flag_strings, MAX_PARSE_OBJECT_FLAGS); + for (i=0; iflags |= (1 << j); + break; + } + } + + if (j == MAX_PARSE_OBJECT_FLAGS) + Warning(LOCATION, "Unknown flag in mission file: %s\n", flag_strings[i]); + } + } + + // always store respawn priority, just for ease of implementation + objp->respawn_priority = 0; + if(optional_string("+Respawn Priority:" )){ + stuff_int(&objp->respawn_priority); + } + + objp->escort_priority = 0; + if ( optional_string("+Escort Priority:" ) ) { + Assert(objp->flags & P_SF_ESCORT); + stuff_int(&objp->escort_priority); + } + + if ( objp->flags & P_OF_PLAYER_START ) { + objp->flags |= P_OF_CARGO_KNOWN; // make cargo known for players + Player_starts++; + } + + objp->special_exp_index = -1; + if ( optional_string("+Special Exp index:" ) ) { + stuff_int(&objp->special_exp_index); + } + + // if the kamikaze flag is set, we should have the next flag + if ( optional_string("+Kamikaze Damage:") ) { + int damage; + + stuff_int(&damage); + objp->kamikaze_damage = i2fl(damage); + } + + objp->hotkey = -1; + if (optional_string("+Hotkey:")) { + stuff_int(&objp->hotkey); + Assert((objp->hotkey >= 0) && (objp->hotkey < 10)); + } + + objp->docked_with[0] = 0; + if (optional_string("+Docked With:")) { + stuff_string(objp->docked_with, F_NAME, NULL); + required_string("$Docker Point:"); + stuff_string(objp->docker_point, F_NAME, NULL); + required_string("$Dockee Point:"); + stuff_string(objp->dockee_point, F_NAME, NULL); + + objp->flags |= P_SF_INITIALLY_DOCKED; + + // put this information into the Initially_docked array. We will need to use this + // informatin later since not all ships will initially get created. + strcpy(Initially_docked[Total_initially_docked].dockee, objp->docked_with); + strcpy(Initially_docked[Total_initially_docked].docker_point, objp->docker_point); + strcpy(Initially_docked[Total_initially_docked].dockee_point, objp->dockee_point); + Initially_docked[Total_initially_docked].docker = objp; + Total_initially_docked++; + } + + // check the optional parameter for destroying the ship before the mission starts. If this parameter is + // here, then we need to destroy the ship N seconds before the mission starts (for debris purposes). + // store the time value here. We want to create this object for sure. Set the arrival cue and arrival + // delay to bogus values + destroy_before_mission_time = -1; + if ( optional_string("+Destroy At:") ) { + + stuff_int(&destroy_before_mission_time); + Assert ( destroy_before_mission_time >= 0 ); + objp->arrival_cue = Locked_sexp_true; + objp->arrival_delay = timestamp(0); + } + + // check for the optional "orders accepted" string which contains the orders from the default + // set that this ship will actually listen to + if ( optional_string("+Orders Accepted:") ) { + stuff_int( &objp->orders_accepted ); + if ( objp->orders_accepted != -1 ){ + objp->flags |= P_SF_USE_UNIQUE_ORDERS; + } + } + + if (optional_string("+Group:")){ + stuff_int(&objp->group); + } else { + objp->group = 0; + } + + if (optional_string("+Score:")){ + stuff_int(&objp->score); + } else { + objp->score = 0; + } + + // parse the persona index if present + if ( optional_string("+Persona Index:")){ + stuff_int(&objp->persona_index); + } else { + objp->persona_index = -1; + } + + objp->wingnum = -1; // set the wing number to -1 -- possibly to be set later + + // for multiplayer, assign a network signature to this parse object. Doing this here will + // allow servers to use the signature with clients when creating new ships, instead of having + // to pass ship names all the time + if ( Game_mode & GM_MULTIPLAYER ){ + objp->net_signature = multi_assign_network_signature( MULTI_SIG_SHIP ); + } + + // set the wing_status position to be -1 for all objects. This will get set to an appropriate + // value when the wing positions are finally determined. + objp->wing_status_wing_index = -1; + objp->wing_status_wing_pos = -1; + objp->respawn_count = 0; + + // if this if the starting player ship, then copy if to Starting_player_pobject (used for ingame join) + if ( !stricmp( objp->name, Player_start_shipname) ) { + Player_start_pobject = *objp; + Player_start_pobject.flags |= P_SF_PLAYER_START_VALID; + } + + +// Now create the object. +// Don't create the new ship blindly. First, check the sexp for the arrival cue +// to determine when this ship should arrive. If not right away, stick this ship +// onto the ship arrival list to be looked at later. Also check to see if it should use the +// wings arrival cue. The ship may get created later depending on whether or not the wing +// is created. +// always create ships when FRED is running + + // don't create the object if it is intially docked for either FreeSpcae or Fred. Fred will + // create the object later in post_process_mission + if ( (objp->flags & P_SF_INITIALLY_DOCKED) || (!Fred_running && (!eval_sexp(objp->arrival_cue) || !timestamp_elapsed(objp->arrival_delay) || (objp->flags & P_SF_REINFORCEMENT))) ) { + Assert ( destroy_before_mission_time == -1 ); // we can't add ships getting destroyed to the arrival list!!! + Assert ( num_ship_arrivals < MAX_SHIP_ARRIVALS ); + memcpy( &ship_arrivals[num_ship_arrivals], objp, sizeof(p_object) ); + list_append(&ship_arrival_list, &ship_arrivals[num_ship_arrivals]); + num_ship_arrivals++; + } + // ingame joiners bail here. + else if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_INGAME_JOIN)){ + return 1; + } + else { + int real_objnum; + + real_objnum = parse_create_object(objp); // this object may later get destroyed depending on wing status!!!! + + Subsys_index = objp->subsys_index; // free elements that are no longer needed. + + // if the ship is supposed to be destroyed before the mission, then blow up the ship, mark the pieces + // as last forever. Only call this stuff when you are blowing up the ship + if ( destroy_before_mission_time >= 0 ) { + object *objp; + + objp = &Objects[real_objnum]; + if ( !Fred_running ) { + int i; + shipfx_blow_up_model( objp, Ships[objp->instance].modelnum, 0, 0, &objp->pos ); + objp->flags |= OF_SHOULD_BE_DEAD; + + // once the ship is exploded, find the debris pieces belonging to this object, mark them + // as not to expire, and move them forward in time N seconds + for (i = 0; i < MAX_DEBRIS_PIECES; i++ ) { + debris *db; + + db = &Debris[i]; + if ( !db->flags & DEBRIS_USED ) // not used, move onto the next one. + continue; + if ( db->source_objnum != real_objnum ) // not from this ship, move to next one + continue; + + debris_clear_expired_flag(db); // mark as don't expire + db->lifeleft = -1.0f; // be sure that lifeleft == -1.0 so that it really doesn't expire! + + // now move the debris along it's path for N seconds + objp = &Objects[db->objnum]; + physics_sim( &objp->pos, &objp->orient, &objp->phys_info, (float)destroy_before_mission_time ); + } + } else { + // be sure to set the variable in the ships structure for the final death time!!! + Ships[objp->instance].final_death_time = destroy_before_mission_time; + Ships[objp->instance].flags |= SF_KILL_BEFORE_MISSION; + } + } + } + + return 1; +} + +void parse_common_object_data(p_object *objp) +{ + int i; + + // set some defaults.. + objp->initial_velocity = 0; + objp->initial_hull = 100; + objp->initial_shields = 100; + + // now change defaults if present + if (optional_string("+Initial Velocity:")) { + stuff_int(&objp->initial_velocity); + } + + if (optional_string("+Initial Hull:")) + stuff_int(&objp->initial_hull); + if (optional_string("+Initial Shields:")) + stuff_int(&objp->initial_shields); + + objp->subsys_index = Subsys_index; + objp->subsys_count = 0; + while (optional_string("+Subsystem:")) { + i = allocate_subsys_status(); + + objp->subsys_count++; + stuff_string(Subsys_status[i].name, F_NAME, NULL); + + if (optional_string("$Damage:")) + stuff_float(&Subsys_status[i].percent); + + Subsys_status[i].subsys_cargo_name = -1; + if (optional_string("+Cargo Name:")) { + char cargo_name[256]; + stuff_string(cargo_name, F_NAME, NULL); + int index = string_lookup(cargo_name, Cargo_names, Num_cargo, "cargo", 0); + if (index == -1 && (Num_cargo < MAX_CARGO)) { + index = Num_cargo; + strcpy(Cargo_names[Num_cargo++], cargo_name); + } + Subsys_status[i].subsys_cargo_name = index; + } + + if (optional_string("+AI Class:")) + Subsys_status[i].ai_class = match_and_stuff(F_NAME, Ai_class_names, Num_ai_classes, "AI class"); + + if (optional_string("+Primary Banks:")) + stuff_int_list(Subsys_status[i].primary_banks, MAX_PRIMARY_BANKS, WEAPON_LIST_TYPE); + + if (optional_string("+Secondary Banks:")) + stuff_int_list(Subsys_status[i].secondary_banks, MAX_SECONDARY_BANKS, WEAPON_LIST_TYPE); + + if (optional_string("+Sbank Ammo:")) + stuff_int_list(Subsys_status[i].secondary_ammo, MAX_SECONDARY_BANKS, RAW_INTEGER_TYPE); + + } +} + +void parse_objects(mission *pm, int flag) +{ + p_object temp; + + Assert(pm != NULL); + + required_string("#Objects"); + + // parse in objects + num_ship_original = 0; + while (required_string_either("#Wings", "$Name:")){ + // not all objects are always valid or legal + if(parse_object(pm, flag, &temp)){ + // add to the default list + if(num_ship_original < MAX_SHIP_ORIGINAL){ + memcpy(&ship_original[num_ship_original++], &temp, sizeof(p_object)); + } + } + } +} + +p_object *mission_parse_get_original_ship( ushort net_signature ) +{ + int idx; + + // look for original ships + for(idx=0; idxtotal_arrived_count; + + // force is used to force creation of the wing -- used for multiplayer + if ( !force ) { + // we only want to evaluate the arrival cue of the wing if: + // 1) single player + // 2) multiplayer and I am the host of the game + // can't create any ships if the arrival cue is false or the timestamp has not elapsed. + + if ( !eval_sexp(wingp->arrival_cue) ) /* || !timestamp_elapsed(wingp->arrival_delay) ) */ + return 0; + + // once the sexpressions becomes true, then check the arrival delay on the wing. The first time, the + // arrival delay will be <= 0 meaning that no timer object has been set yet. Set up the timestamp + // which should always give a number >= 0; + if ( wingp->arrival_delay <= 0 ) { + wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 ); + Assert ( wingp->arrival_delay >= 0 ); + } + + if ( !timestamp_elapsed( wingp->arrival_delay ) ) + return 0; + + // if wing is coming from docking bay, then be sure that ship we are arriving from actually exists + // (or will exist). + if ( wingp->arrival_location == ARRIVE_FROM_DOCK_BAY ) { + int shipnum; + char *name; + + Assert( wingp->arrival_anchor >= 0 ); + name = Parse_names[wingp->arrival_anchor]; + + // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later. + if ( mission_parse_get_arrival_ship( name ) ) + return 0; + + // see if ship is in mission. If not, then we can assume it was destroyed or departed since + // it is not on the arrival list (as shown by above if statement). + shipnum = ship_name_lookup( name ); + if ( shipnum == -1 ) { + int num_remaining; + // since this wing cannot arrive from this place, we need to mark the wing as destroyed and + // set the wing variables appropriatly. Good for directives. + + // set the gone flag + wingp->flags |= WF_WING_GONE; + + // if the current wave is zero, it never existed + wingp->flags |= WF_NEVER_EXISTED; + + // mark the number of waves and number of ships destroyed equal to the last wave and the number + // of ships yet to arrive + num_remaining = ( (wingp->num_waves - wingp->current_wave) * wingp->wave_count); + wingp->total_arrived_count += num_remaining; + wingp->current_wave = wingp->num_waves; + + // replaced following three lines of code with mission log call because of bug with + // the Ships_exited list. + //index = ship_find_exited_ship_by_name( name ); + //Assert( index != -1 ); + //if (Ships_exited[index].flags & SEF_DESTROYED ) { + if ( mission_log_get_time(LOG_SHIP_DESTROYED, name, NULL, NULL) ) { + wingp->total_destroyed += num_remaining; + } else { + wingp->total_departed += num_remaining; + } + + Sexp_nodes[wingp->arrival_cue].value = SEXP_KNOWN_FALSE; + return 0; + } + } + + if ( num_to_create == 0 ) + return 0; + + // check the wave_delay_timestamp field. If it is not valid, make it valid (based on wave delay min + // and max valuds). If it is valid, and not elapsed, then return. If it is valid and elasped, then + // continue on. + if ( !timestamp_valid(wingp->wave_delay_timestamp) ) { + + // if at least one of these is valid, then reset the timestamp. If they are both zero, we will create the + // wave + if ( (wingp->wave_delay_min > 0) || (wingp->wave_delay_max > 0) ) { + Assert ( wingp->wave_delay_min <= wingp->wave_delay_max ); + time_to_arrive = wingp->wave_delay_min + (int)(frand() * (wingp->wave_delay_max - wingp->wave_delay_min)); + + // MWA -- 5/18/98 + // HACK HACK -- in the presense of Mike Comet and Mitri, I have introduced one of the most + // serious breaches of coding standards. I'm to lazy to fix this the correct way. Insert + // a delay before the next wave of the wing can arrive to that clients in the game have ample + // time to kill off any ships in the wing before the next wave arrives. + if ( Game_mode & GM_MULTIPLAYER ){ + time_to_arrive += 7; + } + wingp->wave_delay_timestamp = timestamp(time_to_arrive * 1000); + return 0; + } + + // if we get here, both min and max values are 0; See comments above for a most serious hack + time_to_arrive = 0; + if ( Game_mode & GM_MULTIPLAYER ) + time_to_arrive += 7; + time_to_arrive *= 1000; + wingp->wave_delay_timestamp = timestamp(time_to_arrive); + } + + // now check to see if the wave_delay_timestamp is elapsed or not + if ( !timestamp_elapsed(wingp->wave_delay_timestamp) ) + return 0; + } + + // finally we can create the wing. + + num_create_save = num_to_create; + + wingnum = wingp - Wings; // get the wing number + + // if there are no ships to create, then all ships must be player start ships -- do nothing in this case. + if ( num_to_create == 0 ){ + return 0; + } + + wingp->current_wave++; // we are creating new ships + // we need to create num_to_create ships. Since the arrival cues for ships in a wing + // are ignored, then *all* ships must be in the ship_arrival_list. + + objnum = -1; + objp = GET_FIRST(&ship_arrival_list); + while( objp != END_OF_LIST(&ship_arrival_list) ) { + p_object *temp = GET_NEXT(objp); + + // compare the wingnums. When they are equal, we can create the ship. In the case of + // wings that have multiple waves, this code implies that we essentially creating clones + // of the ships that were created in Fred for the wing when more ships for a new wave + // arrive. The threshold value of a wing can also make one of the ships in a wing be "cloned" + // more often than other ships in the wing. I don't think this matters much. + if ( objp->wingnum == wingnum ) { + ai_info *aip; + + // when ingame joining, we need to create a specific ship out of the list of ships for a + // wing. specific_instance is a 0 based integer which specified which ship in the wing + // to create. So, only create the ship we actually need to. + if ( (Game_mode & GM_MULTIPLAYER) && (specific_instance > 0) ) { + specific_instance--; + objp = temp; + continue; + } + + Assert ( !(objp->flags & P_SF_CANNOT_ARRIVE) ); // get allender + + int index; + + // if we have the maximum number of ships in the wing, we must bail as well + if ( wingp->current_count >= MAX_SHIPS_PER_WING ) { + Int3(); // this is bogus -- we should always allow all ships to be created + num_to_create = 0; + break; + } + + // bash the ship name to be the name of the wing + sone number if there is > 1 wave in + // this wing + // also, if multplayer, set the parse object's net signature to be wing's net signature + // base + total_arrived_count (before adding 1) + if ( Game_mode & GM_MULTIPLAYER ){ + objp->net_signature = (ushort)(wingp->net_signature + wingp->total_arrived_count); + } + + wingp->total_arrived_count++; + if ( wingp->num_waves > 1 ){ + sprintf(objp->name, NOX("%s %d"), wingp->name, wingp->total_arrived_count); + } + + objnum = parse_create_object(objp); + aip = &Ai_info[Ships[Objects[objnum].instance].ai_index]; + + // copy any goals from the wing to the newly created ship + for (index = 0; index < MAX_AI_GOALS; index++) { + if ( wingp->ai_goals[index].ai_mode != AI_GOAL_NONE ){ + ai_copy_mission_wing_goal( &wingp->ai_goals[index], aip ); + } + } + + Ai_info[Ships[Objects[objnum].instance].ai_index].wing = wingnum; + + if ( wingp->flags & WF_NO_DYNAMIC ){ + aip->ai_flags |= AIF_NO_DYNAMIC; + } + + // update housekeeping variables + wingp->ship_index[wingp->current_count] = Objects[objnum].instance; + + // set up wingman status index + hud_wingman_status_set_index(wingp->ship_index[wingp->current_count]); + + objp->wing_status_wing_index = Ships[Objects[objnum].instance].wing_status_wing_index; + objp->wing_status_wing_pos = Ships[Objects[objnum].instance].wing_status_wing_pos; + + wingp->current_count++; + + // keep any player ship on the parse object list -- used for respawns + // 5/8/98 -- MWA -- don't remove ships from the list when you are ingame joining + if ( !(objp->flags & P_OF_PLAYER_START) ) { + if ( (Game_mode & GM_NORMAL) || !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) { + if ( wingp->num_waves == wingp->current_wave ) { // only remove ship if one wave in wing + list_remove( &ship_arrival_list, objp); // remove objp from the list + if ( objp->ai_goals != -1 ){ + free_sexp2(objp->ai_goals); // free up sexp nodes for reuse + } + } + } + } + + // flag ship with SF_FROM_PLAYER_WING if a member of player starting wings + if ( (Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM) ) { + // but for team vs. team games, then just check the alpha and zeta wings + if ( !(stricmp(Starting_wing_names[STARTING_WING_ALPHA], wingp->name)) || !(stricmp(Starting_wing_names[STARTING_WING_ZETA], wingp->name)) ) { + Ships[Objects[objnum].instance].flags |= SF_FROM_PLAYER_WING; + } + } else { + for (int i = 0; i < MAX_STARTING_WINGS; i++ ) { + if ( !stricmp(Starting_wing_names[i], wingp->name) ) { + Ships[Objects[objnum].instance].flags |= SF_FROM_PLAYER_WING; + } + } + } + + // keep track of how many ships to create. Stop when we have done all that we are supposed + // to do. + num_to_create--; + if ( !num_to_create ){ + break; + } + } + objp = temp; + } + + Assert ( num_to_create == 0 ); // we should always have enough ships in the list!!! + + // possibly play some event driven music here. Send a network packet indicating the wing was + // created. Only do this stuff if actually in the mission. + if ( (objnum != -1) && (Game_mode & GM_IN_MISSION) ) { // if true, we have created at least one new ship. + int i, ship_num; + + // see if this wing is a player starting wing, and if so, call the maybe_add_form_goal + // function to possibly make the wing form on the player + for (i = 0; i < MAX_STARTING_WINGS; i++ ) { + if ( Starting_wings[i] == wingnum ){ + break; + } + } + if ( i < MAX_STARTING_WINGS ){ + ai_maybe_add_form_goal( wingp ); + } + + mission_log_add_entry( LOG_WING_ARRIVE, wingp->name, NULL, wingp->current_wave ); + ship_num = wingp->ship_index[0]; + + if ( !(Ships[ship_num].flags & SF_NO_ARRIVAL_MUSIC) && !(wingp->flags & WF_NO_ARRIVAL_MUSIC) ) { + if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) { + Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION); + event_music_arrival(Ships[ship_num].team); + } + } + + // possibly change the location where these ships arrive based on the wings arrival location + mission_set_wing_arrival_location( wingp, num_create_save ); + + // if in multiplayer (and I am the host) and in the mission, send a wing create command to all + // other players + if ( MULTIPLAYER_MASTER ){ + send_wing_create_packet( wingp, num_create_save, pre_create_count ); + } + +#ifndef NDEBUG + // test code to check to be sure that all ships in the wing are ignoring the same types + // of orders from the player + if ( Fred_running ) { + Assert( wingp->ship_index[0] != -1 ); + int orders = Ships[wingp->ship_index[0]].orders_accepted; + for (i = 1; i < wingp->current_count; i++ ) { + if ( orders != Ships[wingp->ship_index[i]].orders_accepted ) { + Warning(LOCATION, "ships in wing %s are ignoring different player orders. Please find Mark A\nto talk to him about this.", wingp->name ); + break; + } + } + } +#endif + + } + + wingp->wave_delay_timestamp = timestamp(-1); // we will need to set this up properly for the next wave + return num_create_save; +} + +void parse_wing(mission *pm) +{ + int wingnum, i, wing_goals, delay; + char name[NAME_LENGTH], ship_names[MAX_SHIPS_PER_WING][NAME_LENGTH]; + char wing_flag_strings[MAX_WING_FLAGS][NAME_LENGTH]; + wing *wingp; + + Assert(pm != NULL); + wingp = &Wings[num_wings]; + + required_string("$Name:"); + stuff_string(wingp->name, F_NAME, NULL); + wingnum = find_wing_name(wingp->name); + if (wingnum != -1) + error_display(0, NOX("Redundant wing name: %s\n"), wingp->name); + wingnum = num_wings; + + wingp->total_arrived_count = 0; + wingp->total_destroyed = 0; + wingp->flags = 0; + + required_string("$Waves:"); + stuff_int(&wingp->num_waves); + Assert ( wingp->num_waves >= 1 ); // there must be at least 1 wave + + wingp->current_wave = 0; + + required_string("$Wave Threshold:"); + stuff_int(&wingp->threshold); + + required_string("$Special Ship:"); + stuff_int(&wingp->special_ship); + + wingp->arrival_anchor = -1; + find_and_stuff("$Arrival Location:", &wingp->arrival_location, F_NAME, Arrival_location_names, Num_arrival_names, "Arrival Location"); + if ( optional_string("+Arrival Distance:") ) { + stuff_int( &wingp->arrival_distance ); + if ( wingp->arrival_location != ARRIVE_AT_LOCATION ) { + required_string("$Arrival Anchor:"); + stuff_string(name, F_NAME, NULL); + wingp->arrival_anchor = get_anchor(name); + } + } + + if (optional_string("+Arrival delay:")) { + stuff_int(&delay); + if ( delay < 0 ) + Error(LOCATION, "Cannot have arrival delay < 0 on wing %s", wingp->name ); + } else + delay = 0; + + if ( !Fred_running ){ + wingp->arrival_delay = -delay; + } else { + wingp->arrival_delay = delay; + } + + required_string("$Arrival Cue:"); + wingp->arrival_cue = get_sexp_main(); + if ( !Fred_running && (wingp->arrival_cue >= 0) ) { + if ( eval_sexp(wingp->arrival_cue) ) // evaluate to determine if sexp is always false. + wingp->arrival_delay = timestamp( -wingp->arrival_delay * 1000 ); + } + + + find_and_stuff("$Departure Location:", &wingp->departure_location, F_NAME, Departure_location_names, Num_arrival_names, "Departure Location"); + wingp->departure_anchor = -1; + if ( wingp->departure_location == DEPART_AT_DOCK_BAY ) { + required_string("$Departure Anchor:"); + stuff_string( name, F_NAME, NULL ); + wingp->departure_anchor = get_anchor(name); + } + + if (optional_string("+Departure delay:")) { + stuff_int(&delay); + if ( delay < 0 ) + Error(LOCATION, "Cannot have departure delay < 0 on wing %s", wingp->name ); + } else + delay = 0; + + + if ( !Fred_running ) + wingp->departure_delay = -delay; // use negative numbers to mean that delay timer not yet set + else + wingp->departure_delay = delay; + + required_string("$Departure Cue:"); + wingp->departure_cue = get_sexp_main(); + + // stores a list of all names of ships in the wing + required_string("$Ships:"); + wingp->wave_count = stuff_string_list( ship_names, MAX_SHIPS_PER_WING ); + wingp->current_count = 0; + + // get the wings goals, if any + wing_goals = -1; + if ( optional_string("$AI Goals:") ) + wing_goals = get_sexp_main(); + + wingp->hotkey = -1; + if (optional_string("+Hotkey:")) { + stuff_int(&wingp->hotkey); + Assert((wingp->hotkey >= 0) && (wingp->hotkey < 10)); + } + + if (optional_string("+Flags:")) { + int count; + + count = stuff_string_list( wing_flag_strings, MAX_WING_FLAGS ); + for (i = 0; i < count; i++ ) { + if ( !stricmp( wing_flag_strings[i], NOX("ignore-count")) ) + wingp->flags |= WF_IGNORE_COUNT; + else if ( !stricmp( wing_flag_strings[i], NOX("reinforcement")) ) + wingp->flags |= WF_REINFORCEMENT; + else if ( !stricmp( wing_flag_strings[i], NOX("no-arrival-music")) ) + wingp->flags |= WF_NO_ARRIVAL_MUSIC; + else if ( !stricmp( wing_flag_strings[i], NOX("no-arrival-message")) ) + wingp->flags |= WF_NO_ARRIVAL_MESSAGE; + else if ( !stricmp( wing_flag_strings[i], NOX("no-arrival-warp")) ) + wingp->flags |= WF_NO_ARRIVAL_WARP; + else if ( !stricmp( wing_flag_strings[i], NOX("no-departure-warp")) ) + wingp->flags |= WF_NO_DEPARTURE_WARP; + else if ( !stricmp( wing_flag_strings[i], NOX("no-dynamic")) ) + wingp->flags |= WF_NO_DYNAMIC; + else + Warning(LOCATION, "unknown wing flag\n%s\n\nSkipping.", wing_flag_strings[i]); + } + } + + // get the wave arrival delay bounds (if present). Used as lower and upper bounds (in seconds) + // which determine when new waves of a wing should arrive. + wingp->wave_delay_min = 0; + wingp->wave_delay_max = 0; + if ( optional_string("+Wave Delay Min:") ) + stuff_int( &(wingp->wave_delay_min) ); + if ( optional_string("+Wave Delay Max:") ) + stuff_int( &(wingp->wave_delay_max) ); + + // be sure to set the wave arrival timestamp of this wing to pop right away so that the + // wing could be created if it needs to be + wingp->wave_delay_timestamp = timestamp(0); + + // initialize wing goals + for (i=0; iai_goals[i].ai_mode = AI_GOAL_NONE; + wingp->ai_goals[i].priority = -1; + } + + // 7/13/98 -- MWA + // error checking against the player ship wings to be sure that wave count doesn't exceed one for + // these wings. + if ( Game_mode & GM_MULTIPLAYER ) { + for (i = 0; i < MAX_STARTING_WINGS+1; i++ ) { + if ( !stricmp(Starting_wing_names[i], wingp->name) ) { + if ( wingp->num_waves > 1 ) { + // only end the game if we're the server - clients will eventually find out :) + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_WAVE_COUNT); + } + // Error(LOCATION, "Player wings Alpha, Beta, Gamma, or Zeta cannot have more than 1 wave."); + } + } + } + } + + // Get the next starting signature for this in this wing. We want to reserve wave_count * num_waves + // of signature. These can be used to construct wings for ingame joiners. + if ( Game_mode & GM_MULTIPLAYER ) { + int next_signature; + + wingp->net_signature = multi_assign_network_signature( MULTI_SIG_SHIP ); + next_signature = wingp->net_signature + (wingp->wave_count * wingp->num_waves); + if ( next_signature > SHIP_SIG_MAX ) + Error(LOCATION, "Too many total ships in mission (%d) for network signature assignment", SHIP_SIG_MAX); + multi_set_network_signature( (ushort)next_signature, MULTI_SIG_SHIP ); + } + + for (i=0; iship_index[i] = -1; + + // set up the ai_goals for this wing -- all ships created from this wing will inherit these goals + // goals for the wing are stored slightly differently than for ships. We simply store the index + // into the sexpression array of each goal (max 10). When a ship in this wing is created, each + // goal in the wings goal array is given to the ship. + if ( wing_goals != -1 ) { + int sexp, index; + + // this will assign the goals to the wings as well as to any ships in the wing that have been + // already created. + index = 0; + for ( sexp = CDR(wing_goals); sexp != -1; sexp = CDR(sexp) ) + ai_add_wing_goal_sexp(sexp, AIG_TYPE_EVENT_WING, wingnum); // used by Fred + + //if (Fred_running) + free_sexp2(wing_goals); // free up sexp nodes for reused, since they aren't needed anymore. + } + + // set the wing number for all ships in the wing + for (i = 0; i < wingp->wave_count; i++ ) { + //char *ship_name = wingp->ship_names[i]; + char *ship_name; + int num, assigned = 0; + p_object *objp; + + ship_name = ship_names[i]; + if (Fred_running) { + num = wingp->ship_index[i] = ship_name_lookup(ship_name, 1); + Assert ( num != -1 ); + + // hack code -- REMOVE + if ( Objects[Ships[num].objnum].flags & OF_PLAYER_SHIP ) + Ships[num].wingnum = wingnum; + + } else { + // determine if this ship is a player ship, and deal with it appropriately. + if ( !strnicmp(ship_name, NOX("Player "), 7) ) { + Error(LOCATION, "Old mission file -- please convert by loading/saving in Fred -- see Allender/Hoffoss for help."); + } + + // assign the wing number to the ship -- if the ship has arrived, doulble check that + // there is only one wave of this wing since ships with their own arrival cue cannot be + // in a wing with > 1 wave. Otherwise, find the ship on the ship arrival list and set + // their wing number + if ( (num = ship_name_lookup(ship_name)) != -1 ) { + Int3(); // this is impossible under the new system + + } else { + objp = GET_FIRST(&ship_arrival_list); + while( objp != END_OF_LIST(&ship_arrival_list) ) { + if ( !strcmp(ship_name, objp->name) ) { + Assert ( objp->wingnum == -1 ); // get Allender -- ship appears to be in multiple wings + objp->wingnum = wingnum; + assigned++; + } + objp = GET_NEXT(objp); + } + } + + if ( !assigned || (assigned > 1) ) + Error(LOCATION, "Cannot load mission -- wing %s -- ship %s not present in #Objects section (or specified multiple times in wing.\n", wingp->name, ship_name); + } + } + + // Fred doesn't create the wing. otherwise, create the wing if is isn't a reinforcement. + if ( !Fred_running && !(wingp->flags & WF_REINFORCEMENT) ) + parse_wing_create_ships( wingp, wingp->wave_count ); +} + +void parse_wings(mission *pm) +{ + required_string("#Wings"); + while (required_string_either("#Events", "$Name:")) { + Assert(num_wings < MAX_WINGS); + parse_wing(pm); + num_wings++; + } +} + +// mission events are sexpressions which cause things to happen based on the outcome +// of other events in a mission. Essentially scripting the different things that can happen +// in a mission + +void parse_event(mission *pm) +{ + char buf[256]; + mission_event *event; + + event = &Mission_events[Num_mission_events]; + event->chain_delay = -1; + + required_string( "$Formula:" ); + event->formula = get_sexp_main(); + + if (optional_string("+Name:")){ + stuff_string(event->name, F_NAME, NULL); + } else { + event->name[0] = 0; + } + + if ( optional_string("+Repeat Count:")){ + stuff_int( &(event->repeat_count) ); + } else { + event->repeat_count = 1; + } + + event->interval = -1; + if ( optional_string("+Interval:")){ + stuff_int( &(event->interval) ); + } + + event->score = 0; + if ( optional_string("+Score:") ){ + stuff_int(&event->score); + } + + if ( optional_string("+Chained:") ){ + stuff_int(&event->chain_delay); + } + + if ( optional_string("+Objective:") ) { + stuff_string(buf, F_NAME, NULL); + event->objective_text = strdup(buf); + } else { + event->objective_text = NULL; + } + + if ( optional_string("+Objective key:") ) { + stuff_string(buf, F_NAME, NULL); + event->objective_key_text = strdup(buf); + } else { + event->objective_key_text = NULL; + } + + event->team = -1; + if( optional_string("+Team:") ) { + stuff_int(&event->team); + } + + event->timestamp = timestamp(-1); + + // sanity check on the repeat count variable + if ( event->repeat_count <= 0 ){ + Error (LOCATION, "Repeat count for mission event %s is <=0.\nMust be >= 1!", event->name ); + } +} + +void parse_events(mission *pm) +{ + required_string("#Events"); + + while (required_string_either( "#Goals", "$Formula:")) { + Assert( Num_mission_events < MAX_MISSION_EVENTS ); + parse_event(pm); + Num_mission_events++; + } +} + +void parse_goal(mission *pm) +{ + int dummy; + + mission_goal *goalp; + + goalp = &Mission_goals[Num_goals++]; + + Assert(Num_goals < MAX_GOALS); + Assert(pm != NULL); + + find_and_stuff("$Type:", &goalp->type, F_NAME, Goal_type_names, Num_goal_type_names, "goal type"); + + required_string("+Name:"); + stuff_string(goalp->name, F_NAME, NULL); + + // backwards compatibility for old Fred missions - all new missions should use $MessageNew + if(optional_string("$Message:")){ + stuff_string(goalp->message, F_NAME, NULL, MAX_GOAL_TEXT); + } else { + required_string("$MessageNew:"); + stuff_string(goalp->message, F_MULTITEXT, NULL, MAX_GOAL_TEXT); + } + + if (optional_string("$Rating:")){ + stuff_int(&dummy); // not used + } + + required_string("$Formula:"); + goalp->formula = get_sexp_main(); + + goalp->flags = 0; + if ( optional_string("+Invalid:") ) + goalp->type |= INVALID_GOAL; + if ( optional_string("+Invalid") ) + goalp->type |= INVALID_GOAL; + if ( optional_string("+No music") ) + goalp->flags |= MGF_NO_MUSIC; + + goalp->score = 0; + if ( optional_string("+Score:") ){ + stuff_int(&goalp->score); + } + + goalp->team = 0; + if ( optional_string("+Team:") ){ + stuff_int( &goalp->team ); + } +} + +void parse_goals(mission *pm) +{ + required_string("#Goals"); + + while (required_string_either("#Waypoints", "$Type:")){ + parse_goal(pm); + } +} + +void parse_waypoint_list(mission *pm) +{ + waypoint_list *wpl; + + + Assert(Num_waypoint_lists < MAX_WAYPOINT_LISTS); + Assert(pm != NULL); + wpl = &Waypoint_lists[Num_waypoint_lists]; + + required_string("$Name:"); + stuff_string(wpl->name, F_NAME, NULL); + + required_string("$List:"); + wpl->count = stuff_vector_list(wpl->waypoints, MAX_WAYPOINTS_PER_LIST); + + Num_waypoint_lists++; +} + +void parse_waypoints(mission *pm) +{ + int z; + vector pos; + + required_string("#Waypoints"); + + Num_jump_nodes = 0; + while (optional_string("$Jump Node:")) { + Assert(Num_jump_nodes < MAX_JUMP_NODES); + stuff_vector(&pos); + z = jumpnode_create(&pos); + Assert(z >= 0); + + if (optional_string("$Jump Node Name:")) { + stuff_string(Jump_nodes[Num_jump_nodes - 1].name, F_NAME, NULL); + } + + // If no name exists, then use a standard name + if ( Jump_nodes[Num_jump_nodes - 1].name[0] == 0 ) { + sprintf(Jump_nodes[Num_jump_nodes - 1].name, "Jump Node %d", Num_jump_nodes); + } + } + + while (required_string_either("#Messages", "$Name:")) + parse_waypoint_list(pm); +} + +void parse_messages(mission *pm) +{ + required_string("#Messages"); + + mprintf(("Starting mission message count : %d\n", Num_message_waves)); + + // the message_parse function can be found in MissionMessage.h. The format in the + // mission file takes the same format as the messages in messages,tbl. Make parsing + // a whole lot easier!!! + while ( required_string_either("#Reinforcements", "$Name")){ + message_parse(); // call the message parsing system + } + + mprintf(("Ending mission message count : %d\n", Num_message_waves)); +} + +void parse_reinforcement(mission *pm) +{ + reinforcements *ptr; + int instance; + + Assert(Num_reinforcements < MAX_REINFORCEMENTS); + Assert(pm != NULL); + ptr = &Reinforcements[Num_reinforcements]; + + required_string("$Name:"); + stuff_string(ptr->name, F_NAME, NULL); + + find_and_stuff("$Type:", &ptr->type, F_NAME, Reinforcement_type_names, Num_reinforcement_type_names, "reinforcement type"); + + required_string("$Num times:"); + stuff_int(&ptr->uses); + ptr->num_uses = 0; + + // reset the flags to 0 + ptr->flags = 0; + + if ( optional_string("+Arrival delay:") ){ + stuff_int( &(ptr->arrival_delay) ); + } + + if ( optional_string("+No Messages:") ){ + stuff_string_list( ptr->no_messages, MAX_REINFORCEMENT_MESSAGES ); + } + + if ( optional_string("+Yes Messages:") ){ + stuff_string_list( ptr->yes_messages, MAX_REINFORCEMENT_MESSAGES ); + } + + // sanity check on the names of reinforcements -- must either be wings/ships/arrival list. + if ( ship_name_lookup(ptr->name) == -1 ) { + if ( wing_name_lookup(ptr->name, 1) == -1 ) { + p_object *p_objp; + + for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) { + if ( !stricmp(ptr->name, p_objp->name) ){ + break; + } + } + + if ( p_objp == END_OF_LIST(&ship_arrival_list) ) { + Warning(LOCATION, "Reinforcement %s not found as ship or wing", ptr->name); + return; + } + } + } + + // now, if the reinforcement is a wing, then set the number of waves of the wing == number of + // uses of the reinforcement + instance = wing_name_lookup(ptr->name, 1); + if ( instance != -1 ) + Wings[instance].num_waves = ptr->uses; + + Num_reinforcements++; +} + +void parse_reinforcements(mission *pm) +{ + Num_reinforcements = 0; + required_string("#Reinforcements"); + + while (required_string_either("#Background bitmaps", "$Name:")) + parse_reinforcement(pm); +} + +void parse_bitmap(mission *pm) +{ + /* + char name[NAME_LENGTH]; + int z; + starfield_bitmaps *ptr; + + Assert(Num_starfield_bitmaps < MAX_STARFIELD_BITMAPS); + Assert(pm != NULL); + ptr = &Starfield_bitmaps[Num_starfield_bitmaps]; + + required_string("$Bitmap:"); + stuff_string(name, F_NAME, NULL); + for (z=0; z= Num_starfield_bitmap_lists ) { + Warning( LOCATION, "Bitmap specified in mission not in game!\n" ); + z = 0; + } + + ptr->bitmap_index = z; + required_string("$Orientation:"); + stuff_matrix(&ptr->m); + + required_string("$Rotation rate:"); + stuff_float(&ptr->rot); + + required_string("$Distance:"); + stuff_float(&ptr->dist); + + required_string("$Light:"); + stuff_int(&ptr->light); + Num_starfield_bitmaps++; + calculate_bitmap_points(ptr); + */ + Int3(); +} + +void parse_bitmaps(mission *pm) +{ + char str[MAX_FILENAME_LEN+1] = ""; + starfield_bitmap_instance b; + int z; + + Num_starfield_bitmaps = 0; + required_string("#Background bitmaps"); + + required_string("$Num stars:"); + stuff_int(&Num_stars); + if (Num_stars >= MAX_STARS) + Num_stars = MAX_STARS; + + int Ambient_light_level; + required_string("$Ambient light level:"); + stuff_int(&Ambient_light_level); + + // This should call light_set_ambient() to + // set the ambient light + + Nebula_index = -1; + Mission_palette = 1; + + if(The_mission.flags & MISSION_FLAG_FULLNEB){ + // no regular nebula stuff + nebula_close(); + + // neb2 info + strcpy(Neb2_texture_name, "Eraseme3"); + Neb2_poof_flags = ((1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5)); + if(optional_string("+Neb2:")){ + stuff_string(Neb2_texture_name, F_NAME, NULL); + + required_string("+Neb2Flags:"); + stuff_int(&Neb2_poof_flags); + + // initialize neb effect. its gross to do this here, but Fred is dumb so I have no choice ... :( + if(Fred_running){ + neb2_level_init(); + } + } + } else { + if (optional_string("+Nebula:")) { + stuff_string(str, F_NAME, NULL, MAX_FILENAME_LEN); + + // parse the proper nebula type (full or not) + for (z=0; z= 0){ + nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading); + } else { + nebula_close(); + } + } + + // parse suns + Num_suns = 0; + while(optional_string("$Sun:")){ + // filename + stuff_string(b.filename, F_NAME, NULL); + + // angles + required_string("+Angles:"); + stuff_float(&b.ang.p); + stuff_float(&b.ang.b); + stuff_float(&b.ang.h); + + // scale + required_string("+Scale:"); + stuff_float(&b.scale_x); + b.scale_y = b.scale_x; + b.div_x = 1; + b.div_y = 1; + + // if we have room, store it + if(Num_suns < MAX_STARFIELD_BITMAPS){ + Suns[Num_suns] = b; + strcpy(Suns[Num_suns].filename, b.filename); + Num_suns++; + } + } + + // parse background bitmaps + Num_starfield_bitmaps = 0; + while(optional_string("$Starbitmap:")){ + // filename + stuff_string(b.filename, F_NAME, NULL); + + // angles + required_string("+Angles:"); + stuff_float(&b.ang.p); + stuff_float(&b.ang.b); + stuff_float(&b.ang.h); + + // scale + // scale + if(optional_string("+Scale:")){ + stuff_float(&b.scale_x); + b.scale_y = b.scale_x; + b.div_x = 1; + b.div_y = 1; + } else { + required_string("+ScaleX:"); + stuff_float(&b.scale_x); + + required_string("+ScaleY:"); + stuff_float(&b.scale_y); + + required_string("+DivX:"); + stuff_int(&b.div_x); + + required_string("+DivY:"); + stuff_int(&b.div_y); + } + + // if we have room, store it + if(Num_starfield_bitmaps < MAX_STARFIELD_BITMAPS){ + Starfield_bitmap_instance[Num_starfield_bitmaps] = b; + strcpy(Starfield_bitmap_instance[Num_starfield_bitmaps].filename, b.filename); + Num_starfield_bitmaps++; + } + } + + if ( optional_string("#Asteroid Fields") ){ + parse_asteroid_fields(pm); + } +} + +void parse_asteroid_fields(mission *pm) +{ +#ifndef FS2_DEMO + + int i, count, subtype; + + Assert(pm != NULL); + for (i=0; iobjnum = objnum; + } + + Player_obj->flags |= OF_PLAYER_SHIP; // make this object a player controlled ship. + Player_ship = &Ships[Player_start_shipnum]; + Player_ai = &Ai_info[Player_ship->ai_index]; + + Player_ai->targeted_subsys = NULL; + Player_ai->targeted_subsys_parent = -1; + + // determine if player start has initial velocity and set forward cruise percent to relect this + if ( Player_obj->phys_info.vel.z > 0.0f ) + Player->ci.forward_cruise_percent = Player_obj->phys_info.vel.z / Player_ship->current_max_speed * 100.0f; + + // put in hard coded starting wing names. + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){ + Starting_wings[0] = wing_name_lookup(Starting_wing_names[0],1); + Starting_wings[1] = wing_name_lookup(Starting_wing_names[MAX_STARTING_WINGS],1); + } else { + for (i = 0; i < MAX_STARTING_WINGS; i++ ) { + Starting_wings[i] = wing_name_lookup(Starting_wing_names[i], 1); + } + } + + init_ai_system(); + + // call a function to deal with intially docked ships + mission_parse_do_initial_docks(); + + // deal with setting up arrival location for all ships. Must do this now after all ships are created + mission_parse_set_arrival_locations(); + + // clear out information about arriving support ships + Arriving_support_ship = NULL; + Num_arriving_repair_targets = 0; + + // convert all ship name indices to ship indices now that mission has been loaded + if (Fred_running) { + for (i=0; i= 0) && (Ships[i].arrival_anchor >= 0) && (Ships[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET)) + Ships[i].arrival_anchor = indices[Ships[i].arrival_anchor]; + + if ( (Ships[i].objnum >= 0) && (Ships[i].departure_anchor >= 0) ) + Ships[i].departure_anchor = indices[Ships[i].departure_anchor]; + } + + for (i=0; i= 0) && (Wings[i].arrival_anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET)) + Wings[i].arrival_anchor = indices[Wings[i].arrival_anchor]; + + if (Wings[i].wave_count && (Wings[i].departure_anchor >= 0) ) + Wings[i].departure_anchor = indices[Wings[i].departure_anchor]; + } + + } + + // before doing anything else, we must validate all of the sexpressions that were loaded into the mission. + // Loop through the Sexp_nodes array and send the top level functions to the check_sexp_syntax parser + + for (i = 0; i < MAX_SEXP_NODES; i++) { + if ( is_sexp_top_level(i) && (!Fred_running || (i != Sexp_clipboard))) { + int result, bindex, op; + + op = identify_operator(CTEXT(i)); + Assert(op != -1); // need to make sure it is an operator before we treat it like one.. + result = check_sexp_syntax( i, query_operator_return_type(op), 1, &bindex); + + // entering this if statement will result in program termination!!!!! + // print out an error based on the return value from check_sexp_syntax() + if ( result ) { + char sexp_str[8192], text[8192]; + + convert_sexp_to_string( i, sexp_str, SEXP_ERROR_CHECK_MODE); + sprintf(text, "%s.\n\nIn sexpression: %s\n(Error appears to be: %s)", + sexp_error_message(result), sexp_str, Sexp_nodes[bindex].text); + + if (!Fred_running) + Error( LOCATION, text ); + else + Warning( LOCATION, text ); + } + } + } + + ai_post_process_mission(); + + + /* + for (i=0; i= 0) { + Assert(Initially_docked[i].docker->type == OBJ_SHIP); + p1 = model_find_dock_name_index(Ships[Initially_docked[i].docker->instance].modelnum, + Initially_docked[i].docker_point); + Assert(Objects[z].type == OBJ_SHIP); + p2 = model_find_dock_name_index(Ships[Objects[z].instance].modelnum, + Initially_docked[i].dockee_point); + + if ((p1 >= 0) && (p2 >= 0)) { + nprintf(("AI", "Initially Docked: %s with %s\n", Ships[Initially_docked[i].docker->instance].ship_name, Ships[Objects[z].instance].ship_name)); + if (ship_docking_valid(Initially_docked[i].docker->instance, Objects[z].instance)) // only dock if they are allowed to be docked. + ai_dock_with_object(Initially_docked[i].docker, &Objects[z], 89, AIDO_DOCK_NOW, p1, p2); + + } else + Int3(); // Curious. Two ships told to dock, but one of the dock points is bogus. + // Get Allender or Hoffoss, one of whom probably wrote the above if () + } + } + */ + + // we must also count all of the ships of particular types. We count all of the ships that do not have + // their SF_IGNORE_COUNT flag set. We don't count ships in wings when the equivalent wing flag is set. + // in counting ships in wings, we increment the count by the wing's wave count to account for everyone. + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + int siflags, num, shipnum; + + shipnum = Objects[so->objnum].instance; + // pass over non-ship objects and player ship objects + if ( Ships[shipnum].objnum == -1 || (Objects[Ships[shipnum].objnum].flags & OF_PLAYER_SHIP) ) + continue; + if ( Ships[shipnum].flags & SF_IGNORE_COUNT ) + continue; + if ( (Ships[shipnum].wingnum != -1) && (Wings[Ships[shipnum].wingnum].flags & WF_IGNORE_COUNT) ) + continue; + + siflags = Ship_info[Ships[shipnum].ship_info_index].flags; + + // determine the number of times we need to add this ship into the count +// if ( Ships[i].wingnum == -1 ) + num = 1; +// else +// num = Wings[Ships[i].wingnum].num_waves; + + ship_add_ship_type_count( siflags, num ); + } + // now go through the list of ships yet to arrive + for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) { + int siflags, num; + + // go through similar motions as above + if ( p_objp->flags & P_SF_IGNORE_COUNT ) + continue; + if ( (p_objp->wingnum != -1) && (Wings[p_objp->wingnum].flags & WF_IGNORE_COUNT) ) + continue; + + siflags = Ship_info[p_objp->ship_class].flags; + + if ( p_objp->wingnum == -1 ) + num = 1; + else + num = Wings[p_objp->wingnum].num_waves - 1; // subtract one since we already counted the first wave + + ship_add_ship_type_count( siflags, num ); + } + + // set player weapons that are selected by default + // AL 09/17/97: I added this code to select the first primary/secondary weapons, + // since I noticed the player ship sometimes doesn't get default weapons selected + + // DB: modified 4/23/98 to take multiplayer into account. Under certain circumstances, multiplayer netplayer ships + // had their current_primary_bank and current_secondary_bank set to -1 (from ship_set()) and left there since + // Player_ship is not the only one we need to need about. + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + ship *shipp = &Ships[Objects[so->objnum].instance]; + + // don't process non player wing ships + if ( !(shipp->flags & SF_FROM_PLAYER_WING ) ) + continue; + + swp = &shipp->weapons; + + // swp = &Player_ship->weapons; + if ( swp->num_primary_banks > 0 ) { + swp->current_primary_bank = 0; // currently selected primary bank + } + + if ( swp->num_secondary_banks > 0 ) { + swp->current_secondary_bank = 0; // currently selected secondary bank + } + } + + ets_init_ship(Player_obj); // init ETS data for the player + + // put the timestamp stuff here for now + Mission_arrival_timestamp = timestamp( ARRIVAL_TIMESTAMP ); + Mission_departure_timestamp = timestamp( DEPARTURE_TIMESTAMP ); + Mission_end_time = -1; + + if(Game_mode & GM_MULTIPLAYER){ + multi_respawn_build_points(); + } + + // maybe reset hotkey defaults when loading new mission + if ( Last_file_checksum != Current_file_checksum ){ + mission_hotkey_reset_saved(); + } + + Allow_arrival_music_timestamp=timestamp(0); + Allow_arrival_message_timestamp=timestamp(0); + Arrival_message_delay_timestamp = timestamp(-1); + + int idx; + for(idx=0; idx<2; idx++){ + Allow_arrival_music_timestamp_m[idx]=timestamp(0); + Allow_arrival_message_timestamp_m[idx]=timestamp(0); + Arrival_message_delay_timestamp_m[idx] = timestamp(-1); + } + + Last_file_checksum = Current_file_checksum; +} + +int get_mission_info(char *filename, mission *mission_p) +{ + int rval; + + // if mission_p is NULL, make it point to The_mission + if ( mission_p == NULL ) + mission_p = &The_mission; + + if ((rval = setjmp(parse_abort)) != 0) { + nprintf(("Error", "Error abort! Code = %d", rval)); + return rval; + + } else { + int filelength; + + // open localization + lcl_ext_open(); + + CFILE *ftemp = cfopen(filename, "rt"); + if (!ftemp){ + // close localization + lcl_ext_close(); + + return -1; + } + + // 7/9/98 -- MWA -- check for 0 length file. + filelength = cfilelength(ftemp); + cfclose(ftemp); + if ( filelength == 0 ){ + // close localization + lcl_ext_close(); + + return -1; + } + + read_file_text(filename, CF_TYPE_MISSIONS); + memset( mission_p, 0, sizeof(mission) ); + init_parse(); + parse_mission_info(mission_p); + + // close localization + lcl_ext_close(); + } + + return 0; +} + +// mai parse routine for parsing a mission. The default parameter flags tells us which information +// to get when parsing the mission. 0 means get everything (default). Other flags just gets us basic +// info such as game type, number of players etc. +int parse_main(char *mission_name, int flags) +{ + int rval, i; + + // fill in Ship_class_names array with the names from the ship_info struct; + Num_parse_names = 0; + Mission_all_attack = 0; // Might get set in mission load. + Assert(Num_ship_types < MAX_SHIP_TYPES); + + for (i = 0; i < Num_ship_types; i++) + Ship_class_names[i] = Ship_info[i].name; + + if ((rval = setjmp(parse_abort)) != 0) { + nprintf(("Error", "Error abort! Code = %i.", rval)); + return rval; + + } else { + // open localization + lcl_ext_open(); + + CFILE *ftemp = cfopen(mission_name, "rt", CFILE_NORMAL, CF_TYPE_MISSIONS); + // fail situation. + if (!ftemp) { + if (!Fred_running) + Error( LOCATION, "Couldn't open mission '%s'\n", mission_name ); + + Current_file_length = -1; + Current_file_checksum = 0; + + // close localization + lcl_ext_close(); + + return -1; + } + + Current_file_length = cfilelength(ftemp); + cfclose(ftemp); + + read_file_text(mission_name, CF_TYPE_MISSIONS); + memset(&The_mission, 0, sizeof(The_mission)); + parse_mission(&The_mission, flags); + display_parse_diagnostics(); + + // close localization + lcl_ext_close(); + } + + if (!Fred_running) + strcpy(Mission_filename, mission_name); + + return 0; +} + +// sets the arrival lcoation of the ships in wingp. pass num_to_set since the threshold value +// for wings may have us create more ships in the wing when there are still some remaining +void mission_set_wing_arrival_location( wing *wingp, int num_to_set ) +{ + int index; + + // get the starting index into the ship_index array of the first ship whose location we need set. + + index = wingp->current_count - num_to_set; + if ( (wingp->arrival_location == ARRIVE_FROM_DOCK_BAY) || (wingp->arrival_location == ARRIVE_AT_LOCATION) ) { + while ( index < wingp->current_count ) { + object *objp; + + objp = &Objects[Ships[wingp->ship_index[index]].objnum]; + mission_set_arrival_location(wingp->arrival_anchor, wingp->arrival_location, wingp->arrival_distance, OBJ_INDEX(objp), NULL, NULL); + + index++; + } + } else { + object *leader_objp; + vector pos; + matrix orient; + int wing_index; + + // wing is not arriving from a docking bay -- possibly move them based on arriving near + // or in front of some other ship. + index = wingp->current_count - num_to_set; + leader_objp = &Objects[Ships[wingp->ship_index[index]].objnum]; + if (mission_set_arrival_location(wingp->arrival_anchor, wingp->arrival_location, wingp->arrival_distance, OBJ_INDEX(leader_objp), &pos, &orient)) { + // modify the remaining ships created + index++; + wing_index = 1; + while ( index < wingp->current_count ) { + object *objp; + + objp = &Objects[Ships[wingp->ship_index[index]].objnum]; + + // change the position of the next ships in the wing. Use the cool function in AiCode.cpp which + // Mike K wrote to give new positions to the wing members. + get_absolute_wing_pos( &objp->pos, leader_objp, wing_index++, 0); + memcpy( &objp->orient, &orient, sizeof(matrix) ); + + index++; + } + } + } + + // create warp effect if in mission and not arriving from docking bay + if ( (Game_mode & GM_IN_MISSION) && (wingp->arrival_location != ARRIVE_FROM_DOCK_BAY) ) { + for ( index = wingp->current_count - num_to_set; index < wingp->current_count; index ++ ) { + shipfx_warpin_start( &Objects[Ships[wingp->ship_index[index]].objnum] ); + } + } +} + +// this function is called after a mission is parsed to set the arrival locations of all ships in the +// mission to the apprioriate spot. Mainly needed because ships might be in dock bays to start +// the mission, so their AI mode must be set appropriately. +void mission_parse_set_arrival_locations() +{ + int i; + object *objp; + + if ( Fred_running ) + return; + + obj_merge_created_list(); + for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + ship *shipp; + + if ( objp->type != OBJ_SHIP ) + continue; + + shipp = &Ships[objp->instance]; + // if the ship is in a wing -- ignore the info and let the wing info handle it + if ( shipp->wingnum != -1 ) + continue; + + // call function to set arrival location for this ship. + mission_set_arrival_location( shipp->arrival_anchor, shipp->arrival_location, shipp->arrival_distance, OBJ_INDEX(objp), NULL, NULL); + } + + // do the wings + for ( i = 0; i < num_wings; i++ ) { + + // if wing has no ships, then don't process it. + if ( Wings[i].current_count == 0 ) + continue; + + mission_set_wing_arrival_location( &Wings[i], Wings[i].current_count ); + } +} + + +// function which iterates through the ship_arrival_list and creates any ship which +// should be intially docked with a ship which currently exists in the mission +void mission_parse_do_initial_docks() +{ + p_object *pobjp, *tmp; + + pobjp = GET_FIRST( &ship_arrival_list ); + while ( pobjp != END_OF_LIST(&ship_arrival_list) ) { + int shipnum; + + tmp = GET_NEXT(pobjp); + + // see if the flag for initial docked is set + if ( pobjp->flags & P_SF_INITIALLY_DOCKED ) { + // see if who this parse object is supposed to be docked with is in the mission + shipnum = ship_name_lookup( pobjp->docked_with ); + if ( shipnum != -1 ) { + int objnum, p1, p2; + + // the ship exists, so create this object, then dock the two. + objnum = parse_create_object( pobjp ); + Assert ( objnum != -1 ); + + list_remove( &ship_arrival_list, pobjp); + + // p1 is the parse object's docking point. + // p2 is the existing objects docking point. + p1 = model_find_dock_name_index(Ships[shipnum].modelnum, pobjp->docker_point); + p2 = model_find_dock_name_index(Ships[Objects[objnum].instance].modelnum, pobjp->dockee_point); + + if ((p1 >= 0) && (p2 >= 0)) { + nprintf(("AI", "Initially Docked: %s with %s\n", Ships[shipnum].ship_name, Ships[Objects[objnum].instance].ship_name)); + if (ship_docking_valid(shipnum, Objects[objnum].instance)) // only dock if they are allowed to be docked. + ai_dock_with_object(&Objects[Ships[shipnum].objnum], &Objects[objnum], 89, AIDO_DOCK_NOW, p1, p2); + else + ai_dock_with_object(&Objects[objnum], &Objects[Ships[shipnum].objnum], 89, AIDO_DOCK_NOW, p2, p1); + + } else + Int3(); // Curious. Two ships told to dock, but one of the dock points is bogus. + // Get Allender or Hoffoss, one of whom probably wrote the above if () + } + } + + pobjp = tmp; + } +} + +// function which returns true or false if the given mission support multiplayers +int mission_parse_is_multi(char *filename, char *mission_name) +{ + int rval, game_type; + int filelength; + CFILE *ftemp; + + // new way of getting information. Open the file, and just get the name and the game_type flags. + // return the flags if a multiplayer mission + + game_type = 0; + + ftemp = cfopen(filename, "rt"); + if (!ftemp) + return 0; + + // 7/9/98 -- MWA -- check for 0 length file. + filelength = cfilelength(ftemp); + cfclose(ftemp); + if ( filelength == 0 ) + return 0; + + // open localization + lcl_ext_open(); + + if ((rval = setjmp(parse_abort)) != 0) { + Error(LOCATION, "Bogus! Trying to get multi game type on mission %s returned as a mission from cf_get_filelist\n"); + } else { + read_file_text(filename, CF_TYPE_MISSIONS); + reset_parse(); + if ( skip_to_string("$Name:") != 1 ) { + nprintf(("Network", "Unable to process %s because we couldn't find $Name:", filename)); + + // close localization + lcl_ext_close(); + + return 0; + } + stuff_string( mission_name, F_NAME, NULL ); + if ( skip_to_string("+Game Type Flags:") != 1 ) { + nprintf(("Network", "Unable to process %s because we couldn't find +Game Type Flags:\n", filename)); + + // close localization + lcl_ext_close(); + + return 0; + } + stuff_int(&game_type); + } + if ( game_type & MISSION_TYPE_MULTI ){ + // close localization + lcl_ext_close(); + + return game_type; + } + + // close localization + lcl_ext_close(); + + return 0; +} + +// function which gets called to retrieve useful information about a mission. We will get the +// name, description, and number of players for a mission. Probably used for multiplayer only? +// The calling function can use the information in The_mission to get the name/description of the mission +// if needed. + +int mission_parse_get_multi_mission_info( char *filename ) +{ + if ( parse_main(filename, MISSION_PARSE_MISSION_INFO) ){ + return -1; + } + + Assert( The_mission.game_type & MISSION_TYPE_MULTI ); // assume multiplayer only for now? + + // return the number of parse_players. later, we might want to include (optionally?) the number + // of other ships in the main players wing (usually wing 'alpha') for inclusion of number of + // players allowed. + + return The_mission.num_players; +} + +// returns true or false if this is on the yet to arrive list +int mission_parse_ship_arrived( char *shipname ) +{ + p_object *objp; + + for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) { + if ( !stricmp( objp->name, shipname) ) + return 0; // still on the arrival list + } + return 1; +} + +// return the parse object on the ship arrival list associated with the given name +p_object *mission_parse_get_arrival_ship( char *name ) +{ + p_object *objp; + + for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) { + if ( !stricmp( objp->name, name) ) + return objp; // still on the arrival list + } + + return NULL; +} + +// return the parse object on the ship arrival list associated with the given signature +p_object *mission_parse_get_arrival_ship( ushort net_signature ) +{ + p_object *objp; + + for ( objp = GET_FIRST(&ship_arrival_list); objp !=END_OF_LIST(&ship_arrival_list); objp = GET_NEXT(objp) ) { + if ( objp->net_signature == net_signature ) + return objp; // still on the arrival list + } + + return NULL; +} + +// mission_set_arrival_location() sets the arrival location of a parse object according to the arrival location +// of the object. Returns true if object set to new position, false if not. +int mission_set_arrival_location(int anchor, int location, int dist, int objnum, vector *new_pos, matrix *new_orient) +{ + int shipnum, anchor_objnum; + vector anchor_pos, rand_vec, new_fvec; + matrix orient; + + if ( location == ARRIVE_AT_LOCATION ) + return 0; + + Assert(anchor >= 0); + + // this ship might possibly arrive at another location. The location is based on the + // proximity of some ship (and some other special tokens) + + // if we didn't find the arrival anchor in the list of special nodes, then do a + // ship name lookup on the anchor + if (anchor < SPECIAL_ARRIVAL_ANCHORS_OFFSET) { + shipnum = ship_name_lookup(Parse_names[anchor]); + if ( shipnum == -1 ) { + Assert ( location != ARRIVE_FROM_DOCK_BAY ); // bogus data somewhere!!! get mwa + nprintf (("allender", "couldn't find ship for arrival anchor -- using location ship created at")); + return 0; + } + + } else { + // come up with a position based on the special token names + shipnum = -1; + + if (anchor == ANY_FRIENDLY) { + shipnum = ship_get_random_team_ship( TEAM_FRIENDLY, SHIP_GET_ANY_SHIP ); + } else if (anchor == ANY_HOSTILE) { + shipnum = ship_get_random_team_ship( opposing_team_mask(Player_ship->team), SHIP_GET_ANY_SHIP ); + } else if (anchor == ANY_FRIENDLY_PLAYER) { + shipnum = ship_get_random_team_ship( TEAM_FRIENDLY, SHIP_GET_ONLY_PLAYERS ); + } else if (anchor == ANY_HOSTILE_PLAYER) { + shipnum = ship_get_random_team_ship( opposing_team_mask(Player_ship->team), SHIP_GET_ONLY_PLAYERS ); + } else + Int3(); // get allender -- unknown special arrival instructions + + // if we didn't get an object from one of the above functions, then make the object + // arrive at it's placed location + if ( shipnum == -1 ) { + nprintf (("Allender", "Couldn't find random ship for arrival anchor -- using default location\n")); + return 0; + } + } + + // take the shipnum and get the position. once we have positions, we can determine where + // to make this ship appear + Assert ( shipnum != -1 ); + anchor_objnum = Ships[shipnum].objnum; + anchor_pos = Objects[anchor_objnum].pos; + + // if arriving from docking bay, then set ai mode and call function as per AL's instructions. + if ( location == ARRIVE_FROM_DOCK_BAY ) { + vector pos, fvec; + + // if we get an error, just let the ship arrive(?) + if ( ai_acquire_emerge_path(&Objects[objnum], anchor_objnum, &pos, &fvec) == -1 ) { + Int3(); // get MWA or AL -- not sure what to do here when we cannot acquire a path + return 0; + } + Objects[objnum].pos = pos; + Objects[objnum].orient.fvec = fvec; + } else { + + // AL: ensure dist > 0 (otherwise get errors in vecmat) + // TODO: maybe set distance to 2x ship radius of ship appearing in front of? + if ( dist <= 0 ) { + Error(LOCATION, "Distance of %d is invalid in mission_set_arrival_location\n", dist); + return 0; + } + + // get a vector which is the ships arrival position based on the type of arrival + // this ship should have. Arriving near a ship we use a random normalized vector + // scaled by the distance given by the designer. Arriving in front of a ship means + // entering the battle in the view cone. + if ( location == ARRIVE_NEAR_SHIP ) { + // get a random vector -- use static randvec if in multiplayer + if ( Game_mode & GM_NORMAL ) + vm_vec_rand_vec_quick(&rand_vec); + else + static_randvec( Objects[objnum].net_signature, &rand_vec ); + } else if ( location == ARRIVE_IN_FRONT_OF_SHIP ) { + vector t1, t2, t3; + int r1, r2; + float x; + + // cool function by MK to give a reasonable random vector "in front" of a ship + // rvec and uvec are the right and up vectors. + // If these are not available, this would be an expensive method. + //x = cos(angle) + x = (float)cos(ANG_TO_RAD(45)); + if ( Game_mode & GM_NORMAL ) { + r1 = rand() < RAND_MAX/2 ? -1 : 1; + r2 = rand() < RAND_MAX/2 ? -1 : 1; + } else { + // in multiplayer, use the static rand functions so that all clients can get the + // same information. + r1 = static_rand(Objects[objnum].net_signature) < RAND_MAX/2 ? -1 : 1; + r2 = static_rand(Objects[objnum].net_signature+1) < RAND_MAX/2 ? -1 : 1; + } + + vm_vec_copy_scale(&t1, &(Objects[anchor_objnum].orient.fvec), x); + vm_vec_copy_scale(&t2, &(Objects[anchor_objnum].orient.rvec), (1.0f - x) * r1); + vm_vec_copy_scale(&t3, &(Objects[anchor_objnum].orient.uvec), (1.0f - x) * r2); + + vm_vec_add(&rand_vec, &t1, &t2); + vm_vec_add2(&rand_vec, &t3); + vm_vec_normalize(&rand_vec); + } + + // add in the radius of the two ships involved. This will make the ship arrive further than + // specified, but will appear more accurate since we are pushing the edge of the model to the + // specified distance. large objects appears to be a lot closer without the following line because + // the object centers were at the correct distance, but the model itself was much closer to the + // target ship. + dist += (int)Objects[objnum].radius + (int)Objects[anchor_objnum].radius; + vm_vec_scale_add(&Objects[objnum].pos, &anchor_pos, &rand_vec, (float)dist); + + // I think that we will always want to orient the ship that is arriving to face towards + // the ship it is arriving near/in front of. The effect will be cool! + // + // calculate the new fvec of the ship arriving and use only that to get the matrix. isn't a big + // deal not getting bank. + vm_vec_sub(&new_fvec, &anchor_pos, &Objects[objnum].pos ); + vm_vector_2_matrix( &orient, &new_fvec, NULL, NULL ); + Objects[objnum].orient = orient; + } + + // set the new_pos parameter since it might be used outside the function (i.e. when dealing with wings). + if ( new_pos ) + memcpy(new_pos, &Objects[objnum].pos, sizeof(vector) ); + + if ( new_orient ) + memcpy( new_orient, &Objects[objnum].orient, sizeof(matrix) ); + + return 1; +} + +// mark a reinforcement as available +void mission_parse_mark_reinforcement_available(char *name) +{ + int i; + reinforcements *rp; + + for (i = 0; i < Num_reinforcements; i++) { + rp = &Reinforcements[i]; + if ( !stricmp(rp->name, name) ) { + if ( !(rp->flags & RF_IS_AVAILABLE) ) { + rp->flags |= RF_IS_AVAILABLE; + + // tell all of the clients. + if ( MULTIPLAYER_MASTER ) { + send_reinforcement_avail( i ); + } + } + return; + } + } + + Assert ( i < Num_reinforcements ); +} + +// mission_did_ship_arrive takes a parse object and checked the arrival cue and delay and +// creates the object if necessary. Returns -1 if not created. objnum of created ship otherwise +int mission_did_ship_arrive(p_object *objp) +{ + int did_arrive; + + // find out in the arrival cue became true + did_arrive = eval_sexp(objp->arrival_cue); + + // we must first check to see if this ship is a reinforcement or not. If so, then don't + // process + if ( objp->flags & P_SF_REINFORCEMENT ) { + + // if this ship did arrive, mark the reinforcement as available, and tell clients if in multiplayer + // mode + if ( did_arrive ) { + mission_parse_mark_reinforcement_available(objp->name); + } + return -1; + } + + if ( did_arrive ) { // has the arrival criteria been met? + int object_num; + + Assert ( !(objp->flags & P_SF_CANNOT_ARRIVE) ); // get allender + + // check to see if the delay field <= 0. if so, then create a timestamp and then maybe + // create the object + if ( objp->arrival_delay <= 0 ) { + objp->arrival_delay = timestamp( -objp->arrival_delay * 1000 ); + Assert( objp->arrival_delay >= 0 ); + } + + // if the timestamp hasn't elapsed, move onto the next ship. + if ( !timestamp_elapsed(objp->arrival_delay) ) + return -1; + + // check to see if this ship is to arrive via a docking bay. If so, and the ship to arrive from + // doesn't exist, don't create. + if ( objp->arrival_location == ARRIVE_FROM_DOCK_BAY ) { + int shipnum; + char *name; + + Assert( objp->arrival_anchor >= 0 ); + name = Parse_names[objp->arrival_anchor]; + + // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later. + if ( mission_parse_get_arrival_ship( name ) ) + return -1; + + // see if ship is in mission. If not, then we can assume it was destroyed or departed since + // it is not on the arrival list (as shown by above if statement). + shipnum = ship_name_lookup( name ); + if ( shipnum == -1 ) { + Sexp_nodes[objp->arrival_cue].value = SEXP_KNOWN_FALSE; + return -1; + } + } + + object_num = parse_create_object(objp); // create the ship + + // since this ship is not in a wing, create a SHIP_ARRIVE entry + //mission_log_add_entry( LOG_SHIP_ARRIVE, objp->name, NULL ); + Assert(object_num >= 0 && object_num < MAX_OBJECTS); + + // Play the music track for an arrival + if ( !(Ships[Objects[object_num].instance].flags & SF_NO_ARRIVAL_MUSIC) ) + if ( timestamp_elapsed(Allow_arrival_music_timestamp) ) { + Allow_arrival_music_timestamp = timestamp(ARRIVAL_MUSIC_MIN_SEPARATION); + event_music_arrival(Ships[Objects[object_num].instance].team); + } + return object_num; + } else { + // check to see if the arrival cue of this ship is known false -- if so, then remove + // the parse object from the ship + if ( Sexp_nodes[objp->arrival_cue].value == SEXP_KNOWN_FALSE ) + objp->flags |= P_SF_CANNOT_ARRIVE; + } + + return -1; + +} + +// funciton to set a flag on all parse objects on ship arrival list which cannot +// arrive in the mission +void mission_parse_mark_non_arrivals() +{ + p_object *pobjp; + + for ( pobjp = GET_FIRST(&ship_arrival_list); pobjp != END_OF_LIST(&ship_arrival_list); pobjp = GET_NEXT(pobjp) ) { + if ( pobjp->wingnum != -1 ) { + if ( Sexp_nodes[Wings[pobjp->wingnum].arrival_cue].value == SEXP_KNOWN_FALSE ) + pobjp->flags |= P_SF_CANNOT_ARRIVE; + } else { + if ( Sexp_nodes[pobjp->arrival_cue].value == SEXP_KNOWN_FALSE ) + pobjp->flags |= P_SF_CANNOT_ARRIVE; + } + } +} + +// function to deal with support ship arrival. objnum is the object number of the arriving support +// ship. This function can get called from either single or multiplayer. Needed to that clients +// can know when to abort rearm. +void mission_parse_support_arrived( int objnum ) +{ + int i; + + // when the support ship arrives, the shipname it is supposed to repair is in the 'misc' + // field of the parse_object. If the ship still exists, call ai function which actually + // issues the goal for the repair + for ( i = 0; i < Num_arriving_repair_targets; i++ ) { + int shipnum; + + shipnum = ship_name_lookup( Arriving_repair_targets[i] ); + + if ( shipnum != -1 ) { + object *requester_objp, *support_objp; + + support_objp = &Objects[objnum]; + requester_objp = &Objects[Ships[shipnum].objnum]; + ai_add_rearm_goal( requester_objp, support_objp ); + } + } + + // MK: A bit of a hack. If on player's team and player isn't allowed shields, don't give this ship shields. + if ((Player_obj->flags & OF_NO_SHIELDS) && (Player_ship->team == Ships[Objects[objnum].instance].team)) + Objects[objnum].flags |= OF_NO_SHIELDS; + + Ships[Objects[objnum].instance].flags |= SF_WARPED_SUPPORT; + + Arriving_support_ship = NULL; + Num_arriving_repair_targets = 0; +} + +MONITOR(NumShipArrivals); + +// mission_parse_arrivals will parse the lists of arriving ships and +// wings -- creating new ships/wings if the arrival criteria have been +// met. +void mission_eval_arrivals() +{ + p_object *objp; + wing *wingp; + int i, objnum; + + // before checking arrivals, check to see if we should play a message concerning arrivals + // of other wings. We use the timestamps to delay the arrival message slightly for + // better effect + if ( timestamp_valid(Arrival_message_delay_timestamp) && timestamp_elapsed(Arrival_message_delay_timestamp) && !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ){ + int rship, use_terran; + + // use terran command 25% of time + use_terran = ((frand() - 0.75) > 0.0f)?1:0; + + rship = ship_get_random_player_wing_ship( SHIP_GET_NO_PLAYERS ); + if ( (rship == -1) || use_terran ){ + message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, NULL, MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 ); + } else if ( rship != -1 ) { + message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 ); + } + + Arrival_message_delay_timestamp = timestamp(-1); // make the stamp invalid + } + +// if ( !timestamp_elapsed(Mission_arrival_timestamp) ) +// return; + + // check the ship_arrival_list + objnum = -1; + objp = GET_FIRST(&ship_arrival_list); + while( objp !=END_OF_LIST(&ship_arrival_list) ) { + p_object *temp = GET_NEXT(objp); + if ( objp->wingnum == -1 ) { // if this object has a wing -- let code for wings determine if it should be created + + objnum = mission_did_ship_arrive( objp ); + if ( objnum != -1 ) { + list_remove( &ship_arrival_list, objp); + MONITOR_INC(NumShipArrivals,1); + } + + } + objp = temp; + } + + // check for any initially docked ships. Do it after all are created since the next function + // messes with the ship_arrival_list + mission_parse_do_initial_docks(); // maybe create it's docked counterpart + + mission_parse_mark_non_arrivals(); // mark parse objects which can no longer arrive + + // check the support ship arrival list + if ( Arriving_support_ship ) { + int objnum; + + objnum = mission_did_ship_arrive( Arriving_support_ship ); + + if ( objnum != -1 ) { + MONITOR_INC(NumShipArrivals,1); + mission_parse_support_arrived( objnum ); + } + } + + // we must also check to see if there are waves of a wing that must + // reappear if all the ships of the current wing have been destroyed or + // have departed. If this is the case, then create the next wave. + + for ( i = 0; i < num_wings; i++ ) { + wingp = &Wings[i]; + + // should we process this wing anymore + if ( wingp->flags & WF_WING_GONE ) + continue; + + // if we have a reinforcement wing, then don't try to create new ships automatically. + if ( wingp->flags & WF_REINFORCEMENT ) { + + // check to see in the wings arrival cue is true, and if so, then mark the reinforcement + // as available + if ( eval_sexp(wingp->arrival_cue) ) { + mission_parse_mark_reinforcement_available(wingp->name); + } + continue; + } + + // don't do evaluations for departing wings + if ( wingp->flags & WF_WING_DEPARTING ){ + continue; + } + + // must check to see if we are at the last wave. Code above to determine when a wing is gone only + // gets run when a ship is destroyed (not every N seconds like it used to). Do a quick check + // here. + if ( wingp->current_wave == wingp->num_waves ){ + continue; + } + + // if the current wave of this wing is 0, then we haven't created the ships in the wing yet. + // call parse_wing_create_ships to try and create it. That function will eval the arrival + // cue of the wing and create the ships if necessary, or if the threshold of the wing has + // been reached, then try and create more ships + if ( (wingp->current_wave == 0) || (wingp->current_count <= wingp->threshold) ) { + int created; + + created = parse_wing_create_ships( wingp, wingp->wave_count ); + + // if we created ships in this wing, check to see if the wings was int the reinforcements + // array. If so, then if we have more uses, then reset the reinforcement flag for the wing + // so the user can call in another set if need be. + if ( created > 0 ) { + int rship; + + mission_parse_do_initial_docks(); // maybe create other initially docked ships + if ( Wings[i].flags & WF_RESET_REINFORCEMENT ) { + Wings[i].flags &= ~WF_RESET_REINFORCEMENT; + Wings[i].flags |= WF_REINFORCEMENT; + } + + // possibly send a message to the player when this wing arrives. + if ( wingp->flags & WF_NO_ARRIVAL_MESSAGE ){ + continue; + } + + // multiplayer team vs. team + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){ + // send a hostile wing arrived message + rship = Wings[i].ship_index[0]; + + int multi_team_filter = Ships[rship].team == TEAM_FRIENDLY ? 1 : 0; + + // there are two timestamps at work here. One to control how often the player receives + // messages about incoming hostile waves, and the other to control how long after + // the wing arrives does the player actually get the message. + if ( timestamp_elapsed(Allow_arrival_message_timestamp_m[multi_team_filter]) ) { + if ( !timestamp_valid(Arrival_message_delay_timestamp_m[multi_team_filter]) ){ + Arrival_message_delay_timestamp_m[multi_team_filter] = timestamp_rand(ARRIVAL_MESSAGE_DELAY_MIN, ARRIVAL_MESSAGE_DELAY_MAX ); + } + Allow_arrival_message_timestamp_m[multi_team_filter] = timestamp(ARRIVAL_MESSAGE_MIN_SEPARATION); + + // send to the proper team + message_send_builtin_to_player( MESSAGE_ARRIVE_ENEMY, NULL, MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, multi_team_filter ); + } + } + // everything else + else { + // see if this is a starting player wing + if ( i == Starting_wings[STARTING_WING_BETA] ) { // this is the beta wing + rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS ); + if ( rship != -1 ){ + message_send_builtin_to_player( MESSAGE_BETA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 ); + } + } else if ( i == Starting_wings[STARTING_WING_GAMMA] ) { // this is the gamma wing + rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS ); + if ( rship != -1 ) { + message_send_builtin_to_player( MESSAGE_GAMMA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 ); + } + } else if ( !stricmp( wingp->name, "delta") ) { + rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS ); + if ( rship != -1 ) { + message_send_builtin_to_player( MESSAGE_DELTA_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 ); + } + } else if ( !stricmp(wingp->name, "epsilon") ) { + rship = ship_get_random_ship_in_wing( i, SHIP_GET_NO_PLAYERS ); + if ( rship != -1 ) { + message_send_builtin_to_player( MESSAGE_EPSILON_ARRIVED, &Ships[rship], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, -1, -1 ); + } + } else { + // see if we have a hostile wing that arrived + rship = Wings[i].ship_index[0]; + if ( Ships[rship].team != TEAM_FRIENDLY ) { + + // there are two timestamps at work here. One to control how often the player receives + // messages about incoming hostile waves, and the other to control how long after + // the wing arrives does the player actually get the message. + if ( timestamp_elapsed(Allow_arrival_message_timestamp) ) { + if ( !timestamp_valid(Arrival_message_delay_timestamp) ){ + Arrival_message_delay_timestamp = timestamp_rand(ARRIVAL_MESSAGE_DELAY_MIN, ARRIVAL_MESSAGE_DELAY_MAX ); + } + Allow_arrival_message_timestamp = timestamp(ARRIVAL_MESSAGE_MIN_SEPARATION); + } + } + } + } + } + } + } + Mission_arrival_timestamp = timestamp(ARRIVAL_TIMESTAMP); +} + +MONITOR(NumShipDepartures); + +// called to make object objp depart. +void mission_do_departure( object *objp ) +{ + ship *shipp; +// vector v; + + MONITOR_INC(NumShipDepartures,1); + + Assert ( objp->type == OBJ_SHIP ); + shipp = &Ships[objp->instance]; + + // if departing to a docking bay, try to find the anchor ship to depart to. If not found, then + // just make it warp out like anything else. + if ( shipp->departure_location == DEPART_AT_DOCK_BAY ) { + int anchor_shipnum; + char *name; + + Assert( shipp->departure_anchor >= 0 ); + name = Parse_names[shipp->departure_anchor]; + + // see if ship is yet to arrive. If so, then return -1 so we can evaluate again later. + if ( mission_parse_get_arrival_ship( name ) ) + goto do_departure_warp; + + // see if ship is in mission. If not, then we can assume it was destroyed or departed since + // it is not on the arrival list (as shown by above if statement). + anchor_shipnum = ship_name_lookup( name ); + if ( anchor_shipnum == -1 ) + goto do_departure_warp; + + ai_acquire_depart_path(objp, Ships[anchor_shipnum].objnum); + return; + } + +do_departure_warp: + ai_set_mode_warp_out( objp, &Ai_info[Ships[objp->instance].ai_index] ); + +} + +// put here because mission_eval_arrivals is here. Might move these to a better location +// later -- MWA +void mission_eval_departures() +{ + int i, j; + object *objp; + wing *wingp; + +// if ( !timestamp_elapsed(Mission_departure_timestamp) ) +// return; + + // scan through the active ships an evaluate their departure cues. For those + // ships whose time has come, set their departing flag. + + for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + if (objp->type == OBJ_SHIP) { + ship *shipp; + + Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS)); + + shipp = &Ships[objp->instance]; + + // don't process a ship that is already departing or dying or disabled + // AL 12-30-97: Added SF_CANNOT_WARP to check + if ( (shipp->flags & (SF_DEPARTING | SF_DYING | SF_CANNOT_WARP )) || ship_subsys_disrupted(shipp, SUBSYSTEM_ENGINE) ) { + continue; + } + + // don't process ships that are part of a wing -- handled in seperate case + if ( shipp->wingnum != -1 ) + continue; + +// && (!timestamp_valid(shipp->departure_delay) || timestamp_elapsed(shipp->departure_delay)) ) + // when the departure cue becomes true, set off the departure delay timer. We store the + // timer as -seconds in Freespace which indicates that the timer has not been set. If the timer + // is not set, then turn it into a valid timer and keep evaluating the timer until it is elapsed + if ( eval_sexp(shipp->departure_cue) ) { + if ( shipp->departure_delay <= 0 ) + shipp->departure_delay = timestamp(-shipp->departure_delay * 1000 ); + if ( timestamp_elapsed(shipp->departure_delay) ) + mission_do_departure( objp ); + } + } + } + + // now scan through the list of wings and check their departure cues. For wings with + // that cue being true, we must update internal variables to indicate that the wing is + // departed and that no further waves of this wing will appear + + for ( i = 0; i < num_wings; i++ ) { + wingp = &Wings[i]; + + // should we process this wing anymore + if ( wingp->flags & WF_WING_DEPARTING ) + continue; + + // evaluate the sexpression. If true, mark all the ships in this wing as departing and increment + // the num departed in the wing structure. Then add number of remaining waves * ships/wave to + // departed count to get total count of ships in the wing which departed. (We are counting ships + // that have not yet arrived as departed if they never arrive -- this may be bad, but for some reason + // seems like the right thing to do). + //&& (!timestamp_valid(wingp->departure_delay) || timestamp_elapsed(wingp->departure_delay)) ) { + + if ( eval_sexp(wingp->departure_cue) ) { + // if we haven't set up the departure timer yet (would be <= 0) setup the timer to pop N seconds + // later + if ( wingp->departure_delay <= 0 ) + wingp->departure_delay = timestamp( -wingp->departure_delay * 1000 ); + if ( !timestamp_elapsed(wingp->departure_delay) ) + continue; + + wingp->flags |= WF_WING_DEPARTING; + for ( j = 0; j < wingp->current_count; j++ ) { + ship *shipp; + + shipp = &Ships[wingp->ship_index[j]]; + if ( (shipp->flags & SF_DEPARTING) || (shipp->flags & SF_DYING) ) + continue; + +// shipp->flags |= SF_DEPARTING; +// shipp->final_depart_time = timestamp(3*1000); + + Assert ( shipp->objnum != -1 ); + objp = &Objects[shipp->objnum]; + + // copy the wing's depature information to the ship + shipp->departure_location = wingp->departure_location; + shipp->departure_anchor = wingp->departure_anchor; + + mission_do_departure( objp ); + // don't add to wingp->total_departed here -- this is taken care of in ship code. + } + + // MWA 2/25/98 -- don't do the follwoing wing member updates. It makes the accurate counts + // sort of messed up and causes problems for the event log. The code in ship_wing_cleanup() + // now keys off of the WF_WING_DEPARTING flag instead of the counts below. + + /* + // now be sure that we update wing structure members if there are any remaining waves left + if ( wingp->current_wave < wingp->num_waves ) { + int num_remaining; + + num_remaining = ( (wingp->num_waves - wingp->current_wave) * wingp->wave_count); + wingp->total_departed += num_remaining; + wingp->total_arrived_count += num_remaining; + wingp->current_wave = wingp->num_waves; + } + */ + + } + } + Mission_departure_timestamp = timestamp(DEPARTURE_TIMESTAMP); +} + +// function called from high level game loop to do mission evaluation stuff +void mission_parse_eval_stuff() +{ + mission_eval_arrivals(); + mission_eval_departures(); +} + +int allocate_subsys_status() +{ + int i; + + Assert(Subsys_index < MAX_SUBSYS_STATUS); + Subsys_status[Subsys_index].percent = 0.0f; + Subsys_status[Subsys_index].primary_banks[0] = SUBSYS_STATUS_NO_CHANGE; + for (i=1; itype == OBJ_SHIP) && (objp->flags & OF_PLAYER_SHIP) ) { + ai_clear_ship_goals( &Ai_info[Ships[objp->instance].ai_index] ); + init_ai_object( OBJ_INDEX(objp) ); + } + } +} + +// code to warp in a new support ship. It works by finding the average position of all ships +// in the mission, creating a vector from that position to the player, and scaling out behind the +// player some distance. Should be sufficient. + +#define WARP_IN_MIN_DISTANCE 1000.0f +#define WARP_IN_TIME_MIN 3000 // warps in min 3 seconds later +#define WARP_IN_TIME_MAX 6000 // warps in max 6 seconds later + +// function which adds requester_objp onto the queue of ships for the arriving support ship to service +void mission_add_to_arriving_support( object *requester_objp ) +{ + int i; + ship *shipp; + + Assert ( Arriving_support_ship ); + + if ( Num_arriving_repair_targets == MAX_AI_GOALS ) { + // Int3(); // get allender -- ship isn't going to get repair, but I hope they never queue up this far!!! + mprintf(("Reached MAX_AI_GOALS trying to add repair request!\n")); + return; + } + + shipp = &Ships[requester_objp->instance]; + // check for duplicates before adding + for (i = 0; i < Num_arriving_repair_targets; i++ ) { + if ( !stricmp(Arriving_repair_targets[i], shipp->ship_name) ){ + break; + } + } + if ( i != Num_arriving_repair_targets ){ // found the ship before reaching the end -- ignore it! + return; + } + + strcpy( Arriving_repair_targets[Num_arriving_repair_targets], Ships[requester_objp->instance].ship_name ); + Num_arriving_repair_targets++; + + if ( MULTIPLAYER_MASTER ){ + multi_maybe_send_repair_info( requester_objp, NULL, REPAIR_INFO_WARP_ADD ); + } +} + +extern int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag); + +// Set the warp in position for a support ship relative to an object. +// Caller tries several positions, passing vector in x, y, z. +int get_warp_in_pos(vector *pos, object *objp, float x, float y, float z) +{ + float rand_val; + + if ( Game_mode & GM_NORMAL ) + rand_val = frand(); + else + rand_val = static_randf(objp->net_signature); + + rand_val = 1.0f + (rand_val - 0.5f)*0.2f; + + *pos = objp->pos; + + vm_vec_scale_add2( pos, &objp->orient.rvec, x*rand_val*800.0f); + vm_vec_scale_add2( pos, &objp->orient.uvec, y*rand_val*800.0f); + vm_vec_scale_add2( pos, &objp->orient.fvec, z*rand_val*800.0f); + + return pp_collide_any(&objp->pos, pos, objp->radius, objp, NULL, 1); +} + +void mission_warp_in_support_ship( object *requester_objp ) +{ + vector center, warp_in_pos; + //float mag; + p_object *pobj; + int i, requester_species; + ship *requester_shipp; + + Assert ( requester_objp->type == OBJ_SHIP ); + requester_shipp = &Ships[requester_objp->instance]; // MK, 10/23/97, used to be ->type, bogus, no? + + // if the support ship is already arriving, add the requester to the list + if ( Arriving_support_ship ) { + mission_add_to_arriving_support( requester_objp ); + return; + } + + // get average position of all ships + obj_get_average_ship_pos( ¢er ); + vm_vec_sub( &warp_in_pos, ¢er, &(requester_objp->pos) ); + + // be sure to account for case as player being only ship left in mission + /* + if ( !(IS_VEC_NULL( warp_in_pos)) ) { + mag = vm_vec_mag( &warp_in_pos ); + if ( mag < WARP_IN_MIN_DISTANCE ) + vm_vec_scale( &warp_in_pos, WARP_IN_MIN_DISTANCE/mag); + else + vm_vec_scale( &warp + } else { + // take -player_pos.fvec scaled by 1000.0f; + warp_in_pos = Player_obj->orient.fvec; + vm_vec_scale( &warp_in_pos, -1000.0f ); + } + */ + + // Choose position to warp in ship. + // Temporary, but changed by MK because it used to be exactly behind the player. + // This could cause an Assert if the player immediately targeted it (before moving). + // Tend to put in front of the player to aid him in flying towards the ship. + + if (!get_warp_in_pos(&warp_in_pos, requester_objp, 1.0f, 0.1f, 1.0f)) + if (!get_warp_in_pos(&warp_in_pos, requester_objp, 1.0f, 0.2f, -1.0f)) + if (!get_warp_in_pos(&warp_in_pos, requester_objp, -1.0f, -0.2f, -1.0f)) + if (!get_warp_in_pos(&warp_in_pos, requester_objp, -1.0f, -0.1f, 1.0f)) + get_warp_in_pos(&warp_in_pos, requester_objp, 0.1f, 1.0f, 0.2f); + + // create a parse object, and put it onto the ship_arrival_list. This whole thing kind of sucks. + // I want to put it into a parse object since it needs to arrive just a little later than + // this function is called. I have to make some assumptions in the code about values for the parse + // object since I'm no longer working with a mission file. These exceptions will be noted with + // comments + + Arriving_support_ship = &Support_ship_pobj; + pobj = Arriving_support_ship; + + // create a name for the ship. use "Support #". look for collisions until one isn't found anymore + i = 1; + do { + sprintf(pobj->name, NOX("Support %d"), i); + if ( (ship_name_lookup(pobj->name) == -1) && (ship_find_exited_ship_by_name(pobj->name) == -1) ) + break; + i++; + } while(1); + + pobj->pos = warp_in_pos; + vm_set_identity( &(pobj->orient) ); + + // *sigh*. Gotta get the ship class. For now, this will amount to finding a ship in the ship_info + // array with the same team as the requester of type SIF_SUPPORT. Might need to be changed, but who knows + // vasudans use the terran support ship. + requester_species = Ship_info[requester_shipp->ship_info_index].species; + + // 5/6/98 -- MWA Don't need to do anything for multiplayer. I think that we always want to use + // the species of the caller ship. + Assert( (requester_species == SPECIES_TERRAN) || (requester_species == SPECIES_VASUDAN) ); +// if ( (Game_mode & GM_NORMAL) && (requester_species == SPECIES_VASUDAN) ) { // make vasundan's use the terran support ship +// requester_species = SPECIES_TERRAN; +// } + + // get index of correct species support ship + for (i=0; i < Num_ship_types; i++) { + if ( (Ship_info[i].species == requester_species) && (Ship_info[i].flags & SIF_SUPPORT) ) + break; + } + + if ( i < Num_ship_types ) + pobj->ship_class = i; + else + Int3(); // BOGUS!!!! gotta figure something out here + + pobj->team = requester_shipp->team; + + pobj->behavior = AIM_NONE; // ASSUMPTION: the mission file has the string "None" which maps to AIM_NONE + + // set the ai_goals to -1. We will put the requester object shipname in repair target array and then take + // care of setting up the goal when creating the ship!!!! + pobj->ai_goals = -1; + Num_arriving_repair_targets = 0; + mission_add_to_arriving_support( requester_objp ); + + // need to set ship's cargo to nothing. scan the cargo_names array looking for the string nothing. + // add it if not found + for (i = 0; i < Num_cargo; i++ ) + if ( !stricmp(Cargo_names[i], NOX("nothing")) ) + break; + + if ( i == Num_cargo ) { + strcpy(Cargo_names[i], NOX("Nothing")); + Num_cargo++; + } + pobj->cargo1 = char(i); + + pobj->status_count = 0; + + pobj->arrival_location = 0; // ASSUMPTION: this is index to arrival_lcation string array for hyperspace!!!! + pobj->arrival_distance = 0; + pobj->arrival_anchor = -1; + pobj->arrival_cue = Locked_sexp_true; + pobj->arrival_delay = timestamp_rand(WARP_IN_TIME_MIN, WARP_IN_TIME_MAX); + + pobj->subsys_count = 0; // number of elements used in subsys_status array + pobj->initial_velocity = 100; // start at 100% velocity + pobj->initial_hull = 100; // start at 100% hull + pobj->initial_shields = 100; // and 100% shields + + pobj->departure_location = 0; // ASSUMPTION: this is index to departure_lcation string array for hyperspace!!!! + pobj->departure_anchor = -1; + pobj->departure_cue = Locked_sexp_false; + pobj->departure_delay= 0; + + pobj->determination = 10; // ASSUMPTION: mission file always had this number written out + pobj->wingnum = -1; + if ( Player_obj->flags & P_OF_NO_SHIELDS ) + pobj->flags = P_OF_NO_SHIELDS; // support ships have no shields when player has not shields + + pobj->ai_class = Ship_info[pobj->ship_class].ai_class; + pobj->hotkey = -1; + pobj->score = 0; + + pobj->docked_with[0] = '\0'; + pobj->group = -1; + pobj->persona_index = -1; + pobj->net_signature = multi_assign_network_signature(MULTI_SIG_SHIP); + pobj->wing_status_wing_index = -1; + pobj->wing_status_wing_pos = -1; + pobj->respawn_count = 0; + pobj->alt_type_index = -1; + +} + +// returns true if a support ship is currently in the process of warping in. +int mission_is_support_ship_arriving() +{ + if ( Arriving_support_ship ) + return 1; + else + return 0; +} + +// returns true if the given ship is scheduled to be repaired by the arriving support ship +int mission_is_repair_scheduled( object *objp ) +{ + char *name; + int i; + + if ( !Arriving_support_ship ) + return 0; + + Assert ( objp->type == OBJ_SHIP ); + name = Ships[objp->instance].ship_name; + for (i = 0; i < Num_arriving_repair_targets; i++ ) { + if ( !strcmp( name, Arriving_repair_targets[i]) ) + return 1; + } + + return 0; +} + +// function which removed the given ship from the list of ships that are to get repair +// by arriving support ship +int mission_remove_scheduled_repair( object *objp ) +{ + char *name; + int i, index; + + if ( !Arriving_support_ship ) + return 0; + + // itereate through the target list looking for this ship name. If not found, we + // can simply return. + Assert ( objp->type == OBJ_SHIP ); + name = Ships[objp->instance].ship_name; + for (index = 0; index < Num_arriving_repair_targets; index++ ) { + if ( !strcmp( name, Arriving_repair_targets[index]) ) + break; + } + if ( index == Num_arriving_repair_targets ) + return 0; + + // ship is found -- compress the array + for ( i = index; i < Num_arriving_repair_targets - 1; i++ ) + strcpy( Arriving_repair_targets[i], Arriving_repair_targets[i+1] ); + + Num_arriving_repair_targets--; + + if ( MULTIPLAYER_MASTER ) + multi_maybe_send_repair_info( objp, NULL, REPAIR_INFO_WARP_REMOVE ); + + return 1; +} + +// alternate name stuff +int mission_parse_lookup_alt(char *name) +{ + int idx; + + // sanity + if(name == NULL){ + return -1; + } + + // lookup + for(idx=0; idx Mission_alt_type_count)){ + if (mission_parse_lookup_alt_index_warn) { + Warning(LOCATION, "Ship with invalid alt_name. Get a programmer"); + mission_parse_lookup_alt_index_warn = 0; + } + return; + } + + // stuff it + strcpy(out, Mission_alt_types[index]); +} + +int mission_parse_add_alt(char *name) +{ + // sanity + if(name == NULL){ + return -1; + } + + // maybe add + if(Mission_alt_type_count < MAX_ALT_TYPE_NAMES){ + // stuff the name + strncpy(Mission_alt_types[Mission_alt_type_count++], name, NAME_LENGTH); + + // done + return Mission_alt_type_count - 1; + } + + return -1; +} + +void mission_parse_reset_alt() +{ + Mission_alt_type_count = 0; +} + +int is_training_mission() +{ + return (The_mission.game_type & MISSION_TYPE_TRAINING); +} diff --git a/src/mission/missiontraining.cpp b/src/mission/missiontraining.cpp new file mode 100644 index 0000000..8bf1ba0 --- /dev/null +++ b/src/mission/missiontraining.cpp @@ -0,0 +1,1180 @@ +/* + * $Logfile: /Freespace2/code/Mission/MissionTraining.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Special code for training missions. Stuff like displaying training messages in + * the special training window, listing the training objectives, etc. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 10 8/27/99 2:20p Andsager + * Sort directives by born on date + * + * 9 8/22/99 7:57p Alanl + * fix bug that was scaling down mission training voice twice + * + * 8 8/02/99 7:29p Jefff + * moved instructor text in hi res + * + * 7 8/02/99 3:49p Jefff + * moved directives window down in 1024 + * + * 6 7/10/99 1:44p Andsager + * Modified directives listing so that current and recently + * satisfied/failed directives stay on screen. + * + * 5 6/10/99 3:43p Dave + * Do a better job of syncing text colors to HUD gauges. + * + * 4 2/17/99 2:10p Dave + * First full run of squad war. All freespace and tracker side stuff + * works. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 58 8/25/98 1:48p Dave + * First rev of EMP effect. Player side stuff basically done. Next comes + * AI code. + * + * 57 6/13/98 5:19p Hoffoss + * externalized control config texts. + * + * 56 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 55 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 54 5/23/98 4:14p John + * Added code to preload textures to video card for AGP. Added in code + * to page in some bitmaps that weren't getting paged in at level start. + * + * 53 5/16/98 9:13p Allender + * don't display the directive display in multiplayer + * + * 52 5/07/98 11:59p Mike + * Don't show training popup for unbound key unless in training mission. + * (Happens in demo01) + * + * 51 4/30/98 12:01a Lawrance + * using nprintf((Warning,...)) when audiostream can't be opened + * + * 50 4/27/98 11:20a Hoffoss + * Doubled TRAINING_MSG_QUE_MAX. + * + * 49 4/22/98 3:21p Hoffoss + * Fixed slight training message problems with training_failure state. + * + * 48 4/16/98 4:33p Hoffoss + * Added support for detecting instructor terminating training due to + * player shooting at him. + * + * 47 4/15/98 10:16p Mike + * Training mission #5. + * Fix application of subsystem damage. + * + * 46 4/15/98 5:25p Lawrance + * only show damage gauge when training msg not visible + * + * 45 4/03/98 2:52p Allender + * use Missiontime insteadof timestamp() for objective display + * + * 44 3/31/98 5:23p Hoffoss + * Fixed bug with training message token handling. + * + * 43 3/18/98 6:22p John + * Made directives display wiggle when afterburner is on + * + * 42 3/11/98 2:46p Hoffoss + * Shortened the width of the training message window so it doesn't + * overlap other hud objects. + * + * 41 2/23/98 8:31a John + * More string externalization + * + * 40 2/22/98 4:30p John + * More string externalization classification + * + * 39 2/04/98 4:32p Allender + * support for multiple briefings and debriefings. Changes to mission + * type (now a bitfield). Bitfield defs for multiplayer modes + * + * 38 1/30/98 4:24p Hoffoss + * Added a 3 second delay for directives before they get displayed. + * + * 37 1/26/98 6:27p Lawrance + * Change popups to use '&' meta-char for underlined hotkey. + * + * 36 1/22/98 11:46p Hoffoss + * Fixed linking problem with Fred. + * + * 35 1/22/98 11:00p Mike + * Fix Fred link error by commenting out line, email Hoffoss with info. + * + * 34 1/22/98 4:15p Hoffoss + * Added code to allow popup to tell player he needs to bind a key for the + * training mission. + * + * 33 1/20/98 2:26p Hoffoss + * Removed references to timestamp_ticker, used timestamp() instead. + * + * 32 1/19/98 9:37p Allender + * Great Compiler Warning Purge of Jan, 1998. Used pragma's in a couple + * of places since I was unsure of what to do with code. + * + * 31 1/15/98 6:00p Hoffoss + * Added option to quit menu (in game) to restart the mission. Doesn't + * seem to quite work, though. Checking code in so someone else can look + * into it. + * + * 30 1/09/98 10:47a Lawrance + * Only clear auto-target, linking etc. when actually starting a training + * mission. + * + * 29 1/08/98 4:07p Hoffoss + * Made objective key text appear in Green. + * + * 28 1/06/98 2:07p Lawrance + * strip leading whitespace on training msg lines + * + * 27 1/05/98 11:05p Lawrance + * Clear primary/secondary linking if playing a training mission. + * + * 26 1/05/98 10:03p Lawrance + * Fix HUD gauge flag that didn't get changed. + * + * 25 1/05/98 4:04p Hoffoss + * Changed training-msg sexp operator to allow it to control the length of + * time a message is displayed for. + * + * 24 1/05/98 11:16a Mike + * Clear auto-targeting and auto-speed-matching in a training mission. + * + * 23 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 22 12/22/97 6:07p Hoffoss + * Made directives flash when completed, fixed but with is-destroyed + * operator. + * + * 21 12/19/97 12:43p Hoffoss + * Changed code to allow counts in directives. + * + * 20 12/18/97 11:53a Lawrance + * fix bug with displaying hud directives + * + * 19 12/16/97 9:13p Lawrance + * Integrate new gauges into HUD config. + * + * 18 12/15/97 5:26p Allender + * temporary code to display for 5 second completion status of objectives + * + * 17 11/20/97 1:10a Lawrance + * add support for voice volumes + * + * 16 11/17/97 6:38p Lawrance + * move directives window down + * + * 15 11/14/97 1:24p Lawrance + * draw 'directives' as the title + * + * 14 11/14/97 1:21p Lawrance + * split directives lines if they get too long + * + * 13 11/12/97 6:00p Hoffoss + * Added training messages to hud scrollback log. + * + * 12 11/11/97 10:27p Lawrance + * implement new HUD directives gauge + * + * 11 11/11/97 12:59a Lawrance + * move objective window up slightly + * + * 10 10/21/97 7:18p Hoffoss + * Overhauled the key/joystick control structure and usage throughout the + * entire FreeSpace code. The whole system is very different now. + * + * 9 10/21/97 3:35p Dan + * ALAN: if wave file doesn't exist, don't play it + * + * 8 10/17/97 6:39p Hoffoss + * Added delayability to key-pressed operator and training-msg operator. + * + * 7 10/17/97 10:23a Hoffoss + * Added more comments. + * + * 6 10/14/97 11:35p Lawrance + * change snd_load parameters + * + * 5 10/13/97 4:33p Hoffoss + * Made training messages go away after time. + * + * 4 10/13/97 3:24p Hoffoss + * Made it so training message text can be arbitrarily bolded. + * + * 3 10/10/97 6:15p Hoffoss + * Implemented a training objective list display. + * + * 2 10/09/97 4:44p Hoffoss + * Dimmed training window glass and made it less transparent, added flags + * to events, set he stage for detecting current events. + * + * 1 10/09/97 2:41p Hoffoss + * + * $NoKeywords: $ + */ + +#include "pstypes.h" +#include "parselo.h" +#include "cfile.h" +#include "sound.h" +#include "audiostr.h" +#include "missionmessage.h" +#include "missiongoals.h" +#include "missionparse.h" +#include "timer.h" +#include "controlsconfig.h" +#include "sexp.h" +#include "2d.h" +#include "hudtarget.h" +#include "freespace.h" +#include "hud.h" +#include "bmpman.h" +#include "hudconfig.h" +#include "player.h" +#include "popup.h" +#include "gamesequence.h" +#include "emp.h" +#include "alphacolors.h" +#include "multi.h" + +#define MAX_TRAINING_MSG_LINES 10 +//#define TRAINING_MSG_WINDOW_X 174 +//#define TRAINING_MSG_WINDOW_Y 40 +#define TRAINING_MSG_WINDOW_WIDTH 266 +#define TRAINING_LINE_WIDTH 250 // width in pixels of actual text +#define TRAINING_TIMING 150 // milliseconds per character to display messages +#define TRAINING_TIMING_BASE 1000 // Minimum milliseconds to display any message +//#define TRAINING_OBJ_WND_X 0 // offset of left edge of window +//#define TRAINING_OBJ_WND_Y 180 // offset of top edge of window +//#define TRAINING_OBJ_WND_Y 187 // offset of top edge of window +#define TRAINING_OBJ_WND_WIDTH 170 // number of pixels wide window is. +#define TRAINING_OBJ_LINE_WIDTH 150 // number of pixels wide text can be +#define TRAINING_OBJ_LINES 50 // number of lines to track in objective list +#define TRAINING_OBJ_DISPLAY_LINES 5 // only display this many lines on screen max +#define MAX_TRAINING_MSG_MODS 20 +#define TRAINING_MSG_QUE_MAX 40 + +#define TRAINING_OBJ_STATUS_UNKNOWN (1 << 28) // directive status is unknown +#define TRAINING_OBJ_STATUS_KNOWN (1 << 29) // directive status is known (satisfied or failed) +#define TRAINING_OBJ_LINES_KEY (1 << 30) // flag indicating line describes the key associated with objective +#define TRAINING_OBJ_LINES_EVENT_STATUS_MASK (TRAINING_OBJ_STATUS_KNOWN | TRAINING_OBJ_STATUS_UNKNOWN) + +#define TRAINING_OBJ_LINES_MASK(n) (Training_obj_lines[n] & 0xffff) + +#define TMMOD_NORMAL 0 +#define TMMOD_BOLD 1 + +typedef struct { + char *pos; + int mode; // what function to perform at given position (TMMOD_*) +} training_msg_mods; // training message modifiers + +typedef struct { + int num; + int timestamp; + int length; +} training_msg_que; + +char Training_buf[8192], Training_text[8192]; +char *Training_lines[MAX_TRAINING_MSG_LINES]; // Training message split into lines +char Training_voice_filename[NAME_LENGTH]; +int Training_msg_timestamp; +int Training_line_sizes[MAX_TRAINING_MSG_LINES]; +int Training_msg_method = 1; +int Training_num_lines = 0; +int Training_voice = -1; +int Training_voice_type; +int Training_voice_handle; +int Training_flag = 0; +int Training_failure = 0; +int Training_msg_que_count = 0; +int Training_bind_warning = -1; // Missiontime at which we last gave warning +int Training_msg_visible; +training_msg_que Training_msg_que[TRAINING_MSG_QUE_MAX]; + +// coordinates for training messages +int Training_msg_window_coords[GR_NUM_RESOLUTIONS][2] = { + { 174, 40 }, + { 379, 125 } +}; + +// coordinates for "directives" window on hud +int Training_obj_window_coords[GR_NUM_RESOLUTIONS][2] = { + { 0, 187 }, + { 0, 287 } +}; + + +// training objectives global vars. +int Training_obj_num_lines; +int Training_obj_lines[TRAINING_OBJ_LINES]; +training_msg_mods Training_msg_mods[MAX_TRAINING_MSG_MODS]; + +// local module prototypes +void training_process_msg(char *msg); +void message_translate_tokens(char *buf, char *text); + + +#define NUM_DIRECTIVE_GAUGES 3 +static hud_frames Directive_gauge[NUM_DIRECTIVE_GAUGES]; +static int Directive_frames_loaded = 0; + +static char *Directive_fnames[3] = +{ +//XSTR:OFF + "directives1", + "directives2", + "directives3" +//XSTR:ON +}; + +#define DIRECTIVE_H 9 +#define DIRECTIVE_X 5 +#define NUM_DIRECTIVE_COORDS 3 +#define DIRECTIVE_COORDS_TOP 0 +#define DIRECTIVE_COORDS_MIDDLE 1 +#define DIRECTIVE_COORDS_TITLE 2 +static int Directive_coords[GR_NUM_RESOLUTIONS][NUM_DIRECTIVE_COORDS][2] = +{ + { + // GR_640 + {5,178}, + {5,190}, + {7,180} + }, + { + // GR_1024 + {5,278}, + {5,290}, + {7,280} + } +}; + +// displays (renders) the training objectives list +void training_obj_display() +{ + char buf[256], *second_line; + int i, t, x, y, z, height, end, offset, bx, by, y_count; + color *c; + + if (!Training_obj_num_lines){ + return; + } + + if ( !hud_gauge_active(HUD_DIRECTIVES_VIEW) ) { + // Always draw the directives display if this is a training mission + if ( !(The_mission.game_type & MISSION_TYPE_TRAINING) ) { + return; + } + } + + // don't ever display directives display in multiplayer missions + // if ( Game_mode & GM_MULTIPLAYER ){ + // return; + // } + + height = gr_get_font_height(); + + offset = 0; + end = Training_obj_num_lines; + if (end > TRAINING_OBJ_DISPLAY_LINES) { + end = TRAINING_OBJ_DISPLAY_LINES; + offset = Training_obj_num_lines - end; + } + + // draw top of objective display + // hud_set_default_color(); + hud_set_gauge_color(HUD_DIRECTIVES_VIEW); + + GR_AABITMAP(Directive_gauge[0].first_frame, Directive_coords[gr_screen.res][DIRECTIVE_COORDS_TOP][0]+fl2i(HUD_offset_x), Directive_coords[gr_screen.res][DIRECTIVE_COORDS_TOP][1]+fl2i(HUD_offset_y)); + // gr_set_bitmap(Directive_gauge[0].first_frame); + // gr_aabitmap(Directive_coords[DIRECTIVE_COORDS_TOP][0]+fl2i(HUD_offset_x), Directive_coords[DIRECTIVE_COORDS_TOP][1]+fl2i(HUD_offset_y)); + + // print out title + emp_hud_printf(Directive_coords[gr_screen.res][DIRECTIVE_COORDS_TITLE][0]+fl2i(HUD_offset_x), Directive_coords[gr_screen.res][DIRECTIVE_COORDS_TITLE][1]+fl2i(HUD_offset_y), EG_OBJ_TITLE, XSTR( "directives", 422)); + // gr_printf(Directive_coords[DIRECTIVE_COORDS_TITLE][0]+fl2i(HUD_offset_x), Directive_coords[DIRECTIVE_COORDS_TITLE][1]+fl2i(HUD_offset_y), XSTR( "directives", 422)); + + bx = DIRECTIVE_X+fl2i(HUD_offset_x); + by = Directive_coords[gr_screen.res][DIRECTIVE_COORDS_MIDDLE][1]+fl2i(HUD_offset_y); + + y_count = 0; + for (i=0; ip_info.team != Mission_events[z].team)){ + continue; + } + } + + switch (mission_get_event_status(z)) { + case EVENT_CURRENT: +// gr_set_color_fast(&Color_bright_white); + c = &Color_bright_white; + break; + + case EVENT_FAILED: +// gr_set_color_fast(&Color_bright_red); + c = &Color_bright_red; + break; + + case EVENT_SATISFIED: +// gr_set_color_fast(&Color_bright_blue); + t = Mission_events[z].satisfied_time; + if (t + i2f(2) > Missiontime) { + if (Missiontime % fl2f(.4f) < fl2f(.2f)){ + c = &Color_bright_blue; + } else { + c = &Color_bright_white; + } + } else { + c = &Color_bright_blue; + } + break; + } + } + + // maybe split the directives line + second_line = split_str_once(buf, 167); + + // blit the background frames + // hud_set_default_color(); + hud_set_gauge_color(HUD_DIRECTIVES_VIEW); + + GR_AABITMAP(Directive_gauge[1].first_frame, bx, by); + // gr_set_bitmap(Directive_gauge[1].first_frame); + // gr_aabitmap(bx, by); + + by += DIRECTIVE_H; + + if ( second_line ) { + GR_AABITMAP(Directive_gauge[1].first_frame, bx, by); + // gr_set_bitmap(Directive_gauge[1].first_frame); + // gr_aabitmap(bx, by); + + by += DIRECTIVE_H; + } + + // blit the text + gr_set_color_fast(c); + + emp_hud_string(x, y, EG_OBJ1 + i, buf); + // gr_printf(x, y, buf); + + y_count++; + + if ( second_line ) { + y = Training_obj_window_coords[gr_screen.res][1] + fl2i(HUD_offset_y) + y_count * height + height / 2 + 1; + + emp_hud_string(x+12, y, EG_OBJ1 + i + 1, second_line); + // gr_printf(x+12, y, second_line); + + y_count++; + } + } + + // draw the bottom of objective display + // hud_set_default_color(); + hud_set_gauge_color(HUD_DIRECTIVES_VIEW); + + GR_AABITMAP(Directive_gauge[2].first_frame, bx, by); + // gr_set_bitmap(Directive_gauge[2].first_frame); + // gr_aabitmap(bx, by); +} + +// mission initializations (called once before a new mission is started) +void training_mission_init() +{ + int i; + + Assert(!Training_num_lines); + Training_obj_num_lines = 0; + Training_msg_que_count = 0; + Training_failure = 0; + for (i=0; iflags &= ~(PLAYER_FLAGS_MATCH_TARGET | PLAYER_FLAGS_MSG_MODE | PLAYER_FLAGS_AUTO_TARGETING | PLAYER_FLAGS_AUTO_MATCH_SPEED | PLAYER_FLAGS_LINK_PRIMARY | PLAYER_FLAGS_LINK_SECONDARY ); + } +} + +void training_mission_page_in() +{ + int i; + for ( i = 0; i < NUM_DIRECTIVE_GAUGES; i++ ) { + bm_page_in_aabitmap( Directive_gauge[i].first_frame, Directive_gauge[i].num_frames ); + } +} + + +int comp_training_lines_by_born_on_date(const void *m1, const void *m2) +{ + int *e1, *e2; + e1 = (int*) m1; + e2 = (int*) m2; + + Assert(Mission_events[*e1 & 0xffff].born_on_date != 0); + Assert(Mission_events[*e2 & 0xffff].born_on_date != 0); + + return (Mission_events[*e1 & 0xffff].born_on_date - Mission_events[*e2 & 0xffff].born_on_date); +} + + +// now sort list of events +// sort on EVENT_CURRENT and born on date, for other events (EVENT_SATISFIED, EVENT_FAILED) sort on born on date +#define MIN_SATISFIED_TIME 5 +#define MIN_FAILED_TIME 7 +void sort_training_objectives() +{ + int i, event_status, offset; + + // start by sorting on born on date + qsort(Training_obj_lines, Training_obj_num_lines, sizeof(int), comp_training_lines_by_born_on_date); + + // get the index of the first directive that will be displayed + // if less than 0, display all lines + offset = Training_obj_num_lines - TRAINING_OBJ_DISPLAY_LINES; + + if (offset <= 0) { + return; + } + + // go through lines 0 to offset-1 and check if there are any CURRENT or RECENTLY_KNOWN events that should be shown + int num_offset_events = 0; + for (i=0; i=0; unkn_vis--) { + if (Training_obj_lines[unkn_vis] & TRAINING_OBJ_STATUS_UNKNOWN) { + break; + } + } + + // find first slot that can be bumped + // look at the last (N-4 to N) positions + for (slot_idx=0; slot_idx0; j--) { + Training_obj_lines[j+offset-1] = Training_obj_lines[j+offset-2]; + } + Training_obj_lines[offset] = Training_obj_lines[unkn_vis]; + Training_obj_lines[unkn_vis] &= ~TRAINING_OBJ_LINES_EVENT_STATUS_MASK; + Training_obj_lines[unkn_vis] |= TRAINING_OBJ_STATUS_KNOWN; + } + + // remove event status + for (i=0; i Mission_events[event_idx].born_on_date + 3000) ) { + if (!Training_failure || !strnicmp(Mission_events[event_idx].name, XSTR( "Training failed", 423), 15)) { + + // check for the actual objective + for (i=0; i= 0) { + if (Training_voice_type) { + audiostream_close_file(Training_voice_handle, 0); + + } else { + snd_stop(Training_voice_handle); + } + } + + Training_voice = -1; + Training_num_lines = Training_obj_num_lines = 0; + *Training_text = 0; +} + +// translates special tokens. Handles one token only. +char *translate_msg_token(char *str) +{ + if (!stricmp(str, NOX("wp"))) { + sprintf(str, "%d", Training_context_goal_waypoint + 1); + return str; + } + + return NULL; +} + +// translates all special tokens in a message, producing the new finalized message to be displayed +void message_translate_tokens(char *buf, char *text) +{ + char temp[40], *toke1, *toke2, *ptr, *orig_buf; + int r; + + orig_buf = buf; + *buf = 0; + toke1 = strchr(text, '$'); + toke2 = strchr(text, '#'); + while (toke1 || toke2) { // is either token types present? + if (!toke2 || (toke1 && (toke1 < toke2))) { // found $ before # + strncpy(buf, text, toke1 - text + 1); // copy text up to token + buf += toke1 - text + 1; + text = toke1 + 1; // advance pointers past processed data + + toke2 = strchr(text, '$'); + if (!toke2) // No second one? + break; + + strncpy(temp, text, toke2 - text); // isolate token into seperate buffer + temp[toke2 - text] = 0; // null terminate string + ptr = translate_key(temp); // try and translate key + if (ptr) { // was key translated properly? + if (!stricmp(ptr, NOX("none")) && (Training_bind_warning != Missiontime)) { + if ( The_mission.game_type & MISSION_TYPE_TRAINING ) { + r = popup(PF_TITLE_BIG | PF_TITLE_RED, 2, XSTR( "&Bind Control", 424), XSTR( "&Abort mission", 425), + XSTR( "Warning\nYou have no control bound to the action \"%s\". You must do so before you can continue with your training.", 426), + XSTR(Control_config[Failed_key_index].text, CONTROL_CONFIG_XSTR + Failed_key_index)); + + if (r) { // do they want to abort the mission? + gameseq_post_event(GS_EVENT_END_GAME); + return; + } + + gameseq_post_event(GS_EVENT_CONTROL_CONFIG); // goto control config screen to bind the control + } + } + + buf--; // erase the $ + strcpy(buf, ptr); // put translated key in place of token + buf += strlen(buf); + text = toke2 + 1; + } + + } else { + strncpy(buf, text, toke2 - text + 1); // copy text up to token + buf += toke2 - text + 1; + text = toke2 + 1; // advance pointers past processed data + + toke1 = strchr(text, '#'); + if (toke1) // No second one? + break; + + strncpy(temp, text, toke1 - text); // isolate token into seperate buffer + temp[toke1 - text] = 0; // null terminate string + ptr = translate_msg_token(temp); // try and translate key + if (ptr) { // was key translated properly? + buf--; // erase the # + strcpy(buf, ptr); // put translated key in place of token + buf += strlen(buf); + text = toke1 + 1; + } + } + + toke1 = strchr(text, '$'); + toke2 = strchr(text, '#'); + } + + strcpy(buf, text); + return; +} + +// plays the voice file associated with a training message. Automatically streams the file +// from disk if it's over 100k, otherwise plays it as a normal file in memory. Returns -1 +// if it didn't play, otherwise index of voice +int message_play_training_voice(int index) +{ + int len; + CFILE *fp; + + if (index < 0) { + if (Training_voice >= 0) { + if (Training_voice_type) { + audiostream_close_file(Training_voice_handle, 0); + + } else { + snd_stop(Training_voice_handle); + } + } + + Training_voice = -1; + return -1; + } + + if (Message_waves[index].num < 0) { + fp = cfopen(Message_waves[index].name, "rb"); + if (!fp) + return -1; + + len = cfilelength(fp); + cfclose(fp); + if (len > 100000) { + if ((Training_voice < 0) || !Training_voice_type || (Training_voice != index)) { + if (Training_voice >= 0) { + if (Training_voice_type) { + if (Training_voice == index) + audiostream_stop(Training_voice_handle); + else + audiostream_close_file(Training_voice_handle, 0); + + } else { + snd_stop(Training_voice_handle); + } + } + + if (stricmp(Message_waves[index].name, NOX("none.wav"))) { + Training_voice_handle = audiostream_open(Message_waves[index].name, ASF_VOICE); + if (Training_voice_handle < 0) { + nprintf(("Warning", "Unable to load voice file %s\n", Message_waves[index].name)); + // Warning(LOCATION, "Unable to load voice file %s\n", Message_waves[index].name); + } + } + } // Training_voice should be valid and loaded now + + Training_voice_type = 1; + if (Training_voice_handle >= 0) + audiostream_play(Training_voice_handle, Master_voice_volume, 0); + + Training_voice = index; + return Training_voice; + + } else { + game_snd tmp_gs; + memset(&tmp_gs, 0, sizeof(game_snd)); + strcpy(tmp_gs.filename, Message_waves[index].name); + Message_waves[index].num = snd_load(&tmp_gs); + if (Message_waves[index].num < 0) { + nprintf(("Warning", "Cannot load message wave: %s. Will not play\n", Message_waves[index].name)); + return -1; + } + } + } + + if (Training_voice >= 0) { + if (Training_voice_type) { + audiostream_close_file(Training_voice_handle, 0); + + } else { + snd_stop(Training_voice_handle); + } + } + + Training_voice = index; + if (Message_waves[index].num >= 0) + Training_voice_handle = snd_play_raw(Message_waves[index].num, 0.0f); + else + Training_voice_handle = -1; + + Training_voice_type = 0; + return Training_voice; +} + +// one time initializations done when we want to display a new training mission. This does +// all the processing and setup required to actually display it, including starting the +// voice file playing +void message_training_setup(int m, int length) +{ + if ((m < 0) || !Messages[m].message[0]) { // remove current message from the screen + Training_num_lines = 0; + return; + } + + message_translate_tokens(Training_buf, Messages[m].message); + HUD_add_to_scrollback(Training_buf, HUD_SOURCE_TRAINING); + strcpy(Training_text, Messages[m].message); + + if (message_play_training_voice(Messages[m].wave_info.index) < 0) { + if (length > 0) + Training_msg_timestamp = timestamp(length * 1000); + else + Training_msg_timestamp = timestamp(TRAINING_TIMING_BASE + strlen(Messages[m].message) * TRAINING_TIMING); // no voice file playing + + } else + Training_msg_timestamp = 0; +} + +// adds simple text to the directives display +/*id message_training_add_simple( char *text ) +{ + int i; + + training_process_msg(text); + HUD_add_to_scrollback(Training_buf, HUD_SOURCE_TRAINING); + Training_num_lines = split_str(Training_buf, TRAINING_LINE_WIDTH, Training_line_sizes, Training_lines, MAX_TRAINING_MSG_LINES); + Assert(Training_num_lines > 0); + for (i=0; i= Num_messages) + return; + } + + Training_msg_que[Training_msg_que_count].num = m; + Training_msg_que[Training_msg_que_count].timestamp = timestamp; + Training_msg_que[Training_msg_que_count].length = length; + Training_msg_que_count++; + } +} + +// check the training message que to see if we should play a new message yet or not. +void message_training_que_check() +{ + int i, j, iship_num; + + // get the instructor's ship. + iship_num = ship_name_lookup(NOX("instructor")); + if ( iship_num == -1 ) + return; + + // if the instructor is dying or departing, do nothing + if (Ships[iship_num].flags & (SF_DYING | SF_DEPARTING)) + return; + + if (Training_failure) + return; + + for (i=0; i 0); + for (i=0; i= 0) && (Training_num_lines > 0) && !(Training_msg_timestamp)) { + if (Training_voice_type) + z = audiostream_is_playing(Training_voice_handle); + else + z = snd_is_playing(Training_voice_handle); + + if (!z) + Training_msg_timestamp = timestamp(2000); // 2 second delay + } +} + +// processes a new training message to get hilighting information and store it in internal structures. +void training_process_msg(char *msg) +{ + int count; + char *src, *dest, buf[8192]; + + message_translate_tokens(buf, msg); + count = 0; + src = buf; + dest = Training_buf; + while (*src) { + if (!strnicmp(src, NOX(""), 3)) { + Assert(count < MAX_TRAINING_MSG_MODS); + src += 3; + Training_msg_mods[count].pos = dest; + Training_msg_mods[count].mode = TMMOD_BOLD; + count++; + } + + if (!strnicmp(src, NOX(""), 4)) { + Assert(count < MAX_TRAINING_MSG_MODS); + src += 4; + Training_msg_mods[count].pos = dest; + Training_msg_mods[count].mode = TMMOD_NORMAL; + count++; + } + + *dest++ = *src++; + } + + *dest = 0; + if (count < MAX_TRAINING_MSG_MODS) + Training_msg_mods[count].pos = NULL; +} + +void training_fail() +{ + Training_failure = 1; + // JasonH: Add code here to suspend training and display a directive to warp out. + // Suspend training. + // Give directive to warp out. + // Also ensure that a special failure debriefing is given. Must mention firing at instructor. + // Ask Sandeep to write it (or you can). +} diff --git a/src/missionui/chatbox.cpp b/src/missionui/chatbox.cpp new file mode 100644 index 0000000..79299c6 --- /dev/null +++ b/src/missionui/chatbox.cpp @@ -0,0 +1,1388 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/Chatbox.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module to handle all code for multiplayer chat windows + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 12 8/05/99 4:05p Jefff + * fixed pause chatbox display coords + * + * 11 7/29/99 10:47p Dave + * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs. + * + * 10 5/22/99 5:35p Dave + * Debrief and chatbox screens. Fixed small hi-res HUD bug. + * + * 9 2/24/99 2:25p Dave + * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn + * bug for dogfight more. + * + * 8 2/23/99 8:11p Dave + * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing. + * Small pass over todolist items. + * + * 7 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 6 2/01/99 5:55p Dave + * Removed the idea of explicit bitmaps for buttons. Fixed text + * highlighting for disabled gadgets. + * + * 5 1/29/99 2:08a Dave + * Fixed beam weapon collisions with players. Reduced size of scoring + * struct for multiplayer. Disabled PXO. + * + * 4 1/28/99 7:09p Neilk + * Modified chatbox to use new interface graphics (only 640) + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 57 9/17/98 3:08p Dave + * PXO to non-pxo game warning popup. Player icon stuff in create and join + * game screens. Upped server count refresh time in PXO to 35 secs (from + * 20). + * + * 56 9/11/98 5:08p Dave + * More tweaks to kick notification system. + * + * 55 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 54 6/09/98 5:15p Lawrance + * French/German localization + * + * 53 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 52 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 51 5/22/98 9:35p Dave + * Put in channel based support for PXO. Put in "shutdown" button for + * standalone. UI tweaks for TvT + * + * 50 5/17/98 6:32p Dave + * Make sure clients/servers aren't kicked out of the debriefing when team + * captains leave a game. Fixed chatbox off-by-one error. Fixed image + * xfer/pilot info popup stuff. + * + * 49 5/17/98 1:43a Dave + * Eradicated chatbox problems. Remove speed match for observers. Put in + * help screens for PXO. Fix messaging and end mission privelges. Fixed + * team select screen bugs. Misc UI fixes. + * + * 48 5/15/98 9:52p Dave + * Added new stats for freespace. Put in artwork for viewing stats on PXO. + * + * 47 5/15/98 5:15p Dave + * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy + * status for team vs. team. Put in asserts to check for invalid team vs. + * team situations. + * + * 46 5/08/98 7:08p Dave + * Lots of UI tweaking. + * + * 45 5/07/98 6:26p Dave + * Fix strange boundary conditions which arise when players die/respawn + * while the game is being ended. Spiff up the chatbox doskey thing a bit. + * + * 44 5/04/98 10:39p Dave + * Put in endgame sequencing. Need to check campaign situations. + * Realigned ship info on team select screen. + * + * 43 5/02/98 5:38p Dave + * Put in new tracker API code. Put in ship information on mp team select + * screen. Make standalone server name permanent. Fixed standalone server + * text messages. + * + * 42 4/29/98 6:00p Dave + * Fixed chatbox font colors. Made observer offscreen indicators work. + * Numerous small UI fixes. Fix rank limitations for mp games. + * + * 41 4/25/98 3:14p Dave + * Fixed chatbox clipping problem. + * + * 40 4/16/98 6:34p Dave + * Fixed reversed team vs. team colors. + * + * 39 4/14/98 5:06p Dave + * Don't load or send invalid pilot pics. Fixed chatbox graphic errors. + * Made chatbox display team icons in a team vs. team game. Fixed up pause + * and endgame sequencing issues. + * + * 38 4/13/98 7:48p Dave + * Fixed chatbox overrun errors. Put in line recall function (4 deep). + * + * 37 4/12/98 2:09p Dave + * Make main hall door text less stupid. Make sure inputbox focus in the + * multi host options screen is managed more intelligently. + * + * 36 4/01/98 11:19p Dave + * Put in auto-loading of xferred pilot pic files. Grey out background + * behind pinfo popup. Put a chatbox message in when players are kicked. + * Moved mission title down in briefing. Other ui fixes. + * + * 35 3/31/98 4:42p Allender + * mission objective support for team v. team mode. Chatbox changes to + * make input box be correct length when typing + * + * 34 3/29/98 1:24p Dave + * Make chatbox not clear between multiplayer screens. Select player ship + * as default in mp team select and weapons select screens. Made create + * game mission list use 2 fixed size columns. + * + * 33 3/19/98 5:05p Dave + * Put in support for targeted multiplayer text and voice messaging (all, + * friendly, hostile, individual). + * + * 32 3/18/98 12:03p John + * Marked all the new strings as externalized or not. + * + * 31 3/17/98 12:30a Dave + * Put in hud support for rtvoice. Several ui interface changes. + * + * 30 3/10/98 10:59p Dave + * Fixed single player pause screen. Put in temporary fix for multiplayer + * version. Fixed several chatbox display and text string bugs. + * + * 29 2/27/98 9:41a Dave + * Made "up/down" arrows flip direction when toggling chatbox sizes. + * + * 28 2/26/98 4:21p Dave + * More robust multiplayer voice. + * + * 27 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 26 2/22/98 12:19p John + * Externalized some strings + * + * 25 2/13/98 3:46p Dave + * Put in dynamic chatbox sizing. Made multiplayer file lookups use cfile + * functions. + * + * 24 2/04/98 10:50p Allender + * zero out chat line before storing text + * + * 23 1/29/98 5:23p Dave + * Made ingame join handle bad packets gracefully. + * + * 22 1/23/98 5:43p Dave + * Finished bringing standalone up to speed. Coded in new host options + * screen. + * + * 21 1/17/98 5:51p Dave + * Bug fixes for bugs generated by multiplayer testing. + * + * 20 1/16/98 5:23p Allender + * more chatbox changes + * + * 19 1/16/98 4:28p Allender + * automatically send line when close to end of chatbox. + * + * 18 1/16/98 2:34p Dave + * Made pause screen work properly (multiplayer). Changed how chat packets + * work. + * + * 17 1/15/98 6:12p Dave + * Fixed weapons loadout bugs with multiplayer respawning. Added + * multiplayer start screen. Fixed a few chatbox bugs. + * + * 16 1/15/98 5:10p Allender + * ton of interface changes. chatbox in multiplayer now behaves + * differently than before. It's always active in any screen that uses + * it. Only non-printatble characters will get passed back out from + * chatbox + * + * 15 1/12/98 5:17p Dave + * Put in a bunch of multiplayer sequencing code. Made weapon/ship select + * work through the standalone. + * + * 14 1/07/98 5:20p Dave + * Put in support for multiplayer campaigns with the new interface + * screens. + * + * 13 1/05/98 5:06p Dave + * Fixed a chat packet bug. Fixed a few state save/restore bugs. Updated a + * few things for multiplayer server transfer. + * + * 12 12/29/97 3:15p Dave + * Put in auto-indenting for multiplayer chatbox. Tweaked some multiplayer + * interface code. + * + * 11 12/22/97 9:13p Allender + * fixed up a few minor issues with chatbox + * + * 10 12/19/97 7:08p Jasen + * Updated coords for new ChatBox (small) + * + * 9 12/18/97 8:59p Dave + * Finished putting in basic support for weapon select and ship select in + * multiplayer. + * + * 8 12/06/97 4:27p Dave + * Another load of interface and multiplayer bug fixes. + * + * 7 12/03/97 4:16p Hoffoss + * Changed sound stuff used in interface screens for interface purposes. + * + * 6 11/12/97 4:40p Dave + * Put in multiplayer campaign support parsing, loading and saving. Made + * command-line variables better named. Changed some things on the initial + * pilot select screen. + * + * 5 10/24/97 10:59p Hoffoss + * Added in create pilot popup window and barracks screen. + * + * 4 10/08/97 5:08p Lawrance + * use mask region for chat box inputbox, so getting focus is easier + * + * 3 10/01/97 4:47p Lawrance + * move some #defines out of header file into .cpp file + * + * 2 10/01/97 4:39p Lawrance + * move chat code into Chatbox.cpp, simplify interface + * + * 1 10/01/97 10:54a Lawrance + * + * $NoKeywords: $ + */ + +#include "ui.h" +#include "mouse.h" +#include "freespace.h" +#include "chatbox.h" +#include "missionscreencommon.h" +#include "multimsgs.h" +#include "gamesnd.h" +#include "bmpman.h" +#include "cmdline.h" +#include "key.h" +#include "multi.h" +#include "multiui.h" +#include "multi_pmsg.h" +#include "alphacolors.h" + +/////////////////////////////////////////////////////////// +// Chat window UI +/////////////////////////////////////////////////////////// + +// a little extra spacing for the team vs. team icons +#define CHATBOX_TEAM_ICON_SPACE 18 + +// SMALL CHATBOX ---------------------------------------------------------------------------------- + +// background bitmap +char* Chatbox_small_bitmap_fname[GR_NUM_RESOLUTIONS] = { + "Chatbox", // GR_640 + "2_Chatbox" // GR_1024 +}; + +// background mask +char* Chatbox_small_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = { + "Chatbox-m", // GR_640 + "2_Chatbox-m" // GR_1024 +}; + +// chatbox coords +int Chatbox_small_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 192, 0 + }, + { // GR_1024 + 308, 0 // ? + } +}; + +// display area coods +int Chatbox_small_display_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 196 + CHATBOX_TEAM_ICON_SPACE, 13, 410 - CHATBOX_TEAM_ICON_SPACE, 74 + }, + { // GR_1024 + 315 + CHATBOX_TEAM_ICON_SPACE, 22, 654 - CHATBOX_TEAM_ICON_SPACE, 116 + } +}; + +// input box coords +int Chatbox_small_input_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 204, 100, 371, 22 + }, + { // GR_1024 + 328, 163, 591, 34 + } +}; + +// max # of lines +int Chatbox_small_max_lines[GR_NUM_RESOLUTIONS] = { + 7, // GR_640 + 12 // GR_1024 +}; + +// BIG CHATBOX ---------------------------------------------------------------------------------- + +// background bitmap +char* Chatbox_big_bitmap_fname[GR_NUM_RESOLUTIONS] = { + "ChatboxBig", // GR_640 + "2_ChatboxBig" // GR_1024 +}; + +// mask +char* Chatbox_big_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = { + "Chatbox-m", // GR_640 + "2_Chatbox-m" // GR_1024 +}; + +// chatbox coords +int Chatbox_big_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 192, 0 + }, + { // GR_1024 + 307, 0 + } +}; + +// display area coords +int Chatbox_big_display_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 196 + CHATBOX_TEAM_ICON_SPACE, 13, 410 - CHATBOX_TEAM_ICON_SPACE, 326 + }, + { // GR_1024 + 315 + CHATBOX_TEAM_ICON_SPACE, 22, 654 - CHATBOX_TEAM_ICON_SPACE, 519 + } +}; + +// input box coords +int Chatbox_big_input_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 204, 352, 371, 22 + }, + { // GR_1024 + 328, 565, 591, 34 + } +}; + +// max # of lines +int Chatbox_big_max_lines[GR_NUM_RESOLUTIONS] = { + 32, // GR_640 + 51 // GR_1024 +}; + +// PAUSED CHATBOX ---------------------------------------------------------------------------------- + +// mask +char* Chatbox_p_bitmap_fname[GR_NUM_RESOLUTIONS] = { + "MPPause", // GR_640 + "2_MPPause" // GR_1024 +}; + +// mask +char* Chatbox_p_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = { + "MPPause-m", // GR_640 + "2_MPPause-m" // GR_1024 +}; + +// chatbox coords +int Chatbox_p_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 192, 0 + }, + { // GR_1024 + 307, 0 + } +}; + +// display area coords +int Chatbox_p_display_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 103 + CHATBOX_TEAM_ICON_SPACE, 149, 380 - CHATBOX_TEAM_ICON_SPACE, 143 + }, + { // GR_1024 + 165 + CHATBOX_TEAM_ICON_SPACE, 244, 654 - CHATBOX_TEAM_ICON_SPACE, 263 + } +}; + +// input box coords +int Chatbox_p_input_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 106, 328, 379, 17 + }, + { // GR_1024 + 165, 525, 607, 27 + } +}; + +// max # of lines +int Chatbox_p_max_lines[GR_NUM_RESOLUTIONS] = { + 17, // GR_640 + 26 // GR_1024 +}; + +// defines for location and other info of chat box MULTI_PAUSED +/* +#define CHATBOX_MP_FNAME NOX("MPPause") +#define CHATBOX_MP_MASK NOX("MPPause-m") +#define CHATBOX_MP_X1 112 +#define CHATBOX_MP_Y1 198 +#define CHATBOX_MP_X2 477 +#define CHATBOX_MP_Y2 308 +#define CHATBOX_MP_ICON_X (CHATBOX_MP_X1) +#define CHATBOX_MP_W (CHATBOX_MP_X2 - CHATBOX_MP_X1 + 1) +#define CHATBOX_MP_H (CHATBOX_MP_Y2 - CHATBOX_MP_Y1 + 1) +#define CHATBOX_MP_BEGIN_X (CHATBOX_MP_X1 + 3 + CHATBOX_TEAM_ICON_SPACE) +#define CHATBOX_MP_BEGIN_Y CHATBOX_MP_Y1 +#define CHATBOX_MP_DISP_W 365 +#define CHATBOX_MP_MAX_LINES 11 +#define CHATBOX_MP_INPUTBOX_X (CHATBOX_MP_X1 + 3) +#define CHATBOX_MP_INPUTBOX_W (CHATBOX_MP_W) +#define CHATBOX_MP_TEXTENTER_Y 329 +*/ + +// CHATBOX ---------------------------------------------------------------------------------- + +// the settings being used for this instance +char Chatbox_mask[50]; +int Chatbox_x1; +int Chatbox_y1; +int Chatbox_icon_x; +int Chatbox_w; +int Chatbox_h; +int Chatbox_begin_x; +int Chatbox_begin_y; +int Chatbox_disp_w; +int Chatbox_max_lines; +int Chatbox_inputbox_x; +int Chatbox_inputbox_w; +int Chatbox_textenter_y; +int Chat_scroll_up_coord[2]; +int Chat_scroll_down_coord[2]; + +// how many pixels to indent succesive lines of text from a given player +#define CHAT_LINE_INDENT 20 + +// what chars other than letters and number's we'll toss +#define CHATBOX_INVALID_CHARS NOX("~`") // this is primarily so that we don't interfere with the voice recording keys + +// common defines and data +#define CHATBOX_STRING_LEN (CALLSIGN_LEN + CHATBOX_MAX_LEN + 32) +UI_WINDOW Chat_window; +UI_INPUTBOX Chat_inputbox; +UI_BUTTON Chat_enter_text; + +// button controls +#define CHATBOX_NUM_BUTTONS 3 +#define CHATBOX_SCROLL_UP 0 +#define CHATBOX_SCROLL_DOWN 1 +#define CHATBOX_TOGGLE_SIZE 2 // used for both big and small + +// coordinate indicies +#define CHATBOX_X_COORD 0 +#define CHATBOX_Y_COORD 1 +#define CHATBOX_W_COORD 2 +#define CHATBOX_H_COORD 3 + +// chatbox buttons +ui_button_info Chatbox_buttons[GR_NUM_RESOLUTIONS][CHATBOX_NUM_BUTTONS+1] = { + { // GR_640 + ui_button_info("CHB_00", 613, 3, -1, -1, 0), + ui_button_info("CHB_01", 613, 41, -1, -1, 1), + ui_button_info("CHB_02a", 607, 74, -1, -1, 2), + ui_button_info("CHB_02b", 607, 74, -1, -1, 2), + }, + { // GR_1024 + ui_button_info("2_CHB_00", 981, 5, -1, -1, 0), + ui_button_info("2_CHB_01", 981, 67, -1, -1, 1), + ui_button_info("2_CHB_02a", 971, 119, -1, -1, 2), + ui_button_info("2_CHB_02b", 971, 119, -1, -1, 2), + } +}; + +int Chatbox_mode_flags = 0; + +int Chatbox_bitmap = -1; +int Chatbox_big_bitmap = -1; +int Chatbox_small_bitmap = -1; +int Chatbox_mp_bitmap = -1; +int Chatbox_created = 0; + +/////////////////////////////////////////////////////////// +// Chat window text +/////////////////////////////////////////////////////////// +#define MAX_BRIEF_CHAT_LINES 60 // how many lines we can store in the scrollback buffer +#define BRIEF_DISPLAY_SPACING 2 // pixel spacing between chat lines + +// the first byte of the text string will be the net player id of the +char Brief_chat_lines[MAX_BRIEF_CHAT_LINES][CHATBOX_STRING_LEN]; +int Brief_chat_indents[MAX_BRIEF_CHAT_LINES]; + +int Brief_chat_next_index[MAX_BRIEF_CHAT_LINES]; +int Brief_chat_prev_index[MAX_BRIEF_CHAT_LINES]; +int Num_brief_chat_lines; +int Brief_current_add_line; +int Brief_start_display_index; + +// chatbox line recall data +#define CHATBOX_MAX_RECALL_LINES 10 +int Chatbox_recall_count = 0; +int Chatbox_recall_index = 0; +int Chatbox_recall_last = -1; +char Chatbox_recall_lines[CHATBOX_MAX_RECALL_LINES][CHATBOX_MAX_LEN+2]; + +/////////////////////////////////////////////////////////// +// forward declarations +/////////////////////////////////////////////////////////// +void chatbox_chat_init(); +void chatbox_render_chat_lines(); +void chatbox_set_mode(int mode_flags); +void chatbox_toggle_size(); +void chatbox_toggle_size_adjust_lines(); +void chat_autosplit_line(char *msg,char *remainder); +int chatbox_num_displayed_lines(); +void chatbox_recall_add(char *string); +void chatbox_recall_up(); +void chatbox_recall_down(); + +// set the chatbox mode without checking for any previous modes which may need to be handled specially +void chatbox_set_mode(int mode_flags) +{ + int size; + + // set the stored mode + Chatbox_mode_flags = mode_flags; + + // small pregame chatbox + if(Chatbox_mode_flags & CHATBOX_FLAG_SMALL){ + size = 0; + } + // big pregame chatbox + else if(Chatbox_mode_flags & CHATBOX_FLAG_BIG){ + size = 1; + } + // multiplayer paused + else { + size = 2; + } + + // set up the display/init variables based upon what mode we chode + switch(size){ + case 0: + strcpy(Chatbox_mask, Chatbox_small_bitmap_mask_fname[gr_screen.res]); + Chatbox_x1 = Chatbox_small_coords[gr_screen.res][CHATBOX_X_COORD]; + Chatbox_y1 = Chatbox_small_coords[gr_screen.res][CHATBOX_Y_COORD]; + Chatbox_icon_x = Chatbox_small_display_coords[gr_screen.res][CHATBOX_X_COORD] - CHATBOX_TEAM_ICON_SPACE; + Chatbox_w = Chatbox_small_display_coords[gr_screen.res][CHATBOX_W_COORD]; + Chatbox_h = Chatbox_small_display_coords[gr_screen.res][CHATBOX_H_COORD]; + Chatbox_begin_x = Chatbox_small_display_coords[gr_screen.res][CHATBOX_X_COORD]; + Chatbox_begin_y = Chatbox_small_display_coords[gr_screen.res][CHATBOX_Y_COORD]; + Chatbox_disp_w = Chatbox_small_display_coords[gr_screen.res][CHATBOX_W_COORD]; + Chatbox_max_lines = Chatbox_small_max_lines[gr_screen.res]; + Chatbox_inputbox_x = Chatbox_small_input_coords[gr_screen.res][CHATBOX_X_COORD]; + Chatbox_inputbox_w = Chatbox_small_input_coords[gr_screen.res][CHATBOX_W_COORD]; + Chatbox_textenter_y = Chatbox_small_input_coords[gr_screen.res][CHATBOX_Y_COORD]; + break; + + case 1: + strcpy(Chatbox_mask, Chatbox_big_bitmap_mask_fname[gr_screen.res]); + Chatbox_x1 = Chatbox_big_coords[gr_screen.res][CHATBOX_X_COORD]; + Chatbox_y1 = Chatbox_big_coords[gr_screen.res][CHATBOX_Y_COORD]; + Chatbox_icon_x = Chatbox_big_display_coords[gr_screen.res][CHATBOX_X_COORD] - CHATBOX_TEAM_ICON_SPACE; + Chatbox_w = Chatbox_big_display_coords[gr_screen.res][CHATBOX_W_COORD]; + Chatbox_h = Chatbox_big_display_coords[gr_screen.res][CHATBOX_H_COORD]; + Chatbox_begin_x = Chatbox_big_display_coords[gr_screen.res][CHATBOX_X_COORD]; + Chatbox_begin_y = Chatbox_big_display_coords[gr_screen.res][CHATBOX_Y_COORD]; + Chatbox_disp_w = Chatbox_big_display_coords[gr_screen.res][CHATBOX_W_COORD]; + Chatbox_max_lines = Chatbox_big_max_lines[gr_screen.res]; + Chatbox_inputbox_x = Chatbox_big_input_coords[gr_screen.res][CHATBOX_X_COORD]; + Chatbox_inputbox_w = Chatbox_big_input_coords[gr_screen.res][CHATBOX_W_COORD]; + Chatbox_textenter_y = Chatbox_big_input_coords[gr_screen.res][CHATBOX_Y_COORD]; + break; + + case 2: + Chatbox_x1 = Chatbox_p_coords[gr_screen.res][CHATBOX_X_COORD]; + Chatbox_y1 = Chatbox_p_coords[gr_screen.res][CHATBOX_Y_COORD]; + Chatbox_icon_x = Chatbox_p_display_coords[gr_screen.res][CHATBOX_X_COORD] - CHATBOX_TEAM_ICON_SPACE; + Chatbox_w = Chatbox_p_display_coords[gr_screen.res][CHATBOX_W_COORD]; + Chatbox_h = Chatbox_p_display_coords[gr_screen.res][CHATBOX_H_COORD]; + Chatbox_begin_x = Chatbox_p_display_coords[gr_screen.res][CHATBOX_X_COORD]; + Chatbox_begin_y = Chatbox_p_display_coords[gr_screen.res][CHATBOX_Y_COORD]; + Chatbox_disp_w = Chatbox_p_display_coords[gr_screen.res][CHATBOX_W_COORD]; + Chatbox_max_lines = Chatbox_p_max_lines[gr_screen.res]; + Chatbox_inputbox_x = Chatbox_p_input_coords[gr_screen.res][CHATBOX_X_COORD]; + Chatbox_inputbox_w = Chatbox_p_input_coords[gr_screen.res][CHATBOX_W_COORD]; + Chatbox_textenter_y = Chatbox_p_input_coords[gr_screen.res][CHATBOX_Y_COORD]; + break; + } +} + +// automatically split up any input text, send it, and leave the remainder +void chatbox_autosplit_line() +{ + char *remainder,msg[150]; + int msg_pixel_width; + + // if the chat line is getting too long, fire off the message, putting the last + // word on the next input line. + memset(msg,0,150); + Chat_inputbox.get_text(msg); + remainder = ""; + // determine if the width of the string in pixels is > than the inputbox width -- if so, + // then send the message + gr_get_string_size(&msg_pixel_width, NULL, msg); + // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) { + if ( msg_pixel_width >= (Chatbox_inputbox_w - 25)) { + remainder = strrchr(msg, ' '); + if ( remainder ) { + *remainder = '\0'; + remainder++; + } else { + remainder = ""; + } + // if I'm the server, then broadcast the packet + chatbox_recall_add(msg); + send_game_chat_packet(Net_player, msg, MULTI_MSG_ALL,NULL); + chatbox_add_line(msg, MY_NET_PLAYER_NUM); + + // display any remainder of text on the next line + Chat_inputbox.set_text(remainder); + } else if((Chat_inputbox.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= CHATBOX_MAX_LEN)) { + // tack on the null terminator in the boundary case + int x = strlen(msg); + if(x >= CHATBOX_MAX_LEN){ + msg[CHATBOX_MAX_LEN-1] = '\0'; + } + // if I'm the server, then broadcast the packet + chatbox_recall_add(msg); + send_game_chat_packet(Net_player, msg, MULTI_MSG_ALL,NULL); + chatbox_add_line(msg, MY_NET_PLAYER_NUM); + + // display any remainder of text on the next line + Chat_inputbox.set_text(remainder); + } +} + +// initialize all chatbox details with the given mode flags +int chatbox_create(int mode_flags) +{ + int idx; + + // don't do anything if the chatbox is already initialized + if (Chatbox_created){ + return -1; + } + + // probably shouldn't be using the chatbox in single player mode + Assert(Game_mode & GM_MULTIPLAYER); + + // setup all data to correspond to our mode flags + chatbox_set_mode(mode_flags); + + // initialize all low-level details related to chatting + chatbox_chat_init(); + + // attempt to load in the chatbox background bitmap + if(Chatbox_mode_flags & CHATBOX_FLAG_DRAW_BOX){ + Chatbox_big_bitmap = bm_load(Chatbox_big_bitmap_fname[gr_screen.res]); + Chatbox_small_bitmap = bm_load(Chatbox_small_bitmap_fname[gr_screen.res]); + Chatbox_mp_bitmap = bm_load(Chatbox_p_bitmap_fname[gr_screen.res]); + + if(Chatbox_mode_flags & CHATBOX_FLAG_SMALL){ + Chatbox_bitmap = Chatbox_small_bitmap; + } else if(Chatbox_mode_flags & CHATBOX_FLAG_BIG){ + Chatbox_bitmap = Chatbox_big_bitmap; + } else { + Chatbox_bitmap = Chatbox_mp_bitmap; + } + + if((Chatbox_bitmap == -1) || (Chatbox_small_bitmap == -1) || (Chatbox_big_bitmap == -1) || (Chatbox_mp_bitmap == -1)){ + return -1; + } + } + + // attempt to create the ui window for the chatbox and assign the mask + Chat_window.create( 0, 0, gr_screen.max_w, gr_screen.max_h, 0 ); + Chat_window.set_mask_bmap(Chatbox_mask); + + // create the chat text enter input area + Chat_inputbox.create( &Chat_window, Chatbox_inputbox_x, Chatbox_textenter_y, Chatbox_inputbox_w, CHATBOX_MAX_LEN, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_EAT_USED, Chatbox_w, Color_netplayer[MY_NET_PLAYER_NUM]); + Chat_inputbox.set_focus(); + Chat_inputbox.set_invalid_chars(CHATBOX_INVALID_CHARS); + + // if we're supposed to supply and check for out own buttons + if((Chatbox_mode_flags & CHATBOX_FLAG_BUTTONS) && (Chatbox_mode_flags & CHATBOX_FLAG_DRAW_BOX)){ + for(idx=0; idx Chatbox_max_lines){ + int prev = Brief_chat_prev_index[Brief_start_display_index]; + + // check to make sure we won't be going "up" above the "beginning" of the text array + if(Brief_chat_prev_index[Brief_current_add_line] != prev && !(Num_brief_chat_lines < MAX_BRIEF_CHAT_LINES && Brief_start_display_index==0)){ + Brief_start_display_index = prev; + return 1; + } + + } + return 0; +} + +// try and scroll the chatbox down, return 0 or 1 on fail or success +int chatbox_scroll_down() +{ + int idx; + int next; + + if(Num_brief_chat_lines > Chatbox_max_lines){ + // yuck. There's got to be a better way to do this. + next = Brief_chat_next_index[Brief_start_display_index]; + for(idx = 1;idx <= Chatbox_max_lines; idx++){ + next = Brief_chat_next_index[next]; + } + + // check to make sure we won't be going "down" below the "bottom" of the text array + if(Brief_chat_next_index[Brief_current_add_line] != next){ + Brief_start_display_index = Brief_chat_next_index[Brief_start_display_index]; + return 1; + } + } + return 0; +} + +// quick explanation as to how the scrolling works : +// Brief_chat_next_index is an array A of size n, where A[i] = i+1 and A[n] = 0 +// Brief_chat_prev_index is an array A of size n, where A[i] = i-1 and A[0] = n +// in other words, if you increment an integer i = A[i], you get the next index (or the prev) +// with wrapping. In this way, as chat lines are entered, they are continuously wrapped +// around the Brief_chat_lines array so we can keep it at a fixed size. These arrays are used +// for both entering new chat strings as well as moving the Brief_start_display_index +// integer, which is self-explanatory + +void chatbox_chat_init() +{ + int idx; + + chatbox_clear(); + + // setup the wraparound arrays + for(idx=0;idx 0 ; idx--){ + Brief_chat_prev_index[idx] = idx - 1; + } + Brief_chat_prev_index[0] = MAX_BRIEF_CHAT_LINES-1; + + // initialize the line recall data + Chatbox_recall_count = 0; + Chatbox_recall_index = 0; + Chatbox_recall_last = -1; + memset(Chatbox_recall_lines,0,CHATBOX_MAX_RECALL_LINES * (CHATBOX_MAX_LEN + 2)); +} + +// int Test_color = 0; +void chatbox_add_line(char *msg, int pid, int add_id) +{ + int backup; + int n_lines,idx; + int n_chars[3]; + char *p_str[3]; // for the initial line (unindented) + char msg_extra[CHATBOX_STRING_LEN]; + + if(!Chatbox_created){ + return; + } + + // maybe stick on who sent the message + if(add_id){ + if(MULTI_STANDALONE(Net_players[pid])){ + sprintf(msg_extra, NOX("%s %s"), NOX(""), msg ); + } else { + sprintf(msg_extra, NOX("%s: %s"), Net_players[pid].player->short_callsign, msg ); + } + } else { + strcpy(msg_extra,msg); + } + Assert(strlen(msg_extra) < (CHATBOX_STRING_LEN - 2)); + + // split the text up into as many lines as necessary + n_lines = split_str(msg_extra, Chatbox_disp_w, n_chars, p_str, 3); + Assert(n_lines != -1); + + // setup the first line -- be sure to clear out the line + memset( Brief_chat_lines[Brief_current_add_line], 0, CHATBOX_STRING_LEN ); + + // add the player id # + Brief_chat_lines[Brief_current_add_line][0] = (char)(pid % 16); + // Brief_chat_lines[Brief_current_add_line][0] = (char)Test_color; + // Test_color = (Test_color == MAX_PLAYERS - 1) ? 0 : Test_color++; + + // set the indent to 0 + Brief_chat_indents[Brief_current_add_line] = 0; + + // copy in the chars + strncpy(&Brief_chat_lines[Brief_current_add_line][1],p_str[0],CHATBOX_STRING_LEN - 1); + if(n_chars[0] >= CHATBOX_STRING_LEN){ + Brief_chat_lines[Brief_current_add_line][CHATBOX_STRING_LEN - 1] = '\0'; + } else { + Brief_chat_lines[Brief_current_add_line][n_chars[0] + 1] = '\0'; + } + + // increment the total line count if we haven't reached the max already + if(Num_brief_chat_lines 1){ + // split up the string after the first break-marker + n_lines = split_str(msg_extra + n_chars[0],Chatbox_disp_w - CHAT_LINE_INDENT,n_chars,p_str,3); + Assert(n_lines != -1); + + // setup these remaining lines + for(idx=0;idx= CHATBOX_STRING_LEN){ + Brief_chat_lines[Brief_current_add_line][CHATBOX_STRING_LEN - 1] = '\0'; + } else { + Brief_chat_lines[Brief_current_add_line][n_chars[idx] + 1] = '\0'; + } + + // increment the total line count if we haven't reached the max already + if(Num_brief_chat_lines0){ // stream to the file if we're supposed to + cfwrite_string(msg,Multi_chat_stream); + cfwrite_char('\n',Multi_chat_stream); + } + + // if this line of text is from the player himself, automatically go to the bottom of + // the chat list + if(pid == MY_NET_PLAYER_NUM){ + if(Num_brief_chat_lines > Chatbox_max_lines){ + Brief_start_display_index = Brief_current_add_line; + for(backup = 1;backup <= Chatbox_max_lines;backup++){ + Brief_start_display_index = Brief_chat_prev_index[Brief_start_display_index]; + } + } + } + // if we have enough lines of text to require scrolling, scroll down by one. + else { + chatbox_scroll_down(); + } +} + +void chatbox_render_chat_lines() +{ + int started_at,player_num,count,ly; + + started_at = Brief_start_display_index; + count = 0; + ly = Chatbox_begin_y; + while((count < Chatbox_max_lines) && (count < Num_brief_chat_lines)){ + // determine what player this chat line came from, and set the appropriate text color + player_num = Brief_chat_lines[started_at][0]; + + // print the line out + gr_set_color_fast(Color_netplayer[player_num]); + + // draw the first line (no indent) + gr_string(Chatbox_begin_x + Brief_chat_indents[started_at],ly,&Brief_chat_lines[started_at][1]); + + // if we're in a team vs. team game, blit the player color icon + if(Netgame.type_flags & NG_TYPE_TEAM){ + switch(Net_players[player_num].p_info.team){ + case 0 : + // if he's a team captain + if(Net_players[player_num].flags & NETINFO_FLAG_TEAM_CAPTAIN){ + if(Multi_common_icons[MICON_TEAM0_SELECT] != -1){ + gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]); + gr_bitmap(Chatbox_icon_x,ly-2); + } + } + // just you're average peon + else { + if(Multi_common_icons[MICON_TEAM0] != -1){ + gr_set_bitmap(Multi_common_icons[MICON_TEAM0]); + gr_bitmap(Chatbox_icon_x,ly-2); + } + } + break; + case 1 : + // if he's a team captain + if(Net_players[player_num].flags & NETINFO_FLAG_TEAM_CAPTAIN){ + if(Multi_common_icons[MICON_TEAM1_SELECT] != -1){ + gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]); + gr_bitmap(Chatbox_icon_x,ly-2); + } + } + // just your average peon + else { + if(Multi_common_icons[MICON_TEAM1] != -1){ + gr_set_bitmap(Multi_common_icons[MICON_TEAM1]); + gr_bitmap(Chatbox_icon_x,ly-2); + } + } + break; + } + } + + // increment the count and line position + count++; + ly += 10; + + // increment the started at index + started_at = Brief_chat_next_index[started_at]; + } +} + +void chatbox_toggle_size() +{ + // if we're in "small" mode + if(Chatbox_mode_flags & CHATBOX_FLAG_SMALL){ + chatbox_force_big(); + + // play a sound + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + } + // if we're in "big" mode + else if(Chatbox_mode_flags & CHATBOX_FLAG_BIG){ + chatbox_force_small(); + + // play a sound + gamesnd_play_iface(SND_IFACE_MOUSE_CLICK); + } +} + +void chatbox_toggle_size_adjust_lines() +{ + int count_diff; + + // if the chatbox is now big, move the starting display index _up_ as far as possible + if(Chatbox_mode_flags & CHATBOX_FLAG_BIG){ + // if we've wrapped around or we have more chatlines then we can display, move back as far as we can + if((Num_brief_chat_lines > MAX_BRIEF_CHAT_LINES) || (Num_brief_chat_lines > Chatbox_max_lines)){ + count_diff = Chatbox_max_lines - chatbox_num_displayed_lines(); + while(count_diff > 0){ + Brief_start_display_index = Brief_chat_prev_index[Brief_start_display_index]; + count_diff--; + } + } + // otherwise start displaying from position 0 + else { + Brief_start_display_index = 0; + } + } + // if the chatbox is now small, move the starting display index down as far as we need + else if(Chatbox_mode_flags & CHATBOX_FLAG_SMALL){ + count_diff = chatbox_num_displayed_lines(); + // if we were displaying more lines than we can now + if(count_diff > Chatbox_max_lines){ + count_diff -= Chatbox_max_lines; + while(count_diff > 0){ + Brief_start_display_index = Brief_chat_next_index[Brief_start_display_index]; + count_diff--; + } + } + } +} + +int chatbox_num_displayed_lines() +{ + int idx = Brief_start_display_index; + int count; + + // count the lines up + count = 0; + while(idx != Brief_current_add_line){ + idx = Brief_chat_next_index[idx]; + count++; + } + + return count; +} + +// force the chatbox to go into small mode (if its in large mode) - will not wotk if in multi paused chatbox mode +void chatbox_force_small() +{ + int new_mode_flags; + + // don't do anything unless we're currently in "big" mode + if(!(Chatbox_mode_flags & CHATBOX_FLAG_BIG)){ + return; + } + + new_mode_flags = Chatbox_mode_flags; + + // switch to the appropriate mode + new_mode_flags &= ~(CHATBOX_FLAG_SMALL | CHATBOX_FLAG_BIG); + new_mode_flags |= CHATBOX_FLAG_SMALL; + Chatbox_bitmap = Chatbox_small_bitmap; + + // flip the up/down arrow + Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE].button.set_bmaps(Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE].filename); + + // call this to set everything up correctly + chatbox_set_mode(new_mode_flags); + + // change the location of the input box + Chat_inputbox.update_dimensions(Chatbox_inputbox_x, Chatbox_textenter_y, Chatbox_inputbox_w,15); + Chat_inputbox.set_focus(); + + // adjust what line we start displaying from based upon the new size of the window + chatbox_toggle_size_adjust_lines(); +} + +// force the chatbox to go into big mode (if its in small mode) - will not work if in multi paused chatbox mode +void chatbox_force_big() +{ + int new_mode_flags; + + // don't do anything unless we're currently in "small" mode + if(!(Chatbox_mode_flags & CHATBOX_FLAG_SMALL)){ + return; + } + + new_mode_flags = Chatbox_mode_flags; + + // switch to the appropriate mode + new_mode_flags &= ~(CHATBOX_FLAG_SMALL | CHATBOX_FLAG_BIG); + new_mode_flags |= CHATBOX_FLAG_BIG; + Chatbox_bitmap = Chatbox_big_bitmap; + + // flip the up/down arrow + Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE].button.set_bmaps(Chatbox_buttons[gr_screen.res][CHATBOX_TOGGLE_SIZE+1].filename); + + // call this to set everything up correctly + chatbox_set_mode(new_mode_flags); + + // change the location of the input box + Chat_inputbox.update_dimensions(Chatbox_inputbox_x, Chatbox_textenter_y, Chatbox_inputbox_w,15); + Chat_inputbox.set_focus(); + + // adjust what line we start displaying from based upon the new size of the window + chatbox_toggle_size_adjust_lines(); +} + +// "lose" the focus on the chatbox inputbox +void chatbox_lose_focus() +{ + if(!Chatbox_created){ + return; + } + + // clear the focus on the inputbox + Chat_inputbox.clear_focus(); +} + +// return if the inputbox for the chatbox currently has focus +int chatbox_has_focus() +{ + return Chat_inputbox.has_focus(); +} + +// grab the focus for the chatbox inputbox +void chatbox_set_focus() +{ + Chat_inputbox.set_focus(); +} + +// return if the inputbox was pressed - "clicked on" +int chatbox_pressed() +{ + return Chat_inputbox.pressed(); +} + +// add the string to the line recall list +void chatbox_recall_add(char *string) +{ + int idx; + + // aleays reset the recall index when adding + Chatbox_recall_index = 0; + Chatbox_recall_last = -1; + + // if this string matches the last string we entered, don't add it again + if(!strcmp(string,Chatbox_recall_lines[0])){ + return; + } + + // move all items up (this works fine for small #'s of recall lines + for(idx=CHATBOX_MAX_RECALL_LINES-1;idx > 0;idx--){ + memcpy(&Chatbox_recall_lines[idx],&Chatbox_recall_lines[idx-1],CHATBOX_MAX_LEN+2); + } + + // copy the new item into spot 0 + strcpy(Chatbox_recall_lines[0],string); + + // increment the recall count if necessary + if(Chatbox_recall_count < CHATBOX_MAX_RECALL_LINES){ + Chatbox_recall_count++; + } +} + +// user has pressed the "up" key +void chatbox_recall_up() +{ + // if we've got no recall lines, do nothing + if(Chatbox_recall_count <= 0){ + return; + } + + // if we can increment up + if(Chatbox_recall_index < (Chatbox_recall_count - 1)){ + // if this is the last line we recalled, pre-increment + if(Chatbox_recall_last == Chatbox_recall_index){ + Chat_inputbox.set_text(Chatbox_recall_lines[++Chatbox_recall_index]); + Chatbox_recall_last = Chatbox_recall_index; + } + // otherwise, post increment + else { + Chat_inputbox.set_text(Chatbox_recall_lines[Chatbox_recall_index++]); + Chatbox_recall_last = Chatbox_recall_index - 1; + } + } + // if we can't increment up + else { + Chat_inputbox.set_text(Chatbox_recall_lines[Chatbox_recall_index]); + Chatbox_recall_last = Chatbox_recall_index; + } +} + +// user has pressed the "down" key +void chatbox_recall_down() +{ + // if we've got no recall lines, do nothing + if(Chatbox_recall_count <= 0){ + return; + } + + // if we can decrement down + if(Chatbox_recall_index > 0){ + // if this is the last line we recalled, pre-decrement + if(Chatbox_recall_last == Chatbox_recall_index){ + Chat_inputbox.set_text(Chatbox_recall_lines[--Chatbox_recall_index]); + Chatbox_recall_last = Chatbox_recall_index; + } + // otherwise post,decrement + else { + Chat_inputbox.set_text(Chatbox_recall_lines[Chatbox_recall_index--]); + Chatbox_recall_last = Chatbox_recall_index + 1; + } + } + // if we can't decrement down + else { + Chat_inputbox.set_text(""); + Chatbox_recall_last = -1; + } +} + +// reset all timestamps associated with the chatbox +void chatbox_reset_timestamps() +{ + int idx; + + // if there is no chatbox created, do nothing + if(!Chatbox_created){ + return; + } + + // otherwise clear all timestamps on all the buttons + for(idx=0; idx= Num_brief_stages ) { + Current_brief_stage = Num_brief_stages - 1; + gamesnd_play_iface(SND_GENERAL_FAIL); + if ( Quick_transition_stage != -1 ) + brief_transition_reset(); + } else { + if ( play_sound ) { + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + } + } + + Assert(Current_brief_stage >= 0); +} + +// -------------------------------------------------------------------------------------- +// brief_do_prev_pressed() +// +// +void brief_do_prev_pressed() +{ + Current_brief_stage--; + if ( Current_brief_stage < 0 ) { + Current_brief_stage = 0; + gamesnd_play_iface(SND_GENERAL_FAIL); + if ( Quick_transition_stage != -1 ) + brief_transition_reset(); + } else { + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + } + Assert(Current_brief_stage >= 0); +} + + +// -------------------------------------------------------------------------------------- +// brief_do_start_pressed() +// +// +void brief_do_start_pressed() +{ + if ( Current_brief_stage != 0 ) { + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + Current_brief_stage = 0; + if ( Quick_transition_stage != -1 ) + brief_transition_reset(); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + Assert(Current_brief_stage >= 0); +} + +// -------------------------------------------------------------------------------------- +// brief_do_end_pressed() +// +// +void brief_do_end_pressed() +{ + if ( Current_brief_stage != Num_brief_stages - 1 ) { + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + Current_brief_stage = Num_brief_stages - 1; + if ( Quick_transition_stage != -1 ) + brief_transition_reset(); + + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + Assert(Current_brief_stage >= 0); +} + + +void brief_scroll_up_text() +{ + Top_brief_text_line--; + if ( Top_brief_text_line < 0 ) { + Top_brief_text_line = 0; + gamesnd_play_iface(SND_GENERAL_FAIL); + } else { + gamesnd_play_iface(SND_SCROLL); + } +} + +void brief_scroll_down_text() +{ + Top_brief_text_line++; + if ( (Num_brief_text_lines[0] - Top_brief_text_line) < Brief_text_max_lines[gr_screen.res]) { + Top_brief_text_line--; + gamesnd_play_iface(SND_GENERAL_FAIL); + } else { + gamesnd_play_iface(SND_SCROLL); + } +} + + +// handles the exit loop option +void brief_exit_loop_pressed() +{ + int val = popup(PF_USE_NEGATIVE_ICON | PF_USE_AFFIRMATIVE_ICON, 2, POPUP_NO, POPUP_YES, XSTR( "Exit Loop\n\n\n\nAre you sure you want to leave the mission loop?", 1489)); + + // bail if esc hit or no clicked + if (val <= 0) { + return; + } + + // handle the details + // this also posts the start game event + mission_campaign_exit_loop(); +} + + +// ------------------------------------------------------------------------------------- +// brief_select_button_do() do the button action for the specified pressed button +// +void brief_button_do(int i) +{ + switch ( i ) { + case BRIEF_BUTTON_LAST_STAGE: + brief_do_end_pressed(); + break; + + case BRIEF_BUTTON_NEXT_STAGE: + brief_do_next_pressed(1); + break; + + case BRIEF_BUTTON_PREV_STAGE: + brief_do_prev_pressed(); + break; + + case BRIEF_BUTTON_FIRST_STAGE: + brief_do_start_pressed(); + break; + + case BRIEF_BUTTON_SCROLL_UP: + brief_scroll_up_text(); + break; + + case BRIEF_BUTTON_SCROLL_DOWN: + brief_scroll_down_text(); + break; + + case BRIEF_BUTTON_PAUSE: + gamesnd_play_iface(SND_USER_SELECT); + Player->auto_advance ^= 1; + break; + + case BRIEF_BUTTON_SKIP_TRAINING: + brief_skip_training_pressed(); + break; + + case BRIEF_BUTTON_EXIT_LOOP: + brief_exit_loop_pressed(); + break; + + case BRIEF_BUTTON_MULTI_LOCK: + Assert(Game_mode & GM_MULTIPLAYER); + // the "lock" button has been pressed + multi_ts_lock_pressed(); + + // disable the button if it is now locked + if(multi_ts_is_locked()){ + Brief_buttons[gr_screen.res][BRIEF_BUTTON_MULTI_LOCK].button.disable(); + } + break; + } // end switch +} + +// ------------------------------------------------------------------- +// brief_check_buttons() +// +// Iterate through the briefing buttons, checking if they are pressed +// +void brief_check_buttons() +{ + int i; + UI_BUTTON *b; + + for (i=0; ipressed() ) { + common_flash_button_init(); + brief_button_do(i); + } + } + + if (Closeup_close_button.pressed()) { + brief_turn_off_closeup_icon(); + } +} + +// ------------------------------------------------------------------- +// brief_redraw_pressed_buttons() +// +// Redraw any briefing buttons that are pressed down. This function is needed +// since we sometimes need to draw pressed buttons last to ensure the entire +// button gets drawn (and not overlapped by other buttons) +// +void brief_redraw_pressed_buttons() +{ + int i; + UI_BUTTON *b; + + common_redraw_pressed_buttons(); + + for ( i = 0; i < NUM_BRIEF_BUTTONS; i++ ) { + b = &Brief_buttons[gr_screen.res][i].button; + if ( b->button_down() ) { + b->draw_forced(2); + } + } + + if ( !Player->auto_advance ) { + Brief_buttons[gr_screen.res][BRIEF_BUTTON_PAUSE].button.draw_forced(2); + } +} + +void brief_buttons_init() +{ + UI_BUTTON *b; + int i; + + //if ( Briefing->num_stages <= 0 ) + // return; + + for ( i = 0; i < NUM_BRIEF_BUTTONS; i++ ) { + b = &Brief_buttons[gr_screen.res][i].button; + b->create( &Brief_ui_window, "", Brief_buttons[gr_screen.res][i].x, Brief_buttons[gr_screen.res][i].y, 60, 30, 0, 1 ); + // set up callback for when a mouse first goes over a button + b->set_highlight_action( common_play_highlight_sound ); + + if ((i == BRIEF_BUTTON_SKIP_TRAINING) || (i == BRIEF_BUTTON_EXIT_LOOP)) { + b->set_bmaps(Brief_buttons[gr_screen.res][i].filename, 3, 0); + } else { + b->set_bmaps(Brief_buttons[gr_screen.res][i].filename); + } + b->link_hotspot(Brief_buttons[gr_screen.res][i].hotspot); + } + + // add all xstrs + for(i=0; iflags & NETINFO_FLAG_TEAM_CAPTAIN)){ + Brief_buttons[gr_screen.res][BRIEF_BUTTON_MULTI_LOCK].button.disable(); + } + } else { + if(!(Net_player->flags & NETINFO_FLAG_GAME_HOST)){ + Brief_buttons[gr_screen.res][BRIEF_BUTTON_MULTI_LOCK].button.disable(); + } + } + } + + // create close button for closeup popup + Closeup_close_button.create( &Brief_ui_window, "", Closeup_button_coords[gr_screen.res][BRIEF_X_COORD], Closeup_button_coords[gr_screen.res][BRIEF_Y_COORD], 60, 30, 0, 1 ); + Closeup_close_button.set_highlight_action( common_play_highlight_sound ); + Closeup_close_button.set_bmaps(Closeup_button_filename[gr_screen.res]); + Closeup_close_button.link_hotspot(Closeup_button_hotspot); + + // set up hotkeys for buttons so we draw the correct animation frame when a key is pressed + Brief_buttons[gr_screen.res][BRIEF_BUTTON_LAST_STAGE].button.set_hotkey(KEY_SHIFTED|KEY_RIGHT); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_NEXT_STAGE].button.set_hotkey(KEY_RIGHT); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_PREV_STAGE].button.set_hotkey(KEY_LEFT); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_FIRST_STAGE].button.set_hotkey(KEY_SHIFTED|KEY_LEFT); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_SCROLL_UP].button.set_hotkey(KEY_UP); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_SCROLL_DOWN].button.set_hotkey(KEY_DOWN); + + Closeup_close_button.disable(); + Closeup_close_button.hide(); + + // if we have no briefing stages, hide and disable briefing buttons + if(Num_brief_stages <= 0){ + Brief_buttons[gr_screen.res][BRIEF_BUTTON_LAST_STAGE].button.disable(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_LAST_STAGE].button.hide(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_NEXT_STAGE].button.disable(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_NEXT_STAGE].button.hide(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_PREV_STAGE].button.disable(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_PREV_STAGE].button.hide(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_FIRST_STAGE].button.disable(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_FIRST_STAGE].button.hide(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_SCROLL_UP].button.disable(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_SCROLL_UP].button.hide(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_SCROLL_DOWN].button.disable(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_SCROLL_DOWN].button.hide(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_PAUSE].button.disable(); + Brief_buttons[gr_screen.res][BRIEF_BUTTON_PAUSE].button.hide(); + } +} + +// -------------------------------------------------------------------------------------- +// brief_get_closeup_icon() +// +// +uint brief_get_closeup_icon() +{ + return (uint)Closeup_icon; +} + +// stop showing the closeup view of an icon +void brief_turn_off_closeup_icon() +{ + // turn off closup + if ( Closeup_icon != NULL ) { + gamesnd_play_iface(SND_BRIEF_ICON_SELECT); + Closeup_icon = NULL; + Closeup_close_button.disable(); + Closeup_close_button.hide(); + } +} + +// -------------------------------------------------------------------------------------- +// brief_load_bitmaps() +// +// +void brief_load_bitmaps() +{ + Brief_text_bitmap = bm_load(Brief_infobox_filename[gr_screen.res]); + Brief_grid_bitmap = bm_load(Brief_win_filename[gr_screen.res]); + + if ( Closeup_bitmap == -1 ) { + Closeup_bitmap = bm_load(Closeup_background_filename[gr_screen.res]); + } +} + +// -------------------------------------------------------------------------------------- +// brief_ui_init() +// +// +void brief_ui_init() +{ + if(Game_mode & GM_MULTIPLAYER) { + Brief_background_bitmap = bm_load(Brief_multi_filename[gr_screen.res]); + } else { + Brief_background_bitmap = bm_load(Brief_filename[gr_screen.res]); + } + + if ( Num_brief_stages <= 0 ){ + return; + } + + brief_load_bitmaps(); +} + + +// -------------------------------------------------------------------------------------- +// brief_set_default_closeup() +// +// +void brief_set_default_closeup() +{ + brief_stage *bs; + int i; + + bs = &Briefing->stages[0]; + + if ( Briefing->num_stages <= 0 ) { + Closeup_icon = NULL; + return; + } + + if ( bs->num_icons <= 0 ) { + Closeup_icon = NULL; + return; + } + + // check for the first highlighted icons to have as the default closeup + for ( i = 0; i < bs->num_icons; i++ ) { + if ( bs->icons[i].flags & BI_HIGHLIGHT ) + break; + } + + if ( i == bs->num_icons ) { + brief_setup_closeup(&bs->icons[0]); + } + else { + brief_setup_closeup(&bs->icons[i]); + } +} + +// funciton to evaluate the sexpressions of the briefing stages eliminating those stages +// which shouldn't get shown +void brief_compact_stages() +{ + int num, result, i; + + /* + if((Game_mode & GM_MULTIPLAYER) && (Netgame.campaign_mode == MP_CAMPAIGN) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Game_mode |= GM_CAMPAIGN_MODE; + } + */ + + num = 0; + while ( num < Briefing->num_stages ) { + result = eval_sexp( Briefing->stages[num].formula ); + if ( !result ) { + if ( Briefing->stages[num].new_text ) { + free( Briefing->stages[num].new_text ); + Briefing->stages[num].new_text = NULL; + } + + if ( Briefing->stages[num].icons ) { + free( Briefing->stages[num].icons ); + Briefing->stages[num].icons = NULL; + } + + + if ( Briefing->stages[num].lines ) { + free( Briefing->stages[num].lines ); + Briefing->stages[num].lines = NULL; + } + + Briefing->stages[num].num_icons = 0; + for ( i = num+1; i < Briefing->num_stages; i++ ) { + Briefing->stages[i-1] = Briefing->stages[i]; + } + Briefing->num_stages--; + continue; + } + num++; + } + + /* + if((Game_mode & GM_MULTIPLAYER) && (Netgame.campaign_mode == MP_CAMPAIGN) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + Game_mode &= ~(GM_CAMPAIGN_MODE); + } + */ +} + + +// -------------------------------------------------------------------------------------- +// brief_init() +// + int red_alert_mission(void); +// +void brief_init() +{ + // Since first stage of briefing can take some time to arrive and play, + // reset the trailer timer on briefing init. +#ifdef FS2_DEMO + demo_reset_trailer_timer(); +#endif + + // for multiplayer, change the state in my netplayer structure + // and initialize the briefing chat area thingy + if ( Game_mode & GM_MULTIPLAYER ){ + Net_player->state = NETPLAYER_STATE_BRIEFING; + } + + // Non standard briefing in red alert mission + if ( red_alert_mission() ) { + gameseq_post_event(GS_EVENT_RED_ALERT); + return; + } + + // get a pointer to the appropriate briefing structure + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){ + Briefing = &Briefings[Net_player->p_info.team]; + } else { + Briefing = &Briefings[0]; + } + + Brief_last_auto_advance = 0; + + brief_compact_stages(); // compact the briefing array to eliminate unused stages + + common_set_interface_palette("BriefingPalette"); + + ship_stop_animation(); + set_active_ui(&Brief_ui_window); + Current_screen = ON_BRIEFING_SELECT; + brief_restart_text_wipe(); + common_flash_button_init(); + common_music_init(SCORE_BRIEFING); + + + help_overlay_set_state(BR_OVERLAY,0); + + if ( Brief_inited == TRUE ) { + common_buttons_maybe_reload(&Brief_ui_window); // AL 11-21-97: this is necessary since we may returning from the hotkey + // screen, which can release common button bitmaps. + common_reset_buttons(); + nprintf(("Alan","brief_init() returning without doing anything\n")); + return; + } + + if (The_mission.game_type & MISSION_TYPE_TRAINING) + Num_brief_stages = Briefing->num_stages; + else + Num_brief_stages = Briefing->num_stages + 1; + + Current_brief_stage = 0; + Last_brief_stage = 0; + + // init the scene-cut data + brief_transition_reset(); + +#ifndef FS2_DEMO + hud_anim_init(&Fade_anim, Brief_static_coords[gr_screen.res][0], Brief_static_coords[gr_screen.res][1], Brief_static_name[gr_screen.res]); + hud_anim_load(&Fade_anim); +#endif + + nprintf(("Alan","Entering brief_init()\n")); + common_select_init(); + + if(Game_mode & GM_MULTIPLAYER) { + BriefingMaskBitmap = bm_load(Brief_multi_mask_filename[gr_screen.res]); + } else { + BriefingMaskBitmap = bm_load(Brief_mask_filename[gr_screen.res]); + } + + if (BriefingMaskBitmap < 0) { + Error(LOCATION,"Could not load in 'brief-m'!"); + } + + Briefing_mask_w = -1; + Briefing_mask_h = -1; + + // get a pointer to bitmap by using bm_lock() + BriefingMaskPtr = bm_lock(BriefingMaskBitmap, 8, BMP_AABITMAP); + BriefingMaskData = (ubyte*)BriefingMaskPtr->data; + bm_get_info(BriefingMaskBitmap, &Briefing_mask_w, &Briefing_mask_h); + + help_overlay_load(BR_OVERLAY); + + // Set up the mask regions + // initialize the different regions of the menu that will react when the mouse moves over it + Num_briefing_regions = 0; + + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", COMMON_BRIEFING_REGION, 0); + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", COMMON_SS_REGION, 0); + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", COMMON_WEAPON_REGION, 0); + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", COMMON_COMMIT_REGION, 0); + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", COMMON_HELP_REGION, 0); + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", COMMON_OPTIONS_REGION, 0); + + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", BRIEF_LAST_STAGE_MASK, 0); + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", BRIEF_NEXT_STAGE_MASK, 0); + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", BRIEF_PREV_STAGE_MASK, 0); + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", BRIEF_FIRST_STAGE_MASK, 0); + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", BRIEF_TEXT_SCROLL_UP_MASK, 0); + snazzy_menu_add_region(&Briefing_select_region[Num_briefing_regions++], "", BRIEF_TEXT_SCROLL_DOWN_MASK, 0); + + // init common UI + Brief_ui_window.create( 0, 0, gr_screen.max_w, gr_screen.max_h, 0 ); + + if(Game_mode & GM_MULTIPLAYER){ + Brief_ui_window.set_mask_bmap(Brief_mask_multi[gr_screen.res]); + } else { + Brief_ui_window.set_mask_bmap(Brief_mask_single[gr_screen.res]); + } + + Brief_ui_window.tooltip_handler = brief_tooltip_handler; + common_buttons_init(&Brief_ui_window); + brief_buttons_init(); + + // if multiplayer, initialize a few other systems + if(Game_mode & GM_MULTIPLAYER){ + // again, should not be necessary, but we'll leave it for now + chatbox_create(); + + // force the chatbox to be small + chatbox_force_small(); + } + + // set up the screen regions + brief_init_screen(Brief_multiplayer); + + // init briefing specific UI + brief_ui_init(); + + // init the briefing map + brief_init_map(); + + // init the briefing voice playback + brief_voice_init(); + brief_voice_load_all(); + + // init objectives display stuff + ML_objectives_init(Brief_goals_coords[gr_screen.res][BRIEF_X_COORD], Brief_goals_coords[gr_screen.res][BRIEF_Y_COORD], Brief_goals_coords[gr_screen.res][BRIEF_W_COORD], Brief_goals_coords[gr_screen.res][BRIEF_H_COORD]); + + // set the camera target + if ( Briefing->num_stages > 0 ) { + brief_set_new_stage(&Briefing->stages[0].camera_pos, &Briefing->stages[0].camera_orient, 0, Current_brief_stage); + brief_reset_icons(Current_brief_stage); + } + + Brief_playing_fade_sound = 0; + Brief_mouse_up_flag = 0; + Closeup_font_height = gr_get_font_height(); + Closeup_icon = NULL; + Brief_inited = TRUE; +} + +// ------------------------------------------------------------------------------------- +// brief_render_closeup_text() +// +// +#define CLOSEUP_TEXT_OFFSET 10 +void brief_render_closeup_text() +{ +/* + brief_icon *bi; + char line[MAX_ICON_TEXT_LINE_LEN]; + int n_lines, i, render_x, render_y; + int n_chars[MAX_ICON_TEXT_LINES]; + char *p_str[MAX_ICON_TEXT_LINES]; + + if ( Closeup_icon == NULL ) { + Int3(); + return; + } + + bi = Closeup_icon; + + render_x = Closeup_region[0]; + render_y = Closeup_region[1] + CLOSEUP_IMG_H; + + gr_set_clip(render_x+CLOSEUP_TEXT_OFFSET, render_y, CLOSEUP_W,CLOSEUP_TEXT_H); + gr_set_color_fast(&Color_white); + +// n_lines = split_str(bi->text, CLOSEUP_W - 2*CLOSEUP_TEXT_OFFSET, n_chars, p_str, MAX_ICON_TEXT_LINES); + Assert(n_lines != -1); + + for ( i = 0; i < n_lines; i++ ) { + Assert(n_chars[i] < MAX_ICON_TEXT_LINE_LEN); + strncpy(line, p_str[i], n_chars[i]); + line[n_chars[i]] = 0; + gr_printf(0,0+i*Closeup_font_height,line); + } +*/ +} + +// ------------------------------------------------------------------------------------- +// brief_render_closeup() +// +// +void brief_render_closeup(int ship_class, float frametime) +{ + matrix view_orient = IDENTITY_MATRIX; + matrix temp_matrix; + float ang; + int w,h; + + if (ship_class < 0) + return; + + if (Closeup_bitmap < 0) + return; + + ang = PI2 * frametime/Closeup_one_revolution_time; + if ( ang > MAX_ANG_CHG ) + ang = MAX_ANG_CHG; + + Closeup_angles.h += ang; + if ( Closeup_angles.h > PI2 ) + Closeup_angles.h -= PI2; + vm_angles_2_matrix(&temp_matrix, &Closeup_angles ); + Closeup_orient = temp_matrix; + + w = Closeup_region[gr_screen.res][2]; + h = Closeup_region[gr_screen.res][3]; + gr_set_clip(Closeup_region[gr_screen.res][0], Closeup_region[gr_screen.res][1], w, h); + + g3_start_frame(1); + + g3_set_view_matrix(&Closeup_cam_pos, &view_orient, Closeup_zoom); + model_clear_instance( Closeup_icon->modelnum ); + model_set_detail_level(0); + + int is_neb = The_mission.flags & MISSION_FLAG_FULLNEB; + + // maybe switch off nebula rendering + if(is_neb){ + The_mission.flags &= ~MISSION_FLAG_FULLNEB; + } + + int model_render_flags; + if ( Closeup_icon->type == ICON_JUMP_NODE ) { + model_set_outline_color(HUD_color_red, HUD_color_green, HUD_color_blue); + model_render_flags = MR_NO_LIGHTING | MR_LOCK_DETAIL | MR_AUTOCENTER | MR_NO_POLYS | MR_SHOW_OUTLINE; + } else { + model_render_flags = MR_NO_LIGHTING | MR_LOCK_DETAIL | MR_AUTOCENTER; + } + + model_render( Closeup_icon->modelnum, &Closeup_orient, &Closeup_pos, model_render_flags ); + + if (is_neb) { + The_mission.flags |= MISSION_FLAG_FULLNEB; + } + + g3_end_frame(); + + gr_set_color_fast(&Color_bright_white); + + gr_printf(0x8000,2,Closeup_icon->closeup_label); +// brief_render_closeup_text(); + + Closeup_close_button.enable(); + Closeup_close_button.unhide(); + + gr_reset_clip(); +} + +// ------------------------------------------------------------------------------------- +// brief_render() +// +// frametime is in seconds +void brief_render(float frametime) +{ + int z; + int w; + +#ifndef NDEBUG + int h; +#endif + + if ( Num_brief_stages <= 0 ) { + gr_set_color_fast(&Color_white); + Assert( Game_current_mission_filename != NULL ); + gr_printf(0x8000,200,XSTR( "No Briefing exists for mission: %s", 430), Game_current_mission_filename); + + #ifndef NDEBUG + gr_get_string_size(&w, &h, The_mission.name); + gr_set_color_fast(&Color_normal); + gr_printf(0x8000, 230, NOX("[filename: %s, last mod: %s]"), Mission_filename, The_mission.modified); + #endif + + return; + } + + gr_set_bitmap(Brief_grid_bitmap); + gr_bitmap(Brief_bmap_coords[gr_screen.res][0], Brief_bmap_coords[gr_screen.res][1]); + + brief_render_map(Current_brief_stage, frametime); + + // draw the frame bitmaps + gr_set_bitmap(Brief_text_bitmap); + gr_bitmap(Brief_infobox_coords[gr_screen.res][0], Brief_infobox_coords[gr_screen.res][1]); + brief_blit_stage_num(Current_brief_stage, Num_brief_stages); + + z = brief_render_text(Top_brief_text_line, Brief_text_coords[gr_screen.res][0], Brief_text_coords[gr_screen.res][1], Brief_text_coords[gr_screen.res][3], frametime); + if (z) { + brief_voice_play(Current_brief_stage); + } + + // maybe output the "more" indicator + if ( (Brief_text_max_lines[gr_screen.res] + Top_brief_text_line + 2) < Num_brief_text_lines[0] ) { + // can be scrolled down + int more_txt_x = Brief_text_coords[gr_screen.res][0] + (Brief_max_line_width[gr_screen.res]/2) - 10; + int more_txt_y = Brief_text_coords[gr_screen.res][1] + Brief_text_coords[gr_screen.res][3] - 2; // located below brief text, centered + int w, h; + gr_get_string_size(&w, &h, XSTR("more", 1469), strlen(XSTR("more", 1469))); + gr_set_color_fast(&Color_black); + gr_rect(more_txt_x-2, more_txt_y, w+3, h); + gr_set_color_fast(&Color_red); + gr_string(more_txt_x, more_txt_y, XSTR("more", 1469)); // base location on the input x and y? + } + + brief_maybe_blit_scene_cut(frametime); + +#if !defined(NDEBUG) || defined(INTERPLAYQA) + gr_set_color_fast(&Color_normal); + int title_y_offset = (Game_mode & GM_MULTIPLAYER) ? 20 : 10; + gr_printf(Brief_bmap_coords[gr_screen.res][0], Brief_bmap_coords[gr_screen.res][1]-title_y_offset, NOX("[name: %s, mod: %s]"), Mission_filename, The_mission.modified); +#endif + + // output mission title + gr_set_color_fast(&Color_bright_white); + if (Game_mode & GM_MULTIPLAYER) { + char buf[256]; + strncpy(buf, The_mission.name, 256); + gr_force_fit_string(buf, 255, Title_coords_multi[gr_screen.res][2]); + gr_string(Title_coords_multi[gr_screen.res][0], Title_coords_multi[gr_screen.res][1], buf); + } else { + gr_get_string_size(&w, NULL, The_mission.name); + gr_string(Title_coords[gr_screen.res][0] - w, Title_coords[gr_screen.res][1], The_mission.name); + } + + // maybe do objectives + if (Current_brief_stage == Briefing->num_stages) { + ML_objectives_do_frame(0); + } +} + +// ------------------------------------------------------------------------------------- +// brief_set_closeup_pos() +// +// +#define CLOSEUP_OFFSET 20 +void brief_set_closeup_pos(brief_icon *bi) +{ + Closeup_y1 = 10; + Closeup_x1 = fl2i(320 - Closeup_coords[gr_screen.res][BRIEF_W_COORD]/2.0f + 0.5f); +} + +void brief_get_closeup_ship_modelnum(brief_icon *ci) +{ + object *objp; + ship *sp; + + // find the model number for the ship to display + for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) { + + if ( objp == &obj_used_list || objp->type != OBJ_SHIP ) { + continue; + } + + sp = &Ships[objp->instance]; + if ( sp->ship_info_index == ci->ship_class ) { + ci->ship_class = sp->ship_info_index; + ci->modelnum = sp->modelnum; + ci->radius = objp->radius; + break; + } + } +} + +// cut any text off after (and including) '#' char +void brief_truncate_label(char *src) +{ + char *pointer_to_last_char; + + pointer_to_last_char = strstr(src, NOX("#")); + + if ( pointer_to_last_char ) { + *pointer_to_last_char = 0; + } +} + +// ------------------------------------------------------------------------------------- +// brief_setup_closeup() +// +// exit: 0 => set-up icon sucessfully +// -1 => could not setup closeup icon +int brief_setup_closeup(brief_icon *bi) +{ + char pof_filename[NAME_LENGTH]; + ship_info *sip=NULL; + vector tvec; + + Closeup_icon = bi; + Closeup_icon->ship_class = bi->ship_class; + Closeup_icon->modelnum = -1; + + Closeup_one_revolution_time = ONE_REV_TIME; + + switch(Closeup_icon->type) { + case ICON_PLANET: + Closeup_icon = NULL; + return -1; + /* + strcpy(pof_filename, NOX("planet.pof")); + strcpy(Closeup_icon->closeup_label, XSTR("planet",-1)); + vm_vec_make(&Closeup_cam_pos, 0.0f, 0.0f, -8300.0f); + Closeup_zoom = 0.5f; + Closeup_one_revolution_time = ONE_REV_TIME * 3; + */ + break; + case ICON_ASTEROID_FIELD: +#ifndef FS2_DEMO + strcpy(pof_filename, Asteroid_info[ASTEROID_TYPE_BIG].pof_files[0]); + strcpy(Closeup_icon->closeup_label, XSTR( "asteroid", 431)); + vm_vec_make(&Closeup_cam_pos, 0.0f, 0.0f, -334.0f); + Closeup_zoom = 0.5f; +#endif + break; + case ICON_JUMP_NODE: + strcpy(pof_filename, NOX("subspacenode.pof")); + strcpy(Closeup_icon->closeup_label, XSTR( "jump node", 432)); + vm_vec_make(&Closeup_cam_pos, 0.0f, 0.0f, -2700.0f); + Closeup_zoom = 0.5f; + Closeup_one_revolution_time = ONE_REV_TIME * 3; + break; + case ICON_UNKNOWN: + case ICON_UNKNOWN_WING: + strcpy(pof_filename, NOX("unknownship.pof")); + strcpy(Closeup_icon->closeup_label, XSTR( "unknown", 433)); + vm_vec_make(&Closeup_cam_pos, 0.0f, 0.0f, -22.0f); + Closeup_zoom = 0.5f; + break; + default: + brief_get_closeup_ship_modelnum(Closeup_icon); + Assert( Closeup_icon->ship_class != -1 ); + sip = &Ship_info[Closeup_icon->ship_class]; + + strcpy(Closeup_icon->closeup_label,sip->name); + + // cut any text off after (and including) '#' char + brief_truncate_label(Closeup_icon->closeup_label); + + if ( sip->flags & (SIF_SMALL_SHIP|SIF_BIG_SHIP|SIF_HUGE_SHIP|SIF_SENTRYGUN) ) { + strcat(Closeup_icon->closeup_label, XSTR( " class", 434)); + } + break; + } + + if ( Closeup_icon->modelnum == -1 ) { + if ( sip == NULL ) { + Closeup_icon->modelnum = model_load(pof_filename, NULL, NULL); + } else { + Closeup_icon->modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]); + } + Closeup_icon->radius = model_get_radius(Closeup_icon->modelnum); + } + + vm_set_identity(&Closeup_orient); + vm_vec_make(&tvec, 0.0f, 0.0f, -1.0f); + Closeup_orient.fvec = tvec; + vm_vec_zero(&Closeup_pos); + Closeup_angles.p = 0.0f; + Closeup_angles.b = 0.0f; + Closeup_angles.h = PI; + + brief_set_closeup_pos(bi); + + if ( sip ) { + Closeup_cam_pos = sip->closeup_pos; + Closeup_zoom = sip->closeup_zoom; + } + + return 0; +} + +// ------------------------------------------------------------------------------------- +// brief_update_closeup_icon() +// +// input: mode => how to update the closeup view +// 0 -> disable +// +void brief_update_closeup_icon(int mode) +{ + brief_stage *bs; + brief_icon *bi; + int i, closeup_index; + + + if ( mode == 0 ) { + // mode 0 means disable the closeup icon + if ( Closeup_icon != NULL ) { + brief_turn_off_closeup_icon(); + } + return; + } + + if ( Closeup_icon == NULL ) + return; + + bs = &Briefing->stages[Current_brief_stage]; + + closeup_index = -1; + // see if any icons are being highlighted this stage + for ( i = 0; i < bs->num_icons; i++ ) { + bi = &bs->icons[i]; + if ( bi->flags & BI_HIGHLIGHT ) { + closeup_index = i; + break; + } + } + + if ( closeup_index != -1 ) { + bi = &bs->icons[closeup_index]; + brief_setup_closeup(bi); + } + else { + Closeup_icon = NULL; + } +} + + +// ------------------------------------------------------------------------------------- +// brief_check_for_anim() +// +// +void brief_check_for_anim() +{ + int mx, my, i, iw, ih; + brief_stage *bs; + brief_icon *bi = NULL; + + bs = &Briefing->stages[Current_brief_stage]; + mouse_get_pos( &mx, &my ); + + // if mouse click is over the VCR controls, don't launch an icon + // FIXME - should prolly push these into defines instead of hardcoding this + if ( mx >= 0 && mx <= 115 && my >= 136 && my <= 148 ) { + return; + } + + // if mouse coords are outside the briefing screen, then go away + my -= bscreen.map_y1; + mx -= bscreen.map_x1; + if ( my < 0 || mx < 0 || mx > (bscreen.map_x2-bscreen.map_x1+1) || my > (bscreen.map_y2-bscreen.map_y1+1) ) + return; + + for ( i = 0; i < bs->num_icons; i++ ) { + bi = &bs->icons[i]; + brief_common_get_icon_dimensions(&iw, &ih, bi->type, bi->ship_class); + if ( mx < bi->x ) continue; + if ( mx > (bi->x + iw) ) continue; + if ( my < bi->y ) continue; + if ( my > (bi->y + ih) ) continue; + // if we've got here, must be a hit + break; + } + + if ( i == bs->num_icons ) { + brief_turn_off_closeup_icon(); + return; + } + + if ( brief_setup_closeup(bi) == 0 ) { + gamesnd_play_iface(SND_BRIEF_ICON_SELECT); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// maybe flash a button if player hasn't done anything for a while +void brief_maybe_flash_button() +{ + UI_BUTTON *b; + + if ( Num_brief_stages <= 0 ) + return; + + if ( Closeup_icon != NULL ) { + common_flash_button_init(); + return; + } + + if ( common_flash_bright() ) { + if ( Current_brief_stage == (Num_brief_stages-1) ) { + + // AL 4-4-98: Don't flash ship selection button on briefing in demo build +#ifdef FS2_DEMO + return; +#else + // AL 30-3-98: Don't flash ship selection button if in a training mission, + if ( brief_only_allow_briefing() ) { + return; + } + + b = &Common_buttons[Current_screen-1][gr_screen.res][1].button; // ship select button +#endif + } else { + b = &Brief_buttons[gr_screen.res][1].button; // next stage button + } + + if ( b->button_hilighted() ) { + common_flash_button_init(); + } else { + b->draw_forced(1); + } + } +} + +// ------------------------------------------------------------------------------------- +// brief_do_frame() +// +// frametime is in seconds +// +void brief_do_frame(float frametime) +{ + int k, brief_choice; + + if ( red_alert_mission() ) { + return; + } + + + if ( !Brief_inited ){ + brief_init(); + } + + int snazzy_action = -1; + brief_choice = snazzy_menu_do(BriefingMaskData, Briefing_mask_w, Briefing_mask_h, Num_briefing_regions, Briefing_select_region, &snazzy_action, 0); + + k = common_select_do(frametime); + + if ( Closeup_icon ) { + Brief_mouse_up_flag = 0; + } + + if ( help_overlay_active(BR_OVERLAY) ) { + common_flash_button_init(); + brief_turn_off_closeup_icon(); + } + + // Check common keypresses + common_check_keys(k); + +#ifndef NDEBUG + int cam_change = 0; +#endif + + if ( Briefing->num_stages > 0 ) { + + // check for special keys + switch(k) { + +#ifndef NDEBUG + case KEY_CTRLED | KEY_PAGEUP: { + if (Closeup_icon->ship_class) { + Closeup_icon->ship_class--; + + ship_info *sip = &Ship_info[Closeup_icon->ship_class]; + if (sip->modelnum < 0) + sip->modelnum = model_load(sip->pof_file, NULL, NULL); + + mprintf(("Shiptype = %d (%s)\n", Closeup_icon->ship_class, sip->name)); + mprintf(("Modelnum = %d (%s)\n", sip->modelnum, sip->pof_file)); + brief_setup_closeup(Closeup_icon); + } + + break; + } + + case KEY_CTRLED | KEY_PAGEDOWN: { + if (Closeup_icon->ship_class < Num_ship_types - 1) { + Closeup_icon->ship_class++; + + ship_info *sip = &Ship_info[Closeup_icon->ship_class]; + if (sip->modelnum < 0) + sip->modelnum = model_load(sip->pof_file, NULL, NULL); + + mprintf(("Shiptype = %d (%s)\n", Closeup_icon->ship_class, sip->name)); + mprintf(("Modelnum = %d (%s)\n", sip->modelnum, sip->pof_file)); + brief_setup_closeup(Closeup_icon); + } + + break; + } + + case KEY_A: + Closeup_cam_pos.z += 1; + cam_change = 1; + break; + + case KEY_A + KEY_SHIFTED: + Closeup_cam_pos.z += 10; + cam_change = 1; + break; + + case KEY_Z: + Closeup_cam_pos.z -= 1; + cam_change = 1; + break; + + case KEY_Z + KEY_SHIFTED: + Closeup_cam_pos.z -= 10; + cam_change = 1; + break; + + case KEY_Y: + Closeup_cam_pos.y += 1; + cam_change = 1; + break; + + case KEY_Y + KEY_SHIFTED: + Closeup_cam_pos.y += 10; + cam_change = 1; + break; + + case KEY_H: + Closeup_cam_pos.y -= 1; + cam_change = 1; + break; + + case KEY_H + KEY_SHIFTED: + Closeup_cam_pos.y -= 10; + cam_change = 1; + break; + + case KEY_COMMA: + Closeup_zoom -= 0.1f; + if ( Closeup_zoom < 0.1 ) + Closeup_zoom = 0.1f; + cam_change = 1; + break; + + case KEY_COMMA+KEY_SHIFTED: + Closeup_zoom -= 0.5f; + if ( Closeup_zoom < 0.1 ) + Closeup_zoom = 0.1f; + cam_change = 1; + break; + + case KEY_PERIOD: + Closeup_zoom += 0.1f; + cam_change = 1; + break; + + case KEY_PERIOD+KEY_SHIFTED: + Closeup_zoom += 0.5f; + cam_change = 1; + break; +#endif + case 1000: // need this to avoid warning about no case + break; + + default: + break; + } // end switch + } + +#ifndef NDEBUG + if ( cam_change ) { + nprintf(("General","Camera pos: %.2f, %.2f %.2f // ", Closeup_cam_pos.x, Closeup_cam_pos.y, Closeup_cam_pos.z)); + nprintf(("General","Camera zoom: %.2f\n", Closeup_zoom)); + } +#endif + + if ( brief_choice > -1 && snazzy_action == SNAZZY_OVER ) { + Brief_mouse_up_flag = 0; + brief_choice = -1; + } + + + common_check_buttons(); + // if ( Briefing->num_stages > 0 ) + brief_check_buttons(); + + if ( brief_choice != -1 ) { + Brief_mouse_up_flag = 0; + } + + gr_reset_clip(); + + common_render(frametime); + + if ( Current_brief_stage < (Num_brief_stages-1) ) { + if ( !help_overlay_active(BR_OVERLAY) && brief_time_to_advance(Current_brief_stage, frametime) ) { + brief_do_next_pressed(0); + common_flash_button_init(); + Brief_last_auto_advance = timer_get_milliseconds(); + } + } + + if ( !Background_playing ) { + int time = -1; + int check_jump_flag = 1; + if ( Current_brief_stage != Last_brief_stage ) { + + // Check if we have a quick transition pending + if ( Quick_transition_stage != -1 ) { + Quick_transition_stage = -1; + brief_reset_last_new_stage(); + time = 0; + check_jump_flag = 0; + } + + if ( check_jump_flag ) { + if ( abs(Current_brief_stage - Last_brief_stage) > 1 ) { + Quick_transition_stage = Current_brief_stage; + Current_brief_stage = Last_brief_stage; + Assert(Current_brief_stage >= 0); + Start_fade_up_anim = 1; + goto Transition_done; + } + } + + if ( time != 0 ) { + if ( Current_brief_stage > Last_brief_stage ) { + if ( Briefing->stages[Last_brief_stage].flags & BS_FORWARD_CUT ) { + Quick_transition_stage = Current_brief_stage; + Current_brief_stage = Last_brief_stage; + Assert(Current_brief_stage >= 0); + Start_fade_up_anim = 1; + goto Transition_done; + } else { + time = Briefing->stages[Current_brief_stage].camera_time; + } + } + else { + if ( Briefing->stages[Last_brief_stage].flags & BS_BACKWARD_CUT ) { + Quick_transition_stage = Current_brief_stage; + Current_brief_stage = Last_brief_stage; + Assert(Current_brief_stage >= 0); + Start_fade_up_anim = 1; + goto Transition_done; + } else { + time = Briefing->stages[Last_brief_stage].camera_time; + } + } + } + + brief_voice_stop(Last_brief_stage); + + if ( Current_brief_stage < 0 ) { + Int3(); + Current_brief_stage=0; + } + + // set the camera target + brief_set_new_stage(&Briefing->stages[Current_brief_stage].camera_pos, + &Briefing->stages[Current_brief_stage].camera_orient, + time, Current_brief_stage); + + Brief_playing_fade_sound = 0; + Last_brief_stage = Current_brief_stage; + brief_reset_icons(Current_brief_stage); + brief_update_closeup_icon(0); + } + + Transition_done: + + if ( Brief_mouse_up_flag && !Closeup_icon) { + brief_check_for_anim(); + } + + brief_render(frametime); + brief_camera_move(frametime, Current_brief_stage); + + if (Closeup_icon && (Closeup_bitmap >= 0)) { + // blit closeup background + gr_set_bitmap(Closeup_bitmap); + gr_bitmap(Closeup_coords[gr_screen.res][BRIEF_X_COORD], Closeup_coords[gr_screen.res][BRIEF_Y_COORD]); + } + + Brief_ui_window.draw(); + brief_redraw_pressed_buttons(); + common_render_selected_screen_button(); + + if (Closeup_icon) { + brief_render_closeup(Closeup_icon->ship_class, frametime); + } + + // render some extra stuff in multiplayer + if (Game_mode & GM_MULTIPLAYER) { + // should render this last so that it overlaps all controls + chatbox_render(); + + // render the status indicator for the voice system + multi_common_voice_display_status(); + + // blit the "ships/players" locked button + // multi_ts_blit_locked_button(); + + // maybe blit the multiplayer "locked" button + // if its locked, everyone blits it as such + if(multi_ts_is_locked()){ + Brief_buttons[gr_screen.res][BRIEF_BUTTON_MULTI_LOCK].button.draw_forced(2); + } + // anyone who can't hit the button sees it off, otherwise + else { + if( ((Netgame.type_flags & NG_TYPE_TEAM) && !(Net_player->flags & NETINFO_FLAG_TEAM_CAPTAIN)) || + ((Netgame.type_flags & NG_TYPE_TEAM) && !(Net_player->flags & NETINFO_FLAG_GAME_HOST)) ){ + Brief_buttons[gr_screen.res][BRIEF_BUTTON_MULTI_LOCK].button.draw_forced(0); + } else { + Brief_buttons[gr_screen.res][BRIEF_BUTTON_MULTI_LOCK].button.draw(); + } + } + } + } + + // maybe flash a button if player hasn't done anything for a while + brief_maybe_flash_button(); + + // blit help overlay if active + help_overlay_maybe_blit(BR_OVERLAY); + + gr_flip(); + + // If the commit button was pressed, do the commit button actions. Done at the end of the + // loop so there isn't a skip in the animation (since ship_create() can take a long time if + // the ship model is not in memory + if (Commit_pressed) { + if (Game_mode & GM_MULTIPLAYER) { + multi_ts_commit_pressed(); + } else { + commit_pressed(); + } + + Commit_pressed = 0; + } +} + +// -------------------------------------------------------------------------------------- +// brief_unload_bitmaps() +// +// +void brief_unload_bitmaps() +{ + if ( BriefingMaskBitmap != -1 ) { + bm_unload(BriefingMaskBitmap); + BriefingMaskBitmap = -1; + } + + if ( Brief_text_bitmap != -1 ) { + bm_unload(Brief_text_bitmap); + Brief_text_bitmap = -1; + } + + if(Brief_grid_bitmap != -1){ + bm_unload(Brief_grid_bitmap); + Brief_grid_bitmap = -1; + } + + if ( Brief_multitext_bitmap != -1 ) { + bm_unload(Brief_multitext_bitmap); + Brief_multitext_bitmap = -1; + } + + if ( Brief_background_bitmap != -1 ) { + bm_unload(Brief_background_bitmap); + Brief_background_bitmap = -1; + } + + help_overlay_unload(BR_OVERLAY); +} + +// ------------------------------------------------------------------------------------ +// brief_close() +// +// +void brief_close() +{ + if ( Brief_inited == FALSE ) { + nprintf(("Warning","brief_close() returning without doing anything\n")); + return; + } + + nprintf(("Alan", "Entering brief_close()\n")); + + ML_objectives_close(); + + // unload the audio streams used for voice playback + brief_voice_unload_all(); + +#ifndef FS2_DEMO + hud_anim_release(&Fade_anim); +#endif + + // done mask bitmap, so unlock it + bm_unlock(BriefingMaskBitmap); + + Brief_ui_window.destroy(); + + // unload the bitmaps + brief_unload_bitmaps(); + + brief_common_close(); + + Brief_inited = FALSE; +} + +void briefing_stop_music() +{ + if ( Briefing_music_handle != -1 ) { + audiostream_close_file(Briefing_music_handle); + Briefing_music_handle = -1; + } +} + +void briefing_load_music(char* fname) +{ + if ( Cmdline_freespace_no_music ) { + return; + } + + if ( Briefing_music_handle != -1 ) + return; + + if ( fname ) + Briefing_music_handle = audiostream_open( fname, ASF_EVENTMUSIC ); +} + +void briefing_start_music() +{ + if ( Briefing_music_handle != -1 ) { + if ( !audiostream_is_playing(Briefing_music_handle) ) + audiostream_play(Briefing_music_handle, Master_event_music_volume); + } + else { + nprintf(("Warning", "No music file exists to play music at this briefing!\n")); + } +} + +void brief_stop_voices() +{ + brief_voice_stop(Current_brief_stage); +} + +void brief_maybe_blit_scene_cut(float frametime) +{ + if ( Start_fade_up_anim ) { + +#ifdef FS2_DEMO + Fade_anim.time_elapsed = 0.0f; + Start_fade_up_anim = 0; + Start_fade_down_anim = 1; + Current_brief_stage = Quick_transition_stage; + + if ( Current_brief_stage < 0 ) { + brief_transition_reset(); + Current_brief_stage = Last_brief_stage; + } + goto Fade_down_anim_start; +#else + int framenum; + + Fade_anim.time_elapsed += frametime; + + if ( !Brief_playing_fade_sound ) { + gamesnd_play_iface(SND_BRIEFING_STATIC); + Brief_playing_fade_sound = 1; + } + + if ( Fade_anim.time_elapsed > Fade_anim.total_time ) { + Fade_anim.time_elapsed = 0.0f; + Start_fade_up_anim = 0; + Start_fade_down_anim = 1; + Current_brief_stage = Quick_transition_stage; + + if ( Current_brief_stage < 0 ) { + brief_transition_reset(); + Current_brief_stage = Last_brief_stage; + } + + Assert(Current_brief_stage >= 0); + goto Fade_down_anim_start; + } + + // draw the correct frame of animation + framenum = fl2i( (Fade_anim.time_elapsed * Fade_anim.num_frames) / Fade_anim.total_time ); + if ( framenum < 0 ) + framenum = 0; + if ( framenum >= Fade_anim.num_frames ) + framenum = Fade_anim.num_frames-1; + + // Blit the bitmap for this frame + gr_set_bitmap(Fade_anim.first_frame + framenum); + gr_bitmap(Fade_anim.sx, Fade_anim.sy); +#endif + } + + + Fade_down_anim_start: + if ( Start_fade_down_anim ) { + +#ifdef FS2_DEMO + Fade_anim.time_elapsed = 0.0f; + Start_fade_up_anim = 0; + Start_fade_down_anim = 0; + return; +#else + + int framenum; + + Fade_anim.time_elapsed += frametime; + + if ( Fade_anim.time_elapsed > Fade_anim.total_time ) { + Fade_anim.time_elapsed = 0.0f; + Start_fade_up_anim = 0; + Start_fade_down_anim = 0; + return; + } + + // draw the correct frame of animation + framenum = fl2i( (Fade_anim.time_elapsed * Fade_anim.num_frames) / Fade_anim.total_time ); + if ( framenum < 0 ) + framenum = 0; + if ( framenum >= Fade_anim.num_frames ) + framenum = Fade_anim.num_frames-1; + + // Blit the bitmap for this frame + gr_set_bitmap(Fade_anim.first_frame + (Fade_anim.num_frames-1) - framenum); + gr_bitmap(Fade_anim.sx, Fade_anim.sy); + +#endif + } +} + +void brief_transition_reset() +{ + Quick_transition_stage = -1; + Start_fade_up_anim = 0; + Start_fade_down_anim = 0; + Fade_anim.time_elapsed = 0.0f; +} + +// return 1 if this mission only allow players to use the briefing (and not ship or +// weapon loadout). Otherwise return 0. +int brief_only_allow_briefing() +{ + if ( The_mission.game_type & MISSION_TYPE_TRAINING ) { + return 1; + } + + if ( The_mission.scramble || The_mission.red_alert) { + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/src/missionui/missioncmdbrief.cpp b/src/missionui/missioncmdbrief.cpp new file mode 100644 index 0000000..704b849 --- /dev/null +++ b/src/missionui/missioncmdbrief.cpp @@ -0,0 +1,811 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionCmdBrief.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Mission Command Briefing Screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 14 10/06/99 10:28a Jefff + * for OEM: cmd brief anim defaults to the default anim if load failure + * + * 13 9/03/99 1:32a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 12 8/27/99 12:04a Dave + * Campaign loop screen. + * + * 11 8/19/99 6:28p Jefff + * move animation in 640 a bit + * + * 10 7/15/99 4:11p Andsager + * Leave command briefs in DEMO + * + * 9 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 8 7/09/99 10:32p Dave + * Command brief and red alert screens. + * + * 7 2/08/99 5:06p Johnson + * Removed reference to a now non-existent palette. + * + * 6 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 5 1/14/99 5:15p Neilk + * changed credits, command debrief interfaces to high resolution support + * + * 4 10/16/98 9:40a Andsager + * Remove ".h" files from model.h + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 42 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 41 6/05/98 9:54a Lawrance + * OEM changes + * + * 40 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 39 5/26/98 11:10a Lawrance + * Fix bug where window controls get disabled when F1 pressed twice + * + * 38 5/23/98 10:38p Lawrance + * Avoid doing a cfile refresh when running debug + * + * 37 5/23/98 6:49p Lawrance + * Fix problems with refreshing the file list when a CD is inserted + * + * 36 5/22/98 11:15a Lawrance + * Tweak how CD gets asked for + * + * 35 5/21/98 6:57p Lawrance + * Only ask for the CD once + * + * 34 5/20/98 9:46p John + * added code so the places in code that change half the palette don't + * have to clear the screen. + * + * 33 5/20/98 6:41p Lawrance + * Add hook for command brief stage changes + * + * 32 5/18/98 5:59p Hoffoss + * Made command briefing advanced now once the speech stops and animation + * has fully played once, whichever is longer. + * + * 31 5/14/98 3:34p Hoffoss + * Made command brief screen wait until animation finishes before + * auto-advancing to the next state (in addition to the wait that was + * already implemented). + * + * 30 5/08/98 5:32p Lawrance + * prompt for CD if can't load animations or voice + * + * 29 5/06/98 11:49p Lawrance + * Add help overlay for command brief + * + * 28 5/05/98 2:44p Hoffoss + * Fixed bug where not having a valid command brief ani would crash the + * cmd brief screen. + * + * 27 4/28/98 4:16p Hoffoss + * Implemented auto-advancing functionality to command briefings. + * + * 26 4/27/98 11:01a Hoffoss + * Changed code to utilize proper palette. + * + * 25 4/26/98 5:11p Hoffoss + * Fixed bug where going to options screen and then returning to the cmd + * brief screen didn't start ani back up. + * + * 24 4/13/98 11:00a Hoffoss + * Made the ani in a cmd brief continue playing if it's the same as the + * new stage. + * + * 23 4/06/98 8:37p Hoffoss + * Fixed a few bugs with command brief screen. Now the voice starts after + * the text has printed, and options screen doesn't reset cmd brief. + * + * 22 4/06/98 11:24a Lawrance + * stop command brief music if returning to main menu + * + * 21 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 20 3/31/98 5:18p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * + * 19 3/31/98 12:00p Hoffoss + * Fixed bug with setting palette using as filename. + * + * 18 3/30/98 3:22p Hoffoss + * Changed Command Brief screen to merge current ani's palette with + * interface palette for better overall color. + * + * 17 3/30/98 12:18a Lawrance + * change some DEMO_RELEASE code to not compile code rather than return + * early + * + * 16 3/29/98 12:55a Lawrance + * Get demo build working with limited set of data. + * + * 15 3/27/98 9:49a Lawrance + * AL: Ensure anim stops playing when leaving the command brief + * + * 14 3/26/98 5:24p Hoffoss + * Changed Command Brief to use memory mapped ani files instead, so we + * avoid the huge pauses for huge anis that play! + * + * 13 3/24/98 8:52a Jasen + * Updated coords for new button. + * + * 12 3/23/98 4:21p Hoffoss + * Fixed bug where command brief screen couldn't be re-entered unless + * mission was reloaded. + * + * 11 3/19/98 5:59p Hoffoss + * Added reset to cmd brief shutdown. + * + * 10 3/19/98 5:32p Lawrance + * Added music to the background of command brief screen. + * + * 9 3/19/98 4:25p Hoffoss + * Added remaining support for command brief screen (ANI and WAVE file + * playing). + * + * 8 3/18/98 12:03p John + * Marked all the new strings as externalized or not. + * + * 7 3/17/98 6:24p Hoffoss + * Added ani playing to command brief screen, which defaults to + * CB_default.ani. + * + * 6 3/13/98 6:12p Frank + * AL: Fix bug caused by conflict with command brief and red alert + * missions + * + * 5 3/13/98 3:44p Hoffoss + * Added stage indication to Mission Command Briefing screen. + * + * 4 3/12/98 4:02p Hoffoss + * Added commit sound + * + * 3 3/05/98 9:38p Hoffoss + * Finished up command brief screen. + * + * 2 3/05/98 3:59p Hoffoss + * Added a bunch of new command brief stuff, and asteroid initialization + * to Fred. + * + * 1 3/02/98 6:13p Hoffoss + * + * $NoKeywords: $ + */ + +#include "ui.h" +#include "uidefs.h" +#include "gamesnd.h" +#include "gamesequence.h" +#include "missionscreencommon.h" +#include "key.h" +#include "bmpman.h" +#include "font.h" +#include "missionbriefcommon.h" +#include "missioncmdbrief.h" +#include "redalert.h" +#include "audiostr.h" +#include "timer.h" +#include "eventmusic.h" +#include "player.h" +#include "contexthelp.h" +#include "alphacolors.h" +#include "animplay.h" + +char *Cmd_brief_fname[GR_NUM_RESOLUTIONS] = { + "CommandBrief", + "2_CommandBrief" +}; + +char *Cmd_brief_mask[GR_NUM_RESOLUTIONS] = { + "CommandBrief-m", + "2_Commandbrief-m" +}; + +// lookups for coordinates +#define CMD_X_COORD 0 +#define CMD_Y_COORD 1 +#define CMD_W_COORD 2 +#define CMD_H_COORD 3 + +int Cmd_text_wnd_coords[GR_NUM_RESOLUTIONS][4] = { + { + 17, 109, 606, 108 // GR_640 + }, + { + 28, 174, 969, 174 // GR_1024 + } +}; + + +int Cmd_stage_y[GR_NUM_RESOLUTIONS] = { + 90, // GR_640 + 145 // GR_1024 +}; + +int Cmd_image_wnd_coords[GR_NUM_RESOLUTIONS][4] = { + { + 26, 258, 441, 204 // GR_640 + }, + { + 155, 475, 706, 327 // GR_1024 + } +}; + +#define NUM_BUTTONS 8 + +#define FIRST_STAGE_BUTTON 0 +#define PREV_STAGE_BUTTON 1 +#define PAUSE_BUTTON 2 +#define NEXT_STAGE_BUTTON 3 +#define LAST_STAGE_BUTTON 4 +#define HELP_BUTTON 5 +#define OPTIONS_BUTTON 6 +#define ACCEPT_BUTTON 7 + +// buttons +ui_button_info Cmd_brief_buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { + { // GR_640 + ui_button_info("CBB_00", 504, 221, -1, -1, 0), + ui_button_info("CBB_01", 527, 221, -1, -1, 1), + ui_button_info("CBB_02", 555, 221, -1, -1, 2), + ui_button_info("CBB_03", 583, 221, -1, -1, 3), + ui_button_info("CBB_04", 607, 221, -1, -1, 4), + ui_button_info("CBB_05", 539, 431, -1, -1, 5), + ui_button_info("CBB_06", 538, 455, -1, -1, 6), + ui_button_info("CBB_07", 575, 432, -1, -1, 7), + }, + { // GR_1024 + ui_button_info("2_CBB_00", 806, 354, -1, -1, 0), + ui_button_info("2_CBB_01", 844, 354, -1, -1, 1), + ui_button_info("2_CBB_02", 888, 354, -1, -1, 2), + ui_button_info("2_CBB_03", 933, 354, -1, -1, 3), + ui_button_info("2_CBB_04", 971, 354, -1, -1, 4), + ui_button_info("2_CBB_05", 863, 690, -1, -1, 5), + ui_button_info("2_CBB_06", 861, 728, -1, -1, 6), + ui_button_info("2_CBB_07", 920, 692, -1, -1, 7), + } +}; + +// text +#define CMD_BRIEF_NUM_TEXT 3 +UI_XSTR Cmd_brief_text[GR_NUM_RESOLUTIONS][CMD_BRIEF_NUM_TEXT] = { + { // GR_640 + { "Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Cmd_brief_buttons[0][HELP_BUTTON].button }, + { "Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Cmd_brief_buttons[0][OPTIONS_BUTTON].button }, + { "Continue", 1069, 564, 413, UI_XSTR_COLOR_PINK, -1, &Cmd_brief_buttons[0][ACCEPT_BUTTON].button }, + }, + { // GR_1024 + { "Help", 928, 800, 704, UI_XSTR_COLOR_GREEN, -1, &Cmd_brief_buttons[1][HELP_BUTTON].button }, + { "Options", 1036, 797, 743, UI_XSTR_COLOR_GREEN, -1, &Cmd_brief_buttons[1][OPTIONS_BUTTON].button }, + { "Continue", 1069, 917, 661, UI_XSTR_COLOR_PINK, -1, &Cmd_brief_buttons[1][ACCEPT_BUTTON].button }, + } +}; + +static UI_WINDOW Ui_window; +static int Background_bitmap; // bitmap for the background of the cmd_briefing +static int Cur_stage; +static int Scroll_offset; +static int Cmd_brief_inited = 0; +// static int Cmd_brief_ask_for_cd; +static int Voice_good_to_go = 0; +static int Voice_started_time = 0; +static int Voice_ended_time; +static int Anim_playing_id = -1; +static anim_instance *Cur_anim_instance = NULL; +static int Last_anim_frame_num; + +static int Cmd_brief_last_voice; +static int Palette_bmp = -1; +static ubyte Palette[768]; +static char Palette_name[128]; + +void cmd_brief_init_voice() +{ + int i; + + Assert(Cur_cmd_brief); + for (i=0; inum_stages; i++) { + Cur_cmd_brief->stage[i].wave = -1; + if (stricmp(Cur_cmd_brief->stage[i].wave_filename, NOX("none")) && Cur_cmd_brief->stage[i].wave_filename[0]) { + Cur_cmd_brief->stage[i].wave = audiostream_open(Cur_cmd_brief->stage[i].wave_filename, ASF_VOICE); + if (Cur_cmd_brief->stage[i].wave < 0) { + nprintf(("General", "Failed to load \"%s\"", Cur_cmd_brief->stage[i].wave_filename)); + } + } + } + + Cmd_brief_last_voice = -1; +} + +int cmd_brief_check_stage_done() +{ + if (!Voice_good_to_go) + return 0; + + if (Voice_ended_time && (timer_get_milliseconds() - Voice_ended_time >= 1000)) + return 1; + + if (Briefing_voice_enabled && (Cmd_brief_last_voice >= 0)) { + if (audiostream_is_playing(Cmd_brief_last_voice)){ + return 0; + } + + if (!Voice_ended_time){ + Voice_ended_time = timer_get_milliseconds(); + } + + return 0; + } + + // if we get here, there is no voice, so we simulate the time it would take instead + if (!Voice_ended_time) + Voice_ended_time = Voice_started_time + max(5000, Num_brief_text_lines[0] * 3500); + + return 0; +} + +// start playback of the voice for a particular briefing stage +void cmd_brief_voice_play(int stage_num) +{ + int voice = -1; + + if (!Voice_good_to_go) { + Voice_started_time = 0; + return; + } + + if (!Voice_started_time) { + Voice_started_time = timer_get_milliseconds(); + Voice_ended_time = 0; + } + + if (!Briefing_voice_enabled){ + return; + } + + if (Cur_stage >= 0 && Cur_stage < Cur_cmd_brief->num_stages){ + voice = Cur_cmd_brief->stage[stage_num].wave; + } + + // are we still on same voice that is currently playing/played? + if (Cmd_brief_last_voice == voice){ + return; // no changes, nothing to do. + } + + // if previous wave is still playing, stop it first. + if (Cmd_brief_last_voice >= 0) { + audiostream_stop(Cmd_brief_last_voice); // stream is automatically rewound + Cmd_brief_last_voice = -1; + } + + // ok, new wave needs playing, so we can start playing it now (and it becomes the current wave) + Cmd_brief_last_voice = voice; + if (voice >= 0){ + audiostream_play(voice, Master_voice_volume, 0); + } +} + +// called to leave the command briefing screen +void cmd_brief_exit() +{ + gameseq_post_event(GS_EVENT_START_BRIEFING); +} + +void cmd_brief_stop_anim(int id) +{ + if (Cur_anim_instance && (id != Anim_playing_id)) { + anim_stop_playing(Cur_anim_instance); + Cur_anim_instance = NULL; + } + + Voice_good_to_go = 0; + if (Cmd_brief_last_voice >= 0) { + audiostream_stop(Cmd_brief_last_voice); // stream is automatically rewound + Cmd_brief_last_voice = -1; + } +} + +void cmd_brief_new_stage(int stage) +{ + int i; + anim_play_struct aps; + + if (stage < 0) { + cmd_brief_stop_anim(-1); + Cur_stage = -1; + Anim_playing_id = -1; + } + + Cur_stage = stage; + brief_color_text_init(Cur_cmd_brief->stage[stage].text, Cmd_text_wnd_coords[gr_screen.res][CMD_W_COORD]); + + i = Cur_cmd_brief->stage[Cur_stage].anim_ref; + if (i < 0) + i = Cur_stage; + + cmd_brief_stop_anim(i); + + if (i != Anim_playing_id) { + if (Cur_cmd_brief->stage[i].anim) { + anim_play_init(&aps, Cur_cmd_brief->stage[i].anim,Cmd_image_wnd_coords[gr_screen.res][CMD_X_COORD], Cmd_image_wnd_coords[gr_screen.res][CMD_Y_COORD]); + aps.looped = 1; + Cur_anim_instance = anim_play(&aps); + Last_anim_frame_num = 0; + } + + Anim_playing_id = i; + } + + if (Cur_cmd_brief->stage[i].anim) { + memcpy(Palette, Cur_cmd_brief->stage[i].anim->palette, 384); + gr_set_palette(Cur_cmd_brief->stage[i].ani_filename, Palette, 1); + } +} + +void cmd_brief_hold() +{ + cmd_brief_stop_anim(-1); + Anim_playing_id = -1; +} + +void cmd_brief_unhold() +{ + cmd_brief_new_stage(Cur_stage); +} + +void cmd_brief_button_pressed(int n) +{ + switch (n) { + case HELP_BUTTON: + launch_context_help(); + gamesnd_play_iface(SND_HELP_PRESSED); + break; + + case OPTIONS_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_OPTIONS_MENU); + break; + + case FIRST_STAGE_BUTTON: + if (Cur_stage) { + cmd_brief_new_stage(0); + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + + break; + + case PREV_STAGE_BUTTON: + if (Cur_stage) { + cmd_brief_new_stage(Cur_stage - 1); + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + + break; + + case NEXT_STAGE_BUTTON: + if (Cur_stage < Cur_cmd_brief->num_stages - 1) { + cmd_brief_new_stage(Cur_stage + 1); + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + + break; + + case LAST_STAGE_BUTTON: + if (Cur_stage < Cur_cmd_brief->num_stages - 1) { + cmd_brief_new_stage(Cur_cmd_brief->num_stages - 1); + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + + case ACCEPT_BUTTON: + cmd_brief_exit(); + gamesnd_play_iface(SND_COMMIT_PRESSED); + break; + + case PAUSE_BUTTON: + gamesnd_play_iface(SND_USER_SELECT); + Player->auto_advance ^= 1; + break; + } +} + +void cmd_brief_ani_wave_init(int index) +{ + char *name; + int i; + + // first, search and see if anim is already used in another stage + for (i=0; istage[i].ani_filename, Cur_cmd_brief->stage[index].ani_filename)) { + if (Cur_cmd_brief->stage[i].anim_ref >= 0) + Cur_cmd_brief->stage[index].anim_ref = Cur_cmd_brief->stage[i].anim_ref; + else + Cur_cmd_brief->stage[index].anim_ref = i; + + return; + } + } + + // this is the first instance of the given anim filename + Cur_cmd_brief->stage[index].anim_ref = -1; + name = Cur_cmd_brief->stage[index].ani_filename; + if (!name[0] || !stricmp(name, NOX("")) || !stricmp(name, NOX("none.ani"))) { + name = NOX("CB_default"); + strcpy(Cur_cmd_brief->stage[index].ani_filename, name); + } + + int load_attempts = 0; + while (1) { + + if ( load_attempts++ > 5 ) { + break; + } + + Cur_cmd_brief->stage[index].anim = anim_load(name, 1); + if ( Cur_cmd_brief->stage[index].anim ) { + break; + } + + // couldn't load animation, ask user to insert CD (if necessary) + // if ( Cmd_brief_ask_for_cd ) { + // if ( game_do_cd_check() == 0 ) { + // Cmd_brief_ask_for_cd = 0; + // break; + // } + // } + } + + // check to see if cb anim loaded, if not, try the default one + if ( !Cur_cmd_brief->stage[index].anim ) { + Cur_cmd_brief->stage[index].anim = anim_load(NOX("CB_default"), 1); + } +} + +void cmd_brief_init(int team) +{ + common_music_init(SCORE_BRIEFING); + +//#ifndef FS2_DEMO + + int i; + ui_button_info *b; + + Cmd_brief_inited = 0; + Cur_cmd_brief = &Cmd_briefs[team]; + + if ( red_alert_mission() ) { + gameseq_post_event(GS_EVENT_RED_ALERT); + return; + } + + if (Cur_cmd_brief->num_stages <= 0) + return; + + gr_reset_clip(); + gr_clear(); + Mouse_hidden++; + gr_flip(); + Mouse_hidden--; + + /* + Palette_bmp = bm_load("BarracksPalette"); //CommandBriefPalette"); + Assert(Palette_bmp); + bm_get_palette(Palette_bmp, Palette, Palette_name); // get the palette for this bitmap + gr_set_palette(Palette_name, Palette, 1); + */ + + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Ui_window.set_mask_bmap(Cmd_brief_mask[gr_screen.res]); + + // Cmd_brief_ask_for_cd = 1; + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, 0, 1); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // add text + for(i=0; inum_stages; i++) + cmd_brief_ani_wave_init(i); + + cmd_brief_init_voice(); + Scroll_offset = 0; + Cur_anim_instance = NULL; + cmd_brief_new_stage(0); + Cmd_brief_inited = 1; + +//#endif +} + +void cmd_brief_close() +{ + int i; + + if (Cmd_brief_inited) { + cmd_brief_stop_anim(-1); + Anim_playing_id = -1; + for (i=0; inum_stages; i++) { + if (Cur_cmd_brief->stage[i].wave >= 0) + audiostream_close_file(Cur_cmd_brief->stage[i].wave, 0); + + if (Cur_cmd_brief->stage[i].anim_ref < 0) + if (Cur_cmd_brief->stage[i].anim) + anim_free(Cur_cmd_brief->stage[i].anim); + } + + if (Background_bitmap >= 0) + bm_unload(Background_bitmap); + + // unload the overlay bitmap + help_overlay_unload(CMD_BRIEF_OVERLAY); + + Ui_window.destroy(); + /* + if (Palette_bmp){ + bm_unload(Palette_bmp); + } + */ + + game_flush(); + Cmd_brief_inited = 0; + } +} + +void cmd_brief_do_frame(float frametime) +{ + char buf[40]; + int i, k, w; + + // if no command briefing exists, skip this screen. + if (!Cmd_brief_inited) { + cmd_brief_exit(); + return; + } + + if ( help_overlay_active(CMD_BRIEF_OVERLAY) ) { + Cmd_brief_buttons[gr_screen.res][HELP_BUTTON].button.reset_status(); + Ui_window.set_ignore_gadgets(1); + } + + k = Ui_window.process() & ~KEY_DEBUGGED; + + if ( (k > 0) || B1_JUST_RELEASED ) { + if ( help_overlay_active(CMD_BRIEF_OVERLAY) ) { + help_overlay_set_state(CMD_BRIEF_OVERLAY, 0); + Ui_window.set_ignore_gadgets(0); + k = 0; + } + } + + if ( !help_overlay_active(CMD_BRIEF_OVERLAY) ) { + Ui_window.set_ignore_gadgets(0); + } + + switch (k) { + case KEY_ESC: + common_music_close(); + gameseq_post_event(GS_EVENT_MAIN_MENU); + break; + } // end switch + + for (i=0; iauto_advance && (Cur_stage < Cur_cmd_brief->num_stages - 1)){ +// if (!Cur_anim_instance || (Cur_anim_instance->frame_num < Last_anim_frame_num)) + if (!Cur_anim_instance || Cur_anim_instance->loop_count){ + cmd_brief_new_stage(Cur_stage + 1); + } + } + + if (Cur_anim_instance){ + Last_anim_frame_num = Cur_anim_instance->frame_num; + } + + GR_MAYBE_CLEAR_RES(Background_bitmap); + if (Background_bitmap >= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + { + // JAS: This code is hacked to allow the animation to use all 256 colors + extern int Palman_allow_any_color; + Palman_allow_any_color = 1; + anim_render_all(0, frametime); + Palman_allow_any_color = 0; + } + Ui_window.draw(); + + if (!Player->auto_advance){ + Cmd_brief_buttons[gr_screen.res][PAUSE_BUTTON].button.draw_forced(2); + } + + gr_set_font(FONT1); + gr_set_color_fast(&Color_text_heading); + + sprintf(buf, XSTR( "Stage %d of %d", 464), Cur_stage + 1, Cur_cmd_brief->num_stages); + gr_get_string_size(&w, NULL, buf); + gr_string(Cmd_text_wnd_coords[gr_screen.res][CMD_X_COORD] + Cmd_text_wnd_coords[gr_screen.res][CMD_W_COORD] - w, Cmd_stage_y[gr_screen.res], buf); + + if (brief_render_text(Scroll_offset, Cmd_text_wnd_coords[gr_screen.res][CMD_X_COORD], Cmd_text_wnd_coords[gr_screen.res][CMD_Y_COORD], Cmd_text_wnd_coords[gr_screen.res][CMD_H_COORD], frametime, 0, 1)){ + Voice_good_to_go = 1; + } + + // blit help overlay if active + help_overlay_maybe_blit(CMD_BRIEF_OVERLAY); + + gr_flip(); +} diff --git a/src/missionui/missiondebrief.cpp b/src/missionui/missiondebrief.cpp new file mode 100644 index 0000000..0ceaa1e --- /dev/null +++ b/src/missionui/missiondebrief.cpp @@ -0,0 +1,2841 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionDebrief.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for running the debriefing + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 57 10/29/99 10:40p Jefff + * hack to make german medal names display without actually changing them + * + * 56 10/13/99 3:26p Jefff + * fixed unnumbered XSTRs + * + * 55 10/06/99 10:28a Jefff + * updates for OEM + * + * 54 9/30/99 5:57p Jefff + * show upsell at end of campaign in OEM builds + * + * 53 9/15/99 3:42a Jefff + * badge voice fix + * + * 52 9/14/99 4:35a Dave + * Argh. Added all kinds of code to handle potential crashes in debriefing + * code. + * + * 51 9/14/99 3:26a Dave + * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer + * respawn-too-early problem. Made a few crash points safe. + * + * 50 9/14/99 12:51a Jefff + * + * 49 9/13/99 6:01p Jefff + * fixed wrong promotion voice mapping for sm1-08 + * + * 48 9/13/99 11:15a Jefff + * clear out award text bug fixed + * + * 47 9/13/99 12:17p Andsager + * Fix traitor debrief not playinf for FS2. Don't append _3 or _1 as in + * FS1. + * + * 46 9/07/99 9:35p Jefff + * fixed bug where award text was not getting cleared properly between + * debriefs. + * + * 45 9/07/99 6:56p Jefff + * a few adjustments to loop detection + * + * 44 9/07/99 1:54p Jefff + * skip mission cleanup + * + * 43 9/06/99 9:45p Jefff + * skip mission support + * + * 42 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 41 9/03/99 1:32a Dave + * CD checking by act. Added support to play 2 cutscenes in a row + * seamlessly. Fixed super low level cfile bug related to files in the + * root directory of a CD. Added cheat code to set campaign mission # in + * main hall. + * + * 40 9/02/99 3:45p Jefff + * forgot to remove some debug code. doh. + * + * 39 9/02/99 3:41p Jefff + * changed badge voice handling to be similar to promotion voice handling + * + * 38 9/01/99 4:41p Jefff + * fixed stoopid text color bug + * + * 37 8/31/99 11:54a Jefff + * minor debrief music tweak + * + * 36 8/27/99 9:57a Dave + * Enabled standard cheat codes. Allow player to continue in a campaing + * after using cheat codes. + * + * 35 8/27/99 12:04a Dave + * Campaign loop screen. + * + * 34 8/26/99 8:49p Jefff + * Updated medals screen and about everything that ever touches medals in + * one way or another. Sheesh. + * + * 33 8/20/99 4:20p Jefff + * hack for choosing the correct promotion voice + * + * 32 8/16/99 4:05p Dave + * Big honking checkin. + * + * 31 8/16/99 9:49a Jefff + * mission title length fix in multi + * + * 30 8/11/99 5:33p Jefff + * added 3rd debrief music track + * + * 29 8/10/99 7:28p Jefff + * shuffled some text around + * + * 28 8/04/99 5:36p Andsager + * Show upsell screens at end of demo campaign before returning to main + * hall. + * + * 27 8/04/99 2:07p Jefff + * mission title no longer overwrites popup + * + * 26 8/02/99 5:37p Jefff + * + * 25 8/02/99 4:52p Jefff + * negative feedback sound when recommendations button pressed and + * disabled. + * + * 24 7/21/99 6:21p Jefff + * added hotkeys to the "you cannot accept" popup + * + * 23 6/15/99 12:04p Anoop + * Added a warning for running out of debrief text lines. + * + * 22 6/09/99 2:17p Dave + * Fixed up pleasewait bitmap rendering. + * + * 21 6/01/99 6:07p Dave + * New loading/pause/please wait bar. + * + * 20 5/22/99 6:05p Dave + * Fixed a few localization # problems. + * + * 19 5/22/99 5:35p Dave + * Debrief and chatbox screens. Fixed small hi-res HUD bug. + * + * 18 3/20/99 3:47p Andsager + * Fix crash with no mission loop description. + * + * 17 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * 16 2/21/99 6:01p Dave + * Fixed standalone WSS packets. + * + * 15 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 14 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 13 1/29/99 2:08a Dave + * Fixed beam weapon collisions with players. Reduced size of scoring + * struct for multiplayer. Disabled PXO. + * + * 12 1/14/99 5:15p Neilk + * changed credits, command debrief interfaces to high resolution support + * + * 11 1/13/99 2:11p Andsager + * change default debriefing string text + * + * 10 12/17/98 4:50p Andsager + * Added debrief_assemble_optional_mission_popup_text() for single and + * multiplayer + * + * 9 12/12/98 3:17p Andsager + * Clean up mission eval, goal, event and mission scoring. + * + * 8 12/10/98 10:19a Andsager + * Fix mission loop assert + * + * 7 12/10/98 9:59a Andsager + * Fix some bugs with mission loops + * + * 6 12/09/98 1:56p Andsager + * Initial checkin of mission loop + * + * 5 11/05/98 4:18p Dave + * First run nebula support. Beefed up localization a bit. Removed all + * conditional compiles for foreign versions. Modified mission file + * format. + * + * 4 10/23/98 3:51p Dave + * Full support for tstrings.tbl and foreign languages. All that remains + * is to make it active in Fred. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 178 9/17/98 3:08p Dave + * PXO to non-pxo game warning popup. Player icon stuff in create and join + * game screens. Upped server count refresh time in PXO to 35 secs (from + * 20). + * + * 177 7/07/98 1:46p Dave + * Fixed nasty debriefing badge/promotion bug. Added extra physics info + * for capital ships. + * + * 175 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 174 6/07/98 3:26p Lawrance + * Fix bug with cut-off popup text + * + * 173 6/05/98 9:54a Lawrance + * OEM changes + * + * 172 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 171 5/27/98 1:24p Allender + * make targeting dots work (as well as other targeting features) properly + * in multiplayer. Don't query for CD when entering debrief in + * multiplayer + * + * 170 5/26/98 11:10a Lawrance + * Fix bug where window controls get disabled when F1 pressed twice + * + * 169 5/24/98 12:55a Mike + * Prevent Assert() when no debriefing text for a stage. + * + * 168 5/23/98 10:38p Lawrance + * Avoid doing a cfile refresh when running debug + * + * 167 5/23/98 6:49p Lawrance + * Fix problems with refreshing the file list when a CD is inserted + * + * 166 5/21/98 6:57p Lawrance + * Don't prompt for the CD if voice not found + * + * 165 5/20/98 2:24a Dave + * Fixed server side voice muting. Tweaked multi debrief/endgame + * sequencing a bit. Much friendlier for stats tossing/accepting now. + * + * 164 5/19/98 8:35p Dave + * Revamp PXO channel listing system. Send campaign goals/events to + * clients for evaluation. Made lock button pressable on all screens. + * + * 163 5/19/98 11:13a Hoffoss + * Fixed bug where wrong wings award was being displayed in debriefing. + * + * 162 5/19/98 12:28a Mike + * Cheat stuff. + * + * 161 5/18/98 8:08p Hoffoss + * Moved placement of 'More' text. + * + * 160 5/18/98 3:50p Dan + * AL: Pick correct traitor debriefing voice file + * + * 159 5/17/98 6:32p Dave + * Make sure clients/servers aren't kicked out of the debriefing when team + * captains leave a game. Fixed chatbox off-by-one error. Fixed image + * xfer/pilot info popup stuff. + * + * 158 5/16/98 9:14p Allender + * fix scoring ckise fir training missions to actually count medals, but + * nothing else. Code used to Assert when wings were granted then taken + * away because they were actually never granted in scoring structure + * + * 157 5/15/98 5:15p Dave + * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy + * status for team vs. team. Put in asserts to check for invalid team vs. + * team situations. + * + * 156 5/15/98 4:36p Allender + * fixed minor bug with wings + * + * 155 5/15/98 4:12p Allender + * removed redbook code. Put back in ingame join timer. Major fixups for + * stats in multiplayer. Pass correct score, medals, etc when leaving + * game. Be sure clients display medals, badges, etc. + * + * 154 5/15/98 2:25p Jasen + * temporarily disable granting of promotion and badges for clients + * + * 153 5/15/98 10:57a Allender + * fixed client side debriefings + * + * 152 5/15/98 10:36a Dave + * Removed 2 bogus bitmap drawing Int3()'s + * + * 151 5/14/98 2:44p Hoffoss + * Added wings awarding to debrief screen. + * + * 150 5/13/98 9:11p Lawrance + * Make 'replay mission' popup warn users about the loss of mission stats. + * + * $NoKeywords: $ + */ + +#include "missiondebrief.h" +#include "missionbriefcommon.h" +#include "missionscreencommon.h" +#include "missiongoals.h" +#include "missionpause.h" +#include "freespace.h" +#include "gamesequence.h" +#include "key.h" +#include "2d.h" +#include "ui.h" +#include "uidefs.h" +#include "gamesnd.h" +#include "sexp.h" +#include "parselo.h" +#include "audiostr.h" +#include "timer.h" +#include "bmpman.h" +#include "contexthelp.h" +#include "stats.h" +#include "player.h" +#include "chatbox.h" +#include "mouse.h" +#include "multi.h" +#include "multimsgs.h" +#include "multiutil.h" +#include "multiteamselect.h" +#include "multiui.h" +#include "eventmusic.h" +#include "font.h" +#include "popup.h" +#include "medals.h" +#include "multi_pinfo.h" +#include "contexthelp.h" +#include "multi_kick.h" +#include "multi_campaign.h" +#include "alphacolors.h" +#include "localize.h" +#include "multi_endgame.h" +#include "osapi.h" + +#define MAX_TOTAL_DEBRIEF_LINES 200 + +#define TEXT_TYPE_NORMAL 1 +#define TEXT_TYPE_RECOMMENDATION 2 + +#define DEBRIEF_NUM_STATS_PAGES 4 +#define DEBRIEF_MISSION_STATS 0 +#define DEBRIEF_MISSION_KILLS 1 +#define DEBRIEF_ALLTIME_STATS 2 +#define DEBRIEF_ALLTIME_KILLS 3 + +// 3rd coord is max width in pixels +int Debrief_title_coords[GR_NUM_RESOLUTIONS][3] = { + { // GR_640 + 18, 118, 174 + }, + { // GR_1024 + 28, 193, 280 + } +}; + +int Debrief_text_wnd_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 43, 140, 339, 303 + }, + { // GR_1024 + 69, 224, 535, 485 + } +}; + +int Debrief_text_x2[GR_NUM_RESOLUTIONS] = { + 276, // GR_640 + 450 // GR_1024 +}; + +int Debrief_stage_info_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 379, 137 + }, + { // GR_1024 + 578, 224 + } +}; + +int Debrief_more_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 323, 453 + }, + { // GR_1024 + 323, 453 + } +}; + +#define MULTI_LIST_TEAM_OFFSET 16 + +int Debrief_multi_list_team_max_display[GR_NUM_RESOLUTIONS] = { + 9, // GR_640 + 12 // GR_1024 +}; + +int Debrief_list_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 416, 280, 195, 101 + }, + { // GR_1024 + 666, 448, 312, 162 + } +}; + +int Debrief_award_wnd_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 411, 126 + }, + { // GR_1024 + 658, 203 + } +}; + + +int Debrief_award_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 416, 140 + }, + { // GR_1024 + 666, 224 + } +}; + +// 0=x, 1=y, 2=width of the field +int Debrief_medal_text_coords[GR_NUM_RESOLUTIONS][3] = { + { // GR_640 + 423, 247, 189 + }, + { // GR_1024 + 666, 333, 67 + } +}; + +// 0=x, 1=y, 2=height of the field +int Debrief_award_text_coords[GR_NUM_RESOLUTIONS][3] = { + { // GR_640 + 416, 210, 42 + }, + { // GR_1024 + 666, 333, 67 + } +}; + +// 0 = with medal +// 1 = without medal (text will use medal space) +#define DB_WITH_MEDAL 0 +#define DB_WITHOUT_MEDAL 1 +int Debrief_award_text_width[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 123, 203 + }, + { // GR_1024 + 196, 312 + } +}; + +char *Debrief_single_name[GR_NUM_RESOLUTIONS] = { + "DebriefSingle", // GR_640 + "2_DebriefSingle" // GR_1024 +}; +char *Debrief_multi_name[GR_NUM_RESOLUTIONS] = { + "DebriefMulti", // GR_640 + "2_DebriefMulti" // GR_1024 +}; +char *Debrief_mask_name[GR_NUM_RESOLUTIONS] = { + "Debrief-m", // GR_640 + "2_Debrief-m" // GR_1024 +}; + +#define NUM_BUTTONS 18 +#define NUM_TABS 2 + +#define DEBRIEF_TAB 0 +#define STATS_TAB 1 +#define TEXT_SCROLL_UP 2 +#define TEXT_SCROLL_DOWN 3 +#define REPLAY_MISSION 4 +#define RECOMMENDATIONS 5 +#define FIRST_STAGE 6 +#define PREV_STAGE 7 +#define NEXT_STAGE 8 +#define LAST_STAGE 9 +#define MULTI_PINFO_POPUP 10 +#define MULTI_KICK 11 +#define MEDALS_BUTTON 12 +#define PLAYER_SCROLL_UP 13 +#define PLAYER_SCROLL_DOWN 14 +#define HELP_BUTTON 15 +#define OPTIONS_BUTTON 16 +#define ACCEPT_BUTTON 17 + +#define REPEAT 1 + +//XSTR:OFF +char* Debrief_loading_bitmap_fname[GR_NUM_RESOLUTIONS] = { + "PleaseWait", // GR_640 + "2_PleaseWait" // GR_1024 +}; + +//XSTR:ON + +typedef struct { + char text[NAME_LENGTH+1]; // name of ship type with a colon + int num; // how many ships of this type player has killed +} debrief_stats_kill_info; + +typedef struct { + int net_player_index; // index into Net_players[] array + int rank_bitmap; // bitmap id for rank + char callsign[CALLSIGN_LEN]; +} debrief_multi_list_info; + +ui_button_info Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { + { // GR_640 + ui_button_info("DB_00", 6, 1, 37, 7, 0), // debriefing + ui_button_info("DB_01", 6, 21, 37, 23, 1), // statistics + ui_button_info("DB_02", 1, 195, -1, -1, 2), // scroll stats up + ui_button_info("DB_03", 1, 236, -1, -1, 3), // scroll stats down + ui_button_info("DB_04", 1, 428, 49, 447, 4), // replay mission + ui_button_info("DB_05", 17, 459, 49, 464, 5), // recommendations + ui_button_info("DB_06", 323, 454, -1, -1, 6), // first page + ui_button_info("DB_07", 348, 454, -1, -1, 7), // prev page + ui_button_info("DB_08", 372, 454, -1, -1, 8), // next page + ui_button_info("DB_09", 396, 454, -1, -1, 9), // last page + ui_button_info("DB_10", 441, 384, 433, 413, 10), // pilot info + ui_button_info("DB_11", 511, 384, 510, 413, 11), // kick + ui_button_info("DB_12", 613, 226, -1, -1, 12), // medals + ui_button_info("DB_13", 615, 329, -1, -1, 13), // scroll pilots up + ui_button_info("DB_14", 615, 371, -1, -1, 14), // scroll pilots down + ui_button_info("DB_15", 538, 431, 500, 440, 15), // help + ui_button_info("DB_16", 538, 455, 479, 464, 16), // options + ui_button_info("DB_17", 573, 432, 572, 413, 17), // accept + }, + { // GR_1024 + ui_button_info("2_DB_00", 10, 1, 59, 12, 0), // debriefing + ui_button_info("2_DB_01", 10, 33, 59, 37, 1), // statistics + ui_button_info("2_DB_02", 1, 312, -1, -1, 2), // scroll stats up + ui_button_info("2_DB_03", 1, 378, -1, -1, 3), // scroll stats down + ui_button_info("2_DB_04", 1, 685, 79, 715, 4), // replay mission + ui_button_info("2_DB_05", 28, 735, 79, 743, 5), // recommendations + ui_button_info("2_DB_06", 517, 726, -1, -1, 6), // first page + ui_button_info("2_DB_07", 556, 726, -1, -1, 7), // prev page + ui_button_info("2_DB_08", 595, 726, -1, -1, 8), // next page + ui_button_info("2_DB_09", 633, 726, -1, -1, 9), // last page + ui_button_info("2_DB_10", 706, 615, 700, 661, 10), // pilot info + ui_button_info("2_DB_11", 817, 615, 816, 661, 11), // kick + ui_button_info("2_DB_12", 981, 362, -1, -1, 12), // medals + ui_button_info("2_DB_13", 984, 526, -1, -1, 13), // scroll pilots up + ui_button_info("2_DB_14", 984, 594, -1, -1, 14), // scroll pilots down + ui_button_info("2_DB_15", 861, 689, 801, 705, 15), // help + ui_button_info("2_DB_16", 861, 728, 777, 744, 16), // options + ui_button_info("2_DB_17", 917, 692, 917, 692, 17), // accept + } +}; + +// text +#define NUM_DEBRIEF_TEXT 10 +#define MP_TEXT_INDEX_1 4 +#define MP_TEXT_INDEX_2 5 +#define MP_TEXT_INDEX_3 6 +UI_XSTR Debrief_strings[GR_NUM_RESOLUTIONS][NUM_DEBRIEF_TEXT] = { + { // GR_640 + { "Debriefing", 804, 37, 7, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][DEBRIEF_TAB].button }, + { "Statistics", 1333, 37, 26, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][STATS_TAB].button }, + { "Replay Mission", 444, 49, 447, UI_XSTR_COLOR_PINK, -1, &Buttons[0][REPLAY_MISSION].button }, + { "Recommendations", 1334, 49, 464, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][RECOMMENDATIONS].button }, + { "Pilot", 1310, 433, 413, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][MULTI_PINFO_POPUP].button }, + { "Info", 1311, 433, 423, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][MULTI_PINFO_POPUP].button }, + { "Kick", 1266, 510, 413, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][MULTI_KICK].button }, + { "Help", 928, 500, 440, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][HELP_BUTTON].button }, + { "Options", 1036, 479, 464, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][OPTIONS_BUTTON].button }, + { "Accept", 1035, 572, 413, UI_XSTR_COLOR_PINK, -1, &Buttons[0][ACCEPT_BUTTON].button }, + }, + { // GR_1024 + { "Debriefing", 804, 59, 12, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][DEBRIEF_TAB].button }, + { "Statistics", 1333, 59, 47, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][STATS_TAB].button }, + { "Replay Mission", 444, 79, 715, UI_XSTR_COLOR_PINK, -1, &Buttons[1][REPLAY_MISSION].button }, + { "Recommendations", 1334, 79, 743, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][RECOMMENDATIONS].button }, + { "Pilot", 1310, 700, 661, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][MULTI_PINFO_POPUP].button }, + { "Info", 1311, 700, 679, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][MULTI_PINFO_POPUP].button }, + { "Kick", 1266, 816, 661, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][MULTI_KICK].button }, + { "Help", 928, 801, 705, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][HELP_BUTTON].button }, + { "Options", 1036, 780, 744, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][OPTIONS_BUTTON].button }, + { "Accept", 1035, 917, 672, UI_XSTR_COLOR_PINK, -1, &Buttons[1][ACCEPT_BUTTON].button }, + } +}; + + +char Debrief_current_callsign[CALLSIGN_LEN+10]; +player *Debrief_player; + +static UI_WINDOW Debrief_ui_window; +static UI_BUTTON List_region; +static int Background_bitmap; // bitmap for the background of the debriefing +static int Award_bg_bitmap; +static int Debrief_multi_loading_bitmap; +static int Rank_bitmap; +static int Medal_bitmap; +static int Badge_bitmap; +static int Wings_bitmap; +static int Crest_bitmap; +//static int Rank_text_bitmap; +//static int Medal_text_bitmap; +//static int Badge_text_bitmap; +static int Promoted; +static int Debrief_accepted; +static int Turned_traitor; +static int Must_replay_mission; + +static int Current_mode; +static int New_mode; +static int Recommend_active; +static int Award_active; +static int Text_offset; +static int Num_text_lines = 0; +static int Num_debrief_lines = 0; +static int Num_normal_debrief_lines = 0; +static int Text_type[MAX_TOTAL_DEBRIEF_LINES]; +static char *Text[MAX_TOTAL_DEBRIEF_LINES]; + +static int Debrief_inited = 0; +static int New_stage; +static int Current_stage; +static int Num_stages; +static int Num_debrief_stages; +static int Stage_voice; + +static int Multi_list_size; +static int Multi_list_offset; + +int Debrief_multi_stages_loaded = 0; +int Debrief_multi_voice_loaded = 0; + +// static int Debrief_voice_ask_for_cd; + +// voice id's for debriefing text +static int Debrief_voices[MAX_DEBRIEF_STAGES]; + +#define DEBRIEF_VOICE_DELAY 400 // time to delay voice playback when a new stage starts +static int Debrief_cue_voice; // timestamp to cue the playback of the voice +static int Debrief_first_voice_flag = 1; // used to delay the first voice playback extra long + +// pointer used for getting to debriefing information +debriefing Traitor_debriefing; // used when player is a traitor + +// pointers to the active stages for this debriefing +static debrief_stage *Debrief_stages[MAX_DEBRIEF_STAGES]; +static debrief_stage Promotion_stage, Badge_stage; +static debrief_stats_kill_info Debrief_stats_kills[MAX_SHIP_TYPES]; +static debrief_multi_list_info Multi_list[MAX_PLAYERS]; +int Multi_list_select; + +// flag indicating if we should display info for the given player (in multiplayer) +int Debrief_should_show_popup = 1; + +// already shown skip mission popup? +static int Debrief_skip_popup_already_shown = 0; + +void debrief_text_init(); +void debrief_accept(int ok_to_post_start_game_event = 1); +void debrief_kick_selected_player(); + + +// promotion voice selection stuff +#define NUM_VOLITION_CAMPAIGNS 1 +struct { + char campaign_name[32]; + int num_missions; +} Volition_campaigns[NUM_VOLITION_CAMPAIGNS] = { + { + BUILTIN_CAMPAIGN, // the only campaign for now, but this leaves room for a mission pack + 35 // make sure this is equal to the number of missions you gave in the corresponding Debrief_promotion_voice_mapping + } +}; + +// data for which voice goes w/ which mission +typedef struct voice_map { + char mission_file[32]; + int persona_index; +} voice_map; + +voice_map Debrief_promotion_voice_mapping[NUM_VOLITION_CAMPAIGNS][MAX_CAMPAIGN_MISSIONS] = { + { // FreeSpace2 campaign + { "SM1-01.fs2", 1 }, + { "SM1-02.fs2", 1 }, + { "SM1-03.fs2", 1 }, + { "SM1-04.fs2", 2 }, + { "SM1-05.fs2", 2 }, + { "SM1-06.fs2", 2 }, + { "SM1-07.fs2", 2 }, + { "SM1-08.fs2", 3 }, + { "SM1-09.fs2", 3 }, + { "SM1-10.fs2", 3 }, + + { "SM2-01.fs2", 6 }, + { "SM2-02.fs2", 6 }, + { "SM2-03.fs2", 6 }, + { "SM2-04.fs2", 7 }, + { "SM2-05.fs2", 7 }, + { "SM2-06.fs2", 7 }, + { "SM2-07.fs2", 8 }, + { "SM2-08.fs2", 8 }, + { "SM2-09.fs2", 8 }, + { "SM2-10.fs2", 8 }, + + { "SM3-01.fs2", 8 }, + { "SM3-02.fs2", 8 }, + { "SM3-03.fs2", 8 }, + { "SM3-04.fs2", 8 }, + { "SM3-05.fs2", 8 }, + { "SM3-06.fs2", 9 }, + { "SM3-07.fs2", 9 }, + { "SM3-08.fs2", 9 }, + { "SM3-09.fs2", 9 }, + { "SM3-10.fs2", 9 }, // no debriefing for 3-10 + + { "loop1-1.fs2", 4 }, + { "loop1-2.fs2", 4 }, + { "loop1-3.fs2", 5 }, + { "loop2-1.fs2", 4 }, + { "loop2-2.fs2", 4 } + } +}; + +#define DB_AWARD_WINGS 0 +#define DB_AWARD_MEDAL 1 +#define DB_AWARD_SOC 2 +#define DB_AWARD_RANK 3 +#define DB_AWARD_BADGE 4 +#define DB_AWARD_BG 5 +static char* Debrief_award_filename[GR_NUM_RESOLUTIONS][6] = { + { + "DebriefWings", + "DebriefMedal", + "DebriefCrest", + "DebriefRank", + "DebriefBadge", + "DebriefAward" + }, + { + "2_DebriefWings", + "2_DebriefMedal", + "2_DebriefCrest", + "2_DebriefRank", + "2_DebriefBadge", + "2_DebriefAward" + } +}; + +#define AWARD_TEXT_MAX_LINES 5 +#define AWARD_TEXT_MAX_LINE_LENGTH 128 +char Debrief_award_text[AWARD_TEXT_MAX_LINES][AWARD_TEXT_MAX_LINE_LENGTH]; +int Debrief_award_text_num_lines = 0; + + + +// prototypes, you know you love 'em +void debrief_add_award_text(char *str); +void debrief_award_text_clear(); + + + +// functions +char *debrief_tooltip_handler(char *str) +{ + if (!stricmp(str, NOX("@.Medal"))) { + if (Award_active){ + return XSTR( "Medal", 435); + } + + } else if (!stricmp(str, NOX("@.Rank"))) { + if (Award_active){ + return XSTR( "Rank", 436); + } + + } else if (!stricmp(str, NOX("@.Badge"))) { + if (Award_active){ + return XSTR( "Badge", 437); + } + + } else if (!stricmp(str, NOX("@Medal"))) { + if (Medal_bitmap >= 0){ + return Medals[Player->stats.m_medal_earned].name; + } + + } else if (!stricmp(str, NOX("@Rank"))) { + if (Rank_bitmap >= 0){ + return Ranks[Promoted].name; + } + + } else if (!stricmp(str, NOX("@Badge"))) { + if (Badge_bitmap >= 0){ + return Medals[Badge_index[Player->stats.m_badge_earned]].name; + } + } + + return NULL; +} + +// initialize the array of handles to the different voice streams +void debrief_voice_init() +{ + int i; + + for (i=0; i 5 ) { + break; + } + + Debrief_voices[voice_num] = audiostream_open( name, ASF_VOICE ); + if ( Debrief_voices[voice_num] >= 0 ) { + break; + } + + // Don't bother to ask for the CD in multiplayer + if ( Game_mode & GM_MULTIPLAYER ) { + break; + } + + // couldn't load voice, ask user to insert CD (if necessary) + + // if ( Debrief_voice_ask_for_cd ) { + // if ( game_do_cd_check() == 0 ) { + // Debrief_voice_ask_for_cd = 0; + // break; + // } + // } + } +} + +// open and pre-load the stream buffers for the different voice streams +void debrief_voice_load_all() +{ + int i; + + // Debrief_voice_ask_for_cd = 1; + + for ( i=0; ivoice) <= 0 ) { + continue; + } + if ( strnicmp(Debrief_stages[i]->voice, NOX("none"), 4) ) { + debrief_load_voice_file(i, Debrief_stages[i]->voice); +// Debrief_voices[i] = audiostream_open(Debrief_stages[i]->voice, ASF_VOICE); + } + } +} + +// close all the briefing voice streams +void debrief_voice_unload_all() +{ + int i; + + for ( i=0; i= Num_debrief_stages){ + return; + } + + // if in delayed start, see if delay has elapsed and start voice if so + if (Debrief_cue_voice) { + if (!timestamp_elapsed(Debrief_cue_voice)){ + return; + } + + Stage_voice++; // move up to next voice + if ((Stage_voice < Num_debrief_stages) && (Debrief_voices[Stage_voice] >= 0)) { + audiostream_play(Debrief_voices[Stage_voice], Master_voice_volume, 0); + Debrief_cue_voice = 0; // indicate no longer in delayed start checking + } + + return; + } + + // see if voice is still playing. If so, do nothing yet. + if ((Stage_voice >= 0) && audiostream_is_playing(Debrief_voices[Stage_voice])){ + return; + } + + // set voice to play in a little while from now. + Debrief_cue_voice = timestamp(DEBRIEF_VOICE_DELAY); +} + +// stop playback of the voice for a particular briefing stage +void debrief_voice_stop() +{ + if ((Stage_voice < 0) || (Stage_voice > Num_debrief_stages) || (Debrief_voices[Stage_voice] < 0)) + return; + + audiostream_stop(Debrief_voices[Stage_voice]); // stream is automatically rewound + Stage_voice = -1; +} + +// function to deal with inserting possible promition and badge stages into the debriefing +// on the clients +void debrief_multi_fixup_stages() +{ + int i; + + // possibly insert the badge stage first, them the promotion stage since they are + // inserted at the front of the debrief stages. + if ( Badge_bitmap >= 0 ) { + // move all stages forward one. Don't + for ( i = Num_debrief_stages; i > 0; i-- ) { + Debrief_stages[i] = Debrief_stages[i-1]; + } + Debrief_stages[0] = &Badge_stage; + Num_debrief_stages++; + } + + if ( Promoted >= 0) { + // move all stages forward one + for ( i = Num_debrief_stages; i > 0; i-- ) { + Debrief_stages[i] = Debrief_stages[i-1]; + } + Debrief_stages[0] = &Promotion_stage; + Num_debrief_stages++; + } +} + + +// function called from multiplayer clients to set up the debriefing information for them +// (sent from the server). +void debrief_set_multi_clients( int stage_count, int active_stages[] ) +{ + int i; + + // set up the right briefing for this guy + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){ + Debriefing = &Debriefings[Net_player->p_info.team]; + } else { + Debriefing = &Debriefings[0]; + } + + // see if this client was promoted -- if so, then add the first stage. + Num_debrief_stages = 0; + + // set the pointers to the debriefings for this client + for (i = 0; i < stage_count; i++) { + Debrief_stages[Num_debrief_stages++] = &Debriefing->stages[active_stages[i]]; + } + + Debrief_multi_stages_loaded = 1; +} + +// evaluate all stages for all teams. Server of a multiplayer game will have to send that +// information to all clients after leaving this screen. +void debrief_multi_server_stuff() +{ + debriefing *debriefp; + + int stage_active[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS]; + int i, j, num_stages, stage_count[MAX_TEAMS]; + + memset( stage_active, 0, sizeof(stage_active) ); + + for (i=0; inum_stages; j++) { + if ( eval_sexp(debriefp->stages[j].formula) ) { + stage_active[i][num_stages] = j; + num_stages++; + } + } + + stage_count[i] = num_stages; + } + + // if we're in campaign mode, evaluate campaign stuff + if (Netgame.campaign_mode == MP_CAMPAIGN) { + multi_campaign_eval_debrief(); + } + + // send the information to all clients. + send_debrief_info( stage_count, stages ); +} + + +// -------------------------------------------------------------------------------------- +// debrief_set_stages_and_multi_stuff() +// +// Set up the active stages for this debriefing +// +// returns: number of active debriefing stages +// +int debrief_set_stages_and_multi_stuff() +{ + int i; + debriefing *debriefp; + + if ( MULTIPLAYER_CLIENT ) { + return 0; + } + + Num_debrief_stages = 0; + + if ( Game_mode & GM_MULTIPLAYER ) { + debrief_multi_server_stuff(); + } + + // check to see if player is a traitor (looking at his team). If so, use the special + // traitor debriefing. Only done in single player + debriefp = Debriefing; + if ( !(Game_mode & GM_MULTIPLAYER) ) { + if (Player_ship->team == TEAM_TRAITOR) + debriefp = &Traitor_debriefing; + } + + Num_debrief_stages = 0; + if (Promoted >= 0) { + Debrief_stages[Num_debrief_stages++] = &Promotion_stage; + } + + if (Badge_bitmap >= 0) { + Debrief_stages[Num_debrief_stages++] = &Badge_stage; + } + + for (i=0; inum_stages; i++) { + if (eval_sexp(debriefp->stages[i].formula) == 1) { + Debrief_stages[Num_debrief_stages++] = &debriefp->stages[i]; + } + } + + return Num_debrief_stages; +} + +// init the buttons that are specific to the debriefing screen +void debrief_buttons_init() +{ + ui_button_info *b; + int i; + + for ( i=0; ibutton.create( &Debrief_ui_window, "", b->x, b->y, 60, 30, 0 /*b->flags & REPEAT*/, 1 ); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action( common_play_highlight_sound ); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // add all xstrs + for(i=0; iflags & NETINFO_FLAG_GAME_HOST)){ + Buttons[gr_screen.res][REPLAY_MISSION].button.disable(); + } + } +} + +// -------------------------------------------------------------------------------------- +// debrief_ui_init() +// +void debrief_ui_init() +{ + // init ship selection masks and buttons + common_set_interface_palette("DebriefPalette"); // set the interface palette + Debrief_ui_window.create( 0, 0, gr_screen.max_w, gr_screen.max_h, 0 ); + Debrief_ui_window.set_mask_bmap(Debrief_mask_name[gr_screen.res]); + Debrief_ui_window.tooltip_handler = debrief_tooltip_handler; + debrief_buttons_init(); + + // load in help overlay bitmap + help_overlay_load(DEBRIEFING_OVERLAY); + help_overlay_set_state(DEBRIEFING_OVERLAY,0); + + if ( Game_mode & GM_MULTIPLAYER ) { + // close down any old instances of the chatbox + chatbox_close(); + + // create the new one + chatbox_create(); + Background_bitmap = bm_load(Debrief_multi_name[gr_screen.res]); + List_region.create(&Debrief_ui_window, "", Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1], Debrief_list_coords[gr_screen.res][2], Debrief_list_coords[gr_screen.res][3], 0, 1); + List_region.hide(); + } else { + Background_bitmap = bm_load(Debrief_single_name[gr_screen.res]); + } + + if ( Background_bitmap < 0 ) { + Warning(LOCATION, "Could not load the background bitmap for debrief screen"); + } + + Award_bg_bitmap = bm_load(Debrief_award_filename[gr_screen.res][DB_AWARD_BG]); + Debrief_multi_loading_bitmap = bm_load(Debrief_loading_bitmap_fname[gr_screen.res]); +} + +// sets Promotion_stage.voice +// defaults to number 9 (Petrarch) for non-volition missions +// this is an ugly, nasty way of doing this, but it saves us changing the missions at this point +void debrief_choose_promotion_voice() +{ + int i, j; + + if(Campaign.current_mission < 0){ + sprintf(Promotion_stage.voice, NOX("9_%s"), Ranks[Promoted].promotion_voice_base); + return; + } + + // search thru all official campaigns for our current campaign + if ((Campaign.missions[Campaign.current_mission].name) && (Campaign.filename)) { + for (i=0; istats.m_badge_earned].voice_base); + } + + if ((Campaign.missions[Campaign.current_mission].name) && (Campaign.filename)) { + // search thru all official campaigns for our current campaign + for (i=0; istats.m_badge_earned].voice_base); + return; + } + } + } + } + } + + // default to petrarch + sprintf(Badge_stage.voice, NOX("9_%s"), Badge_info[Player->stats.m_badge_earned].voice_base); +} + + +void debrief_award_init() +{ + char buf[80]; + int i; + + Rank_bitmap = -1; + Medal_bitmap = -1; + Badge_bitmap = -1; + Wings_bitmap = -1; + Crest_bitmap = -1; + Promoted = -1; + + // be sure there are no old award texts floating around + debrief_award_text_clear(); + + // handle medal earned + if (Player->stats.m_medal_earned != -1) { + if (Player->stats.m_medal_earned == 13) { // special hack for the wings.. + int ver; + if ( Player->stats.medals[13] > 1 ) { + ver = 1; + } else { + ver = 0; + } + sprintf(buf, NOX("%s%0.2d"), Debrief_award_filename[gr_screen.res][DB_AWARD_WINGS], ver); + Wings_bitmap = bm_load(buf); + + } else if (Player->stats.m_medal_earned == 17) { // special hack for the soc crest + Crest_bitmap = bm_load(Debrief_award_filename[gr_screen.res][DB_AWARD_SOC]); + } else { + sprintf(buf, NOX("%s%0.2d"), Debrief_award_filename[gr_screen.res][DB_AWARD_MEDAL], Player->stats.m_medal_earned); + Medal_bitmap = bm_load(buf); + } + + debrief_add_award_text(Medals[Player->stats.m_medal_earned].name); + } + + // handle promotions + if ( Player->stats.m_promotion_earned != -1 ) { + Promoted = Player->stats.m_promotion_earned; + sprintf(buf, NOX("%s%0.2d"), Debrief_award_filename[gr_screen.res][DB_AWARD_RANK], Promoted + 1); + Rank_bitmap = bm_load(buf); + + Promotion_stage.new_text = Ranks[Promoted].promotion_text; + Promotion_stage.new_recommendation_text = NULL; + + // choose appropriate promotion voice for this mission + debrief_choose_promotion_voice(); + + debrief_add_award_text(Ranks[Promoted].name); + } + + // handle badge earned + // only grant badge if earned and allowed. (no_promotion really means no promotion and no badges) + if ( Player->stats.m_badge_earned != -1 ) { + i = Player->stats.m_badge_earned; + sprintf(buf, NOX("%s%0.2d"), Debrief_award_filename[gr_screen.res][DB_AWARD_BADGE], i + 1); + Badge_bitmap = bm_load(buf); + + Badge_stage.new_text = Badge_info[i].promotion_text; + Badge_stage.new_recommendation_text = NULL; + + // choose appropriate voice + debrief_choose_badge_voice(); + + debrief_add_award_text(Medals[Badge_index[i]].name); + } + + if ((Rank_bitmap >= 0) || (Medal_bitmap >= 0) || (Badge_bitmap >= 0) || (Wings_bitmap >= 0) || (Crest_bitmap >= 0)) { + Award_active = 1; + } else { + Award_active = 0; + } +} + +// debrief_traitor_init() initializes local data which could be used if the player leaves the +// mission a traitor. The same debriefing always gets played +void debrief_traitor_init() +{ + static int inited = 0; + + if ( !inited ) { + debriefing *debrief; + debrief_stage *stagep; + int rval; + int stage_num; + + if ((rval = setjmp(parse_abort)) != 0) { + Error(LOCATION, "Unable to parse traitor.tbl! Code = %i.\n", rval); + } + else { + read_file_text("traitor.tbl"); + reset_parse(); + } + + // open localization + lcl_ext_open(); + + // simplied form of the debriefing stuff. + debrief = &Traitor_debriefing; + required_string("#Debriefing_info"); + + required_string("$Num stages:"); + stuff_int(&debrief->num_stages); + Assert(debrief->num_stages == 1); + + stage_num = 0; + stagep = &debrief->stages[stage_num++]; + required_string("$Formula:"); + stagep->formula = get_sexp_main(); + required_string("$multi text"); + if ( Fred_running ) { + stuff_string( stagep->new_text, F_MULTITEXT, NULL, MAX_DEBRIEF_LEN); + } else { + stagep->new_text = stuff_and_malloc_string( F_MULTITEXT, NULL, MAX_DEBRIEF_LEN); + } + required_string("$Voice:"); + char traitor_voice_file[NAME_LENGTH]; + stuff_string(traitor_voice_file, F_FILESPEC, NULL); + +// DKA 9/13/99 Only 1 traitor msg for FS2 +// if ( Player->on_bastion ) { +// strcpy(stagep->voice, NOX("3_")); +// } else { +// strcpy(stagep->voice, NOX("1_")); +// } + + strcat(stagep->voice, traitor_voice_file); + + required_string("$Recommendation text:"); + if ( Fred_running ) { + stuff_string( stagep->new_recommendation_text, F_MULTITEXT, NULL, MAX_RECOMMENDATION_LEN); + } else { + stagep->new_recommendation_text = stuff_and_malloc_string( F_MULTITEXT, NULL, MAX_RECOMMENDATION_LEN); + } + inited = 1; + + // close localization + lcl_ext_close(); + } + + // disable the accept button if in single player and I am a traitor + Debrief_accepted = 0; + Turned_traitor = Must_replay_mission = 0; + if (!(Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_CAMPAIGN_MODE)) { + if (Player_ship->team == TEAM_TRAITOR){ + Turned_traitor = 1; + } + + if (Campaign.next_mission == Campaign.current_mission){ + Must_replay_mission = 1; + } + } + + if (Turned_traitor || Must_replay_mission) { + Buttons[gr_screen.res][ACCEPT_BUTTON].button.hide(); + + // kill off any stats + Player->flags &= ~PLAYER_FLAGS_PROMOTED; + scoring_level_init( &Player->stats ); + } +} + +// initialization for listing of players in game +void debrief_multi_list_init() +{ + Multi_list_size = 0; // number of net players to choose from + Multi_list_offset = 0; + + Multi_list_select = -1; + + if ( !(Game_mode & GM_MULTIPLAYER) ) + return; + + debrief_rebuild_player_list(); + + // switch stats display to this newly selected player + set_player_stats(Multi_list[0].net_player_index); + strcpy(Debrief_current_callsign, Multi_list[0].callsign); + Debrief_player = Player; +} + +void debrief_multi_list_scroll_up() +{ + // if we're at the beginning of the list, don't do anything + if(Multi_list_offset == 0){ + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // otherwise scroll up + Multi_list_offset--; + gamesnd_play_iface(SND_USER_SELECT); +} + +void debrief_multi_list_scroll_down() +{ + // if we can scroll down no further + if(Multi_list_size < Debrief_multi_list_team_max_display[gr_screen.res]){ + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + if((Multi_list_offset + Debrief_multi_list_team_max_display[gr_screen.res]) >= Multi_list_size){ + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + + // otherwise scroll down + Multi_list_offset++; + gamesnd_play_iface(SND_USER_SELECT); +} + +// draw the connected net players +void debrief_multi_list_draw() +{ + int y, z, font_height,idx; + char str[CALLSIGN_LEN+5]; + net_player *np; + + font_height = gr_get_font_height(); + + // if we currently have no item picked, pick a reasonable one + if((Multi_list_size >= 0) && (Multi_list_select == -1)){ + // select the entry which corresponds to the local player + Multi_list_select = 0; + for(idx=0;idx= Multi_list_size){ + break; + } + // set the proper text color for the highlight + if(np->flags & NETINFO_FLAG_GAME_HOST){ + if(Multi_list_select == z){ + gr_set_color_fast(&Color_text_active_hi); + } else { + gr_set_color_fast(&Color_bright); + } + } else { + if(Multi_list_select == z){ + gr_set_color_fast(&Color_text_active); + } else { + gr_set_color_fast(&Color_text_normal); + } + } + + // blit the proper indicator - skipping observers + if(!((np->flags & NETINFO_FLAG_OBSERVER) && !(np->flags & NETINFO_FLAG_OBS_PLAYER))){ + if(Netgame.type_flags & NG_TYPE_TEAM){ + // team 0 + if(np->p_info.team == 0){ + // draw his "selected" icon + if(((np->state == NETPLAYER_STATE_DEBRIEF_ACCEPT) || (np->state == NETPLAYER_STATE_DEBRIEF_REPLAY)) && (Multi_common_icons[MICON_TEAM0_SELECT] != -1)){ + gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]); + gr_bitmap(Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1] + y - 2); + } + // draw his "normal" icon + else if(Multi_common_icons[MICON_TEAM0] != -1){ + gr_set_bitmap(Multi_common_icons[MICON_TEAM0]); + gr_bitmap(Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1] + y - 2); + } + } else if(np->p_info.team == 1){ + // draw his "selected" icon + if(((np->state == NETPLAYER_STATE_DEBRIEF_ACCEPT) || (np->state == NETPLAYER_STATE_DEBRIEF_REPLAY)) && (Multi_common_icons[MICON_TEAM1_SELECT] != -1)){ + gr_set_bitmap(Multi_common_icons[MICON_TEAM1_SELECT]); + gr_bitmap(Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1] + y - 2); + } + // draw his "normal" icon + else if(Multi_common_icons[MICON_TEAM1] != -1){ + gr_set_bitmap(Multi_common_icons[MICON_TEAM1]); + gr_bitmap(Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1] + y - 2); + } + } + } else { + // draw the team 0 selected icon + if(((np->state == NETPLAYER_STATE_DEBRIEF_ACCEPT) || (np->state == NETPLAYER_STATE_DEBRIEF_REPLAY)) && (Multi_common_icons[MICON_TEAM0_SELECT] != -1)){ + gr_set_bitmap(Multi_common_icons[MICON_TEAM0_SELECT]); + gr_bitmap(Debrief_list_coords[gr_screen.res][0], Debrief_list_coords[gr_screen.res][1] + y - 2); + } + } + } + + strcpy(str,Multi_list[z].callsign); + if(Net_players[Multi_list[z].net_player_index].flags & NETINFO_FLAG_OBSERVER && !(Net_players[Multi_list[z].net_player_index].flags & NETINFO_FLAG_OBS_PLAYER)){ + strcat(str,XSTR( "(O)", 438)); + } + + // bli + gr_string(Debrief_list_coords[gr_screen.res][0] + MULTI_LIST_TEAM_OFFSET, Debrief_list_coords[gr_screen.res][1] + y, str); + + y += font_height; + z++; + } +} + +void debrief_kick_selected_player() +{ + if(Multi_list_select >= 0){ + Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST); + multi_kick_player(Multi_list[Multi_list_select].net_player_index); + } +} + +// get optional mission popup text +void debrief_assemble_optional_mission_popup_text(char *buffer, char *mission_loop_desc) +{ + Assert(buffer != NULL); + // base message + + if (mission_loop_desc == NULL) { + strcpy(buffer, XSTR("", 1490)); + mprintf(("No mission loop description avail")); + } else { + strcpy(buffer, mission_loop_desc); + } + + strcat(buffer, XSTR("\n\n\nDo you want to play the optional mission?", 1491)); +} + +// what to do when the accept button is hit +void debrief_accept(int ok_to_post_start_game_event) +{ + int go_loop = 0; + + if ( (/*Cheats_enabled ||*/ Turned_traitor || Must_replay_mission) && (Game_mode & GM_CAMPAIGN_MODE) ) { + char *str; + int z; + + if (Game_mode & GM_MULTIPLAYER) { + return; + } + + if (Player_ship->team == TEAM_TRAITOR){ + str = XSTR( "Your career is over, Traitor! You can't accept new missions!", 439); + }/* else if (Cheats_enabled) { + str = XSTR( "You are a cheater. You cannot accept this mission!", 440); + }*/ else { + str = XSTR( "You have failed this mission and cannot accept. What do you you wish to do instead?", 441); + } + + z = popup(0, 3, XSTR( "Return to &Debriefing", 442), XSTR( "Go to &Flight Deck", 443), XSTR( "&Replay Mission", 444), str); + if (z == 2){ + gameseq_post_event(GS_EVENT_START_BRIEFING); // cycle back to briefing + } else if ( z == 1 ) { + gameseq_post_event(GS_EVENT_END_GAME); // return to main hall, tossing stats + } + + return; + } + + Debrief_accepted = 1; + // save mission stats + if (Game_mode & GM_MULTIPLAYER) { + // note that multi_debrief_accept_hit() will handle all mission_campaign_* calls on its own + // as well as doing stats transfers, etc. + multi_debrief_accept_hit(); + + } else { + + int play_commit_sound = 1; + // only write the player's stats if he's accepted + + // if we are just playing a single mission, then don't do many of the things + // that need to be done. Nothing much should happen when just playing a single + // mission that isn't in a campaign. + if ( Game_mode & GM_CAMPAIGN_MODE ) { + + // check for possible mission loop + // check for (1) mission loop available, (2) dont have to repeat last mission + if(!(Game_mode & GM_MULTIPLAYER)){ + int cur = Campaign.current_mission; + bool require_repeat_mission = (Campaign.current_mission == Campaign.next_mission); + if (Campaign.missions[cur].has_mission_loop) { + Assert(Campaign.loop_mission != CAMPAIGN_LOOP_MISSION_UNINITIALIZED); + } + + if ( (Campaign.missions[cur].has_mission_loop && (Campaign.loop_mission != -1)) && !require_repeat_mission ) { + /* + char buffer[512]; + debrief_assemble_optional_mission_popup_text(buffer, Campaign.missions[cur].mission_loop_desc); + + int choice = popup(0 , 2, POPUP_NO, POPUP_YES, buffer); + if (choice == 1) { + Campaign.loop_enabled = 1; + Campaign.next_mission = Campaign.loop_mission; + } + */ + go_loop = 1; + } + } + + // loopy loopy time + if (go_loop) { + if(ok_to_post_start_game_event){ + gameseq_post_event(GS_EVENT_LOOP_BRIEF); + } else { + play_commit_sound = 0; + } + } + // continue as normal + else { + // end the mission + mission_campaign_mission_over(); + + // check to see if we are out of the loop now + if ( Campaign.next_mission == Campaign.loop_reentry ) { + Campaign.loop_enabled = 0; + } + + // check if campaign is over + if ( Campaign.next_mission == -1 ) { + #if defined(FS2_DEMO) || defined(OEM_BUILD) + gameseq_post_event(GS_EVENT_END_DEMO); + #else + gameseq_post_event(GS_EVENT_MAIN_MENU); + #endif + } else { + if ( ok_to_post_start_game_event ) { + // CD CHECK + if(game_do_cd_mission_check(Game_current_mission_filename)){ + gameseq_post_event(GS_EVENT_START_GAME); + } else { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + } else { + play_commit_sound = 0; + } + } + } + } else { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + + if ( play_commit_sound ) { + gamesnd_play_iface(SND_COMMIT_PRESSED); + } + + game_flush(); + } +} + +void debrief_next_tab() +{ + New_mode = Current_mode + 1; + if (New_mode >= NUM_TABS) + New_mode = 0; +} + +void debrief_prev_tab() +{ + New_mode = Current_mode - 1; + if (New_mode < 0) + New_mode = NUM_TABS - 1; +} + +// -------------------------------------------------------------------------------------- +// debrief_next_stage() +// +void debrief_next_stage() +{ + if (Current_stage < Num_stages - 1) { + New_stage = Current_stage + 1; + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + + } else + gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL); +} + +// -------------------------------------------------------------------------------------- +// debrief_prev_stage() +// +void debrief_prev_stage() +{ + if (Current_stage) { + New_stage = Current_stage - 1; + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + + } else + gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL); +} + +// -------------------------------------------------------------------------------------- +// debrief_first_stage() +void debrief_first_stage() +{ + if (Current_stage) { + New_stage = 0; + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + + } else + gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL); +} + +// -------------------------------------------------------------------------------------- +// debrief_last_stage() +void debrief_last_stage() +{ + if (Current_stage != Num_stages - 1) { + New_stage = Num_stages - 1; + gamesnd_play_iface(SND_BRIEF_STAGE_CHG); + + } else + gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL); +} + +// draw what stage number the debriefing is on +void debrief_render_stagenum() +{ + int w; + char buf[64]; + + if (Num_stages < 2) + return; + + sprintf(buf, XSTR( "%d of %d", 445), Current_stage + 1, Num_stages); + gr_get_string_size(&w, NULL, buf); + gr_set_color_fast(&Color_bright_blue); + gr_string(Debrief_stage_info_coords[gr_screen.res][0] - w, Debrief_stage_info_coords[gr_screen.res][1], buf); + gr_set_color_fast(&Color_white); +} + +// render the mission time at the specified y location +void debrief_render_mission_time(int y_loc) +{ + char time_str[30]; + + game_format_time(Missiontime, time_str); + gr_string(0, y_loc, XSTR( "Mission Time", 446)); + gr_string(Debrief_text_x2[gr_screen.res], y_loc, time_str); +} + +// render out the debriefing text to the scroll window +void debrief_render() +{ + int y, z, font_height; + + if ( Num_stages <= 0 ) + return; + + font_height = gr_get_font_height(); + + gr_set_clip(Debrief_text_wnd_coords[gr_screen.res][0], Debrief_text_wnd_coords[gr_screen.res][1], Debrief_text_wnd_coords[gr_screen.res][2], Debrief_text_wnd_coords[gr_screen.res][3]); + y = 0; + z = Text_offset; + while (y + font_height <= Debrief_text_wnd_coords[gr_screen.res][3]) { + if (z >= Num_text_lines) + break; + + if (Text_type[z] == TEXT_TYPE_NORMAL) + gr_set_color_fast(&Color_white); + else + gr_set_color_fast(&Color_bright_red); + + if (Text[z]) + gr_string(0, y, Text[z]); + + y += font_height; + z++; + } + + gr_reset_clip(); +} + +// render out the stats info to the scroll window +// +void debrief_stats_render() +{ + int i, y, font_height; + + gr_set_color_fast(&Color_blue); + gr_set_clip(Debrief_text_wnd_coords[gr_screen.res][0], Debrief_text_wnd_coords[gr_screen.res][1], Debrief_text_wnd_coords[gr_screen.res][2], Debrief_text_wnd_coords[gr_screen.res][3]); + gr_string(0, 0, Debrief_current_callsign); + font_height = gr_get_font_height(); + y = 30; + + switch ( Current_stage ) { + case DEBRIEF_MISSION_STATS: + i = Current_stage - 1; + if ( i < 0 ) + i = 0; + + gr_set_color_fast(&Color_white); + + // display mission completion time + debrief_render_mission_time(y); + + y += 20; + show_stats_label(i, 0, y, font_height); + show_stats_numbers(i, Debrief_text_x2[gr_screen.res], y, font_height); + break; + case DEBRIEF_ALLTIME_STATS: + i = Current_stage - 1; + if ( i < 0 ) + i = 0; + + gr_set_color_fast(&Color_white); + show_stats_label(i, 0, y, font_height); + show_stats_numbers(i, Debrief_text_x2[gr_screen.res], y, font_height); + break; + + case DEBRIEF_ALLTIME_KILLS: + case DEBRIEF_MISSION_KILLS: + gr_set_color_fast(&Color_white); + i = Text_offset; + while (y + font_height <= Debrief_text_wnd_coords[gr_screen.res][3]) { + if (i >= Num_text_lines) + break; + + if (!i) { + if ( Current_stage == DEBRIEF_MISSION_KILLS ) + gr_printf(0, y, XSTR( "Mission Kills by Ship Type", 447)); + else + gr_printf(0, y, XSTR( "All-time Kills by Ship Type", 448)); + + } else if (i > 1) { + gr_printf(0, y, "%s", Debrief_stats_kills[i - 2].text); + gr_printf(Debrief_text_x2[gr_screen.res], y, "%d", Debrief_stats_kills[i - 2].num); + } + + y += font_height; + i++; + } + + if (Num_text_lines == 2) { + if ( Current_stage == DEBRIEF_MISSION_KILLS ) + gr_printf(0, y, XSTR( "(No ship kills this mission)", 449)); + else + gr_printf(0, y, XSTR( "(No ship kills)", 450)); + } + + break; + + default: + Int3(); + break; + } + + gr_reset_clip(); +} + +// do action for when the replay button is pressed +void debrief_replay_pressed() +{ + if (!Turned_traitor && !Must_replay_mission && (Game_mode & GM_CAMPAIGN_MODE)) { + int choice; + choice = popup(0, 2, POPUP_CANCEL, XSTR( "&Replay", 451), XSTR( "If you choose to replay this mission, you will be required to complete it again before proceeding to future missions.\n\nIn addition, any statistics gathered during this mission will be discarded if you choose to replay.", 452)); + + if (choice != 1){ + return; + } + } + + gameseq_post_event(GS_EVENT_START_BRIEFING); // take us to the briefing + gamesnd_play_iface(SND_COMMIT_PRESSED); +} + +// ------------------------------------------------------------------- +// debrief_redraw_pressed_buttons() +// +// Redraw any debriefing buttons that are pressed down. This function is needed +// since we sometimes need to draw pressed buttons last to ensure the entire +// button gets drawn (and not overlapped by other buttons) +// +void debrief_redraw_pressed_buttons() +{ + int i; + UI_BUTTON *b; + + for ( i=0; ibutton_down() ) { + b->draw_forced(2); + } + } +} + +// debrief specific button with hotspot 'i' has been pressed, so perform the associated action +// +void debrief_button_pressed(int num) +{ + switch (num) { + case DEBRIEF_TAB: + Buttons[gr_screen.res][RECOMMENDATIONS].button.enable(); + // Debrief_ui_window.use_hack_to_get_around_stupid_problem_flag = 0; + if (num != Current_mode){ + gamesnd_play_iface(SND_SCREEN_MODE_PRESSED); + } + New_mode = num; + break; + case STATS_TAB: + // Debrief_ui_window.use_hack_to_get_around_stupid_problem_flag = 1; // allows failure sound to be played + Buttons[gr_screen.res][RECOMMENDATIONS].button.disable(); + if (num != Current_mode){ + gamesnd_play_iface(SND_SCREEN_MODE_PRESSED); + } + New_mode = num; + break; + + case TEXT_SCROLL_UP: + if (Text_offset) { + Text_offset--; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + + case TEXT_SCROLL_DOWN: + if (Text_offset + Debrief_text_wnd_coords[gr_screen.res][3] / gr_get_font_height() < Num_text_lines) { + Text_offset++; + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + + case REPLAY_MISSION: + if(Game_mode & GM_MULTIPLAYER){ + multi_debrief_replay_hit(); + } else { + debrief_replay_pressed(); + } + break; + + case RECOMMENDATIONS: + gamesnd_play_iface(SND_USER_SELECT); + Recommend_active = !Recommend_active; + debrief_text_init(); + break; + + case FIRST_STAGE: + debrief_first_stage(); + break; + + case PREV_STAGE: + debrief_prev_stage(); + break; + + case NEXT_STAGE: + debrief_next_stage(); + break; + + case LAST_STAGE: + debrief_last_stage(); + break; + + case HELP_BUTTON: + gamesnd_play_iface(SND_HELP_PRESSED); + launch_context_help(); + break; + + case OPTIONS_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event( GS_EVENT_OPTIONS_MENU ); + break; + + case ACCEPT_BUTTON: + debrief_accept(); + break; + + case MEDALS_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event(GS_EVENT_VIEW_MEDALS); + break; + + case PLAYER_SCROLL_UP: + debrief_multi_list_scroll_up(); + break; + + case PLAYER_SCROLL_DOWN: + debrief_multi_list_scroll_down(); + break; + + case MULTI_PINFO_POPUP: + Debrief_should_show_popup = 1; + break; + + case MULTI_KICK: + debrief_kick_selected_player(); + break; + } // end swtich +} + +void debrief_setup_ship_kill_stats(int stage_num) +{ + int i; + ushort *kill_arr; + debrief_stats_kill_info *kill_info; + + Assert(Current_stage < DEBRIEF_NUM_STATS_PAGES); + if ( Current_stage == DEBRIEF_MISSION_STATS || Current_stage == DEBRIEF_ALLTIME_STATS ) + return; + + Assert(Debrief_player != NULL); + + // kill_ar points to an array of MAX_SHIP_TYPE ints + if ( Current_stage == DEBRIEF_MISSION_KILLS ) { + kill_arr = Debrief_player->stats.m_okKills; + } else { + kill_arr = Debrief_player->stats.kills; + } + + Num_text_lines = 0; + for ( i=0; inum = kill_arr[i]; + + strcpy(kill_info->text, Ship_info[i].name); + strcat(kill_info->text, NOX(":")); + } + + Num_text_lines += 2; +} + +// Iterate through the debriefing buttons, checking if they are pressed +void debrief_check_buttons() +{ + int i, y, z; + + for ( i=0; i= 0) && (z < Multi_list_size)) { + // switch stats display to this newly selected player + set_player_stats(Multi_list[z].net_player_index); + strcpy(Debrief_current_callsign, Multi_list[z].callsign); + Debrief_player = Net_players[Multi_list[z].net_player_index].player; + Multi_list_select = z; + debrief_setup_ship_kill_stats(Current_stage); + gamesnd_play_iface(SND_USER_SELECT); + } + } + + // if the player was double clicked on - we should popup a player info popup + /* + if (List_region.double_clicked()) { + Debrief_should_show_popup = 1; + } + */ +} + +void debrief_text_stage_init(char *src, int type) +{ + int i, n_lines, n_chars[MAX_DEBRIEF_LINES]; + char line[MAX_DEBRIEF_LINE_LEN]; + char *p_str[MAX_DEBRIEF_LINES]; + + n_lines = split_str(src, Debrief_text_wnd_coords[gr_screen.res][2], n_chars, p_str, MAX_DEBRIEF_LINES); + Assert(n_lines >= 0); + + // if you hit this, you proba + if(n_lines >= MAX_DEBRIEF_LINES){ + Warning(LOCATION, "You have come close to the limit of debriefing lines, try adding more stages"); + } + + for ( i=0; inew_text; + if (src) + debrief_text_stage_init(src, TEXT_TYPE_NORMAL); + + if (Recommend_active) { + src = Debrief_stages[i]->new_recommendation_text; + if (!src && (i == Num_debrief_stages - 1) && !r_count) + src = XSTR( "We have no recommendations for you.", 1054); + + if (src) { + Text[Num_text_lines++] = NULL; + debrief_text_stage_init(src, TEXT_TYPE_RECOMMENDATION); + r_count++; + } + } + } + + Num_debrief_lines = Num_text_lines; + return; + } + + // not in debriefing mode, must be in stats mode + Num_text_lines = 0; + debrief_setup_ship_kill_stats(Current_stage); +} + + +// -------------------------------------------------------------------------------------- +// +void debrief_init() +{ + Assert(!Debrief_inited); +// Campaign.loop_enabled = 0; + Campaign.loop_mission = CAMPAIGN_LOOP_MISSION_UNINITIALIZED; + + // set up the right briefing for this guy + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){ + Debriefing = &Debriefings[Net_player->p_info.team]; + } else { + Debriefing = &Debriefings[0]; + } + + // no longer is mission + Game_mode &= ~(GM_IN_MISSION); + + game_flush(); + Current_mode = -1; + New_mode = DEBRIEF_TAB; + Recommend_active = Award_active = 0; + Current_stage = 0; + + Current_stage = -1; + New_stage = 0; + Debrief_cue_voice = 0; + Num_text_lines = Num_debrief_lines = 0; + Debrief_first_voice_flag = 1; + + Debrief_multi_voice_loaded = 0; + + if ( (Game_mode & GM_CAMPAIGN_MODE) && ( !MULTIPLAYER_CLIENT ) ) { + // MUST store goals and events first - may be used to evaluate next mission + // store goals and events + mission_campaign_store_goals_and_events(); + + // evaluate next mission + mission_campaign_eval_next_mission(); + } + + // call traitor init before calling scoring_level_close. traitor init will essentially nullify + // any stats + if ( !(Game_mode & GM_MULTIPLAYER) ) { // only do for single player + debrief_traitor_init(); // initialize data needed if player becomes traitor. + } + + // call scoring level close for my stats. Needed for award_init. The stats will + // be backed out if used chooses to replace them. + scoring_level_close(); + + debrief_ui_init(); // init UI items + debrief_award_init(); + show_stats_init(); + debrief_voice_init(); + debrief_multi_list_init(); +// rank_bitmaps_clear(); +// rank_bitmaps_load(); + + strcpy(Debrief_current_callsign, Player->callsign); + Debrief_player = Player; +// Debrief_current_net_player_index = debrief_multi_list[0].net_player_index; + + // set up the Debrief_stages[] and Recommendations[] arrays. Only do the following stuff + // for non-clients (i.e. single and game server). Multiplayer clients will get their debriefing + // info directly from the server. + if ( !MULTIPLAYER_CLIENT ) { + debrief_set_stages_and_multi_stuff(); + + if ( Num_debrief_stages <= 0 ) { + Num_debrief_stages = 0; + } else { + debrief_voice_load_all(); + } + } else { + // multiplayer client may have already received their debriefing info. If they have not, + // then set the num debrief stages to 0 + if ( !Debrief_multi_stages_loaded ) { + Num_debrief_stages = 0; + } + } + + /* + if (mission_evaluate_primary_goals() == PRIMARY_GOALS_COMPLETE) { + common_music_init(SCORE_DEBRIEF_SUCCESS); + } else { + common_music_init(SCORE_DEBRIEF_FAIL); + } + */ + + // start up the appropriate music + if (Campaign.next_mission == Campaign.current_mission) { + // you failed the mission because you suck, so you get the suck music + common_music_init(SCORE_DEBRIEF_FAIL); + } else if (mission_goals_met()) { + // you completed all primaries and secondaries, thus you are a stud boy and you get stud boy music + common_music_init(SCORE_DEBRIEF_SUCCESS); + } else { + // you somehow passed the mission, so you get a little something for your efforts. + common_music_init(SCORE_DEBRIEF_AVERAGE); + } + + if (Campaign.next_mission == Campaign.current_mission) { + // better luck next time, increase his retries + Player->failures_this_session++; + } else { + // clear his retries info regardless of whether or not he accepts + Player->failures_this_session = 0; + } + + if (Game_mode & GM_MULTIPLAYER) { + multi_debrief_init(); + + // if i'm not the host of the game, disable the multi kick button + if (!(Net_player->flags & NETINFO_FLAG_GAME_HOST)) { + Buttons[gr_screen.res][MULTI_KICK].button.disable(); + } + } else { + Buttons[gr_screen.res][PLAYER_SCROLL_UP].button.disable(); + Buttons[gr_screen.res][PLAYER_SCROLL_DOWN].button.disable(); + Buttons[gr_screen.res][MULTI_PINFO_POPUP].button.disable(); + Buttons[gr_screen.res][MULTI_KICK].button.disable(); + Buttons[gr_screen.res][PLAYER_SCROLL_UP].button.hide(); + Buttons[gr_screen.res][PLAYER_SCROLL_DOWN].button.hide(); + Buttons[gr_screen.res][MULTI_PINFO_POPUP].button.hide(); + Buttons[gr_screen.res][MULTI_KICK].button.hide(); + } + + if (!Award_active) { + Buttons[gr_screen.res][MEDALS_BUTTON].button.disable(); + Buttons[gr_screen.res][MEDALS_BUTTON].button.hide(); + } + + Debrief_skip_popup_already_shown = 0; + + Debrief_inited = 1; +} + +// -------------------------------------------------------------------------------------- +// debrief_close() +void debrief_close() +{ + int i, idx; + + Assert(Debrief_inited); + + // if the mission wasn't accepted, clear out my stats + // we need to evaluate a little differently for multiplayer since the conditions for "accepting" + // are a little bit different + if (Game_mode & GM_MULTIPLAYER) { + // if stats weren't accepted, backout my own stats + if (multi_debrief_stats_accept_code() != 1) { + if(MULTIPLAYER_MASTER){ + for(idx=0; idxstats); + } + } + } else { + scoring_backout_accept( &Player->stats ); + } + } + } else { + // single player + if( !Debrief_accepted || !(Game_mode & GM_CAMPAIGN_MODE) ){ + scoring_backout_accept( &Player->stats ); + } + } + + // if dude passed the misson and accepted, reset his show skip popup flag + if (Debrief_accepted) { + Player->show_skip_popup = 1; + } + + if (Num_debrief_lines) { + for (i=0; i= 0){ + bm_unload(Background_bitmap); + } + + if (Award_bg_bitmap >= 0){ + bm_unload(Award_bg_bitmap); + } + + if (Rank_bitmap >= 0){ + bm_unload(Rank_bitmap); + } + + if (Medal_bitmap >= 0){ + bm_unload(Medal_bitmap); + } + + if (Badge_bitmap >= 0){ + bm_unload(Badge_bitmap); + } + + if (Wings_bitmap >= 0) { + bm_unload(Wings_bitmap); + } + + if (Crest_bitmap >= 0) { + bm_unload(Crest_bitmap); + } + + Debrief_ui_window.destroy(); + common_free_interface_palette(); // restore game palette + show_stats_close(); + + if (Game_mode & GM_MULTIPLAYER){ + multi_debrief_close(); + } + + game_flush(); + + Debrief_inited = 0; +} + +// handle keypresses in debriefing +void debrief_do_keys(int new_k) +{ + switch (new_k) { + case KEY_TAB: + debrief_next_tab(); + break; + + case KEY_SHIFTED | KEY_TAB: + debrief_prev_tab(); + break; + + case KEY_ESC: { + int pf_flags; + int choice; + + // multiplayer accept popup is a little bit different + if (Game_mode & GM_MULTIPLAYER) { + multi_debrief_esc_hit(); + + // display the normal debrief popup + } else { + if (!Turned_traitor && !Must_replay_mission && (Game_mode & GM_CAMPAIGN_MODE)) { + pf_flags = PF_BODY_BIG; // | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON; + choice = popup(pf_flags, 3, POPUP_CANCEL, XSTR( "&Yes", 454), XSTR( "&No, retry later", 455), XSTR( "Accept this mission outcome?", 456)); + if (choice == 1) { // accept and continue on + debrief_accept(0); + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + + if (choice < 1) + break; + + } else if (Must_replay_mission && (Game_mode & GM_CAMPAIGN_MODE)) { + // need to popup saying that mission was a failure and must be replayed + choice = popup(0, 2, POPUP_NO, POPUP_YES, XSTR( "Because this mission was a failure, you must replay this mission when you continue your campaign.\n\nReturn to the Flight Deck?", 457)); + if (choice <= 0) + break; + } + + // Return to Main Hall + gameseq_post_event(GS_EVENT_END_GAME); + } + } + + default: + break; + } // end switch +} + +// uuuuuugly +void debrief_draw_award_text() +{ + int start_y, curr_y, i, x, sw; + int fh = gr_get_font_height(); + int field_width = (Medal_bitmap > 0) ? Debrief_award_text_width[gr_screen.res][DB_WITH_MEDAL] : Debrief_award_text_width[gr_screen.res][DB_WITHOUT_MEDAL]; + + // vertically centered within field + start_y = Debrief_award_text_coords[gr_screen.res][1] + ((Debrief_award_text_coords[gr_screen.res][2] - (fh * Debrief_award_text_num_lines)) / 2); + curr_y = start_y; + + // draw the strings + for (i=0; i AWARD_TEXT_MAX_LINES) { + return; + } + + char *line2; + int field_width = (Medal_bitmap > 0) ? Debrief_award_text_width[gr_screen.res][DB_WITH_MEDAL] : Debrief_award_text_width[gr_screen.res][DB_WITHOUT_MEDAL]; + + // copy in the line + strcpy(Debrief_award_text[Debrief_award_text_num_lines], str); + + // maybe translate for displaying + if (Lcl_gr) { + medals_translate_name(Debrief_award_text[Debrief_award_text_num_lines], AWARD_TEXT_MAX_LINE_LENGTH); + } + + Debrief_award_text_num_lines++; + + // if its too long, split once ONLY + // assumes text isnt > 2 lines, but this is a safe assumption due to the char limits of the ranks/badges/etc + if (Debrief_award_text_num_lines < AWARD_TEXT_MAX_LINES) { + line2 = split_str_once(Debrief_award_text[Debrief_award_text_num_lines-1], field_width); + if (line2 != NULL) { + sprintf(Debrief_award_text[Debrief_award_text_num_lines], " %s", line2); // indent a space + } + Debrief_award_text_num_lines++; // leave blank line even if it all fits into 1 + } +} + +// called once per frame to drive all the input reading and rendering +void debrief_do_frame(float frametime) +{ + int k=0, new_k=0; + char *please_wait_str = XSTR("Please Wait", 1242); + int str_w, str_h; + char buf[256]; + + Assert(Debrief_inited); + + // first thing is to load the files + if ( MULTIPLAYER_CLIENT && !Debrief_multi_stages_loaded ) { + // draw the background, etc + GR_MAYBE_CLEAR_RES(Background_bitmap); + if (Background_bitmap >= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + Debrief_ui_window.draw(); + chatbox_render(); + if ( Debrief_multi_loading_bitmap > -1 ){ + gr_set_bitmap(Debrief_multi_loading_bitmap); + gr_bitmap( Please_wait_coords[gr_screen.res][0], Please_wait_coords[gr_screen.res][1] ); + } + + // draw "Please Wait" + gr_set_color_fast(&Color_normal); + gr_set_font(FONT2); + gr_get_string_size(&str_w, &str_h, please_wait_str); + gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, please_wait_str); + gr_set_font(FONT1); + + gr_flip(); + + // make sure we run the debrief do frame + if (Game_mode & GM_MULTIPLAYER) { + multi_debrief_do_frame(); + } + + // esc pressed? + os_poll(); + int keypress = game_check_key(); + if(keypress == KEY_ESC){ + // popup to leave + multi_quit_game(PROMPT_CLIENT); + } + + return; + } + + // if multiplayer client, and not loaded voice, then load it + if ( MULTIPLAYER_CLIENT && !Debrief_multi_voice_loaded ) { + debrief_multi_fixup_stages(); + debrief_voice_load_all(); + Debrief_multi_voice_loaded = 1; + } + + if ( help_overlay_active(DEBRIEFING_OVERLAY) ) { + Buttons[gr_screen.res][HELP_BUTTON].button.reset_status(); + Debrief_ui_window.set_ignore_gadgets(1); + } + + k = chatbox_process(); + if ( Game_mode & GM_NORMAL ) { + new_k = Debrief_ui_window.process(k); + } else { + new_k = Debrief_ui_window.process(k, 0); + } + + if ( (k > 0) || (new_k > 0) || B1_JUST_RELEASED ) { + if ( help_overlay_active(DEBRIEFING_OVERLAY) ) { + help_overlay_set_state(DEBRIEFING_OVERLAY, 0); + Debrief_ui_window.set_ignore_gadgets(0); + k = 0; + new_k = 0; + } + } + + if ( !help_overlay_active(DEBRIEFING_OVERLAY) ) { + Debrief_ui_window.set_ignore_gadgets(0); + } + + // don't show pilot info popup by default + Debrief_should_show_popup = 0; + + // see if the mode has changed and handle it if so. + if ( Current_mode != New_mode ) { + debrief_voice_stop(); + Current_mode = New_mode; + Current_stage = -1; + New_stage = 0; + if (New_mode == DEBRIEF_TAB) { + Num_stages = 1; + Debrief_cue_voice = 0; + Stage_voice = -1; + if (Debrief_first_voice_flag) { + Debrief_cue_voice = timestamp(DEBRIEF_VOICE_DELAY * 3); + Debrief_first_voice_flag = 0; + } + } else { + Num_stages = DEBRIEF_NUM_STATS_PAGES; + } + } + + if ((Num_stages > 0) && (New_stage != Current_stage)) { + Current_stage = New_stage; + debrief_text_init(); + } + + debrief_voice_play(); + common_music_do(); + + if (Game_mode & GM_MULTIPLAYER) { + multi_debrief_do_frame(); + } + + // Now do all the rendering for the frame + GR_MAYBE_CLEAR_RES(Background_bitmap); + if (Background_bitmap >= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + // draw the damn awarded stuff, G + if ( Award_active && (Award_bg_bitmap >= 0) ) { + gr_set_bitmap(Award_bg_bitmap); + gr_bitmap(Debrief_award_wnd_coords[gr_screen.res][0], Debrief_award_wnd_coords[gr_screen.res][1]); + if (Rank_bitmap >= 0) { + gr_set_bitmap(Rank_bitmap); + gr_bitmap(Debrief_award_coords[gr_screen.res][0], Debrief_award_coords[gr_screen.res][1]); + } + + if (Medal_bitmap >= 0) { + gr_set_bitmap(Medal_bitmap); + gr_bitmap(Debrief_award_coords[gr_screen.res][0], Debrief_award_coords[gr_screen.res][1]); + } + + if (Badge_bitmap >= 0) { + gr_set_bitmap(Badge_bitmap); + gr_bitmap(Debrief_award_coords[gr_screen.res][0], Debrief_award_coords[gr_screen.res][1]); + } + + if (Wings_bitmap >= 0) { + gr_set_bitmap(Wings_bitmap); + gr_bitmap(Debrief_award_coords[gr_screen.res][0], Debrief_award_coords[gr_screen.res][1]); + } + + if (Crest_bitmap >= 0) { + gr_set_bitmap(Crest_bitmap); + gr_bitmap(Debrief_award_coords[gr_screen.res][0], Debrief_award_coords[gr_screen.res][1]); + } + + // draw medal/badge/rank labels + debrief_draw_award_text(); + +/* if (Rank_text_bitmap >= 0) { + gr_set_bitmap(Rank_text_bitmap); + gr_bitmap(Debrief_award_coords[gr_screen.res][0], Debrief_award_coords[gr_screen.res][1]); + } + + + if (Medal_text_bitmap >= 0) { + gr_set_bitmap(Medal_text_bitmap); + gr_bitmap(Debrief_award_text_coords[gr_screen.res][0], Debrief_award_text_coords[gr_screen.res][1]); + } + + if (Badge_text_bitmap >= 0) { + gr_set_bitmap(Badge_text_bitmap); + gr_bitmap(Debrief_award_text_coords[gr_screen.res][0], Debrief_award_text_coords[gr_screen.res][1]); + } +*/ + } + + Debrief_ui_window.draw(); + debrief_redraw_pressed_buttons(); + Buttons[gr_screen.res][Current_mode].button.draw_forced(2); + if (Recommend_active && (Current_mode != STATS_TAB)) { + Buttons[gr_screen.res][RECOMMENDATIONS].button.draw_forced(2); + } + + // draw the title of the mission + gr_set_color_fast(&Color_bright_white); + strcpy(buf, The_mission.name); + gr_force_fit_string(buf, 255, Debrief_title_coords[gr_screen.res][2]); + gr_string(Debrief_title_coords[gr_screen.res][0], Debrief_title_coords[gr_screen.res][1], buf); + +#if !defined(NDEBUG) || defined(INTERPLAYQA) + gr_set_color_fast(&Color_normal); + gr_printf(Debrief_title_coords[gr_screen.res][0], Debrief_title_coords[gr_screen.res][1] - 10, NOX("[name: %s, mod: %s]"), Mission_filename, The_mission.modified); +#endif + + // draw the screen-specific text + switch (Current_mode) { + case DEBRIEF_TAB: + if ( Num_debrief_stages <= 0 ) { + gr_set_color_fast(&Color_white); + Assert( Game_current_mission_filename != NULL ); + gr_printf(Debrief_text_wnd_coords[gr_screen.res][0], Debrief_text_wnd_coords[gr_screen.res][1], XSTR( "No Debriefing for mission: %s", 458), Game_current_mission_filename); + + } else { + debrief_render(); + } + + break; + + case STATS_TAB: + debrief_stats_render(); + break; + } // end switch + + if (Text_offset + Debrief_text_wnd_coords[gr_screen.res][3] / gr_get_font_height() < Num_text_lines) { + int w; + + gr_set_color_fast(&Color_red); + gr_get_string_size(&w, NULL, XSTR( "More", 459)); + gr_printf(Debrief_text_wnd_coords[gr_screen.res][0] + Debrief_text_wnd_coords[gr_screen.res][2] / 2 - w / 2, Debrief_text_wnd_coords[gr_screen.res][1] + Debrief_text_wnd_coords[gr_screen.res][3], XSTR( "More", 459)); + } + + debrief_render_stagenum(); + debrief_multi_list_draw(); + + // render some extra stuff in multiplayer + if (Game_mode & GM_MULTIPLAYER) { + // render the chatbox last + chatbox_render(); + + // draw tooltips + Debrief_ui_window.draw_tooltip(); + + // render the status indicator for the voice system + multi_common_voice_display_status(); + } + + // AL 3-6-98: Needed to move key reading here, since popups are launched from this code, and we don't + // want to include the mouse pointer which is drawn in the flip + + if ( !help_overlay_active(DEBRIEFING_OVERLAY) ) { + debrief_check_buttons(); + debrief_do_keys(new_k); + } + + // blit help overlay if active + help_overlay_maybe_blit(DEBRIEFING_OVERLAY); + + gr_flip(); + + // dont let dude skip 3-09. hack. + if(Game_mode & GM_CAMPAIGN_MODE){ + if((Campaign.current_mission >= 0) && (Campaign.current_mission < MAX_CAMPAIGN_MISSIONS)){ + if ((Campaign.missions[Campaign.current_mission].name != NULL) && !stricmp(Campaign.missions[Campaign.current_mission].name, "sm3-09.fs2")) { + Debrief_skip_popup_already_shown = 1; + } + } + } + + // maybe show skip mission popup + if ((!Debrief_skip_popup_already_shown) && (Player->show_skip_popup) && (Game_mode & GM_NORMAL) && (Game_mode & GM_CAMPAIGN_MODE) && (Player->failures_this_session >= PLAYER_MISSION_FAILURE_LIMIT) && !(Game_mode & GM_MULTIPLAYER)) { + int popup_choice = popup(0, 3, XSTR("Do Not Skip This Mission", 1473), + XSTR("Advance To The Next Mission", 1474), + XSTR("Don't Show Me This Again", 1475), + XSTR("You have failed this mission five times. If you like, you may advance to the next mission.", 1472) ); + switch (popup_choice) { + case 0: + // stay on this mission, so proceed to normal debrief + // in other words, do nothing. + break; + case 1: + // skip this mission + mission_campaign_skip_to_next(); + gameseq_post_event(GS_EVENT_START_GAME); + break; + case 2: + // dont show this again + Player->show_skip_popup = 0; + break; + } + + Debrief_skip_popup_already_shown = 1; + } + + // check to see if we should be showing a pilot info popup in multiplayer (if a guy was double clicked) + if ((Game_mode & GM_MULTIPLAYER) && Debrief_should_show_popup) { + Assert((Multi_list_select >= 0) && (Multi_list_select < Multi_list_size)); + multi_pinfo_popup(&Net_players[Multi_list[Multi_list_select].net_player_index]); + + Debrief_should_show_popup = 0; + } +} + +void debrief_rebuild_player_list() +{ + int i; + net_player *np; + debrief_multi_list_info *list; + + Multi_list_size = 0; // number of net players to choose from + + for ( i=0; inet_player_index = i; + strcpy(list->callsign, np->player->callsign); + + // make sure to leave some room to blit the team indicator + gr_force_fit_string(list->callsign, CALLSIGN_LEN - 1, Debrief_list_coords[gr_screen.res][2] - MULTI_LIST_TEAM_OFFSET); + } + } // end for +} + +void debrief_handle_player_drop() +{ + debrief_rebuild_player_list(); +} + +void debrief_disable_accept() +{ +} diff --git a/src/missionui/missionloopbrief.cpp b/src/missionui/missionloopbrief.cpp new file mode 100644 index 0000000..79d2e74 --- /dev/null +++ b/src/missionui/missionloopbrief.cpp @@ -0,0 +1,287 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionLoopBrief.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Campaign Loop briefing screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 4 9/06/99 9:44p Jefff + * break out of loop support + * + * 3 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 2 8/27/99 12:04a Dave + * Campaign loop screen. + * + * + * $NoKeywords: $ + */ + +#include "missionloopbrief.h" +#include "missionscreencommon.h" +#include "2d.h" +#include "bmpman.h" +#include "gamesnd.h" +#include "eventmusic.h" +#include "audiostr.h" +#include "key.h" +#include "gamesequence.h" +#include "missioncampaign.h" +#include "animplay.h" +#include "freespace.h" + +// --------------------------------------------------------------------------------------------------------------------------------------- +// MISSION LOOP BRIEF DEFINES/VARS +// + +char *Loop_brief_fname[GR_NUM_RESOLUTIONS] = { + "LoopBrief", // GR_640 + "2_LoopBrief", // GR_1024 +}; + +char *Loop_brief_mask[GR_NUM_RESOLUTIONS] = { + "LoopBrief-m", // GR_640 + "2_Loopbrief-m", // GR_1024 +}; + +#define NUM_LOOP_BRIEF_BUTTONS 2 +#define LOOP_BRIEF_DECLINE 0 +#define LOOP_BRIEF_ACCEPT 1 +ui_button_info Loop_buttons[GR_NUM_RESOLUTIONS][NUM_LOOP_BRIEF_BUTTONS] = { + { // GR_640 + ui_button_info("LBB_00", 529, 437, -1, -1, 0), + ui_button_info("LBB_01", 575, 433, -1, -1, 1), + }, + { // GR_1024 + ui_button_info("2_LBB_00", 846, 699, -1, -1, 0), + ui_button_info("2_LBB_01", 920, 693, -1, -1, 1), + } +}; + +#define NUM_LOOP_TEXT 2 +UI_XSTR Loop_text[GR_NUM_RESOLUTIONS][NUM_LOOP_TEXT] = { + { // GR_640 + { "Decline", 1467, 514, 413, UI_XSTR_COLOR_PINK, -1, &Loop_buttons[0][LOOP_BRIEF_DECLINE].button }, + { "Accept", 1035, 573, 413, UI_XSTR_COLOR_PINK, -1, &Loop_buttons[0][LOOP_BRIEF_ACCEPT].button }, + }, + { // GR_1024 + { "Decline", 1467, 855, 670, UI_XSTR_COLOR_PINK, -1, &Loop_buttons[1][LOOP_BRIEF_DECLINE].button }, + { "Accept", 1035, 928, 670, UI_XSTR_COLOR_PINK, -1, &Loop_buttons[1][LOOP_BRIEF_ACCEPT].button }, + } +}; + +// loop brief anim +int Loop_brief_anim_coords[GR_NUM_RESOLUTIONS][2] = { + { // GR_640 + 24, 267 + }, + { // GR_640 + 167, 491 + } +}; + +// text window coords +int Loop_brief_text_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 25, 107, 591, 143 + }, + { // GR_1024 + 40, 171, 945, 229 + } +}; + +UI_WINDOW Loop_brief_window; +int Loop_brief_bitmap; + +anim *Loop_anim; +anim_instance *Loop_anim_instance; + +int Loop_sound; + +// --------------------------------------------------------------------------------------------------------------------------------------- +// MISSION LOOP BRIEF FUNCTIONS +// + +// button press +void loop_brief_button_pressed(int i) +{ + switch(i){ + case LOOP_BRIEF_DECLINE: + // CD CHECK + if(game_do_cd_mission_check(Game_current_mission_filename)){ + gameseq_post_event(GS_EVENT_START_GAME); + gamesnd_play_iface(SND_USER_SELECT); + } else { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + break; + + case LOOP_BRIEF_ACCEPT: + // select the loop mission + Campaign.loop_enabled = 1; + Campaign.loop_reentry = Campaign.next_mission; // save reentry pt, so we can break out of loop + Campaign.next_mission = Campaign.loop_mission; + + // CD CHECK + if(game_do_cd_mission_check(Game_current_mission_filename)){ + gameseq_post_event(GS_EVENT_START_GAME); + gamesnd_play_iface(SND_USER_SELECT); + } else { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + break; + } +} + +// init +void loop_brief_init() +{ + int idx; + ui_button_info *b; + + // load the background bitmap + Loop_brief_bitmap = bm_load(Loop_brief_fname[gr_screen.res]); + Assert(Loop_brief_bitmap != -1); + + // window + Loop_brief_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Loop_brief_window.set_mask_bmap(Loop_brief_mask[gr_screen.res]); + + // add the buttons + for (idx=0; idxbutton.create(&Loop_brief_window, "", b->x, b->y, 60, 30, 0, 1); + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // add text + for(idx=0; idx= 0) { + gr_set_bitmap(Loop_brief_bitmap); + gr_bitmap(0, 0); + } + + // draw the window + Loop_brief_window.draw(); + + // render the briefing text + brief_render_text(0, Loop_brief_text_coords[gr_screen.res][0], Loop_brief_text_coords[gr_screen.res][1], Loop_brief_text_coords[gr_screen.res][3], flFrametime); + + // render all anims + anim_render_all(GS_STATE_LOOP_BRIEF, flFrametime); + + gr_flip(); +} + +// close +void loop_brief_close() +{ + // this makes sure that we're all cool no matter how the user decides to exit + mission_campaign_mission_over(); + + // free the bitmap + if (Loop_brief_bitmap >= 0){ + bm_unload(Loop_brief_bitmap); + } + Loop_brief_bitmap = -1; + + // destroy the window + Loop_brief_window.destroy(); + + // free up anim stuff + if(Loop_anim_instance != NULL){ + anim_release_render_instance(Loop_anim_instance); + Loop_anim_instance = NULL; + } + if(Loop_anim != NULL){ + anim_free(Loop_anim); + Loop_anim = NULL; + } + + // stop voice + if(Loop_sound != -1){ + audiostream_stop(Loop_sound); + audiostream_close_file(Loop_sound); + Loop_sound = -1; + } + + // stop music + common_music_close(); +} \ No newline at end of file diff --git a/src/missionui/missionpause.cpp b/src/missionui/missionpause.cpp new file mode 100644 index 0000000..f4411ab --- /dev/null +++ b/src/missionui/missionpause.cpp @@ -0,0 +1,313 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionPause.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 7 7/29/99 10:48p Dave + * Multiplayer pause screen. + * + * 6 6/29/99 7:39p Dave + * Lots of small bug fixes. + * + * 5 6/09/99 2:17p Dave + * Fixed up pleasewait bitmap rendering. + * + * + * $NoKeywords: $ + */ + +#include "missionpause.h" +#include "ui.h" +#include "multi_pause.h" +#include "popup.h" +#include "2d.h" +#include "bmpman.h" +#include "key.h" +#include "audiostr.h" +#include "gamesequence.h" +#include "freespace.h" +#include "hud.h" +#include "object.h" +#include "font.h" +#include "alphacolors.h" +#include "beam.h" + +// ---------------------------------------------------------------------------------------------------------------- +// PAUSE DEFINES/VARS +// + +// pause bitmap name +char *Pause_bmp_name[GR_NUM_RESOLUTIONS] = { + "PleaseWait", + "2_PleaseWait" +}; + +// pause bitmap display stuff +int Please_wait_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 152, 217, 316, 26 + }, + { // GR_1024 + 247, 346, 510, 36 + } +}; + +char *Pause_multi_fname[GR_NUM_RESOLUTIONS] = { + "MPPause", + "2_MPPause" +}; +char *Pause_multi_mask[GR_NUM_RESOLUTIONS] = { + "MPPause-m", + "2_MPPause-m" +}; + +// pause window objects +UI_WINDOW Pause_win; +UI_CHECKBOX Pause_single_step; +UI_CHECKBOX Pause_physics; +UI_CHECKBOX Pause_ai; +UI_CHECKBOX Pause_ai_render; +UI_CHECKBOX Pause_firing; +UI_CHECKBOX Pause_external_view_mode_check; +UI_BUTTON Pause_continue; + +// if we're already paused +int Paused = 0; + +// background screen (for the chatbox) +int Pause_background_bitmap = -1; + +// saved background screen +int Pause_saved_screen = -1; + +// if we're in external vie wmode +int Pause_external_view_mode = 0; + +// externs +extern int Player_attacking_enabled; +extern int Ai_render_debug_flag; +extern int Ai_firing_enabled; +extern int physics_paused; +extern int ai_paused; +extern int last_single_step; +extern int game_single_step; + +// ---------------------------------------------------------------------------------------------------------------- +// PAUSE FUNCTIONS +// + +// initialize the pause screen +void pause_init(int multi) +{ + // if we're already paused. do nothing + if ( Paused ) { + return; + } + + // pause all beam weapon sounds + beam_pause_sounds(); + + if(!(Game_mode & GM_STANDALONE_SERVER)){ + Pause_saved_screen = gr_save_screen(); + + // pause all game music + audiostream_pause_all(); + + //JAS: REMOVED CALL TO SET INTERFACE PALETTE TO GET RID OF SCREEN CLEAR WHEN PAUSING + //common_set_interface_palette(); // set the interface palette + Pause_win.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + + if (multi) { + Pause_win.set_mask_bmap(Pause_multi_mask[gr_screen.res]); + Pause_background_bitmap = bm_load(Pause_multi_fname[gr_screen.res]); + + multi_pause_init(&Pause_win); + } else { + Pause_background_bitmap = bm_load(Pause_bmp_name[gr_screen.res]); + } + } else { + multi_pause_init(NULL); + } + + Paused = 1; +} + +// pause do frame - will handle running multiplayer operations if necessary +void pause_do(int multi) +{ + int k; + char *pause_str = XSTR("Paused", 767); + int str_w, str_h; + + if(Game_mode & GM_STANDALONE_SERVER){ + multi_pause_do(); + } else { + // RENDER A GAME FRAME HERE AS THE BACKGROUND + gr_restore_screen(Pause_saved_screen); + if (Pause_background_bitmap >= 0) { + gr_set_bitmap(Pause_background_bitmap); + if(multi){ + gr_bitmap(0,0); + } else { + // draw the bitmap + gr_bitmap(Please_wait_coords[gr_screen.res][0], Please_wait_coords[gr_screen.res][1]); + + // draw "Paused" on it + gr_set_color_fast(&Color_normal); + gr_set_font(FONT2); + gr_get_string_size(&str_w, &str_h, pause_str); + gr_string((gr_screen.max_w - str_w) / 2, (gr_screen.max_h - str_h) / 2, pause_str); + gr_set_font(FONT1); + } + } + + // the multi paused screen will do its own window processing + if (multi) { + multi_pause_do(); + } + // otherwise process the ui window here + else { + k = Pause_win.process() & ~KEY_DEBUGGED; + switch (k) { + case KEY_ESC: + case KEY_PAUSE: + gameseq_post_event(GS_EVENT_PREVIOUS_STATE); + break; + } // end switch + } + + // draw the background window + Pause_win.draw(); + + // a very unique case where we shouldn't be doing the page flip because we're inside of popup code + if(!popup_active()){ + gr_flip(); + } else { + // this should only be happening in a very unique multiplayer case + Assert(Game_mode & GM_MULTIPLAYER); + } + } +} + +// close the pause screen +void pause_close(int multi) +{ + // if we're not paused - do nothing + if ( !Paused ) { + return; + } + + // unpause all beam weapon sounds + beam_unpause_sounds(); + + // deinit stuff + if(Game_mode & GM_STANDALONE_SERVER){ + multi_pause_close(); + } else { + gr_free_screen(Pause_saved_screen); + + if (Pause_background_bitmap){ + bm_unload(Pause_background_bitmap); + } + + Pause_win.destroy(); + game_flush(); + if (multi) { + multi_pause_close(); + } + + // unpause all the music + audiostream_unpause_all(); + } + + Paused = 0; +} + +// debug pause init +void pause_debug_init() +{ + Pause_win.create( 100,100,400,300, WIN_DIALOG ); + + Pause_physics.create( &Pause_win, NOX("Physics Pause

"), 200, 150, physics_paused ); + Pause_ai.create( &Pause_win, NOX("AI Pause "), 200, 175, ai_paused ); + #ifndef NDEBUG + Pause_ai_render.create( &Pause_win, NOX("AI Render Stuff "), 200, 200, Ai_render_debug_flag); + #endif + Pause_firing.create( &Pause_win, NOX("AI firing "), 200, 225, Ai_firing_enabled); + Pause_external_view_mode_check.create( &Pause_win, NOX("External View "), 200, 250, Pause_external_view_mode); + Pause_single_step.create( &Pause_win, NOX("Single Step "), 200, 290, game_single_step ); + Pause_continue.create( &Pause_win, NOX("Leave Pause"), 200, 350, 200, 40 ); + + Pause_single_step.set_hotkey( KEY_S ); + Pause_physics.set_hotkey( KEY_P ); + Pause_ai.set_hotkey( KEY_A ); + Pause_ai_render.set_hotkey( KEY_R ); + Pause_firing.set_hotkey( KEY_F ); + Pause_external_view_mode_check.set_hotkey( KEY_E ); + Pause_continue.set_hotkey( KEY_ESC ); + + Pause_continue.set_focus(); +} + +// debug pause do frame +void pause_debug_do() +{ + int key; + + key = Pause_win.process(); + if ( Pause_single_step.changed()) { + game_single_step = Pause_single_step.checked(); + } + + if ( Pause_physics.changed()) { + physics_paused = Pause_physics.checked(); + } + + if ( Pause_ai.changed()) { + ai_paused = Pause_ai.checked(); + if (ai_paused){ + obj_init_all_ships_physics(); + } + } + + if ( Pause_ai_render.changed()) { + Ai_render_debug_flag = Pause_ai_render.checked(); + } + + if ( Pause_firing.changed()) { + Ai_firing_enabled = Pause_firing.checked(); + } + + if ( Pause_external_view_mode_check.changed()) { + Pause_external_view_mode = Pause_external_view_mode_check.checked(); + if (Pause_external_view_mode){ + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "External view of player ship.", 182)); + } else { + HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "View from inside player ship.", 183)); + } + } + + if ( Pause_continue.pressed() || (key == KEY_PAUSE) ) { // Changed, MK, 11/9/97, only Pause break pause. + gameseq_post_event(GS_EVENT_PREVIOUS_STATE); + } + + gr_clear(); + Pause_win.draw(); + + gr_flip(); +} + +// debug pause close +void pause_debug_close() +{ + last_single_step = 0; // Make so single step waits a frame before stepping + Pause_win.destroy(); + game_flush(); +} diff --git a/src/missionui/missionrecommend.cpp b/src/missionui/missionrecommend.cpp new file mode 100644 index 0000000..65f559f --- /dev/null +++ b/src/missionui/missionrecommend.cpp @@ -0,0 +1,22 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionRecommend.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for the post-mission recommendation interface + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:49a Dave + * + * 1 9/30/97 9:26a Lawrance + * + * $NoKeywords: $ + */ \ No newline at end of file diff --git a/src/missionui/missionscreencommon.cpp b/src/missionui/missionscreencommon.cpp new file mode 100644 index 0000000..554b17f --- /dev/null +++ b/src/missionui/missionscreencommon.cpp @@ -0,0 +1,1566 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionScreenCommon.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 13 10/14/99 2:51p Jefff + * localiztion fixes + * + * 12 8/05/99 3:40p Jefff + * hi-res text adjustments + * + * 11 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 10 2/01/99 5:55p Dave + * Removed the idea of explicit bitmaps for buttons. Fixed text + * highlighting for disabled gadgets. + * + * 9 1/30/99 7:33p Neilk + * Fixed coords problems for mission briefing screens + * + * 8 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 7 1/29/99 4:17p Dave + * New interface screens. + * + * 6 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 5 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 4 11/17/98 11:12a Dave + * Removed player identification by address. Now assign explicit id #'s. + * + * 3 10/16/98 9:40a Andsager + * Remove ".h" files from model.h + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 85 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 84 5/08/98 7:52p Lawrance + * fix bug where wing slot icons were not getting loaded + * + * 83 5/08/98 10:17a Lawrance + * don't play briefing slide-in animation if detail level is below high + * + * 82 5/06/98 11:50p Lawrance + * Clean up help overlay code for loadout screens + * + * 81 5/03/98 1:55a Lawrance + * Fix some sound problems with loadout screens + * + * 80 4/30/98 6:03p Lawrance + * Make drag and drop work better. + * + * 79 4/22/98 5:52p Dave + * Large reworking of endgame sequencing. Updated host options screen for + * new artwork. Put in checks to end game if host leaves or if team + * captains leave mid-game. + * + * 78 4/07/98 7:51p Hoffoss + * Implemented changed to options screen due to artwork changes. + * + * 77 4/07/98 5:42p Dave + * Put in support for ui display of voice system status (recording, + * playing back, etc). Make sure main hall music is stopped before + * entering a multiplayer game via ingame join. + * + * 76 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 75 4/01/98 11:19p Dave + * Put in auto-loading of xferred pilot pic files. Grey out background + * behind pinfo popup. Put a chatbox message in when players are kicked. + * Moved mission title down in briefing. Other ui fixes. + * + * 74 4/01/98 9:21p John + * Made NDEBUG, optimized build with no warnings or errors. + * + * 73 3/31/98 11:47p Lawrance + * Fix some bugs related to wingmen selection when doing a quick mission + * restart. + * + * 72 3/31/98 5:31p Lawrance + * Fix sound bug that happenned when entering briefing + * + * 71 3/30/98 5:16p Lawrance + * centralize a check for disabled loadout screens + * + * 70 3/30/98 12:18a Lawrance + * change some DEMO_RELEASE code to not compile code rather than return + * early + * + * 69 3/29/98 10:16p Allender + * scramble missions need to have ship/weapon selection disabled (this + * code was somehow lost) + * + * 68 3/29/98 1:24p Dave + * Make chatbox not clear between multiplayer screens. Select player ship + * as default in mp team select and weapons select screens. Made create + * game mission list use 2 fixed size columns. + * + * 67 3/29/98 12:55a Lawrance + * Get demo build working with limited set of data. + * + * 66 3/25/98 8:43p Hoffoss + * Changed anim_play() to not be so damn complex when you try and call it. + * + * 65 3/24/98 4:59p Dave + * Fixed several ui bugs. Put in pre and post voice stream playback sound + * fx. Put in error specific popups for clients getting dropped from games + * through actions other than their own. + * + * 64 3/19/98 5:32p Lawrance + * Added music to the background of command brief screen. + * + * 63 3/05/98 5:02p Dave + * More work on team vs. team support for multiplayer. Need to fix bugs in + * weapon select. + * + * 62 3/03/98 8:55p Dave + * Finished pre-briefing team vs. team support. + * + * 61 3/03/98 8:12p Lawrance + * Double timeout before flashing buttons + * + * 60 3/02/98 3:27p Lawrance + * Don't call muti_ts_init() in single player + * + * 59 3/01/98 3:26p Dave + * Fixed a few team select bugs. Put in multiplayer intertface sounds. + * Corrected how ships are disabled/enabled in team select/weapon select + * screens. + * + * 58 2/27/98 4:55p Sandeep + * Fixed a signed/unsigned bug in the wss packet type + * + * 57 2/26/98 4:59p Allender + * groundwork for team vs team briefings. Moved weaponry pool into the + * Team_data structure. Added team field into the p_info structure. + * Allow for mutliple structures in the briefing code. + * + * 56 2/25/98 10:24a Lawrance + * Don't ask for confirmation when ESC is pressed. + * + * 55 2/24/98 12:22a Lawrance + * New coords for revamped briefing graphics + * + * 54 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 53 2/22/98 12:19p John + * Externalized some strings + * + * 52 2/21/98 2:50p Lawrance + * Tell players that their campaign position will get saved if they return + * to main hall from loadout screens. + * + * 51 2/19/98 6:26p Dave + * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in + * initial support for player data uploading. + * + * 50 2/18/98 3:56p Dave + * Several bugs fixed for mp team select screen. Put in standalone packet + * routing for team select. + * + * 49 2/17/98 6:07p Dave + * Tore out old multiplayer team select screen, installed new one. + * + * 48 2/13/98 3:46p Dave + * Put in dynamic chatbox sizing. Made multiplayer file lookups use cfile + * functions. + * + * 47 2/10/98 8:40p Dave + * Fixed some debriefing bugs. + * + * 46 2/04/98 11:06p Lawrance + * Fix a couple of bugs with save/restore of player weapon loadout. + * + * 45 2/04/98 4:32p Allender + * support for multiple briefings and debriefings. Changes to mission + * type (now a bitfield). Bitfield defs for multiplayer modes + * + * 44 1/30/98 10:40a Lawrance + * remove any binding references to hotkey screen + * + * 43 1/20/98 5:52p Lawrance + * prompt user before returning to main hall (in single player) + * + * 42 1/17/98 5:51p Dave + * Bug fixes for bugs generated by multiplayer testing. + * + * 41 1/14/98 11:39a Dave + * Polished up a bunch of popup support items. + * + * 40 1/13/98 5:35p Lawrance + * Show popup when trying to go to weapons loadout without any ships + * selected. + * + * 39 1/10/98 5:48p Lawrance + * Don't allow Tab to go to ship/weapon loadout screen in training, play + * negative feedback sounds if user tries to. + * + * 38 1/10/98 12:45a Lawrance + * Don't restore loadout if mission was modified. + * + * 37 1/09/98 6:06p Dave + * Put in network sound support for multiplayer ship/weapon select + * screens. Made clients exit game correctly through warp effect. Fixed + * main hall menu help overlay bug. + * + * 36 1/09/98 11:04a Lawrance + * Fix bug that prevented buttons from flashing in the loadout screens. + * + * 35 1/07/98 6:45p Lawrance + * Play first briefing music if none specified. + * + * 34 1/05/98 4:57p Lawrance + * reset flash timers when a button is pressed or a key is pressed + * + * 33 12/30/97 5:46p Lawrance + * Rename rnd() to rand_alt(). + * + * 32 12/30/97 4:27p Lawrance + * Add new rnd() function that doesn't affect rand() sequence. + * + * 31 12/29/97 4:21p Lawrance + * Flash buttons on briefing/ship select/weapons loadout when enough time + * has elapsed without activity. + * + * 30 12/28/97 5:52p Lawrance + * Add support for debriefing success/fail music. + * + * 29 12/24/97 8:54p Lawrance + * Integrating new popup code + * + * 28 12/24/97 1:19p Lawrance + * fix some bugs with the multiplayer ship/weapons loadout + * + * 27 12/23/97 11:58a Allender + * changes to ship/wespon selection for multplayer. added sounds to some + * interface screens. Update and modiied end-of-briefing packets -- yet + * to be tested. + * + * $NoKeywords: $ + * + */ + +#include +#include "freespace.h" +#include "eventmusic.h" +#include "key.h" +#include "missionscreencommon.h" +#include "missionshipchoice.h" +#include "missionweaponchoice.h" +#include "missionbrief.h" +#include "multi.h" +#include "multimsgs.h" +#include "timer.h" +#include "sound.h" +#include "gamesequence.h" +#include "bmpman.h" +// #include "movie.h" +#include "gamesnd.h" +#include "palman.h" +#include "mouse.h" +#include "contexthelp.h" +#include "chatbox.h" +#include "time.h" +#include "joy.h" +#include "cmdline.h" +#include "linklist.h" +#include "staticrand.h" // for rand_alt() +#include "popup.h" +#include "multiutil.h" +#include "multiteamselect.h" +#include "hudwingmanstatus.h" +#include "multi_endgame.h" +#include "uidefs.h" +#include "animplay.h" + +////////////////////////////////////////////////////////////////// +// Game Globals +////////////////////////////////////////////////////////////////// + +int Common_select_inited = 0; + +// Dependent on when mouse button goes up +int Drop_icon_mflag, Drop_on_wing_mflag, Brief_mouse_up_flag; + +int Mouse_down_last_frame = 0; + +// Timers used to flash buttons after timeouts +#define MSC_FLASH_AFTER_TIME 60000 // time before flashing a button +#define MSC_FLASH_INTERVAL 200 // time between flashes +int Flash_timer; // timestamp used to start flashing +int Flash_toggle; // timestamp used to toggle flashing +int Flash_bright; // state of button to flash + +////////////////////////////////////////////////////////////////// +// Global to modulde +////////////////////////////////////////////////////////////////// +int Current_screen; +int Next_screen; +static int InterfacePaletteBitmap = -1; // PCX file that holds the interface palette + +////////////////////////////////////////////////////////////////// +// UI +////////////////////////////////////////////////////////////////// +UI_WINDOW *Active_ui_window; + +brief_common_buttons Common_buttons[3][GR_NUM_RESOLUTIONS][NUM_COMMON_BUTTONS] = { + { // UGH + { // GR_640 + brief_common_buttons("CB_00", 7, 3, 37, 7, 0), + brief_common_buttons("CB_01", 7, 19, 37, 23, 1), + brief_common_buttons("CB_02", 7, 35, 37, 39, 2), + brief_common_buttons("CB_05", 571, 425, 572, 413, 5), + brief_common_buttons("CB_06", 533, 425, 500, 440, 6), + brief_common_buttons("CB_07", 533, 455, 479, 464, 7), + }, + { // GR_1024 + brief_common_buttons("2_CB_00", 12, 5, 59, 12, 0), + brief_common_buttons("2_CB_01", 12, 31, 59, 37, 1), + brief_common_buttons("2_CB_02", 12, 56, 59, 62, 2), + brief_common_buttons("2_CB_05", 914, 681, 937, 671, 5), + brief_common_buttons("2_CB_06", 854, 681, 822, 704, 6), + brief_common_buttons("2_CB_07", 854, 724, 800, 743, 7), + } + }, + { // UGH + { // GR_640 + brief_common_buttons("CB_00", 7, 3, 37, 7, 0), + brief_common_buttons("CB_01", 7, 19, 37, 23, 1), + brief_common_buttons("CB_02", 7, 35, 37, 39, 2), + brief_common_buttons("CB_05", 571, 425, 572, 413, 5), + brief_common_buttons("CB_06", 533, 425, 500, 440, 6), + brief_common_buttons("CB_07", 533, 455, 479, 464, 7), + }, + { // GR_1024 + brief_common_buttons("2_CB_00", 12, 5, 59, 12, 0), + brief_common_buttons("2_CB_01", 12, 31, 59, 37, 1), + brief_common_buttons("2_CB_02", 12, 56, 59, 62, 2), + brief_common_buttons("2_CB_05", 914, 681, 937, 671, 5), + brief_common_buttons("2_CB_06", 854, 681, 822, 704, 6), + brief_common_buttons("2_CB_07", 854, 724, 800, 743, 7), + } + }, + { // UGH + { // GR_640 + brief_common_buttons("CB_00", 7, 3, 37, 7, 0), + brief_common_buttons("CB_01", 7, 19, 37, 23, 1), + brief_common_buttons("CB_02", 7, 35, 37, 39, 2), + brief_common_buttons("CB_05", 571, 425, 572, 413, 5), + brief_common_buttons("CB_06", 533, 425, 500, 440, 6), + brief_common_buttons("CB_07", 533, 455, 479, 464, 7), + }, + { // GR_1024 + brief_common_buttons("2_CB_00", 12, 5, 59, 12, 0), + brief_common_buttons("2_CB_01", 12, 31, 59, 37, 1), + brief_common_buttons("2_CB_02", 12, 56, 59, 62, 2), + brief_common_buttons("2_CB_05", 914, 681, 937, 671, 5), + brief_common_buttons("2_CB_06", 854, 681, 822, 704, 6), + brief_common_buttons("2_CB_07", 854, 724, 800, 743, 7), + } + } +}; + +#define COMMON_BRIEFING_BUTTON 0 +#define COMMON_SS_BUTTON 1 +#define COMMON_WEAPON_BUTTON 2 +#define COMMON_COMMIT_BUTTON 3 +#define COMMON_HELP_BUTTON 4 +#define COMMON_OPTIONS_BUTTON 5 + +int Background_playing; // Flag to indicate background animation is playing +static anim *Background_anim; // Ids for the anim data that is loaded + +// value for which Team_data entry to use +int Common_team; + +// Ids for the instance of the anim that is playing +static anim_instance *Background_anim_instance; + +int Wing_slot_empty_bitmap; +int Wing_slot_disabled_bitmap; + +// prototypes +int wss_slots_all_empty(); + +// Display the no ships selected error +void common_show_no_ship_error() +{ + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "At least one ship must be selected before proceeding to weapons loadout", 460)); +} + +// Check the status of the buttons common to the loadout screens +void common_check_buttons() +{ + int i; + UI_BUTTON *b; + + for ( i = 0; i < NUM_COMMON_BUTTONS; i++ ) { + b = &Common_buttons[Current_screen-1][gr_screen.res][i].button; + if ( b->pressed() ) { + + common_button_do(i); + } + } + +/* + // AL 11-23-97: let a joystick button press commit + if ( joy_down_count(0) || joy_down_count(1) ) { + Commit_pressed = 1; + } +*/ + +} + +// ------------------------------------------------------------------- +// common_redraw_pressed_buttons() +// +// Redraw any common buttons that are pressed down. This function is needed +// since we sometimes need to draw pressed buttons last to ensure the entire +// button gets drawn (and not overlapped by other buttons) +// +void common_redraw_pressed_buttons() +{ + int i; + UI_BUTTON *b; + + for ( i = 0; i < NUM_COMMON_BUTTONS; i++ ) { + b = &Common_buttons[Current_screen-1][gr_screen.res][i].button; + if ( b->button_down() ) { + b->draw_forced(2); + } + } +} + +void common_buttons_maybe_reload(UI_WINDOW *ui_window) +{ + UI_BUTTON *b; + int i; + + for ( i = 0; i < NUM_COMMON_BUTTONS; i++ ) { + b = &Common_buttons[Current_screen-1][gr_screen.res][i].button; + b->set_bmaps(Common_buttons[Current_screen-1][gr_screen.res][i].filename); + } +} + +void common_buttons_init(UI_WINDOW *ui_window) +{ + UI_BUTTON *b; + int i; + + for ( i = 0; i < NUM_COMMON_BUTTONS; i++ ) { + b = &Common_buttons[Current_screen-1][gr_screen.res][i].button; + b->create( ui_window, "", Common_buttons[Current_screen-1][gr_screen.res][i].x, Common_buttons[Current_screen-1][gr_screen.res][i].y, 60, 30, 0, 1); + // set up callback for when a mouse first goes over a button + b->set_highlight_action( common_play_highlight_sound ); + b->set_bmaps(Common_buttons[Current_screen-1][gr_screen.res][i].filename); + b->link_hotspot(Common_buttons[Current_screen-1][gr_screen.res][i].hotspot); + } + + // add some text + ui_window->add_XSTR("Briefing", 1504, Common_buttons[Current_screen-1][gr_screen.res][COMMON_BRIEFING_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_BRIEFING_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_BRIEFING_BUTTON].button, UI_XSTR_COLOR_GREEN); + ui_window->add_XSTR("Ship Selection", 1067, Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_BUTTON].button, UI_XSTR_COLOR_GREEN); + ui_window->add_XSTR("Weapon Loadout", 1068, Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_BUTTON].button, UI_XSTR_COLOR_GREEN); + ui_window->add_XSTR("Commit", 1062, Common_buttons[Current_screen-1][gr_screen.res][COMMON_COMMIT_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_COMMIT_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_COMMIT_BUTTON].button, UI_XSTR_COLOR_PINK); + ui_window->add_XSTR("Help", 928, Common_buttons[Current_screen-1][gr_screen.res][COMMON_HELP_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_HELP_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_HELP_BUTTON].button, UI_XSTR_COLOR_GREEN); + ui_window->add_XSTR("Options", 1036, Common_buttons[Current_screen-1][gr_screen.res][COMMON_OPTIONS_BUTTON].xt, Common_buttons[Current_screen-1][gr_screen.res][COMMON_OPTIONS_BUTTON].yt, &Common_buttons[Current_screen-1][gr_screen.res][COMMON_OPTIONS_BUTTON].button, UI_XSTR_COLOR_GREEN); + + common_reset_buttons(); + + Common_buttons[Current_screen-1][gr_screen.res][COMMON_COMMIT_BUTTON].button.set_hotkey(KEY_CTRLED+KEY_ENTER); + Common_buttons[Current_screen-1][gr_screen.res][COMMON_HELP_BUTTON].button.set_hotkey(KEY_F1); + Common_buttons[Current_screen-1][gr_screen.res][COMMON_OPTIONS_BUTTON].button.set_hotkey(KEY_F2); + + // for scramble or training missions, disable the ship/weapon selection regions + if ( brief_only_allow_briefing() ) { + Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_REGION].button.disable(); + Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_REGION].button.disable(); + } + + #ifdef DEMO // allow for FS2_DEMO + Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_REGION].button.disable(); + Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_REGION].button.disable(); + #endif +} + +void set_active_ui(UI_WINDOW *ui_window) +{ + Active_ui_window = ui_window; +} + +void common_music_init(int score_index) +{ + if ( Cmdline_freespace_no_music ) { + return; + } + + if ( score_index >= NUM_SCORES ) { + Int3(); + return; + } + + if ( Mission_music[score_index] < 0 ) { + if ( Num_music_files > 0 ) { + Mission_music[score_index] = 0; + nprintf(("Sound","No briefing music is selected, so play first briefing track: %s\n",Spooled_music[Mission_music[score_index]].name)); + } else { + return; + } + } + + briefing_load_music( Spooled_music[Mission_music[score_index]].filename ); + // Use this id to trigger the start of music playing on the briefing screen + Briefing_music_begin_timestamp = timestamp(BRIEFING_MUSIC_DELAY); +} + +void common_music_do() +{ + if ( Cmdline_freespace_no_music ) { + return; + } + + // Use this id to trigger the start of music playing on the briefing screen + if ( timestamp_elapsed( Briefing_music_begin_timestamp) ) { + Briefing_music_begin_timestamp = 0; + briefing_start_music(); + } +} + +void common_music_close() +{ + if ( Cmdline_freespace_no_music ) { + return; + } + + if ( Num_music_files <= 0 ) + return; + + briefing_stop_music(); +} + +// function that sets the current palette to the interface palette. This function +// needs to be followed by common_free_interface_palette() to restore the game palette. +void common_set_interface_palette(char *filename) +{ + static char buf[MAX_FILENAME_LEN + 1] = {0}; + + if (!filename) + filename = NOX("palette01"); + + Assert(strlen(filename) <= MAX_FILENAME_LEN); + if ( (InterfacePaletteBitmap != -1) && !stricmp(filename, buf) ) + return; // already set to this palette + + strcpy(buf, filename); + + // unload the interface bitmap from memory + if (InterfacePaletteBitmap != -1) { + bm_unload(InterfacePaletteBitmap); + InterfacePaletteBitmap = -1; + } + + // ugh - we don't need this anymore + /* + InterfacePaletteBitmap = bm_load(filename); + if (InterfacePaletteBitmap < 0) { + Error(LOCATION, "Could not load in \"%s\"!", filename); + } + */ + +#ifndef HARDWARE_ONLY + palette_use_bm_palette(InterfacePaletteBitmap); +#endif +} + +// release the interface palette .pcx file, and restore the game palette +void common_free_interface_palette() +{ + // unload the interface bitmap from memory + if (InterfacePaletteBitmap != -1) { + bm_unload(InterfacePaletteBitmap); + InterfacePaletteBitmap = -1; + } + + // restore the normal game palette + palette_restore_palette(); +} + +// Init timers used for flashing buttons +void common_flash_button_init() +{ + Flash_timer = timestamp(MSC_FLASH_AFTER_TIME); + Flash_toggle = 1; + Flash_bright = 0; +} + +// determine if we should draw a button as bright +int common_flash_bright() +{ + if ( timestamp_elapsed(Flash_timer) ) { + if ( timestamp_elapsed(Flash_toggle) ) { + Flash_toggle = timestamp(MSC_FLASH_INTERVAL); + Flash_bright ^= 1; + } + } + + return Flash_bright; +} + +// common_select_init() will load in animations and bitmaps that are common to the +// briefing/ship select/weapon select screens. The global Common_select_inited is set +// after this function is called once, and is only cleared when common_select_close() +// is called. This prevents multiple loadings of animations/bitmaps. +// +// This function also sets the palette based on the file palette01.pcx +void common_select_init() +{ + if ( Common_select_inited ) { + nprintf(("Alan","common_select_init() returning without doing anything\n")); + return; + } + + nprintf(("Alan","entering common_select_init()\n")); + + // No anims are playing + Background_playing = 0; + Background_anim = NULL; + + #ifndef DEMO // not for FS2_DEMO + + /* + if ( current_detail_level() >= (NUM_DEFAULT_DETAIL_LEVELS-2) ) { + + anim_play_struct aps; + + // Load in the background transition anim + if ( Game_mode & GM_MULTIPLAYER ) + Background_anim = anim_load("BriefTransMulti", 1); // 1 as last parm means file is mem-mapped + else { + Background_anim = anim_load("BriefTrans", 1); // 1 as last parm means file is mem-mapped + } + + Assert( Background_anim != NULL ); + anim_play_init(&aps, Background_anim, 0, 0); + aps.framerate_independent = 1; + aps.skip_frames = 0; + Background_anim_instance = anim_play(&aps); + Background_playing = 1; // start playing the Background anim + } + */ + Current_screen = Next_screen = ON_BRIEFING_SELECT; + + // load in the icons for the wing slots + load_wing_icons(NOX("iconwing01")); + + + #endif + + Current_screen = Next_screen = ON_BRIEFING_SELECT; + + Commit_pressed = 0; + + Common_select_inited = 1; + + // this handles the case where the player played a multiplayer game but now is in single player (in one instance + // of Freespace) + if(!(Game_mode & GM_MULTIPLAYER)){ + chatbox_close(); + } + + // get the value of the team + Common_team = 0; // assume the first team -- we'll change this value if we need to + if ( (Game_mode & GM_MULTIPLAYER) && IS_MISSION_MULTI_TEAMS ) + Common_team = Net_player->p_info.team; + + ship_select_common_init(); + weapon_select_common_init(); + common_flash_button_init(); + + if ( Game_mode & GM_MULTIPLAYER ) { + multi_ts_common_init(); + } + + // restore loadout from Player_loadout if this is the same mission as the one previously played + if ( !(Game_mode & GM_MULTIPLAYER) ) { + if ( !stricmp(Player_loadout.filename, Game_current_mission_filename) ) { + wss_restore_loadout(); + ss_synch_interface(); + wl_synch_interface(); + } + } + + Drop_icon_mflag = 0; + Drop_on_wing_mflag = 0; +} + +void common_reset_buttons() +{ + int i; + UI_BUTTON *b; + + for ( i = 0; i < NUM_COMMON_BUTTONS; i++ ) { + b = &Common_buttons[Current_screen-1][gr_screen.res][i].button; + b->reset_status(); + } + + switch(Current_screen) { + case ON_BRIEFING_SELECT: + Common_buttons[Current_screen-1][gr_screen.res][COMMON_BRIEFING_REGION].button.skip_first_highlight_callback(); + break; + case ON_SHIP_SELECT: + Common_buttons[Current_screen-1][gr_screen.res][COMMON_SS_REGION].button.skip_first_highlight_callback(); + break; + case ON_WEAPON_SELECT: + Common_buttons[Current_screen-1][gr_screen.res][COMMON_WEAPON_REGION].button.skip_first_highlight_callback(); + break; + } +} + +// common_select_do() is called once per loop in the interface screens and is used +// for drawing and changing the common animations and blitting common bitmaps. +int common_select_do(float frametime) +{ + int k, new_k; + + // If the mouse went up, set flags. We can't use mouse_up_count() more than once a frame, + // since the count gets zeroed after the call. + // + Drop_icon_mflag = 0; + Drop_on_wing_mflag = 0; + Brief_mouse_up_flag = 0; + + if ( mouse_up_count(MOUSE_LEFT_BUTTON) ) { + Drop_icon_mflag = 1; + Drop_on_wing_mflag = 1; + Brief_mouse_up_flag = 1; + } + + Mouse_down_last_frame = 0; + if ( mouse_down_count(MOUSE_LEFT_BUTTON) ) { + Mouse_down_last_frame = 1; + } + + if ( help_overlay_active(BR_OVERLAY) || help_overlay_active(SS_OVERLAY) || help_overlay_active(WL_OVERLAY) ) { + Common_buttons[0][gr_screen.res][COMMON_HELP_BUTTON].button.reset_status(); + Common_buttons[1][gr_screen.res][COMMON_HELP_BUTTON].button.reset_status(); + Common_buttons[2][gr_screen.res][COMMON_HELP_BUTTON].button.reset_status(); + Active_ui_window->set_ignore_gadgets(1); + } else { + Active_ui_window->set_ignore_gadgets(0); + } + + k = chatbox_process(); + if ( Game_mode & GM_NORMAL ) { + new_k = Active_ui_window->process(k); + } else { + new_k = Active_ui_window->process(k, 0); + } + + if ( (k > 0) || (new_k > 0) || B1_JUST_RELEASED ) { + if ( help_overlay_active(BR_OVERLAY) || help_overlay_active(SS_OVERLAY) || help_overlay_active(WL_OVERLAY) ) { + help_overlay_set_state(BR_OVERLAY, 0); + help_overlay_set_state(SS_OVERLAY, 0); + help_overlay_set_state(WL_OVERLAY, 0); + Active_ui_window->set_ignore_gadgets(0); + k = 0; + new_k = 0; + } + } + + // reset timers for flashing buttons if key pressed + if ( (k>0) || (new_k>0) ) { + common_flash_button_init(); + } + + common_music_do(); + + /* + if ( Background_playing ) { + + if ( Background_anim_instance->frame_num == BUTTON_SLIDE_IN_FRAME ) { + gamesnd_play_iface(SND_BTN_SLIDE); + } + + if ( Background_anim_instance->frame_num == Background_anim_instance->stop_at ) { + // Free up the big honking background animation, since we won't be playing it again + anim_release_render_instance(Background_anim_instance); + anim_free(Background_anim); + + Background_playing = 0; + Current_screen = Next_screen = ON_BRIEFING_SELECT; + } + } + */ + + if ( Current_screen != Next_screen ) { + switch( Next_screen ) { + case ON_BRIEFING_SELECT: + gameseq_post_event( GS_EVENT_START_BRIEFING ); + break; + + case ON_SHIP_SELECT: + // go to the specialized multiplayer team/ship select screen + if(Game_mode & GM_MULTIPLAYER){ + gameseq_post_event(GS_EVENT_TEAM_SELECT); + } + // go to the normal ship select screen + else { + gameseq_post_event(GS_EVENT_SHIP_SELECTION); + } + break; + + case ON_WEAPON_SELECT: + if ( !wss_slots_all_empty() ) { + gameseq_post_event(GS_EVENT_WEAPON_SELECTION); + } else { + common_show_no_ship_error(); + } + break; + } // end switch + } + + return new_k; +} + +// ------------------------------------------------------------------------------------- +// common_render() +// +void common_render(float frametime) +{ + if ( !Background_playing ) { + gr_set_bitmap(Brief_background_bitmap); + gr_bitmap(0, 0); + } + + anim_render_all(0, frametime); + anim_render_all(ON_SHIP_SELECT, frametime); +} + +// ------------------------------------------------------------------------------------- +// common_render_selected_screen_button() +// +// A very ugly piece of special purpose code. This is used to draw the pressed button +// frame for whatever stage of the briefing/ship select/weapons loadout we are on. +// +void common_render_selected_screen_button() +{ + Common_buttons[Next_screen-1][gr_screen.res][Next_screen-1].button.draw_forced(2); +} + +// ------------------------------------------------------------------------------------- +// common_button_do() do the button action for the specified pressed button +// +void common_button_do(int i) +{ + if ( i == COMMON_COMMIT_BUTTON ) { + Commit_pressed = 1; + return; + } + + if ( Background_playing ) + return; + + switch ( i ) { + + case COMMON_BRIEFING_BUTTON: + if ( Current_screen != ON_BRIEFING_SELECT ) { + gamesnd_play_iface(SND_SCREEN_MODE_PRESSED); + Next_screen = ON_BRIEFING_SELECT; + } + break; + + case COMMON_WEAPON_BUTTON: + if ( Current_screen != ON_WEAPON_SELECT ) { + if ( !wss_slots_all_empty() ) { + gamesnd_play_iface(SND_SCREEN_MODE_PRESSED); + Next_screen = ON_WEAPON_SELECT; + } else { + common_show_no_ship_error(); + } + } + break; + + case COMMON_SS_BUTTON: + if ( Current_screen != ON_SHIP_SELECT ) { + gamesnd_play_iface(SND_SCREEN_MODE_PRESSED); + Next_screen = ON_SHIP_SELECT; + } + break; + + case COMMON_OPTIONS_BUTTON: + gamesnd_play_iface(SND_SWITCH_SCREENS); + gameseq_post_event( GS_EVENT_OPTIONS_MENU ); + break; + + case COMMON_HELP_BUTTON: + gamesnd_play_iface(SND_HELP_PRESSED); + launch_context_help(); + break; + + } // end switch +} + +// common_check_keys() will check for keypresses common to all the interface screens. +void common_check_keys(int k) +{ + switch (k) { + + case KEY_ESC: { + + if ( Current_screen == ON_BRIEFING_SELECT ) { + if ( brief_get_closeup_icon() != NULL ) { + brief_turn_off_closeup_icon(); + break; + } + } + + // prompt the host of a multiplayer game + if(Game_mode & GM_MULTIPLAYER){ + multi_quit_game(PROMPT_ALL); + } + // go through the single player quit process + else { + // return to the main menu +/* + int return_to_menu, pf_flags; + pf_flags = PF_USE_AFFIRMATIVE_ICON|PF_USE_NEGATIVE_ICON; + return_to_menu = popup(pf_flags, 2, POPUP_NO, POPUP_YES, XSTR( "Do you want to return to the Main Hall?\n(Your campaign position will be saved)", -1)); + if ( return_to_menu == 1 ) { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } +*/ + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + break; + } + + case KEY_CTRLED + KEY_ENTER: + Commit_pressed = 1; + break; + + case KEY_B: + if ( Current_screen != ON_BRIEFING_SELECT && !Background_playing ) { + Next_screen = ON_BRIEFING_SELECT; + } + break; + + case KEY_W: + if ( brief_only_allow_briefing() ) { + gamesnd_play_iface(SND_GENERAL_FAIL); + break; + } + + #ifndef DEMO // not for FS2_DEMO + if ( Current_screen != ON_WEAPON_SELECT && !Background_playing ) { + if ( !wss_slots_all_empty() ) { + Next_screen = ON_WEAPON_SELECT; + } else { + common_show_no_ship_error(); + } + } + #else + gamesnd_play_iface(SND_GENERAL_FAIL); + #endif + + break; + + case KEY_S: + + if ( brief_only_allow_briefing() ) { + gamesnd_play_iface(SND_GENERAL_FAIL); + break; + } + + #ifndef DEMO // not for FS2_DEMO + if ( Current_screen != ON_SHIP_SELECT && !Background_playing ) { + Next_screen = ON_SHIP_SELECT; + } + #else + gamesnd_play_iface(SND_GENERAL_FAIL); + #endif + + break; + + case KEY_SHIFTED+KEY_TAB: + + if ( brief_only_allow_briefing() ) { + gamesnd_play_iface(SND_GENERAL_FAIL); + break; + } + + #ifndef DEMO // not for FS2_DEMO + if ( !Background_playing ) { + switch ( Current_screen ) { + case ON_BRIEFING_SELECT: + if ( !wss_slots_all_empty() ) { + Next_screen = ON_WEAPON_SELECT; + } else { + common_show_no_ship_error(); + } + break; + + case ON_SHIP_SELECT: + Next_screen = ON_BRIEFING_SELECT; + break; + + case ON_WEAPON_SELECT: + Next_screen = ON_SHIP_SELECT; + break; + default: + Int3(); + break; + } // end switch + } + #else + gamesnd_play_iface(SND_GENERAL_FAIL); + #endif + + break; + + case KEY_TAB: + + if ( brief_only_allow_briefing() ) { + gamesnd_play_iface(SND_GENERAL_FAIL); + break; + } + + #ifndef DEMO // not for FS2_DEMO + if ( !Background_playing ) { + switch ( Current_screen ) { + case ON_BRIEFING_SELECT: + Next_screen = ON_SHIP_SELECT; + break; + + case ON_SHIP_SELECT: + if ( !wss_slots_all_empty() ) { + Next_screen = ON_WEAPON_SELECT; + } else { + common_show_no_ship_error(); + } + break; + + case ON_WEAPON_SELECT: + Next_screen = ON_BRIEFING_SELECT; + break; + default: + Int3(); + break; + } // end switch + } + #else + gamesnd_play_iface(SND_GENERAL_FAIL); + #endif + + break; + + case KEY_P: + if ( Anim_paused ) + Anim_paused = 0; + else + Anim_paused = 1; + break; + } // end switch +} + +// common_select_close() will release the memory for animations and bitmaps that +// were loaded in common_select_init(). This function will abort if the Common_select_inited +// flag is not set. The last thing common_select_close() does in clear the Common_select_inited +// flag. +// +// weapon_select_close() and ship_select_close() are both called, since common_select_close() +// is the function that is called the interface screens are finally exited. +void common_select_close() +{ + if ( !Common_select_inited ) { + nprintf(("Alan","common_select_close() returning without doing anything\n")); + return; + } + + nprintf(("Alan","entering common_select_close()\n")); + + weapon_select_close(); + if(Game_mode & GM_MULTIPLAYER){ + multi_ts_close(); + } + ship_select_close(); + brief_close(); + + common_free_interface_palette(); + + // release the bitmpas that were previously extracted from anim files + unload_wing_icons(); + + // Release any instances that may still exist + anim_release_all_instances(); + + // free the anim's that were loaded into memory + /* + if ( Background_anim ) { + anim_free(Background_anim); + Background_anim = NULL; + } + */ + + common_music_close(); + Common_select_inited = 0; +} + +// ------------------------------------------------------------------------ +// load_wing_icons() creates the bitmaps for wing icons +// +void load_wing_icons(char *filename) +{ + int first_frame, num_frames; + + first_frame = bm_load_animation(filename, &num_frames); + if ( first_frame == -1 ) { + Error(LOCATION, "Could not load icons from %s\n", filename); + return; + } + + Wing_slot_disabled_bitmap = first_frame; + Wing_slot_empty_bitmap = first_frame + 1; +// Wing_slot_player_empty_bitmap = first_frame + 2; +} + +// ------------------------------------------------------------------------ +// common_scroll_up_pressed() +// +int common_scroll_up_pressed(int *start, int size, int max_show) +{ + // check if we even need to scroll at all + if ( size <= max_show ) { + return 0; + } + + if ( (size - *start) > max_show ) { + *start += 1; + return 1; + } + return 0; +} + +// ------------------------------------------------------------------------ +// common_scroll_down_pressed() +// +int common_scroll_down_pressed(int *start, int size, int max_show) +{ + // check if we even need to scroll at all + if ( size <= max_show ) { + return 0; + } + + if ( *start > 0 ) { + *start -= 1; + return 1; + } + return 0; +} + +// NEWSTUFF BEGIN + +loadout_data Player_loadout; // what the ship and weapon loadout is... used since we want to use the + // same loadout if the mission is played again + +//wss_unit Wss_slots[MAX_WSS_SLOTS]; // slot data struct +//int Wl_pool[MAX_WEAPON_TYPES]; // weapon pool +//int Ss_pool[MAX_SHIP_TYPES]; // ship pool +//int Wss_num_wings; // number of player wings + +wss_unit Wss_slots_teams[MAX_TEAMS][MAX_WSS_SLOTS]; +int Wl_pool_teams[MAX_TEAMS][MAX_WEAPON_TYPES]; +int Ss_pool_teams[MAX_TEAMS][MAX_SHIP_TYPES]; +int Wss_num_wings_teams[MAX_TEAMS]; + +wss_unit *Wss_slots; +int *Wl_pool; +int *Ss_pool; +int Wss_num_wings; + +// save ship selection loadout to the Player_loadout struct +void wss_save_loadout() +{ + int i,j; + + // save the ship pool + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + Player_loadout.ship_pool[i] = Ss_pool[i]; + } + + // save the weapons pool + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + Player_loadout.weapon_pool[i] = Wl_pool[i]; + } + + // save the ship class / weapons for each slot + for ( i = 0; i < MAX_WSS_SLOTS; i++ ) { + Player_loadout.unit_data[i].ship_class = Wss_slots[i].ship_class; + + for ( j = 0; j < MAX_WL_WEAPONS; j++ ) { + Player_loadout.unit_data[i].wep[j] = Wss_slots[i].wep[j]; + Player_loadout.unit_data[i].wep_count[j] = Wss_slots[i].wep_count[j]; + } + } +} + +// restore ship/weapons loadout from the Player_loadout struct +void wss_restore_loadout() +{ + int i,j; + wss_unit *slot; + + // only restore if mission hasn't changed + if ( stricmp(Player_loadout.last_modified, The_mission.modified) ) { + return; + } + + // restore the ship pool + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + Ss_pool[i] = Player_loadout.ship_pool[i]; + } + + // restore the weapons pool + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + Wl_pool[i] = Player_loadout.weapon_pool[i]; + } + + // restore the ship class / weapons for each slot + for ( i = 0; i < MAX_WSS_SLOTS; i++ ) { + slot = &Player_loadout.unit_data[i]; + Wss_slots[i].ship_class = slot->ship_class; + + for ( j = 0; j < MAX_WL_WEAPONS; j++ ) { + Wss_slots[i].wep[j]= slot->wep[j]; + Wss_slots[i].wep_count[j] = slot->wep_count[j]; + } + } +} + +// Do a direct restore of the Player_loadout ship/weapon data to the wings +void wss_direct_restore_loadout() +{ + int i, j; + wing *wp; + wss_unit *slot; + + // only restore if mission hasn't changed + if ( stricmp(Player_loadout.last_modified, The_mission.modified) ) { + return; + } + + for ( i = 0; i < MAX_WING_BLOCKS; i++ ) { + + if ( Starting_wings[i] < 0 ) + continue; + + wp = &Wings[Starting_wings[i]]; + + // If this wing is still on the arrival list, then update the parse objects + if ( wp->ship_index[0] == -1 ) { + p_object *p_objp; + j=0; + for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) { + slot = &Player_loadout.unit_data[i*4+j]; + if ( p_objp->wingnum == WING_INDEX(wp) ) { + p_objp->ship_class = slot->ship_class; + wl_update_parse_object_weapons(p_objp, slot); + j++; + } + } + } else { + int k; + int cleanup_ship_index[MAX_WING_SLOTS]; + ship *shipp; + + for ( k = 0; k < MAX_WING_SLOTS; k++ ) { + cleanup_ship_index[k] = -1; + } + + // This wing is already created, so directly update the ships + for ( j = 0; j < MAX_WING_SLOTS; j++ ) { + slot = &Player_loadout.unit_data[i*4+j]; + shipp = &Ships[wp->ship_index[j]]; + if ( shipp->ship_info_index != slot->ship_class ) { + + if ( wp->ship_index[j] == -1 ) { + continue; + } + + if ( slot->ship_class == -1 ) { + cleanup_ship_index[j] = wp->ship_index[j]; + ship_add_exited_ship( shipp, SEF_PLAYER_DELETED ); + obj_delete(shipp->objnum); + hud_set_wingman_status_none( shipp->wing_status_wing_index, shipp->wing_status_wing_pos); + continue; + } else { + change_ship_type(wp->ship_index[j], slot->ship_class); + } + } + wl_bash_ship_weapons(&Ships[wp->ship_index[j]].weapons, slot); + } + + for ( k = 0; k < MAX_WING_SLOTS; k++ ) { + if ( cleanup_ship_index[k] != -1 ) { + ship_wing_cleanup( cleanup_ship_index[k], wp ); + } + } + + } + } // end for +} +int wss_slots_all_empty() +{ + int i; + + for ( i = 0; i < MAX_WSS_SLOTS; i++ ) { + if ( Wss_slots[i].ship_class >= 0 ) + break; + } + + if ( i == MAX_WSS_SLOTS ) + return 1; + else + return 0; +} + +// determine the mode (WSS_...) based on slot/list index values +int wss_get_mode(int from_slot, int from_list, int to_slot, int to_list, int wl_ship_slot) +{ + int mode, to_slot_empty=0; + + if ( wl_ship_slot >= 0 ) { + // weapons loadout + if ( to_slot >= 0 ) { + if ( Wss_slots[wl_ship_slot].wep_count[to_slot] == 0 ) { + to_slot_empty = 1; + } + } + } else { + // ship select + if ( to_slot >= 0 ) { + if ( Wss_slots[to_slot].ship_class == -1 ){ + to_slot_empty = 1; + } + } + } + + // determine mode + if ( from_slot >= 0 && to_slot >= 0 ) { + mode = WSS_SWAP_SLOT_SLOT; + } else if ( from_slot >= 0 && to_list >= 0 ) { + mode = WSS_DUMP_TO_LIST; + } else if ( (from_list >= 0) && (to_slot >= 0) && (to_slot_empty) ) { + mode = WSS_GRAB_FROM_LIST; + } else if ( (from_list >= 0) && (to_slot >= 0) && (!to_slot_empty) ) { + mode = WSS_SWAP_LIST_SLOT; + } else { + mode = -1; // no changes required + } + + return mode; +} + +// store all the unit data and pool data +int store_wss_data(ubyte *block, int max_size, int sound,int player_index) +{ + int j, i,offset=0; + short player_id; + short ishort; + + // write the ship pool + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Ss_pool[i] > 0 ) { + block[offset++] = (ubyte)i; + Assert( Ss_pool[i] < UCHAR_MAX ); + + // take care of sign issues + if(Ss_pool[i] == -1){ + block[offset++] = 0xff; + } else { + block[offset++] = (ubyte)Ss_pool[i]; + } + } + } + + block[offset++] = 0xff; // signals start of weapons pool + + // write the weapon pool + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + if ( Wl_pool[i] > 0 ) { + block[offset++] = (ubyte)i; + ishort = (short)Wl_pool[i]; + memcpy(block+offset, &ishort, sizeof(short)); + offset += sizeof(short); + } + } + + // write the unit data + + block[offset++] = 0xff; // signals start of unit data + + for ( i=0; i MAX_SHIP_TYPES ) { + Int3(); + break; + } + + b1 = block[offset++]; + if ( b1 == 0xff ) { + break; + } + + // take care of sign issues + b2 = block[offset++]; + if(b2 == 0xff){ + Ss_pool[b1] = -1; + } else { + Ss_pool[b1] = b2; + } + } + + // restore weapons pool + sanity=0; + memset(Wl_pool, 0, MAX_WEAPON_TYPES*sizeof(int)); + for (;;) { + if ( sanity++ > MAX_WEAPON_TYPES ) { + Int3(); + break; + } + + b1 = block[offset++]; + if ( b1 == 0xff ) { + break; + } + + memcpy(&ishort, block+offset, sizeof(short)); + offset += sizeof(short); + Wl_pool[b1] = ishort; + } + + for ( i=0; iplayer_id == player_id)){ + // play the sound + if(sound != 0xff){ + gamesnd_play_iface((int)sound); + } + } + + if(!(Game_mode & GM_MULTIPLAYER)){ + ss_synch_interface(); + } + return offset; +} + +// NEWSTUFF END diff --git a/src/missionui/missionshipchoice.cpp b/src/missionui/missionshipchoice.cpp new file mode 100644 index 0000000..b58cb5c --- /dev/null +++ b/src/missionui/missionshipchoice.cpp @@ -0,0 +1,3384 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionShipChoice.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module to allow player ship selection for the mission + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 26 11/02/99 3:23p Jefff + * "x Meters" to "x Meter" in German + * + * 25 8/05/99 3:40p Jefff + * hi-res text adjustments + * + * 24 8/04/99 11:56a Jefff + * fixed gamma wing hi-res coordinate error. fixed + * ss_load_individual_animation to load anis from a packfile. + * + * 23 7/30/99 4:22p Andsager + * restored ship and weapon anim sounds for demo. Added sound for + * changing ship in weapon loadout screen. + * + * 22 7/28/99 12:23p Jefff + * updated hi-res wing icon coords + * + * 21 7/21/99 3:24p Andsager + * Modify demo to use 2 frame ship and weapon select anis and cut sounds + * associated with ani + * + * 20 7/16/99 2:23p Anoop + * Fixed build error. + * + * 19 7/16/99 1:49p Dave + * 8 bit aabitmaps. yay. + * + * 18 7/15/99 6:36p Jamesa + * Moved default ship name into the ships.tbl + * + * 17 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 16 6/29/99 7:39p Dave + * Lots of small bug fixes. + * + * 15 6/04/99 11:32a Dave + * Added reset text to ship select screen. Fixed minor xstr bug in ui + * window + * + * 14 3/23/99 9:26p Neilk + * fixed various multiplayer lock button problems + * + * 13 3/23/99 11:55a Neilk + * new support for ship anis + * + * 14 3/15/99 6:27p Neilk + * Added hires support for the ship animations + * + * 13 3/15/99 11:18a Neilk + * Modified animation code so ship animations loop back to frame 51 + * + * 12 3/12/99 12:02p Davidg + * Modified coordinates for low res ship animations for new artwork + * + * 11 3/10/99 6:21p Neilk + * Added new artwork for hires + * + * 10 2/21/99 6:01p Dave + * Fixed standalone WSS packets. + * + * 9 2/18/99 11:46a Neilk + * hires interface coord support + * + * 8 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 7 2/01/99 5:55p Dave + * Removed the idea of explicit bitmaps for buttons. Fixed text + * highlighting for disabled gadgets. + * + * 6 1/29/99 4:17p Dave + * New interface screens. + * + * 5 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 4 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 3 10/13/98 9:28a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 102 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 101 6/09/98 10:31a Hoffoss + * Created index numbers for all xstr() references. Any new xstr() stuff + * added from here on out should be added to the end if the list. The + * current list count can be found in FreeSpace.cpp (search for + * XSTR_SIZE). + * + * 100 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 99 5/23/98 5:50p Lawrance + * Don't reset scroll offset when rebuilding the list + * + * 98 5/06/98 11:50p Lawrance + * Clean up help overlay code for loadout screens + * + * 97 4/30/98 6:03p Lawrance + * Make drag and drop work better. + * + * 96 4/29/98 3:31p Lawrance + * draw disabled frames for icons when appropriate + * + * 95 4/28/98 9:35a Dave + * Remove bogus assert in create_wings() for ships which arrive late and + * haven't been created yet. + * + * 94 4/27/98 6:02p Dave + * Modify how missile scoring works. Fixed a team select ui bug. Speed up + * multi_lag system. Put in new main hall. + * + * 93 4/14/98 12:57a Dave + * Made weapon select screen show netplayer names above ships. Fixed pilot + * info popup to show the status of pilot images more correctly. + * + * 92 4/13/98 3:27p Lawrance + * fix coords for ship selection pool numbers + * + * 91 4/13/98 3:11p Andsager + * Fixed bug when there is no description for a ship. + * + * 90 4/10/98 4:51p Hoffoss + * Made several changes related to tooltips. + * + * 89 4/05/98 7:43p Lawrance + * fix up saving/restoring of link status and auto-target/match-speed. + * + * 88 4/03/98 4:16p Adam + * changed coord's and skip frame for new SS animations + * + * 87 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 86 4/01/98 11:19p Dave + * Put in auto-loading of xferred pilot pic files. Grey out background + * behind pinfo popup. Put a chatbox message in when players are kicked. + * Moved mission title down in briefing. Other ui fixes. + * + * 85 4/01/98 9:21p John + * Made NDEBUG, optimized build with no warnings or errors. + * + * 84 3/31/98 11:47p Lawrance + * Fix some bugs related to wingmen selection when doing a quick mission + * restart. + * + * 83 3/31/98 1:50p Duncan + * ALAN: fix bugs with selecting alternate weapons + * + * 82 3/30/98 12:18a Lawrance + * change some DEMO_RELEASE code to not compile code rather than return + * early + * + * 81 3/29/98 12:55a Lawrance + * Get demo build working with limited set of data. + * + * 80 3/26/98 6:01p Dave + * Put in file checksumming routine in cfile. Made pilot pic xferring more + * robust. Cut header size of voice data packets in half. Put in + * restricted game host query system. + * + * 79 3/25/98 8:43p Hoffoss + * Changed anim_play() to not be so damn complex when you try and call it. + * + * 78 3/12/98 4:03p Lawrance + * don't press buttons when icon dropped on them + * + * 77 3/09/98 11:13a Lawrance + * Fix up drop sound effects used in loadout screens. + * + * 76 3/06/98 5:36p Dave + * Finished up first rev of team vs team. Probably needs to be debugged + * thoroughly. + * + * 75 3/05/98 6:48p Lawrance + * reposition commit_pressed() to ensure popup gets called after clear but + * before flip + * + * 74 3/05/98 5:03p Dave + * More work on team vs. team support for multiplayer. Need to fix bugs in + * weapon select. + * + * 73 3/05/98 12:38a Lawrance + * Fix bug with flashing buttons showing over help overlay + * + * 72 3/02/98 5:42p John + * Removed WinAVI stuff from Freespace. Made all HUD gauges wriggle from + * afterburner. Made gr_set_clip work good with negative x &y. Made + * model_caching be on by default. Made each cached model have it's own + * bitmap id. Made asteroids not rotate when model_caching is on. + * + * 71 3/01/98 3:26p Dave + * Fixed a few team select bugs. Put in multiplayer intertface sounds. + * Corrected how ships are disabled/enabled in team select/weapon select + * screens. + * + * 70 2/28/98 7:04p Lawrance + * Don't show reset button in multiplayer + * + * 69 2/26/98 8:21p Allender + * fix compiler warning + * + * 68 2/26/98 4:59p Allender + * groundwork for team vs team briefings. Moved weaponry pool into the + * Team_data structure. Added team field into the p_info structure. + * Allow for mutliple structures in the briefing code. + * + * 67 2/24/98 6:21p Lawrance + * Integrate new reset button into loadout screens + * + * 66 2/22/98 4:30p John + * More string externalization classification + * + * 65 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 64 2/22/98 12:19p John + * Externalized some strings + * + * 63 2/19/98 6:26p Dave + * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in + * initial support for player data uploading. + * + * 62 2/18/98 3:56p Dave + * Several bugs fixed for mp team select screen. Put in standalone packet + * routing for team select. + * + * 61 2/17/98 6:07p Dave + * Tore out old multiplayer team select screen, installed new one. + * + * 60 2/13/98 3:46p Dave + * Put in dynamic chatbox sizing. Made multiplayer file lookups use cfile + * functions. + * + * 59 2/12/98 2:38p Allender + * fix multiplayer primary/secondary weapon problems when ships are + * outfitted with less than max number + * + * 58 2/07/98 5:47p Lawrance + * reset flashing if a button gets highlighted + * + * 57 2/05/98 11:21p Lawrance + * When flashing buttons, use highlight frame + * + * 56 1/30/98 10:00a Allender + * made large ships able to attack other ships. Made goal code recognize + * when ships removed from wings during ship select + * + * 55 1/22/98 5:26p Dave + * Modified some pregame sequencing packets. Starting to repair broken + * standalone stuff. + * + * 54 1/17/98 2:46a Dave + * Reworked multiplayer join/accept process. Ingame join still needs to be + * integrated. + * + * 53 1/15/98 4:11p Lawrance + * Add call to check if slot is player occupied. + * + * 52 1/13/98 4:47p Allender + * change default terran ship to reflect new ship class name + * + * 51 1/12/98 5:17p Dave + * Put in a bunch of multiplayer sequencing code. Made weapon/ship select + * work through the standalone. + * + * 50 1/10/98 12:46a Lawrance + * Store last_modified time for mission into player loadout struct. + * + * 49 1/09/98 6:06p Dave + * Put in network sound support for multiplayer ship/weapon select + * screens. Made clients exit game correctly through warp effect. Fixed + * main hall menu help overlay bug. + * + * 48 1/08/98 11:38a Lawrance + * correct typo + * + * 47 1/08/98 11:36a Lawrance + * Get ship select and weapons loadout icon dropping sound effects working + * for single and multiplayer + * + * 46 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 45 12/29/97 4:21p Lawrance + * Flash buttons on briefing/ship select/weapons loadout when enough time + * has elapsed without activity. + * + * 44 12/29/97 9:42a Lawrance + * Ensure that WING_SLOT_IS_PLAYER gets set correctly in multiplayer. + * + * 43 12/24/97 8:54p Lawrance + * Integrating new popup code + * + * 42 12/24/97 1:19p Lawrance + * fix some bugs with the multiplayer ship/weapons loadout + * + * 41 12/23/97 5:25p Allender + * more fixes to multiplayer ship selection. Fixed strange reentrant + * problem with cf_callback when loading freespace data + * + * 40 12/23/97 11:59a Allender + * changes to ship/wespon selection for multplayer. added sounds to some + * interface screens. Update and modiied end-of-briefing packets -- yet + * to be tested. + * + * 39 12/23/97 11:04a Lawrance + * fix bug in ss_swap_slot_slot() + * + * 38 12/23/97 10:57a Lawrance + * move player_set_weapon_prefs() to when commit is pressed in briefing + * (from entering gameplay) + * + * 37 12/23/97 10:54a Lawrance + * fix some bugs in multiplayer ship selection + * + * 36 12/22/97 6:18p Lawrance + * Get save/restore of player loadout working with new code + * + * 35 12/22/97 1:40a Lawrance + * Re-write ship select/weapons loadout to be multiplayer friendly + * + * 34 12/19/97 1:23p Dave + * Put in multiplayer groundwork for new weapon/ship select screens. + * + * $NoKeywords: $ + * +*/ + +#include "missionscreencommon.h" +#include "missionshipchoice.h" +#include "missionparse.h" +#include "missionbrief.h" +#include "freespace.h" +#include "gamesequence.h" +#include "ship.h" +#include "key.h" +#include "2d.h" +#include "line.h" +#include "3d.h" +#include "model.h" +#include "timer.h" +#include "math.h" +#include "linklist.h" +#include "mouse.h" +#include "weapon.h" +#include "ui.h" +#include "ailocal.h" +#include "player.h" +#include "audiostr.h" +#include "bmpman.h" +#include "palman.h" +#include "snazzyui.h" +#include "animplay.h" +#include "packunpack.h" +#include "missionweaponchoice.h" +#include "contexthelp.h" +#include "gamesnd.h" +#include "sound.h" +#include "missionhotkey.h" +#include "multi.h" +#include "multimsgs.h" +#include "missionload.h" +#include "eventmusic.h" +#include "chatbox.h" +#include "popup.h" +#include "multiui.h" +#include "multiteamselect.h" +#include "multiutil.h" +#include "hudwingmanstatus.h" +#include "alphacolors.h" +#include "localize.h" + +////////////////////////////////////////////////////// +// Game-wide Globals +////////////////////////////////////////////////////// +char default_player_ship[255] = NOX("GTF Ulysses"); +int Select_default_ship = 0; +int Ship_select_open = 0; // This game-wide global flag is set to 1 to indicate that the ship + // select screen has been opened and memory allocated. This flag + // is needed so we can know if ship_select_close() needs to called if + // restoring a game from the Options screen invoked from ship select + +int Commit_pressed; // flag to indicate that the commit button was pressed + // use a flag, so the ship_create() can be done at the end of the loop + +////////////////////////////////////////////////////// +// Module Globals +////////////////////////////////////////////////////// +static int Ship_anim_class = -1; // ship class that is playing as an animation +static int Ss_delta_x, Ss_delta_y; // used to offset the carried icon to make it smoothly leave static position + +////////////////////////////////////////////////////// +// UI Data structs +////////////////////////////////////////////////////// +typedef struct ss_icon_info +{ + int icon_bmaps[NUM_ICON_FRAMES]; + int current_icon_bitmap; + anim_t *anim; + anim_instance_t *anim_instance; +} ss_icon_info; + +typedef struct ss_slot_info +{ + int status; // slot status (WING_SLOT_DISABLED, etc) + int sa_index; // index into ship arrival list, -1 if ship is created + int original_ship_class; +} ss_slot_info; + +typedef struct ss_wing_info +{ + int num_slots; + int wingnum; + int is_late; + ss_slot_info ss_slots[MAX_WING_SLOTS]; +} ss_wing_info; + +//ss_icon_info Ss_icons[MAX_SHIP_TYPES]; // holds ui info on different ship icons +//ss_wing_info Ss_wings[MAX_WING_BLOCKS]; // holds ui info for wings and wing slots + +ss_wing_info Ss_wings_teams[MAX_TEAMS][MAX_WING_BLOCKS]; +ss_wing_info *Ss_wings; + +ss_icon_info Ss_icons_teams[MAX_TEAMS][MAX_SHIP_TYPES]; +ss_icon_info *Ss_icons; + +int Ss_mouse_down_on_region = -1; + +int Selected_ss_class; // set to ship class of selected ship, -1 if none selected +int Hot_ss_icon; // index that icon is over in list (0..4) +int Hot_ss_slot; // index for slot that mouse is over (0..11) + +//////////////////////////////////////////////////////////// +// Ship Select UI +//////////////////////////////////////////////////////////// +UI_WINDOW Ship_select_ui_window; + +static int Ship_anim_coords[GR_NUM_RESOLUTIONS][2] = { + { + 257, 84 // GR_640 + }, + { + 412, 135 // GR_1024 + } +}; + +static int Ship_info_coords[GR_NUM_RESOLUTIONS][2] = { + { + 28, 78 // GR_640 + }, + { + 45, 125 // GR_1024 + } +}; + +// coordinate lookup indicies +#define SHIP_SELECT_X_COORD 0 +#define SHIP_SELECT_Y_COORD 1 +#define SHIP_SELECT_W_COORD 2 +#define SHIP_SELECT_H_COORD 3 + + +// NK: changed from 37 to 51 for new FS2 animations +#ifdef FS2_DEMO +#define SHIP_ANIM_LOOP_FRAME 0 +#else +#define SHIP_ANIM_LOOP_FRAME 51 +#endif + +#define MAX_ICONS_ON_SCREEN 4 + +// (x,y) pairs for ship icon and ship icon number +int Ship_list_coords[GR_NUM_RESOLUTIONS][MAX_ICONS_ON_SCREEN][4] = { + { + {23,331,4,341}, + {23,361,4,371}, + {23,391,4,401}, + {23,421,4,431} + }, + { + {29,530,10,540}, + {29,578,10,588}, + {29,626,10,636}, + {29,674,10,684} + } +}; + +// Store the x locations for the icons in the wing formations +int Wing_icon_coords[GR_NUM_RESOLUTIONS][MAX_WSS_SLOTS][2] = { + { + {124,345}, + {100,376}, + {148,376}, + {124,407}, + + {222,345}, + {198,376}, + {246,376}, + {222,407}, + + {320,345}, + {296,376}, + {344,376}, + {320,407} + }, + { + {218,584}, + {194,615}, + {242,615}, + {218,646}, + + {373,584}, + {349,615}, + {397,615}, + {373,646}, + + {531,584}, + {507,615}, + {555,615}, + {531,646} + } +}; + +////////////////////////////////////////////////////// +// Linked List of icons to show on ship selection list +////////////////////////////////////////////////////// +#define SS_ACTIVE_ITEM_USED (1<<0) +typedef struct ss_active_item +{ + ss_active_item *prev, *next; + int ship_class; + int flags; +} ss_active_item; + +static ss_active_item SS_active_head; +static ss_active_item SS_active_items[MAX_WSS_SLOTS]; + +static int SS_active_list_start; +static int SS_active_list_size; + +////////////////////////////////////////////////////// +// Background bitmaps data for ship_select +////////////////////////////////////////////////////// +static char* Ship_select_background_fname[GR_NUM_RESOLUTIONS] = { + "ShipSelect", + "2_ShipSelect" +}; + +static char* Ship_select_background_mask_fname[GR_NUM_RESOLUTIONS] = { + "ShipSelect-m", + "2_ShipSelect-m" +}; + +int Ship_select_background_bitmap; + +////////////////////////////////////////////////////// +// Ship select specific buttons +////////////////////////////////////////////////////// +#define NUM_SS_BUTTONS 4 +#define SS_BUTTON_SCROLL_UP 0 +#define SS_BUTTON_SCROLL_DOWN 1 +#define SS_BUTTON_RESET 2 +#define SS_BUTTON_DUMMY 3 // needed to capture mouse for drag/drop icons + +// convenient struct for handling all button controls +struct ss_buttons { + char *filename; + int x, y, xt, yt; + int hotspot; + int scrollable; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + ss_buttons(char *name, int x1, int y1, int xt1, int yt1, int h, int s) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h), scrollable(s) {} +}; + +static ss_buttons Ship_select_buttons[GR_NUM_RESOLUTIONS][NUM_SS_BUTTONS] = { + { // GR_640 + ss_buttons("ssb_08", 5, 303, -1, -1, 8, 0), // SCROLL UP + ss_buttons("ssb_09", 5, 454, -1, -1, 9, 0), // SCROLL DOWN + ss_buttons("ssb_39", 571, 347, -1, -1, 39,0), // RESET + ss_buttons("ssb_39", 0, 0, -1, -1, 99,0) // dummy for drag n' drop + }, + { // GR_1024 + ss_buttons("2_ssb_08", 8, 485, -1, -1, 8, 0), // SCROLL UP + ss_buttons("2_ssb_09", 8, 727, -1, -1, 9, 0), // SCROLL DOWN + ss_buttons("2_ssb_39", 913, 556, -1, -1, 39,0), // RESET + ss_buttons("2_ssb_39", 0, 0, -1, -1, 99,0) // dummy for drag n' drop + } +}; + +// ship select text +#define SHIP_SELECT_NUM_TEXT 1 +UI_XSTR Ship_select_text[GR_NUM_RESOLUTIONS][SHIP_SELECT_NUM_TEXT] = { + { // GR_640 + { "Reset", 1337, 580, 337, UI_XSTR_COLOR_GREEN, -1, &Ship_select_buttons[0][SS_BUTTON_RESET].button } + }, + { // GR_1024 + { "Reset", 1337, 938, 546, UI_XSTR_COLOR_GREEN, -1, &Ship_select_buttons[1][SS_BUTTON_RESET].button } + } +}; + +// Mask bitmap pointer and Mask bitmap_id +static bitmap* ShipSelectMaskPtr; // bitmap pointer to the ship select mask bitmap +static ubyte* ShipSelectMaskData; // pointer to actual bitmap data +static int Shipselect_mask_w, Shipselect_mask_h; +static int ShipSelectMaskBitmap; // bitmap id of the ship select mask bitmap + +static MENU_REGION Region[NUM_SHIP_SELECT_REGIONS]; +static int Num_mask_regions; + +////////////////////////////////////////////////////// +// Drag and Drop variables +////////////////////////////////////////////////////// +typedef struct ss_carry_icon_info +{ + int from_slot; // slot index (0..11), -1 if carried from list + int ship_class; // ship class of carried icon + int from_x, from_y; +} ss_carry_icon_info; + +ss_carry_icon_info Carried_ss_icon; + +//////////////////////////////////////////////////////////////////// +// Internal function prototypes +//////////////////////////////////////////////////////////////////// + +// render functions +void draw_ship_icons(); +void draw_ship_icon_with_number(int screen_offset, int ship_class); +void stop_ship_animation(); +void start_ship_animation(int ship_class, int play_sound=0); + +// pick-up +int pick_from_ship_list(int screen_offset, int ship_class); +void pick_from_wing(int wb_num, int ws_num); + +// ui related +void ship_select_button_do(int i); +void ship_select_common_init(); +void ss_reset_selected_ship(); +void ss_restore_loadout(); +void maybe_change_selected_wing_ship(int wb_num, int ws_num); + +// init functions +void ss_init_pool(team_data *pteam); +int create_wings(); + +// loading/unloading +void ss_unload_icons(); +void ss_init_units(); +void unload_ship_anim_instances(); +void unload_ship_anims(); +anim* ss_load_individual_animation(int ship_class); + +// Carry icon functions +int ss_icon_being_carried(); +void ss_reset_carried_icon(); +void ss_set_carried_icon(int from_slot, int ship_class); + +#define SHIP_DESC_X 445 +#define SHIP_DESC_Y 273 + +char *ss_tooltip_handler(char *str) +{ + if (Selected_ss_class < 0) + return NULL; + + if (!stricmp(str, NOX("@ship_name"))) { + return Ship_info[Selected_ss_class].name; + + } else if (!stricmp(str, NOX("@ship_type"))) { + return Ship_info[Selected_ss_class].type_str; + + } else if (!stricmp(str, NOX("@ship_maneuverability"))) { + return Ship_info[Selected_ss_class].maneuverability_str; + + } else if (!stricmp(str, NOX("@ship_armor"))) { + return Ship_info[Selected_ss_class].armor_str; + + } else if (!stricmp(str, NOX("@ship_manufacturer"))) { + return Ship_info[Selected_ss_class].manufacturer_str; + + } else if (!stricmp(str, NOX("@ship_desc"))) { + char *str; + int x, y, w, h; + + str = Ship_info[Selected_ss_class].desc; + if (!str) + return NULL; + + gr_get_string_size(&w, &h, str); + x = SHIP_DESC_X - w / 2; + y = SHIP_DESC_Y - h / 2; + + gr_set_color_fast(&Color_black); + gr_rect(x - 5, y - 5, w + 10, h + 10); + + gr_set_color_fast(&Color_bright_white); + gr_string(x, y, str); + return NULL; + } + + return NULL; +} + +// Is an icon being carried? +int ss_icon_being_carried() +{ + if ( Carried_ss_icon.ship_class >= 0 ) { + return 1; + } + + return 0; +} + +// Clear out carried icon info +void ss_reset_carried_icon() +{ + Carried_ss_icon.from_slot = -1; + Carried_ss_icon.ship_class = -1; +} + +// return !0 if carried icon has moved from where it was picked up +int ss_carried_icon_moved() +{ + int mx, my; + + mouse_get_pos( &mx, &my ); + if ( Carried_ss_icon.from_x != mx || Carried_ss_icon.from_y != my) { + return 1; + } + + return 0; +} + +// Set carried icon data +void ss_set_carried_icon(int from_slot, int ship_class) +{ + Carried_ss_icon.from_slot = from_slot; + Carried_ss_icon.ship_class = ship_class; + + // Set the mouse to captured + Ship_select_buttons[gr_screen.res][SS_BUTTON_DUMMY].button.capture_mouse(); +} + +// clear all active list items, and reset the flags inside the SS_active_items[] array +void clear_active_list() +{ + int i; + for ( i = 0; i < MAX_WSS_SLOTS; i++ ) { + SS_active_items[i].flags = 0; + SS_active_items[i].ship_class = -1; + } + list_init(&SS_active_head); + + SS_active_list_start = 0; + SS_active_list_size = 0; +} + +// get a free element from SS_active_items[] +ss_active_item *get_free_active_list_node() +{ + int i; + for ( i = 0; i < MAX_WSS_SLOTS; i++ ) { + if ( SS_active_items[i].flags == 0 ) { + SS_active_items[i].flags |= SS_ACTIVE_ITEM_USED; + return &SS_active_items[i]; + } + } + return NULL; +} + +// add a ship into the active list +void active_list_add(int ship_class) +{ + ss_active_item *sai; + + sai = get_free_active_list_node(); + Assert(sai != NULL); + sai->ship_class = ship_class; + list_append(&SS_active_head, sai); +} + +// remove a ship from the active list +void active_list_remove(int ship_class) +{ + ss_active_item *sai, *temp; + + // next store players not assigned to wings + sai = GET_FIRST(&SS_active_head); + + while(sai != END_OF_LIST(&SS_active_head)){ + temp = GET_NEXT(sai); + if ( sai->ship_class == ship_class ) { + list_remove(&SS_active_head, sai); + sai->flags = 0; + } + sai = temp; + } +} + +// Build up the ship selection active list, which is a list of all ships that the player +// can choose from. +void init_active_list() +{ + int i; + ss_active_item *sai; + + clear_active_list(); + + // build the active list + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Ss_pool[i] > 0 ) { + sai = get_free_active_list_node(); + if ( sai != NULL ) { + sai->ship_class = i; + list_append(&SS_active_head, sai); + SS_active_list_size++; + } + } + } +} + +void ship_select_check_buttons() +{ + int i; + ss_buttons *b; + + for ( i = 0; i < NUM_SS_BUTTONS; i++ ) { + b = &Ship_select_buttons[gr_screen.res][i]; + if ( b->button.pressed() ) { + ship_select_button_do(b->hotspot); + } + } +} + +// reset the ship selection to the mission defaults +void ss_reset_to_default() +{ + if ( Game_mode & GM_MULTIPLAYER ) { + Int3(); + return; + } + + stop_ship_animation(); + + ss_init_pool(&Team_data[Common_team]); + ss_init_units(); + init_active_list(); + ss_reset_selected_ship(); + ss_reset_carried_icon(); + + // reset weapons + wl_reset_to_defaults(); + + start_ship_animation(Selected_ss_class, 1); +} + +// ------------------------------------------------------------------- +// ship_select_redraw_pressed_buttons() +// +// Redraw any ship select buttons that are pressed down. This function is needed +// since we sometimes need to draw pressed buttons last to ensure the entire +// button gets drawn (and not overlapped by other buttons) +// +void ship_select_redraw_pressed_buttons() +{ + int i; + ss_buttons *b; + + common_redraw_pressed_buttons(); + + for ( i = 0; i < NUM_SS_BUTTONS; i++ ) { + b = &Ship_select_buttons[gr_screen.res][i]; + if ( b->button.pressed() ) { + b->button.draw_forced(2); + } + } +} + +void ship_select_buttons_init() +{ + ss_buttons *b; + int i; + + for ( i = 0; i < NUM_SS_BUTTONS; i++ ) { + b = &Ship_select_buttons[gr_screen.res][i]; + b->button.create( &Ship_select_ui_window, "", b->x, b->y, 60, 30, b->scrollable); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action( common_play_highlight_sound ); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // add all xstrs + for(i=0; ip_info.ship_index/4,Net_player->p_info.ship_index % 4); + } + + set_active_ui(&Ship_select_ui_window); + Current_screen = ON_SHIP_SELECT; + + Ss_mouse_down_on_region = -1; + + help_overlay_set_state(SS_OVERLAY,0); + + if ( Ship_select_open ) { + start_ship_animation( Selected_ss_class ); + common_buttons_maybe_reload(&Ship_select_ui_window); // AL 11-21-97: this is necessary since we may returning from the hotkey + // screen, which can release common button bitmaps. + common_reset_buttons(); + nprintf(("Alan","ship_select_init() returning without doing anything\n")); + return; + } + + nprintf(("Alan","entering ship_select_init()\n")); + common_select_init(); + + ShipSelectMaskBitmap = bm_load(Ship_select_background_mask_fname[gr_screen.res]); + if (ShipSelectMaskBitmap < 0) { + if (gr_screen.res == GR_640) { + Error(LOCATION,"Could not load in 'shipselect-m'!"); + } else if (gr_screen.res == GR_1024) { + Error(LOCATION,"Could not load in '2_shipselect-m'!"); + } + } + + Shipselect_mask_w = -1; + Shipselect_mask_h = -1; + + // get a pointer to bitmap by using bm_lock() + ShipSelectMaskPtr = bm_lock(ShipSelectMaskBitmap, 8, BMP_AABITMAP); + ShipSelectMaskData = (ubyte*)ShipSelectMaskPtr->data; + bm_get_info(ShipSelectMaskBitmap, &Shipselect_mask_w, &Shipselect_mask_h); + + help_overlay_load(SS_OVERLAY); + + // Set up the mask regions + // initialize the different regions of the menu that will react when the mouse moves over it + Num_mask_regions = 0; + + snazzy_menu_add_region(&Region[Num_mask_regions++], "", COMMON_BRIEFING_REGION, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", COMMON_SS_REGION, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", COMMON_WEAPON_REGION, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", COMMON_COMMIT_REGION, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", COMMON_HELP_REGION, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", COMMON_OPTIONS_REGION, 0); + + snazzy_menu_add_region(&Region[Num_mask_regions++], "", SHIP_SELECT_SHIP_SCROLL_UP, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", SHIP_SELECT_SHIP_SCROLL_DOWN, 0); + + snazzy_menu_add_region(&Region[Num_mask_regions++], "", SHIP_SELECT_ICON_0, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", SHIP_SELECT_ICON_1, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", SHIP_SELECT_ICON_2, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", SHIP_SELECT_ICON_3, 0); + + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_0_SHIP_0, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_0_SHIP_1, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_0_SHIP_2, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_0_SHIP_3, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_1_SHIP_0, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_1_SHIP_1, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_1_SHIP_2, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_1_SHIP_3, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_2_SHIP_0, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_2_SHIP_1, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_2_SHIP_2, 0); + snazzy_menu_add_region(&Region[Num_mask_regions++], "", WING_2_SHIP_3, 0); + + Ship_select_open = 1; // This game-wide global flag is set to 1 to indicate that the ship + // select screen has been opened and memory allocated. This flag + // is needed so we can know if ship_select_close() needs to called if + // restoring a game from the Options screen invoked from ship select + + // init ship selection masks and buttons + Ship_select_ui_window.create( 0, 0, gr_screen.max_w, gr_screen.max_h, 0 ); + Ship_select_ui_window.set_mask_bmap(Ship_select_background_mask_fname[gr_screen.res]); + Ship_select_ui_window.tooltip_handler = ss_tooltip_handler; + common_buttons_init(&Ship_select_ui_window); + ship_select_buttons_init(); + start_ship_animation( Selected_ss_class ); + + // init ship selection background bitmpa + Ship_select_background_bitmap = bm_load(Ship_select_background_fname[gr_screen.res]); +} + + +// Return the ship class for the icon specified by index. Need to iterate through the active +// list of icons to find out what ship class for this icon +// +// input: index => list index (0..3) +// exit: ship class, -1 if none +// +int ss_get_ship_class_from_list(int index) +{ + ss_active_item *sai; + int list_entry, i, count; + + i = 0; + count = 0; + list_entry = -1; + for ( sai = GET_FIRST(&SS_active_head); sai != END_OF_LIST(&SS_active_head); sai = GET_NEXT(sai) ) { + count++; + if ( count <= SS_active_list_start ) + continue; + + if ( i >= MAX_ICONS_ON_SCREEN ) + break; + + if ( i == index ) { + list_entry = sai->ship_class; + break; + } + + i++; + } + + return list_entry; +} + +// --------------------------------------------------------------------- +// maybe_pick_up_list_icon() +// +void maybe_pick_up_list_icon(int offset) +{ + int ship_class; + + ship_class = ss_get_ship_class_from_list(offset); + if ( ship_class != -1 ) { + pick_from_ship_list(offset, ship_class); + } +} + +// --------------------------------------------------------------------- +// maybe_change_selected_ship() +// +void maybe_change_selected_ship(int offset) +{ + int ship_class; + + ship_class = ss_get_ship_class_from_list(offset); + if ( ship_class == -1 ) + return; + + if ( Ss_mouse_down_on_region != (SHIP_SELECT_ICON_0+offset) ) { + return; + } + + if ( Selected_ss_class == -1 ) { + Selected_ss_class = ship_class; + start_ship_animation(Selected_ss_class, 1); + } + else if ( Selected_ss_class != ship_class ) { + Selected_ss_class = ship_class; + start_ship_animation(Selected_ss_class, 1); + } + else + Assert( Selected_ss_class == ship_class ); +} + +void maybe_change_selected_wing_ship(int wb_num, int ws_num) +{ + ss_slot_info *ss_slot; + + Assert(wb_num >= 0 && wb_num < MAX_WING_BLOCKS); + Assert(ws_num >= 0 && ws_num < MAX_WING_SLOTS); + + if ( Ss_wings[wb_num].wingnum < 0 ) { + return; + } + + ss_slot = &Ss_wings[wb_num].ss_slots[ws_num]; + switch ( ss_slot->status & ~WING_SLOT_LOCKED ) { + + case WING_SLOT_FILLED: + case WING_SLOT_FILLED|WING_SLOT_IS_PLAYER: + if ( Selected_ss_class != -1 && Selected_ss_class != Wss_slots[wb_num*4+ws_num].ship_class ) { + Selected_ss_class = Wss_slots[wb_num*4+ws_num].ship_class; + start_ship_animation(Selected_ss_class, 1); + } + break; + + default: + // do nothing + break; + } // end switch +} + +// --------------------------------------------------------------------- +// do_mouse_over_wing_slot() +// +// returns: 0 => icon wasn't dropped onto slot +// 1 => icon was dropped onto slot +int do_mouse_over_wing_slot(int block, int slot) +{ + Hot_ss_slot = block*4 + slot; + + if ( !mouse_down(MOUSE_LEFT_BUTTON) ) { + if ( ss_icon_being_carried() ) { + + if ( ss_disabled_slot(block*4+slot) ) { + gamesnd_play_iface(SND_ICON_DROP); + return 0; + } + + if ( !ss_carried_icon_moved() ) { + ss_reset_carried_icon(); + return 0; + } + + ss_drop(Carried_ss_icon.from_slot, Carried_ss_icon.ship_class, Hot_ss_slot, -1); + ss_reset_carried_icon(); + } + } + else { + if ( Ss_mouse_down_on_region == (WING_0_SHIP_0+block*4+slot) ) { + pick_from_wing(block, slot); + } + } + + return 1; +} + +void do_mouse_over_list_slot(int index) +{ + Hot_ss_icon = index; + + if ( Ss_mouse_down_on_region != (SHIP_SELECT_ICON_0+index) ){ + return; + } + + if ( mouse_down(MOUSE_LEFT_BUTTON) ) + maybe_pick_up_list_icon(index); +} + +// Icon has been dropped, but not onto a wing slot +void ss_maybe_drop_icon() +{ + if ( Drop_icon_mflag ) { + if ( ss_icon_being_carried() ) { + // Add back into the ship entry list + if ( Carried_ss_icon.from_slot >= 0 ) { + // return to list + ss_drop(Carried_ss_icon.from_slot, -1, -1, Carried_ss_icon.ship_class); + } else { + if ( ss_carried_icon_moved() ) { + gamesnd_play_iface(SND_ICON_DROP); + } + } + ss_reset_carried_icon(); + } + } +} + +void ss_anim_pause() +{ + if ( Selected_ss_class >= 0 && Ss_icons[Selected_ss_class].anim_instance ) { + anim_pause(Ss_icons[Selected_ss_class].anim_instance); + } +} + +void ss_anim_unpause() +{ + if ( Selected_ss_class >= 0 && Ss_icons[Selected_ss_class].anim_instance ) { + anim_unpause(Ss_icons[Selected_ss_class].anim_instance); + } +} + +// maybe flash a button if player hasn't done anything for a while +void ss_maybe_flash_button() +{ + if ( common_flash_bright() ) { + // weapon loadout button + if ( Common_buttons[Current_screen-1][gr_screen.res][2].button.button_hilighted() ) { + common_flash_button_init(); + } else { + Common_buttons[Current_screen-1][gr_screen.res][2].button.draw_forced(1); + } + } +} + + +// ------------------------------------------------------------------------------------- +// ship_select_render(float frametime) +// +void ship_select_render(float frametime) +{ + if ( !Background_playing ) { + gr_set_bitmap(Ship_select_background_bitmap); + gr_bitmap(0, 0); + } + + anim_render_all(0, frametime); + anim_render_all(ON_SHIP_SELECT, frametime); +} + + +// blit any active ship information text +void ship_select_blit_ship_info() +{ + int y_start; + ship_info *sip; + char str[100]; + color *header = &Color_white; + color *text = &Color_green; + + + // if we don't have a valid ship selected, do nothing + if(Selected_ss_class == -1){ + return; + } + + // get the ship class + sip = &Ship_info[Selected_ss_class]; + + // starting line + y_start = Ship_info_coords[gr_screen.res][SHIP_SELECT_Y_COORD]; + + memset(str,0,100); + + // blit the ship class (name) + gr_set_color_fast(header); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD], y_start,XSTR("Class",739)); + y_start += 10; + if(strlen(sip->name)){ + gr_set_color_fast(text); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD]+4, y_start,sip->name); + } + y_start += 10; + + // blit the ship type + gr_set_color_fast(header); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD], y_start,XSTR("Type",740)); + y_start += 10; + if((sip->type_str != NULL) && strlen(sip->type_str)){ + gr_set_color_fast(text); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD]+4, y_start,sip->type_str); + } + y_start+=10; + + // blit the ship length + gr_set_color_fast(header); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD], y_start,XSTR("Length",741)); + y_start += 10; + if((sip->ship_length != NULL) && strlen(sip->ship_length)){ + if (Lcl_gr) { + // in german, drop the s from Meters and make sure M is caps + char *sp = strstr(sip->ship_length, "Meters"); + if (sp) { + sp[5] = ' '; // make the old s a space now + } + } + gr_set_color_fast(text); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD]+4, y_start, sip->ship_length); + } + y_start += 10; + + // blit the max velocity + gr_set_color_fast(header); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD], y_start,XSTR("Max Velocity",742)); + y_start += 10; + sprintf(str,XSTR("%d m/s",743),(int)sip->max_vel.z); + gr_set_color_fast(text); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD]+4, y_start,str); + y_start += 10; + + // blit the maneuverability + gr_set_color_fast(header); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD], y_start,XSTR("Maneuverability",744)); + y_start += 10; + if((sip->maneuverability_str != NULL) && strlen(sip->maneuverability_str)){ + gr_set_color_fast(text); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD]+4, y_start,sip->maneuverability_str); + } + y_start += 10; + + // blit the armor + gr_set_color_fast(header); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD], y_start,XSTR("Armor",745)); + y_start += 10; + if((sip->armor_str != NULL) && strlen(sip->armor_str)){ + gr_set_color_fast(text); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD]+4, y_start,sip->armor_str); + } + y_start += 10; + + // blit the gun mounts + gr_set_color_fast(header); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD], y_start,XSTR("Gun Mounts",746)); + y_start += 10; + if((sip->gun_mounts != NULL) && strlen(sip->gun_mounts)){ + gr_set_color_fast(text); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD]+4, y_start,sip->gun_mounts); + } + y_start += 10; + + // blit the missile banke + gr_set_color_fast(header); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD], y_start,XSTR("Missile Banks",747)); + y_start += 10; + if((sip->missile_banks != NULL) && strlen(sip->missile_banks)){ + gr_set_color_fast(text); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD]+4, y_start,sip->missile_banks); + } + y_start += 10; + + // blit the manufacturer + gr_set_color_fast(header); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD], y_start,XSTR("Manufacturer",748)); + y_start += 10; + if((sip->manufacturer_str != NULL) && strlen(sip->manufacturer_str)){ + gr_set_color_fast(text); + gr_string(Ship_info_coords[gr_screen.res][SHIP_SELECT_X_COORD]+4, y_start,sip->manufacturer_str); + } + y_start += 10; + + // blit the _short_ text description + /* + Assert(Multi_ts_ship_info_line_count < 3); + gr_set_color_fast(&Color_normal); + for(idx=0;idx -1 ) { + + switch(ship_select_choice) { + case SHIP_SELECT_ICON_0: + do_mouse_over_list_slot(0); + break; + case SHIP_SELECT_ICON_1: + do_mouse_over_list_slot(1); + break; + case SHIP_SELECT_ICON_2: + do_mouse_over_list_slot(2); + break; + case SHIP_SELECT_ICON_3: + do_mouse_over_list_slot(3); + break; + case WING_0_SHIP_0: + if ( do_mouse_over_wing_slot(0,0) ) + ship_select_choice = -1; + break; + case WING_0_SHIP_1: + if ( do_mouse_over_wing_slot(0,1) ) + ship_select_choice = -1; + break; + case WING_0_SHIP_2: + if ( do_mouse_over_wing_slot(0,2) ) + ship_select_choice = -1; + break; + case WING_0_SHIP_3: + if ( do_mouse_over_wing_slot(0,3) ) + ship_select_choice = -1; + break; + case WING_1_SHIP_0: + if ( do_mouse_over_wing_slot(1,0) ) + ship_select_choice = -1; + break; + case WING_1_SHIP_1: + if ( do_mouse_over_wing_slot(1,1) ) + ship_select_choice = -1; + break; + case WING_1_SHIP_2: + if ( do_mouse_over_wing_slot(1,2) ) + ship_select_choice = -1; + break; + case WING_1_SHIP_3: + if ( do_mouse_over_wing_slot(1,3) ) + ship_select_choice = -1; + break; + case WING_2_SHIP_0: + if ( do_mouse_over_wing_slot(2,0) ) + ship_select_choice = -1; + break; + case WING_2_SHIP_1: + if ( do_mouse_over_wing_slot(2,1) ) + ship_select_choice = -1; + break; + case WING_2_SHIP_2: + if ( do_mouse_over_wing_slot(2,2) ) + ship_select_choice = -1; + break; + case WING_2_SHIP_3: + if ( do_mouse_over_wing_slot(2,3) ) + ship_select_choice = -1; + break; + + default: + break; + } // end switch + } + + // check buttons + common_check_buttons(); + ship_select_check_buttons(); + + // Check for the mouse clicks over a region + if ( ship_select_choice > -1 && snazzy_action == SNAZZY_CLICKED ) { + switch (ship_select_choice) { + + case SHIP_SELECT_ICON_0: + maybe_change_selected_ship(0); + break; + + case SHIP_SELECT_ICON_1: + maybe_change_selected_ship(1); + break; + + case SHIP_SELECT_ICON_2: + maybe_change_selected_ship(2); + break; + + case SHIP_SELECT_ICON_3: + maybe_change_selected_ship(3); + break; + + case WING_0_SHIP_0: + maybe_change_selected_wing_ship(0,0); + break; + + case WING_0_SHIP_1: + maybe_change_selected_wing_ship(0,1); + break; + + case WING_0_SHIP_2: + maybe_change_selected_wing_ship(0,2); + break; + + case WING_0_SHIP_3: + maybe_change_selected_wing_ship(0,3); + break; + + case WING_1_SHIP_0: + maybe_change_selected_wing_ship(1,0); + break; + + case WING_1_SHIP_1: + maybe_change_selected_wing_ship(1,1); + break; + + case WING_1_SHIP_2: + maybe_change_selected_wing_ship(1,2); + break; + + case WING_1_SHIP_3: + maybe_change_selected_wing_ship(1,3); + break; + + case WING_2_SHIP_0: + maybe_change_selected_wing_ship(2,0); + break; + + case WING_2_SHIP_1: + maybe_change_selected_wing_ship(2,1); + break; + + case WING_2_SHIP_2: + maybe_change_selected_wing_ship(2,2); + break; + + case WING_2_SHIP_3: + maybe_change_selected_wing_ship(2,3); + break; + + default: + break; + + } // end switch + } + + ss_maybe_drop_icon(); + + if ( Ship_anim_class >= 0) { + Assert(Selected_ss_class >= 0); + if ( Ss_icons[Selected_ss_class].anim_instance->frame_num == Ss_icons[Selected_ss_class].anim_instance->stop_at ) { + nprintf(("anim", "Frame number = %d, Stop at %d\n", Ss_icons[Selected_ss_class].anim_instance->frame_num, Ss_icons[Selected_ss_class].anim_instance->stop_at)); + anim_play_struct aps; + anim_release_render_instance(Ss_icons[Selected_ss_class].anim_instance); + anim_play_init(&aps, Ss_icons[Selected_ss_class].anim, Ship_anim_coords[gr_screen.res][0], Ship_anim_coords[gr_screen.res][1]); + aps.start_at = SHIP_ANIM_LOOP_FRAME; +// aps.start_at = 0; + aps.screen_id = ON_SHIP_SELECT; + aps.framerate_independent = 1; + aps.skip_frames = 0; + Ss_icons[Selected_ss_class].anim_instance = anim_play(&aps); + } + } + + gr_reset_clip(); + + ship_select_render(frametime); + if ( !Background_playing ) { + Ship_select_ui_window.draw(); + ship_select_redraw_pressed_buttons(); + common_render_selected_screen_button(); + } + + // The background transition plays once. Display ship icons after Background done playing + if ( !Background_playing ) { + draw_ship_icons(); + for ( int i = 0; i < MAX_WING_BLOCKS; i++ ) { + draw_wing_block(i, Hot_ss_slot, -1, Selected_ss_class); + } + } + + if ( ss_icon_being_carried() ) { + int mouse_x, mouse_y; + mouse_get_pos( &mouse_x, &mouse_y ); + gr_set_bitmap(Ss_icons[Carried_ss_icon.ship_class].icon_bmaps[ICON_FRAME_SELECTED]); + gr_bitmap(mouse_x + Ss_delta_x , mouse_y + Ss_delta_y); + } + + // draw out ship information + ship_select_blit_ship_info(); + + + ss_maybe_flash_button(); + + // blit help overlay if active + help_overlay_maybe_blit(SS_OVERLAY); + + // If the commit button was pressed, do the commit button actions. Done at the end of the + // loop so there isn't a skip in the animation (since ship_create() can take a long time if + // the ship model is not in memory + if ( Commit_pressed ) { + commit_pressed(); + Commit_pressed = 0; + } + + gr_flip(); + + if ( Game_mode & GM_MULTIPLAYER ) { + if ( Selected_ss_class >= 0 ) + Net_player->p_info.ship_class = Selected_ss_class; + } + + if(!Background_playing){ + // should render this as close to last as possible so it overlaps all controls + // chatbox_render(); + } + + // If the commit button was pressed, do the commit button actions. Done at the end of the + // loop so there isn't a skip in the animation (since ship_create() can take a long time if + // the ship model is not in memory + if ( Commit_pressed ) { + commit_pressed(); + Commit_pressed = 0; + } +} + + +// ------------------------------------------------------------------------ +// ship_select_close() is called once when the ship select screen is exited +// +// +void ship_select_close() +{ + key_flush(); + + if ( !Ship_select_open ) { + nprintf(("Alan","ship_select_close() returning without doing anything\n")); + return; + } + + nprintf(("Alan", "Entering ship_select_close()\n")); + + // done with the bitmaps, so unlock it + bm_unlock(ShipSelectMaskBitmap); + + // unload the bitmaps + bm_unload(ShipSelectMaskBitmap); + help_overlay_unload(SS_OVERLAY); + + // release the bitmpas that were previously extracted from anim files + ss_unload_icons(); + + // Release any active ship anim instances + unload_ship_anim_instances(); + + // unload ship animations if they were loaded + unload_ship_anims(); + + Ship_select_ui_window.destroy(); + + Ship_anim_class = -1; + Ship_select_open = 0; // This game-wide global flag is set to 0 to indicate that the ship + // select screen has been closed and memory freed. This flag + // is needed so we can know if ship_select_close() needs to called if + // restoring a game from the Options screen invoked from ship select +} + +// ss_unload_icons() frees the bitmaps used for ship icons +void ss_unload_icons() +{ + int i,j; + ss_icon_info *icon; + + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + icon = &Ss_icons[i]; + + for ( j = 0; j < NUM_ICON_FRAMES; j++ ) { + if ( icon->icon_bmaps[j] >= 0 ) { + bm_release(icon->icon_bmaps[j]); + icon->icon_bmaps[j] = -1; + } + } + } +} + +// ------------------------------------------------------------------------ +// draw_ship_icons() will request which icons to draw on screen. +void draw_ship_icons() +{ + int i; + int count=0; + + ss_active_item *sai; + i = 0; + for ( sai = GET_FIRST(&SS_active_head); sai != END_OF_LIST(&SS_active_head); sai = GET_NEXT(sai) ) { + count++; + if ( count <= SS_active_list_start ) + continue; + + if ( i >= MAX_ICONS_ON_SCREEN ) + break; + + draw_ship_icon_with_number(i, sai->ship_class); + i++; + } +} + +// ------------------------------------------------------------------------ +// draw_ship_icon_with_number() will draw a ship icon on screen with the +// number of available ships to the left. +// +// +void draw_ship_icon_with_number(int screen_offset, int ship_class) +{ + char buf[32]; + int num_x,num_y; + ss_icon_info *ss_icon; + + + Assert( screen_offset >= 0 && screen_offset <= 3 ); + Assert( ship_class >= 0 ); + ss_icon = &Ss_icons[ship_class]; + + num_x = Ship_list_coords[gr_screen.res][screen_offset][2]; + num_y = Ship_list_coords[gr_screen.res][screen_offset][3]; + + // assume default bitmap is to be used + ss_icon->current_icon_bitmap = ss_icon->icon_bmaps[ICON_FRAME_NORMAL]; + + // next check if ship has mouse over it + if ( Hot_ss_icon > -1 ) { + Assert(Hot_ss_icon <= 3); + if ( Hot_ss_icon == screen_offset ) + ss_icon->current_icon_bitmap = ss_icon->icon_bmaps[ICON_FRAME_HOT]; + } + + // highest precedence is if the ship is selected + if ( Selected_ss_class > -1 ) { + if ( Selected_ss_class == ship_class ) + ss_icon->current_icon_bitmap = ss_icon->icon_bmaps[ICON_FRAME_SELECTED]; + } + + if ( Ss_pool[ship_class] <= 0 ) { + return; + } + + // blit the icon + gr_set_bitmap(ss_icon->current_icon_bitmap); + gr_bitmap(Ship_list_coords[gr_screen.res][screen_offset][0], Ship_list_coords[gr_screen.res][screen_offset][1]); + + // blit the number + sprintf(buf, "%d", Ss_pool[ship_class] ); + gr_set_color_fast(&Color_white); + gr_string(num_x, num_y, buf); +} + +// ------------------------------------------------------------------------ +// stop_ship_animation() will halt the currently playing ship animation. The +// instance will be freed, (but the compressed data is not freed). The animation +// will not display after this function is called (even on this frame), since +// the instance is removed from the anim_render_list. +void stop_ship_animation() +{ + ss_icon_info *ss_icon; + + if ( Ship_anim_class == -1 ) + return; + + ss_icon = &Ss_icons[Ship_anim_class]; + + anim_release_render_instance(ss_icon->anim_instance); + ss_icon->anim_instance = NULL; + + Ship_anim_class = -1; +} + + +// ------------------------------------------------------------------------ +// this loads an individual animation file +// it attempts to load a hires version (ie, it attaches a "2_" in front of the +// filename. if no hires version is available, it defaults to the lowres + +anim* ss_load_individual_animation(int ship_class) +{ + anim *p_anim; + char animation_filename[CF_MAX_FILENAME_LENGTH+4]; + + // 1024x768 SUPPORT + // If we are in 1024x768, we first want to append "2_" in front of the filename + if (gr_screen.res == GR_1024) { + Assert(strlen(Ship_info[ship_class].anim_filename) <= 30); + strcpy(animation_filename, "2_"); + strcat(animation_filename, Ship_info[ship_class].anim_filename); + // now check if file exists + // GRR must add a .ANI at the end for detection + strcat(animation_filename, ".ani"); + + p_anim = anim_load(animation_filename, 1); + if (p_anim == NULL) { + // failed loading hi-res, revert to low res + strcpy(animation_filename, Ship_info[ship_class].anim_filename); + p_anim = anim_load(animation_filename, 1); + mprintf(("Ship ANI: Can not find %s, using lowres version instead.\n", animation_filename)); + } else { + mprintf(("SHIP ANI: Found hires version of %s\n",animation_filename)); + } + /* + // this is lame and doesnt work cuz cf_exist() doesnt search the packfiles + if (!cf_exist(animation_filename, CF_TYPE_INTERFACE)) { + // file does not exist, use original low res version + strcpy(animation_filename, Ship_info[ship_class].anim_filename); + mprintf(("Ship ANI: Can not find %s, using lowres version instead.\n", animation_filename)); + } else { + animation_filename[strlen(animation_filename) - 4] = '\0'; + mprintf(("SHIP ANI: Found hires version of %s\n",animation_filename)); + } + */ + } else { + strcpy(animation_filename, Ship_info[ship_class].anim_filename); + p_anim = anim_load(animation_filename, 1); + } + + return p_anim; +} + +// ------------------------------------------------------------------------ +// start_ship_animation() will start a ship animation playing, and will +// load the compressed anim from disk if required. +void start_ship_animation(int ship_class, int play_sound) +{ + ss_icon_info *ss_icon; + Assert( ship_class >= 0 ); + + if ( Ship_anim_class == ship_class ) + return; + + if ( Ship_anim_class >= 0 ) { + stop_ship_animation(); + } + + ss_icon = &Ss_icons[ship_class]; + + // see if we need to load in the animation from disk + if ( ss_icon->anim == NULL ) { + ss_icon->anim = ss_load_individual_animation(ship_class); + } + + // see if we need to get an instance + if ( ss_icon->anim_instance == NULL ) { + anim_play_struct aps; + + anim_play_init(&aps, ss_icon->anim, Ship_anim_coords[gr_screen.res][0], Ship_anim_coords[gr_screen.res][1]); + aps.screen_id = ON_SHIP_SELECT; + aps.framerate_independent = 1; + aps.skip_frames = 0; + ss_icon->anim_instance = anim_play(&aps); + } + + Ship_anim_class = ship_class; + +// if ( play_sound ) { + gamesnd_play_iface(SND_SHIP_ICON_CHANGE); +// } +} + +// ------------------------------------------------------------------------ +// unload_ship_anims() will free all compressed anims from memory that were +// loaded for the ship animations. +// +// +void unload_ship_anims() +{ + for ( int i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Ss_icons[i].anim ) { + anim_free(Ss_icons[i].anim); + Ss_icons[i].anim = NULL; + } + } +} + +// ------------------------------------------------------------------------ +// unload_ship_anim_instances() will free any active ship animation instances. +// +// +void unload_ship_anim_instances() +{ + for ( int i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Ss_icons[i].anim_instance ) { + anim_release_render_instance(Ss_icons[i].anim_instance); + Ss_icons[i].anim_instance = NULL; + } + } +} + +// ------------------------------------------------------------------------ +// commit_pressed() is called when the commit button from any of the briefing/ship select/ weapon +// select screens is pressed. The ship selected is created, and the interface music is stopped. +void commit_pressed() +{ + int player_ship_info_index; + + if ( Wss_num_wings > 0 ) { + if(!(Game_mode & GM_MULTIPLAYER)){ + int rc; + rc = create_wings(); + if (rc != 0) { + gamesnd_play_iface(SND_GENERAL_FAIL); + return; + } + } + } + else { + + if ( Selected_ss_class == -1 ) { + player_ship_info_index = Team_data[Common_team].default_ship; + + } else { + Assert(Selected_ss_class >= 0 ); + player_ship_info_index = Selected_ss_class; + } + + update_player_ship( player_ship_info_index ); + if ( wl_update_ship_weapons(Ships[Player_obj->instance].objnum, &Wss_slots[0]) == -1 ) { + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "Player ship has no weapons", 461)); + return; + } + } + + // Check to ensure that the hotkeys are still pointing to valid objects. It is possible + // for the player to assign a ship to a hotkey, then go and delete that ship in the + // ship selection, and then try to start the mission. This function will detect those objects, + // and remove them from the hotkey linked lists. + mission_hotkey_validate(); + + gamesnd_play_iface(SND_COMMIT_PRESSED); + + // save the player loadout + if ( !(Game_mode & GM_MULTIPLAYER) ) { + strcpy(Player_loadout.filename, Game_current_mission_filename); + strcpy(Player_loadout.last_modified, The_mission.modified); + wss_save_loadout(); + } + + // move to the next stage + // in multiplayer this is the final mission sync + if(Game_mode & GM_MULTIPLAYER){ + Multi_sync_mode = MULTI_SYNC_POST_BRIEFING; + gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC); + + // otherwise tell the standalone to move everyone into this state and continue + if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + send_mission_sync_packet(MULTI_SYNC_POST_BRIEFING); + } + } + // in single player we jump directly into the mission + else { + gameseq_post_event(GS_EVENT_ENTER_GAME); + } +} + +// ------------------------------------------------------------------------ +// pick_from_ship_list() will determine if an icon from the ship selection +// list can be picked up (for drag and drop). It calculates the difference +// in x & y between the icon and the mouse, so we can move the icon with the +// mouse in a realistic way +int pick_from_ship_list(int screen_offset, int ship_class) +{ + int rval = -1; + Assert(ship_class >= 0); + + if ( Wss_num_wings == 0 ) + return rval; + + // If carrying an icon, then do nothing + if ( ss_icon_being_carried() ) + return rval; + + if ( Ss_pool[ship_class] > 0 ) { + int mouse_x, mouse_y; + + ss_set_carried_icon(-1, ship_class); + mouse_get_pos( &mouse_x, &mouse_y ); + Ss_delta_x = Ship_list_coords[gr_screen.res][screen_offset][0] - mouse_x; + Ss_delta_y = Ship_list_coords[gr_screen.res][screen_offset][1] - mouse_y; + Assert( Ss_pool[ship_class] >= 0 ); + rval = 0; + } + + common_flash_button_init(); + return rval; +} + +// ------------------------------------------------------------------------ +// pick_from_wing() will determine if an icon from the wing formation (wb_num) +// and slot number (ws_num) can be picked up (for drag and drop). It calculates +// the difference in x & y between the icon and the mouse, so we can move the icon with the +// mouse in a realistic way +void pick_from_wing(int wb_num, int ws_num) +{ + int slot_index; + Assert(wb_num >= 0 && wb_num < MAX_WING_BLOCKS); + Assert(ws_num >= 0 && ws_num < MAX_WING_SLOTS); + + ss_wing_info *wb; + ss_slot_info *ws; + wb = &Ss_wings[wb_num]; + ws = &wb->ss_slots[ws_num]; + slot_index = wb_num*4+ws_num; + + if ( wb->wingnum < 0 ) + return; + + // Take care of case where the mouse button goes from up to down in one frame while + // carrying an icon + if ( Drop_on_wing_mflag && ss_icon_being_carried() ) { + if ( !ss_disabled_slot(slot_index) ) { + ss_drop(Carried_ss_icon.from_slot, Carried_ss_icon.ship_class, slot_index, -1); + ss_reset_carried_icon(); + gamesnd_play_iface(SND_ICON_DROP_ON_WING); + } + } + + if ( ss_icon_being_carried() ) + return; + + if ( ss_disabled_slot(slot_index) ) { + return; + } + + switch ( ws->status ) { + case WING_SLOT_DISABLED: + case WING_SLOT_IGNORE: + return; + break; + + case WING_SLOT_EMPTY: + case WING_SLOT_EMPTY|WING_SLOT_IS_PLAYER: + // TODO: add fail sound + return; + break; + + case WING_SLOT_FILLED|WING_SLOT_IS_PLAYER: + case WING_SLOT_FILLED: + { + int mouse_x, mouse_y; + Assert(Wss_slots[slot_index].ship_class >= 0); + ss_set_carried_icon(slot_index, Wss_slots[slot_index].ship_class); + + mouse_get_pos( &mouse_x, &mouse_y ); + Ss_delta_x = Wing_icon_coords[gr_screen.res][slot_index][0] - mouse_x; + Ss_delta_y = Wing_icon_coords[gr_screen.res][slot_index][1] - mouse_y; + Carried_ss_icon.from_x = mouse_x; + Carried_ss_icon.from_y = mouse_y; + } + break; + + default: + Int3(); + break; + + } // end switch + + common_flash_button_init(); +} + +// ------------------------------------------------------------------------ +// draw_wing_block() will draw the wing icons for the wing formation number +// passed in as a parameter. +// +// input: wb_num => wing block number (numbering starts at 0) +// hot_slot => index of slot that mouse is over +// selected_slot => index of slot that is selected +// class_select => all ships of this class are drawn selected (send -1 to not use) +void draw_wing_block(int wb_num, int hot_slot, int selected_slot, int class_select) +{ + ss_wing_info *wb; + ss_slot_info *ws; + ss_icon_info *icon; + wing *wp; + int i, bitmap_to_draw, w, h, sx, sy, slot_index; + + Assert(wb_num >= 0 && wb_num < MAX_WING_BLOCKS); + wb = &Ss_wings[wb_num]; + + if ( wb->wingnum == -1 ) + return; + + // print the wing name under the wing + wp = &Wings[wb->wingnum]; + gr_get_string_size(&w, &h, wp->name); + sx = Wing_icon_coords[gr_screen.res][wb_num*4][0] + 16 - w/2; + sy = Wing_icon_coords[gr_screen.res][wb_num*4 + 3][1] + 32 + h; + gr_set_color_fast(&Color_normal); + gr_string(sx, sy, wp->name); + + for ( i = 0; i < MAX_WING_SLOTS; i++ ) { + bitmap_to_draw = -1; + ws = &wb->ss_slots[i]; + slot_index = wb_num*4 + i; + + if ( Wss_slots[slot_index].ship_class >= 0 ) { + icon = &Ss_icons[Wss_slots[slot_index].ship_class]; + } else { + icon = NULL; + } + + switch(ws->status & ~WING_SLOT_LOCKED ) { + case WING_SLOT_FILLED: + case WING_SLOT_FILLED|WING_SLOT_IS_PLAYER: + + Assert(icon); + + if ( class_select >= 0 ) { // only ship select + if ( Carried_ss_icon.from_slot == slot_index ) { + if ( ss_carried_icon_moved() ) { + bitmap_to_draw = Wing_slot_empty_bitmap; + } else { + bitmap_to_draw = -1; + } + break; + } + } + + if ( ws->status & WING_SLOT_LOCKED ) { + bitmap_to_draw = icon->icon_bmaps[ICON_FRAME_DISABLED]; + + // in multiplayer, determine if this it the special case where the slot is disabled, and + // it is also _my_ slot (ie, team capatains/host have not locked players yet) + if((Game_mode & GM_MULTIPLAYER) && multi_ts_disabled_high_slot(slot_index)){ + bitmap_to_draw = icon->icon_bmaps[ICON_FRAME_DISABLED_HIGH]; + } + + break; + } + + bitmap_to_draw = icon->icon_bmaps[ICON_FRAME_NORMAL]; + if ( selected_slot == slot_index || class_select == Wss_slots[slot_index].ship_class) { + bitmap_to_draw = icon->icon_bmaps[ICON_FRAME_SELECTED]; + } else if ( hot_slot == slot_index ) { + if ( mouse_down(MOUSE_LEFT_BUTTON) ){ + bitmap_to_draw = icon->icon_bmaps[ICON_FRAME_SELECTED]; + } else { + bitmap_to_draw = icon->icon_bmaps[ICON_FRAME_HOT]; + } + } + + if ( ws->status & WING_SLOT_IS_PLAYER && (selected_slot != slot_index) ) { + bitmap_to_draw = icon->icon_bmaps[ICON_FRAME_PLAYER]; + } + break; + + case WING_SLOT_EMPTY: + case WING_SLOT_EMPTY|WING_SLOT_IS_PLAYER: + bitmap_to_draw = Wing_slot_empty_bitmap; + break; + + case WING_SLOT_DISABLED: + case WING_SLOT_IGNORE: + if ( icon ) { + bitmap_to_draw = icon->icon_bmaps[ICON_FRAME_DISABLED]; + } else { + bitmap_to_draw = Wing_slot_disabled_bitmap; + } + break; + + default: + Int3(); + break; + + } // end switch + + + if ( bitmap_to_draw != -1 ) { + gr_set_bitmap(bitmap_to_draw); + gr_bitmap(Wing_icon_coords[gr_screen.res][slot_index][0], Wing_icon_coords[gr_screen.res][slot_index][1]); + } + } +} + +// called by multiplayer team select to set the slot based flags +void ss_make_slot_empty(int slot_index) +{ + int wing_num,slot_num; + ss_wing_info *wb; + ss_slot_info *ws; + + // calculate the wing # + wing_num = slot_index / 4; + slot_num = slot_index % 4; + + // get the wing and slot entries + wb = &Ss_wings[wing_num]; + ws = &wb->ss_slots[slot_num]; + + // set the flags + ws->status &= ~(WING_SLOT_FILLED | WING_SLOT_DISABLED); + ws->status |= WING_SLOT_EMPTY; +} + +// called by multiplayer team select to set the slot based flags +void ss_make_slot_full(int slot_index) +{ + int wing_num,slot_num; + ss_wing_info *wb; + ss_slot_info *ws; + + // calculate the wing # + wing_num = slot_index / 4; + slot_num = slot_index % 4; + + // get the wing and slot entries + wb = &Ss_wings[wing_num]; + ws = &wb->ss_slots[slot_num]; + + // set the flags + ws->status &= ~(WING_SLOT_EMPTY | WING_SLOT_DISABLED); + ws->status |= WING_SLOT_FILLED; +} + +void ss_blit_ship_icon(int x,int y,int ship_class,int bmap_num) +{ + // blit the bitmap in the correct location + if(ship_class == -1){ + gr_set_bitmap(Wing_slot_empty_bitmap); + } else { + ss_icon_info *icon = &Ss_icons[ship_class]; + Assert(icon->icon_bmaps[bmap_num] != -1); + gr_set_bitmap(icon->icon_bmaps[bmap_num]); + } + gr_bitmap(x,y); +} + +// ------------------------------------------------------------------------ +// unload_ship_icons() frees the memory that was used to hold the bitmaps +// for ship icons +// +void unload_wing_icons() +{ + if ( Wing_slot_empty_bitmap != -1 ) { + bm_release(Wing_slot_empty_bitmap); + Wing_slot_empty_bitmap = -1; + } + + if ( Wing_slot_disabled_bitmap != -1 ) { + bm_release(Wing_slot_disabled_bitmap); + Wing_slot_disabled_bitmap = -1; + } +} + +// ------------------------------------------------------------------------ +// create_wings() will ensure the correct ships are in the player wings +// for the game. It works by calling change_ship_type() on the wing ships +// so they match what the player selected. ship_create() is called for the +// player ship (and current_count, ship_index[] is updated), since it is not yet +// part of the wing structure. +// +// returns: 0 ==> success +// !0 ==> failure +int create_wings() +{ + ss_wing_info *wb; + ss_slot_info *ws; + wing *wp; + p_object *p_objp; + + int shipnum, objnum, slot_index; + int cleanup_ship_index[MAX_WING_SLOTS]; + int i,j,k; + int found_pobj; + + for ( i = 0; i < MAX_WING_BLOCKS; i++ ) { + + wb = &Ss_wings[i]; + + if ( wb->wingnum == -1 ) + continue; + + wp = &Wings[wb->wingnum]; + + for ( j = 0; j < MAX_WING_SLOTS; j++ ) { + slot_index = i*4+j; + ws = &wb->ss_slots[j]; + switch ( ws->status ) { + + case WING_SLOT_FILLED: + case WING_SLOT_FILLED|WING_SLOT_IS_PLAYER: + case WING_SLOT_FILLED|WING_SLOT_LOCKED: + case WING_SLOT_FILLED|WING_SLOT_IS_PLAYER|WING_SLOT_LOCKED: + if ( wp->ship_index[j] >= 0 ) { + Assert(Ships[wp->ship_index[j]].objnum >= 0); + } + + if ( ws->status & WING_SLOT_IS_PLAYER ) { + update_player_ship(Wss_slots[slot_index].ship_class); + + if ( wl_update_ship_weapons(Ships[Player_obj->instance].objnum, &Wss_slots[i*4+j]) == -1 ) { + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "Player ship has no weapons", 461)); + return -1; + } + + objnum = OBJ_INDEX(Player_obj); + shipnum = Objects[objnum].instance; + } else { + if ( wb->is_late) { + found_pobj = 0; + for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) { + if ( p_objp->wingnum == WING_INDEX(wp) ) { + if ( ws->sa_index == (p_objp-ship_arrivals) ) { + p_objp->ship_class = Wss_slots[slot_index].ship_class; + wl_update_parse_object_weapons(p_objp, &Wss_slots[i*4+j]); + found_pobj = 1; + break; + } + } + } + Assert(found_pobj); + } + else { + // AL 10/04/97 + // Change the ship type of the ship if different than current. + // NOTE: This will reset the weapons for this ship. I think this is + // the right thing to do, since the ships may have different numbers + // of weapons and may not have the same allowed weapon types + if ( Ships[wp->ship_index[j]].ship_info_index != Wss_slots[slot_index].ship_class ) + change_ship_type(wp->ship_index[j], Wss_slots[slot_index].ship_class); + wl_update_ship_weapons(Ships[wp->ship_index[j]].objnum, &Wss_slots[i*4+j]); + } + } + + break; + + case WING_SLOT_EMPTY: + case WING_SLOT_EMPTY|WING_SLOT_IS_PLAYER: + if ( ws->status & WING_SLOT_IS_PLAYER ) { + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "Player %s must select a place in player wing", 462), Player->callsign); + return -1; + } + break; + + default: + break; + } + } // end for (wing slot) + } // end for (wing block) + + for ( i = 0; i < MAX_WING_BLOCKS; i++ ) { + wb = &Ss_wings[i]; + wp = &Wings[wb->wingnum]; + + if ( wb->wingnum == -1 ) + continue; + + for ( k = 0; k < MAX_WING_SLOTS; k++ ) { + cleanup_ship_index[k] = -1; + } + + for ( j = 0; j < MAX_WING_SLOTS; j++ ) { + ws = &wb->ss_slots[j]; + switch( ws->status ) { + case WING_SLOT_EMPTY: + // delete ship that is not going to be used by the wing + if ( wb->is_late ) { + list_remove( &ship_arrival_list, &ship_arrivals[ws->sa_index]); + wp->wave_count--; + Assert(wp->wave_count >= 0); + } + else { + shipnum = wp->ship_index[j]; + Assert( shipnum >= 0 && shipnum < MAX_SHIPS ); + cleanup_ship_index[j] = shipnum; + ship_add_exited_ship( &Ships[shipnum], SEF_PLAYER_DELETED ); + obj_delete(Ships[shipnum].objnum); + hud_set_wingman_status_none( Ships[shipnum].wing_status_wing_index, Ships[shipnum].wing_status_wing_pos); + } + break; + + default: + break; + + } // end switch + + } // end for (wing slot) + + for ( k = 0; k < MAX_WING_SLOTS; k++ ) { + if ( cleanup_ship_index[k] != -1 ) { + ship_wing_cleanup( cleanup_ship_index[k], wp ); + } + } + + } // end for (wing block) + + return 0; +} + +void ship_stop_animation() +{ + if ( Ship_anim_class >= 0 ) + stop_ship_animation(); +} + +// ---------------------------------------------------------------------------- +// update_player_ship() +// +// Updates the ship class of the player ship +// +// parameters: si_index => ship info index of ship class to change to +// +// +void update_player_ship(int si_index) +{ + Assert( si_index >= 0 ); + Assert( Player_obj != NULL); + + // AL 10/04/97 + // Change the ship type of the player ship if different than current. + // NOTE: This will reset the weapons for this ship. I think this is + // the right thing to do, since the ships may have different numbers + // of weapons and may not have the same allowed weapon types + if ( Player_ship->ship_info_index != si_index ) + change_ship_type(Player_obj->instance, si_index); + + Player->last_ship_flown_si_index = si_index; +} + +// ---------------------------------------------------------------------------- +// create a default player ship +// +// parameters: use_last_flown => select ship that was last flown on a mission +// (this is a default parameter which is set to 1) +// +// returns: 0 => success +// !0 => failure +// +int create_default_player_ship(int use_last_flown) +{ + int player_ship_class=-1, i; + + // find the ship that matches the string stored in default_player_ship + + if ( use_last_flown ) { + player_ship_class = Players[Player_num].last_ship_flown_si_index; + } + else { + for (i = 0; i < Num_ship_types; i++) { + if ( !stricmp(Ship_info[i].name, default_player_ship) ) { + player_ship_class = i; + Players[Player_num].last_ship_flown_si_index = player_ship_class; + break; + } + } + + if (i == Num_ship_types) + return 1; + } + + update_player_ship(player_ship_class); + + // debug code to keep using descent style physics if the player starts a new game +#ifndef NDEBUG + if ( use_descent ) { + use_descent = 0; + toggle_player_object(); + } +#endif + + return 0; +} + +// return the original ship class for the specified slot +int ss_return_original_ship_class(int slot_num) +{ + int wnum, snum; + + wnum = slot_num/4; + snum = slot_num%4; + + return Ss_wings[wnum].ss_slots[snum].original_ship_class; +} + +// return the ship arrival index for the slot (-1 means no ship arrival index) +int ss_return_saindex(int slot_num) +{ + int wnum, snum; + + wnum = slot_num/4; + snum = slot_num%4; + + return Ss_wings[wnum].ss_slots[snum].sa_index; +} + +// ---------------------------------------------------------------------------- +// ss_return_ship() +// +// For a given wing slot, return the ship index if the ship has been created. +// Otherwise, find the index into ship_arrivals[] for the ship +// +// input: wing_block => wing block of ship to find +// wing_slot => wing slot of ship to find +// ship_index => OUTPUT parameter: the Ships[] index of the ship in the wing slot +// This value will be -1 if there is no ship created yet +// ppobjp => OUTPUT parameter: returns a pointer to a parse object for +// the ship that hasn't been created yet. Set to NULL if the +// ship has already been created +// +// returns: the original ship class of the ship, or -1 if the ship doesn't exist +// +// NOTE: For the player wing, the player is not yet in the wp->ship_index[].. so +// that is why there is an offset of 1 when getting ship indicies from the player +// wing. The player is special cased by looking at the status of the wing slot +// +int ss_return_ship(int wing_block, int wing_slot, int *ship_index, p_object **ppobjp) +{ + *ship_index = -1; + *ppobjp = NULL; + + ss_slot_info *ws; + + if (!Wss_num_wings) { + *ppobjp = NULL; + *ship_index = Player_obj->instance; + return Player_ship->ship_info_index; + } + + if ( Ss_wings[wing_block].wingnum < 0 ) { + return -1; + } + + ws = &Ss_wings[wing_block].ss_slots[wing_slot]; + + // Check to see if ship is on the ship_arrivals[] list + if ( ws->sa_index != -1 ) { + *ship_index = -1; + *ppobjp = &ship_arrivals[ws->sa_index]; + } else { + *ship_index = Wings[Ss_wings[wing_block].wingnum].ship_index[wing_slot]; + Assert(*ship_index != -1); + } + + return ws->original_ship_class; +} + +// return the name of the ship in the specified wing position... if the ship is the +// player ship, return the player callsign +// +// input: ensure at least NAME_LENGTH bytes allocated for name buffer +void ss_return_name(int wing_block, int wing_slot, char *name) +{ + ss_slot_info *ws; + wing *wp; + + ws = &Ss_wings[wing_block].ss_slots[wing_slot]; + wp = &Wings[Ss_wings[wing_block].wingnum]; + + if (!Wss_num_wings) { + strcpy(name, Player->callsign); + return; + } + + // Check to see if ship is on the ship_arrivals[] list + if ( ws->sa_index != -1 ) { + strcpy(name, ship_arrivals[ws->sa_index].name); + } else { + ship *sp; + sp = &Ships[wp->ship_index[wing_slot]]; + + // in multiplayer, return the callsigns of the players who are in the ships + if(Game_mode & GM_MULTIPLAYER){ + int player_index = multi_find_player_by_object(&Objects[sp->objnum]); + if(player_index != -1){ + strcpy(name,Net_players[player_index].player->callsign); + } else { + strcpy(name,sp->ship_name); + } + } else { + strcpy(name, sp->ship_name); + } + } +} + +int ss_get_selected_ship() +{ + return Selected_ss_class; +} + +// Set selected ship to the first occupied wing slot, or first ship in pool if no slots are occupied +void ss_reset_selected_ship() +{ + int i; + + Selected_ss_class = -1; + + if ( Wss_num_wings <= 0 ) { + Selected_ss_class = Team_data[Common_team].default_ship; + return; + } + + // get first ship class found on slots + for ( i = 0; i < MAX_WSS_SLOTS; i++ ) { + if ( Wss_slots[i].ship_class >= 0 ) { + Selected_ss_class = Wss_slots[i].ship_class; + break; + } + } + + if ( Selected_ss_class == -1 ) { + Int3(); + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Ss_pool[i] > 0 ) { + Selected_ss_class = i; + } + } + } + + if ( Selected_ss_class == -1 ) { + Int3(); + return; + } +} + +// There may be ships that are in wings but not in Team_data[0]. Since we still want to show those +// icons in the ship selection list, the code below checks for these cases. If a ship is found in +// a wing, and is not in Team_data[0], it is appended to the end of the ship_count[] and ship_list[] arrays +// that are in Team_data[0] +// +// exit: number of distinct ship classes available to choose from +int ss_fixup_team_data(team_data *tdata) +{ + int i, j, k, ship_in_parse_player, list_size; + p_object *p_objp; + team_data *p_team_data; + + p_team_data = tdata; + ship_in_parse_player = 0; + list_size = p_team_data->number_choices; + + for ( i = 0; i < MAX_PLAYER_WINGS; i++ ) { + wing *wp; + if ( Starting_wings[i] == -1 ) + continue; + wp = &Wings[Starting_wings[i]]; + for ( j = 0; j < wp->current_count; j++ ) { + ship_in_parse_player = 0; + + for ( k = 0; k < p_team_data->number_choices; k++ ) { + Assert( p_team_data->ship_count[k] >= 0 ); + if ( p_team_data->ship_list[k] == Ships[wp->ship_index[j]].ship_info_index ) { + ship_in_parse_player = 1; + break; + } + } // end for, go to next item in parse player + + if ( !ship_in_parse_player ) { + p_team_data->ship_count[list_size] = 0; + p_team_data->ship_list[list_size] = Ships[wp->ship_index[j]].ship_info_index; + p_team_data->number_choices++; + list_size++; + } + } // end for, go get next ship in wing + + if ( wp->current_count == 0 ) { + + for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) { + if ( p_objp->wingnum == WING_INDEX(wp) ) { + ship_in_parse_player = 0; + + for ( k = 0; k < p_team_data->number_choices; k++ ) { + Assert( p_team_data->ship_count[k] >= 0 ); + if ( p_team_data->ship_list[k] == p_objp->ship_class ) { + ship_in_parse_player = 1; + break; + } + } // end for, go to next item in parse player + + if ( !ship_in_parse_player ) { + p_team_data->ship_count[list_size] = 0; + p_team_data->ship_list[list_size] = p_objp->ship_class; + p_team_data->number_choices++; + list_size++; + } + } + } + } + } // end for, go to next wing + + if ( list_size == 0 ) { + // ensure that the default player ship is in the ship_list too + ship_in_parse_player = 0; + for ( k = 0; k < p_team_data->number_choices; k++ ) { + Assert( p_team_data->ship_count[k] >= 0 ); + if ( p_team_data->ship_list[k] == p_team_data->default_ship ) { + ship_in_parse_player = 1; + break; + } + } + if ( !ship_in_parse_player ) { + p_team_data->ship_count[list_size] = 0; + p_team_data->ship_list[list_size] = p_team_data->default_ship; + p_team_data->number_choices++; + list_size++; + } + } + + return list_size; +} + +// set numbers of ships in pool to default values +void ss_init_pool(team_data *pteam) +{ + int i; + + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + Ss_pool[i] = -1; + } + + // set number of available ships based on counts in team_data + for ( i = 0; i < pteam->number_choices; i++ ) { + Ss_pool[pteam->ship_list[i]] = pteam->ship_count[i]; + } +} + +// load the icons for a specific ship class +void ss_load_icons(int ship_class) +{ + ss_icon_info *icon; + int first_frame, num_frames, i; + + icon = &Ss_icons[ship_class]; + + first_frame = bm_load_animation(Ship_info[ship_class].icon_filename, &num_frames); + if ( first_frame == -1 ) { + Int3(); // Could not load in icon frames.. get Alan + return; + } + + for ( i = 0; i < num_frames; i++ ) { + icon->icon_bmaps[i] = first_frame+i; + } + + // set the current bitmap for the ship icon + icon->current_icon_bitmap = icon->icon_bmaps[ICON_FRAME_NORMAL]; +} + +// load all the icons for ships in the pool +void ss_load_all_icons() +{ + #ifndef DEMO // not for FS2_DEMO + + int i, j; + + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + // clear out data + Ss_icons[i].current_icon_bitmap = -1; + Ss_icons[i].anim = NULL; + Ss_icons[i].anim_instance = NULL; + for ( j = 0; j < NUM_ICON_FRAMES; j++ ) { + Ss_icons[i].icon_bmaps[j] = -1; + } + + if ( Ss_pool[i] >= 0 ) { + ss_load_icons(i); + } + } + + #endif +} + +// Load in a specific ship animation. The data is loaded as a memory-mapped file since these animations +// are awfully big. +void ss_load_anim(int ship_class) +{ + ss_icon_info *icon; + + icon = &Ss_icons[ship_class]; + + // load the compressed ship animation into memory + // NOTE: if last parm of load_anim is 1, the anim file is mapped to memory + Assert( icon->anim == NULL ); + icon->anim = ss_load_individual_animation(ship_class); + if ( icon->anim == NULL ) { + Int3(); // couldn't load anim filename.. get Alan + } +} + +// Load in any ship animations. This function assumes that Ss_pool has been inited. +void ss_load_all_anims() +{ + #ifndef DEMO // not for FS2_DEMO + + int i; + + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Ss_pool[i] > 0 ) { + ss_load_anim(i); + } + } + + #endif +} + +// determine if the slot is disabled +int ss_disabled_slot(int slot_num) +{ + if ( Wss_num_wings <= 0 ){ + return 0; + } + + // HACK HACK HACK - call the team select function in multiplayer + if(Game_mode & GM_MULTIPLAYER) { + return multi_ts_disabled_slot(slot_num); + } + return ( Ss_wings[slot_num/4].ss_slots[slot_num%4].status & WING_SLOT_IGNORE ); +} + +// reset the slot data +void ss_clear_slots() +{ + int i,j; + ss_slot_info *slot; + + for ( i = 0; i < MAX_WSS_SLOTS; i++ ) { + Wss_slots[i].ship_class = -1; + } + + for ( i = 0; i < 3; i++ ) { + for ( j = 0; j < 4; j++ ) { + slot = &Ss_wings[i].ss_slots[j]; + slot->status = WING_SLOT_DISABLED; + slot->sa_index = -1; + slot->original_ship_class = -1; + } + } +} + +// initialize all wing struct stuff +void ss_clear_wings() +{ + int idx; + + for(idx=0;idxwingnum = Starting_wings[starting_wing_num]; + Wss_num_wings++; + + wp = &Wings[Ss_wings[wing_num].wingnum]; + ss_wing->num_slots = wp->current_count; + + if ( wp->current_count == 0 || wp->ship_index[0] == -1 ) { + p_object *p_objp; + // Temporarily fill in the current count and initialize the ship list in the wing + // This gets cleaned up before the mission is started + for ( p_objp = GET_FIRST(&ship_arrival_list); p_objp != END_OF_LIST(&ship_arrival_list); p_objp = GET_NEXT(p_objp) ) { + if ( p_objp->wingnum == WING_INDEX(wp) ) { + slot = &ss_wing->ss_slots[ss_wing->num_slots++]; + slot->sa_index = p_objp-ship_arrivals; + slot->original_ship_class = p_objp->ship_class; + } + ss_wing->is_late = 1; + } + } +} + +// Determine if a ship is actually a console player ship +int ss_wing_slot_is_console_player(int index) +{ + int wingnum, slotnum; + + wingnum=index/4; + slotnum=index%4; + + if ( wingnum >= Wss_num_wings ) { + return 0; + } + + if ( Ss_wings[wingnum].ss_slots[slotnum].status & WING_SLOT_IS_PLAYER ) { + return 1; + } + + return 0; +} + +// init the ship selection portion of the units, and set up the ui data +void ss_init_units() +{ + int i,j; + wing *wp; + ss_slot_info *ss_slot; + ss_wing_info *ss_wing; + + for ( i = 0; i < Wss_num_wings; i++ ) { + + ss_wing = &Ss_wings[i]; + + if ( ss_wing->wingnum < 0 ) { + Int3(); + continue; + } + + wp = &Wings[ss_wing->wingnum]; + + for ( j = 0; j < ss_wing->num_slots; j++ ) { + + ss_slot = &ss_wing->ss_slots[j]; + + if ( ss_slot->sa_index == -1 ) { + ss_slot->original_ship_class = Ships[wp->ship_index[j]].ship_info_index; + } + + // Set the type of slot. Check if the slot is marked as locked, if so then the player is not + // going to be able to modify that ship. + if ( ss_slot->sa_index == -1 ) { + int objnum; + if ( Ships[wp->ship_index[j]].flags & SF_LOCKED ) { + ss_slot->status = WING_SLOT_DISABLED; + ss_slot->status |= WING_SLOT_LOCKED; + } else { + ss_slot->status = WING_SLOT_FILLED; + } + + objnum = Ships[wp->ship_index[j]].objnum; + if ( Objects[objnum].flags & OF_PLAYER_SHIP ) { + if ( ss_slot->status & WING_SLOT_LOCKED ) { + // Int3(); // Get Alan + + // just unflag it + ss_slot->status &= ~(WING_SLOT_LOCKED); + } + ss_slot->status = WING_SLOT_FILLED; + if ( objnum == OBJ_INDEX(Player_obj) ) { + ss_slot->status |= WING_SLOT_IS_PLAYER; + } + } + } else { + if ( ship_arrivals[ss_slot->sa_index].flags & P_SF_LOCKED ) { + ss_slot->status = WING_SLOT_DISABLED; + ss_slot->status |= WING_SLOT_LOCKED; + } else { + ss_slot->status = WING_SLOT_FILLED; + } + if ( ship_arrivals[ss_slot->sa_index].flags & P_OF_PLAYER_START ) { + if ( ss_slot->status & WING_SLOT_LOCKED ) { + // Int3(); // Get Alan + + // just unflag it + ss_slot->status &= ~(WING_SLOT_LOCKED); + } + ss_slot->status = WING_SLOT_FILLED; + ss_slot->status |= WING_SLOT_IS_PLAYER; + } + } + + // Assign the ship class to the unit + Wss_slots[i*4+j].ship_class = ss_slot->original_ship_class; + + } // end for + } // end for + + // lock/unlock any necessary slots for multiplayer + if(Game_mode & GM_MULTIPLAYER){ + ss_recalc_multiplayer_slots(); + } +} + +// set the necessary pointers +void ss_set_team_pointers(int team) +{ + Ss_wings = Ss_wings_teams[team]; + Ss_icons = Ss_icons_teams[team]; + Ss_pool = Ss_pool_teams[team]; + Wl_pool = Wl_pool_teams[team]; + Wss_slots = Wss_slots_teams[team]; +} + +// initialize team specific stuff +void ship_select_init_team_data(int team_num) +{ + int idx; + + // set up the pointers to initialize the data structures. + Ss_wings = Ss_wings_teams[team_num]; + Ss_icons = Ss_icons_teams[team_num]; + Ss_pool = Ss_pool_teams[team_num]; + Wl_pool = Wl_pool_teams[team_num]; + Wss_slots = Wss_slots_teams[team_num]; + + ss_fixup_team_data(&Team_data[team_num]); + ss_init_pool(&Team_data[team_num]); + + ss_clear_slots(); // reset data for slots + ss_clear_wings(); + + // determine how many wings we should be checking for + Wss_num_wings = 0; + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){ + // now setup wings for easy reference + ss_init_wing_info(0,team_num); + } else { + // now setup wings for easy reference + for(idx=0;idxstatus & WING_SLOT_FILLED ) { + slot->status &= ~WING_SLOT_FILLED; + slot->status |= WING_SLOT_EMPTY; + } + } else { + if ( slot->status & WING_SLOT_EMPTY ) { + slot->status &= ~WING_SLOT_EMPTY; + slot->status |= WING_SLOT_FILLED; + } + } + } +} + +// exit: data changed flag +int ss_swap_slot_slot(int from_slot, int to_slot, int *sound) +{ + int i, tmp, fwnum, fsnum, twnum, tsnum; + + if ( from_slot == to_slot ) { + *sound=SND_ICON_DROP_ON_WING; + return 0; + } + + // ensure from_slot has a ship to pick up + if ( Wss_slots[from_slot].ship_class < 0 ) { + *sound=SND_ICON_DROP; + return 0; + } + + fwnum = from_slot/4; + fsnum = from_slot%4; + + twnum = to_slot/4; + tsnum = to_slot%4; + + // swap ship class + tmp = Wss_slots[from_slot].ship_class; + Wss_slots[from_slot].ship_class = Wss_slots[to_slot].ship_class; + Wss_slots[to_slot].ship_class = tmp; + + // swap weapons + for ( i = 0; i < MAX_WL_WEAPONS; i++ ) { + tmp = Wss_slots[from_slot].wep[i]; + Wss_slots[from_slot].wep[i] = Wss_slots[to_slot].wep[i]; + Wss_slots[to_slot].wep[i] = tmp; + + tmp = Wss_slots[from_slot].wep_count[i]; + Wss_slots[from_slot].wep_count[i] = Wss_slots[to_slot].wep_count[i]; + Wss_slots[to_slot].wep_count[i] = tmp; + } + + *sound=SND_ICON_DROP_ON_WING; + return 1; +} + +// exit: data changed flag +int ss_dump_to_list(int from_slot, int to_list, int *sound) +{ + int i, fwnum, fsnum; + wss_unit *slot; + + slot = &Wss_slots[from_slot]; + + // ensure from_slot has a ship to pick up + if ( slot->ship_class < 0 ) { + *sound=SND_ICON_DROP; + return 0; + } + + fwnum = from_slot/4; + fsnum = from_slot%4; + + // put ship back in list + Ss_pool[to_list]++; // return to list + slot->ship_class = -1; // remove from slot + + // put weapons back in list + for ( i = 0; i < MAX_WL_WEAPONS; i++ ) { + if ( (slot->wep[i] >= 0) && (slot->wep_count[i] > 0) ) { + Wl_pool[slot->wep[i]] += slot->wep_count[i]; + slot->wep[i] = -1; + slot->wep_count[i] = 0; + } + } + + *sound=SND_ICON_DROP; + return 1; +} + +// exit: data changed flag +int ss_grab_from_list(int from_list, int to_slot, int *sound) +{ + wss_unit *slot; + int i, wep[MAX_WL_WEAPONS], wep_count[MAX_WL_WEAPONS]; + + slot = &Wss_slots[to_slot]; + + // ensure that pool has ship + if ( Ss_pool[from_list] <= 0 ) { + *sound=SND_ICON_DROP; + return 0; + } + + Assert(slot->ship_class < 0 ); // slot should be empty + + // take ship from list->slot + Ss_pool[from_list]--; + slot->ship_class = from_list; + + // take weapons from list->slot + wl_get_default_weapons(from_list, to_slot, wep, wep_count); + wl_remove_weps_from_pool(wep, wep_count, slot->ship_class); + for ( i = 0; i < MAX_WL_WEAPONS; i++ ) { + slot->wep[i] = wep[i]; + slot->wep_count[i] = wep_count[i]; + } + + *sound=SND_ICON_DROP_ON_WING; + return 1; +} + +// exit: data changed flag +int ss_swap_list_slot(int from_list, int to_slot, int *sound) +{ + int i, wep[MAX_WL_WEAPONS], wep_count[MAX_WL_WEAPONS]; + wss_unit *slot; + + // ensure that pool has ship + if ( Ss_pool[from_list] <= 0 ) { + *sound=SND_ICON_DROP; + return 0; + } + + slot = &Wss_slots[to_slot]; + Assert(slot->ship_class >= 0 ); // slot should be filled + + // put ship from slot->list + Ss_pool[Wss_slots[to_slot].ship_class]++; + + // put weapons from slot->list + for ( i = 0; i < MAX_WL_WEAPONS; i++ ) { + if ( (slot->wep[i] >= 0) && (slot->wep_count[i] > 0) ) { + Wl_pool[slot->wep[i]] += slot->wep_count[i]; + slot->wep[i] = -1; + slot->wep_count[i] = 0; + } + } + + // take ship from list->slot + Ss_pool[from_list]--; + slot->ship_class = from_list; + + // take weapons from list->slot + wl_get_default_weapons(from_list, to_slot, wep, wep_count); + wl_remove_weps_from_pool(wep, wep_count, slot->ship_class); + for ( i = 0; i < MAX_WL_WEAPONS; i++ ) { + slot->wep[i] = wep[i]; + slot->wep_count[i] = wep_count[i]; + } + + *sound=SND_ICON_DROP_ON_WING; + return 1; +} + +void ss_apply(int mode, int from_slot, int from_list, int to_slot, int to_list,int player_index) +{ + int update=0; + int sound=-1; + + switch(mode){ + case WSS_SWAP_SLOT_SLOT: + update = ss_swap_slot_slot(from_slot, to_slot, &sound); + break; + case WSS_DUMP_TO_LIST: + update = ss_dump_to_list(from_slot, to_list, &sound); + break; + case WSS_GRAB_FROM_LIST: + update = ss_grab_from_list(from_list, to_slot, &sound); + break; + case WSS_SWAP_LIST_SLOT: + update = ss_swap_list_slot(from_list, to_slot, &sound); + break; + } + + // only play this sound if the move was done locally (by the host in other words) + if ( (sound >= 0) && (player_index == -1) ) { + gamesnd_play_iface(sound); + } + + if ( update ) { + // NO LONGER USED - THERE IS A MULTIPLAYER VERSION OF THIS SCREEN NOW + /* + if ( MULTIPLAYER_HOST ) { + int size; + ubyte wss_data[MAX_PACKET_SIZE-20]; + size = store_wss_data(wss_data, MAX_PACKET_SIZE-20, sound); + send_wss_update_packet(wss_data, size, player_index); + } + */ + + ss_synch_interface(); + } +} + +void ss_drop(int from_slot,int from_list,int to_slot,int to_list,int player_index) +{ + int mode; + common_flash_button_init(); + + mode = wss_get_mode(from_slot, from_list, to_slot, to_list, -1); + if ( mode >= 0 ) { + ss_apply(mode, from_slot, from_list, to_slot, to_list,player_index); + } +} + +// lock/unlock any necessary slots for multiplayer +void ss_recalc_multiplayer_slots() +{ + int i,j,objnum; + wing *wp; + ss_slot_info *ss_slot; + ss_wing_info *ss_wing; + + // no wings + if ( Wss_num_wings <= 0 ) { + Wss_slots[0].ship_class = Team_data[Common_team].default_ship;; + return; + } + + for ( i = 0; i < Wss_num_wings; i++ ) { + ss_wing = &Ss_wings[i]; + if ( ss_wing->wingnum < 0 ) { + Int3(); + continue; + } + + // NOTE : the method below will eventually have to change to account for all possible netgame options + + // get the wing pointer + wp = &Wings[ss_wing->wingnum]; + for ( j = 0; j < ss_wing->num_slots; j++ ) { + // get the objnum of the ship in this slot + objnum = Ships[wp->ship_index[j]].objnum; + + // get the slot pointer + ss_slot = &ss_wing->ss_slots[j]; + + if (ss_slot->sa_index == -1) { + // lock all slots by default + ss_slot->status |= WING_SLOT_LOCKED; + + // if this is my slot, then unlock it + if(!multi_ts_disabled_slot((i*4)+j)){ + ss_slot->status &= ~WING_SLOT_LOCKED; + } + } + } + } +} + diff --git a/src/missionui/missionstats.cpp b/src/missionui/missionstats.cpp new file mode 100644 index 0000000..a4cc9c1 --- /dev/null +++ b/src/missionui/missionstats.cpp @@ -0,0 +1,22 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionStats.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for the post-mission stats interface + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 1 9/30/97 9:25a Lawrance + * + * $NoKeywords: $ + */ \ No newline at end of file diff --git a/src/missionui/missionweaponchoice.cpp b/src/missionui/missionweaponchoice.cpp new file mode 100644 index 0000000..db30dd2 --- /dev/null +++ b/src/missionui/missionweaponchoice.cpp @@ -0,0 +1,3826 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/MissionWeaponChoice.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * C module for the weapon loadout screen + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 40 11/01/99 11:22a Jefff + * some weapon name translations + * + * 39 9/10/99 10:26a Jefff + * default weapon anim selection fix + * + * 38 9/06/99 3:30p Mikek + * Added system for restricting weapon choices based on ship class for + * dogfight missions. + * + * 37 8/27/99 11:59a Jefff + * fixed unnecessary sound playing + * + * 36 8/05/99 3:40p Jefff + * hi-res text adjustments + * + * 35 8/05/99 11:32a Jefff + * fixed hi-res weapon anims not loading from packfile + * + * 34 7/30/99 4:35p Andsager + * Clean up change ship sound + * + * 33 7/30/99 4:22p Andsager + * restored ship and weapon anim sounds for demo. Added sound for + * changing ship in weapon loadout screen. + * + * 32 7/27/99 7:34p Jefff + * Lotsa clean up, moving stuff around, generally making the whole damn + * thing work right. + * + * 31 7/27/99 3:01p Jefff + * crippled wl_start_slot_animation to prevent loading of overhead ship + * .anis. also began modifications for multiplayer interface. + * + * 30 7/25/99 5:49p Jefff + * initial weapon ani is now one of the ship's weapons. this will fall + * back to the original search if the ship should be weaponless. + * + * 29 7/24/99 6:03p Jefff + * Added "lock" text to multiplayer lock button + * + * 28 7/21/99 6:02p Jefff + * fixed weapon counts running into weapon icons + * + * 27 7/21/99 3:24p Andsager + * Modify demo to use 2 frame ship and weapon select anis and cut sounds + * associated with ani + * + * 26 7/20/99 7:07p Jefff + * added "reset" text above reset button + * + * 25 7/20/99 1:48p Jefff + * Long weapon titles now wrap to next line + * + * 24 7/20/99 10:44a Jefff + * increased WEAPON_DESC_MAX_LINES to 6 + * + * 23 7/20/99 12:26a Jefff + * text wipe sound FX + * + * 22 7/19/99 5:08p Jefff + * Added text descriptions for selected weapons, complete with wipe + * effect. + * + * 21 7/16/99 1:50p Dave + * 8 bit aabitmaps. yay. + * + * 20 7/15/99 9:20a Andsager + * FS2_DEMO initial checkin + * + * 19 7/11/99 3:20p Kellys + * Make sure we load high-res weapon rotations in 1024 + * + * 18 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 17 7/09/99 12:21a Dave + * Change weapon loop anim frame. + * + * 16 3/25/99 2:45p Neilk + * Fixed lock button + * + * 15 3/23/99 9:23p Neilk + * fixed various multiplayer lock button problems + * + * 14 3/10/99 6:21p Neilk + * Added new artwork for hires + * + * 13 2/21/99 6:01p Dave + * Fixed standalone WSS packets. + * + * 12 2/18/99 11:46a Neilk + * hires interface coord support + * + * 11 2/11/99 3:08p Dave + * PXO refresh button. Very preliminary squad war support. + * + * 10 2/01/99 5:55p Dave + * Removed the idea of explicit bitmaps for buttons. Fixed text + * highlighting for disabled gadgets. + * + * 9 1/30/99 1:29a Dave + * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot + * screen. Fixed beam weapon death messages. + * + * 8 1/29/99 4:17p Dave + * New interface screens. + * + * 7 1/27/99 9:56a Dave + * Temporary checkin of beam weapons for Dan to make cool sounds. + * + * 6 12/18/98 1:13a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 5 11/30/98 1:07p Dave + * 16 bit conversion, first run. + * + * 4 11/17/98 11:12a Dave + * Removed player identification by address. Now assign explicit id #'s. + * + * 3 10/13/98 9:29a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 119 6/17/98 11:06a Lawrance + * put in weapon description tooltip + * + * 118 6/13/98 6:01p Hoffoss + * Externalized all new (or forgot to be added) strings to all the code. + * + * 117 6/01/98 11:43a John + * JAS & MK: Classified all strings for localization. + * + * 116 5/19/98 8:35p Dave + * Revamp PXO channel listing system. Send campaign goals/events to + * clients for evaluation. Made lock button pressable on all screens. + * + * 115 5/18/98 2:49p Lawrance + * Put in weapon animation sound hook + * + * 114 5/08/98 7:52p Lawrance + * Add code to loop new weapon animations + * + * 113 5/06/98 11:50p Lawrance + * Clean up help overlay code for loadout screens + * + * 112 5/04/98 5:27p Johnson + * Fixed a team vs. team weapons loadout bug. + * + * 111 5/03/98 1:55a Lawrance + * Fix some sound problems with loadout screens + * + * 110 4/30/98 6:03p Lawrance + * Make drag and drop work better. + * + * 109 4/27/98 6:02p Dave + * Modify how missile scoring works. Fixed a team select ui bug. Speed up + * multi_lag system. Put in new main hall. + * + * 108 4/24/98 2:17a Lawrance + * repositioin ship name in weapons loadout + * + * 107 4/22/98 7:24p Dave + * Made sure the "player/ships" locked button for multiplayer appears on + * all briefing screens. + * + * 106 4/21/98 6:45p Dave + * AL: Fix bug with replacement weapons on ships + * + * 105 4/17/98 5:27p Dave + * More work on the multi options screen. Fixed many minor ui todo bugs. + * + * 104 4/16/98 8:05p Lawrance + * fix some bugs with numbers over-writing weapon icons + * + * 103 4/10/98 4:51p Hoffoss + * Made several changes related to tooltips. + * + * 102 4/08/98 1:18a Lawrance + * Fix bug that could occur when picking alternate weapons (ie count could + * have been wrong due to missile size) + * + * 101 4/07/98 5:42p Dave + * Put in support for ui display of voice system status (recording, + * playing back, etc). Make sure main hall music is stopped before + * entering a multiplayer game via ingame join. + * + * 100 4/02/98 11:40a Lawrance + * check for #ifdef DEMO instead of #ifdef DEMO_RELEASE + * + * 99 3/31/98 1:50p Duncan + * ALAN: fix bugs with selecting alternate weapons + * + * 98 3/30/98 12:18a Lawrance + * change some DEMO_RELEASE code to not compile code rather than return + * early + * + * 97 3/29/98 1:24p Dave + * Make chatbox not clear between multiplayer screens. Select player ship + * as default in mp team select and weapons select screens. Made create + * game mission list use 2 fixed size columns. + * + * 96 3/29/98 12:55a Lawrance + * Get demo build working with limited set of data. + * + * 95 3/26/98 5:47p Lawrance + * Implement default weapon picking if default weapons not available in + * pool + * + * 94 3/25/98 8:43p Hoffoss + * Changed anim_play() to not be so damn complex when you try and call it. + * + * 93 3/25/98 11:23a Mike + * Fix stack overwrite due to async between MAX_SECONDARY_WEAPONS and + * MAX_WL_SECONDARY. + * + * 92 3/21/98 2:47p Dave + * Fixed chat packet routing problem on standalone. Fixed wss_request + * packet routing on standalone. + * + * 91 3/19/98 5:34p Lawrance + * Tweak drag/drop behavior in weapons loadout + * + * 90 3/14/98 2:48p Dave + * Cleaned up observer joining code. Put in support for file xfers to + * ingame joiners (observers or not). Revamped and reinstalled pseudo + * lag/loss system. + * + * 89 3/12/98 4:03p Lawrance + * don't press buttons when icon dropped on them + * + * 88 3/10/98 1:35p Lawrance + * Fix default selected weapon bug + * + * 87 3/09/98 9:55p Lawrance + * Move secondary icons to the right to avoid overlap with numbers + * + * 86 3/09/98 11:13a Lawrance + * Fix up drop sound effects used in loadout screens. + * + * 85 3/06/98 5:36p Dave + * Finished up first rev of team vs team. Probably needs to be debugged + * thoroughly. + * + * 84 3/05/98 5:03p Dave + * More work on team vs. team support for multiplayer. Need to fix bugs in + * weapon select. + * + * 83 3/01/98 3:26p Dave + * Fixed a few team select bugs. Put in multiplayer intertface sounds. + * Corrected how ships are disabled/enabled in team select/weapon select + * screens. + * + * 82 2/28/98 7:04p Lawrance + * Don't show reset button in multiplayer + * + * 81 2/27/98 11:32a Andsager + * AL: Fix bug with showing valid weapon slots for ships that arrive late. + * + * 80 2/26/98 4:59p Allender + * groundwork for team vs team briefings. Moved weaponry pool into the + * Team_data structure. Added team field into the p_info structure. + * Allow for mutliple structures in the briefing code. + * + * 79 2/24/98 6:21p Lawrance + * Integrate new reset button into loadout screens + * + * 78 2/22/98 4:17p John + * More string externalization classification... 190 left to go! + * + * 77 2/22/98 12:19p John + * Externalized some strings + * + * 76 2/19/98 6:26p Dave + * Fixed a few file xfer bugs. Tweaked mp team select screen. Put in + * initial support for player data uploading. + * + * 75 2/18/98 10:37p Dave + * Fixed a mp team select bug which allowed players to commit at + * inappropriate times. + * + * 74 2/18/98 3:56p Dave + * Several bugs fixed for mp team select screen. Put in standalone packet + * routing for team select. + * + * 73 2/16/98 10:27a Lawrance + * Fix bug in wl_cull_illegal_weapons() + * + * 72 2/15/98 11:28p Allender + * allow increase in MAX_WEAPON_TYPES by chaning all bitfield references + * + * 71 2/13/98 5:15p Lawrance + * Fix help overlay bug in weapons loadout. + * + * 70 2/13/98 3:46p Dave + * Put in dynamic chatbox sizing. Made multiplayer file lookups use cfile + * functions. + * + * 69 2/09/98 11:24p Lawrance + * Allow player to click on disallowed weapons, but don't let them drag + * them away from the list. + * + * 68 2/07/98 5:47p Lawrance + * reset flashing if a button gets highlighted + * + * 67 2/06/98 5:15p Jasen + * AL: Allow up to 10000 of any given missile in the pool + * + * 66 2/05/98 11:21p Lawrance + * When flashing buttons, use highlight frame + * + * 65 2/02/98 3:36p Jasen + * AL: remove weapon debug name, and allow for 2 frame weapon anis + * + * 64 2/02/98 3:21p Jasen + * Updated coords for weapon anim location. + * + * 63 1/20/98 5:52p Lawrance + * account for no player ship when moving to weapons loadout + * + * 62 1/20/98 2:23p Dave + * Removed optimized build warnings. 99% done with ingame join fixes. + * + * 61 1/20/98 11:08a Lawrance + * Fix sound error when clicking on a weapon. + * + * 60 1/19/98 2:17p Lawrance + * Fix bug that was not recognizing non-default weapons on late-arriving + * wings. + * + * 59 1/15/98 4:09p Lawrance + * fix weapon scrolling bug + * + * 58 1/14/98 6:44p Lawrance + * Take out unnecessary instance checking when freeing anims. + * + * 57 1/12/98 5:17p Dave + * Put in a bunch of multiplayer sequencing code. Made weapon/ship select + * work through the standalone. + * + * 56 1/10/98 12:47a Lawrance + * update some comments + * + * 55 1/09/98 6:06p Dave + * Put in network sound support for multiplayer ship/weapon select + * screens. Made clients exit game correctly through warp effect. Fixed + * main hall menu help overlay bug. + * + * 54 1/09/98 4:08p John + * Fixed a bug loading too many icon frames + * + * 53 1/08/98 10:55p Lawrance + * Fix bug where forbidden weapons were not updated when a ship was + * changed before weapons loadout is entered. + * + * 52 1/08/98 5:19p Sandeep + * Alan made a change which fixed missions with more than 4 types of + * secondary weapons. + * + * 51 1/08/98 11:38a Lawrance + * correct typo + * + * 50 1/08/98 11:36a Lawrance + * Get ship select and weapons loadout icon dropping sound effects working + * for single and multiplayer + * + * 49 1/02/98 9:10p Lawrance + * Big changes to how colors get set on the HUD. + * + * 48 12/30/97 6:08p Lawrance + * Fix bug where player discarded ship before entering weapon select + * + * 47 12/29/97 4:21p Lawrance + * Flash buttons on briefing/ship select/weapons loadout when enough time + * has elapsed without activity. + * + * 46 12/29/97 10:11a Lawrance + * Don't play drop sound when a weapon is clicked on in a slot. + * + * 45 12/24/97 8:54p Lawrance + * Integrating new popup code + * + * 44 12/24/97 1:19p Lawrance + * fix some bugs with the multiplayer ship/weapons loadout + * + * 43 12/23/97 5:25p Allender + * more fixes to multiplayer ship selection. Fixed strange reentrant + * problem with cf_callback when loading freespace data + * + * 42 12/23/97 11:59a Allender + * changes to ship/wespon selection for multplayer. added sounds to some + * interface screens. Update and modiied end-of-briefing packets -- yet + * to be tested. + * + * 41 12/23/97 11:48a Lawrance + * fix bug that could cause assert when swapping secondary weapons + * + * 40 12/22/97 6:18p Lawrance + * Get save/restore of player loadout working with new code + * + * 39 12/22/97 1:40a Lawrance + * Re-write ship select/weapons loadout to be multiplayer friendly + * + * 38 12/19/97 1:23p Dave + * Put in multiplayer groundwork for new weapon/ship select screens. + * + * 37 12/19/97 12:44p Dave + * Put in final touches on ship/weapon select. However, this is all going + * to be rewritten. + * + * $NoKeywords: $ + */ + +#include "missionscreencommon.h" +#include "missionweaponchoice.h" +#include "missionshipchoice.h" +#include "timer.h" +#include "key.h" +#include "mouse.h" +#include "bmpman.h" +#include "2d.h" +#include "snazzyui.h" +#include "animplay.h" +#include "freespace.h" +#include "gamesequence.h" +#include "multi.h" +#include "missionbrief.h" +#include "ui.h" +#include "gamesnd.h" +#include "animplay.h" +#include "contexthelp.h" +#include "chatbox.h" +#include "linklist.h" +#include "multimsgs.h" +#include "popup.h" +#include "multiteamselect.h" +#include "multiui.h" +#include "alphacolors.h" +#include "localize.h" + +//#define MAX_PRIMARY_BANKS 3 +//#define MAX_SECONDARY_BANKS 3 // Lowered from 5 to 3 by MK on 3/25/98. This needs to be <= MAX_WL_SECONDARY or you'll get stack overwrites. + +#define IS_BANK_PRIMARY(x) (x<3?1:0) +#define IS_BANK_SECONDARY(x) (x>2?1:0) + +#define IS_LIST_PRIMARY(x) (Weapon_info[x].subtype==WP_MISSILE?0:1) +#define IS_LIST_SECONDARY(x) (Weapon_info[x].subtype==WP_MISSILE?1:0) + +//XSTR:OFF +#if (MAX_PRIMARY_BANKS > MAX_WL_PRIMARY) +#error "Illegal: MAX_PRIMARY_BANKS greater than MAX_WL_PRIMARY" +#endif + +#if (MAX_SECONDARY_BANKS > MAX_WL_SECONDARY) +#error "Illegal: MAX_SECONDARY_BANKS greater than MAX_WL_SECONDARY" +#endif +//XSTR:ON + +////////////////////////////////////////////////////////////////// +// Game-wide globals +////////////////////////////////////////////////////////////////// + +// This game-wide global flag is set to 1 to indicate that the weapon +// select screen has been opened and memory allocated. This flag +// is needed so we can know if weapon_select_close() needs to called if +// restoring a game from the Options screen invoked from weapon select +int Weapon_select_open = 0; + +////////////////////////////////////////////////////////////////// +// UI Data +////////////////////////////////////////////////////////////////// + +typedef struct wl_bitmap_group +{ + int first_frame; + int num_frames; +} wl_bitmap_group; + +#ifdef FS2_DEMO +#define WEAPON_ANIM_LOOP_FRAME 1 +#else +#define WEAPON_ANIM_LOOP_FRAME 52 // frame (from 0) to loop weapon anim +#endif + +#define WEAPON_ICON_FRAME_NORMAL 0 +#define WEAPON_ICON_FRAME_HOT 1 +#define WEAPON_ICON_FRAME_SELECTED 2 +#define WEAPON_ICON_FRAME_DISABLED 3 + +// Weapn loadout specific buttons +#define NUM_WEAPON_BUTTONS 7 +#define WL_BUTTON_SCROLL_PRIMARY_UP 0 +#define WL_BUTTON_SCROLL_PRIMARY_DOWN 1 +#define WL_BUTTON_SCROLL_SECONDARY_UP 2 +#define WL_BUTTON_SCROLL_SECONDARY_DOWN 3 +#define WL_BUTTON_RESET 4 +#define WL_BUTTON_DUMMY 5 +#define WL_BUTTON_MULTI_LOCK 6 +UI_WINDOW Weapon_ui_window; +//UI_BUTTON Weapon_buttons[NUM_WEAPON_BUTTONS]; + +static char *Wl_mask_single[GR_NUM_RESOLUTIONS] = { + "weaponloadout-m", + "2_weaponloadout-m" +}; + +static char *Wl_mask_multi[GR_NUM_RESOLUTIONS] = { + "weaponloadoutmulti-m", + "2_weaponloadoutmulti-m" +}; + +static char *Wl_loadout_select_mask[GR_NUM_RESOLUTIONS] = { + "weaponloadout-m", + "2_weaponloadout-m" +}; + + +static char *Weapon_select_background_fname[GR_NUM_RESOLUTIONS] = { + "WeaponLoadout", + "2_WeaponLoadout" +}; + +static char *Weapon_select_multi_background_fname[GR_NUM_RESOLUTIONS] = { + "WeaponLoadoutMulti", + "2_WeaponLoadoutMulti" +}; + +int Weapon_select_background_bitmap; // bitmap for weapon select brackground + +static MENU_REGION Weapon_select_region[NUM_WEAPON_REGIONS]; +static int Num_weapon_select_regions; + +// Mask bitmap pointer and Mask bitmap_id +static bitmap* WeaponSelectMaskPtr; // bitmap pointer to the weapon select mask bitmap +static ubyte* WeaponSelectMaskData; // pointer to actual bitmap data +static int Weaponselect_mask_w, Weaponselect_mask_h; +static int WeaponSelectMaskBitmap; // bitmap id of the weapon select mask bitmap + + +// convenient struct for handling all button controls +struct wl_buttons { + char *filename; + int x, y, xt, yt; + int hotspot; + UI_BUTTON button; // because we have a class inside this struct, we need the constructor below.. + + wl_buttons(char *name, int x1, int y1, int xt1, int yt1, int h) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h) {} +}; + +static wl_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_WEAPON_BUTTONS] = { + { + wl_buttons("WLB_26", 24, 125, -1, -1, 26), + wl_buttons("WLB_27", 24, 276, -1, -1, 27), + wl_buttons("WLB_08", 24, 303, -1, -1, 8), + wl_buttons("WLB_09", 24, 454, -1, -1, 9), + wl_buttons("ssb_39", 571, 347, -1, -1, 39), + wl_buttons("ssb_39", 0, 0, -1, -1, 99), + wl_buttons("TSB_34", 603, 374, -1, -1, 34) + }, + { + wl_buttons("2_WLB_26", 39, 200, -1, -1, 26), + wl_buttons("2_WLB_27", 39, 442, -1, -1, 27), + wl_buttons("2_WLB_08", 39, 485, -1, -1, 8), + wl_buttons("2_WLB_09", 39, 727, -1, -1, 9), + wl_buttons("2_ssb_39", 913, 556, -1, -1, 39), + wl_buttons("2_ssb_39", 0, 0, -1, -1, 99), + wl_buttons("2_TSB_34", 966, 599, -1, -1, 34) + } +}; + +//static wl_bitmap_group wl_button_bitmaps[NUM_WEAPON_BUTTONS]; + +static int Weapon_button_scrollable[NUM_WEAPON_BUTTONS] = {0, 0, 0, 0, 0, 0, 0}; + +#define MAX_WEAPON_ICONS_ON_SCREEN 8 + +// X and Y locations of the weapon icons in the scrollable lists +//int Weapon_icon_x[MAX_WEAPON_ICONS_ON_SCREEN] = {27, 27, 27, 27, 36, 36, 36, 36}; +//int Weapon_icon_y[MAX_WEAPON_ICONS_ON_SCREEN] = {152, 182, 212, 242, 331, 361, 391, 421}; +static int Wl_weapon_icon_coords[GR_NUM_RESOLUTIONS][MAX_WEAPON_ICONS_ON_SCREEN][2] = { + { + {27, 152}, + {27, 182}, + {27, 212}, + {27, 242}, + {36, 331}, + {36, 361}, + {36, 391}, + {36, 421} + }, + { + {59, 251}, + {59, 299}, + {59, 347}, + {59, 395}, + {59, 538}, + {59, 586}, + {59, 634}, + {59, 682} + } +}; + + +static int Wl_bank_coords[GR_NUM_RESOLUTIONS][MAX_WL_WEAPONS][2] = { + { + {106,127}, + {106,158}, + {106,189}, + {322,127}, + {322,158}, + {322,189}, + {322,220}, + }, + { + {170,203}, + {170,246}, + {170,290}, + {552,203}, + {552,246}, + {552,290}, + {552,333}, + } +}; + +static int Wl_bank_count_draw_flags[MAX_WL_WEAPONS] = { + 0, 0, 0, // primaries -- dont draw counts + 1, 1, 1, 1 // secondaries -- do draw counts +}; + + +static int Weapon_anim_class = -1; +static int Last_wl_ship_class; + +static int Wl_overhead_coords[GR_NUM_RESOLUTIONS][2] = { + { + // GR_640 + 91, 117 + }, + { + // GR_1024 + 156, 183 + } +}; + +static int Wl_weapon_ani_coords[GR_NUM_RESOLUTIONS][2] = { + { + 408, 82 // GR_640 + }, + { + 648, 128 // GR_1024 + } +}; + +static int Wl_weapon_ani_coords_multi[GR_NUM_RESOLUTIONS][2] = { + { + 408, 143 // GR_640 + }, + { + 648, 226 // GR_1024 + } +}; + + +static int Wl_weapon_desc_coords[GR_NUM_RESOLUTIONS][2] = { + { + 508, 283 // GR_640 + }, + { + 813, 453 // GR_1024 + } +}; + +static int Wl_delta_x, Wl_delta_y; + +static int Wl_ship_name_coords[GR_NUM_RESOLUTIONS][2] = { + { + 85, 106 + }, + { + 136, 170 + } +}; + + +/////////////////////////////////////////////////////////////////////// +// UI data structs +/////////////////////////////////////////////////////////////////////// +typedef struct wl_ship_class_info +{ + int overhead_bitmap; + anim_t *anim; + anim_instance_t *anim_instance; +} wl_ship_class_info; + +wl_ship_class_info Wl_ships[MAX_SHIP_TYPES]; + +typedef struct wl_icon_info +{ + int icon_bmaps[NUM_ICON_FRAMES]; + int current_icon_bmap; + int can_use; + anim_t *anim; + anim_instance_t *anim_instance; +} wl_icon_info; + +wl_icon_info Wl_icons_teams[MAX_TEAMS][MAX_WEAPON_TYPES]; +wl_icon_info *Wl_icons; + +int Plist[MAX_WEAPON_TYPES]; // used to track scrolling of primary icon list +int Plist_start, Plist_size; + +int Slist[MAX_WEAPON_TYPES]; // used to track scrolling of primary icon list +int Slist_start, Slist_size; + +static int Selected_wl_slot = -1; // Currently selected ship slot +static int Selected_wl_class = -1; // Class of weapon that is selected +static int Hot_wl_slot = -1; // Ship slot that mouse is over (0..11) +static int Hot_weapon_icon = -1; // Icon number (0-7) which has mouse over it +static int Hot_weapon_bank = -1; // index (0-7) for weapon slot on ship that has a droppable icon over it +static int Hot_weapon_bank_icon = -1; + +static int Wl_mouse_down_on_region = -1; + + +// weapon desc stuff +#define WEAPON_DESC_WIPE_TIME 1.5f // time in seconds for wipe to occur (over WEAPON_DESC_MAX_LENGTH number of chars) +#define WEAPON_DESC_MAX_LINES 7 // max lines in the description incl. title +#define WEAPON_DESC_MAX_LENGTH 50 // max chars per line of description text +static int Weapon_desc_wipe_done = 0; +static float Weapon_desc_wipe_time_elapsed = 0.0f; +static char Weapon_desc_lines[WEAPON_DESC_MAX_LINES][WEAPON_DESC_MAX_LENGTH]; // 1st 2 lines are title, rest are desc + +// maximum width the weapon title can be -- used in the line breaking +int Weapon_title_max_width[GR_NUM_RESOLUTIONS] = { 200, 320 }; + +static int Wl_new_weapon_title_coords[GR_NUM_RESOLUTIONS][2] = { + { + 408, 75 // GR_640 + }, + { + 648, 118 // GR_1024 + } +}; + +static int Wl_new_weapon_title_coords_multi[GR_NUM_RESOLUTIONS][2] = { + { + 408, 136 // GR_640 + }, + { + 648, 216 // GR_1024 + } +}; + +static int Wl_new_weapon_desc_coords[GR_NUM_RESOLUTIONS][2] = { + { + 408, 247 // GR_640 + }, + { + 648, 395 // GR_1024 + } +}; + +static int Wl_new_weapon_desc_coords_multi[GR_NUM_RESOLUTIONS][2] = { + { + 408, 308 // GR_640 + }, + { + 648, 493 // GR_1024 + } +}; + +// ship select text +#define WEAPON_SELECT_NUM_TEXT 2 +UI_XSTR Weapon_select_text[GR_NUM_RESOLUTIONS][WEAPON_SELECT_NUM_TEXT] = { + { // GR_640 + { "Reset", 1337, 580, 337, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][WL_BUTTON_RESET].button }, + { "Lock", 1270, 602, 364, UI_XSTR_COLOR_GREEN, -1, &Buttons[0][WL_BUTTON_MULTI_LOCK].button } + }, + { // GR_1024 + { "Reset", 1337, 938, 546, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][WL_BUTTON_RESET].button }, + { "Lock", 1270, 964, 584, UI_XSTR_COLOR_GREEN, -1, &Buttons[1][WL_BUTTON_MULTI_LOCK].button } + } +}; + + +/////////////////////////////////////////////////////////////////////// +// Carried Icon +/////////////////////////////////////////////////////////////////////// +typedef struct carried_icon +{ + int weapon_class; // index Wl_icons[] for carried icon (-1 if carried from bank) + int num; // number of units of weapon + int from_bank; // bank index that icon came from (0..2 primary, 3..6 secondary). -1 if from list + int from_slot; // ship slot that weapon is part of + int from_x, from_y; +} carried_icon; + +static carried_icon Carried_wl_icon; + +// forward declarations +void draw_wl_icons(); +void wl_draw_ship_weapons(int index); +void wl_pick_icon_from_list(int index); +void pick_from_ship_slot(int num); +void start_weapon_animation(int weapon_class); +void stop_weapon_animation(); +void wl_start_slot_animation(int n); +int wl_get_pilot_subsys_index(p_object *pobjp); + +void wl_reset_to_defaults(); + +void wl_set_selected_slot(int slot_num); +void wl_maybe_reset_selected_slot(); +void wl_maybe_reset_selected_weapon_class(); + +void wl_render_icon_count(int num, int x, int y); +void wl_render_weapon_desc(); + + + +// carry icon functions +void wl_reset_carried_icon(); +int wl_icon_being_carried(); +void wl_set_carried_icon(int from_bank, int from_slot, int weapon_class); + + +// Determine hack offset for how to draw fury missile icon. +int wl_fury_missile_offset_hack(int weapon_class, int num_missiles) +{ + if ( weapon_class < 0 ) { + return 0; + } + + if ( num_missiles < 100 ) { + return 0 ; + } + + if ( !strnicmp(Weapon_info[weapon_class].name, NOX("fury"), 4) ) { + return 3; + } + + return 0; +} + +char *wl_tooltip_handler(char *str) +{ + if (Selected_wl_class < 0) + return NULL; + + if (!stricmp(str, "@weapon_desc")) { + char *str; + int x, y, w, h; + + str = Weapon_info[Selected_wl_class].desc; + gr_get_string_size(&w, &h, str); + x = Wl_weapon_desc_coords[gr_screen.res][0] - w / 2; + y = Wl_weapon_desc_coords[gr_screen.res][1] - h / 2; + + gr_set_color_fast(&Color_black); + gr_rect(x - 5, y - 5, w + 10, h + 10); + + gr_set_color_fast(&Color_bright_white); + gr_string(x, y, str); + return NULL; + } + + return NULL; +} + +// reset the data inside the Carried_wl_icon +void wl_reset_carried_icon() +{ + Carried_wl_icon.weapon_class = -1; + Carried_wl_icon.num = 0; + Carried_wl_icon.from_bank = -1; + Carried_wl_icon.from_slot = -1; +} + +// Is an icon being carried? +int wl_icon_being_carried() +{ + if ( Carried_wl_icon.weapon_class >= 0 ) { + return 1; + } + + return 0; +} + +// Set carried icon data +void wl_set_carried_icon(int from_bank, int from_slot, int weapon_class) +{ + int mx,my; + + Carried_wl_icon.from_bank = from_bank; + Carried_wl_icon.from_slot = from_slot; + Carried_wl_icon.weapon_class = weapon_class; + + mouse_get_pos( &mx, &my ); + Carried_wl_icon.from_x=mx; + Carried_wl_icon.from_y=my; + + Buttons[gr_screen.res][WL_BUTTON_DUMMY].button.capture_mouse(); +} + +// determine if the carried icon has moved +int wl_carried_icon_moved() +{ + int mx, my; + mouse_get_pos( &mx, &my ); + if ( Carried_wl_icon.from_x != mx || Carried_wl_icon.from_y != my) { + return 1; + } + + return 0; +} + +// return the index for the pilot subsystem in the parse object +int wl_get_pilot_subsys_index(p_object *pobjp) +{ + int pilot_index, start_index, end_index, i; + + // see if there is a PILOT subystem + start_index = pobjp->subsys_index; + end_index = start_index + pobjp->subsys_count; + pilot_index = -1; + for ( i = start_index; i < end_index; i++ ) { + if ( !stricmp(Subsys_status[i].name, NOX("pilot") ) ) { + pilot_index = i; + break; + } + } + + if ( pilot_index == -1 ) { + Error(LOCATION,"Parse object doesn't have a pilot subsystem\n"); + return -1; + } + + return pilot_index; +} + +// Pause the current weapon animation +void wl_pause_anim() +{ + if ( Weapon_anim_class >= 0 && Wl_icons[Weapon_anim_class].anim_instance ) { + anim_pause(Wl_icons[Weapon_anim_class].anim_instance); + } +} + +// Unpause the current weapon animation +void wl_unpause_anim() +{ + if ( Weapon_anim_class >= 0 && Wl_icons[Weapon_anim_class].anim_instance ) { + anim_unpause(Wl_icons[Weapon_anim_class].anim_instance); + } +} + +// --------------------------------------------------------------------------------- +// weapon_button_do() +// +void weapon_button_do(int i) +{ + switch ( i ) { + case PRIMARY_SCROLL_UP: + if ( common_scroll_up_pressed(&Plist_start, Plist_size, 4) ) { + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + + case PRIMARY_SCROLL_DOWN: + if ( common_scroll_down_pressed(&Plist_start, Plist_size, 4) ) { + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + + case SECONDARY_SCROLL_UP: + if ( common_scroll_up_pressed(&Slist_start, Slist_size, 4) ) { + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + + case SECONDARY_SCROLL_DOWN: + if ( common_scroll_down_pressed(&Slist_start, Slist_size, 4) ) { + gamesnd_play_iface(SND_SCROLL); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + + case WL_RESET_BUTTON_MASK: + wl_reset_to_defaults(); + break; + + case WL_BUTTON_MULTI_LOCK: + Assert(Game_mode & GM_MULTIPLAYER); + // the "lock" button has been pressed + multi_ts_lock_pressed(); + + // disable the button if it is now locked + if(multi_ts_is_locked()){ + Buttons[gr_screen.res][WL_BUTTON_MULTI_LOCK].button.disable(); + } + break; + + default: + break; + } +} + +// ------------------------------------------------------------------- +// weapon_check_buttons() +// +// Check if any weapons loadout screen buttons have been pressed, and +// call weapon_button_do() if they have. +// +void weapon_check_buttons() +{ + int i; + wl_buttons *b; + + for ( i = 0; i < NUM_WEAPON_BUTTONS; i++ ) { + b = &Buttons[gr_screen.res][i]; + + if ( b->button.pressed() ) { + if(i == WL_BUTTON_MULTI_LOCK){ + weapon_button_do(i); + } else { + weapon_button_do(b->hotspot); + } + } + } +} + +// ------------------------------------------------------------------- +// wl_redraw_pressed_buttons() +// +// Redraw any weapon loadout buttons that are pressed down. This function is needed +// since we sometimes need to draw pressed buttons last to ensure the entire +// button gets drawn (and not overlapped by other buttons) +// +void wl_redraw_pressed_buttons() +{ + int i; + wl_buttons *b; + + common_redraw_pressed_buttons(); + + for ( i = 0; i < NUM_WEAPON_BUTTONS; i++ ) { + b = &Buttons[gr_screen.res][i]; + if ( b->button.button_down() ) { + b->button.draw_forced(2); + } + } +} + +// --------------------------------------------------------------------------------- +// weapon_buttons_init() +// +void weapon_buttons_init() +{ + wl_buttons *b; + int i; + + for ( i = 0; i < NUM_WEAPON_BUTTONS; i++ ) { + b = &Buttons[gr_screen.res][i]; + b->button.create( &Weapon_ui_window, "", Buttons[gr_screen.res][i].x, Buttons[gr_screen.res][i].y, 60, 30, Weapon_button_scrollable[i]); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action( common_play_highlight_sound ); + b->button.set_bmaps(Buttons[gr_screen.res][i].filename); + b->button.link_hotspot(Buttons[gr_screen.res][i].hotspot); + } + + if ( Game_mode & GM_MULTIPLAYER ) { + Buttons[gr_screen.res][WL_BUTTON_RESET].button.hide(); + Buttons[gr_screen.res][WL_BUTTON_RESET].button.disable(); + + // if we're not the host of the game (or a team captain in team vs. team mode), disable the lock button + if(Netgame.type_flags & NG_TYPE_TEAM){ + if(!(Net_player->flags & NETINFO_FLAG_TEAM_CAPTAIN)){ + Buttons[gr_screen.res][WL_BUTTON_MULTI_LOCK].button.disable(); + } + } else { + if(!(Net_player->flags & NETINFO_FLAG_GAME_HOST)){ + Buttons[gr_screen.res][WL_BUTTON_MULTI_LOCK].button.disable(); + } + } + } else { + Buttons[gr_screen.res][WL_BUTTON_MULTI_LOCK].button.hide(); + Buttons[gr_screen.res][WL_BUTTON_MULTI_LOCK].button.disable(); + } + + // add all xstrs + for(i=0; ianim_instance == NULL ) { + if ( wl_ship->overhead_bitmap < 0 ) { + // load the bitmap + if (gr_screen.res == GR_640) + { + // lo-res + wl_ship->overhead_bitmap = bm_load(Ship_info[ship_class].overhead_filename); + } else { + // high-res + char filename[NAME_LENGTH+2] = "2_"; + strcat(filename, Ship_info[ship_class].overhead_filename); + wl_ship->overhead_bitmap = bm_load(filename); + } + if ( wl_ship->overhead_bitmap < 0 ) { + Int3(); // bad things happened + return; + } + } + gr_set_bitmap(wl_ship->overhead_bitmap); + gr_bitmap(Wl_overhead_coords[gr_screen.res][0], Wl_overhead_coords[gr_screen.res][1]); + } + + ss_return_name(Selected_wl_slot/4, Selected_wl_slot%4, name); + gr_set_color_fast(&Color_normal); + gr_string(Wl_ship_name_coords[gr_screen.res][0], Wl_ship_name_coords[gr_screen.res][1], name); +} + +// --------------------------------------------------------------------------------- +// wl_get_ship_class() +// +// +int wl_get_ship_class(int wl_slot) +{ + return Wss_slots[wl_slot].ship_class; +} + +// Return true if weapon_flags indicates a weapon that is legal for use in current game type. +// Function added by MK on 9/6/99 to support separate legal loadouts for dogfight missions. +int weapon_allowed_for_game_type(int weapon_flags) +{ + int rval = 0; + + if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_DOGFIGHT)) { + if (weapon_flags & (1 << 1)) + rval = 1; + } else if (weapon_flags & (1 << 0)) + rval = 1; + + return rval; +} + +// go through the possible weapons to choose from, and flag some as disabled since +// that ship class cannot use that kind of weapon. The weapon filter is specified +// in ships.tbl, where each ship has a list of all the possible weapons it can use. +void wl_set_disabled_weapons(int ship_class) +{ + int i; + ship_info *sip; + + if ( ship_class == - 1 ) + return; + + Assert(ship_class >= 0 && ship_class < MAX_SHIP_TYPES); + + sip = &Ship_info[ship_class]; + + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + // Determine whether weapon #i is allowed on this ship class in the current type of mission. + // As of 9/6/99, the only difference is dogfight missions have a different list of legal weapons. + Wl_icons[i].can_use = weapon_allowed_for_game_type(sip->allowed_weapons[i]); + } +} + +// --------------------------------------------------------------------------------- +// maybe_select_wl_slot() +// +// A slot index was clicked on, mabye change Selected_wl_slot +void maybe_select_wl_slot(int block, int slot) +{ + int sidx; + + if ( Wss_num_wings <= 0 ) + return; + + sidx = block*4 + slot; + if ( Wss_slots[sidx].ship_class < 0 ) { + return; + } + + wl_set_selected_slot(sidx); +} + +// --------------------------------------------------------------------------------- +// maybe_select_new_weapon() +// +// Change to the weapon that corresponds to index in the weapon list +// +// input: index => weapon icon index (0-7) +// +void maybe_select_new_weapon(int index) +{ + int weapon_class; + + // if a weapon is being carried, do nothing + if ( wl_icon_being_carried() ) { + return; + } + + int region_index = ICON_PRIMARY_0+index; + if ( index > 3 ) { + region_index = ICON_SECONDARY_0 + (index - 4); + } + + if ( Wl_mouse_down_on_region != region_index ) { + return; + } + + if ( index < 4 ) { + weapon_class = Plist[Plist_start+index]; + } else { + weapon_class = Slist[Slist_start+index-4]; + } + + if ( weapon_class >= 0 ) { + Selected_wl_class = weapon_class; + wl_pick_icon_from_list(index); + } +} + +// --------------------------------------------------------------------------------- +// maybe_select_new_ship_weapon() +// +// Change to the weapon that corresponds to the ship weapon slot +// +// input: index -> index of bank (0..2 primary, 0..6 secondary) +void maybe_select_new_ship_weapon(int index) +{ + int *wep, *wep_count; + + if ( Selected_wl_slot == -1 ) + return; + + if ( wl_icon_being_carried() ) { + return; + } + + wep = Wss_slots[Selected_wl_slot].wep; + wep_count = Wss_slots[Selected_wl_slot].wep_count; + + if ( wep[index] < 0 || wep_count[index] <= 0 ) { + return; + } + + if ( Wl_mouse_down_on_region != (ICON_SHIP_PRIMARY_0+index) ) { + return; + } + + + Selected_wl_class = wep[index]; +} + +// Initialize Wl_pool[] to mission deafult +void wl_init_pool(team_data *td) +{ + int i; + + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + Wl_pool[i] = -1; + } + + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + Wl_pool[i] = td->weaponry_pool[i]; // read from mission + } +} + +// load the icons for a specific ship class +void wl_load_icons(int weapon_class) +{ + wl_icon_info *icon; + int first_frame, num_frames, i; + + icon = &Wl_icons[weapon_class]; + + first_frame = bm_load_animation(Weapon_info[weapon_class].icon_filename, &num_frames); + if ( first_frame == -1 ) { + Int3(); // Could not load in icon frames.. get Alan + return; + } + + for ( i = 0; i < num_frames ; i++ ) { + icon->icon_bmaps[i] = first_frame+i; + } +} + +// Load in a specific weapon animation. The data is loaded as a memory-mapped file since these animations +// can be large. +void wl_load_anim(int weapon_class) +{ + char animation_filename[CF_MAX_FILENAME_LENGTH+4]; + wl_icon_info *icon; + + icon = &Wl_icons[weapon_class]; + Assert( icon->anim == NULL ); + + // 1024x768 SUPPORT + // If we are in 1024x768, we first want to append "2_" in front of the filename + if (gr_screen.res == GR_1024) { + Assert(strlen(Weapon_info[weapon_class].anim_filename) <= 30); + strcpy(animation_filename, "2_"); + strcat(animation_filename, Weapon_info[weapon_class].anim_filename); + + // now check if file exists + // GRR must add a .ANI at the end for detection + strcat(animation_filename,".ani"); + icon->anim = anim_load(animation_filename, 1); + + if (icon->anim == NULL) { + mprintf(("Weapon ANI: Can not find %s, using lowres version instead.\n",animation_filename)); + strcpy(animation_filename, Weapon_info[weapon_class].anim_filename); + icon->anim = anim_load(animation_filename, 1); + } + + /* + if (!cf_exist(animation_filename, CF_TYPE_INTERFACE)) { + // file does not exist, use original low res version + mprintf(("Weapon ANI: Can not find %s, using lowres version instead.\n",animation_filename)); + strcpy(animation_filename, Weapon_info[weapon_class].anim_filename); + } else { + animation_filename[strlen(animation_filename) - 4] = '\0'; + mprintf(("Weapon ANI: Found hires version of %s\n",animation_filename)); + } + */ + } else { + strcpy(animation_filename, Weapon_info[weapon_class].anim_filename); + // load the compressed ship animation into memory + // NOTE: if last parm of load_anim is 1, the anim file is mapped to memory + icon->anim = anim_load(animation_filename, 1); + } + + if ( icon->anim == NULL ) { + Int3(); // couldn't load anim filename.. get Alan + } +} + +// Load in any weapon animations. This function assumes that Wl_pool has been inited. +void wl_load_all_anims() +{ + #ifndef DEMO // not for FS2_DEMO + + int i; + + // init anim members for weapon animations + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + Wl_icons[i].anim = NULL; + Wl_icons[i].anim_instance = NULL; + } + + // init anim member for overhead ship animations + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + Wl_ships[i].anim = NULL; + Wl_ships[i].anim_instance = NULL; + } + + // load up the animations used for weapons (they are memory-mapped) + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + if ( Wl_pool[i] > 0 ) { + wl_load_anim(i); + } + } + + #endif +} + +// release any anim instances +void wl_unload_all_anim_instances() +{ + // stop any weapon anim instances + for ( int i = 0; i < MAX_WEAPON_TYPES; i++ ) { + if ( Wl_icons[i].anim_instance ) { + anim_release_render_instance(Wl_icons[i].anim_instance); + Wl_icons[i].anim_instance = NULL; + } + } + + // stop any overhead anim instances + for (int i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Wl_ships[i].anim_instance ) { + anim_release_render_instance(Wl_ships[i].anim_instance); + Wl_ships[i].anim_instance = NULL; + } + } +} + +// free source anim data if allocated +void wl_unload_all_anims() +{ + int i; + + // unload overhead anim instances + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + if ( Wl_ships[i].anim ) { + anim_free(Wl_ships[i].anim); + Wl_ships[i].anim = NULL; + } + } + + // unload weapon anim instances + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + if ( Wl_icons[i].anim ) { + anim_free(Wl_icons[i].anim); + Wl_icons[i].anim = NULL; + } + } +} + +// load all the icons for weapons in the pool +void wl_load_all_icons() +{ + #ifndef DEMO // not for FS2_DEMO + + int i, j; + + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + // clear out data + Wl_icons[i].anim = NULL; + Wl_icons[i].anim_instance = NULL; + for ( j = 0; j < NUM_ICON_FRAMES; j++ ) { + Wl_icons[i].icon_bmaps[j] = -1; + } + + if ( Wl_pool[i] > 0 ) { + wl_load_icons(i); + } + } + + #endif +} + +// wl_unload_icons() frees the bitmaps used for weapon icons +void wl_unload_icons() +{ + int i,j; + wl_icon_info *icon; + + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + icon = &Wl_icons[i]; + + for ( j = 0; j < NUM_ICON_FRAMES; j++ ) { + if ( icon->icon_bmaps[j] >= 0 ) { + bm_release(icon->icon_bmaps[j]); + icon->icon_bmaps[j] = -1; + } + } + } +} + +// init ship-class specific data +void wl_init_ship_class_data() +{ + int i; + wl_ship_class_info *wl_ship; + + for ( i=0; ioverhead_bitmap = -1; + wl_ship->anim = NULL; + wl_ship->anim_instance = NULL; + } +} + +// free any allocated ship-class specific data +void wl_free_ship_class_data() +{ + int i; + wl_ship_class_info *wl_ship; + + for ( i=0; ioverhead_bitmap != -1 ) { + bm_release(wl_ship->overhead_bitmap); + wl_ship->overhead_bitmap = -1; + } + + if ( wl_ship->anim != NULL ) { + wl_ship->anim = NULL; + } + + if ( wl_ship->anim_instance != NULL ) { + wl_ship->anim_instance = NULL; + } + } +} + +// Set selected slot to first placed ship +void wl_reset_selected_slot() +{ + int i; + Selected_wl_slot = -1; + + // in multiplayer, select the slot of the player's ship by default + if((Game_mode & GM_MULTIPLAYER) && !MULTI_PERM_OBSERVER(Net_players[MY_NET_PLAYER_NUM]) && (Wss_slots[Net_player->p_info.ship_index].ship_class >= 0)){ + wl_set_selected_slot(Net_player->p_info.ship_index); + return; + } + + for ( i=0; i= 0) ) { + wl_set_selected_slot(i); + return; + } + } + } + + // Didn't locate player ship, so just select the first ship we find + for ( i=0; i= 0 ) { + wl_set_selected_slot(i); + break; + } + } +} + +// called whenever it is possible that the current selected slot has had it's ship disappear +void wl_maybe_reset_selected_slot() +{ + int reset=0; + + if ( Selected_wl_slot == -1 ) { + reset = 1; + } + + if ( Wss_slots[Selected_wl_slot].ship_class < 0 ) { + reset = 1; + } + + if ( reset ) { + wl_reset_selected_slot(); + } +} + +// If Selected_wl_class is -1, choose the first weapon available from the pool for an animation +// - on second thought, choose the first weapon that is oin the ship, then go to the pools +void wl_maybe_reset_selected_weapon_class() +{ + int i; + + if ( Selected_wl_class >= 0 ) + return; + + // try to locate a weapon class to show animation for + // first check for a weapon on the ship + for (i=0; i= 0) { + Selected_wl_class = Wss_slots[0].wep[i]; + return; + } + } + + // then check for a primary weapon in the pool + for ( i = 0; i < Plist_size; i++ ) { + if ( Plist[i] >= 0 ) { + Selected_wl_class = Plist[i]; + return; + } + } + + // finally, if no others found yet, check for a secondary weapon in the pool + for ( i = 0; i < Slist_size; i++ ) { + if ( Slist[i] >= 0 ) { + Selected_wl_class = Slist[i]; + return; + } + } +} + +// start an overhead animation, since selected slot has changed +void wl_start_slot_animation(int n) +{ + #ifndef DEMO // not for FS2_DEMO + + // don't use ani's + // fallback code in wl_render_overhead_view() will + // use the .pcx files + // should prolly scrub out the 1e06 lines of dead code this leaves + return; + + /* + + int ship_class; + wl_ship_class_info *wl_ship; + anim_play_struct aps; + + if ( n < 0 ) { + return; + } + + ship_class = Wss_slots[n].ship_class; + + if ( ship_class < 0 ) { + Int3(); + return; + } + + wl_ship = &Wl_ships[ship_class]; + + // maybe this animation is already playing? + if ( wl_ship->anim_instance ) { + anim_stop_playing(wl_ship->anim_instance); + wl_ship->anim_instance = NULL; + } + + // maybe we have to load this animation + if ( wl_ship->anim == NULL ) { + wl_ship->anim = anim_load(Ship_info[ship_class].overhead_filename, 1); + if ( wl_ship->anim == NULL ) { + Int3(); // couldn't load anim filename.. get Alan + return; + } + } + + anim_play_init(&aps, wl_ship->anim, Wl_overhead_coords[gr_screen.res][0], Wl_overhead_coords[gr_screen.res][1]); + aps.screen_id = ON_WEAPON_SELECT; + aps.framerate_independent = 1; + aps.skip_frames = 0; + wl_ship->anim_instance = anim_play(&aps); +*/ + #endif +} + +// Call when Selected_wl_slot needs to be changed +void wl_set_selected_slot(int slot_num) +{ + if ( (slot_num >= 0) && (slot_num != Selected_wl_slot) ) { + // slot has changed.... start an animation + wl_start_slot_animation(slot_num); +/* + if ( Current_screen == ON_WEAPON_SELECT ) { + gamesnd_play_iface(SND_OVERHEAD_SHIP_ANIM); + } +*/ + } + + Selected_wl_slot = slot_num; + if ( Selected_wl_slot >= 0 ) { + wl_set_disabled_weapons(Wss_slots[slot_num].ship_class); + } +} + +// determine how many missiles of type 'wi_index' will fit into capacity +int wl_calc_missile_fit(int wi_index, int capacity) +{ + if ( wi_index < 0 ) { + return 0; + } + + Assert(Weapon_info[wi_index].subtype == WP_MISSILE); + return fl2i( capacity / Weapon_info[wi_index].cargo_size + 0.5f ); +} + +// fill out the weapons for this ship_class +void wl_get_ship_class_weapons(int ship_class, int *wep, int *wep_count) +{ + ship_info *sip; + int i; + + Assert(ship_class >= 0 && ship_class < MAX_SHIP_TYPES); + sip = &Ship_info[ship_class]; + + // reset weapons arrays + for ( i=0; i < MAX_WL_WEAPONS; i++ ) { + wep[i] = -1; + wep_count[i] = -1; // -1 means weapon bank doesn't exist.. 0 just means it is empty + } + + for ( i = 0; i < sip->num_primary_banks; i++ ) { + wep[i] = sip->primary_bank_weapons[i]; + wep_count[i] = 1; + } + + for ( i = 0; i < sip->num_secondary_banks; i++ ) { + wep[i+MAX_WL_PRIMARY] = sip->secondary_bank_weapons[i]; + wep_count[i+MAX_WL_PRIMARY] = wl_calc_missile_fit(sip->secondary_bank_weapons[i], sip->secondary_bank_ammo_capacity[i]); + } +} + +// fill out the wep[] and wep_count[] arrays for a ship +void wl_get_ship_weapons(int ship_index, int *wep, int *wep_count) +{ + int i; + wing *wp; + ship_weapon *swp; + + Assert(ship_index >= 0); + + Assert(Ships[ship_index].wingnum >= 0); + wp = &Wings[Ships[ship_index].wingnum]; + swp = &Ships[ship_index].weapons; + + for ( i = 0; i < swp->num_primary_banks; i++ ) { + wep[i] = swp->primary_bank_weapons[i]; + wep_count[i] = 1; + if ( wep[i] == -1 ) { + wep_count[i] = 0; + } + } + + for ( i = 0; i < swp->num_secondary_banks; i++ ) { + wep[i+MAX_WL_PRIMARY] = swp->secondary_bank_weapons[i]; + wep_count[i+MAX_WL_PRIMARY] = swp->secondary_bank_ammo[i]; + if ( wep[i+MAX_WL_PRIMARY] == -1 ) { + wep_count[i+MAX_WL_PRIMARY] = 0; + } + } +} + +// set wep and wep_count from a ship which sits in the ship_arrivals[] list at index sa_index +void wl_get_parseobj_weapons(int sa_index, int ship_class, int *wep, int *wep_count) +{ + int i, pilot_index; + subsys_status *ss; + ship_info *sip; + p_object *pobjp; + + pobjp = &ship_arrivals[sa_index]; + sip = &Ship_info[ship_class]; + + pilot_index = wl_get_pilot_subsys_index(pobjp); + + if ( pilot_index == -1 ) + return; + + ss = &Subsys_status[pilot_index]; + + if ( ss->primary_banks[0] != SUBSYS_STATUS_NO_CHANGE ) { + for ( i=0; i < MAX_PRIMARY_BANKS; i++ ) { + wep[i] = ss->primary_banks[i]; + } // end for + } + + if ( ss->secondary_banks[0] != SUBSYS_STATUS_NO_CHANGE ) { + for ( i=0; i < MAX_SECONDARY_BANKS; i++ ) { + wep[i+MAX_WL_PRIMARY] = ss->secondary_banks[i]; + } // end for + } + + // ammo counts could still be modified + for ( i=0; i < MAX_SECONDARY_BANKS; i++ ) { + if ( wep[i+MAX_WL_PRIMARY] >= 0 ) { + wep_count[i+MAX_WL_PRIMARY] = wl_calc_missile_fit(wep[i+MAX_WL_PRIMARY], fl2i(ss->secondary_ammo[i]/100.0f * sip->secondary_bank_ammo_capacity[i] + 0.5f)); + } + } +} + +// ensure that there aren't any bogus weapons assigned by default +void wl_cull_illegal_weapons(int ship_class, int *wep, int *wep_count) +{ + int i; + for ( i=0; i < MAX_WL_WEAPONS; i++ ) { + if ( wep[i] < 0 ) { + continue; + } + + if ( !weapon_allowed_for_game_type(Ship_info[ship_class].allowed_weapons[wep[i]]) ) { +// wep[i] = -1; + wep_count[i] = 0; + } + } +} + +// get the weapons info that should be on ship by default +void wl_get_default_weapons(int ship_class, int slot_num, int *wep, int *wep_count) +{ + int original_ship_class, i; + + Assert(slot_num >= 0 && slot_num < MAX_WSS_SLOTS); + + // clear out wep and wep_count + for ( i = 0; i < MAX_WL_WEAPONS; i++ ) { + wep[i] = -1; + wep_count[i] = -1; + } + + if ( ship_class < 0 ) + return; + + original_ship_class = ss_return_original_ship_class(slot_num); + + if ( original_ship_class != ship_class ) { + wl_get_ship_class_weapons(ship_class, wep, wep_count); + } else { + int sa_index; // ship arrival index + sa_index = ss_return_saindex(slot_num); + + if ( sa_index >= 0 ) { + // still a parse object + wl_get_ship_class_weapons(ship_class, wep, wep_count); + wl_get_parseobj_weapons(sa_index, ship_class, wep, wep_count); + } else { + // ship has been created +// wl_get_ship_weapons(slot_num/4, slot_num%4, wep, wep_count); + int ship_index = -1; + p_object *pobjp; + ss_return_ship(slot_num/4, slot_num%4, &ship_index, &pobjp); + Assert(ship_index != -1); + wl_get_ship_weapons(ship_index, wep, wep_count); + } + } + + // ensure that there aren't any bogus weapons assigned by default + wl_cull_illegal_weapons(ship_class, wep, wep_count); +} + +// function to add a weapon_class to ui lists +void wl_add_index_to_list(int wi_index) +{ + int i; + if ( Weapon_info[wi_index].subtype == WP_MISSILE ) { + + for ( i=0; i= 0 ) { + if ( (wep_count[i] > 0) && ((Wl_pool[wi_index] - wep_count[i]) >= 0) ) { + Wl_pool[wi_index] -= wep_count[i]; + } else { + // not enough weapons in pool + // TEMP HACK: FRED doesn't fill in a weapons pool if there are no starting wings... so + // add to the pool. This should be fixed. + if ( Wss_num_wings <= 0 ) { + wl_add_index_to_list(wi_index); + } else { + + if ( (Wl_pool[wi_index] <= 0) || (wep_count[i] == 0) ) { + // fresh out of this weapon, pick an alternate pool weapon if we can + int wep_pool_index, wep_precedence_index, new_wi_index = -1; + for ( wep_pool_index = 0; wep_pool_index < MAX_WEAPON_TYPES; wep_pool_index++ ) { + + if ( Wl_pool[wep_pool_index] <= 0 ) { + continue; + } + + // AL 3-31-98: Only pick another primary if primary, etc + if ( Weapon_info[wi_index].subtype != Weapon_info[wep_pool_index].subtype ) { + continue; + } + + if ( !weapon_allowed_for_game_type(Ship_info[ship_class].allowed_weapons[wep_pool_index]) ) { + continue; + } + + + for ( wep_precedence_index = 0; wep_precedence_index < Num_player_weapon_precedence; wep_precedence_index++ ) { + if ( wep_pool_index == Player_weapon_precedence[wep_precedence_index] ) { + new_wi_index = wep_pool_index; + break; + } + } + + if ( new_wi_index >= 0 ) { + break; + } + } + + if ( new_wi_index >= 0 ) { + wep[i] = new_wi_index; + wi_index = new_wi_index; + } + } + + int new_wep_count = wep_count[i]; + if ( Weapon_info[wi_index].subtype == WP_MISSILE ) { + int secondary_bank_index; + secondary_bank_index = i-3; + if ( secondary_bank_index < 0 ) { + Int3(); + secondary_bank_index = 0; + } + new_wep_count = wl_calc_missile_fit(wi_index, Ship_info[ship_class].secondary_bank_ammo_capacity[secondary_bank_index]); + } + + wep_count[i] = min(new_wep_count, Wl_pool[wi_index]); + Assert(wep_count[i] >= 0); + Wl_pool[wi_index] -= wep_count[i]; + if ( wep_count[i] <= 0 ) { + wep[i] = -1; + } + } + } + } + } +} + +// Init the weapons portion of Wss_slots[] and the ui data in Wl_slots[] +// NOTE: it is assumed that Wl_pool[] has been initialized, and Wss_slots[].ship_class is correctly set +void wl_fill_slots() +{ + int i, j; + int wep[MAX_WL_WEAPONS]; + int wep_count[MAX_WL_WEAPONS]; + + for ( i = 0; i < MAX_WSS_SLOTS; i++ ) { + if ( Wss_slots[i].ship_class < 0 ){ + continue; + } + + // get the weapons info that should be on ship by default + wl_get_default_weapons(Wss_slots[i].ship_class, i, wep, wep_count); + wl_remove_weps_from_pool(wep, wep_count, Wss_slots[i].ship_class); + + // copy to Wss_slots[] + for ( j = 0; j < MAX_WL_WEAPONS; j++ ) { + Wss_slots[i].wep[j] = wep[j]; + Wss_slots[i].wep_count[j] = wep_count[j]; + } + } +} + +// set up the primary and secondary icons lists that hold the weapons the player can choose from +void wl_init_icon_lists() +{ + int i; + + Plist_start = 0; // offset into Plist[] + Slist_start = 0; + + Plist_size = 0; // number of active elements in Plist[] + Slist_size = 0; + + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + Plist[i] = -1; + Slist[i] = -1; + } + + for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) { + if ( Wl_pool[i] > 0 ) { + if ( Weapon_info[i].subtype == WP_MISSILE ) { + Slist[Slist_size++] = i; + } else { + Plist[Plist_size++] = i; + } + } + } +} + +// initialize team specific weapon select data structures +void weapon_select_init_team(int team_num) +{ + Wl_icons = Wl_icons_teams[team_num]; + ss_set_team_pointers(team_num); + + wl_init_pool(&Team_data[team_num]); + wl_init_icon_lists(); + wl_init_ship_class_data(); + + wl_load_all_icons(); + wl_load_all_anims(); + + wl_fill_slots(); +} + +// This init is called even before the weapons loadout screen is entered. It is called when the +// briefing state is entered. +void weapon_select_common_init() +{ + int idx; + + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){ + // initialize for all teams + for(idx=0;idxstate = NETPLAYER_STATE_WEAPON_SELECT; + + ship_stop_animation(); + stop_weapon_animation(); + Weapon_anim_class = -1; + + set_active_ui(&Weapon_ui_window); + Current_screen = ON_WEAPON_SELECT; + Last_wl_ship_class = -1; + + wl_maybe_reset_selected_slot(); + wl_set_disabled_weapons(Wss_slots[Selected_wl_slot].ship_class); + + help_overlay_set_state(WL_OVERLAY,0); + + if ( Weapon_select_open ) { + wl_maybe_reset_selected_weapon_class(); + wl_start_slot_animation(Selected_wl_slot); + common_buttons_maybe_reload(&Weapon_ui_window); // AL 11-21-97: this is necessary since we may returning from the hotkey + // screen, which can release common button bitmaps. + common_reset_buttons(); + nprintf(("Alan","weapon_select_init() returning without doing anything\n")); + + // if we're in multiplayer always select the player's ship + wl_reset_selected_slot(); + + return; + } + + nprintf(("Alan","entering weapon_select_init()\n")); + common_select_init(); + + WeaponSelectMaskBitmap = bm_load(Wl_loadout_select_mask[gr_screen.res]); + if (WeaponSelectMaskBitmap < 0) { + if (gr_screen.res == GR_640) { + Error(LOCATION,"Could not load in 'weaponloadout-m'!"); + } else if (gr_screen.res == GR_1024) { + Error(LOCATION,"Could not load in '2_weaponloadout-m'!"); + } else { + Error(LOCATION,"Could not load in 'weaponloadout-m'!"); + } + } + + Weaponselect_mask_w = -1; + Weaponselect_mask_h = -1; + + // get a pointer to bitmap by using bm_lock() + WeaponSelectMaskPtr = bm_lock(WeaponSelectMaskBitmap, 8, BMP_AABITMAP); + WeaponSelectMaskData = (ubyte*)WeaponSelectMaskPtr->data; + Assert(WeaponSelectMaskData != NULL); + bm_get_info(WeaponSelectMaskBitmap, &Weaponselect_mask_w, &Weaponselect_mask_h); + + + // Set up the mask regions + // initialize the different regions of the menu that will react when the mouse moves over it + Num_weapon_select_regions = 0; + + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", COMMON_BRIEFING_REGION, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", COMMON_SS_REGION, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", COMMON_WEAPON_REGION, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", COMMON_COMMIT_REGION, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", COMMON_HELP_REGION, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", COMMON_OPTIONS_REGION, 0); + + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_0_SHIP_0, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_0_SHIP_1, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_0_SHIP_2, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_0_SHIP_3, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_1_SHIP_0, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_1_SHIP_1, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_1_SHIP_2, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_1_SHIP_3, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_2_SHIP_0, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_2_SHIP_1, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_2_SHIP_2, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", WING_2_SHIP_3, 0); + + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_PRIMARY_0, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_PRIMARY_1, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_PRIMARY_2, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_PRIMARY_3, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SECONDARY_0, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SECONDARY_1, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SECONDARY_2, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SECONDARY_3, 0); + + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SHIP_PRIMARY_0, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SHIP_PRIMARY_1, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SHIP_PRIMARY_2, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SHIP_SECONDARY_0, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SHIP_SECONDARY_1, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SHIP_SECONDARY_2, 0); + snazzy_menu_add_region(&Weapon_select_region[Num_weapon_select_regions++], "", ICON_SHIP_SECONDARY_3, 0); + + // init common UI + Weapon_ui_window.create( 0, 0, gr_screen.max_w, gr_screen.max_h, 0 ); + + if(Game_mode & GM_MULTIPLAYER){ + Weapon_ui_window.set_mask_bmap(Wl_mask_multi[gr_screen.res]); + } else { + Weapon_ui_window.set_mask_bmap(Wl_mask_single[gr_screen.res]); + } + + // initialize background bitmap + if(Game_mode & GM_MULTIPLAYER) { + Weapon_select_background_bitmap = bm_load(Weapon_select_multi_background_fname[gr_screen.res]); + } else { + Weapon_select_background_bitmap = bm_load(Weapon_select_background_fname[gr_screen.res]); + } + + Weapon_ui_window.tooltip_handler = wl_tooltip_handler; + common_buttons_init(&Weapon_ui_window); + weapon_buttons_init(); + Weapon_select_open = 1; + + // if we're in multiplayer always select the player's ship + wl_reset_selected_slot(); +} + +// ---------------------------------------------------------------- +// wl_dump_carried_icon() +void wl_dump_carried_icon() +{ + if ( wl_icon_being_carried() ) { + // Add back into the weapon pool + if ( Carried_wl_icon.from_bank >= 0 ) { + // return to list + wl_drop(Carried_wl_icon.from_bank, -1, -1, Carried_wl_icon.weapon_class, Carried_wl_icon.from_slot); + } else { + if ( wl_carried_icon_moved() ) { + gamesnd_play_iface(SND_ICON_DROP); + } + } + + wl_reset_carried_icon(); + } +} + +// ---------------------------------------------------------------- +// drop_icon_on_slot() +// +// Drop the Carried_wl_icon onto the specified slot. The slot numbering is: +// +// 0->2: primary weapons +// 3-6: secondary weapons +// +// These are the slots that exist beside the overhead view of the ship. +// on the weapons loadout screen. +// +int drop_icon_on_slot(int bank_num) +{ + if ( Selected_wl_slot == -1 ) { + return 0; + } + + if(Game_mode & GM_MULTIPLAYER){ + if(multi_ts_disabled_slot(Selected_wl_slot)){ + return 0; + } + } else { + if ( ss_disabled_slot( Selected_wl_slot ) ){ + return 0; + } + } + + // check if slot exists + if ( Wss_slots[Selected_wl_slot].wep_count[bank_num] < 0 ) { + return 0; + } + + if ( !wl_carried_icon_moved() ) { + wl_reset_carried_icon(); + return 0; + } + + wl_drop(Carried_wl_icon.from_bank, Carried_wl_icon.weapon_class, bank_num, -1, Selected_wl_slot); + return 1; +} + +// ---------------------------------------------------------------- +// maybe_drop_icon_on_slot() +// +void maybe_drop_icon_on_slot(int bank_num) +{ + int dropped=0; + if ( !mouse_down(MOUSE_LEFT_BUTTON) ) { + if ( wl_icon_being_carried() ) + dropped = drop_icon_on_slot(bank_num); + + if ( dropped ) { + wl_reset_carried_icon(); + } + } +} + +// ---------------------------------------------------------------- +// wl_check_for_stopped_ship_anims() +// +void wl_check_for_stopped_ship_anims() +{ + int i; + anim_instance *ai; + for ( i = 0; i < MAX_SHIP_TYPES; i++ ) { + ai = Wl_ships[i].anim_instance; + if ( ai != NULL ) { + if ( !anim_playing(ai) ) { + Wl_ships[i].anim_instance = NULL; + } + } + } +} + +// --------------------------------------------------------------------------------- +// do_mouse_over_list_weapon() +// +void do_mouse_over_list_weapon(int index) +{ + Hot_weapon_icon = index; + + int region_index = ICON_PRIMARY_0+index; + if ( index > 3 ) { + region_index = ICON_SECONDARY_0 + (index - 4); + } + + if ( Wl_mouse_down_on_region != region_index ){ + return; + } + + if ( mouse_down(MOUSE_LEFT_BUTTON) ) + wl_pick_icon_from_list(index); +} + +// --------------------------------------------------------------------------------- +// do_mouse_over_ship_weapon() +// +// +// input: index -> bank index on ship (0..6) +// +// returns: +// 0 -> icon was not dropped on a slot +// 1 -> icon was dropped on a slot +int do_mouse_over_ship_weapon(int index) +{ + int dropped_on_slot, is_moved, mx, my; + + dropped_on_slot = 0; + Assert(Selected_wl_slot >= 0); + + if ( ss_disabled_slot( Selected_wl_slot ) ) + return 0; + + Hot_weapon_bank_icon = index; // bank icon will be drawn highlighted + + if ( mouse_down(MOUSE_LEFT_BUTTON) ) { + if ( Wl_mouse_down_on_region == (ICON_SHIP_PRIMARY_0+index) ){ + pick_from_ship_slot(index); + } + } else { + int was_carried = wl_icon_being_carried(); + maybe_drop_icon_on_slot(index); + if ( was_carried && !wl_icon_being_carried() ) { + mouse_get_pos( &mx, &my ); + if ( Carried_wl_icon.from_x != mx || Carried_wl_icon.from_y != my) { + dropped_on_slot = 1; + } + } + } + + // set Hot_weapon_bank if a droppable icon is being held over a slot that + // can accept that icon + is_moved = 0; + mouse_get_pos( &mx, &my ); + if ( Carried_wl_icon.from_x != mx || Carried_wl_icon.from_y != my) { + is_moved = 1; + } + + if ( wl_icon_being_carried() && is_moved ) { + if ( Weapon_info[Carried_wl_icon.weapon_class].subtype != WP_MISSILE ) { + if ( (index < 3) && (Wss_slots[Selected_wl_slot].wep_count[index] >= 0) ) + Hot_weapon_bank = index; + } else { + if ( index >= 3 && ( Wss_slots[Selected_wl_slot].wep_count[index] >= 0) ) + Hot_weapon_bank = index; + } + } + + return dropped_on_slot; +} + + +// maybe flash a button if player hasn't done anything for a while +void wl_maybe_flash_button() +{ + if ( common_flash_bright() ) { + // commit button + if ( Common_buttons[Current_screen-1][gr_screen.res][3].button.button_hilighted() ) { + common_flash_button_init(); + } else { + Common_buttons[Current_screen-1][gr_screen.res][3].button.draw_forced(1); + } + } +} + + +void weapon_select_render(float frametime) +{ + if ( !Background_playing ) { + gr_set_bitmap(Weapon_select_background_bitmap); + gr_bitmap(0, 0); + } + + anim_render_all(0, frametime); + anim_render_all(ON_SHIP_SELECT, frametime); +} + +// draw the weapon description text +// this wipes in +void wl_render_weapon_desc(float frametime) +{ + int *weapon_desc_coords; + int *weapon_title_coords; + + // retrieve the correct set of text coordinates + if (Game_mode & GM_MULTIPLAYER) { + weapon_desc_coords = Wl_new_weapon_desc_coords_multi[gr_screen.res]; + weapon_title_coords = Wl_new_weapon_title_coords_multi[gr_screen.res]; + } else { + weapon_desc_coords = Wl_new_weapon_desc_coords[gr_screen.res]; + weapon_title_coords = Wl_new_weapon_title_coords[gr_screen.res]; + } + + // render the normal version of the weapom desc + char bright_char[WEAPON_DESC_MAX_LINES]; // one bright char per line + if (!Weapon_desc_wipe_done) { + // draw mid-wipe version + // decide which char is last (and bright) + int bright_char_index = (int)(Weapon_desc_wipe_time_elapsed * WEAPON_DESC_MAX_LENGTH / WEAPON_DESC_WIPE_TIME); + int i, w, h, curr_len; + + // draw weapon title (above weapon anim) + for (i=0; i<2; i++) { + curr_len = strlen(Weapon_desc_lines[i]); + + if (bright_char_index < curr_len) { + // save bright char and plunk in some nulls to shorten string + bright_char[i] = Weapon_desc_lines[i][bright_char_index]; + Weapon_desc_lines[i][bright_char_index] = '\0'; + + // draw the strings + gr_set_color_fast(&Color_white); + gr_string(weapon_title_coords[0], weapon_title_coords[1]+(10*i), Weapon_desc_lines[i]); + + // draw the bright letters + gr_set_color_fast(&Color_bright_white); + gr_get_string_size(&w, &h, Weapon_desc_lines[i], curr_len); + gr_printf(weapon_title_coords[0]+w, weapon_title_coords[1]+(10*i), "%c", bright_char[i]); + + // restore the bright char to the string + Weapon_desc_lines[i][bright_char_index] = bright_char[i]; + + } else { + // draw the string + gr_set_color_fast(&Color_white); + gr_string(weapon_title_coords[0], weapon_title_coords[1]+(10*i), Weapon_desc_lines[i]); + } + } + + // draw weapon desc (below weapon anim) + for (i=2; i= WEAPON_DESC_WIPE_TIME) { + // wipe is done,set flag and stop sound + Weapon_desc_wipe_done = 1; + } + + } else { + + // draw full version + // FIXME - change to use a for loop + gr_set_color_fast(&Color_white); + gr_string(weapon_title_coords[0], weapon_title_coords[1], Weapon_desc_lines[0]); + gr_string(weapon_title_coords[0], weapon_title_coords[1] + 10, Weapon_desc_lines[1]); + gr_string(weapon_desc_coords[0], weapon_desc_coords[1], Weapon_desc_lines[2]); + gr_string(weapon_desc_coords[0], weapon_desc_coords[1] + 10, Weapon_desc_lines[3]); + gr_string(weapon_desc_coords[0], weapon_desc_coords[1] + 20, Weapon_desc_lines[4]); + gr_string(weapon_desc_coords[0], weapon_desc_coords[1] + 30, Weapon_desc_lines[5]); + } +} + + + +// re-inits wiping vars and causes the current text to wipe in again +void wl_weapon_desc_start_wipe() +{ + int currchar_src = 0, currline_dest = 2, currchar_dest = 0, i; + int w, h; + int title_len = strlen(Weapon_info[Selected_wl_class].title); + + // init wipe vars + Weapon_desc_wipe_time_elapsed = 0.0f; + Weapon_desc_wipe_done = 0; + + // break title into two lines if too long + strcpy(Weapon_desc_lines[0], Weapon_info[Selected_wl_class].title); + gr_get_string_size(&w, &h, Weapon_info[Selected_wl_class].title, title_len); + if (w > Weapon_title_max_width[gr_screen.res]) { + // split + currchar_src = (int)(((float)title_len / (float)w) * Weapon_title_max_width[gr_screen.res]); // char to start space search at + while (Weapon_desc_lines[0][currchar_src] != ' ') { + currchar_src--; + if (currchar_src <= 0) { + currchar_src = title_len; + break; + } + } + + Weapon_desc_lines[0][currchar_src] = '\0'; // shorten line 0 + strcpy(Weapon_desc_lines[1], &(Weapon_desc_lines[0][currchar_src+1])); // copy remainder into line 1 + } else { + // entire title in line 0, thus line 1 is empty + Weapon_desc_lines[1][0] = '\0'; + } + + // break current description into lines (break at the /n's) + currchar_src = 0; + while (Weapon_info[Selected_wl_class].desc[currchar_src] != '\0') { + if (Weapon_info[Selected_wl_class].desc[currchar_src] == '\n') { + // break here + if (currchar_src != 0) { // protect against leading /n's + Weapon_desc_lines[currline_dest][currchar_dest] = '\0'; + currline_dest++; + currchar_dest = 0; + } + } else { + // straight copy + Weapon_desc_lines[currline_dest][currchar_dest] = Weapon_info[Selected_wl_class].desc[currchar_src]; + currchar_dest++; + } + + currchar_src++; + + Assert(currline_dest < WEAPON_DESC_MAX_LINES); + Assert(currchar_dest < WEAPON_DESC_MAX_LENGTH); + } + + // wrap up the line processing + Weapon_desc_lines[currline_dest][currchar_dest] = '\0'; + for (i=currline_dest+1; i= 0 ) { + if ( snazzy_action == SNAZZY_CLICKED ) { + nprintf(("Alan","got one\n")); + } + } + + Hot_wl_slot = -1; + Hot_weapon_icon = -1; + Hot_weapon_bank = -1; + Hot_weapon_bank_icon = -1; + + k = common_select_do(frametime); + + if ( help_overlay_active(WL_OVERLAY) ) { + if ( Weapon_anim_class >= 0 && Wl_icons[Weapon_anim_class].anim_instance ) { + anim_pause(Wl_icons[Weapon_anim_class].anim_instance); + } + } + else { + if ( Weapon_anim_class >= 0 && Wl_icons[Weapon_anim_class].anim_instance ) { + anim_unpause(Wl_icons[Weapon_anim_class].anim_instance); + } + } + + // Check common keypresses + common_check_keys(k); + + if ( Mouse_down_last_frame ) { + Wl_mouse_down_on_region = wl_choice; + } + + if ( wl_choice > -1 ) { + switch(wl_choice) { + case ICON_PRIMARY_0: + do_mouse_over_list_weapon(0); + break; + case ICON_PRIMARY_1: + do_mouse_over_list_weapon(1); + break; + case ICON_PRIMARY_2: + do_mouse_over_list_weapon(2); + break; + case ICON_PRIMARY_3: + do_mouse_over_list_weapon(3); + break; + case ICON_SECONDARY_0: + do_mouse_over_list_weapon(4); + break; + case ICON_SECONDARY_1: + do_mouse_over_list_weapon(5); + break; + case ICON_SECONDARY_2: + do_mouse_over_list_weapon(6); + break; + case ICON_SECONDARY_3: + do_mouse_over_list_weapon(7); + break; + case ICON_SHIP_PRIMARY_0: + if ( do_mouse_over_ship_weapon(0) ) + wl_choice = -1; + break; + case ICON_SHIP_PRIMARY_1: + if ( do_mouse_over_ship_weapon(1) ) + wl_choice = -1; + break; + case ICON_SHIP_PRIMARY_2: + if ( do_mouse_over_ship_weapon(2) ) + wl_choice = -1; + break; + case ICON_SHIP_SECONDARY_0: + if ( do_mouse_over_ship_weapon(3) ) + wl_choice = -1; + break; + case ICON_SHIP_SECONDARY_1: + if ( do_mouse_over_ship_weapon(4) ) + wl_choice = -1; + break; + case ICON_SHIP_SECONDARY_2: + if ( do_mouse_over_ship_weapon(5) ) + wl_choice = -1; + break; + case ICON_SHIP_SECONDARY_3: + if ( do_mouse_over_ship_weapon(6) ) + wl_choice = -1; + break; + case WING_0_SHIP_0: + Hot_wl_slot = 0; + break; + case WING_0_SHIP_1: + Hot_wl_slot = 1; + break; + case WING_0_SHIP_2: + Hot_wl_slot = 2; + break; + case WING_0_SHIP_3: + Hot_wl_slot = 3; + break; + case WING_1_SHIP_0: + Hot_wl_slot = 4; + break; + case WING_1_SHIP_1: + Hot_wl_slot = 5; + break; + case WING_1_SHIP_2: + Hot_wl_slot = 6; + break; + case WING_1_SHIP_3: + Hot_wl_slot = 7; + break; + case WING_2_SHIP_0: + Hot_wl_slot = 8; + break; + case WING_2_SHIP_1: + Hot_wl_slot = 9; + break; + case WING_2_SHIP_2: + Hot_wl_slot = 10; + break; + case WING_2_SHIP_3: + Hot_wl_slot = 11; + break; + + default: + break; + } // end switch + } + + if ( !mouse_down(MOUSE_LEFT_BUTTON) ) { + wl_dump_carried_icon(); + } + + // Check for a mouse click on buttons + common_check_buttons(); + weapon_check_buttons(); + + // Check for the mouse clicks over a region + if ( wl_choice > -1 && snazzy_action == SNAZZY_CLICKED ) { + switch (wl_choice) { + case ICON_PRIMARY_0: + maybe_select_new_weapon(0); + break; + case ICON_PRIMARY_1: + maybe_select_new_weapon(1); + break; + case ICON_PRIMARY_2: + maybe_select_new_weapon(2); + break; + case ICON_PRIMARY_3: + maybe_select_new_weapon(3); + break; + case ICON_SECONDARY_0: + maybe_select_new_weapon(4); + break; + case ICON_SECONDARY_1: + maybe_select_new_weapon(5); + break; + case ICON_SECONDARY_2: + maybe_select_new_weapon(6); + break; + case ICON_SECONDARY_3: + maybe_select_new_weapon(7); + break; + case ICON_SHIP_PRIMARY_0: + maybe_select_new_ship_weapon(0); + break; + case ICON_SHIP_PRIMARY_1: + maybe_select_new_ship_weapon(1); + break; + case ICON_SHIP_PRIMARY_2: + maybe_select_new_ship_weapon(2); + break; + case ICON_SHIP_SECONDARY_0: + maybe_select_new_ship_weapon(3); + break; + case ICON_SHIP_SECONDARY_1: + maybe_select_new_ship_weapon(4); + break; + case ICON_SHIP_SECONDARY_2: + maybe_select_new_ship_weapon(5); + break; + case ICON_SHIP_SECONDARY_3: + maybe_select_new_ship_weapon(6); + break; + case WING_0_SHIP_0: + maybe_select_wl_slot(0,0); + break; + case WING_0_SHIP_1: + maybe_select_wl_slot(0,1); + break; + case WING_0_SHIP_2: + maybe_select_wl_slot(0,2); + break; + case WING_0_SHIP_3: + maybe_select_wl_slot(0,3); + break; + case WING_1_SHIP_0: + maybe_select_wl_slot(1,0); + break; + case WING_1_SHIP_1: + maybe_select_wl_slot(1,1); + break; + case WING_1_SHIP_2: + maybe_select_wl_slot(1,2); + break; + case WING_1_SHIP_3: + maybe_select_wl_slot(1,3); + break; + case WING_2_SHIP_0: + maybe_select_wl_slot(2,0); + break; + case WING_2_SHIP_1: + maybe_select_wl_slot(2,1); + break; + case WING_2_SHIP_2: + maybe_select_wl_slot(2,2); + break; + case WING_2_SHIP_3: + maybe_select_wl_slot(2,3); + break; + + default: + break; + + } // end switch + } + + gr_reset_clip(); + + if ( Weapon_anim_class != -1 && ( Selected_wl_class == Weapon_anim_class ) ) { + wl_icon_info *icon; + Assert(Selected_wl_class >= 0 && Selected_wl_class < MAX_WEAPON_TYPES ); + if ( Weapon_anim_class != Selected_wl_class ) + start_weapon_animation(Selected_wl_class); + + Assert(Weapon_anim_class == Selected_wl_class); + icon = &Wl_icons[Selected_wl_class]; + if ( icon->anim_instance ) { + if ( icon->anim_instance->frame_num == icon->anim_instance->stop_at ) { + anim_play_struct aps; + int *weapon_ani_coords; + + // get the correct weapon animations coords + if (Game_mode & GM_MULTIPLAYER) { + weapon_ani_coords = Wl_weapon_ani_coords_multi[gr_screen.res]; + } else { + weapon_ani_coords = Wl_weapon_ani_coords[gr_screen.res]; + } + + anim_release_render_instance(icon->anim_instance); + anim_play_init(&aps, icon->anim, weapon_ani_coords[0], weapon_ani_coords[1]); + aps.start_at = WEAPON_ANIM_LOOP_FRAME-1; + aps.screen_id = ON_WEAPON_SELECT; + aps.framerate_independent = 1; + aps.skip_frames = 0; + icon->anim_instance = anim_play(&aps); + } + } + } + + weapon_select_render(frametime); + if ( !Background_playing ) { + Weapon_ui_window.draw(); + wl_redraw_pressed_buttons(); + draw_wl_icons(); + anim_render_all(ON_WEAPON_SELECT, frametime); + wl_check_for_stopped_ship_anims(); + wl_render_overhead_view(frametime); + wl_draw_ship_weapons(Selected_wl_slot); + for ( int i = 0; i < MAX_WING_BLOCKS; i++ ) { + draw_wing_block(i, Hot_wl_slot, Selected_wl_slot, -1); + } + common_render_selected_screen_button(); + } + + // maybe blit the multiplayer "locked" button + if((Game_mode & GM_MULTIPLAYER) && multi_ts_is_locked()){ + Buttons[gr_screen.res][WL_BUTTON_MULTI_LOCK].button.draw_forced(2); + } + + + if ( wl_icon_being_carried() ) { + int mx, my, sx, sy; + Assert(Carried_wl_icon.weapon_class < MAX_WEAPON_TYPES); + mouse_get_pos( &mx, &my ); + sx = mx + Wl_delta_x; + sy = my + Wl_delta_y; + + + if ( Wl_icons[Carried_wl_icon.weapon_class].can_use > 0) { + gr_set_color_fast(&Color_blue); + gr_set_bitmap(Wl_icons[Carried_wl_icon.weapon_class].icon_bmaps[WEAPON_ICON_FRAME_SELECTED]); + gr_bitmap(sx, sy); + } + + // draw number to prevent it from disappearing on clicks + if ( Carried_wl_icon.from_bank >= MAX_WL_PRIMARY ) { + if ( mx == Carried_wl_icon.from_x && my == Carried_wl_icon.from_y ) { + int num_missiles = Wss_slots[Carried_wl_icon.from_slot].wep_count[Carried_wl_icon.from_bank]; + //sprintf(buf, "%d", num_missiles); + //gr_set_color_fast(&Color_white); + + //int x_offset = wl_fury_missile_offset_hack(Carried_wl_icon.weapon_class, num_missiles); + //gr_string(sx-19-x_offset, sy+8, buf); + + + wl_render_icon_count(num_missiles, Wl_bank_coords[gr_screen.res][Carried_wl_icon.from_bank][0], Wl_bank_coords[gr_screen.res][Carried_wl_icon.from_bank][1]); + } + } + + // check so see if this is really a legal weapon to carry away + if ( !Wl_icons[Carried_wl_icon.weapon_class].can_use ) { + int diffx, diffy; + diffx = abs(Carried_wl_icon.from_x-mx); + diffy = abs(Carried_wl_icon.from_y-my); + if ( (diffx > 2) || (diffy > 2) ) { + int ship_class = Wss_slots[Selected_wl_slot].ship_class; + wl_pause_anim(); + if (Lcl_gr) { + // might have to get weapon name translation + char display_name[128]; + strncpy(display_name, Weapon_info[Carried_wl_icon.weapon_class].name, 128); + lcl_translate_wep_name(display_name); + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "A %s is unable to carry %s weaponry", 633), Ship_info[ship_class].name, display_name); + } else { + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, XSTR( "A %s is unable to carry %s weaponry", 633), Ship_info[ship_class].name, Weapon_info[Carried_wl_icon.weapon_class].name); + } + wl_unpause_anim(); + wl_dump_carried_icon(); + } + } + } + + if ( Weapon_anim_class != Selected_wl_class ) { + start_weapon_animation(Selected_wl_class); + } + + // render weapon description text + wl_render_weapon_desc(frametime); + + wl_maybe_flash_button(); + + // should render the chatbox as close to the end as possible so it overlaps all controls + if(!Background_playing){ + + // render some extra stuff in multiplayer + if(Game_mode & GM_MULTIPLAYER){ + // render the chatbox + chatbox_render(); + + // draw tooltips + Weapon_ui_window.draw_tooltip(); + + // render the status indicator for the voice system + multi_common_voice_display_status(); + + // blit the "ships/players" locked button + // multi_ts_blit_locked_button(); + } + } + + // blit help overlay if active + help_overlay_maybe_blit(WL_OVERLAY); + gr_flip(); + + // If the commit button was pressed, do the commit button actions. Done at the end of the + // loop so there isn't a skip in the animation (since ship_create() can take a long time if + // the ship model is not in memory + if ( Commit_pressed ) { + if(Game_mode & GM_MULTIPLAYER){ + multi_ts_commit_pressed(); + } else { + commit_pressed(); + } + Commit_pressed = 0; + } +} + +// ------------------------------------------------------------------------------- +// weapon_select_close() will free the bitmap slot and memory that was allocated +// to store the mask bitmap. +// +// Weapon_select_open is cleared when this function completes. +// +void weapon_select_close() +{ + if ( !Weapon_select_open ) { + nprintf(("Alan","weapon_select_close() returning without doing anything\n")); + return; + } + + nprintf(("Alan", "Entering weapon_select_close()\n")); + + stop_weapon_animation(); + + // done with the bitmaps, so unlock it + bm_unlock(WeaponSelectMaskBitmap); + + Weapon_ui_window.destroy(); + + // unload bitmaps + help_overlay_unload(WL_OVERLAY); + + bm_unload(WeaponSelectMaskBitmap); + + wl_unload_icons(); + wl_unload_all_anim_instances(); + wl_unload_all_anims(); + + Selected_wl_class = -1; + Weapon_select_open = 0; +} + + +// ------------------------------------------------------------------------ +// wl_render_icon_count() +// renders the number next to the weapon icon +// +// input: x,y => x,y screen position OF THE ICON (NOT where u want the text, +// this is calculated to prevent overlapping) +// num => the actual count to be printed +// +void wl_render_icon_count(int num, int x, int y) +{ + char buf[32]; + int num_w, num_h; + int number_to_draw = (num > 1000) ? 999 : num; // cap count @ 999 + Assert(number_to_draw >= 0); + + sprintf(buf, "%d", number_to_draw); + gr_get_string_size(&num_w, &num_h, buf, strlen(buf)); + + // render + gr_set_color_fast(&Color_white); + gr_string(x-num_w-4, y+8, buf); +} + + +// ------------------------------------------------------------------------ +// wl_render_icon() +// +// input: index => index into Wl_icons[], identifying which weapon to draw +// x,y => x,y screen position to draw icon at +// num => count for weapon +// draw_num_flag => 0 if not to draw count for weapon, nonzero otherwise +// hot_mask => value that should match Hot_weapon_icon to show mouse is over +// hot_bank_mask => value that should match Hot_weapon_bank_icon to show mouse is over +// select_mask => value that should match Selected_wl_class to show icon is selected +// +void wl_render_icon(int index, int x, int y, int num, int draw_num_flag, int hot_mask, int hot_bank_mask, int select_mask) +{ + int bitmap_id; + wl_icon_info *icon; + + if ( Selected_wl_slot == -1 ) + return; + + icon = &Wl_icons[index]; + + if ( icon->icon_bmaps[0] == -1 ) { + wl_load_icons(index); + } + + // assume default bitmap is to be used + bitmap_id = icon->icon_bmaps[WEAPON_ICON_FRAME_NORMAL]; // normal frame + + if ( bitmap_id < 0 ) { + Int3(); + return; + } + + // next check if ship has mouse over it + if ( !wl_icon_being_carried() ) { + if ( Hot_weapon_icon > -1 && Hot_weapon_icon == hot_mask ) + bitmap_id = icon->icon_bmaps[WEAPON_ICON_FRAME_HOT]; + + if ( Hot_weapon_bank_icon > -1 && Hot_weapon_bank_icon == hot_bank_mask ) + bitmap_id = icon->icon_bmaps[WEAPON_ICON_FRAME_HOT]; + } + + // if icon is selected + if ( Selected_wl_class > -1 ) { + if ( Selected_wl_class == select_mask) + bitmap_id = icon->icon_bmaps[WEAPON_ICON_FRAME_SELECTED]; // selected icon + } + + // if icon is disabled + if ( !icon->can_use ) { + bitmap_id = icon->icon_bmaps[WEAPON_ICON_FRAME_DISABLED]; // disabled icon + } + + gr_set_color_fast(&Color_blue); + gr_set_bitmap(bitmap_id); + gr_bitmap(x, y); + + // draw the number of the item + // now, right justified + if ( draw_num_flag != 0 ) { + wl_render_icon_count(num, x, y); + } +} + +// ------------------------------------------------------------------------ +// wl_draw_ship_weapons() +// +// Draw the icons for the weapons that are currently on the selected ship +// +// input: slot_num => Slot to draw weapons for +// +void wl_draw_ship_weapons(int index) +{ + int i; + int *wep, *wep_count; + + if ( index == -1 ) + return; + + Assert(index >= 0 && index < MAX_WSS_SLOTS); + wep = Wss_slots[index].wep; + wep_count = Wss_slots[index].wep_count; + + for ( i = 0; i < MAX_WL_WEAPONS; i++ ) { + + if ( Carried_wl_icon.from_bank == i && Carried_wl_icon.from_slot == index ) { + continue; + } + + if ( (wep[i] != -1) && (wep_count[i] > 0) ) { + int x_offset = wl_fury_missile_offset_hack(wep[i], wep_count[i]); + x_offset =0; + wl_render_icon( wep[i], Wl_bank_coords[gr_screen.res][i][0]+x_offset, Wl_bank_coords[gr_screen.res][i][1], wep_count[i], Wl_bank_count_draw_flags[i], -1, i, wep[i]); + } + } +} + +// ------------------------------------------------------------------------ +// draw_wl_icon_with_number() +// +// input: list_count => list position on screen (0-7) +// weapon_class => class of weapon +// +void draw_wl_icon_with_number(int list_count, int weapon_class) +{ + Assert( list_count >= 0 && list_count < 8 ); + + wl_render_icon(weapon_class, Wl_weapon_icon_coords[gr_screen.res][list_count][0], Wl_weapon_icon_coords[gr_screen.res][list_count][1], + Wl_pool[weapon_class], 1, list_count, -1, weapon_class); +} + +// ------------------------------------------------------------------------ +// draw_wl_icons() +// +// Draw the weapon icons that are available +void draw_wl_icons() +{ + int i, count; + + count=0; + for ( i = Plist_start; i < Plist_size; i++ ) { + draw_wl_icon_with_number(count, Plist[i]); + if ( ++count > 3 ) + break; + } + + count=0; + for ( i = Slist_start; i < Slist_size; i++ ) { + draw_wl_icon_with_number(count+4, Slist[i]); + if ( ++count > 3 ) + break; + } +} + +// ------------------------------------------------------------------------ +// wl_pick_icon_from_list() +// +// determine if an icon from the scrollable weapon list can be picked up +// (for drag and drop). It calculates the difference in x & y between the icon +// and the mouse, so we can move the icon with the mouse in a realistic way +// input: index (0..7) +void wl_pick_icon_from_list(int index) +{ + int weapon_class, mx, my; + + // if this is a multiplayer game and the player is an observer, he can never pick any weapons up + if((Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){ + return; + } + + // if a weapon is being carried, do nothing + if ( wl_icon_being_carried() ) { + return; + } + + if ( index < 4 ) { + weapon_class = Plist[Plist_start+index]; + } else { + weapon_class = Slist[Slist_start+index-4]; + } + + // there isn't a weapon there at all! + if ( weapon_class < 0 ) + return; + + // no weapons left of that class + if ( Wl_pool[weapon_class] <= 0 ) { + return; + } + +/* + // some are available, but weapon cannot be used on current ship class + if ( !Wl_icons[weapon_class].can_use ) { + wl_pause_anim(); + + int ship_class = Wss_slots[Selected_wl_slot].ship_class; + popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "A %s is unable to carry %s weaponry", Ship_info[ship_class].name, Weapon_info[weapon_class].name); + + wl_unpause_anim(); + return; + } +*/ + + wl_set_carried_icon(-1, -1, weapon_class); + common_flash_button_init(); + + mouse_get_pos( &mx, &my ); + Wl_delta_x = Wl_weapon_icon_coords[gr_screen.res][index][0] - mx; + Wl_delta_y = Wl_weapon_icon_coords[gr_screen.res][index][1] - my; +} + +// ------------------------------------------------------------------------ +// pick_from_ship_slot() +// +// input: num -> index into shipb banks (0..2 primary, 3..6 secondary) +void pick_from_ship_slot(int num) +{ + int mx, my, *wep, *wep_count; + + Assert(num < 7); + + if ( Selected_wl_slot == -1 ) + return; + + if ( wl_icon_being_carried() ) + return; + + if ( ss_disabled_slot( Selected_wl_slot ) ) + return; + + wep = Wss_slots[Selected_wl_slot].wep; + wep_count = Wss_slots[Selected_wl_slot].wep_count; + + // check if a weapon even exists in that slot + if ( (wep[num] < 0) || (wep_count[num] <= 0) ) { + return; + } + + Assert(Wl_icons[wep[num]].can_use); + + wl_set_carried_icon(num, Selected_wl_slot, wep[num]); + common_flash_button_init(); + + mouse_get_pos( &mx, &my ); + + int x_offset = wl_fury_missile_offset_hack(wep[num], wep_count[num]); + Wl_delta_x = Wl_bank_coords[gr_screen.res][num][0] - mx + x_offset; + Wl_delta_y = Wl_bank_coords[gr_screen.res][num][1] - my; + + Carried_wl_icon.from_x = mx; + Carried_wl_icon.from_y = my; +} + +// determine if this slot has no weapons +int wl_slots_all_empty(wss_unit *slot) +{ + int i; + + for ( i = 0; i < MAX_WL_WEAPONS; i++ ) { + if ( (slot->wep_count[i] > 0) && (slot->wep[i] >= 0) ) + return 0; + } + + return 1; +} + +// ------------------------------------------------------------------------ +// wl_update_ship_weapons() +// +// Change a ship's weapons based on the information contained in the +// Weapon_data[] structure that is filled in during weapon loadout +// +// returns: -1 => if the playre ship has no weapons +// 0 => function finished without errors +int wl_update_ship_weapons(int objnum, wss_unit *slot ) +{ + // AL 11-15-97: Ensure that the player ship hasn't removed all + // weapons from their ship. This will cause a warning to appear. + if ( objnum == OBJ_INDEX(Player_obj) && Weapon_select_open ) { + if ( wl_slots_all_empty(slot) ) { + return -1; + } + } + + wl_bash_ship_weapons(&Ships[Objects[objnum].instance].weapons, slot); + return 0; +} + + +// ------------------------------------------------------------------------ +// wl_update_parse_object_weapons() +// +// Set the Pilot subsystem of a parse_object to the weapons that are setup +// for the wing_block,wing_slot ship +// +// input: pobjp => pointer to parse object that references Pilot subsystem +// +void wl_update_parse_object_weapons(p_object *pobjp, wss_unit *slot) +{ + int i, j, sidx, pilot_index, max_count; + subsys_status *ss; + ship_info *sip; + + Assert(slot->ship_class >= 0); + sip = &Ship_info[slot->ship_class]; + + pilot_index = wl_get_pilot_subsys_index(pobjp); + + if ( pilot_index == -1 ) + return; + + ss = &Subsys_status[pilot_index]; + + for ( i = 0; i < MAX_PRIMARY_BANKS; i++ ) { + ss->primary_banks[i] = -1; + } + + for ( i = 0; i < MAX_SECONDARY_BANKS; i++ ) { + ss->secondary_banks[i] = -1; + } + + j = 0; + for ( i = 0; i < MAX_WL_PRIMARY; i++ ) { + if ( (slot->wep_count[i] > 0) && (slot->wep[i] >= 0) ) { + ss->primary_banks[j] = slot->wep[i]; + j++; + } + } + + j = 0; + for ( i = 0; i < MAX_WL_SECONDARY; i++ ) { + sidx = i+MAX_WL_PRIMARY; + if ( (slot->wep_count[sidx] > 0) && (slot->wep[sidx] >= 0) ) { + ss->secondary_banks[j] = slot->wep[sidx]; + // Important: the secondary_ammo[] value is a percentage of max capacity! + max_count = wl_calc_missile_fit(slot->wep[sidx], Ship_info[slot->ship_class].secondary_bank_ammo_capacity[j]); + ss->secondary_ammo[j] = fl2i( i2fl(slot->wep_count[sidx]) / max_count * 100.0f + 0.5f); + j++; + } + } +} + +// ------------------------------------------------------------------------ +// stop_weapon_animation() +// +// Stop the current weapon animation from playing. +// +void stop_weapon_animation() +{ + if ( Weapon_anim_class < 0 ) + return; + + if ( anim_playing(Wl_icons[Weapon_anim_class].anim_instance) ) + anim_release_render_instance(Wl_icons[Weapon_anim_class].anim_instance); + + Wl_icons[Weapon_anim_class].anim_instance = NULL; + Weapon_anim_class = -1; +} + +// ------------------------------------------------------------------------ +// start_weapon_animation() +// +// Start the current weapon animation from playing. +// +void start_weapon_animation(int weapon_class) +{ + wl_icon_info *icon; + int *weapon_ani_coords; + + if ( weapon_class < 0 ) + return; + + if ( weapon_class == Weapon_anim_class ) + return; + + // get the correct weapon animations coords + if (Game_mode & GM_MULTIPLAYER) { + weapon_ani_coords = Wl_weapon_ani_coords_multi[gr_screen.res]; + } else { + weapon_ani_coords = Wl_weapon_ani_coords[gr_screen.res]; + } + + icon = &Wl_icons[weapon_class]; + + // stop current animation playing + stop_weapon_animation(); + + // see if we need to load in the animation from disk + if ( icon->anim == NULL ) { + wl_load_anim(weapon_class); + /* + icon->anim = anim_load(Weapon_info[weapon_class].anim_filename, 1); + if ( icon->anim == NULL ) { + Int3(); // could not open the weapon animation + return; + } + */ + } + + // see if we need to get an instance + if ( icon->anim_instance == NULL ) { + anim_play_struct aps; + + anim_play_init(&aps, icon->anim, weapon_ani_coords[0], weapon_ani_coords[1]); + aps.screen_id = ON_WEAPON_SELECT; + aps.framerate_independent = 1; + aps.skip_frames = 0; + icon->anim_instance = anim_play(&aps); + gamesnd_play_iface(SND_WEAPON_ANIM_START); + } + + Weapon_anim_class = weapon_class; + + // start the text wipe + wl_weapon_desc_start_wipe(); +} + +// reset the weapons loadout to the defaults in the mission +void wl_reset_to_defaults() +{ + // don't reset of weapons pool in multiplayer + if(Game_mode & GM_MULTIPLAYER){ + return; + } + + wl_init_pool(&Team_data[Common_team]); + wl_init_icon_lists(); + wl_fill_slots(); + wl_reset_selected_slot(); + wl_reset_carried_icon(); + wl_maybe_reset_selected_weapon_class(); +} + +// Bash ship weapons, based on what is stored in the stored weapons loadout +// NOTE: Wss_slots[] is assumed to be correctly set +void wl_bash_ship_weapons(ship_weapon *swp, wss_unit *slot) +{ + int i, j, sidx; + + for ( i = 0; i < MAX_PRIMARY_BANKS; i++ ) { + swp->primary_bank_weapons[i] = -1; + } + + for ( i = 0; i < MAX_SECONDARY_BANKS; i++ ) { + swp->secondary_bank_weapons[i] = -1; + } + + j = 0; + for ( i = 0; i < MAX_WL_PRIMARY; i++ ) { + if ( (slot->wep_count[i] > 0) && (slot->wep[i] >= 0) ) { + swp->primary_bank_weapons[j] = slot->wep[i]; + j++; + } + } + swp->num_primary_banks = j; + + j = 0; + for ( i = 0; i < MAX_WL_SECONDARY; i++ ) { + sidx = i+MAX_WL_PRIMARY; + if ( (slot->wep_count[sidx] > 0) && (slot->wep[sidx] >= 0) ) { + swp->secondary_bank_weapons[j] = slot->wep[sidx]; + swp->secondary_bank_ammo[j] = slot->wep_count[sidx]; + j++; + } + } + swp->num_secondary_banks = j; +} + +// utility function for swapping two banks +void wl_swap_weapons(int ship_slot, int from_bank, int to_bank) +{ + wss_unit *slot; + int tmp; + slot = &Wss_slots[ship_slot]; + + if ( from_bank == to_bank ) { + return; + } + + // swap weapon type + tmp = slot->wep[from_bank]; + slot->wep[from_bank] = slot->wep[to_bank]; + slot->wep[to_bank] = tmp; + + // swap weapon count + tmp = slot->wep_count[from_bank]; + slot->wep_count[from_bank] = slot->wep_count[to_bank]; + slot->wep_count[to_bank] = tmp; +} + +// utility function used to put back overflow into the weapons pool +void wl_saturate_bank(int ship_slot, int bank) +{ + wss_unit *slot; + int max_count, overflow; + + slot = &Wss_slots[ship_slot]; + + if ( (slot->wep[bank] < 0) || (slot->wep_count <= 0) ) { + return; + } + + max_count = wl_calc_missile_fit(slot->wep[bank], Ship_info[slot->ship_class].secondary_bank_ammo_capacity[bank-3]); + overflow = slot->wep_count[bank] - max_count; + if ( overflow > 0 ) { + slot->wep_count[bank] -= overflow; + // add overflow back to pool + Wl_pool[slot->wep[bank]] += overflow; + } +} + +// exit: 0 -> no data changed +// 1 -> data changed +// sound => gets filled with sound id to play +int wl_swap_slot_slot(int from_bank, int to_bank, int ship_slot, int *sound) +{ + wss_unit *slot; + slot = &Wss_slots[ship_slot]; + + if ( slot->ship_class == -1 ) { + Int3(); // should not be possible + return 0; + } + + // do nothing if swapping with self + if ( from_bank == to_bank ) { + *sound=SND_ICON_DROP_ON_WING; + return 0; // no update + } + + // ensure that source bank exists and has something to pick from + if ( slot->wep[from_bank] == -1 || slot->wep_count[from_bank] <= 0 ) { + return 0; + } + + // ensure that the dest bank exists + if ( slot->wep_count[to_bank] < 0 ) { + return 0; + } + + // ensure that the banks are both of the same class + if ( (IS_BANK_PRIMARY(from_bank) && IS_BANK_SECONDARY(to_bank)) || (IS_BANK_SECONDARY(from_bank) && IS_BANK_PRIMARY(to_bank)) ) { + // put from_bank back into list + Wl_pool[slot->wep[from_bank]] += slot->wep_count[from_bank];; // return to list + slot->wep[from_bank] = -1; // remove from slot + slot->wep_count[from_bank] = 0; + *sound=SND_ICON_DROP; + return 1; + } + + // case 1: primaries (easy) + if ( IS_BANK_PRIMARY(from_bank) && IS_BANK_PRIMARY(to_bank) ) { + wl_swap_weapons(ship_slot, from_bank, to_bank); + *sound=SND_ICON_DROP_ON_WING; + return 1; + } + + // case 2: secondaries (harder) + if ( IS_BANK_SECONDARY(from_bank) && IS_BANK_SECONDARY(to_bank) ) { + + // case 2a: secondaries are the same type + if ( slot->wep[from_bank] == slot->wep[to_bank] ) { + int dest_max, dest_can_fit, source_can_give; + dest_max = wl_calc_missile_fit(slot->wep[to_bank], Ship_info[slot->ship_class].secondary_bank_ammo_capacity[to_bank-3]); + + dest_can_fit = dest_max - slot->wep_count[to_bank]; + + if ( dest_can_fit <= 0 ) { + // dest bank is already full.. nothing to do here + return 0; + } + + // see how much source can give + source_can_give = min(dest_can_fit, slot->wep_count[from_bank]); + + if ( source_can_give > 0 ) { + slot->wep_count[to_bank] += source_can_give; // add to dest + slot->wep_count[from_bank] -= source_can_give; // take from source + *sound=SND_ICON_DROP_ON_WING; + return 1; + } else { + return 0; + } + } + + // case 2b: secondaries are different types + if ( slot->wep[from_bank] != slot->wep[to_bank] ) { + + // swap 'em + wl_swap_weapons(ship_slot, from_bank, to_bank); + + // put back some on list if required + wl_saturate_bank(ship_slot, from_bank); + wl_saturate_bank(ship_slot, to_bank); + *sound=SND_ICON_DROP_ON_WING; + return 1; + } + } + + Int3(); // should never get here + return 0; +} + +// exit: 0 -> no data changed +// 1 -> data changed +// sound => gets filled with sound id to play +int wl_dump_to_list(int from_bank, int to_list, int ship_slot, int *sound) +{ + wss_unit *slot; + slot = &Wss_slots[ship_slot]; + + // ensure that source bank exists and has something to pick from + if ( slot->wep[from_bank] == -1 || slot->wep_count[from_bank] <= 0 ) { + return 0; + } + + // put weapon bank to the list + Wl_pool[to_list] += slot->wep_count[from_bank]; // return to list + slot->wep[from_bank] = -1; // remove from slot + slot->wep_count[from_bank] = 0; + *sound=SND_ICON_DROP; + + return 1; +} + +// exit: 0 -> no data changed +// 1 -> data changed +// sound => gets filled with sound id to play +int wl_grab_from_list(int from_list, int to_bank, int ship_slot, int *sound) +{ + wss_unit *slot; + slot = &Wss_slots[ship_slot]; + int max_fit; + + // ensure that the banks are both of the same class + if ( (IS_LIST_PRIMARY(from_list) && IS_BANK_SECONDARY(to_bank)) || (IS_LIST_SECONDARY(from_list) && IS_BANK_PRIMARY(to_bank)) ) { + // do nothing + *sound=SND_ICON_DROP; + return 0; + } + + // ensure that dest bank exists + if ( slot->wep_count[to_bank] < 0 ) { + *sound=SND_ICON_DROP; + return 0; + } + + // bank should be empty: + Assert(slot->wep_count[to_bank] == 0); + Assert(slot->wep[to_bank] < 0); + + // ensure that pool has weapon + if ( Wl_pool[from_list] <= 0 ) { + return 0; + } + + // find how much dest bank can fit + if ( to_bank < MAX_WL_PRIMARY ) { + max_fit = 1; + } else { + max_fit = wl_calc_missile_fit(from_list, Ship_info[slot->ship_class].secondary_bank_ammo_capacity[to_bank-MAX_WL_PRIMARY]); + } + + // take weapon from list + if ( Wl_pool[from_list] < max_fit ) { + max_fit = Wl_pool[from_list]; + } + Wl_pool[from_list] -= max_fit; + + // put on the slot + slot->wep[to_bank] = from_list; + slot->wep_count[to_bank] = max_fit; + + *sound=SND_ICON_DROP_ON_WING; + return 1; +} + +// exit: 0 -> no data changed +// 1 -> data changed +// sound => gets filled with sound id to play +int wl_swap_list_slot(int from_list, int to_bank, int ship_slot, int *sound) +{ + wss_unit *slot; + slot = &Wss_slots[ship_slot]; + int max_fit; + + // ensure that the banks are both of the same class + if ( (IS_LIST_PRIMARY(from_list) && IS_BANK_SECONDARY(to_bank)) || (IS_LIST_SECONDARY(from_list) && IS_BANK_PRIMARY(to_bank)) ) { + // do nothing + *sound=SND_ICON_DROP; + return 0; + } + + // ensure that dest bank exists + if ( slot->wep_count[to_bank] < 0 ) { + return 0; + } + + // bank should have something in it + Assert(slot->wep_count[to_bank] > 0); + Assert(slot->wep[to_bank] >= 0); + + // ensure that pool has weapon + if ( Wl_pool[from_list] <= 0 ) { + return 0; + } + + // dump slot weapon back into list + Wl_pool[slot->wep[to_bank]] += slot->wep_count[to_bank]; + slot->wep_count[to_bank] = 0; + slot->wep[to_bank] = -1; + + // put weapon on ship from list + + // find how much dest bank can fit + if ( to_bank < MAX_WL_PRIMARY ) { + max_fit = 1; + } else { + max_fit = wl_calc_missile_fit(from_list, Ship_info[slot->ship_class].secondary_bank_ammo_capacity[to_bank-MAX_WL_PRIMARY]); + } + + // take weapon from list + if ( Wl_pool[from_list] < max_fit ) { + max_fit = Wl_pool[from_list]; + } + Wl_pool[from_list] -= max_fit; + + // put on the slot + slot->wep[to_bank] = from_list; + slot->wep_count[to_bank] = max_fit; + + *sound=SND_ICON_DROP_ON_WING; + return 1; +} + +// update any interface data that may be dependent on Wss_slots[] +void wl_synch_interface() +{ +} + +void wl_apply(int mode,int from_bank,int from_list,int to_bank,int to_list,int ship_slot,int player_index) +{ + int update=0; + int sound=-1; + net_player *pl; + + // get the appropriate net player + if(Game_mode & GM_MULTIPLAYER){ + if(player_index == -1){ + pl = Net_player; + } else { + pl = &Net_players[player_index]; + } + } else { + pl = NULL; + } + + switch(mode){ + case WSS_SWAP_SLOT_SLOT: + update = wl_swap_slot_slot(from_bank, to_bank, ship_slot, &sound); + break; + case WSS_DUMP_TO_LIST: + update = wl_dump_to_list(from_bank, to_list, ship_slot, &sound); + break; + case WSS_GRAB_FROM_LIST: + update = wl_grab_from_list(from_list, to_bank, ship_slot, &sound); + break; + case WSS_SWAP_LIST_SLOT: + update = wl_swap_list_slot(from_list, to_bank, ship_slot, &sound); + break; + } + + // only play this sound if the move was done locally (by the host in other words) + if ( (sound >= 0) && (player_index == -1) ) { + gamesnd_play_iface(sound); + } + + if ( update ) { + if ( MULTIPLAYER_HOST ) { + int size; + ubyte wss_data[MAX_PACKET_SIZE-20]; + + size = store_wss_data(wss_data, MAX_PACKET_SIZE-20,sound,player_index); + Assert(pl != NULL); + send_wss_update_packet(pl->p_info.team,wss_data, size); + } + + if(Game_mode & GM_MULTIPLAYER){ + Assert(pl != NULL); + + // if the pool we're using has changed, synch stuff up + if(pl->p_info.team == Net_player->p_info.team){ + wl_synch_interface(); + } + } else { + wl_synch_interface(); + } + } +} + +void wl_drop(int from_bank,int from_list,int to_bank,int to_list, int ship_slot, int player_index) +{ + int mode; + net_player *pl; + + // get the appropriate net player + if(Game_mode & GM_MULTIPLAYER){ + if(player_index == -1){ + pl = Net_player; + } else { + pl = &Net_players[player_index]; + } + } else { + pl = NULL; + } + + common_flash_button_init(); + if ( !(Game_mode & GM_MULTIPLAYER) || MULTIPLAYER_HOST ) { + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){ + // set the global pointers to the right pools + ss_set_team_pointers(pl->p_info.team); + } + + + mode = wss_get_mode(from_bank, from_list, to_bank, to_list, ship_slot); + if ( mode >= 0 ) { + wl_apply(mode, from_bank, from_list, to_bank, to_list, ship_slot, player_index); + } + + if((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)){ + // set the global pointers to the right pools + ss_set_team_pointers(Net_player->p_info.team); + } + } else { + send_wss_request_packet(Net_player->player_id, from_bank, from_list, to_bank, to_list, ship_slot, -1, WSS_WEAPON_SELECT); + } +} diff --git a/src/missionui/redalert.cpp b/src/missionui/redalert.cpp new file mode 100644 index 0000000..43895cc --- /dev/null +++ b/src/missionui/redalert.cpp @@ -0,0 +1,931 @@ +/* + * $Logfile: /Freespace2/code/MissionUI/RedAlert.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Module for Red Alert mission interface and code + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 16 9/11/99 12:31a Mikek + * Bumped up max red alert status ships and status subsystems. + * + * 15 9/06/99 6:38p Dave + * Improved CD detection code. + * + * 14 8/24/99 5:27p Andsager + * Make subsystems with zero strength before mission blown off. Protect + * red alert pilot against dying between orders and jump. + * + * 13 8/11/99 2:17p Jefff + * changed a string + * + * 12 7/19/99 8:56p Andsager + * Added Ship_exited red_alert_carry flag and hull strength for RED ALERT + * carry over of Exited_ships + * + * 11 7/19/99 2:13p Dave + * Added some new strings for Heiko. + * + * 10 7/16/99 12:22p Jefff + * Added sound FX to red alert popup + * + * 9 7/09/99 10:32p Dave + * Command brief and red alert screens. + * + * 8 5/07/99 10:34a Andsager + * Make red alert work in FS2 + * + * 7 2/16/99 5:07p Neilk + * Added hires support + * + * 6 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 5 1/27/99 9:56a Dave + * Temporary checkin of beam weapons for Dan to make cool sounds. + * + * 4 11/18/98 11:15a Andsager + * Removed old version or red_alert_read_wingman_status + * + * 3 10/13/98 9:29a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 21 5/14/98 11:26a Lawrance + * ESC will return to the main hall + * + * 20 5/13/98 5:14p Allender + * red alert support to go back to previous mission + * + * 19 5/05/98 6:19p Lawrance + * Fix problems with "retry mission" for red alerts + * + * 18 5/05/98 3:14p Comet + * AL: Fix bug with restoring secondary weapons in red alert mode + * + * 17 5/04/98 10:49p Lawrance + * Make sure old pilot files don't cause problems with the new RedAlert + * data format + * + * 16 5/04/98 6:06p Lawrance + * Make red alert mode work! + * + * 15 5/02/98 4:10p Lawrance + * Only use first stage of briefing for red alerts + * + * 14 5/01/98 9:18p Ed + * mark all goals and events as failed when red alert moves ahead to next + * mission + * + * 13 4/22/98 10:13a John + * added assert to trap a bug + * + * 12 4/21/98 12:08a Allender + * only make red alert move to next mission in campaign mode + * + * 11 4/03/98 10:31a John + * Made briefing and debriefing arrays be malloc'd + * + * 10 3/28/98 2:53p Allender + * added hud gauge when entering a red alert mission + * + * 9 3/12/98 10:28p Lawrance + * Deal with situation where wingman status may not have been properly + * saved + * + * 8 3/12/98 9:44p Allender + * go to debrief in red alert when not in campaign mode + * + * 6 3/09/98 9:55p Lawrance + * improve some comments + * + * 5 3/09/98 4:41p Lawrance + * Fix up some merge problems. + * + * 4 3/09/98 4:30p Allender + * multiplayer secondary weapon changes. red-alert and cargo-known-delay + * sexpressions. Add time cargo revealed to ship structure + * + * 3 3/09/98 4:23p Lawrance + * Replay mission, full save/restore of wingman status + * + * 2 3/09/98 12:13a Lawrance + * Add support for Red Alert missions + * + * 1 3/08/98 4:54p Lawrance + * + * $NoKeywords: $ + */ + +#include "ui.h" +#include "gamesnd.h" +#include "gamesequence.h" +#include "missionscreencommon.h" +#include "key.h" +#include "bmpman.h" +#include "font.h" +#include "redalert.h" +#include "hud.h" +#include "missionbriefcommon.h" +#include "timer.h" +#include "missioncampaign.h" +#include "missiongoals.h" +#include "linklist.h" +#include "hudwingmanstatus.h" +#include "audiostr.h" +#include "freespace.h" +#include "alphacolors.h" + +///////////////////////////////////////////////////////////////////////////// +// Red Alert Mission-Level +///////////////////////////////////////////////////////////////////////////// + +static int Red_alert_status; +static int Red_alert_new_mission_timestamp; // timestamp used to give user a little warning for red alerts +static int Red_alert_num_slots_used = 0; +static int Red_alert_voice_started; + +#define RED_ALERT_WARN_TIME 4000 // time to warn user that new orders are coming + +#define RED_ALERT_NONE 0 +#define RED_ALERT_MISSION 1 + +#define MAX_RED_ALERT_SLOTS 32 +#define MAX_RED_ALERT_SUBSYSTEMS 64 +#define RED_ALERT_EXITED_SHIP_CLASS -1 + +typedef struct red_alert_ship_status +{ + char name[NAME_LENGTH]; + float hull; + int ship_class; + float subsys_current_hits[MAX_RED_ALERT_SUBSYSTEMS]; + float subsys_aggregate_current_hits[SUBSYSTEM_MAX]; + int wep[MAX_WL_WEAPONS]; + int wep_count[MAX_WL_WEAPONS]; +} red_alert_ship_status; + +static red_alert_ship_status Red_alert_wingman_status[MAX_RED_ALERT_SLOTS]; +static char Red_alert_precursor_mission[MAX_FILENAME_LEN]; + +///////////////////////////////////////////////////////////////////////////// +// Red Alert Interface +///////////////////////////////////////////////////////////////////////////// + +char *Red_alert_fname[GR_NUM_RESOLUTIONS] = { + "RedAlert", + "2_RedAlert" +}; + +char *Red_alert_mask[GR_NUM_RESOLUTIONS] = { + "RedAlert-m", + "2_RedAlert-m" +}; + +// font to use for "incoming transmission" +int Ra_flash_font[GR_NUM_RESOLUTIONS] = { + FONT2, + FONT2 +}; + +int Ra_flash_y[GR_NUM_RESOLUTIONS] = { + 140, + 200 +}; + +static int Ra_flash_coords[GR_NUM_RESOLUTIONS][2] = { + { + 61, 108 // GR_640 + }, + { + 61, 108 // GR_1024 + } +}; + +#define NUM_BUTTONS 2 + +#define RA_REPLAY_MISSION 0 +#define RA_CONTINUE 1 + +ui_button_info Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = { + { // GR_640 + ui_button_info("RAB_00", 2, 445, -1, -1, 0), + ui_button_info("RAB_01", 575, 432, -1, -1, 1), + }, + { // GR_1024 + ui_button_info("2_RAB_00", 4, 712, -1, -1, 0), + ui_button_info("2_RAB_01", 920, 691, -1, -1, 1), + } +}; + +#define RED_ALERT_NUM_TEXT 3 +UI_XSTR Red_alert_text[GR_NUM_RESOLUTIONS][RED_ALERT_NUM_TEXT] = { + { // GR_640 + { "Replay", 1405, 46, 451, UI_XSTR_COLOR_PINK, -1, &Buttons[0][RA_REPLAY_MISSION].button }, + { "Previous Mission", 1452, 46, 462, UI_XSTR_COLOR_PINK, -1, &Buttons[0][RA_REPLAY_MISSION].button }, + { "Continue", 1069, 564, 413, UI_XSTR_COLOR_PINK, -1, &Buttons[0][RA_CONTINUE].button }, + }, + { // GR_1024 + { "Replay", 1405, 75, 722, UI_XSTR_COLOR_PINK, -1, &Buttons[1][RA_REPLAY_MISSION].button }, + { "Previous Mission", 1452, 75, 733, UI_XSTR_COLOR_PINK, -1, &Buttons[1][RA_REPLAY_MISSION].button }, + { "Continue", 1069, 902, 661, UI_XSTR_COLOR_PINK, -1, &Buttons[1][RA_CONTINUE].button }, + } +}; + +// indicies for coordinates +#define RA_X_COORD 0 +#define RA_Y_COORD 1 +#define RA_W_COORD 2 +#define RA_H_COORD 3 + + +static int Text_delay; + +int Ra_brief_text_wnd_coords[GR_NUM_RESOLUTIONS][4] = { + { + 14, 151, 522, 289 + }, + { + 52, 241, 785, 463 + } +}; + +static UI_WINDOW Ui_window; +// static hud_anim Flash_anim; +static int Background_bitmap; +static int Red_alert_inited = 0; + +static int Red_alert_voice; + +// open and pre-load the stream buffers for the different voice streams +void red_alert_voice_load() +{ + Assert( Briefing != NULL ); + if ( strnicmp(Briefing->stages[0].voice, NOX("none"), 4) && (strlen(Briefing->stages[0].voice) > 0) ) { + Red_alert_voice = audiostream_open( Briefing->stages[0].voice, ASF_VOICE ); + } +} + +// close all the briefing voice streams +void red_alert_voice_unload() +{ + if ( Red_alert_voice != -1 ) { + audiostream_close_file(Red_alert_voice, 0); + Red_alert_voice = -1; + } +} + +// start playback of the red alert voice +void red_alert_voice_play() +{ + if ( Red_alert_voice == -1 ){ + return; // voice file doesn't exist + } + + if ( !Briefing_voice_enabled ) { + return; + } + + if ( audiostream_is_playing(Red_alert_voice) ){ + return; + } + + audiostream_play(Red_alert_voice, Master_voice_volume, 0); + Red_alert_voice_started = 1; +} + +// stop playback of the red alert voice +void red_alert_voice_stop() +{ + if ( Red_alert_voice == -1 ) + return; + + audiostream_stop(Red_alert_voice); // stream is automatically rewound +} + +// a button was pressed, deal with it +void red_alert_button_pressed(int n) +{ + switch (n) { + case RA_CONTINUE: + if(game_do_cd_mission_check(Game_current_mission_filename)){ + gameseq_post_event(GS_EVENT_ENTER_GAME); + } else { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + break; + + case RA_REPLAY_MISSION: + if ( Game_mode & GM_CAMPAIGN_MODE ) { + // TODO: make call to campaign code to set correct mission for loading + // mission_campaign_play_previous_mission(Red_alert_precursor_mission); + if ( !mission_campaign_previous_mission() ) { + gamesnd_play_iface(SND_GENERAL_FAIL); + break; + } + + // CD CHECK + if(game_do_cd_mission_check(Game_current_mission_filename)){ + gameseq_post_event(GS_EVENT_START_GAME); + } else { + gameseq_post_event(GS_EVENT_MAIN_MENU); + } + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + break; + } +} + +// blit "incoming transmission" +#define RA_FLASH_CYCLE 0.25f +float Ra_flash_time = 0.0f; +int Ra_flash_up = 0; +void red_alert_blit_title() +{ + char *str = XSTR("Incoming Transmission", 1406); + int w, h; + + // get the string size + gr_set_font(Ra_flash_font[gr_screen.res]); + gr_get_string_size(&w, &h, str); + + // set alpha color + color flash_color; + if(Ra_flash_up){ + gr_init_alphacolor(&flash_color, (int)(255.0f * (Ra_flash_time / RA_FLASH_CYCLE)), 0, 0, 255); + } else { + gr_init_alphacolor(&flash_color, (int)(255.0f * (1.0f - (Ra_flash_time / RA_FLASH_CYCLE))), 0, 0, 255); + } + + // draw + gr_set_color_fast(&flash_color); + gr_string(Ra_brief_text_wnd_coords[gr_screen.res][0] + ((Ra_brief_text_wnd_coords[gr_screen.res][2] - w) / 2), Ra_flash_y[gr_screen.res] - h - 5, str); + gr_set_color_fast(&Color_normal); + + // increment flash time + Ra_flash_time += flFrametime; + if(Ra_flash_time >= RA_FLASH_CYCLE){ + Ra_flash_time = 0.0f; + Ra_flash_up = !Ra_flash_up; + } + + // back to the original font + gr_set_font(FONT1); +} + +// Called once when red alert interface is started +void red_alert_init() +{ + int i; + ui_button_info *b; + + if ( Red_alert_inited ) { + return; + } + + // common_set_interface_palette("ControlConfigPalette"); // set the interface palette + Ui_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Ui_window.set_mask_bmap(Red_alert_mask[gr_screen.res]); + + for (i=0; ibutton.create(&Ui_window, "", b->x, b->y, 60, 30, 0, 1); + // set up callback for when a mouse first goes over a button + b->button.set_highlight_action(common_play_highlight_sound); + b->button.set_bmaps(b->filename); + b->button.link_hotspot(b->hotspot); + } + + // all text + for(i=0; inum_stages > 0 ) { + Assert(Briefing->stages[0].new_text); + brief_color_text_init(Briefing->stages[0].new_text, Ra_brief_text_wnd_coords[gr_screen.res][RA_W_COORD], 0); + } + + red_alert_voice_load(); + + Text_delay = timestamp(200); + + Red_alert_voice_started = 0; + Red_alert_inited = 1; +} + +// Called once when the red alert interface is exited +void red_alert_close() +{ + if (Red_alert_inited) { + + red_alert_voice_stop(); + red_alert_voice_unload(); + + if (Background_bitmap >= 0) { + bm_unload(Background_bitmap); + } + + Ui_window.destroy(); + // hud_anim_release(&Flash_anim); + common_free_interface_palette(); // restore game palette + game_flush(); + } + + Red_alert_inited = 0; +} + +// called once per frame when game state is GS_STATE_RED_ALERT +void red_alert_do_frame(float frametime) +{ + int i, k; + + // ensure that the red alert interface has been initialized + if (!Red_alert_inited) { + Int3(); + return; + } + + k = Ui_window.process() & ~KEY_DEBUGGED; + switch (k) { + case KEY_ESC: +// gameseq_post_event(GS_EVENT_ENTER_GAME); + gameseq_post_event(GS_EVENT_MAIN_MENU); + break; + } // end switch + + for (i=0; i= 0) { + gr_set_bitmap(Background_bitmap); + gr_bitmap(0, 0); + } + + Ui_window.draw(); + // hud_anim_render(&Flash_anim, frametime); + + gr_set_font(FONT1); + + if ( timestamp_elapsed(Text_delay) ) { + int finished_wipe = 0; + if ( Briefing->num_stages > 0 ) { + finished_wipe = brief_render_text(0, Ra_brief_text_wnd_coords[gr_screen.res][RA_X_COORD], Ra_brief_text_wnd_coords[gr_screen.res][RA_Y_COORD], Ra_brief_text_wnd_coords[gr_screen.res][RA_H_COORD], frametime, 0); + } + + if (finished_wipe) { + red_alert_voice_play(); + } + } + + // blit incoming transmission + red_alert_blit_title(); + + gr_flip(); +} + +// set the red alert status for the current mission +void red_alert_set_status(int status) +{ + Red_alert_status = status; + Red_alert_new_mission_timestamp = timestamp(-1); // make invalid +} + +// Store a ships weapons into a wingman status structure +void red_alert_store_weapons(red_alert_ship_status *ras, ship_weapon *swp) +{ + int i, sidx; + + if (swp == NULL) { + return; + } + + for ( i = 0; i < MAX_WL_PRIMARY; i++ ) { + ras->wep[i] = swp->primary_bank_weapons[i]; + if ( ras->wep[i] >= 0 ) { + ras->wep_count[i] = 1; + } else { + ras->wep_count[i] = -1; + } + } + + for ( i = 0; i < MAX_WL_SECONDARY; i++ ) { + sidx = i+MAX_WL_PRIMARY; + ras->wep[sidx] = swp->secondary_bank_weapons[i]; + if ( ras->wep[sidx] >= 0 ) { + ras->wep_count[sidx] = swp->secondary_bank_ammo[i]; + } else { + ras->wep_count[sidx] = -1; + } + } +} + +// Take the weapons stored in a wingman_status struct, and bash them into the ship weapons struct +void red_alert_bash_weapons(red_alert_ship_status *ras, ship_weapon *swp) +{ + int i, j, sidx; + + // restore from ship_exited + if (ras->ship_class == RED_ALERT_EXITED_SHIP_CLASS) { + return; + } + + j = 0; + for ( i = 0; i < MAX_WL_PRIMARY; i++ ) { + if ( (ras->wep_count[i] > 0) && (ras->wep[i] >= 0) ) { + swp->primary_bank_weapons[j] = ras->wep[i]; + j++; + } + } + swp->num_primary_banks = j; + + j = 0; + for ( i = 0; i < MAX_WL_SECONDARY; i++ ) { + sidx = i+MAX_WL_PRIMARY; + if ( ras->wep[sidx] >= 0 ) { + swp->secondary_bank_weapons[j] = ras->wep[sidx]; + swp->secondary_bank_ammo[j] = ras->wep_count[sidx]; + j++; + } + } + swp->num_secondary_banks = j; +} + +void red_alert_bash_subsys_status(red_alert_ship_status *ras, ship *shipp) +{ + ship_subsys *ss; + int count = 0; + + // restore from ship_exited + if (ras->ship_class == RED_ALERT_EXITED_SHIP_CLASS) { + return; + } + + ss = GET_FIRST(&shipp->subsys_list); + while ( ss != END_OF_LIST( &shipp->subsys_list ) ) { + + if ( count >= MAX_RED_ALERT_SUBSYSTEMS ) { + Int3(); // ran out of subsystems + break; + } + + ss->current_hits = ras->subsys_current_hits[count]; + if (ss->current_hits <= 0) { + ss->submodel_info_1.blown_off = 1; + } + + ss = GET_NEXT( ss ); + count++; + } + + int i; + + for ( i = 0; i < SUBSYSTEM_MAX; i++ ) { + shipp->subsys_info[i].current_hits = ras->subsys_aggregate_current_hits[i]; + } +} + + +void red_alert_store_subsys_status(red_alert_ship_status *ras, ship *shipp) +{ + ship_subsys *ss; + int count = 0; + + if (shipp == NULL) { + return; + } + + ss = GET_FIRST(&shipp->subsys_list); + while ( ss != END_OF_LIST( &shipp->subsys_list ) ) { + + if ( count >= MAX_RED_ALERT_SUBSYSTEMS ) { + Int3(); // ran out of subsystems + break; + } + + ras->subsys_current_hits[count] = ss->current_hits; + + ss = GET_NEXT( ss ); + count++; + } + + int i; + + for ( i = 0; i < SUBSYSTEM_MAX; i++ ) { + ras->subsys_aggregate_current_hits[i] = shipp->subsys_info[i].current_hits; + } +} + + +// Record the current state of the players wingman +void red_alert_store_wingman_status() +{ + ship *shipp; + red_alert_ship_status *ras; + ship_obj *so; + object *ship_objp; + + Red_alert_num_slots_used = 0; + + // store the mission filename for the red alert precursor mission + strcpy(Red_alert_precursor_mission, Game_current_mission_filename);; + + // store status for all existing ships + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + ship_objp = &Objects[so->objnum]; + Assert(ship_objp->type == OBJ_SHIP); + shipp = &Ships[ship_objp->instance]; + + if ( shipp->flags & SF_DYING ) { + continue; + } + + if ( Red_alert_num_slots_used >= MAX_RED_ALERT_SLOTS ) { + Int3(); // ran out of red alert slots + continue; + } + + if ( !(shipp->flags & SF_FROM_PLAYER_WING) && !(shipp->flags & SF_RED_ALERT_STORE_STATUS) ) { + continue; + } + + ras = &Red_alert_wingman_status[Red_alert_num_slots_used]; + Red_alert_num_slots_used++; + + strcpy(ras->name, shipp->ship_name); + ras->hull = Objects[shipp->objnum].hull_strength; + ras->ship_class = shipp->ship_info_index; + red_alert_store_weapons(ras, &shipp->weapons); + red_alert_store_subsys_status(ras, shipp); + } + + // store exited ships that did not die + for (int idx=0; idx= MAX_RED_ALERT_SLOTS ) { + Int3(); // ran out of red alert slots + continue; + } + + if (Ships_exited[idx].flags & SEF_RED_ALERT_CARRY) { + ras = &Red_alert_wingman_status[Red_alert_num_slots_used]; + Red_alert_num_slots_used++; + + strcpy(ras->name, Ships_exited[idx].ship_name); + ras->hull = float(Ships_exited[idx].hull_strength); + ras->ship_class = RED_ALERT_EXITED_SHIP_CLASS; //shipp->ship_info_index; + red_alert_store_weapons(ras, NULL); + red_alert_store_subsys_status(ras, NULL); + } + } + + Assert(Red_alert_num_slots_used > 0); +} + +// Delete a ship in a red alert mission (since it must have died/departed in the previous mission) +void red_alert_delete_ship(ship *shipp) +{ + if ( (shipp->wing_status_wing_index >= 0) && (shipp->wing_status_wing_pos >= 0) ) { + hud_set_wingman_status_dead(shipp->wing_status_wing_index, shipp->wing_status_wing_pos); + } + + ship_add_exited_ship( shipp, SEF_PLAYER_DELETED ); + obj_delete(shipp->objnum); + if ( shipp->wingnum >= 0 ) { + ship_wing_cleanup( shipp-Ships, &Wings[shipp->wingnum] ); + } +} + +// Take the stored wingman status information, and adjust the player wing ships accordingly +void red_alert_bash_wingman_status() +{ + int i; + ship *shipp; + red_alert_ship_status *ras; + ship_obj *so; + object *ship_objp; + + if ( !(Game_mode & GM_CAMPAIGN_MODE) ) { + return; + } + + if ( Red_alert_num_slots_used <= 0 ) { + return; + } + + // go through all ships in the game, and see if there is red alert status data for any + + int remove_list[MAX_RED_ALERT_SLOTS]; + int remove_count = 0; + + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + ship_objp = &Objects[so->objnum]; + Assert(ship_objp->type == OBJ_SHIP); + shipp = &Ships[ship_objp->instance]; + + if ( !(shipp->flags & SF_FROM_PLAYER_WING) && !(shipp->flags & SF_RED_ALERT_STORE_STATUS) ) { + continue; + } + + int found_match = 0; + + for ( i = 0; i < Red_alert_num_slots_used; i++ ) { + ras = &Red_alert_wingman_status[i]; + + if ( !stricmp(ras->name, shipp->ship_name) ) { + found_match = 1; + if ( ras->ship_class == RED_ALERT_EXITED_SHIP_CLASS) { + // if exited ship, we can only restore hull strength + ship_objp->hull_strength = ras->hull; + } else { + // if necessary, restore correct ship class + if ( ras->ship_class != shipp->ship_info_index ) { + change_ship_type(SHIP_INDEX(shipp), ras->ship_class); + } + // restore hull and weapons + ship_objp->hull_strength = ras->hull; + red_alert_bash_weapons(ras, &shipp->weapons); + red_alert_bash_subsys_status(ras, shipp); + } + } + } + + if ( !found_match ) { + remove_list[remove_count++] = SHIP_INDEX(shipp); + } + } + + // remove ships + for ( i = 0; i < remove_count; i++ ) { + // remove ship + red_alert_delete_ship(&Ships[remove_list[i]]); + } +} + +// write wingman status out to the specified file +void red_alert_write_wingman_status(CFILE *fp) +{ + int i, j; + red_alert_ship_status *ras; + + cfwrite_int(Red_alert_num_slots_used, fp); + + if ( Red_alert_num_slots_used <= 0 ) { + return; + } + + Assert(strlen(Red_alert_precursor_mission) > 0 ); + cfwrite_string(Red_alert_precursor_mission, fp); + + for ( i = 0; i < Red_alert_num_slots_used; i++ ) { + ras = &Red_alert_wingman_status[i]; + cfwrite_string(ras->name, fp); + cfwrite_float(ras->hull, fp); + cfwrite_int(ras->ship_class, fp); + + for ( j = 0; j < MAX_RED_ALERT_SUBSYSTEMS; j++ ) { + cfwrite_float(ras->subsys_current_hits[j], fp); + } + + for ( j = 0; j < SUBSYSTEM_MAX; j++ ) { + cfwrite_float(ras->subsys_aggregate_current_hits[j], fp); + } + + for ( j = 0; j < MAX_WL_WEAPONS; j++ ) { + cfwrite_int( ras->wep[j], fp ) ; + cfwrite_int( ras->wep_count[j], fp ); + } + } +} + +// red wingman status out of the specified file +void red_alert_read_wingman_status(CFILE *fp, int version) +{ + int i, j; + red_alert_ship_status *ras; + + Red_alert_num_slots_used = cfread_int(fp); + + if ( Red_alert_num_slots_used <= 0 ) { + return; + } + + cfread_string(Red_alert_precursor_mission, MAX_FILENAME_LEN, fp); + + for ( i = 0; i < Red_alert_num_slots_used; i++ ) { + ras = &Red_alert_wingman_status[i]; + cfread_string(ras->name, NAME_LENGTH, fp); + ras->hull = cfread_float(fp); + ras->ship_class = cfread_int(fp); + + for ( j = 0; j < MAX_RED_ALERT_SUBSYSTEMS; j++ ) { + ras->subsys_current_hits[j] = cfread_float(fp); + } + + for ( j = 0; j < SUBSYSTEM_MAX; j++ ) { + ras->subsys_aggregate_current_hits[j] = cfread_float(fp); + } + + for ( j = 0; j < MAX_WL_WEAPONS; j++ ) { + ras->wep[j] = cfread_int(fp) ; + ras->wep_count[j] = cfread_int(fp); + } + } +} + +// return !0 if this is a red alert mission, otherwise return 0 +int red_alert_mission() +{ + if ( Red_alert_status == RED_ALERT_MISSION ) { + return 1; + } + + return 0; +} + +// called from sexpression code to start a red alert mission +void red_alert_start_mission() +{ + // if we are not in campaign mode, go to debriefing +// if ( !(Game_mode & GM_CAMPAIGN_MODE) ) { +// gameseq_post_event( GS_EVENT_DEBRIEF ); // proceed to debriefing +// return; +// } + + // check player health here. + // if we're dead (or about to die), don't start red alert mission. + if (Player_obj->type == OBJ_SHIP) { + if (Player_obj->hull_strength > 0) { + // make sure we don't die + Player_obj->flags |= OF_GUARDIAN; + + // do normal red alert stuff + Red_alert_new_mission_timestamp = timestamp(RED_ALERT_WARN_TIME); + + // throw down a sound here to make the warning seem ultra-important + // gamesnd_play_iface(SND_USER_SELECT); + snd_play(&(Snds[SND_DIRECTIVE_COMPLETE])); + } + } +} + +// called from main game loop to check to see if we should move to a red alert mission +int red_alert_check_status() +{ + // if the timestamp is invalid, do nothing. + if ( !timestamp_valid(Red_alert_new_mission_timestamp) ) + return 0; + + // return if the timestamp hasn't elapsed yet + if ( timestamp_elapsed(Red_alert_new_mission_timestamp) ) { + + // basic premise here is to stop the current mission, and then set the next mission in the campaign + // which better be a red alert mission + if ( Game_mode & GM_CAMPAIGN_MODE ) { + red_alert_store_wingman_status(); + mission_goal_fail_incomplete(); + mission_campaign_store_goals_and_events(); + scoring_level_close(); + mission_campaign_eval_next_mission(); + mission_campaign_mission_over(); + + // CD CHECK + gameseq_post_event(GS_EVENT_START_GAME); + } else { + gameseq_post_event(GS_EVENT_END_GAME); + } + } + + return 1; +} \ No newline at end of file diff --git a/src/model/modelcollide.cpp b/src/model/modelcollide.cpp new file mode 100644 index 0000000..ff95e7d --- /dev/null +++ b/src/model/modelcollide.cpp @@ -0,0 +1,1044 @@ +/* + * $Logfile: /Freespace2/code/Model/ModelCollide.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for detecting collisions of models. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 5 3/08/99 7:03p Dave + * First run of new object update system. Looks very promising. + * + * 4 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 3 11/19/98 11:07p Andsager + * Check in of physics and collision detection of rotating submodels + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 82 4/22/98 9:43p John + * Added code to allow checking of invisible faces, flagged by any texture + * name with invisible in it. + * + * 81 4/02/98 8:16a John + * Fixed Assert in model_collide with large ships + * + * 80 3/31/98 5:18p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * + * 79 3/25/98 1:36p Andsager + * comment out assert + * + * 78 3/18/98 3:04p Andsager + * Increase collision error warning distance + * + * 77 3/09/98 12:08a Andsager + * Remove assert + * + * 76 2/12/98 3:47p Hoffoss + * Added MC_CHECK_RAY support in model_collide(), since Fred would be + * better using rays instead of line segments for intersection tests. + * + * 75 2/05/98 9:21p John + * Some new Direct3D code. Added code to monitor a ton of stuff in the + * game. + * + * 74 1/27/98 11:02a John + * Added first rev of sparks. Made all code that calls model_render call + * clear_instance first. Made debris pieces not render by default when + * clear_instance is called. + * + * 73 1/19/98 11:30a Sandeep + * Remove Int3() in sphere poly collision + * + * 72 1/16/98 5:30p Andsager + * Added debug code for bad collisions. + * + * 71 1/12/98 9:21p Andsager + * Modify calling procedure to fvi_sphere_plane. + * + * 70 12/22/97 9:17a John + * added functions for Dave that check only one submodel. + * + * 69 12/18/97 9:26a John + * put back in some critical "debug" code DaveA deleted. + * + * 68 12/16/97 5:25p Andsager + * Get best collision info. Comment out some debug info. + * + * 67 11/14/97 5:29p Andsager + * Improve bounding box test for sphere:polygon. Set higher tolerance for + * collision alert. + * + * 66 11/12/97 11:09p Jasen + * Comment out another Int3() that causes grief, but seems to do little + * good. + * + * 65 11/12/97 12:13p Mike + * Suppress disruptive, but apparently harmless, Int3(). + * + * 64 11/05/97 5:48p Andsager + * Added debug code to check_sphereline_face. Expected radius separation + * can be off by up to .003 for 1 m sphere. + * + * 63 10/31/97 4:01p John + * added face_Radius to call to check_face + * + * 62 10/31/97 3:19p John + * changed id field in face to be radius + * + * 61 10/30/97 3:41p Andsager + * fixed bug in check_sphereline_face that allowed collisions at negative + * times + * + * 60 10/28/97 4:57p John + * Put Andsager's new sphereline collide code officially into the code + * base and did a little restructuring. Fixed a few little bugs with it + * and added some simple bounding box elimination and did some timings. + * + * + * 59 10/25/97 10:14a Andsager + * Moved SPHERE_POLY_CHECK to ObjCollide.h + * + * 58 10/22/97 10:26p Andsager + * modify (slightly) mc_check_shield to allow sphere-polygon collisions + * + * 57 10/19/97 9:32p Andsager + * Using model_collide with 2nd parameter radius (default = 0) + * + * 56 10/17/97 2:06a Andsager + * moved assert + * + * 55 10/17/97 1:26a Andsager + * added radius to mc_info struct. add sphere_face collisions + * + * 54 9/17/97 5:12p John + * Restructured collision routines. Probably broke a lot of stuff. + * + * 53 9/15/97 5:45p John + * took out chunk stuff. + * made pofview display thrusters as blue polies. + * + * 52 9/12/97 8:54a John + * made model_collide fill in mc_info even if invalid model passed + * through. + * + * 51 9/03/97 12:02a Andsager + * implement sphere_sphere collision detection. Use debug console + * CHECK_SPHERE + * + * 50 8/25/97 11:27a John + * took out the USE_OCTANTS define, since we want to always use them. + * + * 49 8/15/97 4:10p John + * new code to use the new octrees style bsp trees + * + * 48 8/11/97 10:43a Mike + * Enable octant checking by defining USE_OCTANTS + * + * 47 8/11/97 10:19a John + * fixed a bug that was setting the wrong triangle for shield hit with new + * octant stuff. + * + * 46 8/10/97 3:04p Lawrance + * get rid of warning if USE_OCTANTS no defined + * + * 45 8/08/97 3:58p Mike + * Shield hit system. + * Shield damage handled/managed-by-AI on a quadrant basis. + * + * 44 8/06/97 5:40p Mike + * Add hit_normal field to mc_info so we can have ships react to the + * object they hit. + * + * 43 7/22/97 9:41a John + * Made flat faces appear in octant list, so collision detection now + * works. Made them do smoothing if needed. + * + * 42 7/21/97 2:29p John + * moved the bounding sphere check into the model collide code + * + * + * 41 7/03/97 9:14a John + * fixed incorrect octant vertices. + * + * 40 6/27/97 12:15p John + * added support for only checking model's bounding box... made hud_target + * use it. + * + * 39 6/26/97 11:44a John + * made model_collide require that you specifically tell it to check the + * model to avoid unnecessary checks. + * + * 38 6/26/97 11:19a John + * Made model face & shield collisions look only at octants it needs to. + * Shield sped up 4x, faces sped up about 2x. + * + * 37 6/23/97 1:42p John + * updated sphere checking code + * + * 36 5/30/97 3:42p Mike + * Shield mesh and hit system. + * + * 35 5/28/97 12:52p John + * Added code in to check shield mesh. + * + * 34 4/17/97 6:06p John + * New code/data for v19 of BSPGEN with smoothing and zbuffer + * optimizations. + * + * 33 4/07/97 3:08p John + * added flag to model_collide to allow collision checks with rays not + * just segments. + * + * 32 4/07/97 10:37a John + * Took out limits on number of children a submodel can have. (So now + * capital ships can have tons of children on them). + * + * 31 4/01/97 1:05p John + * took out debug printf + * + * 30 4/01/97 1:03p John + * Changed fvi_ray_plane to take a dir, not two points. + * + * 29 3/24/97 4:43p John + * speed up chunked collision detection by only checking cubes the vector + * goes through. + * + * 28 3/24/97 3:55p John + * renamed fvi functions so rays and segments aren't confusing. + * + * 27 3/24/97 3:26p John + * Cleaned up and restructured model_collide code and fvi code. In fvi + * made code that finds uvs work.. Added bm_get_pixel to BmpMan. + * + * 26 3/20/97 5:19p John + * began adding a fast bounding box checks for collision detection. + * + * + * 25 2/26/97 5:28p John + * Fixed a bunch of chunked model bugs. Basically they almost work better + * than non-chunked objects! + * + * 24 2/07/97 10:45a John + * Initial bare bones implementation of blowing off turrets. + * + * 23 2/04/97 7:37p John + * Finally!!! Turret stuff working! + * + * 22 1/31/97 11:36a John + * + * 21 1/28/97 10:07a John + * More turret stuff, still not working, though. + * + * 20 1/27/97 2:35p Adam + * (John) One of the models is getting a subcall with model 10 billion or + * something so I just made it return when it gets something that big. + * + * 19 1/27/97 10:25a Mike + * Fix AVI file load error message. + * Hack physics to allow weapons to pass through a destroyed shield + * triangle. Need to have something cleaner (and faster). + * + * 18 1/24/97 4:55p John + * Started adding code for turning turrets. + * Had to change POF format to 18.0 + * + * $NoKeywords: $ + */ + + +#include + +#define MODEL_LIB + +#include "2d.h" +#include "3d.h" +#include "model.h" +#include "tmapper.h" +#include "floating.h" +#include "fvi.h" +#include "lighting.h" +#include "modelsinc.h" +#include "objcollide.h" + +#define TOL 1E-4 +#define DIST_TOL 1.0 + +// Some global variables that get set by model_collide and are used internally for +// checking a collision rather than passing a bunch of parameters around. These are +// not persistant between calls to model_collide + +static mc_info *Mc; // The mc_info passed into model_collide + +static polymodel *Mc_pm; // The polygon model we're checking +static int Mc_submodel; // The current submodel we're checking + +static matrix Mc_orient; // A matrix to rotate a world point into the current + // submodel's frame of reference. +static vector Mc_base; // A point used along with Mc_orient. + +static vector Mc_p0; // The ray origin rotated into the current submodel's frame of reference +static vector Mc_p1; // The ray end rotated into the current submodel's frame of reference +static float Mc_mag; // The length of the ray +static vector Mc_direction; // A vector from the ray's origin to it's end, in the current submodel's frame of reference + +static vector * Mc_point_list[MAX_POLYGON_VECS]; // A pointer to the current submodel's vertex list + +static float Mc_edge_time; + + +// Returns non-zero if vector from p0 to pdir +// intersects the bounding box. +// hitpos could be NULL, so don't fill it if it is. +int mc_ray_boundingbox( vector *min, vector *max, vector * p0, vector *pdir, vector *hitpos ) +{ + + vector tmp_hitpos; + if ( hitpos == NULL ) { + hitpos = &tmp_hitpos; + } + + + if ( Mc->flags & MC_CHECK_SPHERELINE ) { + + // In the case of a sphere, just increase the size of the box by the radius + // of the sphere in all directions. + + vector sphere_mod_min, sphere_mod_max; + + sphere_mod_min.x = min->x - Mc->radius; + sphere_mod_max.x = max->x + Mc->radius; + sphere_mod_min.y = min->y - Mc->radius; + sphere_mod_max.y = max->y + Mc->radius; + sphere_mod_min.z = min->z - Mc->radius; + sphere_mod_max.z = max->z + Mc->radius; + + return fvi_ray_boundingbox( &sphere_mod_min, &sphere_mod_max, p0, pdir, hitpos ); + } else { + return fvi_ray_boundingbox( min, max, p0, pdir, hitpos ); + } +} + + + +// ----- +// mc_check_face +// nv -- number of vertices +// verts -- actual vertices +// plane_pnt -- A point on the plane. Could probably use the first vertex. +// plane_norm -- normal of the plane +// uvl_list -- list of uv coords for the poly. +// ntmap -- The tmap index into the model's textures array. +// +// detects whether or not a vector has collided with a polygon. vector points stored in global +// Mc_p0 and Mc_p1. Results stored in global mc_info * Mc. + +static void mc_check_face(int nv, vector **verts, vector *plane_pnt, float face_rad, vector *plane_norm, uv_pair *uvl_list, int ntmap, ubyte *poly) +{ + vector hit_point; + float dist; + float u, v; + + // Check to see if poly is facing away from ray. If so, don't bother + // checking it. + if (vm_vec_dot(&Mc_direction,plane_norm) > 0.0f) { + return; + } + + // Find the intersection of this ray with the plane that the poly + dist = fvi_ray_plane(NULL, plane_pnt, plane_norm, &Mc_p0, &Mc_direction, 0.0f); + + if ( dist < 0.0f ) return; // If the ray is behind the plane there is no collision + if ( !(Mc->flags & MC_CHECK_RAY) && (dist > 1.0f) ) return; // The ray isn't long enough to intersect the plane + + // If the ray hits, but a closer intersection has already been found, return + if ( Mc->num_hits && (dist >= Mc->hit_dist ) ) return; + + // Find the hit point + vm_vec_scale_add( &hit_point, &Mc_p0, &Mc_direction, dist ); + + // Check to see if the point of intersection is on the plane. If so, this + // also finds the uv's where the ray hit. + if ( fvi_point_face(&hit_point, nv, verts, plane_norm, &u,&v, uvl_list ) ) { + Mc->hit_dist = dist; + + Mc->hit_point = hit_point; + Mc->hit_submodel = Mc_submodel; + + Mc->hit_normal = *plane_norm; + + if ( uvl_list ) { + Mc->hit_u = u; + Mc->hit_v = v; + Mc->hit_bitmap = Mc_pm->textures[ntmap]; + } + + if(ntmap >= 0){ + Mc->t_poly = poly; + Mc->f_poly = NULL; + } else { + Mc->t_poly = NULL; + Mc->f_poly = poly; + } + +// mprintf(( "Bing!\n" )); + + Mc->num_hits++; + } +} + +// ---------------------------------------------------------------------------------------------------------- +// check face with spheres +// +// inputs: nv => number of vertices +// verts => array of vertices +// plane_pnt => center point in plane (about which radius is measured) +// face_rad => radius of face +// plane_norm => normal of face +static void mc_check_sphereline_face( int nv, vector ** verts, vector * plane_pnt, float face_rad, vector * plane_norm, uv_pair * uvl_list, int ntmap, ubyte *poly) +{ + vector hit_point; + float u, v; + float delta_t; // time sphere takes to cross from one side of plane to the other + float face_t; // time at which face touches plane + // NOTE all times are normalized so that t = 1.0 at the end of the frame + int check_face = 1; // assume we'll check the face. + int check_edges = 1; // assume we'll check the edges. + + // Check to see if poly is facing away from ray. If so, don't bother + // checking it. + + if (vm_vec_dot(&Mc_direction,plane_norm) > 0.0f) { + return; + } + + // Find the intersection of this sphere with the plane of the poly + if ( !fvi_sphere_plane( &hit_point, &Mc_p0, &Mc_direction, Mc->radius, plane_norm, plane_pnt, &face_t, &delta_t ) ) { + return; + } + + if ( face_t < 0 || face_t > 1) { + check_face = 0; // If the ray is behind the plane there is no collision + } + + if ( !(Mc->flags & MC_CHECK_RAY) && (face_t > 1.0f) ) { + check_face = 0; // The ray isn't long enough to intersect the plane + } + + // If the ray hits, but a closer intersection has already been found, don't check face + if ( Mc->num_hits && (face_t >= Mc->hit_dist ) ) { + check_face = 0; // The ray isn't long enough to intersect the plane + } + + + vector temp_sphere; + vector temp_dir; + float temp_dist; + // DA 11/5/97 Above is used to test distance between hit_point and sphere_hit_point. + // This can be as large as 0.003 on a unit sphere. I suspect that with larger spheres, + // both the relative and absolute error decrease, but this should still be checked for the + // case of larger spheres (about 5-10 units). The error also depends on the geometry of the + // object we're colliding against, but I think to a lesser degree. + + if ( check_face ) { + // Find the time of the sphere surface touches the plane + // If this is within the collision window, check to see if we hit a face + if ( fvi_point_face(&hit_point, nv, verts, plane_norm, &u, &v, uvl_list) ) { + + Mc->hit_dist = face_t; + Mc->hit_point = hit_point; + Mc->hit_normal = *plane_norm; + Mc->hit_submodel = Mc_submodel; + Mc->edge_hit = 0; + + if ( uvl_list ) { + Mc->hit_u = u; + Mc->hit_v = v; + Mc->hit_bitmap = Mc_pm->textures[ntmap]; + } + + if(ntmap >= 0){ + Mc->t_poly = poly; + Mc->f_poly = NULL; + } else { + Mc->t_poly = NULL; + Mc->f_poly = poly; + } + + Mc->num_hits++; + check_edges = 0; + vm_vec_scale_add( &temp_sphere, &Mc_p0, &Mc_direction, Mc->hit_dist ); + temp_dist = vm_vec_dist( &temp_sphere, &hit_point ); + if ( (temp_dist - DIST_TOL > Mc->radius) || (temp_dist + DIST_TOL < Mc->radius) ) { + // get Andsager + mprintf(("Estimated radius error: Estimate %f, actual %f Mc->radius\n", temp_dist, Mc->radius)); + } + vm_vec_sub( &temp_dir, &hit_point, &temp_sphere ); + // Assert( vm_vec_dotprod( &temp_dir, &Mc_direction ) > 0 ); + } + } + + + if ( check_edges ) { + + // Either (face_t) is out of range or we miss the face + // Check for sphere hitting edge + + // If checking shields, we *still* need to check edges + + // First check whether sphere can hit edge in allowed time range + if ( face_t > 1.0f || face_t+delta_t < 0.0f ) { + return; + } + + // this is where we need another test to cull checking for edges + // PUT TEST HERE + + // check each edge to see if we hit, find the closest edge + // Mc->hit_dist stores the best edge time of *all* faces + float sphere_time; + if ( fvi_polyedge_sphereline(&hit_point, &Mc_p0, &Mc_direction, Mc->radius, nv, verts, &sphere_time)) { + + Assert( sphere_time >= 0.0f ); + vm_vec_scale_add( &temp_sphere, &Mc_p0, &Mc_direction, sphere_time ); + temp_dist = vm_vec_dist( &temp_sphere, &hit_point ); + if ( (temp_dist - DIST_TOL > Mc->radius) || (temp_dist + DIST_TOL < Mc->radius) ) { + // get Andsager + mprintf(("Estimated radius error: Estimate %f, actual %f Mc->radius\n", temp_dist, Mc->radius)); + } + vm_vec_sub( &temp_dir, &hit_point, &temp_sphere ); +// Assert( vm_vec_dotprod( &temp_dir, &Mc_direction ) > 0 ); + + if ( (Mc->num_hits==0) || (sphere_time < Mc->hit_dist) ) { + // This is closer than best so far + Mc->hit_dist = sphere_time; + Mc->hit_point = hit_point; + Mc->hit_submodel = Mc_submodel; + Mc->edge_hit = 1; + Mc->hit_bitmap = Mc_pm->textures[ntmap]; + + if(ntmap >= 0){ + Mc->t_poly = poly; + Mc->f_poly = NULL; + } else { + Mc->t_poly = NULL; + Mc->f_poly = poly; + } + + Mc->num_hits++; + + // nprintf(("Physics", "edge sphere time: %f, normal: (%f, %f, %f) hit_point: (%f, %f, %f)\n", sphere_time, + // Mc->hit_normal.x, Mc->hit_normal.y, Mc->hit_normal.z, + // hit_point.x, hit_point.y, hit_point.z)); + } else { // Not best so far + Assert(Mc->num_hits>0); + Mc->num_hits++; + } + } + } +} + + +// Point list +// +0 int id +// +4 int size +// +8 int n_verts +// +12 int n_norms +// +16 int offset from start of chunk to vertex data +// +20 n_verts*char norm_counts +// +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals. +void model_collide_defpoints(ubyte * p) +{ + int n; + int nverts = w(p+8); + int offset = w(p+16); + + ubyte * normcount = p+20; + vector *src = vp(p+offset); + + Assert( nverts < MAX_POLYGON_VECS ); + + for (n=0; nflags & MC_CHECK_SPHERELINE ) { + mc_check_sphereline_face(nv, points, vp(p+20), fl(p+32), vp(p+8), NULL, -1, p); + } else { + mc_check_face(nv, points, vp(p+20), fl(p+32), vp(p+8), NULL, -1, p); + } +} + + +// Textured Poly +// +0 int id +// +4 int size +// +8 vector normal +// +20 vector normal_point +// +32 int tmp = 0 +// +36 int nverts +// +40 int tmap_num +// +44 nverts*(model_tmap_vert) vertlist (n,u,v) +void model_collide_tmappoly(ubyte * p) +{ + int i; + int nv; + uv_pair uvlist[TMAP_MAX_VERTS]; + vector * points[TMAP_MAX_VERTS]; + model_tmap_vert *verts; + + nv = w(p+36); + if ( nv < 0 ) return; + + int tmap_num = w(p+40); + + if ( (!(Mc->flags & MC_CHECK_INVISIBLE_FACES)) && (Mc_pm->textures[tmap_num] < 0) ) { + // Don't check invisible polygons. + return; + } + + verts = (model_tmap_vert *)(p+44); + + for (i=0;iflags & MC_CHECK_SPHERELINE ) { + mc_check_sphereline_face(nv, points, vp(p+20), fl(p+32), vp(p+8), uvlist, tmap_num, p); + } else { + mc_check_face(nv, points, vp(p+20), fl(p+32), vp(p+8), uvlist, tmap_num, p); + } +} + + +// Sortnorms +// +0 int id +// +4 int size +// +8 vector normal +// +20 vector center +// +32 float radius +// 36 int front offset +// 40 int back offset +// 44 int prelist offset +// 48 int postlist offset +// 52 int online offset + +int model_collide_sub( void *model_ptr ); + +void model_collide_sortnorm(ubyte * p) +{ + int frontlist = w(p+36); + int backlist = w(p+40); + int prelist = w(p+44); + int postlist = w(p+48); + int onlist = w(p+52); + + if ( Mc_pm->version >= 2000 ) { + if (!mc_ray_boundingbox( vp(p+56), vp(p+68), &Mc_p0, &Mc_direction, NULL )) { + return; + } + } + + if (prelist) model_collide_sub(p+prelist); + if (backlist) model_collide_sub(p+backlist); + if (onlist) model_collide_sub(p+onlist); + if (frontlist) model_collide_sub(p+frontlist); + if (postlist) model_collide_sub(p+postlist); +} + +//calls the object interpreter to render an object. The object renderer +//is really a seperate pipeline. returns true if drew +int model_collide_sub(void *model_ptr ) +{ + ubyte *p = (ubyte *)model_ptr; + int chunk_type, chunk_size; + + chunk_type = w(p); + chunk_size = w(p+4); + + while (chunk_type != OP_EOF) { + +// mprintf(( "Processing chunk type %d, len=%d\n", chunk_type, chunk_size )); + + switch (chunk_type) { + case OP_EOF: return 1; + case OP_DEFPOINTS: model_collide_defpoints(p); break; + case OP_FLATPOLY: model_collide_flatpoly(p); break; + case OP_TMAPPOLY: model_collide_tmappoly(p); break; + case OP_SORTNORM: model_collide_sortnorm(p); break; + case OP_BOUNDBOX: + if (!mc_ray_boundingbox( vp(p+8), vp(p+20), &Mc_p0, &Mc_direction, NULL )) { + return 1; + } + break; + default: + mprintf(( "Bad chunk type %d, len=%d in model_collide_sub\n", chunk_type, chunk_size )); + Int3(); // Bad chunk type! + return 0; + } + p += chunk_size; + chunk_type = w(p); + chunk_size = w(p+4); + } + return 1; +} + + + +// checks a vector collision against a ships shield (if it has shield points defined). +void mc_check_shield() +{ + int i, j; + vector * points[3]; + float dist; + float sphere_check_closest_shield_dist = FLT_MAX; + + if ( Mc_pm->shield.ntris < 1 ) + return; + + int o; + for (o=0; o<8; o++ ) { + model_octant * poct1 = &Mc_pm->octants[o]; + + if (!mc_ray_boundingbox( &poct1->min, &poct1->max, &Mc_p0, &Mc_direction, NULL )) { + continue; + } + + for (i = 0; i < poct1->nshield_tris; i++) { + vector hitpoint; + shield_tri * tri; + tri = poct1->shield_tris[i]; + + // Check to see if Mc_pmly is facing away from ray. If so, don't bother + // checking it. + if (vm_vec_dot(&Mc_direction,&tri->norm) > 0.0f) { + continue; + } + + // get the vertices in the form the next function wants them + for (j = 0; j < 3; j++ ) + points[j] = &Mc_pm->shield.verts[tri->verts[j]].pos; + + if (!(Mc->flags & MC_CHECK_SPHERELINE) ) { // Don't do this test for sphere colliding against shields + // Find the intersection of this ray with the plane that the Mc_pmly + // lies in + dist = fvi_ray_plane(NULL, points[0],&tri->norm,&Mc_p0,&Mc_direction,0.0f); + + if ( dist < 0.0f ) continue; // If the ray is behind the plane there is no collision + if ( !(Mc->flags & MC_CHECK_RAY) && (dist > 1.0f) ) continue; // The ray isn't long enough to intersect the plane + + // Find the hit Mc_pmint + vm_vec_scale_add( &hitpoint, &Mc_p0, &Mc_direction, dist ); + + // Check to see if the Mc_pmint of intersection is on the plane. If so, this + // also finds the uv's where the ray hit. + if ( fvi_point_face(&hitpoint, 3, points, &tri->norm, NULL,NULL,NULL ) ) { + Mc->hit_dist = dist; + Mc->shield_hit_tri = tri - Mc_pm->shield.tris; + Mc->hit_point = hitpoint; + Mc->hit_normal = tri->norm; + Mc->hit_submodel = -1; + Mc->num_hits++; + return; // We hit, so we're done + } + } else { // Sphere check against shield + // This needs to look at *all* shield tris and not just return after the first hit + + // HACK HACK!! The 10000.0 is the face radius, I didn't know this, + // so I'm assume 10000 would be as big as ever. + mc_check_sphereline_face(3, points, points[0], 10000.0f, &tri->norm, NULL, 0, NULL); + if (Mc->num_hits && Mc->hit_dist < sphere_check_closest_shield_dist) { + + // same behavior whether face or edge + // normal, edge_hit, hit_point all updated thru sphereline_face + sphere_check_closest_shield_dist = Mc->hit_dist; + Mc->shield_hit_tri = tri - Mc_pm->shield.tris; + Mc->hit_submodel = -1; + Mc->num_hits++; + } + } + } + } +} + + +// This function recursively checks a submodel and its children +// for a collision with a vector. +void mc_check_subobj( int mn ) +{ + vector tempv; + vector hitpt; // used in bounding box check + bsp_info * sm; + int i; + + Assert( mn >= 0 ); + Assert( mn < Mc_pm->n_models ); + + if ( (mn < 0) || (mn>=Mc_pm->n_models) ) return; + + sm = &Mc_pm->submodel[mn]; + + // Rotate the world check points into the current subobject's + // frame of reference. + // After this block, Mc_p0, Mc_p1, Mc_direction, and Mc_mag are correct + // and relative to this subobjects' frame of reference. + vm_vec_sub(&tempv, Mc->p0, &Mc_base); + vm_vec_rotate(&Mc_p0, &tempv, &Mc_orient); + + vm_vec_sub(&tempv, Mc->p1, &Mc_base); + vm_vec_rotate(&Mc_p1, &tempv, &Mc_orient); + vm_vec_sub(&Mc_direction, &Mc_p1, &Mc_p0); + + // If we are checking the root submodel, then we might want + // to check the shield at this point + if (Mc_pm->detail[0] == mn) { + + // Do a quick out on the entire bounding box of the object + if (!mc_ray_boundingbox( &Mc_pm->mins, &Mc_pm->maxs, &Mc_p0, &Mc_direction, NULL)) { + return; + } + + // Check shield if we're supposed to + if ((Mc->flags & MC_CHECK_SHIELD) && (Mc_pm->shield.ntris > 0 )) { + // Mc->flags &= ~MC_CHECK_SPHERELINE; + // mc_check_shield(); + // int ray_num_hits = Mc->num_hits; + // Mc->num_hits = 0; + // Mc->flags |= MC_CHECK_SPHERELINE; + mc_check_shield(); + // if ( (ray_num_hits > 0) && (Mc->num_hits == 0)) + // Int3(); + return; + } + + } + + if(!(Mc->flags & MC_CHECK_MODEL)) return; + + Mc_submodel = mn; + + // Check if the ray intersects this subobject's bounding box + if (mc_ray_boundingbox(&sm->min, &sm->max, &Mc_p0, &Mc_direction, &hitpt)) { + + // The ray interects this bounding box, so we have to check all the + // polygons in this submodel. + if ( Mc->flags & MC_ONLY_BOUND_BOX ) { + float dist = vm_vec_dist( &Mc_p0, &hitpt ); + + if ( dist < 0.0f ) goto NoHit; // If the ray is behind the plane there is no collision + if ( !(Mc->flags & MC_CHECK_RAY) && (dist > Mc_mag) ) goto NoHit; // The ray isn't long enough to intersect the plane + + // If the ray hits, but a closer intersection has already been found, return + if ( Mc->num_hits && (dist >= Mc->hit_dist ) ) goto NoHit; + + Mc->hit_dist = dist; + Mc->hit_point = hitpt; + Mc->hit_submodel = Mc_submodel; + Mc->hit_bitmap = -1; + Mc->num_hits++; + } else { + model_collide_sub(sm->bsp_data); + } + } else { + //Int3(); + } + +NoHit: + + // If we're only checking one submodel, return + if (Mc->flags & MC_SUBMODEL) { + return; + } + + + // If this subobject doesn't have any children, we're done checking it. + if ( sm->num_children < 1 ) return; + + // Save instance (Mc_orient, Mc_base, Mc_point_base) + matrix saved_orient = Mc_orient; + vector saved_base = Mc_base; + + // Check all of this subobject's children + i = sm->first_child; + while ( i>-1 ) { + bsp_info * csm = &Mc_pm->submodel[i]; + + // Don't check it or its children if it is destroyed + if (!csm->blown_off) { + //instance for this subobject + matrix tm; + + vm_vec_unrotate(&Mc_base, &csm->offset, &saved_orient ); + vm_vec_add2(&Mc_base, &saved_base ); + + vm_angles_2_matrix(&tm, &csm->angs); + vm_matrix_x_matrix(&Mc_orient, &saved_orient, &tm); + + mc_check_subobj( i ); + } + + i = csm->next_sibling; + } + +} + +MONITOR(NumFVI); + +// See model.h for usage. I don't want to put the +// usage here because you need to see the #defines and structures +// this uses while reading the help. +int model_collide(mc_info * mc_info) +{ + Mc = mc_info; + + MONITOR_INC(NumFVI,1); + + Mc->num_hits = 0; // How many collisions were found + Mc->shield_hit_tri = -1; // Assume we won't hit any shield polygons + Mc->hit_bitmap = -1; + Mc->edge_hit = 0; + + if ( (Mc->flags & MC_CHECK_SHIELD) && (Mc->flags & MC_CHECK_MODEL) ) { + Error( LOCATION, "Checking both shield and model!\n" ); + return 0; + } + + //Fill in some global variables that all the model collide routines need internally. + Mc_pm = model_get(Mc->model_num); + Mc_orient = *Mc->orient; + Mc_base = *Mc->pos; + Mc_mag = vm_vec_dist( Mc->p0, Mc->p1 ); + Mc_edge_time = FLT_MAX; + + // DA 11/19/98 - disable this check for rotating submodels + // Don't do check if for very small movement +// if (Mc_mag < 0.01f) { +// return 0; +// } + + float model_radius; // How big is the model we're checking against + int first_submodel; // Which submodel gets returned as hit if MC_ONLY_SPHERE specified + + if ( (Mc->flags & MC_SUBMODEL) || (Mc->flags & MC_SUBMODEL_INSTANCE) ) { + first_submodel = Mc->submodel_num; + model_radius = Mc_pm->submodel[first_submodel].rad; + } else { + first_submodel = Mc_pm->detail[0]; + model_radius = Mc_pm->rad; + } + + if ( Mc->flags & MC_CHECK_SPHERELINE ) { + Assert( Mc->radius > 0.0f ); + + // Do a quick check on the Bounding Sphere + if (fvi_segment_sphere(&Mc->hit_point_world, Mc->p0, Mc->p1, Mc->pos, model_radius+Mc->radius) ) { + if ( Mc->flags & MC_ONLY_SPHERE ) { + Mc->hit_point = Mc->hit_point_world; + Mc->hit_submodel = first_submodel; + Mc->num_hits++; + return (Mc->num_hits > 0); + } + // continue checking polygons. + } else { + return 0; + } + } else { + int r; + + // Do a quick check on the Bounding Sphere + if ( Mc->flags & MC_CHECK_RAY ) { + r = fvi_ray_sphere(&Mc->hit_point_world, Mc->p0, Mc->p1, Mc->pos, model_radius); + } else { + r = fvi_segment_sphere(&Mc->hit_point_world, Mc->p0, Mc->p1, Mc->pos, model_radius); + } + if (r) { + if ( Mc->flags & MC_ONLY_SPHERE ) { + Mc->hit_point = Mc->hit_point_world; + Mc->hit_submodel = first_submodel; + Mc->num_hits++; + return (Mc->num_hits > 0); + } + // continue checking polygons. + } else { + return 0; + } + + } + + if ( Mc->flags & MC_SUBMODEL ) { + // Check only one subobject + mc_check_subobj( Mc->submodel_num ); + // Check submodel and any children + } else if (Mc->flags & MC_SUBMODEL_INSTANCE) { + mc_check_subobj(Mc->submodel_num); + } else { + // Check all the the highest detail model polygons and subobjects for intersections + + // Don't check it or its children if it is destroyed + if (!Mc_pm->submodel[Mc_pm->detail[0]].blown_off) { + mc_check_subobj( Mc_pm->detail[0] ); + } + } + + + //If we found a hit, then rotate it into world coordinates + if ( Mc->num_hits ) { + if ( Mc->flags & MC_SUBMODEL ) { + // If we're just checking one submodel, don't use normal instancing to find world points + vm_vec_unrotate(&Mc->hit_point_world, &Mc->hit_point, Mc->orient); + vm_vec_add2(&Mc->hit_point_world, Mc->pos); + } else { + model_find_world_point(&Mc->hit_point_world, &Mc->hit_point,Mc->model_num, Mc->hit_submodel, Mc->orient, Mc->pos); + } + } + + return Mc->num_hits; +} diff --git a/src/model/modelinterp.cpp b/src/model/modelinterp.cpp new file mode 100644 index 0000000..09f5992 --- /dev/null +++ b/src/model/modelinterp.cpp @@ -0,0 +1,3312 @@ +/* + * $Logfile: /Freespace2/code/Model/ModelInterp.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Rendering models, I think. + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 37 9/13/99 11:25p Dave + * Fixed problem with mode-switching and D3D movies. + * + * 36 9/13/99 10:09a Andsager + * Add debug console commands to lower model render detail and fireball + * LOD for big ship explosiosns. + * + * 35 9/08/99 12:03a Dave + * Make squad logos render properly in D3D all the time. Added intel anim + * directory. + * + * 34 9/01/99 10:09a Dave + * Pirate bob. + * + * 33 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 32 8/24/99 8:55p Dave + * Make sure nondimming pixels work properly in tech menu. + * + * 31 7/29/99 10:47p Dave + * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs. + * + * 30 7/29/99 12:05a Dave + * Nebula speed optimizations. + * + * 29 7/27/99 3:09p Dave + * Made g400 work. Whee. + * + * 28 7/24/99 4:19p Dave + * Fixed dumb code with briefing bitmaps. Made d3d zbuffer work much + * better. Made model code use zbuffer more intelligently. + * + * 27 7/19/99 7:20p Dave + * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula + * pre-rendering. + * + * 26 7/18/99 5:20p Dave + * Jump node icon. Fixed debris fogging. Framerate warning stuff. + * + * 25 7/15/99 2:13p Dave + * Added 32 bit detection. + * + * 24 6/22/99 7:03p Dave + * New detail options screen. + * + * 23 6/18/99 5:16p Dave + * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD + * dialog to PXO screen. + * + * 22 5/26/99 11:46a Dave + * Added ship-blasting lighting and made the randomization of lighting + * much more customizable. + * + * 21 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * 20 5/12/99 10:05a Johnson + * DKA: Fix fred bug from engine wash + * + * 19 5/08/99 8:25p Dave + * Upped object pairs. First run of nebula lightning. + * + * 18 4/26/99 8:49p Dave + * Made all pof based nebula stuff full customizable through fred. + * + * 17 4/23/99 5:53p Dave + * Started putting in new pof nebula support into Fred. + * + * 16 4/20/99 3:30p Andsager + * Let get_model_closest_box_point_with_delta() take NULL as pointer to + * is_inside + * + * 15 4/19/99 12:51p Andsager + * Add function to find the nearest point on extneded bounding box and + * check if inside bounding box. + * + * 14 3/31/99 8:24p Dave + * Beefed up all kinds of stuff, incluging beam weapons, nebula effects + * and background nebulae. Added per-ship non-dimming pixel colors. + * + * 13 3/24/99 6:14p Dave + * Added position and orientation checksumming for multiplayer. Fixed LOD + * rendering bugs for squad insignias + * + * 12 3/23/99 5:17p Dave + * Changed model file format somewhat to account for LOD's on insignias + * + * 11 3/19/99 9:51a Dave + * Checkin to repair massive source safe crash. Also added support for + * pof-style nebulae, and some new weapons code. + * + * 10 3/08/99 7:03p Dave + * First run of new object update system. Looks very promising. + * + * 9 3/02/99 9:25p Dave + * Added a bunch of model rendering debug code. Started work on fixing + * beam weapon wacky firing. + * + * 8 2/19/99 11:42a Dave + * Put in model rendering autocentering. + * + * 7 1/14/99 6:06p Dave + * 100% full squad logo support for single player and multiplayer. + * + * 6 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * 5 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 4 12/09/98 7:34p Dave + * Cleanup up nebula effect. Tweaked many values. + * + * 3 12/06/98 2:36p Dave + * Drastically improved nebula fogging. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 193 8/28/98 3:29p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 192 6/13/98 3:18p Hoffoss + * NOX()ed out a bunch of strings that shouldn't be translated. + * + * 191 5/24/98 4:53p John + * changed rounding for model caching to get rid of some empty lines in + * cached bitmap. + * + * 190 5/16/98 4:47p John + * Put back in my version 188 changes that someone so rudely (but + * hopefully unintentionally) deleted. + * + * 189 5/13/98 11:34p Mike + * Model caching system. + * + * 187 5/12/98 11:32a Mike + * Support for weapon pof detail levels. + * + * 186 5/08/98 1:32p John + * Added code for using two layered subspace effects. + * + * 185 4/29/98 11:03a John + * Added code to show octants. + * + * 184 4/22/98 9:58p John + * Added code to view invisible faces. + * + * 183 4/22/98 9:43p John + * Added code to allow checking of invisible faces, flagged by any texture + * name with invisible in it. + * + * 182 4/20/98 4:44p John + * Fixed problems with black being xparent on model cache rneders. Made + * model cache key off of detail level setting and framerate. + * + * 181 4/18/98 4:00p John + * Made highest model caching detail level disable model caching. + * + * 180 4/14/98 11:11p John + * Made ships with < 50% hull left show electrical damage arcs. + * + * 179 4/13/98 4:54p John + * Made uv rotate independently on subspace effect. Put in DCF function + * for setting subspace speeds. + * + * 178 4/12/98 5:54p John + * Made models work with subspace. Made subspace rotate also. + * + * 177 4/12/98 9:56a John + * Made lighting detail flags work. Made explosions cast light on + * highest. + * + * 176 4/11/98 6:53p John + * Added first rev of subspace effect. + * + * 175 4/10/98 5:20p John + * Changed RGB in lighting structure to be ubytes. Removed old + * not-necessary 24 bpp software stuff. + * + * 174 4/09/98 11:40p John + * Fixed some bugs with hardware lighting. + * + * 173 4/09/98 4:38p John + * Made non-darkening and transparent textures work under Glide. Fixed + * bug with Jim's computer not drawing any bitmaps. + * + * $NoKeywords: $ + */ + +#include + +#define MODEL_LIB + +#include "2d.h" +#include "3d.h" +#include "model.h" +#include "tmapper.h" +#include "floating.h" +#include "fvi.h" +#include "lighting.h" +#include "modelsinc.h" +#include "fireballs.h" +#include "fix.h" +#include "bmpman.h" +#include "systemvars.h" +#include "key.h" +#include "3dinternal.h" +#include "timer.h" +#include "grinternal.h" +#include "palman.h" +#include "object.h" // For MAX_OBJECTS +#include "missionparse.h" +#include "neb.h" + + +// Some debug variables used externally for displaying stats +#ifndef NDEBUG +int modelstats_num_polys = 0; +int modelstats_num_polys_drawn = 0; +int modelstats_num_verts = 0; +int modelstats_num_sortnorms = 0; +int modelstats_num_boxes = 0; +#endif + +// a lighting object +typedef struct model_light_object { + ubyte r[MAX_POLYGON_NORMS]; + ubyte g[MAX_POLYGON_NORMS]; + ubyte b[MAX_POLYGON_NORMS]; + int objnum; + int skip; + int skip_max; +} model_light_object; + +// ----------------------- +// Local variables +// + +// Vertices used internally to rotate model points +static vertex Interp_points[MAX_POLYGON_VECS]; +vector *Interp_verts[MAX_POLYGON_VECS]; +static int Interp_num_verts; + + +// ------------------------------------------------------------------- +// lighting save stuff +// +#define MAX_MODEL_LIGHTING_SAVE 30 +int hack_skip_max = 1; +DCF(skip, "") +{ + dc_get_arg(ARG_INT); + hack_skip_max = Dc_arg_int; +} +// model_light_object Interp_lighting_save[MAX_MODEL_LIGHTING_SAVE]; +model_light_object Interp_lighting_temp; +model_light_object *Interp_lighting = &Interp_lighting_temp; +int Interp_use_saved_lighting = 0; +int Interp_saved_lighting_full = 0; +// +// lighting save stuff +// ------------------------------------------------------------------- + + +static ubyte Interp_light_applied[MAX_POLYGON_NORMS]; +static vector *Interp_norms[MAX_POLYGON_NORMS]; +static int Interp_num_norms = 0; +static ubyte *Interp_lights; + +static float Interp_fog_level = 0.0f; + +// Stuff to control rendering parameters +static color Interp_outline_color; +static int Interp_detail_level = 0; +static uint Interp_flags = 0; +static uint Interp_tmap_flags = 0; + +// If non-zero, then the subobject gets scaled by Interp_thrust_scale. +static int Interp_thrust_scale_subobj=0; +static float Interp_thrust_scale = 0.1f; +static int Interp_thrust_bitmap = -1; +static int Interp_thrust_glow_bitmap = -1; +static float Interp_thrust_glow_noise = 1.0f; + +static int Interp_objnum = -1; + +// if != -1, use this bitmap when rendering ship insignias +static int Interp_insignia_bitmap = -1; + +// if != -1, use this bitmap when rendering with a forced texture +static int Interp_forced_bitmap = -1; + +// for rendering with the MR_ALL_XPARENT FLAG SET +static float Interp_xparent_alpha = 1.0f; + +float Interp_light = 0.0f; + +// forward references +int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check ); + +// call at the beginning of a level. after the level has been loaded +void model_level_post_init() +{ + /* + int idx; + + // reset lighting stuff + for(idx=0; idx 1.0f ) { + Interp_thrust_scale = 1.0f; + } + + polymodel * pm = model_get( model_num ); + int i; + + // If thrust is set up, use it. + for (i=0; inum_lights; i++ ) { + if ( pm->lights[i].type == BSP_LIGHT_TYPE_THRUSTER ) { + float scale = (Interp_thrust_scale-0.1f)*0.5f; + + pm->lights[i].value += (scale+Interp_thrust_glow_noise*0.2f) / 255.0f; + } + } +} + + +// Point list +// +0 int id +// +4 int size +// +8 int n_verts +// +12 int n_norms +// +16 int offset from start of chunk to vertex data +// +20 n_verts*char norm_counts +// +offset vertex data. Each vertex n is a point followed by norm_counts[n] normals. +void model_interp_defpoints(ubyte * p, polymodel *pm, bsp_info *sm) +{ + int i, n; + int nverts = w(p+8); + int offset = w(p+16); + int next_norm = 0; + + ubyte * normcount = p+20; + vertex *dest = Interp_points; + vector *src = vp(p+offset); + + // Get pointer to lights + Interp_lights = p+20+nverts; + + Assert( nverts < MAX_POLYGON_VECS ); + // Assert( nnorms < MAX_POLYGON_NORMS ); + + Interp_num_verts = nverts; + #ifndef NDEBUG + modelstats_num_verts += nverts; + #endif + +/* + static int Max_vecs = 0; + static int Max_norms = 0; + + if ( Max_vecs < nverts ) { + Max_vecs = nverts; + mprintf(( "MAX NORMS = %d\n", Max_norms )); + mprintf(( "MAX VECS = %d\n", Max_vecs )); + } + + if ( Max_norms < nnorms ) { + Max_norms = nnorms; + mprintf(( "MAX NORMS = %d\n", Max_norms )); + mprintf(( "MAX VECS = %d\n", Max_vecs )); + } +*/ + + if (Interp_thrust_scale_subobj) { + + // Only scale vertices that aren't on the "base" of + // the effect. Base is something Adam decided to be + // anything under 1.5 meters, hence the 1.5f. + float min_thruster_dist = -1.5f; + + if ( Interp_flags & MR_IS_MISSILE ) { + min_thruster_dist = 0.5f; + } + + for (n=0; nz < min_thruster_dist ) { + tmp.x = src->x * 1.0f; + tmp.y = src->y * 1.0f; + tmp.z = src->z * Interp_thrust_scale; + } else { + tmp = *src; + } + + g3_rotate_vertex(dest,&tmp); + + src++; // move to normal + + for (i=0; i 0.0f){ + float theta; + float st, ct; + + // determine theta for this vertex + theta = fl_radian(20.0f + Interp_thrust_twist2); + st = sin(theta); + ct = cos(theta); + + // twist + tmp.z = (src->z * ct) - (src->y * st); + tmp.y = (src->z * st) + (src->y * ct); + + // scale the z a bit + tmp.z += Interp_thrust_twist; + } + + g3_rotate_vertex(dest, &tmp); + */ + + g3_rotate_vertex(dest, src); + + src++; // move to normal + + for (i=0; iu = (float)atan2( R.x, -R.z) / (2.0f * 3.14159f); + if (pnt->u < 0.0) pnt->u += 1.0f; + pnt->v = 1.0f - (float)atan2( a, R.y) / 3.14159f; +} +*/ + + +// Flat Poly +// +0 int id +// +4 int size +// +8 vector normal +// +20 vector center +// +32 float radius +// +36 int nverts +// +40 byte red +// +41 byte green +// +42 byte blue +// +43 byte pad +// +44 nverts*short*short vertlist, smoothlist +void model_interp_flatpoly(ubyte * p,polymodel * pm) +{ + vertex *Interp_list[TMAP_MAX_VERTS]; + int nv = w(p+36); + + if ( nv < 0 ) return; + + #ifndef NDEBUG + modelstats_num_polys++; + #endif + + if (!g3_check_normal_facing(vp(p+20),vp(p+8)) ) return; + + + int i; + short * verts = (short *)(p+44); + + for (i=0;ir = 191; + Interp_list[i]->g = 191; + Interp_list[i]->b = 191; + } else { + Interp_list[i]->b = 191; + } + } else { + int vertnum = verts[i*2+0]; + int norm = verts[i*2+1]; + + if ( Interp_flags & MR_NO_SMOOTHING ) { + if ( D3D_enabled ) { + light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light ); + } else { + Interp_list[i]->b = light_apply( Interp_verts[vertnum], vp(p+8), Interp_light ); + } + } else { + // if we're not using saved lighting + if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) { + if ( D3D_enabled ) { + light_apply_rgb( &Interp_lighting->r[norm], &Interp_lighting->g[norm], &Interp_lighting->b[norm], Interp_verts[vertnum], vp(p+8), Interp_light ); + } else { + Interp_lighting->b[norm] = light_apply( Interp_verts[vertnum], Interp_norms[norm], Interp_light ); + } + Interp_light_applied[norm] = 1; + } + + if ( D3D_enabled ) { + Interp_list[i]->r = Interp_lighting->r[norm]; + Interp_list[i]->g = Interp_lighting->g[norm]; + Interp_list[i]->b = Interp_lighting->b[norm]; + } else { + Interp_list[i]->b = Interp_lighting->b[norm]; + } + } + } + } + + // HACK!!! FIX ME!!! I'M SLOW!!!! + if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) { + gr_set_color( *(p+40), *(p+41), *(p+42) ); + } + + if ( !(Interp_flags & MR_NO_POLYS)) { + if ( D3D_enabled ) { + g3_draw_poly( nv, Interp_list, TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB ); + } else { + g3_draw_poly( nv, Interp_list, TMAP_FLAG_GOURAUD | TMAP_FLAG_RAMP ); + } + } + + if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET)) { + int i, j; + + if ( Interp_flags & MR_SHOW_OUTLINE ) { + gr_set_color_fast( &Interp_outline_color ); + } + + for (i=0; itextures[w(p+40)]<0)) { + // Don't draw invisible polygons. + if ( !(Interp_flags & MR_SHOW_INVISIBLE_FACES)) { + return; + } else { + is_invisible = 1; + } + } + + nv = w(p+36); + +// Tmap_show_layers = 1; + + #ifndef NDEBUG + modelstats_num_polys++; + #endif + + if (!g3_check_normal_facing(vp(p+20),vp(p+8)) && !(Interp_flags & MR_NO_CULL)) return; + + if ( nv < 0 ) return; + + verts = (model_tmap_vert *)(p+44); + + for (i=0;iu = verts[i].u; + Interp_list[i]->v = verts[i].v; + + if ( Interp_subspace ) { + Interp_list[i]->v += Interp_subspace_offset_u; + Interp_list[i]->u += Interp_subspace_offset_v; + Interp_list[i]->r = Interp_subspace_r; + Interp_list[i]->g = Interp_subspace_g; + Interp_list[i]->b = Interp_subspace_b; + } else { + + // if ( !(pm->flags & PM_FLAG_ALLOW_TILING) ) { + // Assert(verts[i].u <= 1.0f ); + // Assert(verts[i].v <= 1.0f ); + // } + + // Assert( verts[i].normnum == verts[i].vertnum ); + + if ( Interp_flags & MR_NO_LIGHTING ) { + if ( D3D_enabled ) { + Interp_list[i]->r = 191; + Interp_list[i]->g = 191; + Interp_list[i]->b = 191; + } else { + Interp_list[i]->b = 191; + } + } else { + int vertnum = verts[i].vertnum; + int norm = verts[i].normnum; + + if ( Interp_flags & MR_NO_SMOOTHING ) { + if ( D3D_enabled ) { + light_apply_rgb( &Interp_list[i]->r, &Interp_list[i]->g, &Interp_list[i]->b, Interp_verts[vertnum], vp(p+8), Interp_light ); + } else { + Interp_list[i]->b = light_apply( Interp_verts[vertnum], vp(p+8), Interp_light ); + } + } else { + // if we're applying lighting as normal, and not using saved lighting + if ( !Interp_use_saved_lighting && !Interp_light_applied[norm] ) { + + if ( D3D_enabled ) { + light_apply_rgb( &Interp_lighting->r[norm], &Interp_lighting->g[norm], &Interp_lighting->b[norm], Interp_verts[vertnum], Interp_norms[norm], Interp_light ); + + } else { + int li; + ubyte l; + l = light_apply( Interp_verts[vertnum], Interp_norms[norm], Interp_light ); + + + if ( Detail.lighting > 1 ) { + // Add in precalculated muzzle flashes + float fl = i2fl(l)/255.0f; + ubyte *tmp = &Interp_lights[norm*pm->num_lights]; + + for ( li=0; linum_lights; li++ ) { + fl += i2fl(tmp[li])*pm->lights[li].value; + } + + if ( fl < 0.0f ) { + fl = 0.0f; + } else if ( fl > 1.0f ) { + fl = 1.0f; + } + + l = (ubyte)fl2i(fl*255.0f); + + } + + Interp_lighting->b[norm] = l; + } + + + Interp_light_applied[norm] = 1; + } + + if ( D3D_enabled ) { + Interp_list[i]->r = Interp_lighting->r[norm]; + Interp_list[i]->g = Interp_lighting->g[norm]; + Interp_list[i]->b = Interp_lighting->b[norm]; + } else { + Interp_list[i]->b = Interp_lighting->b[norm]; + } + } + } + } + +// Assert(verts[i].u >= 0.0f ); +// Assert(verts[i].v >= 0.0f ); + } + + #ifndef NDEBUG + modelstats_num_polys_drawn++; + #endif + + if (!(Interp_flags & MR_NO_POLYS) ) { + if ( is_invisible ) { + gr_set_color( 0, 255, 0 ); + g3_draw_poly( nv, Interp_list, 0 ); + } else if (Interp_thrust_scale_subobj) { + if ((Interp_thrust_bitmap>-1) && (Interp_thrust_scale > 0.0f) && !Pofview_running) { + gr_set_bitmap( Interp_thrust_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f ); + g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED ); + } else if(!Pofview_running){ + if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) { + gr_set_color( 128, 128, 255 ); + } + uint tflags = Interp_tmap_flags; + tflags &= (~(TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT)); + g3_draw_poly( nv, Interp_list, tflags ); + } + } else { + // all textured polys go through here + if ( Interp_tmap_flags & TMAP_FLAG_TEXTURED ) { + // subspace special case + if ( Interp_subspace && D3D_enabled ) { + gr_set_bitmap( pm->textures[w(p+40)], GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, 1.2f ); + } + // all other textures + else { + int texture; + + // if we're rendering a nebula background pof, maybe select a custom texture + if((Interp_flags & MR_FORCE_TEXTURE) && (Interp_forced_bitmap >= 0)){ + texture = Interp_forced_bitmap; + } else { + texture = pm->textures[w(p+40)]; + } + + // muzzle flashes draw xparent + if(Interp_flags & MR_ALL_XPARENT){ + gr_set_bitmap( texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Interp_xparent_alpha ); + } else { + gr_set_bitmap( texture ); + } + } + } else { + if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) { + gr_set_color( 128, 128, 128 ); + } + } + + if ( Interp_subspace ) { + g3_draw_poly( nv, Interp_list, TMAP_FLAG_TEXTURED|TMAP_FLAG_TILED|TMAP_FLAG_CORRECT ); + } else { + if(Interp_flags & MR_ALL_XPARENT){ + g3_draw_poly( nv, Interp_list, Interp_tmap_flags ); + } else { + g3_draw_poly( nv, Interp_list, Interp_tmap_flags|TMAP_FLAG_NONDARKENING ); + } + } + } + } + + if (Interp_flags & (MR_SHOW_OUTLINE|MR_SHOW_OUTLINE_PRESET) ) { + + if ( Interp_flags & MR_SHOW_OUTLINE ) { + gr_set_color_fast( &Interp_outline_color ); + } + + for (i=0; iparent==-1) ) { +// gr_set_color(128,128,0); +// g3_draw_sphere_ez( &vmd_zero_vector, pm->core_radius ); +// } + + // Draw a red pivot point + gr_set_color(128,0,0); + g3_draw_sphere_ez(&vmd_zero_vector, 2.0f ); + + // Draw a green center of mass when drawing the hull + if ( submodel && (submodel->parent==-1) ) { + gr_set_color(0,128,0); + g3_draw_sphere_ez( &pm->center_of_mass, 1.0f ); + } + + if ( submodel ) { + // Draw a blue center point + gr_set_color(0,0,128); + g3_draw_sphere_ez( &submodel->geometric_center, 0.9f ); + } + + // Draw the bounding box + int i; + vertex pts[8]; + + if ( submodel ) { + for (i=0; i<8; i++ ) { + g3_rotate_vertex( &pts[i], &submodel->bounding_box[i] ); + } + gr_set_color(128,128,128); + g3_draw_line( &pts[0], &pts[1] ); + g3_draw_line( &pts[1], &pts[2] ); + g3_draw_line( &pts[2], &pts[3] ); + g3_draw_line( &pts[3], &pts[0] ); + + g3_draw_line( &pts[4], &pts[5] ); + g3_draw_line( &pts[5], &pts[6] ); + g3_draw_line( &pts[6], &pts[7] ); + g3_draw_line( &pts[7], &pts[4] ); + + g3_draw_line( &pts[0], &pts[4] ); + g3_draw_line( &pts[1], &pts[5] ); + g3_draw_line( &pts[2], &pts[6] ); + g3_draw_line( &pts[3], &pts[7] ); + } else { + //for (i=0; i<8; i++ ) { + // g3_rotate_vertex( &pts[i], &pm->bounding_box[i] ); + //} + gr_set_color(0,255,0); + + int j; + for (j=0; j<8; j++ ) { + + vector bounding_box[8]; // caclulated fron min/max + model_calc_bound_box(bounding_box,&pm->octants[j].min,&pm->octants[j].max); + + for (i=0; i<8; i++ ) { + g3_rotate_vertex( &pts[i], &bounding_box[i] ); + } + gr_set_color(128,0,0); + g3_draw_line( &pts[0], &pts[1] ); + g3_draw_line( &pts[1], &pts[2] ); + g3_draw_line( &pts[2], &pts[3] ); + g3_draw_line( &pts[3], &pts[0] ); + + g3_draw_line( &pts[4], &pts[5] ); + g3_draw_line( &pts[5], &pts[6] ); + g3_draw_line( &pts[6], &pts[7] ); + g3_draw_line( &pts[7], &pts[4] ); + + g3_draw_line( &pts[0], &pts[4] ); + g3_draw_line( &pts[1], &pts[5] ); + g3_draw_line( &pts[2], &pts[6] ); + g3_draw_line( &pts[3], &pts[7] ); + } + } +} + +// Debug code to show all the paths of a model +void model_draw_paths( int model_num ) +{ + int i,j; + vector pnt; + polymodel * pm; + + if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) { + return; + } + + pm = model_get(model_num); + + if (pm->n_paths<1){ + return; + } + + for (i=0; in_paths; i++ ) { + vertex prev_pnt; + + for (j=0; jpaths[i].nverts; j++ ) { + // Rotate point into world coordinates + pnt = pm->paths[i].verts[j].pos; + + // Pnt is now the x,y,z world coordinates of this vert. + // For this example, I am just drawing a sphere at that + // point. + { + vertex tmp; + g3_rotate_vertex(&tmp,&pnt); + + if ( pm->paths[i].verts[j].nturrets > 0 ){ + gr_set_color( 0, 0, 255 ); // draw points covered by turrets in blue + } else { + gr_set_color( 255, 0, 0 ); + } + +// g3_draw_sphere( &tmp, pm->paths[i].verts[j].radius ); + g3_draw_sphere( &tmp, 0.5f ); + + gr_set_color( 255, 0, 0 ); + if (j){ + g3_draw_line(&prev_pnt, &tmp); + } + + prev_pnt = tmp; + } + } + } +} + + +// docking bay and fighter bay paths +void model_draw_bay_paths(int model_num) +{ + int idx, s_idx; + vector v1, v2; + vertex l1, l2; + + polymodel *pm = model_get(model_num); + if(pm == NULL){ + return; + } + + // render docking bay normals + gr_set_color(0, 255, 0); + for(idx=0; idxn_docks; idx++){ + for(s_idx=0; s_idxdocking_bays[idx].num_slots; s_idx++){ + v1 = pm->docking_bays[idx].pnt[s_idx]; + vm_vec_scale_add(&v2, &v1, &pm->docking_bays[idx].norm[s_idx], 10.0f); + + // rotate the points + g3_rotate_vertex(&l1, &v1); + g3_rotate_vertex(&l2, &v2); + + // draw the point and normal + g3_draw_sphere(&l1, 2.0); + g3_draw_line(&l1, &l2); + } + } + + // render figher bay paths + gr_set_color(0, 255, 255); + + // iterate through the paths that exist in the polymodel, searching for $bayN pathnames + for (idx = 0; idxn_paths; idx++) { + if ( !strnicmp(pm->paths[idx].name, NOX("$bay"), 4) ) { + for(s_idx=0; s_idxpaths[idx].nverts-1; s_idx++){ + v1 = pm->paths[idx].verts[s_idx].pos; + v2 = pm->paths[idx].verts[s_idx+1].pos; + + // rotate and draw + g3_rotate_vertex(&l1, &v1); + g3_rotate_vertex(&l2, &v2); + g3_draw_line(&l1, &l2); + } + } + } +} +/* +// struct that holds the indicies into path information associated with a fighter bay on a capital ship +// NOTE: Fighter bay paths are identified by the path_name $bayN (where N is numbered from 1). +// Capital ships only have ONE fighter bay on the entire ship +#define MAX_SHIP_BAY_PATHS 10 +typedef struct ship_bay { + int num_paths; // how many paths are associated with the model's fighter bay + int paths[MAX_SHIP_BAY_PATHS]; // index into polymodel->paths[] array + int arrive_flags; // bitfield, set to 1 when that path number is reserved for an arrival + int depart_flags; // bitfield, set to 1 when that path number is reserved for a departure +} ship_bay; + + typedef struct mp_vert { + vector pos; // xyz coordinates of vertex in object's frame of reference + int nturrets; // number of turrets guarding this vertex + int *turret_ids; // array of indices into ship_subsys linked list (can't index using [] though) + float radius; // How far the closest obstruction is from this vertex +} mp_vert; + +typedef struct model_path { + char name[MAX_NAME_LEN]; // name of the subsystem. Probably displayed on HUD + char parent_name[MAX_NAME_LEN]; // parent name of submodel that path is linked to in POF + int parent_submodel; + int nverts; + mp_vert *verts; + int goal; // Which of the verts is the one closest to the goal of this path + int type; // What this path takes you to... See MP_TYPE_??? defines above for details + int value; // This depends on the type. + // For MP_TYPE_UNUSED, this means nothing. + // For MP_TYPE_SUBSYS, this is the subsystem number this path takes you to. +} model_path; +*/ + + +void interp_render_arc_segment( vector *v1, vector *v2, int depth ) +{ + float d = vm_vec_dist_quick( v1, v2 ); + + if ( d < 0.30f || (depth>4) ) { + vertex p1, p2; + g3_rotate_vertex( &p1, v1 ); + g3_rotate_vertex( &p2, v2 ); + + //g3_draw_rod( v1, 0.2f, v2, 0.2f, NULL, 0); + g3_draw_line( &p1, &p2 ); + } else { + // divide in half + vector tmp; + vm_vec_avg( &tmp, v1, v2 ); + + float scaler = 0.30f; + tmp.x += (frand()-0.5f)*d*scaler; + tmp.y += (frand()-0.5f)*d*scaler; + tmp.z += (frand()-0.5f)*d*scaler; + + interp_render_arc_segment( v1, &tmp, depth+1 ); + interp_render_arc_segment( &tmp, v2, depth+1 ); + } +} + +int Interp_lightning = 1; +DCF_BOOL( Arcs, Interp_lightning ) + +int AR = 64; +int AG = 64; +int AB = 5; +int AR2 = 128; +int AG2 = 128; +int AB2 = 10; +void interp_render_lightning( polymodel *pm, bsp_info * sm ) +{ + Assert( sm->num_arcs > 0 ); + + int i; + + if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) { + return; + } + + if (!Interp_lightning) return; + +// if ( keyd_pressed[KEY_LSHIFT] ) return; +// if ( rad < 3.0f ) return; + + for (i=0; inum_arcs; i++ ) { + // pick a color based upon arc type + switch(sm->arc_type[i]){ + // "normal", Freespace 1 style arcs + case MARC_TYPE_NORMAL: + if ( (rand()>>4) & 1 ) { + gr_set_color( 64, 64, 255 ); + } else { + gr_set_color( 128, 128, 255 ); + } + break; + + // "EMP" style arcs + case MARC_TYPE_EMP: + if ( (rand()>>4) & 1 ) { + gr_set_color( AR, AG, AB ); + } else { + gr_set_color( AR2, AG2, AB2 ); + } + break; + + default: + Int3(); + } + + // render the actual arc segment + interp_render_arc_segment( &sm->arc_pts[i][0], &sm->arc_pts[i][1], 0 ); + } +} + +void model_interp_subcall(polymodel * pm, int mn, int detail_level) +{ + int i; + int zbuf_mode = gr_zbuffering_mode; + + if ( (mn < 0) || (mn>=pm->n_models) ) + return; + + Assert( mn >= 0 ); + Assert( mn < pm->n_models ); + +// mprintf(( "Name = '%s'\n", pm->submodel[mn].name )); +// char * p = pm->submodel[mn].name; + + if (pm->submodel[mn].blown_off){ + return; + } + + if (pm->submodel[mn].is_thruster ) { + if ( !(Interp_flags & MR_SHOW_THRUSTERS) ){ + return; + } + Interp_thrust_scale_subobj=1; + } else { + Interp_thrust_scale_subobj=0; + } + + g3_start_instance_angles(&pm->submodel[mn].offset, &pm->submodel[mn].angs); + if ( !(Interp_flags & MR_NO_LIGHTING ) ) { + light_rotate_all(); + } + + model_interp_sub( pm->submodel[mn].bsp_data, pm, &pm->submodel[mn], 0 ); + + if (Interp_flags & MR_SHOW_PIVOTS ) + model_draw_debug_points( pm, &pm->submodel[mn] ); + + if ( pm->submodel[mn].num_arcs ) { + interp_render_lightning( pm, &pm->submodel[mn]); + } + + i = pm->submodel[mn].first_child; + while( i>-1 ) { + if (!pm->submodel[i].is_thruster ) { + if(Interp_flags & MR_NO_ZBUFFER){ + zbuf_mode = GR_ZBUFF_NONE; + } else { + zbuf_mode = GR_ZBUFF_FULL; // read only + } + + gr_zbuffer_set(zbuf_mode); + + model_interp_subcall( pm, i, detail_level ); + } + i = pm->submodel[i].next_sibling; + } + + + + g3_done_instance(); +} + +// Returns one of the following +#define IBOX_ALL_OFF 0 +#define IBOX_ALL_ON 1 +#define IBOX_SOME_ON_SOME_OFF 2 + +int interp_box_offscreen( vector *min, vector *max ) +{ + if ( keyd_pressed[KEY_LSHIFT] ) { + return IBOX_ALL_ON; + } + + vector v[8]; + v[0].x = min->x; v[0].y = min->y; v[0].z = min->z; + v[1].x = max->x; v[1].y = min->y; v[1].z = min->z; + v[2].x = max->x; v[2].y = max->y; v[2].z = min->z; + v[3].x = min->x; v[3].y = max->y; v[3].z = min->z; + + v[4].x = min->x; v[4].y = min->y; v[4].z = max->z; + v[5].x = max->x; v[5].y = min->y; v[5].z = max->z; + v[6].x = max->x; v[6].y = max->y; v[6].z = max->z; + v[7].x = min->x; v[7].y = max->y; v[7].z = max->z; + + ubyte and_codes = 0xff; + ubyte or_codes = 0xff; + int i; + + for (i=0; i<8; i++ ) { + vertex tmp; + ubyte codes=g3_rotate_vertex( &tmp, &v[i] ); +// Early out which we cannot do because we want to differentiate btwn +// IBOX_SOME_ON_SOME_OFF and IBOX_ALL_ON +// if ( !codes ) { +// //mprintf(( "A point is inside, so render it.\n" )); +// return 0; // this point is in, so return 0 +// } + or_codes |= codes; + and_codes &= codes; + } + + // If and_codes is set this means that all points lie off to the + // same side of the screen. + if (and_codes) { + //mprintf(( "All points offscreen, so don't render it.\n" )); + return IBOX_ALL_OFF; //all points off screen + } + + // If this is set it means at least one of the points is offscreen, + // but they aren't all off to the same side. + if (or_codes) { + return IBOX_SOME_ON_SOME_OFF; + } + + // They are all onscreen. + return IBOX_ALL_ON; +} + + +//calls the object interpreter to render an object. +//returns true if drew +int model_interp_sub(void *model_ptr, polymodel * pm, bsp_info *sm, int do_box_check ) +{ + ubyte *p = (ubyte *)model_ptr; + int chunk_type, chunk_size; + int pushed = 0; + + chunk_type = w(p); + chunk_size = w(p+4); + + while ( chunk_type != OP_EOF ) { + +// mprintf(( "Processing chunk type %d, len=%d\n", chunk_type, chunk_size )); + + switch (chunk_type) { + case OP_EOF: return 1; + case OP_DEFPOINTS: model_interp_defpoints(p,pm,sm); break; + case OP_FLATPOLY: model_interp_flatpoly(p,pm); break; + case OP_TMAPPOLY: model_interp_tmappoly(p,pm); break; + case OP_SORTNORM: model_interp_sortnorm(p,pm,sm,do_box_check); break; + + case OP_BOUNDBOX: + + if ( do_box_check ) { + int retval = interp_box_offscreen( vp(p+8), vp(p+20) ); + switch( retval ) { + case IBOX_ALL_OFF: + goto DoneWithThis; // Don't need to draw any more polys from this box + break; + + case IBOX_ALL_ON: + do_box_check = 0; // Don't need to check boxes any more + break; + + case IBOX_SOME_ON_SOME_OFF: + // continue like we were + break; + default: + Int3(); + } + } + + + if (Interp_flags & MR_SHOW_PIVOTS ) { + #ifndef NDEBUG + modelstats_num_boxes++; + #endif + interp_draw_box( vp(p+8), vp(p+20) ); + } + + if ( !(Interp_flags & MR_NO_LIGHTING ) ) { + if ( pushed ) { + light_filter_pop(); + pushed = 0; + + } + light_filter_push_box( vp(p+8), vp(p+20) ); + pushed = 1; + } + break; + + default: + mprintf(( "Bad chunk type %d, len=%d in model_interp_sub\n", chunk_type, chunk_size )); + Int3(); // Bad chunk type! + return 0; + } + p += chunk_size; + chunk_type = w(p); + chunk_size = w(p+4); + } + +DoneWithThis: + + if ( !(Interp_flags & MR_NO_LIGHTING ) ) { + if ( pushed ) { + light_filter_pop(); + pushed = 0; + } + } + + return 1; +} + + +void model_render_shields( polymodel * pm ) +{ + int i, j; + shield_tri *tri; + vertex pnt0, tmp, prev_pnt; + + if ( Interp_flags & MR_SHOW_OUTLINE_PRESET ) { + return; + } + + gr_set_color(0, 0, 200 ); + + // Scan all the triangles in the mesh. + for (i=0; ishield.ntris; i++ ) { + + tri = &pm->shield.tris[i]; + + if (g3_check_normal_facing(&pm->shield.verts[tri->verts[0]].pos,&tri->norm ) ) { + + // Process the vertices. + // Note this rotates each vertex each time it's needed, very dumb. + for (j=0; j<3; j++ ) { + + g3_rotate_vertex(&tmp, &pm->shield.verts[tri->verts[j]].pos ); + + if (j) + g3_draw_line(&prev_pnt, &tmp); + else + pnt0 = tmp; + prev_pnt = tmp; + } + + g3_draw_line(&pnt0, &prev_pnt); + } + } +} + +void model_render_insignias(polymodel *pm, int detail_level) +{ + int idx, s_idx; + vertex vecs[3]; + vertex *vlist[3] = { &vecs[0], &vecs[1], &vecs[2] }; + vector t1, t2, t3; + int i1, i2, i3; + + // if the model has no insignias we're done + if(pm->num_ins <= 0){ + return; + } + + // set the proper texture + if(Interp_insignia_bitmap >= 0){ + gr_set_bitmap(Interp_insignia_bitmap); + } + // otherwise don't even bother rendering + else { + return; + } + + // otherwise render them + for(idx=0; idxnum_ins; idx++){ + // skip insignias not on our detail level + if(pm->ins[idx].detail_level != detail_level){ + continue; + } + + for(s_idx=0; s_idxins[idx].num_faces; s_idx++){ + // get vertex indices + i1 = pm->ins[idx].faces[s_idx][0]; + i2 = pm->ins[idx].faces[s_idx][1]; + i3 = pm->ins[idx].faces[s_idx][2]; + + // transform vecs and setup vertices + vm_vec_add(&t1, &pm->ins[idx].vecs[i1], &pm->ins[idx].offset); + vm_vec_add(&t2, &pm->ins[idx].vecs[i2], &pm->ins[idx].offset); + vm_vec_add(&t3, &pm->ins[idx].vecs[i3], &pm->ins[idx].offset); + g3_rotate_vertex(&vecs[0], &t1); + g3_rotate_vertex(&vecs[1], &t2); + g3_rotate_vertex(&vecs[2], &t3); + + // setup texture coords + vecs[0].u = pm->ins[idx].u[s_idx][0]; vecs[0].v = pm->ins[idx].v[s_idx][0]; + vecs[1].u = pm->ins[idx].u[s_idx][1]; vecs[1].v = pm->ins[idx].v[s_idx][1]; + vecs[2].u = pm->ins[idx].u[s_idx][2]; vecs[2].v = pm->ins[idx].v[s_idx][2]; + + // draw the polygon + g3_draw_poly(3, vlist, TMAP_FLAG_TEXTURED); + } + } +} + +int Model_texturing = 1; +int Model_polys = 1; + +DCF_BOOL( model_texturing, Model_texturing ) +DCF_BOOL( model_polys, Model_polys ) + +MONITOR( NumModelsRend ); +MONITOR( NumHiModelsRend ); +MONITOR( NumMedModelsRend ); +MONITOR( NumLowModelsRend ); + + +typedef struct model_cache { + int model_num; + //matrix orient; + vector pos; + int num_lights; + + float last_dot; + + float cr; + + int w, h; + ubyte *data; + int cached_valid; + int bitmap_id; + + angles angs; + + // thrust stuff + float thrust_scale; + int thrust_bitmap; + int thrust_glow_bitmap; + float thrust_glow_noise; + + int last_frame_rendered; // last frame in which this model was rendered not from the cache +} model_cache; + +#define MAX_MODEL_CACHE MAX_OBJECTS +model_cache Model_cache[MAX_MODEL_CACHE]; // Indexed by objnum +int Model_cache_inited = 0; + + + +// Returns 0 if not valid points +int model_cache_calc_coords(vector *pnt,float rad, float *cx, float *cy, float *cr) +{ + vertex pt; + ubyte flags; + + flags = g3_rotate_vertex(&pt,pnt); + + if (flags == 0) { + + g3_project_vertex(&pt); + + if (!(pt.flags & (PF_OVERFLOW|CC_BEHIND))) { + + *cx = pt.sx; + *cy = pt.sy; + *cr = rad*Matrix_scale.x*Canv_w2/pt.z; + + if ( *cr < 1.0f ) { + *cr = 1.0f; + } + + int x1, x2, y1, y2; + + x1 = fl2i(*cx-*cr); + if ( x1 < gr_screen.clip_left ) return 0; + x2 = fl2i(*cx+*cr); + if ( x2 > gr_screen.clip_right ) return 0; + y1 = fl2i(*cy-*cr); + if ( y1 < gr_screen.clip_top ) return 0; + y2 = fl2i(*cy+*cr); + if ( y2 > gr_screen.clip_bottom ) return 0; + + return 1; + } + } + return 0; +} + +void model_really_render(int model_num, matrix *orient, vector * pos, uint flags, int light_ignore_id ); + + +//draws a bitmap with the specified 3d width & height +//returns 1 if off screen, 0 if not +int model_get_rotated_bitmap_points(vertex *pnt,float angle, float rad, vertex *v) +{ + float sa, ca; + int i; + + Assert( G3_count == 1 ); + + + +// angle = 0.0f; + + sa = (float)sin(angle); + ca = (float)cos(angle); + + float width, height; + + width = height = rad; + + v[0].x = (-width*ca - height*sa)*Matrix_scale.x + pnt->x; + v[0].y = (-width*sa + height*ca)*Matrix_scale.y + pnt->y; + v[0].z = pnt->z; + v[0].sw = 0.0f; + v[0].u = 0.0f; + v[0].v = 0.0f; + + v[1].x = (width*ca - height*sa)*Matrix_scale.x + pnt->x; + v[1].y = (width*sa + height*ca)*Matrix_scale.y + pnt->y; + v[1].z = pnt->z; + v[1].sw = 0.0f; + v[1].u = 1.0f; + v[1].v = 0.0f; + + v[2].x = (width*ca + height*sa)*Matrix_scale.x + pnt->x; + v[2].y = (width*sa - height*ca)*Matrix_scale.y + pnt->y; + v[2].z = pnt->z; + v[2].sw = 0.0f; + v[2].u = 1.0f; + v[2].v = 1.0f; + + v[3].x = (-width*ca + height*sa)*Matrix_scale.x + pnt->x; + v[3].y = (-width*sa - height*ca)*Matrix_scale.y + pnt->y; + v[3].z = pnt->z; + v[3].sw = 0.0f; + v[3].u = 0.0f; + v[3].v = 1.0f; + + ubyte codes_and=0xff; + + float sw,z; + z = pnt->z - rad / 4.0f; + if ( z < 0.0f ) z = 0.0f; + sw = 1.0f / z; + + for (i=0; i<4; i++ ) { + //now code the four points + codes_and &= g3_code_vertex(&v[i]); + v[i].flags = 0; // mark as not yet projected + g3_project_vertex(&v[i]); + v[i].sw = sw; + } + + if (codes_and) + return 1; //1 means off screen + + return 0; +} + + +int Model_caching = 1; +DCF_BOOL( model_caching, Model_caching ); + +extern int Tmap_scan_read; // 0 = normal mapper, 1=read, 2=write + +#define MODEL_MAX_BITMAP_SIZE 128 +ubyte tmp_bitmap[MODEL_MAX_BITMAP_SIZE*MODEL_MAX_BITMAP_SIZE]; + +void mc_get_bmp( ubyte *data, int x, int y, int w, int h ) +{ + gr_lock(); + + int i,j; + + for (i = 0; i < h; i++) { + ubyte *dptr = GR_SCREEN_PTR(ubyte,x,i+y); + ubyte *sptr = data+(i*w); + for (j=0; j v2 ) return max; + + v = (v - v1)/(v2-v1); + v = v*(max-min)+min; + + return v; +} + +void model_render(int model_num, matrix *orient, vector * pos, uint flags, int objnum, int lighting_skip ) +{ + polymodel *pm = model_get(model_num); + + // maybe turn off (hardware) culling + if(flags & MR_NO_CULL){ + gr_set_cull(0); + } + + Interp_objnum = objnum; + + if ( flags & MR_NO_LIGHTING ) { + Interp_light = 1.0f; + + // never use saved lighitng for this object + model_set_saved_lighting(-1, -1); + } else if ( flags & MR_IS_ASTEROID ) { + // Dim it based on distance + float depth = vm_vec_dist_quick( pos, &Eye_position ); + if ( depth > Interp_depth_scale ) { + Interp_light = Interp_depth_scale/depth; + // If it is too far, exit + if ( Interp_light < (1.0f/32.0f) ) { + Interp_light = 0.0f; + return; + } else if ( Interp_light > 1.0f ) { + Interp_light = 1.0f; + } + } else { + Interp_light = 1.0f; + } + + // never use saved lighitng for this object + model_set_saved_lighting(-1, -1); + } else { + Interp_light = 1.0f; + + // maybe use saved lighting + model_set_saved_lighting(objnum, hack_skip_max); + } + + int num_lights = 0; + + if ( !(flags & MR_NO_LIGHTING ) ) { + if ( D3D_enabled ) { + num_lights = light_filter_push( objnum, pos, pm->rad ); + } else { + num_lights = light_filter_push( objnum, pos, pm->rad ); + } + } + + model_try_cache_render(model_num, orient, pos, flags, objnum, num_lights ); + + if ( !(flags & MR_NO_LIGHTING ) ) { + light_filter_pop(); + } + + // maybe turn culling back on + if(flags & MR_NO_CULL){ + gr_set_cull(1); + } + + // turn off fog after each model renders + if(The_mission.flags & MISSION_FLAG_FULLNEB){ + gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0); + } +} + + +void model_cache_init() +{ + if ( !Model_cache_inited ) { + int i; + + Model_cache_inited = 1; + + for (i=0; i-1) && (objnum 3) ) { + if ( mc ) { + mc->cached_valid = 0; + } + model_really_render(model_num, orient, pos, flags, objnum ); + return; + } + + Assert( mc != NULL ); + + // Fake the detail level based on framerate. + if ( 1.0f / flFrametime < Mc_framerate_lo[Model_object_caching_tmp] ) { + Model_object_caching_tmp--; + // mprintf(( "Model cache level bumped down to %d\n", Model_object_caching )); + } else if ( 1.0f / flFrametime > Mc_framerate_hi[Model_object_caching_tmp] ) { + Model_object_caching_tmp++; + // mprintf(( "Model cache level bumped up to %d\n", Model_object_caching )); + } + + int tmp_detail_level = Model_object_caching_tmp + Mc_detail_add[Detail.object_caching]; + + if ( tmp_detail_level < 0 ) { + tmp_detail_level = 0; + } else if (tmp_detail_level > MAX_DETAIL_LEVEL ) { + tmp_detail_level = MAX_DETAIL_LEVEL; + } + + if ( tmp_detail_level > 3 ) { + if ( mc ) { + mc->cached_valid = 0; + } + model_really_render(model_num, orient, pos, flags, objnum ); + return; + } + + +// static int last_one = -1; +// if ( last_one != tmp_detail_level ) { +// last_one = tmp_detail_level; +// mprintf(( "Detail level %d\n", tmp_detail_level )); +// } + +// if ( keyd_pressed[KEY_LSHIFT] ) { +// mc->cached_valid = 0; +// model_really_render(model_num, orient, pos, flags, objnum ); +// return; +// } + + +// mprintf(( "Rendering cache model\n" )); + + polymodel *pm = model_get(model_num); + vertex v[4]; + vertex *vertlist[4] = { &v[0], &v[1], &v[2], &v[3] }; + float cx, cy, cr; + vertex pt; + ubyte ccflags; + + matrix tempm, tempm2; + angles new_angles; + + vm_copy_transpose_matrix(&tempm2,orient); + vm_matrix_x_matrix(&tempm,&tempm2,&Eye_matrix); + vm_extract_angles_matrix(&new_angles, &tempm ); + + if ( !model_cache_calc_coords(pos,pm->rad, &cx, &cy, &cr) ) { + // Not onscreen, do a real render and exit + mc->cached_valid = 0; + model_really_render(model_num, orient, pos, flags, objnum ); + return; + } + + //================================================================ + // A bunch of checks to see if we need to redraw the model or not + + + vector ship_to_eye; + + vm_vec_sub( &ship_to_eye, &Eye_position, pos ); + vm_vec_normalize_safe(&ship_to_eye); + float this_dot = vm_vec_dot( &ship_to_eye, &orient->fvec ); + this_dot += vm_vec_dot( &ship_to_eye, &orient->rvec ); + + float diff = 0.0f; + + if ( !mc->cached_valid ) { + // Nothing cached + goto RedrawIt; + } + + Assert( mc->data != NULL ); + + if (Framecount - mc->last_frame_rendered > 1 + 2*(MAX_DETAIL_LEVEL - Detail.object_caching - 1)) { + goto RedrawIt; + } + + diff = fl_abs( this_dot - mc->last_dot ); + + if ( diff > Mc_viewer_pos_factor[tmp_detail_level] ) { +// mprintf(( "Redraw!!! %.4f\n", diff )); + goto RedrawIt; + } + +// if ( keyd_pressed[KEY_LSHIFT] ) { +// goto RedrawIt; +// } + + if (tmp_detail_level > 2) { + if ( mc->thrust_glow_bitmap != Interp_thrust_glow_bitmap ) { + // Engline glow bitmap changed + // mprintf(( "MC: Glow bitmap changed! %d -> %d\n", mc->thrust_glow_bitmap, Interp_thrust_glow_bitmap )); + goto RedrawIt; + } + } + + if (tmp_detail_level > 2) { + if ( cr > 4.0f ) { + float diff = fl_abs( mc->thrust_scale - Interp_thrust_scale ); + + if ( diff > 0.1f ) { + // Thruster size has changed + //mprintf(( "MC: Thruster size changed! %.2f -> %.2f\n", mc->thrust_scale, Interp_thrust_scale )); + goto RedrawIt; + } + } + } + +// if (0) { +// float diff = fl_abs( mc->thrust_glow_noise - Interp_thrust_glow_noise ); + +// if ( diff > 0.1f ) { + // Glow noise has changed + //mprintf(( "MC: Thruster glow changed! %.2f -> %.2f\n", mc->thrust_glow_noise, Interp_thrust_glow_noise )); +// goto RedrawIt; +// } +// } + + + if ( mc->model_num != model_num ) { + // Model changed + goto RedrawIt; + } + + if ( cr>mc->cr*Mc_size_factor[tmp_detail_level] ) { + // Scaling up too far + goto RedrawIt; + } + + if (tmp_detail_level > 2) { + if ( cr > 4.0f ) { + if ( !(Interp_flags & MR_NO_LIGHTING ) ) { + if (mc->num_lights != num_lights) { + // Lighting changed + goto RedrawIt; + } + } + } + } + + // This method is correct, but rotating ship makes things redraw which is too slow. + #if 0 + if ( cr > 4.0f ) { + // Check orientation + float angle_error = max( fl_abs( mc->angs.p-new_angles.p ),fl_abs( mc->angs.h-new_angles.h )); + + // Exact + //if ( angle_error > 0.075f ) { + + // Rough + if ( angle_error > 0.40f ) { + // Ship/view turned too much + //mprintf(( "Ship/view turned too much %.4f\n", angle_error )); + + goto RedrawIt; + } + } + #endif + + +// mprintf(( "Dot = %.5f\n", dot )); + +#if 0 + if (0) { + float dx, dy, dz; + + dx = vm_vec_dot( &orient->rvec, &mc->orient.rvec )+1.0f; + dy = vm_vec_dot( &orient->uvec, &mc->orient.uvec )+1.0f; + dz = vm_vec_dot( &orient->fvec, &mc->orient.fvec )+1.0f; + + float angle_error = (dx+dy+dz)*1000.0f/6.0f; + + //mprintf(( "Angle_error = %.4f\n", angle_error )); + + // Compare it to 999.75f at R = 64.0f + // 0.0000f at R = 0.0f + + float cmp_val = 999.75f; // old +// if ( is_asteroid ) { +// cmp_val = scale_it( 0.0f, 999.75f, cr, 0.0f, 64.0f ); +// } + + if ( angle_error < cmp_val ) { + // Ship turned too much + goto RedrawIt; + } + } +#endif + + + // Have a valid cache entry, mc + ccflags = g3_rotate_vertex(&pt,pos); + + if ( ccflags ) { + // offscreen + goto RedrawIt; + } + + if ( model_get_rotated_bitmap_points(&pt,mc->angs.b - new_angles.b, pm->rad, v )) { + // offscreen + goto RedrawIt; + } + + + gr_set_bitmap( mc->bitmap_id ); + + Tmap_scan_read = 2; + g3_draw_poly(4, vertlist, TMAP_FLAG_TEXTURED ); + Tmap_scan_read = 0; + + // if ( keyd_pressed[KEY_LSHIFT] ) { + // gr_set_color( 255, 0, 0 ); + // gr_pixel( fl2i(v[0].sx), fl2i(v[0].sy) ); + // } + + //if ( keyd_pressed[KEY_RSHIFT] ) { + // gr_line( fl2i(v[0].sx), fl2i(v[0].sy), fl2i(v[1].sx), fl2i(v[1].sy) ); + // gr_line( fl2i(v[1].sx), fl2i(v[1].sy), fl2i(v[2].sx), fl2i(v[2].sy) ); + // gr_line( fl2i(v[2].sx), fl2i(v[2].sy), fl2i(v[3].sx), fl2i(v[3].sy) ); + // gr_line( fl2i(v[3].sx), fl2i(v[3].sy), fl2i(v[0].sx), fl2i(v[0].sy) ); + //} + + + return; + + + //========================================================== + // Cache is bad for model, so draw it and save it +RedrawIt: + + +// if ( mc->data != NULL ) { +// free(mc->data); +// mc->data = NULL; +// } + + if ( mc->bitmap_id != -1 ) { + bm_release(mc->bitmap_id); + mc->bitmap_id = -1; + } + + mc->cached_valid = 0; + mc->model_num = model_num; + mc->pos = *pos; + //mc->orient = *orient; + mc->cr = cr; + mc->angs = new_angles; //-Physics_viewer_bank; + + mc->thrust_scale = Interp_thrust_scale; + mc->thrust_bitmap = Interp_thrust_bitmap; + mc->thrust_glow_bitmap = Interp_thrust_glow_bitmap; + mc->thrust_glow_noise = Interp_thrust_glow_noise; + + mc->last_dot = this_dot; + + if ( cr > MODEL_MAX_BITMAP_SIZE/2-1 ) + goto JustDrawIt; + + //Physics_viewer_bank + + ccflags = g3_rotate_vertex(&pt,pos); + + if ( ccflags ) { + goto JustDrawIt; + } + + model_get_rotated_bitmap_points(&pt,0.0f, pm->rad, v ); + + int x1, y1, x2, y2, w, h; + + x1 = fl_round_2048( v[0].sx ); + y1 = fl_round_2048( v[0].sy ); + + x2 = fl_round_2048( v[2].sx ); //+0.5f ); + y2 = fl_round_2048( v[2].sy ); //+0.5f ); + + if ( x1 < gr_screen.clip_left) + goto JustDrawIt; + + if ( y1 < gr_screen.clip_top ) + goto JustDrawIt; + + if ( x2 > gr_screen.clip_right) + goto JustDrawIt; + + if ( y2 > gr_screen.clip_bottom) + goto JustDrawIt; + + w = x2 - x1 + 1; + if ( w < 0 ) + Int3(); + + if ( w < 2 ) + w = 2; + + h = y2 - y1 + 1; + + if ( h < 0 ) + Int3(); + + if ( h < 2 ) + h = 2; + + if ( w > MODEL_MAX_BITMAP_SIZE ) + goto JustDrawIt; + + if ( h > MODEL_MAX_BITMAP_SIZE ) + goto JustDrawIt; + + mc->w = w; + mc->h = h; + +// mprintf(( "Mallocing a %dx%d bitmap\n", w, h )); + + if ( mc->data == NULL ) { + mc->data = (ubyte *)malloc( MODEL_MAX_BITMAP_SIZE * MODEL_MAX_BITMAP_SIZE ); + } + +// mprintf(( "Done mallocing a %dx%d bitmap\n", w, h )); + + if ( mc->data == NULL ) { + goto JustDrawIt; + } + for (i = 0; i < w*h; i++) { + mc->data[i] = 255; + } + + + mc->bitmap_id = bm_create( 8, mc->w, mc->h, mc->data, 0 ); + + if ( mc->bitmap_id < 0 ) { + goto JustDrawIt; + } + + // Save stars and stuff on screen + mc_get_bmp( tmp_bitmap, x1, y1, w, h ); + + mc->num_lights = num_lights; + + // Didn't render a cached one... so render it and then save it in the cache + + // Turn on stippling + model_really_render(model_num, orient, pos, flags, objnum ); + + // Save screen to bitmap + gr_set_bitmap( mc->bitmap_id ); + Tmap_scan_read = 1; + g3_draw_poly(4, vertlist, TMAP_FLAG_TEXTURED ); + Tmap_scan_read = 0; + + // Restore stars and stuff to screen + mc_put_bmp( tmp_bitmap, x1, y1, w, h ); + + // Draw the model + gr_set_bitmap( mc->bitmap_id ); + Tmap_scan_read = 2; + g3_draw_poly(4, vertlist, TMAP_FLAG_TEXTURED ); + Tmap_scan_read = 0; + + mc->cached_valid = 1; + mc->last_frame_rendered = Framecount; + return; + +JustDrawIt: + + // Too big to save + model_really_render(model_num, orient, pos, flags, objnum ); + */ +} + +// Find the distance from p0 to the closest point on a box. +// The box's dimensions from 'min' to 'max'. +float interp_closest_dist_to_box( vector *hitpt, vector *p0, vector *min, vector *max ) +{ + float *origin = (float *)&p0->x; + float *minB = (float *)min; + float *maxB = (float *)max; + float *coord = (float *)&hitpt->x; + int inside = 1; + int i; + + for (i=0; i<3; i++ ) { + if ( origin[i] < minB[i] ) { + coord[i] = minB[i]; + inside = 0; + } else if (origin[i] > maxB[i] ) { + coord[i] = maxB[i]; + inside = 0; + } else { + coord[i] = origin[i]; + } + } + + if ( inside ) { + return 0.0f; + } + + return vm_vec_dist(hitpt,p0); +} + + +// Finds the closest point on a model to a point in space. Actually only finds a point +// on the bounding box of the model. +// Given: +// model_num Which model +// orient Orientation of the model +// pos Position of the model +// eye_pos Point that you want to find the closest point to +// Returns: +// distance from eye_pos to closest_point. 0 means eye_pos is +// on or inside the bounding box. +// Also fills in outpnt with the actual closest point. +float model_find_closest_point( vector *outpnt, int model_num, int submodel_num, matrix *orient, vector * pos, vector *eye_pos ) +{ + vector closest_pos, tempv, eye_rel_pos; + + polymodel *pm = model_get(model_num); + + if ( submodel_num < 0 ) { + submodel_num = pm->detail[0]; + } + + // Rotate eye pos into object coordinates + vm_vec_sub(&tempv,pos,eye_pos ); + vm_vec_rotate(&eye_rel_pos,&tempv,orient ); + + return interp_closest_dist_to_box( &closest_pos, &eye_rel_pos, &pm->submodel[submodel_num].min, &pm->submodel[submodel_num].max ); +} + +int tiling = 1; +DCF(tiling, "") +{ + tiling = !tiling; + if(tiling){ + dc_printf("Tiled textures\n"); + } else { + dc_printf("Non-tiled textures\n"); + } +} + +extern void d3d_zbias(int bias); +void model_really_render(int model_num, matrix *orient, vector * pos, uint flags, int light_ignore_id ) +{ + int i, detail_level; + polymodel * pm; + uint save_gr_zbuffering_mode; + int zbuf_mode; + + MONITOR_INC( NumModelsRend, 1 ); + + Interp_orient = orient; + Interp_pos = pos; + + int tmp_detail_level = Game_detail_level; + +// if ( D3D_enabled ) { +// tmp_detail_level = -1; // Force no hires models for Direct3D +// } + + // Tmap_show_layers = 1; +// model_set_detail_level(0); +// flags |= MR_LOCK_DETAIL|MR_NO_TEXTURING|MR_NO_LIGHTING; //MR_LOCK_DETAIL | |MR_NO_LIGHTING|MR_NO_SMOOTHINGMR_NO_TEXTURING | + + // Turn off engine effect + Interp_thrust_scale_subobj=0; + + if (!Model_texturing) + flags |= MR_NO_TEXTURING; + + if ( !Model_polys ) { + flags |= MR_NO_POLYS; + } + + Interp_flags = flags; + + pm = model_get(model_num); + + // Set the flags we will pass to the tmapper + if ( D3D_enabled ) { + Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB; + } else { + Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RAMP; + } + + // if we're in nebula mode + if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){ + Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG; + } + + if ( !(Interp_flags & MR_NO_TEXTURING) ) { + Interp_tmap_flags |= TMAP_FLAG_TEXTURED; + + if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling) + Interp_tmap_flags |= TMAP_FLAG_TILED; + + if ( !(Interp_flags & MR_NO_CORRECT) ) { + Interp_tmap_flags |= TMAP_FLAG_CORRECT; + } + } + + save_gr_zbuffering_mode = gr_zbuffering_mode; + zbuf_mode = gr_zbuffering_mode; + + if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) { + gr_set_color(0,128,0); + g3_draw_sphere_ez( pos, pm->rad ); + return; + } + + g3_start_instance_matrix(pos,orient); + + if ( Interp_flags & MR_SHOW_RADIUS ) { + if ( !(Interp_flags & MR_SHOW_OUTLINE_PRESET) ) { + gr_set_color(0,64,0); + g3_draw_sphere_ez(&vmd_zero_vector,pm->rad); + } + } + + Assert( pm->n_detail_levels < MAX_MODEL_DETAIL_LEVELS ); + + vector closest_pos; + float depth = model_find_closest_point( &closest_pos, model_num, -1, orient, pos, &Eye_position ); + if ( pm->n_detail_levels > 1 ) { + + if ( Interp_flags & MR_LOCK_DETAIL ) { + i = Interp_detail_level+1; + } else { + + //gr_set_color(0,128,0); + //g3_draw_sphere_ez( &closest_pos, 2.0f ); + + #if MAX_DETAIL_LEVEL != 4 + #error Code in modelInterp.cpp assumes MAX_DETAIL_LEVEL == 4 + #endif + + switch( Detail.detail_distance ) { + case 0: // lowest + depth *= 8.0f; + break; + case 1: // lower than normal + depth *= 4.0f; + break; + case 2: // default (leave the same) + break; + case 3: // above normal + depth /= 4.0f; + break; + case 4: // even more normal + depth /= 8.0f; + break; + } + + // nebula ? + if(The_mission.flags & MISSION_FLAG_FULLNEB){ + depth *= neb2_get_lod_scale(Interp_objnum); + } + + for ( i=0; in_detail_levels; i++ ) { + if ( depth<=pm->detail_depth[i] ){ + break; + } + } + + // If no valid detail depths specified, use highest. + if ( (i > 1) && (pm->detail_depth[i-1] < 1.0f)) { + i = 1; + } + } + + + // maybe force lower detail + if (Interp_flags & MR_FORCE_LOWER_DETAIL) { + i++; + } + + //detail_level = fl2i(depth/10.0f); + //detail_level = 0; + detail_level = i-1-tmp_detail_level; + + if ( detail_level < 0 ) + detail_level = 0; + else if (detail_level >= pm->n_detail_levels ) + detail_level = pm->n_detail_levels-1; + + //mprintf(( "Depth = %.2f, detail = %d\n", depth, detail_level )); + + } else { + detail_level = 0; + } + +#ifndef NDEBUG + if ( detail_level==0 ) { + MONITOR_INC( NumHiModelsRend, 1 ); + } else if ( detail_level ==pm->n_detail_levels-1 ) { + MONITOR_INC( NumLowModelsRend, 1 ); + } else { + MONITOR_INC( NumMedModelsRend, 1 ); + } +#endif + + if((Interp_flags & MR_AUTOCENTER) && (pm->flags & PM_FLAG_AUTOCEN)){ + vector auto_back = pm->autocenter; + vm_vec_scale(&auto_back, -1.0f); + g3_start_instance_matrix(&auto_back, NULL); + } + + if(gr_screen.mode == GR_DIRECT3D){ + d3d_zbias(1); + } + + // Draw the subobjects + i = pm->submodel[pm->detail[detail_level]].first_child; + + while( i>-1 ) { + if (!pm->submodel[i].is_thruster ) { + zbuf_mode = GR_ZBUFF_WRITE; + + // no zbuffering + if(Interp_flags & MR_NO_ZBUFFER){ + zbuf_mode = GR_ZBUFF_NONE; + } + + gr_zbuffer_set(zbuf_mode); + + model_interp_subcall( pm, i, detail_level ); + } + i = pm->submodel[i].next_sibling; + } + + // rotate lights for the hull + if ( !(Interp_flags & MR_NO_LIGHTING ) ) { + light_rotate_all(); + } + + if ( pm->submodel[pm->detail[detail_level]].num_children > 0 ){ + // zbuf_mode |= GR_ZBUFF_WRITE; // write only + zbuf_mode = GR_ZBUFF_FULL; + } + + // no zbuffering + if(Interp_flags & MR_NO_ZBUFFER){ + zbuf_mode = GR_ZBUFF_NONE; + } + + gr_zbuffer_set(zbuf_mode); + + if(gr_screen.mode == GR_DIRECT3D){ + d3d_zbias(0); + } + + // draw the hull of the ship + model_interp_sub( (ubyte *)pm->submodel[pm->detail[detail_level]].bsp_data, pm, &pm->submodel[pm->detail[detail_level]], 0 ); + + if (Interp_flags & MR_SHOW_PIVOTS ) { + model_draw_debug_points( pm, NULL ); + model_draw_debug_points( pm, &pm->submodel[pm->detail[detail_level]] ); + + if(pm->flags & PM_FLAG_AUTOCEN){ + gr_set_color(255, 255, 255); + g3_draw_sphere_ez(&pm->autocenter, pm->rad / 4.5f); + } + } + + if ( pm->submodel[pm->detail[0]].num_arcs ) { + interp_render_lightning( pm, &pm->submodel[pm->detail[0]]); + } + + if ( Interp_flags & MR_SHOW_SHIELDS ) { + model_render_shields(pm); + } + + // render model insignias + if(gr_screen.mode == GR_DIRECT3D){ + d3d_zbias(1); + } + gr_zbuffer_set(GR_ZBUFF_READ); + model_render_insignias(pm, detail_level); + + // zbias back to 0 + if(gr_screen.mode == GR_DIRECT3D){ + d3d_zbias(0); + } + + // Draw the thruster glow + if ( (Interp_thrust_glow_bitmap != -1) && (Interp_flags & MR_SHOW_THRUSTERS) /*&& (Detail.engine_glows)*/ ) { + + for (i = 0; i < pm->n_thrusters; i++ ) { + thruster_bank *bank = &pm->thrusters[i]; + int j; + + for ( j=0; jnum_slots; j++ ) { + float d; + vector tempv; + vm_vec_sub(&tempv,&View_position,&bank->pnt[j]); + vm_vec_normalize(&tempv); + + d = vm_vec_dot(&tempv,&bank->norm[j]); + + if ( d > 0.0f) { + vertex p; + + // Make glow bitmap fade in/out quicker from sides. + d *= 3.0f; + if ( d > 1.0f ) d = 1.0f; + + // fade them in the nebula as well + if(The_mission.flags & MISSION_FLAG_FULLNEB){ + d *= (1.0f - Interp_fog_level); + } + + //ADAM: Min throttle draws rad*MIN_SCALE, max uses max. + #define NOISE_SCALE 0.5f + #define MIN_SCALE 3.4f + #define MAX_SCALE 4.7f + float scale = MIN_SCALE; + + scale = (Interp_thrust_scale-0.1f)*(MAX_SCALE-MIN_SCALE)+MIN_SCALE; + + float w = bank->radius[j]*(scale+Interp_thrust_glow_noise*NOISE_SCALE ); + + // disable fogging + gr_fog_set(GR_FOGMODE_NONE, 0, 0, 0); + + g3_rotate_vertex( &p, &bank->pnt[j] ); + gr_set_bitmap( Interp_thrust_glow_bitmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, d ); + { + extern int Gr_scaler_zbuffering; + Gr_scaler_zbuffering = 1; + g3_draw_bitmap(&p,0,w*0.5f, TMAP_FLAG_TEXTURED ); + //g3_draw_rotated_bitmap(&p,0.0f,w,w, TMAP_FLAG_TEXTURED ); + Gr_scaler_zbuffering = 0; + } + } + + } + } + } + + Interp_thrust_glow_bitmap = -1; + + gr_set_cull(0); + + // Draw the thruster subobjects + i = pm->submodel[pm->detail[detail_level]].first_child; + while( i>-1 ) { + if (pm->submodel[i].is_thruster ) { + zbuf_mode = GR_ZBUFF_READ; + + // no zbuffering + if(Interp_flags & MR_NO_ZBUFFER){ + zbuf_mode = GR_ZBUFF_NONE; + } + + gr_zbuffer_set(zbuf_mode); + + model_interp_subcall( pm, i, detail_level ); + } + i = pm->submodel[i].next_sibling; + } + + gr_set_cull(1); + + if ( Interp_flags & MR_SHOW_PATHS ){ + model_draw_paths(model_num); + } + + if (Interp_flags & MR_BAY_PATHS ){ + model_draw_bay_paths(model_num); + } + + if((Interp_flags & MR_AUTOCENTER) && (pm->flags & PM_FLAG_AUTOCEN)){ + g3_done_instance(); + } + + g3_done_instance(); + gr_zbuffer_set(save_gr_zbuffering_mode); +} + + +void submodel_render(int model_num, int submodel_num, matrix *orient, vector * pos, uint flags, int light_ignore_id) +{ + polymodel * pm; + + MONITOR_INC( NumModelsRend, 1 ); + + if (!(Game_detail_flags & DETAIL_FLAG_MODELS) ) return; + + // Turn off engine effect + Interp_thrust_scale_subobj=0; + + if (!Model_texturing) + flags |= MR_NO_TEXTURING; + + Interp_flags = flags; + + pm = model_get(model_num); + + // Set the flags we will pass to the tmapper + if ( D3D_enabled ) { + Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RGB; + } else { + Interp_tmap_flags = TMAP_FLAG_GOURAUD | TMAP_FLAG_RAMP; + } + + // if we're in nebula mode + if((The_mission.flags & MISSION_FLAG_FULLNEB) && (Neb2_render_mode != NEB2_RENDER_NONE)){ + Interp_tmap_flags |= TMAP_FLAG_PIXEL_FOG; + } + + if ( !(Interp_flags & MR_NO_TEXTURING) ) { + Interp_tmap_flags |= TMAP_FLAG_TEXTURED; + + if ( (pm->flags & PM_FLAG_ALLOW_TILING) && tiling ) + Interp_tmap_flags |= TMAP_FLAG_TILED; + + if ( !(Interp_flags & MR_NO_CORRECT) ) { + Interp_tmap_flags |= TMAP_FLAG_CORRECT; + } + } + + if ( !(Interp_flags & MR_NO_LIGHTING ) ) { + if ( D3D_enabled ) { + light_filter_push( -1, pos, pm->submodel[submodel_num].rad ); + } else { + light_filter_push( light_ignore_id, pos, pm->submodel[submodel_num].rad ); + } + } + + g3_start_instance_matrix(pos,orient); + + if ( !(Interp_flags & MR_NO_LIGHTING ) ) { + light_rotate_all(); + } + + model_interp_sub( pm->submodel[submodel_num].bsp_data, pm, &pm->submodel[submodel_num], 0 ); + if ( pm->submodel[submodel_num].num_arcs ) { + interp_render_lightning( pm, &pm->submodel[submodel_num]); + } + + if (Interp_flags & MR_SHOW_PIVOTS ) + model_draw_debug_points( pm, &pm->submodel[submodel_num] ); + + if ( !(Interp_flags & MR_NO_LIGHTING ) ) { + light_filter_pop(); + } + g3_done_instance(); + +} + + + +// Fills in an array with points from a model. +// Only gets up to max_num verts; +// Returns number of verts found; +static int submodel_get_points_internal(int model_num, int submodel_num, int max_num, vector **pnts, vector **norms ) +{ + polymodel * pm; + + pm = model_get(model_num); + + if ( submodel_num < 0 ) { + submodel_num = pm->detail[0]; + } + + ubyte *p = pm->submodel[submodel_num].bsp_data; + int chunk_type, chunk_size; + + chunk_type = w(p); + chunk_size = w(p+4); + + while (chunk_type != OP_EOF) { + switch (chunk_type) { + case OP_EOF: return 1; + case OP_DEFPOINTS: { + int n; + int nverts = w(p+8); + int offset = w(p+16); + + ubyte * normcount = p+20; + vector *src = vp(p+offset); + + if ( nverts > max_num ) + nverts = max_num; + + for (n=0; n>5) % nv; + int vn2 = (myrand()>>5) % nv; + + *v1 = *Interp_verts[vn1]; + *v2 = *Interp_verts[vn2]; + + if(n1 != NULL){ + *n1 = *Interp_norms[vn1]; + } + if(n2 != NULL){ + *n2 = *Interp_norms[vn2]; + } +} + +// If MR_FLAG_OUTLINE bit set this color will be used for outlines. +// This defaults to black. +void model_set_outline_color(int r, int g, int b ) +{ + gr_init_color( &Interp_outline_color, r, g, b ); + +} + +// If MR_FLAG_OUTLINE bit set this color will be used for outlines. +// This defaults to black. +void model_set_outline_color_fast(void *outline_color) +{ + Interp_outline_color = *((color*)(outline_color)); +} + +// IF MR_LOCK_DETAIL is set, then it will always draw detail level 'n' +// This defaults to 0. (0=highest, larger=lower) +void model_set_detail_level(int n) +{ + Interp_detail_level = n; +} + + +// Returns number of verts in a submodel; +int submodel_get_num_verts(int model_num, int submodel_num ) +{ + polymodel * pm; + + pm = model_get(model_num); + + ubyte *p = pm->submodel[submodel_num].bsp_data; + int chunk_type, chunk_size; + + chunk_type = w(p); + chunk_size = w(p+4); + + while (chunk_type != OP_EOF) { + switch (chunk_type) { + case OP_EOF: return 0; + case OP_DEFPOINTS: { + int n=w(p+8); + return n; // Read in 'n' points + } + break; + case OP_FLATPOLY: break; + case OP_TMAPPOLY: break; + case OP_SORTNORM: break; + case OP_BOUNDBOX: break; + default: + mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_verts\n", chunk_type, chunk_size )); + Int3(); // Bad chunk type! + return 0; + } + p += chunk_size; + chunk_type = w(p); + chunk_size = w(p+4); + } + return 0; // Couldn't find 'em +} + +// Returns number of tmaps & flat polys in a submodel; +int submodel_get_num_polys_sub( ubyte *p ) +{ + int chunk_type = w(p); + int chunk_size = w(p+4); + int n = 0; + + while (chunk_type != OP_EOF) { + switch (chunk_type) { + case OP_EOF: return n; + case OP_DEFPOINTS: break; + case OP_FLATPOLY: n++; break; + case OP_TMAPPOLY: n++; break; + case OP_SORTNORM: { + int frontlist = w(p+36); + int backlist = w(p+40); + int prelist = w(p+44); + int postlist = w(p+48); + int onlist = w(p+52); + n += submodel_get_num_polys_sub(p+frontlist); + n += submodel_get_num_polys_sub(p+backlist); + n += submodel_get_num_polys_sub(p+prelist); + n += submodel_get_num_polys_sub(p+postlist ); + n += submodel_get_num_polys_sub(p+onlist ); + } + break; + case OP_BOUNDBOX: break; + default: + mprintf(( "Bad chunk type %d, len=%d in submodel_get_num_polys\n", chunk_type, chunk_size )); + Int3(); // Bad chunk type! + return 0; + } + p += chunk_size; + chunk_type = w(p); + chunk_size = w(p+4); + } + return n; +} + +// Returns number of tmaps & flat polys in a submodel; +int submodel_get_num_polys(int model_num, int submodel_num ) +{ + polymodel * pm; + + pm = model_get(model_num); + + return submodel_get_num_polys_sub( pm->submodel[submodel_num].bsp_data ); + +} + +// Sets the submodel instance data in a submodel +// If show_damaged is true it shows only damaged submodels. +// If it is false it shows only undamaged submodels. +void model_show_damaged(int model_num, int show_damaged ) +{ + polymodel * pm; + int i; + + pm = model_get(model_num); + + for (i=0; in_models; i++ ) { + bsp_info *sm = &pm->submodel[i]; + + // Set the "blown out" flags + sm->blown_off = 0; + } + + for (i=0; in_models; i++ ) { + bsp_info *sm = &pm->submodel[i]; + + // Set the "blown out" flags + if ( show_damaged ) { + if ( sm->my_replacement > -1 ) { + pm->submodel[sm->my_replacement].blown_off = 0; + sm->blown_off = 1; + } + } else { + if ( sm->my_replacement > -1 ) { + pm->submodel[sm->my_replacement].blown_off = 1; + sm->blown_off = 0; + } + } + } +} + +// set the insignia bitmap to be used when rendering a ship with an insignia (-1 switches it off altogether) +void model_set_insignia_bitmap(int bmap) +{ + Interp_insignia_bitmap = bmap; +} + +// set the forces bitmap +void model_set_forced_texture(int bmap) +{ + Interp_forced_bitmap = bmap; +} + +// set model transparency for use with MR_ALL_XPARENT +void model_set_alpha(float alpha) +{ + Interp_xparent_alpha = alpha; +} + +// see if the given texture is used by the passed model. 0 if not used, 1 if used, -1 on error +int model_find_texture(int model_num, int bitmap) +{ + polymodel * pm; + int idx; + + // get a handle to the model + pm = model_get(model_num); + if(pm == NULL){ + return -1; + } + + // find the texture + for(idx=0; idxn_textures; idx++){ + if(pm->textures[idx] == bitmap){ + return 1; + } + } + + // no texture + return 0; +} + +// find closest point on extended bounding box (the bounding box plus all the planes that make it up) +// returns closest distance to extended box +// positive return value means start_point is outside extended box +// displaces closest point an optional amount delta to the outside of the box +// closest_box_point can be NULL. +float get_model_closest_box_point_with_delta(vector *closest_box_point, vector *start_point, int modelnum, int *is_inside, float delta) +{ + int i, idx; + vector box_point, ray_direction, *extremes; + float dist, best_dist; + polymodel *pm; + int inside = 0; + int masks[6] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20}; + int mask_inside = 0x3f; + + best_dist = FLT_MAX; + pm = model_get(modelnum); + + for (i=0; i<6; i++) { + idx = i / 2; // which row vector of Identity matrix + + memcpy(&ray_direction, vmd_identity_matrix.a2d[idx], sizeof(vector)); + + // do negative, then positive plane for each axis + if (2 * idx == i) { + extremes = &pm->mins; + vm_vec_negate(&ray_direction); + } else { + extremes = &pm->maxs; + } + + // a negative distance means started outside the box + dist = fvi_ray_plane(&box_point, extremes, &ray_direction, start_point, &ray_direction, 0.0f); + if (dist > 0) { + inside |= masks[i]; + } + if (fabs(dist) < fabs(best_dist)) { + best_dist = dist; + if (closest_box_point) { + vm_vec_scale_add(closest_box_point, &box_point, &ray_direction, delta); + } + } + } + + // is start_point inside the box + if (is_inside) { + *is_inside = (inside == mask_inside); + } + + return -best_dist; +} + +// find closest point on extended bounding box (the bounding box plus all the planes that make it up) +// returns closest distance to extended box +// positive return value means start_point is outside extended box +// displaces closest point an optional amount delta to the outside of the box +// closest_box_point can be NULL. +float get_world_closest_box_point_with_delta(vector *closest_box_point, object *box_obj, vector *start_point, int *is_inside, float delta) +{ + vector temp, box_start; + float dist; + int modelnum; + + // get modelnum + modelnum = Ships[box_obj->instance].modelnum; + + // rotate start_point to box_obj RF + vm_vec_sub(&temp, start_point, &box_obj->pos); + vm_vec_rotate(&box_start, &temp, &box_obj->orient); + + dist = get_model_closest_box_point_with_delta(closest_box_point, &box_start, modelnum, is_inside, delta); + + // rotate closest_box_point to world RF + if (closest_box_point) { + vm_vec_unrotate(&temp, closest_box_point, &box_obj->orient); + vm_vec_add(closest_box_point, &temp, &box_obj->pos); + } + + return dist; +} + +void model_set_fog_level(float l) +{ + Interp_fog_level = l; +} + +// given a newly loaded model, page in all textures +void model_page_in_textures(int modelnum, int ship_info_index) +{ + int idx; + ship_info *sip; + + // valid ship type? + if((ship_info_index < 0) || (ship_info_index >= Num_ship_types)){ + return; + } + sip = &Ship_info[ship_info_index]; + + polymodel *pm = model_get(modelnum); + + // bogus + if(pm == NULL){ + return; + } + + // set nondarkening pixels + if(sip->num_nondark_colors){ + palman_set_nondarkening(sip->nondark_colors, sip->num_nondark_colors); + } + // use the colors from the default table + else { + palman_set_nondarkening(Palman_non_darkening_default, Palman_num_nondarkening_default); + } + + for (idx=0; idxn_textures; idx++ ){ + int bitmap_num = pm->original_textures[idx]; + + if ( bitmap_num > -1 ) { + // if we're in Glide (and maybe later with D3D), use nondarkening textures + if(gr_screen.mode == GR_GLIDE){ + bm_lock(bitmap_num, 16, BMP_TEX_NONDARK); + bm_unlock(bitmap_num); + } else { + bm_lock(bitmap_num, 16, BMP_TEX_OTHER); + bm_unlock(bitmap_num); + } + } + } +} + +// is the given model a pirate ship? +int model_is_pirate_ship(int modelnum) +{ + return 0; +} \ No newline at end of file diff --git a/src/model/modeloctant.cpp b/src/model/modeloctant.cpp new file mode 100644 index 0000000..45870e2 --- /dev/null +++ b/src/model/modeloctant.cpp @@ -0,0 +1,555 @@ +/* + * $Logfile: /Freespace2/code/Model/ModelOctant.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Routines for model octants + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 3 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 16 4/29/98 5:01p Mike + * Large overhaul in how turrets fire. + * + * 15 4/02/98 8:16a John + * Fixed Assert in model_collide with large ships + * + * 14 4/01/98 5:34p John + * Made only the used POFs page in for a level. Reduced some interp + * arrays. Made custom detail level work differently. + * + * 13 3/31/98 5:18p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * + * 12 10/31/97 3:19p John + * changed id field in face to be radius + * + * 11 9/10/97 11:40a John + * took out unused parts of model_octant, like faces and submodels. Made + * the vertices actually be face center points. Made the find_faces code + * save the poly center as the normal_point in the face structure. + * + * 10 8/15/97 4:10p John + * new code to use the new octrees style bsp trees + * + * 9 7/22/97 9:41a John + * Made flat faces appear in octant list, so collision detection now + * works. Made them do smoothing if needed. + * + * 8 7/03/97 9:14a John + * fixed incorrect octant vertices. + * + * 7 6/26/97 12:37p John + * fixed bug with octant submodels. disabled octant submodels correctly, + * since they aren't yet implemented. + * + * 6 6/26/97 11:19a John + * Made model face & shield collisions look only at octants it needs to. + * Shield sped up 4x, faces sped up about 2x. + * + * 5 6/26/97 9:02a Mike + * Comment out frequent mprintf(). + * + * 4 6/25/97 6:08p John + * made which_octant functions also return a pointer to the octant data. + * + * 3 6/25/97 5:34p John + * Added functions to tell which octant a point is in. + * + * 2 6/25/97 5:11p John + * added foundation for model octants. + * + * 1 6/25/97 4:07p John + * + * $NoKeywords: $ + */ + + +#include + +#define MODEL_LIB + +#include "2d.h" +#include "3d.h" +#include "model.h" +#include "tmapper.h" +#include "floating.h" +#include "fvi.h" +#include "lighting.h" +#include "modelsinc.h" + + +// returns 1 if a point is in an octant. +int point_in_octant( polymodel * pm, model_octant * oct, vector *vert ) +{ + if ( vert->x < oct->min.x ) return 0; + if ( vert->x > oct->max.x ) return 0; + + if ( vert->y < oct->min.y ) return 0; + if ( vert->y > oct->max.y ) return 0; + + if ( vert->z< oct->min.z) return 0; + if ( vert->z> oct->max.z) return 0; + + return 1; +} + + +void model_octant_find_shields( polymodel * pm, model_octant * oct ) +{ + int i, j, n; + shield_tri *tri; + + n = 0; + + // Scan all the triangles in the mesh to find how many tris there are. + for (i=0; ishield.ntris; i++ ) { + + tri = &pm->shield.tris[i]; + + for (j=0; j<3; j++ ) { + if ( point_in_octant( pm, oct, &pm->shield.verts[tri->verts[j]].pos )) { + n++; + break; + } + } + } + + //mprintf(( "Octant has %d shield polys in it\n", n )); + + oct->nshield_tris = n; + oct->shield_tris = (shield_tri **)malloc( sizeof(shield_tri *) * oct->nshield_tris ); + Assert(oct->shield_tris!=NULL); + + n = 0; + + // Rescan all the triangles in the mesh. + for (i=0; ishield.ntris; i++ ) { + + tri = &pm->shield.tris[i]; + + for (j=0; j<3; j++ ) { + if ( point_in_octant( pm, oct, &pm->shield.verts[tri->verts[j]].pos )) { + oct->shield_tris[n++] = tri; + break; + } + } + } + + Assert( oct->nshield_tris == n ); +} + + + +void moff_defpoints(ubyte * p) +{ + int n; + int nverts = w(p+8); + int offset = w(p+16); + + ubyte * normcount = p+20; + vector *src = vp(p+offset); + + Assert( nverts < MAX_POLYGON_VECS ); + // Assert( nnorms < MAX_POLYGON_NORMS ); + + for (n=0; nversion < 2003 ) { + // Set the "normal_point" part of field to be the center of the polygon + vector center_point; + vm_vec_zero( ¢er_point ); + + for (i=0;i rad ) { + rad = dist; + } + } + fl(p+32) = rad; + } + + // Put each face into a particular octant + if ( point_in_octant( pm, oct, vp(p+20) ) ) { + if (just_count) + oct->nverts++; + else + oct->verts[oct->nverts++] = vp(p+20); + return; + } +} + + +// Flat Poly +// +0 int id +// +4 int size +// +8 vector normal +// +20 vector center +// +32 float radius +// +36 int nverts +// +40 byte red +// +41 byte green +// +42 byte blue +// +43 byte pad +// +44 nverts*int vertlist +void moff_flatpoly(ubyte * p, polymodel * pm, model_octant * oct, int just_count ) +{ + int i, nv; + short *verts; + + nv = w(p+36); + if ( nv < 0 ) return; + + verts = (short *)(p+44); + + if ( pm->version < 2003 ) { + // Set the "normal_point" part of field to be the center of the polygon + vector center_point; + vm_vec_zero( ¢er_point ); + + for (i=0;i rad ) { + rad = dist; + } + } + fl(p+32) = rad; + } + + // Put each face's center point into a particular octant + if ( point_in_octant( pm, oct, vp(p+20) ) ) { + if (just_count) + oct->nverts++; + else + oct->verts[oct->nverts++] = vp(p+20); + } +} + + + +int model_octant_find_faces_sub(polymodel * pm, model_octant * oct, void *model_ptr, int just_count ) +{ + ubyte *p = (ubyte *)model_ptr; + int chunk_type, chunk_size; + + chunk_type = w(p); + chunk_size = w(p+4); + + while (chunk_type != OP_EOF) { + + switch (chunk_type) { + case OP_EOF: return 1; + case OP_DEFPOINTS: + moff_defpoints(p); + break; + case OP_FLATPOLY: moff_flatpoly(p, pm, oct, just_count ); break; + case OP_TMAPPOLY: moff_tmappoly(p, pm, oct, just_count ); break; + case OP_SORTNORM: { + int frontlist = w(p+36); + int backlist = w(p+40); + int prelist = w(p+44); + int postlist = w(p+48); + int onlist = w(p+52); + + if (prelist) model_octant_find_faces_sub(pm,oct,p+prelist,just_count); + if (backlist) model_octant_find_faces_sub(pm,oct,p+backlist,just_count); + if (onlist) model_octant_find_faces_sub(pm,oct,p+onlist,just_count); + if (frontlist) model_octant_find_faces_sub(pm,oct,p+frontlist,just_count); + if (postlist) model_octant_find_faces_sub(pm,oct,p+postlist,just_count); + } + break; + case OP_BOUNDBOX: break; + default: + mprintf(( "Bad chunk type %d, len=%d in model_octant_find_faces_sub\n", chunk_type, chunk_size )); + Int3(); // Bad chunk type! + return 0; + } + p += chunk_size; + chunk_type = w(p); + chunk_size = w(p+4); + } + return 1; +} + + +void model_octant_find_faces( polymodel * pm, model_octant * oct ) +{ + ubyte *p; + int submodel_num = pm->detail[0]; + + p = pm->submodel[submodel_num].bsp_data; + + oct->nverts = 0; + model_octant_find_faces_sub(pm, oct, p, 1 ); + + if ( oct->nverts < 1 ) { + oct->nverts = 0; + oct->verts = NULL; + return; + } + + oct->verts = (vector **)malloc( sizeof(vector *) * oct->nverts ); + Assert(oct->verts!=NULL); + + oct->nverts = 0; + model_octant_find_faces_sub(pm, oct, p, 0 ); + +// mprintf(( "Octant has %d faces\n", oct->nfaces )); +} + + +// Creates the octants for a given polygon model +void model_octant_create( polymodel * pm ) +{ + vector min, max, center; + int i, x, y, z; + + min = pm->mins; + max = pm->maxs; + + vm_vec_avg( ¢er, &min, &max ); + + for (i=0; i<8; i++ ) { + x = i & 4; + y = i & 2; + z = i & 1; + + if ( x ) { + pm->octants[i].max.x = max.x; + pm->octants[i].min.x = center.x; + } else { + pm->octants[i].max.x = center.x; + pm->octants[i].min.x = min.x; + } + + if ( y ) { + pm->octants[i].max.y = max.y; + pm->octants[i].min.y = center.y; + } else { + pm->octants[i].max.y = center.y; + pm->octants[i].min.y = min.y; + } + + if ( z ) { + pm->octants[i].max.z = max.z; + pm->octants[i].min.z = center.z; + } else { + pm->octants[i].max.z = center.z; + pm->octants[i].min.z = min.z; + } + + model_octant_find_shields( pm, &pm->octants[i] ); + model_octant_find_faces( pm, &pm->octants[i] ); + + } + +} + + +// frees the memory the octants use for a given polygon model +void model_octant_free( polymodel * pm ) +{ + int i; + + for (i=0; i<8; i++ ) { + model_octant * oct = &pm->octants[i]; + + if ( oct->verts ) { + free(oct->verts); + oct->verts = NULL; + } + + if ( oct->shield_tris ) { + free( oct->shield_tris ); + oct->shield_tris = NULL; + } + + } +} + + +// Returns which octant point pnt is closet to. This will always return +// a valid octant (0-7) since the point doesn't have to be in an octant. +// If model_orient and/or model_pos are NULL, pnt is assumed to already +// be rotated into the model's local coordinates. +// If oct is not null, it will be filled in with a pointer to the octant +// data. +int model_which_octant_distant_many( vector *pnt, int model_num,matrix *model_orient, vector * model_pos, polymodel **pm, int *octs) +{ + vector tempv, rotpnt; + + *pm = model_get(model_num); + + if ( model_orient && model_pos ) { + // First, rotate pnt into the model's frame of reference. + vm_vec_sub( &tempv, pnt, model_pos ); + vm_vec_rotate( &rotpnt, &tempv, model_orient ); + } else { + rotpnt = *pnt; + } + + vector center; + vm_vec_avg( ¢er, &((*pm)->mins), &((*pm)->maxs )); + int i, x, y, z; + + if ( rotpnt.x > center.x ) x = 1; else x = 0; + if ( rotpnt.y > center.y ) y = 1; else y = 0; + if ( rotpnt.z > center.z ) z = 1; else z = 0; + + i = ( (x<<2) | (y<<1) | z ); + + octs[0] = i; + octs[1] = i ^ 4; // Adjacent octant in x dimension + octs[2] = i ^ 2; // Adjacent octant in y dimension + octs[3] = i ^ 1; // Adjacent octant in z dimension + + return i; +} + + +// Returns which octant point pnt is closet to. This will always return +// a valid octant (0-7) since the point doesn't have to be in an octant. +// If model_orient and/or model_pos are NULL, pnt is assumed to already +// be rotated into the model's local coordinates. +// If oct is not null, it will be filled in with a pointer to the octant +// data. +int model_which_octant_distant( vector *pnt, int model_num,matrix *model_orient, vector * model_pos, model_octant **oct ) +{ + polymodel * pm; + vector tempv, rotpnt; + + pm = model_get(model_num); + + if ( model_orient && model_pos ) { + // First, rotate pnt into the model's frame of reference. + vm_vec_sub( &tempv, pnt, model_pos ); + vm_vec_rotate( &rotpnt, &tempv, model_orient ); + } else { + rotpnt = *pnt; + } + + vector center; + vm_vec_avg( ¢er, &pm->mins, &pm->maxs ); + int i, x, y, z; + + if ( rotpnt.x > center.x ) x = 1; else x = 0; + if ( rotpnt.y > center.y ) y = 1; else y = 0; + if ( rotpnt.z > center.z ) z = 1; else z = 0; + + i = ( (x<<2) | (y<<1) | z ); + + if ( oct ) + *oct = &pm->octants[i]; + + return i; +} + + + +// Returns which octant point pnt is in. This might return +// -1 if the point isn't in any octant. +// If model_orient and/or model_pos are NULL, pnt is assumed to already +// be rotated into the model's local coordinates. +// If oct is not null, it will be filled in with a pointer to the octant +// data. Or NULL if the pnt isn't in the octant. +int model_which_octant( vector *pnt, int model_num,matrix *model_orient, vector * model_pos, model_octant **oct ) +{ + polymodel * pm; + vector tempv, rotpnt; + + pm = model_get(model_num); + + if ( model_orient && model_pos ) { + // First, rotate pnt into the model's frame of reference. + vm_vec_sub( &tempv, pnt, model_pos ); + vm_vec_rotate( &rotpnt, &tempv, model_orient ); + } else { + rotpnt = *pnt; + } + + vector center; + vm_vec_avg( ¢er, &pm->mins, &pm->maxs ); + int i, x, y, z; + + if ( rotpnt.x > center.x ) x = 1; else x = 0; + if ( rotpnt.y > center.y ) y = 1; else y = 0; + if ( rotpnt.z > center.z ) z = 1; else z = 0; + + i = (x<<2) | (y<<1) | z; + + if ( point_in_octant( pm, &pm->octants[i], &rotpnt ) ) { + if ( oct ) + *oct = &pm->octants[i]; + return i; + } + + if ( oct ) + *oct = NULL; + + return -1; +} diff --git a/src/model/modelread.cpp b/src/model/modelread.cpp new file mode 100644 index 0000000..99c8179 --- /dev/null +++ b/src/model/modelread.cpp @@ -0,0 +1,3432 @@ +/* + * $Logfile: /Freespace2/code/Model/ModelRead.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * file which reads and deciphers POF information + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 36 7/22/99 2:26p Mattk + * DA: don't show engine wash error if no subsystems. (ie, Pofview...) + * + * 35 7/19/99 12:02p Andsager + * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to + * only blow up subsystem if its strength is > 0 + * + * 34 7/07/99 5:00p Andsager + * Fixed bug in stepped rotation + * + * 33 7/06/99 4:42p Anoop + * Add MiniCap hack for engine wash + * + * 32 7/06/99 3:52p Andsager + * That wacky model_subsystem name vs. subobj_name thing bites us in the + * ass again. + * + * 31 7/06/99 3:15p Anoop + * Better debug info for engine wash error. + * + * 30 7/06/99 11:37a Andsager + * Fix warning message. + * + * 29 7/06/99 10:45a Andsager + * Modify engine wash to work on any ship that is not small. Add AWACS + * ask for help. + * + * 28 7/01/99 4:23p Dave + * Full support for multiple linked ambient engine sounds. Added "big + * damage" flag. + * + * 27 6/08/99 2:33p Dave + * Fixed release build warning. Put in hud config stuff. + * + * 26 6/01/99 8:35p Dave + * Finished lockarm weapons. Added proper supercap weapons/damage. Added + * awacs-set-radius sexpression. + * + * 25 5/28/99 1:51p Andsager + * Allow models to rotate in both direction on an axis, specifying a + * negative rotation time in submodel properties. + * + * 24 5/27/99 3:48p Andsager + * Add assert to model_read code to not allow overwrite of memory + * + * 23 5/27/99 12:11p Andsager + * Allow more than 1 subsystem to have live debris on any ship + * + * 22 5/27/99 10:57a Mattk + * Put in a warning about too many firing points per turret. + * + * 21 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * 20 5/19/99 11:09a Andsager + * Turn on engine wash. Check every 1/4 sec. + * + * 19 5/11/99 10:16p Andsager + * First pass on engine wash effect. Rotation (control input), damage, + * shake. + * + * 18 4/09/99 11:38a Andsager + * Change POF format for Freespace2 + * + * 17 3/23/99 5:17p Dave + * Changed model file format somewhat to account for LOD's on insignias + * + * 16 3/20/99 3:46p Dave + * Added support for model-based background nebulae. Added 3 new + * sexpressions. + * + * 15 3/19/99 6:15p Dave + * Put in checks for insignia bitmaps for having too many faces or + * vertices + * + * 14 2/21/99 6:01p Dave + * Fixed standalone WSS packets. + * + * 13 2/19/99 11:42a Dave + * Put in model rendering autocentering. + * + * 12 2/10/99 3:52p Enricco + * Fix stupid assert + * + * 11 1/14/99 6:06p Dave + * 100% full squad logo support for single player and multiplayer. + * + * 10 1/11/99 12:42p Andsager + * Add live debris - debris which is created from a destroyed subsystem, + * when the ship is still alive + * + * 9 1/08/99 2:08p Dave + * Fixed software rendering for pofview. Super early support for AWACS and + * beam weapons. + * + * 8 1/06/99 2:24p Dave + * Stubs and release build fixes. + * + * 7 12/23/98 2:53p Andsager + * Added stepped rotation support + * + * 6 12/04/98 3:34p Andsager + * Handle norotating submodels + * + * 5 12/03/98 3:14p Andsager + * Check in code that checks rotating submodel actually has ship subsystem + * + * 4 11/19/98 11:07p Andsager + * Check in of physics and collision detection of rotating submodels + * + * 3 10/23/98 3:03p Andsager + * Initial support for changing rotation rate. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 177 8/28/98 3:29p Dave + * EMP effect done. AI effects may need some tweaking as required. + * + * 176 5/19/98 8:31p Andsager + * Added split planes (for big ship explosions) + * + * 175 5/07/98 5:39p Andsager + * Changes to model to hold cross section info + * + * 174 4/30/98 4:53p John + * Restructured and cleaned up cfile code. Added capability to read off + * of CD-ROM drive and out of multiple pack files. + * + * 173 4/22/98 9:43p John + * Added code to allow checking of invisible faces, flagged by any texture + * name with invisible in it. + * + * 172 4/14/98 11:11p John + * Made ships with < 50% hull left show electrical damage arcs. + * + * 171 4/01/98 5:34p John + * Made only the used POFs page in for a level. Reduced some interp + * arrays. Made custom detail level work differently. + * + * 170 4/01/98 9:14a Mike + * Fixed free bug + * + * 169 3/31/98 5:18p John + * Removed demo/save/restore. Made NDEBUG defined compile. Removed a + * bunch of debug stuff out of player file. Made model code be able to + * unload models and malloc out only however many models are needed. + * + * + * 168 3/17/98 2:46p Allender + * Copy subsystem name when copying model subsystems + * + * 167 3/02/98 5:42p John + * Removed WinAVI stuff from Freespace. Made all HUD gauges wriggle from + * afterburner. Made gr_set_clip work good with negative x &y. Made + * model_caching be on by default. Made each cached model have it's own + * bitmap id. Made asteroids not rotate when model_caching is on. + * + * 166 2/24/98 5:04p Allender + * allow different ship classes to use the same model. Lot's of subsystem + * stuff to deal with + * + * 165 2/16/98 3:46p John + * Made mass be proportional to area in newer pofs + * + * 164 2/13/98 5:19p John + * First rev of new model caching system. Needs to be ripped out and + * moved into ship stuff. + * + * 163 1/29/98 5:52p John + * new version of electrical arcing code + * + * 162 1/29/98 8:39a Andsager + * Changed mass and moment of intertia based area vs. volume + * + * 161 1/28/98 2:15p Mike + * Convert volume to surface area at runtime. Write note to John to + * cleanup if he modifies bspgen. + * + * 160 1/27/98 11:02a John + * Added first rev of sparks. Made all code that calls model_render call + * clear_instance first. Made debris pieces not render by default when + * clear_instance is called. + * + * 159 1/19/98 6:15p John + * Fixed all my Optimized Build compiler warnings + * + * 158 1/09/98 11:25a John + * Took the thruster animation stuff out of the model. + * + * 157 12/31/97 4:47p John + * Made all the eye points fit inside core radius + * + * 156 12/31/97 2:35p John + * Added code for core_radius + * + * 155 12/17/97 2:41p John + * made target bounding box be on hull only. + * + * 154 12/17/97 9:54a John + * added code for precalculated weapon flash lighting. + * + * 153 11/29/97 2:05p John + * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2, + * like they used to incorrectly assume. Added code to model to read in + * thruster radius's. + * + * 152 11/25/97 11:41a John + * added code to read in pof v20.05, which adds radius to thrusters. + * + * 151 10/28/97 12:34a Mike + * Set useful values of mass and inertia matrix if values not otherwise + * set. Generate Warnings for unexpected ones. + * + * 150 10/22/97 5:54p Lawrance + * get rid of leading '$' when storing parent_name for paths + * + * 149 10/20/97 5:57p Lawrance + * have index to polymodel->paths[] within the model_subsystem struct. + * + * 148 9/15/97 5:45p John + * took out chunk stuff. + * made pofview display thrusters as blue polies. + * + * 147 9/09/97 3:39p Sandeep + * warning level 4 bugs + * + * 146 9/04/97 5:09p Andsager + * implement physics using moment of inertia and mass (from BSPgen). + * Added to phys_info struct. Updated ship_info, polymodel structs. + * Updated weapon ($Mass and $Force) and ship ($Mass -> $Density) tables + * + * 145 9/03/97 5:57p Lawrance + * take out warning + * + * 144 9/03/97 4:32p John + * changed bmpman to only accept ani and pcx's. made passing .pcx or .ani + * to bm_load functions not needed. Made bmpman keep track of palettes + * for bitmaps not mapped into game palettes. + * + * 143 8/19/97 11:49p Lawrance + * using atan2_safe() in place of atan2() + * + * 142 8/19/97 8:44a John + * Initial hooks for thruster stuff. + * + * 141 8/15/97 4:10p John + * new code to use the new octrees style bsp trees + * + * 140 8/14/97 5:46p John + * added code to read in mass,center of mass, and moment of interia from + * the pof. Also added code to display green center of mass dot when Show + * debug points is on in pofview. + * + * 139 7/10/97 8:34a John + * Added code to read TGA files. + * + * 138 6/26/97 3:18p Mike + * Recognize "turret", etc. in a subsystem field anywhere in name. + * Generate warning for bogus subsystem names. + * + * 137 6/25/97 5:11p John + * added foundation for model octants. + * + * 136 6/25/97 10:28a John + * Made debris chunks radius be set correctly to the submodel size, not + * hardcoded to 10.0f. + * + * 135 6/06/97 11:55a Mike + * Fix firing direction for zero piece turrets. + * + * 134 5/31/97 2:36p Mike + * Comment out mprintfs pertaining to debris. + * + * 133 5/30/97 4:41p Hoffoss + * Made some additions required for Fred. + * + * 132 5/30/97 3:42p Mike + * Shield mesh and hit system. + * + * 131 5/30/97 10:40a Hoffoss + * Added a function for Fred to get docking point names. + * + * 130 5/29/97 4:34p Allender + * removed Int3 from model_find_dock_index. + * + * 129 4/30/97 10:41a Allender + * don't dump out token 'none' when dumping out subsystem information + * + * 128 4/17/97 6:06p John + * New code/data for v19 of BSPGEN with smoothing and zbuffer + * optimizations. + * + * 127 4/09/97 3:31p Lawrance + * if any points of bounding box don't project, return project fail code + * in model_find_2d_bound_min() + * + * 126 4/07/97 10:37a John + * Took out limits on number of children a submodel can have. (So now + * capital ships can have tons of children on them). + * + * 125 4/03/97 3:18p Allender + * + * 124 4/03/97 1:29p Allender + * subsystem enhancement. Initial work on making subsystem status + * meaningful + * + * 123 3/28/97 2:49p John + * added code to fix targetting of debris chunks to draw bounding boxes + * right. + * + * 122 3/28/97 1:19p John + * Made TestCode not show damaged submodels. + * + * 121 3/28/97 11:17a John + * added code for linking lower detail submodels to the highest one; added + * code to pofview to view these. + * + * 120 3/17/97 11:24a John + * + * 119 3/15/97 5:06p John + * added bounding boxes for each subobject and code to display them. + * + * 118 3/13/97 10:32a John + * Added code for tiled 256x256 textures in certain models. + * + * 117 3/06/97 5:36p Mike + * Change vec_normalize_safe() back to vec_normalize(). + * Spruce up docking a bit. + * + * 116 3/06/97 1:06p Allender + * more docking stuff. docking points now specified by strings in mission + * file. Changes to builtin missions and code to reflect this change. + * + * 115 3/06/97 10:56a Mike + * Write error checking version of vm_vec_normalize(). + * Fix resultant problems. + * + * 114 3/06/97 10:05a Lawrance + * added missing cfclose's + * + * 113 3/05/97 5:27p John + * more work with turret stuff + * + * 112 3/05/97 12:49p John + * added Viewer_obj. Took out the interp_??? variables for turning + * outline,etc on and put them in flags you pass to model_render. + * Cleaned up model_interp code to fit new coding styles. + * + * 111 3/05/97 12:42p Adam + * john: fixed bug while model transversel code that assumed the root + * subobject was 0. + * + * 110 3/05/97 11:09a Allender + * DOH! MAX_SLOTS just way too low. Upped to 25 and Asserts put in to + * check bounds. + * + * 109 3/04/97 5:08p John + * Started adding debug code to make it so you can view from a turret's + * view. + * + * 108 3/04/97 3:36p John + * took out old debug "h' key. Made zbuffer flag bit bit field so you + * can turn on/off each value. Fixed a bug with turret rotation where + * different handedness turrets wouldn't work. Fixed a bug with two + * large ships within each other's radius not rendering correctly. + * + * 107 3/04/97 12:52p Allender + * docking styff. Added dock type to docking structure in model code. + * Renamed structure member in ai_goals. Temporary checkin. (i.e. + * rearming will use rearm dock points) + * + * 106 3/03/97 5:47p John + * fixed some rotating object discrepencies. Took out the gun firing + * point average beause it was inconsitent and wsn't as correct as using + * the pivot point. + * + * 105 3/03/97 4:20p Hoffoss + * Fixed bug in bug I just fixed. :) + * + * 104 3/03/97 4:00p Hoffoss + * fixed bug that caused docking bays without a name to be garbage when + * loaded. + * + * 103 3/03/97 8:58a Lawrance + * replaced a fprintf() with a cfputs() + * + * 102 3/01/97 2:12p Lawrance + * made to work with new cfile changes + * + * 101 2/28/97 10:57a John + * Made so you can blow off any subsystems, not just radars. + * + * + * 100 2/27/97 3:45p John + * Added a tree dialog to pofview that shows a model's tree. + * + * 99 2/27/97 12:07p John + * Added code to suppord debris chunks after a ship is exploded.. + * + * 98 2/26/97 5:28p John + * Fixed a bunch of chunked model bugs. Basically they almost work better + * than non-chunked objects! + * + * 97 2/24/97 1:13p Allender + * support for multiple eye positions. Still some kinks to be worked out + * though.... + * + * 96 2/20/97 4:06p Allender + * allow for multiple spline paths to lead to a docking bay + * + * 95 2/20/97 3:25p Allender + * change to docking bay structure -- added a name (for use in Fred + * mainly), and index into spline paths to reach this docking point + * + * 94 2/19/97 4:03p Allender + * dump a 0.0 instead of a 0 when dumping out missing subsystem + * information + * + * 93 2/19/97 2:52p Allender + * added crewspot name to subsystems (used only with turrets). Will be + * used by fred for ai class stuff for turrets + * + * 92 2/17/97 4:30p John + * Added code to automatically rotate any subsystems with the $rotate + * value set (except for turrets) + * + * 91 2/17/97 12:03p John + * Added code to set the angles of a subobject. + * + * 90 2/14/97 4:02p John + * changed a vector being passed as value to reference. + * + * 89 2/14/97 3:16p Mike + * Start on AI path garbage collection + * + * 88 2/14/97 1:06p John + * upped bspgen version number. + * + * 87 2/13/97 10:17a John + * INCORPORATED THE FOLLOWING CHANGES: + * ??? 2/13/97 9:49a John + * added code for geometric centers of objects. + * ??? 2/12/97 6:30p John + * upped model version number. + * + * + * 86 2/13/97 9:49a John + * ROLLEDBACK TO ALLENDER'S 85 + * + * 85 2/12/97 5:34p Allender + * added a ton of debug code to help recify the subsystems in ships.tbl + * with subsystems in the models. Fixed a bug with subsystems not being + * given the correct type. + * + * 84 2/11/97 4:10p Allender + * major change of subsystem stuff. obj_subsystem renamed to + * model_subsystem. Dyamically allocate space in ship_info for + * subsystems. + * + * 83 2/10/97 4:20p Allender + * added distance to closest geometry to spline points + * + * 82 2/10/97 10:58a John + * Added back in sparks for where laser bolts hit. + * Added code to red damaged objects and replace then. + * + * 81 2/07/97 1:24p Allender + * added turret lists for spline points + * + * 80 2/06/97 2:56p John + * Added a "blown-off" field to the instance of a model. Also took out + * the model_set_angles functions and replaced with the more general + * model_set_instance functions. + * + * 79 2/04/97 7:37p John + * Finally!!! Turret stuff working! + * + * 78 1/31/97 12:51p John + * Added turret_fov and turret_fov_cos + * + * 77 1/31/97 12:43p John + * Turrets working nicely, finally! + * + * 76 1/31/97 11:36a John + * + * 75 1/29/97 4:46p John + * Code to read $crewpoint flag on turrets and store it in obj_subsystem + * info. + * + * 74 1/29/97 3:14p John + * Added field to ships.tbl to control how fast turret rotates. + * + * 73 1/28/97 11:23a John + * added functions to get/set model angles. + * + * 72 1/28/97 10:07a John + * More turret stuff, still not working, though. + * + * 71 1/27/97 2:46p John + * more turret stuff + * + * 70 1/27/97 11:35a Allender + * upped compatible object version + * + * 69 1/24/97 5:25p John + * More turret stuff. Not working yet, though. + * + * 68 1/24/97 4:55p John + * Started adding code for turning turrets. + * Had to change POF format to 18.0 + * + * 67 1/20/97 2:16p Allender + * new shield structures being used + * + * 66 1/15/97 5:19p Allender + * new POF version. old version obsolete. Added shield and eye stuff. + * New format for gun/missile/docking points + * + * 65 1/10/97 5:15p Mike + * Moved ship-specific parameters from obj_subsystem to ship_subsys. + * + * Added turret code to AI system. + * + * 64 1/10/97 2:25p John + * Added code to support detail level distances. + * + * 63 1/02/97 11:45a Allender + * changed turret code to have > 1 firing point as well as fixing the + * problems of turrets being multiple subobjects. Turret norms should be + * getting stuffed correctly now. + * + * 62 12/31/96 4:14p Mike + * Turret firing. + * Correct bug in HUDtarget.cpp, showing wrong target. + * + * 61 12/27/96 4:09p Mike + * Integrate John's new model code. + * + * 60 12/26/96 1:42p John + * Changed function ordering to put model_init at top. + * + * 59 12/23/96 3:56p John + * Changed POF code to support pof 15.0. + * + * 58 12/23/96 11:00a John + * Restructured POF stuff to support LOD in one pof. + * + * 57 12/20/96 11:55a Allender + * removed debug code + * + * 56 12/20/96 11:40a Allender + * modifed subsystem stuff to do correct hit percentages and other cool + * stuff + * + * 55 12/18/96 4:04p Allender + * added "unknown" subsystem type for subsystems which aren't recognized. + * + * 54 12/17/96 11:37a Mike + * Big ship AI. + * + * 53 12/17/96 8:59a Mike + * Path-extraction-from-mdoel test code. + * + * 52 12/13/96 5:27p John + * fixed my sloppy code that didn't free model data. + * + * 51 12/13/96 3:54p Mike + * Major improvement to model_draw_paths -- connect the dots! Uses + * sophisticated connect-to-next algorithm. + * + * 50 12/13/96 2:59p John + * Added some code to test spline paths. + * + * 49 12/13/96 2:41p John + * Added code to read in spline paths on models. + * + * 48 12/13/96 10:29a Adam + * from allender: fixed up linked list walking when checking for turrets + * in subsystems + * + * 47 12/12/96 3:38p Allender + * fix up docking special object names. Added $thruster stuff to model + * and code + * + * 46 12/12/96 2:35p Mike + * Subsystem support + * + * 45 12/12/96 12:29p Lawrance + * added function to find the 2d bound for a subobject + * + * 44 12/11/96 5:35p John + * Added variables for viewer eye. + * + * 43 12/11/96 5:15p John + * Made the dist for detail level be calculated before the object is + * instanced. + * + * 42 12/11/96 4:50p John + * Added detail levels to the non-chunky ships. + * + * 41 12/11/96 4:33p Mike + * Temporary version so other people can build. + * + * 40 12/11/96 4:24p Mike + * Fix broken code...oops! clumsy in editor! + * + * 39 12/11/96 4:17p Adam + * + * 38 12/11/96 12:57p Mike + * Support for "Big Ship" AI behavior. + * More turret stuff. + * + * 37 12/11/96 12:43p Allender + * $gun and $missile don't get appended to models gun/missile list if the + * gun/missile belongs to a turret. Turrets are special instances of + * these + * + * 36 12/11/96 11:57a Allender + * added objnum reference to obj_subsystems so that $gun and $missile + * special polygons can refernce a particular subsystem if needed (i.e. to + * get the norms, etc). + * + * 35 12/11/96 10:33a Allender + * added stuff for FOV for turrets + * + * 34 12/10/96 3:27p Allender + * + * 33 12/09/96 5:05p Allender + * made subsystem lists and model_special lists as seperate entities. + * + * 32 12/09/96 1:16p Allender + * made seperate lists for guns/missiles/subsystems, etc + * + * 31 12/09/96 11:01a Allender + * added special polygon information to models. Linked list contains + * gun/missile/docking/subsystem/etc information. + * + * $NoKeywords: $ + */ + +#include +#include + +#define MODEL_LIB + +#include "cfile.h" +#include "model.h" +#include "bmpman.h" +#include "floating.h" +#include "3d.h" +#include "ship.h" +#include "modelsinc.h" +#include "key.h" +#include "2d.h" +#include "3dinternal.h" +#include "linklist.h" +#include "timer.h" +#include "freespace.h" // For flFrameTime +#include "fvi.h" + +#define MAX_SUBMODEL_COLLISION_ROT_ANGLE (PI / 6.0f) // max 30 degrees per frame + +// info for special polygon lists + +polymodel *Polygon_models[MAX_POLYGON_MODELS]; + +static int model_initted = 0; + +#ifndef NDEBUG +CFILE *ss_fp; // file pointer used to dump subsystem information +char model_filename[_MAX_PATH]; // temp used to store filename +char debug_name[_MAX_PATH]; +int ss_warning_shown; // have we shown the warning dialog concerning the subsystems? +char Global_filename[256]; +int Model_ram = 0; // How much RAM the models use total +#endif + + + +// Anything less than this is considered incompatible. +#define PM_COMPATIBLE_VERSION 1900 + +// Anything greater than or equal to PM_COMPATIBLE_VERSION and +// whose major version is less than or equal to this is considered +// compatible. +#define PM_OBJFILE_MAJOR_VERSION 21 + +static int Model_signature = 0; + +// Free up a model, getting rid of all its memory +// Can't be called from outside of model code because more +// than one person might be using this model so we can't free +// it. +static void model_unload(int modelnum) +{ + int i,j; + + if ( (modelnum < 0) || (modelnum>MAX_POLYGON_MODELS)) { + return; + } + + polymodel *pm = Polygon_models[modelnum]; + + if ( !pm ) { + return; + } + +#ifndef NDEBUG + Model_ram -= pm->ram_used; +#endif + + if (pm->paths) { + for (i=0; in_paths; i++ ) { + for (j=0; jpaths[i].nverts; j++ ) { + if ( pm->paths[i].verts[j].turret_ids ) { + free(pm->paths[i].verts[j].turret_ids); + } + } + if (pm->paths[i].verts) { + free(pm->paths[i].verts); + } + } + free(pm->paths); + } + + if ( pm->shield.verts ) { + free( pm->shield.verts ); + } + + if ( pm->shield.tris ) { + free(pm->shield.tris); + } + + if ( pm->missile_banks ) { + free(pm->missile_banks); + } + + if ( pm->docking_bays ) { + for (i=0; in_docks; i++ ) { + if ( pm->docking_bays[i].splines ) { + free( pm->docking_bays[i].splines ); + } + } + free(pm->docking_bays); + } + + + if ( pm->thrusters ) { + free(pm->thrusters); + } + +#ifndef NDEBUG + if ( pm->debug_info ) { + free(pm->debug_info); + } +#endif + + model_octant_free( pm ); + + if (pm->submodel) { + for (i=0; in_models; i++ ) { + if ( pm->submodel[i].bsp_data ) { + free(pm->submodel[i].bsp_data); + } + } + free(pm->submodel); + } + + if ( pm->xc ) { + free(pm->xc); + } + + if ( pm->lights ) { + free(pm->lights); + } + + if ( pm->gun_banks ) { + free(pm->gun_banks); + } + + pm->id = 0; + memset( pm, 0, sizeof(polymodel)); + free( pm ); + + Polygon_models[modelnum] = NULL; +} + +void model_free_all() +{ + int i; + + if ( !model_initted) { + model_init(); + return; + } + + mprintf(( "Freeing all existing models...\n" )); + + for (i=0;isubobj_name, dest->subobj_name) ) { + dest->flags = source->flags; + dest->subobj_num = source->subobj_num; + dest->model_num = source->model_num; + dest->pnt = source->pnt; + dest->radius = source->radius; + dest->type = source->type; + dest->turn_rate = source->turn_rate; + dest->turret_gun_sobj = source->turret_gun_sobj; + + strcpy( dest->name, source->name ); + + if ( dest->type == SUBSYSTEM_TURRET ) { + int nfp; + + dest->turret_fov = source->turret_fov; + dest->turret_num_firing_points = source->turret_num_firing_points; + dest->turret_norm = source->turret_norm; + dest->turret_matrix = source->turret_matrix; + + for (nfp = 0; nfp < dest->turret_num_firing_points; nfp++ ) + dest->turret_firing_point[nfp] = source->turret_firing_point[nfp]; + + if ( dest->flags & MSS_FLAG_CREWPOINT ) + strcpy(dest->crewspot, source->crewspot); + } + break; + } + } + if ( j == n_subsystems ) + Int3(); // get allender -- something is amiss with models + + } +} + +// routine to get/set subsystem information +static void set_subsystem_info( model_subsystem *subsystemp, char *props, char *dname ) +{ + char *p; + char buf[32]; + char lcdname[256]; + + if ( (p = strstr(props, "$name")) != NULL) + get_user_prop_value(p+5, subsystemp->name); + else + strcpy( subsystemp->name, dname ); + + strcpy(lcdname, dname); + strlwr(lcdname); + + // check the name for it's specific type + if ( strstr(lcdname, "engine") ) { + subsystemp->type = SUBSYSTEM_ENGINE; + } else if ( strstr(lcdname, "radar") ) { + subsystemp->type = SUBSYSTEM_RADAR; + } else if ( strstr(lcdname, "turret") ) { + float angle; + + subsystemp->type = SUBSYSTEM_TURRET; + if ( (p = strstr(props, "$fov")) != NULL ) + get_user_prop_value(p+4, buf); // get the value of the fov + else + strcpy(buf,"180"); + angle = ANG_TO_RAD(atoi(buf))/2.0f; + subsystemp->turret_fov = (float)cos(angle); + subsystemp->turret_num_firing_points = 0; + + if ( (p = strstr(props, "$crewspot")) != NULL) { + subsystemp->flags |= MSS_FLAG_CREWPOINT; + get_user_prop_value(p+9, subsystemp->crewspot); + } + + } else if ( strstr(lcdname, "navigation") ) { + subsystemp->type = SUBSYSTEM_NAVIGATION; + } else if ( strstr(lcdname, "communication") ) { + subsystemp->type = SUBSYSTEM_COMMUNICATION; + } else if ( strstr(lcdname, "weapons") ) { + subsystemp->type = SUBSYSTEM_WEAPONS; + } else if ( strstr(lcdname, "sensors") ) { + subsystemp->type = SUBSYSTEM_SENSORS; + } else if ( strstr(lcdname, "solar") ) { + subsystemp->type = SUBSYSTEM_SOLAR; + } else if ( strstr(lcdname, "gas") ) { + subsystemp->type = SUBSYSTEM_GAS_COLLECT; + } else if ( strstr(lcdname, "activator") ) { + subsystemp->type = SUBSYSTEM_ACTIVATION; + } else { // If unrecognized type, set to unknown so artist can continue working... + subsystemp->type = SUBSYSTEM_UNKNOWN; + mprintf(("Warning: Ignoring unrecognized subsystem %s, believed to be in ship %s\n", dname, Global_filename)); + } + + // Rotating subsystem + if ( (p = strstr(props, "$rotate")) != NULL) { + subsystemp->flags |= MSS_FLAG_ROTATES; + + // get time for (a) complete rotation (b) step (c) activation + float turn_time; + get_user_prop_value(p+7, buf); + turn_time = (float)atof(buf); + + // CASE OF STEPPED ROTATION + if ( (p = strstr(props, "$stepped")) != NULL) { + + subsystemp->stepped_rotation = new(stepped_rotation); + subsystemp->flags |= MSS_FLAG_STEPPED_ROTATE; + + // get number of steps + if ( (p = strstr(props, "$steps")) != NULL) { + get_user_prop_value(p+6, buf); + subsystemp->stepped_rotation->num_steps = atoi(buf); + } else { + subsystemp->stepped_rotation->num_steps = 8; + } + + // get pause time + if ( (p = strstr(props, "$t_paused")) != NULL) { + get_user_prop_value(p+9, buf); + subsystemp->stepped_rotation->t_pause = (float)atof(buf); + } else { + subsystemp->stepped_rotation->t_pause = 2.0f; + } + + // get transition time - time to go between steps + if ( (p = strstr(props, "$t_transit")) != NULL) { + get_user_prop_value(p+10, buf); + subsystemp->stepped_rotation->t_transit = (float)atof(buf); + } else { + subsystemp->stepped_rotation->t_transit = 2.0f; + } + + // get fraction of time spent in accel + if ( (p = strstr(props, "$fraction_accel")) != NULL) { + get_user_prop_value(p+15, buf); + subsystemp->stepped_rotation->fraction = (float)atof(buf); + Assert(subsystemp->stepped_rotation->fraction > 0 && subsystemp->stepped_rotation->fraction < 0.5); + } else { + subsystemp->stepped_rotation->fraction = 0.3f; + } + + int num_steps = subsystemp->stepped_rotation->num_steps; + float t_trans = subsystemp->stepped_rotation->t_transit; + float fraction = subsystemp->stepped_rotation->fraction; + + subsystemp->stepped_rotation->max_turn_accel = PI2 / (fraction*(1.0f - fraction) * num_steps * t_trans*t_trans); + subsystemp->stepped_rotation->max_turn_rate = PI2 / ((1.0f - fraction) * num_steps *t_trans); + + } + + // CASE OF AI ROTATION + else if ( (p = strstr(props, "$ai")) != NULL) { + get_user_prop_value(p+8, buf); + subsystemp->flags |= MSS_FLAG_AI_ROTATE; + + // get parameters - ie, speed / dist / other ?? + // time to activate + // condition + } + + // CASE OF NORMAL CONTINUOUS ROTATION + else { + if ( fabs(turn_time) < 1 ) { + // Warning(LOCATION, "%s has subsystem %s with rotation time less than 1 sec", dname, Global_filename ); + subsystemp->flags &= ~MSS_FLAG_ROTATES; + subsystemp->turn_rate = 0.0f; + } else { + subsystemp->turn_rate = PI2 / turn_time; + } + } + } + +} + +// used in collision code to check if submode rotates too far +float get_submodel_delta_angle(submodel_instance_info *sii) +{ + vector diff; + vm_vec_sub(&diff, (vector*)&sii->angs, (vector*)&sii->prev_angs); + + // find the angle + float delta_angle = vm_vec_mag(&diff); + + // make sure we get the short way around + if (delta_angle > PI) { + delta_angle = (PI2 - delta_angle); + } + + return delta_angle; +} + +void do_new_subsystem( int n_subsystems, model_subsystem *slist, int subobj_num, float rad, vector *pnt, char *props, char *subobj_name, int model_num ) +{ + int i; + model_subsystem *subsystemp; + + + if ( slist==NULL ) return; // For TestCode, POFView, etc don't bother + + // try to find the name of the subsystem passed here on the list of subsystems currently on the + // ship. Assign the values only when the right subsystem is found + + for (i = 0; i < n_subsystems; i++ ) { + subsystemp = &slist[i]; + if ( !stricmp(subobj_name, subsystemp->subobj_name) ) { + subsystemp->flags = 0; + subsystemp->subobj_num = subobj_num; + subsystemp->turret_gun_sobj = -1; + subsystemp->model_num = model_num; + subsystemp->pnt = *pnt; // use the offset to get the center point of the subsystem + subsystemp->radius = rad; + set_subsystem_info( subsystemp, props, subobj_name); + strcpy(subsystemp->subobj_name, subobj_name); // copy the object name + return; + } + } +#ifndef NDEBUG + if ( !ss_warning_shown) { +#ifdef PLAT_UNIX + STUB_FUNCTION; +#else + char bname[_MAX_FNAME]; + + _splitpath(model_filename, NULL, NULL, bname, NULL); + Warning(LOCATION, "A subsystem was found in model %s that does not have a record in ships.tbl.\nA list of subsystems for this ship will be dumped to:\n\ndata\\tables\\%s.subsystems for inclusion\n into ships.tbl.", model_filename, bname); +#endif + ss_warning_shown = 1; + } else +#endif + nprintf(("warning", "Subsystem %s in ships.tbl not found in model!\n", subobj_name)); +#ifndef NDEBUG + if ( ss_fp ) { + char tmp_buffer[128]; + sprintf(tmp_buffer, "$Subsystem:\t\t\t%s,1,0.0\n", subobj_name); + cfputs(tmp_buffer, ss_fp); + } +#endif + +} + +void print_family_tree( polymodel *obj, int modelnum, char * ident, int islast ) +{ + char temp[50]; + + if ( modelnum < 0 ) return; + if (obj==NULL) return; + + if (strlen(ident)==0 ) { + mprintf(( " %s", obj->submodel[modelnum].name )); + sprintf( temp, " " ); + } else if ( islast ) { + mprintf(( "%sÀÄ%s", ident, obj->submodel[modelnum].name )); + sprintf( temp, "%s ", ident ); + } else { + mprintf(( "%sÃÄ%s", ident, obj->submodel[modelnum].name )); + sprintf( temp, "%s³ ", ident ); + } + + mprintf(( "\n" )); + + int child = obj->submodel[modelnum].first_child; + while( child > -1 ) { + if ( obj->submodel[child].next_sibling < 0 ) + print_family_tree( obj, child, temp,1 ); + else + print_family_tree( obj, child, temp,0 ); + child = obj->submodel[child].next_sibling; + } +} + +void dump_object_tree(polymodel *obj) +{ + print_family_tree( obj, 0, "", 0 ); + key_getch(); +} + +void create_family_tree(polymodel *obj) +{ + int i; + for (i=0; in_models; i++ ) { + obj->submodel[i].num_children = 0; + obj->submodel[i].first_child = -1; + obj->submodel[i].next_sibling = -1; + } + + for (i=0; in_models; i++ ) { + int pn; + pn = obj->submodel[i].parent; + if ( pn > -1 ) { + obj->submodel[pn].num_children++; + int tmp = obj->submodel[pn].first_child; + obj->submodel[pn].first_child = i; + obj->submodel[i].next_sibling = tmp; + } + } +} + +void model_calc_bound_box( vector *box, vector *big_mn, vector *big_mx) +{ + box[0].x = big_mn->x; box[0].y = big_mn->y; box[0].z = big_mn->z; + box[1].x = big_mx->x; box[1].y = big_mn->y; box[1].z = big_mn->z; + box[2].x = big_mx->x; box[2].y = big_mx->y; box[2].z = big_mn->z; + box[3].x = big_mn->x; box[3].y = big_mx->y; box[3].z = big_mn->z; + + + box[4].x = big_mn->x; box[4].y = big_mn->y; box[4].z = big_mx->z; + box[5].x = big_mx->x; box[5].y = big_mn->y; box[5].z = big_mx->z; + box[6].x = big_mx->x; box[6].y = big_mx->y; box[6].z = big_mx->z; + box[7].x = big_mn->x; box[7].y = big_mx->y; box[7].z = big_mx->z; +} + + +// Debug thing so we don't repeatedly show warning messages. +#ifndef NDEBUG +int Bogus_warning_flag_1903 = 0; +#endif + +//reads a binary file containing a 3d model +int read_model_file(polymodel * pm, char *filename, int n_subsystems, model_subsystem *subsystems) +{ + CFILE *fp; + int version; + int id, len, next_chunk; + int i,j; + +#ifndef NDEBUG + strcpy(Global_filename, filename); +#endif + + fp = cfopen(filename,"rb"); + if (!fp){ + Error( LOCATION, "Can't open file <%s>",filename); + return 0; + } + + // code to get a filename to write out subsystem information for each model that + // is read. This info is essentially debug stuff that is used to help get models + // into the game quicker +#if 0 + { + char bname[_MAX_FNAME]; + + _splitpath(filename, NULL, NULL, bname, NULL); + sprintf(debug_name, "%s.subsystems", bname); + ss_fp = cfopen(debug_name, "wb", CFILE_NORMAL, CF_TYPE_TABLES ); + if ( !ss_fp ) { + mprintf(( "Can't open debug file for writing subsystems for %s\n", filename)); + } else { + strcpy(model_filename, filename); + ss_warning_shown = 0; + } + } +#endif + + id = cfread_int(fp); + + if (id!='OPSP') + Error( LOCATION, "Bad ID in model file <%s>",filename); + + // Version is major*100+minor + // So, major = version / 100; + // minor = version % 100; + version = cfread_int(fp); + + //Warning( LOCATION, "POF Version = %d", version ); + + if (version < PM_COMPATIBLE_VERSION || (version/100) > PM_OBJFILE_MAJOR_VERSION) { + Warning(LOCATION,"Bad version (%d) in model file <%s>",version,filename); + return 0; + } + + pm->version = version; + Assert( strlen(filename) < FILENAME_LEN ); + strncpy(pm->filename, filename, FILENAME_LEN); + + memset( &pm->view_positions, 0, sizeof(pm->view_positions) ); + + // reset insignia counts + pm->num_ins = 0; + + id = cfread_int(fp); + len = cfread_int(fp); + next_chunk = cftell(fp) + len; + + while (!cfeof(fp)) { + +// mprintf(("Processing chunk <%c%c%c%c>, len = %d\n",id,id>>8,id>>16,id>>24,len)); +// key_getch(); + + switch (id) { + + case ID_OHDR: { //Object header + //vector v; + + //mprintf(0,"Got chunk OHDR, len=%d\n",len); + +#if defined( FREESPACE1_FORMAT ) + pm->n_models = cfread_int(fp); +// mprintf(( "Num models = %d\n", pm->n_models )); + pm->rad = cfread_float(fp); + pm->flags = cfread_int(fp); // 1=Allow tiling +#elif defined( FREESPACE2_FORMAT ) + pm->rad = cfread_float(fp); + pm->flags = cfread_int(fp); // 1=Allow tiling + pm->n_models = cfread_int(fp); +// mprintf(( "Num models = %d\n", pm->n_models )); +#endif + + pm->submodel = (bsp_info *)malloc( sizeof(bsp_info)*pm->n_models ); + Assert(pm->submodel != NULL ); + memset( pm->submodel, 0, sizeof(bsp_info)*pm->n_models ); + + //Assert(pm->n_models <= MAX_SUBMODELS); + + cfread_vector(&pm->mins,fp); + cfread_vector(&pm->maxs,fp); + model_calc_bound_box(pm->bounding_box,&pm->mins,&pm->maxs); + + pm->n_detail_levels = cfread_int(fp); + // mprintf(( "There are %d detail levels\n", pm->n_detail_levels )); + for (i=0; in_detail_levels;i++ ) { + pm->detail[i] = cfread_int(fp); + pm->detail_depth[i] = 0.0f; + /// mprintf(( "Detail level %d is model %d.\n", i, pm->detail[i] )); + } + + pm->num_debris_objects = cfread_int(fp); + Assert( pm->num_debris_objects <= MAX_DEBRIS_OBJECTS ); + // mprintf(( "There are %d debris objects\n", pm->num_debris_objects )); + for (i=0; inum_debris_objects;i++ ) { + pm->debris_objects[i] = cfread_int(fp); + // mprintf(( "Debris object %d is model %d.\n", i, pm->debris_objects[i] )); + } + + if ( pm->version >= 1903 ) { + + if ( pm->version >= 2009 ) { + + pm->mass = cfread_float(fp); + cfread_vector( &pm->center_of_mass, fp ); + cfread_vector( &pm->moment_of_inertia.rvec, fp ); + cfread_vector( &pm->moment_of_inertia.uvec, fp ); + cfread_vector( &pm->moment_of_inertia.fvec, fp ); + } else { + // old code where mass wasn't based on area, so do the calculation manually + + float vol_mass = cfread_float(fp); + // Attn: John Slagel: The following is better done in bspgen. + // Convert volume (cubic) to surface area (quadratic) and scale so 100 -> 100 + float area_mass = (float) pow(vol_mass, 0.6667f) * 4.65f; + + pm->mass = area_mass; + float mass_ratio = vol_mass / area_mass; + + cfread_vector( &pm->center_of_mass, fp ); + cfread_vector( &pm->moment_of_inertia.rvec, fp ); + cfread_vector( &pm->moment_of_inertia.uvec, fp ); + cfread_vector( &pm->moment_of_inertia.fvec, fp ); + + // John remove this with change to bspgen + vm_vec_scale( &pm->moment_of_inertia.rvec, mass_ratio ); + vm_vec_scale( &pm->moment_of_inertia.uvec, mass_ratio ); + vm_vec_scale( &pm->moment_of_inertia.fvec, mass_ratio ); + } + } else { +#ifndef NDEBUG + if (stricmp("fighter04.pof", filename)) { + if (Bogus_warning_flag_1903 == 0) { + Warning(LOCATION, "Ship %s is old. Cannot compute mass.\nSetting to 50.0f. Talk to John.", filename); + Bogus_warning_flag_1903 = 1; + } + } +#endif + pm->mass = 50.0f; + vm_vec_zero( &pm->center_of_mass ); + vm_set_identity( &pm->moment_of_inertia ); + vm_vec_scale(&pm->moment_of_inertia.rvec, 0.001f); + vm_vec_scale(&pm->moment_of_inertia.uvec, 0.001f); + vm_vec_scale(&pm->moment_of_inertia.fvec, 0.001f); + } + + // read in cross section info + pm->xc = NULL; + if ( pm->version >= 2014 ) { + pm->num_xc = cfread_int(fp); + if (pm->num_xc > 0) { + pm->xc = (cross_section*) malloc(pm->num_xc*sizeof(cross_section)); + for (int i=0; inum_xc; i++) { + pm->xc[i].z = cfread_float(fp); + pm->xc[i].radius = cfread_float(fp); + } + } + } else { + pm->num_xc = 0; + } + + if ( pm->version >= 2007 ) { + pm->num_lights = cfread_int(fp); + //mprintf(( "Found %d lights!\n", pm->num_lights )); + + pm->lights = (bsp_light *)malloc( sizeof(bsp_light)*pm->num_lights ); + for (i=0; inum_lights; i++ ) { + cfread_vector(&pm->lights[i].pos,fp); + pm->lights[i].type = cfread_int(fp); + pm->lights[i].value = 0.0f; + } + } else { + pm->num_lights = 0; + pm->lights = NULL; + } + + break; + } + + case ID_SOBJ: { //Subobject header + int n; + char *p, props[MAX_PROP_LEN]; +// float d; + + //mprintf(0,"Got chunk SOBJ, len=%d\n",len); + + n = cfread_int(fp); + + Assert(n < pm->n_models ); + +#if defined( FREESPACE2_FORMAT ) + pm->submodel[n].rad = cfread_float(fp); //radius +#endif + + pm->submodel[n].parent = cfread_int(fp); + +// cfread_vector(&pm->submodel[n].norm,fp); +// d = cfread_float(fp); +// cfread_vector(&pm->submodel[n].pnt,fp); + cfread_vector(&pm->submodel[n].offset,fp); + +// mprintf(( "Subobj %d, offs = %.1f, %.1f, %.1f\n", n, pm->submodel[n].offset.x, pm->submodel[n].offset.y, pm->submodel[n].offset.z )); + +#if defined ( FREESPACE1_FORMAT ) + pm->submodel[n].rad = cfread_float(fp); //radius +#endif + +// pm->submodel[n].tree_offset = cfread_int(fp); //offset +// pm->submodel[n].data_offset = cfread_int(fp); //offset + + cfread_vector(&pm->submodel[n].geometric_center,fp); + + cfread_vector(&pm->submodel[n].min,fp); + cfread_vector(&pm->submodel[n].max,fp); + + model_calc_bound_box(pm->submodel[n].bounding_box,&pm->submodel[n].min,&pm->submodel[n].max); + + pm->submodel[n].name[0] = '\0'; + + cfread_string_len( pm->submodel[n].name, MAX_NAME_LEN, fp); // get the name + cfread_string_len(props, MAX_PROP_LEN, fp); // and the user properites + + pm->submodel[n].movement_type = cfread_int(fp); + pm->submodel[n].movement_axis = cfread_int(fp); + + // change turret movement type to MOVEMENT_TYPE_ROT_SPECIAL + if (pm->submodel[n].movement_type == MOVEMENT_TYPE_ROT) { + if ( strstr(pm->submodel[n].name, "turret") || strstr(pm->submodel[n].name, "gun") || strstr(pm->submodel[n].name, "cannon")) { + pm->submodel[n].movement_type = MOVEMENT_TYPE_ROT_SPECIAL; + } else if (strstr(pm->submodel[n].name, "thruster")) { + // Int3(); + pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE; + pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE; + } + } + + if ( pm->submodel[n].name[0] == '\0' ) { + strcpy(pm->submodel[n].name, "unknown object name"); + } + + bool rotating_submodel_has_subsystem = !(pm->submodel[n].movement_type == MOVEMENT_TYPE_ROT); + if ( ( p = strstr(props, "$special"))!= NULL ) { + char type[32]; + + get_user_prop_value(p+9, type); + if ( !stricmp(type, "subsystem") ) { // if we have a subsystem, put it into the list! + do_new_subsystem( n_subsystems, subsystems, n, pm->submodel[n].rad, &pm->submodel[n].offset, props, pm->submodel[n].name, pm->id ); + rotating_submodel_has_subsystem = true; + } else if ( !stricmp(type, "no_rotate") ) { + // mark those submodels which should not rotate - ie, those with no subsystem + pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE; + pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE; + } else { + // if submodel rotates (via bspgen), then there is either a subsys or special=no_rotate + Assert( pm->submodel[n].movement_type != MOVEMENT_TYPE_ROT ); + } + } + + if ( !rotating_submodel_has_subsystem ) { + nprintf(("Model", "Model %s: Rotating Submodel without subsystem: %s\n", pm->filename, pm->submodel[n].name)); + + // mark those submodels which should not rotate - ie, those with no subsystem + pm->submodel[n].movement_type = MOVEMENT_TYPE_NONE; + pm->submodel[n].movement_axis = MOVEMENT_AXIS_NONE; + } + + + pm->submodel[n].angs.p = 0.0f; + pm->submodel[n].angs.b = 0.0f; + pm->submodel[n].angs.h = 0.0f; + + { + int nchunks = cfread_int( fp ); // Throw away nchunks + if ( nchunks > 0 ) { + Error( LOCATION, "Model '%s' is chunked. See John or Adam!\n", pm->filename ); + } + } + pm->submodel[n].bsp_data_size = cfread_int(fp); + if ( pm->submodel[n].bsp_data_size > 0 ) { + pm->submodel[n].bsp_data = (ubyte *)malloc(pm->submodel[n].bsp_data_size); + cfread(pm->submodel[n].bsp_data,1,pm->submodel[n].bsp_data_size,fp); + } else { + pm->submodel[n].bsp_data = NULL; + } + + if ( strstr( pm->submodel[n].name, "thruster") ) + pm->submodel[n].is_thruster=1; + else + pm->submodel[n].is_thruster=0; + + if ( strstr( pm->submodel[n].name, "-destroyed") ) + pm->submodel[n].is_damaged=1; + else + pm->submodel[n].is_damaged=0; + + //mprintf(( "Submodel %d, name '%s', parent = %d\n", n, pm->submodel[n].name, pm->submodel[n].parent )); + //key_getch(); + + //mprintf(( "Submodel %d, tree offset %d\n", n, pm->submodel[n].tree_offset )); + //mprintf(( "Submodel %d, data offset %d\n", n, pm->submodel[n].data_offset )); + //key_getch(); + + + break; + + } + + case ID_SHLD: + { + int nverts, ntris; + + nverts = cfread_int( fp ); // get the number of vertices in the list + pm->shield.nverts = nverts; + pm->shield.verts = (shield_vertex *)malloc(nverts * sizeof(shield_vertex) ); + Assert( pm->shield.verts ); + for ( i = 0; i < nverts; i++ ) // read in the vertex list + cfread_vector( &(pm->shield.verts[i].pos), fp ); + + ntris = cfread_int( fp ); // get the number of triangles that compose the shield + pm->shield.ntris = ntris; + pm->shield.tris = (shield_tri *)malloc(ntris * sizeof(shield_tri) ); + Assert( pm->shield.tris ); + for ( i = 0; i < ntris; i++ ) { + cfread_vector( &(pm->shield.tris[i].norm), fp ); + for ( j = 0; j < 3; j++ ) { + pm->shield.tris[i].verts[j] = cfread_int( fp ); // read in the indices into the shield_vertex list + /* +#ifndef NDEBUG + if (pm->shield.tris[i].verts[j] >= nverts) + if (!warning_displayed) { + warning_displayed = 1; + Warning(LOCATION, "Ship %s has a bogus shield mesh.\nOnly %i vertices, index %i found.\n", filename, nverts, pm->shield.tris[i].verts[j]); + } +#endif + */ + } + for ( j = 0; j < 3; j++ ) + pm->shield.tris[i].neighbors[j] = cfread_int( fp ); // read in the neighbor indices -- indexes into tri list + } + break; + + + + } + break; + + case ID_GPNT: + pm->n_guns = cfread_int(fp); + pm->gun_banks = (w_bank *)malloc(sizeof(w_bank) * pm->n_guns); + Assert( pm->gun_banks != NULL ); + + for (i = 0; i < pm->n_guns; i++ ) { + w_bank *bank = &pm->gun_banks[i]; + + bank->num_slots = cfread_int(fp); + Assert ( bank->num_slots < MAX_SLOTS ); + for (j = 0; j < bank->num_slots; j++) { + cfread_vector( &(bank->pnt[j]), fp ); + cfread_vector( &(bank->norm[j]), fp ); + } + } + break; + + case ID_MPNT: + pm->n_missiles = cfread_int(fp); + pm->missile_banks = (w_bank *)malloc(sizeof(w_bank) * pm->n_missiles); + Assert( pm->missile_banks != NULL ); + + for (i = 0; i < pm->n_missiles; i++ ) { + w_bank *bank = &pm->missile_banks[i]; + + bank->num_slots = cfread_int(fp); + Assert ( bank->num_slots < MAX_SLOTS ); + for (j = 0; j < bank->num_slots; j++) { + cfread_vector( &(bank->pnt[j]), fp ); + cfread_vector( &(bank->norm[j]), fp ); + } + } + break; + + case ID_DOCK: { + char props[MAX_PROP_LEN]; + + pm->n_docks = cfread_int(fp); + pm->docking_bays = (dock_bay *)malloc(sizeof(dock_bay) * pm->n_docks); + Assert( pm->docking_bays != NULL ); + + for (i = 0; i < pm->n_docks; i++ ) { + char *p; + dock_bay *bay = &pm->docking_bays[i]; + + cfread_string_len( props, MAX_PROP_LEN, fp ); + if ( (p = strstr(props, "$name"))!= NULL ) + get_user_prop_value(p+5, bay->name); + else + sprintf(bay->name, "", 'A' + i); + bay->num_spline_paths = cfread_int( fp ); + if ( bay->num_spline_paths > 0 ) { + bay->splines = (int *)malloc(sizeof(int) * bay->num_spline_paths); + for ( j = 0; j < bay->num_spline_paths; j++ ) + bay->splines[j] = cfread_int(fp); + } else { + bay->splines = NULL; + } + + // determine what this docking bay can be used for + if ( !strnicmp(bay->name, "cargo", 5) ) + bay->type_flags = DOCK_TYPE_CARGO; + else + bay->type_flags = (DOCK_TYPE_REARM | DOCK_TYPE_GENERIC); + + bay->num_slots = cfread_int(fp); + Assert( bay->num_slots == 2 ); // Get Allender if Asserted! + for (j = 0; j < bay->num_slots; j++) { + cfread_vector( &(bay->pnt[j]), fp ); + cfread_vector( &(bay->norm[j]), fp ); + } + } + break; + } + + case ID_FUEL: + char props[MAX_PROP_LEN]; + pm->n_thrusters = cfread_int(fp); + pm->thrusters = (thruster_bank *)malloc(sizeof(thruster_bank) * pm->n_thrusters); + Assert( pm->thrusters != NULL ); + + for (i = 0; i < pm->n_thrusters; i++ ) { + thruster_bank *bank = &pm->thrusters[i]; + + bank->num_slots = cfread_int(fp); + + if (pm->version < 2117) { + bank->wash_info_index = -1; + } else { + cfread_string_len( props, MAX_PROP_LEN, fp ); + // look for $engine_subsystem=xxx + int length = strlen(props); + if (length > 0) { + int base_length = strlen("$engine_subsystem="); + Assert( strstr( (const char *)&props, "$engine_subsystem=") != NULL ); + Assert( length > base_length ); + char *engine_subsys_name = props + base_length; + if (engine_subsys_name[0] == '$') { + engine_subsys_name++; + } + + nprintf(("wash", "Ship %s with engine wash associated with subsys %s\n", filename, engine_subsys_name)); + + // set wash_info_index to invalid + int table_error = 1; + bank->wash_info_index = -1; + for (int k=0; kwash_info_index = subsystems[k].engine_wash_index; + if (bank->wash_info_index >= 0) { + table_error = 0; + } + break; + } + } + + if ( (bank->wash_info_index == -1) && (n_subsystems > 0) ) { + if (table_error) { + Warning(LOCATION, "No engine wash table entry in ships.tbl for ship model %s", filename); + } else { + Warning(LOCATION, "Inconsistent model: Engine wash engine subsystem does not match any ship subsytem names for ship model %s", filename); + } + } + } else { + bank->wash_info_index = -1; + } + } + + for (j = 0; j < bank->num_slots; j++) { + + cfread_vector( &(bank->pnt[j]), fp ); + cfread_vector( &(bank->norm[j]), fp ); + if ( pm->version > 2004 ) { + bank->radius[j] = cfread_float( fp ); + //mprintf(( "Rad = %.2f\n", rad )); + } else { + bank->radius[j] = 1.0f; + } + } + //mprintf(( "Num slots = %d\n", bank->num_slots )); + + } + break; + + case ID_TGUN: + case ID_TMIS: { + int n_banks, n_slots, parent; + model_subsystem *subsystemp; + int i, j, snum=-1; + + n_banks = cfread_int(fp); // number of turret points + for ( i = 0; i < n_banks; i++ ) { + int physical_parent; // who are we attached to? + parent = cfread_int( fp ); // get the turret parent of the object + + physical_parent = cfread_int(fp); // The parent subobj that this is physically attached to + + if ( subsystems ) { + for ( snum = 0; snum < n_subsystems; snum++ ) { + subsystemp = &subsystems[snum]; + + if ( parent == subsystemp->subobj_num ) { + cfread_vector( &subsystemp->turret_norm, fp ); + vm_vector_2_matrix(&subsystemp->turret_matrix,&subsystemp->turret_norm,NULL,NULL); + + n_slots = cfread_int( fp ); + subsystemp->turret_gun_sobj = physical_parent; + Assert(n_slots < MAX_TFP); // only MAX_TFP firing points per model_subsystem + for (j = 0; j < n_slots; j++ ) { + cfread_vector( &subsystemp->turret_firing_point[j], fp ); + } + Assert( n_slots > 0 ); + + subsystemp->turret_num_firing_points = n_slots; + + break; + } + } + } + +//turret_gun_sobj + + if ( (n_subsystems == 0) || (snum == n_subsystems) ) { + vector bogus; + + nprintf(("Warning", "Turret object not found for turret firing point in model %s\n", model_filename)); + cfread_vector( &bogus, fp ); + n_slots = cfread_int( fp ); + for (j = 0; j < n_slots; j++ ) + cfread_vector( &bogus, fp ); + } + } + break; + } + + case ID_SPCL: { + char name[MAX_NAME_LEN], props[MAX_PROP_LEN], *p; + int n_specials; + float radius; + vector pnt; + + n_specials = cfread_int(fp); // get the number of special subobjects we have + for (i = 0; i < n_specials; i++) { + + // get the next free object of the subobject list. Flag error if no more room + + cfread_string_len(name, MAX_NAME_LEN, fp); // get the name of this special polygon + + cfread_string_len(props, MAX_PROP_LEN, fp); // will definately have properties as well! + cfread_vector( &pnt, fp ); + radius = cfread_float( fp ); + + // check if $Split + p = strstr(name, "$split"); + if (p != NULL) { + pm->split_plane[pm->num_split_plane] = pnt.z; + pm->num_split_plane++; + Assert(pm->num_split_plane <= MAX_SPLIT_PLANE); + } else if ( ( p = strstr(props, "$special"))!= NULL ) { + char type[32]; + + get_user_prop_value(p+9, type); + if ( !stricmp(type, "subsystem") ) // if we have a subsystem, put it into the list! + do_new_subsystem( n_subsystems, subsystems, -1, radius, &pnt, props, &name[1], pm->id ); // skip the first '$' character of the name + } else if ( strstr(name, "$enginelarge") || strstr(name, "$enginehuge") ){ + do_new_subsystem( n_subsystems, subsystems, -1, radius, &pnt, props, &name[1], pm->id ); // skip the first '$' character of the name + } else { + nprintf(("Warning", "Unknown special object type %s while reading model %s\n", name, pm->filename)); + } + } + break; + } + + case ID_TXTR: { //Texture filename list + int i,n; +// char name_buf[128]; + + //mprintf(0,"Got chunk TXTR, len=%d\n",len); + + n = cfread_int(fp); + pm->n_textures = n; + // Dont overwrite memory!! + Assert(n <= MAX_MODEL_TEXTURES); + //mprintf(0," num textures = %d\n",n); + for (i=0; itextures[i] = -1; + } else { + pm->textures[i] = bm_load( tmp_name ); + if (pm->textures[i]<0) { + Error( LOCATION, "Couldn't open texture '%s'\nreferenced by model '%s'\n", tmp_name, pm->filename ); + } + } + pm->original_textures[i] = pm->textures[i]; + //mprintf(0,"<%s>\n",name_buf); + } + + break; + } + +/* case ID_IDTA: //Interpreter data + //mprintf(0,"Got chunk IDTA, len=%d\n",len); + + pm->model_data = (ubyte *)malloc(len); + pm->model_data_size = len; + Assert(pm->model_data != NULL ); + + cfread(pm->model_data,1,len,fp); + + break; +*/ + + case ID_INFO: // don't need to do anything with info stuff + + #ifndef NDEBUG + pm->debug_info_size = len; + pm->debug_info = (char *)malloc(pm->debug_info_size+1); + Assert(pm->debug_info!=NULL); + memset(pm->debug_info,0,len+1); + cfread( pm->debug_info, 1, len, fp ); + #endif + break; + + case ID_GRID: + break; + + case ID_PATH: + pm->n_paths = cfread_int( fp ); + pm->paths = (model_path *)malloc(sizeof(model_path)*pm->n_paths); + Assert( pm->paths != NULL ); + + for (i=0; in_paths; i++ ) { + cfread_string_len(pm->paths[i].name , MAX_NAME_LEN-1, fp); + if ( pm->version >= 2002 ) { + // store the sub_model name number of the parent + cfread_string_len(pm->paths[i].parent_name , MAX_NAME_LEN-1, fp); + // get rid of leading '$' char in name + if ( pm->paths[i].parent_name[0] == '$' ) { + char tmpbuf[MAX_NAME_LEN]; + strcpy(tmpbuf, pm->paths[i].parent_name+1); + strcpy(pm->paths[i].parent_name, tmpbuf); + } + // store the sub_model index (ie index into pm->submodel) of the parent + pm->paths[i].parent_submodel = -1; + for ( j = 0; j < pm->n_models; j++ ) { + if ( !stricmp( pm->submodel[j].name, pm->paths[i].parent_name) ) { + pm->paths[i].parent_submodel = j; + } + } + } else { + pm->paths[i].parent_name[0] = 0; + pm->paths[i].parent_submodel = -1; + } + pm->paths[i].nverts = cfread_int( fp ); + pm->paths[i].verts = (mp_vert *)malloc( sizeof(mp_vert) * pm->paths[i].nverts ); + pm->paths[i].goal = pm->paths[i].nverts - 1; + pm->paths[i].type = MP_TYPE_UNUSED; + pm->paths[i].value = 0; + Assert(pm->paths[i].verts!=NULL); + for (j=0; jpaths[i].nverts; j++ ) { + cfread_vector(&pm->paths[i].verts[j].pos,fp ); + pm->paths[i].verts[j].radius = cfread_float( fp ); + + { // version 1802 added turret stuff + int nturrets, k; + + nturrets = cfread_int( fp ); + pm->paths[i].verts[j].nturrets = nturrets; + pm->paths[i].verts[j].turret_ids = (int *)malloc( sizeof(int) * nturrets ); + for ( k = 0; k < nturrets; k++ ) + pm->paths[i].verts[j].turret_ids[k] = cfread_int( fp ); + } + + } + } + break; + + case ID_EYE: // an eye position(s) + { + int num_eyes, i; + + // all eyes points are stored simply as vectors and their normals. + // 0th element is used as usual player view position. + + num_eyes = cfread_int( fp ); + pm->n_view_positions = num_eyes; + Assert ( num_eyes < MAX_EYES ); + for (i = 0; i < num_eyes; i++ ) { + pm->view_positions[i].parent = cfread_int( fp ); + cfread_vector( &pm->view_positions[i].pnt, fp ); + cfread_vector( &pm->view_positions[i].norm, fp ); + } + } + break; + + case ID_INSG: + int num_ins, num_verts, num_faces, idx, idx2, idx3; + + // get the # of insignias + num_ins = cfread_int(fp); + pm->num_ins = num_ins; + + // read in the insignias + for(idx=0; idxins[idx].detail_level = cfread_int(fp); + + // # of faces + num_faces = cfread_int(fp); + pm->ins[idx].num_faces = num_faces; + Assert(num_faces <= MAX_INS_FACES); + + // # of vertices + num_verts = cfread_int(fp); + Assert(num_verts <= MAX_INS_VECS); + + // read in all the vertices + for(idx2=0; idx2ins[idx].vecs[idx2], fp); + } + + // read in world offset + cfread_vector(&pm->ins[idx].offset, fp); + + // read in all the faces + for(idx2=0; idx2ins[idx].num_faces; idx2++){ + // read in 3 vertices + for(idx3=0; idx3<3; idx3++){ + pm->ins[idx].faces[idx2][idx3] = cfread_int(fp); + pm->ins[idx].u[idx2][idx3] = cfread_float(fp); + pm->ins[idx].v[idx2][idx3] = cfread_float(fp); + } + } + } + break; + + // autocentering info + case ID_ACEN: + cfread_vector(&pm->autocenter, fp); + pm->flags |= PM_FLAG_AUTOCEN; + break; + + default: + mprintf(("Unknown chunk <%c%c%c%c>, len = %d\n",id,id>>8,id>>16,id>>24,len)); + cfseek(fp,len,SEEK_CUR); + break; + + } + cfseek(fp,next_chunk,SEEK_SET); + + id = cfread_int(fp); + len = cfread_int(fp); + next_chunk = cftell(fp) + len; + + } + +#ifndef NDEBUG + if ( ss_fp) { + int size; + + cfclose(ss_fp); + ss_fp = cfopen(debug_name, "rb"); + if ( ss_fp ) { + size = cfilelength(ss_fp); + cfclose(ss_fp); + if ( size <= 0 ) { + _unlink(debug_name); + } + } + } +#endif + + cfclose(fp); + // mprintf(("Done processing chunks\n")); + return 1; +} + + + +//returns the number of this model +int model_load(char *filename, int n_subsystems, model_subsystem *subsystems) +{ + int i, num, arc_idx; + + if ( !model_initted ) + model_init(); + +// int Model_ram = 0; + +#ifndef NDEBUG + int ram_before = TotalRam; +#endif + + //Assert(strlen(filename) <= 12); + + num = -1; + + for (i=0; i< MAX_POLYGON_MODELS; i++) { + if ( Polygon_models[i] ) { + if (!stricmp(filename, Polygon_models[i]->filename)) { + // Model already loaded; just return. + return Polygon_models[i]->id; + } + } else if ( num == -1 ) { + // This is the first empty slot + num = i; + } + } + + // No empty slot + if ( num == -1 ) { + Error( LOCATION, "Too many models" ); + return -1; + } + + mprintf(( "Loading model '%s'\n", filename )); + + polymodel * pm = (polymodel *)malloc( sizeof(polymodel) ); + + Polygon_models[num] = pm; + + memset(pm, 0, sizeof(polymodel)); + + pm->n_paths = 0; + pm->paths = NULL; + + int org_sig = Model_signature; + Model_signature+=MAX_POLYGON_MODELS; + if ( Model_signature < org_sig ) { + Model_signature = 0; + } + Assert( (Model_signature % MAX_POLYGON_MODELS) == 0 ); + pm->id = Model_signature + num; + + if (!read_model_file(pm, filename, n_subsystems, subsystems)) { + return -1; + } + +//mprintf(( "Loading model '%s'\n", filename )); +//key_getch(); + +//============================= +// Find the destroyed replacement models + + // Set up the default values + for (i=0; in_models; i++ ) { + pm->submodel[i].my_replacement = -1; // assume nothing replaces this + pm->submodel[i].i_replace = -1; // assume this doesn't replaces anything + } + + // Search for models that have destroyed versions + for (i=0; in_models; i++ ) { + int j; + char destroyed_name[128]; + + strcpy( destroyed_name, pm->submodel[i].name ); + strcat( destroyed_name, "-destroyed" ); + for (j=0; jn_models; j++ ) { + if ( !stricmp( pm->submodel[j].name, destroyed_name )) { + // mprintf(( "Found destroyed model for '%s'\n", pm->submodel[i].name )); + pm->submodel[i].my_replacement = j; + pm->submodel[j].i_replace = i; + } + } + + // Search for models with live debris + // This debris comes from a destroyed subsystem when ship is still alive + char live_debris_name[128]; + + strcpy( live_debris_name, "debris-" ); + strcat( live_debris_name, pm->submodel[i].name ); + + + pm->submodel[i].num_live_debris = 0; + for (j=0; jn_models; j++ ) { + // check if current model name is substring of destroyed + if ( strstr( pm->submodel[j].name, live_debris_name )) { + mprintf(( "Found live debris model for '%s'\n", pm->submodel[i].name )); + Assert(pm->submodel[i].num_live_debris < MAX_LIVE_DEBRIS); + pm->submodel[i].live_debris[pm->submodel[i].num_live_debris++] = j; + pm->submodel[j].is_live_debris = 1; + } + } + + } + + create_family_tree(pm); +// dump_object_tree(pm); + +//============================== +// Find all the lower detail versions of the hires model + for (i=0; in_models; i++ ) { + int j, l1; + bsp_info * sm1 = &pm->submodel[i]; + + // set all arc types to be default + for(arc_idx=0; arc_idx < MAX_ARC_EFFECTS; arc_idx++){ + sm1->arc_type[arc_idx] = MARC_TYPE_NORMAL; + } + + sm1->num_details = 0; + l1 = strlen(sm1->name); + + for (j=0; jnum_debris_objects;j++ ) { + if ( i == pm->debris_objects[j] ) { + sm1->is_damaged = 1; + } + } + + + for (j=0; jdetails[j] = -1; + } + + for (j=0; jn_models; j++ ) { + int k; + bsp_info * sm2 = &pm->submodel[j]; + + if ( i==j ) continue; + + // set all arc types to be default + for(arc_idx=0; arc_idx < MAX_ARC_EFFECTS; arc_idx++){ + sm2->arc_type[arc_idx] = MARC_TYPE_NORMAL; + } + + // if sm2 is a detail of sm1 and sm1 is a high detail, then add it to sm1's list + if ((int)strlen(sm2->name)!=l1) continue; + + int ndiff = 0; + int first_diff = 0; + for ( k=0; kname[k] != sm2->name[k] ) { + if (ndiff==0) first_diff = k; + ndiff++; + } + } + if (ndiff==1) { // They only differ by one character! + int dl1, dl2; + dl1 = tolower(sm1->name[first_diff]) - 'a'; + dl2 = tolower(sm2->name[first_diff]) - 'a'; + + if ( (dl1<0) || (dl2<0) || (dl1>=MAX_MODEL_DETAIL_LEVELS) || (dl2>=MAX_MODEL_DETAIL_LEVELS) ) continue; // invalid detail levels + + if ( dl1 == 0 ) { + dl2--; // Start from 1 up... + if (dl2 >= sm1->num_details ) sm1->num_details = dl2+1; + sm1->details[dl2] = j; + //mprintf(( "Submodel '%s' is detail level %d of '%s'\n", sm2->name, dl2, sm1->name )); + } + } + } + + for (j=0; jnum_details; j++ ) { + if ( sm1->details[j] == -1 ) { + Warning( LOCATION, "Model '%s' could find all detail levels for submodel '%s'", pm->filename, sm1->name ); + sm1->num_details = 0; + } + } + + } + + + model_octant_create( pm ); + + // Find the core_radius... the minimum of + float rx, ry, rz; + rx = fl_abs( pm->submodel[pm->detail[0]].max.x - pm->submodel[pm->detail[0]].min.x ); + ry = fl_abs( pm->submodel[pm->detail[0]].max.y - pm->submodel[pm->detail[0]].min.y ); + rz = fl_abs( pm->submodel[pm->detail[0]].max.z - pm->submodel[pm->detail[0]].min.z ); + + pm->core_radius = min( rx, min(ry, rz) ) / 2.0f; + + for (i=0; in_view_positions; i++ ) { + if ( pm->view_positions[i].parent == pm->detail[0] ) { + float d = vm_vec_mag( &pm->view_positions[i].pnt ); + + d += 0.1f; // Make the eye 1/10th of a meter inside the sphere. + + if ( d > pm->core_radius ) { + //mprintf(( "Model %s core radius increased from %.1f to %.1f to fit eye\n", pm->filename, pm->core_radius, d )); + pm->core_radius = d; + } + } + } + +#ifndef NDEBUG + int ram_after = TotalRam; + + pm->ram_used = ram_after - ram_before; + Model_ram += pm->ram_used; + //mprintf(( "Model RAM = %d KB\n", Model_ram )); +#endif + + return pm->id; +} + +// Get "parent" submodel for live debris submodel +int model_get_parent_submodel_for_live_debris( int model_num, int live_debris_model_num ) +{ + polymodel *pm = model_get(model_num); + + Assert(pm->submodel[live_debris_model_num].is_live_debris == 1); + + int mn; + bsp_info *child; + + // Start with the high level of detail hull + // Check all its children until we find the submodel to which the live debris belongs + child = &pm->submodel[pm->detail[0]]; + mn = child->first_child; + + while (mn > 0) { + child = &pm->submodel[mn]; + + if (child->num_live_debris > 0) { + // check all live debris submodels for the current child + for (int idx=0; idxnum_live_debris; idx++) { + if (child->live_debris[idx] == live_debris_model_num) { + return mn; + } + } + // DKA 5/26/99: can multiple live debris subsystems with each ship + // NO LONGER TRUE Can only be 1 submodel with live debris + // Error( LOCATION, "Could not find parent submodel for live debris. Possible model error"); + } + + // get next child + mn = child->next_sibling; + } + Error( LOCATION, "Could not find parent submodel for live debris"); + return -1; +} + + +float model_get_radius( int modelnum ) +{ + polymodel *pm; + + pm = model_get(modelnum); + + return pm->rad; +} + +float model_get_core_radius( int modelnum ) +{ + polymodel *pm; + + pm = model_get(modelnum); + + return pm->core_radius; +} + +float submodel_get_radius( int modelnum, int submodelnum ) +{ + polymodel *pm; + + pm = model_get(modelnum); + + return pm->submodel[submodelnum].rad; +} + + + +polymodel * model_get(int model_num) +{ + Assert( model_num > -1 ); + + int num = model_num % MAX_POLYGON_MODELS; + + Assert( num > -1 ); + Assert( num < MAX_POLYGON_MODELS ); + Assert( Polygon_models[num]->id == model_num ); + + return Polygon_models[num]; +} + + +/* +// Finds the 3d bounding box of a model. If submodel_num is -1, +// then it starts from the root object. If inc_children is non-zero, +// then this will recurse and find the bounding box for all children +// also. +void model_find_bound_box_3d(int model_num,int submodel_num, int inc_children, matrix *orient, vector * pos, vector * box ) +{ + polymodel * pm; + vector to_root_xlat; + matrix to_root_rotate; + int n_steps, steps[16]; + int tmp_sobj; + + if ( (model_num < 0) || (model_num >= N_polygon_models) ) return; + + pm = &Polygon_models[model_num]; + + if ( submodel_num < 0 ) submodel_num = pm->detail[0]; + + // traverse up the model tree to a root object. + // Store this path in n_steps, + n_steps = 0; + tmp_sobj = submodel_num; + while( tmp_sobj > -1 ) { + steps[n_steps++] = tmp_sobj; + tmp_sobj = pm->submodel[tmp_sobj].parent; + } + + + +// vm_copy_transpose_matrix(&to_world_rotate, orient ); +// to_world_xlat = *pos; + +} +*/ + + + +// Returns zero is x1,y1,x2,y2 are valid +// returns 1 for invalid model, 2 for point offscreen. +// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates! +int model_find_2d_bound_min(int model_num,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 ) +{ + polymodel * po; + int n_valid_pts; + int i, x,y,min_x, min_y, max_x, max_y; + int rval = 0; + + po = model_get(model_num); + + g3_start_instance_matrix(pos,orient); + + n_valid_pts = 0; + + int hull = po->detail[0]; + + min_x = min_y = max_x = max_y = 0; + + for (i=0; i<8; i++ ) { + vertex pt; + ubyte flags; + + flags = g3_rotate_vertex(&pt,&po->submodel[hull].bounding_box[i]); + if ( !(flags&CC_BEHIND) ) { + g3_project_vertex(&pt); + + if (!(pt.flags & PF_OVERFLOW)) { + x = fl2i(pt.sx); + y = fl2i(pt.sy); + if ( n_valid_pts == 0 ) { + min_x = x; + min_y = y; + max_x = x; + max_y = y; + } else { + if ( x < min_x ) min_x = x; + if ( y < min_y ) min_y = y; + + if ( x > max_x ) max_x = x; + if ( y > max_y ) max_y = y; + } + n_valid_pts++; + } + } + } + + if ( n_valid_pts < 8 ) { + rval = 2; + } + + if (x1) *x1 = min_x; + if (y1) *y1 = min_y; + + if (x2) *x2 = max_x; + if (y2) *y2 = max_y; + + g3_done_instance(); + + return rval; +} + + +// Returns zero is x1,y1,x2,y2 are valid +// returns 1 for invalid model, 2 for point offscreen. +// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates! +int submodel_find_2d_bound_min(int model_num,int submodel, matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 ) +{ + polymodel * po; + int n_valid_pts; + int i, x,y,min_x, min_y, max_x, max_y; + bsp_info * sm; + + po = model_get(model_num); + if ( (submodel < 0) || (submodel >= po->n_models ) ) return 1; + sm = &po->submodel[submodel]; + + g3_start_instance_matrix(pos,orient); + + n_valid_pts = 0; + + min_x = min_y = max_x = max_y = 0; + + for (i=0; i<8; i++ ) { + vertex pt; + ubyte flags; + + flags = g3_rotate_vertex(&pt,&sm->bounding_box[i]); + if ( !(flags&CC_BEHIND) ) { + g3_project_vertex(&pt); + + if (!(pt.flags & PF_OVERFLOW)) { + x = fl2i(pt.sx); + y = fl2i(pt.sy); + if ( n_valid_pts == 0 ) { + min_x = x; + min_y = y; + max_x = x; + max_y = y; + } else { + if ( x < min_x ) min_x = x; + if ( y < min_y ) min_y = y; + + if ( x > max_x ) max_x = x; + if ( y > max_y ) max_y = y; + } + n_valid_pts++; + } + } + } + + if ( n_valid_pts == 0 ) { + return 2; + } + + if (x1) *x1 = min_x; + if (y1) *y1 = min_y; + + if (x2) *x2 = max_x; + if (y2) *y2 = max_y; + + g3_done_instance(); + + return 0; +} + + +// Returns zero is x1,y1,x2,y2 are valid +// returns 1 for invalid model, 2 for point offscreen. +// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates! +int model_find_2d_bound(int model_num,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 ) +{ + float t,w,h; + vertex pnt; + ubyte flags; + polymodel * po; + + po = model_get(model_num); + float width = po->rad; + float height = po->rad; + + flags = g3_rotate_vertex(&pnt,pos); + + if ( pnt.flags & CC_BEHIND ) + return 2; + + if (!(pnt.flags&PF_PROJECTED)) + g3_project_vertex(&pnt); + + if (pnt.flags & PF_OVERFLOW) + return 2; + + t = (width * Canv_w2)/pnt.z; + w = t*Matrix_scale.x; + + t = (height*Canv_h2)/pnt.z; + h = t*Matrix_scale.y; + + if (x1) *x1 = fl2i(pnt.sx - w); + if (y1) *y1 = fl2i(pnt.sy - h); + + if (x2) *x2 = fl2i(pnt.sx + w); + if (y2) *y2 = fl2i(pnt.sy + h); + + return 0; +} + +// Returns zero is x1,y1,x2,y2 are valid +// returns 2 for point offscreen. +// note that x1,y1,x2,y2 aren't clipped to 2d screen coordinates! +int subobj_find_2d_bound(float radius ,matrix *orient, vector * pos,int *x1, int *y1, int *x2, int *y2 ) +{ + float t,w,h; + vertex pnt; + ubyte flags; + + float width = radius; + float height = radius; + + flags = g3_rotate_vertex(&pnt,pos); + + if ( pnt.flags & CC_BEHIND ) + return 2; + + if (!(pnt.flags&PF_PROJECTED)) + g3_project_vertex(&pnt); + + if (pnt.flags & PF_OVERFLOW) + return 2; + + t = (width * Canv_w2)/pnt.z; + w = t*Matrix_scale.x; + + t = (height*Canv_h2)/pnt.z; + h = t*Matrix_scale.y; + + if (x1) *x1 = fl2i(pnt.sx - w); + if (y1) *y1 = fl2i(pnt.sy - h); + + if (x2) *x2 = fl2i(pnt.sx + w); + if (y2) *y2 = fl2i(pnt.sy + h); + + return 0; +} + + +// Given a vector that is in sub_model_num's frame of +// reference, and given the object's orient and position, +// return the vector in the model's frame of reference. +void model_find_obj_dir(vector *w_vec, vector *m_vec, object *ship_obj, int sub_model_num) +{ + vector tvec, vec; + matrix m; + int mn; + + Assert(ship_obj->type == OBJ_SHIP); + + polymodel *pm = model_get(Ships[ship_obj->instance].modelnum); + vec = *m_vec; + mn = sub_model_num; + + // instance up the tree for this point + while ((mn>-1) && (pm->submodel[mn].parent > -1)) { + + vm_angles_2_matrix(&m, &pm->submodel[mn].angs); + vm_vec_unrotate(&tvec, &vec, &m); + vec = tvec; + + mn = pm->submodel[mn].parent; + } + + // now instance for the entire object + vm_vec_unrotate(w_vec, &vec, &ship_obj->orient); +} + + +// Given a point (pnt) that is in sub_model_num's frame of +// reference, return the point in in the object's frame of reference +void model_rot_sub_into_obj(vector * outpnt, vector *mpnt,polymodel *pm, int sub_model_num) +{ + vector pnt; + vector tpnt; + matrix m; + int mn; + + pnt = *mpnt; + mn = sub_model_num; + + //instance up the tree for this point + while ((mn>-1) && (pm->submodel[mn].parent > -1) ) { + + vm_angles_2_matrix(&m,&pm->submodel[mn].angs ); + vm_transpose_matrix(&m); + vm_vec_rotate(&tpnt,&pnt,&m); + + vm_vec_add(&pnt,&tpnt,&pm->submodel[mn].offset ); + + mn = pm->submodel[mn].parent; + } + + //now instance for the entire object + *outpnt = pnt; +} + + +// Given a rotating submodel, find the ship and world axes or rotatation. +void model_get_rotating_submodel_axis(vector *model_axis, vector *world_axis, int modelnum, int submodel_num, object *obj) +{ + polymodel *pm = model_get(modelnum); + + bsp_info *sm = &pm->submodel[submodel_num]; + Assert(sm->movement_type == MOVEMENT_TYPE_ROT); + + if (sm->movement_axis == MOVEMENT_AXIS_X) { + vm_vec_make(model_axis, 1.0f, 0.0f, 0.0f); + } else if (sm->movement_axis == MOVEMENT_AXIS_Y) { + vm_vec_make(model_axis, 0.0f, 1.0f, 0.0f); + } else { + Assert(sm->movement_axis == MOVEMENT_AXIS_Z); + vm_vec_make(model_axis, 0.0f, 0.0f, 1.0f); + } + + model_find_obj_dir(world_axis, model_axis, obj, submodel_num); +} + + +// Does stepped rotation of a submodel +#pragma warning ( push ) +#pragma warning (disable : 4701) +void submodel_stepped_rotate(model_subsystem *psub, submodel_instance_info *sii) +{ + Assert(psub->flags & MSS_FLAG_STEPPED_ROTATE); + + if ( psub->subobj_num < 0 ) return; + + polymodel *pm = model_get(psub->model_num); + bsp_info *sm = &pm->submodel[psub->subobj_num]; + + if ( sm->movement_type != MOVEMENT_TYPE_ROT ) return; + + // get active rotation time this frame + int end_stamp = timestamp(); + float rotation_time = 0.001f * (end_stamp - sii->step_zero_timestamp); + Assert(rotation_time >= 0); + + // save last angles + sii->prev_angs = sii->angs; + + // float pointer into struct to get angle (either p,b,h) + float *ang_prev, *ang_next; + switch( sm->movement_axis ) { + case MOVEMENT_AXIS_X: + ang_prev = &sii->prev_angs.p; + ang_next = &sii->angs.p; + break; + + case MOVEMENT_AXIS_Y: + ang_prev = &sii->prev_angs.h; + ang_next = &sii->angs.h; + break; + + case MOVEMENT_AXIS_Z: + ang_prev = &sii->prev_angs.b; + ang_next = &sii->angs.b; + break; + } + + // angular displacement of one step + float step_size = (PI2 / psub->stepped_rotation->num_steps); + + // get time to complete one step, including pause + float step_time = psub->stepped_rotation->t_transit + psub->stepped_rotation->t_pause; + + // cur_step is step number relative to zero (0 - num_steps) + // step_offset_time is TIME into current step + float step_offset_time = (float)fmod(rotation_time, step_time); + // subtract off fractional step part, round up (ie, 1.999999 -> 2) + int cur_step = int( ((rotation_time - step_offset_time) / step_time) + 0.5f); + // mprintf(("cur step %d\n", cur_step)); + // Assert(step_offset_time >= 0); + + if (cur_step >= psub->stepped_rotation->num_steps) { + // I don;t know why, but removing this line makes it all good. + // sii->step_zero_timestamp += int(1000.0f * (psub->stepped_rotation->num_steps * step_time) + 0.5f); + + // reset cur_step (use mod to handle physics/ai pause) + cur_step = cur_step % psub->stepped_rotation->num_steps; + } + + // get base angle + *ang_next = cur_step * step_size; + + // determine which phase of rotation we're in + float coast_start_time = psub->stepped_rotation->fraction * psub->stepped_rotation->t_transit; + float decel_start_time = psub->stepped_rotation->t_transit * (1.0f - psub->stepped_rotation->fraction); + float pause_start_time = psub->stepped_rotation->t_transit; + + float start_coast_angle = 0.5f * psub->stepped_rotation->max_turn_accel * coast_start_time * coast_start_time; + + if (step_offset_time < coast_start_time) { + // do accel + float accel_time = step_offset_time; + *ang_next += 0.5f * psub->stepped_rotation->max_turn_accel * accel_time * accel_time; + sii->cur_turn_rate = psub->stepped_rotation->max_turn_accel * accel_time; + } else if (step_offset_time < decel_start_time) { + // do coast + float coast_time = step_offset_time - coast_start_time; + *ang_next += start_coast_angle + psub->stepped_rotation->max_turn_rate * coast_time; + sii->cur_turn_rate = psub->stepped_rotation->max_turn_rate; + } else if (step_offset_time < pause_start_time) { + // do decel + float time_to_pause = psub->stepped_rotation->t_transit - step_offset_time; + *ang_next += (step_size - 0.5f * psub->stepped_rotation->max_turn_accel * time_to_pause * time_to_pause); + sii->cur_turn_rate = psub->stepped_rotation->max_turn_rate * time_to_pause; + } else { + // do pause + *ang_next += step_size; + sii->cur_turn_rate = 0.0f; + } +} +#pragma warning ( pop ) + + +// Rotates the angle of a submodel. Use this so the right unlocked axis +// gets stuffed. +void submodel_rotate(model_subsystem *psub, submodel_instance_info *sii) +{ + bsp_info * sm; + + if ( psub->subobj_num < 0 ) return; + + polymodel *pm = model_get(psub->model_num); + sm = &pm->submodel[psub->subobj_num]; + + if ( sm->movement_type != MOVEMENT_TYPE_ROT ) return; + + // save last angles + sii->prev_angs = sii->angs; + + // probably send in a calculated desired turn rate + float diff = sii->desired_turn_rate - sii->cur_turn_rate; + + float final_turn_rate; + if (diff > 0) { + final_turn_rate = sii->cur_turn_rate + sii->turn_accel * flFrametime; + if (final_turn_rate > sii->desired_turn_rate) { + final_turn_rate = sii->desired_turn_rate; + } + } else if (diff < 0) { + final_turn_rate = sii->cur_turn_rate - sii->turn_accel * flFrametime; + if (final_turn_rate < sii->desired_turn_rate) { + final_turn_rate = sii->desired_turn_rate; + } + } else { + final_turn_rate = sii->desired_turn_rate; + } + + float delta = (sii->cur_turn_rate + final_turn_rate) * 0.5f * flFrametime; + sii->cur_turn_rate = final_turn_rate; + + + //float delta = psub->turn_rate * flFrametime; + + switch( sm->movement_axis ) { + case MOVEMENT_AXIS_X: + sii->angs.p += delta; + if (sii->angs.p > PI2 ) + sii->angs.p -= PI2; + else if (sii->angs.p < 0.0f ) + sii->angs.p += PI2; + break; + case MOVEMENT_AXIS_Y: + sii->angs.h += delta; + if (sii->angs.h > PI2 ) + sii->angs.h -= PI2; + else if (sii->angs.h < 0.0f ) + sii->angs.h += PI2; + break; + case MOVEMENT_AXIS_Z: + sii->angs.b += delta; + if (sii->angs.b > PI2 ) + sii->angs.b -= PI2; + else if (sii->angs.b < 0.0f ) + sii->angs.b += PI2; + break; + } +} + + +//========================================================================= +// Make a turret's correct orientation matrix. This should be done when +// the model is read, but I wasn't sure at what point all the data that I +// needed was read, so I just check a flag and call this routine when +// I determine I need the correct matrix. In this code, you can't use +// vm_vec_2_matrix or anything, since these turrets could be either +// right handed or left handed. +void model_make_turrent_matrix(int model_num, model_subsystem * turret ) +{ + polymodel * pm; + vector fvec, uvec, rvec; + + pm = model_get(model_num); + bsp_info * sm = &pm->submodel[turret->turret_gun_sobj]; + bsp_info * sm_parent = &pm->submodel[turret->subobj_num]; + + + model_clear_instance(model_num); + model_find_world_dir(&fvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL ); + + sm_parent->angs.h = -PI/2.0f; + sm->angs.p = -PI/2.0f; + model_find_world_dir(&rvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL ); + + sm_parent->angs.h = 0.0f; + sm->angs.p = -PI/2.0f; + model_find_world_dir(&uvec, &turret->turret_norm, model_num, turret->turret_gun_sobj, &vmd_identity_matrix, NULL ); + + vm_vec_normalize(&fvec); + vm_vec_normalize(&rvec); + vm_vec_normalize(&uvec); + + turret->turret_matrix.fvec = fvec; + turret->turret_matrix.rvec = rvec; + turret->turret_matrix.uvec = uvec; + +// vm_vector_2_matrix(&turret->turret_matrix,&turret->turret_norm,NULL,NULL); + + // HACK!! WARNING!!! + // I'm doing nothing to verify that this matrix is orthogonal!! + // In other words, there's no guarantee that the vectors are 90 degrees + // from each other. + // I'm not doing this because I don't know how to do it without ruining + // the handedness of the matrix... however, I'm not too worried about + // this because I am creating these 3 vectors by making them 90 degrees + // apart, so this should be close enough. I think this will start + // causing weird errors when we view from turrets. -John + turret->flags |= MSS_FLAG_TURRET_MATRIX; +} + +// Tries to move joints so that the turrent points to the point dst. +// turret1 is the angles of the turret, turret2 is the angles of the gun from turret +// Returns 1 if rotated gun, 0 if no gun to rotate (rotation handled by AI) +int model_rotate_gun(int model_num, model_subsystem *turret, matrix *orient, angles *turret1, angles *turret2, vector *pos, vector *dst) +{ + polymodel * pm; + + pm = model_get(model_num); + bsp_info * sm = &pm->submodel[turret->turret_gun_sobj]; + bsp_info * sm_parent = &pm->submodel[turret->subobj_num]; + + // Check for a valid turret + Assert( turret->turret_num_firing_points > 0 ); + + + if ( sm_parent == sm ) { + return 0; + } + + // Build the correct turret matrix if there isn't already one + if ( !(turret->flags & MSS_FLAG_TURRET_MATRIX) ) + model_make_turrent_matrix(model_num, turret ); + + Assert( turret->flags & MSS_FLAG_TURRET_MATRIX); +// Assert( sm->movement_axis == MOVEMENT_AXIS_X ); // Gun must be able to change pitch +// Assert( sm_parent->movement_axis == MOVEMENT_AXIS_Z ); // Parent must be able to change heading + +//====================================================== +// DEBUG code to draw the normal out of this gun and a circle +// at the gun point. +#if 0 + { + vector tmp; + vector tmp1; + vertex dpnt1, dpnt2; + + model_clear_instance(model_num); + sm->angs.p = turret2->p; + sm_parent->angs.h = turret1->h; + + model_find_world_point(&tmp, &vmd_zero_vector, model_num, turret->turret_gun_sobj, orient, pos ); + gr_set_color(255,0,0); + g3_rotate_vertex( &dpnt1, &tmp ); + + gr_set_color(255,0,0); + g3_draw_sphere(&dpnt1,1.0f); + + vm_vec_copy_scale( &tmp1, &turret->turret_matrix.fvec, 10.0f ); + model_find_world_point(&tmp, &tmp1, model_num, turret->turret_gun_sobj, orient, pos ); + g3_rotate_vertex( &dpnt2, &tmp ); + + gr_set_color(0,255,0); + g3_draw_line(&dpnt1,&dpnt2); + gr_set_color(0,128,0); + g3_draw_sphere(&dpnt2,0.2f); + + vm_vec_copy_scale( &tmp1, &turret->turret_matrix.rvec, 10.0f ); + model_find_world_point(&tmp, &tmp1, model_num, turret->turret_gun_sobj, orient, pos ); + g3_rotate_vertex( &dpnt2, &tmp ); + + gr_set_color(0,0,255); + g3_draw_line(&dpnt1,&dpnt2); + + vm_vec_copy_scale( &tmp1, &turret->turret_matrix.uvec, 10.0f ); + model_find_world_point(&tmp, &tmp1, model_num, turret->turret_gun_sobj, orient, pos ); + g3_rotate_vertex( &dpnt2, &tmp ); + + gr_set_color(255,0,0); + g3_draw_line(&dpnt1,&dpnt2); + } +#endif + + //------------ + // rotate the dest point into the turret gun normal's frame of + // reference, but not using the turret's angles. + // Call this vector of_dst + vector of_dst; + matrix world_to_turret_matrix; // converts world coordinates to turret's FOR + vector world_to_turret_translate; // converts world coordinates to turret's FOR + vector tempv; + + vm_vec_unrotate( &tempv, &sm_parent->offset, orient); + vm_vec_add( &world_to_turret_translate, pos, &tempv ); + + vm_matrix_x_matrix( &world_to_turret_matrix, orient, &turret->turret_matrix ); + + vm_vec_sub( &tempv, dst, &world_to_turret_translate ); + vm_vec_rotate( &of_dst, &tempv, &world_to_turret_matrix ); + + vm_vec_normalize(&of_dst); + + //------------ + // Find the heading and pitch that the gun needs to turn to + // by extracting them from the of_dst vector. + // Call this the desired_angles + angles desired_angles; + + desired_angles.p = (float)acos(of_dst.z); + desired_angles.h = PI - atan2_safe(of_dst.x, of_dst.y); + desired_angles.b = 0.0f; + + // mprintf(( "Z = %.1f, atan= %.1f\n", of_dst.z, desired_angles.p )); + + //------------ + // Gradually turn the turret towards the desired angles + float step_size = turret->turret_turning_rate * flFrametime; + + vm_interp_angle(&turret1->h,desired_angles.h,step_size); + vm_interp_angle(&turret2->p,desired_angles.p,step_size); + +// turret1->h -= step_size*(key_down_timef(KEY_1)-key_down_timef(KEY_2) ); +// turret2->p += step_size*(key_down_timef(KEY_3)-key_down_timef(KEY_4) ); + + return 1; + +} + + +// Given a point (pnt) that is in sub_model_num's frame of +// reference, and given the object's orient and position, +// return the point in 3-space in outpnt. +void model_find_world_point(vector * outpnt, vector *mpnt,int model_num,int sub_model_num, matrix * objorient, vector * objpos ) +{ + vector pnt; + vector tpnt; + matrix m; + int mn; + polymodel *pm = model_get(model_num); + + pnt = *mpnt; + mn = sub_model_num; + + //instance up the tree for this point + while ((mn>-1) && (pm->submodel[mn].parent > -1) ) { + + vm_angles_2_matrix(&m,&pm->submodel[mn].angs ); + vm_vec_unrotate(&tpnt,&pnt,&m); + + vm_vec_add(&pnt,&tpnt,&pm->submodel[mn].offset ); + + mn = pm->submodel[mn].parent; + } + + //now instance for the entire object + vm_vec_unrotate(outpnt,&pnt,objorient); + vm_vec_add2(outpnt,objpos); +} + +// Given a point in the world RF, find the corresponding point in the model RF. +// This is special purpose code, specific for model collision. +// NOTE - this code ASSUMES submodel is 1 level down from hull (detail[0]) +// +// out - point in model RF +// world_pt - point in world RF +// pm - polygon model +// submodel_num - submodel in whose RF we're trying to find the corresponding world point +// orient - orient matrix of ship +// pos - pos vector of ship +void world_find_model_point(vector *out, vector *world_pt, polymodel *pm, int submodel_num, matrix *orient, vector *pos) +{ + Assert( (pm->submodel[submodel_num].parent == pm->detail[0]) || (pm->submodel[submodel_num].parent == -1) ); + + vector tempv1, tempv2; + matrix m; + + // get into ship RF + vm_vec_sub(&tempv1, world_pt, pos); + vm_vec_rotate(&tempv2, &tempv1, orient); + + if (pm->submodel[submodel_num].parent == -1) { + *out = tempv2; + return; + } + + // put into submodel RF + vm_vec_sub2(&tempv2, &pm->submodel[submodel_num].offset); + vm_angles_2_matrix(&m, &pm->submodel[submodel_num].angs); + vm_vec_rotate(out, &tempv2, &m); +} + +// Verify rotating submodel has corresponding ship subsystem -- info in which to store rotation angle +int rotating_submodel_has_ship_subsys(int submodel, ship *shipp) +{ + model_subsystem *psub; + ship_subsys *pss; + + int found = 0; + + // Go through all subsystems and look for submodel + // the subsystems that need it. + for ( pss = GET_FIRST(&shipp->subsys_list); pss != END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) { + psub = pss->system_info; + if (psub->subobj_num == submodel) { + found = 1; + break; + } + } + + return found; +} + +void model_get_rotating_submodel_list(int *submodel_list, int *num_rotating_submodels, object *objp) +{ + Assert(objp->type == OBJ_SHIP); + + // Check if not currently rotating - then treat as part of superstructure. + int modelnum = Ships[objp->instance].modelnum; + polymodel *pm = model_get(modelnum); + bsp_info *child_submodel; + + *num_rotating_submodels = 0; + child_submodel = &pm->submodel[pm->detail[0]]; + + int i = child_submodel->first_child; + while ( i>-1 ) { + child_submodel = &pm->submodel[i]; + + // Don't check it or its children if it is destroyed or it is a replacement (non-moving) + if ( !child_submodel->blown_off && (child_submodel->i_replace == -1) ) { + + // Only look for submodels that rotate + if (child_submodel->movement_type == MOVEMENT_TYPE_ROT) { + + // find ship subsys and check submodel rotation is less than max allowed. + ship *pship = &Ships[objp->instance]; + ship_subsys *subsys; + + for ( subsys = GET_FIRST(&pship->subsys_list); subsys !=END_OF_LIST(&pship->subsys_list); subsys = GET_NEXT(subsys) ) { + Assert(subsys->system_info->model_num == modelnum); + if (i == subsys->system_info->subobj_num) { + // found the correct subsystem - now check delta rotation angle not too large + float delta_angle = get_submodel_delta_angle(&subsys->submodel_info_1); + if (delta_angle < MAX_SUBMODEL_COLLISION_ROT_ANGLE) { + Assert(*num_rotating_submodels < MAX_ROTATING_SUBMODELS-1); + submodel_list[(*num_rotating_submodels)++] = i; + } + break; + } + } + } + } + i = child_submodel->next_sibling; + } + + // error checking +//#define MODEL_CHECK +#ifdef MODEL_CHECK + ship *pship = &Ships[objp->instance]; + for (int idx=0; idx<*num_rotating_submodels; idx++) { + int valid = rotating_submodel_has_ship_subsys(submodel_list[idx], pship); +// Assert( valid ); + if ( !valid ) { + + Warning( LOCATION, "Ship %s has rotating submodel [%s] without ship subsystem\n", pship->ship_name, pm->submodel[submodel_list[idx]].name ); + pm->submodel[submodel_list[idx]].movement_type &= ~MOVEMENT_TYPE_ROT; + *num_rotating_submodels = 0; + } + } +#endif + +} + + +// Given a direction (pnt) that is in sub_model_num's frame of +// reference, and given the object's orient and position, +// return the point in 3-space in outpnt. +void model_find_world_dir(vector * out_dir, vector *in_dir,int model_num, int sub_model_num, matrix * objorient, vector * objpos ) +{ + vector pnt; + vector tpnt; + matrix m; + int mn; + polymodel *pm = model_get(model_num); + + pnt = *in_dir; + mn = sub_model_num; + + //instance up the tree for this point + while ((mn>-1) && (pm->submodel[mn].parent > -1) ) { + + vm_angles_2_matrix(&m,&pm->submodel[mn].angs ); + vm_vec_unrotate(&tpnt,&pnt,&m); + pnt = tpnt; + + mn = pm->submodel[mn].parent; + } + + //now instance for the entire object + vm_vec_unrotate(out_dir,&pnt,objorient); +} + + + +// Clears all the submodel instances stored in a model to their defaults. +void model_clear_instance(int model_num) +{ + polymodel * pm; + int i; + + pm = model_get(model_num); + + // reset textures to original ones + for (i=0; in_textures; i++ ) { + pm->textures[i] = pm->original_textures[i]; + } + + for (i=0; in_models; i++ ) { + bsp_info *sm = &pm->submodel[i]; + + if ( pm->submodel[i].is_damaged ) { + sm->blown_off = 1; + } else { + sm->blown_off = 0; + } + sm->angs.p = 0.0f; + sm->angs.b = 0.0f; + sm->angs.h = 0.0f; + + // set pointer to other ship subsystem info [turn rate, accel, moment, axis, ...] + sm->sii = NULL; + + sm->num_arcs = 0; // Turn off any electric arcing effects + } + + for (i=0; inum_lights; i++ ) { + pm->lights[i].value = 0.0f; + } + + interp_clear_instance(); + +// if ( keyd_pressed[KEY_1] ) pm->lights[0].value = 1.0f/255.0f; +// if ( keyd_pressed[KEY_2] ) pm->lights[1].value = 1.0f/255.0f; +// if ( keyd_pressed[KEY_3] ) pm->lights[2].value = 1.0f/255.0f; +// if ( keyd_pressed[KEY_4] ) pm->lights[3].value = 1.0f/255.0f; +// if ( keyd_pressed[KEY_5] ) pm->lights[4].value = 1.0f/255.0f; +// if ( keyd_pressed[KEY_6] ) pm->lights[5].value = 1.0f/255.0f; + + +} + +// initialization during ship set +void model_clear_instance_info( submodel_instance_info * sii ) +{ + sii->blown_off = 0; + sii->angs.p = 0.0f; + sii->angs.b = 0.0f; + sii->angs.h = 0.0f; + sii->prev_angs.p = 0.0f; + sii->prev_angs.b = 0.0f; + sii->prev_angs.h = 0.0f; + + sii->cur_turn_rate = 0.0f; + sii->desired_turn_rate = 0.0f; + sii->turn_accel = 0.0f; +} + +// initialization during ship set +void model_set_instance_info(submodel_instance_info *sii, float turn_rate, float turn_accel) +{ + sii->blown_off = 0; + sii->angs.p = 0.0f; + sii->angs.b = 0.0f; + sii->angs.h = 0.0f; + sii->prev_angs.p = 0.0f; + sii->prev_angs.b = 0.0f; + sii->prev_angs.h = 0.0f; + + sii->cur_turn_rate = turn_rate * 0.0f; + sii->desired_turn_rate = turn_rate; + sii->turn_accel = turn_accel; + sii->axis_set = 0; + sii->step_zero_timestamp = timestamp(); +} + + + +// Sets the submodel instance data in a submodel (for all detail levels) +void model_set_instance(int model_num, int sub_model_num, submodel_instance_info * sii) +{ + int i; + polymodel * pm; + + pm = model_get(model_num); + + Assert( sub_model_num >= 0 ); + Assert( sub_model_num < pm->n_models ); + + if ( sub_model_num < 0 ) return; + if ( sub_model_num >= pm->n_models ) return; + bsp_info *sm = &pm->submodel[sub_model_num]; + + // Set the "blown out" flags + sm->blown_off = sii->blown_off; + + if ( sm->blown_off ) { + if ( sm->my_replacement > -1 ) { + pm->submodel[sm->my_replacement].blown_off = 0; + pm->submodel[sm->my_replacement].angs = sii->angs; + pm->submodel[sm->my_replacement].sii = sii; + } + } else { + if ( sm->my_replacement > -1 ) { + pm->submodel[sm->my_replacement].blown_off = 1; + } + } + + // Set the angles + sm->angs = sii->angs; + sm->sii = sii; + + // For all the detail levels of this submodel, set them also. + for (i=0; inum_details; i++ ) { + model_set_instance(model_num, sm->details[i], sii ); + } +} + + +// Finds a point on the rotation axis of a submodel, used in collision, generally find rotational velocity +void model_init_submodel_axis_pt(submodel_instance_info *sii, int model_num, int submodel_num) +{ + vector axis; + vector *mpoint1, *mpoint2; + vector p1, v1, p2, v2, int1; + + polymodel *pm = model_get(model_num); + Assert(pm->submodel[submodel_num].movement_type == MOVEMENT_TYPE_ROT); + Assert(sii); + + mpoint1 = NULL; + mpoint2 = NULL; + + // find 2 fixed points in submodel RF + // these will be rotated to about the axis an angle of 0 and PI and we'll find the intersection of the + // two lines to find a point on the axis + if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_X) { + axis = vmd_x_vector; + mpoint1 = &vmd_y_vector; + mpoint2 = &vmd_z_vector; + } else if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_Y) { + mpoint1 = &vmd_x_vector; + axis = vmd_z_vector; // rotation about y is a change in heading (p,b,h), so we need z + mpoint2 = &vmd_z_vector; + } else if (pm->submodel[submodel_num].movement_axis == MOVEMENT_AXIS_Z) { + mpoint1 = &vmd_x_vector; + mpoint2 = &vmd_y_vector; + axis = vmd_y_vector; // rotation about z is a change in bank (p,b,h), so we need y + } else { + // must be one of these axes or submodel_rot_hit is incorrectly set + Int3(); + } + + // copy submodel angs + angles copy_angs = pm->submodel[submodel_num].angs; + + // find two points rotated into model RF when angs set to 0 + vm_vec_copy_scale((vector*)&pm->submodel[submodel_num].angs, &axis, 0.0f); + model_find_world_point(&p1, mpoint1, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector); + model_find_world_point(&p2, mpoint2, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector); + + // find two points rotated into model RF when angs set to PI + vm_vec_copy_scale((vector*)&pm->submodel[submodel_num].angs, &axis, PI); + model_find_world_point(&v1, mpoint1, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector); + model_find_world_point(&v2, mpoint2, model_num, submodel_num, &vmd_identity_matrix, &vmd_zero_vector); + + // reset submodel angs + pm->submodel[submodel_num].angs = copy_angs; + + // find direction vectors of the two lines + vm_vec_sub2(&v1, &p1); + vm_vec_sub2(&v2, &p2); + + // find the intersection of the two lines + float s, t; + fvi_two_lines_in_3space(&p1, &v1, &p2, &v2, &s, &t); + + // find the actual intersection points + vm_vec_scale_add(&int1, &p1, &v1, s); + + // set flag to init + sii->pt_on_axis = int1; + sii->axis_set = 1; +} + + +// Adds an electrical arcing effect to a submodel +void model_add_arc(int model_num, int sub_model_num, vector *v1, vector *v2, int arc_type ) +{ + polymodel * pm; + + pm = model_get(model_num); + + if ( sub_model_num == -1 ) { + sub_model_num = pm->detail[0]; + } + + Assert( sub_model_num >= 0 ); + Assert( sub_model_num < pm->n_models ); + + if ( sub_model_num < 0 ) return; + if ( sub_model_num >= pm->n_models ) return; + bsp_info *sm = &pm->submodel[sub_model_num]; + + if ( sm->num_arcs < MAX_ARC_EFFECTS ) { + sm->arc_type[sm->num_arcs] = (ubyte)arc_type; + sm->arc_pts[sm->num_arcs][0] = *v1; + sm->arc_pts[sm->num_arcs][1] = *v2; + sm->num_arcs++; + } +} + +// function to return an index into the docking_bays array which matches the criteria passed +// to this function. dock_type is one of the DOCK_TYPE_XXX defines in model.h +int model_find_dock_index(int modelnum, int dock_type) +{ + int i; + + polymodel *pm; + + pm = model_get(modelnum); + + // no docking points -- return -1 + if ( pm->n_docks <= 0 ) + return -1; + + for (i = 0; i < pm->n_docks; i++ ) { + if ( dock_type & pm->docking_bays[i].type_flags ) + return i; + } + + return -1; +} + +int model_get_dock_index_type(int modelnum, int index) +{ + polymodel *pm = model_get(modelnum); + + return pm->docking_bays[index].type_flags; +} + +// get all the different docking point types on a model +int model_get_dock_types(int modelnum) +{ + int i, type = 0; + polymodel *pm; + + pm = model_get(modelnum); + for (i=0; in_docks; i++) + type |= pm->docking_bays[i].type_flags; + + return type; +} + +// function to return an index into the docking_bays array which matches the string passed +// Fred uses strings to identify docking positions. This functin also accepts generic strings +// so that a desginer doesn't have to know exact names if building a mission from hand. +int model_find_dock_name_index( int modelnum, char *name ) +{ + int i; + polymodel *pm; + + pm = model_get(modelnum); + if ( pm->n_docks <= 0 ) + return -1; + + // check the generic names and call previous function to find first dock point of + // the specified type + if ( !stricmp(name, "cargo") ) + return model_find_dock_index( modelnum, DOCK_TYPE_CARGO ); + else if (!stricmp( name, "rearm") ) + return model_find_dock_index( modelnum, DOCK_TYPE_REARM ); + else if (!stricmp( name, "generic") ) + return model_find_dock_index( modelnum, DOCK_TYPE_GENERIC ); + + for (i = 0; i < pm->n_docks; i++ ) { + if ( !stricmp(pm->docking_bays[i].name, name) ) + return i; + } + + // if we get here, name wasn't found -- return -1 and hope for the best + return -1; +} + +// returns the actual name of a docking point on a model, needed by Fred. +char *model_get_dock_name(int modelnum, int index) +{ + polymodel *pm; + + pm = model_get(modelnum); + Assert((index >= 0) && (index < pm->n_docks)); + return pm->docking_bays[index].name; +} + +int model_get_num_dock_points(int modelnum) +{ + polymodel *pm; + + pm = model_get(modelnum); + return pm->n_docks; +} diff --git a/src/nebedit/nebedit.cpp b/src/nebedit/nebedit.cpp new file mode 100644 index 0000000..1085174 --- /dev/null +++ b/src/nebedit/nebedit.cpp @@ -0,0 +1,967 @@ +/* + * $Logfile: /Freespace2/code/nebedit/NebEdit.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Program to edit nebulas in 2d + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 7 7/15/99 3:07p Dave + * 32 bit detection support. Mouse coord commandline. + * + * 6 5/19/99 4:07p Dave + * Moved versioning code into a nice isolated common place. Fixed up + * updating code on the pxo screen. Fixed several stub problems. + * + * 5 1/08/99 2:06p Dave + * Fixed pfoview for software mode. + * + * 4 12/18/98 1:14a Dave + * Rough 1024x768 support for Direct3D. Proper detection and usage through + * the launcher. + * + * 3 11/13/98 2:32p Dave + * Improved commandline parsing for exe pathname. + * + * 2 10/24/98 9:51p Dave + * + * 1 10/24/98 9:39p Dave + * + * 13 4/13/98 10:11a John + * Made timer functions thread safe. Made timer_init be called in all + * projects. + * + * 12 3/23/98 1:35p Sandeep + * + * 11 3/05/98 11:15p Hoffoss + * Changed non-game key checking to use game_check_key() instead of + * game_poll(). + * + * 10 12/29/97 5:10p Allender + * fixed problems with speed not being reported properly in multiplayer + * games. Made read_flying_controls take frametime as a parameter. More + * ship/weapon select stuff + * + * 9 12/04/97 3:47p John + * Made joystick move mouse cursor + * + * 8 11/21/97 11:32a John + * Added nebulas. Fixed some warpout bugs. + * + * 7 11/19/97 10:15p Adam + * upped maxtris to 200 + * + * 6 11/19/97 4:28p Sandeep + * added poly/vert counter + * + * 5 11/19/97 1:59p Sandeep + * Added multiple vertex editing mode + * + * 4 11/16/97 2:29p John + * added versioning to nebulas; put nebula code into freespace. + * + * 3 11/13/97 12:04p Sandeep + * Added face editing support, deletion support, and a saving and loading + * stuff. + * + * 2 11/10/97 9:59p John + * some tweaking + * + * 1 11/10/97 9:42p John + * + * $NoKeywords: $ + */ + + +#include +#include +#include +#include +#include +#include + +#include "pstypes.h" +#include "2d.h" +#include "3d.h" +#include "key.h" +#include "palman.h" +#include "bmpman.h" +#include "timer.h" +#include "floating.h" +#include "osapi.h" +#include "cfile.h" +#include "linklist.h" +#include "lighting.h" +#include "mouse.h" +#include "vecmat.h" +#include "physics.h" +#include "model.h" +#include "font.h" + + +#define SCREEN_W 640 +#define SCREEN_H 480 + +vector ViewerPos; +matrix ViewerOrient; +matrix ModelOrient; +vector ModelPos; +physics_info ViewerPhysics; +float ViewerZoom = 1.0f; + +int test_model = -1; +int Fred_running = 0; +int Pofview_running = 0; +float flFrametime = 0.0f; + +int Font1 = -1; + +color color_green; + +vector Global_light_world = { 0.208758f, -0.688253f, -0.694782f }; + +// nebula stuff + +#define NEB_W 6 +#define NEB_H 6 +#define MAX_TRIS 200 +#define MAX_POINTS 300 + +int neb_w = 0, neb_h = 0; + +int nebula_inited = 0; +int num_pts = 0; + +int x[MAX_POINTS], y[MAX_POINTS], l[MAX_POINTS]; +float scale_factor = 1.0f; + +int num_tris = 0; +int tri[MAX_TRIS][3]; + +color nebula_color; + +int Mouse_x, Mouse_y; +int Current_point; +BOOL Selected[MAX_POINTS]; +BOOL Sel_mode = 0; // 0 = 1 point at a time, 1 = select multiple points +int Current_face; + +int View_mode = 0; // 0 = 2d editor, 1 = 3d viewer + +int Vert_mode = 0; // 0 = Move vertices/Add vertices, 2 = Move face/Add face + +int Which_vert = 0; // Current vertex of the faceadd + +int Orig_pos_x; +int Orig_pos_y; +int End_pos_x; +int End_pos_y; +BOOL Draw_sel_box = FALSE; + +int Neb_created = 0; + +int Nebedit_running = 1; + +extern int load_nebula_sub(char*); +extern void project_2d_onto_sphere(vector *, float, float); + +void create_default_neb() +{ + int i,j; + neb_w = NEB_W; + neb_h = NEB_H; + num_pts = neb_w*neb_h; + + for (j=0; jsx = i2fl(x[index[v]]); + verts[v]->sy = i2fl(y[index[v]]); + verts[v]->u = 0.0f; + verts[v]->v = 0.0f; + verts[v]->sw = 1.0f; + verts[v]->r = verts[v]->g = verts[v]->b = (ubyte)(i2fl(l[index[v]])/31.0f); + + } + +// gr_set_color( 0, 0, 0 ); + gr_tmapper(3, verts, TMAP_FLAG_RAMP | TMAP_FLAG_GOURAUD | TMAP_FLAG_NEBULA ); + + if ( !keyd_pressed[KEY_LSHIFT] ) { + gr_set_color(100,100,100); + gr_line( x[i], y[i], x[j], y[j] ); + gr_line( x[j], y[j], x[k], y[k] ); + gr_line( x[k], y[k], x[i], y[i] ); + } +} + +void nebula_draw_2d() +{ + int i; + + for (i=0; i-1)) { + gr_set_color(0,100,0); + gr_circle( x[Current_point], y[Current_point], 5); + } else if (Vert_mode == 1) { + for (i=0;i-1) { + gr_line(x[tri[Current_face][0]], y[tri[Current_face][0]], + x[tri[Current_face][1]], y[tri[Current_face][1]]); + gr_line(x[tri[Current_face][1]], y[tri[Current_face][1]], + x[tri[Current_face][2]], y[tri[Current_face][2]]); + gr_line(x[tri[Current_face][2]], y[tri[Current_face][2]], + x[tri[Current_face][0]], y[tri[Current_face][0]]); + } + } +} + + +void draw_tri_3d( int i, int j, int k ) +{ + int index[3]; + + index[2] = k; + index[1] = j; + index[0] = i; + + vertex va, vb, vc; + vertex * verts[3] = { &va, &vb, &vc }; + //gr_zbuffering = 0; + for (int v=0; v<3; v++ ) { + vector tmp; + + project_2d_onto_sphere( &tmp, 1.0f - i2fl(x[index[v]])/640.0f, i2fl(y[index[v]])/480.0f ); + + vm_vec_scale( &tmp, 10.0f ); + + g3_rotate_faraway_vertex( verts[v], &tmp ); + //g3_rotate_vertex( verts[v], &tmp ); + g3_project_vertex( verts[v] ); + + verts[v]->r = verts[v]->g = verts[v]->b = (ubyte)(i2fl(l[index[v]])/31.0f); + } + + //gr_zbuffering = 0; + //gr_set_color_fast( &nebula_color ); + //gr_set_color( 0, 0, 0 ); + g3_draw_poly(3, verts, TMAP_FLAG_RAMP | TMAP_FLAG_GOURAUD | TMAP_FLAG_NEBULA ); +} + +void nebula_draw_3d() +{ + int i; + for (i=0; i= 0){ + gr_printf(180, 20, "Current vertex intensity : %d\n", l[Current_point]); + } + } else if (Vert_mode ==1) { + gr_printf(180,10,"Face Editing"); + } + char blah[255]; + gr_printf(20,30,"# Points:"); + gr_printf(100,30, itoa(num_pts, blah, 10)); + gr_printf(220,30,"# Polys:"); + gr_printf(300,30, itoa(num_tris, blah, 10)); + } else { + nebula_draw_3d(); + model_render( test_model, &ModelOrient, &ModelPos ); + } + + g3_end_frame(); + + gr_flip(); +} + +int get_closest(int mx, int my) +{ + int i, closest = -1, cval = 100000; + + for (i=0; ii) tri[j][0]--; + if (tri[j][1]>i) tri[j][1]--; + if (tri[j][2]>i) tri[j][2]--; + } + for (j=i;jx2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1>y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + for (int i=0;i=x1) && + (y[i]<=y2) && (y[i]>=y1)) { + Selected[i] = TRUE; + } + } +} + +void sphericalize_nebula() +{ + int idx1, idx2; + int px = SCREEN_W / (neb_w - 1); + int py = SCREEN_H / (neb_h - 1); + + // flatten out the nebula so that it covers the entire sphere evenly + for(idx1=0; idx1heading; + float temp1 = ci->pitch; + memset( ci, 0, sizeof(control_info) ); + ci->heading = temp; + ci->pitch = temp1; + } + + // From keyboard... + kh = (key_down_timef(KEY_PAD6) - key_down_timef(KEY_PAD4))/8.0f; + if (kh == 0.0f) + ci->heading = 0.0f; + else if (kh > 0.0f) { + if (ci->heading < 0.0f) + ci->heading = 0.0f; + } else // kh < 0 + if (ci->heading > 0.0f) + ci->heading = 0.0f; + ci->heading += kh; + + kh = (key_down_timef(KEY_PAD8) - key_down_timef(KEY_PAD2))/8.0f; + if (kh == 0.0f) + ci->pitch = 0.0f; + else if (kh > 0.0f) { + if (ci->pitch < 0.0f) + ci->pitch = 0.0f; + } else // kh < 0 + if (ci->pitch > 0.0f) + ci->pitch = 0.0f; + ci->pitch += kh; + + ci->bank = (key_down_timef(KEY_PAD7) - key_down_timef(KEY_PAD9))*.75f; + ci->forward = key_down_timef(KEY_A) - key_down_timef(KEY_Z); + ci->sideways = key_down_timef(KEY_PAD3) - key_down_timef(KEY_PAD1); + ci->vertical = key_down_timef(KEY_PADPLUS) - key_down_timef(KEY_PADENTER); +} + +int check_keys() +{ + int k; + + while( (k = key_inkey()) != 0 ) { +//mprintf(( "Key = %x\n", k )); + if ( k == KEY_ESC ) { + return 1; + } + + switch( k ) { + case KEY_ENTER: + Sel_mode = FALSE; + Vert_mode = !Vert_mode; + Which_vert = 0; + break; + + case KEY_DELETE: + if (Sel_mode) break; + if (Vert_mode==1) delete_face(Current_face); + else if (Vert_mode==0) { + delete_vert(Current_point); + } + break; + + case KEY_MINUS: + scale_factor -= 0.05f; + mprintf(( "Scale = %.1f\n", scale_factor )); + break; + + + case KEY_EQUAL: + scale_factor += 0.05f; + mprintf(( "Scale = %.1f\n", scale_factor )); + break; + + case KEY_INSERT: + Sel_mode = !Sel_mode; + break; + + case KEY_SPACEBAR: + View_mode = !View_mode; + break; + + case KEY_COMMA: + if (Sel_mode) { + int i; + for (i=0;i 0 ) { + l[i]--; + } + } + } else if (Vert_mode==0) { + if ( Current_point > -1 ) { + if ( l[Current_point] > 0 ) { + l[Current_point]--; + } + } + } else if (Vert_mode ==1) { + if ( l[tri[Current_face][0]] > 0 ) { + l[tri[Current_face][0]]--; + } + if ( l[tri[Current_face][1]] > 0 ) { + l[tri[Current_face][1]]--; + } + if ( l[tri[Current_face][2]] > 0 ) { + l[tri[Current_face][2]]--; + } + } + break; + + case KEY_PERIOD: + if (Sel_mode) { + int i; + for (i=0;i -1 ) { + if ( l[Current_point] < 31 ) { + l[Current_point]++; + } + } + } else if (Vert_mode ==1) { + if ( l[tri[Current_face][0]] <31 ) { + l[tri[Current_face][0]]++; + } + if ( l[tri[Current_face][1]] <31) { + l[tri[Current_face][1]]++; + } + if ( l[tri[Current_face][2]] <31 ) { + l[tri[Current_face][2]]++; + } + } + + break; + + case KEY_F5: + save_nebula(); + break; + case KEY_F7: + load_nebula(); + break; + + case KEY_BACKSP: + sphericalize_nebula(); + break; + } + } + return 0; +} + +void os_close() +{ + exit(1); +} + +int newtri[3]; + +int mdflag = 0; + +int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow) +{ + int i; + fix t1, t2; + control_info ci; + float speed = 20.0f; // how fast viewer moves + + // setup the fred exe directory so CFILE can init properly + /* + char *c = GetCommandLine(); + Assert(c != NULL); + char *tok = strtok(c, " "); + Assert(tok != NULL); + */ + + timer_init(); + // cfile_init(tok); + cfile_init(__argv[0]); + os_init( "NebEdit", "NebEdit" ); //SCREEN_W, SCREEN_H ); + palette_load_table( "gamepalette1-01.pcx" ); + gr_init(GR_640, GR_SOFTWARE, 8); + key_init(); + mouse_init(); + Font1 = gr_init_font( "font01.vf" ); + gr_init_alphacolor( &color_green, 0,255,0,255 ); + + test_model = model_load( "fighter01.pof", 0, NULL ); + + physics_init( &ViewerPhysics ); + ViewerPhysics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED; + + ViewerPhysics.max_vel.x = 2.0f*speed; //sideways + ViewerPhysics.max_vel.y = 2.0f*speed; //up/down + ViewerPhysics.max_vel.z = 2.0f*speed; //forward + ViewerPhysics.max_rear_vel = 2.0f*speed; //backward -- controlled seperately + + memset( &ci, 0, sizeof(control_info) ); + + ModelOrient = vmd_identity_matrix; + ModelPos.x=0.0f; ModelPos.y = 0.0f; ModelPos.z = 0.0f; + + ViewerOrient = vmd_identity_matrix; + ViewerPos.x=0.0f; ViewerPos.y = 0.0f; ViewerPos.z = -50.0f; + + flFrametime = 0.033f; + + t1 = timer_get_fixed_seconds(); + + nebula_init(); + + int some_selected = 0; + + while(1) { + some_selected = FALSE; + if (Sel_mode==1) { + for (i=0;i2) { + Which_vert = 0; + num_tris++; + } + } + } + } + } + controls_read_all(&ci, flFrametime ); + physics_read_flying_controls( &ViewerOrient, &ViewerPhysics, &ci, flFrametime ); + physics_sim(&ViewerPos, &ViewerOrient, &ViewerPhysics, flFrametime ); + + render_frame(); + + t2 = timer_get_fixed_seconds(); + if ( t2 > t1 ) { + flFrametime = f2fl(t2 - t1); + } + + t1 = t2; + } + + nebedit_close(); + + return 0; +} + +// Stub functions and variables. +// These do nothing but are needed to prevent link errors. +void demo_set_playback_filter() {} + +void freespace_menu_background() +{ + gr_reset_clip(); + gr_clear(); +} + +int game_check_key() +{ + return key_inkey(); +} + +int game_poll() +{ + return key_inkey(); +} + +vector Camera_pos; +vector Dead_player_last_vel; +// end stubs diff --git a/src/nebedit/nebstubs.cpp b/src/nebedit/nebstubs.cpp new file mode 100644 index 0000000..06204db --- /dev/null +++ b/src/nebedit/nebstubs.cpp @@ -0,0 +1,85 @@ +/* + * $Logfile: /Freespace2/code/nebedit/Nebstubs.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 5 5/19/99 4:07p Dave + * Moved versioning code into a nice isolated common place. Fixed up + * updating code on the pxo screen. Fixed several stub problems. + * + * 4 4/07/99 6:22p Dave + * Fred and Freespace support for multiple background bitmaps and suns. + * Fixed link errors on all subprojects. Moved encrypt_init() to + * cfile_init() and lcl_init(), since its safe to call twice. + * + * 3 1/06/99 2:26p Dave + * Stubs and release build fixes. + * + * 2 10/24/98 11:46p Dave + * + * 1 10/24/98 11:45p Dave + * + * 2 10/24/98 9:51p Dave + * + * + * $NoKeywords: $ + */ + +void game_enter_state(int, int){} +void game_leave_state(int, int){} +void game_do_state(int){} +void game_process_event(int, int){} +char *Game_CDROM_dir; +void game_stop_looped_sounds(){} +int set_cdrom_path(int){return 0;} +int find_freespace_cd(char*){return 0;} +void game_flush(){} +int Game_skill_level; +void game_stop_time(){} +void game_start_time(){} +void game_do_state_common(int, int){} +void game_set_frametime(int){} +void game_increase_skill_level(){} +int Test_begin; +long Game_time_compression; +int Framerate_delay; +char *Game_current_mission_filename; +int Warpout_forced; +float Warpout_time; +int game_start_mission(void){return 0;} +void game_level_close(){} +void game_flash(float, float, float){} +void game_whack_apply(float, float){} +int game_do_cd_check(char*){return 0;} +struct fs_builtin_mission *game_find_builtin_mission(char*){return 0;} +int Game_do_state_should_skip; +int Show_target_weapons; +int Show_target_debug_info; +int Sun_drew; +int Game_subspace_effect; +void game_load_palette(){} +void game_format_time(long, char*){} +float Freespace_gamma; +void get_version_string(char*){} +int game_get_default_skill_level(){return 0;} +int game_cd_changed(){return 0;} +void game_set_view_clip(){} +float Viewer_zoom; +int Interface_framerate; +int Game_weapons_tbl_valid; +int Game_ships_tbl_valid; +void game_shudder_apply(int, float){} +int Debug_octant; +int game_hacked_data(){return 0;} +int game_do_cd_mission_check(char*){return 0;} +int Player_multi_died_check; +void game_tst_mark(struct object*, struct ship*){} +int tst; +int game_single_step; +int last_single_step; \ No newline at end of file diff --git a/src/nebula/neb.cpp b/src/nebula/neb.cpp new file mode 100644 index 0000000..0efa12b --- /dev/null +++ b/src/nebula/neb.cpp @@ -0,0 +1,1845 @@ +/* + * $Logfile: /Freespace2/code/Nebula/Neb.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Nebula effect + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 50 8/30/99 5:01p Dave + * Made d3d do less state changing in the nebula. Use new chat server for + * PXO. + * + * 49 8/10/99 6:54p Dave + * Mad optimizations. Added paging to the nebula effect. + * + * 48 8/05/99 2:05a Dave + * Whee. + * + * 47 7/30/99 10:55a Anoop + * Hmm. Fixed release build problem again, with area-rotated bitmaps. + * + * 46 7/29/99 10:47p Dave + * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs. + * + * 44 7/29/99 12:05a Dave + * Nebula speed optimizations. + * + * 43 7/19/99 7:20p Dave + * Beam tooling. Specialized player-killed-self messages. Fixed d3d nebula + * pre-rendering. + * + * 42 7/18/99 5:20p Dave + * Jump node icon. Fixed debris fogging. Framerate warning stuff. + * + * 41 7/13/99 1:15p Dave + * 32 bit support. Whee! + * + * 40 7/09/99 5:54p Dave + * Seperated cruiser types into individual types. Added tons of new + * briefing icons. Campaign screen. + * + * 39 7/07/99 10:44a Jamesa + * Make sure the nebula regens properly after loading a mission. + * + * 38 6/11/99 2:32p Dave + * Toned down nebula brightness a bit. + * + * 37 5/26/99 3:39p Dave + * Fixed nebula regeneration problem. Removed optimizations from + * neblightning.cpp + * + * 36 5/26/99 11:46a Dave + * Added ship-blasting lighting and made the randomization of lighting + * much more customizable. + * + * 35 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * $NoKeywords: $ + */ + +#include "neb.h" +#include "vecmat.h" +#include "3d.h" +#include "bmpman.h" +#include "2d.h" +#include "object.h" +#include "glide.h" +#include "timer.h" +#include "multi.h" +#include "freespace.h" +#include "key.h" +#include "nebula.h" +#include "starfield.h" +#include "parselo.h" +#include "beam.h" +#include "sound.h" +#include "gamesnd.h" +#include "grinternal.h" + +#include "alphacolors.h" + +// -------------------------------------------------------------------------------------------------------- +// NEBULA DEFINES/VARS +// + +// #define NEB2_THUMBNAIL + +/* +3D CARDS THAT FOG PROPERLY +Voodoo1 +Voodoo2 +G200 +TNT + +3D CARDS THAT DON'T FOG PROPERLY +Permedia2 +AccelStar II +*/ + +// if nebula rendering is active (DCF stuff - not mission specific) +int Neb2_render_mode = NEB2_RENDER_NONE; + +// array of neb2 poofs +char Neb2_poof_filenames[MAX_NEB2_POOFS][MAX_FILENAME_LEN] = { + "", "", "", "", "", "" +}; +int Neb2_poofs[MAX_NEB2_POOFS] = { -1, -1, -1, -1, -1, -1 }; +int Neb2_poof_flags = ( (1<<0) | (1<<1) | (1<<2) | (1<<3) | (1<<4) | (1<<5) ); +int Neb2_poof_count = 0; + +// array of neb2 bitmaps +char Neb2_bitmap_filenames[MAX_NEB2_BITMAPS][MAX_FILENAME_LEN] = { + "", "", "", "", "", "" +}; +int Neb2_bitmap[MAX_NEB2_BITMAPS] = { -1, -1, -1, -1, -1, -1 }; +int Neb2_bitmap_count = 0; + +// texture to use for this level +char Neb2_texture_name[MAX_FILENAME_LEN] = ""; + +// nebula flags +#define NF_USED (1<<0) // if this nebula slot is used + +float max_rotation = 3.75f; +float neb2_flash_fade = 0.3f; + +// fog values for different ship types +float Neb_ship_fog_vals_glide[MAX_SHIP_TYPE_COUNTS][2] = { + {0.0f, 0.0f}, // SHIP_TYPE_NONE + {10.0f, 500.0f}, // SHIP_TYPE_CARGO + {10.0f, 500.0f}, // SHIP_TYPE_FIGHTER_BOMBER + {10.0f, 600.0f}, // SHIP_TYPE_CRUISER + {10.0f, 600.0f}, // SHIP_TYPE_FREIGHTER + {10.0f, 750.0f}, // SHIP_TYPE_CAPITAL + {10.0f, 500.0f}, // SHIP_TYPE_TRANSPORT + {10.0f, 500.0f}, // SHIP_TYPE_REPAIR_REARM + {10.0f, 500.0f}, // SHIP_TYPE_NAVBUOY + {10.0f, 500.0f}, // SHIP_TYPE_SENTRYGUN + {10.0f, 600.0f}, // SHIP_TYPE_ESCAPEPOD + {10.0f, 1000.0f}, // SHIP_TYPE_SUPERCAP + {10.0f, 500.0f}, // SHIP_TYPE_STEALTH + {10.0f, 500.0f}, // SHIP_TYPE_FIGHTER + {10.0f, 500.0f}, // SHIP_TYPE_BOMBER + {10.0f, 750.0f}, // SHIP_TYPE_DRYDOCK + {10.0f, 600.0f}, // SHIP_TYPE_AWACS + {10.0f, 600.0f}, // SHIP_TYPE_GAS_MINER + {10.0f, 600.0f}, // SHIP_TYPE_CORVETTE + {10.0f, 1000.0f}, // SHIP_TYPE_KNOSSOS_DEVICE +}; +float Neb_ship_fog_vals_d3d[MAX_SHIP_TYPE_COUNTS][2] = { + {0.0f, 0.0f}, // SHIP_TYPE_NONE + {10.0f, 500.0f}, // SHIP_TYPE_CARGO + {10.0f, 500.0f}, // SHIP_TYPE_FIGHTER_BOMBER + {10.0f, 600.0f}, // SHIP_TYPE_CRUISER + {10.0f, 600.0f}, // SHIP_TYPE_FREIGHTER + {10.0f, 750.0f}, // SHIP_TYPE_CAPITAL + {10.0f, 500.0f}, // SHIP_TYPE_TRANSPORT + {10.0f, 500.0f}, // SHIP_TYPE_REPAIR_REARM + {10.0f, 500.0f}, // SHIP_TYPE_NAVBUOY + {10.0f, 500.0f}, // SHIP_TYPE_SENTRYGUN + {10.0f, 600.0f}, // SHIP_TYPE_ESCAPEPOD + {10.0f, 1000.0f}, // SHIP_TYPE_SUPERCAP + {10.0f, 500.0f}, // SHIP_TYPE_STEALTH + {10.0f, 500.0f}, // SHIP_TYPE_FIGHTER + {10.0f, 500.0f}, // SHIP_TYPE_BOMBER + {10.0f, 750.0f}, // SHIP_TYPE_DRYDOCK + {10.0f, 600.0f}, // SHIP_TYPE_AWACS + {10.0f, 600.0f}, // SHIP_TYPE_GAS_MINER + {10.0f, 600.0f}, // SHIP_TYPE_CORVETTE + {10.0f, 1000.0f}, // SHIP_TYPE_KNOSSOS_DEVICE +}; + +// fog near and far values for rendering the background nebula +#define NEB_BACKG_FOG_NEAR_GLIDE 2.5f +#define NEB_BACKG_FOG_NEAR_D3D 4.5f +#define NEB_BACKG_FOG_FAR_GLIDE 10.0f +#define NEB_BACKG_FOG_FAR_D3D 10.0f +float Neb_backg_fog_near = NEB_BACKG_FOG_NEAR_GLIDE; +float Neb_backg_fog_far = NEB_BACKG_FOG_FAR_GLIDE; + +// stats +int pneb_tried = 0; // total pnebs tried to render +int pneb_tossed_alpha = 0; // pnebs tossed because of alpha optimization +int pneb_tossed_dot = 0; // pnebs tossed because of dot product +int pneb_tossed_off = 0; // pnebs tossed because of being offscree +int neb_tried = 0; // total nebs tried +int neb_tossed_alpha = 0; // nebs tossed because of alpha +int neb_tossed_dot = 0; // nebs tossed because of dot product +int neb_tossed_count = 0; // nebs tossed because of max render count + +// the AWACS suppresion level for the nebula +float Neb2_awacs = -1.0f; + +// how many "slices" are in the current player nebuls +int Neb2_slices = 5; + +cube_poof Neb2_cubes[MAX_CPTS][MAX_CPTS][MAX_CPTS]; + +// nebula detail level +typedef struct neb2_detail { + float max_alpha_glide; // max alpha for this detail level in Glide + float max_alpha_d3d; // max alpha for this detail level in D3d + float break_alpha; // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot + float break_x, break_y; // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen + float cube_dim; // total dimension of player poof cube + float cube_inner; // inner radius of the player poof cube + float cube_outer; // outer radius of the player pood cube + float prad; // radius of the poofs + float wj, hj, dj; // width, height, depth jittering. best left at 1.0 +} neb2_detail; +neb2_detail Neb2_detail[MAX_DETAIL_LEVEL] = { + { // lowest detail level + 0.575f, // max alpha for this detail level in Glide + 0.71f, // max alpha for this detail level in D3d + 0.13f, // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot + 150.0f, 150.0f / 1.3333f, // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen + 510.0f, // total dimension of player poof cube + 50.0f, // inner radius of the player poof cube + 250.0f, // outer radius of the player pood cube + 120.0f, // radius of the poofs + 1.0f, 1.0f, 1.0f // width, height, depth jittering. best left at 1.0 + }, + { // 2nd lowest detail level + 0.575f, // max alpha for this detail level in Glide + 0.71f, // max alpha for this detail level in D3d + 0.125f, // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot + 300.0f, 300.0f / 1.3333f, // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen + 550.0f, // total dimension of player poof cube + 100.0f, // inner radius of the player poof cube + 250.0f, // outer radius of the player pood cube + 125.0f, // radius of the poofs + 1.0f, 1.0f, 1.0f // width, height, depth jittering. best left at 1.0 + }, + { // 2nd highest detail level + 0.575f, // max alpha for this detail level in Glide + 0.71f, // max alpha for this detail level in D3d + 0.1f, // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot + 300.0f, 300.0f / 1.3333f, // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen + 550.0f, // total dimension of player poof cube + 150.0f, // inner radius of the player poof cube + 250.0f, // outer radius of the player pood cube + 125.0f, // radius of the poofs + 1.0f, 1.0f, 1.0f // width, height, depth jittering. best left at 1.0 + }, + { // higest detail level + 0.475f, // max alpha for this detail level in Glide + 0.575f, // max alpha for this detail level in D3d + 0.05f, // break alpha (below which, poofs don't draw). this affects the speed and visual quality a lot + 200.0f, 200.0f / 1.3333f, // x and y alpha fade/break values. adjust alpha on the polys as they move offscreen + 750.0f, // total dimension of player poof cube + 200.0f, // inner radius of the player poof cube + 360.0f, // outer radius of the player pood cube + 150.0f, // radius of the poofs + 1.0f, 1.0f, 1.0f // width, height, depth jittering. best left at 1.0 + }, +}; +neb2_detail *Nd = &Neb2_detail[MAX_DETAIL_LEVEL - 2]; + +int Neb2_background_color[3] = {0, 0, 255}; // rgb background color (used for lame rendering) + +int Neb2_regen = 0; + +// -------------------------------------------------------------------------------------------------------- +// NEBULA FORWARD DECLARATIONS +// + +// return the alpha the passed poof should be rendered with, for a 2 shell nebula +float neb2_get_alpha_2shell(float inner_radius, float outer_radius, float magic_num, vector *v); + +// return an alpha value for a bitmap offscreen based upon "break" value +float neb2_get_alpha_offscreen(float sx, float sy, float incoming_alpha); + +// do a pre-render of the background nebula +void neb2_pre_render(vector *eye_pos, matrix *eye_orient); + +// fill in the position of the eye for this frame +void neb2_get_eye_pos(vector *eye); + +// fill in the eye orient for this frame +void neb2_get_eye_orient(matrix *eye); + +// get a (semi) random bitmap to use for a poof +int neb2_get_bitmap(); + +// regenerate the player nebula +void neb2_regen(); + + +// -------------------------------------------------------------------------------------------------------- +// NEBULA FUNCTIONS +// + +// initialize neb2 stuff at game startup +void neb2_init() +{ + char name[255] = ""; + + // read in the nebula.tbl + read_file_text("nebula.tbl"); + reset_parse(); + + // background bitmaps + Neb2_bitmap_count = 0; + while(!optional_string("#end")){ + // nebula + required_string("+Nebula:"); + stuff_string(name, F_NAME, NULL); + + if(Neb2_bitmap_count < MAX_NEB2_BITMAPS){ + strcpy(Neb2_bitmap_filenames[Neb2_bitmap_count++], name); + } + } + + // poofs + Neb2_poof_count = 0; + while(!optional_string("#end")){ + // nebula + required_string("+Poof:"); + stuff_string(name, F_NAME, NULL); + + if(Neb2_poof_count < MAX_NEB2_POOFS){ + strcpy(Neb2_poof_filenames[Neb2_poof_count++], name); + } + } + + // should always have 6 neb poofs + Assert(Neb2_poof_count == 6); +} + +// set detail level +void neb2_set_detail_level(int level) +{ + // sanity + if(level < 0){ + Nd = &Neb2_detail[0]; + return; + } + if(level >= MAX_DETAIL_LEVEL){ + Nd = &Neb2_detail[MAX_DETAIL_LEVEL-1]; + return; + } + + Nd = &Neb2_detail[level]; + + // regen the player neb + Neb2_regen = 1; +} + +// initialize nebula stuff - call from game_post_level_init(), so the mission has been loaded +void neb2_level_init() +{ + int idx; + + // standalone servers can bail here + if(Game_mode & GM_STANDALONE_SERVER){ + return; + } + + // if the mission is not a fullneb mission, skip + if(!(The_mission.flags & MISSION_FLAG_FULLNEB)){ + Neb2_render_mode = NEB2_RENDER_NONE; + Neb2_awacs = -1.0f; + return; + } + + /* + if(gr_screen.mode == GR_DIRECT3D){ + max_alpha_player = NEB2_MAX_ALPHA_D3D; + } else { + max_alpha_player = NEB2_MAX_ALPHA_GLIDE; + } + */ + + // by default we'll use pof rendering + Neb2_render_mode = NEB2_RENDER_POF; + stars_set_background_model(BACKGROUND_MODEL_FILENAME, Neb2_texture_name); + + // load in all nebula bitmaps + for(idx=0; idx= 0){ + bm_unload(Neb2_poofs[idx]); + Neb2_poofs[idx] = -1; + } + } + + // unflag the mission as being fullneb so stuff doesn't fog in the techdata room :D + The_mission.flags &= ~MISSION_FLAG_FULLNEB; +} + +// call before beginning all rendering +void neb2_render_setup(vector *eye_pos, matrix *eye_orient) +{ + // standalone servers can bail here + if(Game_mode & GM_STANDALONE_SERVER){ + return; + } + + // if the mission is not a fullneb mission, skip + if(!(The_mission.flags & MISSION_FLAG_FULLNEB)){ + return; + } + + // pre-render the real background nebula + neb2_pre_render(eye_pos, eye_orient); +} + +// level paging code +void neb2_page_in() +{ + int idx; + + // load in all nebula bitmaps + for(idx=0; idx= 0) && (Neb2_poof_flags & (1<type ) { + // some objects we always render + case OBJ_SHOCKWAVE: + case OBJ_JUMP_NODE: + case OBJ_NONE: + case OBJ_GHOST: + case OBJ_BEAM: + return 0; + + // any weapon over 500 meters away + case OBJ_WEAPON: + if(z_depth >= 500.0f){ + return 1; + } + break; + + // any small ship over the fog limit, or any cruiser 50% further than the fog limit + case OBJ_SHIP: + ship_info *sip; + if((objp->instance >= 0) && (Ships[objp->instance].ship_info_index >= 0)){ + sip = &Ship_info[Ships[objp->instance].ship_info_index]; + } else { + return 0; + } + + // small ships over the fog limit by a small factor + if((sip->flags & SIF_SMALL_SHIP) && (z_depth >= (fog_far * 1.3f))){ + return 1; + } + + // big ships + if((sip->flags & SIF_BIG_SHIP) && (z_depth >= (fog_far * 2.0f))){ + return 1; + } + + // huge ships + if((sip->flags & SIF_HUGE_SHIP) && (z_depth >= (fog_far * 3.0f))){ + return 1; + } + break; + + // any fireball over the fog limit for small ships + case OBJ_FIREBALL: + /* + if(z_depth >= fog_far){ + return 1; + } + */ + return 0; + break; + + // any debris over the fog limit for small ships + case OBJ_DEBRIS: + /* + if(z_depth >= fog_far){ + return 1; + } + */ + return 0; + break; + + // any asteroids 50% farther than the fog limit for small ships + case OBJ_ASTEROID: + if(z_depth >= (fog_far * 1.5f)){ + return 1; + } + break; + + // any countermeasures over 100 meters away + case OBJ_CMEASURE: + if(z_depth >= 100.0f){ + return 1; + } + break; + + // hmmm. unknown object type - should probably let it through + default: + return 0; + } + + return 0; +} + +// extend LOD +float neb2_get_lod_scale(int objnum) +{ + ship *shipp; + ship_info *sip; + + // bogus + if((objnum < 0) || (objnum >= MAX_OBJECTS) || (Objects[objnum].type != OBJ_SHIP) || (Objects[objnum].instance < 0) || (Objects[objnum].instance >= MAX_SHIPS)){ + return 1.0f; + } + shipp = &Ships[Objects[objnum].instance]; + sip = &Ship_info[shipp->ship_info_index]; + + // small ship? + if(sip->flags & SIF_SMALL_SHIP){ + return 1.8f; + } else if(sip->flags & SIF_BIG_SHIP){ + return 1.4f; + } + + // hmm + return 1.0f; +} + + +// -------------------------------------------------------------------------------------------------------- +// NEBULA FORWARD DEFINITIONS +// + +// return the alpha the passed poof should be rendered with, for a 2 shell nebula +float neb2_get_alpha_2shell(float inner_radius, float outer_radius, float magic_num, vector *v) +{ + float dist; + float alpha; + vector eye_pos; + + // get the eye position + neb2_get_eye_pos(&eye_pos); + + // determine what alpha to draw this bitmap with + // higher alpha the closer the bitmap gets to the eye + dist = vm_vec_dist_quick(&eye_pos, v); + + // if the point is inside the inner radius, alpha is based on distance to the player's eye, + // becoming more transparent as it gets close + if(dist <= inner_radius){ + // alpha per meter between the magic # and the inner radius + alpha = Nd->max_alpha_glide / (inner_radius - magic_num); + + // above value times the # of meters away we are + alpha *= (dist - magic_num); + return alpha < 0.0f ? 0.0f : alpha; + } + // if the point is outside the inner radius, it starts out as completely transparent at max + // outer radius, and becomes more opaque as it moves towards inner radius + else if(dist <= outer_radius){ + // alpha per meter between the outer radius and the inner radius + alpha = Nd->max_alpha_glide / (outer_radius - inner_radius); + + // above value times the range between the outer radius and the poof + return alpha < 0.0f ? 0.0f : alpha * (outer_radius - dist); + } + + // otherwise transparent + return 0.0f; +} + +// return an alpha value for a bitmap offscreen based upon "break" value +float neb2_get_alpha_offscreen(float sx, float sy, float incoming_alpha) +{ + float alpha = 0.0f; + float per_pixel_x = incoming_alpha / (float)Nd->break_x; + float per_pixel_y = incoming_alpha / (float)Nd->break_y; + int off_x = ((sx < 0.0f) || (sx > (float)gr_screen.max_w)); + int off_y = ((sy < 0.0f) || (sy > (float)gr_screen.max_h)); + float off_x_amount = 0.0f; + float off_y_amount = 0.0f; + + // determine how many pixels outside we are + if(off_x){ + if(sx < 0.0f){ + off_x_amount = fl_abs(sx); + } else { + off_x_amount = sx - (float)gr_screen.max_w; + } + } + if(off_y){ + if(sy < 0.0f){ + off_y_amount = fl_abs(sy); + } else { + off_y_amount = sy - (float)gr_screen.max_h; + } + } + + // if offscreen X + if(off_x){ + // offscreen X and Y - and Y is greater + if(off_y && (off_y_amount > off_x_amount)){ + alpha = incoming_alpha - (off_y_amount * per_pixel_y); + } else { + alpha = incoming_alpha - (off_x_amount * per_pixel_x); + } + } + // offscreen y + else if(off_y){ + alpha = incoming_alpha - (off_y_amount * per_pixel_y); + } + // should never get here + else { + Int3(); + } + + return alpha < 0.0f ? 0.0f : alpha; +} + +// ------------------------------------------------------------------------------------------------- +// WACKY LOCAL PLAYER NEBULA STUFF +// + +vector cube_cen; + +int crossed_border() +{ + vector eye_pos; + float ws = Nd->cube_dim / (float)Neb2_slices; + float hs = Nd->cube_dim / (float)Neb2_slices; + float ds = Nd->cube_dim / (float)Neb2_slices; + + // get the eye position + neb2_get_eye_pos(&eye_pos); + + // check left, right (0, and 1, x and -x) + if(cube_cen.x - eye_pos.x > ws){ + // -x + return 0; + } else if(eye_pos.x - cube_cen.x > ws){ + // +x + return 1; + } + + // check up, down (2, and 3, y and -y) + if(cube_cen.y - eye_pos.y > hs){ + // -y + return 2; + } else if(eye_pos.y - cube_cen.y > hs){ + // +y + return 3; + } + + // check front, back (4, and 5, z and -z) + if(cube_cen.z - eye_pos.z > ds){ + // -z + return 4; + } else if(eye_pos.z - cube_cen.z > ds){ + // +z + return 5; + } + + // nothing + return -1; +} + +void neb2_copy(int xyz, int src, int dest) +{ + int idx1, idx2; + + switch(xyz){ + case 0: + for(idx1=0; idx1cube_dim / (float)Neb2_slices; + h_incw = ws / 2.0f; + hs = Nd->cube_dim / (float)Neb2_slices; + h_inch = hs / 2.0f; + ds = Nd->cube_dim / (float)Neb2_slices; + h_incd = ds / 2.0f; + cube_corner = *cube_center; + cube_corner.x -= (Nd->cube_dim / 2.0f); + cube_corner.y -= (Nd->cube_dim / 2.0f); + cube_corner.z -= (Nd->cube_dim / 2.0f); + switch(xyz){ + case 0: + for(idx1=0; idx1x = h_incw + (ws * (float)src) + frand_range(-Nd->wj, Nd->wj); + v->y = h_inch + (hs * (float)idx1) + frand_range(-Nd->hj, Nd->hj); + v->z = h_incd + (ds * (float)idx2) + frand_range(-Nd->dj, Nd->dj); + vm_vec_add2(v, &cube_corner); + + // set the bitmap + Neb2_cubes[src][idx1][idx2].bmap = neb2_get_bitmap(); + + // set the rotation speed + Neb2_cubes[src][idx1][idx2].rot = 0.0f; + Neb2_cubes[src][idx1][idx2].rot_speed = frand_range(-max_rotation, max_rotation); + Neb2_cubes[src][idx1][idx2].flash = 0.0f; + } + } + break; + case 1: + for(idx1=0; idx1x = h_incw + (ws * (float)idx1) + frand_range(-Nd->wj, Nd->wj); + v->y = h_inch + (hs * (float)src) + frand_range(-Nd->hj, Nd->hj); + v->z = h_incd + (ds * (float)idx2) + frand_range(-Nd->dj, Nd->dj); + vm_vec_add2(v, &cube_corner); + + // set the bitmap + Neb2_cubes[idx1][src][idx2].bmap = neb2_get_bitmap(); + + // set the rotation speed + Neb2_cubes[idx1][src][idx2].rot = 0.0f; + Neb2_cubes[idx1][src][idx2].rot_speed = frand_range(-max_rotation, max_rotation); + Neb2_cubes[src][idx1][idx2].flash = 0.0f; + } + } + break; + case 2: + for(idx1=0; idx1x = h_incw + (ws * (float)idx1) + frand_range(-Nd->wj, Nd->wj); + v->y = h_inch + (hs * (float)idx2) + frand_range(-Nd->hj, Nd->hj); + v->z = h_incd + (ds * (float)src) + frand_range(-Nd->dj, Nd->dj); + vm_vec_add2(v, &cube_corner); + + // set the bitmap + Neb2_cubes[idx1][idx2][src].bmap = neb2_get_bitmap(); + + // set the rotation speed + Neb2_cubes[idx1][idx2][src].rot = 0.0f; + Neb2_cubes[idx1][idx2][src].rot_speed = frand_range(-max_rotation, max_rotation); + Neb2_cubes[src][idx1][idx2].flash = 0.0f; + } + } + break; + default: + Int3(); + break; + } +} + +// regenerate the player nebula +void neb2_regen() +{ + int idx; + vector eye_pos; + matrix eye_orient; + + mprintf(("Regenerating local nebula!\n")); + + // get eye position and orientation + neb2_get_eye_pos(&eye_pos); + neb2_get_eye_orient(&eye_orient); + + // determine the corner of the cube + cube_cen = eye_pos; + + // generate slices of the cube + for(idx=0; idxcube_dim / (float)Neb2_slices; + for(idx1=Neb2_slices-1; idx1>0; idx1--){ + neb2_copy(0, idx1-1, idx1); + } + neb2_gen_slice(0, 0, &cube_cen); + break; + // x + case 1 : + cube_cen.x += Nd->cube_dim / (float)Neb2_slices; + for(idx1=0; idx1cube_dim / (float)Neb2_slices; + for(idx1=Neb2_slices-1; idx1>0; idx1--){ + neb2_copy(1, idx1-1, idx1); + } + neb2_gen_slice(1, 0, &cube_cen); + break; + // y + case 3 : + cube_cen.y += Nd->cube_dim / (float)Neb2_slices; + for(idx1=0; idx1cube_dim / (float)Neb2_slices; + for(idx1=Neb2_slices-1; idx1>0; idx1--){ + neb2_copy(2, idx1-1, idx1); + } + neb2_gen_slice(2, 0, &cube_cen); + break; + // z + case 5 : + cube_cen.z += Nd->cube_dim / (float)Neb2_slices; + for(idx1=0; idx1= 360.0f){ + Neb2_cubes[idx1][idx2][idx3].rot = 0.0f; + } + + // optimization 1 - don't draw backfacing poly's + // useless + if(vm_vec_dot_to_point(&eye_orient.fvec, &eye_pos, &Neb2_cubes[idx1][idx2][idx3].pt) <= 0.0f){ + pneb_tossed_dot++; + continue; + } + + // rotate and project the vertex into viewspace + g3_rotate_vertex(&p, &Neb2_cubes[idx1][idx2][idx3].pt); + ptemp = p; + g3_project_vertex(&ptemp); + + // get the proper alpha value + alpha = neb2_get_alpha_2shell(Nd->cube_inner, Nd->cube_outer, Nd->prad/4.0f, &Neb2_cubes[idx1][idx2][idx3].pt); + + // optimization 2 - don't draw 0.0f or less poly's + // this amounts to big savings + if(alpha <= Nd->break_alpha){ + pneb_tossed_alpha++; + continue; + } + + // drop poly's which are offscreen at all + // if the poly's are offscreen + if((ptemp.sx < 0.0f) || (ptemp.sx > (float)gr_screen.max_w) || (ptemp.sy < 0.0f) || (ptemp.sy > (float)gr_screen.max_h) ){ + alpha = neb2_get_alpha_offscreen(ptemp.sx, ptemp.sy, alpha); + } + + // optimization 2 - don't draw 0.0f or less poly's + // this amounts to big savings + if(alpha <= Nd->break_alpha){ + pneb_tossed_alpha++; + continue; + } + + // set the bitmap and render + gr_set_bitmap(Neb2_cubes[idx1][idx2][idx3].bmap, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, alpha + Neb2_cubes[idx1][idx2][idx3].flash); + +#ifndef NDEBUG + this_area = g3_draw_rotated_bitmap_area(&p, fl_radian(Neb2_cubes[idx1][idx2][idx3].rot), Nd->prad, TMAP_FLAG_TEXTURED, max_area); + total_area += this_area; + frame_area -= this_area; + frame_rendered++; +#else + g3_draw_rotated_bitmap(&p, fl_radian(Neb2_cubes[idx1][idx2][idx3].rot), Nd->prad, TMAP_FLAG_TEXTURED); +#endif + } + } + } + + frames_total += frame_rendered; + frame_count++; + frame_avg = (float)frames_total / (float)frame_count; + + // gr_set_color_fast(&Color_bright_red); + // gr_printf(30, 100, "Area %.3f", total_area); +#ifdef NEB2_THUMBNAIL + extern int tbmap; + if(tbmap != -1){ + gr_set_bitmap(tbmap); + gr_bitmap(0, 0); + } +#endif +} + +// call this when the player's viewpoint has changed, this will cause the code to properly reset +// the eye's local poofs +void neb2_eye_changed() +{ + Neb2_regen = 1; +} + +// get near and far fog values based upon object type and rendering mode +void neb2_get_fog_values(float *fnear, float *ffar, object *objp) +{ + int fog_index; + + // default values in case something truly nasty happens + *fnear = 10.0f; + *ffar = 1000.0f; + + // determine what fog index to use + if(objp->type == OBJ_SHIP){ + Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS)); + if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){ + fog_index = SHIP_TYPE_FIGHTER_BOMBER; + } else { + fog_index = ship_query_general_type(objp->instance); + Assert(fog_index >= 0); + if(fog_index < 0){ + fog_index = SHIP_TYPE_FIGHTER_BOMBER; + } + } + } + // fog everything else like a fighter + else { + fog_index = SHIP_TYPE_FIGHTER_BOMBER; + } + + // get the values + switch(gr_screen.mode){ + case GR_GLIDE: + *fnear = Neb_ship_fog_vals_glide[fog_index][0]; + *ffar = Neb_ship_fog_vals_glide[fog_index][1]; + break; + + case GR_DIRECT3D: + *fnear = Neb_ship_fog_vals_d3d[fog_index][0]; + *ffar = Neb_ship_fog_vals_d3d[fog_index][1]; + break; + + default: + Int3(); + } +} + +// given a position in space, return a value from 0.0 to 1.0 representing the fog level +float neb2_get_fog_intensity(object *obj) +{ + float f_near, f_far, pct; + + // get near and far fog values based upon object type and rendering mode + neb2_get_fog_values(&f_near, &f_far, obj); + + // get the fog pct + pct = vm_vec_dist_quick(&Eye_position, &obj->pos) / (f_far - f_near); + if(pct < 0.0f){ + return 0.0f; + } else if(pct > 1.0f){ + return 1.0f; + } + + return pct; +} + +// fogging stuff -------------------------------------------------------------------- + +// do a pre-render of the background nebula +#define ESIZE 32 +ubyte tpixels[ESIZE * ESIZE * 4]; // for 32 bits +int last_esize = -1; +int this_esize = ESIZE; +extern float Viewer_zoom; +float ex_scale, ey_scale; +int tbmap = -1; +void neb2_pre_render(vector *eye_pos, matrix *eye_orient) +{ + // bail early in lame and poly modes + if(Neb2_render_mode != NEB2_RENDER_POF){ + return; + } + + // set the view clip + gr_screen.clip_width = this_esize; + gr_screen.clip_height = this_esize; + g3_start_frame(1); // Turn on zbuffering + g3_set_view_matrix(eye_pos, eye_orient, Viewer_zoom); + gr_set_clip(0, 0, this_esize, this_esize); + + // render the background properly + // hack - turn off nebula stuff + int neb_save = Neb2_render_mode; + Neb2_render_mode = NEB2_RENDER_NONE; + + // draw background stuff nebula + extern void stars_draw_background(); + stars_draw_background(); + + Neb2_render_mode = neb_save; + + // HACK - flush d3d here so everything is rendered + if(gr_screen.mode == GR_DIRECT3D){ + extern void d3d_flush(); + d3d_flush(); + } + + // grab the region + gr_get_region(0, this_esize, this_esize, (ubyte*)tpixels); + +#ifdef NEB2_THUMBNAIL + if(tbmap == -1){ + tbmap = bm_create(16, this_esize, this_esize, tpixels, 0); + bm_lock(tbmap, 16, 0); + bm_unlock(tbmap); + } +#endif + + // maybe do some swizzling + + // end the frame + g3_end_frame(); + + gr_clear(); + + // HACK - flush d3d here so everything is properly cleared + if(gr_screen.mode == GR_DIRECT3D){ + extern void d3d_flush(); + d3d_flush(); + } + + // if the size has changed between frames, make a new bitmap + if(this_esize != last_esize){ + last_esize = this_esize; + + // recalculate ex_scale and ey_scale values for looking up color values + ex_scale = (float)this_esize / (float)gr_screen.max_w; + ey_scale = (float)this_esize / (float)gr_screen.max_h; + } + + // restore the game clip stuff + extern void game_set_view_clip(); + game_set_view_clip(); +} + +// wacky scheme for smoothing colors +int wacky_scheme = 3; + +// get the color of the pixel in the small pre-rendered background nebula +#define PIXEL_INDEX_SMALL(xv, yv) ( (this_esize * (yv) * gr_screen.bytes_per_pixel) + ((xv) * gr_screen.bytes_per_pixel) ) +void neb2_get_pixel(int x, int y, int *r, int *g, int *b) +{ + int ra, ga, ba; + ubyte rv, gv, bv; + int avg_count; + int xs, ys; + + // if we're in lame rendering mode, return a constant value + if(Neb2_render_mode == NEB2_RENDER_LAME){ + *r = Neb2_background_color[0]; + *g = Neb2_background_color[1]; + *b = Neb2_background_color[2]; + + return; + } + + // get the proper pixel index to be looking up + rv = gv = bv = 0; + + // select screen format + BM_SELECT_SCREEN_FORMAT(); + + // pixel plus all immediate neighbors (on the small screen - should be more effective than 2 or 1) + xs = (int)(ex_scale * x); + ys = (int)(ey_scale * y); + + // sometimes goes over by 1 in direct3d + if(ys >= (this_esize - 1)){ + ys--; + } + + avg_count = 0; + bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs, ys)], &rv, &gv, &bv, NULL); + ra = rv; + ga = gv; + ba = bv; + avg_count++; + if(xs > 0){ + bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs - 1, ys)], &rv, &gv, &bv, NULL); // left + ra += rv; + ba += bv; + ga += gv; + avg_count++; + } + if(xs < this_esize - 1){ + bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs + 1, ys)], &rv, &gv, &bv, NULL); // right + ra += rv; + ba += bv; + ga += gv; + avg_count++; + } + if(ys > 0){ + bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs, ys - 1)], &rv, &gv, &bv, NULL); // top + ra += rv; + ba += bv; + ga += gv; + avg_count++; + } + if(ys < this_esize - 2){ + bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs, ys + 1)], &rv, &gv, &bv, NULL); // bottom + ra += rv; + ba += bv; + ga += gv; + avg_count++; + } + + if((xs > 0) && (ys > 0)){ + bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs - 1, ys - 1)], &rv, &gv, &bv, NULL); // upper left + ra += rv; + ba += bv; + ga += gv; + avg_count++; + } + if((xs < this_esize - 1) && (ys < this_esize - 1)){ + bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs + 1, ys + 1)], &rv, &gv, &bv, NULL); // lower right + ra += rv; + ba += bv; + ga += gv; + avg_count++; + } + if((ys > 0) && (xs < this_esize - 1)){ + bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs + 1, ys - 1)], &rv, &gv, &bv, NULL); // lower left + ra += rv; + ba += bv; + ga += gv; + avg_count++; + } + if((ys < this_esize - 1) && (xs > 0)){ + bm_get_components(&tpixels[PIXEL_INDEX_SMALL(xs - 1, ys + 1)], &rv, &gv, &bv, NULL); // upper right + ra += rv; + ba += bv; + ga += gv; + avg_count++; + } + + rv = (ubyte) (ra / avg_count); + gv = (ubyte) (ga / avg_count); + bv = (ubyte) (ba / avg_count); + + // return values + *r = (int)rv; + *g = (int)gv; + *b = (int)bv; +} + +// get the color to fog the background color to +void neb2_get_backg_color(int *r, int *g, int *b) +{ + *r = Neb2_background_color[0]; + *g = Neb2_background_color[1]; + *b = Neb2_background_color[2]; +} + +// set the background color +void neb2_set_backg_color(int r, int g, int b) +{ + Neb2_background_color[0] = r; + Neb2_background_color[1] = g; + Neb2_background_color[2] = b; +} + +// fill in the position of the eye for this frame +void neb2_get_eye_pos(vector *eye) +{ + *eye = Eye_position; +} + +// fill in the eye orient for this frame +void neb2_get_eye_orient(matrix *eye) +{ + *eye = Eye_matrix; +} + +// get a (semi) random bitmap to use for a poof +int neb2_get_bitmap() +{ + int count = 0; + int huh; + static int neb2_choose = 0; + + // get a random count + count = (int)frand_range(1.0f, 5.0f); + + // very ad-hoc + while(count > 0){ + // don't cycle too many times + huh = 0; + do { + if(neb2_choose == MAX_NEB2_POOFS - 1){ + neb2_choose = 0; + } else { + neb2_choose++; + } + + huh++; + } while(!(Neb2_poof_flags & (1< : set near and far fog planes for ship type X\n"); + dc_printf("where X is an integer from 1 - 11\n"); + dc_printf("1 = cargo containers, 2 = fighters/bombers, 3 = cruisers\n"); + dc_printf("4 = freighters, 5 = capital ships, 6 = transports, 7 = support ships\n"); + dc_printf("8 = navbuoys, 9 = sentryguns, 10 = escape pods, 11 = background nebula polygons\n\n"); + + dc_printf("neb2_max_alpha : max alpha value (0.0 to 1.0) for cloud poofs. 0.0 is completely transparent\n"); + dc_printf("neb2_break_alpha : alpha value (0.0 to 1.0) at which faded polygons are not drawn. higher values generally equals higher framerate, with more visual cloud popping\n"); + dc_printf("neb2_break_off : how many pixels offscreen (left, right, top, bottom) when a cloud poof becomes fully transparent. Lower values cause quicker fading\n"); + dc_printf("neb2_smooth : magic fog smoothing modes (0 - 3)\n"); + dc_printf("neb2_select : where the first # is the bitmap to be adjusting (0 through 5), and the second int is a 0 or 1, to turn off and on\n"); + dc_printf("neb2_rot : set max rotation speed for poofs\n"); + dc_printf("neb2_prad : set cloud poof radius\n"); + dc_printf("neb2_cdim : poof cube dimension\n"); + dc_printf("neb2_cinner : poof cube inner dimension\n"); + dc_printf("neb2_couter : poof cube outer dimension\n"); + dc_printf("neb2_jitter : poof jitter\n"); + dc_printf("neb2_mode : switch between no nebula, polygon background, amd pof background (0, 1 and 2 respectively)\n\n"); + dc_printf("neb2_ff : flash fade/sec\n"); + dc_printf("neb2_background : rgb background color\n"); + + dc_printf("neb2_fog_vals : display all the current settings for all above values\n"); +} + +DCF(neb2_prad, "") +{ + dc_get_arg(ARG_FLOAT); + Nd->prad = Dc_arg_float; +} +DCF(neb2_cdim, "") +{ + dc_get_arg(ARG_FLOAT); + Nd->cube_dim = Dc_arg_float; +} + +DCF(neb2_cinner, "") +{ + dc_get_arg(ARG_FLOAT); + Nd->cube_inner = Dc_arg_float; +} + +DCF(neb2_couter, "") +{ + dc_get_arg(ARG_FLOAT); + Nd->cube_outer = Dc_arg_float; +} + +DCF(neb2_jitter, "") +{ + dc_get_arg(ARG_FLOAT); + Nd->hj = Nd->dj = Nd->wj = Dc_arg_float; +} + +DCF(neb2_fog, "") +{ + int index; + float fnear, ffar; + dc_get_arg(ARG_INT); + index = Dc_arg_int; + dc_get_arg(ARG_FLOAT); + fnear = Dc_arg_float; + dc_get_arg(ARG_FLOAT); + ffar = Dc_arg_float; + + if((index >= 1) && (index <= 11) && (fnear >= 0.0f) && (ffar >= 0.0f) && (ffar > fnear)){ + if(index == 11){ + Neb_backg_fog_near = fnear; + Neb_backg_fog_far = ffar; + } else { + if(gr_screen.mode == GR_GLIDE){ + Neb_ship_fog_vals_glide[index][0] = fnear; + Neb_ship_fog_vals_glide[index][1] = ffar; + } else { + Assert(gr_screen.mode == GR_DIRECT3D); + Neb_ship_fog_vals_d3d[index][0] = fnear; + Neb_ship_fog_vals_d3d[index][1] = ffar; + } + } + } +} + +DCF(neb2_max_alpha, "") +{ + dc_get_arg(ARG_FLOAT); + Nd->max_alpha_glide = Dc_arg_float; +} + +DCF(neb2_break_alpha, "") +{ + dc_get_arg(ARG_FLOAT); + Nd->break_alpha = Dc_arg_float; +} + +DCF(neb2_break_off, "") +{ + dc_get_arg(ARG_INT); + Nd->break_y = (float)Dc_arg_int; + Nd->break_x = Nd->break_y * 1.3333f; +} + +DCF(neb2_smooth, "") +{ + int index; + dc_get_arg(ARG_INT); + index = Dc_arg_int; + if((index >= 0) && (index <= 3)){ + wacky_scheme = index; + } +} + +DCF(neb2_select, "") +{ + dc_get_arg(ARG_INT); + int bmap = Dc_arg_int; + if((bmap >= 0) && (bmap < Neb2_poof_count)){ + dc_get_arg(ARG_INT); + + if(Dc_arg_int){ + Neb2_poof_flags |= (1<max_alpha_glide); + dc_printf("neb2_break_alpha : %f\n", Nd->break_alpha); + dc_printf("neb2_break_off : %d\n", (int)Nd->break_y); + dc_printf("neb2_smooth : %d\n", wacky_scheme); + dc_printf("neb2_toggle : %s\n", Neb2_render_mode ? "on" : "off"); + dc_printf("neb2_rot : %f\n", max_rotation); + dc_printf("neb2_prad : %f\n", Nd->prad); + dc_printf("neb2_cdim : %f\n", Nd->cube_dim); + dc_printf("neb2_couter : %f\n", Nd->cube_outer); + dc_printf("neb2_cinner : %f\n", Nd->cube_inner); + dc_printf("neb2_jitter : %f\n", Nd->wj); + dc_printf("neb2_ff : %f\n", neb2_flash_fade); + dc_printf("neb2_background : %d %d %d\n", Neb2_background_color[0], Neb2_background_color[1], Neb2_background_color[2]); +} + +/* Obsolete !? +DCF(neb2_create, "create a basic nebula") +{ + int points = 0; + float rad1 = 0.0f; + float rad2 = 0.0f; + + dc_get_arg(ARG_INT); + if(Dc_arg_type & ARG_INT){ + points = Dc_arg_int; + } + dc_get_arg(ARG_FLOAT); + if(Dc_arg_type & ARG_FLOAT){ + rad1 = Dc_arg_float; + } + dc_get_arg(ARG_FLOAT); + if(Dc_arg_type & ARG_FLOAT){ + rad2 = Dc_arg_float; + } + neb2_create(&vmd_zero_vector, points, rad1, -1.0f, rad2); +} + +DCF(neb2_del, "delete existing nebulae") +{ + for(int idx=0; idxnum_poofs + num_poofs) > neb->max_poofs ? neb->max_poofs : (neb->num_poofs + num_poofs); + + // add the points a pick a random bitmap + for(idx=neb->num_poofs; idxfvec, frand_range(neb->magic_num, neb->inner_radius)); + + // rotate the point by -ang <-> ang around the up vector + vm_rot_point_around_line(&pt2, &pt, fl_radian(frand_range(-ang, ang)), &vmd_zero_vector, &orient->uvec); + + // rotate the point by -ang <-> ang around the right vector + vm_rot_point_around_line(&pt3, &pt2, fl_radian(frand_range(-ang, ang)), &vmd_zero_vector, &orient->rvec); + + // now add in the center of the nebula so its placed properly (ie, not around the origin) + vm_vec_add(&neb->pts[idx], &pt3, &Objects[neb->objnum].pos); + } else { + neb->pts[idx].x = frand_range(-1.0f * neb->inner_radius, neb->inner_radius) + Objects[neb->objnum].pos.x; + neb->pts[idx].y = frand_range(-1.0f * neb->inner_radius, neb->inner_radius) + Objects[neb->objnum].pos.y; + neb->pts[idx].z = frand_range(-1.0f * neb->inner_radius, neb->inner_radius) + Objects[neb->objnum].pos.z; + } + + neb->bmaps[idx] = (int)frand_range(0.0f, (float)2); + neb->num_poofs++; + } +} + +// add N poofs to the outer shell of the nebula +// if orient and ang are specified, generate the poofs so that they are "visible" around +// the orient fvec in a cone of ang degrees +void neb2_add_outer(neb2 *neb, int num_poofs, matrix *orient, float ang) +{ + int idx; + float phi, theta; + vector pt, pt2, pt3; + int final_index = (neb->num_poofs + num_poofs) > neb->max_poofs ? neb->max_poofs : (neb->num_poofs + num_poofs); + + // add the points a pick a random bitmap + for(idx=neb->num_poofs; idxfvec, neb->outer_radius); + + // rotate the point by -ang <-> ang around the up vector + vm_rot_point_around_line(&pt2, &pt, fl_radian(frand_range(-ang, ang)), &vmd_zero_vector, &orient->uvec); + + // rotate the point by -ang <-> ang around the right vector + vm_rot_point_around_line(&pt3, &pt2, fl_radian(frand_range(-ang, ang)), &vmd_zero_vector, &orient->rvec); + + // now add in the center of the nebula so its placed properly (ie, not around the origin) + vm_vec_add(&neb->pts[idx], &pt, &Objects[neb->objnum].pos); + } else { + // get a random point on the very outer radius, using spherical coords + phi = fl_radian(frand_range(0.0f, 360.0f)); + theta = fl_radian(frand_range(0.0f, 360.f)); + + neb->pts[idx].x = neb->outer_radius * (float)sin(phi) * (float)cos(theta); + neb->pts[idx].y = neb->outer_radius * (float)sin(phi) * (float)sin(theta); + neb->pts[idx].z = neb->outer_radius * (float)cos(phi); + } + + // pick a random bitmap and increment the # of poofs + neb->bmaps[idx] = (int)frand_range(0.0f, (float)2); + neb->num_poofs++; + } +} + +// return the alpha the passed poof should be rendered with, for a 1 shell nebula +float neb2_get_alpha_1shell(neb2 *neb, int poof_index) +{ + float dist; + float alpha; + vector eye_pos; + + // get the eye position + neb2_get_eye_pos(&eye_pos); + + // determine what alpha to draw this bitmap with + // higher alpha the closer the bitmap gets to the eye + dist = vm_vec_dist_quick(&eye_pos, &neb->pts[poof_index]); + + // at poof radius or greater, alpha should be 1.0 + // scale from 0.0 to 1.0 between radius and magic + if(dist >= neb->max_poof_radius){ + return max_alpha - 0.0001f; + } else if(dist > neb->magic_num){ + // alpha per meter between the magic # and the max radius + alpha = max_alpha / (neb->max_poof_radius - neb->magic_num); + + // above value times the # of meters away we are + return alpha * (dist - neb->magic_num); + } + + // otherwise transparent + return 0.0f; +} +*/ diff --git a/src/nebula/neblightning.cpp b/src/nebula/neblightning.cpp new file mode 100644 index 0000000..633ed0a --- /dev/null +++ b/src/nebula/neblightning.cpp @@ -0,0 +1,1322 @@ +/* + * $Logfile: /Freespace2/code/Nebula/NebLightning.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Nebula effect + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 10 8/15/99 3:50p Dave + * Don't process lightning at the very beginning of a mission. + * + * 9 8/12/99 10:38a Anoop + * Removed unnecessary Int3(). + * + * 8 8/05/99 2:06a Dave + * Whee. + * + * 7 7/27/99 9:51p Andsager + * make mprintf's into nprintf's + * + * 6 7/03/99 5:50p Dave + * Make rotated bitmaps draw properly in padlock views. + * + * 5 7/02/99 4:31p Dave + * Much more sophisticated lightning support. + * + * 4 6/09/99 10:32a Dave + * Made random lighting bolts behave more like the E3 demo. Generally more + * active. + * + * 3 5/26/99 11:46a Dave + * Added ship-blasting lighting and made the randomization of lighting + * much more customizable. + * + * 2 5/24/99 5:45p Dave + * Added detail levels to the nebula, with a decent speedup. Split nebula + * lightning into its own section. + * + * $NoKeywords: $ + */ + +#include "parselo.h" +#include "linklist.h" +#include "bmpman.h" +#include "timer.h" +#include "freespace.h" +#include "gamesnd.h" +#include "3d.h" +#include "missionparse.h" +#include "neb.h" +#include "neblightning.h" +#include "multi.h" +#include "emp.h" +#include "multimsgs.h" + +// ------------------------------------------------------------------------------------------------------ +// NEBULA LIGHTNING DEFINES/VARS +// + +// debug stuff +#define MAX_BOLT_TYPES_INTERNAL 11 + +// see lightning.tbl for explanations of these values +typedef struct bolt_type { + char name[NAME_LENGTH]; + + float b_scale; + float b_shrink; + float b_poly_pct; + float b_add; + float b_rand; + + float noise; + int lifetime; + int num_strikes; + + float emp_intensity; + float emp_time; + + int texture; + int glow; + + float b_bright; +} bolt_type; + +int Num_bolt_types = 0; +bolt_type Bolt_types[MAX_BOLT_TYPES_INTERNAL]; + +// storm types +int Num_storm_types = 0; +storm_type Storm_types[MAX_STORM_TYPES]; + + +// actual lightning bolt stuff ------- + +#define MAX_LIGHTNING_NODES 500 + +// nodes in a lightning bolt +#define LINK_LEFT 0 +#define LINK_RIGHT 1 +#define LINK_CHILD 2 +typedef struct l_node { + vector pos; // world position + l_node *links[3]; // 3 links for lightning children + + l_node *next, *prev; // for used and free-lists only +} l_node; + + +// nodes +l_node Nebl_nodes[MAX_LIGHTNING_NODES]; +int Num_lnodes = 0; + +// lightning node lists +l_node Nebl_free_list; +l_node Nebl_used_list; + +// actual lightning bolt themselves +typedef struct l_bolt { + l_node *head; // head of the lightning bolt + int bolt_life; // remaining life timestamp + ubyte used; // used or not + ubyte first_frame; // if he hasn't been rendered at least once + char type; + + // bolt info + vector start, strike, midpoint; + int delay; // delay stamp + int strikes_left; // #of strikes left + float width; +} l_bolt; + +#define MAX_LIGHTNING_BOLTS 10 + +// lightning bolts +l_bolt Nebl_bolts[MAX_LIGHTNING_BOLTS]; +int Nebl_bolt_count = 0; + +// one cross-section of a lightning bolt +typedef struct l_section { + vertex vex[3]; + vertex glow_vex[2]; +} l_section; + +// points on the basic cross section +vector Nebl_ring[3] = { + { -1.0f, 0.0f, 0.0f }, + { 1.0f, 0.70f, 0.0f }, + { 1.0f, -0.70f, 0.0f } +}; + +// pinched off cross-section +vector Nebl_ring_pinched[3] = { + { -0.05f, 0.0f, 0.0f }, + { 0.05f, 0.035f, 0.0f }, + { 0.05f, -0.035f, 0.0f } +}; + +// globals used for rendering and generating bolts +int Nebl_flash_count = 0; // # of points rendered onscreen for this bolt +float Nebl_flash_x = 0.0f; // avg x of the points rendered +float Nebl_flash_y = 0.0f; // avg y of the points rendered +float Nebl_bang = 0.0; // distance to the viewer object +float Nebl_alpha = 0.0f; // alpha to use when rendering the bolt itself +float Nebl_glow_alpha = 0.0f; // alpha to use when rendering the bolt glow +int Nebl_stamp = -1; // random timestamp for making bolts +float Nebl_bolt_len; // length of the current bolt being generated +bolt_type *Nebl_type; // bolt type +matrix Nebl_bolt_dir; // orientation matrix of the bolt being generated +vector Nebl_bolt_start; // start point of the bolt being generated +vector Nebl_bolt_strike; // strike point of the bolt being generated + +// the type of active storm +storm_type *Storm = NULL; + +// vars +DCF(b_scale, "") +{ + dc_get_arg(ARG_FLOAT); + Bolt_types[DEBUG_BOLT].b_scale = Dc_arg_float; +} +DCF(b_rand, "") +{ + dc_get_arg(ARG_FLOAT); + Bolt_types[DEBUG_BOLT].b_rand = Dc_arg_float; +} +DCF(b_shrink, "") +{ + dc_get_arg(ARG_FLOAT); + Bolt_types[DEBUG_BOLT].b_shrink = Dc_arg_float; +} +DCF(b_poly_pct, "") +{ + dc_get_arg(ARG_FLOAT); + Bolt_types[DEBUG_BOLT].b_poly_pct = Dc_arg_float; +} +DCF(b_add, "") +{ + dc_get_arg(ARG_FLOAT); + Bolt_types[DEBUG_BOLT].b_add = Dc_arg_float; +} +DCF(b_strikes, "") +{ + dc_get_arg(ARG_INT); + Bolt_types[DEBUG_BOLT].num_strikes = Dc_arg_int; +} +DCF(b_noise, "") +{ + dc_get_arg(ARG_FLOAT); + Bolt_types[DEBUG_BOLT].noise = Dc_arg_float; +} +DCF(b_bright, "") +{ + dc_get_arg(ARG_FLOAT); + Bolt_types[DEBUG_BOLT].b_bright = Dc_arg_float; +} +DCF(b_lifetime, "") +{ + dc_get_arg(ARG_INT); + Bolt_types[DEBUG_BOLT].lifetime = Dc_arg_int; +} +DCF(b_list, "") +{ + dc_printf("Debug lightning bolt settings :\n"); + + dc_printf("b_scale : %f\n", Bolt_types[DEBUG_BOLT].b_scale); + dc_printf("b_rand : %f\n", Bolt_types[DEBUG_BOLT].b_rand); + dc_printf("b_shrink : %f\n", Bolt_types[DEBUG_BOLT].b_shrink); + dc_printf("b_poly_pct : %f\n", Bolt_types[DEBUG_BOLT].b_poly_pct); + dc_printf("b_add : %f\n", Bolt_types[DEBUG_BOLT].b_add); + dc_printf("b_strikes : %d\n", Bolt_types[DEBUG_BOLT].num_strikes); + dc_printf("b_noise : %f\n", Bolt_types[DEBUG_BOLT].noise); + dc_printf("b_bright : %f\n", Bolt_types[DEBUG_BOLT].b_bright); + dc_printf("b_lifetime : %d\n", Bolt_types[DEBUG_BOLT].lifetime); +} + + +// nebula lightning intensity (0.0 to 1.0) +float Nebl_intensity = 0.6667f; + +// min and max times for random lightning +int Nebl_random_min = 750; // min random time +int Nebl_random_max = 10000; // max random time + +// min and max times for cruiser lightning +int Nebl_cruiser_min = 5000; // min cruiser time +int Nebl_cruiser_max = 25000; // max cruiser time + +// min and max times for cap ships +int Nebl_cap_min = 4000; // min cap time +int Nebl_cap_max = 18000; // max cap time + +// min and max time for super caps +int Nebl_supercap_min = 3000; // min supercap time +int Nebl_supercap_max = 12000; // max supercap time + +DCF(lightning_intensity, "") +{ + dc_get_arg(ARG_FLOAT); + float val = Dc_arg_float; + if(val < 0.0f){ + val = 0.0f; + } else if(val > 1.0f){ + val = 1.0f; + } + + Nebl_intensity = 1.0f - val; +} + +// ------------------------------------------------------------------------------------------------------ +// NEBULA LIGHTNING FORWARD DECLARATIONS +// + +// "new" a lightning node +l_node *nebl_new(); + +// "delete" a lightning node +void nebl_delete(l_node *lp); + +// free up a the nodes of the passed in bolt +void nebl_release(l_node *bolt_head); + +// generate a lightning bolt, returns l_left (the "head") and l_right (the "tail") +int nebl_gen(vector *left, vector *right, float depth, float max_depth, int child, l_node **l_left, l_node **l_right); + +// output top and bottom vectors +// fvec == forward vector (eye viewpoint basically. in world coords) +// pos == world coordinate of the point we're calculating "around" +// w == width of the diff between top and bottom around pos +void nebl_calc_facing_pts_smart( vector *top, vector *bot, vector *fvec, vector *pos, float w, float z_add ); + +// render a section of the bolt +void nebl_render_section(bolt_type *bi, l_section *a, l_section *b); + +// generate a section +void nebl_generate_section(bolt_type *bi, float width, l_node *a, l_node *b, l_section *c, l_section *cap, int pinch_a, int pinch_b); + +// render the bolt +void nebl_render(bolt_type *bi, l_node *whee, float width, l_section *prev = NULL); + +// given a valid, complete bolt, jitter him based upon his noise +void nebl_jitter(l_bolt *b); + +// return the index of a given bolt type by name +int nebl_get_bolt_index(char *name); + +// return the index of a given storm type by name +int nebl_get_storm_index(char *name); + + +// ------------------------------------------------------------------------------------------------------ +// NEBULA LIGHTNING FUNCTIONS +// + +// initialize nebula lightning at game startup +void nebl_init() +{ + char name[NAME_LENGTH+10] = ""; + bolt_type bogus_lightning, *l; + storm_type bogus_storm, *s; + int temp; + + // parse the lightning table + read_file_text("lightning.tbl"); + reset_parse(); + + Num_bolt_types = 0; + Num_storm_types = 0; + + memset(Bolt_types, 0, sizeof(bolt_type) * MAX_BOLT_TYPES_INTERNAL); + + // parse the individual lightning bolt types + required_string("#Bolts begin"); + while(!optional_string("#Bolts end")){ + // get a pointer + if(Num_bolt_types >= MAX_BOLT_TYPES){ + l = &bogus_lightning; + } else { + l = &Bolt_types[Num_bolt_types]; + } + + // bolt title + required_string("$Bolt:"); + stuff_string(l->name, F_NAME, NULL); + + // b_scale + required_string("+b_scale:"); + stuff_float(&l->b_scale); + + // b_shrink + required_string("+b_shrink:"); + stuff_float(&l->b_shrink); + + // b_poly_pct + required_string("+b_poly_pct:"); + stuff_float(&l->b_poly_pct); + + // child rand + required_string("+b_rand:"); + stuff_float(&l->b_rand); + + // z add + required_string("+b_add:"); + stuff_float(&l->b_add); + + // # strikes + required_string("+b_strikes:"); + stuff_int(&l->num_strikes); + + // lifetime + required_string("+b_lifetime:"); + stuff_int(&l->lifetime); + + // noise + required_string("+b_noise:"); + stuff_float(&l->noise); + + // emp effect + required_string("+b_emp:"); + stuff_float(&l->emp_intensity); + stuff_float(&l->emp_time); + + // texture + required_string("+b_texture:"); + stuff_string(name, F_NAME, NULL); + if((l != &bogus_lightning) && !Fred_running){ + l->texture = bm_load(name); + } + + // glow + required_string("+b_glow:"); + stuff_string(name, F_NAME, NULL); + if((l != &bogus_lightning) && !Fred_running){ + l->glow = bm_load(name); + } + + // brightness + required_string("+b_bright:"); + stuff_float(&l->b_bright); + + // increment the # of bolt types + if(l != &bogus_lightning){ + Num_bolt_types++; + } + } + + // copy the first bolt to the debug bolt + memcpy(&Bolt_types[DEBUG_BOLT], &Bolt_types[0], sizeof(bolt_type)); + + // parse storm types + required_string("#Storms begin"); + while(!optional_string("#Storms end")){ + // get a pointer + if(Num_storm_types >= MAX_STORM_TYPES){ + s = &bogus_storm; + } else { + s = &Storm_types[Num_storm_types]; + } + + // bolt title + required_string("$Storm:"); + stuff_string(s->name, F_NAME, NULL); + + // bolt types + s->num_bolt_types = 0; + while(optional_string("+bolt:")){ + stuff_string(name, F_NAME, NULL); + + // fill this guy in + if(s->num_bolt_types < MAX_BOLT_TYPES){ + s->bolt_types[s->num_bolt_types] = (char)nebl_get_bolt_index(name); + Assert(s->bolt_types[s->num_bolt_types] != -1); + + s->num_bolt_types++; + } + // bogus + else { + required_string("+bolt_prec:"); + stuff_int(&temp); + } + } + + // flavor + required_string("+flavor:"); + stuff_float(&s->flavor.x); + stuff_float(&s->flavor.y); + stuff_float(&s->flavor.z); + + // frequencies + required_string("+random_freq:"); + stuff_int(&s->min); + stuff_int(&s->max); + + // counts + required_string("+random_count:"); + stuff_int(&s->min_count); + stuff_int(&s->max_count); + + // increment the # of bolt types + if(s != &bogus_storm){ + Num_storm_types++; + } + } +} + +// initialize lightning before entering a level +void nebl_level_init() +{ + int idx; + + // zero all lightning bolts + for(idx=0; idxused){ + Assert(b->head != NULL); + + // bogus bolt + if(b->head == NULL){ + b->used = 0; + continue; + } + if((b->type < 0) || ((b->type >= Num_bolt_types) && (b->type != DEBUG_BOLT)) ){ + Int3(); + b->used = 0; + continue; + } + bi = &Bolt_types[b->type]; + + // if this guy is still on a delay + if(b->delay != -1){ + if(timestamp_elapsed(b->delay)){ + b->delay = -1; + } else { + continue; + } + } + + // if the timestamp on this guy has expired + if((b->bolt_life < 0) || timestamp_elapsed(b->bolt_life)){ + // if this is a multiple strike bolt, jitter it and reset + if(b->strikes_left-1 > 0){ + b->bolt_life = timestamp(bi->lifetime / bi->num_strikes); + b->first_frame = 1; + b->strikes_left--; + nebl_jitter(b); + + // by continuing here we skip rendering for one frame, which makes it look more like real lightning + continue; + } + // otherwise he's completely done, so release him + else { + // maybe free up node data + if(b->head != NULL){ + nebl_release(b->head); + b->head = NULL; + + Nebl_bolt_count--; + + nprintf(("lightning", "Released bolt. %d used nodes!\n", Num_lnodes)); + } + + b->used = 0; + } + } + + // pick some cool alpha values + Nebl_alpha = frand(); + Nebl_glow_alpha = frand(); + + // otherwise render him + Nebl_flash_count = 0; + Nebl_flash_x = 0.0f; + Nebl_flash_y = 0.0f; + Nebl_bang = 10000000.0f; + nebl_render(bi, b->head, b->width); + + // if this is the first frame he has been rendered, determine if we need to make a flash and sound effect + if(b->first_frame){ + float flash = 0.0f; + + b->first_frame = 0; + + // if we rendered any points + if(Nebl_flash_count){ + Nebl_flash_x /= (float)Nebl_flash_count; + Nebl_flash_y /= (float)Nebl_flash_count; + + // quick distance from the center of the screen + float x = Nebl_flash_x - (gr_screen.max_w / 2.0f); + float y = Nebl_flash_y - (gr_screen.max_h / 2.0f); + float dist = fl_sqrt((x * x) + (y * y)); + if(dist / (gr_screen.max_w / 2.0f) < 1.0f){ + flash = 1.0f - (dist / (gr_screen.max_w / 2.0f)); + + // scale the flash by bolt type + flash *= bi->b_bright; + + game_flash(flash, flash, flash); + } + + // do some special stuff on the very first strike of the bolt + if(b->strikes_left == bi->num_strikes){ + // play a sound + float bang; + if(Nebl_bang < 40.0f){ + bang = 1.0f; + } else if(Nebl_bang > 400.0f){ + bang = 0.0f; + } else { + bang = 1.0f - (Nebl_bang / 400.0f); + } + if(frand_range(0.0f, 1.0f) < 0.5f){ + snd_play(&Snds[SND_LIGHTNING_2], 0.0f, bang, SND_PRIORITY_DOUBLE_INSTANCE); + } else { + snd_play(&Snds[SND_LIGHTNING_1], 0.0f, bang, SND_PRIORITY_DOUBLE_INSTANCE); + } + + // apply em pulse + if(bi->emp_intensity > 0.0f){ + emp_apply(&b->midpoint, 0.0f, vm_vec_dist(&b->start, &b->strike), bi->emp_intensity, bi->emp_time); + } + } + } + } + } + } +} + +// process lightning (randomly generate bolts, etc, etc); +void nebl_process() +{ + int num_bolts, idx; + + // non-nebula mission + if(!(The_mission.flags & MISSION_FLAG_FULLNEB)){ + return; + } + + // non servers in multiplayer don't do this + if((Game_mode & GM_MULTIPLAYER) && !MULTIPLAYER_MASTER){ + return; + } + + // if there's no chosen storm + if(Storm == NULL){ + return; + } + + // don't process lightning bolts unless we're a few seconds in + if(f2fl(Missiontime) < 3.0f){ + return; + } + + // random stamp + if(Nebl_stamp == -1){ + Nebl_stamp = timestamp((int)frand_range((float)Storm->min, (float)Storm->max)); + return; + } + + // maybe make a bolt + if(timestamp_elapsed(Nebl_stamp)){ + // determine how many bolts to spew + num_bolts = (int)frand_range((float)Storm->min_count, (float)Storm->max_count); + for(idx=0; idxflavor)){ + // start with your basic hot sauce. measure how much you have + vector your_basic_hot_sauce; + vm_vec_sub(&your_basic_hot_sauce, &strike, &start); + float how_much_hot_sauce = vm_vec_normalize(&your_basic_hot_sauce); + + // now figure out how much of that good wing sauce to add + vector wing_sauce = Storm->flavor; + if(frand_range(0.0, 1.0f) < 0.5f){ + vm_vec_scale(&wing_sauce, -1.0f); + } + float how_much_of_that_good_wing_sauce_to_add = vm_vec_normalize(&wing_sauce); + + // mix the two together, taking care not to add too much + vector the_mixture; + if(how_much_of_that_good_wing_sauce_to_add > 1000.0f){ + how_much_of_that_good_wing_sauce_to_add = 1000.0f; + } + vm_vec_interp_constant(&the_mixture, &your_basic_hot_sauce, &wing_sauce, how_much_of_that_good_wing_sauce_to_add / 1000.0f); + + // take the final sauce and store it in the proper container + vm_vec_scale(&the_mixture, how_much_hot_sauce); + + // make sure to put it on everything! whee! + vm_vec_add(&strike, &start, &the_mixture); + } + + int type = (int)frand_range(0.0f, (float)(Storm->num_bolt_types-1)); + nebl_bolt(Storm->bolt_types[type], &start, &strike); + } + + // reset the timestamp + Nebl_stamp = timestamp((int)frand_range((float)Storm->min, (float)Storm->max)); + } +} + +// create a lightning bolt +void nebl_bolt(int type, vector *start, vector *strike) +{ + vector dir; + l_bolt *bolt; + l_node *tail; + int idx; + int found; + bolt_type *bi; + float bolt_len; + + if(!(The_mission.flags & MISSION_FLAG_FULLNEB)){ + return; + } + + // find a free bolt + found = 0; + for(idx=0; idx= Num_bolt_types) && (type != DEBUG_BOLT)) ){ + return; + } + bi = &Bolt_types[type]; + + // get a pointer to the bolt + bolt = &Nebl_bolts[idx]; + + // setup bolt into + bolt->start = *start; + bolt->strike = *strike; + bolt->strikes_left = bi->num_strikes; + bolt->delay = -1; + bolt->type = (char)type; + bolt->first_frame = 1; + bolt->bolt_life = timestamp(bi->lifetime / bi->num_strikes); + + Nebl_bolt_start = *start; + Nebl_bolt_strike = *strike; + + // setup fire delay + if(bolt->delay != -1){ + bolt->delay = timestamp(bolt->delay); + } + + // setup the rest of the important bolt data + if(vm_vec_same(&Nebl_bolt_start, &Nebl_bolt_strike)){ + Nebl_bolt_strike.z += 150.0f; + } + Nebl_bolt_len = vm_vec_dist(&Nebl_bolt_start, &Nebl_bolt_strike); + vm_vec_sub(&dir, &Nebl_bolt_strike, &Nebl_bolt_start); + + // setup midpoint + vm_vec_scale_add(&bolt->midpoint, &Nebl_bolt_start, &dir, 0.5f); + + bolt_len = vm_vec_normalize(&dir); + vm_vector_2_matrix(&Nebl_bolt_dir, &dir, NULL, NULL); + + // global type for generating the bolt + Nebl_type = bi; + + // try and make the bolt + if(!nebl_gen(&Nebl_bolt_start, &Nebl_bolt_strike, 0, 4, 0, &bolt->head, &tail)){ + if(bolt->head != NULL){ + nebl_release(bolt->head); + } + + return; + } + + Nebl_bolt_count++; + + // setup the rest of the data + bolt->used = 1; + bolt->width = bi->b_poly_pct * bolt_len; + + // if i'm a multiplayer master, send a bolt packet + if(MULTIPLAYER_MASTER){ + send_lightning_packet(type, start, strike); + } +} + +// get the current # of active lightning bolts +int nebl_get_active_bolts() +{ + return Nebl_bolt_count; +} + +// get the current # of active nodes +int nebl_get_active_nodes() +{ + return Num_lnodes; +} + +// set the storm (call from mission parse) +void nebl_set_storm(char *name) +{ + int index = nebl_get_storm_index(name); + + // sanity + Storm = NULL; + if((index >= 0) && (index < Num_storm_types)){ + Storm = &Storm_types[index]; + } +} + +// ------------------------------------------------------------------------------------------------------ +// NEBULA LIGHTNING FORWARD DEFINITIONS +// + +// "new" a lightning node +l_node *nebl_new() +{ + l_node *lp; + + // if we're out of nodes + if(Num_lnodes >= MAX_LIGHTNING_NODES){ + // Int3(); + nprintf(("lightning", "Out of lightning nodes!\n")); + return NULL; + } + + // get a new node off the freelist + lp = GET_FIRST(&Nebl_free_list); + Assert( lp != &Nebl_free_list ); // shouldn't have the dummy element + + // remove trailp from the free list + list_remove( &Nebl_free_list, lp ); + + // insert trailp onto the end of used list + list_append( &Nebl_used_list, lp ); + + // increment counter + Num_lnodes++; + + lp->links[0] = NULL; + lp->links[1] = NULL; + lp->links[2] = NULL; + + // return the pointer + return lp; +} + +// "delete" a lightning node +void nebl_delete(l_node *lp) +{ + // remove objp from the used list + list_remove( &Nebl_used_list, lp ); + + // add objp to the end of the free + list_append( &Nebl_free_list, lp ); + + // decrement counter + Num_lnodes--; +} + +// free a lightning bolt +void nebl_release(l_node *whee) +{ + // if we're invalid + if(whee == NULL){ + return; + } + + // release all of our children + if(whee->links[LINK_RIGHT] != NULL){ + nebl_release(whee->links[LINK_RIGHT]); + } + if(whee->links[LINK_CHILD] != NULL){ + nebl_release(whee->links[LINK_CHILD]); + } + + // delete this node + nebl_delete(whee); +} + +int nebl_gen(vector *left, vector *right, float depth, float max_depth, int child, l_node **l_left, l_node **l_right) +{ + l_node *child_node = NULL; + float d = vm_vec_dist_quick( left, right ); + + // if we've reached the critical point + if ( d < 0.30f || (depth > max_depth) ){ + // generate ne items + l_node *new_left = nebl_new(); + if(new_left == NULL){ + return 0; + } + new_left->links[0] = NULL; new_left->links[1] = NULL; new_left->links[2] = NULL; + new_left->pos = vmd_zero_vector; + l_node *new_right = nebl_new(); + if(new_right == NULL){ + nebl_delete(new_left); + return 0; + } + new_right->links[0] = NULL; new_right->links[1] = NULL; new_right->links[2] = NULL; + new_right->pos = vmd_zero_vector; + + // left side + new_left->pos = *left; + new_left->links[LINK_RIGHT] = new_right; + *l_left = new_left; + + // right side + new_right->pos = *right; + new_right->links[LINK_LEFT] = new_left; + *l_right = new_right; + + // done + return 1; + } + + // divide in half + vector tmp; + vm_vec_avg( &tmp, left, right ); + + // sometimes generate children + if(!child && (frand() <= Nebl_type->b_rand)){ + // get a point on the plane of the strike + vector tmp2; + vm_vec_random_in_circle(&tmp2, &Nebl_bolt_strike, &Nebl_bolt_dir, Nebl_bolt_len * Nebl_type->b_scale, 0); + + // maybe move away from the plane + vector dir; + vm_vec_sub(&dir, &tmp2, &tmp); + vm_vec_scale_add(&tmp2, &tmp, &dir, Nebl_type->b_shrink); + + // child + l_node *argh; + if(!nebl_gen(&tmp, &tmp2, 0, 2, 1, &child_node, &argh)){ + if(child_node != NULL){ + nebl_release(child_node); + } + return 0; + } + } + + float scaler = 0.30f; + tmp.x += (frand()-0.5f)*d*scaler; + tmp.y += (frand()-0.5f)*d*scaler; + tmp.z += (frand()-0.5f)*d*scaler; + + // generate left half + l_node *ll = NULL; + l_node *lr = NULL; + if(!nebl_gen( left, &tmp, depth+1, max_depth, child, &ll, &lr )){ + if(child_node != NULL){ + nebl_release(child_node); + } + if(ll != NULL){ + nebl_release(ll); + } + return 0; + } + + // generate right half + l_node *rl = NULL; + l_node *rr = NULL; + if(!nebl_gen( &tmp, right, depth+1, max_depth, child, &rl, &rr )){ + if(child_node != NULL){ + nebl_release(child_node); + } + if(ll != NULL){ + nebl_release(ll); + } + if(rl != NULL){ + nebl_release(rl); + } + return 0; + } + + // splice the two together + lr->links[LINK_RIGHT] = rl->links[LINK_RIGHT]; + lr->links[LINK_RIGHT]->links[LINK_LEFT] = lr; + nebl_delete(rl); + + // if we generated a child, stick him on + if(child_node != NULL){ + lr->links[LINK_CHILD] = child_node; + } + + // return these + *l_left = ll; + *l_right = rr; + + return 1; +} + + +// output top and bottom vectors +// fvec == forward vector (eye viewpoint basically. in world coords) +// pos == world coordinate of the point we're calculating "around" +// w == width of the diff between top and bottom around pos +void nebl_calc_facing_pts_smart( vector *top, vector *bot, vector *fvec, vector *pos, float w, float z_add ) +{ + vector uvec, rvec; + vector temp; + + temp = *pos; + + vm_vec_sub( &rvec, &Eye_position, &temp ); + vm_vec_normalize( &rvec ); + + vm_vec_crossprod(&uvec,fvec,&rvec); + vm_vec_normalize(&uvec); + + vm_vec_scale_add( top, &temp, &uvec, w/2.0f ); + vm_vec_scale_add( bot, &temp, &uvec, -w/2.0f ); + + vm_vec_scale_add2( top, &rvec, z_add ); + vm_vec_scale_add2( bot, &rvec, z_add ); +} + +// render a section of the bolt +void nebl_render_section(bolt_type *bi, l_section *a, l_section *b) +{ + vertex v[4]; + vertex *verts[4] = {&v[0], &v[1], &v[2], &v[3]}; + int idx; + + // Sets mode. Returns previous mode. + gr_zbuffer_set(GR_ZBUFF_FULL); + + // draw some stuff + for(idx=0; idx<2; idx++){ + v[0] = a->vex[idx]; + v[0].u = 0.0f; v[0].v = 0.0f; + + v[1] = a->vex[idx+1]; + v[1].u = 1.0f; v[1].v = 0.0f; + + v[2] = b->vex[idx+1]; + v[2].u = 1.0f; v[2].v = 1.0f; + + v[3] = b->vex[idx]; + v[3].u = 0.0f; v[3].v = 1.0f; + + // draw + gr_set_bitmap(bi->texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Nebl_alpha); + g3_draw_poly(4, verts, TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT); + } + + // draw + v[0] = a->vex[2]; + v[0].u = 0.0f; v[0].v = 0.0f; + + v[1] = a->vex[0]; + v[1].u = 1.0f; v[1].v = 0.0f; + + v[2] = b->vex[0]; + v[2].u = 1.0f; v[2].v = 1.0f; + + v[3] = b->vex[2]; + v[3].u = 0.0f; v[3].v = 1.0f; + + gr_set_bitmap(bi->texture, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Nebl_alpha); + g3_draw_poly(4, verts, TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT); + + // draw the glow beam + verts[0] = &a->glow_vex[0]; + verts[0]->v = 0.0f; verts[0]->u = 0.0f; + + verts[1] = &a->glow_vex[1]; + verts[1]->v = 1.0f; verts[1]->u = 0.0f; + + verts[2] = &b->glow_vex[1]; + verts[2]->v = 1.0f; verts[2]->u = 1.0f; + + verts[3] = &b->glow_vex[0]; + verts[3]->v = 0.0f; verts[3]->u = 1.0f; + + gr_set_bitmap(bi->glow, GR_ALPHABLEND_FILTER, GR_BITBLT_MODE_NORMAL, Nebl_glow_alpha); + g3_draw_poly(4, verts, TMAP_FLAG_TEXTURED | TMAP_FLAG_CORRECT); +} + +// generate a section +void nebl_generate_section(bolt_type *bi, float width, l_node *a, l_node *b, l_section *c, l_section *cap, int pinch_a, int pinch_b) +{ + vector dir; + vector dir_normal; + matrix m; + int idx; + vector temp, pt; + vector glow_a, glow_b; + + // direction matrix + vm_vec_sub(&dir, &a->pos, &b->pos); + vm_vec_copy_normalize(&dir_normal, &dir); + vm_vector_2_matrix(&m, &dir_normal, NULL, NULL); + + // distance to player + float bang_dist = vm_vec_dist_quick(&Eye_position, &a->pos); + if(bang_dist < Nebl_bang){ + Nebl_bang = bang_dist; + } + + // rotate the basic section into world + for(idx=0; idx<3; idx++){ + // rotate to world + if(pinch_a){ + vm_vec_rotate(&pt, &Nebl_ring_pinched[idx], &m); + } else { + vm_vec_copy_scale(&temp, &Nebl_ring[idx], width); + vm_vec_rotate(&pt, &temp, &m); + } + vm_vec_add2(&pt, &a->pos); + + // transform + g3_rotate_vertex(&c->vex[idx], &pt); + g3_project_vertex(&c->vex[idx]); + + // if first frame, keep track of the average screen pos + if((c->vex[idx].sx >= 0) && (c->vex[idx].sx < gr_screen.max_w) && (c->vex[idx].sy >= 0) && (c->vex[idx].sy < gr_screen.max_h)){ + Nebl_flash_x += c->vex[idx].sx; + Nebl_flash_y += c->vex[idx].sy; + Nebl_flash_count++; + } + } + // calculate the glow points + nebl_calc_facing_pts_smart(&glow_a, &glow_b, &dir_normal, &a->pos, pinch_a ? 0.5f : width * 6.0f, Nebl_type->b_add); + g3_rotate_vertex(&c->glow_vex[0], &glow_a); + g3_project_vertex(&c->glow_vex[0]); + g3_rotate_vertex(&c->glow_vex[1], &glow_b); + g3_project_vertex(&c->glow_vex[1]); + + // maybe do a cap + if(cap != NULL){ + // rotate the basic section into world + for(idx=0; idx<3; idx++){ + // rotate to world + if(pinch_b){ + vm_vec_rotate(&pt, &Nebl_ring_pinched[idx], &m); + } else { + vm_vec_copy_scale(&temp, &Nebl_ring[idx], width); + vm_vec_rotate(&pt, &temp, &m); + } + vm_vec_add2(&pt, &b->pos); + + // transform + g3_rotate_vertex(&cap->vex[idx], &pt); + g3_project_vertex(&cap->vex[idx]); + + // if first frame, keep track of the average screen pos + if( (c->vex[idx].sx >= 0) && (c->vex[idx].sx < gr_screen.max_w) && (c->vex[idx].sy >= 0) && (c->vex[idx].sy < gr_screen.max_h)){ + Nebl_flash_x += c->vex[idx].sx; + Nebl_flash_y += c->vex[idx].sy; + Nebl_flash_count++; + } + } + + // calculate the glow points + nebl_calc_facing_pts_smart(&glow_a, &glow_b, &dir_normal, &b->pos, pinch_b ? 0.5f : width * 6.0f, bi->b_add); + g3_rotate_vertex(&cap->glow_vex[0], &glow_a); + g3_project_vertex(&cap->glow_vex[0]); + g3_rotate_vertex(&cap->glow_vex[1], &glow_b); + g3_project_vertex(&cap->glow_vex[1]); + } +} + +// render the bolt +void nebl_render(bolt_type *bi, l_node *whee, float width, l_section *prev) +{ + l_section start; + l_section end; + l_section child_start; + + // bad + if(whee == NULL){ + return; + } + + // if prev is NULL, we're just starting so we need our start point + if(prev == NULL){ + Assert(whee->links[LINK_RIGHT] != NULL); + nebl_generate_section(bi, width, whee, whee->links[LINK_RIGHT], &start, NULL, 1, 0); + } else { + start = *prev; + } + + // if we have a child section + if(whee->links[LINK_CHILD]){ + // generate section + nebl_generate_section(bi, width * 0.5f, whee, whee->links[LINK_CHILD], &child_start, &end, 0, whee->links[LINK_CHILD]->links[LINK_RIGHT] == NULL ? 1 : 0); + + // render + nebl_render_section(bi, &child_start, &end); + + // maybe continue + if(whee->links[LINK_CHILD]->links[LINK_RIGHT] != NULL){ + nebl_render(bi, whee->links[LINK_CHILD], width * 0.5f, &end); + } + } + + // if the next section is an end section + if(whee->links[LINK_RIGHT]->links[LINK_RIGHT] == NULL){ + l_section temp; + + // generate section + nebl_generate_section(bi, width, whee, whee->links[LINK_RIGHT], &temp, &end, 0, 1); + + // render the section + nebl_render_section(bi, &start, &end); + } + // a middle section + else if(whee->links[LINK_RIGHT]->links[LINK_RIGHT] != NULL){ + // generate section + nebl_generate_section(bi, width, whee->links[LINK_RIGHT], whee->links[LINK_RIGHT]->links[LINK_RIGHT], &end, NULL, 0, 0); + + // render the section + nebl_render_section(bi, &start, &end); + + // recurse through him + nebl_render(bi, whee->links[LINK_RIGHT], width, &end); + } +} + +// given a valid, complete bolt, jitter him based upon his noise +void nebl_jitter(l_bolt *b) +{ + matrix m; + vector temp; + float length; + l_node *moveup; + bolt_type *bi = NULL; + + // sanity + if(b == NULL){ + return; + } + if((b->type < 0) || ((b->type >= Num_bolt_types) && (b->type != DEBUG_BOLT)) ){ + return; + } + bi = &Bolt_types[b->type]; + + // get the bolt direction + vm_vec_sub(&temp, &b->strike, &b->start); + length = vm_vec_normalize_quick(&temp); + vm_vector_2_matrix(&m, &temp, NULL, NULL); + + // jitter all nodes on the main trunk + moveup = b->head; + while(moveup != NULL){ + temp = moveup->pos; + vm_vec_random_in_circle(&moveup->pos, &temp, &m, frand_range(0.0f, length * bi->noise), 0); + + // just on the main trunk + moveup = moveup->links[LINK_RIGHT]; + } +} + +// return the index of a given bolt type by name +int nebl_get_bolt_index(char *name) +{ + int idx; + + for(idx=0; idx + +#include "pstypes.h" +#include "multi.h" +#include "multiutil.h" +#include "multimsgs.h" +#include "psnet.h" +#include "linklist.h" +#include "object.h" +#include "ship.h" +#include "hud.h" +#include "timer.h" +#include "player.h" +#include "missionload.h" +#include "missionparse.h" +#include "missionshipchoice.h" +#include "gamesequence.h" +#include "freespace.h" +#include "osapi.h" +#include "math.h" +#include "mouse.h" +#include "stats.h" +#include "stand_gui.h" +#include "multi_xfer.h" +#include "multiui.h" +#include "key.h" +#include "multilag.h" +#include "multiutil.h" +#include "multi_ingame.h" +#include "bmpman.h" +#include "popup.h" +#include "cmdline.h" +#include "chatbox.h" +#include "multiteamselect.h" +#include "multi_data.h" +#include "multi_kick.h" +#include "multi_campaign.h" +#include "multi_voice.h" +#include "multi_team.h" +#include "multi_respawn.h" +#include "multi_pmsg.h" +#include "multi_endgame.h" +#include "missiondebrief.h" +#include "multi_pause.h" +#include "multi_obj.h" +#include "missiongoals.h" +#include "multi_log.h" +#include "multi_rate.h" +#include "hudescort.h" +#include "alphacolors.h" + +// ---------------------------------------------------------------------------------------- +// Basic module scope defines +// +// + +// timestamp defines +// #define NETGAME_SEND_TIME 1000 // time between sending netgame update packets +// #define STATE_SEND_TIME 1000 // time between sending netplayer state packets +// #define GAMEINFO_SEND_TIME 3000 // time between sending game information packets +// #define PING_SEND_TIME 2000 // time between player pings +// #define INGAME_UPDATE_TIME 1000 // time limit between ingame join operations +#define NETGAME_SEND_TIME 2 // time between sending netgame update packets +#define STATE_SEND_TIME 2 // time between sending netplayer state packets +#define GAMEINFO_SEND_TIME 3 // time between sending game information packets +#define PING_SEND_TIME 2 // time between player pings +#define BYTES_SENT_TIME 5 // every five seconds + +// netplayer stuff +#define CULL_ZOMBIE_TIME 1000 // zombies are checked for at this interval (in milliseconds) + +// object update stuff +#define SEND_POS_INTERVAL 30 // min time in milliseconds between sending position updates + +// local network buffer stuff +#define MAX_NET_BUFFER (1024 * 16) // define and variable declaration for our local tcp buffer +#define NUM_REENTRANT_LEVELS 3 + +// time (in fixed seconds) to put up dialog about no contect from server +#define MULTI_SERVER_MAX_TIMEOUT (F1_0 * 4) // after this number of milliseoncds, stop client simulation +#define MULTI_SERVER_MAX_TIMEOUT_LARGE (F1_0 * 40) // done anytime not in mission +#define MULTI_SERVER_WAIT_TIME (F1_0 * 60) // wait 60 seconds to reconnect with the server +#define MULTI_SERVER_GONE 1 +#define MULTI_SERVER_ALIVE 2 + +#define DISCONNECT_TIMEOUT (F1_0 * 10) // 10 seconds to timeout someone who cannot connnect + +// define for when to show "slow network" icon +#define MULTI_SERVER_SLOW_PING_TIME 700 // when average ping time to server reaches this -- display hud icon + +// update times for clients ships based on object update level +#define MULTI_CLIENT_UPDATE_TIME 333 + +int Multi_display_netinfo = 1; + +// ---------------------------------------------------------------------------------------- +// Multiplayer vars +// +// + +// net player vars +net_player Net_players[MAX_PLAYERS]; // array of all netplayers in the game +net_player *Net_player; // pointer to console's net_player entry + +// network object management +ushort Next_ship_signature; // next permanent network signature to assign to an object +ushort Next_asteroid_signature; // next signature for an asteroid +ushort Next_non_perm_signature; // next non-permanent network signature to assign to an object +ushort Next_debris_signature; // next debris signature + +// netgame vars +netgame_info Netgame; // netgame information +int Multi_mission_loaded = 0; // flag, so that we dont' load the mission more than once client side +int Ingame_join_net_signature = -1; // signature for the player obj for use when joining ingame +int Multi_button_info_ok = 0; // flag saying it is ok to apply critical button info on a client machine +int Multi_button_info_id = 0; // identifier of the stored button info to be applying + +// low level networking vars +int ADDRESS_LENGTH; // will be 6 for IPX, 4 for IP +int PORT_LENGTH; // will be 2 for IPX, 2 for IP +int HEADER_LENGTH; // 1 byte (packet type) + +// misc data +active_game* Active_game_head; // linked list of active games displayed on Join screen +int Active_game_count; // for interface screens as well +CFILE* Multi_chat_stream; // for streaming multiplayer chat strings to a file +int Multi_has_cd = 0; // if this machine has a cd or not (call multi_common_verify_cd() to set this) +int Multi_connection_speed; // connection speed of this machine. +int Multi_num_players_at_start = 0; // the # of players present (kept track of only on the server) at the very start of the mission +short Multi_id_num = 0; // for assigning player id #'s + +// permanent server list +server_item* Game_server_head; // list of permanent game servers to be querying + +// timestamp data +int Netgame_send_time = -1; // timestamp used to send netgame info to players before misison starts +int State_send_time = -1; // timestamp used to send state information to the host before a mission starts +int Gameinfo_send_time = -1; // timestamp used by master to send game information to clients +int Next_ping_time = -1; // when we should next ping all +int Multi_server_check_count = 0; // var to keep track of reentrancy when checking server status +int Next_bytes_time = -1; // bytes sent + +// how often each player gets updated +int Multi_client_update_times[MAX_PLAYERS]; // client update packet timestamp + +// local network buffer data +LOCAL ubyte net_buffer[NUM_REENTRANT_LEVELS][MAX_NET_BUFFER]; +LOCAL ubyte Multi_read_count; + +int Multi_restr_query_timestamp = -1; +join_request Multi_restr_join_request; +net_addr Multi_restr_addr; +int Multi_join_restr_mode = -1; + +LOCAL fix Multi_server_wait_start; // variable to hold start time when waiting to reestablish with server + +// non API master tracker vars +char Multi_tracker_login[100] = ""; +char Multi_tracker_passwd[100] = ""; +char Multi_tracker_squad_name[100] = ""; +int Multi_tracker_id = -1; +char Multi_tracker_id_string[255]; + +// current file checksum +ushort Multi_current_file_checksum = 0; +int Multi_current_file_length = -1; + + +// ------------------------------------------------------------------------------------------------- +// multi_init() is called only once, at game start-up. Get player address + port, initialize the +// network players list. +// +// + +void multi_init() +{ + int idx; + + // read in config file + multi_options_read_config(); + + Assert( Net_player == NULL ); + Multi_id_num = 0; + + // clear out all netplayers + memset(Net_players, 0, sizeof(net_player) * MAX_PLAYERS); + for(idx=0; idxtracker_player_id = Multi_tracker_id; + Net_player->player = Player; + Net_player->flags = 0; + Net_player->s_info.xfer_handle = -1; + Net_player->player_id = multi_get_new_id(); + Net_player->client_cinfo_seq = 0; + Net_player->client_server_seq = 0; + + // get our connection speed + Multi_connection_speed = multi_get_connection_speed(); + + // initialize other stuff + multi_log_init(); + + // load up common multiplayer icons + multi_load_common_icons(); +} + +// this is an important function which re-initializes any variables required in multiplayer games. +// Always make sure globals you add are re-initialized here !!!! +void multi_vars_init() +{ + // initialize this variable right away. Used in game_level_init for init'ing the player + Next_ship_signature = SHIP_SIG_MIN; + Next_asteroid_signature = ASTEROID_SIG_MIN; + Next_non_perm_signature = NPERM_SIG_MIN; + Next_debris_signature = DEBRIS_SIG_MIN; + + // server-client critical stuff + Multi_button_info_ok = 0; + Multi_button_info_id = 0; + + // Ingame join stuff + Ingame_join_net_signature = -1; + + // Netgame stuff + Netgame.game_state = NETGAME_STATE_FORMING; + + // team select stuff + Multi_ts_inited = 0; + + // load send stuff + Multi_mission_loaded = 0; // client side + + // restricted game stuff + Multi_restr_query_timestamp = -1; + + // respawn stuff + Multi_server_check_count = 0; + + // reentrant variable + Multi_read_count = 0; + + // unset the "have cd" var + // NOTE: we unset this here because we are going to be calling multi_common_verify_cd() + // immediately after this (in multi_level_init() to re-check the status) + Multi_has_cd = 0; + + // current file checksum + Multi_current_file_checksum = 0; + Multi_current_file_length = -1; + + Active_game_head = NULL; + Game_server_head = NULL; + + // only the server should ever care about this + Multi_id_num = 0; +} + +// ------------------------------------------------------------------------------------------------- +// multi_level_init() is called whenever the player starts a multiplayer game +// +// + +void multi_level_init() +{ + int idx; + + // NETLOG + ml_string(NOX("multi_level_init()")); + + // initialize the Net_players array + for(idx=0;idxp_info.addr) ){ + Net_player->p_info.addr = Psnet_my_addr; + } + + if ( !psnet_same(&Psnet_my_addr, &(Netgame.server_addr)) ){ + Netgame.server_addr = Psnet_my_addr; + } + + // the connection was accepted in check_for_listen. Find the netplayer whose address we connected + // with and assign the socket descriptor + for (i = 0; i < MAX_PLAYERS; i++ ) { + if ( (Net_players[i].flags & NETINFO_FLAG_CONNECTED) && (!memcmp(&(addr.addr), &(Net_players[i].p_info.addr.addr), 6)) ) { + // mark this flag so we know he's "fully" connected + Net_players[i].flags |= NETINFO_FLAG_RELIABLE_CONNECTED; + Net_players[i].reliable_socket = sock; + + // send player information to the joiner + send_accept_player_data( &Net_players[i], (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN)?1:0 ); + + // send a netgame update so the new client has all the necessary settings + send_netgame_update_packet(); + + // if this is a team vs. team game, send an update + if(Netgame.type_flags & NG_TYPE_TEAM){ + multi_team_send_update(); + } + + // NETLOG + ml_printf(NOX("Accepted TCP connection from %s"), Net_players[i].player == NULL ? NOX("Unknown") : Net_players[i].player->callsign); + break; + } + } + + // if we didn't find a player, close the socket + if ( i == MAX_PLAYERS ) { + nprintf(("Network", "Got accept on my listen socket, but unknown player. Closing socket.\n")); + psnet_rel_close_socket(&sock); + } + } +} + +// returns true is server hasn't been heard from in N seconds. false otherwise +int multi_client_server_dead() +{ + fix this_time, last_time, max; + + // get the last time we have heard from the server. If greater than some default, then maybe + // display some icon on the HUD indicating slow network connection. if greater than some higher + // max, stop simulating on the client side until we hear from the server again. + this_time = timer_get_fixed_seconds(); + last_time = Netgame.server->last_heard_time; + // check for wrap! must return 0 + if ( last_time > this_time ) + return 0; + + this_time -= last_time; + + // if in mission, use the smaller timeout value. Outside of mission, use a large one. + if ( MULTI_IN_MISSION ){ + max = MULTI_SERVER_MAX_TIMEOUT; + } else { + max = MULTI_SERVER_MAX_TIMEOUT_LARGE; + } + + if ( this_time > max){ + return 1; + } else { + return 0; + } +} + +void multi_process_incoming(); // prototype for function later in this module + +// function to process network data in hopes of getting info back from server +int multi_client_wait_on_server() +{ + int is_dead; + + is_dead = multi_client_server_dead(); + + // if the server is back alive, tell our popup + if ( !is_dead ){ + return MULTI_SERVER_ALIVE; + } + + // on release version -- keep popup active for 60 seconds, then bail +#ifdef NDEBUG + fix this_time = timer_get_fixed_seconds(); + // if the timer wrapped: + if ( this_time < Multi_server_wait_start ) { + Multi_server_wait_start = timer_get_fixed_seconds(); + return FALSE; + } + // check to see if timeout expired + this_time -= Multi_server_wait_start; + if ( this_time > MULTI_SERVER_WAIT_TIME ){ + return MULTI_SERVER_GONE; + } +#endif + + return FALSE; +} + +// function called by multiplayer clients to stop simulating when they have not heard from the server +// in a while. +void multi_client_check_server() +{ + int rval; + + Assert( MULTIPLAYER_CLIENT ); + + // this function can get called while in the popup code below. So we include this check as a + // reentrancy check. + if ( Multi_server_check_count ) + return; + + // make sure we have a valid server + if(Netgame.server == NULL){ + return; + } + + Multi_server_check_count++; + if(multi_client_server_dead()){ + Netgame.flags |= NG_FLAG_SERVER_LOST; + } else { + Netgame.flags &= ~(NG_FLAG_SERVER_LOST); + } + + if(Netgame.flags & NG_FLAG_SERVER_LOST) { + if(!(Game_mode & GM_IN_MISSION) && !popup_active()){ + // need to start a popup + Multi_server_wait_start = timer_get_fixed_seconds(); + rval = popup_till_condition( multi_client_wait_on_server, XSTR("Cancel",641), XSTR("Contact lost with server. Stopping simulation until contact reestablished. Press Cancel to exit game.",642) ); + + if ( !rval || (rval == MULTI_SERVER_GONE) ) { + multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONTACT_LOST); + } + Netgame.flags &= ~(NG_FLAG_SERVER_LOST); + } + } + + Multi_server_check_count--; +} + + +// ------------------------------------------------------------------------------------------------- +// process_packet_normal() will determine what sort of packet it is, and send it to the appropriate spot. +// Prelimiary verification of the magic number and checksum are done here. +// + +void process_packet_normal(ubyte* data, header *header_info) +{ + switch ( data[0] ) { + + case JOIN: + process_join_packet(data, header_info); + break; + + case GAME_CHAT: + process_game_chat_packet( data, header_info ); + break; + + case NOTIFY_NEW_PLAYER: + process_new_player_packet(data, header_info); + break; + + case HUD_MSG: + process_hud_message(data, header_info); + break; + + case MISSION_MESSAGE: + process_mission_message_packet( data, header_info ); + break; + + case LEAVE_GAME: + process_leave_game_packet(data, header_info); + break; + + case GAME_QUERY: + process_game_query(data, header_info); + break; + + case GAME_ACTIVE: + process_game_active_packet(data, header_info); + break; + + case GAME_INFO: + process_game_info_packet( data, header_info ); + break; + + case SECONDARY_FIRED_AI: + process_secondary_fired_packet(data, header_info, 0); + break; + + case SECONDARY_FIRED_PLR: + process_secondary_fired_packet(data, header_info, 1); + break; + + case COUNTERMEASURE_FIRED: + process_countermeasure_fired_packet( data, header_info ); + break; + + case FIRE_TURRET_WEAPON: + process_turret_fired_packet( data, header_info ); + break; + + case GAME_UPDATE: + process_netgame_update_packet( data, header_info ); + break; + + case UPDATE_DESCRIPT: + process_netgame_descript_packet( data, header_info ); + break; + + case NETPLAYER_UPDATE: + process_netplayer_update_packet( data, header_info ); + break; + + case ACCEPT : + process_accept_packet(data, header_info); + break; + + case OBJECT_UPDATE: + multi_oo_process_update(data, header_info); + break; + + case SHIP_KILL: + process_ship_kill_packet( data, header_info ); + break; + + case WING_CREATE: + process_wing_create_packet( data, header_info ); + break; + + case SHIP_CREATE: + process_ship_create_packet( data, header_info ); + break; + + case SHIP_DEPART: + process_ship_depart_packet( data, header_info ); + break; + + case MISSION_LOG_ENTRY: + process_mission_log_packet( data, header_info ); + break; + + case PING: + process_ping_packet(data, header_info); + break; + + case PONG: + process_pong_packet(data, header_info); + break; + + case XFER_PACKET: + Assert(header_info->id >= 0); + int np_index; + PSNET_SOCKET_RELIABLE sock; + sock = INVALID_SOCKET; + + // if I'm the server of the game, find out who this came from + if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + np_index = find_player_id(header_info->id); + if(np_index >= 0){ + sock = Net_players[np_index].reliable_socket; + } + } + // otherwise always use my own socket + else if(Net_player != NULL){ + sock = Net_player->reliable_socket; + } + + header_info->bytes_processed = multi_xfer_process_packet(data + HEADER_LENGTH, sock) + HEADER_LENGTH; + break; + + case MISSION_REQUEST: + process_mission_request_packet(data,header_info); + break; + + case MISSION_ITEM: + process_mission_item_packet(data,header_info); + break; + + case MULTI_PAUSE_REQUEST: + process_multi_pause_packet(data, header_info); + break; + + case INGAME_NAK: + process_ingame_nak(data, header_info); + break; + + case SHIPS_INGAME_PACKET: + process_ingame_ships_packet(data, header_info); + break; + + case WINGS_INGAME_PACKET: + process_ingame_wings_packet(data, header_info); + break; + + case MISSION_END: + process_endgame_packet(data, header_info); + break; + + case OBSERVER_UPDATE: + process_observer_update_packet(data, header_info); + break; + + case NETPLAYER_SLOTS_P: + process_netplayer_slot_packet(data, header_info); + break; + + case SHIP_STATUS_CHANGE: + process_ship_status_packet(data, header_info); + break; + + case PLAYER_ORDER_PACKET: + process_player_order_packet(data, header_info); + break; + + case INGAME_SHIP_UPDATE: + process_ingame_ship_update_packet(data, header_info); + break; + + case INGAME_SHIP_REQUEST: + process_ingame_ship_request_packet(data, header_info); + break; + + case FILE_SIG_INFO: + process_file_sig_packet(data, header_info); + break; + + case RESPAWN_NOTICE: + multi_respawn_process_packet(data,header_info); + break; + + case SUBSYSTEM_DESTROYED: + process_subsystem_destroyed_packet( data, header_info ); + break; + + case LOAD_MISSION_NOW : + process_netplayer_load_packet(data, header_info); + break; + + case FILE_SIG_REQUEST : + process_file_sig_request(data, header_info); + break; + + case JUMP_INTO_GAME: + process_jump_into_mission_packet(data, header_info); + break; + + case CLIENT_REPAIR_INFO: + process_repair_info_packet(data,header_info); + break; + + case MISSION_SYNC_DATA: + process_mission_sync_packet(data,header_info); + break; + + case STORE_MISSION_STATS: + process_store_stats_packet(data, header_info); + break; + + case DEBRIS_UPDATE: + process_debris_update_packet(data, header_info); + break; + + case SHIP_WSTATE_CHANGE: + process_ship_weapon_change( data, header_info ); + break; + + case WSS_UPDATE_PACKET: + process_wss_update_packet(data, header_info); + break; + + case WSS_REQUEST_PACKET: + process_wss_request_packet( data, header_info ); + break; + + case FIRING_INFO: + process_firing_info_packet( data, header_info ); + break; + + case CARGO_REVEALED: + process_cargo_revealed_packet( data, header_info); + break; + + case SUBSYS_CARGO_REVEALED: + process_subsystem_cargo_revealed_packet( data, header_info); + break; + + case MISSION_GOAL_INFO: + process_mission_goal_info_packet(data, header_info); + break; + + case KICK_PLAYER: + process_player_kick_packet(data, header_info); + break; + + case PLAYER_SETTINGS: + process_player_settings_packet(data, header_info); + break; + + case DENY: + process_deny_packet(data, header_info); + break; + + case POST_SYNC_DATA: + process_post_sync_data_packet(data, header_info); + break; + + case WSS_SLOTS_DATA: + process_wss_slots_data_packet(data,header_info); + break; + + case SHIELD_EXPLOSION: + process_shield_explosion_packet( data, header_info ); + break; + + case PLAYER_STATS: + process_player_stats_block_packet(data, header_info); + break; + + case SLOT_UPDATE: + process_pslot_update_packet(data,header_info); + break; + + case AI_INFO_UPDATE: + process_ai_info_update_packet( data, header_info ); + break; + + case CAMPAIGN_UPDATE : + multi_campaign_process_update(data,header_info); + break; + + case CAMPAIGN_UPDATE_INGAME: + multi_campaign_process_ingame_start(data,header_info); + break; + + case VOICE_PACKET : + multi_voice_process_packet(data,header_info); + break; + + case TEAM_UPDATE : + multi_team_process_packet(data,header_info); + break; + + case ASTEROID_INFO: + process_asteroid_info(data, header_info); + break; + + case HOST_RESTR_QUERY: + process_host_restr_packet(data, header_info); + break; + + case OPTIONS_UPDATE: + multi_options_process_packet(data,header_info); + break; + + case SQUADMSG_PLAYER: + multi_msg_process_squadmsg_packet(data,header_info); + break; + + case NETGAME_END_ERROR: + process_netgame_end_error_packet(data,header_info); + break; + + case COUNTERMEASURE_SUCCESS: + process_countermeasure_success_packet( data, header_info ); + break; + + case CLIENT_UPDATE: + process_client_update_packet(data, header_info); + break; + + case COUNTDOWN: + process_countdown_packet(data, header_info); + break; + + case DEBRIEF_INFO: + process_debrief_info( data, header_info ); + break; + + case ACCEPT_PLAYER_DATA: + process_accept_player_data( data, header_info ); + break; + + case HOMING_WEAPON_UPDATE: + process_homing_weapon_info( data, header_info ); + break; + + case EMP_EFFECT: + process_emp_effect(data, header_info); + break; + + case REINFORCEMENT_AVAIL: + process_reinforcement_avail( data, header_info ); + break; + + case CHANGE_IFF: + process_change_iff_packet(data, header_info); + break; + + case PRIMARY_FIRED_NEW: + process_NEW_primary_fired_packet(data, header_info); + break; + + case COUNTERMEASURE_NEW: + process_NEW_countermeasure_fired_packet(data, header_info); + break; + + case BEAM_FIRED: + process_beam_fired_packet(data, header_info); + break; + + case SW_STD_QUERY: + process_sw_query_packet(data, header_info); + break; + + case EVENT_UPDATE: + process_event_update_packet(data, header_info); + break; + + case OBJECT_UPDATE_NEW: + multi_oo_process_update(data, header_info); + break; + + case WEAPON_DET: + process_weapon_detonate_packet(data, header_info); + break; + + case FLAK_FIRED: + process_flak_fired_packet(data, header_info); + break; + + case NETPLAYER_PAIN: + process_player_pain_packet(data, header_info); + break; + + case LIGHTNING_PACKET: + process_lightning_packet(data, header_info); + break; + + case BYTES_SENT: + process_bytes_recvd_packet(data, header_info); + break; + + case TRANSFER_HOST: + process_host_captain_change_packet(data, header_info); + break; + + case SELF_DESTRUCT: + process_self_destruct_packet(data, header_info); + break; + + default: + nprintf(("Network", "Received packet with unknown type %d\n", data[0] )); + header_info->bytes_processed = 10000; + break; + + } // end switch +} + + + +// Takes a bunch of messages, check them for validity, +// and pass them to multi_process_data. +// --------------------^ +// this should be process_packet() I think, or with the new code +// process_tracker_packet() as defined in MultiTracker.[h,cpp] +void multi_process_bigdata(ubyte *data, int len, net_addr *from_addr, int reliable) +{ + int type, bytes_processed; + int player_num; + header header_info; + ubyte *buf; + + // the only packets we will process from an unknown player are GAME_QUERY, GAME_INFO, JOIN, PING, PONG, ACCEPT, and GAME_ACTIVE packets + player_num = find_player(from_addr); + + // find the player who sent the message and mark the last_heard time for this player + // check to see if netplayer is null (it may be in cases such as getting lists of games from the tracker) + if(player_num >= 0){ + Net_players[player_num].last_heard_time = timer_get_fixed_seconds(); + } + + // store fields that were passed along in the message + // store header information that was captured from the network-layer header + memcpy(header_info.addr, from_addr->addr, 6); + memcpy(header_info.net_id, from_addr->net_id, 4); + header_info.port = from_addr->port; + if(player_num >= 0){ + header_info.id = Net_players[player_num].player_id; + } else { + header_info.id = -1; + } + + bytes_processed = 0; + while( (bytes_processed >= 0) && (bytes_processed < len) ) { + + buf = &(data[bytes_processed]); + + type = buf[0]; + + // if its coming from an unknown source, there are only certain packets we will actually process + if((player_num == -1) && !multi_is_valid_unknown_packet((ubyte)type)){ + return ; + } + + if ( (type<0) || (type > MAX_TYPE_ID )) { + nprintf( ("Network", "multi_process_bigdata: Invalid packet type %d!\n", type )); + return; + } + + // perform any special processing checks here + process_packet_normal(buf,&header_info); + + // MWA -- magic number was removed from header on 8/4/97. Replaced with bytes_processed + // variable which gets stuffed whenever a packet is processed. + bytes_processed += header_info.bytes_processed; + } + + // if this is not reliable data and we have a valid player + if(Net_player != NULL){ + if(!MULTIPLAYER_MASTER && !reliable && (Game_mode & GM_IN_MISSION)){ + Net_player->cl_bytes_recvd += len; + } + } +} + +// process all reliable socket details +void multi_process_reliable_details() +{ + int idx; + int sock_status; + + // run reliable sockets +#ifdef PSNET2 + psnet_rel_work(); +#endif + + // server operations + if ( MULTIPLAYER_MASTER ){ + // listen for new reliable socket connections + multi_check_listen(); + + // check for any broken sockets and delete any players + for(idx=0; idx MULTI_RELIABLE_CONNECT_WAIT) && (Net_players[idx].reliable_socket == INVALID_SOCKET)){ + ml_string("Player timed out while connecting on reliable socket!"); + delete_player(idx); + } + } + } + } + } + // clients should detect broken sockets + else { + extern unsigned int Serverconn; + if(Serverconn != 0xffffffff){ + int status = psnet_rel_get_status(Serverconn); + if(status == RNF_BROKEN){ + mprintf(("CLIENT SOCKET DISCONNECTED")); + + // quit the game + if(!multi_endgame_ending()){ + multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONTACT_LOST); + } + } + } + } +} + +// multi_process_incoming reads incoming data off the unreliable and reliable ports and sends +// the data to process_big_data +void multi_process_incoming() +{ + int size; + ubyte *data, *savep; + net_addr from_addr; + + Assert( Multi_read_count < NUM_REENTRANT_LEVELS ); + savep = net_buffer[Multi_read_count]; + + Multi_read_count++; + + data = savep; + + // get the other net players data + while( (size = psnet_get(data, &from_addr))>0 ) { + // ingame joiners will ignore UDP packets until they are have picked a ship and are in the mission + if( (Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Net_player->state != NETPLAYER_STATE_INGAME_SHIP_SELECT) ){ + //nprintf(("Network","Tossing UDP like a good little ingame joiner...\n")); + } + // otherwise process incoming data normally + else { + multi_process_bigdata(data, size, &from_addr, 0); + } + } // end while + + // read reliable sockets for data + data = savep; + int idx; + + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + for (idx=0;idxplayer_id != Net_players[idx].player_id)){ + while( (size = psnet_rel_get(Net_players[idx].reliable_socket, data, MAX_NET_BUFFER)) > 0){ + multi_process_bigdata(data, size, &Net_players[idx].p_info.addr, 1); + } + } + } + } else { + // if I'm not the master of the game, read reliable data from my connection with the server + if((Net_player->reliable_socket != INVALID_SOCKET) && (Net_player->reliable_socket != 0)){ + while( (size = psnet_rel_get(Net_player->reliable_socket,data, MAX_NET_BUFFER)) > 0){ + multi_process_bigdata(data, size, &Netgame.server_addr, 1); + } + } + } + + Multi_read_count--; +} + +// ------------------------------------------------------------------------------------------------- +// multi_do_frame() is called once per game loop do update all the multiplayer objects, and send +// the player data to all the other net players. +// +// + +int eye_tog = 1; +DCF(eye_tog, "") +{ + eye_tog = !eye_tog; + if(eye_tog){ + dc_printf("proper eye stuff on\n"); + } else { + dc_printf("proper eye stuff off\n"); + } +} + +void multi_do_frame() +{ + PSNET_TOP_LAYER_PROCESS(); + + // always set the local player eye position/orientation here so we know its valid throughout all multiplayer + // function calls + if((Net_player != NULL) && eye_tog){ + player_get_eye(&Net_player->s_info.eye_pos, &Net_player->s_info.eye_orient); + } + + // send all buffered packets from the previous frame + multi_io_send_buffered_packets(); + + // datarate tracking + multi_rate_process(); + + // always process any pending endgame details + multi_endgame_process(); + + // process all reliable socket details, including : + // 1.) Listening for new pending reliable connections (server) + // 2.) Checking for broken sockets (server/client) + // 3.) Checking for clients who haven't fully connected + multi_process_reliable_details(); + + // get the other net players data + multi_process_incoming(); + + // process object update datarate stuff (for clients and server both) + multi_oo_rate_process(); + + // clients should check when last time they heard from sever was -- if too long, then + // pause the simulation so wait for it to possibly come back + if ( (MULTIPLAYER_CLIENT) && (Net_player->flags & NETINFO_FLAG_CONNECTED) ){ + multi_client_check_server(); + } + + // everybody pings all the time + if((Next_ping_time < 0) || ((time(NULL) - Next_ping_time) > PING_SEND_TIME) ){ + if( (Net_player->flags & NETINFO_FLAG_AM_MASTER) ){ + send_netplayer_update_packet(); + } + + // ping everyone + multi_ping_send_all(); + Next_ping_time = time(NULL); + } + + // if I am the master, and we are not yet actually playing the mission, send off netgame + // status to all other players in the game. If I am not the master of the game, and we + // are not in the game, then send out my netplayer status to the host + if ( (Net_player->flags & NETINFO_FLAG_CONNECTED) && !(Game_mode & GM_IN_MISSION)){ + if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) { + if ( (Netgame_send_time < 0) || ((time(NULL) - Netgame_send_time) > NETGAME_SEND_TIME) ) { + send_netgame_update_packet(); + + Netgame_send_time = time(NULL); + } + } else { + if ( (State_send_time < 0) || ((time(NULL) - State_send_time) > STATE_SEND_TIME) ){ + // observers shouldn't send an update state packet + if ( !(Net_player->flags & NETINFO_FLAG_OBSERVER) ){ + send_netplayer_update_packet(); + } + + State_send_time = time(NULL); + } + } + } + else if ( (Net_player->flags & NETINFO_FLAG_CONNECTED) && (Game_mode & GM_IN_MISSION) ) { + // if I am connected and am in the mission, do things that need to be done on a regular basis + if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) { + if ( (Gameinfo_send_time < 0) || ((time(NULL) - Gameinfo_send_time) > GAMEINFO_SEND_TIME)){ + send_game_info_packet(); + + Gameinfo_send_time = time(NULL); + } + + // for any potential respawns + multi_respawn_handle_invul_players(); + multi_respawn_check_ai(); + + // for any potential ingame joiners + multi_handle_ingame_joiners(); + } else { + // the clients need to do some processing of stuff as well + } + } + + // check to see if we're waiting on confirmation for a restricted ingame join + if(Multi_restr_query_timestamp != -1){ + // if it has elapsed, unset the ingame join flag + if(timestamp_elapsed(Multi_restr_query_timestamp)){ + Multi_restr_query_timestamp = -1; + Netgame.flags &= ~(NG_FLAG_INGAME_JOINING); + } + } + + // while in the mission, send my PlayerControls to the host so that he can process + // my movement + if ( Game_mode & GM_IN_MISSION ) { + // tickers + extern void oo_update_time(); + oo_update_time(); + + + if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + if(Net_player->flags & NETINFO_FLAG_OBSERVER){ + // if the rate limiting system says its ok + if(multi_oo_cirate_can_send()){ + // send my observer position/object update + send_observer_update_packet(); + } + } else if ( !(Player_ship->flags & SF_DEPARTING ) ){ + // if the rate limiting system says its ok + if(multi_oo_cirate_can_send()){ + // use the new method + multi_oo_send_control_info(); + } + } + + // bytes received info + if( (Next_bytes_time < 0) || ((time(NULL) - Next_bytes_time) > BYTES_SENT_TIME) ){ + if(Net_player != NULL){ + send_bytes_recvd_packet(Net_player); + + // reset bytes recvd + Net_player->cl_bytes_recvd = 0; + } + + // reset timestamp + Next_bytes_time = time(NULL); + } + } else { + // sending new objects from here is dependent on having objects only created after + // the game is done moving the objects. I think that I can enforce this. + multi_oo_process(); + + // evaluate whether the time limit has been reached or max kills has been reached + // Commented out by Sandeep 4/12/98, was causing problems with testing. + if( ((f2fl(Netgame.options.mission_time_limit) > 0.0f) && (Missiontime > Netgame.options.mission_time_limit)) || + multi_kill_limit_reached() ) { + + // make sure we don't do this more than once + if(Netgame.game_state == NETGAME_STATE_IN_MISSION){ + multi_handle_end_mission_request(); + } + } + } + } + + // periodically send a client update packet to all clients + if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + int idx; + for(idx=0;idxs_info.eye_pos, &Net_player->s_info.eye_orient); + // } + + // send all buffered packets from the previous frame + multi_io_send_buffered_packets(); + + // always process any pending endgame details + multi_endgame_process(); + + // process all reliable socket details, including : + // 1.) Listening for new pending reliable connections (server) + // 2.) Checking for broken sockets (server/client) + // 3.) Checking for clients who haven't fully connected + multi_process_reliable_details(); + + // these timestamps and handlers shoul be evaluated in the pause state + if ( Net_player->flags & NETINFO_FLAG_AM_MASTER ) { + if ( (Gameinfo_send_time < 0) || ((time(NULL) - Gameinfo_send_time) > GAMEINFO_SEND_TIME) ){ + send_game_info_packet(); + + Gameinfo_send_time = time(NULL); + } + } + + // everybody pings all the time + if((Next_ping_time < 0) || ((time(NULL) - Next_ping_time) > PING_SEND_TIME) ){ + multi_ping_send_all(); + + Next_ping_time = time(NULL); + } + + // periodically send a client update packet to all clients + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + int idx; + + for(idx=0;idxtracker_player_id = -1; + Net_player->flags |= (NETINFO_FLAG_AM_MASTER | NETINFO_FLAG_CONNECTED | NETINFO_FLAG_DO_NETWORKING | NETINFO_FLAG_MISSION_OK); + Net_player->state = NETPLAYER_STATE_WAITING; + Net_player->player = Player; + strcpy(Player->callsign, "server"); + Net_player->p_info.addr = Psnet_my_addr; + Net_player->s_info.xfer_handle = -1; + Net_player->player_id = multi_get_new_id(); + Netgame.server = Net_player; + + // maybe flag the game as having a hacked ships.tbl + if(!Game_ships_tbl_valid){ + Netgame.flags |= NG_FLAG_HACKED_SHIPS_TBL; + } + // maybe flag the game as having a hacked weapons.tbl + if(!Game_weapons_tbl_valid){ + Netgame.flags |= NG_FLAG_HACKED_WEAPONS_TBL; + } + + // hacked data + if(game_hacked_data()){ + Net_player->flags |= NETINFO_FLAG_HAXOR; + } + + // setup debug flags + Netgame.debug_flags = 0; + /* + if(!Cmdline_server_firing){ + Netgame.debug_flags |= NETD_FLAG_CLIENT_FIRING; + } + if(!Cmdline_client_dodamage){ + Netgame.debug_flags |= NETD_FLAG_CLIENT_NODAMAGE; + } + */ + + // setup the default game name for the standalone + std_connect_set_gamename(NULL); + + // set netgame default options + multi_options_set_netgame_defaults(&Netgame.options); + + // set local netplayer default options + multi_options_set_local_defaults(&Net_player->p_info.options); + + // set our object update level from the standalone default + Net_player->p_info.options.obj_update_level = Multi_options_g.std_datarate; + switch(Net_player->p_info.options.obj_update_level){ + case OBJ_UPDATE_LOW: + nprintf(("Network","STANDALONE USING LOW UPDATES\n")); + break; + case OBJ_UPDATE_MEDIUM: + nprintf(("Network","STANDALONE USING MEDIUM UPDATES\n")); + break; + case OBJ_UPDATE_HIGH: + nprintf(("Network","STANDALONE USING HIGH UPDATE\n")); + break; + case OBJ_UPDATE_LAN: + nprintf(("Network","STANDALONE USING LAN UPDATE\n")); + break; + } + + // clear out various things + psnet_flush(); + game_flush(); + ship_init(); + + std_debug_set_standalone_state_string("Main Do"); + std_set_standalone_fps((float)0); + std_multi_set_standalone_missiontime((float)0); + + // load my missions and campaigns + multi_create_list_load_missions(); + multi_create_list_load_campaigns(); + + // if this is a tracker game, validate missions + if(MULTI_IS_TRACKER_GAME){ + multi_update_valid_missions(); + } +} + + +// -------------------------------------------------------------------------------- +// standalone_main_do() +// + +// DESCRIPTION : the standalone server will wait in this state until the host of the game +// is "Waiting". That is, his state==NETPLAYER_STATE_WAITING, and he has finished +// doing everything and wants to play the game. Once this happens, we will jump +// into GS_STATE_MULTI_SERVER_WAIT + +void standalone_main_do() +{ + Sleep(10); // since nothing will really be going on here, we can afford to give some time + // back to the operating system. + + // kind of a do-nothing spin state. + // The standalone will eventually move into the GS_STATE_MULTI_MISSION_SYNC state when a host connects and + // attempts to start a game +} + +// -------------------------------------------------------------------------------- +// standalone_main_close() +// + +void standalone_main_close() +{ + std_debug_set_standalone_state_string("Main Close"); +} + +void multi_standalone_reset_all() +{ + int idx; + + // NETLOG + ml_string(NOX("Standalone resetting")); + + // shut all game stuff down + game_level_close(); + + // reinitialize the gui + std_reset_standalone_gui(); + + // close down all sockets + for(idx=0;idxflags & NETINFO_FLAG_CONNECTED){ + Net_players[idx].client_cinfo_seq = 0; + Net_players[idx].client_server_seq = 0; + } + } +} + + +// this is an artificial state which will push the standalone into the main state without it having to go through +// the init function (which would disconnect everyone and generally just screw things up) +// it will also eventually do tracker stats update +extern int Multi_debrief_server_framecount; +void multi_standalone_postgame_init() +{ + std_debug_set_standalone_state_string("Postgame / Send Stats"); + + // NETLOG + ml_string(NOX("Standlone entering postgame")); + + mission_goal_fail_incomplete(); + + // handle campaign stuff + if ( Game_mode & GM_CAMPAIGN_MODE ) { + // MUST store goals and events first - may be used to evaluate next mission + // store goals and events + mission_campaign_store_goals_and_events(); + + // evaluate next mission + mission_campaign_eval_next_mission(); + } + + // always set my state to be "DEBRIEF_ACCEPT" + Net_player->state = NETPLAYER_STATE_DEBRIEF_ACCEPT; + + // mark stats as not being store yet + Netgame.flags &= ~(NG_FLAG_STORED_MT_STATS); + + Multi_debrief_server_framecount = 0; + + // reset network timestamps + multi_reset_timestamps(); +} + +void multi_standalone_postgame_do() +{ + // wait until everyone is in the debriefing + if((Netgame.game_state != NETGAME_STATE_DEBRIEF) && multi_netplayer_state_check(NETPLAYER_STATE_DEBRIEF, 1)){ + Netgame.game_state = NETGAME_STATE_DEBRIEF; + send_netgame_update_packet(); + debrief_multi_server_stuff(); + } + + // process server debriefing details + if(Netgame.game_state == NETGAME_STATE_DEBRIEF){ + multi_debrief_server_process(); + } +} + +void multi_standalone_postgame_close() +{ +} + +void multi_reset_timestamps() +{ + int i; + + for ( i = 0 ; i < MAX_PLAYERS; i++ ){ + Multi_client_update_times[i] = -1; + } + Netgame_send_time = -1; + Gameinfo_send_time = -1; + Next_ping_time = -1; + State_send_time = -1; + Next_bytes_time = -1; + + chatbox_reset_timestamps(); + + // do for all players so that ingame joiners work properly. + for (i = 0; i < MAX_PLAYERS; i++ ) { + Players[i].update_dumbfire_time = timestamp(0); + Players[i].update_lock_time = timestamp(0); + + Net_players[i].s_info.voice_token_timestamp = -1; + } + + // reset standalone gui timestamps (these are not game critical, so there is not much danger) + std_reset_timestamps(); + + // initialize all object update timestamps + multi_oo_gameplay_init(); +} + +// netgame debug flags for debug console stuff +DCF(netd, "change/list netgame debug flags") +{ + dc_get_arg(ARG_INT); + + // if we got an integer, and we're the server, change flags + if((Dc_arg_type & ARG_INT) && (Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Dc_arg_int <= 7)){ + Netgame.debug_flags ^= (1<callsign, Net_players[idx].sv_bytes_sent, 0); sy += 10; + } else { + gr_printf(sx, sy, "%s : %d, %d pl", Net_players[idx].player->callsign, Net_players[idx].sv_bytes_sent, Net_players[idx].sv_last_pl); sy += 10; + } + } + } + } else { + gr_string(sx, sy, "CLIENT"); sy += 10; + + // display PL + if(Net_player != NULL){ + if(Net_player->cl_last_pl < 0){ + gr_printf(sx, sy, "PL : %d %d pl\n", Net_player->cl_bytes_recvd, 0); sy += 10; + } else { + gr_printf(sx, sy, "PL : %d %d pl\n", Net_player->cl_bytes_recvd, Net_player->cl_last_pl); sy += 10; + } + } + } +} \ No newline at end of file diff --git a/src/network/multi_campaign.cpp b/src/network/multi_campaign.cpp new file mode 100644 index 0000000..cc8d30f --- /dev/null +++ b/src/network/multi_campaign.cpp @@ -0,0 +1,922 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_campaign.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 6 3/10/99 6:50p Dave + * Changed the way we buffer packets for all clients. Optimized turret + * fired packets. Did some weapon firing optimizations. + * + * 5 3/09/99 6:24p Dave + * More work on object update revamping. Identified several sources of + * unnecessary bandwidth. + * + * 4 12/12/98 3:17p Andsager + * Clean up mission eval, goal, event and mission scoring. + * + * 3 11/19/98 8:03a Dave + * Full support for D3-style reliable sockets. Revamped packet lag/loss + * system, made it receiver side and at the lowest possible level. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 31 6/13/98 6:01p Hoffoss + * Externalized all new (or forgot to be added) strings to all the code. + * + * 30 5/23/98 7:09p Dave + * Fixed a potentially serious ingame join campaign bug. + * + * 29 5/23/98 4:28p Jasen + * Insure goals pointer is not null. + * + * 28 5/22/98 11:11a Allender + * more ingame join campaign fixes + * + * 27 5/21/98 11:34p Allender + * ingame join fixes + * + * 26 5/21/98 2:03a Allender + * fix for campaign misison names + * + * 25 5/21/98 12:14a Allender + * fix ingame join problems + * + * 24 5/20/98 3:25p Allender + * ingame join changes (which probably won't make the final version). + * Added RAS code into psnet + * + * 23 5/19/98 8:35p Dave + * Revamp PXO channel listing system. Send campaign goals/events to + * clients for evaluation. Made lock button pressable on all screens. + * + * 22 5/08/98 5:05p Dave + * Go to the join game screen when quitting multiplayer. Fixed mission + * text chat bugs. Put mission type symbols on the create game list. + * Started updating standalone gui controls. + * + * 21 5/06/98 12:36p Dave + * Make sure clients can leave the debrief screen easily at all times. Fix + * respawn count problem. + * + * 20 5/05/98 7:25p Adam + * Fixed a few potential sequencing problems (packets getting lost or + * ignored when doing state transitions) + * + * 19 5/05/98 5:02p Dave + * Fix end-of-campaign sequencing to work right. Make all individual + * missions of a campaign replayable. + * + * 18 5/05/98 2:10p Dave + * Verify campaign support for testing. More new tracker code. + * + * 17 5/04/98 10:39p Dave + * Put in endgame sequencing. Need to check campaign situations. + * Realigned ship info on team select screen. + * + * 16 4/22/98 5:52p Dave + * Large reworking of endgame sequencing. Updated host options screen for + * new artwork. Put in checks to end game if host leaves or if team + * captains leave mid-game. + * + * 15 4/20/98 4:56p Allender + * allow AI ships to respawn as many times as there are respawns in the + * mission. + * + * 14 4/06/98 10:24p Dave + * Fixed up Netgame.respawn for the standalone case. + * + * 13 4/06/98 6:37p Dave + * Put in max_observers netgame server option. Make sure host is always + * defaulted to alpha 1 or zeta 1. Changed create game so that MAX_PLAYERS + * can always join but need to be kicked before commit can happen. Put in + * support for server ending a game and notifying clients of a special + * condition. + * + * 12 3/24/98 4:59p Dave + * Fixed several ui bugs. Put in pre and post voice stream playback sound + * fx. Put in error specific popups for clients getting dropped from games + * through actions other than their own. + * + * 11 3/17/98 12:16a Allender + * asteroids in multiplayer -- minor problems with position being correct + * + * 10 3/16/98 2:35p Dave + * Numerous bug fixes. Made the "cue sound" sound play before incoming + * voice. + * + * 9 3/15/98 4:17p Dave + * Fixed oberver hud problems. Put in handy netplayer macros. Reduced size + * of network orientation matrices. + * + * 8 3/12/98 10:45p Allender + * more ingame join stuff. Mission events are not evaluated during + * critical ingame portion. Wings/ships appear to work great. Support + * ships still has a few problems I think + * + * 7 3/12/98 5:45p Dave + * Put in new observer HUD. Made it possible for observers to join at the + * beginning of a game and follow it around as an observer full-time. + * + * 6 3/05/98 5:03p Dave + * More work on team vs. team support for multiplayer. Need to fix bugs in + * weapon select. + * + * 5 3/03/98 5:12p Dave + * 50% done with team vs. team interface issues. Added statskeeping to + * secondary weapon blasts. Numerous multiplayer ui bug fixes. + * + * 4 2/23/98 11:09p Dave + * Finished up multiplayer campaign support. Seems bug-free. + * + * 3 2/23/98 5:08p Allender + * made net_signature an unsigned short. Now using permanent and + * non-permanent object "pools". + * + * 2 2/22/98 2:53p Dave + * Put in groundwork for advanced multiplayer campaign options. + * + * 1 2/20/98 4:39p Dave + * Split up mp campaign functionality into its own module. + * + * $NoKeywords: $ + */ + +#include + +#include "gamesequence.h" +#include "multi.h" +#include "multiui.h" +#include "freespace.h" +#include "multimsgs.h" +#include "multiutil.h" +#include "popup.h" +#include "multi_xfer.h" +#include "multi_campaign.h" +#include "multi_endgame.h" +#include "stand_gui.h" +#include "missiongoals.h" + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER CAMPAIGN DEFINES/VARS +// + +// packet codes +#define MC_CODE_POOL 0 // pool/weapons update +#define MC_CODE_DONE 1 // campaign is "done" +#define MC_CODE_DEBRIEF 2 // debrief info +#define MC_CODE_START 3 // start campaign information + +// packet code for ingame joining +#define MC_JIP_INITIAL_PACKET 1 // initial data +#define MC_JIP_GE_STATUS 2 // goal event status for all missions +#define MC_JIP_GOAL_NAMES 3 // goal names +#define MC_JIP_EVENT_NAMES 4 // event names +#define MC_JIP_END_DATA 5 // this is the end of the data + + +#define MC_INGAME_DATA_SLOP 50 + + +// flags indicating the "accept" status of all players involved in a campaign +int Multi_campaign_accept_flags[MAX_PLAYERS]; + + +// ------------------------------------------------------------------------------------ +// MULTIPLAYER CAMPAIGN FUNCTIONS +// + +// load a new campaign file or notify the standalone if we're not the server +void multi_campaign_start(char *filename) +{ + int max_players; + char str[255]; + + // set the netgame mode + Netgame.campaign_mode = MP_CAMPAIGN; + + // set the campaign filename + strcpy(Netgame.campaign_name,filename); + + // add the campaign mode flag + Game_mode |= GM_CAMPAIGN_MODE; + + // if we're the server of the game we should also be loading the campaign up. otherwise, we let the standalone do it + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + // start the campaign, passing 0 so we do _not_ load the savefile. this is only for starting + // new campaigns + mission_campaign_load(filename); + mission_campaign_next_mission(); + + // setup various filenames and mission names + strcpy(Netgame.mission_name,Campaign.missions[Campaign.current_mission].name); + strcpy(Netgame.campaign_name,filename); + strcpy(Game_current_mission_filename,Netgame.mission_name); + + // if we're the standalone server, set the mission and campaign names + if(Game_mode & GM_STANDALONE_SERVER){ + memset(str,0,255); + strcpy(str,Netgame.mission_name); + strcat(str," ("); + strcat(str,Netgame.campaign_name); + strcat(str,")"); + + // set the control on the stand_gui + std_multi_set_standalone_mission_name(str); + } + + // maybe override the Netgame.respawn setting + max_players = mission_parse_get_multi_mission_info( Netgame.mission_name ); + Netgame.respawn = The_mission.num_respawns; + nprintf(("Network","MULTI CAMPAIGN : overriding respawn setting with mission max %d\n",The_mission.num_respawns)); + + // send a "start campaign" packet + multi_campaign_send_start(); + } +} + +// client-side start of a campaign +void multi_campaign_client_start() +{ + memset(&Campaign,0,sizeof(Campaign)); + + // set campaign mode. what the hell. + Game_mode |= GM_CAMPAIGN_MODE; +} + +// move everything and eveyrone into the next mission state +void multi_campaign_next_mission() +{ + char str[255]; + + // flush the important data + multi_campaign_flush_data(); + + // call the campaign over function + mission_campaign_mission_over(); + + // now we should be sequencing through the next stage (mission load, etc) + // this will eventually be replaced with the real filename of the next mission + if(Campaign.current_mission != -1){ + strncpy(Game_current_mission_filename, Campaign.missions[Campaign.current_mission].name, MAX_FILENAME_LEN); + strcpy(Netgame.mission_name,Game_current_mission_filename); + + // if we're the standalone server, set the mission and campaign names + if(Game_mode & GM_STANDALONE_SERVER){ + memset(str,0,255); + strcpy(str,Netgame.mission_name); + strcat(str," ("); + strcat(str,Netgame.campaign_name); + strcat(str,")"); + + // set the control on the stand_gui + std_multi_set_standalone_mission_name(str); + } + + } +} + +// flush all important data between missions +void multi_campaign_flush_data() +{ + // blast the accept flags + memset(Multi_campaign_accept_flags,0,sizeof(int) * MAX_PLAYERS); + + // flush mission stuff + multi_flush_mission_stuff(); +} + +// call in the debriefing stage to evaluate what we should be doing in regards to the campaign +// if player_status == 0, nothing should be done +// == 1, players want to continue to the next mission +// == 2, players want to repeat the previous mission +void multi_campaign_do_debrief(int player_status) +{ + // the server (standalone or no) + if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Campaign.current_mission != -1) && player_status){ + // if players want to go to the next mission + if(player_status == 1){ + // move the multiplayer campaign along + multi_campaign_next_mission(); + + // if we're at the end of the campaign + if(Campaign.current_mission == -1){ + // set the netgame state to be forming and continue + Netgame.game_state = NETGAME_STATE_FORMING; + send_netgame_update_packet(); + + if(Game_mode & GM_STANDALONE_SERVER){ + gameseq_post_event(GS_EVENT_STANDALONE_MAIN); + } else { + gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP); + } + multi_reset_timestamps(); + } + // if we're still in the campaign + else { + Netgame.game_state = NETGAME_STATE_MISSION_SYNC; + send_netgame_update_packet(); + + Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING; + gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC); + + multi_reset_timestamps(); + } + } + // if the players want to replay the current mission + else if(player_status == 2){ + Netgame.game_state = NETGAME_STATE_MISSION_SYNC; + send_netgame_update_packet(); + + Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING; + gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC); + + multi_reset_timestamps(); + } else { + Int3(); + } + } +} + +// display the done popup +void multi_campaign_done_popup() +{ + popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("The Campaign Is Complete. Thank You For Playing",643)); + multi_quit_game(PROMPT_NONE); +} + +// evaluate post mission goal stuff for the campaign and send all relevant stuff to clients +void multi_campaign_eval_debrief() +{ + // evaluate mission stuff (fills in structures, etc). + // DKA 12/12/98 already done + // mission_campaign_eval_next_mission(); + + // send the campaign debriefing packet + multi_campaign_send_debrief_info(); +} + +// clients should store mission goal/event names in the campaign now +void multi_campaign_client_store_goals(int mission_num) +{ + int idx; + + // copy mission goals into the campaign goals + for(idx=0;idxstate = NETPLAYER_STATE_CPOOL_ACK; + send_netplayer_update_packet(); + break; + + case MC_CODE_DEBRIEF: + GET_DATA(cur_mission); + GET_DATA(next_mission); + + // add the filename + GET_STRING(fname); + Campaign.missions[cur_mission].name = strdup(fname); + + // add the # of goals and events + GET_DATA(val); + Campaign.missions[cur_mission].num_goals = val; + Campaign.missions[cur_mission].goals = (mgoal*)malloc(sizeof(mgoal) * val); + + GET_DATA(val); + Campaign.missions[cur_mission].num_events = val; + Campaign.missions[cur_mission].events = (mevent*)malloc(sizeof(mevent) * val); + + // add the goals + for(idx=0;idxflags & NETINFO_FLAG_AM_MASTER); + multi_io_send_to_all_reliable(data, packet_size); + + // notification message + multi_common_add_text(XSTR("Campaign ship/weapon pool\n",644),1); +} + +// send a "start campaign" packet +void multi_campaign_send_start(net_player *pl) +{ + ubyte data[MAX_PACKET_SIZE],val; + int idx; + int packet_size = 0; + + // build the header + BUILD_HEADER(CAMPAIGN_UPDATE); + + // add the code + val = MC_CODE_START; + ADD_DATA(val); + + // add the # of missions, and their filenames + ADD_DATA(Campaign.num_missions); + for(idx=0;idx MAX_PACKET_SIZE ) { + *ptr = goal_count; + multi_io_send_reliable(pl, data, packet_size); + BUILD_HEADER(CAMPAIGN_UPDATE_INGAME); + packet_type = MC_JIP_GOAL_NAMES; + ADD_DATA( packet_type ); + ADD_DATA(i); + ptr = &data[packet_size]; + goal_count = 0; + ADD_DATA( goal_count ); + starting_goal_num = (ubyte)j; + ADD_DATA( starting_goal_num ); + } + } + + *ptr = goal_count; + multi_io_send_reliable(pl, data, packet_size); + } + + // send the goal/event names. + for ( i = 0; i < Campaign.num_missions; i++ ) { + ubyte event_count, starting_event_num; + + // first the goal names + Assert( Campaign.missions[i].num_events < UCHAR_MAX ); + num_events = (ubyte)Campaign.missions[i].num_events; + + // don't do anything if mission hasn't been completed + if ( !Campaign.missions[i].completed ) + continue; + + BUILD_HEADER(CAMPAIGN_UPDATE_INGAME); + packet_type = MC_JIP_EVENT_NAMES; + ADD_DATA(packet_type); + ADD_DATA(i); + + // save a pointer so we can put the number of goals written here. + ptr = &data[packet_size]; + + event_count = 0; + ADD_DATA( event_count ); + + starting_event_num = 0; + ADD_DATA( starting_event_num ); + + for ( j = 0; j < num_events; j++ ) { + ADD_STRING( Campaign.missions[i].events[j].name ); + event_count++; + + // if packet will get too big, send it off. + if ( (packet_size + MC_INGAME_DATA_SLOP) > MAX_PACKET_SIZE ) { + *ptr = event_count; + multi_io_send_reliable(pl, data, packet_size); + BUILD_HEADER(CAMPAIGN_UPDATE_INGAME); + packet_type = MC_JIP_EVENT_NAMES; + ADD_DATA( packet_type ); + ADD_DATA(i); + ptr = &data[packet_size]; + event_count = 0; + ADD_DATA( event_count ); + starting_event_num = (ubyte)j; + ADD_DATA( starting_event_num ); + } + } + + *ptr = event_count; + multi_io_send_reliable(pl, data, packet_size); + } + } + + // add the stop byte + BUILD_HEADER(CAMPAIGN_UPDATE_INGAME); + packet_type = MC_JIP_END_DATA; + ADD_DATA(packet_type); + multi_io_send_reliable(pl, data, packet_size); +} + +void multi_campaign_process_ingame_start( ubyte *data, header *hinfo ) +{ + int offset, mission_num, i; + ubyte packet_type, num_goals, num_events, status, starting_num; + char fname[255]; + + offset = HEADER_LENGTH; + + GET_DATA( packet_type ); + switch( packet_type ) { + case MC_JIP_INITIAL_PACKET: + + // clear out the names of the missions + mission_campaign_close(); // should free all data structures which need to be freed + + // get the number of campaigns and their names. + GET_DATA(Campaign.num_missions); + for( i = 0; i < Campaign.num_missions ; i++) { + GET_STRING(fname); + Campaign.missions[i].name = strdup(fname); + } + + break; + + case MC_JIP_GE_STATUS: + + GET_DATA( mission_num ); + GET_DATA( num_goals ); + // need to malloc out the data + Assert( Campaign.missions[mission_num].num_goals == 0 ); + Campaign.missions[mission_num].num_goals = num_goals; + if ( num_goals > 0 ){ + Campaign.missions[mission_num].goals = (mgoal *)malloc( sizeof(mgoal) * num_goals ); + } + for ( i = 0; i < num_goals; i++ ) { + GET_DATA(status); + // AL: .goals was a NULL pointer here! I have no idea why. Putting + // in a check to avoid the unhandled exception + if ( Campaign.missions[mission_num].goals ) { + Campaign.missions[mission_num].goals[i].status = status; + } + } + + // now the events + GET_DATA( num_events ); + // need to malloc out the data + Assert( Campaign.missions[mission_num].num_events == 0 ); + Campaign.missions[mission_num].num_events = num_events; + if ( num_events > 0 ){ + Campaign.missions[mission_num].events = (mevent *)malloc( sizeof(mevent) * num_events ); + } + + for ( i = 0; i < num_events; i++ ) { + GET_DATA(status); + Campaign.missions[mission_num].events[i].status = status; + } + break; + + case MC_JIP_GOAL_NAMES: + GET_DATA( mission_num ); + GET_DATA( num_goals ); + GET_DATA( starting_num ); + for ( i = starting_num; i < (starting_num + num_goals); i++ ) { + GET_STRING(Campaign.missions[mission_num].goals[i].name); + } + break; + + case MC_JIP_EVENT_NAMES: + GET_DATA( mission_num ); + GET_DATA( num_events ); + GET_DATA( starting_num ); + for ( i = starting_num; i < (starting_num + num_events); i++ ) { + GET_STRING(Campaign.missions[mission_num].events[i].name); + } + break; + + case MC_JIP_END_DATA: + Net_player->state = NETPLAYER_STATE_INGAME_CINFO; + send_netplayer_update_packet(); + break; + } + + PACKET_SET_SIZE(); +} diff --git a/src/network/multi_data.cpp b/src/network/multi_data.cpp new file mode 100644 index 0000000..3236251 --- /dev/null +++ b/src/network/multi_data.cpp @@ -0,0 +1,526 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_data.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 8 6/16/99 4:06p Dave + * New pilot info popup. Added new draw-bitmap-as-poly function. + * + * 7 1/21/99 2:06p Dave + * Final checkin for multiplayer testing. + * + * 6 1/14/99 6:06p Dave + * 100% full squad logo support for single player and multiplayer. + * + * 5 12/18/98 12:31p Johnson + * Fixed a bug where the server would not properly redistribute a data + * file that he already had to other players. + * + * 4 12/14/98 4:01p Dave + * Got multi_data stuff working well with new xfer stuff. + * + * 3 12/14/98 12:13p Dave + * Spiffed up xfer system a bit. Put in support for squad logo file xfer. + * Need to test now. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 19 6/13/98 3:18p Hoffoss + * NOX()ed out a bunch of strings that shouldn't be translated. + * + * 18 5/21/98 3:45a Sandeep + * Make sure file xfer sender side uses correct directory type. + * + * 17 5/17/98 6:32p Dave + * Make sure clients/servers aren't kicked out of the debriefing when team + * captains leave a game. Fixed chatbox off-by-one error. Fixed image + * xfer/pilot info popup stuff. + * + * 16 5/13/98 6:54p Dave + * More sophistication to PXO interface. Changed respawn checking so + * there's no window for desynchronization between the server and the + * clients. + * + * 15 4/30/98 4:53p John + * Restructured and cleaned up cfile code. Added capability to read off + * of CD-ROM drive and out of multiple pack files. + * + * 14 4/21/98 4:44p Dave + * Implement Vasudan ships in multiplayer. Added a debug function to bash + * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui + * problem in options screen. + * + * 13 4/16/98 1:55p Dave + * Removed unneeded Assert when processing chat packets. Fixed standalone + * sequencing bugs. Laid groundwork for join screen server status + * icons/text. + * + * 12 4/14/98 12:19p Dave + * Revised the pause system yet again. Seperated into its own module. + * + * 11 4/08/98 2:51p Dave + * Fixed pilot image xfer once again. Solidify team selection process in + * pre-briefing multiplayer. + * + * 10 4/04/98 4:22p Dave + * First rev of UDP reliable sockets is done. Seems to work well if not + * overly burdened. + * + * 9 3/30/98 6:27p Dave + * Put in a more official set of multiplayer options, including a system + * for distributing netplayer and netgame settings. + * + * 8 3/26/98 6:01p Dave + * Put in file checksumming routine in cfile. Made pilot pic xferring more + * robust. Cut header size of voice data packets in half. Put in + * restricted game host query system. + * + * 7 3/23/98 5:42p Dave + * Put in automatic xfer of pilot pic files. Changed multi_xfer system so + * that it can support multiplayer sends/received between client and + * server simultaneously. + * + * 6 3/21/98 7:14p Dave + * Fixed up standalone player slot switching. Made training missions not + * count towards player stats. + * + * 5 3/15/98 4:17p Dave + * Fixed oberver hud problems. Put in handy netplayer macros. Reduced size + * of network orientation matrices. + * + * 4 3/11/98 2:04p Dave + * Removed optimized build warnings. + * + * 3 2/20/98 4:43p Dave + * Finished support for multiplayer player data files. Split off + * multiplayer campaign functionality. + * + * 2 2/19/98 6:44p Dave + * Finished server getting data from players. Now need to rebroadcast the + * data. + * + * 1 2/19/98 6:21p Dave + * Player data file xfer module. + * + * $NoKeywords: $ + */ + +#include "bmpman.h" +#include "psnet.h" +#include "multi_data.h" +#include "freespace.h" +#include "multi_xfer.h" +#include "multi.h" +#include "multimsgs.h" +#include "multiutil.h" +#include "player.h" + +// ------------------------------------------------------------------------- +// MULTI DATA DEFINES/VARS +// + +#define MAX_MULTI_DATA 40 + +// player data struct +typedef struct np_data { + char filename[MAX_FILENAME_LEN+1]; // filename + ubyte status[MAX_PLAYERS]; // status for all players (0 == don't have it, 1 == have or sending, 2 == i sent it originally) + ushort player_id; // id of the player who sent the file + ubyte used; // in use or not +} np_data; + +np_data Multi_data[MAX_MULTI_DATA]; + +time_t Multi_data_time = 0; + + +// ------------------------------------------------------------------------- +// MULTI DATA FORWARD DECLARATIONS +// + +// is the given filename for a "multi data" file (pcx, wav, etc) +int multi_data_is_data(char *filename); + +// get a free np_data slot +int multi_data_get_free(); + +// server side - add a new file +int multi_data_add_new(char *filename, int player_index); + +// maybe try and reload player squad logo bitmaps +void multi_data_maybe_reload(); + + +// ------------------------------------------------------------------------- +// MULTI DATA FUNCTIONS +// + +// reset the data xfer system +void multi_data_reset() +{ + int idx; + + // clear out all used bits + for(idx=0; idxflags & NETINFO_FLAG_AM_MASTER)){ + // for all valid files + for(idx=0; idxflags & NETINFO_FLAG_AM_MASTER) && (player_index >= 0)){ + multi_data_add_new(fname, player_index); + } + return; + } + + // if I'm the server of the game, do stuff a little differently + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + if(player_index == -1){ + nprintf(("Network", "Could not find player for incoming player data stream!\n")); + + // kill the stream + multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT); + return; + } + + // attempt to add the file + if(!multi_data_add_new(fname, player_index)){ + // kill the stream + multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT); + return; + } else { + // force the file to go into the multi cache directory + multi_xfer_handle_force_dir(handle, CF_TYPE_MULTI_CACHE); + + // mark it as autodestroy + multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_AUTODESTROY); + } + } + // if i'm a client, this is an incoming file from the server + else { + // if i'm not accepting pilot pics, abort + if(!(Net_player->p_info.options.flags & MLO_FLAG_ACCEPT_PIX)){ + nprintf(("Network", "Client not accepting files because we don't want 'em!\n")); + + // kill the stream + multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT); + return; + } + + // set the xfer handle to autodestroy itself since we don't really want to have to manage all incoming pics + multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_AUTODESTROY); + + // force the file to go into the multi cache directory + multi_xfer_handle_force_dir(handle, CF_TYPE_MULTI_CACHE); + + // begin receiving the file + nprintf(("Network", "Client receiving xfer handle %d\n",handle)); + } +} + +// send all my files as necessary +void multi_data_send_my_junk() +{ + char *with_ext; + int bmap, w, h; + int ok_to_send = 1; + + // pilot pic -------------------------------------------------------------- + + // verify that my pilot pic is valid + if(strlen(Net_player->player->image_filename)){ + bmap = bm_load(Net_player->player->image_filename); + if(bmap == -1){ + ok_to_send = 0; + } + + // verify image dimensions + if(ok_to_send){ + w = -1; + h = -1; + bm_get_info(bmap,&w,&h); + + // release the bitmap + bm_release(bmap); + bmap = -1; + + // if the dimensions are invalid, kill the filename + if((w != PLAYER_PILOT_PIC_W) || (h != PLAYER_PILOT_PIC_H)){ + ok_to_send = 0; + } + } + } else { + ok_to_send = 0; + } + + if(ok_to_send){ + with_ext = cf_add_ext(Net_player->player->image_filename, NOX(".pcx")); + if(with_ext != NULL){ + strcpy(Net_player->player->image_filename, with_ext); + } + + // host should put his own pic file in the list now + if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Game_mode & GM_STANDALONE_SERVER) && (strlen(Net_player->player->image_filename) > 0)){ + multi_data_add_new(Net_player->player->image_filename, MY_NET_PLAYER_NUM); + } + // otherwise clients should just queue up a send + else { + // add a file extension if necessary + multi_xfer_send_file(Net_player->reliable_socket, Net_player->player->image_filename, CF_TYPE_ANY, MULTI_XFER_FLAG_AUTODESTROY | MULTI_XFER_FLAG_QUEUE); + } + } + + + // squad logo -------------------------------------------------------------- + + // verify that my pilot pic is valid + ok_to_send = 1; + if(strlen(Net_player->player->squad_filename)){ + bmap = bm_load(Net_player->player->squad_filename); + if(bmap == -1){ + ok_to_send = 0; + } + + if(ok_to_send){ + // verify image dimensions + w = -1; + h = -1; + bm_get_info(bmap,&w,&h); + + // release the bitmap + bm_release(bmap); + bmap = -1; + + // if the dimensions are invalid, kill the filename + if((w != PLAYER_SQUAD_PIC_W) || (h != PLAYER_SQUAD_PIC_H)){ + ok_to_send = 0; + } + } + } else { + ok_to_send = 0; + } + + if(ok_to_send){ + with_ext = cf_add_ext(Net_player->player->squad_filename, NOX(".pcx")); + if(with_ext != NULL){ + strcpy(Net_player->player->squad_filename,with_ext); + } + + // host should put his own pic file in the list now + if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && !(Game_mode & GM_STANDALONE_SERVER) && (strlen(Net_player->player->squad_filename) > 0)){ + multi_data_add_new(Net_player->player->squad_filename, MY_NET_PLAYER_NUM); + } + // otherwise clients should just queue up a send + else { + // add a file extension if necessary + multi_xfer_send_file(Net_player->reliable_socket, Net_player->player->squad_filename, CF_TYPE_ANY, MULTI_XFER_FLAG_AUTODESTROY | MULTI_XFER_FLAG_QUEUE); + } + } +} + + +// ------------------------------------------------------------------------- +// MULTI DATA FORWARD DECLARATIONS +// + +// is the give file xfer handle for a "multi data" file (pcx, wav, etc) +int multi_data_is_data(char *filename) +{ + int len,idx; + + Assert(filename != NULL); + + // some kind of error + if(filename == NULL){ + return 0; + } + + // convert to lowercase + len = strlen(filename); + for(idx=0;idxsquad_filename) && (Net_players[idx].player->insignia_texture == -1)){ + Net_players[idx].player->insignia_texture = bm_load_duplicate(Net_players[idx].player->squad_filename); + + // if the bitmap loaded properly, lock it as a transparent texture + if(Net_players[idx].player->insignia_texture != -1){ + bm_lock(Net_players[idx].player->insignia_texture, 16, BMP_TEX_XPARENT); + bm_unlock(Net_players[idx].player->insignia_texture); + } + } + } +} \ No newline at end of file diff --git a/src/network/multi_dogfight.cpp b/src/network/multi_dogfight.cpp new file mode 100644 index 0000000..36ca8e1 --- /dev/null +++ b/src/network/multi_dogfight.cpp @@ -0,0 +1,534 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_dogfight.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 11 8/18/99 11:31a Jefff + * mission title on kill matrix + * + * 10 8/17/99 9:55a Dave + * Fixed dogfight scoring problem. + * + * 9 8/06/99 12:29a Dave + * Multiple bug fixes. + * + * 8 5/03/99 8:32p Dave + * New version of multi host options screen. + * + * 7 4/12/99 10:07p Dave + * Made network startup more forgiving. Added checkmarks to dogfight + * screen for players who hit commit. + * + * 6 4/12/99 2:22p Dave + * More checks for dogfight stats. + * + * 5 2/24/99 3:40p Dave + * Fixed problem where ships were becoming traitor all the time. + * + * 4 2/24/99 2:25p Dave + * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn + * bug for dogfight more. + * + * 3 2/23/99 8:11p Dave + * Tidied up dogfight mode. Fixed TvT ship type problems for alpha wing. + * Small pass over todolist items. + * + * 2 2/23/99 2:29p Dave + * First run of oldschool dogfight mode. + * + * $NoKeywords: $ + */ + +#include "multi.h" +#include "multiutil.h" +#include "multi_log.h" +#include "bmpman.h" +#include "object.h" +#include "systemvars.h" +#include "freespace.h" +#include "key.h" +#include "missionscreencommon.h" +#include "eventmusic.h" +#include "gamesnd.h" +#include "multiui.h" +#include "chatbox.h" +#include "ui.h" +#include "font.h" +#include "multi_dogfight.h" +#include "alphacolors.h" + +// ---------------------------------------------------------------------------------------------------- +// MULTI DOGFIGHT DEFINES/VARS +// + +// interface stuff +UI_WINDOW Multi_df_window; + +#define NUM_MULTI_DF_BUTTONS 1 +#define ACCEPT_BUTTON 0 + +ui_button_info Multi_df_buttons[GR_NUM_RESOLUTIONS][NUM_MULTI_DF_BUTTONS] = { + { // GR_640 + // accept + ui_button_info("CB_05a", 571, 425, 578, 413, 5), + }, + { // GR_1024 + // accept + ui_button_info("2_CB_05a", 914, 681, 914, 660, 5), + } +}; + +int Multi_df_background_bitmap = -1; +char *Multi_df_background_fname[GR_NUM_RESOLUTIONS] = { + "KillMatrix", + "2_KillMatrix" +}; +char *Multi_df_mask_fname[GR_NUM_RESOLUTIONS] = { + "KillMatrix-m", + "2_KillMatrix-m" +}; + +// coord 3 is max width +static int Kill_matrix_title_coords[GR_NUM_RESOLUTIONS][3] = { + { // GR_640 + 19, 118, 172 + }, + { // GR_1024 + 33, 194, 272 + } +}; + +// display area coords +int Multi_df_display_coords[GR_NUM_RESOLUTIONS][4] = { + { // GR_640 + 43, 133, 569, 269 + }, + { // GR_1024 + 60, 213, 919, 429 + } +}; + +#define MULTI_DF_TOTAL_ADJUST 5 + +// "check" icon coords +int Multi_df_check_coords[GR_NUM_RESOLUTIONS] = { + // GR_640 + 28, + + // GR_1024 + 45 +}; + +// players when the screen started - we need to store this explicity so that even after players leave, we can display the full kill matrix +typedef struct multi_df_score { + char callsign[CALLSIGN_LEN+1]; // callsign for this guy + scoring_struct stats; // stats for the guy + int np_index; // absolute index into the netplayers array +} multi_df_score; +multi_df_score Multi_df_score[MAX_PLAYERS]; +int Multi_df_score_count = 0; + + +// ---------------------------------------------------------------------------------------------------- +// MULTI DOGFIGHT FORWARD DECLARATIONS +// + +// process button presses +void multi_df_process_buttons(); + +// button was pressed +void multi_df_button_pressed(int button); + +// setup kill matrix data +void multi_df_setup_kill_matrix(); + +// blit the kill matrix +void multi_df_blit_kill_matrix(); + +// stuff a string representing the # of kills, player X had on player Y (where X and Y are indices into Multi_df_score) +// returns the # of kills +int multi_df_stuff_kills(char *kills, int player_x, int player_y); + + +// ---------------------------------------------------------------------------------------------------- +// MULTI DOGFIGHT FUNCTIONS +// + +// call once per level just before entering the mission +void multi_df_level_pre_enter() +{ + int idx; + + // if we're not in dogfight mode, do nothing + if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){ + return; + } + + // go through all player ships and make them hostile + for(idx=0; idxobjnum >= 0) && (Objects[Net_players[idx].player->objnum].type == OBJ_SHIP)){ + Ships[Objects[Net_players[idx].player->objnum].instance].team = TEAM_TRAITOR; + } + } + + // +} + +// evaluate a kill in dogfight by a netplayer +void multi_df_eval_kill(net_player *killer, object *dead_obj) +{ + int dead_index = -1; + + // if we're not in dogfight mode, do nothing + if(!(Netgame.type_flags & NG_TYPE_DOGFIGHT)){ + return; + } + + // sanity checks + if((killer == NULL) || (dead_obj == NULL) || (killer->player == NULL)){ + return; + } + + // try and find the dead player + dead_index = multi_find_player_by_object(dead_obj); + if(dead_index < 0){ + return; + } + Assert(dead_index < MAX_PLAYERS); + if(dead_index == NET_PLAYER_INDEX(killer)){ + return; + } + + // update his kills + killer->player->stats.m_dogfight_kills[dead_index]++; +} + +// debrief +void multi_df_debrief_init() +{ + // no longer is mission + Game_mode &= ~(GM_IN_MISSION); + game_flush(); + + // call scoring level close for my stats. Needed for award_init. The stats will + // be backed out if used chooses to replace them. + scoring_level_close(); + + // multiplayer debriefing stuff + multi_debrief_init(); + + // close down any old instances of the chatbox + chatbox_close(); + + // create the new one + chatbox_create(); + + // always play success music + common_music_init(SCORE_DEBRIEF_SUCCESS); + + // setup kill matrix + multi_df_setup_kill_matrix(); + + UI_WINDOW *w; + ui_button_info *b; + int idx; + + // load background bitmap + Multi_df_background_bitmap = bm_load(Multi_df_background_fname[gr_screen.res]); + Assert(Multi_df_background_bitmap); + + // create the UI window + Multi_df_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0); + Multi_df_window.set_mask_bmap(Multi_df_mask_fname[gr_screen.res]); + + // initialize the control buttons + for (idx=0; idxbutton.create(&Multi_df_window, NULL, b->x, b->y, 60, 30, 1, 1); + + // set its highlight action + b->button.set_highlight_action(common_play_highlight_sound); + + // set its animation bitmaps + b->button.set_bmaps(b->filename); + + // link the mask hotspot + b->button.link_hotspot(b->hotspot); + } + + // add some text + w = &Multi_df_window; + w->add_XSTR("Accept", 1035, Multi_df_buttons[gr_screen.res][ACCEPT_BUTTON].xt, Multi_df_buttons[gr_screen.res][ACCEPT_BUTTON].yt, &Multi_df_buttons[gr_screen.res][ACCEPT_BUTTON].button, UI_XSTR_COLOR_PINK); +} + +// do frame +void multi_df_debrief_do() +{ + int k, new_k; + char buf[256]; + + k = chatbox_process(); + new_k = Multi_df_window.process(k, 0); + + // process keypresses + switch(new_k){ + case KEY_ESC: + multi_debrief_esc_hit(); + break; + } + + // process buttons + multi_df_process_buttons(); + + // music stuff + common_music_do(); + + // process debriefing details + multi_debrief_do_frame(); + + // draw the background + GR_MAYBE_CLEAR_RES(Multi_df_background_bitmap); + if (Multi_df_background_bitmap >= 0) { + gr_set_bitmap(Multi_df_background_bitmap); + gr_bitmap(0, 0); + } + + // draw the window + Multi_df_window.draw(); + + // kill matrix + multi_df_blit_kill_matrix(); + + // render the chatbox + chatbox_render(); + + // draw the mission title + strcpy(buf, The_mission.name); + gr_force_fit_string(buf, 255, Kill_matrix_title_coords[gr_screen.res][2]); + gr_set_color_fast(&Color_bright_white); + gr_string(Kill_matrix_title_coords[gr_screen.res][0], Kill_matrix_title_coords[gr_screen.res][1], buf); + + // flip + gr_flip(); +} + +// close +void multi_df_debrief_close() +{ + int idx; + + // shutdown the chatbox + chatbox_close(); + + // if stats weren't accepted, backout my own stats + if (multi_debrief_stats_accept_code() != 1) { + // if stats weren't accepted, backout my own stats + if (multi_debrief_stats_accept_code() != 1) { + if(MULTIPLAYER_MASTER){ + for(idx=0; idxstats); + } + } + } else { + scoring_backout_accept( &Player->stats ); + } + } + } + + // music stuff + common_music_close(); +} + + +// ---------------------------------------------------------------------------------------------------- +// MULTI DOGFIGHT FORWARD DEFINITIONS +// + +// process button presses +void multi_df_process_buttons() +{ + int idx; + + for(idx=0; idxcallsign); + for(s_idx=0; s_idxstats.m_dogfight_kills[s_idx]); + } + + s->stats = Net_players[idx].player->stats; + strcpy(s->callsign, Net_players[idx].player->callsign); + s->np_index = idx; + } + } +} + +// blit the kill matrix +void multi_df_blit_kill_matrix() +{ + int idx, s_idx, str_len; + int cx, cy; + char squashed_string[CALLSIGN_LEN+1] = ""; + + // max width of an individual item, and the text that can be in that item + float max_item_width = ((float)Multi_df_display_coords[gr_screen.res][2] - 40.0f) / (float)(Multi_df_score_count + 1); + float max_text_width = max_item_width * 0.8f; + + // start x for the top bar (one item to the right) + int top_x_start = Multi_df_display_coords[gr_screen.res][0] + (int)max_item_width; + int top_y_start = Multi_df_display_coords[gr_screen.res][1]; + + // start x for the side bar + int side_x_start = Multi_df_display_coords[gr_screen.res][0]; + int side_y_start = Multi_df_display_coords[gr_screen.res][1] + 10; + + // draw the top bar + cx = top_x_start; + cy = top_y_start; + for(idx=0; idx= 0); + if(Multi_df_score[idx].np_index >= 0){ + gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); + } + gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string); + + // next spot + cx += (int)max_item_width; + } + + // draw the rest of the scoreboard + cx = side_x_start; + cy = side_y_start; + int row_total; + for(idx=0; idx= 0); + if(Multi_df_score[idx].np_index >= 0){ + gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); + } + gr_string(cx, cy, squashed_string); + + cx = top_x_start; + row_total = 0; + for(s_idx=0; s_idx= 0); + if(Multi_df_score[idx].np_index >= 0){ + gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); + } + } + + // draw the string + gr_force_fit_string(squashed_string, CALLSIGN_LEN, (int)max_text_width); + gr_get_string_size(&str_len, NULL, squashed_string); + gr_string(cx + (int)((max_item_width - (float)str_len)/2.0f), cy, squashed_string); + + // next spot + cx += (int)max_item_width; + } + + // draw the row total + gr_set_color_fast(Color_netplayer[Multi_df_score[idx].np_index]); + sprintf(squashed_string, "(%d)", row_total); + gr_get_string_size(&str_len, NULL, squashed_string); + gr_string(Multi_df_display_coords[gr_screen.res][0] + Multi_df_display_coords[gr_screen.res][2] - (MULTI_DF_TOTAL_ADJUST + str_len), cy, squashed_string); + + cy += 10; + } + + /* + // blit totals + int column_total; + cx = top_x_start; + cy += 3; + for(idx=0; idxstats.m_dogfight_kills[Multi_df_score[player_y].np_index]); + return s->stats.m_dogfight_kills[Multi_df_score[player_y].np_index]; +} \ No newline at end of file diff --git a/src/network/multi_endgame.cpp b/src/network/multi_endgame.cpp new file mode 100644 index 0000000..592430b --- /dev/null +++ b/src/network/multi_endgame.cpp @@ -0,0 +1,739 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_endgame.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 10 8/22/99 1:55p Dave + * Cleaned up host/team-captain leaving code. + * + * 9 8/22/99 1:19p Dave + * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in + * which d3d cards are detected. + * + * 8 4/08/99 1:05p Dave + * Fixed some leave game packet problems. Updated builtin mission list for + * multiplayer. + * + * 7 3/24/99 4:05p Dave + * Put in support for assigning the player to a specific squadron with a + * specific logo. Preliminary work for doing pos/orient checksumming in + * multiplayer to reduce bandwidth. + * + * 6 11/19/98 4:57p Dave + * Ignore PXO option if IPX is selected. + * + * 5 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 4 11/19/98 8:03a Dave + * Full support for D3-style reliable sockets. Revamped packet lag/loss + * system, made it receiver side and at the lowest possible level. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 36 9/17/98 3:08p Dave + * PXO to non-pxo game warning popup. Player icon stuff in create and join + * game screens. Upped server count refresh time in PXO to 35 secs (from + * 20). + * + * 35 9/15/98 7:24p Dave + * Minor UI changes. Localized bunch of new text. + * + * 34 9/14/98 3:40p Allender + * better error checking for invalid number of waves for player wings in a + * multiplayer game. Better popup message in FreeSpace side. + * + * 33 9/11/98 5:53p Dave + * Final revisions to kick system changes. + * + * 32 9/11/98 5:08p Dave + * More tweaks to kick notification system. + * + * 31 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 30 8/07/98 10:15a Allender + * changed the way the endgame sequencing timer works so that timer wrap + * doesn't hurt. Also use the obj_set_flags for the COULD_BE_PLAYER flag + * + * 29 7/24/98 9:27a Dave + * Tidied up endgame sequencing by removing several old flags and + * standardizing _all_ endgame stuff with a single function call. + * + * 28 7/13/98 5:34p Lawrance + * index a localized string in multi_endgame.cpp + * + * 27 7/13/98 5:19p Dave + * + * 26 6/30/98 4:53p Allender + * fixed endgame problems where standalone wasn't properly dropping + * everyone out of a game. Be sure that timestamp for endgame processing + * gets reset + * + * 25 6/13/98 9:32p Mike + * Kill last character in file which caused "Find in Files" to report the + * file as "not a text file." + * + * 24 6/13/98 6:01p Hoffoss + * Externalized all new (or forgot to be added) strings to all the code. + * + * 23 6/13/98 3:18p Hoffoss + * NOX()ed out a bunch of strings that shouldn't be translated. + * + * 22 5/24/98 8:15p Dave + * Tweaked pxo some more. + * + * 21 5/21/98 10:09a Dave + * Enable DNS checking for PXO and both trackers. + * Fixed problem with leaving the mission from the pause state. Fixed + * build errors in multimsgs.cpp + * + * 20 5/20/98 9:01p Allender + * change RELEASE to NDEBUG. Fix reentryancy problem in process_endgame + * for multiplayer + * + * 19 5/17/98 11:54p Allender + * only clear flying controls when in mission + * + * 18 5/17/98 11:34p Allender + * deal with resetting player controls better + * + * 17 5/17/98 6:32p Dave + * Make sure clients/servers aren't kicked out of the debriefing when team + * captains leave a game. Fixed chatbox off-by-one error. Fixed image + * xfer/pilot info popup stuff. + * + * 16 5/15/98 5:15p Dave + * Fix a standalone resetting bug.Tweaked PXO interface. Display captaincy + * status for team vs. team. Put in asserts to check for invalid team vs. + * team situations. + * + * 15 5/15/98 12:09a Dave + * New tracker api code. New game tracker code. Finished up first run of + * the PXO screen. Fixed a few game server list exceptions. + * + * 14 5/14/98 12:40a Dave + * Still more additions to the PXO screen. Updated tracker code. + * + * 13 5/09/98 7:16p Dave + * Put in CD checking. Put in standalone host password. Made pilot into + * popup scrollable. + * + * 12 5/08/98 5:05p Dave + * Go to the join game screen when quitting multiplayer. Fixed mission + * text chat bugs. Put mission type symbols on the create game list. + * Started updating standalone gui controls. + * + * 11 5/07/98 6:26p Dave + * Fix strange boundary conditions which arise when players die/respawn + * while the game is being ended. Spiff up the chatbox doskey thing a bit. + * + * 10 5/07/98 3:29p Jim + * Make sure standalone doesn't display a popup when ending a game. + * + * 9 5/05/98 5:02p Dave + * Fix end-of-campaign sequencing to work right. Make all individual + * missions of a campaign replayable. + * + * 8 5/04/98 10:39p Dave + * Put in endgame sequencing. Need to check campaign situations. + * Realigned ship info on team select screen. + * + * 7 5/04/98 1:43p Dave + * Fixed up a standalone resetting problem. Fixed multiplayer stats + * collection for clients. Make sure all multiplayer ui screens have the + * correct palette at all times. + * + * 6 5/03/98 7:04p Dave + * Make team vs. team work mores smoothly with standalone. Change how host + * interacts with standalone for picking missions. Put in a time limit for + * ingame join ship select. Fix ingame join ship select screen for Vasudan + * ship icons. + * + * 5 4/30/98 5:12p Dave + * Fixed game polling code for joining clients. Reworked some file xfer + * stuff. + * + * 4 4/30/98 12:13a Allender + * reset control info and afterburner when entering the quit game code. + * Prevents odd things from happening while the popup is up. + * + * 3 4/28/98 5:10p Dave + * Fixed multi_quit_game() client side sequencing problem. Turn off + * afterburners when ending multiplayer mission. Begin integration of mt + * API from Kevin Bentley. + * + * 2 4/23/98 1:28a Dave + * Seemingly nailed the current_primary_bank and current_secondary_bank -1 + * problem. Made sure non-critical button presses are _never_ sent to the + * server. + * + * 1 4/22/98 5:50p Dave + * + * + * $NoKeywords: $ + */ + +// #include +#include "multi.h" +#include "popup.h" +#include "object.h" +#include "freespace.h" +#include "gamesequence.h" +#include "chatbox.h" +#include "popupdead.h" +#include "multi_endgame.h" +#include "multimsgs.h" +#include "multiui.h" +#include "multiutil.h" +#include "multi_pmsg.h" + + +// ---------------------------------------------------------------------------------------------------------- +// Put all functions/data related to leaving a netgame, handling players leaving, handling the server leaving, +// and notifying the user of all of these actions, here. +// + + +// ---------------------------------------------------------------------------------------------------------- +// MULTI ENDGAME DEFINES/VARS +// + + +// set when the server/client has ended the game on some notification or error and is waiting for clients to leave +#define MULTI_ENDGAME_SERVER_WAIT 5.0f +int Multi_endgame_server_waiting = 0; +float Multi_endgame_server_wait_stamp = -1.0f; +int Multi_endgame_client_waiting = 0; + +// error/notification codes (taken from parameters to multi_quit_game(...) +int Multi_endgame_notify_code; +int Multi_endgame_error_code; +int Multi_endgame_wsa_error; + +// for reentrancy problems on standalone +int Multi_endgame_processing; + +// ---------------------------------------------------------------------------------------------------------- +// MULTI ENDGAME FORWARD DECLARATIONS +// + +// called when a given netgame is about to end completely +void multi_endgame_cleanup(); + +// throw up a popup with the given notification code and optional winsock code +void multi_endgame_popup(int notify_code,int error_code,int wsa_error = -1); + +// called when server is waiting for clients to disconnect +int multi_endgame_server_ok_to_leave(); + +// check to see if we need to be warping out (strange transition possibilities) +void multi_endgame_check_for_warpout(); + + +// ---------------------------------------------------------------------------------------------------------- +// MULTI ENDGAME FUNCTIONS +// + +// initialize the endgame processor (call when joining/starting a new netgame) +void multi_endgame_init() +{ + // set this so that the server/client knows he hasn't tried to end the game + Multi_endgame_server_waiting = 0; + Multi_endgame_client_waiting = 0; + + // reset the timestamp used when server is waiting for all other clients to leave. + Multi_endgame_server_wait_stamp = -1.0f; + + // initialiaze all endgame notify and error codes + Multi_endgame_notify_code = -1; + Multi_endgame_error_code = -1; + Multi_endgame_wsa_error = -1; + + // for reentrancy problems in to endgame_process + Multi_endgame_processing = 0; +} + +// process all endgame related events +void multi_endgame_process() +{ + if ( Multi_endgame_processing ) + return; + + Multi_endgame_processing = 1; + + // check to see if we need to be warping out (strange transition possibilities) + multi_endgame_check_for_warpout(); + + // if we're the server of the game + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + // if we're not waiting for clients to leave, do nothing + if(!Multi_endgame_server_waiting){ + Multi_endgame_processing = 0; + return; + } + + // if a popup is already active, do nothing + if(popup_active()){ + Multi_endgame_processing = 0; + return; + } + + // otherwise popup until things are hunky-dory + if(!multi_endgame_server_ok_to_leave()){ + if(Game_mode & GM_STANDALONE_SERVER){ + while(!multi_endgame_server_ok_to_leave()){ + // run networking, etc. + game_set_frametime(-1); + game_do_state_common(gameseq_get_state()); + } + } else { + popup_till_condition( multi_endgame_server_ok_to_leave , XSTR("&Cancel",645), XSTR("Waiting for clients to disconnect",646)); + } + } + + // mark myself as not waiting and get the hell out + multi_endgame_cleanup(); + } else { + // if we're not waiting to leave the game, do nothing + if(!Multi_endgame_client_waiting){ + Multi_endgame_processing = 0; + return; + } + + // otherwise, check to see if there is a popup active + if(popup_active()){ + Multi_endgame_processing = 0; + return; + } + + // if not, then we are good to leave + multi_endgame_cleanup(); + } + + Multi_endgame_processing = 0; +} + +// if the game has been flagged as ended (ie, its going to be reset) +int multi_endgame_ending() +{ + return (Multi_endgame_client_waiting || Multi_endgame_server_waiting); +} + +// reentrancy check +int Multi_quit_game = 0; +// general quit function, with optional notification, error, and winsock error codes +int multi_quit_game(int prompt, int notify_code, int err_code, int wsa_error) +{ + int ret_val,quit_already; + + // check for reentrancy + if(Multi_quit_game){ + return 0; + } + + // if we're not connected or have not net-player + if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_CONNECTED)){ + return 1; + } + + // reentrancy + Multi_quit_game = 1; + + quit_already = 0; + + // reset my control info so that I don't continually do whacky stuff. This is ugly + //player_control_reset_ci( &Player->ci ); + if ( Game_mode & GM_IN_MISSION ) { + memset(&Player->ci, 0, sizeof(Player->ci) ); + Player->ci.afterburner_stop = 1; + physics_read_flying_controls( &Player_obj->orient, &Player_obj->phys_info, &(Player->ci), flFrametime); + } + + // CASE 1 - response to a user request + // if there is no associated notification or error code, don't override the prompt argument + if((err_code == -1) && (notify_code == -1)){ + // if we're the server and we're already waiting for clients to leave, don't do anything + if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && Multi_endgame_server_waiting){ + Multi_quit_game = 0; + return 0; + } + + // if we're the client and we're already waiting to leave, don't do anythin + if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER) && Multi_endgame_client_waiting){ + Multi_quit_game = 0; + return 0; + } + + // see if we should be prompting the host for confirmation + if((prompt==PROMPT_HOST || prompt==PROMPT_ALL) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){ + int p_flags; + + p_flags = PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_BODY_BIG; + if ( Game_mode & GM_IN_MISSION ) + p_flags |= PF_RUN_STATE; + + ret_val = popup(p_flags,2,POPUP_CANCEL,POPUP_OK,XSTR("Warning - quitting will end the game for all players!",647)); + + // check for host cancel + if((ret_val == 0) || (ret_val == -1)){ + Multi_quit_game = 0; + return 0; + } + + // set this so that under certain circumstances, we don't call the popup below us as well + quit_already = 1; + } + + // see if we should be prompting the client for confirmation + if((prompt==PROMPT_CLIENT || prompt==PROMPT_ALL) && !quit_already){ + ret_val = popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_BODY_BIG,2,POPUP_NO,POPUP_YES,XSTR("Are you sure you want to quit?",648)); + + // check for host cancel + if((ret_val == 0) || (ret_val == -1)){ + Multi_quit_game = 0; + return 0; + } + quit_already = 1; + } + + // if i'm the server of the game, tell all clients that i'm leaving, then wait + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + send_netgame_end_error_packet(MULTI_END_NOTIFY_SERVER_LEFT,MULTI_END_ERROR_NONE); + + // set the waiting flag and the waiting timestamp + Multi_endgame_server_waiting = 1; + Multi_endgame_server_wait_stamp = MULTI_ENDGAME_SERVER_WAIT; + } + // if i'm the client, quit now + else { + multi_endgame_cleanup(); + } + } + // CASE 2 - response to an error code or packet from the server + // this is the case where we're being forced to quit the game because of some error or other notification + else { + // if i'm the server, send a packet to the clients telling them that I'm leaving and why + if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && !Multi_endgame_server_waiting){ + // if we're in the debrief state, mark down that the server has left the game + if(((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) && !(Game_mode & GM_STANDALONE_SERVER)){ + multi_debrief_server_left(); + + // add a message to the chatbox + multi_display_chat_msg(XSTR("",649),0,0); + + // set ourselves to be "not quitting" + Multi_quit_game = 0; + + // tell the users, the game has ended + send_netgame_end_error_packet(notify_code,err_code); + return 0; + } + + send_netgame_end_error_packet(notify_code,err_code); + + // store the globals + Multi_endgame_notify_code = notify_code; + Multi_endgame_error_code = err_code; + Multi_endgame_wsa_error = wsa_error; + + // by setting this, multi_endgame_process() will know to check and see if it is ok for us to leave + Multi_endgame_server_waiting = 1; + Multi_endgame_server_wait_stamp = MULTI_ENDGAME_SERVER_WAIT; + } + // if i'm the client, set the error codes and leave the game now + else if(!Multi_endgame_client_waiting){ + // if we're in the debrief state, mark down that the server has left the game + if((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){ + multi_debrief_server_left(); + + // add a message to the chatbox + multi_display_chat_msg(XSTR("",650),0,0); + + // shut our reliable socket to the server down + psnet_rel_close_socket(&Net_player->reliable_socket); + Net_player->reliable_socket = INVALID_SOCKET; + + // remove our do-notworking flag + Net_player->flags &= ~(NETINFO_FLAG_DO_NETWORKING); + + Multi_quit_game = 0; + return 0; + } + + Multi_endgame_notify_code = notify_code; + Multi_endgame_error_code = err_code; + Multi_endgame_wsa_error = wsa_error; + + // by setting this, multi_endgame_process() will know to check and see if it is ok for us to leave + Multi_endgame_client_waiting = 1; + } + } + + // unset the reentrancy flag + Multi_quit_game = 0; + + return 1; +} + + +// ---------------------------------------------------------------------------------------------------------- +// MULTI ENDGAME FORWARD DEFINITIONS +// + +// called when a given netgame is about to end completely +void multi_endgame_cleanup() +{ + int idx; + + send_leave_game_packet(); + + // flush all outgoing io, force all packets through + multi_io_send_buffered_packets(); + + // mark myself as disconnected + if(!(Game_mode & GM_STANDALONE_SERVER)){ + Net_player->flags &= ~(NETINFO_FLAG_CONNECTED|NETINFO_FLAG_DO_NETWORKING); + } + + // this is a semi-hack so that if we're the master and we're quitting, we don't get an assert + if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Player_obj != NULL)){ + Player_obj->flags &= ~(OF_PLAYER_SHIP); + obj_set_flags( Player_obj, Player_obj->flags | OF_COULD_BE_PLAYER ); + } + + // shut my socket down (will also let the server know i've received any notifications/error from him) + // psnet_rel_close_socket( &(Net_player->reliable_socket) ); + + // 11/18/98 - DB, changed the above to kill all sockets. Its the safest thing to do + for(idx=0; idxflags & NETINFO_FLAG_AM_MASTER){ + int idx; + // do it for all players, since we're leaving anyway. + for(idx=0;idxflags |= PLAYER_FLAGS_IS_MULTI; + + // if we're in Parallax Online mode, log back in there + gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME); + + // if we have an error code, bring up the discon popup + if((Multi_endgame_notify_code != -1) || (Multi_endgame_error_code != -1) && !(Game_mode & GM_STANDALONE_SERVER)){ + multi_endgame_popup(Multi_endgame_notify_code,Multi_endgame_error_code,Multi_endgame_wsa_error); + } + } + + /* + extern CFILE *obj_stream; + if(obj_stream != NULL){ + cfclose(obj_stream); + obj_stream = NULL; + } + */ + + // unload the multiplayer common interface palette + multi_common_unload_palette(); + + // reinitialize endgame stuff + // multi_endgame_init(); +} + +// throw up a popup with the given notification code and optional winsock code +void multi_endgame_popup(int notify_code,int error_code,int wsa_error) +{ + char err_msg[255]; + int flags = PF_USE_AFFIRMATIVE_ICON; + + // if there is a popup already active, just kill it + if(popup_active()){ + // if there is already a popup active, kill it + popup_kill_any_active(); + + Int3(); + } else { + // if there is a winsock error code, stick it on the end of the text + if(wsa_error != -1){ + sprintf(err_msg,NOX("WSAERROR : %d\n\n"),wsa_error); + flags |= PF_TITLE_RED; + } else { + strcpy(err_msg,""); + } + + // setup the error message string + if(notify_code != MULTI_END_NOTIFY_NONE){ + switch(notify_code){ + case MULTI_END_NOTIFY_KICKED : + strcat(err_msg,XSTR("You have been kicked",651)); + break; + case MULTI_END_NOTIFY_SERVER_LEFT: + strcat(err_msg,XSTR("The server has left the game",652)); + break; + case MULTI_END_NOTIFY_FILE_REJECTED: + strcat(err_msg,XSTR("Your mission file has been rejected by the server",653)); + break; + case MULTI_END_NOTIFY_EARLY_END: + strcat(err_msg,XSTR("The game has ended while you were ingame joining",654)); + break; + case MULTI_END_NOTIFY_INGAME_TIMEOUT: + strcat(err_msg,XSTR("You have waited too long to select a ship",655)); + break; + case MULTI_END_NOTIFY_KICKED_BAD_XFER: + strcat(err_msg,XSTR("You were kicked because mission file xfer failed",998)); + break; + case MULTI_END_NOTIFY_KICKED_CANT_XFER: + strcat(err_msg,XSTR("You were kicked because you do not have the builtin mission",999)); + strcat(err_msg, NOX(" ")); + strcat(err_msg, Game_current_mission_filename); + break; + case MULTI_END_NOTIFY_KICKED_INGAME_ENDED: + strcat(err_msg,XSTR("You were kicked because you were ingame joining a game that has ended",1000)); + break; + default : + Int3(); + } + } else { + switch(error_code){ + case MULTI_END_ERROR_CONTACT_LOST : + strcat(err_msg,XSTR("Contact with server has been lost",656)); + break; + case MULTI_END_ERROR_CONNECT_FAIL : + strcat(err_msg,XSTR("Failed to connect to server on reliable socket",657)); + break; + case MULTI_END_ERROR_LOAD_FAIL : + strcat(err_msg,XSTR("Failed to load mission file properly",658)); + break; + case MULTI_END_ERROR_INGAME_SHIP : + strcat(err_msg,XSTR("Unable to create ingame join player ship",659)); + break; + case MULTI_END_ERROR_INGAME_BOGUS : + strcat(err_msg,XSTR("Recevied bogus packet data while ingame joining",660)); + break; + case MULTI_END_ERROR_STRANS_FAIL : + strcat(err_msg,XSTR("Server transfer failed (obsolete)",661)); + break; + case MULTI_END_ERROR_SHIP_ASSIGN: + strcat(err_msg,XSTR("Server encountered errors trying to assign players to ships",662)); + break; + case MULTI_END_ERROR_HOST_LEFT: + strcat(err_msg,XSTR("Host has left the game, aborting...",663)); + break; + case MULTI_END_ERROR_XFER_FAIL: + strcat(err_msg,XSTR("There was an error receiving the mission file!",665)); + break; + case MULTI_END_ERROR_WAVE_COUNT: + strcat(err_msg,XSTR("The player wings Alpha, Beta, Gamma, and Zeta must have only 1 wave. One of these wings currently has more than 1 wave.", 987)); + break; + case MULTI_END_ERROR_TEAM0_EMPTY: + strcat(err_msg,XSTR("All players from team 1 have left the game", 1466)); + break; + case MULTI_END_ERROR_TEAM1_EMPTY: + strcat(err_msg,XSTR("All players from team 2 have left the game", 1467)); + break; + case MULTI_END_ERROR_CAPTAIN_LEFT: + strcat(err_msg,XSTR("Team captain(s) have left the game, aborting...",664)); + break; + default : + Int3(); + } + } + + // show the popup + popup(flags,1,POPUP_OK,err_msg); + } +} + +// called when server is waiting for clients to disconnect +int multi_endgame_server_ok_to_leave() +{ + int idx,clients_gone; + + // check to see if our client disconnect timestamp has elapsed + if ( Multi_endgame_server_wait_stamp > 0.0f ) { + Multi_endgame_server_wait_stamp -= flFrametime; + if ( Multi_endgame_server_wait_stamp <= 0.0f ) { + return 1; + } + } + + // check to see if all clients have disconnected + clients_gone = 1; + for(idx=0;idxflags & NETINFO_FLAG_WARPING_OUT)){ + return; + } + + // determine if sufficient warping-out conditions exist + if((Game_mode & GM_IN_MISSION) && // if i'm still in the mission + ((Netgame.game_state == NETGAME_STATE_ENDGAME) || // if the netgame ended + (Netgame.game_state == NETGAME_STATE_DEBRIEF)) // if the netgame is now in the debriefing state + ) { + need_to_warpout = 1; + } + + // if we need to be warping out but are stuck in a dead popup, cancel it + if(need_to_warpout && (popupdead_is_active() || (Net_player->flags & NETINFO_FLAG_RESPAWNING) || (Net_player->flags & NETINFO_FLAG_OBSERVER)) ){ + // flush all active pushed state + multi_handle_state_special(); + + // begin the warpout process + gameseq_post_event(GS_EVENT_DEBRIEF); + + // if text input mode is active, clear it + multi_msg_text_flush(); + } +} \ No newline at end of file diff --git a/src/network/multi_ingame.cpp b/src/network/multi_ingame.cpp new file mode 100644 index 0000000..bf9b6c5 --- /dev/null +++ b/src/network/multi_ingame.cpp @@ -0,0 +1,2120 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_ingame.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 20 8/03/99 11:02p Dave + * Maybe fixed sync problems in multiplayer. + * + * 19 7/30/99 7:01p Dave + * Dogfight escort gauge. Fixed up laser rendering in Glide. + * + * 18 7/28/99 5:33p Dave + * Nailed the missing stats bug to the wall. Problem was optimized build + * and using GET_DATA() with array elements. BLECH. + * + * 17 7/26/99 6:07p Dave + * Removed some warnings. + * + * 16 7/26/99 5:50p Dave + * Revised ingame join. Better? We'll see.... + * + * 15 7/24/99 5:48p Jefff + * converted to new UI stuff -- added 1024 support + * + * 14 7/08/99 10:53a Dave + * New multiplayer interpolation scheme. Not 100% done yet, but still + * better than the old way. + * + * 13 4/21/99 6:15p Dave + * Did some serious housecleaning in the beam code. Made it ready to go + * for anti-fighter "pulse" weapons. Fixed collision pair creation. Added + * a handy macro for recalculating collision pairs for a given object. + * + * 12 3/10/99 6:50p Dave + * Changed the way we buffer packets for all clients. Optimized turret + * fired packets. Did some weapon firing optimizations. + * + * 11 3/09/99 6:24p Dave + * More work on object update revamping. Identified several sources of + * unnecessary bandwidth. + * + * 10 3/08/99 7:03p Dave + * First run of new object update system. Looks very promising. + * + * 9 3/01/99 7:39p Dave + * Added prioritizing ship respawns. Also fixed respawns in TvT so teams + * don't mix respawn points. + * + * 8 1/30/99 5:08p Dave + * More new hi-res stuff.Support for nice D3D textures. + * + * 7 1/27/99 9:56a Dave + * Temporary checkin of beam weapons for Dan to make cool sounds. + * + * 6 11/19/98 8:03a Dave + * Full support for D3-style reliable sockets. Revamped packet lag/loss + * system, made it receiver side and at the lowest possible level. + * + * 5 11/17/98 11:12a Dave + * Removed player identification by address. Now assign explicit id #'s. + * + * 4 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 3 10/13/98 9:29a Dave + * Started neatening up freespace.h. Many variables renamed and + * reorganized. Added AlphaColors.[h,cpp] + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 83 9/18/98 4:54p Dave + * Fixed ingame ship select icon problem. + * + * 82 9/18/98 4:23p Dave + * Reversed the logic on a bogus assert. + * + * 81 9/18/98 3:13p Allender + * actually delete ships from wings when ingame joining. Shoudl get over + * problem were too many ships are presend in the mission since we don't + * delete unused ships until after ingame join is completed + * + * 80 9/15/98 1:59p Dave + * Fixed bonehead mistake calling multi_endgame_ending() + * + * 79 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 78 9/11/98 2:10p Allender + * base temporary parse_object's ai_goals to -1 so that we don't try and + * free sepxressions that shouldn't be freed + * + * 77 8/07/98 10:16a Allender + * use obj_set_flags for the COULD_BE_PLAYER flag + * + * 76 7/24/98 9:27a Dave + * Tidied up endgame sequencing by removing several old flags and + * standardizing _all_ endgame stuff with a single function call. + * + * 75 6/30/98 2:17p Dave + * Revised object update system. Removed updates for all weapons. Put + * button info back into control info packet. + * + * 74 6/13/98 6:01p Hoffoss + * Externalized all new (or forgot to be added) strings to all the code. + * + * 73 6/13/98 3:18p Hoffoss + * NOX()ed out a bunch of strings that shouldn't be translated. + * + * 72 6/10/98 2:56p Dave + * Substantial changes to reduce bandwidth and latency problems. + * + * 71 6/04/98 11:46a Dave + * Drastically reduce size/rate of client control info update packets. Put + * in rate limiting for object updating from server. + * + * 70 5/26/98 11:54a Allender + * fix multiplayer problems and sexpression crash + * + * 69 5/25/98 1:33a Allender + * fixed timestamp problem for client update packets, small fix for ingame + * validation code + * + * 68 5/24/98 9:17p Allender + * commented out test code to help rectify bogus player objects when + * ingame joining + * + * 67 5/23/98 3:16p Allender + * work on object update packet optimizations (a new updating system). + * Don't allow medals/promotions/badges when playing singple player + * missions through the simulator + * + * 66 5/22/98 9:35p Dave + * Put in channel based support for PXO. Put in "shutdown" button for + * standalone. UI tweaks for TvT + * + * 65 5/21/98 10:03p Allender + * add secondary ammo counts to ingame join packets + * + * 64 5/21/98 1:52a Dave + * Remove obsolete command line functions. Reduce shield explosion packets + * drastically. Tweak PXO screen even more. Fix file xfer system so that + * we can guarantee file uniqueness. + * + * 63 5/21/98 12:14a Allender + * fix ingame join problems + * + * 62 5/20/98 3:25p Allender + * ingame join changes (which probably won't make the final version). + * Added RAS code into psnet + * + * 61 5/19/98 11:36p Allender + * fixed very nasty mask problem with ingame joiner marking player objects + * incorrectly. Named ingame joiner ship and observer ship unique names + * + * 60 5/19/98 8:35p Dave + * Revamp PXO channel listing system. Send campaign goals/events to + * clients for evaluation. Made lock button pressable on all screens. + * + * 59 5/18/98 12:41a Allender + * fixed subsystem problems on clients (i.e. not reporting properly on + * damage indicator). Fixed ingame join problem with respawns. minor + * comm menu stuff + * + * 58 5/15/98 4:12p Allender + * removed redbook code. Put back in ingame join timer. Major fixups for + * stats in multiplayer. Pass correct score, medals, etc when leaving + * game. Be sure clients display medals, badges, etc. + * + * 57 5/15/98 1:44p Allender + * initialize hotkeys when entering the mission ingame joining + * + * 56 5/11/98 4:33p Allender + * fixed ingame join problems -- started to work on new object updating + * code (currently ifdef'ed out) + * + * 55 5/09/98 4:31p Chad + * Fixed weapon recharge rate problem and fixed weapon link status problem + * for ingame joiners. + * + * 54 5/08/98 11:21a Allender + * fix ingame join trouble. Small messaging fix. Enable collisions for + * friendlies again + * + * 53 5/03/98 7:04p Dave + * Make team vs. team work mores smoothly with standalone. Change how host + * interacts with standalone for picking missions. Put in a time limit for + * ingame join ship select. Fix ingame join ship select screen for Vasudan + * ship icons. + * + * 52 5/03/98 2:52p Dave + * Removed multiplayer furball mode. + * + * 51 5/02/98 1:47a Dave + * Make sure ingame joiners know how many respawns they have left. Tidy up + * some multiui stuff. + * + * 50 5/01/98 4:11p Comet + * Fixed ship_ets bug. I think. + * + * 49 4/30/98 12:49a Allender + * change ship type and weapons of any player wing ship, not just ones + * that players currently occupy + * + * 48 4/29/98 9:36p Allender + * ingame join tweaks. added network message for countermeasures + * + * 47 4/29/98 12:28p Chad + * set packet size when selecting a player's ingame choice + * + * 46 4/25/98 2:02p Dave + * Put in multiplayer context help screens. Reworked ingame join ship + * select screen. Fixed places where network timestamps get hosed. + * + * 45 4/23/98 11:52p Allender + * make homing weapons send their homing object. Fixed ingame joiners so + * they bash ship types and weapons correctly when joining + * + * 44 4/23/98 1:49a Allender + * major rearm/repair fixes for multiplayer. Fixed respawning of AI ships + * to not respawn until 5 seconds after they die. Send escort information + * to ingame joiners + * + * 43 4/22/98 5:53p Dave + * Large reworking of endgame sequencing. Updated multi host options + * screen for new artwork. Put in checks for host or team captains leaving + * midgame. + * + * 42 4/22/98 4:59p Allender + * new multiplayer dead popup. big changes to the comm menu system for + * team vs. team. Start of debriefing stuff for team vs. team Make form + * on my wing work with individual ships who have high priority orders + * + * 41 4/21/98 11:56p Dave + * Put in player deaths statskeeping. Use arrow keys in the ingame join + * ship select screen. Don't quit the game if in the debriefing and server + * leaves. + * + * 40 4/20/98 12:40a Allender + * fixed nasty problem where network read code was not reentrant. minor + * UI tweaks. ingame joiners now get netgame info correctly. + * + * 39 4/07/98 5:42p Dave + * Put in support for ui display of voice system status (recording, + * playing back, etc). Make sure main hall music is stopped before + * entering a multiplayer game via ingame join. + * + * 38 4/06/98 6:47p Allender + * be sure that ingame joiner has reasonable current primary and secondary + * banks + * + * 37 4/06/98 12:33a Allender + * ingame joiners need to unmark all player ships as could be players + * before getting ship information from host. Don't send count with + * countermeasure fired packet + * + * 36 4/04/98 4:22p Dave + * First rev of UDP reliable sockets is done. Seems to work well if not + * overly burdened. + * + * 35 4/04/98 3:55p Allender + * made ingame join send packet to all other clients in the game when in + * game joiner selects his ship + * + * 34 4/03/98 1:03a Dave + * First pass at unreliable guaranteed delivery packets. + * + * 33 4/02/98 5:36p John + * Made the level paging occur at the same time as the level is read in. + * + * 32 3/31/98 4:03p Allender + * some ingame join fixed -- make sure ingame joiners have parse objects + * for all other players + * + * 31 3/24/98 5:12p Allender + * ingame join packet sequencing + * + * $NoKeywords: $ + */ + +#include + +#include "ai.h" +#include "object.h" +#include "ship.h" +#include "multi.h" +#include "multiutil.h" +#include "multimsgs.h" +#include "multiui.h" +#include "missionparse.h" +#include "freespace.h" +#include "gamesequence.h" +#include "2d.h" +#include "ui.h" +#include "key.h" +#include "gamesnd.h" +#include "linklist.h" +#include "multi_ingame.h" +#include "missionscreencommon.h" +#include "popup.h" +#include "bmpman.h" +#include "mouse.h" +#include "multi_observer.h" +#include "multi_xfer.h" +#include "multi_kick.h" +#include "missiongoals.h" +#include "mainhallmenu.h" +#include "stats.h" +#include "multiteamselect.h" +#include "missionweaponchoice.h" +#include "multi_endgame.h" +#include "hudescort.h" +#include "hudshield.h" +#include "objcollide.h" +#include "missionhotkey.h" +#include "multi_campaign.h" +#include "multi_obj.h" +#include "alphacolors.h" +#include "timer.h" + +// -------------------------------------------------------------------------------------------------- +// DAVE's BIGASS INGAME JOIN WARNING/DISCLAIMER +// +// Ingame joining is another delicate system. Although not as delicate as server transfer, it will +// help to take as many precautions as possible when handling ingame joins. Please be sure to follow +// all the same rules as explained in multi_strans.h +// +// -------------------------------------------------------------------------------------------------- + +// -------------------------------------------------------------------------------------------------- +// INGAME JOIN DESCRIPTION +// +// 1.) Joiner sends a JOIN packet to the server +// 2.) If the server accepts him, he receives an ACCEPT packet in return +// 3.) The client then moves into the INGAME_SYNC state to begin receiving data from the server +// 4.) The first thing he does on this screen is send his filesig packet to the server. At which +// point the server will either let him in or deny him. There are no file transfers ingame. +// 5.) The server calls multi_handle_ingame_joiners() once per frame, through multi_do_frame() +// 6.) After verifiying or kicking the player because of his file signature, the server tells the +// player to load the mission +// 7.) When the mission is loaded, the server, sends a netgame update to the client +// 8.) Without waiting, the server then begins sending data ship packets to the player +// 9.) Upon confirmation of receiving these packets, the server sends wing data packets +// 10.) Upon completion of this, the server sends respawn point packets +// 11.) Upon completion of this, the server sends a post briefing data block packet containing ship class and +// weapon information +// 12.) After this, the server sends a player settings packet (to all players for good measure) +// 13.) At this point, the server sends a jump into mission packet +// 14.) Upon receipt of this packet, the client moves into the ingame ship select state +// 15.) The first thing the client does in this state is load the mission data (textures, etc) +// 16.) The player is presented with a list of ships he can choose from. He selects one and sends +// an INGAME_SHIP_REQUEST to the server. +// 17.) The server checks to see if this request is acceptable and sends an INGAME_SHIP_REQUEST back +// with the appropriate data. +// 18.) If the client received an affirmative, he selects the ship and jumps into the mission, otherwise +// he removes it from the list and tries for another ship +// -------------------------------------------------------------------------------------------------- + + +LOCAL int Ingame_ships_deleted = 0; +LOCAL int Ingame_ships_to_delete[MAX_SHIPS]; + + +// -------------------------------------------------------------------------------------------------- +// INGAME JOIN FORWARD DECLARATIONS +// + +void multi_ingame_send_ship_update(net_player *p); + +void multi_ingame_join_check_buttons(); +void multi_ingame_join_button_pressed(int n); + + + +// -------------------------------------------------------------------------------------------------- +// INGAME JOIN COMMON DEFINITIONS +// + + +// -------------------------------------------------------------------------------------------------- +// INGAME JOIN SERVER FUNCTIONS +// + +// called on the server to process ingame joiners and move them through the motions of ingame joining +void multi_handle_ingame_joiners() +{ + int idx; + + Assert( MULTIPLAYER_MASTER ); + + // if my ingame joining flag isn't set, then don't do anything. + if ( !(Netgame.flags & NG_FLAG_INGAME_JOINING) ){ + return; + } + + // traverse through all the players + for(idx = 0; idxflags &= ~(NETINFO_FLAG_INGAME_JOIN); + + // set my state to be in-mission + Net_player->state = NETPLAYER_STATE_IN_MISSION; + send_netplayer_update_packet(); + + // jump into the game + gameseq_post_event(GS_EVENT_ENTER_GAME); +} + +// -------------------------------------------------------------------------------------------------- +// INGAME DATA SYNC SCREEN +// + +// mission sync screen init function for ingame joining +void multi_ingame_sync_init() +{ + // if we couldn't get the file signature correctly. send some bogus values + multi_get_mission_checksum(Game_current_mission_filename); + + // everyone should re-initialize these + init_multiplayer_stats(); + + // reset all sequencing info + multi_oo_reset_sequencing(); + + // send the file signature to the host for possible mission file transfer + strcpy(Netgame.mission_name,Game_current_mission_filename); + send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length); + + Ingame_ships_deleted = 0; +} + +// mission sync screen do function for ingame joining +void multi_ingame_sync_do() +{ +} + +// mission sync screen do function for ingame joining +void multi_ingame_sync_close() +{ +} + + +// -------------------------------------------------------------------------------------------------- +// INGAME SHIP SELECT SCREEN +// +static char *Multi_ingame_join_bitmap_fname[GR_NUM_RESOLUTIONS] = { + "MultiIngame", // GR_640 + "2_MultiIngame" // GR_1024 +}; + +static char *Multi_ingame_join_bitmap_mask_fname[GR_NUM_RESOLUTIONS] = { + "MultiIngame-M", // GR_640 + "2_MultiIngame-M" // GR_1024 +}; + + +// button defs +#define MULTI_INGAME_JOIN_NUM_BUTTONS 2 +#define MIJ_CANCEL 0 +#define MIJ_JOIN 1 + +ui_button_info Multi_ingame_join_buttons[GR_NUM_RESOLUTIONS][MULTI_INGAME_JOIN_NUM_BUTTONS] = { + { // GR_640 + ui_button_info( "MIB_00", 532, 434, 510, 413, 0 ), // cancel + ui_button_info( "MIB_01", 572, 428, 585, 413, 1 ), // join + }, + { // GR_1024 + ui_button_info( "2_MIB_00", 851, 695, 916, 685, 0 ), // cancel + ui_button_info( "2_MIB_01", 916, 685, 950, 665, 1 ), // join + } +}; + +#define MULTI_INGAME_JOIN_NUM_TEXT 8 + +UI_XSTR Multi_ingame_join_text[GR_NUM_RESOLUTIONS][MULTI_INGAME_JOIN_NUM_TEXT] = { + { // GR_640 + {"Cancel", 387, 510, 413, UI_XSTR_COLOR_PINK, -1, &Multi_ingame_join_buttons[GR_640][MIJ_CANCEL].button}, + {"Join", 1303, 585, 413, UI_XSTR_COLOR_PINK, -1, &Multi_ingame_join_buttons[GR_640][MIJ_JOIN].button}, + {"Select Ship", 317, 39, 6, UI_XSTR_COLOR_PINK, -1, NULL}, + {"name", 1423, 39, 28, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"class", 1424, 145, 28, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"status", 1425, 214, 28, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"primary", 1426, 295, 28, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"secondary", 1427, 440, 28, UI_XSTR_COLOR_GREEN, -1, NULL} + }, + { // GR_1024 + {"Cancel", 387, 843, 665, UI_XSTR_COLOR_PINK, -1, &Multi_ingame_join_buttons[GR_1024][MIJ_CANCEL].button}, + {"Join", 1303, 950, 665, UI_XSTR_COLOR_PINK, -1, &Multi_ingame_join_buttons[GR_1024][MIJ_JOIN].button}, + {"Select Ship", 317, 63, 14, UI_XSTR_COLOR_PINK, -1, NULL}, + {"name", 1423, 63, 45, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"class", 1424, 233, 45, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"status", 1425, 343, 45, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"primary", 1426, 473, 45, UI_XSTR_COLOR_GREEN, -1, NULL}, + {"secondary", 1427, 704, 45, UI_XSTR_COLOR_GREEN, -1, NULL} + } +}; + +#define MI_FIELD_X 0 +#define MI_FIELD_Y 1 +#define MI_FIELD_W 2 +#define MI_FIELD_H 3 + +static int Mi_width[GR_NUM_RESOLUTIONS] = { + 569, // GR_640 + 910 // GR_1024 +}; + +static int Mi_height[GR_NUM_RESOLUTIONS] = { + 339, // GR_640 + 542 // GR_1024 +}; + +static int Mi_spacing[GR_NUM_RESOLUTIONS] = { + 30, + 48 +}; + +static int Mi_name_field[GR_NUM_RESOLUTIONS][4] = { + // GR_640 + { + 33, // x + 49, // y + 100, // width + 339 // height + }, + // GR_1024 + { + 53, // x + 78, // y + 160, // width + 542 // height + } +}; + +static int Mi_class_field[GR_NUM_RESOLUTIONS][4] = { + // GR_640 + { + 140, // x + 49, // y + 59, // width + 339 // height + }, + // GR_1024 + { + 224, // x + 78, // y + 94, // width + 542 // height + } +}; + +static int Mi_status_field[GR_NUM_RESOLUTIONS][4] = { + // GR_640 + { + 209, // x + 49, // y + 69, // width + 339 // height + }, + // GR_1024 + { + 334, // x + 78, // y + 110, // width + 542 // height + } +}; + +static int Mi_primary_field[GR_NUM_RESOLUTIONS][4] = { + // GR_640 + { + 287, // x + 49, // y + 145, // width + 339 // height + }, + // GR_1024 + { + 459, // x + 78, // y + 232, // width + 542 // height + } +}; + +static int Mi_secondary_field[GR_NUM_RESOLUTIONS][4] = { + // GR_640 + { + 441, // x + 49, // y + 145, // width + 339 // height + }, + // GR_1024 + { + 706, // x + 78, // y + 232, // width + 542 // height + } +}; + +// for timing a player out +static int Multi_ingame_timer_coords[GR_NUM_RESOLUTIONS][2] = { + { + // GR_640 + 26, + 411 + }, + { + // GR_1024 + 42, + 658 + } +}; + +//#define MULTI_INGAME_TIME_LEFT_X 26 +//#define MULTI_INGAME_TIME_LEFT_Y 411 + +#define MULTI_INGAME_TIME_SECONDS (1000 * 15) +LOCAL int Ingame_time_left; + +// uses MULTI_JOIN_REFRESH_TIME as its timestamp +UI_WINDOW Multi_ingame_window; // the window object for the join screen +UI_BUTTON Multi_ingame_select_button; // for selecting list items +int Multi_ingame_bitmap; // the background bitmap + +// ship class icons +#define MULTI_INGAME_MAX_SHIP_ICONS 40 +typedef struct is_icon { + int bmaps[NUM_ICON_FRAMES]; + int ship_class; +} is_icon; +is_icon Multi_ingame_ship_icon[MULTI_INGAME_MAX_SHIP_ICONS]; +int Multi_ingame_num_ship_icons; + +// # of available ships (also == the # currently being displayed) +int Multi_ingame_num_avail; + +// signatures for each of the available ships +ushort Multi_ingame_ship_sigs[MAX_PLAYERS]; + +// net signature of the ship we've requested to grab as an ingame joiner (waiting for server response if >= 0) +ushort Multi_ingame_join_sig; + +// the index into the list of the ship currently selected +int Multi_ingame_ship_selected; + +// temporary stuff - used only until we come up with a more permanent interface for this screen +#define MAX_INGAME_SHIPS 50 +#define INGAME_FINAL_TIMEOUT 4000 + +ushort Ingame_ship_signatures[MAX_INGAME_SHIPS]; +LOCAL int Ingame_final_timeout; + +//XSTR:ON + +// local variables to hold ship/obj/ai information for the joiner. We need to +// create a bogus ship so that packets that the joiner receives during his join +// have valid Player_ship, Player_obj, and Player_ai to work with +int Ingame_shipnum; + +// display the available ships (OF_COULD_BE_PLAYER flagged) +void multi_ingame_join_display_avail(); + +// try and scroll the selected ship up +void multi_ingame_scroll_select_up(); + +// try and scroll the selected ship down +void multi_ingame_scroll_select_down(); + +// handle all timeout details +void multi_ingame_handle_timeout(); + +int multi_ingame_get_ship_class_icon(int ship_class) +{ + int idx; + + // lookup through all available ship icons + for(idx=0;idx 0) && (Multi_ingame_num_ship_icons < MULTI_INGAME_MAX_SHIP_ICONS)){ + // set the ship class + Multi_ingame_ship_icon[Multi_ingame_num_ship_icons].ship_class = idx; + + // load in the animation frames for the icon + first_frame = bm_load_animation(Ship_info[idx].icon_filename, &num_frames); + if ( first_frame == -1 ) { + Int3(); // Could not load in icon frames.. get Dave + } + for ( s_idx = 0; s_idx < num_frames; s_idx++ ) { + Multi_ingame_ship_icon[Multi_ingame_num_ship_icons].bmaps[s_idx] = first_frame+s_idx; + } + + Multi_ingame_num_ship_icons++; + } + } +} + +void multi_ingame_unload_icons() +{ + int idx,s_idx; + + // unload all the bitmaps + for(idx=0;idxplayer->objnum = -1; + + // create a ship, then find a ship to copy crucial information from. Save and restore the wing + // number to be safe. + /* + wingnum_save = Player_start_pobject.wingnum; + net_signature = Player_start_pobject.net_signature; + goals_save = Player_start_pobject.ai_goals; + Player_start_pobject.wingnum = -1; + Player_start_pobject.net_signature = 0; + Player_start_pobject.ai_goals = -1; + objnum = parse_create_object( &Player_start_pobject ); + Player_start_pobject.wingnum = wingnum_save; + Player_start_pobject.net_signature = net_signature; + Player_start_pobject.ai_goals = goals_save; + + if ( objnum == -1 ) { + nprintf(("Network", "Bailing ingame join because unable to create parse object player ship\n")); + multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_INGAME_SHIP); + return; + } + + // make it invalid + Player_obj = &Objects[objnum]; + Player_obj->net_signature = 0; + Player_ship = &Ships[Player_obj->instance]; + strcpy(Player_ship->ship_name, NOX("JIP Ship")); + Player_ai = &Ai_info[Player_ship->ai_index]; + */ + + // load the temp ship icons + multi_ingame_load_icons(); + + // blast all the ingame ship signatures + memset(Multi_ingame_ship_sigs,0,sizeof(ushort) * MAX_PLAYERS); + + // the index into the list of the ship currently selected + Multi_ingame_ship_selected = -1; + + // initialize the time he has left to select a ship + Ingame_time_left = timestamp(MULTI_INGAME_TIME_SECONDS); + + // initialize GUI data + + // create the interface window + Multi_ingame_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0); + Multi_ingame_window.set_mask_bmap(Multi_ingame_join_bitmap_mask_fname[gr_screen.res]); + + // load the background bitmap + Multi_ingame_bitmap = bm_load(Multi_ingame_join_bitmap_fname[gr_screen.res]); + if(Multi_ingame_bitmap < 0) + Error(LOCATION, "Couldn't load background bitmap for ingame join"); + + // create the interface buttons + for(idx=0; idx 0)){ + gamesnd_play_iface(SND_USER_SELECT); + Multi_ingame_ship_selected = 0; + } + + // if we currently have a ship selected, but it disappears, select the next ship (is possible0 + if((Multi_ingame_ship_selected >= 0) && (Multi_ingame_ship_selected >= Multi_ingame_num_avail)){ + gamesnd_play_iface(SND_USER_SELECT); + Multi_ingame_ship_selected = Multi_ingame_num_avail-1; + } + + // if the player clicked on the select button, see if the selection has changed + if(Multi_ingame_select_button.pressed()){ + Multi_ingame_select_button.get_mouse_pos(NULL,&y); + select_index = y / Mi_spacing[gr_screen.res]; + + // if we've selected a valid item + if((select_index >= 0) && (select_index < Multi_ingame_num_avail)){ + // if we're not selected the same item, play a sound + if(Multi_ingame_ship_selected != select_index){ + gamesnd_play_iface(SND_USER_SELECT); + } + + // select the item + Multi_ingame_ship_selected = select_index; + } + } +} + + +// determines if a button was pressed, and acts accordingly +void multi_ingame_join_check_buttons() +{ + int idx; + for(idx=0; idx= 0) { + gamesnd_play_iface(SND_USER_SELECT); + + // select the sig of this ship and send a request for it + Multi_ingame_join_sig = Multi_ingame_ship_sigs[Multi_ingame_ship_selected]; + + // send a request to the + send_ingame_ship_request_packet(INGAME_SR_REQUEST,Multi_ingame_join_sig); + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } + + break; + default: + // how the hell did this happen? + Int3(); + } +} + + +// ingame join ship selection screen do +void multi_ingame_select_do() +{ + int k = Multi_ingame_window.process(); + + // process any keypresses + switch(k){ + case KEY_ESC : + multi_quit_game(PROMPT_CLIENT); + break; + + case KEY_UP: + multi_ingame_scroll_select_up(); + break; + + case KEY_DOWN: + multi_ingame_scroll_select_down(); + break; + } + + // process button presses + // multi_ingame_process_buttons(); + multi_ingame_join_check_buttons(); + + // process any ship list related events + multi_ingame_ship_list_process(); + + // draw the background, etc + gr_reset_clip(); + GR_MAYBE_CLEAR_RES(Multi_ingame_bitmap); + if(Multi_ingame_bitmap != -1){ + gr_set_bitmap(Multi_ingame_bitmap); + gr_bitmap(0,0); + } + Multi_ingame_window.draw(); + + // handle all timeout details. blitting, etc + multi_ingame_handle_timeout(); + + // display the available ships + multi_ingame_join_display_avail(); + + // flip the buffer + gr_flip(); +} + +// ingame join ship select close +void multi_ingame_select_close() +{ + // unload any bitmaps + if(!bm_unload(Multi_ingame_bitmap)){ + nprintf(("General","WARNING : could not unload background bitmap %s\n",Multi_ingame_join_bitmap_fname[gr_screen.res])); + } + + // unload all the ship class icons + multi_ingame_unload_icons(); + + // destroy the UI_WINDOW + Multi_ingame_window.destroy(); + + // stop main hall music + main_hall_stop_music(); +} + +// display an individual ships information, starting at the indicated y pixel value +void multi_ingame_join_display_ship(object *objp,int y_start) +{ + int icon_num,idx; + ship_info *sip; + int y_spacing; + ship_weapon *wp; + + sip = &Ship_info[Ships[objp->instance].ship_info_index]; + + // blit the ship name itself + gr_set_color_fast(&Color_normal); + gr_string(Mi_name_field[gr_screen.res][MI_FIELD_X],y_start+10, Ships[objp->instance].ship_name); + + // blit the ship class icon + icon_num = multi_ingame_get_ship_class_icon(Ships[objp->instance].ship_info_index); + if(icon_num != -1){ + gr_set_bitmap(Multi_ingame_ship_icon[icon_num].bmaps[0]); + gr_bitmap(Mi_class_field[gr_screen.res][MI_FIELD_X] + 15, y_start); + } + + gr_set_color_fast(&Color_bright); + wp = &Ships[objp->instance].weapons; + + // blit the ship's primary weapons + y_spacing = (Mi_spacing[gr_screen.res] - (wp->num_primary_banks * 10)) / 2; + for(idx=0;idxnum_primary_banks;idx++){ + gr_string(Mi_primary_field[gr_screen.res][MI_FIELD_X], y_start + y_spacing + (idx * 10), Weapon_info[wp->primary_bank_weapons[idx]].name); + } + + // blit the ship's secondary weapons + y_spacing = (Mi_spacing[gr_screen.res] - (wp->num_secondary_banks * 10)) / 2; + for(idx=0;idxnum_secondary_banks;idx++){ + gr_string(Mi_secondary_field[gr_screen.res][MI_FIELD_X], y_start + y_spacing + (idx * 10), Weapon_info[wp->secondary_bank_weapons[idx]].name); + } + + // blit the shield/hull integrity + hud_shield_show_mini(objp, Mi_status_field[gr_screen.res][MI_FIELD_X] + 15, y_start + 3,5,7); +} + +// display the available ships (OF_COULD_BE_PLAYER flagged) +void multi_ingame_join_display_avail() +{ + ship_obj *moveup; + + // recalculate this # every frame + Multi_ingame_num_avail = 0; + + // display a background highlight rectangle for any selected lines + if(Multi_ingame_ship_selected != -1){ + int y_start = (Mi_name_field[gr_screen.res][MI_FIELD_Y] + (Multi_ingame_ship_selected * Mi_spacing[gr_screen.res])); + + // draw the border + gr_set_color_fast(&Color_bright_blue); + gr_line(Mi_name_field[gr_screen.res][MI_FIELD_X]-1,y_start-1, (Mi_name_field[gr_screen.res][MI_FIELD_X]-1) + (Mi_width[gr_screen.res]+2),y_start-1); + gr_line(Mi_name_field[gr_screen.res][MI_FIELD_X]-1,y_start + Mi_spacing[gr_screen.res] - 2, (Mi_name_field[gr_screen.res][MI_FIELD_X]-1) + (Mi_width[gr_screen.res]+2),y_start + Mi_spacing[gr_screen.res] - 2); + gr_line(Mi_name_field[gr_screen.res][MI_FIELD_X]-1,y_start, Mi_name_field[gr_screen.res][MI_FIELD_X]-1, y_start + Mi_spacing[gr_screen.res] - 2); + gr_line((Mi_name_field[gr_screen.res][MI_FIELD_X]-1) + (Mi_width[gr_screen.res]+2), y_start,(Mi_name_field[gr_screen.res][MI_FIELD_X]-1) + (Mi_width[gr_screen.res]+2),y_start + Mi_spacing[gr_screen.res] - 2); + } + + moveup = GET_FIRST(&Ship_obj_list); + while(moveup != END_OF_LIST(&Ship_obj_list)){ + if( !(Ships[Objects[moveup->objnum].instance].flags & (SF_DYING|SF_DEPARTING)) && (Objects[moveup->objnum].flags & OF_COULD_BE_PLAYER) ) { + // display the ship + multi_ingame_join_display_ship(&Objects[moveup->objnum],Mi_name_field[gr_screen.res][MI_FIELD_Y] + (Multi_ingame_num_avail * Mi_spacing[gr_screen.res])); + + // set the ship signature + Multi_ingame_ship_sigs[Multi_ingame_num_avail] = Objects[moveup->objnum].net_signature; + + // inc the # available + Multi_ingame_num_avail++; + } + moveup = GET_NEXT(moveup); + } +} + +// try and scroll the selected ship up +void multi_ingame_scroll_select_up() +{ + if(Multi_ingame_ship_selected > 0){ + gamesnd_play_iface(SND_USER_SELECT); + Multi_ingame_ship_selected--; + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// try and scroll the selected ship down +void multi_ingame_scroll_select_down() +{ + if(Multi_ingame_ship_selected < (Multi_ingame_num_avail - 1)){ + gamesnd_play_iface(SND_USER_SELECT); + Multi_ingame_ship_selected++; + } else { + gamesnd_play_iface(SND_GENERAL_FAIL); + } +} + +// handle all timeout details +void multi_ingame_handle_timeout() +{ + /* + // uncomment this block to disable the timer + gr_set_color_fast(&Color_bright_red); + gr_string(Multi_ingame_timer_coords[gr_screen.res][0], Multi_ingame_timer_coords[gr_screen.res][1], "Timer disabled!!"); + return; + */ + + // if we've timed out, leave the game + if( timestamp_elapsed(Ingame_time_left) ) { + multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_INGAME_TIMEOUT, MULTI_END_ERROR_NONE); + return; + } + + // otherwise, blit how much time we have left + int time_left = timestamp_until(Ingame_time_left) / 1000; + char tl_string[100]; + gr_set_color_fast(&Color_bright); + memset(tl_string,0,100); + sprintf(tl_string,XSTR("Time remaining : %d s\n",682),time_left); + gr_string(Multi_ingame_timer_coords[gr_screen.res][0], Multi_ingame_timer_coords[gr_screen.res][1], tl_string); +} + + +// -------------------------------------------------------------------------------------------------- +// PACKET HANDLER functions +// these are also defined in multimsgs.h, but the implementations are in the module for the sake of convenience +// + +#define INGAME_PACKET_SLOP 75 // slop value used for packets to ingame joiner + +void process_ingame_ships_packet( ubyte *data, header *hinfo ) +{ + int offset, sflags, oflags, team, j; + ubyte p_type; + ushort net_signature; + short wing_data; + int team_val, slot_index, idx; + char ship_name[255] = ""; + object *objp; + int net_sig_modify; + + // go through the ship obj list and delete everything. YEAH + if(!Ingame_ships_deleted){ + int idx; + + // no player object + Player_obj = NULL; + Player_ship = NULL; + Player_ai = NULL; + + // delete all ships + for(idx=0; idx= 0) && (Ships[idx].objnum < MAX_OBJECTS)){ + obj_delete(Ships[idx].objnum); + } + } + + Ingame_ships_deleted = 1; + } + + offset = HEADER_LENGTH; + + // go + GET_DATA( p_type ); + while ( p_type == INGAME_SHIP_NEXT ) { + p_object *p_objp; + int ship_num, objnum; + + GET_STRING( ship_name ); + GET_DATA( net_signature ); + GET_DATA( sflags ); + GET_DATA( oflags ); + GET_DATA( team ); + GET_DATA( wing_data ); + net_sig_modify = 0; + if(wing_data >= 0){ + GET_DATA(Wings[wing_data].current_wave); + net_sig_modify = Wings[wing_data].current_wave - 1; + } + + // lookup ship in the original ships array + p_objp = mission_parse_get_original_ship(net_signature); + if(p_objp == NULL){ + // if this ship is part of wing not on its current wave, look for its "original" by subtracting out wave # + p_objp = mission_parse_get_arrival_ship((ushort)(net_signature - (ushort)net_sig_modify)); + } + if(p_objp == NULL){ + Int3(); + nprintf(("Network", "Couldn't find ship %s in either arrival list or in mission")); + multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_INGAME_BOGUS); + return; + } + + // go ahead and create the parse object. Set the network signature of this guy before + // creation + // multi_set_network_signature( net_signature, MULTI_SIG_SHIP ); + objnum = parse_create_object( p_objp ); + ship_num = Objects[objnum].instance; + Objects[objnum].flags = oflags; + Objects[objnum].net_signature = net_signature; + + // assign any common data + strcpy(Ships[ship_num].ship_name, ship_name); + Ships[ship_num].flags = sflags; + Ships[ship_num].team = team; + Ships[ship_num].wingnum = (int)wing_data; + + GET_DATA( p_type ); + } + + PACKET_SET_SIZE(); + + // if we have reached the end of the list and change our network state + if ( p_type == INGAME_SHIP_LIST_EOL ) { + // merge all created list + obj_merge_created_list(); + + // fixup player ship stuff + for(idx=0; idxinstance, Wss_slots_teams[team_val][slot_index].ship_class); + wl_bash_ship_weapons(&Ships[idx].weapons, &Wss_slots_teams[team_val][slot_index]); + + // Be sure to mark this ship as as a could_be_player + obj_set_flags( objp, objp->flags | OF_COULD_BE_PLAYER ); + objp->flags &= ~OF_PLAYER_SHIP; + } + + // if this is a player ship, make sure we find out who's it is and set their objnum accordingly + if(team_val != -1){ + for( j = 0; j < MAX_PLAYERS; j++){ + if(MULTI_CONNECTED(Net_players[j]) && (Net_players[j].player->objnum == Objects[Ships[idx].objnum].net_signature)) { + // nprintf(("Network", "Making %s ship for %s\n", Ships[shipnum].ship_name, Net_players[j].player->callsign)); + multi_assign_player_ship( j, objp, Ships[idx].ship_info_index ); + objp->flags |= OF_PLAYER_SHIP; + objp->flags &= ~OF_COULD_BE_PLAYER; + break; + } + } + } + } + + // notify the server that we're all good. + Net_player->state = NETPLAYER_STATE_INGAME_SHIPS; + send_netplayer_update_packet(); + + // add some mission sync text + multi_common_add_text(XSTR("Ships packet ack (ingame)\n",683)); + } +} + +void send_ingame_ships_packet(net_player *player) +{ + ubyte data[MAX_PACKET_SIZE]; + ubyte p_type; + ship_obj *so; + int packet_size; + short wing_data; + + BUILD_HEADER( SHIPS_INGAME_PACKET ); + + // essentially, we are going to send a list of ship names to the joiner for ships that are not + // in wings. The joiner will take the list, create any ships which should be created, and delete all + // other ships after the list is sent. + for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + ship *shipp; + + shipp = &Ships[Objects[so->objnum].instance]; + + // skip all wings. + // if ( shipp->wingnum != -1 ){ + // continue; + // } + + if ( Objects[so->objnum].net_signature == STANDALONE_SHIP_SIG ){ + continue; + } + + // add the ship name and other information such as net signature, ship and object(?) flags. + p_type = INGAME_SHIP_NEXT; + ADD_DATA( p_type ); + ADD_STRING( shipp->ship_name ); + ADD_DATA( Objects[so->objnum].net_signature ); + ADD_DATA( shipp->flags ); + ADD_DATA( Objects[so->objnum].flags ); + ADD_DATA( shipp->team ); + wing_data = (short)shipp->wingnum; + ADD_DATA(wing_data); + if(wing_data >= 0){ + ADD_DATA(Wings[wing_data].current_wave); + } + + // don't send anymore data if we are getting close to the maximum size of this packet. Send it off and + // keep going + if ( packet_size > (MAX_PACKET_SIZE - INGAME_PACKET_SLOP) ) { + p_type = INGAME_SHIP_LIST_EOP; + ADD_DATA( p_type ); + multi_io_send_reliable(player, data, packet_size); + BUILD_HEADER( SHIPS_INGAME_PACKET ); + } + } + + // end of the ship list!!! + p_type = INGAME_SHIP_LIST_EOL; + ADD_DATA( p_type ); + multi_io_send_reliable(player, data, packet_size); +} + +void process_ingame_wings_packet( ubyte *data, header *hinfo ) +{ + Int3(); +} +/* +// code to process the wing data from a server. +void process_ingame_wings_packet( ubyte *data, header *hinfo ) +{ + int offset, wingnum; + ubyte p_type, what; + + offset = HEADER_LENGTH; + + GET_DATA( p_type ); + + // p_type tells us whether to stop or not + while ( p_type == INGAME_WING_NEXT ) { + wing *wingp; + + // get the wingnum and a pointer to it. The game stores data for all wings always, so this + // is perfectly valid + GET_DATA( wingnum ); + wingp = &Wings[wingnum]; + + GET_DATA( what ); + if ( what == INGAME_WING_NOT_ARRIVED ) { + Assert( wingp->total_arrived_count == 0 ); // this had better be true!!! + } else if ( what == INGAME_WING_DEPARTED ) { + // mark the wing as gone. if it isn't, it soon will be. Maybe we should send more information + // about these wings later (like total_arrived_count, etc), but we will see. + wingp->flags |= WF_WING_GONE; + } else { + int total_arrived_count, current_count, current_wave, i, j; + ushort signature; + int shipnum; + + // the wing is present in the mission on the server. Get the crucial information about the + // wing. Then get the ships for this wing in order on the client machine + GET_DATA( total_arrived_count ); + GET_DATA( current_count ); + GET_DATA( current_wave ); + + Assert( current_wave > 0 ); + Assert( total_arrived_count > 0 ); + + // for this wing, strip it down to nothing. Let the parse object ocde recreate the + // wing from the parse objects, then bash any weapons, etc for player wings. We need + // to do this because we might actually wind up with > MAX_SHIPS_PER_WING if we + // don't delete them all first, and have a > 0 threshold, and are on something other + // than the first wave. Only do this for non-player wings. + + nprintf(("Network", "Clearing %s -- %d ships\n", wingp->name, wingp->current_count)); + for ( i = 0; i < wingp->current_count; i++ ) { + int index, objnum; + + index = wingp->ship_index[i]; + Assert( index != -1 ); + objnum = Ships[index].objnum; + Assert( objnum != -1 ); + + // delete the object since we are filling the wing again anyway. + obj_delete( objnum ); + Objects[objnum].net_signature = 0; // makes this object "invalid" until dead. + if ( Objects[objnum].type == OBJ_GHOST ) { + nprintf(("Network", "Marking ghost objnum %d as dead\n", objnum)); + Objects[objnum].flags |= OF_SHOULD_BE_DEAD; + } + Ingame_ships_to_delete[index] = 0; // be sure that this guy doesn't get deleted, since we already deleted it + wingp->ship_index[i] = -1; + } + wingp->current_count = 0; + wingp->total_arrived_count = 0; + + // now, recreate all the ships needed + for (i = 0; i < current_count; i++ ) { + int which_one, team, slot_index, specific_instance;; + ship *shipp; + object *objp; + + GET_DATA( signature ); + + // assign which_one to be the given signature - wing's base signature. This let's us + // know which ship to create (i.e. the total_arrivel_count); + which_one = signature - wingp->net_signature; + Assert( (which_one >= 0) && (which_one < (wingp->net_signature + (wingp->wave_count*wingp->num_waves))) ); + wingp->total_arrived_count = (ushort)which_one; + + // determine which ship in the ahip arrival list this guy is. It is a 0 based index + specific_instance = which_one % wingp->wave_count; + + // call parse_wing_create_ships making sure that we only ever create 1 ship at a time. We don't + // want parse_wing_create_ships() to assign network signature either. We will directly + // assign it here. + + wingp->current_wave = 0; // make it the first wave. Ensures that ships don't get removed off the list + parse_wing_create_ships( wingp, 1, 1, specific_instance ); + shipnum = wingp->ship_index[wingp->current_count-1]; + Ingame_ships_to_delete[shipnum] = 0; // "unmark" this ship so it doesn't get deleted. + + // kind of stupid, but bash the name since it won't get recreated properly from + // the parse_wing_create_ships call. + shipp = &Ships[shipnum]; + sprintf(shipp->ship_name, NOX("%s %d"), wingp->name, which_one + 1); + nprintf(("Network", "Created %s\n", shipp->ship_name)); + + objp = &Objects[shipp->objnum]; + objp->net_signature = (ushort)(wingp->net_signature + which_one); + + // get the team and slot. Team will be -1 when it isn't a part of player wing. So, if + // not -1, then be sure we have a valid slot, then change the ship type, etc. + multi_ts_get_team_and_slot(shipp->ship_name, &team, &slot_index); + if ( team != -1 ) { + Assert( slot_index != -1 ); + + // change the ship type and the weapons + change_ship_type(objp->instance, Wss_slots_teams[team][slot_index].ship_class); + wl_bash_ship_weapons(&shipp->weapons,&Wss_slots_teams[team][slot_index]); + + // Be sure to mark this ship as as a could_be_player + obj_set_flags( objp, objp->flags | OF_COULD_BE_PLAYER ); + objp->flags &= ~OF_PLAYER_SHIP; + } + + // if this is a player ship, make sure we find out who's it is and set their objnum accordingly + for( j = 0; j < MAX_PLAYERS; j++){ + if(MULTI_CONNECTED(Net_players[j]) && (Net_players[j].player->objnum == signature)) { + Assert( team != -1 ); // to help trap errors!!! + nprintf(("Network", "Making %s ship for %s\n", Ships[shipnum].ship_name, Net_players[j].player->callsign)); + multi_assign_player_ship( j, objp, Ships[shipnum].ship_info_index ); + objp->flags |= OF_PLAYER_SHIP; + objp->flags &= ~OF_COULD_BE_PLAYER; + break; + } + } + } + + + // we will have no ships in any wings at this point (we didn't create any when we loaded the + // mission). Set the current wave of this wing to be 1 less than was passed in since this value + // will get incremented in parse_wing_create_ships; + wingp->current_wave = current_wave; + wingp->total_arrived_count = total_arrived_count; + } + + GET_DATA( p_type ); + } + + PACKET_SET_SIZE(); + + // if we have reached the end of the list change our network state + if ( p_type == INGAME_WING_LIST_EOL ) { + Net_player->state = NETPLAYER_STATE_INGAME_WINGS; + send_netplayer_update_packet(); + + // add some mission sync text + multi_common_add_text(XSTR("Wings packet (ingame)\n",684)); + } + +} + +// function to send information about wings. We need to send enough information to let the client +// construct or reconstruct any wings in the mission. We will rely on the fact that the host wing array +// will exactly match the client wing array (in terms of number, and wing names) +void send_ingame_wings_packet( net_player *player ) +{ + ubyte data[MAX_PACKET_SIZE]; + ubyte p_type; + int packet_size, i; + ubyte what; + + BUILD_HEADER( WINGS_INGAME_PACKET ); + + // iterate through the wings list + for ( i = 0; i < num_wings; i++ ) { + wing *wingp; + + wingp = &Wings[i]; + + p_type = INGAME_WING_NEXT; + ADD_DATA( p_type ); + + ADD_DATA( i ); + + // add wing data that the client needs. There are several conditions to send to clients: + // + // 1. wing hasn't arrived -- total_arrived_count will be 0 + // 2. wing is done (or currently departing) + // 3. wing is present (any wave, any number of ships). + // + // 1 and 2 are easy to handle. (3) is the hardest. + if ( wingp->total_arrived_count == 0 ) { + what = INGAME_WING_NOT_ARRIVED; + ADD_DATA( what ); + } else if ( wingp->flags & (WF_WING_GONE | WF_WING_DEPARTING) ) { + what = INGAME_WING_DEPARTED; + ADD_DATA( what ); + } else { + int j; + + // include to code to possibly send more wing data here in this part of the if/else + // chain. We can do this because MAX_WINGS * 8 (8 being the number of byte for a minimum + // description of a wing) is always less than MAX_PACKET_SIZE. Checking here ensures that + // we have enough space for *this* wing in the packet, and not the largest wing. The + // formula below looks at number of ships in the wing, the name length, length of the signature, + // and the size of the bytes added before the ship names. 32 accounts for a little slop + if ( packet_size > (MAX_PACKET_SIZE - (wingp->current_count * (NAME_LENGTH+2) + 32)) ) { + p_type = INGAME_WING_LIST_EOP; + ADD_DATA( p_type ); + multi_io_send_reliable(player, data, packet_size); + BUILD_HEADER( WINGS_INGAME_PACKET ); + } + what = INGAME_WING_PRESENT; + ADD_DATA( what ); + ADD_DATA( wingp->total_arrived_count ); + ADD_DATA( wingp->current_count ); + ADD_DATA( wingp->current_wave ); + + // add the ship name and net signature of all ships currently in the wing. + for ( j = 0; j < wingp->current_count; j++ ) { + ship *shipp; + + shipp = &Ships[wingp->ship_index[j]]; + //ADD_STRING( shipp->ship_name ); + ADD_DATA( Objects[shipp->objnum].net_signature ); + } + } + + } + + p_type = INGAME_WING_LIST_EOL; + ADD_DATA( p_type ); + + multi_io_send_reliable(player, data, packet_size); +} +*/ + +// send a request or a reply regarding ingame join ship choice +void send_ingame_ship_request_packet(int code,int rdata,net_player *pl) +{ + ubyte data[MAX_PACKET_SIZE],val; + ship *shipp; + int i, packet_size = 0; + ushort signature; + p_object *pobj; + + // add the data + BUILD_HEADER(INGAME_SHIP_REQUEST); + + // add the code + ADD_DATA(code); + + // add any code specific data + switch(code){ + case INGAME_SR_REQUEST: + // add the net signature of the ship we're requesting + signature = (ushort)rdata; + ADD_DATA( signature ); + break; + case INGAME_SR_CONFIRM: + // get a pointer to the ship + shipp = &Ships[Objects[rdata].instance]; + + // add the most recent position and orientation for the requested ship + ADD_DATA(Objects[rdata].pos); + ADD_ORIENT(Objects[rdata].orient); + ADD_DATA( Missiontime ); + + // add the # of respawns this ship has left + pobj = mission_parse_get_arrival_ship( Objects[rdata].net_signature ); + Assert(pobj != NULL); + ADD_DATA(pobj->respawn_count); + + // add the ships ets settings + val = (ubyte)shipp->weapon_recharge_index; + ADD_DATA(val); + val = (ubyte)shipp->shield_recharge_index; + ADD_DATA(val); + val = (ubyte)shipp->engine_recharge_index; + ADD_DATA(val); + + // add current primary and secondary banks, and add link status + val = (ubyte)shipp->weapons.current_primary_bank; + ADD_DATA(val); + val = (ubyte)shipp->weapons.current_secondary_bank; + ADD_DATA(val); + + // add the current ammo count for secondary banks; + val = (ubyte)shipp->weapons.num_secondary_banks; // for sanity checking + ADD_DATA(val); + for ( i = 0; i < shipp->weapons.num_secondary_banks; i++ ) { + Assert( shipp->weapons.secondary_bank_ammo[i] < UCHAR_MAX ); + val = (ubyte)shipp->weapons.secondary_bank_ammo[i]; + ADD_DATA(val); + } + + // add the link status of weapons + // primary link status + val = 0; + if(shipp->flags & SF_PRIMARY_LINKED){ + val |= (1<<0); + } + if(shipp->flags & SF_SECONDARY_DUAL_FIRE){ + val |= (1<<1); + } + ADD_DATA(val); + break; + } + + // send the packet + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + Assert(pl != NULL); + multi_io_send_reliable(pl, data, packet_size); + } else { + multi_io_send_reliable(Net_player, data, packet_size); + } + + // if this is a confirm to a player -- send data to the other players in the game telling them + if ( (code == INGAME_SR_CONFIRM) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) ) { + int i, player_num; + + player_num = NET_PLAYER_NUM(pl); + code = INGAME_PLAYER_CHOICE; + BUILD_HEADER(INGAME_SHIP_REQUEST); + ADD_DATA(code); + ADD_DATA(player_num); + ADD_DATA(Objects[rdata].net_signature); + for (i = 0; i < MAX_PLAYERS; i++ ) { + if(MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) && (i != player_num) ) { + multi_io_send_reliable(&Net_players[i], data, packet_size); + } + } + } +} + +// function to validate all players in the game according to their team select index. If discrepancies +// are found, this function should be able to fix them up. +void multi_ingame_validate_players() +{ + int i; + + for ( i = 0; i < MAX_PLAYERS; i++ ) { + if( MULTI_CONNECTED(Net_players[i]) && (Net_player != &Net_players[i]) && !MULTI_STANDALONE(Net_players[i]) ) { + char *ship_name; + int shipnum, objnum, player_objnum; + + player_objnum = Net_players[i].player->objnum; + if ( (Objects[player_objnum].type != OBJ_SHIP) || (Objects[player_objnum].type != OBJ_GHOST) ) { + Int3(); + } + + ship_name = multi_ts_get_shipname( Net_players[i].p_info.team, Net_players[i].p_info.ship_index ); + Assert( ship_name != NULL ); + shipnum = ship_name_lookup( ship_name ); + if ( shipnum == -1 ) { + // ship could be respawning + continue; + } + objnum = Ships[shipnum].objnum; + Assert( objnum != -1 ); + + // if this guy's objnum isn't a ship, then it should proably be a ghost!! + if ( Objects[objnum].type == OBJ_SHIP ) { + if ( objnum != Net_players[i].player->objnum ) { + Int3(); + Net_players[i].player->objnum = objnum; + } + } else { + Assert( Objects[objnum].type == OBJ_GHOST ); + } + } + } +} + +// process an ingame ship request packet +void process_ingame_ship_request_packet(ubyte *data, header *hinfo) +{ + int code; + object *objp; + int offset = HEADER_LENGTH; + int team, slot_index, i; + uint respawn_count; + ubyte val, num_secondary_banks; + p_object *pobj; + + // get the code + GET_DATA(code); + + switch(code){ + // a request for a ship from an ingame joiner + case INGAME_SR_REQUEST: + int player_num; + ushort sig_request; + + // lookup the player and make sure he doesn't already have an objnum (along with possible error conditions) + GET_DATA(sig_request); + PACKET_SET_SIZE(); + + player_num = find_player_id(hinfo->id); + if(player_num == -1){ + nprintf(("Network","Received ingame ship request packet from unknown player!!\n")); + break; + } + + // make sure this player doesn't already have an object + Assert(MULTI_CONNECTED(Net_players[player_num])); + if(Net_players[player_num].player->objnum != -1){ + send_ingame_ship_request_packet(INGAME_SR_DENY,0,&Net_players[player_num]); + break; + } + + // try and find the object + objp = NULL; + objp = multi_get_network_object(sig_request); + if(objp == NULL || !(objp->flags & OF_COULD_BE_PLAYER)){ + send_ingame_ship_request_packet(INGAME_SR_DENY,0,&Net_players[player_num]); + break; + } + + // Assign the player this objnum and ack him + Net_players[player_num].player->objnum = OBJ_INDEX(objp); + Net_players[player_num].state = NETPLAYER_STATE_IN_MISSION; // since he'll do this anyway... + Net_players[player_num].flags &= ~(NETINFO_FLAG_INGAME_JOIN); + multi_assign_player_ship( player_num, objp, Ships[objp->instance].ship_info_index ); + + // update his ets and link status stuff + multi_server_update_player_weapons(&Net_players[player_num],&Ships[objp->instance]); + + objp->flags &= ~(OF_COULD_BE_PLAYER); + objp->flags |= OF_PLAYER_SHIP; + + // send a player settings packet to update all other players of this guy's final choices + send_player_settings_packet(); + + // initialize datarate limiting for this guy + multi_oo_rate_init(&Net_players[player_num]); + + // ack him + send_ingame_ship_request_packet(INGAME_SR_CONFIRM,OBJ_INDEX(objp),&Net_players[player_num]); + + // clear my ingame join flag so that others may join + Netgame.flags &= ~NG_FLAG_INGAME_JOINING; + + // clear his net stats + scoring_level_init( &(Net_players[player_num].player->stats) ); + break; + + // a denial for the ship we requested from the server + case INGAME_SR_DENY : + PACKET_SET_SIZE(); + + // set this to -1 so we can pick again + Multi_ingame_join_sig = 0; + + // display a popup + popup(PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been denied the requested ship",686)); + break; + + // a confirmation that we can use the selected ship + case INGAME_SR_CONFIRM: + // object *temp_objp; + + // delete the ship this ingame joiner was using. Unassign Player_obj so that this object + // doesn't become a ghost. + // temp_objp = Player_obj; + // Player_obj = NULL; + // obj_delete( OBJ_INDEX(temp_objp) ); + + // get the object itself + objp = multi_get_network_object(Multi_ingame_join_sig); + Assert(objp != NULL); + + // get its most recent position and orientation + GET_DATA(objp->pos); + GET_ORIENT(objp->orient); + GET_DATA( Missiontime ); + GET_DATA( respawn_count ); + + // tell the server I'm in the mission + Net_player->state = NETPLAYER_STATE_IN_MISSION; + send_netplayer_update_packet(); + + // setup our object + Net_player->player->objnum = OBJ_INDEX(objp); + Player_obj = objp; + Player_obj->flags &= ~(OF_COULD_BE_PLAYER); + Player_obj->flags |= OF_PLAYER_SHIP; + multi_assign_player_ship( MY_NET_PLAYER_NUM, objp, Ships[objp->instance].ship_info_index ); + + // must change the ship type and weapons. An ingame joiner know about the default class + // and weapons for a ship, but these could have changed. + multi_ts_get_team_and_slot(Player_ship->ship_name, &team, &slot_index); + Assert( team != -1 ); + Assert( slot_index != -1 ); + change_ship_type(objp->instance, Wss_slots_teams[team][slot_index].ship_class); + wl_bash_ship_weapons(&Player_ship->weapons,&Wss_slots_teams[team][slot_index]); + + // get the parse object for it and assign the respawn count + pobj = mission_parse_get_arrival_ship( objp->net_signature ); + Assert(pobj != NULL); + pobj->respawn_count = respawn_count; + + // get the ships ets settings + GET_DATA(val); + Player_ship->weapon_recharge_index = val; + GET_DATA(val); + Player_ship->shield_recharge_index = val; + GET_DATA(val); + Player_ship->engine_recharge_index = val; + + // get current primary and secondary banks, and add link status + GET_DATA(val); + Player_ship->weapons.current_primary_bank = val; + GET_DATA(val); + Player_ship->weapons.current_secondary_bank = val; + + // secondary bank ammo data + GET_DATA( num_secondary_banks ); + Assert( num_secondary_banks == Player_ship->weapons.num_secondary_banks ); + for ( i = 0; i < Player_ship->weapons.num_secondary_banks; i++ ) { + GET_DATA(val); + Player_ship->weapons.secondary_bank_ammo[i] = val; + } + + + // get the link status of weapons + GET_DATA(val); + if(val & (1<<0)){ + Player_ship->flags |= SF_PRIMARY_LINKED; + } + if(val & (1<<1)){ + Player_ship->flags |= SF_SECONDARY_DUAL_FIRE; + } + PACKET_SET_SIZE(); + + // be sure that this ships current primary/secondary weapons are valid. Easiest is to just + // bash the values to 0! + /* + if ( Player_ship->weapons.current_primary_bank == -1 ) + Player_ship->weapons.current_primary_bank = 0; + if ( Player_ship->weapons.current_secondary_bank == -1 ) + Player_ship->weapons.current_secondary_bank = 0; + */ + + Net_player->flags &= ~(NETINFO_FLAG_INGAME_JOIN); + + // clear all object collision pairs, then add myself to the list + extern void obj_reset_all_collisions(); + obj_reset_all_collisions(); + // obj_reset_pairs(); + // obj_add_pairs( OBJ_INDEX(Player_obj) ); + + mission_hotkey_set_defaults(); + + //multi_ingame_validate_players(); + + // jump into the mission + // NOTE : we check this flag because its possible that the player could have received an endgame packet in the same + // frame as getting this confirmation. In that case, he should be quitting to the main menu. We must not make + // him continue on into the mission + if(!multi_endgame_ending()){ + gameseq_post_event(GS_EVENT_ENTER_GAME); + } + break; + + case INGAME_PLAYER_CHOICE: { + int player_num; + ushort net_signature; + object *objp; + + // get the player number of this guy, and the net signature of the ship he has chosen + GET_DATA(player_num); + GET_DATA(net_signature); + PACKET_SET_SIZE(); + + objp = multi_get_network_object(net_signature); + if ( objp == NULL ) { + // bogus!!! couldn't find the object -- we cannot connect his -- this is really bad!!! + nprintf(("Network", "Couldn't find ship for ingame joiner %s\n", Net_players[player_num].player->callsign)); + break; + } + objp->flags |= OF_PLAYER_SHIP; + objp->flags &= ~OF_COULD_BE_PLAYER; + + multi_assign_player_ship( player_num, objp, Ships[objp->instance].ship_info_index ); + + break; + } + } +} + + +// -------------------------------------------------------------------------------------------------- +// INGAME JOIN FORWARD DEFINITIONS +// + +void multi_ingame_send_ship_update(net_player *p) +{ + ship_obj *moveup; + + // get the first object on the list + moveup = GET_FIRST(&Ship_obj_list); + + // go through the list and send all ships which are mark as OF_COULD_BE_PLAYER + while(moveup!=END_OF_LIST(&Ship_obj_list)){ + if(Objects[moveup->objnum].flags & OF_COULD_BE_PLAYER){ + // send the update + send_ingame_ship_update_packet(p,&Ships[Objects[moveup->objnum].instance]); + } + + // move to the next item + moveup = GET_NEXT(moveup); + } +} + +// for now, I guess we'll just send hull and shield % values +void send_ingame_ship_update_packet(net_player *p,ship *sp) +{ + ubyte data[MAX_PACKET_SIZE]; + object *objp; + int idx; + int packet_size = 0; + float f_tmp; + + BUILD_HEADER(INGAME_SHIP_UPDATE); + + // just send net signature, shield and hull percentages + objp = &Objects[sp->objnum]; + ADD_DATA(objp->net_signature); + ADD_DATA(objp->flags); + ADD_DATA(objp->hull_strength); + + // shield percentages + for(idx=0; idxshields[idx]; + ADD_DATA(f_tmp); + } + + multi_io_send_reliable(p, data, packet_size); +} + +void process_ingame_ship_update_packet(ubyte *data, header *hinfo) +{ + int offset; + float garbage; + int flags; + int idx; + ushort net_sig; + object *lookup; + float f_tmp; + + offset = HEADER_LENGTH; + // get the net sig for the ship and do a lookup + GET_DATA(net_sig); + GET_DATA(flags); + + // get the object + lookup = multi_get_network_object(net_sig); + if(lookup == NULL){ + // read in garbage values if we can't find the ship + nprintf(("Network","Got ingame ship update for unknown object\n")); + GET_DATA(garbage); + for(idx=0;idxflags = flags; + GET_DATA(lookup->hull_strength); + for(idx=0;idxshields[idx] = f_tmp; + } + + PACKET_SET_SIZE(); +} + diff --git a/src/network/multi_kick.cpp b/src/network/multi_kick.cpp new file mode 100644 index 0000000..4842a8c --- /dev/null +++ b/src/network/multi_kick.cpp @@ -0,0 +1,361 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_kick.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 8 10/13/99 3:51p Jefff + * fixed unnumbered XSTRs + * + * 7 3/10/99 6:50p Dave + * Changed the way we buffer packets for all clients. Optimized turret + * fired packets. Did some weapon firing optimizations. + * + * 6 3/09/99 6:24p Dave + * More work on object update revamping. Identified several sources of + * unnecessary bandwidth. + * + * 5 11/19/98 8:03a Dave + * Full support for D3-style reliable sockets. Revamped packet lag/loss + * system, made it receiver side and at the lowest possible level. + * + * 4 11/17/98 11:12a Dave + * Removed player identification by address. Now assign explicit id #'s. + * + * 3 11/05/98 5:55p Dave + * Big pass at reducing #includes + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 16 9/15/98 7:24p Dave + * Minor UI changes. Localized bunch of new text. + * + * 15 9/11/98 5:53p Dave + * Final revisions to kick system changes. + * + * 14 9/11/98 5:08p Dave + * More tweaks to kick notification system. + * + * 13 9/11/98 4:14p Dave + * Fixed file checksumming of < file_size. Put in more verbose kicking and + * PXO stats store reporting. + * + * 12 6/13/98 9:32p Mike + * Kill last character in file which caused "Find in Files" to report the + * file as "not a text file." + * + * 11 6/13/98 6:01p Hoffoss + * Externalized all new (or forgot to be added) strings to all the code. + * + * 10 4/23/98 6:18p Dave + * Store ETS values between respawns. Put kick feature in the text + * messaging system. Fixed text messaging system so that it doesn't + * process or trigger ship controls. Other UI fixes. + * + * 9 4/04/98 4:22p Dave + * First rev of UDP reliable sockets is done. Seems to work well if not + * overly burdened. + * + * 8 4/03/98 1:03a Dave + * First pass at unreliable guaranteed delivery packets. + * + * 7 4/01/98 11:19p Dave + * Put in auto-loading of xferred pilot pic files. Grey out background + * behind pinfo popup. Put a chatbox message in when players are kicked. + * Moved mission title down in briefing. Other ui fixes. + * + * 6 3/30/98 6:27p Dave + * Put in a more official set of multiplayer options, including a system + * for distributing netplayer and netgame settings. + * + * 5 3/24/98 5:00p Dave + * Fixed several ui bugs. Put in pre and post voice stream playback sound + * fx. Put in error specific popups for clients getting dropped from games + * through actions other than their own. + * + * 4 3/18/98 5:52p Dave + * Put in netgame password popup. Numerous ui changes. Laid groundwork for + * streamed multi_voice data. + * + * 3 3/17/98 5:29p Dave + * Minor bug fixes in player select menu. Solidified mp joining process. + * Made furball mode support ingame joiners and dropped players correctly. + * + * 2 3/15/98 4:17p Dave + * Fixed oberver hud problems. Put in handy netplayer macros. Reduced size + * of network orientation matrices. + * + * 1 2/12/98 4:38p Dave + * Multiplayer kick functionality + * + * $NoKeywords: $ + */ +#include "multi.h" +#include "multi_kick.h" +#include "multimsgs.h" +#include "multiutil.h" +#include "freespace.h" +#include "chatbox.h" +#include "timer.h" + +// ---------------------------------------------------------------------------------- +// KICK DEFINES/VARS +// + +#define MULTI_KICK_RESPONSE_TIME 4000 // if someone who has been kicked has not responded in this time, disconnect him hard + +#define MAX_BAN_SLOTS 30 +net_addr Multi_kick_ban_slots[MAX_BAN_SLOTS]; // banned addresses +int Multi_kick_num_ban_slots; // the # of banned addresses + +// ---------------------------------------------------------------------------------- +// KICK FORWARD DECLARATIONS +// + +// send a player kick packet +void send_player_kick_packet(int player_index, int ban = 1, int reason = KICK_REASON_NORM); + +// process a player kick packet +void process_player_kick_packet(ubyte *data, header *hinfo); + +// add a net address to the banned list +void multi_kick_add_ban(net_addr *addr); + +// can the given player perform a kick +int multi_kick_can_kick(net_player *player); + + +// ---------------------------------------------------------------------------------- +// KICK FUNCTIONS +// + +// initialize all kicking details (ban lists, etc). it is safe to call this function at any time +void multi_kick_init() +{ + // blast all the ban slots + memset(Multi_kick_ban_slots,0,sizeof(net_addr)*MAX_BAN_SLOTS); + Multi_kick_num_ban_slots = 0; +} + +// process all kick details (disconnecting players who have been kicked but haven't closed their socket) +void multi_kick_process() +{ + int idx; + + // if i'm not the server, don't do anything + if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + return; + } + + // disconnect any kicked players who have timed out on leaving + for(idx=0;idxflags & NETINFO_FLAG_AM_MASTER){ + // if we're supposed to ban him, add his address to the banned list + if(ban){ + multi_kick_add_ban(&Net_players[player_index].p_info.addr); + } + + // mark him as having been kicked + Net_players[player_index].flags |= NETINFO_FLAG_KICKED; + + // set his kick timestamp and send him a leave game packet + Net_players[player_index].s_info.kick_timestamp = timestamp(MULTI_KICK_RESPONSE_TIME); + Net_players[player_index].s_info.kick_reason = reason; + send_leave_game_packet(Net_players[player_index].player_id, reason, &Net_players[player_index]); + + // tell everyone else that he was kicked + send_leave_game_packet(Net_players[player_index].player_id, reason); + + // wait until he either shuts his connection down or he times out) + // add the string to the chatbox and the hud (always safe - if it is not inited, nothing bad will happen) + char str[512]; + memset(str, 0, 512); + sprintf(str, XSTR("", 1501), Net_players[player_index].player->callsign); + multi_display_chat_msg(str, player_index, 0); + } + // otherwise, we should send the packet indicating that this guy should be kicked + else { + send_player_kick_packet(player_index, ban, reason); + } + } +} + +// is this net address currently kicked and banded +int multi_kick_is_banned(net_addr *addr) +{ + int idx; + + // traverse the banned list + for(idx=0;idxcallsign,Dc_arg)==0)){ + player_num = idx; + break; + } + } + + // if we didn't find the player, notify of the results + if(player_num == -1){ + dc_printf("Could not find player %s to kick!",Dc_arg); + } + // if we found the guy, then try and kick him + else { + multi_kick_player(player_num); + } +} + +// fill in the passed string with the appropriate "kicked" string +void multi_kick_get_text(net_player *pl, int reason, char *str) +{ + // safety net + if((pl == NULL) || (pl->player == NULL)){ + strcpy(str, NOX("")); + } + + switch(reason){ + case KICK_REASON_BAD_XFER: + sprintf(str, XSTR("<%s was kicked because of mission file xfer failure>", 1003), pl->player->callsign); + break; + case KICK_REASON_CANT_XFER: + sprintf(str, XSTR("<%s was kicked for not having builtin mission %s>", 1004), pl->player->callsign, Game_current_mission_filename); + break; + case KICK_REASON_INGAME_ENDED: + sprintf(str, XSTR("<%s was kicked for ingame joining an ended game>",1005), pl->player->callsign); + break; + default: + sprintf(str, XSTR("<%s was kicked>",687), pl->player->callsign); + break; + } +} + + +// ---------------------------------------------------------------------------------- +// KICK FORWARD DEFINITIONS +// + +// add a net address to the banned list +void multi_kick_add_ban(net_addr *addr) +{ + // if we still have any slots left + if(Multi_kick_num_ban_slots < (MAX_BAN_SLOTS - 1)){ + memcpy(&Multi_kick_ban_slots[Multi_kick_num_ban_slots++],addr,sizeof(net_addr)); + } +} + + +// ---------------------------------------------------------------------------------- +// KICK PACKET HANDLERS +// + +// send a player kick packet +void send_player_kick_packet(int player_index, int ban, int reason) +{ + ubyte data[MAX_PACKET_SIZE]; + int packet_size = 0; + + BUILD_HEADER(KICK_PLAYER); + + // add the address of the player to be kicked + ADD_DATA(Net_players[player_index].player_id); + + // indicate if he should be banned + ADD_DATA(ban); + ADD_DATA(reason); + + // send the request to the server + multi_io_send_reliable(Net_player, data, packet_size); +} + +// process a player kick packet +void process_player_kick_packet(ubyte *data, header *hinfo) +{ + int player_num,from_player,ban,reason; + short player_id; + int offset = HEADER_LENGTH; + + // get the address of the guy who is to be kicked + GET_DATA(player_id); + GET_DATA(ban); + GET_DATA(reason); + player_num = find_player_id(player_id); + PACKET_SET_SIZE(); + + // only the server should ever receive a request to kick a guy + Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER); + + // determine who sent the packet + from_player = find_player_id(hinfo->id); + + // check to see if this guy is allowed to make such a request + if((from_player == -1) || !multi_kick_can_kick(&Net_players[from_player]) ){ + nprintf(("Network","Received a kick request from an invalid player!!\n")); + } + // otherwise, process the request fully + else { + // make sure we have a valid player to kick + if(player_num == -1){ + nprintf(("Network","Received request to kick an unknown player!\n")); + } else { + // will handle all the rest of the details + multi_kick_player(player_num,ban,reason); + } + } +} + +// can the given player perform a kick +int multi_kick_can_kick(net_player *player) +{ + // only host or server can kick + if((player->flags & NETINFO_FLAG_AM_MASTER) || (player->flags & NETINFO_FLAG_GAME_HOST)){ + return 1; + } + + // this guy cannot kick + return 0; +} diff --git a/src/network/multi_log.cpp b/src/network/multi_log.cpp new file mode 100644 index 0000000..b55f094 --- /dev/null +++ b/src/network/multi_log.cpp @@ -0,0 +1,231 @@ +/* + * $Logfile: /Freespace2/code/Network/multi_log.cpp $ + * $Revision$ + * $Date$ + * $Author$ + * + * Header file to support multiplayer logging functions + * + * $Log$ + * Revision 1.1 2002/05/03 03:28:10 root + * Initial revision + * + * + * 8 8/17/99 2:24p Dave + * Fixed wacky squad color stuff. + * + * 7 6/07/99 9:51p Dave + * Consolidated all multiplayer ports into one. + * + * 6 4/27/99 2:59p Dave + * Potential fix for reliable socket connection problem. + * + * 5 4/09/99 2:21p Dave + * Multiplayer beta stuff. CD checking. + * + * 4 1/24/99 11:37p Dave + * First full rev of beam weapons. Very customizable. Removed some bogus + * Int3()'s in low level net code. + * + * 3 11/19/98 4:19p Dave + * Put IPX sockets back in psnet. Consolidated all multiplayer config + * files into one. + * + * 2 10/07/98 10:53a Dave + * Initial checkin. + * + * 1 10/07/98 10:50a Dave + * + * 3 8/21/98 1:14p Dave + * Put in log system hooks in useful places. + * + * 2 8/20/98 5:31p Dave + * Put in handy multiplayer logfile system. Now need to put in useful + * applications of it all over the code. + * + * 1 8/20/98 2:00p Dave + * + * + * $NoKeywords: $ + */ + +#include +#include +#include "multi_log.h" +#include "multi_options.h" +#include "cmdline.h" +#include "cfile.h" + +// ---------------------------------------------------------------------------------------------------- +// MULTI LOGFILE DEFINES/VARS +// + +// max length for a line of the logfile +#define MAX_LOGFILE_LINE_LEN 256 + +// name of the multiplayer logfile +#define MULTI_LOGFILE_NAME "multi.log" + +// echo all ml_printf's to the debug window +#define MULTI_LOGFILE_ECHO_TO_DEBUG + +// how often we'll write an update to the logfile (in seconds) +#define MULTI_LOGFILE_UPDATE_TIME 2520 // every 42 minutes + +// outfile itself +CFILE *Multi_log_out = NULL; + +// time when the logfile was opened +int Multi_log_open_systime = -1; + +// time when we last updated the logfile +int Multi_log_update_systime = -1; + +// ---------------------------------------------------------------------------------------------------- +// MULTI LOGFILE FUNCTIONS +// + +// write the standard header to the logfile +void multi_log_write_header() +{ + char str[1024]; + time_t timer; + + // header message + timer = time(NULL); + strftime(str, 1024, "Freespace Multi Log - Opened %a, %b %d, %Y at %I:%M%p\n----\n----\n----\n\n", localtime(&timer)); + ml_string(str, 0); +} + +// write the standard shutdown trailer +void multi_log_write_trailer() +{ + char str[1024]; + time_t timer; + + // header message + timer = time(NULL); + strftime(str, 1024, "\n\n----\n----\n----\nFreespace Multi Log - Closing on %a, %b %d, %Y at %I:%M%p", localtime(&timer)); + ml_string(str, 0); +} + +// write out some info about stuff +void multi_log_write_update() +{ + int diff = (int)difftime(time(NULL), Multi_log_open_systime); + int hours, mins, seconds; + + // figure out some time values + hours = diff / 3600; + mins = (diff - (hours * 3600)) / 60; + seconds = (diff - (hours * 3600) - (mins * 60)); + + // print it out + ml_printf("Server has been active for %d hours, %d minutes, and %d seconds", hours, mins, seconds); +} + +// initialize the multi logfile +void multi_log_init() +{ + // attempt to open the file + Multi_log_out = cfopen(MULTI_LOGFILE_NAME, "wt", CFILE_NORMAL, CF_TYPE_DATA); + + // if we successfully opened the file, write the header + if(Multi_log_out != NULL){ + multi_log_write_header(); + + // initialize our timer info + Multi_log_open_systime = time(NULL); + Multi_log_update_systime = Multi_log_open_systime; + } else { + nprintf(("Network","Error opening %s for writing!!\n",MULTI_LOGFILE_NAME)); + } +} + +// close down the multi logfile +void multi_log_close() +{ + // if we have a valid file, write a trailer and close + if(Multi_log_out != NULL){ + multi_log_write_trailer(); + + cfclose(Multi_log_out); + Multi_log_out = NULL; + } +} + +// give some processing time to the logfile system so it can check up on stuff +void multi_log_process() +{ + // if we don't have a valid logfile, do nothing + if(Multi_log_out == NULL){ + return; + } + + // check to see if we've been active a long enough time, and + if(time(NULL) - Multi_log_update_systime > MULTI_LOGFILE_UPDATE_TIME){ + // write the update + multi_log_write_update(); + + Multi_log_update_systime = time(NULL); + } +} + +// printf function itself called by the ml_printf macro +void ml_printf(char *format, ...) +{ + char tmp[MAX_LOGFILE_LINE_LEN*4]; + va_list args; + + // if we don't have a valid logfile do nothing + if(Multi_log_out == NULL){ + return; + } + + // format the text + va_start(args, format); + vsprintf(tmp, format, args); + va_end(args); + + // log the string + ml_string(tmp); +} + +// string print function +void ml_string(char *string, int add_time) +{ + char tmp[MAX_LOGFILE_LINE_LEN*4]; + char time_str[128]; + time_t timer; + + // if we don't have a valid logfile do nothing + if(Multi_log_out == NULL){ + return; + } + + // if the passed string is NULL, do nothing + if(string == NULL){ + return; + } + + // maybe add the time + if(add_time){ + timer = time(NULL); + + strftime(time_str, 128, "%m/%d %H:%M:%S~ ", localtime(&timer)); + strcpy(tmp, time_str); + strcat(tmp, string); + } else{ + strcpy(tmp, string); + } + strcat(tmp, "\n"); + + // now print it to the logfile if necessary + cfputs(tmp, Multi_log_out); + cflush(Multi_log_out); + +#if defined(MULTI_LOGFILE_ECHO_TO_DEBUG) + // nprintf(("Network","%s\n",tmp)); + mprintf(("ML %s", tmp)); +#endif +} \ No newline at end of file diff --git a/src/network/multi_obj.cpp b/src/network/multi_obj.cpp new file mode 100644 index 0000000..8b93db6 --- /dev/null +++ b/src/network/multi_obj.cpp @@ -0,0 +1,1987 @@ +#include "freespace.h" +#include "timer.h" +#include "linklist.h" +#include "weapon.h" +#include "multimsgs.h" +#include "multiutil.h" +#include "multi_obj.h" +#include "multi_rate.h" +#include "multi.h" +#include "object.h" +#include "key.h" +#include "gamesnd.h" +#include "spline.h" +#include "alphacolors.h" +#include "afterburner.h" + +// --------------------------------------------------------------------------------------------------- +// OBJECT UPDATE DEFINES/VARS +// + +// test stuff +float oo_arrive_time[MAX_SHIPS][5]; // the last 5 arrival times for each ship +int oo_arrive_time_count[MAX_SHIPS]; // size of the arrival queue +float oo_arrive_time_avg_diff[MAX_SHIPS]; // the average time between arrivals +float oo_arrive_time_next[MAX_SHIPS]; // how many seconds have gone by. should be equal to oo_arrive_time_avg_diff[] the next time we get an update + +// interp stuff +int oo_interp_count[MAX_SHIPS]; +vector oo_interp_points[MAX_SHIPS][2]; +bez_spline oo_interp_splines[MAX_SHIPS][2]; +void multi_oo_calc_interp_splines(int ship_index, vector *cur_pos, matrix *cur_orient, physics_info *cur_phys_info, vector *new_pos, matrix *new_orient, physics_info *new_phys_info); + +// how much data we're willing to put into a given oo packet +#define OO_MAX_SIZE 480 + +// tolerance for bashing position +#define OO_POS_UPDATE_TOLERANCE 100.0f + +// new improved - more compacted info type +#define OO_POS_NEW (1<<0) // +#define OO_ORIENT_NEW (1<<1) // +#define OO_HULL_NEW (1<<2) // Hull AND shields +#define OO_AFTERBURNER_NEW (1<<3) // +#define OO_SUBSYSTEMS_AND_AI_NEW (1<<4) // +#define OO_PRIMARY_BANK (1<<5) // if this is set, fighter has selected bank one +#define OO_PRIMARY_LINKED (1<<6) // if this is set, banks are linked +#define OO_TRIGGER_DOWN (1<<7) // if this is set, trigger is DOWN + +#define OO_VIEW_CONE_DOT (0.1f) +#define OO_VIEW_DIFF_TOL (0.15f) // if the dotproducts differ this far between frames, he's coming into view + +// no timestamp should ever have sat for longer than this. +#define OO_MAX_TIMESTAMP 2500 + +// distance class +#define OO_NEAR 0 +#define OO_NEAR_DIST (200.0f) +#define OO_MIDRANGE 1 +#define OO_MIDRANGE_DIST (600.0f) +#define OO_FAR 2 +#define OO_FAR_DIST (1400.0f) + +// how often we should send full hull/shield updates +#define OO_HULL_SHIELD_TIME 600 +#define OO_SUBSYS_TIME 1000 + +// timestamp values for object update times based on client's update level. +int Multi_oo_target_update_times[MAX_OBJ_UPDATE_LEVELS] = +{ + 100, // 15x a second + 100, // 15x a second + 66, // 30x a second + 66, +}; + +// for near ships +int Multi_oo_front_near_update_times[MAX_OBJ_UPDATE_LEVELS] = +{ + 150, // low update + 100, // medium update + 66, // high update + 66, +}; + +// for medium ships +int Multi_oo_front_medium_update_times[MAX_OBJ_UPDATE_LEVELS] = +{ + 250, // low update + 180, // medium update + 120, // high update + 66, +}; + +// for far ships +int Multi_oo_front_far_update_times[MAX_OBJ_UPDATE_LEVELS] = +{ + 750, // low update + 350, // medium update + 150, // high update + 66, +}; + +// for near ships +int Multi_oo_rear_near_update_times[MAX_OBJ_UPDATE_LEVELS] = +{ + 300, // low update + 200, // medium update + 100, // high update + 66, +}; + +// for medium ships +int Multi_oo_rear_medium_update_times[MAX_OBJ_UPDATE_LEVELS] = +{ + 800, // low update + 600, // medium update + 300, // high update + 66, +}; + +// for far ships +int Multi_oo_rear_far_update_times[MAX_OBJ_UPDATE_LEVELS] = +{ + 2500, // low update + 1500, // medium update + 400, // high update + 66, +}; + +// ship index list for possibly sorting ships based upon distance, etc +short OO_ship_index[MAX_SHIPS]; + +int OO_update_index = -1; // index into OO_update_records for displaying update record info + +// --------------------------------------------------------------------------------------------------- +// OBJECT UPDATE FUNCTIONS +// + +object *OO_player_obj; +int OO_sort = 1; + +int multi_oo_sort_func(const void *ship1, const void *ship2) +{ + object *obj1, *obj2; + short index1, index2; + float dist1, dist2; + float dot1, dot2; + vector v1, v2; + vector vn1, vn2; + + // get the 2 indices + memcpy(&index1, ship1, sizeof(short)); + memcpy(&index2, ship2, sizeof(short)); + + // if the indices are bogus, or the objnums are bogus, return ">" + if((index1 < 0) || (index2 < 0) || (Ships[index1].objnum < 0) || (Ships[index2].objnum < 0)){ + return 1; + } + + // get the 2 objects + obj1 = &Objects[Ships[index1].objnum]; + obj2 = &Objects[Ships[index2].objnum]; + + // get the distance and dot product to the player obj for both + vm_vec_sub(&v1, &OO_player_obj->pos, &obj1->pos); + dist1 = vm_vec_copy_normalize(&vn1, &v1); + vm_vec_sub(&v2, &OO_player_obj->pos, &obj2->pos); + dist2 = vm_vec_copy_normalize(&vn2, &v2); + dot1 = vm_vec_dotprod(&OO_player_obj->orient.fvec, &vn1); + dot2 = vm_vec_dotprod(&OO_player_obj->orient.fvec, &vn2); + + // objects in front take precedence + if((dot1 < 0.0f) && (dot2 >= 0.0f)){ + return 1; + } else if((dot2 < 0.0f) && (dot1 >= 0.0f)){ + return -1; + } + + // otherwise go by distance + return (dist1 <= dist2) ? -1 : 1; +} + +// build the list of ship indices to use when updating for this player +void multi_oo_build_ship_list(net_player *pl) +{ + int ship_index; + int idx; + ship_obj *moveup; + object *player_obj; + + // set all indices to be -1 + for(idx = 0;idxplayer->objnum < 0){ + return; + } + player_obj = &Objects[pl->player->objnum]; + + // go through all other relevant objects + ship_index = 0; + for ( moveup = GET_FIRST(&Ship_obj_list); moveup != END_OF_LIST(&Ship_obj_list); moveup = GET_NEXT(moveup) ) { + // if it is an invalid ship object, skip it + if((moveup->objnum < 0) || (Objects[moveup->objnum].instance < 0) || (Objects[moveup->objnum].type != OBJ_SHIP)){ + continue; + } + + // if we're a standalone server, don't send any data regarding its pseudo-ship + if((Game_mode & GM_STANDALONE_SERVER) && ((&Objects[moveup->objnum] == Player_obj) || (Objects[moveup->objnum].net_signature == STANDALONE_SHIP_SIG)) ){ + continue; + } + + // must be a ship, a weapon, and _not_ an observer + if (Objects[moveup->objnum].flags & OF_SHOULD_BE_DEAD){ + continue; + } + + // don't send info for dying ships + if (Ships[Objects[moveup->objnum].instance].flags & SF_DYING){ + continue; + } + + // never update the knossos device + if ((Ships[Objects[moveup->objnum].instance].ship_info_index >= 0) && (Ships[Objects[moveup->objnum].instance].ship_info_index < Num_ship_types) && (Ship_info[Ships[Objects[moveup->objnum].instance].ship_info_index].flags & SIF_KNOSSOS_DEVICE)){ + continue; + } + + // don't send him info for himself + if ( &Objects[moveup->objnum] == player_obj ){ + continue; + } + + // don't send info for his targeted ship here, since its always done first + if((pl->s_info.target_objnum != -1) && (moveup->objnum == pl->s_info.target_objnum)){ + continue; + } + + // add the ship + if(ship_index < MAX_SHIPS){ + OO_ship_index[ship_index++] = (short)Objects[moveup->objnum].instance; + } + } + + // maybe qsort the thing here + OO_player_obj = player_obj; + if(OO_sort){ + qsort(OO_ship_index, ship_index, sizeof(short), multi_oo_sort_func); + } +} + +// pack information for a client (myself), return bytes added +int multi_oo_pack_client_data(ubyte *data) +{ + ubyte out_flags; + ushort tnet_signature; + char t_subsys, l_subsys; + int packet_size = 0; + + // get our firing stuff + out_flags = Net_player->s_info.accum_buttons; + + // zero these values for now + Net_player->s_info.accum_buttons = 0; + + // add any necessary targeting flags + if ( Player_ai->current_target_is_locked ){ + out_flags |= OOC_TARGET_LOCKED; + } + if ( Player_ai->ai_flags & AIF_SEEK_LOCK ){ + out_flags |= OOC_TARGET_SEEK_LOCK; + } + if ( Player->locking_on_center ){ + out_flags |= OOC_LOCKING_ON_CENTER; + } + if ( (Player_ship != NULL) && (Player_ship->flags & SF_TRIGGER_DOWN) ){ + out_flags |= OOC_TRIGGER_DOWN; + } + + // send my bank info + if(Player_ship != NULL){ + if(Player_ship->weapons.current_primary_bank > 0){ + out_flags |= OOC_PRIMARY_BANK; + } + + // linked or not + if(Player_ship->flags & SF_PRIMARY_LINKED){ + out_flags |= OOC_PRIMARY_LINKED; + } + } + + // copy the final flags in + memcpy(data, &out_flags, sizeof(ubyte)); + packet_size++; + + // client targeting information + t_subsys = -1; + l_subsys = -1; + + // if nothing targeted + if(Player_ai->target_objnum == -1){ + tnet_signature = 0; + } + // if something is targeted + else { + // target net signature + tnet_signature = Objects[Player_ai->target_objnum].net_signature; + + // targeted subsys index + if(Player_ai->targeted_subsys != NULL){ + t_subsys = (char)ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum ); + } + + // locked targeted subsys index + if(Player->locking_subsys != NULL){ + l_subsys = (char)ship_get_index_from_subsys( Player->locking_subsys, Player_ai->target_objnum, 1 ); + } + } + + // add them all + memcpy(data + packet_size, &tnet_signature, sizeof(ushort)); + packet_size += sizeof(ushort); + memcpy(data + packet_size, &t_subsys, sizeof(char)); + packet_size += sizeof(char); + memcpy(data + packet_size, &l_subsys, sizeof(char)); + packet_size += sizeof(char); + + return packet_size; +} + +// pack the appropriate info into the data +#define PACK_PERCENT(v) {ubyte upercent; if(v < 0.0f){v = 0.0f;} upercent = (v * 255.0f) <= 255.0f ? (ubyte)(v * 255.0f) : (ubyte)255; memcpy(data + packet_size + header_bytes, &upercent, sizeof(ubyte)); packet_size++; } +int multi_oo_pack_data(net_player *pl, object *objp, ubyte oo_flags, ubyte *data_out) +{ + ubyte data[255]; + ubyte data_size = 0; + char percent; + ship *shipp; + ship_info *sip; + ubyte ret; + float temp; + int header_bytes; + int packet_size = 0; + + // make sure we have a valid ship + Assert(objp->type == OBJ_SHIP); + if((objp->instance >= 0) && (Ships[objp->instance].ship_info_index >= 0)){ + shipp = &Ships[objp->instance]; + sip = &Ship_info[shipp->ship_info_index]; + } else { + return 0; + } + + // invalid player + if(pl == NULL){ + return 0; + } + + // no flags -> do nothing + if(oo_flags == 0){ + // Int3(); + return 0; + } + + // if i'm the client, make sure I only send certain things + if(!MULTIPLAYER_MASTER){ + Assert(oo_flags & (OO_POS_NEW | OO_ORIENT_NEW)); + Assert(!(oo_flags & (OO_HULL_NEW | OO_SUBSYSTEMS_AND_AI_NEW))); + } + // server + else { + // Assert(oo_flags & OO_POS_NEW); + } + + // header sizes + if(MULTIPLAYER_MASTER){ + header_bytes = 5; + } else { + header_bytes = 2; + } + + // if we're a client (and therefore sending control info), pack client-specific info + if((Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + packet_size += multi_oo_pack_client_data(data + packet_size + header_bytes); + } + + // position, velocity + if ( oo_flags & OO_POS_NEW ) { + ret = (ubyte)multi_pack_unpack_position( 1, data + packet_size + header_bytes, &objp->pos ); + packet_size += ret; + + // global records + multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret); + + ret = (ubyte)multi_pack_unpack_vel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info ); + packet_size += ret; + + // global records + multi_rate_add(NET_PLAYER_NUM(pl), "pos", ret); + } + + // orientation + if(oo_flags & OO_ORIENT_NEW){ + ret = (ubyte)multi_pack_unpack_orient( 1, data + packet_size + header_bytes, &objp->orient ); + // Assert(ret == OO_ORIENT_RET_SIZE); + packet_size += ret; + multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret); + + ret = (ubyte)multi_pack_unpack_rotvel( 1, data + packet_size + header_bytes, &objp->orient, &objp->pos, &objp->phys_info ); + packet_size += ret; + + // global records + multi_rate_add(NET_PLAYER_NUM(pl), "ori", ret); + } + + // forward thrust + percent = (char)(objp->phys_info.forward_thrust * 100.0f); + Assert( percent <= 100 ); + + memcpy(data + packet_size + header_bytes, &percent, sizeof(char)); + packet_size += 1; + + // global records + multi_rate_add(NET_PLAYER_NUM(pl), "fth", 1); + + // hull info + if ( oo_flags & OO_HULL_NEW ){ + // add the hull value for this guy + temp = (objp->hull_strength / Ship_info[shipp->ship_info_index].initial_hull_strength); + PACK_PERCENT(temp); + multi_rate_add(NET_PLAYER_NUM(pl), "hul", 1); + + // pack 2 shield values into each byte + + // pack quadrant 1 + temp = (objp->shields[0] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS)); + PACK_PERCENT(temp); + + // pack quadrant 2 + temp = (objp->shields[1] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS)); + PACK_PERCENT(temp); + + // pack quadrant 3 + temp = (objp->shields[2] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS)); + PACK_PERCENT(temp); + + // pack quadrant 2 + temp = (objp->shields[3] / (Ship_info[shipp->ship_info_index].shields / MAX_SHIELD_SECTIONS)); + PACK_PERCENT(temp); + + multi_rate_add(NET_PLAYER_NUM(pl), "shl", 4); + } + + // subsystem info + if( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ){ + ubyte ns; + ship_subsys *subsysp; + + // just in case we have some kind of invalid data (should've been taken care of earlier in this function) + if(shipp->ship_info_index < 0){ + ns = 0; + memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte)); + packet_size++; + + multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1); + } + // add the # of subsystems, and their data + else { + ns = (ubyte)Ship_info[shipp->ship_info_index].n_subsystems; + memcpy(data + packet_size + header_bytes, &ns, sizeof(ubyte)); + packet_size++; + multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1); + + // now the subsystems. + for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) { + temp = (float)subsysp->current_hits / (float)subsysp->system_info->max_hits; + PACK_PERCENT(temp); + + multi_rate_add(NET_PLAYER_NUM(pl), "sub", 1); + } + } + + // ai mode info + ubyte umode = (ubyte)(Ai_info[shipp->ai_index].mode); + short submode = (short)(Ai_info[shipp->ai_index].submode); + ushort target_signature; + + target_signature = 0; + if ( Ai_info[shipp->ai_index].target_objnum != -1 ){ + target_signature = Objects[Ai_info[shipp->ai_index].target_objnum].net_signature; + } + + memcpy(data + packet_size + header_bytes, &umode, sizeof(ubyte)); + packet_size++; + memcpy(data + packet_size + header_bytes, &submode, sizeof(short)); + packet_size += 2; + memcpy(data + packet_size + header_bytes, &target_signature, sizeof(ushort)); + packet_size += 2; + + multi_rate_add(NET_PLAYER_NUM(pl), "aim", 5); + + // primary weapon energy + temp = shipp->weapon_energy / sip->max_weapon_reserve; + PACK_PERCENT(temp); + } + + // afterburner info + oo_flags &= ~PF_AFTERBURNER_ON; + if(objp->phys_info.flags & PF_AFTERBURNER_ON){ + oo_flags |= OO_AFTERBURNER_NEW; + } + + // if this ship is a support ship, send some extra info + ubyte support_extra = 0; + if(MULTIPLAYER_MASTER && (sip->flags & SIF_SUPPORT) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){ + ushort dock_sig; + + // flag + support_extra = 1; + memcpy(data + packet_size + header_bytes, &support_extra, sizeof(ubyte)); + packet_size += 1; + memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].ai_flags, sizeof(int)); + packet_size += 4; + memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].mode, sizeof(int)); + packet_size += 4; + memcpy(data + packet_size + header_bytes, &Ai_info[shipp->ai_index].submode, sizeof(int)); + packet_size += 4; + if((Ai_info[shipp->ai_index].dock_objnum < 0) || (Ai_info[shipp->ai_index].dock_objnum >= MAX_OBJECTS)){ + dock_sig = 0; + } else { + dock_sig = Objects[Ai_info[shipp->ai_index].dock_objnum].net_signature; + } + memcpy(data + packet_size + header_bytes, &dock_sig, sizeof(ushort)); + packet_size += 2; + } else { + support_extra = 0; + memcpy(data + packet_size + header_bytes, &support_extra, sizeof(ubyte)); + packet_size += 1; + } + + // make sure we have a valid chunk of data + Assert(packet_size < 255); + if(packet_size >= 255){ + return 0; + } + data_size = (ubyte)packet_size; + + // add the object's net signature, type and oo_flags + packet_size = 0; + // don't add for clients + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + multi_rate_add(NET_PLAYER_NUM(pl), "sig", 2); + ADD_DATA( objp->net_signature ); + + multi_rate_add(NET_PLAYER_NUM(pl), "flg", 1); + ADD_DATA( oo_flags ); + } + multi_rate_add(NET_PLAYER_NUM(pl), "siz", 1); + ADD_DATA( data_size ); + + multi_rate_add(NET_PLAYER_NUM(pl), "seq", 1); + ADD_DATA( shipp->np_updates[NET_PLAYER_NUM(pl)].seq ); + + packet_size += data_size; + + // copy to the outgoing data + memcpy(data_out, data, packet_size); + + return packet_size; +} + +// unpack information for a client , return bytes processed +int multi_oo_unpack_client_data(net_player *pl, ubyte *data) +{ + ubyte in_flags; + ship *shipp; + int offset = 0; + + memcpy(&in_flags, data, sizeof(ubyte)); + offset++; + + // get the player ship + shipp = NULL; + if((pl->player->objnum >= 0) && (Objects[pl->player->objnum].type == OBJ_SHIP) && (Objects[pl->player->objnum].instance >= 0)){ + shipp = &Ships[Objects[pl->player->objnum].instance]; + } + + // if we have a valid netplayer pointer + if((pl != NULL) && !(pl->flags & NETINFO_FLAG_RESPAWNING) && !(pl->flags & NETINFO_FLAG_LIMBO)){ + // primary fired + pl->player->ci.fire_primary_count = 0; + + // secondary fired + pl->player->ci.fire_secondary_count = 0; + if ( in_flags & OOC_FIRE_SECONDARY ){ + pl->player->ci.fire_secondary_count = 1; + } + + // countermeasure fired + pl->player->ci.fire_countermeasure_count = 0; + + // set up aspect locking information + pl->player->locking_on_center = 0; + if ( in_flags & OOC_LOCKING_ON_CENTER ){ + pl->player->locking_on_center = 1; + } + + // trigger down, bank info + if(shipp != NULL){ + if(in_flags & OOC_TRIGGER_DOWN){ + shipp->flags |= SF_TRIGGER_DOWN; + } else { + shipp->flags &= ~SF_TRIGGER_DOWN; + } + + if(in_flags & OOC_PRIMARY_BANK){ + shipp->weapons.current_primary_bank = 1; + } else { + shipp->weapons.current_primary_bank = 0; + } + + // linked or not + shipp->flags &= ~SF_PRIMARY_LINKED; + if(in_flags & OOC_PRIMARY_LINKED){ + shipp->flags |= SF_PRIMARY_LINKED; + } + } + + // other locking information + if((shipp != NULL) && (shipp->ai_index != -1)){ + Ai_info[shipp->ai_index].current_target_is_locked = ( in_flags & OOC_TARGET_LOCKED) ? 1 : 0; + if ( in_flags & OOC_TARGET_SEEK_LOCK ) { + Ai_info[shipp->ai_index].ai_flags |= AIF_SEEK_LOCK; + } else { + Ai_info[shipp->ai_index].ai_flags &= ~AIF_SEEK_LOCK; + } + } + } + + // client targeting information + ushort tnet_sig; + char t_subsys,l_subsys; + object *tobj; + + // get the data + GET_DATA(tnet_sig); + GET_DATA(t_subsys); + GET_DATA(l_subsys); + + // try and find the targeted object + tobj = NULL; + if(tnet_sig != 0){ + tobj = multi_get_network_object( tnet_sig ); + } + // maybe fill in targeted object values + if((tobj != NULL) && (pl != NULL) && (pl->player->objnum != -1)){ + // assign the target object + if(Objects[pl->player->objnum].type == OBJ_SHIP){ + Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].target_objnum = OBJ_INDEX(tobj); + } + pl->s_info.target_objnum = OBJ_INDEX(tobj); + + // assign subsystems if possible + if(Objects[pl->player->objnum].type == OBJ_SHIP){ + Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = NULL; + if((t_subsys != -1) && (tobj->type == OBJ_SHIP)){ + Ai_info[Ships[Objects[pl->player->objnum].instance].ai_index].targeted_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], t_subsys); + } + } + + pl->player->locking_subsys = NULL; + if(Objects[pl->player->objnum].type == OBJ_SHIP){ + if((l_subsys != -1) && (tobj->type == OBJ_SHIP)){ + pl->player->locking_subsys = ship_get_indexed_subsys( &Ships[tobj->instance], l_subsys); + } + } + } + + return offset; +} + +// unpack the object data, return bytes processed +#define UNPACK_PERCENT(v) { ubyte temp_byte; memcpy(&temp_byte, data + offset, sizeof(ubyte)); v = (float)temp_byte / 255.0f; offset++;} +int multi_oo_unpack_data(net_player *pl, ubyte *data) +{ + int offset = 0; + object *pobjp; + ushort net_sig = 0; + ubyte data_size, oo_flags; + ubyte seq_num; + char percent; + float fpct; + ship *shipp; + ship_info *sip; + + // add the object's net signature, type and oo_flags + if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + GET_DATA( net_sig ); + GET_DATA( oo_flags ); + } + // clients always pos and orient stuff only + else { + oo_flags = (OO_POS_NEW | OO_ORIENT_NEW); + } + GET_DATA( data_size ); + GET_DATA( seq_num ); + + // try and find the object + if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + pobjp = multi_get_network_object(net_sig); + } else { + if((pl != NULL) && (pl->player->objnum != -1)){ + pobjp = &Objects[pl->player->objnum]; + } else { + pobjp = NULL; + } + } + + // if we can't find the object, set pointer to bogus object to continue reading the data + // ignore out of sequence packets here as well + if ( (pobjp == NULL) || (pobjp->type != OBJ_SHIP) || (pobjp->instance < 0) || (pobjp->instance >= MAX_SHIPS) || (Ships[pobjp->instance].ship_info_index < 0) || (Ships[pobjp->instance].ship_info_index >= Num_ship_types)){ + offset += data_size; + return offset; + } + + // ship pointer + shipp = &Ships[pobjp->instance]; + sip = &Ship_info[shipp->ship_info_index]; + + // --------------------------------------------------------------------------------------------------------------- + // CRITICAL OBJECT UPDATE SHIZ + // --------------------------------------------------------------------------------------------------------------- + + // if the packet is out of order + if(seq_num < shipp->np_updates[NET_PLAYER_NUM(pl)].seq){ + // non-wraparound case + if((shipp->np_updates[NET_PLAYER_NUM(pl)].seq - seq_num) <= 100){ + offset += data_size; + return offset; + } + } + + // if this is from a player, read his button info + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + offset += multi_oo_unpack_client_data(pl, data + offset); + } + + // new info + vector new_pos = pobjp->pos; + physics_info new_phys_info = pobjp->phys_info; + matrix new_orient = pobjp->orient; + + // position + if ( oo_flags & OO_POS_NEW ) { + // AVERAGE TIME BETWEEN PACKETS FOR THIS SHIP + // store this latest time stamp + if(oo_arrive_time_count[shipp - Ships] == 5){ + memmove(&oo_arrive_time[shipp - Ships][0], &oo_arrive_time[shipp - Ships][1], sizeof(float) * 4); + oo_arrive_time[shipp - Ships][4] = f2fl(Missiontime); + } else { + oo_arrive_time[shipp - Ships][oo_arrive_time_count[shipp - Ships]++] = f2fl(Missiontime); + } + // if we've got 5 elements calculate the average + if(oo_arrive_time_count[shipp - Ships] == 5){ + int idx; + oo_arrive_time_avg_diff[shipp - Ships] = 0.0f; + for(idx=0; idx<4; idx++){ + oo_arrive_time_avg_diff[shipp - Ships] += oo_arrive_time[shipp - Ships][idx + 1] - oo_arrive_time[shipp - Ships][idx]; + } + oo_arrive_time_avg_diff[shipp - Ships] /= 5.0f; + } + // next expected arrival time + oo_arrive_time_next[shipp - Ships] = 0.0f; + + // int r1 = multi_pack_unpack_position( 0, data + offset, &pobjp->pos ); + int r1 = multi_pack_unpack_position( 0, data + offset, &new_pos ); + offset += r1; + + // int r3 = multi_pack_unpack_vel( 0, data + offset, &pobjp->orient, &pobjp->pos, &pobjp->phys_info ); + int r3 = multi_pack_unpack_vel( 0, data + offset, &pobjp->orient, &new_pos, &new_phys_info ); + offset += r3; + + // bash desired vel to be velocity + // pobjp->phys_info.desired_vel = pobjp->phys_info.vel; + } + + // orientation + if ( oo_flags & OO_ORIENT_NEW ) { + // int r2 = multi_pack_unpack_orient( 0, data + offset, &pobjp->orient ); + int r2 = multi_pack_unpack_orient( 0, data + offset, &new_orient ); + offset += r2; + + // int r5 = multi_pack_unpack_rotvel( 0, data + offset, &pobjp->orient, &pobjp->pos, &pobjp->phys_info ); + int r5 = multi_pack_unpack_rotvel( 0, data + offset, &new_orient, &new_pos, &new_phys_info ); + offset += r5; + + // bash desired rotvel to be 0 + // pobjp->phys_info.desired_rotvel = vmd_zero_vector; + } + + // forward thrust + percent = (char)(pobjp->phys_info.forward_thrust * 100.0f); + Assert( percent <= 100 ); + GET_DATA(percent); + + // now stuff all this new info + if(oo_flags & OO_POS_NEW){ + // if we're past the position update tolerance, bash. + // this should cause our 2 interpolation splines to be exactly the same. so we'll see a jump, + // but it should be nice and smooth immediately afterwards + if(vm_vec_dist(&new_pos, &pobjp->pos) > OO_POS_UPDATE_TOLERANCE){ + pobjp->pos = new_pos; + } + + // recalc any interpolation info + if(oo_interp_count[shipp - Ships] < 2){ + oo_interp_points[shipp - Ships][oo_interp_count[shipp - Ships]++] = new_pos; + } else { + oo_interp_points[shipp - Ships][0] = oo_interp_points[shipp - Ships][1]; + oo_interp_points[shipp - Ships][1] = new_pos; + + multi_oo_calc_interp_splines(shipp - Ships, &pobjp->pos, &pobjp->orient, &pobjp->phys_info, &new_pos, &new_orient, &new_phys_info); + } + + pobjp->phys_info.vel = new_phys_info.vel; + pobjp->phys_info.desired_vel = new_phys_info.vel; + } + + // we'll just sim rotation straight. it works fine. + if(oo_flags & OO_ORIENT_NEW){ + pobjp->orient = new_orient; + pobjp->phys_info.rotvel = new_phys_info.rotvel; + // pobjp->phys_info.desired_rotvel = vmd_zero_vector; + pobjp->phys_info.desired_rotvel = new_phys_info.rotvel; + } + + + // --------------------------------------------------------------------------------------------------------------- + // ANYTHING BELOW HERE WORKS FINE - nothing here which causes jumpiness or bandwidth problems :) WHEEEEE! + // --------------------------------------------------------------------------------------------------------------- + + // hull info + if ( oo_flags & OO_HULL_NEW ){ + UNPACK_PERCENT(fpct); + pobjp->hull_strength = fpct * Ship_info[Ships[pobjp->instance].ship_info_index].initial_hull_strength; + + float shield_0, shield_1, shield_2, shield_3; + + // unpack the 4 quadrants + UNPACK_PERCENT(shield_0); + UNPACK_PERCENT(shield_1); + UNPACK_PERCENT(shield_2); + UNPACK_PERCENT(shield_3); + + pobjp->shields[0] = (shield_0 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS; + pobjp->shields[1] = (shield_1 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS; + pobjp->shields[2] = (shield_2 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS; + pobjp->shields[3] = (shield_3 * Ship_info[Ships[pobjp->instance].ship_info_index].shields) / MAX_SHIELD_SECTIONS; + } + + if ( oo_flags & OO_SUBSYSTEMS_AND_AI_NEW ) { + ubyte n_subsystems, subsys_count; + float subsystem_percent[MAX_MODEL_SUBSYSTEMS]; + ship_subsys *subsysp; + float val; + int i; + + // get the data for the subsystems + GET_DATA( n_subsystems ); + for ( i = 0; i < n_subsystems; i++ ){ + UNPACK_PERCENT( subsystem_percent[i] ); + } + + // fill in the subsystem data + subsys_count = 0; + for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) { + int subsys_type; + + val = subsystem_percent[subsys_count] * subsysp->system_info->max_hits; + subsysp->current_hits = val; + + // add the value just generated (it was zero'ed above) into the array of generic system types + subsys_type = subsysp->system_info->type; // this is the generic type of subsystem + Assert ( subsys_type < SUBSYSTEM_MAX ); + shipp->subsys_info[subsys_type].current_hits += val; + subsys_count++; + + // if we've reached max subsystems for some reason, bail out + if(subsys_count >= n_subsystems){ + break; + } + } + + // recalculate all ship subsystems + ship_recalc_subsys_strength( shipp ); + + // ai mode info + ubyte umode; + short submode; + ushort target_signature; + object *target_objp; + + GET_DATA( umode ); + GET_DATA( submode ); + GET_DATA( target_signature ); + + if(shipp->ai_index >= 0){ + Ai_info[shipp->ai_index].mode = umode; + Ai_info[shipp->ai_index].submode = submode; + + // set this guys target objnum + target_objp = multi_get_network_object( target_signature ); + if ( target_objp == NULL ){ + Ai_info[shipp->ai_index].target_objnum = -1; + } else { + Ai_info[shipp->ai_index].target_objnum = OBJ_INDEX(target_objp); + } + } + + // primary weapon energy + float weapon_energy_pct; + UNPACK_PERCENT(weapon_energy_pct); + shipp->weapon_energy = sip->max_weapon_reserve * weapon_energy_pct; + } + + // support ship extra info + ubyte support_extra; + GET_DATA(support_extra); + if(support_extra){ + ushort dock_sig; + int ai_flags, ai_mode, ai_submode; + + // flag + GET_DATA(ai_flags); + GET_DATA(ai_mode); + GET_DATA(ai_submode); + GET_DATA(dock_sig); + + // valid ship? + if((shipp != NULL) && (shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO)){ + Ai_info[shipp->ai_index].ai_flags = ai_flags; + Ai_info[shipp->ai_index].mode = ai_mode; + Ai_info[shipp->ai_index].submode = ai_submode; + + object *objp = multi_get_network_object( dock_sig ); + if(objp != NULL){ + Ai_info[shipp->ai_index].dock_objnum = OBJ_INDEX(objp); + } + } + } + + // afterburner info + if(oo_flags & OO_AFTERBURNER_NEW){ + // maybe turn them on + if(!(pobjp->phys_info.flags & PF_AFTERBURNER_ON)){ + afterburners_start(pobjp); + } + } else { + // maybe turn them off + if(pobjp->phys_info.flags & PF_AFTERBURNER_ON){ + afterburners_stop(pobjp); + } + } + + // primary info (only clients care about this) + if( !MULTIPLAYER_MASTER && (shipp != NULL) ){ + // what bank + if(oo_flags & OO_PRIMARY_BANK){ + shipp->weapons.current_primary_bank = 1; + } else { + shipp->weapons.current_primary_bank = 0; + } + + // linked or not + shipp->flags &= ~SF_PRIMARY_LINKED; + if(oo_flags & OO_PRIMARY_LINKED){ + shipp->flags |= SF_PRIMARY_LINKED; + } + + // trigger down or not - server doesn't care about this. he'll get it from clients anyway + shipp->flags &= ~SF_TRIGGER_DOWN; + if(oo_flags & OO_TRIGGER_DOWN){ + shipp->flags |= SF_TRIGGER_DOWN; + } + } + + // if we're the multiplayer server, set eye position and orient + if(MULTIPLAYER_MASTER && (pl != NULL) && (pobjp != NULL)){ + pl->s_info.eye_pos = pobjp->pos; + pl->s_info.eye_orient = pobjp->orient; + } + + // update the sequence # + shipp->np_updates[NET_PLAYER_NUM(pl)].seq = seq_num; + + // flag the object as just updated + // pobjp->flags |= OF_JUST_UPDATED; + + return offset; +} + +// reset the timestamp appropriately for the passed in object +void multi_oo_reset_timestamp(net_player *pl, object *objp, int range, int in_cone) +{ + int stamp = 0; + + // if this is the guy's target, + if((pl->s_info.target_objnum != -1) && (pl->s_info.target_objnum == OBJ_INDEX(objp))){ + stamp = Multi_oo_target_update_times[pl->p_info.options.obj_update_level]; + } else { + // reset the timestamp appropriately + if(in_cone){ + // base it upon range + switch(range){ + case OO_NEAR: + stamp = Multi_oo_front_near_update_times[pl->p_info.options.obj_update_level]; + break; + + case OO_MIDRANGE: + stamp = Multi_oo_front_medium_update_times[pl->p_info.options.obj_update_level]; + break; + + case OO_FAR: + stamp = Multi_oo_front_far_update_times[pl->p_info.options.obj_update_level]; + break; + } + } else { + // base it upon range + switch(range){ + case OO_NEAR: + stamp = Multi_oo_rear_near_update_times[pl->p_info.options.obj_update_level]; + break; + + case OO_MIDRANGE: + stamp = Multi_oo_rear_medium_update_times[pl->p_info.options.obj_update_level]; + break; + + case OO_FAR: + stamp = Multi_oo_rear_far_update_times[pl->p_info.options.obj_update_level]; + break; + } + } + } + + // reset the timestamp for this object + if(objp->type == OBJ_SHIP){ + Ships[objp->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp = timestamp(stamp); + } +} + +// reset the timestamp appropriately for the passed in object +void multi_oo_reset_status_timestamp(object *objp, int player_index) +{ + Ships[objp->instance].np_updates[player_index].status_update_stamp = timestamp(OO_HULL_SHIELD_TIME); +} + +// reset the timestamp appropriately for the passed in object +void multi_oo_reset_subsys_timestamp(object *objp, int player_index) +{ + Ships[objp->instance].np_updates[player_index].subsys_update_stamp = timestamp(OO_SUBSYS_TIME); +} + +// determine what needs to get sent for this player regarding the passed object, and when +int multi_oo_maybe_update(net_player *pl, object *obj, ubyte *data) +{ + ubyte oo_flags; + int stamp; + int player_index; + vector player_eye; + vector obj_dot; + float eye_dot, dist; + int in_cone; + int range; + int ship_index; + ship *shipp; + ship_info *sip; + ushort cur_pos_chksum = 0; + ushort cur_orient_chksum = 0; + + // if the timestamp has elapsed for this guy, send stuff + player_index = NET_PLAYER_INDEX(pl); + if(!(player_index >= 0) || !(player_index < MAX_PLAYERS)){ + return 0; + } + + // determine what the timestamp is for this object + if(obj->type == OBJ_SHIP){ + stamp = Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].update_stamp; + ship_index = &Ships[obj->instance] - Ships; + } else { + return 0; + } + + // stamp hasn't popped yet + if((stamp != -1) && !timestamp_elapsed_safe(stamp, OO_MAX_TIMESTAMP)){ + return 0; + } + + // if we're supposed to update this guy + + // get the ship pointer + shipp = &Ships[obj->instance]; + + // get ship info pointer + sip = NULL; + if(shipp->ship_info_index >= 0){ + sip = &Ship_info[shipp->ship_info_index]; + } + + // check dot products + player_eye = pl->s_info.eye_orient.fvec; + vm_vec_sub(&obj_dot, &obj->pos, &pl->s_info.eye_pos); + vm_vec_normalize(&obj_dot); + eye_dot = vm_vec_dot(&obj_dot, &player_eye); + in_cone = (eye_dot >= OO_VIEW_CONE_DOT) ? 1 : 0; + + // determine distance (near, medium, far) + vm_vec_sub(&obj_dot, &obj->pos, &pl->s_info.eye_pos); + dist = vm_vec_mag(&obj_dot); + if(dist < OO_NEAR_DIST){ + range = OO_NEAR; + } else if(dist < OO_MIDRANGE_DIST){ + range = OO_MIDRANGE; + } else { + range = OO_FAR; + } + + // reset the timestamp for the next update for this guy + multi_oo_reset_timestamp(pl, obj, range, in_cone); + + // base oo_flags + oo_flags = OO_POS_NEW | OO_ORIENT_NEW; + + // if its a small ship, add weapon link info + if((sip != NULL) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_STEALTH))){ + // primary bank 0 or 1 + if(shipp->weapons.current_primary_bank > 0){ + oo_flags |= OO_PRIMARY_BANK; + } + + // linked or not + if(shipp->flags & SF_PRIMARY_LINKED){ + oo_flags |= OO_PRIMARY_LINKED; + } + + // trigger down or not + if(shipp->flags & SF_TRIGGER_DOWN){ + oo_flags |= OO_TRIGGER_DOWN; + } + } + + // if the object's hull/shield timestamp has expired + if((Ships[obj->instance].np_updates[player_index].status_update_stamp == -1) || timestamp_elapsed_safe(Ships[obj->instance].np_updates[player_index].status_update_stamp, OO_MAX_TIMESTAMP)){ + oo_flags |= (OO_HULL_NEW); + + // reset the timestamp + multi_oo_reset_status_timestamp(obj, player_index); + } + + // if the object's hull/shield timestamp has expired + if((Ships[obj->instance].np_updates[player_index].subsys_update_stamp == -1) || timestamp_elapsed_safe(Ships[obj->instance].np_updates[player_index].subsys_update_stamp, OO_MAX_TIMESTAMP)){ + oo_flags |= OO_SUBSYSTEMS_AND_AI_NEW; + + // reset the timestamp + multi_oo_reset_subsys_timestamp(obj, player_index); + } + + // add info for a targeted object + if((pl->s_info.target_objnum != -1) && (OBJ_INDEX(obj) == pl->s_info.target_objnum)){ + oo_flags |= (OO_POS_NEW | OO_ORIENT_NEW | OO_HULL_NEW); + } + // all other cases + else { + // add info which is contingent upon being "in front" + if(in_cone){ + oo_flags |= OO_ORIENT_NEW; + } + } + + // get current position and orient checksums + cur_pos_chksum = cf_add_chksum_short(cur_pos_chksum, (char*)(&obj->pos), sizeof(vector)); + cur_orient_chksum = cf_add_chksum_short(cur_orient_chksum, (char*)(&obj->orient), sizeof(matrix)); + + // if position or orientation haven't changed + if((shipp->np_updates[player_index].pos_chksum != 0) && (shipp->np_updates[player_index].pos_chksum == cur_pos_chksum)){ + // if we otherwise would have been sending it, keep track of it (debug only) +#ifndef NDEBUG + if(oo_flags & OO_POS_NEW){ + multi_rate_add(player_index, "skp_p", OO_POS_RET_SIZE + OO_VEL_RET_SIZE); + } +#endif + oo_flags &= ~(OO_POS_NEW); + } + if((shipp->np_updates[player_index].orient_chksum != 0) && (shipp->np_updates[player_index].orient_chksum == cur_orient_chksum)){ + // if we otherwise would have been sending it, keep track of it (debug only) +#ifndef NDEBUG + if(oo_flags & OO_ORIENT_NEW){ + multi_rate_add(player_index, "skp_o", OO_ORIENT_RET_SIZE + OO_ROTVEL_RET_SIZE); + } +#endif + oo_flags &= ~(OO_ORIENT_NEW); + } + shipp->np_updates[player_index].pos_chksum = cur_pos_chksum; + shipp->np_updates[player_index].orient_chksum = cur_orient_chksum; + + // pack stuff only if we have to + int packed = multi_oo_pack_data(pl, obj, oo_flags ,data); + + // increment sequence # + Ships[obj->instance].np_updates[NET_PLAYER_NUM(pl)].seq++; + + // bytes packed + return packed; +} + +// process all other objects for this player +void multi_oo_process_all(net_player *pl) +{ + ubyte data[MAX_PACKET_SIZE]; + ubyte data_add[MAX_PACKET_SIZE]; + ubyte stop; + int add_size; + int packet_size = 0; + int idx; + + object *moveup; + object *pobj; + + // if the player has an invalid objnum.. + if(pl->player->objnum < 0){ + return; + } + + // get the player's object + pobj = &Objects[pl->player->objnum]; + + object *targ_obj; + + // build the list of ships to check against + multi_oo_build_ship_list(pl); + + // do nothing if he has no object targeted, or if he has a weapon targeted + if((pl->s_info.target_objnum != -1) && (Objects[pl->s_info.target_objnum].type == OBJ_SHIP)){ + // build the header + BUILD_HEADER(OBJECT_UPDATE); + + // get a pointer to the object + targ_obj = &Objects[pl->s_info.target_objnum]; + + // run through the maybe_update function + add_size = multi_oo_maybe_update(pl, targ_obj, data_add); + + // copy in any relevant data + if(add_size){ + stop = 0xff; + multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1); + ADD_DATA(stop); + + memcpy(data + packet_size, data_add, add_size); + packet_size += add_size; + } + } else { + // just build the header for the rest of the function + BUILD_HEADER(OBJECT_UPDATE); + } + + idx = 0; + while((OO_ship_index[idx] >= 0) && (idx < MAX_SHIPS)){ + // if this guy is over his datarate limit, do nothing + if(multi_oo_rate_exceeded(pl)){ + nprintf(("Network","Capping client\n")); + idx++; + + continue; + } + + // get the object + moveup = &Objects[Ships[OO_ship_index[idx]].objnum]; + + // maybe send some info + add_size = multi_oo_maybe_update(pl, moveup, data_add); + + // if this data is too much for the packet, send off what we currently have and start over + if(packet_size + add_size > OO_MAX_SIZE){ + stop = 0x00; + multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1); + ADD_DATA(stop); + + multi_io_send(pl, data, packet_size); + pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE; + + packet_size = 0; + BUILD_HEADER(OBJECT_UPDATE); + } + + if(add_size){ + stop = 0xff; + multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1); + ADD_DATA(stop); + + // copy in the data + memcpy(data + packet_size,data_add,add_size); + packet_size += add_size; + } + + // next ship + idx++; + } + + // if we have anything more than 3 byte in the packet, send the last one off + if(packet_size > 3){ + stop = 0x00; + multi_rate_add(NET_PLAYER_NUM(pl), "stp", 1); + ADD_DATA(stop); + + multi_io_send(pl, data, packet_size); + pl->s_info.rate_bytes += packet_size + UDP_HEADER_SIZE; + } +} + +// process all object update details for this frame +void multi_oo_process() +{ + int idx; + + // process each player + for(idx=0; idxobjnum >= 0) && !(Net_players[idx].flags & NETINFO_FLAG_LIMBO) && !(Net_players[idx].flags & NETINFO_FLAG_RESPAWNING)){ + if((Objects[Net_players[idx].player->objnum].flags & OF_PLAYER_SHIP) && !(Objects[Net_players[idx].player->objnum].flags & OF_SHOULD_BE_DEAD)){ + obj_player_fire_stuff( &Objects[Net_players[idx].player->objnum], Net_players[idx].player->ci ); + } + } + } + } +} + +// process incoming object update data +void multi_oo_process_update(ubyte *data, header *hinfo) +{ + ubyte stop; + int player_index; + int offset = HEADER_LENGTH; + net_player *pl = NULL; + + // if this is processed on the server, its a client object update packet + player_index = -1; + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + // determine what player this came from + player_index = find_player_id(hinfo->id); + if(player_index != -1){ + pl = &Net_players[player_index]; + } else { + pl = NULL; + } + } + // otherwise its a "regular" object update packet on a client from the server. use "myself" as the reference player + else { + pl = Net_player; + } + + GET_DATA(stop); + + while(stop == 0xff){ + // process the data + offset += multi_oo_unpack_data(pl, data + offset); + + GET_DATA(stop); + } + PACKET_SET_SIZE(); +} + +// initialize all object update timestamps (call whenever entering gameplay state) +void multi_oo_gameplay_init() +{ + int cur, s_idx, idx; + //ship_obj *so; + ship *shipp; + /* + int num_ships = ship_get_num_ships(); + + if(num_ships <= 0){ + return; + } + split = 3000 / num_ships; + */ + //split = 1; + //split = 150; + + // server should setup initial update timestamps + // stagger initial updates over 3 seconds or so + cur = 0; + //for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) { + //if(Objects[so->objnum].type == OBJ_SHIP){ + for(s_idx=0; s_idxnp_updates[idx].update_stamp = timestamp(cur); + shipp->np_updates[idx].status_update_stamp = timestamp(cur); + shipp->np_updates[idx].subsys_update_stamp = timestamp(cur); + shipp->np_updates[idx].seq = 0; + shipp->np_updates[idx].pos_chksum = 0; + shipp->np_updates[idx].orient_chksum = 0; + } + + oo_arrive_time_count[shipp - Ships] = 0; + oo_interp_count[shipp - Ships] = 0; + + // increment the time +// cur += split; + } + //} + + // reset datarate stamp now + extern int OO_gran; + for(idx=0; idxflags & SF_DYING)){ + return; + } + + // build the header + BUILD_HEADER(OBJECT_UPDATE); + + // pos and orient always + oo_flags = (OO_POS_NEW | OO_ORIENT_NEW); + + // pack the appropriate info into the data + add_size = multi_oo_pack_data(Net_player, Player_obj, oo_flags, data_add); + + // copy in any relevant data + if(add_size){ + stop = 0xff; + multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1); + + ADD_DATA(stop); + + memcpy(data + packet_size, data_add, add_size); + packet_size += add_size; + } + + // add the final stop byte + stop = 0x0; + multi_rate_add(NET_PLAYER_NUM(Net_player), "stp", 1); + ADD_DATA(stop); + + // increment sequence # + Player_ship->np_updates[MY_NET_PLAYER_NUM].seq++; + + // send to the server + if(Netgame.server != NULL){ + multi_io_send(Net_player, data, packet_size); + } +} + +// display any oo info on the hud +void multi_oo_display() +{ +#ifndef NDEBUG +#endif +} + + +// --------------------------------------------------------------------------------------------------- +// DATARATE DEFINES/VARS +// + +// low object update datarate limit +#define OO_LIMIT_LOW 1800 +#define OO_LIMIT_MED 3400 + +// timestamp for sending control info (movement only - we'll send button info all the time) +#define OO_CIRATE 85 // 15x a second +int Multi_cirate_stamp = -1; // timestamp for waiting on control info time +int Multi_cirate_can_send = 1; // if we can send control info this frame + +// global max rates +int OO_server_rate = -1; // max _total_ bandwidth to send to all clients +int OO_client_rate = -1; // max bandwidth to go to an individual client + +// update timestamp for server datarate checking +#define RATE_UPDATE_TIME 1250 // in ms +int OO_server_rate_stamp = -1; + +// bandwidth granularity +int OO_gran = 1; +DCF(oog, "") +{ + dc_get_arg(ARG_INT); + OO_gran = Dc_arg_int; +} + +// process datarate limiting stuff for the server +void multi_oo_server_process(); + +// process datarate limiting stuff for the client +void multi_oo_client_process(); + +// update the server datarate +void multi_oo_update_server_rate(); + + +// --------------------------------------------------------------------------------------------------- +// DATARATE FUNCTIONS +// + +// process all object update datarate details +void multi_oo_rate_process() +{ + // if I have no valid player, drop out here + if(Net_player == NULL){ + return; + } + + // if we're not in mission, don't do anything + if(!(Game_mode & GM_IN_MISSION)){ + return; + } + + // if I'm the server of a game, process server stuff + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + multi_oo_server_process(); + } + // otherwise process client-side stuff + else { + multi_oo_client_process(); + } +} + +// process datarate limiting stuff for the server +void multi_oo_server_process() +{ + int idx; + + // go through all players + for(idx=0;idx= (int)(1000.0f / (float)OO_gran)) ){ + Net_players[idx].s_info.rate_stamp = timestamp( (int)(1000.0f / (float)OO_gran) ); + Net_players[idx].s_info.rate_bytes = 0; + } + } + } + + // determine if we should be updating the server datarate + if((OO_server_rate_stamp == -1) || timestamp_elapsed_safe(OO_server_rate_stamp, OO_MAX_TIMESTAMP)){ + // reset the timestamp + OO_server_rate_stamp = timestamp(RATE_UPDATE_TIME); + + // update the server datarate + multi_oo_update_server_rate(); + + // nprintf(("Network","UPDATING SERVER DATARATE\n")); + } +} + +// process datarate limiting stuff for the client +void multi_oo_client_process() +{ + // if the timestamp is -1 or has elapsed, reset it + if((Multi_cirate_stamp == -1) || timestamp_elapsed_safe(Multi_cirate_stamp, OO_CIRATE)){ + Multi_cirate_can_send = 1; + Multi_cirate_stamp = timestamp(OO_CIRATE); + } +} + + +// datarate limiting system for server ------------------------------------- + +// initialize the rate limiting system for all players +void multi_oo_rate_init_all() +{ + int idx; + + // if I don't have a net_player, bail here + if(Net_player == NULL){ + return; + } + + // if I'm the server of the game + if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ + // go through all players + for(idx=0;idxs_info.rate_stamp = -1; + pl->s_info.rate_bytes = 0; +} + +// if the given net-player has exceeded his datarate limit +int multi_oo_rate_exceeded(net_player *pl) +{ + int rate_compare; + + // check against the guy's object update level + switch(pl->p_info.options.obj_update_level){ + // low update level + case OBJ_UPDATE_LOW: + // the low object update limit + rate_compare = OO_LIMIT_LOW; + break; + + // medium update level + case OBJ_UPDATE_MEDIUM: + // the low object update limit + rate_compare = OO_LIMIT_MED; + break; + + // high update level - super high datarate (no capping, just intelligent updating) + case OBJ_UPDATE_HIGH: + rate_compare = 100000000; + break; + + // LAN - no rate max + case OBJ_UPDATE_LAN: + return 0; + + // default level + default: + Int3(); + rate_compare = OO_LIMIT_LOW; + break; + } + + // if the server global rate PER CLIENT (OO_client_rate) is actually lower + if(OO_client_rate < rate_compare){ + rate_compare = OO_client_rate; + } + + // compare his bytes sent against the allowable amount + if(pl->s_info.rate_bytes >= rate_compare){ + return 1; + } + + // we're allowed to send + return 0; +} + +// if it is ok for me to send a control info (will be ~N times a second) +int multi_oo_cirate_can_send() +{ + // if we're allowed to send + if(Multi_cirate_can_send){ + Multi_cirate_can_send = 0; + return 1; + } + + return 0; +} + +// dynamically update the server capped bandwidth rate +void multi_oo_update_server_rate() +{ + int num_connections; + + // bail conditions + if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_AM_MASTER)){ + return; + } + + // get the # of connections + num_connections = multi_num_connections(); + if(!(Game_mode & GM_STANDALONE_SERVER)){ + num_connections--; + } + // make sure we always pretend there's at least one guy available + if(num_connections <= 0){ + num_connections = 1; + } + + // set the data rate + switch(Net_player->p_info.options.obj_update_level){ + // LAN update level + case OBJ_UPDATE_LAN: + // set to something super big so we don't limit anything + OO_server_rate = 500000000; + break; + + // high update level + case OBJ_UPDATE_HIGH: + // set to 0 so we don't limit anything + OO_server_rate = Multi_options_g.datarate_cap; + break; + + // medium update level + case OBJ_UPDATE_MEDIUM: + // set the rate to be "medium" update level + OO_server_rate = OO_LIMIT_MED; + break; + + // low update level + case OBJ_UPDATE_LOW: + // set the rate to be the "low" update level + OO_server_rate = OO_LIMIT_LOW; + break; + + default: + Int3(); + return; + } + + // set the individual client level + OO_client_rate = (int)(((float)OO_server_rate / (float)OO_gran) / (float)num_connections); +} + +// reset all sequencing info (obsolete for new object update stuff) +void multi_oo_reset_sequencing() +{ +} + +// is this object one which needs to go through the interpolation +int multi_oo_is_interp_object(object *objp) +{ + // if not multiplayer, skip it + if(!(Game_mode & GM_MULTIPLAYER)){ + return 0; + } + + // if its not a ship, skip it + if(objp->type != OBJ_SHIP){ + return 0; + } + + // other bogus cases + if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){ + return 0; + } + + // if I'm a client and this is not me, I need to interp it + if(!MULTIPLAYER_MASTER){ + if(objp != Player_obj){ + return 1; + } else { + return 0; + } + } + + // servers only interpolate other player ships + if(!(objp->flags & OF_PLAYER_SHIP)){ + return 0; + } + + // here we know its a player ship - is it mine? + if(objp == Player_obj){ + return 0; + } + + // interp it + return 1; +} + +// interp +void multi_oo_interp(object *objp) +{ + // make sure its a valid ship + Assert(Game_mode & GM_MULTIPLAYER); + if(objp->type != OBJ_SHIP){ + return; + } + if((objp->instance < 0) || (objp->instance >= MAX_SHIPS)){ + return; + } + + // increment his approx "next" time + oo_arrive_time_next[objp->instance] += flFrametime; + + // do stream weapon firing for this ship + Assert(objp != Player_obj); + if(objp != Player_obj){ + ship_fire_primary(objp, 1, 0); + } + + // if this ship doesn't have enough data points yet, skip it + if((oo_interp_count[objp->instance] < 2) || (oo_arrive_time_count[objp->instance] < 5)){ + return; + } + + // store the magnitude of his velocity + // float vel_mag = vm_vec_mag(&objp->phys_info.vel); + + // determine how far along we are (0.0 to 1.0) until we should be getting the next packet + float t = oo_arrive_time_next[objp->instance] / oo_arrive_time_avg_diff[objp->instance]; + + // gr_set_color_fast(&Color_bright); + // gr_printf(100, 10, "%f\n", t); + + // we've overshot. hmm. just keep the sim running I guess + if(t > 1.0f){ + physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime); + return; + } + + // otherwise, blend the two curves together to get the new point + float u = 0.5f + (t * 0.5f); + vector p_bad, p_good; + oo_interp_splines[objp->instance][0].bez_get_point(&p_bad, u); + oo_interp_splines[objp->instance][1].bez_get_point(&p_good, u); + vm_vec_scale(&p_good, t); + vm_vec_scale(&p_bad, 1.0f - t); + vm_vec_add(&objp->pos, &p_bad, &p_good); + + // set new velocity + // vm_vec_sub(&objp->phys_info.vel, &objp->pos, &objp->last_pos); + + // run the sim for rotation + physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime); + + // blend velocity vectors together with an average weight + /* + vector v_bad, v_good; + oo_interp_splines[objp->instance][0].herm_get_deriv(&v_bad, u, 0); + oo_interp_splines[objp->instance][1].herm_get_deriv(&v_good, u, 0); + + // t -= 1.0f; + vm_vec_scale(&v_good, t); + vm_vec_scale(&v_bad, 1.0f - t); + vm_vec_avg(&objp->phys_info.vel, &v_bad, &v_good); + + // run the sim + physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime); + */ + + /* + vector v_bad, v_good; + oo_interp_splines[objp->instance][0].herm_get_point(&v_bad, u, 0); + oo_interp_splines[objp->instance][1].herm_get_point(&v_good, u, 0); + + // t -= 1.0f; + vm_vec_scale(&v_good, t); + vm_vec_scale(&v_bad, 1.0f - t); + vm_vec_avg(&objp->pos, &v_bad, &v_good); + + // run the sim + // physics_sim(&objp->pos, &objp->orient, &objp->phys_info, flFrametime); + physics_sim_rot(&objp->orient, &objp->phys_info, flFrametime); + */ +} + +float oo_error = 0.8f; +DCF(oo_error, "") +{ + dc_get_arg(ARG_FLOAT); + oo_error = Dc_arg_float; +} + +void multi_oo_calc_interp_splines(int ship_index, vector *cur_pos, matrix *cur_orient, physics_info *cur_phys_info, vector *new_pos, matrix *new_orient, physics_info *new_phys_info) +{ + vector a, b, c; + // vector da, db, dc; + matrix m_copy; + physics_info p_copy; + vector *pts[3] = {&a, &b, &c}; + // vector *d_pts[3] = {&da, &db, &dc}; + + // average time between packets + float avg_diff = oo_arrive_time_avg_diff[ship_index]; + + // would this cause us to rubber-band? + vector v_norm = cur_phys_info->vel; + vector v_dir; + vm_vec_sub(&v_dir, new_pos, cur_pos); + if(!IS_VEC_NULL(&v_norm) && !IS_VEC_NULL(&v_dir)){ + vm_vec_normalize(&v_dir); + vm_vec_normalize(&v_norm); + if(vm_vec_dotprod(&v_dir, &v_norm) < 0.0f){ + *new_pos = *cur_pos; + } + } + + // get the spline representing our "bad" movement. its better to be little bit off than to overshoot altogether + a = oo_interp_points[ship_index][0]; + b = *cur_pos; + c = *cur_pos; + m_copy = *cur_orient; + p_copy = *cur_phys_info; + physics_sim(&c, &m_copy, &p_copy, avg_diff * oo_error); // next point, assuming we followed our current path + oo_interp_splines[ship_index][0].bez_set_points(3, pts); + + // get the spline representing where this new point tells us we'd be heading + a = oo_interp_points[ship_index][0]; + b = oo_interp_points[ship_index][1]; + c = oo_interp_points[ship_index][1]; + m_copy = *new_orient; + p_copy = *new_phys_info; + physics_sim(&c, &m_copy, &p_copy, avg_diff); // next point, given this new info + oo_interp_splines[ship_index][1].bez_set_points(3, pts); + + // get the spline for our "bad" movement + /* + a = oo_interp_points[ship_index][0]; + b = *cur_pos; + da = oo_interp_vel[ship_index][0]; + db = cur_phys_info->vel; + oo_interp_splines[ship_index][0].herm_set_points(2, pts, d_pts); + + // get the spline for our "good" movement + a = oo_interp_points[ship_index][0]; + b = oo_interp_points[ship_index][1]; + da = oo_interp_vel[ship_index][0]; + db = oo_interp_vel[ship_index][1]; + oo_interp_splines[ship_index][1].herm_set_points(2, pts, d_pts); + */ + + // now we've got a spline representing our "new" path and where we would've gone had we been perfect before + // we'll modify our velocity to move along a blend of these splines. +} + +#include "alphacolors.h" + +void oo_update_time() +{ +} + +int display_oo_bez = 0; +DCF(bez, "") +{ + display_oo_bez = !display_oo_bez; + if(display_oo_bez){ + dc_printf("Showing interp splines"); + } else { + dc_printf("Not showing interp splines"); + } +} + +void oo_display() +{ +/* int idx; + + + gr_set_color_fast(&Color_bright); + + for(idx=0; idx

+

Build Log

+

+--------------------Configuration: Ac - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\WINDOWS\TEMP\RSP12B0.TMP" with contents +[ +/nologo /G5 /MLd /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fo"AcDebug/" /Fd"AcDebug/" /FD /GZ /c +"E:\projects\freespace2_public\code\AC\ac.cpp" +"E:\projects\freespace2_public\code\AC\ac_stubs.cpp" +"E:\projects\freespace2_public\code\AC\convert.cpp" +] +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSP12B0.TMP" +Creating temporary file "C:\WINDOWS\TEMP\RSP12B1.TMP" with contents +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib vfw32.lib wsock32.lib msacm32.lib winmm.lib comctl32.lib code.lib /nologo /subsystem:console /incremental:yes /pdb:"Ac.pdb" /debug /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /out:"Ac.exe" /pdbtype:sept /libpath:"debug" +.\AcDebug\ac.obj +.\AcDebug\ac_stubs.obj +.\AcDebug\convert.obj +.\Debug\code.lib +] +Creating command line "link.exe @C:\WINDOWS\TEMP\RSP12B1.TMP" +

Output Window

+Compiling... +ac.cpp +ac_stubs.cpp +convert.cpp +Generating Code... +Linking... + + + +

Results

+Ac.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/winfiles/Cfilearchiver.dsp b/winfiles/Cfilearchiver.dsp new file mode 100644 index 0000000..547b3f5 --- /dev/null +++ b/winfiles/Cfilearchiver.dsp @@ -0,0 +1,90 @@ +# Microsoft Developer Studio Project File - Name="Cfilearchiver" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Cfilearchiver - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Cfilearchiver.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Cfilearchiver.mak" CFG="Cfilearchiver - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Cfilearchiver - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Cfilearchiver - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Freespace2", FPCCAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Cfilearchiver - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "CFileArchiverRelease" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\nebula" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /U "_DEBUG" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "Cfilearchiver - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Cfilearchiver___Win32_Debug" +# PROP BASE Intermediate_Dir "Cfilearchiver___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "CFileArchiverDebug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /G5 /W4 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "Cfilearchiver - Win32 Release" +# Name "Cfilearchiver - Win32 Debug" +# Begin Source File + +SOURCE=.\code\Cfilearchiver\CfileArchiver.cpp +# End Source File +# End Target +# End Project diff --git a/winfiles/Cfilearchiver.plg b/winfiles/Cfilearchiver.plg new file mode 100644 index 0000000..bc6a59f --- /dev/null +++ b/winfiles/Cfilearchiver.plg @@ -0,0 +1,32 @@ + + +
+

Build Log

+

+--------------------Configuration: Cfilearchiver - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\WINDOWS\TEMP\RSP1264.TMP" with contents +[ +/nologo /G5 /MLd /W4 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fo"CFileArchiverDebug/" /Fd"CFileArchiverDebug/" /FD /GZ /c +"E:\projects\freespace2_public\code\Cfilearchiver\CfileArchiver.cpp" +] +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSP1264.TMP" +Creating temporary file "C:\WINDOWS\TEMP\RSP1265.TMP" with contents +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"Cfilearchiver.pdb" /debug /machine:I386 /out:"Cfilearchiver.exe" /pdbtype:sept +.\CFileArchiverDebug\CfileArchiver.obj +] +Creating command line "link.exe @C:\WINDOWS\TEMP\RSP1265.TMP" +

Output Window

+Compiling... +CfileArchiver.cpp +Linking... + + + +

Results

+Cfilearchiver.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/winfiles/Cryptstring.dsp b/winfiles/Cryptstring.dsp new file mode 100644 index 0000000..1d69685 --- /dev/null +++ b/winfiles/Cryptstring.dsp @@ -0,0 +1,89 @@ +# Microsoft Developer Studio Project File - Name="Cryptstring" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Cryptstring - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Cryptstring.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Cryptstring.mak" CFG="Cryptstring - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Cryptstring - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Cryptstring - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Freespace2", KPCCAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Cryptstring - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "CryptstringRelease" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\nebula" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /U "_DEBUG" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "Cryptstring - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Cryptstring___Win32_Debug" +# PROP BASE Intermediate_Dir "Cryptstring___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "CryptstringDebug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /G5 /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "Cryptstring - Win32 Release" +# Name "Cryptstring - Win32 Debug" +# Begin Source File + +SOURCE=.\code\Cryptstring\cryptstring.cpp +# End Source File +# End Target +# End Project diff --git a/winfiles/Cryptstring.plg b/winfiles/Cryptstring.plg new file mode 100644 index 0000000..a59280e --- /dev/null +++ b/winfiles/Cryptstring.plg @@ -0,0 +1,32 @@ + + +
+

Build Log

+

+--------------------Configuration: Cryptstring - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\WINDOWS\TEMP\RSP1231.TMP" with contents +[ +/nologo /G5 /MLd /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fo"CryptstringDebug/" /Fd"CryptstringDebug/" /FD /GZ /c +"E:\projects\freespace2_public\code\Cryptstring\cryptstring.cpp" +] +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSP1231.TMP" +Creating temporary file "C:\WINDOWS\TEMP\RSP1232.TMP" with contents +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"Cryptstring.pdb" /debug /machine:I386 /out:"Cryptstring.exe" /pdbtype:sept +.\CryptstringDebug\cryptstring.obj +] +Creating command line "link.exe @C:\WINDOWS\TEMP\RSP1232.TMP" +

Output Window

+Compiling... +cryptstring.cpp +Linking... + + + +

Results

+Cryptstring.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/winfiles/FS2AutoRun.ico b/winfiles/FS2AutoRun.ico new file mode 100644 index 0000000000000000000000000000000000000000..3c8ce7eaee0319b6d4b3b7bc4b87bc53656678b1 GIT binary patch literal 3310 zcmcJRy=z=Y5Wq({k-I{0_jLDEi9}Hmq|UGqP-UYBE(`)Us^ucADuLk&H37dQAXEkd zg6&)+CMnYBAIRY%MYOflL~tOGgY1w~Cmi9;Z}z?W)K`L&y_@}*nVtFV?0d(FOi4u+ z78;`M_YXuqV{9~v_0p8cM-{=YR*@fSA`hoUmY6Rf50EKALCe{DeakUo$@5~MXDXde zhfxh7ekBB*A$(M&UD2vf`?F%K<^Scn`m~mhW~k+rd!re4$yUqznw+_=At+_AmR*!; z8}3(^(Y4wu_w=JPTCUy5x{MLkYSKp02R)O){#?}~>%woCZ*ooD)xXcH<|t^s3VTd+VqXM%%w#4{js8dls&h)6Ec6I`ZsNq}n4}_ruN5`E)aMksk9) zsfN&%`{DFSHJpB4Ddb5N*Lqjv{-sok7^lR!A;t?Q>NB z@Htm5ZRIue$7-!G>`E^Gcz-iB#F$;&B^@yyeuv*%JTBEtgo-j+S;0|uCB_;%8KM)1bO^8$hUGR z;f;ouns6qeq?Vqvkc#3RFENp4AG32I@9Wx!~e zSXeA9u`F;{I4m3%4hu&t6&w}@3xkD0OK0J*FjyEoxdMGGA$f`q>sVf`4piiJc*Rna z+ri{W>PX;Va^!Xx9YGwq9l7Im#N6n~@5t}SuUF*A@2HJ~!NK6DgI*g|iKj=yNUzsO z4LtcgY#t6zzIe6DK@B_{UO6~eylysmSUfBq2v5TJ2*Bdm=yCEWJS=<>;&3M(7O(Y) zEc!I)Eue!Tzz|>vFa#I^4DoS-!NU+>2rzgU2!r4cLx3T`5MUrQf*ytdLx3Rw5XkQd z7T6eYvc#~gEgT`nRv#;$$oI0bu_4>r+p@E>BYS&$a&T}U`}_NHbaW)g$H#JVaw2DE zXL5diE*BRU()%mOch8^m9_Lh7-vJ$n7grirm%18jywSI|yX@`ut=r{&?XO?o9ADWU zJraJFuJ^ga-fFj#q|r!{b~|e|&&FcRrdZl-{)EwrG0D=c(x<8WRL@w~{b`zJX_jTJ z**|d6y$uY?u6z?`coTQm8_}kAmC=?K4pHA~UA4=sI@B9UN-kt_Ke4u&83+kTSvs3$ zqtS=+G;0E#k}lzJvqM8U)3`V%$aTC7&LcDB})lFQu?&+HwlGW=O^(8dg{_bXhGjc!JTYYO}+&XKvGVU#*N{nN+!6EK=KH_tw{5L&c8ffBf h2eJE;_kI5F+J76HGF-9W^gk>9m(%}Y|MUO3zX5=2+FJks literal 0 HcmV?d00001 diff --git a/winfiles/Fonttool.dsp b/winfiles/Fonttool.dsp new file mode 100644 index 0000000..18fada2 --- /dev/null +++ b/winfiles/Fonttool.dsp @@ -0,0 +1,110 @@ +# Microsoft Developer Studio Project File - Name="Fonttool" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Fonttool - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Fonttool.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Fonttool.mak" CFG="Fonttool - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Fonttool - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Fonttool - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Freespace2", WPCCAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Fonttool - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "FonttoolRelease" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\nebula" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /U "_DEBUG" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /libpath:"release" + +!ELSEIF "$(CFG)" == "Fonttool - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Fonttool___Win32_Debug" +# PROP BASE Intermediate_Dir "Fonttool___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "FonttoolDebug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /G5 /MTd /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /X /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib comctl32.lib winmm.lib msacm32.lib code.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /pdbtype:sept /libpath:"debug" + +!ENDIF + +# Begin Target + +# Name "Fonttool - Win32 Release" +# Name "Fonttool - Win32 Debug" +# Begin Source File + +SOURCE=.\code\Fonttool\FontCreate.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\Fonttool\FontKern.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\Fonttool\FontKernCopy.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\Fonttool\FontStubs.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\Fonttool\FontTool.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\Fonttool\FontTool.h +# End Source File +# End Target +# End Project diff --git a/winfiles/Fonttool.plg b/winfiles/Fonttool.plg new file mode 100644 index 0000000..c02addd --- /dev/null +++ b/winfiles/Fonttool.plg @@ -0,0 +1,46 @@ + + +
+

Build Log

+

+--------------------Configuration: Fonttool - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\WINDOWS\TEMP\RSP1162.TMP" with contents +[ +/nologo /G5 /MTd /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fo"FonttoolDebug/" /Fd"FonttoolDebug/" /FD /GZ /c +"E:\projects\freespace2_public\code\Fonttool\FontCreate.cpp" +"E:\projects\freespace2_public\code\Fonttool\FontKern.cpp" +"E:\projects\freespace2_public\code\Fonttool\FontKernCopy.cpp" +"E:\projects\freespace2_public\code\Fonttool\FontStubs.cpp" +"E:\projects\freespace2_public\code\Fonttool\FontTool.cpp" +] +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSP1162.TMP" +Creating temporary file "C:\WINDOWS\TEMP\RSP1163.TMP" with contents +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib comctl32.lib winmm.lib msacm32.lib code.lib /nologo /subsystem:console /incremental:yes /pdb:"Fonttool.pdb" /debug /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /out:"Fonttool.exe" /pdbtype:sept /libpath:"debug" +.\FonttoolDebug\FontCreate.obj +.\FonttoolDebug\FontKern.obj +.\FonttoolDebug\FontKernCopy.obj +.\FonttoolDebug\FontStubs.obj +.\FonttoolDebug\FontTool.obj +.\Debug\code.lib +] +Creating command line "link.exe @C:\WINDOWS\TEMP\RSP1163.TMP" +

Output Window

+Compiling... +FontCreate.cpp +FontKern.cpp +FontKernCopy.cpp +FontStubs.cpp +FontTool.cpp +Generating Code... +Linking... + + + +

Results

+Fonttool.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/winfiles/Fred2.dsp b/winfiles/Fred2.dsp new file mode 100644 index 0000000..36d6fb8 --- /dev/null +++ b/winfiles/Fred2.dsp @@ -0,0 +1,593 @@ +# Microsoft Developer Studio Project File - Name="fred2" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=fred2 - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Fred2.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Fred2.mak" CFG="fred2 - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "fred2 - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "fred2 - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Freespace2", RHWBAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "fred2 - Win32 Release" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 5 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /G5 /MT /W4 /GX /Ot /Ow /Og /Oi /Oy /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\nebula" /FI"PSTypes.h" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "FRED" /U "_DEBUG" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386 +# ADD LINK32 winmm.lib vfw32.lib msacm32.lib code.lib wsock32.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmtd.lib" /nodefaultlib:"nafxcwd.lib" /out:"E:\games\Freespace2\Fred2.exe" /libpath:"release" +# SUBTRACT LINK32 /debug + +!ELSEIF "$(CFG)" == "fred2 - Win32 Debug" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "fred2___Win32_Debug" +# PROP BASE Intermediate_Dir "fred2___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "FredDebug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /G5 /MDd /W4 /GX /ZI /Od /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\nebula" /FI"PSTypes.h" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_AFXDLL" /U "NDEBUG" /FR /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo "./FredDebug/*.sbr" +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 winmm.lib vfw32.lib msacm32.lib wsock32.lib code.lib /nologo /subsystem:windows /debug /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmtd.lib" /nodefaultlib:"nafxcwd.lib" /out:"e:\games\freespace2\Fred2.exe" /pdbtype:sept /libpath:"debug" +# SUBTRACT LINK32 /incremental:no + +!ENDIF + +# Begin Target + +# Name "fred2 - Win32 Release" +# Name "fred2 - Win32 Debug" +# Begin Group "res" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\code\fred2\res\bitmap1.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\res\black_do.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\res\bmp00001.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\res\chained.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\res\chained_directive.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\res\cursor1.cur +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\res\cursor2.cur +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\res\data.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\res\FRED.ico +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\FRED.rc +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\res\FRED.rc2 +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\res\FREDDoc.ico +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\res\green_do.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\resource.h +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\resource.hm +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\res\root.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\res\root_directive.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\res\Toolbar.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\res\toolbar1.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\res\variable.bmp +# End Source File +# End Group +# Begin Source File + +SOURCE=.\code\FRED2\AddVariableDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\AddVariableDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\AdjustGridDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\AdjustGridDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\AsteroidEditorDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\AsteroidEditorDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\BgBitmapDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\BgBitmapDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\BriefingEditorDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\BriefingEditorDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CampaignEditorDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CampaignEditorDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CampaignFilelistBox.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CampaignFilelistBox.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CampaignTreeView.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CampaignTreeView.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CampaignTreeWnd.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CampaignTreeWnd.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CmdBrief.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CmdBrief.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CreateWingDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\CreateWingDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\DebriefingEditorDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\DebriefingEditorDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\dialog1.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\dialog1.h +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\DumpStats.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\fred2\DumpStats.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\editor.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\EventEditor.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\EventEditor.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\FRED.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\FRED.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\FREDDoc.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\FREDDoc.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\FredRender.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\FredRender.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\FredStubs.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\FREDView.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\FREDView.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\Grid.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\Grid.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\IgnoreOrdersDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\IgnoreOrdersDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\InitialShips.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\InitialShips.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\InitialStatus.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\InitialStatus.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\MainFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\Management.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\Management.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\MessageEditorDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\MessageEditorDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\MissionGoalsDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\MissionGoalsDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\MissionNotesDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\MissionNotesDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\MissionSave.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\MissionSave.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ModifyVariableDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ModifyVariableDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\OperatorArgTypeSelect.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\OperatorArgTypeSelect.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\OrientEditor.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\OrientEditor.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\PlayerStartEditor.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\PlayerStartEditor.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\PrefsDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\PrefsDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ReinforcementEditorDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ReinforcementEditorDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\Sexp_tree.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\Sexp_tree.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShieldSysDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShieldSysDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ship_select.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ship_select.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipCheckListBox.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipCheckListBox.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipClassEditorDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipClassEditorDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipEditorDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipEditorDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipFlagsDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipFlagsDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipGoalsDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipGoalsDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipSpecialDamage.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\ShipSpecialDamage.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\StarfieldEditor.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\StarfieldEditor.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\StdAfx.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\stdafx.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\TextViewDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\TextViewDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\WaypointPathDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\WaypointPathDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\WeaponEditorDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\WeaponEditorDlg.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\wing.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\wing.h +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\wing_editor.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FRED2\wing_editor.h +# End Source File +# End Target +# End Project diff --git a/winfiles/Fred2.plg b/winfiles/Fred2.plg new file mode 100644 index 0000000..3326b49 --- /dev/null +++ b/winfiles/Fred2.plg @@ -0,0 +1,77 @@ + + +
+

Build Log

+

+--------------------Configuration: fred2 - Win32 Release-------------------- +

+

Command Lines

+Creating temporary file "C:\WINDOWS\TEMP\RSPA095.TMP" with contents +[ +winmm.lib vfw32.lib msacm32.lib code.lib wsock32.lib /nologo /subsystem:windows /incremental:no /pdb:"Fred2.pdb" /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmtd.lib" /nodefaultlib:"nafxcwd.lib" /out:"E:\games\Freespace2\Fred2.exe" /libpath:"release" +.\Release\AddVariableDlg.obj +.\Release\AdjustGridDlg.obj +.\Release\AsteroidEditorDlg.obj +.\Release\BgBitmapDlg.obj +.\Release\BriefingEditorDlg.obj +.\Release\CampaignEditorDlg.obj +.\Release\CampaignFilelistBox.obj +.\Release\CampaignTreeView.obj +.\Release\CampaignTreeWnd.obj +.\Release\CmdBrief.obj +.\Release\CreateWingDlg.obj +.\Release\DebriefingEditorDlg.obj +.\Release\dialog1.obj +.\Release\DumpStats.obj +.\Release\EventEditor.obj +.\Release\FRED.obj +.\Release\FREDDoc.obj +.\Release\FredRender.obj +.\Release\FredStubs.obj +.\Release\FREDView.obj +.\Release\Grid.obj +.\Release\IgnoreOrdersDlg.obj +.\Release\InitialShips.obj +.\Release\InitialStatus.obj +.\Release\MainFrm.obj +.\Release\Management.obj +.\Release\MessageEditorDlg.obj +.\Release\MissionGoalsDlg.obj +.\Release\MissionNotesDlg.obj +.\Release\MissionSave.obj +.\Release\ModifyVariableDlg.obj +.\Release\OperatorArgTypeSelect.obj +.\Release\OrientEditor.obj +.\Release\PlayerStartEditor.obj +.\Release\PrefsDlg.obj +.\Release\ReinforcementEditorDlg.obj +.\Release\Sexp_tree.obj +.\Release\ShieldSysDlg.obj +.\Release\ship_select.obj +.\Release\ShipCheckListBox.obj +.\Release\ShipClassEditorDlg.obj +.\Release\ShipEditorDlg.obj +.\Release\ShipFlagsDlg.obj +.\Release\ShipGoalsDlg.obj +.\Release\ShipSpecialDamage.obj +.\Release\StarfieldEditor.obj +.\Release\StdAfx.obj +.\Release\TextViewDlg.obj +.\Release\WaypointPathDlg.obj +.\Release\WeaponEditorDlg.obj +.\Release\wing.obj +.\Release\wing_editor.obj +.\Release\FRED.res +.\Release\code.lib +] +Creating command line "link.exe @C:\WINDOWS\TEMP\RSPA095.TMP" +

Output Window

+Linking... + + + +

Results

+Fred2.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/winfiles/Freespace2.dsp b/winfiles/Freespace2.dsp new file mode 100644 index 0000000..b8cd9f5 --- /dev/null +++ b/winfiles/Freespace2.dsp @@ -0,0 +1,140 @@ +# Microsoft Developer Studio Project File - Name="Freespace2" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=Freespace2 - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Freespace2.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Freespace2.mak" CFG="Freespace2 - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Freespace2 - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "Freespace2 - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Freespace2", RHWBAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Freespace2 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /G5 /MT /W4 /GX /Zi /Ot /Ow /Og /Oi /Oy /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\nebula" /I "code\demo" /I "code\inetfile" /I "code\ExceptionHandler" /I "code\3dnow" /FI"PSTypes.h" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /U "_DEBUG" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib winmm.lib vfw32.lib msacm32.lib comctl32.lib code.lib /nologo /subsystem:windows /map /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /out:"E:\games\freespace2\FS2.exe" /libpath:"release" + +!ELSEIF "$(CFG)" == "Freespace2 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /G5 /MTd /W4 /Gm /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\nebula" /I "code\demo" /I "code\inetfile" /I "code\ExceptionHandler" /I "code\3dnow" /D "WIN32" /D "_WINDOWS" /D "_DEBUG" /D "NO_CD_CHECK" /U "NDEBUG" /Fr /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo "./debug/*.sbr" +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib winmm.lib vfw32.lib msacm32.lib comctl32.lib code.lib /nologo /subsystem:windows /incremental:no /debug /debugtype:both /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /out:"e:\games\freespace2\FS2.exe" /libpath:"debug" +# SUBTRACT LINK32 /pdb:none /map + +!ENDIF + +# Begin Target + +# Name "Freespace2 - Win32 Release" +# Name "Freespace2 - Win32 Debug" +# Begin Source File + +SOURCE=.\code\FREESPACE2\app_icon.ico +# End Source File +# Begin Source File + +SOURCE=.\code\FREESPACE2\FreeSpace.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FREESPACE2\FreeSpace.h +# End Source File +# Begin Source File + +SOURCE=.\code\FREESPACE2\FreeSpace.rc +# End Source File +# Begin Source File + +SOURCE=.\code\FREESPACE2\FreespaceResource.h +# End Source File +# Begin Source File + +SOURCE=.\code\freespace2\goal_com.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\freespace2\goal_fail.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\freespace2\goal_inc.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\freespace2\goal_none.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\freespace2\goal_ord.bmp +# End Source File +# Begin Source File + +SOURCE=.\code\FREESPACE2\LevelPaging.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\FREESPACE2\LevelPaging.h +# End Source File +# End Target +# End Project diff --git a/winfiles/Freespace2.plg b/winfiles/Freespace2.plg new file mode 100644 index 0000000..f667d15 --- /dev/null +++ b/winfiles/Freespace2.plg @@ -0,0 +1,310 @@ + + +
+

Build Log

+

+--------------------Configuration: code - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\DOCUME~1\daveb\LOCALS~1\Temp\RSP295.tmp" with contents +[ +/nologo /G5 /MTd /W4 /GX /Zi /Od /Ob2 /I ".." /I "anim" /I "asteroid" /I "bmpman" /I "cfile" /I "cmdline" /I "cmeasure" /I "controlconfig" /I "cutscene" /I "debris" /I "debugconsole" /I "directx" /I "fireball" /I "fred" /I "freespace2" /I "fs2launch" /I "gamehelp" /I "gamesequence" /I "gamesnd" /I "glide" /I "globalincs" /I "graphics" /I "hud" /I "io" /I "jumpnode" /I "lighting" /I "math" /I "menuui" /I "mission" /I "missionui" /I "model" /I "movie" /I "network" /I "object" /I "observer" /I "osapi" /I "palman" /I "parse" /I "particle" /I "pcxutils" /I "physics" /I "playerman" /I "popup" /I "radar" /I "render" /I "ship" /I "sndman" /I "sound" /I "starfield" /I "stats" /I "ui" /I "vcodec" /I "weapon" /I "localization" /I "fred2" /I "nebula" /I "TgaUtils" /I "demo" /I "inetfile" /I "exceptionhandler" /I "3dnow" /FI"PSTypes.h" /D "_WINDOWS" /D "WIN32" /D "_DEBUG" /U "NDEBUG" /FR"..\Debug/" /Fp"..\Debug/code.pch" /YX /Fo"..\Debug/" /Fd"..\Debug/" /FD /GZ /c +"C:\projects\freespace2_public\code\Ship\AiCode.cpp" +"C:\projects\freespace2_public\code\Ship\Ship.cpp" +"C:\projects\freespace2_public\code\Stats\Medals.cpp" +"C:\projects\freespace2_public\code\Weapon\Beam.cpp" +"C:\projects\freespace2_public\code\Network\multi_campaign.cpp" +] +Creating command line "cwcl.exe @C:\DOCUME~1\daveb\LOCALS~1\Temp\RSP295.tmp" +Creating temporary file "C:\DOCUME~1\daveb\LOCALS~1\Temp\RSP296.tmp" with contents +[ +/nologo /out:"..\Debug\code.lib" +\projects\freespace2_public\Debug\AnimPlay.obj +\projects\freespace2_public\Debug\PackUnpack.obj +\projects\freespace2_public\Debug\Asteroid.obj +\projects\freespace2_public\Debug\BmpMan.obj +\projects\freespace2_public\Debug\cfile.obj +\projects\freespace2_public\Debug\CfileArchive.obj +\projects\freespace2_public\Debug\CfileList.obj +\projects\freespace2_public\Debug\CfileSystem.obj +\projects\freespace2_public\Debug\cmdline.obj +\projects\freespace2_public\Debug\CMeasure.obj +\projects\freespace2_public\Debug\ControlsConfig.obj +\projects\freespace2_public\Debug\ControlsConfigCommon.obj +\projects\freespace2_public\Debug\Cutscenes.obj +\projects\freespace2_public\Debug\Debris.obj +\projects\freespace2_public\Debug\Console.obj +\projects\freespace2_public\Debug\FireBalls.obj +\projects\freespace2_public\Debug\WarpInEffect.obj +\projects\freespace2_public\Debug\ContextHelp.obj +\projects\freespace2_public\Debug\GameplayHelp.obj +\projects\freespace2_public\Debug\GameSequence.obj +\projects\freespace2_public\Debug\EventMusic.obj +\projects\freespace2_public\Debug\GameSnd.obj +\projects\freespace2_public\Debug\Glide.obj +\projects\freespace2_public\Debug\AlphaColors.obj +\projects\freespace2_public\Debug\crypt.obj +\projects\freespace2_public\Debug\SystemVars.obj +\projects\freespace2_public\Debug\version.obj +\projects\freespace2_public\Debug\WinDebug.obj +\projects\freespace2_public\Debug\2d.obj +\projects\freespace2_public\Debug\aaline.obj +\projects\freespace2_public\Debug\Bitblt.obj +\projects\freespace2_public\Debug\Circle.obj +\projects\freespace2_public\Debug\Colors.obj +\projects\freespace2_public\Debug\Font.obj +\projects\freespace2_public\Debug\Gradient.obj +\projects\freespace2_public\Debug\GrD3D.obj +\projects\freespace2_public\Debug\GrD3DRender.obj +\projects\freespace2_public\Debug\GrD3DTexture.obj +\projects\freespace2_public\Debug\GrDirectDraw.obj +\projects\freespace2_public\Debug\GrGlide.obj +\projects\freespace2_public\Debug\GrGlideTexture.obj +\projects\freespace2_public\Debug\GrOpenGL.obj +\projects\freespace2_public\Debug\GrSoft.obj +\projects\freespace2_public\Debug\GrZbuffer.obj +\projects\freespace2_public\Debug\Line.obj +\projects\freespace2_public\Debug\Pixel.obj +\projects\freespace2_public\Debug\Rect.obj +\projects\freespace2_public\Debug\Scaler.obj +\projects\freespace2_public\Debug\Shade.obj +\projects\freespace2_public\Debug\TmapGenericScans.obj +\projects\freespace2_public\Debug\Tmapper.obj +\projects\freespace2_public\Debug\TmapScanline.obj +\projects\freespace2_public\Debug\TmapScanTiled128x128.obj +\projects\freespace2_public\Debug\TmapScanTiled16x16.obj +\projects\freespace2_public\Debug\TmapScanTiled256x256.obj +\projects\freespace2_public\Debug\TmapScanTiled32x32.obj +\projects\freespace2_public\Debug\TmapScanTiled64x64.obj +\projects\freespace2_public\Debug\HUD.obj +\projects\freespace2_public\Debug\HudArtillery.obj +\projects\freespace2_public\Debug\HUDbrackets.obj +\projects\freespace2_public\Debug\HUDconfig.obj +\projects\freespace2_public\Debug\HUDescort.obj +\projects\freespace2_public\Debug\HUDets.obj +\projects\freespace2_public\Debug\HUDlock.obj +\projects\freespace2_public\Debug\HUDmessage.obj +\projects\freespace2_public\Debug\HUDObserver.obj +\projects\freespace2_public\Debug\HUDreticle.obj +\projects\freespace2_public\Debug\HUDshield.obj +\projects\freespace2_public\Debug\HUDsquadmsg.obj +\projects\freespace2_public\Debug\HUDtarget.obj +\projects\freespace2_public\Debug\HUDtargetbox.obj +\projects\freespace2_public\Debug\HUDWingmanStatus.obj +\projects\freespace2_public\Debug\Joy.obj +\projects\freespace2_public\Debug\Joy_ff.obj +\projects\freespace2_public\Debug\Key.obj +\projects\freespace2_public\Debug\KeyControl.obj +\projects\freespace2_public\Debug\Mouse.obj +\projects\freespace2_public\Debug\swff_lib.obj +\projects\freespace2_public\Debug\Timer.obj +\projects\freespace2_public\Debug\JumpNode.obj +\projects\freespace2_public\Debug\Lighting.obj +\projects\freespace2_public\Debug\Fix.obj +\projects\freespace2_public\Debug\Floating.obj +\projects\freespace2_public\Debug\Fvi.obj +\projects\freespace2_public\Debug\spline.obj +\projects\freespace2_public\Debug\StaticRand.obj +\projects\freespace2_public\Debug\VecMat.obj +\projects\freespace2_public\Debug\Barracks.obj +\projects\freespace2_public\Debug\Credits.obj +\projects\freespace2_public\Debug\fishtank.obj +\projects\freespace2_public\Debug\MainHallMenu.obj +\projects\freespace2_public\Debug\MainHallTemp.obj +\projects\freespace2_public\Debug\OptionsMenu.obj +\projects\freespace2_public\Debug\OptionsMenuMulti.obj +\projects\freespace2_public\Debug\PlayerMenu.obj +\projects\freespace2_public\Debug\ReadyRoom.obj +\projects\freespace2_public\Debug\SnazzyUI.obj +\projects\freespace2_public\Debug\TechMenu.obj +\projects\freespace2_public\Debug\TrainingMenu.obj +\projects\freespace2_public\Debug\MissionBriefCommon.obj +\projects\freespace2_public\Debug\MissionCampaign.obj +\projects\freespace2_public\Debug\MissionGoals.obj +\projects\freespace2_public\Debug\MissionGrid.obj +\projects\freespace2_public\Debug\MissionHotKey.obj +\projects\freespace2_public\Debug\MissionLoad.obj +\projects\freespace2_public\Debug\MissionLog.obj +\projects\freespace2_public\Debug\MissionMessage.obj +\projects\freespace2_public\Debug\MissionParse.obj +\projects\freespace2_public\Debug\MissionTraining.obj +\projects\freespace2_public\Debug\Chatbox.obj +\projects\freespace2_public\Debug\MissionBrief.obj +\projects\freespace2_public\Debug\MissionCmdBrief.obj +\projects\freespace2_public\Debug\MissionDebrief.obj +\projects\freespace2_public\Debug\MissionLoopBrief.obj +\projects\freespace2_public\Debug\MissionPause.obj +\projects\freespace2_public\Debug\MissionRecommend.obj +\projects\freespace2_public\Debug\MissionScreenCommon.obj +\projects\freespace2_public\Debug\MissionShipChoice.obj +\projects\freespace2_public\Debug\MissionStats.obj +\projects\freespace2_public\Debug\MissionWeaponChoice.obj +\projects\freespace2_public\Debug\RedAlert.obj +\projects\freespace2_public\Debug\ModelCollide.obj +\projects\freespace2_public\Debug\ModelInterp.obj +\projects\freespace2_public\Debug\ModelOctant.obj +\projects\freespace2_public\Debug\ModelRead.obj +\projects\freespace2_public\Debug\CollideDebrisShip.obj +\projects\freespace2_public\Debug\CollideDebrisWeapon.obj +\projects\freespace2_public\Debug\CollideShipShip.obj +\projects\freespace2_public\Debug\CollideShipWeapon.obj +\projects\freespace2_public\Debug\CollideWeaponWeapon.obj +\projects\freespace2_public\Debug\ObjCollide.obj +\projects\freespace2_public\Debug\Object.obj +\projects\freespace2_public\Debug\ObjectSnd.obj +\projects\freespace2_public\Debug\ObjectSort.obj +\projects\freespace2_public\Debug\Observer.obj +\projects\freespace2_public\Debug\OsApi.obj +\projects\freespace2_public\Debug\OsRegistry.obj +\projects\freespace2_public\Debug\OutWnd.obj +\projects\freespace2_public\Debug\PalMan.obj +\projects\freespace2_public\Debug\Encrypt.obj +\projects\freespace2_public\Debug\PARSELO.OBJ +\projects\freespace2_public\Debug\SEXP.OBJ +\projects\freespace2_public\Debug\Particle.obj +\projects\freespace2_public\Debug\pcxutils.obj +\projects\freespace2_public\Debug\Physics.obj +\projects\freespace2_public\Debug\ManagePilot.obj +\projects\freespace2_public\Debug\PlayerControl.obj +\projects\freespace2_public\Debug\Popup.obj +\projects\freespace2_public\Debug\PopupDead.obj +\projects\freespace2_public\Debug\Radar.obj +\projects\freespace2_public\Debug\3dClipper.obj +\projects\freespace2_public\Debug\3ddraw.obj +\projects\freespace2_public\Debug\3dLaser.obj +\projects\freespace2_public\Debug\3dMath.obj +\projects\freespace2_public\Debug\3dSetup.obj +\projects\freespace2_public\Debug\Afterburner.obj +\projects\freespace2_public\Debug\ai.obj +\projects\freespace2_public\Debug\AiBig.obj +\projects\freespace2_public\Debug\AiCode.obj +\projects\freespace2_public\Debug\AiGoals.obj +\projects\freespace2_public\Debug\AWACS.obj +\projects\freespace2_public\Debug\Shield.obj +\projects\freespace2_public\Debug\Ship.obj +\projects\freespace2_public\Debug\ShipContrails.obj +\projects\freespace2_public\Debug\ShipFX.obj +\projects\freespace2_public\Debug\ShipHit.obj +\projects\freespace2_public\Debug\acm.obj +\projects\freespace2_public\Debug\AudioStr.obj +\projects\freespace2_public\Debug\ds.obj +\projects\freespace2_public\Debug\ds3d.obj +\projects\freespace2_public\Debug\dscap.obj +\projects\freespace2_public\Debug\midifile.obj +\projects\freespace2_public\Debug\RBAudio.obj +\projects\freespace2_public\Debug\rsx_lib.obj +\projects\freespace2_public\Debug\rtvoice.obj +\projects\freespace2_public\Debug\Sound.obj +\projects\freespace2_public\Debug\WinMIDI.obj +\projects\freespace2_public\Debug\winmidi_base.obj +\projects\freespace2_public\Debug\Nebula.obj +\projects\freespace2_public\Debug\StarField.obj +\projects\freespace2_public\Debug\Supernova.obj +\projects\freespace2_public\Debug\Medals.obj +\projects\freespace2_public\Debug\Scoring.obj +\projects\freespace2_public\Debug\Stats.obj +\projects\freespace2_public\Debug\BUTTON.obj +\projects\freespace2_public\Debug\CHECKBOX.obj +\projects\freespace2_public\Debug\GADGET.obj +\projects\freespace2_public\Debug\icon.obj +\projects\freespace2_public\Debug\INPUTBOX.obj +\projects\freespace2_public\Debug\KEYTRAP.obj +\projects\freespace2_public\Debug\LISTBOX.obj +\projects\freespace2_public\Debug\RADIO.obj +\projects\freespace2_public\Debug\SCROLL.obj +\projects\freespace2_public\Debug\slider.obj +\projects\freespace2_public\Debug\SLIDER2.obj +\projects\freespace2_public\Debug\UIDRAW.obj +\projects\freespace2_public\Debug\UIMOUSE.obj +\projects\freespace2_public\Debug\WINDOW.obj +\projects\freespace2_public\Debug\CODEC1.OBJ +\projects\freespace2_public\Debug\Beam.obj +\projects\freespace2_public\Debug\Corkscrew.obj +\projects\freespace2_public\Debug\Emp.obj +\projects\freespace2_public\Debug\Flak.obj +\projects\freespace2_public\Debug\MuzzleFlash.obj +\projects\freespace2_public\Debug\Shockwave.obj +\projects\freespace2_public\Debug\Swarm.obj +\projects\freespace2_public\Debug\Trails.obj +\projects\freespace2_public\Debug\Weapons.obj +\projects\freespace2_public\Debug\Neb.obj +\projects\freespace2_public\Debug\NebLightning.obj +\projects\freespace2_public\Debug\fhash.obj +\projects\freespace2_public\Debug\localize.obj +\projects\freespace2_public\Debug\TgaUtils.obj +\projects\freespace2_public\Debug\Demo.obj +\projects\freespace2_public\Debug\CFtp.obj +\projects\freespace2_public\Debug\Chttpget.obj +\projects\freespace2_public\Debug\inetgetfile.obj +\projects\freespace2_public\Debug\ExceptionHandler.obj +\projects\freespace2_public\Debug\Multi.obj +\projects\freespace2_public\Debug\multi_campaign.obj +\projects\freespace2_public\Debug\multi_data.obj +\projects\freespace2_public\Debug\multi_dogfight.obj +\projects\freespace2_public\Debug\multi_endgame.obj +\projects\freespace2_public\Debug\multi_ingame.obj +\projects\freespace2_public\Debug\multi_kick.obj +\projects\freespace2_public\Debug\multi_log.obj +\projects\freespace2_public\Debug\multi_obj.obj +\projects\freespace2_public\Debug\multi_observer.obj +\projects\freespace2_public\Debug\multi_oo.obj +\projects\freespace2_public\Debug\multi_options.obj +\projects\freespace2_public\Debug\multi_pause.obj +\projects\freespace2_public\Debug\multi_pinfo.obj +\projects\freespace2_public\Debug\multi_ping.obj +\projects\freespace2_public\Debug\multi_pmsg.obj +\projects\freespace2_public\Debug\multi_rate.obj +\projects\freespace2_public\Debug\multi_respawn.obj +\projects\freespace2_public\Debug\multi_team.obj +\projects\freespace2_public\Debug\multi_update.obj +\projects\freespace2_public\Debug\multi_voice.obj +\projects\freespace2_public\Debug\multi_xfer.obj +\projects\freespace2_public\Debug\multilag.obj +\projects\freespace2_public\Debug\MultiMsgs.obj +\projects\freespace2_public\Debug\MultiTeamSelect.obj +\projects\freespace2_public\Debug\MultiUI.obj +\projects\freespace2_public\Debug\MultiUtil.obj +\projects\freespace2_public\Debug\PsNet.obj +\projects\freespace2_public\Debug\Psnet2.obj +\projects\freespace2_public\Debug\stand_gui.obj +.\DirectX\vDdraw.lib +.\DirectX\vDinput.lib +.\DirectX\vDsound.lib +.\DirectX\vDxguid.lib +.\Sound\VerifyA3D.lib +] +Creating command line "link.exe -lib @C:\DOCUME~1\daveb\LOCALS~1\Temp\RSP296.tmp" +

Output Window

+Compiling... +AiCode.cpp +Ship.cpp +Medals.cpp +Beam.cpp +multi_campaign.cpp +Creating library... +vDinput.lib(DINPUT.dll) : warning LNK4006: __NULL_IMPORT_DESCRIPTOR already defined in vDdraw.lib(DDRAW.dll); second definition ignored +vDsound.lib(DSOUND.dll) : warning LNK4006: __NULL_IMPORT_DESCRIPTOR already defined in vDdraw.lib(DDRAW.dll); second definition ignored +VerifyA3D.lib(VERSION.dll) : warning LNK4006: __NULL_IMPORT_DESCRIPTOR already defined in vDdraw.lib(DDRAW.dll); second definition ignored +

+--------------------Configuration: Freespace2 - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\DOCUME~1\daveb\LOCALS~1\Temp\RSP297.tmp" with contents +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib winmm.lib vfw32.lib msacm32.lib comctl32.lib code.lib /nologo /subsystem:windows /incremental:no /pdb:"FS2.pdb" /debug /debugtype:both /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /out:"e:\games\freespace2\FS2.exe" /libpath:"debug" +.\Debug\FreeSpace.obj +.\Debug\LevelPaging.obj +.\Debug\FreeSpace.res +.\Debug\code.lib +] +Creating command line "cwlink.exe @C:\DOCUME~1\daveb\LOCALS~1\Temp\RSP297.tmp" +

Output Window

+Linking... + + + +

Results

+FS2.exe - 0 error(s), 3 warning(s) +
+ + diff --git a/winfiles/HelpEd.dsp b/winfiles/HelpEd.dsp new file mode 100644 index 0000000..7cf4274 --- /dev/null +++ b/winfiles/HelpEd.dsp @@ -0,0 +1,167 @@ +# Microsoft Developer Studio Project File - Name="HelpEd" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=HelpEd - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "HelpEd.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "HelpEd.mak" CFG="HelpEd - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "HelpEd - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "HelpEd - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Freespace2", WSSCAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "HelpEd - Win32 Release" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386 +# ADD LINK32 /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "HelpEd - Win32 Debug" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "HelpEd___Win32_Debug" +# PROP BASE Intermediate_Dir "HelpEd___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "HelpEdDebug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /G5 /MDd /W4 /Gm- /GX /Zi /Od /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 winmm.lib /nologo /subsystem:windows /incremental:no /debug /debugtype:both /machine:I386 /nodefaultlib:"libcd" /nodefaultlib:"libc" /pdbtype:sept /libpath:"debug" + +!ENDIF + +# Begin Target + +# Name "HelpEd - Win32 Release" +# Name "HelpEd - Win32 Debug" +# Begin Group "res" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\code\HelpEd\res\HelpEd.ico +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\res\HelpEd.rc2 +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\res\HelpEdDoc.ico +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\res\Toolbar.bmp +# End Source File +# End Group +# Begin Source File + +SOURCE=.\code\HelpEd\HelpEd.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\HelpEd.h +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\HelpEd.rc +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\HelpEdDoc.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\HelpEdDoc.h +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\HelpEdLine.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\HelpEdLine.h +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\HelpEdView.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\HelpEdView.h +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\MainFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\Resource.h +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\StdAfx.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\HelpEd\StdAfx.h +# End Source File +# End Target +# End Project diff --git a/winfiles/HelpEd.plg b/winfiles/HelpEd.plg new file mode 100644 index 0000000..7cbdd1c --- /dev/null +++ b/winfiles/HelpEd.plg @@ -0,0 +1,52 @@ + + +
+

Build Log

+

+--------------------Configuration: HelpEd - Win32 Debug-------------------- +

+

Command Lines

+Creating command line "rc.exe /l 0x409 /fo"HelpEdDebug/HelpEd.res" /i "code\HelpEd" /d "_DEBUG" /d "_AFXDLL" "E:\projects\freespace2_public\code\HelpEd\HelpEd.rc"" +Creating temporary file "C:\WINDOWS\TEMP\RSPD2C3.TMP" with contents +[ +/nologo /G5 /MDd /W4 /GX /Zi /Od /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Fo"HelpEdDebug/" /Fd"HelpEdDebug/" /FD /GZ /c +"E:\projects\freespace2_public\code\HelpEd\HelpEd.cpp" +"E:\projects\freespace2_public\code\HelpEd\HelpEdDoc.cpp" +"E:\projects\freespace2_public\code\HelpEd\HelpEdLine.cpp" +"E:\projects\freespace2_public\code\HelpEd\HelpEdView.cpp" +"E:\projects\freespace2_public\code\HelpEd\MainFrm.cpp" +"E:\projects\freespace2_public\code\HelpEd\StdAfx.cpp" +] +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSPD2C3.TMP" +Creating temporary file "C:\WINDOWS\TEMP\RSPD2C4.TMP" with contents +[ +winmm.lib /nologo /subsystem:windows /incremental:no /pdb:"HelpEd.pdb" /debug /debugtype:both /machine:I386 /nodefaultlib:"libcd" /nodefaultlib:"libc" /out:"HelpEd.exe" /pdbtype:sept /libpath:"debug" +.\HelpEdDebug\HelpEd.obj +.\HelpEdDebug\HelpEdDoc.obj +.\HelpEdDebug\HelpEdLine.obj +.\HelpEdDebug\HelpEdView.obj +.\HelpEdDebug\MainFrm.obj +.\HelpEdDebug\StdAfx.obj +.\HelpEdDebug\HelpEd.res +.\Debug\code.lib +] +Creating command line "link.exe @C:\WINDOWS\TEMP\RSPD2C4.TMP" +

Output Window

+Compiling resources... +Compiling... +HelpEd.cpp +HelpEdDoc.cpp +HelpEdLine.cpp +HelpEdView.cpp +MainFrm.cpp +StdAfx.cpp +Generating Code... +Linking... + + + +

Results

+HelpEd.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/winfiles/Nebedit.dsp b/winfiles/Nebedit.dsp new file mode 100644 index 0000000..5c0b358 --- /dev/null +++ b/winfiles/Nebedit.dsp @@ -0,0 +1,99 @@ +# Microsoft Developer Studio Project File - Name="Nebedit" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=Nebedit - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Nebedit.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Nebedit.mak" CFG="Nebedit - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Nebedit - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "Nebedit - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Freespace2", RQCCAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Nebedit - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "NebeditRelease" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\nebula" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /U "_DEBUG" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /libpath:"release" + +!ELSEIF "$(CFG)" == "Nebedit - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Nebedit___Win32_Debug" +# PROP BASE Intermediate_Dir "Nebedit___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "NebEditDebug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /G5 /MTd /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "NEBEDIT" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib msacm32.lib comctl32.lib wsock32.lib code.lib /nologo /subsystem:windows /debug /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /out:"E:\games\freespace2\Nebedit.exe" /pdbtype:sept /libpath:"debug" + +!ENDIF + +# Begin Target + +# Name "Nebedit - Win32 Release" +# Name "Nebedit - Win32 Debug" +# Begin Source File + +SOURCE=.\code\nebedit\NebEdit.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\nebedit\Nebstubs.cpp +# End Source File +# End Target +# End Project diff --git a/winfiles/Nebedit.plg b/winfiles/Nebedit.plg new file mode 100644 index 0000000..b9782b1 --- /dev/null +++ b/winfiles/Nebedit.plg @@ -0,0 +1,37 @@ + + +
+

Build Log

+

+--------------------Configuration: Nebedit - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\WINDOWS\TEMP\RSPD221.TMP" with contents +[ +/nologo /G5 /MTd /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "NEBEDIT" /Fo"NebEditDebug/" /Fd"NebEditDebug/" /FD /GZ /c +"E:\projects\freespace2_public\code\nebedit\NebEdit.cpp" +"E:\projects\freespace2_public\code\nebedit\Nebstubs.cpp" +] +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSPD221.TMP" +Creating temporary file "C:\WINDOWS\TEMP\RSPD222.TMP" with contents +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib msacm32.lib comctl32.lib wsock32.lib code.lib /nologo /subsystem:windows /incremental:yes /pdb:"Nebedit.pdb" /debug /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /out:"E:\games\freespace2\Nebedit.exe" /pdbtype:sept /libpath:"debug" +.\NebEditDebug\NebEdit.obj +.\NebEditDebug\Nebstubs.obj +.\Debug\code.lib +] +Creating command line "link.exe @C:\WINDOWS\TEMP\RSPD222.TMP" +

Output Window

+Compiling... +NebEdit.cpp +Nebstubs.cpp +Generating Code... +Linking... + + + +

Results

+Nebedit.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/winfiles/Pofview.dsp b/winfiles/Pofview.dsp new file mode 100644 index 0000000..4db79cc --- /dev/null +++ b/winfiles/Pofview.dsp @@ -0,0 +1,179 @@ +# Microsoft Developer Studio Project File - Name="Pofview" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=Pofview - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Pofview.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Pofview.mak" CFG="Pofview - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Pofview - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "Pofview - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Freespace2", ARCCAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Pofview - Win32 Release" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "PofViewRelease" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\nebula" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /U "_DEBUG" /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386 +# ADD LINK32 winmm.lib msacm32.lib comctl32.lib wsock32.lib code.lib /nologo /subsystem:windows /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmtd.lib" /libpath:"release" + +!ELSEIF "$(CFG)" == "Pofview - Win32 Debug" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Pofview___Win32_Debug" +# PROP BASE Intermediate_Dir "Pofview___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "PofViewDebug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /G5 /MDd /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\fred2" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_AFXDLL" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 winmm.lib msacm32.lib comctl32.lib wsock32.lib code.lib /nologo /subsystem:windows /debug /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmtd.lib" /out:"E:\games\freespace2\Pofview.exe" /pdbtype:sept /libpath:"debug" + +!ENDIF + +# Begin Target + +# Name "Pofview - Win32 Release" +# Name "Pofview - Win32 Debug" +# Begin Group "res" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\code\pofview\res\PofView.ico +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\res\PofView.rc2 +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\res\PofViewDoc.ico +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\res\Toolbar.bmp +# End Source File +# End Group +# Begin Source File + +SOURCE=.\code\pofview\ChildFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\ChildFrm.h +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\MainFrm.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\MainFrm.h +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\ObjectTree.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\ObjectTree.h +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\PofView.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\PofView.h +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\PofView.rc +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\PofViewDoc.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\PofViewDoc.h +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\PofViewStubs.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\PofViewView.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\PofViewView.h +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\resource.h +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\StdAfx.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\pofview\StdAfx.h +# End Source File +# End Target +# End Project diff --git a/winfiles/Pofview.plg b/winfiles/Pofview.plg new file mode 100644 index 0000000..51772a7 --- /dev/null +++ b/winfiles/Pofview.plg @@ -0,0 +1,58 @@ + + +
+

Build Log

+

+--------------------Configuration: Pofview - Win32 Debug-------------------- +

+

Command Lines

+Creating command line "rc.exe /l 0x409 /fo"PofViewDebug/PofView.res" /i "code\pofview" /d "_DEBUG" /d "_AFXDLL" "E:\projects\freespace2_public\code\pofview\PofView.rc"" +Creating temporary file "C:\WINDOWS\TEMP\RSPD022.TMP" with contents +[ +/nologo /G5 /MDd /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\fred2" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_AFXDLL" /Fo"PofViewDebug/" /Fd"PofViewDebug/" /FD /GZ /c +"E:\projects\freespace2_public\code\pofview\ChildFrm.cpp" +"E:\projects\freespace2_public\code\pofview\MainFrm.cpp" +"E:\projects\freespace2_public\code\pofview\ObjectTree.cpp" +"E:\projects\freespace2_public\code\pofview\PofView.cpp" +"E:\projects\freespace2_public\code\pofview\PofViewDoc.cpp" +"E:\projects\freespace2_public\code\pofview\PofViewStubs.cpp" +"E:\projects\freespace2_public\code\pofview\PofViewView.cpp" +"E:\projects\freespace2_public\code\pofview\StdAfx.cpp" +] +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSPD022.TMP" +Creating temporary file "C:\WINDOWS\TEMP\RSPD023.TMP" with contents +[ +winmm.lib msacm32.lib comctl32.lib wsock32.lib code.lib /nologo /subsystem:windows /incremental:yes /pdb:"Pofview.pdb" /debug /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /nodefaultlib:"libcmtd.lib" /out:"E:\games\freespace2\Pofview.exe" /pdbtype:sept /libpath:"debug" +.\PofViewDebug\ChildFrm.obj +.\PofViewDebug\MainFrm.obj +.\PofViewDebug\ObjectTree.obj +.\PofViewDebug\PofView.obj +.\PofViewDebug\PofViewDoc.obj +.\PofViewDebug\PofViewStubs.obj +.\PofViewDebug\PofViewView.obj +.\PofViewDebug\StdAfx.obj +.\PofViewDebug\PofView.res +.\Debug\code.lib +] +Creating command line "link.exe @C:\WINDOWS\TEMP\RSPD023.TMP" +

Output Window

+Compiling resources... +Compiling... +ChildFrm.cpp +MainFrm.cpp +ObjectTree.cpp +PofView.cpp +PofViewDoc.cpp +PofViewStubs.cpp +PofViewView.cpp +StdAfx.cpp +Generating Code... +Linking... + + + +

Results

+Pofview.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/winfiles/Scramble.dsp b/winfiles/Scramble.dsp new file mode 100644 index 0000000..2533b0e --- /dev/null +++ b/winfiles/Scramble.dsp @@ -0,0 +1,94 @@ +# Microsoft Developer Studio Project File - Name="Scramble" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Scramble - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Scramble.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Scramble.mak" CFG="Scramble - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Scramble - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Scramble - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/Freespace2", XSCCAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Scramble - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "" +# PROP Intermediate_Dir "ScrambleRelease" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /I "code\nebula" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /U "_DEBUG" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /libpath:"release" + +!ELSEIF "$(CFG)" == "Scramble - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Scramble___Win32_Debug" +# PROP BASE Intermediate_Dir "Scramble___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "" +# PROP Intermediate_Dir "ScrambleDebug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /G5 /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib msacm32.lib comctl32.lib wsock32.lib code.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /pdbtype:sept /libpath:"debug" + +!ENDIF + +# Begin Target + +# Name "Scramble - Win32 Release" +# Name "Scramble - Win32 Debug" +# Begin Source File + +SOURCE=.\code\Scramble\scramble.cpp +# End Source File +# Begin Source File + +SOURCE=.\code\Scramble\scramble.h +# End Source File +# End Target +# End Project diff --git a/winfiles/Scramble.plg b/winfiles/Scramble.plg new file mode 100644 index 0000000..8b42304 --- /dev/null +++ b/winfiles/Scramble.plg @@ -0,0 +1,33 @@ + + +
+

Build Log

+

+--------------------Configuration: Scramble - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\WINDOWS\TEMP\RSPC383.TMP" with contents +[ +/nologo /G5 /MLd /W4 /GX /Zi /Od /Ob2 /I "code\anim" /I "code\asteroid" /I "code\bmpman" /I "code\cfile" /I "code\cmdline" /I "code\cmeasure" /I "code\controlconfig" /I "code\cutscene" /I "code\debris" /I "code\debugconsole" /I "code\directx" /I "code\fireball" /I "code\gamehelp" /I "code\freespace2" /I "code\fs2launch" /I "code\fred" /I "code\gamesequence" /I "code\gamesnd" /I "code\glide" /I "code\globalincs" /I "code\graphics" /I "code\hud" /I "code\io" /I "code\jumpnode" /I "code\lighting" /I "code\math" /I "code\menuui" /I "code\mission" /I "code\missionui" /I "code\model" /I "code\movie" /I "code\network" /I "code\object" /I "code\observer" /I "code\osapi" /I "code\palman" /I "code\parse" /I "code\particle" /I "code\pcxutils" /I "code\physics" /I "code\playerman" /I "code\popup" /I "code\radar" /I "code\render" /I "code\ship" /I "code\sndman" /I "code\sound" /I "code\starfield" /I "code\stats" /I "code\testcode" /I "code\ui" /I "code\vcodec" /I "code\weapon" /I "code\localization" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Fo"ScrambleDebug/" /Fd"ScrambleDebug/" /FD /GZ /c +"E:\projects\freespace2_public\code\Scramble\scramble.cpp" +] +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSPC383.TMP" +Creating temporary file "C:\WINDOWS\TEMP\RSPC384.TMP" with contents +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib msacm32.lib comctl32.lib wsock32.lib code.lib /nologo /subsystem:console /incremental:yes /pdb:"Scramble.pdb" /debug /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcd.lib" /out:"Scramble.exe" /pdbtype:sept /libpath:"debug" +.\ScrambleDebug\scramble.obj +.\Debug\code.lib +] +Creating command line "link.exe @C:\WINDOWS\TEMP\RSPC384.TMP" +

Output Window

+Compiling... +scramble.cpp +Linking... + + + +

Results

+Scramble.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/winfiles/freespace2.dsw b/winfiles/freespace2.dsw new file mode 100644 index 0000000..411e8f1 --- /dev/null +++ b/winfiles/freespace2.dsw @@ -0,0 +1,173 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Ac"=.\Ac.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name code + End Project Dependency +}}} + +############################################################################### + +Project: "Cfilearchiver"=.\Cfilearchiver.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "Cryptstring"=.\Cryptstring.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "Fonttool"=.\Fonttool.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name code + End Project Dependency +}}} + +############################################################################### + +Project: "Freespace2"=.\Freespace2.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name code + End Project Dependency +}}} + +############################################################################### + +Project: "HelpEd"=.\HelpEd.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name code + End Project Dependency +}}} + +############################################################################### + +Project: "Nebedit"=.\Nebedit.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name code + End Project Dependency +}}} + +############################################################################### + +Project: "Pofview"=.\Pofview.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name code + End Project Dependency +}}} + +############################################################################### + +Project: "Scramble"=.\Scramble.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name code + End Project Dependency +}}} + +############################################################################### + +Project: "code"=.\code\code.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "fred2"=.\Fred2.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name code + End Project Dependency +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/winfiles/freespace2.opt b/winfiles/freespace2.opt new file mode 100644 index 0000000000000000000000000000000000000000..fce6df3ccdf7247bcdfc7bfb8be2ca2340431063 GIT binary patch literal 115200 zcmeHQdvqgLd6#xATatae-d*ozlMqls-b)_qB?Ll9V@vW{8+$Dz?>d0VW<0u-+Yhzzq9ctx88ojPXl$!C4u#UKRgu)bSr;fhwnij z4+OCOsi!3LKjrt=LGWi@o+S)CiE+ko%+^D?AR&l6<~@)wBm#*-dLi;S%kTY=4G>I$ zz(&X>$Y#hOWD7*<9f!|tkmDgIKu&}_2XYeRxsc~Uq+SZ2r$C+$ITdmmyEtvr)5W}9A@M%@Iz{(Dnt(Y-rrn<=aLM`W?{jDF| z_V_(P*}tzhjovNnosGTGz=FU?vKWU3kzBM}Vu5u**}0s9^0S@3TraO<)yayF5%g|? zEgQW_9*=YxK>zEpkvtbV?Y~cUB2N}9NgQqhlCqaZX(pEnkSbsoK0T-Ehk2w^vJLub%ij!GSWi=MJM zw_u1vXVKfFT`hmBUfo(MJGNsPrTjVcmhqaVzjc>x=EQb-GpRBo|MNHMDT!H;Gn_U9 z-Q#NcTSaTf^#yc?V8xpN{}M?E!vJ2~TcVFs81W`G%B2ABb6fEi#0 zm;q*h8DIvO0cL<1cm^0)Uwfk?O?qtq0|{*Z^US?$|9b|`NbZsuU+-&!wcqJNpIK=v({)|$07+vQiU{}iJ5Q|M{98i8Na z>GT`ouUGo+wEqj5^dHB58rWxn$al%cJkUZ1G3losMgM=I^uHcQE?V>Ri^L0>ZSOa(>^OIBc@|AGTdw0?KPa%EXU9b!i7D%w4%tCaxU^z zR(I#eLi@q+9RJ-qQ2@t(cX|O~J!XIzU+$AUJR3^v(z1q#W*wE%FWSiC0hJg-rs<{#y8mC9pX22_{ zEZ4SK-;;EWTlLaDu5GJc2GF%_RLM2DwhgZ&qFmc%)g+#4+w$r}rfb_a5~?2K%LF&i z3@`)C05iZ0Fayj0Gr$Zm1Iz$3zzi@0&te9y{Dm9%Jun=PofDf*?b$gLOC(43?&|hy zu!IBgwCm{AptEUMmm3)+Y?Kiw(_&Xk_6#V0PKen`UKZT>MM%1d%5T4`ev_PahX=FS zkqjcwdjs()&B_ZW<%nXZ%_>b)O@cvGqVmU@5M@!yiITqLb+SGuP_$++5U;I?!`i7C zC1H6gq3~d0_q6)|)3t-L{jcVixwa-Rw*M)0nb`g(F9p0X1Iz$3zzi@0%m6dM3@`)C z05iZ0Fayj$3kH1q--+?j)Od0@6CX?MOJ-J^0yj3Pt6Z}RQd^$1YRhJ}!V{Zo)R_(C z;9D3ApI#SA!>ZlSw*s>L?`b-Mvj3l7JDvUiT3mtn&&&Wbzzi@0%m6dM3@`)C05iZ0 zFayj0GjOaK@be$Wk#%q>P!2c&8)8*6K;}E>Jcj&>ebDQIbejKgw`{jA|Kg~Z`O(3r z%>CHuasv8~LRm_u{ZGlp8~V49ZBbzF%gDeevr!I7t+vZ^(SJX*WZ!aWy8#rgkV-Fb zHGTfa&|?98Cy{saNS3YW-|gA`zmshpspRdyZ~r|$o_R%jDm#@)?b)^3`nw@+EtMVH zv5Zpw+-ilKn(epsRwpW3HM9F3-)w!I)wd&O(WCE3vhZ}ALuU(y-X^S^{Xg0N(@Pi1 z_Fs9v@xlx+1Iz$3zzi@0%m6dM3@`)C05iZ0Fays31HS!l>*(a3sj11y@zut^zSV_1 zYL>qi`rCZ{W;VaA4J(hv;CD1#NMb!x-?54m*ByJNX6*8eK5YN1QNr=xC_$uI`n<6q zthFVBMEU--D*`>*9TC)Bp7w*z*An#V#XCNG-Fqu{b?GMgFA4xK9&h1B?EXmb;dR%s-+ zHR-NuS(HuvJXB3O>8782y1lneT}kY@oD(faDp#((fwF^~m(k%$&M>o%b?$sHutCj^ zDT`lwLyBbQjq;e`P&(Z?duNVPh?F(U76p^i=_^(Wj$6NYUYW@+3s%OjQ{<|yTUPZ`D1Z9yq+^c3U8T-!!_(`LC+P6&;hFMij% zOszm3z76iAG0P72iq!0#kkf0gV-%=eiHz+{6il5wSfM>)mTIFXUodhcVbSWS3tFCR zX3`!m8^M7xZ!I1wE(}HfN9>Mkn3FF(FD7veyyKvNZi5 zdE9!Yj3Rw@MUaGeB9%x{U4N!xn_dF}D%q6`!u?k3^M ziZfjz&;4H8(Mq}f`3gDryLTo=FQ=~CJrk3OexjxBh?26 zN~dq-LPAc1KKGv4cJSjye%@&}rFNT3`{&3zS#)yN78YK;QA3ZU?Wv`*Ac+d6?UHbY zsAfXol3E|Go^E6>p*drbYN8)Epei$^Hq=Dp6UfV6vFLg0-9l#%kbO>BNSBWO+8wh< zHaoRrUn)VJd0j)~ITDFmB4^MSufECI194QVY;&iL3>)L7u95fr=4S8UIsi@^rK3+g&O8poL#aHXe4V#-gJIQ)_pQM5~V^_7XYm`fver z3_XLZFV$gBM$2g=QDHe(=qpaogjt~;XXF*Uud_Zz3v%Z zFv)B17LPpl>97O(R&ElrCV6?1(>t$K^7N(Dost!YJT=`t!SgLSal7`Ws9DmBONG## zEIqBG6P3e<3j)8??0M=SbZO*sN!L#D$`>6jl;<^ExJ>$IY450Ei4n|Ia>pZC-K>UY z64NT05xL=lu*h5chUyt43jnt}aU-)R^x@d#6{0s@aco_b+DriauEgbrp7Q=P>O}hs zVF=Fw)Pai*!yaIn208R-wxk_Cyo3owP#Q^=bQu>*>4Yv5a~q@&FsySvy2t zYzN#6Th7em7w9}W#y;$(e2zv%Yif`Y`75r?l_8X*Zx{ZBG^@i}T5A8xv3o>lBy*dR zi@K=(gdW4a80Rjz2YNAAKp`7SXXVb+G{4oVz2>b~Jj{|>6!Hd9;Dfl+v!3%5qb0_fW3cd$;hl0Nbe1U@R1-?+h z4*;)9QYdwz6rSd#kKu= zKX6#VUj#l|!9N0?Q1GL`lL|g{dO7$W1?PYtRPb%UgDvS1R~7z&Qm! z0X(PR(B;d)ih{QT-=g3g@Pi7T2Yy(=2Y??@@Lj;$FRG0%^a`}E;3?p*DtI68*A;xq zE0=@sdTDL@7XyD%!50C4TEXuF?zy<8|5Mg{T%fRPdQX9`bz&jND zUEmP~{~oxY;8S0X`J~`c;5!vO4ScVHXMpch@Rh*#D_949K*0j|Aq8I#{2K+o3HZMi z{9)jMKT-Avc!z?&0=!efj{xsh@Q;8e6#OXgq=JK2EC;Vra2K$n;H|)KRPd$1pHuJ^ zz~5HzmB9Ba_-f$C75rA<-zxZa;FB+{?cXE7yA}L1;Jpg|58!D95A0tKUZda(fj^<( z5#Y}&I01Zzg7*S{O~DTEzbp7tz{d{<(vK zW(B_x_%;Q<2>9a)P600~cnXrwX=#|4qT~0DelrcL8r7sqN3VfM2HIhk?fw z{0Q)F1qZa{U|GSvz*j3c3j7)cUjY0u1?PYtRImyBsDiHr{!ay82mD_Oz72RVUK{_z zz+(yy&Eop0;7!1uNA5d_AZaMf`1#bqvPQhD%-=N^LfNxgtcHmnStOGxw;9G!uM{DEx zIPg{le+KwO1%DR!c?!NA_+$m&2Yj)DH;Con>lC~h_&Nn|0e*vmp9}nU1@8p@sDhV( zf3DygfrGnhvoCdyI!B+!6 zuHb8cf2H8p1OHmV-TCFf}uY%74zFEQBfp1apH1NF&7QhcF zI1l`N1>XStq=J3_-@&oucsiL_?fV;*?kCAyz29$B-L0-}y}$1|?E3FQ)&0kRgLO(r z6aU|$BiTjM=H&`M%_rV31d%GXS|om#Wmd}G3fG)@j8X$+U`EFaGdT|S|7HK*R!-L4 z?EkATDNg79n-%u|rC!STK9}tO%l^MPI0UO_5c~hK|8F)sl8LhaFGyhj-_`+6p0}XM z{=e-1%l^Oen(4bF#o7N?eI{rBUpYD0|CjxL|496Q{rW?DP=9C^l0)UEYJDJSuIgaf zhUj`}( zAa8`c3G!yhTOfZx!+(a)w?S@zydClm$U7l7Lf!>=H$>{a7oRsl-UoR<Ek})cX)VKMc7Q@)5{KA-6$32KhMT6Od0rJ_Y$S|FBR;BYDI zZ`qR^NhVTLt6u>0BFa#P;nlwZXs&&nr(ORJ0J|F4Qon8`LC0gE=QYXoG2{1qR2e}$ zvwk2L=ncfDs!^>eB!W2d0m`9!jUA)61FX#CQ{D-(eQx9`9B&SIjbpYIR8gH z(d>bo^M8b^mlDqZaSZc+tl0n3lcW1m$>}=#U)r2oFvOt-`(IP-<2>zp`=5M2;O5_z z-)i2#Tk7@O*#!O3tf@_~eh`YgZu0>uwz~Dth(mD)W<%NpSvO&xeci|Dr_Bs{Fsc{cmeFo*AAP8BeY@0QMoFtyY{xg65oE z4hF(*=sdE*w$R_^>$kH32Bj4Z%hrYsjwUBS%WjD-qM+b^*|6}_fapz$BU-QQew*RsHZzRL@zgFcN@N-eLsYt~3Keqp|{m(MU z@&s)EWBVW5|G55FgUg&-8-(kB)kUwc{g3z+$M!$A|BYmt*8lSDf17kOCsvGhRU2Gb z{;A&X*FAeGm#gD3C5obrC9IzhWz)Q>_n-x@K-MDMB&q*Wn{{P~INZpQ+5Kebotp z&TNz3y97!W>dxgvXJJ{@a28b!DIHmnO{R(^bo#1Q+q^2+gkFzV-HlKQ?UE&A35Dpc zf{7ugN;*YSc$cM>=TUZyXleaeB}c4Ymoka9;oX&Dc@IuXO2;pkOz3o>wjb^mE%V@Z z%Mx*WQ-ns3S4xmjiFhhxJD~~e4K)+G-4cmJKPaN;)^#RyLX-Az*&r0U;h)v!#IAZk z5>3Fx%PnO|>gbDmWg%`tr^l;ZN2o;H%GvD(Y?dq32^6Z<@FlwG&d4BPi_qwjh5d;2 zp=#whLLnmSrO(MK$b?cz)+DAVTep`yw)^6kWIEA7w_E;}(m*zfNPnZ@Kj!FHwC&9qDWo`QYb*)<~}n}Uq6x95Stggh~f*k~(%d?;S;r%q$9I zll3Y)DKgvmYHYQaZV%`G~7My zLn&^Ys1zJy3ggS7aFI?j+>7`GVe6(vYIRF90o|ox+xXo)WnqkMyhkF{;q6UP?L|Br z3Z>IiZHLl`N{g{HwNOg_3{PtnoBTaH;LSwaHQ7>adOrT;c1`vr7evXKsMv;1HP=(M z!cZE$*-BYhC38Wev>JbHOY=G}%m6d+tYyH@|2>E7*E0L}&=Ky*Ao5>tz&5=QnajA7 z&HqX&$%NHd{Qg~Z=1TM@DGLfHh6#d_$ z^q&Chvw=z=kADKk;7Awb|32dUmh z?v^>JyYdRzdk@@=z8(vmw36Xj+J5njr;DK(x9ujmTxvS4UJY+v-BNRL11@zx@8!{W0!N7JmQR zP&}63|27uX<@djh(}dsu^7~(o|KRu!j{nF}#->P|<3Bk5gX2FMUYWYDKREt_<3HRV zWRWahzqEO0!to!z6PWMhPu+T=*^+kn@KT#sOn)TxAAS2@bL*|N=d%3|53BV-C~W_; z%ma--_2R;Ev1jAC6dbWDq3ztU4w1 z`F0*%(Xzf+kzr?4OVX7K!B}}gx^i*-H?IFy;`k53Yn$UgIR1m3*mu_ppYohTvq%_eieco6QRR_i3 zHNA0bsqEN}G(5Xn;ZX!yIHpNA z+0FT11QPvpP^X}SyAJ#RvHu_Y|FQoc`~S_$%x9EQp6kC2r_)KU|Hk#-iguoSeZ~2o z2}>gT|FQpHi~P^g!1}61Ef6>npP@kT(ukL+;+(i#A@eqe@mD|L@Hr!v&<^@*5H)+v zmV}1zp%$Vwr%lM)I9?j9J{bz$4FeTG@^@?*LyVY0hBnkvQ8T3*DG zmSrN@sJrGQtl69@6hvMt#8M?#8Y{Y+S<_wrHmu(=Y+2e;Y{aaTa&}DSOA$GNM53<7 z4ba#+S}-*ymJlV=Dx#rDWVz5vD=Ke?%AluWEFcANY<4O3N?{@QrrX9lY6?iXxv1$* zEF)~BNAVTj35Da_7E+}J?0nAcLmldk(CK$|CSgFGE0~A;&EJJp8{JiKgwYSi;`2gB z8cw(OcVq1ackN!8h*jP4IIN58F=G?R6lyK`Tbzc!Zrfv)bl75zQbj}EzA7(ebv&m- zX|q4tgkhIW+fdiP2kW<{N{+}2E0$==%X^_R;H%W{)J<~xw1o|E^hB=N(R&gq{q7#u zZ+$@!Qz9`w9@}F&u~B(w{hb@Zn&ah~R0-piNs+2^DK@I1qZO^!28wqNAggc%yWY^m z>#%Zj+A?*Kt5{9ehvm3@YQ`{A3a;rH2Sm4vKRIr~&@Iz8=bTu4`}Rg=p%&+C$oZdFTW>l4bK@N0{Le5k z^>F@Y&j0K``g8v0ATl*IcTMVbqYBO01C%wY-_Ot2&fzqY%}(vumr9&F*1F5CA=2{2 z@xSuSW&0o7|5leY@Nz?P{4dABCF4i&~B5=)*9vbU$Zo3I!KJ{-{_I$P)Gj3 zBC-#b0uJ&Z9@!%EKgzt1{?f_T-;+vS{>4u8+v)gy%g=VY$721nx&Q8}m3AwPz-<3z z`>(V|vi;XJZT<#}{Xg0N6VY01|84!ob%R%